Merge branch 'master' of ssh://git.indexdata.com/home/git/pub/yaz
authorDennis Schafroth <dennis@indexdata.com>
Thu, 19 Aug 2010 08:25:18 +0000 (10:25 +0200)
committerDennis Schafroth <dennis@indexdata.com>
Thu, 19 Aug 2010 08:25:18 +0000 (10:25 +0200)
31 files changed:
.cproject
NEWS
client/client.c
configure.ac
debian/changelog
debian/rules
include/yaz/Makefile.am
include/yaz/record_render.h [new file with mode: 0644]
include/yaz/rpn2solr.h [new file with mode: 0644]
include/yaz/solr.h [new file with mode: 0644]
include/yaz/srw.h
include/yaz/zoom.h
src/Makefile.am
src/cqltransform.c
src/record_render.c [new file with mode: 0644]
src/rpn2solr.c [new file with mode: 0644]
src/solr.c [new file with mode: 0644]
src/solrtransform.c [new file with mode: 0644]
src/sru-p.h [new file with mode: 0644]
src/srwutil.c
src/zoom-c.c
src/zoom-event.c [new file with mode: 0644]
src/zoom-opt.c
src/zoom-p.h
src/zoom-query.c [new file with mode: 0644]
src/zoom-record-cache.c [new file with mode: 0644]
src/zoom-sru.c [new file with mode: 0644]
src/zoom-z3950.c [new file with mode: 0644]
test/Makefile.am
test/test_rpn2solr.c [new file with mode: 0644]
win/makefile

index d3926f3..2b699b9 100644 (file)
--- a/.cproject
+++ b/.cproject
@@ -40,6 +40,7 @@
                                                                <option defaultValue="true" id="macosx.cpp.link.option.shared.2082905640" name="Shared (-dynamiclib)" superClass="macosx.cpp.link.option.shared" valueType="boolean"/>
                                                        </tool>
                                                        <tool id="cdt.managedbuild.tool.gnu.assembler.macosx.base.1871976203" name="GCC Assembler" superClass="cdt.managedbuild.tool.gnu.assembler.macosx.base">
+                                                               <option id="gnu.both.asm.option.include.paths.998378482" name="Include paths (-I)" superClass="gnu.both.asm.option.include.paths" valueType="includePath"/>
                                                                <inputType id="cdt.managedbuild.tool.gnu.assembler.input.1256561366" superClass="cdt.managedbuild.tool.gnu.assembler.input"/>
                                                        </tool>
                                                        <tool id="cdt.managedbuild.tool.gnu.archiver.macosx.base.1171332498" name="GCC Archiver" superClass="cdt.managedbuild.tool.gnu.archiver.macosx.base"/>
@@ -98,6 +99,7 @@
                                                                        <listOptionValue builtIn="false" value="HAVE_XSLTSAVERESULTTOSTRING=1"/>
                                                                </option>
                                                                <option id="gnu.c.compiler.option.include.paths.773210380" name="Include paths (-I)" superClass="gnu.c.compiler.option.include.paths" valueType="includePath">
+                                                                       <listOptionValue builtIn="false" value="&quot;${workspace_loc:/yaz/include}&quot;"/>
                                                                        <listOptionValue builtIn="false" value="/usr/include/libxml2"/>
                                                                        <listOptionValue builtIn="false" value="/usr/local/include"/>
                                                                </option>
                                                        </tool>
                                                </toolChain>
                                        </folderInfo>
-                                       <fileInfo id="cdt.managedbuild.toolchain.gnu.macosx.base.368006805.1041315687" name="client.c" rcbsApplicability="disable" resourcePath="client/client.c" toolsToInvoke="cdt.managedbuild.tool.gnu.c.compiler.macosx.base.1367873707.2080555490">
-                                               <tool id="cdt.managedbuild.tool.gnu.c.compiler.macosx.base.1367873707.2080555490" name="GCC C Compiler" superClass="cdt.managedbuild.tool.gnu.c.compiler.macosx.base.1367873707"/>
-                                       </fileInfo>
                                        <sourceEntries>
                                                <entry excluding="*.lo|*.o" flags="VALUE_WORKSPACE_PATH" kind="sourcePath" name="src"/>
                                                <entry excluding="*.o|*.lo" flags="VALUE_WORKSPACE_PATH" kind="sourcePath" name="util"/>
                                                <entry flags="VALUE_WORKSPACE_PATH" kind="sourcePath" name="client"/>
-                                               <entry flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="sourcePath" name="include"/>
-                                               <entry excluding="*.lo|*.o" flags="VALUE_WORKSPACE_PATH" kind="sourcePath" name="zoom"/>
                                                <entry excluding="*.o|*.lo" flags="VALUE_WORKSPACE_PATH" kind="sourcePath" name="test"/>
                                                <entry flags="VALUE_WORKSPACE_PATH" kind="sourcePath" name="zoom"/>
                                        </sourceEntries>
                                                </scannerInfoProvider>
                                        </profile>
                                </scannerConfigBuildInfo>
+                               <scannerConfigBuildInfo instanceId="cdt.managedbuild.toolchain.gnu.macosx.base.368006805;cdt.managedbuild.toolchain.gnu.macosx.base.368006805.566575041;cdt.managedbuild.tool.gnu.c.compiler.macosx.base.747936463;cdt.managedbuild.tool.gnu.c.compiler.input.1402339125">
+                                       <autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfileC"/>
+                                       <profile id="org.eclipse.cdt.make.core.GCCStandardMakePerProjectProfile">
+                                               <buildOutputProvider>
+                                                       <openAction enabled="true" filePath=""/>
+                                                       <parser enabled="true"/>
+                                               </buildOutputProvider>
+                                               <scannerInfoProvider id="specsFile">
+                                                       <runAction arguments="-E -P -v -dD ${plugin_state_location}/${specs_file}" command="gcc" useDefault="true"/>
+                                                       <parser enabled="true"/>
+                                               </scannerInfoProvider>
+                                       </profile>
+                                       <profile id="org.eclipse.cdt.make.core.GCCStandardMakePerFileProfile">
+                                               <buildOutputProvider>
+                                                       <openAction enabled="true" filePath=""/>
+                                                       <parser enabled="true"/>
+                                               </buildOutputProvider>
+                                               <scannerInfoProvider id="makefileGenerator">
+                                                       <runAction arguments="-f ${project_name}_scd.mk" command="make" useDefault="true"/>
+                                                       <parser enabled="true"/>
+                                               </scannerInfoProvider>
+                                       </profile>
+                                       <profile id="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfile">
+                                               <buildOutputProvider>
+                                                       <openAction enabled="true" filePath=""/>
+                                                       <parser enabled="true"/>
+                                               </buildOutputProvider>
+                                               <scannerInfoProvider id="specsFile">
+                                                       <runAction arguments="-E -P -v -dD ${plugin_state_location}/${specs_file}" command="gcc" useDefault="true"/>
+                                                       <parser enabled="true"/>
+                                               </scannerInfoProvider>
+                                       </profile>
+                                       <profile id="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfileCPP">
+                                               <buildOutputProvider>
+                                                       <openAction enabled="true" filePath=""/>
+                                                       <parser enabled="true"/>
+                                               </buildOutputProvider>
+                                               <scannerInfoProvider id="specsFile">
+                                                       <runAction arguments="-E -P -v -dD ${plugin_state_location}/specs.cpp" command="g++" useDefault="true"/>
+                                                       <parser enabled="true"/>
+                                               </scannerInfoProvider>
+                                       </profile>
+                                       <profile id="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfileC">
+                                               <buildOutputProvider>
+                                                       <openAction enabled="true" filePath=""/>
+                                                       <parser enabled="true"/>
+                                               </buildOutputProvider>
+                                               <scannerInfoProvider id="specsFile">
+                                                       <runAction arguments="-E -P -v -dD ${plugin_state_location}/specs.c" command="gcc" useDefault="true"/>
+                                                       <parser enabled="true"/>
+                                               </scannerInfoProvider>
+                                       </profile>
+                                       <profile id="org.eclipse.cdt.managedbuilder.core.GCCWinManagedMakePerProjectProfile">
+                                               <buildOutputProvider>
+                                                       <openAction enabled="true" filePath=""/>
+                                                       <parser enabled="true"/>
+                                               </buildOutputProvider>
+                                               <scannerInfoProvider id="specsFile">
+                                                       <runAction arguments="-c 'gcc -E -P -v -dD &quot;${plugin_state_location}/${specs_file}&quot;'" command="sh" useDefault="true"/>
+                                                       <parser enabled="true"/>
+                                               </scannerInfoProvider>
+                                       </profile>
+                                       <profile id="org.eclipse.cdt.managedbuilder.core.GCCWinManagedMakePerProjectProfileCPP">
+                                               <buildOutputProvider>
+                                                       <openAction enabled="true" filePath=""/>
+                                                       <parser enabled="true"/>
+                                               </buildOutputProvider>
+                                               <scannerInfoProvider id="specsFile">
+                                                       <runAction arguments="-c 'g++ -E -P -v -dD &quot;${plugin_state_location}/specs.cpp&quot;'" command="sh" useDefault="true"/>
+                                                       <parser enabled="true"/>
+                                               </scannerInfoProvider>
+                                       </profile>
+                                       <profile id="org.eclipse.cdt.managedbuilder.core.GCCWinManagedMakePerProjectProfileC">
+                                               <buildOutputProvider>
+                                                       <openAction enabled="true" filePath=""/>
+                                                       <parser enabled="true"/>
+                                               </buildOutputProvider>
+                                               <scannerInfoProvider id="specsFile">
+                                                       <runAction arguments="-c 'gcc -E -P -v -dD &quot;${plugin_state_location}/specs.c&quot;'" command="sh" useDefault="true"/>
+                                                       <parser enabled="true"/>
+                                               </scannerInfoProvider>
+                                       </profile>
+                               </scannerConfigBuildInfo>
                                <scannerConfigBuildInfo instanceId="cdt.managedbuild.toolchain.gnu.macosx.base.368006805;cdt.managedbuild.toolchain.gnu.macosx.base.368006805.791362720;cdt.managedbuild.tool.gnu.c.compiler.macosx.base.2071622157;cdt.managedbuild.tool.gnu.c.compiler.input.986509299">
                                        <autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfileC"/>
                                        <profile id="org.eclipse.cdt.make.core.GCCStandardMakePerProjectProfile">
                                                </scannerInfoProvider>
                                        </profile>
                                </scannerConfigBuildInfo>
+                               <scannerConfigBuildInfo instanceId="cdt.managedbuild.toolchain.gnu.macosx.base.368006805;cdt.managedbuild.toolchain.gnu.macosx.base.368006805.761048058;cdt.managedbuild.tool.gnu.c.compiler.macosx.base.1367873707;cdt.managedbuild.tool.gnu.c.compiler.input.1402339125">
+                                       <autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfileC"/>
+                                       <profile id="org.eclipse.cdt.make.core.GCCStandardMakePerProjectProfile">
+                                               <buildOutputProvider>
+                                                       <openAction enabled="true" filePath=""/>
+                                                       <parser enabled="true"/>
+                                               </buildOutputProvider>
+                                               <scannerInfoProvider id="specsFile">
+                                                       <runAction arguments="-E -P -v -dD ${plugin_state_location}/${specs_file}" command="gcc" useDefault="true"/>
+                                                       <parser enabled="true"/>
+                                               </scannerInfoProvider>
+                                       </profile>
+                                       <profile id="org.eclipse.cdt.make.core.GCCStandardMakePerFileProfile">
+                                               <buildOutputProvider>
+                                                       <openAction enabled="true" filePath=""/>
+                                                       <parser enabled="true"/>
+                                               </buildOutputProvider>
+                                               <scannerInfoProvider id="makefileGenerator">
+                                                       <runAction arguments="-f ${project_name}_scd.mk" command="make" useDefault="true"/>
+                                                       <parser enabled="true"/>
+                                               </scannerInfoProvider>
+                                       </profile>
+                                       <profile id="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfile">
+                                               <buildOutputProvider>
+                                                       <openAction enabled="true" filePath=""/>
+                                                       <parser enabled="true"/>
+                                               </buildOutputProvider>
+                                               <scannerInfoProvider id="specsFile">
+                                                       <runAction arguments="-E -P -v -dD ${plugin_state_location}/${specs_file}" command="gcc" useDefault="true"/>
+                                                       <parser enabled="true"/>
+                                               </scannerInfoProvider>
+                                       </profile>
+                                       <profile id="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfileCPP">
+                                               <buildOutputProvider>
+                                                       <openAction enabled="true" filePath=""/>
+                                                       <parser enabled="true"/>
+                                               </buildOutputProvider>
+                                               <scannerInfoProvider id="specsFile">
+                                                       <runAction arguments="-E -P -v -dD ${plugin_state_location}/specs.cpp" command="g++" useDefault="true"/>
+                                                       <parser enabled="true"/>
+                                               </scannerInfoProvider>
+                                       </profile>
+                                       <profile id="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfileC">
+                                               <buildOutputProvider>
+                                                       <openAction enabled="true" filePath=""/>
+                                                       <parser enabled="true"/>
+                                               </buildOutputProvider>
+                                               <scannerInfoProvider id="specsFile">
+                                                       <runAction arguments="-E -P -v -dD ${plugin_state_location}/specs.c" command="gcc" useDefault="true"/>
+                                                       <parser enabled="true"/>
+                                               </scannerInfoProvider>
+                                       </profile>
+                                       <profile id="org.eclipse.cdt.managedbuilder.core.GCCWinManagedMakePerProjectProfile">
+                                               <buildOutputProvider>
+                                                       <openAction enabled="true" filePath=""/>
+                                                       <parser enabled="true"/>
+                                               </buildOutputProvider>
+                                               <scannerInfoProvider id="specsFile">
+                                                       <runAction arguments="-c 'gcc -E -P -v -dD &quot;${plugin_state_location}/${specs_file}&quot;'" command="sh" useDefault="true"/>
+                                                       <parser enabled="true"/>
+                                               </scannerInfoProvider>
+                                       </profile>
+                                       <profile id="org.eclipse.cdt.managedbuilder.core.GCCWinManagedMakePerProjectProfileCPP">
+                                               <buildOutputProvider>
+                                                       <openAction enabled="true" filePath=""/>
+                                                       <parser enabled="true"/>
+                                               </buildOutputProvider>
+                                               <scannerInfoProvider id="specsFile">
+                                                       <runAction arguments="-c 'g++ -E -P -v -dD &quot;${plugin_state_location}/specs.cpp&quot;'" command="sh" useDefault="true"/>
+                                                       <parser enabled="true"/>
+                                               </scannerInfoProvider>
+                                       </profile>
+                                       <profile id="org.eclipse.cdt.managedbuilder.core.GCCWinManagedMakePerProjectProfileC">
+                                               <buildOutputProvider>
+                                                       <openAction enabled="true" filePath=""/>
+                                                       <parser enabled="true"/>
+                                               </buildOutputProvider>
+                                               <scannerInfoProvider id="specsFile">
+                                                       <runAction arguments="-c 'gcc -E -P -v -dD &quot;${plugin_state_location}/specs.c&quot;'" command="sh" useDefault="true"/>
+                                                       <parser enabled="true"/>
+                                               </scannerInfoProvider>
+                                       </profile>
+                               </scannerConfigBuildInfo>
                                <scannerConfigBuildInfo instanceId="cdt.managedbuild.toolchain.gnu.macosx.base.368006805;cdt.managedbuild.toolchain.gnu.macosx.base.368006805.791362720;cdt.managedbuild.tool.gnu.c.compiler.macosx.base.2071622157;cdt.managedbuild.tool.gnu.c.compiler.input.1402339125">
                                        <autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfileC"/>
                                        <profile id="org.eclipse.cdt.make.core.GCCStandardMakePerProjectProfile">
                                                </scannerInfoProvider>
                                        </profile>
                                </scannerConfigBuildInfo>
-                               <scannerConfigBuildInfo instanceId="cdt.managedbuild.toolchain.gnu.macosx.base.368006805;cdt.managedbuild.toolchain.gnu.macosx.base.368006805.761048058;cdt.managedbuild.tool.gnu.c.compiler.macosx.base.1367873707;cdt.managedbuild.tool.gnu.c.compiler.input.1402339125">
+                               <scannerConfigBuildInfo instanceId="cdt.managedbuild.toolchain.gnu.macosx.base.368006805;cdt.managedbuild.toolchain.gnu.macosx.base.368006805.566575041;cdt.managedbuild.tool.gnu.c.compiler.macosx.base.747936463;cdt.managedbuild.tool.gnu.c.compiler.input.364396502">
                                        <autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfileC"/>
                                        <profile id="org.eclipse.cdt.make.core.GCCStandardMakePerProjectProfile">
                                                <buildOutputProvider>
diff --git a/NEWS b/NEWS
index 7f50c11..eb25879 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,16 @@
+--- 4.0.13 2010/MM/DD
+
+SOLR WebService support for yaz-client and ZOOM.
+
+--- 4.0.12 2010/08/17
+
+Z39.50 facet support. Facet specifications are passed for search in
+other-information with ASN.1 as defined in facet.asn and OID
+.10.1000.81.5 . Facets are expressed as an Attribute list as known
+from Type-1/RPN queries.  Type 1 is field. Type 2 is sort order. Type
+3 is limit (number of entries to get in response). yaz-ztest returns
+dummy facets if a client asks for them.
+
 --- 4.0.11 2010/07/09
 
 CCL fixes: bug 2895 and bug 3539.
index 7b9ca9c..6bb6b5e 100644 (file)
@@ -1282,6 +1282,11 @@ static int send_srw_host_path(Z_SRW_PDU *sr, const char *host_port,
     {
         yaz_sru_soap_encode(gdu->u.HTTP_Request, sr, out, charset);
     }
+    else if (!yaz_matchstr(sru_method, "solr"))
+    {
+        yaz_solr_encode_request(gdu->u.HTTP_Request, sr, out, charset);
+    }
+
     return send_gdu(gdu);
 }
 
@@ -2799,8 +2804,7 @@ static int cmd_sru(const char *arg)
     }
     else
     {
-        int r;
-        r = sscanf(arg, "%9s %9s", sru_method, sru_version);
+        int r = sscanf(arg, "%9s %9s", sru_method, sru_version);
         if (r >= 1)
         {
             if (!yaz_matchstr(sru_method, "post"))
@@ -2809,11 +2813,13 @@ static int cmd_sru(const char *arg)
                 ;
             else if (!yaz_matchstr(sru_method, "soap"))
                 ;
+            else if (!yaz_matchstr(sru_method, "solr"))
+                ;
             else
             {
                 strcpy(sru_method, "soap");
                 printf("Unknown SRU method: %s\n", arg);
-                printf("Specify one of POST, GET, SOAP\n");
+                printf("Specify one of POST, GET, SOAP, SOLR\n");
             }
         }
     }
@@ -4350,7 +4356,6 @@ static void http_response(Z_HTTP_Response *hres)
     int ret = -1;
     const char *connection_head = z_HTTP_header_lookup(hres->headers,
                                                        "Connection");
-
     if (hres->code != 200)
     {
         printf("HTTP Error Status=%d\n", hres->code);
@@ -4360,51 +4365,69 @@ static void http_response(Z_HTTP_Response *hres)
         printf("Content type does not appear to be XML\n");
     else
     {
-        Z_SOAP *soap_package = 0;
-        ODR o = odr_createmem(ODR_DECODE);
-        Z_SOAP_Handler soap_handlers[3] = {
-            {YAZ_XMLNS_SRU_v1_1, 0, (Z_SOAP_fun) yaz_srw_codec},
-            {YAZ_XMLNS_UPDATE_v0_9, 0, (Z_SOAP_fun) yaz_ucp_codec},
-            {0, 0, 0}
-        };
-        ret = z_soap_codec(o, &soap_package,
-                           &hres->content_buf, &hres->content_len,
-                           soap_handlers);
-        if (!ret && soap_package->which == Z_SOAP_generic)
+        if (!yaz_matchstr(sru_method, "solr"))
         {
-            Z_SRW_PDU *sr = (Z_SRW_PDU *) soap_package->u.generic->p;
-            if (sr->which == Z_SRW_searchRetrieve_response)
+            Z_SRW_PDU *sr = 0;
+            ODR o = odr_createmem(ODR_DECODE);
+            ret = yaz_solr_decode_response(o, hres, &sr);
+
+            if (ret == 0 && sr->which == Z_SRW_searchRetrieve_response)
                 handle_srw_response(sr->u.response);
-            else if (sr->which == Z_SRW_explain_response)
-                handle_srw_explain_response(sr->u.explain_response);
-            else if (sr->which == Z_SRW_scan_response)
-                handle_srw_scan_response(sr->u.scan_response);
-            else if (sr->which == Z_SRW_update_response)
-                printf("Got update response. Status: %s\n",
-                       sr->u.update_response->operationStatus);
             else
             {
-                printf("Decoding of SRW package failed\n");
+                printf("Decoding of SOLR package failed\n");
                 ret = -1;
             }
-        }
-        else if (soap_package && (soap_package->which == Z_SOAP_fault
-                                  || soap_package->which == Z_SOAP_error))
-        {
-            printf("SOAP Fault code %s\n",
-                    soap_package->u.fault->fault_code);
-            printf("SOAP Fault string %s\n",
-                    soap_package->u.fault->fault_string);
-            if (soap_package->u.fault->details)
-                printf("SOAP Details %s\n",
-                        soap_package->u.fault->details);
+            odr_destroy(o);
         }
         else
         {
-            printf("z_soap_codec failed. (no SOAP error)\n");
-            ret = -1;
+            Z_SOAP *soap_package = 0;
+            ODR o = odr_createmem(ODR_DECODE);
+            Z_SOAP_Handler soap_handlers[3] = {
+                {YAZ_XMLNS_SRU_v1_1, 0, (Z_SOAP_fun) yaz_srw_codec},
+                {YAZ_XMLNS_UPDATE_v0_9, 0, (Z_SOAP_fun) yaz_ucp_codec},
+                {0, 0, 0}
+            };
+            ret = z_soap_codec(o, &soap_package,
+                               &hres->content_buf, &hres->content_len,
+                               soap_handlers);
+            if (!ret && soap_package->which == Z_SOAP_generic)
+            {
+                Z_SRW_PDU *sr = (Z_SRW_PDU *) soap_package->u.generic->p;
+                if (sr->which == Z_SRW_searchRetrieve_response)
+                    handle_srw_response(sr->u.response);
+                else if (sr->which == Z_SRW_explain_response)
+                    handle_srw_explain_response(sr->u.explain_response);
+                else if (sr->which == Z_SRW_scan_response)
+                    handle_srw_scan_response(sr->u.scan_response);
+                else if (sr->which == Z_SRW_update_response)
+                    printf("Got update response. Status: %s\n",
+                           sr->u.update_response->operationStatus);
+                else
+                {
+                    printf("Decoding of SRW package failed\n");
+                    ret = -1;
+                }
+            }
+            else if (soap_package && (soap_package->which == Z_SOAP_fault
+                                      || soap_package->which == Z_SOAP_error))
+            {
+                printf("SOAP Fault code %s\n",
+                       soap_package->u.fault->fault_code);
+                printf("SOAP Fault string %s\n",
+                       soap_package->u.fault->fault_string);
+                if (soap_package->u.fault->details)
+                    printf("SOAP Details %s\n",
+                           soap_package->u.fault->details);
+            }
+            else
+            {
+                printf("z_soap_codec failed. (no SOAP error)\n");
+                ret = -1;
+            }
+            odr_destroy(o);
         }
-        odr_destroy(o);
     }
     if (ret)
         close_session(); /* close session on error */
index c31a5e9..fca1e8f 100644 (file)
@@ -1,7 +1,7 @@
 dnl This file is part of the YAZ toolkit.
 dnl Copyright (C) 1995-2010 Index Data
 AC_PREREQ([2.60])
-AC_INIT([yaz],[4.0.11],[yaz-help@indexdata.dk])
+AC_INIT([yaz],[4.0.13],[yaz-help@indexdata.dk])
 AC_CONFIG_SRCDIR([configure.ac])
 AC_CONFIG_AUX_DIR([config])
 AM_INIT_AUTOMAKE([1.9])
index 27b4bf2..ec61805 100644 (file)
@@ -1,3 +1,15 @@
+yaz (4.0.13-1indexdata) unstable; urgency=low
+
+  * Upstream.
+
+ -- Adam Dickmeiss <adam@indexdata.dk>  Thu, 19 Aug 2010 10:08:53 +0200
+
+yaz (4.0.12-1indexdata) unstable; urgency=low
+
+  * Upstream.
+
+ -- Adam Dickmeiss <adam@indexdata.dk>  Tue, 17 Aug 2010 12:33:25 +0200
+
 yaz (4.0.11-1indexdata) unstable; urgency=low
 
   * Upstream.
index 2b011be..a6854cf 100755 (executable)
@@ -98,7 +98,7 @@ binary-arch: build install
        dh_fixperms
 #      dh_perl
 #      dh_python
-       dh_makeshlibs -V 'libyaz4 (>= 4.0.10)'
+       dh_makeshlibs -V 'libyaz4 (>= 4.0.12)'
        dh_installdeb
        dh_shlibdeps  -l debian/libyaz4/usr/lib
        dh_gencontrol
index f4e6548..f34b2a7 100644 (file)
@@ -3,11 +3,12 @@
 
 noinst_HEADERS = icu_I18N.h
 
-pkginclude_HEADERS= backend.h ccl.h ccl_xml.h cql.h rpn2cql.h comstack.h \
+pkginclude_HEADERS= backend.h ccl.h ccl_xml.h cql.h rpn2cql.h rpn2solr.h \
+ solr.h comstack.h \
  diagbib1.h diagsrw.h diagsru_update.h sortspec.h log.h logrpn.h marcdisp.h \
  nmem.h nmem_xml.h odr.h errno.h facet.h \
  options.h otherinfo.h pquery.h prt-ext.h querytowrbuf.h \
- readconf.h record_conv.h retrieval.h statserv.h \
+ readconf.h record_conv.h record_render.h retrieval.h statserv.h \
  tcpip.h test.h timing.h unix.h tpath.h wrbuf.h xmalloc.h \
  yaz-ccl.h yaz-iconv.h yaz-util.h yaz-version.h yconfig.h proto.h \
  xmlquery.h xmltypes.h snprintf.h query-charset.h \
diff --git a/include/yaz/record_render.h b/include/yaz/record_render.h
new file mode 100644 (file)
index 0000000..bdc0889
--- /dev/null
@@ -0,0 +1,68 @@
+/* This file is part of the YAZ toolkit.
+ * Copyright (C) 1995-2010 Index Data.
+ * All rights reserved.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of Index Data nor the names of its contributors
+ *       may be used to endorse or promote products derived from this
+ *       software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * \file record_render.h
+ * \brief Record render header
+ */
+
+#ifndef YAZ_RECORD_RENDER_H
+#define YAZ_RECORD_RENDER_H
+
+#include <stddef.h>
+#include <yaz/yconfig.h>
+#include <yaz/z-core.h>
+#include <yaz/wrbuf.h>
+
+YAZ_BEGIN_CDECL
+
+/** \brief render records (ZOOM style)
+    \param npr record structure to be rendered (Z39.50)
+    \param schema for record (if known)
+    \param wrbuf Working WRBUF
+    \param type_spec format spec
+    \param len length of returned buffer
+    \retval !=0 buffer
+    \retval =0 record could not be rendered
+*/
+YAZ_EXPORT
+const char *yaz_record_render(Z_NamePlusRecord *npr, const char *schema,
+                              WRBUF wrbuf,
+                              const char *type_spec, int *len);
+
+YAZ_END_CDECL
+
+#endif
+/*
+ * Local variables:
+ * c-basic-offset: 4
+ * c-file-style: "Stroustrup"
+ * indent-tabs-mode: nil
+ * End:
+ * vim: shiftwidth=4 tabstop=8 expandtab
+ */
+
diff --git a/include/yaz/rpn2solr.h b/include/yaz/rpn2solr.h
new file mode 100644 (file)
index 0000000..f4dced9
--- /dev/null
@@ -0,0 +1,89 @@
+/* This file is part of the YAZ toolkit.
+ * Copyright (C) 1995-2010 Index Data.
+ * All rights reserved.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of Index Data nor the names of its contributors
+ *       may be used to endorse or promote products derived from this
+ *       software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** \file
+    \brief Header for RPN to SOLR tranform
+*/
+
+#ifndef RPN2SOLR_H_INCLUDED
+#define RPN2SOLR_H_INCLUDED
+#include <yaz/z-core.h>
+#include <yaz/solr.h>
+#include <yaz/wrbuf.h>
+
+YAZ_BEGIN_CDECL
+
+/** \brief transforms RPN query to SOLR output stream
+    \param ct SOLR transform handle
+    \param pr print function
+    \param client_data opaque data to be passed to print handler
+    \param q RPN Query
+    \retval 0 success
+    \retval !=0 failure (error code)
+ */
+YAZ_EXPORT
+int solr_transform_rpn2solr_stream(solr_transform_t ct,
+                                 void (*pr)(const char *buf, void *client_data),
+                                 void *client_data,
+                                 Z_RPNQuery *q);
+
+
+/** \brief transforms RPN query to SOLR WRBUF
+    \param ct SOLR transform handle
+    \param w WRBUF handle for result
+    \param q RPN Query
+    \retval 0 success
+    \retval !=0 failure (error code)
+ */
+YAZ_EXPORT
+int solr_transform_rpn2solr_wrbuf(solr_transform_t ct,
+                                WRBUF w,
+                                Z_RPNQuery *q);
+
+/** \brief find a pattern that has a subset of attributes
+    \param ct SOLR transform handle
+    \param category pattern category
+    \param attributes RPN attributes
+    \returns pattern value or NULL if not found
+ */
+const char *solr_lookup_reverse(solr_transform_t ct,
+                               const char *category,
+                               Z_AttributeList *attributes);
+
+YAZ_END_CDECL
+
+#endif
+/* SOLR_H_INCLUDED */
+/*
+ * Local variables:
+ * c-basic-offset: 4
+ * c-file-style: "Stroustrup"
+ * indent-tabs-mode: nil
+ * End:
+ * vim: shiftwidth=4 tabstop=8 expandtab
+ */
+
diff --git a/include/yaz/solr.h b/include/yaz/solr.h
new file mode 100644 (file)
index 0000000..09ecdcc
--- /dev/null
@@ -0,0 +1,376 @@
+/* This file is part of the YAZ toolkit.
+ * Copyright (C) 1995-2010 Index Data.
+ * All rights reserved.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of Index Data nor the names of its contributors
+ *       may be used to endorse or promote products derived from this
+ *       software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** \file solr.h
+    \brief Header with public definitions about SOLR.
+*/
+
+#ifndef SOLR_H_INCLUDED
+#define SOLR_H_INCLUDED
+#include <stdio.h>
+#include <yaz/nmem.h>
+
+YAZ_BEGIN_CDECL
+
+/** \brief SOLR parser handle (opaque pointer) */
+typedef struct solr_parser *SOLR_parser;
+
+/** \brief creates a SOLR parser.
+    \returns CCL parser
+    
+    Returns SOLR parser or NULL if parser could not be created.
+ */
+YAZ_EXPORT 
+SOLR_parser solr_parser_create(void);
+
+/** \brief destroys a SOLR parser.
+    \param cp SOLR parser
+
+    This function does nothing if NULL if received.
+ */
+YAZ_EXPORT 
+void solr_parser_destroy(SOLR_parser cp);
+
+/** \brief parses a SOLR query (string)
+    \param cp SOLR parser
+    \param str SOLR string
+    \retval 0 success
+    \retval !=0 failure
+ */
+YAZ_EXPORT 
+int solr_parser_string(SOLR_parser cp, const char *str);
+
+/** \brief parses SOLR query (query stream)
+    \param cp SOLR parser
+    \param getbyte function which reads one character from stream
+    \param ungetbyte function which unreads one character from stream
+    \param client_data data to be passed to stream functions
+    \retval 0 success
+    \retval !=0 failure
+    
+    This function is similar to solr_parser_string but takes a
+    functions to read each query character from a stream.
+    
+    The functions pointers getbytes, ungetbyte are similar to
+    that known from stdios getc, ungetc.
+*/
+YAZ_EXPORT 
+int solr_parser_stream(SOLR_parser cp,
+                      int (*getbyte)(void *client_data),
+                      void (*ungetbyte)(int b, void *client_data),
+                      void *client_data);
+
+/** \brief parses SOLR query (from FILE)
+    \param cp SOLR parser
+    \param f file where query is read from
+    \retval 0 success
+    \retval !=0 failure
+    
+    This function is similar to solr_parser_string but reads from
+    stdio FILE handle instead.
+*/
+YAZ_EXPORT
+int solr_parser_stdio(SOLR_parser cp, FILE *f);
+
+/** \brief Node type: search term */
+#define SOLR_NODE_ST 1
+/** \brief Node type: boolean */
+#define SOLR_NODE_BOOL 2
+/** \brief SOLR parse tree (node)
+ */
+struct solr_node {
+    /** node type */
+    int which;
+    union {
+        /** which == SOLR_NODE_ST */
+        struct {
+            /** SOLR index */
+            char *index;
+            /** SOLR index URI or NULL if no URI */
+            char *index_uri;
+            /** Search term */
+            char *term;
+            /** relation */
+            char *relation;
+            /** relation URL or NULL if no relation URI) */
+            char *relation_uri;
+            /** relation modifiers */
+            struct solr_node *modifiers;
+            /** term list */
+            struct solr_node *extra_terms;
+        } st;
+        /** which == SOLR_NODE_BOOL */
+        struct {
+            /** operator name "and", "or", ... */
+            char *value;
+            /** left operand */
+            struct solr_node *left;
+            /** right operand */ 
+            struct solr_node *right;
+            /** modifiers (NULL for no list) */
+            struct solr_node *modifiers;
+        } boolean;
+    } u;
+};
+
+/** \brief Private structure that describes the SOLR properties (profile)
+ */
+struct solr_properties;
+
+/** \brief Structure used by solr_buf_write_handler
+ */
+struct solr_buf_write_info {
+    int max;
+    int off;
+    char *buf;
+};
+
+/** \brief Handler for solr_buf_write_info
+ */
+YAZ_EXPORT
+void solr_buf_write_handler(const char *b, void *client_data);
+
+/** \brief Prints a SOLR node and all sub nodes.
+    Hence this function prints the parse tree which is as returned by
+    solr_parser_result.
+*/
+YAZ_EXPORT
+void solr_node_print(struct solr_node *cn);
+
+/** \brief creates a search clause node (st). */
+YAZ_EXPORT
+struct solr_node *solr_node_mk_sc(NMEM nmem, const char *index,
+                                const char *relation, const char *term);
+
+/** \brief applies a prefix+uri to "unresolved" index and relation URIs.
+    "unresolved" URIs are those nodes where member index_uri / relation_uri
+    is NULL.
+*/
+YAZ_EXPORT
+struct solr_node *solr_apply_prefix(NMEM nmem, struct solr_node *cn,
+                                  const char *prefix, const char *uri);
+
+/** \brief creates a boolean node. */
+YAZ_EXPORT
+struct solr_node *solr_node_mk_boolean(NMEM nmem, const char *op);
+
+/** \brief destroys a node and its children. */
+YAZ_EXPORT
+void solr_node_destroy(struct solr_node *cn);
+
+/** duplicates a node (returns a copy of supplied node) . */
+YAZ_EXPORT
+struct solr_node *solr_node_dup (NMEM nmem, struct solr_node *cp);
+
+/** \brief returns the parse tree of the most recently parsed SOLR query.
+    \param cp SOLR parser
+    \returns SOLR node or NULL for failure
+*/
+YAZ_EXPORT
+struct solr_node *solr_parser_result(SOLR_parser cp);
+
+/** \brief converts SOLR tree to XSOLR and writes to user-defined stream
+    \param cn SOLR node (tree)
+    \param pr print function
+    \param client_data data to be passed to pr function
+ */
+YAZ_EXPORT
+void solr_to_xml(struct solr_node *cn,
+                void (*pr)(const char *buf, void *client_data),
+                void *client_data);
+/** \brief converts SOLR tree to XSOLR and writes to file
+    \param cn SOLR node (tree)
+    \param f file handle
+ */
+YAZ_EXPORT
+void solr_to_xml_stdio(struct solr_node *cn, FILE *f);
+
+/** \brief converts SOLR tree to XSOLR and writes result to buffer
+    \param cn SOLR node (tree)
+    \param out buffer
+    \param max size of buffer (max chars to write)
+    \returns length of resulting buffer
+ */
+YAZ_EXPORT
+int solr_to_xml_buf(struct solr_node *cn, char *out, int max);
+
+/** \brief stream handle for file (used by solr_to_xml_stdio) */
+YAZ_EXPORT
+void solr_fputs(const char *buf, void *client_data);
+
+/** \brief SOLR transform handle.
+    The transform describes how to convert from SOLR to PQF (Type-1 AKA RPN).
+*/
+typedef struct solr_transform_t_ *solr_transform_t;
+
+/** \brief creates a SOLR transform handle
+    \returns transform handle or NULL for failure
+*/
+YAZ_EXPORT
+solr_transform_t solr_transform_create(void);
+
+/** \brief creates a SOLR transform handle from am opened file handle
+    \param f file where transformation spec is read
+    \returns transform handle or NULL for failure
+
+    The transformation spec is read from a FILE handle which is assumed
+    opened for reading.
+*/
+YAZ_EXPORT
+solr_transform_t solr_transform_open_FILE (FILE *f);
+
+/** \brief creates a SOLR transform handle from a file
+    \param fname name of where transformation spec is read
+    \returns transform handle or NULL for failure
+*/
+YAZ_EXPORT
+solr_transform_t solr_transform_open_fname(const char *fname);
+
+
+/** \brief defines SOLR transform pattern
+    \param ct SOLR transform handle
+    \param pattern pattern string
+    \param value pattern value
+    \returns 0 for succes; -1 for failure
+*/
+YAZ_EXPORT
+int solr_transform_define_pattern(solr_transform_t ct, const char *pattern,
+                                 const char *value);
+    
+
+
+/** \brief destroys a SOLR transform handle
+    \param ct SOLR transform handle
+ */
+YAZ_EXPORT
+void solr_transform_close(solr_transform_t ct);
+
+/** \brief tranforms PQF given a SOLR tree
+    \param ct SOLR transform handle
+    \param cn SOLR node tree
+    \param pr print function
+    \param client_data data to be passed to pr
+    \retval 0 success
+    \retval != 0 error
+
+    The result is written to a user-defined stream.
+*/
+YAZ_EXPORT
+int solr_transform(solr_transform_t ct,
+                  struct solr_node *cn,
+                  void (*pr)(const char *buf, void *client_data),
+                  void *client_data);
+
+/** \brief transforms PQF given a SOLR tree (from FILE)
+    \param ct SOLR transform handle
+    \param cn SOLR tree
+    \param f FILE where output is written
+    \retval 0 success
+    \retval !=0 failure (error code)
+
+    The result is written to a file specified by FILE handle (which must
+    be opened for writing.
+*/
+YAZ_EXPORT
+int solr_transform_FILE(solr_transform_t ct,
+                       struct solr_node *cn, FILE *f);
+
+/** \brief transforms PQF given a SOLR tree (from FILE)
+    \param ct SOLR transform handle
+    \param cn SOLR tree
+    \param out buffer for output
+    \param max maximum bytes for output (size of buffer)
+    \retval 0 success
+    \retval !=0 failure (error code)
+ */
+YAZ_EXPORT
+int solr_transform_buf(solr_transform_t ct,
+                      struct solr_node *cn, char *out, int max);
+
+/** \brief returns additional information for last transform
+    \param ct SOLR transform handle
+    \param addinfo additional info (result)
+    \returns error code
+ */
+YAZ_EXPORT
+int solr_transform_error(solr_transform_t ct, const char **addinfo);
+
+/** \brief sets error and addinfo for transform
+    \param ct SOLR transform handle
+    \param error error code
+    \param addinfo additional info
+ */
+YAZ_EXPORT
+void solr_transform_set_error(solr_transform_t ct, int error, const char *addinfo);
+
+/** \brief returns the SOLR message corresponding to a given error code.
+    \param code error code
+    \returns text message
+*/
+YAZ_EXPORT
+const char *solr_strerror(int code);
+
+/** \brief returns the standard SOLR context set URI.
+    \returns SOLR URI string
+*/
+YAZ_EXPORT
+const char *solr_uri(void);
+
+/** \brief compares two SOLR strings (ala strcmp)
+    \param s1 string 1
+    \param s2 string 2
+    \returns comparison value
+    Compares two SOLR strings (for relations, operators, etc)
+    (unfortunately defined as case-insensitive unlike XML etc)
+*/
+YAZ_EXPORT
+int solr_strcmp(const char *s1, const char *s2);
+
+/** \brief compares two SOLR strings (ala strncmp)
+    \param s1 string 1
+    \param s2 string 2
+    \param n size
+    \returns comparison value
+    Compares two SOLR strings at most n bytes
+    (unfortunately defined as case-insensitive unlike XML etc)
+ */
+YAZ_EXPORT
+int solr_strncmp(const char *s1, const char *s2, size_t n);
+
+YAZ_END_CDECL
+
+#endif
+/* SOLR_H_INCLUDED */
+/*
+ * Local variables:
+ * c-basic-offset: 4
+ * c-file-style: "Stroustrup"
+ * indent-tabs-mode: nil
+ * End:
+ * vim: shiftwidth=4 tabstop=8 expandtab
+ */
+
index d5e7043..7c2e575 100644 (file)
@@ -266,11 +266,23 @@ YAZ_EXPORT void yaz_decode_uri_component(char *dst, const char *uri,
 
 YAZ_EXPORT int yaz_srw_decode(Z_HTTP_Request *hreq, Z_SRW_PDU **srw_pdu,
                               Z_SOAP **soap_package, ODR decode, char **charset);
+
 YAZ_EXPORT int yaz_sru_decode(Z_HTTP_Request *hreq, Z_SRW_PDU **srw_pdu,
                               Z_SOAP **soap_package, ODR decode, 
                               char **charset,
                               Z_SRW_diagnostic **, int *num_diagnostic);
 
+/** \brief decode SOLR response (HTTP)
+    \param o ODR for result
+    \param hres HTTP response to be decoded
+    \param pdup SRW response pointer (set if successful)
+    \retval -1 fail
+    \retval 0 OK
+*/
+YAZ_EXPORT int yaz_solr_decode_response(ODR o, Z_HTTP_Response *hres,
+                                        Z_SRW_PDU **pdup);
+
+
 YAZ_EXPORT void yaz_add_srw_diagnostic(ODR o, Z_SRW_diagnostic **d,
                                        int *num, int code,
                                        const char *addinfo);
@@ -298,6 +310,17 @@ YAZ_EXPORT int yaz_sru_post_encode(Z_HTTP_Request *hreq, Z_SRW_PDU *srw_pdu,
 YAZ_EXPORT int yaz_sru_soap_encode(Z_HTTP_Request *hreq, Z_SRW_PDU *srw_pdu,
                                    ODR odr, const char *charset);
 
+/** \brief encode SOLR request (HTTP)
+    \param hreq HTTP request to be modified (with encoded request)
+    \param srw_pdu SRW request
+    \param encode ODR for result
+    \param charset character set for request (may be NULL)
+    \retval -1 fail
+    \retval 0 OK
+*/
+YAZ_EXPORT int yaz_solr_encode_request(Z_HTTP_Request *hreq, Z_SRW_PDU *srw_pdu,
+                                       ODR encode, const char *charset);
+
 YAZ_EXPORT char *yaz_negotiate_sru_version(char *input_ver);
 
 YAZ_EXPORT
index 3551a77..ca38b0d 100644 (file)
@@ -293,6 +293,9 @@ ZOOM_query_prefix(ZOOM_query s, const char *str);
 ZOOM_API(int)
 ZOOM_query_sortby(ZOOM_query s, const char *criteria);
 
+ZOOM_API(void)
+ZOOM_query_addref(ZOOM_query s);
+
 /* ----------------------------------------------------------- */
 /* scan */
 ZOOM_API(ZOOM_scanset)
index e79d237..d0060df 100644 (file)
@@ -88,11 +88,14 @@ libyaz_la_SOURCES=version.c options.c log.c \
   zget.c yaz-ccl.c diag-entry.c diag-entry.h \
   logrpn.c \
   otherinfo.c pquery.c sortspec.c charneg.c initopt.c \
-  zoom-c.c zoom-socket.c zoom-opt.c zoom-p.h \
-  grs1disp.c zgdu.c soap.c srw.c srwutil.c uri.c \
+  zoom-c.c zoom-z3950.c zoom-sru.c zoom-query.c zoom-record-cache.c \
+  zoom-event.c \
+  record_render.c zoom-socket.c zoom-opt.c zoom-p.h sru-p.h \
+  grs1disp.c zgdu.c soap.c srw.c srwutil.c uri.c solr.c \
   opacdisp.c cclfind.c ccltoken.c cclerrms.c cclqual.c cclptree.c cclp.h \
   cclqfile.c cclstr.c cclxmlconfig.c ccl_stop_words.c \
   cql.y cqlstdio.c cqltransform.c cqlutil.c xcqlutil.c cqlstring.c rpn2cql.c \
+  rpn2solr.c solrtransform.c \
   cqlstrer.c querytowrbuf.c \
   tcpdchk.c \
   test.c timing.c \
index de777d0..55c3366 100644 (file)
@@ -607,11 +607,11 @@ static int has_modifier(struct cql_node *cn, const char *name) {
 }
 
 
-void emit_term(cql_transform_t ct,
-               struct cql_node *cn,
-               const char *term, int length,
-               void (*pr)(const char *buf, void *client_data),
-               void *client_data)
+static void emit_term(cql_transform_t ct,
+                      struct cql_node *cn,
+                      const char *term, int length,
+                      void (*pr)(const char *buf, void *client_data),
+                      void *client_data)
 {
     int i;
     const char *ns = cn->u.st.index_uri;
@@ -748,11 +748,11 @@ void emit_term(cql_transform_t ct,
     xfree(z3958_mem);
 }
 
-void emit_terms(cql_transform_t ct,
-                struct cql_node *cn,
-                void (*pr)(const char *buf, void *client_data),
-                void *client_data,
-                const char *op)
+static void emit_terms(cql_transform_t ct,
+                       struct cql_node *cn,
+                       void (*pr)(const char *buf, void *client_data),
+                       void *client_data,
+                       const char *op)
 {
     struct cql_node *ne = cn->u.st.extra_terms;
     if (ne)
@@ -776,11 +776,11 @@ void emit_terms(cql_transform_t ct,
     }
 }
 
-void emit_wordlist(cql_transform_t ct,
-                   struct cql_node *cn,
-                   void (*pr)(const char *buf, void *client_data),
-                   void *client_data,
-                   const char *op)
+static void emit_wordlist(cql_transform_t ct,
+                          struct cql_node *cn,
+                          void (*pr)(const char *buf, void *client_data),
+                          void *client_data,
+                          const char *op)
 {
     const char *cp0 = cn->u.st.term;
     const char *cp1;
diff --git a/src/record_render.c b/src/record_render.c
new file mode 100644 (file)
index 0000000..d4180dd
--- /dev/null
@@ -0,0 +1,319 @@
+/* This file is part of the YAZ toolkit.
+ * Copyright (C) 1995-2010 Index Data
+ * See the file LICENSE for details.
+ */
+/**
+ * \file record_render.c
+ * \brief Render Z39.50 records (NamePlusRecord)
+ */
+
+#include <assert.h>
+#include <string.h>
+#include <errno.h>
+
+#include <yaz/marcdisp.h>
+#include <yaz/record_render.h>
+#include <yaz/yaz-iconv.h>
+#include <yaz/proto.h>
+#include <yaz/oid_db.h>
+
+static yaz_iconv_t iconv_create_charset(const char *record_charset)
+{
+    char to[40];
+    char from[40];
+    yaz_iconv_t cd = 0;
+
+    *from = '\0';
+    strcpy(to, "UTF-8");
+    if (record_charset && *record_charset)
+    {
+        /* Use "from,to" or just "from" */
+        const char *cp = strchr(record_charset, ',');
+        size_t clen = strlen(record_charset);
+        if (cp && cp[1])
+        {
+            strncpy( to, cp+1, sizeof(to)-1);
+            to[sizeof(to)-1] = '\0';
+            clen = cp - record_charset;
+        }
+        if (clen > sizeof(from)-1)
+            clen = sizeof(from)-1;
+        
+        if (clen)
+            strncpy(from, record_charset, clen);
+        from[clen] = '\0';
+    }
+    if (*from && *to)
+        cd = yaz_iconv_open(to, from);
+    return cd;
+}
+
+static const char *return_marc_record(WRBUF wrbuf,
+                                      int marc_type,
+                                      int *len,
+                                      const char *buf, int sz,
+                                      const char *record_charset)
+{
+    yaz_iconv_t cd = iconv_create_charset(record_charset);
+    yaz_marc_t mt = yaz_marc_create();
+    const char *ret_string = 0;
+
+    if (cd)
+        yaz_marc_iconv(mt, cd);
+    yaz_marc_xml(mt, marc_type);
+    if (yaz_marc_decode_wrbuf(mt, buf, sz, wrbuf) > 0)
+    {
+        if (len)
+            *len = wrbuf_len(wrbuf);
+        ret_string = wrbuf_cstr(wrbuf);
+    }
+    yaz_marc_destroy(mt);
+    if (cd)
+        yaz_iconv_close(cd);
+    return ret_string;
+}
+
+static const char *return_opac_record(WRBUF wrbuf,
+                                      int marc_type,
+                                      int *len,
+                                      Z_OPACRecord *opac_rec,
+                                      const char *record_charset)
+{
+    yaz_iconv_t cd = iconv_create_charset(record_charset);
+    yaz_marc_t mt = yaz_marc_create();
+
+    if (cd)
+        yaz_marc_iconv(mt, cd);
+    yaz_marc_xml(mt, marc_type);
+
+    yaz_opac_decode_wrbuf(mt, opac_rec, wrbuf);
+    yaz_marc_destroy(mt);
+
+    if (cd)
+        yaz_iconv_close(cd);
+    if (len)
+        *len = wrbuf_len(wrbuf);
+    return wrbuf_cstr(wrbuf);
+}
+
+static const char *return_string_record(WRBUF wrbuf,
+                                        int *len,
+                                        const char *buf, int sz,
+                                        const char *record_charset)
+{
+    yaz_iconv_t cd = iconv_create_charset(record_charset);
+
+    if (cd)
+    {
+        wrbuf_iconv_write(wrbuf, cd, buf, sz);
+        wrbuf_iconv_reset(wrbuf, cd);
+
+        buf = wrbuf_cstr(wrbuf);
+        sz = wrbuf_len(wrbuf);
+        yaz_iconv_close(cd);
+    }
+    if (len)
+        *len = sz;
+    return buf;
+}
+
+static const char *return_record_wrbuf(WRBUF wrbuf, int *len,
+                                       Z_NamePlusRecord *npr,
+                                       int marctype, const char *charset)
+{
+    Z_External *r = (Z_External *) npr->u.databaseRecord;
+    const Odr_oid *oid = r->direct_reference;
+
+    wrbuf_rewind(wrbuf);
+    /* render bibliographic record .. */
+    if (r->which == Z_External_OPAC)
+    {
+        return return_opac_record(wrbuf, marctype, len,
+                                  r->u.opac, charset);
+    }
+    if (r->which == Z_External_sutrs)
+        return return_string_record(wrbuf, len,
+                                    (char*) r->u.sutrs->buf,
+                                    r->u.sutrs->len,
+                                    charset);
+    else if (r->which == Z_External_octet)
+    {
+        if (yaz_oid_is_iso2709(oid))
+        {
+            const char *ret_buf = return_marc_record(
+                wrbuf, marctype, len,
+                (const char *) r->u.octet_aligned->buf,
+                r->u.octet_aligned->len,
+                charset);
+            if (ret_buf)
+                return ret_buf;
+            /* bad ISO2709. Return fail unless raw (ISO2709) is wanted */
+            if (marctype != YAZ_MARC_ISO2709)
+                return 0;
+        }
+        return return_string_record(wrbuf, len,
+                                    (const char *) r->u.octet_aligned->buf,
+                                    r->u.octet_aligned->len,
+                                    charset);
+    }
+    else if (r->which == Z_External_grs1)
+    {
+        yaz_display_grs1(wrbuf, r->u.grs1, 0);
+        return return_string_record(wrbuf, len,
+                                    wrbuf_buf(wrbuf),
+                                    wrbuf_len(wrbuf),
+                                    charset);
+    }
+    return 0;
+}
+    
+static const char *get_record_format(WRBUF wrbuf, int *len,
+                                     Z_NamePlusRecord *npr,
+                                     int marctype, const char *charset,
+                                     const char *format)
+{
+    const char *res = return_record_wrbuf(wrbuf, len, npr, marctype, charset);
+#if YAZ_HAVE_XML2
+    if (*format == '1' && len)
+    {
+        /* try to XML format res */
+        xmlDocPtr doc;
+        xmlKeepBlanksDefault(0); /* get get xmlDocFormatMemory to work! */
+        doc = xmlParseMemory(res, *len);
+        if (doc)
+        {
+            xmlChar *xml_mem;
+            int xml_size;
+            xmlDocDumpFormatMemory(doc, &xml_mem, &xml_size, 1);
+            wrbuf_rewind(wrbuf);
+            wrbuf_write(wrbuf, (const char *) xml_mem, xml_size);
+            xmlFree(xml_mem);
+            xmlFreeDoc(doc);
+            res = wrbuf_cstr(wrbuf);
+            *len = wrbuf_len(wrbuf);
+        } 
+    }
+#endif
+    return res;
+}
+
+const char *yaz_record_render(Z_NamePlusRecord *npr, const char *schema,
+                              WRBUF wrbuf,
+                              const char *type_spec, int *len)
+{
+    size_t i;
+    char type[40];
+    char charset[40];
+    char format[3];
+    const char *cp = type_spec;
+
+    for (i = 0; cp[i] && cp[i] != ';' && cp[i] != ' ' && i < sizeof(type)-1;
+         i++)
+        type[i] = cp[i];
+    type[i] = '\0';
+    charset[0] = '\0';
+    format[0] = '\0';
+    while (1)
+    {
+        while (cp[i] == ' ')
+            i++;
+        if (cp[i] != ';')
+            break;
+        i++;
+        while (cp[i] == ' ')
+            i++;
+        if (!strncmp(cp + i, "charset=", 8))
+        {
+            size_t j = 0;
+            i = i + 8; /* skip charset= */
+            for (j = 0; cp[i] && cp[i] != ';' && cp[i] != ' '; i++)
+            {
+                if (j < sizeof(charset)-1)
+                    charset[j++] = cp[i];
+            }
+            charset[j] = '\0';
+        }
+        else if (!strncmp(cp + i, "format=", 7))
+        {
+            size_t j = 0; 
+            i = i + 7;
+            for (j = 0; cp[i] && cp[i] != ';' && cp[i] != ' '; i++)
+            {
+                if (j < sizeof(format)-1)
+                    format[j++] = cp[i];
+            }
+            format[j] = '\0';
+        } 
+    }
+    if (!strcmp(type, "database"))
+    {
+        if (len)
+            *len = (npr->databaseName ? strlen(npr->databaseName) : 0);
+        return npr->databaseName;
+    }
+    else if (!strcmp(type, "schema"))
+    {
+        if (len)
+            *len = schema ? strlen(schema) : 0;
+        return schema;
+    }
+    else if (!strcmp(type, "syntax"))
+    {
+        const char *desc = 0;   
+        if (npr->which == Z_NamePlusRecord_databaseRecord)
+        {
+            Z_External *r = (Z_External *) npr->u.databaseRecord;
+            desc = yaz_oid_to_string(yaz_oid_std(), r->direct_reference, 0);
+        }
+        if (!desc)
+            desc = "none";
+        if (len)
+            *len = strlen(desc);
+        return desc;
+    }
+    if (npr->which != Z_NamePlusRecord_databaseRecord)
+        return 0;
+
+    /* from now on - we have a database record .. */
+    if (!strcmp(type, "render"))
+    {
+        return get_record_format(wrbuf, len, npr, YAZ_MARC_LINE, charset, format);
+    }
+    else if (!strcmp(type, "xml"))
+    {
+        return get_record_format(wrbuf, len, npr, YAZ_MARC_MARCXML, charset,
+                                 format);
+    }
+    else if (!strcmp(type, "txml"))
+    {
+        return get_record_format(wrbuf, len, npr, YAZ_MARC_TURBOMARC, charset,
+                                 format);
+    }
+    else if (!strcmp(type, "raw"))
+    {
+        return get_record_format(wrbuf, len, npr, YAZ_MARC_ISO2709, charset,
+            format);
+    }
+    else if (!strcmp(type, "ext"))
+    {
+        if (len) *len = -1;
+        return (const char *) npr->u.databaseRecord;
+    }
+    else if (!strcmp(type, "opac"))
+    {
+        if (npr->u.databaseRecord->which == Z_External_OPAC)
+            return get_record_format(wrbuf, len, npr, YAZ_MARC_MARCXML, charset,
+                                     format);
+    }
+    return 0;
+}
+
+/*
+ * Local variables:
+ * c-basic-offset: 4
+ * c-file-style: "Stroustrup"
+ * indent-tabs-mode: nil
+ * End:
+ * vim: shiftwidth=4 tabstop=8 expandtab
+ */
+
diff --git a/src/rpn2solr.c b/src/rpn2solr.c
new file mode 100644 (file)
index 0000000..de485ab
--- /dev/null
@@ -0,0 +1,336 @@
+/* This file is part of the YAZ toolkit.
+ * Copyright (C) 1995-2010 Index Data
+ * See the file LICENSE for details.
+ */
+
+/**
+ * \file
+ * \brief Implements RPN to SOLR conversion
+ *
+ */
+
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include <yaz/rpn2solr.h>
+#include <yaz/xmalloc.h>
+#include <yaz/diagbib1.h>
+#include <yaz/z-core.h>
+#include <yaz/wrbuf.h>
+
+static void wrbuf_vputs(const char *buf, void *client_data)
+{
+    wrbuf_write((WRBUF) client_data, buf, strlen(buf));
+}
+
+static const char *lookup_index_from_string_attr(Z_AttributeList *attributes)
+{
+    int j;
+    int server_choice = 1;
+    for (j = 0; j < attributes->num_attributes; j++)
+    {
+        Z_AttributeElement *ae = attributes->attributes[j];
+        if (*ae->attributeType == 1) /* use attribute */
+        {
+            if (ae->which == Z_AttributeValue_complex)
+            {
+                Z_ComplexAttribute *ca = ae->value.complex;
+                int i;
+                for (i = 0; i < ca->num_list; i++)
+                {
+                    Z_StringOrNumeric *son = ca->list[i];
+                    if (son->which == Z_StringOrNumeric_string)
+                        return son->u.string;
+                }
+            }
+            server_choice = 0; /* not serverChoice because we have use attr */
+        }
+    }
+    if (server_choice)
+        return "solr.serverChoice";
+    return 0;
+}
+
+static const char *lookup_relation_index_from_attr(Z_AttributeList *attributes)
+{
+    int j;
+    for (j = 0; j < attributes->num_attributes; j++)
+    {
+        Z_AttributeElement *ae = attributes->attributes[j];
+        if (*ae->attributeType == 2) /* relation attribute */
+        {
+            if (ae->which == Z_AttributeValue_numeric)
+            {
+                /* Only support for numeric relation */
+                Odr_int *relation = ae->value.numeric;
+                /* map this numeric to representation in SOLR */
+                switch (*relation)
+                {
+                    /* Unsure on whether this is the relation attribute constants? */
+                case Z_ProximityOperator_Prox_lessThan: 
+                    return 0;
+                case Z_ProximityOperator_Prox_lessThanOrEqual: 
+                    return 0;
+                case Z_ProximityOperator_Prox_equal: 
+                    return ":";
+                case Z_ProximityOperator_Prox_greaterThanOrEqual: 
+                    return 0;
+                case Z_ProximityOperator_Prox_greaterThan: 
+                    return 0;
+                case Z_ProximityOperator_Prox_notEqual: 
+                    return 0;
+                case 100: 
+                    /* phonetic is not implemented*/
+                    return 0; 
+                case 101: 
+                    /* stem is not not implemented */
+                    return 0; 
+                case 102: 
+                    /* relevance is supported in SOLR, but not implemented yet */
+                    return 0; 
+                default:
+                    /* Invalid relation */
+                    return 0;
+                }
+            }
+            else {
+                /*  Can we have a complex relation value?
+                    Should we implement something?
+                */
+            }
+        }
+    }
+    return ":";
+}
+
+static int rpn2solr_attr(solr_transform_t ct,
+                        Z_AttributeList *attributes, WRBUF w)
+{
+    const char *relation = solr_lookup_reverse(ct, "relation.", attributes);
+    const char *index = solr_lookup_reverse(ct, "index.", attributes);
+    const char *structure = solr_lookup_reverse(ct, "structure.", attributes);
+
+    /* if transform (properties) do not match, we'll just use a USE string attribute (bug #2978) */
+    if (!index)
+        index = lookup_index_from_string_attr(attributes);
+
+    /* Attempt to fix bug #2978: Look for a relation attribute */
+    if (!relation) 
+        relation = lookup_relation_index_from_attr(attributes);
+
+    if (!index)
+    {
+        solr_transform_set_error(ct,
+                                YAZ_BIB1_UNSUPP_USE_ATTRIBUTE, 0);
+        return -1;
+    }
+    /* for serverChoice we omit index+relation+structure */
+    if (strcmp(index, "solr.serverChoice"))
+    {
+        wrbuf_puts(w, index);
+        if (relation)
+        {
+            if (!strcmp(relation, "exact"))
+                /* TODO Verify if a exact  SOLR exists */
+                relation = ":";
+            else if (!strcmp(relation, "eq"))
+                relation = ":";
+            else if (!strcmp(relation, "le")) {
+                /* TODO Not support as such, but could perhaps be transformed into a range
+                relation = ":[ * to ";
+                close_range = "]"
+                */
+            }
+            else if (!strcmp(relation, "ge")) {
+                /* TODO Not support as such, but could perhaps be transformed into a range
+                relation = "[";
+                relation = ":[ * to ";
+                close_range = "]"
+                */
+            }
+            /* Missing mapping of not equal, phonetic, stem and relevance */
+            wrbuf_puts(w, relation);
+        }
+        else
+            wrbuf_puts(w, ":");
+
+        if (structure)
+        {
+            if (strcmp(structure, "*"))
+            {
+                wrbuf_puts(w, "/");
+                wrbuf_puts(w, structure);
+                wrbuf_puts(w, " ");
+            }
+        }
+    }
+    return 0;
+}
+
+/* Bug 2878: Currently only support left and right truncation. Specific check for this */
+static int checkForTruncation(int flag, Z_AttributeList *attributes)
+{
+    int j;
+    for (j = 0; j < attributes->num_attributes; j++)
+    {
+        Z_AttributeElement *ae = attributes->attributes[j];
+        if (*ae->attributeType == 5) /* truncation attribute */
+        {
+            if (ae->which == Z_AttributeValue_numeric)
+            {
+                Odr_int truncation = *(ae->value.numeric);
+                /* This logic only works for Left, right and both. eg. 1,2,3 */
+               if (truncation <= 3)
+                    return ((int) truncation & flag);
+            }
+            /* Complex: Shouldn't happen */
+        }
+    }
+    /* No truncation or unsupported */
+    return 0;
+};
+
+static int checkForLeftTruncation(Z_AttributeList *attributes) {
+       return checkForTruncation(1, attributes);
+}
+
+static int checkForRightTruncation(Z_AttributeList *attributes) {
+       return checkForTruncation(2, attributes);
+};
+
+static int rpn2solr_simple(solr_transform_t ct,
+                          void (*pr)(const char *buf, void *client_data),
+                          void *client_data,
+                          Z_Operand *q, WRBUF w)
+{
+    int ret = 0;
+    if (q->which != Z_Operand_APT)
+    {
+        ret = -1;
+        solr_transform_set_error(ct, YAZ_BIB1_RESULT_SET_UNSUPP_AS_A_SEARCH_TERM, 0);
+    }
+    else
+    {
+        Z_AttributesPlusTerm *apt = q->u.attributesPlusTerm;
+        Z_Term *term = apt->term;
+        const char *sterm = 0;
+        size_t lterm = 0;
+
+        wrbuf_rewind(w);
+        ret = rpn2solr_attr(ct, apt->attributes, w);
+
+        switch(term->which)
+        {
+        case Z_Term_general:
+            lterm = term->u.general->len;
+            sterm = (const char *) term->u.general->buf;
+            break;
+        case Z_Term_numeric:
+            wrbuf_printf(w, ODR_INT_PRINTF, *term->u.numeric);
+            break;
+        case Z_Term_characterString:
+            sterm = term->u.characterString;
+            lterm = strlen(sterm);
+            break;
+        default:
+            ret = -1;
+            solr_transform_set_error(ct, YAZ_BIB1_TERM_TYPE_UNSUPP, 0);
+        }
+
+        if (term)
+        {
+            size_t i;
+            int must_quote = 0;
+            for (i = 0 ; i < lterm; i++)
+                if (sterm[i] == ' ')
+                    must_quote = 1;
+            if (must_quote)
+                wrbuf_puts(w, "\"");
+            /* Bug 2878: Check and add Truncation */
+                       if (checkForLeftTruncation(apt->attributes))
+                wrbuf_puts(w, "*");
+            wrbuf_write(w, sterm, lterm);
+            /* Bug 2878: Check and add Truncation */
+                       if (checkForRightTruncation(apt->attributes))
+                wrbuf_puts(w, "*");
+            if (must_quote)
+                wrbuf_puts(w, "\"");
+        }
+        if (ret == 0)
+            pr(wrbuf_cstr(w), client_data);
+    }
+    return ret;
+}
+
+
+static int rpn2solr_structure(solr_transform_t ct,
+                             void (*pr)(const char *buf, void *client_data),
+                             void *client_data,
+                             Z_RPNStructure *q, int nested,
+                             WRBUF w)
+{
+    if (q->which == Z_RPNStructure_simple)
+        return rpn2solr_simple(ct, pr, client_data, q->u.simple, w);
+    else
+    {
+        Z_Operator *op = q->u.complex->roperator;
+        int r;
+
+        if (nested)
+            pr("(", client_data);
+
+        r = rpn2solr_structure(ct, pr, client_data, q->u.complex->s1, 1, w);
+        if (r)
+            return r;
+        switch(op->which)
+        {
+        case  Z_Operator_and:
+            pr(" AND ", client_data);
+            break;
+        case  Z_Operator_or:
+            pr(" OR ", client_data);
+            break;
+        case  Z_Operator_and_not:
+            pr(" AND NOT ", client_data);
+            break;
+        case  Z_Operator_prox:
+            solr_transform_set_error(ct, YAZ_BIB1_UNSUPP_SEARCH, 0);
+            return -1;
+        }
+        r = rpn2solr_structure(ct, pr, client_data, q->u.complex->s2, 1, w);
+        if (nested)
+            pr(")", client_data);
+        return r;
+    }
+}
+
+int solr_transform_rpn2solr_stream(solr_transform_t ct,
+                                 void (*pr)(const char *buf, void *client_data),
+                                 void *client_data,
+                                 Z_RPNQuery *q)
+{
+    int r;
+    WRBUF w = wrbuf_alloc();
+    solr_transform_set_error(ct, 0, 0);
+    r = rpn2solr_structure(ct, pr, client_data, q->RPNStructure, 0, w);
+    wrbuf_destroy(w);
+    return r;
+}
+
+
+int solr_transform_rpn2solr_wrbuf(solr_transform_t ct,
+                                WRBUF w,
+                                Z_RPNQuery *q)
+{
+    return solr_transform_rpn2solr_stream(ct, wrbuf_vputs, w, q);
+}
+
+/*
+ * Local variables:
+ * c-basic-offset: 4
+ * c-file-style: "Stroustrup"
+ * indent-tabs-mode: nil
+ * End:
+ * vim: shiftwidth=4 tabstop=8 expandtab
+ */
+
diff --git a/src/solr.c b/src/solr.c
new file mode 100644 (file)
index 0000000..eaf822c
--- /dev/null
@@ -0,0 +1,205 @@
+/* This file is part of the YAZ toolkit.
+ * Copyright (C) 1995-2010 Index Data
+ * See the file LICENSE for details.
+ */
+/**
+ * \file srwutil.c
+ * \brief Implements SRW/SRU utilities.
+ */
+
+#include <stdlib.h>
+#include <assert.h>
+#include <yaz/srw.h>
+#include <yaz/matchstr.h>
+#include <yaz/yaz-iconv.h>
+
+#include "sru-p.h"
+
+#if YAZ_HAVE_XML2
+#include <libxml/parser.h>
+#include <libxml/tree.h>
+#endif
+
+int yaz_solr_decode_response(ODR o, Z_HTTP_Response *hres, Z_SRW_PDU **pdup)
+{
+#if YAZ_HAVE_XML2
+    const char *content_buf = hres->content_buf;
+    int content_len = hres->content_len;
+    xmlDocPtr doc = xmlParseMemory(content_buf, content_len);
+    int ret = 0;
+    xmlNodePtr ptr = 0;
+    Odr_int start = 0;
+    Z_SRW_PDU *pdu = yaz_srw_get(o, Z_SRW_searchRetrieve_response);
+    Z_SRW_searchRetrieveResponse *sr = pdu->u.response;
+
+    if (!doc)
+    {
+        ret = -1;
+    }
+    if (doc)
+    {
+        xmlNodePtr root = xmlDocGetRootElement(doc);
+        if (!root)
+        {
+            ret = -1;
+        }
+        else if (strcmp((const char *) root->name, "response"))
+        {
+            ret = -1;
+        }
+        else
+        {
+            /** look for result node */
+            for (ptr = root->children; ptr; ptr = ptr->next)
+            {
+                if (ptr->type == XML_ELEMENT_NODE &&
+                    !strcmp((const char *) ptr->name, "result"))
+                    break;
+            }
+            if (!ptr)
+            {
+                ret = -1;
+            }
+        }
+    }
+    if (ptr)
+    {   /* got result node */
+        struct _xmlAttr *attr;
+
+        for (attr = ptr->properties; attr; attr = attr->next)
+            if (attr->children && attr->children->type == XML_TEXT_NODE)
+            {
+                if (!strcmp((const char *) attr->name, "numFound"))
+                {
+                    sr->numberOfRecords =
+                        odr_intdup(o, 
+                                   odr_atoi(
+                                       (const char *) attr->children->content));
+                }
+                else if (!strcmp((const char *) attr->name, "start"))
+                {
+                    start = odr_atoi((const char *) attr->children->content);
+                }
+            }
+    }
+    if (ptr)
+    {
+        xmlNodePtr node;
+        int offset = 0;
+        int i = 0;
+
+        sr->num_records = 0;
+        for (node = ptr->children; node; node = node->next)
+            if (node->type == XML_ELEMENT_NODE)
+                sr->num_records++;
+
+        sr->records = odr_malloc(o, sizeof(*sr->records) * sr->num_records);
+
+        for (node = ptr->children; node; node = node->next)
+        {
+            if (node->type == XML_ELEMENT_NODE)
+            {
+                Z_SRW_record *record = sr->records + i;
+                xmlBufferPtr buf = xmlBufferCreate();
+                xmlNode *tmp = xmlCopyNode(node, 1);
+
+                xmlNodeDump(buf, tmp->doc, tmp, 0, 0);
+
+                xmlFreeNode(tmp);
+
+                record->recordSchema = 0;
+                record->recordPacking = Z_SRW_recordPacking_XML;
+                record->recordData_len = buf->use;
+                record->recordData_buf = odr_malloc(o, buf->use + 1);
+                memcpy(record->recordData_buf, buf->content, buf->use);
+                record->recordData_buf[buf->use] = '\0';
+                record->recordPosition = odr_intdup(o, start + offset);
+
+                xmlBufferFree(buf);
+
+                offset++;
+                i++;
+            }
+        }
+    }
+    if (doc)
+        xmlFreeDoc(doc);
+    if (ret == 0)
+        *pdup = pdu;
+    return ret;
+#else
+    return -1;
+#endif
+}
+
+int yaz_solr_encode_request(Z_HTTP_Request *hreq, Z_SRW_PDU *srw_pdu,
+                            ODR encode, const char *charset)
+{
+    const char *solr_op = 0;
+    char *name[30], *value[30];
+    char *uri_args;
+    char *path;
+    int i = 0;
+
+    z_HTTP_header_add_basic_auth(encode, &hreq->headers, 
+                                 srw_pdu->username, srw_pdu->password);
+
+    switch (srw_pdu->which)
+    {
+    case Z_SRW_searchRetrieve_request:
+        solr_op = "select";
+        
+        switch(srw_pdu->u.request->query_type)
+        {
+        case Z_SRW_query_type_pqf:
+            yaz_add_name_value_str(encode, name, value, &i,
+                                   "q", srw_pdu->u.request->query.pqf);
+            break;
+        case Z_SRW_query_type_cql:
+            yaz_add_name_value_str(encode, name, value, &i,
+                                   "q", srw_pdu->u.request->query.cql);
+            break;
+        default:
+            return -1;
+        }
+        if (srw_pdu->u.request->startRecord)
+        {
+            Odr_int start = *srw_pdu->u.request->startRecord - 1;
+            yaz_add_name_value_int(encode, name, value, &i,
+                                   "start", &start);
+        }
+        yaz_add_name_value_int(encode, name, value, &i,
+                               "rows", srw_pdu->u.request->maximumRecords);
+        yaz_add_name_value_str(encode, name, value, &i,
+                               "fl", srw_pdu->u.request->recordSchema);
+        break;
+    default:
+        return -1;
+    }
+    name[i] = 0;
+    yaz_array_to_uri(&uri_args, encode, name, value);
+    
+    hreq->method = "GET";
+    
+    path = (char *)
+        odr_malloc(encode, strlen(hreq->path) +
+                   strlen(uri_args) + strlen(solr_op) + 4);
+
+    sprintf(path, "%s/%s?%s", hreq->path, solr_op, uri_args);
+    hreq->path = path;
+
+    z_HTTP_header_add_content_type(encode, &hreq->headers,
+                                   "text/xml", charset);
+    return 0;
+}
+
+
+/*
+ * Local variables:
+ * c-basic-offset: 4
+ * c-file-style: "Stroustrup"
+ * indent-tabs-mode: nil
+ * End:
+ * vim: shiftwidth=4 tabstop=8 expandtab
+ */
+
diff --git a/src/solrtransform.c b/src/solrtransform.c
new file mode 100644 (file)
index 0000000..2d41044
--- /dev/null
@@ -0,0 +1,995 @@
+/* This file is part of the YAZ toolkit.
+ * Copyright (C) 1995-2010 Index Data
+ * See the file LICENSE for details.
+ */
+
+/**
+ * \file solrtransform.c
+ * \brief Implements SOLR transform (SOLR to RPN conversion).
+ *
+ * Evaluation order of rules:
+ *
+ * always
+ * relation
+ * structure
+ * position
+ * truncation
+ * index
+ * relationModifier
+ */
+
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <yaz/rpn2solr.h>
+#include <yaz/xmalloc.h>
+#include <yaz/diagsrw.h>
+#include <yaz/tokenizer.h>
+#include <yaz/wrbuf.h>
+#include <yaz/z-core.h>
+#include <yaz/matchstr.h>
+#include <yaz/oid_db.h>
+#include <yaz/log.h>
+#include <yaz/cql.h>
+
+struct solr_prop_entry {
+    char *pattern;
+    char *value;
+    Z_AttributeList attr_list;
+    struct solr_prop_entry *next;
+};
+
+struct solr_transform_t_ {
+    struct solr_prop_entry *entry;
+    yaz_tok_cfg_t tok_cfg;
+    int error;
+    char *addinfo;
+    WRBUF w;
+    NMEM nmem;
+};
+
+
+/* TODO Utility functions, evt. split out int separate file */
+int solr_strcmp(const char *s1, const char *s2) {
+    return cql_strcmp(s1, s2);
+}
+
+int solr_strncmp(const char *s1, const char *s2, size_t n) {
+    return cql_strncmp(s1, s2, n);
+}
+
+/* TODO FIX */
+const char *solr_uri(void)
+{
+    return "TODO:SOLR URI";
+}
+
+void solr_buf_write_handler (const char *b, void *client_data)
+{
+    struct solr_buf_write_info *info = (struct solr_buf_write_info *)client_data;
+    int l = strlen(b);
+    if (info->off < 0 || (info->off + l >= info->max))
+    {
+        info->off = -1;
+        return;
+    }
+    memcpy (info->buf + info->off, b, l);
+    info->off += l;
+}
+
+
+/* Utillity functions end */
+
+solr_transform_t solr_transform_create(void)
+{
+    solr_transform_t ct = (solr_transform_t) xmalloc(sizeof(*ct));
+    ct->tok_cfg = yaz_tok_cfg_create();
+    ct->w = wrbuf_alloc();
+    ct->error = 0;
+    ct->addinfo = 0;
+    ct->entry = 0;
+    ct->nmem = nmem_create();
+    return ct;
+}
+
+static int solr_transform_parse_tok_line(solr_transform_t ct,
+                                        const char *pattern,
+                                        yaz_tok_parse_t tp)
+{
+    int ae_num = 0;
+    Z_AttributeElement *ae[20];
+    int ret = 0; /* 0=OK, != 0 FAIL */
+    int t;
+    t = yaz_tok_move(tp);
+    
+    while (t == YAZ_TOK_STRING && ae_num < 20)
+    {
+        WRBUF type_str = wrbuf_alloc();
+        WRBUF set_str = 0;
+        Z_AttributeElement *elem = 0;
+        const char *value_str = 0;
+        /* attset type=value  OR  type=value */
+        
+        elem = (Z_AttributeElement *) nmem_malloc(ct->nmem, sizeof(*elem));
+        elem->attributeSet = 0;
+        ae[ae_num] = elem;
+        wrbuf_puts(ct->w, yaz_tok_parse_string(tp));
+        wrbuf_puts(type_str, yaz_tok_parse_string(tp));
+        t = yaz_tok_move(tp);
+        if (t == YAZ_TOK_EOF)
+        {
+            wrbuf_destroy(type_str);
+            if (set_str)
+                wrbuf_destroy(set_str);                
+            break;
+        }
+        if (t == YAZ_TOK_STRING)  
+        {  
+            wrbuf_puts(ct->w, " ");
+            wrbuf_puts(ct->w, yaz_tok_parse_string(tp));
+            set_str = type_str;
+            
+            elem->attributeSet =
+                yaz_string_to_oid_nmem(yaz_oid_std(), CLASS_ATTSET,
+                                       wrbuf_cstr(set_str), ct->nmem);
+            
+            type_str = wrbuf_alloc();
+            wrbuf_puts(type_str, yaz_tok_parse_string(tp));
+            t = yaz_tok_move(tp);
+        }
+        elem->attributeType = nmem_intdup(ct->nmem, 0);
+        if (sscanf(wrbuf_cstr(type_str), ODR_INT_PRINTF, elem->attributeType)
+            != 1)
+        {
+            wrbuf_destroy(type_str);
+            if (set_str)
+                wrbuf_destroy(set_str);                
+            yaz_log(YLOG_WARN, "Expected numeric attribute type");
+            ret = -1;
+            break;
+        }
+
+        wrbuf_destroy(type_str);
+        if (set_str)
+            wrbuf_destroy(set_str);                
+        
+        if (t != '=')
+        {
+            yaz_log(YLOG_WARN, "Expected = after after attribute type");
+            ret = -1;
+            break;
+        }
+        t = yaz_tok_move(tp);
+        if (t != YAZ_TOK_STRING) /* value */
+        {
+            yaz_log(YLOG_WARN, "Missing attribute value");
+            ret = -1;
+            break;
+        }
+        value_str = yaz_tok_parse_string(tp);
+        if (isdigit(*value_str))
+        {
+            elem->which = Z_AttributeValue_numeric;
+            elem->value.numeric =
+                nmem_intdup(ct->nmem, atoi(value_str));
+        }
+        else
+        {
+            Z_ComplexAttribute *ca = (Z_ComplexAttribute *)
+                nmem_malloc(ct->nmem, sizeof(*ca));
+            elem->which = Z_AttributeValue_complex;
+            elem->value.complex = ca;
+            ca->num_list = 1;
+            ca->list = (Z_StringOrNumeric **)
+                nmem_malloc(ct->nmem, sizeof(Z_StringOrNumeric *));
+            ca->list[0] = (Z_StringOrNumeric *)
+                nmem_malloc(ct->nmem, sizeof(Z_StringOrNumeric));
+            ca->list[0]->which = Z_StringOrNumeric_string;
+            ca->list[0]->u.string = nmem_strdup(ct->nmem, value_str);
+            ca->num_semanticAction = 0;
+            ca->semanticAction = 0;
+        }
+        wrbuf_puts(ct->w, "=");
+        wrbuf_puts(ct->w, yaz_tok_parse_string(tp));
+        t = yaz_tok_move(tp);
+        wrbuf_puts(ct->w, " ");
+        ae_num++;
+    }
+    if (ret == 0) /* OK? */
+    {
+        struct solr_prop_entry **pp = &ct->entry;
+        while (*pp)
+            pp = &(*pp)->next;
+        *pp = (struct solr_prop_entry *) xmalloc(sizeof(**pp));
+        (*pp)->pattern = xstrdup(pattern);
+        (*pp)->value = xstrdup(wrbuf_cstr(ct->w));
+
+        (*pp)->attr_list.num_attributes = ae_num;
+        if (ae_num == 0)
+            (*pp)->attr_list.attributes = 0;
+        else
+        {
+            (*pp)->attr_list.attributes = (Z_AttributeElement **)
+                nmem_malloc(ct->nmem,
+                            ae_num * sizeof(Z_AttributeElement *));
+            memcpy((*pp)->attr_list.attributes, ae, 
+                   ae_num * sizeof(Z_AttributeElement *));
+        }
+        (*pp)->next = 0;
+
+        if (0)
+        {
+            ODR pr = odr_createmem(ODR_PRINT);
+            Z_AttributeList *alp = &(*pp)->attr_list;
+            odr_setprint(pr, yaz_log_file());
+            z_AttributeList(pr, &alp, 0, 0);
+            odr_setprint(pr, 0);
+            odr_destroy(pr);
+        }
+    }
+    return ret;
+}
+
+int solr_transform_define_pattern(solr_transform_t ct, const char *pattern,
+                                 const char *value)
+{
+    int r;
+    yaz_tok_parse_t tp = yaz_tok_parse_buf(ct->tok_cfg, value);
+    yaz_tok_cfg_single_tokens(ct->tok_cfg, "=");
+    r = solr_transform_parse_tok_line(ct, pattern, tp);
+    yaz_tok_parse_destroy(tp);
+    return r;
+}
+    
+solr_transform_t solr_transform_open_FILE(FILE *f)
+{
+    solr_transform_t ct = solr_transform_create();
+    char line[1024];
+
+    yaz_tok_cfg_single_tokens(ct->tok_cfg, "=");
+
+    while (fgets(line, sizeof(line)-1, f))
+    {
+        yaz_tok_parse_t tp = yaz_tok_parse_buf(ct->tok_cfg, line);
+        int t;
+        wrbuf_rewind(ct->w);
+        t = yaz_tok_move(tp);
+        if (t == YAZ_TOK_STRING)
+        {
+            char * pattern = xstrdup(yaz_tok_parse_string(tp));
+            t = yaz_tok_move(tp);
+            if (t != '=')
+            {
+                yaz_tok_parse_destroy(tp);
+                solr_transform_close(ct);
+                return 0;
+            }
+            if (solr_transform_parse_tok_line(ct, pattern, tp))
+            {
+                yaz_tok_parse_destroy(tp);
+                solr_transform_close(ct);
+                return 0;
+            }
+            xfree(pattern);
+        }
+        else if (t != YAZ_TOK_EOF)
+        {
+            yaz_tok_parse_destroy(tp);
+            solr_transform_close(ct);
+            return 0;
+        }
+        yaz_tok_parse_destroy(tp);
+    }
+    return ct;
+}
+
+void solr_transform_close(solr_transform_t ct)
+{
+    struct solr_prop_entry *pe;
+    if (!ct)
+        return;
+    pe = ct->entry;
+    while (pe)
+    {
+        struct solr_prop_entry *pe_next = pe->next;
+        xfree(pe->pattern);
+        xfree(pe->value);
+        xfree(pe);
+        pe = pe_next;
+    }
+    xfree(ct->addinfo);
+    yaz_tok_cfg_destroy(ct->tok_cfg);
+    wrbuf_destroy(ct->w);
+    nmem_destroy(ct->nmem);
+    xfree(ct);
+}
+
+solr_transform_t solr_transform_open_fname(const char *fname)
+{
+    solr_transform_t ct;
+    FILE *f = fopen(fname, "r");
+    if (!f)
+        return 0;
+    ct = solr_transform_open_FILE(f);
+    fclose(f);
+    return ct;
+}
+
+#if 0
+struct Z_AttributeElement {
+       Z_AttributeSetId *attributeSet; /* OPT */
+       int *attributeType;
+       int which;
+       union {
+               int *numeric;
+               Z_ComplexAttribute *complex;
+#define Z_AttributeValue_numeric 1
+#define Z_AttributeValue_complex 2
+       } value;
+};
+#endif
+
+static int compare_attr(Z_AttributeElement *a, Z_AttributeElement *b)
+{
+    ODR odr_a = odr_createmem(ODR_ENCODE);
+    ODR odr_b = odr_createmem(ODR_ENCODE);
+    int len_a, len_b;
+    char *buf_a, *buf_b;
+    int ret;
+
+    z_AttributeElement(odr_a, &a, 0, 0);
+    z_AttributeElement(odr_b, &b, 0, 0);
+    
+    buf_a = odr_getbuf(odr_a, &len_a, 0);
+    buf_b = odr_getbuf(odr_b, &len_b, 0);
+
+    ret = yaz_memcmp(buf_a, buf_b, len_a, len_b);
+
+    odr_destroy(odr_a);
+    odr_destroy(odr_b);
+    return ret;
+}
+
+const char *solr_lookup_reverse(solr_transform_t ct,
+                               const char *category,
+                               Z_AttributeList *attributes)
+{
+    struct solr_prop_entry *e;
+    size_t clen = strlen(category);
+    for (e = ct->entry; e; e = e->next)
+    {
+        if (!strncmp(e->pattern, category, clen))
+        {
+            /* category matches.. See if attributes in pattern value
+               are all listed in actual attributes */
+            int i;
+            for (i = 0; i < e->attr_list.num_attributes; i++)
+            {
+                /* entry attribute */
+                Z_AttributeElement *e_ae = e->attr_list.attributes[i];
+                int j;
+                for (j = 0; j < attributes->num_attributes; j++)
+                {
+                    /* actual attribute */
+                    Z_AttributeElement *a_ae = attributes->attributes[j];
+                    int r = compare_attr(e_ae, a_ae);
+                    if (r == 0)
+                        break;
+                }
+                if (j == attributes->num_attributes)
+                    break; /* i was not found at all.. try next pattern */
+                    
+            }
+            if (i == e->attr_list.num_attributes)
+                return e->pattern + clen;
+        }
+    }
+    return 0;
+}
+                                      
+static const char *solr_lookup_property(solr_transform_t ct,
+                                       const char *pat1, const char *pat2,
+                                       const char *pat3)
+{
+    char pattern[120];
+    struct solr_prop_entry *e;
+
+    if (pat1 && pat2 && pat3)
+        sprintf(pattern, "%.39s.%.39s.%.39s", pat1, pat2, pat3);
+    else if (pat1 && pat2)
+        sprintf(pattern, "%.39s.%.39s", pat1, pat2);
+    else if (pat1 && pat3)
+        sprintf(pattern, "%.39s.%.39s", pat1, pat3);
+    else if (pat1)
+        sprintf(pattern, "%.39s", pat1);
+    else
+        return 0;
+    
+    for (e = ct->entry; e; e = e->next)
+    {
+        if (!solr_strcmp(e->pattern, pattern))
+            return e->value;
+    }
+    return 0;
+}
+
+int solr_pr_attr_uri(solr_transform_t ct, const char *category,
+                   const char *uri, const char *val, const char *default_val,
+                   void (*pr)(const char *buf, void *client_data),
+                   void *client_data,
+                   int errcode)
+{
+    const char *res = 0;
+    const char *eval = val ? val : default_val;
+    const char *prefix = 0;
+    
+    if (uri)
+    {
+        struct solr_prop_entry *e;
+        
+        for (e = ct->entry; e; e = e->next)
+            if (!memcmp(e->pattern, "set.", 4) && e->value &&
+                !strcmp(e->value, uri))
+            {
+                prefix = e->pattern+4;
+                break;
+            }
+        /* must have a prefix now - if not it's an error */
+    }
+
+    if (!uri || prefix)
+    {
+        if (!res)
+            res = solr_lookup_property(ct, category, prefix, eval);
+        /* we have some aliases for some relations unfortunately.. */
+        if (!res && !prefix && !strcmp(category, "relation"))
+        {
+            if (!strcmp(val, "=="))
+                res = solr_lookup_property(ct, category, prefix, "exact");
+            if (!strcmp(val, "="))
+                res = solr_lookup_property(ct, category, prefix, "eq");
+            if (!strcmp(val, "<="))
+                res = solr_lookup_property(ct, category, prefix, "le");
+            if (!strcmp(val, ">="))
+                res = solr_lookup_property(ct, category, prefix, "ge");
+        }
+        if (!res)
+            res = solr_lookup_property(ct, category, prefix, "*");
+    }
+    if (res)
+    {
+        char buf[64];
+
+        const char *cp0 = res, *cp1;
+        while ((cp1 = strchr(cp0, '=')))
+        {
+            int i;
+            while (*cp1 && *cp1 != ' ')
+                cp1++;
+            if (cp1 - cp0 >= (ptrdiff_t) sizeof(buf))
+                break;
+            memcpy(buf, cp0, cp1 - cp0);
+            buf[cp1-cp0] = 0;
+            (*pr)("@attr ", client_data);
+
+            for (i = 0; buf[i]; i++)
+            {
+                if (buf[i] == '*')
+                    (*pr)(eval, client_data);
+                else
+                {
+                    char tmp[2];
+                    tmp[0] = buf[i];
+                    tmp[1] = '\0';
+                    (*pr)(tmp, client_data);
+                }
+            }
+            (*pr)(" ", client_data);
+            cp0 = cp1;
+            while (*cp0 == ' ')
+                cp0++;
+        }
+        return 1;
+    }
+    /* error ... */
+    if (errcode && !ct->error)
+    {
+        ct->error = errcode;
+        if (val)
+            ct->addinfo = xstrdup(val);
+        else
+            ct->addinfo = 0;
+    }
+    return 0;
+}
+
+int solr_pr_attr(solr_transform_t ct, const char *category,
+                const char *val, const char *default_val,
+                void (*pr)(const char *buf, void *client_data),
+                void *client_data,
+                int errcode)
+{
+    return solr_pr_attr_uri(ct, category, 0 /* uri */,
+                           val, default_val, pr, client_data, errcode);
+}
+
+
+static void solr_pr_int(int val,
+                       void (*pr)(const char *buf, void *client_data),
+                       void *client_data)
+{
+    char buf[21];              /* enough characters to 2^64 */
+    sprintf(buf, "%d", val);
+    (*pr)(buf, client_data);
+    (*pr)(" ", client_data);
+}
+
+
+static int solr_pr_prox(solr_transform_t ct, struct solr_node *mods,
+                       void (*pr)(const char *buf, void *client_data),
+                       void *client_data)
+{
+    int exclusion = 0;
+    int distance;               /* to be filled in later depending on unit */
+    int distance_defined = 0;
+    int ordered = 0;
+    int proxrel = 2;            /* less than or equal */
+    int unit = 2;               /* word */
+
+    while (mods)
+    {
+        const char *name = mods->u.st.index;
+        const char *term = mods->u.st.term;
+        const char *relation = mods->u.st.relation;
+
+        if (!strcmp(name, "distance")) {
+            distance = strtol(term, (char**) 0, 0);
+            distance_defined = 1;
+            if (!strcmp(relation, "="))
+                proxrel = 3;
+            else if (!strcmp(relation, ">"))
+                proxrel = 5;
+            else if (!strcmp(relation, "<"))
+                proxrel = 1;
+            else if (!strcmp(relation, ">=")) 
+                proxrel = 4;
+            else if (!strcmp(relation, "<="))
+                proxrel = 2;
+            else if (!strcmp(relation, "<>"))
+                proxrel = 6;
+            else 
+            {
+                ct->error = YAZ_SRW_UNSUPP_PROX_RELATION;
+                ct->addinfo = xstrdup(relation);
+                return 0;
+            }
+        } 
+        else if (!strcmp(name, "ordered"))
+            ordered = 1;
+        else if (!strcmp(name, "unordered"))
+            ordered = 0;
+        else if (!strcmp(name, "unit"))
+        {
+            if (!strcmp(term, "word"))
+                unit = 2;
+            else if (!strcmp(term, "sentence"))
+                unit = 3;
+            else if (!strcmp(term, "paragraph"))
+                unit = 4;
+            else if (!strcmp(term, "element"))
+                unit = 8;
+            else 
+            {
+                ct->error = YAZ_SRW_UNSUPP_PROX_UNIT;
+                ct->addinfo = xstrdup(term);
+                return 0;
+            }
+        } 
+        else 
+        {
+            ct->error = YAZ_SRW_UNSUPP_BOOLEAN_MODIFIER;
+            ct->addinfo = xstrdup(name);
+            return 0;
+        }
+        mods = mods->u.st.modifiers;
+    }
+
+    if (!distance_defined)
+        distance = (unit == 2) ? 1 : 0;
+
+    solr_pr_int(exclusion, pr, client_data);
+    solr_pr_int(distance, pr, client_data);
+    solr_pr_int(ordered, pr, client_data);
+    solr_pr_int(proxrel, pr, client_data);
+    (*pr)("k ", client_data);
+    solr_pr_int(unit, pr, client_data);
+
+    return 1;
+}
+
+/* Returns location of first wildcard character in the `length'
+ * characters starting at `term', or a null pointer of there are
+ * none -- like memchr().
+ */
+static const char *wcchar(int start, const char *term, int length)
+{
+    while (length > 0)
+    {
+        if (start || term[-1] != '\\')
+            if (strchr("*?", *term))
+                return term;
+        term++;
+        length--;
+        start = 0;
+    }
+    return 0;
+}
+
+
+/* ### checks for SOLR relation-name rather than Type-1 attribute */
+static int has_modifier(struct solr_node *cn, const char *name) {
+    struct solr_node *mod;
+    for (mod = cn->u.st.modifiers; mod != 0; mod = mod->u.st.modifiers) {
+        if (!strcmp(mod->u.st.index, name))
+            return 1;
+    }
+
+    return 0;
+}
+
+
+static void emit_term(solr_transform_t ct,
+                      struct solr_node *cn,
+                      const char *term, int length,
+                      void (*pr)(const char *buf, void *client_data),
+                      void *client_data)
+{
+    int i;
+    const char *ns = cn->u.st.index_uri;
+    int process_term = !has_modifier(cn, "regexp");
+    char *z3958_mem = 0;
+
+    assert(cn->which == SOLR_NODE_ST);
+
+    if (process_term && length > 0)
+    {
+        if (length > 1 && term[0] == '^' && term[length-1] == '^')
+        {
+            solr_pr_attr(ct, "position", "firstAndLast", 0,
+                        pr, client_data, YAZ_SRW_ANCHORING_CHAR_IN_UNSUPP_POSITION);
+            term++;
+            length -= 2;
+        }
+        else if (term[0] == '^')
+        {
+            solr_pr_attr(ct, "position", "first", 0,
+                        pr, client_data, YAZ_SRW_ANCHORING_CHAR_IN_UNSUPP_POSITION);
+            term++;
+            length--;
+        }
+        else if (term[length-1] == '^')
+        {
+            solr_pr_attr(ct, "position", "last", 0,
+                        pr, client_data, YAZ_SRW_ANCHORING_CHAR_IN_UNSUPP_POSITION);
+            length--;
+        }
+        else
+        {
+            solr_pr_attr(ct, "position", "any", 0,
+                        pr, client_data, YAZ_SRW_ANCHORING_CHAR_IN_UNSUPP_POSITION);
+        }
+    }
+
+    if (process_term && length > 0)
+    {
+        const char *first_wc = wcchar(1, term, length);
+        const char *second_wc = first_wc ?
+            wcchar(0, first_wc+1, length-(first_wc-term)-1) : 0;
+
+        /* Check for well-known globbing patterns that represent
+         * simple truncation attributes as expected by, for example,
+         * Bath-compliant server.  If we find such a pattern but
+         * there's no mapping for it, that's fine: we just use a
+         * general pattern-matching attribute.
+         */
+        if (first_wc == term && second_wc == term + length-1 
+            && *first_wc == '*' && *second_wc == '*' 
+            && solr_pr_attr(ct, "truncation", "both", 0, pr, client_data, 0))
+        {
+            term++;
+            length -= 2;
+        }
+        else if (first_wc == term && second_wc == 0 && *first_wc == '*'
+                 && solr_pr_attr(ct, "truncation", "left", 0,
+                                pr, client_data, 0))
+        {
+            term++;
+            length--;
+        }
+        else if (first_wc == term + length-1 && second_wc == 0
+                 && *first_wc == '*'
+                 && solr_pr_attr(ct, "truncation", "right", 0,
+                                pr, client_data, 0))
+        {
+            length--;
+        }
+        else if (first_wc)
+        {
+            /* We have one or more wildcard characters, but not in a
+             * way that can be dealt with using only the standard
+             * left-, right- and both-truncation attributes.  We need
+             * to translate the pattern into a Z39.58-type pattern,
+             * which has been supported in BIB-1 since 1996.  If
+             * there's no configuration element for "truncation.z3958"
+             * we indicate this as error 28 "Masking character not
+             * supported".
+             */
+            int i;
+            solr_pr_attr(ct, "truncation", "z3958", 0,
+                        pr, client_data, YAZ_SRW_MASKING_CHAR_UNSUPP);
+            z3958_mem = (char *) xmalloc(length+1);
+            for (i = 0; i < length; i++)
+            {
+                if (i > 0 && term[i-1] == '\\')
+                    z3958_mem[i] = term[i];
+                else if (term[i] == '*')
+                    z3958_mem[i] = '?';
+                else if (term[i] == '?')
+                    z3958_mem[i] = '#';
+                else
+                    z3958_mem[i] = term[i];
+            }
+            z3958_mem[length] = '\0';
+            term = z3958_mem;
+        }
+        else {
+            /* No masking characters.  Use "truncation.none" if given. */
+            solr_pr_attr(ct, "truncation", "none", 0,
+                        pr, client_data, 0);
+        }
+    }
+    if (ns) {
+        solr_pr_attr_uri(ct, "index", ns,
+                        cn->u.st.index, "serverChoice",
+                        pr, client_data, YAZ_SRW_UNSUPP_INDEX);
+    }
+    if (cn->u.st.modifiers)
+    {
+        struct solr_node *mod = cn->u.st.modifiers;
+        for (; mod; mod = mod->u.st.modifiers)
+        {
+            solr_pr_attr(ct, "relationModifier", mod->u.st.index, 0,
+                        pr, client_data, YAZ_SRW_UNSUPP_RELATION_MODIFIER);
+        }
+    }
+
+    (*pr)("\"", client_data);
+    for (i = 0; i<length; i++)
+    {
+        /* pr(int) each character */
+        /* we do not need to deal with \-sequences because the
+           SOLR and PQF terms have same \-format, bug #1988 */
+        char buf[2];
+
+        buf[0] = term[i];
+        buf[1] = '\0';
+        (*pr)(buf, client_data);
+    }
+    (*pr)("\" ", client_data);
+    xfree(z3958_mem);
+}
+
+static void emit_terms(solr_transform_t ct,
+                       struct solr_node *cn,
+                       void (*pr)(const char *buf, void *client_data),
+                       void *client_data,
+                       const char *op)
+{
+    struct solr_node *ne = cn->u.st.extra_terms;
+    if (ne)
+    {
+        (*pr)("@", client_data);
+        (*pr)(op, client_data);
+        (*pr)(" ", client_data);
+    }
+    emit_term(ct, cn, cn->u.st.term, strlen(cn->u.st.term),
+              pr, client_data);
+    for (; ne; ne = ne->u.st.extra_terms)
+    {
+        if (ne->u.st.extra_terms)
+        {
+            (*pr)("@", client_data);
+            (*pr)(op, client_data);
+            (*pr)(" ", client_data);
+        }            
+        emit_term(ct, cn, ne->u.st.term, strlen(ne->u.st.term),
+                  pr, client_data);
+    }
+}
+
+static void emit_wordlist(solr_transform_t ct,
+                          struct solr_node *cn,
+                          void (*pr)(const char *buf, void *client_data),
+                          void *client_data,
+                          const char *op)
+{
+    const char *cp0 = cn->u.st.term;
+    const char *cp1;
+    const char *last_term = 0;
+    int last_length = 0;
+    while(cp0)
+    {
+        while (*cp0 == ' ')
+            cp0++;
+        cp1 = strchr(cp0, ' ');
+        if (last_term)
+        {
+            (*pr)("@", client_data);
+            (*pr)(op, client_data);
+            (*pr)(" ", client_data);
+            emit_term(ct, cn, last_term, last_length, pr, client_data);
+        }
+        last_term = cp0;
+        if (cp1)
+            last_length = cp1 - cp0;
+        else
+            last_length = strlen(cp0);
+        cp0 = cp1;
+    }
+    if (last_term)
+        emit_term(ct, cn, last_term, last_length, pr, client_data);
+}
+
+void solr_transform_r(solr_transform_t ct,
+                     struct solr_node *cn,
+                     void (*pr)(const char *buf, void *client_data),
+                     void *client_data)
+{
+    const char *ns;
+    struct solr_node *mods;
+
+    if (!cn)
+        return;
+    switch (cn->which)
+    {
+    case SOLR_NODE_ST:
+        ns = cn->u.st.index_uri;
+        if (ns)
+        {
+            /* TODO If relevant fix with solr_uri */
+            if (!strcmp(ns, solr_uri())
+                && cn->u.st.index && !solr_strcmp(cn->u.st.index, "resultSet"))
+            {
+                (*pr)("@set \"", client_data);
+                (*pr)(cn->u.st.term, client_data);
+                (*pr)("\" ", client_data);
+                return ;
+            }
+        }
+        else
+        {
+            if (!ct->error)
+            {
+                ct->error = YAZ_SRW_UNSUPP_CONTEXT_SET;
+                ct->addinfo = 0;
+            }
+        }
+        solr_pr_attr(ct, "always", 0, 0, pr, client_data, 0);
+        solr_pr_attr(ct, "relation", cn->u.st.relation, 0, pr, client_data,
+                    YAZ_SRW_UNSUPP_RELATION);
+        solr_pr_attr(ct, "structure", cn->u.st.relation, 0,
+                    pr, client_data, YAZ_SRW_UNSUPP_COMBI_OF_RELATION_AND_TERM);
+        if (cn->u.st.relation && !solr_strcmp(cn->u.st.relation, "all"))
+            emit_wordlist(ct, cn, pr, client_data, "and");
+        else if (cn->u.st.relation && !solr_strcmp(cn->u.st.relation, "any"))
+            emit_wordlist(ct, cn, pr, client_data, "or");
+        else
+            emit_terms(ct, cn, pr, client_data, "and");
+        break;
+    case SOLR_NODE_BOOL:
+        (*pr)("@", client_data);
+        (*pr)(cn->u.boolean.value, client_data);
+        (*pr)(" ", client_data);
+        mods = cn->u.boolean.modifiers;
+        if (!strcmp(cn->u.boolean.value, "prox")) 
+        {
+            if (!solr_pr_prox(ct, mods, pr, client_data))
+                return;
+        } 
+        else if (mods)
+        {
+            /* Boolean modifiers other than on proximity not supported */
+            ct->error = YAZ_SRW_UNSUPP_BOOLEAN_MODIFIER;
+            ct->addinfo = xstrdup(mods->u.st.index);
+            return;
+        }
+
+        solr_transform_r(ct, cn->u.boolean.left, pr, client_data);
+        solr_transform_r(ct, cn->u.boolean.right, pr, client_data);
+        break;
+
+    default:
+        fprintf(stderr, "Fatal: impossible SOLR node-type %d\n", cn->which);
+        abort();
+    }
+}
+
+int solr_transform(solr_transform_t ct, struct solr_node *cn,
+                  void (*pr)(const char *buf, void *client_data),
+                  void *client_data)
+{
+    struct solr_prop_entry *e;
+    NMEM nmem = nmem_create();
+
+    ct->error = 0;
+    xfree(ct->addinfo);
+    ct->addinfo = 0;
+
+    for (e = ct->entry; e ; e = e->next)
+    {
+        /* TODO remove as SOLR dont supports sets.
+        if (!solr_strncmp(e->pattern, "set.", 4))
+            solr_apply_prefix(nmem, cn, e->pattern+4, e->value);
+        else if (!solr_strcmp(e->pattern, "set"))
+            solr_apply_prefix(nmem, cn, 0, e->value);
+         */
+    }
+    solr_transform_r(ct, cn, pr, client_data);
+    nmem_destroy(nmem);
+    return ct->error;
+}
+
+
+int solr_transform_FILE(solr_transform_t ct, struct solr_node *cn, FILE *f)
+{
+    /* We can use the cql_fputs util */
+    return solr_transform(ct, cn, cql_fputs, f);
+}
+
+int solr_transform_buf(solr_transform_t ct, struct solr_node *cn, char *out, int max)
+{
+    struct solr_buf_write_info info;
+    int r;
+
+    info.off = 0;
+    info.max = max;
+    info.buf = out;
+    r = solr_transform(ct, cn, cql_buf_write_handler, &info);
+    if (info.off < 0) {
+        /* Attempt to write past end of buffer.  For some reason, this
+           SRW diagnostic is deprecated, but it's so perfect for our
+           purposes that it would be stupid not to use it. */
+        char numbuf[30];
+        ct->error = YAZ_SRW_TOO_MANY_CHARS_IN_QUERY;
+        sprintf(numbuf, "%ld", (long) info.max);
+        ct->addinfo = xstrdup(numbuf);
+        return -1;
+    }
+    if (info.off >= 0)
+        info.buf[info.off] = '\0';
+    return r;
+}
+
+int solr_transform_error(solr_transform_t ct, const char **addinfo)
+{
+    *addinfo = ct->addinfo;
+    return ct->error;
+}
+
+void solr_transform_set_error(solr_transform_t ct, int error, const char *addinfo)
+{
+    xfree(ct->addinfo);
+    ct->addinfo = addinfo ? xstrdup(addinfo) : 0;
+    ct->error = error;
+}
+
+/*
+ * Local variables:
+ * c-basic-offset: 4
+ * c-file-style: "Stroustrup"
+ * indent-tabs-mode: nil
+ * End:
+ * vim: shiftwidth=4 tabstop=8 expandtab
+ */
+
diff --git a/src/sru-p.h b/src/sru-p.h
new file mode 100644 (file)
index 0000000..7fd57c4
--- /dev/null
@@ -0,0 +1,45 @@
+/* This file is part of the YAZ toolkit.
+ * Copyright (C) 1995-2010 Index Data.
+ * All rights reserved.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of Index Data nor the names of its contributors
+ *       may be used to endorse or promote products derived from this
+ *       software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+/**
+ * \file sru-p.h
+ * \brief SRU private header
+ */
+
+void yaz_add_name_value_str(ODR o, char **name, char **value,  int *i,
+                            char *a_name, char *val);
+
+void yaz_add_name_value_int(ODR o, char **name, char **value, int *i,
+                            char *a_name, Odr_int *val);
+/*
+ * Local variables:
+ * c-basic-offset: 4
+ * c-file-style: "Stroustrup"
+ * indent-tabs-mode: nil
+ * End:
+ * vim: shiftwidth=4 tabstop=8 expandtab
+ */
+
index 36d88b6..9e71129 100644 (file)
@@ -12,6 +12,7 @@
 #include <yaz/srw.h>
 #include <yaz/matchstr.h>
 #include <yaz/yaz-iconv.h>
+#include "sru-p.h"
 
 static char *yaz_decode_sru_dbpath_odr(ODR n, const char *uri, size_t len)
 {
@@ -339,7 +340,7 @@ static int yaz_sru_decode_integer(ODR odr, const char *pname,
 #endif
 
 /**
-  http://www.loc.gov/z3950/agency/zing/srw/service.html
+   http://www.loc.gov/z3950/agency/zing/srw/service.html
 */ 
 int yaz_sru_decode(Z_HTTP_Request *hreq, Z_SRW_PDU **srw_pdu,
                    Z_SOAP **soap_package, ODR decode, char **charset,
@@ -352,7 +353,7 @@ int yaz_sru_decode(Z_HTTP_Request *hreq, Z_SRW_PDU **srw_pdu,
     };
 #endif
     const char *content_type = z_HTTP_header_lookup(hreq->headers,
-                                            "Content-Type");
+                                                    "Content-Type");
 
     /*
       SRU GET: ignore content type.
@@ -361,9 +362,9 @@ int yaz_sru_decode(Z_HTTP_Request *hreq, Z_SRW_PDU **srw_pdu,
     */
     if (!strcmp(hreq->method, "GET")
         || 
-             (!strcmp(hreq->method, "POST") && content_type &&
-              !yaz_strcmp_del("application/x-www-form-urlencoded",
-                              content_type, "; ")))
+        (!strcmp(hreq->method, "POST") && content_type &&
+         !yaz_strcmp_del("application/x-www-form-urlencoded",
+                         content_type, "; ")))
     {
         char *db = "Default";
         const char *p0 = hreq->path, *p1;
@@ -1045,8 +1046,8 @@ int yaz_diag_srw_to_bib1(int code)
     return 1;
 }
 
-static void add_val_int(ODR o, char **name, char **value,  int *i,
-                        char *a_name, Odr_int *val)
+void yaz_add_name_value_int(ODR o, char **name, char **value, int *i,
+                            char *a_name, Odr_int *val)
 {
     if (val)
     {
@@ -1057,8 +1058,8 @@ static void add_val_int(ODR o, char **name, char **value,  int *i,
     }
 }
 
-static void add_val_str(ODR o, char **name, char **value,  int *i,
-                        char *a_name, char *val)
+void yaz_add_name_value_str(ODR o, char **name, char **value,  int *i,
+                            char *a_name, char *val)
 {
     if (val)
     {
@@ -1072,7 +1073,7 @@ static int yaz_get_sru_parms(const Z_SRW_PDU *srw_pdu, ODR encode,
                              char **name, char **value, int max_names)
 {
     int i = 0;
-    add_val_str(encode, name, value, &i, "version", srw_pdu->srw_version);
+    yaz_add_name_value_str(encode, name, value, &i, "version", srw_pdu->srw_version);
     name[i] = "operation";
     switch(srw_pdu->which)
     {
@@ -1081,16 +1082,16 @@ static int yaz_get_sru_parms(const Z_SRW_PDU *srw_pdu, ODR encode,
         switch(srw_pdu->u.request->query_type)
         {
         case Z_SRW_query_type_cql:
-            add_val_str(encode, name, value, &i, "query",
-                        srw_pdu->u.request->query.cql);
+            yaz_add_name_value_str(encode, name, value, &i, "query",
+                                   srw_pdu->u.request->query.cql);
             break;
         case Z_SRW_query_type_pqf:
-            add_val_str(encode, name, value, &i, "x-pquery",
-                        srw_pdu->u.request->query.pqf);
+            yaz_add_name_value_str(encode, name, value, &i, "x-pquery",
+                                   srw_pdu->u.request->query.pqf);
             break;
         case Z_SRW_query_type_xcql:
-            add_val_str(encode, name, value, &i, "x-cql",
-                        srw_pdu->u.request->query.xcql);
+            yaz_add_name_value_str(encode, name, value, &i, "x-cql",
+                                   srw_pdu->u.request->query.xcql);
             break;
         }
         switch(srw_pdu->u.request->sort_type)
@@ -1098,29 +1099,29 @@ static int yaz_get_sru_parms(const Z_SRW_PDU *srw_pdu, ODR encode,
         case Z_SRW_sort_type_none:
             break;
         case Z_SRW_sort_type_sort:            
-            add_val_str(encode, name, value, &i, "sortKeys",
-                        srw_pdu->u.request->sort.sortKeys);
+            yaz_add_name_value_str(encode, name, value, &i, "sortKeys",
+                                   srw_pdu->u.request->sort.sortKeys);
             break;
         }
-        add_val_int(encode, name, value, &i, "startRecord", 
-                    srw_pdu->u.request->startRecord);
-        add_val_int(encode, name, value, &i, "maximumRecords", 
-                    srw_pdu->u.request->maximumRecords);
-        add_val_str(encode, name, value, &i, "recordSchema",
-                    srw_pdu->u.request->recordSchema);
-        add_val_str(encode, name, value, &i, "recordPacking",
-                    srw_pdu->u.request->recordPacking);
-        add_val_str(encode, name, value, &i, "recordXPath",
-                    srw_pdu->u.request->recordXPath);
-        add_val_str(encode, name, value, &i, "stylesheet",
-                    srw_pdu->u.request->stylesheet);
-        add_val_int(encode, name, value, &i, "resultSetTTL", 
-                    srw_pdu->u.request->resultSetTTL);
+        yaz_add_name_value_int(encode, name, value, &i, "startRecord", 
+                               srw_pdu->u.request->startRecord);
+        yaz_add_name_value_int(encode, name, value, &i, "maximumRecords", 
+                               srw_pdu->u.request->maximumRecords);
+        yaz_add_name_value_str(encode, name, value, &i, "recordSchema",
+                               srw_pdu->u.request->recordSchema);
+        yaz_add_name_value_str(encode, name, value, &i, "recordPacking",
+                               srw_pdu->u.request->recordPacking);
+        yaz_add_name_value_str(encode, name, value, &i, "recordXPath",
+                               srw_pdu->u.request->recordXPath);
+        yaz_add_name_value_str(encode, name, value, &i, "stylesheet",
+                               srw_pdu->u.request->stylesheet);
+        yaz_add_name_value_int(encode, name, value, &i, "resultSetTTL", 
+                               srw_pdu->u.request->resultSetTTL);
         break;
     case Z_SRW_explain_request:
         value[i++] = "explain";
-        add_val_str(encode, name, value, &i, "stylesheet",
-                    srw_pdu->u.explain_request->stylesheet);
+        yaz_add_name_value_str(encode, name, value, &i, "stylesheet",
+                               srw_pdu->u.explain_request->stylesheet);
         break;
     case Z_SRW_scan_request:
         value[i++] = "scan";
@@ -1128,24 +1129,24 @@ static int yaz_get_sru_parms(const Z_SRW_PDU *srw_pdu, ODR encode,
         switch(srw_pdu->u.scan_request->query_type)
         {
         case Z_SRW_query_type_cql:
-            add_val_str(encode, name, value, &i, "scanClause",
-                        srw_pdu->u.scan_request->scanClause.cql);
+            yaz_add_name_value_str(encode, name, value, &i, "scanClause",
+                                   srw_pdu->u.scan_request->scanClause.cql);
             break;
         case Z_SRW_query_type_pqf:
-            add_val_str(encode, name, value, &i, "x-pScanClause",
-                        srw_pdu->u.scan_request->scanClause.pqf);
+            yaz_add_name_value_str(encode, name, value, &i, "x-pScanClause",
+                                   srw_pdu->u.scan_request->scanClause.pqf);
             break;
         case Z_SRW_query_type_xcql:
-            add_val_str(encode, name, value, &i, "x-cqlScanClause",
-                        srw_pdu->u.scan_request->scanClause.xcql);
+            yaz_add_name_value_str(encode, name, value, &i, "x-cqlScanClause",
+                                   srw_pdu->u.scan_request->scanClause.xcql);
             break;
         }
-        add_val_int(encode, name, value, &i, "responsePosition", 
-                    srw_pdu->u.scan_request->responsePosition);
-        add_val_int(encode, name, value, &i, "maximumTerms", 
-                    srw_pdu->u.scan_request->maximumTerms);
-        add_val_str(encode, name, value, &i, "stylesheet",
-                    srw_pdu->u.scan_request->stylesheet);
+        yaz_add_name_value_int(encode, name, value, &i, "responsePosition", 
+                               srw_pdu->u.scan_request->responsePosition);
+        yaz_add_name_value_int(encode, name, value, &i, "maximumTerms", 
+                               srw_pdu->u.scan_request->maximumTerms);
+        yaz_add_name_value_str(encode, name, value, &i, "stylesheet",
+                               srw_pdu->u.scan_request->stylesheet);
         break;
     case Z_SRW_update_request:
         value[i++] = "update";
index d42e394..70769e0 100644 (file)
 #include <yaz/xmalloc.h>
 #include <yaz/otherinfo.h>
 #include <yaz/log.h>
-#include <yaz/pquery.h>
-#include <yaz/marcdisp.h>
 #include <yaz/diagbib1.h>
 #include <yaz/charneg.h>
-#include <yaz/ill.h>
-#include <yaz/srw.h>
-#include <yaz/cql.h>
-#include <yaz/ccl.h>
 #include <yaz/query-charset.h>
-#include <yaz/copy_types.h>
 #include <yaz/snprintf.h>
 #include <yaz/facet.h>
 
 YAZ_SHPTR_TYPE(WRBUF)
 #endif
 
-static int log_api = 0;
-static int log_details = 0;
-
-typedef enum {
-    zoom_pending,
-    zoom_complete
-} zoom_ret;
+static int log_api0 = 0;
+static int log_details0 = 0;
 
 static void resultset_destroy(ZOOM_resultset r);
-static zoom_ret ZOOM_connection_send_init(ZOOM_connection c);
 static zoom_ret do_write_ex(ZOOM_connection c, char *buf_out, int len_out);
-static char *cql2pqf(ZOOM_connection c, const char *cql);
-
-ZOOM_API(const char *) ZOOM_get_event_str(int event)
-{
-    static const char *ar[] = {
-        "NONE",
-        "CONNECT",
-        "SEND_DATA",
-        "RECV_DATA",
-        "TIMEOUT",
-        "UNKNOWN",
-        "SEND_APDU",
-        "RECV_APDU",
-        "RECV_RECORD",
-        "RECV_SEARCH",
-        "END"
-    };
-    return ar[event];
-}
-
-/*
- * This wrapper is just for logging failed lookups.  It would be nicer
- * if it could cause failure when a lookup fails, but that's hard.
- */
-static Odr_oid *zoom_yaz_str_to_z3950oid(ZOOM_connection c,
-                                     oid_class oid_class, const char *str) {
-    Odr_oid *res = yaz_string_to_oid_odr(yaz_oid_std(), oid_class, str,
-                                     c->odr_out);
-    if (res == 0)
-        yaz_log(YLOG_WARN, "%p OID lookup (%d, '%s') failed",
-                c, (int) oid_class, str);
-    return res;
-}
-
 
 static void initlog(void)
 {
     static int log_level_initialized = 0;
     if (!log_level_initialized)
     {
-        log_api = yaz_log_module_level("zoom");
-        log_details = yaz_log_module_level("zoomdetails");
+        log_api0 = yaz_log_module_level("zoom");
+        log_details0 = yaz_log_module_level("zoomdetails");
         log_level_initialized = 1;
     }
 }
 
-static ZOOM_Event ZOOM_Event_create(int kind)
-{
-    ZOOM_Event event = (ZOOM_Event) xmalloc(sizeof(*event));
-    event->kind = kind;
-    event->next = 0;
-    event->prev = 0;
-    yaz_log(log_details, "ZOOM_Event_create(kind=%d)", kind);
-    return event;
-}
-
-static void ZOOM_Event_destroy(ZOOM_Event event)
-{
-    xfree(event);
-}
-
-static void ZOOM_connection_put_event(ZOOM_connection c, ZOOM_Event event)
-{
-    if (c->m_queue_back)
-    {
-        c->m_queue_back->prev = event;
-        assert(c->m_queue_front);
-    }
-    else
-    {
-        assert(!c->m_queue_front);
-        c->m_queue_front = event;
-    }
-    event->next = c->m_queue_back;
-    event->prev = 0;
-    c->m_queue_back = event;
-}
-
-static ZOOM_Event ZOOM_connection_get_event(ZOOM_connection c)
-{
-    ZOOM_Event event = c->m_queue_front;
-    if (!event)
-    {
-        c->last_event = ZOOM_EVENT_NONE;
-        return 0;
-    }
-    assert(c->m_queue_back);
-    c->m_queue_front = event->prev;
-    if (c->m_queue_front)
-    {
-        assert(c->m_queue_back);
-        c->m_queue_front->next = 0;
-    }
-    else
-        c->m_queue_back = 0;
-    c->last_event = event->kind;
-    return event;
-}
-
-static void ZOOM_connection_remove_events(ZOOM_connection c)
-{
-    ZOOM_Event event;
-    while ((event = ZOOM_connection_get_event(c)))
-        ZOOM_Event_destroy(event);
-}
-
-ZOOM_API(int) ZOOM_connection_peek_event(ZOOM_connection c)
-{
-    ZOOM_Event event = c->m_queue_front;
-
-    return event ? event->kind : ZOOM_EVENT_NONE;
-}
-
 void ZOOM_connection_remove_tasks(ZOOM_connection c);
 
-static void set_dset_error(ZOOM_connection c, int error,
-                           const char *dset,
-                           const char *addinfo, const char *addinfo2)
+void ZOOM_set_dset_error(ZOOM_connection c, int error,
+                         const char *dset,
+                         const char *addinfo, const char *addinfo2)
 {
     char *cp;
 
@@ -188,7 +74,7 @@ static void set_dset_error(ZOOM_connection c, int error,
         c->addinfo = xstrdup(addinfo);
     if (error != ZOOM_ERROR_NONE)
     {
-        yaz_log(log_api, "%p set_dset_error %s %s:%d %s %s",
+        yaz_log(c->log_api, "%p set_dset_error %s %s:%d %s %s",
                 c, c->host_port ? c->host_port : "<>", dset, error,
                 addinfo ? addinfo : "",
                 addinfo2 ? addinfo2 : "");
@@ -196,7 +82,7 @@ static void set_dset_error(ZOOM_connection c, int error,
     }
 }
 
-static int uri_to_code(const char *uri)
+int ZOOM_uri_to_code(const char *uri)
 {
     int code = 0;       
     const char *cp;
@@ -205,27 +91,11 @@ static int uri_to_code(const char *uri)
     return code;
 }
 
-#if YAZ_HAVE_XML2
-static void set_HTTP_error(ZOOM_connection c, int error,
-                           const char *addinfo, const char *addinfo2)
-{
-    set_dset_error(c, error, "HTTP", addinfo, addinfo2);
-}
-
-static void set_SRU_error(ZOOM_connection c, Z_SRW_diagnostic *d)
-{
-    const char *uri = d->uri;
-    if (uri)
-        set_dset_error(c, uri_to_code(uri), uri, d->details, 0);
-}
-
-#endif
 
 
-static void set_ZOOM_error(ZOOM_connection c, int error,
-                           const char *addinfo)
+void ZOOM_set_error(ZOOM_connection c, int error, const char *addinfo)
 {
-    set_dset_error(c, error, "ZOOM", addinfo, 0);
+    ZOOM_set_dset_error(c, error, "ZOOM", addinfo, 0);
 }
 
 static void clear_error(ZOOM_connection c)
@@ -250,7 +120,7 @@ static void clear_error(ZOOM_connection c)
     case ZOOM_ERROR_UNSUPPORTED_PROTOCOL:
         break;
     default:
-        set_ZOOM_error(c, ZOOM_ERROR_NONE, 0);
+        ZOOM_set_error(c, ZOOM_ERROR_NONE, 0);
     }
 }
 
@@ -362,9 +232,6 @@ void ZOOM_connection_remove_tasks(ZOOM_connection c)
         ZOOM_connection_remove_task(c);
 }
 
-static ZOOM_record record_cache_lookup(ZOOM_resultset r, int pos,
-                                       const char *syntax,
-                                       const char *elementSetName);
 
 ZOOM_API(ZOOM_connection)
     ZOOM_connection_create(ZOOM_options options)
@@ -373,7 +240,10 @@ ZOOM_API(ZOOM_connection)
 
     initlog();
 
-    yaz_log(log_api, "%p ZOOM_connection_create", c);
+    c->log_api = log_api0;
+    c->log_details = log_details0;
+
+    yaz_log(c->log_api, "%p ZOOM_connection_create", c);
 
     c->proto = PROTO_Z3950;
     c->cs = 0;
@@ -382,7 +252,7 @@ ZOOM_API(ZOOM_connection)
     c->state = STATE_IDLE;
     c->addinfo = 0;
     c->diagset = 0;
-    set_ZOOM_error(c, ZOOM_ERROR_NONE, 0);
+    ZOOM_set_error(c, ZOOM_ERROR_NONE, 0);
     c->buf_in = 0;
     c->len_in = 0;
     c->buf_out = 0;
@@ -428,8 +298,8 @@ ZOOM_API(ZOOM_connection)
 
 /* set database names. Take local databases (if set); otherwise
    take databases given in ZURL (if set); otherwise use Default */
-static char **set_DatabaseNames(ZOOM_connection con, ZOOM_options options,
-                                int *num, ODR odr)
+char **ZOOM_connection_get_databases(ZOOM_connection con, ZOOM_options options,
+                                     int *num, ODR odr)
 {
     char **databaseNames;
     const char *cp = ZOOM_options_get(options, "databaseName");
@@ -468,6 +338,8 @@ static zoom_sru_mode get_sru_mode_from_string(const char *s)
         return zoom_sru_get;
     else if (!yaz_matchstr(s, "post"))
         return zoom_sru_post;
+    else if (!yaz_matchstr(s, "solr"))
+        return zoom_sru_solr;
     return zoom_sru_error;
 }
 
@@ -480,10 +352,10 @@ ZOOM_API(void)
 
     initlog();
 
-    yaz_log(log_api, "%p ZOOM_connection_connect host=%s portnum=%d",
+    yaz_log(c->log_api, "%p ZOOM_connection_connect host=%s portnum=%d",
             c, host ? host : "null", portnum);
 
-    set_ZOOM_error(c, ZOOM_ERROR_NONE, 0);
+    ZOOM_set_error(c, ZOOM_ERROR_NONE, 0);
     ZOOM_connection_remove_tasks(c);
 
     if (c->odr_print)
@@ -501,17 +373,17 @@ ZOOM_API(void)
 
     if (c->cs)
     {
-        yaz_log(log_details, "%p ZOOM_connection_connect reconnect ok", c);
+        yaz_log(c->log_details, "%p ZOOM_connection_connect reconnect ok", c);
         c->reconnect_ok = 1;
         return;
     }
-    yaz_log(log_details, "%p ZOOM_connection_connect connect", c);
+    yaz_log(c->log_details, "%p ZOOM_connection_connect connect", c);
     xfree(c->proxy);
     c->proxy = 0;
     val = ZOOM_options_get(c->options, "proxy");
     if (val && *val)
     {
-        yaz_log(log_details, "%p ZOOM_connection_connect proxy=%s", c, val);
+        yaz_log(c->log_details, "%p ZOOM_connection_connect proxy=%s", c, val);
         c->proxy = xstrdup(val);
     }
 
@@ -520,7 +392,7 @@ ZOOM_API(void)
     val = ZOOM_options_get(c->options, "charset");
     if (val && *val)
     {
-        yaz_log(log_details, "%p ZOOM_connection_connect charset=%s", c, val);
+        yaz_log(c->log_details, "%p ZOOM_connection_connect charset=%s", c, val);
         c->charset = xstrdup(val);
     }
 
@@ -528,7 +400,7 @@ ZOOM_API(void)
     val = ZOOM_options_get(c->options, "lang");
     if (val && *val)
     {
-        yaz_log(log_details, "%p ZOOM_connection_connect lang=%s", c, val);
+        yaz_log(c->log_details, "%p ZOOM_connection_connect lang=%s", c, val);
         c->lang = xstrdup(val);
     }
     else
@@ -592,7 +464,7 @@ ZOOM_API(void)
     val = ZOOM_options_get(c->options, "cookie");
     if (val && *val)
     { 
-        yaz_log(log_details, "%p ZOOM_connection_connect cookie=%s", c, val);
+        yaz_log(c->log_details, "%p ZOOM_connection_connect cookie=%s", c, val);
         c->cookie_out = xstrdup(val);
     }
 
@@ -601,7 +473,7 @@ ZOOM_API(void)
     val = ZOOM_options_get(c->options, "clientIP");
     if (val && *val)
     {
-        yaz_log(log_details, "%p ZOOM_connection_connect clientIP=%s",
+        yaz_log(c->log_details, "%p ZOOM_connection_connect clientIP=%s",
                 c, val);
         c->client_IP = xstrdup(val);
     }
@@ -633,7 +505,7 @@ ZOOM_API(void)
         ZOOM_options_get_int(c->options, "preferredMessageSize", 1024*1024);
 
     c->async = ZOOM_options_get_bool(c->options, "async", 0);
-    yaz_log(log_details, "%p ZOOM_connection_connect async=%d", c, c->async);
+    yaz_log(c->log_details, "%p ZOOM_connection_connect async=%d", c, c->async);
  
     task = ZOOM_connection_add_task(c, ZOOM_TASK_CONNECT);
 
@@ -644,156 +516,6 @@ ZOOM_API(void)
     }
 }
 
-ZOOM_API(ZOOM_query)
-    ZOOM_query_create(void)
-{
-    ZOOM_query s = (ZOOM_query) xmalloc(sizeof(*s));
-
-    yaz_log(log_details, "%p ZOOM_query_create", s);
-    s->refcount = 1;
-    s->z_query = 0;
-    s->sort_spec = 0;
-    s->odr = odr_createmem(ODR_ENCODE);
-    s->query_string = 0;
-
-    return s;
-}
-
-ZOOM_API(void)
-    ZOOM_query_destroy(ZOOM_query s)
-{
-    if (!s)
-        return;
-
-    (s->refcount)--;
-    yaz_log(log_details, "%p ZOOM_query_destroy count=%d", s, s->refcount);
-    if (s->refcount == 0)
-    {
-        odr_destroy(s->odr);
-        xfree(s);
-    }
-}
-
-ZOOM_API(int)
-    ZOOM_query_prefix(ZOOM_query s, const char *str)
-{
-    s->query_string = odr_strdup(s->odr, str);
-    s->z_query = (Z_Query *) odr_malloc(s->odr, sizeof(*s->z_query));
-    s->z_query->which = Z_Query_type_1;
-    s->z_query->u.type_1 =  p_query_rpn(s->odr, str);
-    if (!s->z_query->u.type_1)
-    {
-        yaz_log(log_details, "%p ZOOM_query_prefix str=%s failed", s, str);
-        s->z_query = 0;
-        return -1;
-    }
-    yaz_log(log_details, "%p ZOOM_query_prefix str=%s", s, str);
-    return 0;
-}
-
-ZOOM_API(int)
-    ZOOM_query_cql(ZOOM_query s, const char *str)
-{
-    Z_External *ext;
-
-    s->query_string = odr_strdup(s->odr, str);
-
-    ext = (Z_External *) odr_malloc(s->odr, sizeof(*ext));
-    ext->direct_reference = odr_oiddup(s->odr, yaz_oid_userinfo_cql);
-    ext->indirect_reference = 0;
-    ext->descriptor = 0;
-    ext->which = Z_External_CQL;
-    ext->u.cql = s->query_string;
-    
-    s->z_query = (Z_Query *) odr_malloc(s->odr, sizeof(*s->z_query));
-    s->z_query->which = Z_Query_type_104;
-    s->z_query->u.type_104 =  ext;
-
-    yaz_log(log_details, "%p ZOOM_query_cql str=%s", s, str);
-
-    return 0;
-}
-
-/*
- * Translate the CQL string client-side into RPN which is passed to
- * the server.  This is useful for server's that don't themselves
- * support CQL, for which ZOOM_query_cql() is useless.  `conn' is used
- * only as a place to stash diagnostics if compilation fails; if this
- * information is not needed, a null pointer may be used.
- */
-ZOOM_API(int)
-    ZOOM_query_cql2rpn(ZOOM_query s, const char *str, ZOOM_connection conn)
-{
-    char *rpn;
-    int ret;
-    ZOOM_connection freeme = 0;
-
-    yaz_log(log_details, "%p ZOOM_query_cql2rpn str=%s conn=%p", s, str, conn);
-    if (conn == 0)
-        conn = freeme = ZOOM_connection_create(0);
-
-    rpn = cql2pqf(conn, str);
-    if (freeme != 0)
-        ZOOM_connection_destroy(freeme);
-    if (rpn == 0)
-        return -1;
-
-    ret = ZOOM_query_prefix(s, rpn);
-    xfree(rpn);
-    return ret;
-}
-
-/*
- * Analogous in every way to ZOOM_query_cql2rpn(), except that there
- * is no analogous ZOOM_query_ccl() that just sends uninterpreted CCL
- * to the server, as the YAZ GFS doesn't know how to handle this.
- */
-ZOOM_API(int)
-    ZOOM_query_ccl2rpn(ZOOM_query s, const char *str, const char *config,
-                       int *ccl_error, const char **error_string,
-                       int *error_pos)
-{
-    int ret;
-    struct ccl_rpn_node *rpn;
-    CCL_bibset bibset = ccl_qual_mk();
-
-    if (config)
-        ccl_qual_buf(bibset, config);
-
-    rpn = ccl_find_str(bibset, str, ccl_error, error_pos);
-    if (!rpn)
-    {
-        *error_string = ccl_err_msg(*ccl_error);
-        ret = -1;
-    }
-    else
-    {
-        WRBUF wr = wrbuf_alloc();
-        ccl_pquery(wr, rpn);
-        ccl_rpn_delete(rpn);
-        ret = ZOOM_query_prefix(s, wrbuf_cstr(wr));
-        wrbuf_destroy(wr);
-    }
-    ccl_qual_rm(&bibset);
-    return ret;
-}
-
-ZOOM_API(int)
-    ZOOM_query_sortby(ZOOM_query s, const char *criteria)
-{
-    s->sort_spec = yaz_sort_spec(s->odr, criteria);
-    if (!s->sort_spec)
-    {
-        yaz_log(log_details, "%p ZOOM_query_sortby criteria=%s failed",
-                s, criteria);
-        return -1;
-    }
-    yaz_log(log_details, "%p ZOOM_query_sortby criteria=%s", s, criteria);
-    return 0;
-}
-
-static zoom_ret do_write(ZOOM_connection c);
-
 ZOOM_API(void) ZOOM_resultset_release(ZOOM_resultset r)
 {
 #if ZOOM_RESULT_LISTS
@@ -827,7 +549,7 @@ ZOOM_API(void)
 #endif
     if (!c)
         return;
-    yaz_log(log_api, "%p ZOOM_connection_destroy", c);
+    yaz_log(c->log_api, "%p ZOOM_connection_destroy", c);
     if (c->cs)
         cs_close(c->cs);
 
@@ -879,7 +601,7 @@ void ZOOM_resultset_addref(ZOOM_resultset r)
     {
         yaz_mutex_enter(r->mutex);
         (r->refcount)++;
-        yaz_log(log_details, "%p ZOOM_resultset_addref count=%d",
+        yaz_log(log_details0, "%p ZOOM_resultset_addref count=%d",
                 r, r->refcount);
         yaz_mutex_leave(r->mutex);
     }
@@ -892,7 +614,7 @@ ZOOM_resultset ZOOM_resultset_create(void)
 
     initlog();
 
-    yaz_log(log_details, "%p ZOOM_resultset_create", r);
+    yaz_log(log_details0, "%p ZOOM_resultset_create", r);
     r->refcount = 1;
     r->size = 0;
     r->odr = odr_createmem(ODR_ENCODE);
@@ -946,8 +668,8 @@ ZOOM_API(ZOOM_resultset)
     ZOOM_resultsets set;
 #endif
 
-    yaz_log(log_api, "%p ZOOM_connection_search set %p query %p", c, r, q);
-    r->r_sort_spec = q->sort_spec;
+    yaz_log(c->log_api, "%p ZOOM_connection_search set %p query %p", c, r, q);
+    r->r_sort_spec = ZOOM_query_get_sortspec(q);
     r->query = q;
 
     r->options = ZOOM_options_create_with_parent(c->options);
@@ -968,7 +690,7 @@ ZOOM_API(ZOOM_resultset)
     if (cp)
         r->schema = xstrdup(cp);
 
-    r->databaseNames = set_DatabaseNames(c, c->options, &r->num_databaseNames,
+    r->databaseNames = ZOOM_connection_get_databases(c, c->options, &r->num_databaseNames,
                                          r->odr);
     
     r->connection = c;
@@ -988,12 +710,12 @@ ZOOM_API(ZOOM_resultset)
     {
         if (!c->cs)
         {
-            yaz_log(log_details, "ZOOM_connection_search: no comstack");
+            yaz_log(c->log_details, "ZOOM_connection_search: no comstack");
             ZOOM_connection_add_task(c, ZOOM_TASK_CONNECT);
         }
         else
         {
-            yaz_log(log_details, "ZOOM_connection_search: reconnect");
+            yaz_log(c->log_details, "ZOOM_connection_search: reconnect");
             c->reconnect_ok = 1;
         }
     }
@@ -1012,7 +734,7 @@ ZOOM_API(ZOOM_resultset)
    
     ZOOM_resultset_addref(r);
 
-    (q->refcount)++;
+    ZOOM_query_addref(q);
 
     if (!c->async)
     {
@@ -1041,7 +763,7 @@ ZOOM_API(int)
     if (ZOOM_query_sortby(newq, sort_spec) < 0)
         return -1;
 
-    yaz_log(log_api, "%p ZOOM_resultset_sort r=%p sort_type=%s sort_spec=%s",
+    yaz_log(c->log_api, "%p ZOOM_resultset_sort r=%p sort_type=%s sort_spec=%s",
             r, r, sort_type, sort_spec);
     if (!c)
         return 0;
@@ -1050,12 +772,12 @@ ZOOM_API(int)
     {
         if (!c->cs)
         {
-            yaz_log(log_details, "%p ZOOM_resultset_sort: no comstack", r);
+            yaz_log(c->log_details, "%p ZOOM_resultset_sort: no comstack", r);
             ZOOM_connection_add_task(c, ZOOM_TASK_CONNECT);
         }
         else
         {
-            yaz_log(log_details, "%p ZOOM_resultset_sort: prepare reconnect",
+            yaz_log(c->log_details, "%p ZOOM_resultset_sort: prepare reconnect",
                     r);
             c->reconnect_ok = 1;
         }
@@ -1077,38 +799,6 @@ ZOOM_API(int)
     return 0;
 }
 
-static void ZOOM_record_release(ZOOM_record rec)
-{
-    if (!rec)
-        return;
-
-#if SHPTR
-    if (rec->record_wrbuf)
-        YAZ_SHPTR_DEC(rec->record_wrbuf, wrbuf_destroy);
-#else
-    if (rec->wrbuf)
-        wrbuf_destroy(rec->wrbuf);
-#endif
-
-    if (rec->odr)
-        odr_destroy(rec->odr);
-}
-
-ZOOM_API(void)
-    ZOOM_resultset_cache_reset(ZOOM_resultset r)
-{
-    int i;
-    for (i = 0; i<RECORD_HASH_SIZE; i++)
-    {
-        ZOOM_record_cache rc;
-        for (rc = r->record_hash[i]; rc; rc = rc->next)
-        {
-            ZOOM_record_release(&rc->rec);
-        }
-        r->record_hash[i] = 0;
-    }
-}
-
 ZOOM_API(void)
     ZOOM_resultset_destroy(ZOOM_resultset r)
 {
@@ -1121,13 +811,13 @@ static void resultset_destroy(ZOOM_resultset r)
         return;
     yaz_mutex_enter(r->mutex);
     (r->refcount)--;
-    yaz_log(log_details, "%p ZOOM_resultset_destroy r=%p count=%d",
+    yaz_log(log_details0, "%p ZOOM_resultset_destroy r=%p count=%d",
             r, r, r->refcount);
     if (r->refcount == 0)
     {
         yaz_mutex_leave(r->mutex);
 
-        yaz_log(log_details, "%p ZOOM_connection resultset_destroy: Deleting resultset (%p) ", r->connection, r);
+        yaz_log(log_details0, "%p ZOOM_connection resultset_destroy: Deleting resultset (%p) ", r->connection, r);
         ZOOM_resultset_cache_reset(r);
         ZOOM_resultset_release(r);
         ZOOM_query_destroy(r->query);
@@ -1148,27 +838,16 @@ static void resultset_destroy(ZOOM_resultset r)
 ZOOM_API(size_t)
     ZOOM_resultset_size(ZOOM_resultset r)
 {
-    yaz_log(log_details, "ZOOM_resultset_size r=%p count=" ODR_INT_PRINTF,
-            r, r->size);
     return r->size;
 }
 
-static void do_close(ZOOM_connection c)
-{
-    if (c->cs)
-        cs_close(c->cs);
-    c->cs = 0;
-    ZOOM_connection_set_mask(c, 0);
-    c->state = STATE_IDLE;
-}
-
-static int ZOOM_test_reconnect(ZOOM_connection c)
+int ZOOM_test_reconnect(ZOOM_connection c)
 {
     ZOOM_Event event;
 
     if (!c->reconnect_ok)
         return 0;
-    do_close(c);
+    ZOOM_connection_close(c);
     c->reconnect_ok = 0;
     c->tasks->running = 0;
     ZOOM_connection_insert_task(c, ZOOM_TASK_CONNECT);
@@ -1189,7 +868,7 @@ static void ZOOM_resultset_retrieve(ZOOM_resultset r,
 
     if (!r)
         return;
-    yaz_log(log_details, "%p ZOOM_resultset_retrieve force_sync=%d start=%d"
+    yaz_log(log_details0, "%p ZOOM_resultset_retrieve force_sync=%d start=%d"
             " count=%d", r, force_sync, start, count);
     c = r->connection;
     if (!c)
@@ -1199,12 +878,12 @@ static void ZOOM_resultset_retrieve(ZOOM_resultset r,
     {
         if (!c->cs)
         {
-            yaz_log(log_details, "%p ZOOM_resultset_retrieve: no comstack", r);
+            yaz_log(log_details0, "%p ZOOM_resultset_retrieve: no comstack", r);
             ZOOM_connection_add_task(c, ZOOM_TASK_CONNECT);
         }
         else
         {
-            yaz_log(log_details, "%p ZOOM_resultset_retrieve: prepare "
+            yaz_log(log_details0, "%p ZOOM_resultset_retrieve: prepare "
                     "reconnect", r);
             c->reconnect_ok = 1;
         }
@@ -1245,7 +924,7 @@ ZOOM_API(void)
 
     if (!r)
         return ;
-    yaz_log(log_api, "%p ZOOM_resultset_records r=%p start=%ld count=%ld",
+    yaz_log(log_api0, "%p ZOOM_resultset_records r=%p start=%ld count=%ld",
             r, r, (long) start, (long) count);
     if (count && recs)
         force_present = 1;
@@ -1341,7 +1020,7 @@ static zoom_ret do_connect_host(ZOOM_connection c, const char *effective_host,
 {
     void *add;
 
-    yaz_log(log_details, "%p do_connect effective_host=%s", c, effective_host);
+    yaz_log(c->log_details, "%p do_connect effective_host=%s", c, effective_host);
 
     if (c->cs)
         cs_close(c->cs);
@@ -1362,8 +1041,8 @@ static zoom_ret do_connect_host(ZOOM_connection c, const char *effective_host,
             yaz_encode_sru_dbpath_buf(c->path, db);
         }
 #else
-        set_ZOOM_error(c, ZOOM_ERROR_UNSUPPORTED_PROTOCOL, "SRW");
-        do_close(c);
+        ZOOM_set_error(c, ZOOM_ERROR_UNSUPPORTED_PROTOCOL, "SRW");
+        ZOOM_connection_close(c);
         return zoom_complete;
 #endif
     }
@@ -1376,7 +1055,7 @@ static zoom_ret do_connect_host(ZOOM_connection c, const char *effective_host,
             ZOOM_connection_put_event(c, event);
             get_cert(c);
             if (c->proto == PROTO_Z3950)
-                ZOOM_connection_send_init(c);
+                ZOOM_connection_Z3950_send_init(c);
             else
             {
                 /* no init request for SRW .. */
@@ -1401,1642 +1080,104 @@ static zoom_ret do_connect_host(ZOOM_connection c, const char *effective_host,
         }
     }
     c->state = STATE_IDLE;
-    set_ZOOM_error(c, ZOOM_ERROR_CONNECT, logical_url);
+    ZOOM_set_error(c, ZOOM_ERROR_CONNECT, logical_url);
     return zoom_complete;
 }
 
-static void otherInfo_attach(ZOOM_connection c, Z_APDU *a, ODR out)
+/* returns 1 if PDU was sent OK (still pending )
+   0 if PDU was not sent OK (nothing to wait for) 
+*/
+
+ZOOM_API(ZOOM_record)
+    ZOOM_resultset_record_immediate(ZOOM_resultset s,size_t pos)
 {
-    int i;
-    for (i = 0; i<200; i++)
-    {
-        size_t len;
-        Odr_oid *oid;
-        Z_OtherInformation **oi;
-        char buf[80];
-        const char *val;
-        const char *cp;
-
-        sprintf(buf, "otherInfo%d", i);
-        val = ZOOM_options_get(c->options, buf);
-        if (!val)
-            break;
-        cp = strchr(val, ':');
-        if (!cp)
-            continue;
-        len = cp - val;
-        if (len >= sizeof(buf))
-            len = sizeof(buf)-1;
-        memcpy(buf, val, len);
-        buf[len] = '\0';
-        
-        oid = yaz_string_to_oid_odr(yaz_oid_std(), CLASS_USERINFO,
-                                    buf, out);
-        if (!oid)
-            continue;
-        
-        yaz_oi_APDU(a, &oi);
-        yaz_oi_set_string_oid(oi, out, oid, 1, cp+1);
-    }
+    const char *syntax =
+        ZOOM_options_get(s->options, "preferredRecordSyntax"); 
+    const char *elementSetName =
+        ZOOM_options_get(s->options, "elementSetName");
+
+    return ZOOM_record_cache_lookup(s, pos, syntax, elementSetName);
 }
 
-static int encode_APDU(ZOOM_connection c, Z_APDU *a, ODR out)
+ZOOM_API(ZOOM_record)
+    ZOOM_resultset_record(ZOOM_resultset r, size_t pos)
 {
-    assert(a);
-    if (c->cookie_out)
-    {
-        Z_OtherInformation **oi;
-        yaz_oi_APDU(a, &oi);
-        yaz_oi_set_string_oid(oi, out, yaz_oid_userinfo_cookie, 
-                              1, c->cookie_out);
-    }
-    if (c->client_IP)
-    {
-        Z_OtherInformation **oi;
-        yaz_oi_APDU(a, &oi);
-        yaz_oi_set_string_oid(oi, out, yaz_oid_userinfo_client_ip, 
-                              1, c->client_IP);
-    }
-    otherInfo_attach(c, a, out);
-    if (!z_APDU(out, &a, 0, 0))
+    ZOOM_record rec = ZOOM_resultset_record_immediate(r, pos);
+
+    if (!rec)
     {
-        FILE *outf = fopen("/tmp/apdu.txt", "a");
-        if (a && outf)
-        {
-            ODR odr_pr = odr_createmem(ODR_PRINT);
-            fprintf(outf, "a=%p\n", a);
-            odr_setprint(odr_pr, outf);
-            z_APDU(odr_pr, &a, 0, 0);
-            odr_destroy(odr_pr);
-        }
-        yaz_log(log_api, "%p encoding_APDU: encoding failed", c);
-        set_ZOOM_error(c, ZOOM_ERROR_ENCODE, 0);
-        odr_reset(out);
-        return -1;
+        /*
+         * MIKE: I think force_sync should always be zero, but I don't
+         * want to make this change until I get the go-ahead from
+         * Adam, in case something depends on the old synchronous
+         * behaviour.
+         */
+        int force_sync = 1;
+        if (getenv("ZOOM_RECORD_NO_FORCE_SYNC")) force_sync = 0;
+        ZOOM_resultset_retrieve(r, force_sync, pos, 1);
+        rec = ZOOM_resultset_record_immediate(r, pos);
     }
-    if (c->odr_print)
-        z_APDU(c->odr_print, &a, 0, 0);
-    yaz_log(log_details, "%p encoding_APDU encoding OK", c);
-    return 0;
+    return rec;
 }
 
-static zoom_ret send_APDU(ZOOM_connection c, Z_APDU *a)
+ZOOM_API(ZOOM_scanset)
+    ZOOM_connection_scan(ZOOM_connection c, const char *start)
 {
-    ZOOM_Event event;
-    assert(a);
-    if (encode_APDU(c, a, c->odr_out))
-        return zoom_complete;
-    yaz_log(log_details, "%p send APDU type=%d", c, a->which);
-    c->buf_out = odr_getbuf(c->odr_out, &c->len_out, 0);
-    event = ZOOM_Event_create(ZOOM_EVENT_SEND_APDU);
-    ZOOM_connection_put_event(c, event);
-    odr_reset(c->odr_out);
-    return do_write(c);
-}
+    ZOOM_scanset s;
+    ZOOM_query q = ZOOM_query_create();
 
-/* returns 1 if PDU was sent OK (still pending )
-   0 if PDU was not sent OK (nothing to wait for) 
-*/
+    ZOOM_query_prefix(q, start);
+
+    s = ZOOM_connection_scan1(c, q);
+    ZOOM_query_destroy(q);
+    return s;
+
+}
 
-static zoom_ret ZOOM_connection_send_init(ZOOM_connection c)
+ZOOM_API(ZOOM_scanset)
+    ZOOM_connection_scan1(ZOOM_connection c, ZOOM_query q)
 {
-    Z_APDU *apdu = zget_APDU(c->odr_out, Z_APDU_initRequest);
-    Z_InitRequest *ireq = apdu->u.initRequest;
-    Z_IdAuthentication *auth = (Z_IdAuthentication *)
-        odr_malloc(c->odr_out, sizeof(*auth));
-
-    ODR_MASK_SET(ireq->options, Z_Options_search);
-    ODR_MASK_SET(ireq->options, Z_Options_present);
-    ODR_MASK_SET(ireq->options, Z_Options_scan);
-    ODR_MASK_SET(ireq->options, Z_Options_sort);
-    ODR_MASK_SET(ireq->options, Z_Options_extendedServices);
-    ODR_MASK_SET(ireq->options, Z_Options_namedResultSets);
-    
-    ODR_MASK_SET(ireq->protocolVersion, Z_ProtocolVersion_1);
-    ODR_MASK_SET(ireq->protocolVersion, Z_ProtocolVersion_2);
-    ODR_MASK_SET(ireq->protocolVersion, Z_ProtocolVersion_3);
-    
-    ireq->implementationId =
-        odr_prepend(c->odr_out,
-                    ZOOM_options_get(c->options, "implementationId"),
-                    ireq->implementationId);
-    
-    ireq->implementationName = 
-        odr_prepend(c->odr_out,
-                    ZOOM_options_get(c->options, "implementationName"),
-                    odr_prepend(c->odr_out, "ZOOM-C",
-                                ireq->implementationName));
-    
-    ireq->implementationVersion = 
-        odr_prepend(c->odr_out,
-                    ZOOM_options_get(c->options, "implementationVersion"),
-                                ireq->implementationVersion);
-    
-    *ireq->maximumRecordSize = c->maximum_record_size;
-    *ireq->preferredMessageSize = c->preferred_message_size;
-    
-    if (c->group || c->password)
-    {
-        Z_IdPass *pass = (Z_IdPass *) odr_malloc(c->odr_out, sizeof(*pass));
-        pass->groupId = odr_strdup_null(c->odr_out, c->group);
-        pass->userId = odr_strdup_null(c->odr_out, c->user);
-        pass->password = odr_strdup_null(c->odr_out, c->password);
-        auth->which = Z_IdAuthentication_idPass;
-        auth->u.idPass = pass;
-        ireq->idAuthentication = auth;
-    }
-    else if (c->user)
-    {
-        auth->which = Z_IdAuthentication_open;
-        auth->u.open = odr_strdup(c->odr_out, c->user);
-        ireq->idAuthentication = auth;
-    }
-    if (c->proxy)
-    {
-        yaz_oi_set_string_oid(&ireq->otherInfo, c->odr_out,
-                              yaz_oid_userinfo_proxy, 1, c->host_port);
-    }
-    if (c->charset || c->lang)
+    ZOOM_scanset scan = 0;
+    Z_Query *z_query = ZOOM_query_get_Z_Query(q);
+
+    if (!z_query)
+        return 0;
+    scan = (ZOOM_scanset) xmalloc(sizeof(*scan));
+    scan->connection = c;
+    scan->odr = odr_createmem(ODR_DECODE);
+    scan->options = ZOOM_options_create_with_parent(c->options);
+    scan->refcount = 1;
+    scan->scan_response = 0;
+    scan->srw_scan_response = 0;
+
+    scan->query = q;
+    ZOOM_query_addref(q);
+    scan->databaseNames = ZOOM_connection_get_databases(c, c->options,
+                                            &scan->num_databaseNames,
+                                            scan->odr);
+
+    if (1)
     {
-        Z_OtherInformation **oi;
-        Z_OtherInformationUnit *oi_unit;
-        
-        yaz_oi_APDU(apdu, &oi);
+        ZOOM_task task = ZOOM_connection_add_task(c, ZOOM_TASK_SCAN);
+        task->u.scan.scan = scan;
         
-        if ((oi_unit = yaz_oi_update(oi, c->odr_out, NULL, 0, 0)))
+        (scan->refcount)++;
+        if (!c->async)
         {
-            ODR_MASK_SET(ireq->options, Z_Options_negotiationModel);
-            oi_unit->which = Z_OtherInfo_externallyDefinedInfo;
-            oi_unit->information.externallyDefinedInfo =
-                yaz_set_proposal_charneg_list(c->odr_out, " ",
-                                              c->charset, c->lang, 1);
+            while (ZOOM_event(1, &c))
+                ;
         }
     }
-    assert(apdu);
-    return send_APDU(c, apdu);
+    return scan;
 }
 
-#if YAZ_HAVE_XML2
-static zoom_ret send_srw(ZOOM_connection c, Z_SRW_PDU *sr)
+ZOOM_API(void)
+    ZOOM_scanset_destroy(ZOOM_scanset scan)
 {
-    Z_GDU *gdu;
-    ZOOM_Event event;
-    const char *database =  ZOOM_options_get(c->options, "databaseName");
-    char *fdatabase = 0;
-    
-    if (database)
-        fdatabase = yaz_encode_sru_dbpath_odr(c->odr_out, database);
-    gdu = z_get_HTTP_Request_host_path(c->odr_out, c->host_port,
-                                       fdatabase ? fdatabase : c->path);
-
-    if (c->sru_mode == zoom_sru_get)
-    {
-        yaz_sru_get_encode(gdu->u.HTTP_Request, sr, c->odr_out, c->charset);
-    }
-    else if (c->sru_mode == zoom_sru_post)
-    {
-        yaz_sru_post_encode(gdu->u.HTTP_Request, sr, c->odr_out, c->charset);
-    }
-    else if (c->sru_mode == zoom_sru_soap)
-    {
-        yaz_sru_soap_encode(gdu->u.HTTP_Request, sr, c->odr_out, c->charset);
-    }
-    if (!z_GDU(c->odr_out, &gdu, 0, 0))
-        return zoom_complete;
-    if (c->odr_print)
-        z_GDU(c->odr_print, &gdu, 0, 0);
-    c->buf_out = odr_getbuf(c->odr_out, &c->len_out, 0);
-        
-    event = ZOOM_Event_create(ZOOM_EVENT_SEND_APDU);
-    ZOOM_connection_put_event(c, event);
-    odr_reset(c->odr_out);
-    return do_write(c);
-}
-#endif
-
-#if YAZ_HAVE_XML2
-static Z_SRW_PDU *ZOOM_srw_get_pdu(ZOOM_connection c, int type)
-{
-    Z_SRW_PDU *sr = yaz_srw_get_pdu(c->odr_out, type, c->sru_version);
-    sr->username = c->user;
-    sr->password = c->password;
-    return sr;
-}
-#endif
-
-#if YAZ_HAVE_XML2
-static zoom_ret ZOOM_connection_srw_send_search(ZOOM_connection c)
-{
-    int i;
-    int *start, *count;
-    ZOOM_resultset resultset = 0;
-    Z_SRW_PDU *sr = 0;
-    const char *option_val = 0;
-
-    if (c->error)                  /* don't continue on error */
-        return zoom_complete;
-    assert(c->tasks);
-    switch(c->tasks->which)
-    {
-    case ZOOM_TASK_SEARCH:
-        resultset = c->tasks->u.search.resultset;
-        if (!resultset->setname)
-            resultset->setname = xstrdup("default");
-        ZOOM_options_set(resultset->options, "setname", resultset->setname);
-        start = &c->tasks->u.search.start;
-        count = &c->tasks->u.search.count;
-        break;
-    case ZOOM_TASK_RETRIEVE:
-        resultset = c->tasks->u.retrieve.resultset;
-
-        start = &c->tasks->u.retrieve.start;
-        count = &c->tasks->u.retrieve.count;
-
-        if (*start >= resultset->size)
-            return zoom_complete;
-        if (*start + *count > resultset->size)
-            *count = resultset->size - *start;
-
-        for (i = 0; i < *count; i++)
-        {
-            ZOOM_record rec =
-                record_cache_lookup(resultset, i + *start,
-                                    c->tasks->u.retrieve.syntax,
-                                    c->tasks->u.retrieve.elementSetName);
-            if (!rec)
-                break;
-            else
-            {
-                ZOOM_Event event = ZOOM_Event_create(ZOOM_EVENT_RECV_RECORD);
-                ZOOM_connection_put_event(c, event);
-            }
-        }
-        *start += i;
-        *count -= i;
-
-        if (*count == 0)
-            return zoom_complete;
-        break;
-    default:
-        return zoom_complete;
-    }
-    assert(resultset->query);
-        
-    sr = ZOOM_srw_get_pdu(c, Z_SRW_searchRetrieve_request);
-    if (resultset->query->z_query->which == Z_Query_type_104
-        && resultset->query->z_query->u.type_104->which == Z_External_CQL)
-    {
-        sr->u.request->query_type = Z_SRW_query_type_cql;
-        sr->u.request->query.cql =resultset->query->z_query->u.type_104->u.cql;
-    }
-    else if (resultset->query->z_query->which == Z_Query_type_1 &&
-             resultset->query->z_query->u.type_1)
-    {
-        sr->u.request->query_type = Z_SRW_query_type_pqf;
-        sr->u.request->query.pqf = resultset->query->query_string;
-    }
-    else
-    {
-        set_ZOOM_error(c, ZOOM_ERROR_UNSUPPORTED_QUERY, 0);
-        return zoom_complete;
-    }
-    sr->u.request->startRecord = odr_intdup(c->odr_out, *start + 1);
-    sr->u.request->maximumRecords = odr_intdup(
-        c->odr_out, (resultset->step > 0 && resultset->step < *count) ? 
-        resultset->step : *count);
-    sr->u.request->recordSchema = resultset->schema;
-    
-    option_val = ZOOM_resultset_option_get(resultset, "recordPacking");
-    if (option_val)
-        sr->u.request->recordPacking = odr_strdup(c->odr_out, option_val);
-
-    option_val = ZOOM_resultset_option_get(resultset, "extraArgs");
-    yaz_encode_sru_extra(sr, c->odr_out, option_val);
-    return send_srw(c, sr);
-}
-#else
-static zoom_ret ZOOM_connection_srw_send_search(ZOOM_connection c)
-{
-    return zoom_complete;
-}
-#endif
-
-static zoom_ret ZOOM_connection_send_search(ZOOM_connection c)
-{
-    ZOOM_resultset r;
-    int lslb, ssub, mspn;
-    const char *syntax;
-    Z_APDU *apdu = zget_APDU(c->odr_out, Z_APDU_searchRequest);
-    Z_SearchRequest *search_req = apdu->u.searchRequest;
-    const char *elementSetName;
-    const char *smallSetElementSetName;
-    const char *mediumSetElementSetName;
-    const char *facets;
-
-    assert(c->tasks);
-    assert(c->tasks->which == ZOOM_TASK_SEARCH);
-
-    r = c->tasks->u.search.resultset;
-
-    yaz_log(log_details, "%p ZOOM_connection_send_search set=%p", c, r);
-
-    elementSetName =
-        ZOOM_options_get(r->options, "elementSetName");
-    smallSetElementSetName  =
-        ZOOM_options_get(r->options, "smallSetElementSetName");
-    mediumSetElementSetName =
-        ZOOM_options_get(r->options, "mediumSetElementSetName");
-
-    if (!smallSetElementSetName)
-        smallSetElementSetName = elementSetName;
-
-    if (!mediumSetElementSetName)
-        mediumSetElementSetName = elementSetName;
-
-    facets = ZOOM_options_get(r->options, "facets");
-    if (facets) {
-        Z_FacetList *facet_list = yaz_pqf_parse_facet_list(c->odr_out, facets);
-        if (facet_list) {
-            Z_OtherInformation **oi;
-            yaz_oi_APDU(apdu, &oi);
-            if (facet_list) {
-                yaz_oi_set_facetlist(oi, c->odr_out, facet_list);
-            }
-        }
-    }
-
-    assert(r);
-    assert(r->query);
-
-    /* prepare query for the search request */
-    search_req->query = r->query->z_query;
-    if (!search_req->query)
-    {
-        set_ZOOM_error(c, ZOOM_ERROR_INVALID_QUERY, 0);
-        return zoom_complete;
-    }
-    if (r->query->z_query->which == Z_Query_type_1 || 
-        r->query->z_query->which == Z_Query_type_101)
-    {
-        const char *cp = ZOOM_options_get(r->options, "rpnCharset");
-        if (cp)
-        {
-            yaz_iconv_t cd = yaz_iconv_open(cp, "UTF-8");
-            if (cd)
-            {
-                int r;
-                search_req->query = yaz_copy_Z_Query(search_req->query,
-                                                     c->odr_out);
-                
-                r = yaz_query_charset_convert_rpnquery_check(
-                    search_req->query->u.type_1,
-                    c->odr_out, cd);
-                yaz_iconv_close(cd);
-                if (r)
-                {  /* query could not be char converted */
-                    set_ZOOM_error(c, ZOOM_ERROR_INVALID_QUERY, 0);
-                    return zoom_complete;
-                }
-            }
-        }
-    }
-    search_req->databaseNames = r->databaseNames;
-    search_req->num_databaseNames = r->num_databaseNames;
-
-    /* get syntax (no need to provide unless piggyback is in effect) */
-    syntax = c->tasks->u.search.syntax;
-
-    lslb = ZOOM_options_get_int(r->options, "largeSetLowerBound", -1);
-    ssub = ZOOM_options_get_int(r->options, "smallSetUpperBound", -1);
-    mspn = ZOOM_options_get_int(r->options, "mediumSetPresentNumber", -1);
-    if (lslb != -1 && ssub != -1 && mspn != -1)
-    {
-        /* So're a Z39.50 expert? Let's hope you don't do sort */
-        *search_req->largeSetLowerBound = lslb;
-        *search_req->smallSetUpperBound = ssub;
-        *search_req->mediumSetPresentNumber = mspn;
-    }
-    else if (c->tasks->u.search.start == 0 && c->tasks->u.search.count > 0
-             && r->piggyback && !r->r_sort_spec && !r->schema)
-    {
-        /* Regular piggyback - do it unless we're going to do sort */
-        *search_req->largeSetLowerBound = 2000000000;
-        *search_req->smallSetUpperBound = 1;
-        *search_req->mediumSetPresentNumber = 
-            r->step>0 ? r->step : c->tasks->u.search.count;
-    }
-    else
-    {
-        /* non-piggyback. Need not provide elementsets or syntaxes .. */
-        smallSetElementSetName = 0;
-        mediumSetElementSetName = 0;
-        syntax = 0;
-    }
-    if (smallSetElementSetName && *smallSetElementSetName)
-    {
-        Z_ElementSetNames *esn = (Z_ElementSetNames *)
-            odr_malloc(c->odr_out, sizeof(*esn));
-        
-        esn->which = Z_ElementSetNames_generic;
-        esn->u.generic = odr_strdup(c->odr_out, smallSetElementSetName);
-        search_req->smallSetElementSetNames = esn;
-    }
-    if (mediumSetElementSetName && *mediumSetElementSetName)
-    {
-        Z_ElementSetNames *esn =(Z_ElementSetNames *)
-            odr_malloc(c->odr_out, sizeof(*esn));
-        
-        esn->which = Z_ElementSetNames_generic;
-        esn->u.generic = odr_strdup(c->odr_out, mediumSetElementSetName);
-        search_req->mediumSetElementSetNames = esn;
-    }
-    if (syntax)
-        search_req->preferredRecordSyntax =
-            zoom_yaz_str_to_z3950oid(c, CLASS_RECSYN, syntax);
-    
-    if (!r->setname)
-    {
-        if (c->support_named_resultsets)
-        {
-            char setname[14];
-            int ord;
-            /* find the lowest unused ordinal so that we re-use
-               result sets on the server. */
-            for (ord = 1; ; ord++)
-            {
-#if ZOOM_RESULT_LISTS
-                ZOOM_resultsets rsp;
-                sprintf(setname, "%d", ord);
-                for (rsp = c->resultsets; rsp; rsp = rsp->next)
-                    if (rsp->resultset->setname && !strcmp(rsp->resultset->setname, setname))
-                        break;
-                if (!rsp)
-                    break;
-#else
-                ZOOM_resultset rp;
-                sprintf(setname, "%d", ord);
-                for (rp = c->resultsets; rp; rp = rp->next)
-                    if (rp->setname && !strcmp(rp->setname, setname))
-                        break;
-                if (!rp)
-                    break;
-#endif
-
-            }
-            r->setname = xstrdup(setname);
-            yaz_log(log_details, "%p ZOOM_connection_send_search: allocating "
-                    "set %s", c, r->setname);
-        }
-        else
-        {
-            yaz_log(log_details, "%p ZOOM_connection_send_search: using "
-                    "default set", c);
-            r->setname = xstrdup("default");
-        }
-        ZOOM_options_set(r->options, "setname", r->setname);
-    }
-    search_req->resultSetName = odr_strdup(c->odr_out, r->setname);
-    return send_APDU(c, apdu);
-}
-
-static void response_default_diag(ZOOM_connection c, Z_DefaultDiagFormat *r)
-{
-    char oid_name_buf[OID_STR_MAX];
-    const char *oid_name;
-    char *addinfo = 0;
-
-    oid_name = yaz_oid_to_string_buf(r->diagnosticSetId, 0, oid_name_buf);
-    switch (r->which)
-    {
-    case Z_DefaultDiagFormat_v2Addinfo:
-        addinfo = r->u.v2Addinfo;
-        break;
-    case Z_DefaultDiagFormat_v3Addinfo:
-        addinfo = r->u.v3Addinfo;
-        break;
-    }
-    xfree(c->addinfo);
-    c->addinfo = 0;
-    set_dset_error(c, *r->condition, oid_name, addinfo, 0);
-}
-
-static void response_diag(ZOOM_connection c, Z_DiagRec *p)
-{
-    if (p->which != Z_DiagRec_defaultFormat)
-        set_ZOOM_error(c, ZOOM_ERROR_DECODE, 0);
-    else
-        response_default_diag(c, p->u.defaultFormat);
-}
-
-ZOOM_API(ZOOM_record)
-    ZOOM_record_clone(ZOOM_record srec)
-{
-    char *buf;
-    int size;
-    ODR odr_enc;
-    ZOOM_record nrec;
-
-    odr_enc = odr_createmem(ODR_ENCODE);
-    if (!z_NamePlusRecord(odr_enc, &srec->npr, 0, 0))
-        return 0;
-    buf = odr_getbuf(odr_enc, &size, 0);
-    
-    nrec = (ZOOM_record) xmalloc(sizeof(*nrec));
-    yaz_log(log_details, "ZOOM_record create");
-    nrec->odr = odr_createmem(ODR_DECODE);
-#if SHPTR
-    nrec->record_wrbuf = 0;
-#else
-    nrec->wrbuf = 0;
-#endif
-    odr_setbuf(nrec->odr, buf, size, 0);
-    z_NamePlusRecord(nrec->odr, &nrec->npr, 0, 0);
-    
-    nrec->schema = odr_strdup_null(nrec->odr, srec->schema);
-    nrec->diag_uri = odr_strdup_null(nrec->odr, srec->diag_uri);
-    nrec->diag_message = odr_strdup_null(nrec->odr, srec->diag_message);
-    nrec->diag_details = odr_strdup_null(nrec->odr, srec->diag_details);
-    nrec->diag_set = odr_strdup_null(nrec->odr, srec->diag_set);
-    odr_destroy(odr_enc);
-    return nrec;
-}
-
-ZOOM_API(ZOOM_record)
-    ZOOM_resultset_record_immediate(ZOOM_resultset s,size_t pos)
-{
-    const char *syntax =
-        ZOOM_options_get(s->options, "preferredRecordSyntax"); 
-    const char *elementSetName =
-        ZOOM_options_get(s->options, "elementSetName");
-
-    return record_cache_lookup(s, pos, syntax, elementSetName);
-}
-
-ZOOM_API(ZOOM_record)
-    ZOOM_resultset_record(ZOOM_resultset r, size_t pos)
-{
-    ZOOM_record rec = ZOOM_resultset_record_immediate(r, pos);
-
-    if (!rec)
-    {
-        /*
-         * MIKE: I think force_sync should always be zero, but I don't
-         * want to make this change until I get the go-ahead from
-         * Adam, in case something depends on the old synchronous
-         * behaviour.
-         */
-        int force_sync = 1;
-        if (getenv("ZOOM_RECORD_NO_FORCE_SYNC")) force_sync = 0;
-        ZOOM_resultset_retrieve(r, force_sync, pos, 1);
-        rec = ZOOM_resultset_record_immediate(r, pos);
-    }
-    return rec;
-}
-
-ZOOM_API(void)
-    ZOOM_record_destroy(ZOOM_record rec)
-{
-    ZOOM_record_release(rec);
-    yaz_log(log_details, "ZOOM_record destroy");
-    xfree(rec);
-}
-
-
-static yaz_iconv_t iconv_create_charset(const char *record_charset)
-{
-    char to[40];
-    char from[40];
-    yaz_iconv_t cd = 0;
-
-    *from = '\0';
-    strcpy(to, "UTF-8");
-    if (record_charset && *record_charset)
-    {
-        /* Use "from,to" or just "from" */
-        const char *cp = strchr(record_charset, ',');
-        size_t clen = strlen(record_charset);
-        if (cp && cp[1])
-        {
-            strncpy( to, cp+1, sizeof(to)-1);
-            to[sizeof(to)-1] = '\0';
-            clen = cp - record_charset;
-        }
-        if (clen > sizeof(from)-1)
-            clen = sizeof(from)-1;
-        
-        if (clen)
-            strncpy(from, record_charset, clen);
-        from[clen] = '\0';
-    }
-    if (*from && *to)
-        cd = yaz_iconv_open(to, from);
-    return cd;
-}
-
-static const char *return_marc_record(WRBUF wrbuf,
-                                      int marc_type,
-                                      int *len,
-                                      const char *buf, int sz,
-                                      const char *record_charset)
-{
-    yaz_iconv_t cd = iconv_create_charset(record_charset);
-    yaz_marc_t mt = yaz_marc_create();
-    const char *ret_string = 0;
-
-    if (cd)
-        yaz_marc_iconv(mt, cd);
-    yaz_marc_xml(mt, marc_type);
-    if (yaz_marc_decode_wrbuf(mt, buf, sz, wrbuf) > 0)
-    {
-        if (len)
-            *len = wrbuf_len(wrbuf);
-        ret_string = wrbuf_cstr(wrbuf);
-    }
-    yaz_marc_destroy(mt);
-    if (cd)
-        yaz_iconv_close(cd);
-    return ret_string;
-}
-
-static const char *return_opac_record(WRBUF wrbuf,
-                                      int marc_type,
-                                      int *len,
-                                      Z_OPACRecord *opac_rec,
-                                      const char *record_charset)
-{
-    yaz_iconv_t cd = iconv_create_charset(record_charset);
-    yaz_marc_t mt = yaz_marc_create();
-
-    if (cd)
-        yaz_marc_iconv(mt, cd);
-    yaz_marc_xml(mt, marc_type);
-
-    yaz_opac_decode_wrbuf(mt, opac_rec, wrbuf);
-    yaz_marc_destroy(mt);
-
-    if (cd)
-        yaz_iconv_close(cd);
-    if (len)
-        *len = wrbuf_len(wrbuf);
-    return wrbuf_cstr(wrbuf);
-}
-
-static const char *return_string_record(WRBUF wrbuf,
-                                        int *len,
-                                        const char *buf, int sz,
-                                        const char *record_charset)
-{
-    yaz_iconv_t cd = iconv_create_charset(record_charset);
-
-    if (cd)
-    {
-        wrbuf_iconv_write(wrbuf, cd, buf, sz);
-        wrbuf_iconv_reset(wrbuf, cd);
-
-        buf = wrbuf_cstr(wrbuf);
-        sz = wrbuf_len(wrbuf);
-        yaz_iconv_close(cd);
-    }
-    if (len)
-        *len = sz;
-    return buf;
-}
-
-static const char *return_record_wrbuf(WRBUF wrbuf, int *len,
-                                       Z_NamePlusRecord *npr,
-                                       int marctype, const char *charset)
-{
-    Z_External *r = (Z_External *) npr->u.databaseRecord;
-    const Odr_oid *oid = r->direct_reference;
-
-    wrbuf_rewind(wrbuf);
-    /* render bibliographic record .. */
-    if (r->which == Z_External_OPAC)
-    {
-        return return_opac_record(wrbuf, marctype, len,
-                                  r->u.opac, charset);
-    }
-    if (r->which == Z_External_sutrs)
-        return return_string_record(wrbuf, len,
-                                    (char*) r->u.sutrs->buf,
-                                    r->u.sutrs->len,
-                                    charset);
-    else if (r->which == Z_External_octet)
-    {
-        if (yaz_oid_is_iso2709(oid))
-        {
-            const char *ret_buf = return_marc_record(
-                wrbuf, marctype, len,
-                (const char *) r->u.octet_aligned->buf,
-                r->u.octet_aligned->len,
-                charset);
-            if (ret_buf)
-                return ret_buf;
-            /* bad ISO2709. Return fail unless raw (ISO2709) is wanted */
-            if (marctype != YAZ_MARC_ISO2709)
-                return 0;
-        }
-        return return_string_record(wrbuf, len,
-                                    (const char *) r->u.octet_aligned->buf,
-                                    r->u.octet_aligned->len,
-                                    charset);
-    }
-    else if (r->which == Z_External_grs1)
-    {
-        yaz_display_grs1(wrbuf, r->u.grs1, 0);
-        return return_string_record(wrbuf, len,
-                                    wrbuf_buf(wrbuf),
-                                    wrbuf_len(wrbuf),
-                                    charset);
-    }
-    return 0;
-}
-    
-ZOOM_API(int)
-    ZOOM_record_error(ZOOM_record rec, const char **cp,
-                      const char **addinfo, const char **diagset)
-{
-    Z_NamePlusRecord *npr;
-    
-    if (!rec)
-        return 0;
-
-    npr = rec->npr;
-    if (rec->diag_uri)
-    {
-        if (cp)
-            *cp = rec->diag_message;
-        if (addinfo)
-            *addinfo = rec->diag_details;
-        if (diagset)
-            *diagset = rec->diag_set;
-        return uri_to_code(rec->diag_uri);
-    }
-    if (npr && npr->which == Z_NamePlusRecord_surrogateDiagnostic)
-    {
-        Z_DiagRec *diag_rec = npr->u.surrogateDiagnostic;
-        int error = YAZ_BIB1_UNSPECIFIED_ERROR;
-        const char *add = 0;
-
-        if (diag_rec->which == Z_DiagRec_defaultFormat)
-        {
-            Z_DefaultDiagFormat *ddf = diag_rec->u.defaultFormat;
-            oid_class oclass;
-    
-            error = *ddf->condition;
-            switch (ddf->which)
-            {
-            case Z_DefaultDiagFormat_v2Addinfo:
-                add = ddf->u.v2Addinfo;
-                break;
-            case Z_DefaultDiagFormat_v3Addinfo:
-                add = ddf->u.v3Addinfo;
-                break;
-            }
-            if (diagset)
-                *diagset =
-                    yaz_oid_to_string(yaz_oid_std(),
-                                      ddf->diagnosticSetId, &oclass);
-        }
-        else
-        {
-            if (diagset)
-                *diagset = "Bib-1";
-        }
-        if (addinfo)
-            *addinfo = add ? add : "";
-        if (cp)
-            *cp = diagbib1_str(error);
-        return error;
-    }
-    return 0;
-}
-
-static const char *get_record_format(WRBUF wrbuf, int *len,
-                                     Z_NamePlusRecord *npr,
-                                     int marctype, const char *charset,
-                                     const char *format)
-{
-    const char *res = return_record_wrbuf(wrbuf, len, npr, marctype, charset);
-#if YAZ_HAVE_XML2
-    if (*format == '1' && len)
-    {
-        /* try to XML format res */
-        xmlDocPtr doc;
-        xmlKeepBlanksDefault(0); /* get get xmlDocFormatMemory to work! */
-        doc = xmlParseMemory(res, *len);
-        if (doc)
-        {
-            xmlChar *xml_mem;
-            int xml_size;
-            xmlDocDumpFormatMemory(doc, &xml_mem, &xml_size, 1);
-            wrbuf_rewind(wrbuf);
-            wrbuf_write(wrbuf, (const char *) xml_mem, xml_size);
-            xmlFree(xml_mem);
-            xmlFreeDoc(doc);
-            res = wrbuf_cstr(wrbuf);
-            *len = wrbuf_len(wrbuf);
-        } 
-    }
-#endif
-    return res;
-}
-
-
-static const char *npr_format(Z_NamePlusRecord *npr, const char *schema,
-                              WRBUF wrbuf,
-                              const char *type_spec, int *len)
-{
-    size_t i;
-    char type[40];
-    char charset[40];
-    char format[3];
-    const char *cp = type_spec;
-
-    for (i = 0; cp[i] && cp[i] != ';' && cp[i] != ' ' && i < sizeof(type)-1;
-         i++)
-        type[i] = cp[i];
-    type[i] = '\0';
-    charset[0] = '\0';
-    format[0] = '\0';
-    while (1)
-    {
-        while (cp[i] == ' ')
-            i++;
-        if (cp[i] != ';')
-            break;
-        i++;
-        while (cp[i] == ' ')
-            i++;
-        if (!strncmp(cp + i, "charset=", 8))
-        {
-            size_t j = 0;
-            i = i + 8; /* skip charset= */
-            for (j = 0; cp[i] && cp[i] != ';' && cp[i] != ' '; i++)
-            {
-                if (j < sizeof(charset)-1)
-                    charset[j++] = cp[i];
-            }
-            charset[j] = '\0';
-        }
-        else if (!strncmp(cp + i, "format=", 7))
-        {
-            size_t j = 0; 
-            i = i + 7;
-            for (j = 0; cp[i] && cp[i] != ';' && cp[i] != ' '; i++)
-            {
-                if (j < sizeof(format)-1)
-                    format[j++] = cp[i];
-            }
-            format[j] = '\0';
-        } 
-    }
-    if (!strcmp(type, "database"))
-    {
-        if (len)
-            *len = (npr->databaseName ? strlen(npr->databaseName) : 0);
-        return npr->databaseName;
-    }
-    else if (!strcmp(type, "schema"))
-    {
-        if (len)
-            *len = schema ? strlen(schema) : 0;
-        return schema;
-    }
-    else if (!strcmp(type, "syntax"))
-    {
-        const char *desc = 0;   
-        if (npr->which == Z_NamePlusRecord_databaseRecord)
-        {
-            Z_External *r = (Z_External *) npr->u.databaseRecord;
-            desc = yaz_oid_to_string(yaz_oid_std(), r->direct_reference, 0);
-        }
-        if (!desc)
-            desc = "none";
-        if (len)
-            *len = strlen(desc);
-        return desc;
-    }
-    if (npr->which != Z_NamePlusRecord_databaseRecord)
-        return 0;
-
-    /* from now on - we have a database record .. */
-    if (!strcmp(type, "render"))
-    {
-        return get_record_format(wrbuf, len, npr, YAZ_MARC_LINE, charset, format);
-    }
-    else if (!strcmp(type, "xml"))
-    {
-        return get_record_format(wrbuf, len, npr, YAZ_MARC_MARCXML, charset,
-                                 format);
-    }
-    else if (!strcmp(type, "txml"))
-    {
-        return get_record_format(wrbuf, len, npr, YAZ_MARC_TURBOMARC, charset,
-                                 format);
-    }
-    else if (!strcmp(type, "raw"))
-    {
-        return get_record_format(wrbuf, len, npr, YAZ_MARC_ISO2709, charset,
-            format);
-    }
-    else if (!strcmp(type, "ext"))
-    {
-        if (len) *len = -1;
-        return (const char *) npr->u.databaseRecord;
-    }
-    else if (!strcmp(type, "opac"))
-    {
-        if (npr->u.databaseRecord->which == Z_External_OPAC)
-            return get_record_format(wrbuf, len, npr, YAZ_MARC_MARCXML, charset,
-                                     format);
-    }
-    return 0;
-}
-
-ZOOM_API(const char *)
-    ZOOM_record_get(ZOOM_record rec, const char *type_spec, int *len)
-{
-    WRBUF wrbuf;
-    
-    if (len)
-        *len = 0; /* default return */
-        
-    if (!rec || !rec->npr)
-        return 0;
-
-#if SHPTR
-    if (!rec->record_wrbuf)
-    {
-        WRBUF w = wrbuf_alloc();
-        YAZ_SHPTR_INIT(rec->record_wrbuf, w);
-    }
-    wrbuf = rec->record_wrbuf->ptr;
-#else
-    if (!rec->wrbuf)
-        rec->wrbuf = wrbuf_alloc();
-    wrbuf = rec->wrbuf;
-#endif
-    return npr_format(rec->npr, rec->schema, wrbuf, type_spec, len);
-}
-
-static int strcmp_null(const char *v1, const char *v2)
-{
-    if (!v1 && !v2)
-        return 0;
-    if (!v1 || !v2)
-        return -1;
-    return strcmp(v1, v2);
-}
-
-static size_t record_hash(int pos)
-{
-    if (pos < 0)
-        pos = 0;
-    return pos % RECORD_HASH_SIZE;
-}
-
-static void record_cache_add(ZOOM_resultset r, Z_NamePlusRecord *npr, 
-                             int pos,
-                             const char *syntax, const char *elementSetName,
-                             const char *schema,
-                             Z_SRW_diagnostic *diag)
-{
-    ZOOM_record_cache rc = 0;
-    
-    ZOOM_Event event = ZOOM_Event_create(ZOOM_EVENT_RECV_RECORD);
-    ZOOM_connection_put_event(r->connection, event);
-
-    for (rc = r->record_hash[record_hash(pos)]; rc; rc = rc->next)
-    {
-        if (pos == rc->pos 
-            && strcmp_null(r->schema, rc->schema) == 0
-            && strcmp_null(elementSetName,rc->elementSetName) == 0
-            && strcmp_null(syntax, rc->syntax) == 0)
-            break;
-    }
-    if (!rc)
-    {
-        rc = (ZOOM_record_cache) odr_malloc(r->odr, sizeof(*rc));
-        rc->rec.odr = 0;
-#if SHPTR
-        YAZ_SHPTR_INC(r->record_wrbuf);
-        rc->rec.record_wrbuf = r->record_wrbuf;
-#else
-        rc->rec.wrbuf = 0;
-#endif
-        rc->elementSetName = odr_strdup_null(r->odr, elementSetName);
-        
-        rc->syntax = odr_strdup_null(r->odr, syntax);
-        
-        rc->schema = odr_strdup_null(r->odr, r->schema);
-
-        rc->pos = pos;
-        rc->next = r->record_hash[record_hash(pos)];
-        r->record_hash[record_hash(pos)] = rc;
-    }
-    rc->rec.npr = npr;
-    rc->rec.schema = odr_strdup_null(r->odr, schema);
-    rc->rec.diag_set = 0;
-    rc->rec.diag_uri = 0;
-    rc->rec.diag_message = 0;
-    rc->rec.diag_details = 0;
-    if (diag)
-    {
-        if (diag->uri)
-        {
-            char *cp;
-            rc->rec.diag_set = odr_strdup(r->odr, diag->uri);
-            if ((cp = strrchr(rc->rec.diag_set, '/')))
-                *cp = '\0';
-            rc->rec.diag_uri = odr_strdup(r->odr, diag->uri);
-        }
-        rc->rec.diag_message = odr_strdup_null(r->odr, diag->message);            
-        rc->rec.diag_details = odr_strdup_null(r->odr, diag->details);
-    }
-}
-
-static ZOOM_record record_cache_lookup(ZOOM_resultset r, int pos,
-                                       const char *syntax,
-                                       const char *elementSetName)
-{
-    ZOOM_record_cache rc;
-    
-    for (rc = r->record_hash[record_hash(pos)]; rc; rc = rc->next)
-    {
-        if (pos == rc->pos)
-        {
-            if (strcmp_null(r->schema, rc->schema))
-                continue;
-            if (strcmp_null(elementSetName,rc->elementSetName))
-                continue;
-            if (strcmp_null(syntax, rc->syntax))
-                continue;
-            return &rc->rec;
-        }
-    }
-    return 0;
-}
-                                             
-static void handle_records(ZOOM_connection c, Z_Records *sr,
-                           int present_phase)
-{
-    ZOOM_resultset resultset;
-    int *start, *count;
-    const char *syntax = 0, *elementSetName = 0;
-
-    if (!c->tasks)
-        return ;
-    switch (c->tasks->which)
-    {
-    case ZOOM_TASK_SEARCH:
-        resultset = c->tasks->u.search.resultset;
-        start = &c->tasks->u.search.start;
-        count = &c->tasks->u.search.count;
-        syntax = c->tasks->u.search.syntax;
-        elementSetName = c->tasks->u.search.elementSetName;
-        break;
-    case ZOOM_TASK_RETRIEVE:
-        resultset = c->tasks->u.retrieve.resultset;        
-        start = &c->tasks->u.retrieve.start;
-        count = &c->tasks->u.retrieve.count;
-        syntax = c->tasks->u.retrieve.syntax;
-        elementSetName = c->tasks->u.retrieve.elementSetName;
-        break;
-    default:
-        return;
-    }
-    if (sr && sr->which == Z_Records_NSD)
-        response_default_diag(c, sr->u.nonSurrogateDiagnostic);
-    else if (sr && sr->which == Z_Records_multipleNSD)
-    {
-        if (sr->u.multipleNonSurDiagnostics->num_diagRecs >= 1)
-            response_diag(c, sr->u.multipleNonSurDiagnostics->diagRecs[0]);
-        else
-            set_ZOOM_error(c, ZOOM_ERROR_DECODE, 0);
-    }
-    else 
-    {
-        if (*count + *start > resultset->size)
-            *count = resultset->size - *start;
-        if (*count < 0)
-            *count = 0;
-        if (sr && sr->which == Z_Records_DBOSD)
-        {
-            int i;
-            NMEM nmem = odr_extract_mem(c->odr_in);
-            Z_NamePlusRecordList *p =
-                sr->u.databaseOrSurDiagnostics;
-            for (i = 0; i<p->num_records; i++)
-            {
-                record_cache_add(resultset, p->records[i], i + *start,
-                                 syntax, elementSetName,
-                                 elementSetName, 0);
-            }
-            *count -= i;
-            if (*count < 0)
-                *count = 0;
-            *start += i;
-            yaz_log(log_details, 
-                    "handle_records resultset=%p start=%d count=%d",
-                    resultset, *start, *count);
-
-            /* transfer our response to search_nmem .. we need it later */
-            nmem_transfer(odr_getmem(resultset->odr), nmem);
-            nmem_destroy(nmem);
-            if (present_phase && p->num_records == 0)
-            {
-                /* present response and we didn't get any records! */
-                Z_NamePlusRecord *myrec = 
-                    zget_surrogateDiagRec(
-                        resultset->odr, 0, 
-                        YAZ_BIB1_SYSTEM_ERROR_IN_PRESENTING_RECORDS,
-                        "ZOOM C generated. Present phase and no records");
-                record_cache_add(resultset, myrec, *start,
-                                 syntax, elementSetName, 0, 0);
-            }
-        }
-        else if (present_phase)
-        {
-            /* present response and we didn't get any records! */
-            Z_NamePlusRecord *myrec = 
-                zget_surrogateDiagRec(
-                    resultset->odr, 0,
-                    YAZ_BIB1_SYSTEM_ERROR_IN_PRESENTING_RECORDS,
-                    "ZOOM C generated: Present response and no records");
-            record_cache_add(resultset, myrec, *start, syntax, elementSetName,
-                             0, 0);
-        }
-    }
-}
-
-static void handle_present_response(ZOOM_connection c, Z_PresentResponse *pr)
-{
-    handle_records(c, pr->records, 1);
-}
-
-static void handle_queryExpressionTerm(ZOOM_options opt, const char *name,
-                                       Z_Term *term)
-{
-    switch (term->which)
-    {
-    case Z_Term_general:
-        ZOOM_options_setl(opt, name,
-                          (const char *)(term->u.general->buf), 
-                          term->u.general->len);
-        break;
-    case Z_Term_characterString:
-        ZOOM_options_set(opt, name, term->u.characterString);
-        break;
-    case Z_Term_numeric:
-        ZOOM_options_set_int(opt, name, *term->u.numeric);
-        break;
-    }
-}
-
-static void handle_queryExpression(ZOOM_options opt, const char *name,
-                                   Z_QueryExpression *exp)
-{
-    char opt_name[80];
-    
-    switch (exp->which)
-    {
-    case Z_QueryExpression_term:
-        if (exp->u.term && exp->u.term->queryTerm)
-        {
-            sprintf(opt_name, "%s.term", name);
-            handle_queryExpressionTerm(opt, opt_name, exp->u.term->queryTerm);
-        }
-        break;
-    case Z_QueryExpression_query:
-        break;
-    }
-}
-
-static void handle_search_result(ZOOM_connection c, ZOOM_resultset resultset,
-                                Z_OtherInformation *o)
-{
-    int i;
-    for (i = 0; o && i < o->num_elements; i++)
-    {
-        if (o->list[i]->which == Z_OtherInfo_externallyDefinedInfo)
-        {
-            Z_External *ext = o->list[i]->information.externallyDefinedInfo;
-            
-            if (ext->which == Z_External_searchResult1)
-            {
-                int j;
-                Z_SearchInfoReport *sr = ext->u.searchResult1;
-                
-                if (sr->num)
-                    ZOOM_options_set_int(
-                        resultset->options, "searchresult.size", sr->num);
-
-                for (j = 0; j < sr->num; j++)
-                {
-                    Z_SearchInfoReport_s *ent =
-                        ext->u.searchResult1->elements[j];
-                    char pref[80];
-                    
-                    sprintf(pref, "searchresult.%d", j);
-
-                    if (ent->subqueryId)
-                    {
-                        char opt_name[80];
-                        sprintf(opt_name, "%s.id", pref);
-                        ZOOM_options_set(resultset->options, opt_name,
-                                         ent->subqueryId);
-                    }
-                    if (ent->subqueryExpression)
-                    {
-                        char opt_name[80];
-                        sprintf(opt_name, "%s.subquery", pref);
-                        handle_queryExpression(resultset->options, opt_name,
-                                               ent->subqueryExpression);
-                    }
-                    if (ent->subqueryInterpretation)
-                    {
-                        char opt_name[80];
-                        sprintf(opt_name, "%s.interpretation", pref);
-                        handle_queryExpression(resultset->options, opt_name,
-                                               ent->subqueryInterpretation);
-                    }
-                    if (ent->subqueryRecommendation)
-                    {
-                        char opt_name[80];
-                        sprintf(opt_name, "%s.recommendation", pref);
-                        handle_queryExpression(resultset->options, opt_name,
-                                               ent->subqueryRecommendation);
-                    }
-                    if (ent->subqueryCount)
-                    {
-                        char opt_name[80];
-                        sprintf(opt_name, "%s.count", pref);
-                        ZOOM_options_set_int(resultset->options, opt_name,
-                                             *ent->subqueryCount);
-                    }                                             
-                }
-            }
-        }
-    }
-}
-
-static char *get_term_cstr(ODR odr, Z_Term *term) {
-
-    switch (term->which) {
-    case Z_Term_general:
-            return odr_strdupn(odr, (const char *) term->u.general->buf, (size_t) term->u.general->len);
-        break;
-    case Z_Term_characterString:
-        return odr_strdup(odr, term->u.characterString);
-    }
-    return 0;
-}
-
-static ZOOM_facet_field get_zoom_facet_field(ODR odr, Z_FacetField *facet) {
-    int term_index;
-    struct yaz_facet_attr attr_values;
-    ZOOM_facet_field facet_field = odr_malloc(odr, sizeof(*facet_field));
-    yaz_facet_attr_init(&attr_values);
-    yaz_facet_attr_get_z_attributes(facet->attributes, &attr_values);
-    facet_field->facet_name = odr_strdup(odr, attr_values.useattr);
-    facet_field->num_terms = facet->num_terms;
-    yaz_log(YLOG_DEBUG, "ZOOM_facet_field %s %d terms %d", attr_values.useattr, attr_values.limit, facet->num_terms);
-    facet_field->facet_terms = odr_malloc(odr, facet_field->num_terms * sizeof(*facet_field->facet_terms));
-    for (term_index = 0 ; term_index < facet->num_terms; term_index++) {
-        Z_FacetTerm *facetTerm = facet->terms[term_index];
-        facet_field->facet_terms[term_index].frequency = *facetTerm->count;
-        facet_field->facet_terms[term_index].term = get_term_cstr(odr, facetTerm->term);
-        yaz_log(YLOG_DEBUG, "    term[%d] %s %d",
-                term_index, facet_field->facet_terms[term_index].term, facet_field->facet_terms[term_index].frequency);
-    }
-    return facet_field;
-}
-
-static void handle_facet_result(ZOOM_connection c, ZOOM_resultset r,
-                                Z_OtherInformation *o)
-{
-    int i;
-    for (i = 0; o && i < o->num_elements; i++)
-    {
-        if (o->list[i]->which == Z_OtherInfo_externallyDefinedInfo)
-        {
-            Z_External *ext = o->list[i]->information.externallyDefinedInfo;
-            if (ext->which == Z_External_userFacets)
-            {
-                int j;
-                Z_FacetList *fl = ext->u.facetList;
-                r->num_facets   = fl->num;
-                yaz_log(YLOG_DEBUG, "Facets found: %d", fl->num);
-                r->facets       =  odr_malloc(r->odr, r->num_facets * sizeof(*r->facets));
-                r->facets_names =  odr_malloc(r->odr, r->num_facets * sizeof(*r->facets_names));
-                for (j = 0; j < fl->num; j++)
-                {
-                    r->facets[j] = get_zoom_facet_field(r->odr, fl->elements[j]);
-                    if (!r->facets[j])
-                        yaz_log(YLOG_DEBUG, "Facet field missing on index %d !", j);
-                    r->facets_names[j] = (char *) ZOOM_facet_field_name(r->facets[j]);
-                }
-            }
-        }
-    }
-}
-
-
-static void handle_search_response(ZOOM_connection c, Z_SearchResponse *sr)
-{
-    ZOOM_resultset resultset;
-    ZOOM_Event event;
-
-    if (!c->tasks || c->tasks->which != ZOOM_TASK_SEARCH)
-        return ;
-
-    event = ZOOM_Event_create(ZOOM_EVENT_RECV_SEARCH);
-    ZOOM_connection_put_event(c, event);
-
-    resultset = c->tasks->u.search.resultset;
-
-    if (sr->resultSetStatus)
-    {
-        ZOOM_options_set_int(resultset->options, "resultSetStatus",
-                             *sr->resultSetStatus);
-    }
-    if (sr->presentStatus)
-    {
-        ZOOM_options_set_int(resultset->options, "presentStatus",
-                             *sr->presentStatus);
-    }
-    handle_search_result(c, resultset, sr->additionalSearchInfo);
-
-    handle_facet_result(c, resultset, sr->additionalSearchInfo);
-
-    resultset->size = *sr->resultCount;
-    handle_records(c, sr->records, 0);
-}
-
-static void sort_response(ZOOM_connection c, Z_SortResponse *res)
-{
-    if (res->diagnostics && res->num_diagnostics > 0)
-        response_diag(c, res->diagnostics[0]);
-}
-
-static int scan_response(ZOOM_connection c, Z_ScanResponse *res)
-{
-    NMEM nmem = odr_extract_mem(c->odr_in);
-    ZOOM_scanset scan;
-
-    if (!c->tasks || c->tasks->which != ZOOM_TASK_SCAN)
-        return 0;
-    scan = c->tasks->u.scan.scan;
-
-    if (res->entries && res->entries->nonsurrogateDiagnostics)
-        response_diag(c, res->entries->nonsurrogateDiagnostics[0]);
-    scan->scan_response = res;
-    scan->srw_scan_response = 0;
-    nmem_transfer(odr_getmem(scan->odr), nmem);
-    if (res->stepSize)
-        ZOOM_options_set_int(scan->options, "stepSize", *res->stepSize);
-    if (res->positionOfTerm)
-        ZOOM_options_set_int(scan->options, "position", *res->positionOfTerm);
-    if (res->scanStatus)
-        ZOOM_options_set_int(scan->options, "scanStatus", *res->scanStatus);
-    if (res->numberOfEntriesReturned)
-        ZOOM_options_set_int(scan->options, "number",
-                             *res->numberOfEntriesReturned);
-    nmem_destroy(nmem);
-    return 1;
-}
-
-static zoom_ret send_sort(ZOOM_connection c,
-                          ZOOM_resultset resultset)
-{
-    if (c->error)
-        resultset->r_sort_spec = 0;
-    if (resultset->r_sort_spec)
-    {
-        Z_APDU *apdu = zget_APDU(c->odr_out, Z_APDU_sortRequest);
-        Z_SortRequest *req = apdu->u.sortRequest;
-        
-        req->num_inputResultSetNames = 1;
-        req->inputResultSetNames = (Z_InternationalString **)
-            odr_malloc(c->odr_out, sizeof(*req->inputResultSetNames));
-        req->inputResultSetNames[0] =
-            odr_strdup(c->odr_out, resultset->setname);
-        req->sortedResultSetName = odr_strdup(c->odr_out, resultset->setname);
-        req->sortSequence = resultset->r_sort_spec;
-        resultset->r_sort_spec = 0;
-        return send_APDU(c, apdu);
-    }
-    return zoom_complete;
-}
-
-static zoom_ret send_present(ZOOM_connection c)
-{
-    Z_APDU *apdu = 0;
-    Z_PresentRequest *req = 0;
-    int i = 0;
-    const char *syntax = 0;
-    const char *elementSetName = 0;
-    ZOOM_resultset  resultset;
-    int *start, *count;
-
-    if (!c->tasks)
-    {
-        yaz_log(log_details, "%p send_present no tasks", c);
-        return zoom_complete;
-    }
-    
-    switch (c->tasks->which)
-    {
-    case ZOOM_TASK_SEARCH:
-        resultset = c->tasks->u.search.resultset;
-        start = &c->tasks->u.search.start;
-        count = &c->tasks->u.search.count;
-        syntax = c->tasks->u.search.syntax;
-        elementSetName = c->tasks->u.search.elementSetName;
-        break;
-    case ZOOM_TASK_RETRIEVE:
-        resultset = c->tasks->u.retrieve.resultset;
-        start = &c->tasks->u.retrieve.start;
-        count = &c->tasks->u.retrieve.count;
-        syntax = c->tasks->u.retrieve.syntax;
-        elementSetName = c->tasks->u.retrieve.elementSetName;
-        break;
-    default:
-        return zoom_complete;
-    }
-    yaz_log(log_details, "%p send_present start=%d count=%d",
-            c, *start, *count);
-
-    if (*start < 0 || *count < 0 || *start + *count > resultset->size)
-    {
-        set_dset_error(c, YAZ_BIB1_PRESENT_REQUEST_OUT_OF_RANGE, "Bib-1",
-                       "", 0);
-    }
-    if (c->error)                  /* don't continue on error */
-        return zoom_complete;
-    yaz_log(log_details, "send_present resultset=%p start=%d count=%d",
-            resultset, *start, *count);
-
-    for (i = 0; i < *count; i++)
-    {
-        ZOOM_record rec =
-            record_cache_lookup(resultset, i + *start, syntax, elementSetName);
-        if (!rec)
-            break;
-        else
-        {
-            ZOOM_Event event = ZOOM_Event_create(ZOOM_EVENT_RECV_RECORD);
-            ZOOM_connection_put_event(c, event);
-        }
-    }
-    *start += i;
-    *count -= i;
-
-    if (*count == 0)
-    {
-        yaz_log(log_details, "%p send_present skip=%d no more to fetch", c, i);
-        return zoom_complete;
-    }
-
-    apdu = zget_APDU(c->odr_out, Z_APDU_presentRequest);
-    req = apdu->u.presentRequest;
-
-    if (i)
-        yaz_log(log_details, "%p send_present skip=%d", c, i);
-
-    *req->resultSetStartPoint = *start + 1;
-
-    if (resultset->step > 0 && resultset->step < *count)
-        *req->numberOfRecordsRequested = resultset->step;
-    else
-        *req->numberOfRecordsRequested = *count;
-    
-    if (*req->numberOfRecordsRequested + *start > resultset->size)
-        *req->numberOfRecordsRequested = resultset->size - *start;
-    assert(*req->numberOfRecordsRequested > 0);
-
-    if (syntax && *syntax)
-        req->preferredRecordSyntax =
-            zoom_yaz_str_to_z3950oid(c, CLASS_RECSYN, syntax);
-
-    if (resultset->schema && *resultset->schema)
-    {
-        Z_RecordComposition *compo = (Z_RecordComposition *)
-            odr_malloc(c->odr_out, sizeof(*compo));
-
-        req->recordComposition = compo;
-        compo->which = Z_RecordComp_complex;
-        compo->u.complex = (Z_CompSpec *)
-            odr_malloc(c->odr_out, sizeof(*compo->u.complex));
-        compo->u.complex->selectAlternativeSyntax = (bool_t *) 
-            odr_malloc(c->odr_out, sizeof(bool_t));
-        *compo->u.complex->selectAlternativeSyntax = 0;
-
-        compo->u.complex->generic = (Z_Specification *)
-            odr_malloc(c->odr_out, sizeof(*compo->u.complex->generic));
-
-        compo->u.complex->generic->which = Z_Schema_oid;
-        compo->u.complex->generic->schema.oid = (Odr_oid *)
-            zoom_yaz_str_to_z3950oid (c, CLASS_SCHEMA, resultset->schema);
-
-        if (!compo->u.complex->generic->schema.oid)
-        {
-            /* OID wasn't a schema! Try record syntax instead. */
-
-            compo->u.complex->generic->schema.oid = (Odr_oid *)
-                zoom_yaz_str_to_z3950oid (c, CLASS_RECSYN, resultset->schema);
-        }
-        if (elementSetName && *elementSetName)
-        {
-            compo->u.complex->generic->elementSpec = (Z_ElementSpec *)
-                odr_malloc(c->odr_out, sizeof(Z_ElementSpec));
-            compo->u.complex->generic->elementSpec->which =
-                Z_ElementSpec_elementSetName;
-            compo->u.complex->generic->elementSpec->u.elementSetName =
-                odr_strdup(c->odr_out, elementSetName);
-        }
-        else
-            compo->u.complex->generic->elementSpec = 0;
-        compo->u.complex->num_dbSpecific = 0;
-        compo->u.complex->dbSpecific = 0;
-        compo->u.complex->num_recordSyntax = 0;
-        compo->u.complex->recordSyntax = 0;
-    }
-    else if (elementSetName && *elementSetName)
-    {
-        Z_ElementSetNames *esn = (Z_ElementSetNames *)
-            odr_malloc(c->odr_out, sizeof(*esn));
-        Z_RecordComposition *compo = (Z_RecordComposition *)
-            odr_malloc(c->odr_out, sizeof(*compo));
-        
-        esn->which = Z_ElementSetNames_generic;
-        esn->u.generic = odr_strdup(c->odr_out, elementSetName);
-        compo->which = Z_RecordComp_simple;
-        compo->u.simple = esn;
-        req->recordComposition = compo;
-    }
-    req->resultSetId = odr_strdup(c->odr_out, resultset->setname);
-    return send_APDU(c, apdu);
-}
-
-ZOOM_API(ZOOM_scanset)
-    ZOOM_connection_scan(ZOOM_connection c, const char *start)
-{
-    ZOOM_scanset s;
-    ZOOM_query q = ZOOM_query_create();
-
-    ZOOM_query_prefix(q, start);
-
-    s = ZOOM_connection_scan1(c, q);
-    ZOOM_query_destroy(q);
-    return s;
-
-}
-
-ZOOM_API(ZOOM_scanset)
-    ZOOM_connection_scan1(ZOOM_connection c, ZOOM_query q)
-{
-    ZOOM_scanset scan = 0;
-
-    if (!q->z_query)
-        return 0;
-    scan = (ZOOM_scanset) xmalloc(sizeof(*scan));
-    scan->connection = c;
-    scan->odr = odr_createmem(ODR_DECODE);
-    scan->options = ZOOM_options_create_with_parent(c->options);
-    scan->refcount = 1;
-    scan->scan_response = 0;
-    scan->srw_scan_response = 0;
-
-    scan->query = q;
-    (q->refcount)++;
-    scan->databaseNames = set_DatabaseNames(c, c->options,
-                                            &scan->num_databaseNames,
-                                            scan->odr);
-
-    if (1)
-    {
-        ZOOM_task task = ZOOM_connection_add_task(c, ZOOM_TASK_SCAN);
-        task->u.scan.scan = scan;
-        
-        (scan->refcount)++;
-        if (!c->async)
-        {
-            while (ZOOM_event(1, &c))
-                ;
-        }
-    }
-    return scan;
-}
-
-ZOOM_API(void)
-    ZOOM_scanset_destroy(ZOOM_scanset scan)
-{
-    if (!scan)
-        return;
-    (scan->refcount)--;
-    if (scan->refcount == 0)
+    if (!scan)
+        return;
+    (scan->refcount)--;
+    if (scan->refcount == 0)
     {
         ZOOM_query_destroy(scan->query);
 
@@ -3051,7 +1192,7 @@ static zoom_ret send_package(ZOOM_connection c)
 {
     ZOOM_Event event;
 
-    yaz_log(log_details, "%p send_package", c);
+    yaz_log(c->log_details, "%p send_package", c);
     if (!c->tasks)
         return zoom_complete;
     assert (c->tasks->which == ZOOM_TASK_PACKAGE);
@@ -3062,126 +1203,8 @@ static zoom_ret send_package(ZOOM_connection c)
     c->buf_out = c->tasks->u.package->buf_out;
     c->len_out = c->tasks->u.package->len_out;
 
-    return do_write(c);
-}
-
-static zoom_ret ZOOM_connection_send_scan(ZOOM_connection c)
-{
-    ZOOM_scanset scan;
-    Z_APDU *apdu = zget_APDU(c->odr_out, Z_APDU_scanRequest);
-    Z_ScanRequest *req = apdu->u.scanRequest;
-
-    yaz_log(log_details, "%p send_scan", c);
-    if (!c->tasks)
-        return zoom_complete;
-    assert (c->tasks->which == ZOOM_TASK_SCAN);
-    scan = c->tasks->u.scan.scan;
-
-    /* Z39.50 scan can only carry RPN */
-    if (scan->query->z_query->which == Z_Query_type_1 ||
-        scan->query->z_query->which == Z_Query_type_101)
-    {
-        Z_RPNQuery *rpn = scan->query->z_query->u.type_1;
-        const char *cp = ZOOM_options_get(scan->options, "rpnCharset");
-        if (cp)
-        {
-            yaz_iconv_t cd = yaz_iconv_open(cp, "UTF-8");
-            if (cd)
-            {
-                rpn = yaz_copy_z_RPNQuery(rpn, c->odr_out);
-
-                yaz_query_charset_convert_rpnquery(
-                    rpn, c->odr_out, cd);
-                yaz_iconv_close(cd);
-            }
-        }
-        req->attributeSet = rpn->attributeSetId;
-        if (!req->attributeSet)
-            req->attributeSet = odr_oiddup(c->odr_out, yaz_oid_attset_bib_1);
-        if (rpn->RPNStructure->which == Z_RPNStructure_simple &&
-            rpn->RPNStructure->u.simple->which == Z_Operand_APT)
-        {
-            req->termListAndStartPoint =
-                rpn->RPNStructure->u.simple->u.attributesPlusTerm;
-        }
-        else
-        {
-            set_ZOOM_error(c, ZOOM_ERROR_INVALID_QUERY, 0);
-            return zoom_complete;
-        }
-    }
-    else
-    {
-        set_ZOOM_error(c, ZOOM_ERROR_UNSUPPORTED_QUERY, 0);
-        return zoom_complete;
-    }
-
-    *req->numberOfTermsRequested =
-        ZOOM_options_get_int(scan->options, "number", 20);
-
-    req->preferredPositionInResponse =
-        odr_intdup(c->odr_out,
-                   ZOOM_options_get_int(scan->options, "position", 1));
-
-    req->stepSize =
-        odr_intdup(c->odr_out,
-                   ZOOM_options_get_int(scan->options, "stepSize", 0));
-    
-    req->databaseNames = scan->databaseNames;
-    req->num_databaseNames = scan->num_databaseNames;
-
-    return send_APDU(c, apdu);
-}
-
-#if YAZ_HAVE_XML2
-static zoom_ret ZOOM_connection_srw_send_scan(ZOOM_connection c)
-{
-    ZOOM_scanset scan;
-    Z_SRW_PDU *sr = 0;
-    const char *option_val = 0;
-
-    if (!c->tasks)
-        return zoom_complete;
-    assert (c->tasks->which == ZOOM_TASK_SCAN);
-    scan = c->tasks->u.scan.scan;
-        
-    sr = ZOOM_srw_get_pdu(c, Z_SRW_scan_request);
-
-    /* SRU scan can only carry CQL and PQF */
-    if (scan->query->z_query->which == Z_Query_type_104)
-    {
-        sr->u.scan_request->query_type = Z_SRW_query_type_cql;
-        sr->u.scan_request->scanClause.cql = scan->query->query_string;
-    }
-    else if (scan->query->z_query->which == Z_Query_type_1
-             || scan->query->z_query->which == Z_Query_type_101)
-    {
-        sr->u.scan_request->query_type = Z_SRW_query_type_pqf;
-        sr->u.scan_request->scanClause.pqf = scan->query->query_string;
-    }
-    else
-    {
-        set_ZOOM_error(c, ZOOM_ERROR_UNSUPPORTED_QUERY, 0);
-        return zoom_complete;
-    }
-
-    sr->u.scan_request->maximumTerms = odr_intdup(
-        c->odr_out, ZOOM_options_get_int(scan->options, "number", 10));
-    
-    sr->u.scan_request->responsePosition = odr_intdup(
-        c->odr_out, ZOOM_options_get_int(scan->options, "position", 1));
-    
-    option_val = ZOOM_options_get(scan->options, "extraArgs");
-    yaz_encode_sru_extra(sr, c->odr_out, option_val);
-    return send_srw(c, sr);
-}
-#else
-static zoom_ret ZOOM_connection_srw_send_scan(ZOOM_connection c)
-{
-    return zoom_complete;
+    return ZOOM_send_buf(c);
 }
-#endif
-
 
 ZOOM_API(size_t)
     ZOOM_scanset_size(ZOOM_scanset scan)
@@ -3240,548 +1263,65 @@ static void ZOOM_scanset_term_x(ZOOM_scanset scan, size_t pos,
         Z_SRW_scanTerm *t = res->terms + pos;
         if (t)
         {
-            *value_term = t->value;
-            *value_len = strlen(*value_term);
-
-            if (t->displayTerm)
-                *disp_term = t->displayTerm;
-            else
-                *disp_term = t->value;
-            *disp_len = strlen(*disp_term);
-            *occ = t->numberOfRecords ? *t->numberOfRecords : 0;
-        }
-    }
-}
-
-ZOOM_API(const char *)
-    ZOOM_scanset_term(ZOOM_scanset scan, size_t pos,
-                      size_t *occ, size_t *len)
-{
-    const char *value_term = 0;
-    size_t value_len = 0;
-    const char *disp_term = 0;
-    size_t disp_len = 0;
-
-    ZOOM_scanset_term_x(scan, pos, occ, &value_term, &value_len,
-                        &disp_term, &disp_len);
-    
-    *len = value_len;
-    return value_term;
-}
-
-ZOOM_API(const char *)
-    ZOOM_scanset_display_term(ZOOM_scanset scan, size_t pos,
-                              size_t *occ, size_t *len)
-{
-    const char *value_term = 0;
-    size_t value_len = 0;
-    const char *disp_term = 0;
-    size_t disp_len = 0;
-
-    ZOOM_scanset_term_x(scan, pos, occ, &value_term, &value_len,
-                        &disp_term, &disp_len);
-    
-    *len = disp_len;
-    return disp_term;
-}
-
-ZOOM_API(const char *)
-    ZOOM_scanset_option_get(ZOOM_scanset scan, const char *key)
-{
-    return ZOOM_options_get(scan->options, key);
-}
-
-ZOOM_API(void)
-    ZOOM_scanset_option_set(ZOOM_scanset scan, const char *key,
-                            const char *val)
-{
-    ZOOM_options_set(scan->options, key, val);
-}
-
-static Z_APDU *create_es_package(ZOOM_package p, const Odr_oid *oid)
-{
-    const char *str;
-    Z_APDU *apdu = zget_APDU(p->odr_out, Z_APDU_extendedServicesRequest);
-    Z_ExtendedServicesRequest *req = apdu->u.extendedServicesRequest;
-    
-    str = ZOOM_options_get(p->options, "package-name");
-    if (str && *str)
-        req->packageName = odr_strdup(p->odr_out, str);
-    
-    str = ZOOM_options_get(p->options, "user-id");
-    if (str)
-        req->userId = odr_strdup_null(p->odr_out, str);
-    
-    req->packageType = odr_oiddup(p->odr_out, oid);
-
-    str = ZOOM_options_get(p->options, "function");
-    if (str)
-    {
-        if (!strcmp (str, "create"))
-            *req->function = Z_ExtendedServicesRequest_create;
-        if (!strcmp (str, "delete"))
-            *req->function = Z_ExtendedServicesRequest_delete;
-        if (!strcmp (str, "modify"))
-            *req->function = Z_ExtendedServicesRequest_modify;
-    }
-
-    str = ZOOM_options_get(p->options, "waitAction");
-    if (str)
-    {
-        if (!strcmp (str, "wait"))
-            *req->waitAction = Z_ExtendedServicesRequest_wait;
-        if (!strcmp (str, "waitIfPossible"))
-            *req->waitAction = Z_ExtendedServicesRequest_waitIfPossible;
-        if (!strcmp (str, "dontWait"))
-            *req->waitAction = Z_ExtendedServicesRequest_dontWait;
-        if (!strcmp (str, "dontReturnPackage"))
-            *req->waitAction = Z_ExtendedServicesRequest_dontReturnPackage;
-    }
-    return apdu;
-}
-
-static const char *ill_array_lookup(void *clientData, const char *idx)
-{
-    ZOOM_package p = (ZOOM_package) clientData;
-    return ZOOM_options_get(p->options, idx+4);
-}
-
-static Z_External *encode_ill_request(ZOOM_package p)
-{
-    ODR out = p->odr_out;
-    ILL_Request *req;
-    Z_External *r = 0;
-    struct ill_get_ctl ctl;
-        
-    ctl.odr = p->odr_out;
-    ctl.clientData = p;
-    ctl.f = ill_array_lookup;
-        
-    req = ill_get_ILLRequest(&ctl, "ill", 0);
-        
-    if (!ill_Request(out, &req, 0, 0))
-    {
-        int ill_request_size;
-        char *ill_request_buf = odr_getbuf(out, &ill_request_size, 0);
-        if (ill_request_buf)
-            odr_setbuf(out, ill_request_buf, ill_request_size, 1);
-        return 0;
-    }
-    else
-    {
-        int illRequest_size = 0;
-        char *illRequest_buf = odr_getbuf(out, &illRequest_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;
-        r->descriptor = 0;
-        r->which = Z_External_single;
-                
-        r->u.single_ASN1_type =
-            odr_create_Odr_oct(out,
-                               (unsigned char *)illRequest_buf,
-                               illRequest_size);
-    }
-    return r;
-}
-
-static Z_ItemOrder *encode_item_order(ZOOM_package p)
-{
-    Z_ItemOrder *req = (Z_ItemOrder *) odr_malloc(p->odr_out, sizeof(*req));
-    const char *str;
-    int len;
-    
-    req->which = Z_IOItemOrder_esRequest;
-    req->u.esRequest = (Z_IORequest *) 
-        odr_malloc(p->odr_out,sizeof(Z_IORequest));
-
-    /* to keep part ... */
-    req->u.esRequest->toKeep = (Z_IOOriginPartToKeep *)
-        odr_malloc(p->odr_out,sizeof(Z_IOOriginPartToKeep));
-    req->u.esRequest->toKeep->supplDescription = 0;
-    req->u.esRequest->toKeep->contact = (Z_IOContact *)
-        odr_malloc(p->odr_out, sizeof(*req->u.esRequest->toKeep->contact));
-        
-    str = ZOOM_options_get(p->options, "contact-name");
-    req->u.esRequest->toKeep->contact->name =
-        odr_strdup_null(p->odr_out, str);
-        
-    str = ZOOM_options_get(p->options, "contact-phone");
-    req->u.esRequest->toKeep->contact->phone =
-        odr_strdup_null(p->odr_out, str);
-        
-    str = ZOOM_options_get(p->options, "contact-email");
-    req->u.esRequest->toKeep->contact->email =
-        odr_strdup_null(p->odr_out, str);
-        
-    req->u.esRequest->toKeep->addlBilling = 0;
-        
-    /* not to keep part ... */
-    req->u.esRequest->notToKeep = (Z_IOOriginPartNotToKeep *)
-        odr_malloc(p->odr_out,sizeof(Z_IOOriginPartNotToKeep));
-        
-    str = ZOOM_options_get(p->options, "itemorder-setname");
-    if (!str)
-        str = "default";
-
-    if (!*str) 
-        req->u.esRequest->notToKeep->resultSetItem = 0;
-    else
-    {
-        req->u.esRequest->notToKeep->resultSetItem = (Z_IOResultSetItem *)
-            odr_malloc(p->odr_out, sizeof(Z_IOResultSetItem));
-
-        req->u.esRequest->notToKeep->resultSetItem->resultSetId =
-            odr_strdup(p->odr_out, str);
-        req->u.esRequest->notToKeep->resultSetItem->item =
-            odr_intdup(p->odr_out, 0);
-        
-        str = ZOOM_options_get(p->options, "itemorder-item");
-        *req->u.esRequest->notToKeep->resultSetItem->item =
-            (str ? atoi(str) : 1);
-    }
-
-    str = ZOOM_options_getl(p->options, "doc", &len);
-    if (str)
-    {
-        req->u.esRequest->notToKeep->itemRequest =
-            z_ext_record_xml(p->odr_out, str, len);
-    }
-    else
-        req->u.esRequest->notToKeep->itemRequest = encode_ill_request(p);
-    
-    return req;
-}
-
-Z_APDU *create_admin_package(ZOOM_package p, int type, 
-                             Z_ESAdminOriginPartToKeep **toKeepP,
-                             Z_ESAdminOriginPartNotToKeep **notToKeepP)
-{
-    Z_APDU *apdu = create_es_package(p, yaz_oid_extserv_admin);
-    if (apdu)
-    {
-        Z_ESAdminOriginPartToKeep  *toKeep;
-        Z_ESAdminOriginPartNotToKeep  *notToKeep;
-        Z_External *r = (Z_External *) odr_malloc(p->odr_out, sizeof(*r));
-        const char *first_db = "Default";
-        int num_db;
-        char **db = set_DatabaseNames(p->connection, p->options, &num_db,
-                                      p->odr_out);
-        if (num_db > 0)
-            first_db = db[0];
-            
-        r->direct_reference = odr_oiddup(p->odr_out, yaz_oid_extserv_admin);
-        r->descriptor = 0;
-        r->indirect_reference = 0;
-        r->which = Z_External_ESAdmin;
-        
-        r->u.adminService = (Z_Admin *)
-            odr_malloc(p->odr_out, sizeof(*r->u.adminService));
-        r->u.adminService->which = Z_Admin_esRequest;
-        r->u.adminService->u.esRequest = (Z_AdminEsRequest *)
-            odr_malloc(p->odr_out, sizeof(*r->u.adminService->u.esRequest));
-        
-        toKeep = r->u.adminService->u.esRequest->toKeep =
-            (Z_ESAdminOriginPartToKeep *) 
-            odr_malloc(p->odr_out, sizeof(*r->u.adminService->u.esRequest->toKeep));
-        toKeep->which = type;
-        toKeep->databaseName = odr_strdup(p->odr_out, first_db);
-        toKeep->u.create = odr_nullval();
-        apdu->u.extendedServicesRequest->taskSpecificParameters = r;
-        
-        r->u.adminService->u.esRequest->notToKeep = notToKeep =
-            (Z_ESAdminOriginPartNotToKeep *)
-            odr_malloc(p->odr_out,
-                       sizeof(*r->u.adminService->u.esRequest->notToKeep));
-        notToKeep->which = Z_ESAdminOriginPartNotToKeep_recordsWillFollow;
-        notToKeep->u.recordsWillFollow = odr_nullval();
-        if (toKeepP)
-            *toKeepP = toKeep;
-        if (notToKeepP)
-            *notToKeepP = notToKeep;
-    }
-    return apdu;
-}
-
-static Z_APDU *create_xmlupdate_package(ZOOM_package p)
-{
-    Z_APDU *apdu = create_es_package(p, yaz_oid_extserv_xml_es);
-    Z_ExtendedServicesRequest *req = apdu->u.extendedServicesRequest;
-    Z_External *ext = (Z_External *) odr_malloc(p->odr_out, sizeof(*ext));
-    int len;
-    const char *doc = ZOOM_options_getl(p->options, "doc", &len);
-
-    if (!doc)
-    {
-        doc = "";
-        len = 0;
-    }
-
-    req->taskSpecificParameters = ext;
-    ext->direct_reference = req->packageType;
-    ext->descriptor = 0;
-    ext->indirect_reference = 0;
-    
-    ext->which = Z_External_octet;
-    ext->u.single_ASN1_type =
-        odr_create_Odr_oct(p->odr_out, (const unsigned char *) doc, len);
-    return apdu;
-}
-
-static Z_APDU *create_update_package(ZOOM_package p)
-{
-    Z_APDU *apdu = 0;
-    const char *first_db = "Default";
-    int num_db;
-    char **db = set_DatabaseNames(p->connection, p->options, &num_db, p->odr_out);
-    const char *action = ZOOM_options_get(p->options, "action");
-    int recordIdOpaque_len;
-    const char *recordIdOpaque = ZOOM_options_getl(p->options, "recordIdOpaque",
-        &recordIdOpaque_len);
-    const char *recordIdNumber = ZOOM_options_get(p->options, "recordIdNumber");
-    int record_len;
-    const char *record_buf = ZOOM_options_getl(p->options, "record",
-        &record_len);
-    int recordOpaque_len;
-    const char *recordOpaque_buf = ZOOM_options_getl(p->options, "recordOpaque",
-        &recordOpaque_len);
-    const char *syntax_str = ZOOM_options_get(p->options, "syntax");
-    const char *version = ZOOM_options_get(p->options, "updateVersion");
-
-    const char *correlationInfo_note =
-        ZOOM_options_get(p->options, "correlationInfo.note");
-    const char *correlationInfo_id =
-        ZOOM_options_get(p->options, "correlationInfo.id");
-    int action_no = -1;
-    Odr_oid *syntax_oid = 0;
-    const Odr_oid *package_oid = yaz_oid_extserv_database_update;
-
-    if (!version)
-        version = "3";
-    if (!syntax_str)
-        syntax_str = "xml";
-    if (!record_buf && !recordOpaque_buf)
-    {
-        record_buf = "void";
-        record_len = 4;
-        syntax_str = "SUTRS";
-    }
-
-    if (syntax_str)
-    {
-        syntax_oid = yaz_string_to_oid_odr(yaz_oid_std(),
-                                           CLASS_RECSYN, syntax_str,
-                                           p->odr_out);
-    }
-    if (!syntax_oid)
-        return 0;
-
-    if (num_db > 0)
-        first_db = db[0];
-    
-    switch(*version)
-    {
-    case '1':
-        package_oid = yaz_oid_extserv_database_update_first_version;
-        /* old update does not support specialUpdate */
-        if (!action)
-            action = "recordInsert";
-        break;
-    case '2':
-        if (!action)
-            action = "specialUpdate";
-        package_oid = yaz_oid_extserv_database_update_second_version;
-        break;
-    case '3':
-        if (!action)
-            action = "specialUpdate";
-        package_oid = yaz_oid_extserv_database_update;
-        break;
-    default:
-        return 0;
-    }
-    
-    if (!strcmp(action, "recordInsert"))
-        action_no = Z_IUOriginPartToKeep_recordInsert;
-    else if (!strcmp(action, "recordReplace"))
-        action_no = Z_IUOriginPartToKeep_recordReplace;
-    else if (!strcmp(action, "recordDelete"))
-        action_no = Z_IUOriginPartToKeep_recordDelete;
-    else if (!strcmp(action, "elementUpdate"))
-        action_no = Z_IUOriginPartToKeep_elementUpdate;
-    else if (!strcmp(action, "specialUpdate"))
-        action_no = Z_IUOriginPartToKeep_specialUpdate;
-    else
-        return 0;
-
-    apdu = create_es_package(p, package_oid);
-    if (apdu)
-    {
-        Z_IUOriginPartToKeep *toKeep;
-        Z_IUSuppliedRecords *notToKeep;
-        Z_External *r = (Z_External *)
-            odr_malloc(p->odr_out, sizeof(*r));
-        const char *elementSetName =
-            ZOOM_options_get(p->options, "elementSetName");
-        
-        apdu->u.extendedServicesRequest->taskSpecificParameters = r;
-        
-        r->direct_reference = odr_oiddup(p->odr_out, package_oid);
-        r->descriptor = 0;
-        r->which = Z_External_update;
-        r->indirect_reference = 0;
-        r->u.update = (Z_IUUpdate *)
-            odr_malloc(p->odr_out, sizeof(*r->u.update));
-        
-        r->u.update->which = Z_IUUpdate_esRequest;
-        r->u.update->u.esRequest = (Z_IUUpdateEsRequest *)
-            odr_malloc(p->odr_out, sizeof(*r->u.update->u.esRequest));
-        toKeep = r->u.update->u.esRequest->toKeep = 
-            (Z_IUOriginPartToKeep *)
-            odr_malloc(p->odr_out, sizeof(*toKeep));
-        
-        toKeep->databaseName = odr_strdup(p->odr_out, first_db);
-        toKeep->schema = 0;
-        
-        toKeep->elementSetName = odr_strdup_null(p->odr_out, elementSetName);
-            
-        toKeep->actionQualifier = 0;
-        toKeep->action = odr_intdup(p->odr_out, action_no);
-        
-        notToKeep = r->u.update->u.esRequest->notToKeep = 
-            (Z_IUSuppliedRecords *)
-            odr_malloc(p->odr_out, sizeof(*notToKeep));
-        notToKeep->num = 1;
-        notToKeep->elements = (Z_IUSuppliedRecords_elem **)
-            odr_malloc(p->odr_out, sizeof(*notToKeep->elements));
-        notToKeep->elements[0] = (Z_IUSuppliedRecords_elem *)
-            odr_malloc(p->odr_out, sizeof(**notToKeep->elements));
-        notToKeep->elements[0]->which = Z_IUSuppliedRecords_elem_opaque;
-        if (recordIdOpaque)
-        {
-            notToKeep->elements[0]->u.opaque = 
-                odr_create_Odr_oct(p->odr_out,
-                                   (const unsigned char *) recordIdOpaque,
-                                   recordIdOpaque_len);
-        }
-        else if (recordIdNumber)
-        {
-            notToKeep->elements[0]->which = Z_IUSuppliedRecords_elem_number;
-            
-            notToKeep->elements[0]->u.number =
-                odr_intdup(p->odr_out, atoi(recordIdNumber));
-        }
-        else
-            notToKeep->elements[0]->u.opaque = 0;
-        notToKeep->elements[0]->supplementalId = 0;
-        if (correlationInfo_note || correlationInfo_id)
-        {
-            Z_IUCorrelationInfo *ci;
-            ci = notToKeep->elements[0]->correlationInfo =
-                (Z_IUCorrelationInfo *) odr_malloc(p->odr_out, sizeof(*ci));
-            ci->note = odr_strdup_null(p->odr_out, correlationInfo_note);
-            ci->id = correlationInfo_id ?
-                odr_intdup(p->odr_out, atoi(correlationInfo_id)) : 0;
-        }
-        else
-            notToKeep->elements[0]->correlationInfo = 0;
-        if (recordOpaque_buf)
-        {
-            notToKeep->elements[0]->record =
-                z_ext_record_oid_any(p->odr_out, syntax_oid,
-                                 recordOpaque_buf, recordOpaque_len);
-        }
-        else
-        {
-            notToKeep->elements[0]->record =
-                z_ext_record_oid(p->odr_out, syntax_oid,
-                                 record_buf, record_len);
+            *value_term = t->value;
+            *value_len = strlen(*value_term);
+
+            if (t->displayTerm)
+                *disp_term = t->displayTerm;
+            else
+                *disp_term = t->value;
+            *disp_len = strlen(*disp_term);
+            *occ = t->numberOfRecords ? *t->numberOfRecords : 0;
         }
     }
-    if (0 && apdu)
-    {
-        ODR print = odr_createmem(ODR_PRINT);
+}
 
-        z_APDU(print, &apdu, 0, 0);
-        odr_destroy(print);
-    }
-    return apdu;
+ZOOM_API(const char *)
+    ZOOM_scanset_term(ZOOM_scanset scan, size_t pos,
+                      size_t *occ, size_t *len)
+{
+    const char *value_term = 0;
+    size_t value_len = 0;
+    const char *disp_term = 0;
+    size_t disp_len = 0;
+
+    ZOOM_scanset_term_x(scan, pos, occ, &value_term, &value_len,
+                        &disp_term, &disp_len);
+    
+    *len = value_len;
+    return value_term;
 }
 
-ZOOM_API(void)
-    ZOOM_package_send(ZOOM_package p, const char *type)
+ZOOM_API(const char *)
+    ZOOM_scanset_display_term(ZOOM_scanset scan, size_t pos,
+                              size_t *occ, size_t *len)
 {
-    Z_APDU *apdu = 0;
-    ZOOM_connection c;
-    if (!p)
-        return;
-    c = p->connection;
-    odr_reset(p->odr_out);
-    xfree(p->buf_out);
-    p->buf_out = 0;
-    if (!strcmp(type, "itemorder"))
-    {
-        apdu = create_es_package(p, yaz_oid_extserv_item_order);
-        if (apdu)
-        {
-            Z_External *r = (Z_External *) odr_malloc(p->odr_out, sizeof(*r));
-            
-            r->direct_reference = 
-                odr_oiddup(p->odr_out, yaz_oid_extserv_item_order);
-            r->descriptor = 0;
-            r->which = Z_External_itemOrder;
-            r->indirect_reference = 0;
-            r->u.itemOrder = encode_item_order(p);
-
-            apdu->u.extendedServicesRequest->taskSpecificParameters = r;
-        }
-    }
-    else if (!strcmp(type, "create"))  /* create database */
-    {
-        apdu = create_admin_package(p, Z_ESAdminOriginPartToKeep_create,
-                                    0, 0);
-    }   
-    else if (!strcmp(type, "drop"))  /* drop database */
-    {
-        apdu = create_admin_package(p, Z_ESAdminOriginPartToKeep_drop,
-                                    0, 0);
-    }
-    else if (!strcmp(type, "commit"))  /* commit changes */
-    {
-        apdu = create_admin_package(p, Z_ESAdminOriginPartToKeep_commit,
-                                    0, 0);
-    }
-    else if (!strcmp(type, "update")) /* update record(s) */
-    {
-        apdu = create_update_package(p);
-    }
-    else if (!strcmp(type, "xmlupdate"))
-    {
-        apdu = create_xmlupdate_package(p);
-    }
-    if (apdu)
-    {
-        if (encode_APDU(p->connection, apdu, p->odr_out) == 0)
-        {
-            char *buf;
+    const char *value_term = 0;
+    size_t value_len = 0;
+    const char *disp_term = 0;
+    size_t disp_len = 0;
 
-            ZOOM_task task = ZOOM_connection_add_task(c, ZOOM_TASK_PACKAGE);
-            task->u.package = p;
-            buf = odr_getbuf(p->odr_out, &p->len_out, 0);
-            p->buf_out = (char *) xmalloc(p->len_out);
-            memcpy(p->buf_out, buf, p->len_out);
-            
-            (p->refcount)++;
-            if (!c->async)
-            {
-                while (ZOOM_event(1, &c))
-                    ;
-            }
-        }
-    }
+    ZOOM_scanset_term_x(scan, pos, occ, &value_term, &value_len,
+                        &disp_term, &disp_len);
+    
+    *len = disp_len;
+    return disp_term;
 }
 
+ZOOM_API(const char *)
+    ZOOM_scanset_option_get(ZOOM_scanset scan, const char *key)
+{
+    return ZOOM_options_get(scan->options, key);
+}
+
+ZOOM_API(void)
+    ZOOM_scanset_option_set(ZOOM_scanset scan, const char *key,
+                            const char *val)
+{
+    ZOOM_options_set(scan->options, key, val);
+}
+
+
 ZOOM_API(ZOOM_package)
     ZOOM_connection_package(ZOOM_connection c, ZOOM_options options)
 {
@@ -3846,18 +1386,18 @@ ZOOM_API(int)
 
     if (!task)
         return 0;
-    yaz_log(log_details, "%p ZOOM_connection_exec_task type=%d run=%d",
+    yaz_log(c->log_details, "%p ZOOM_connection_exec_task type=%d run=%d",
             c, task->which, task->running);
     if (c->error != ZOOM_ERROR_NONE)
     {
-        yaz_log(log_details, "%p ZOOM_connection_exec_task "
+        yaz_log(c->log_details, "%p ZOOM_connection_exec_task "
                 "removing tasks because of error = %d", c, c->error);
         ZOOM_connection_remove_tasks(c);
         return 0;
     }
     if (task->running)
     {
-        yaz_log(log_details, "%p ZOOM_connection_exec_task "
+        yaz_log(c->log_details, "%p ZOOM_connection_exec_task "
                 "task already running", c);
         return 0;
     }
@@ -3871,13 +1411,13 @@ ZOOM_API(int)
             if (c->proto == PROTO_HTTP)
                 ret = ZOOM_connection_srw_send_search(c);
             else
-                ret = ZOOM_connection_send_search(c);
+                ret = ZOOM_connection_Z3950_send_search(c);
             break;
         case ZOOM_TASK_RETRIEVE:
             if (c->proto == PROTO_HTTP)
                 ret = ZOOM_connection_srw_send_search(c);
             else
-                ret = send_present(c);
+                ret = send_Z3950_present(c);
             break;
         case ZOOM_TASK_CONNECT:
             ret = do_connect(c);
@@ -3886,460 +1426,36 @@ ZOOM_API(int)
             if (c->proto == PROTO_HTTP)
                 ret = ZOOM_connection_srw_send_scan(c);
             else
-                ret = ZOOM_connection_send_scan(c);
+                ret = ZOOM_connection_Z3950_send_scan(c);
             break;
         case ZOOM_TASK_PACKAGE:
             ret = send_package(c);
             break;
         case ZOOM_TASK_SORT:
             c->tasks->u.sort.resultset->r_sort_spec = 
-                c->tasks->u.sort.q->sort_spec;
-            ret = send_sort(c, c->tasks->u.sort.resultset);
+                ZOOM_query_get_sortspec(c->tasks->u.sort.q);
+            ret = send_Z3950_sort(c, c->tasks->u.sort.resultset);
             break;
         }
     }
     else
     {
-        yaz_log(log_details, "%p ZOOM_connection_exec_task "
+        yaz_log(c->log_details, "%p ZOOM_connection_exec_task "
                 "remove tasks because no connection exist", c);
         ZOOM_connection_remove_tasks(c);
     }
     if (ret == zoom_complete)
     {
-        yaz_log(log_details, "%p ZOOM_connection_exec_task "
+        yaz_log(c->log_details, "%p ZOOM_connection_exec_task "
                 "task removed (complete)", c);
         ZOOM_connection_remove_task(c);
         return 0;
     }
-    yaz_log(log_details, "%p ZOOM_connection_exec_task "
+    yaz_log(c->log_details, "%p ZOOM_connection_exec_task "
             "task pending", c);
     return 1;
 }
 
-static zoom_ret send_sort_present(ZOOM_connection c)
-{
-    zoom_ret r = zoom_complete;
-
-    if (c->tasks && c->tasks->which == ZOOM_TASK_SEARCH)
-        r = send_sort(c, c->tasks->u.search.resultset);
-    if (r == zoom_complete)
-        r = send_present(c);
-    return r;
-}
-
-static int es_response_taskpackage_update(ZOOM_connection c,
-               Z_IUUpdateTaskPackage *utp)
-{
-       if (utp && utp->targetPart)
-       {
-               Z_IUTargetPart *targetPart = utp->targetPart;
-               switch ( *targetPart->updateStatus ) {
-                       case Z_IUTargetPart_success:
-                               ZOOM_options_set(c->tasks->u.package->options,"updateStatus", "success");
-                               break;
-                       case Z_IUTargetPart_partial:
-                               ZOOM_options_set(c->tasks->u.package->options,"updateStatus", "partial");
-                               break;
-                       case Z_IUTargetPart_failure:
-                               ZOOM_options_set(c->tasks->u.package->options,"updateStatus", "failure");
-                               if (targetPart->globalDiagnostics && targetPart->num_globalDiagnostics > 0)
-                                       response_diag(c, targetPart->globalDiagnostics[0]);
-                               break;
-               }
-               // NOTE: Individual record status, surrogate diagnostics, and supplemental diagnostics ARE NOT REPORTED.
-       }
-    return 1;
-}
-
-static int es_response_taskpackage(ZOOM_connection c,
-                                   Z_TaskPackage *taskPackage)
-{
-       // targetReference
-       Odr_oct *id = taskPackage->targetReference;
-       if (id)
-               ZOOM_options_setl(c->tasks->u.package->options,
-                                                       "targetReference", (char*) id->buf, id->len);
-       
-       // taskStatus
-       switch ( *taskPackage->taskStatus ) {
-               case Z_TaskPackage_pending:
-                       ZOOM_options_set(c->tasks->u.package->options,"taskStatus", "pending");
-                       break;
-               case Z_TaskPackage_active:
-                       ZOOM_options_set(c->tasks->u.package->options,"taskStatus", "active");
-                       break;
-               case Z_TaskPackage_complete:
-                       ZOOM_options_set(c->tasks->u.package->options,"taskStatus", "complete");
-                       break;
-               case Z_TaskPackage_aborted:
-                       ZOOM_options_set(c->tasks->u.package->options,"taskStatus", "aborted");
-                       if ( taskPackage->num_packageDiagnostics && taskPackage->packageDiagnostics )
-                               response_diag(c, taskPackage->packageDiagnostics[0]);
-                       break;
-       }
-       
-       // taskSpecificParameters
-       // NOTE: Only Update implemented, no others.
-       if ( taskPackage->taskSpecificParameters->which == Z_External_update ) {
-                       Z_IUUpdateTaskPackage *utp = taskPackage->taskSpecificParameters->u.update->u.taskPackage;
-                       es_response_taskpackage_update(c, utp);
-       }
-       return 1;
-}
-
-
-static int es_response(ZOOM_connection c,
-                       Z_ExtendedServicesResponse *res)
-{
-    if (!c->tasks || c->tasks->which != ZOOM_TASK_PACKAGE)
-        return 0;
-    switch (*res->operationStatus) {
-        case Z_ExtendedServicesResponse_done:
-            ZOOM_options_set(c->tasks->u.package->options,"operationStatus", "done");
-            break;
-        case Z_ExtendedServicesResponse_accepted:
-            ZOOM_options_set(c->tasks->u.package->options,"operationStatus", "accepted");
-            break;
-        case Z_ExtendedServicesResponse_failure:
-            ZOOM_options_set(c->tasks->u.package->options,"operationStatus", "failure");
-            if (res->diagnostics && res->num_diagnostics > 0)
-                response_diag(c, res->diagnostics[0]);
-            break;
-    }
-    if (res->taskPackage &&
-        res->taskPackage->which == Z_External_extendedService)
-    {
-        Z_TaskPackage *taskPackage = res->taskPackage->u.extendedService;
-        es_response_taskpackage(c, taskPackage);
-    }
-    if (res->taskPackage && 
-        res->taskPackage->which == Z_External_octet)
-    {
-        Odr_oct *doc = res->taskPackage->u.octet_aligned;
-        ZOOM_options_setl(c->tasks->u.package->options,
-                          "xmlUpdateDoc", (char*) doc->buf, doc->len);
-    }
-    return 1;
-}
-
-static void interpret_init_diag(ZOOM_connection c,
-                                Z_DiagnosticFormat *diag)
-{
-    if (diag->num > 0)
-    {
-        Z_DiagnosticFormat_s *ds = diag->elements[0];
-        if (ds->which == Z_DiagnosticFormat_s_defaultDiagRec)
-            response_default_diag(c, ds->u.defaultDiagRec);
-    }
-}
-
-
-static void interpret_otherinformation_field(ZOOM_connection c,
-                                             Z_OtherInformation *ui)
-{
-    int i;
-    for (i = 0; i < ui->num_elements; i++)
-    {
-        Z_OtherInformationUnit *unit = ui->list[i];
-        if (unit->which == Z_OtherInfo_externallyDefinedInfo &&
-            unit->information.externallyDefinedInfo &&
-            unit->information.externallyDefinedInfo->which ==
-            Z_External_diag1) 
-        {
-            interpret_init_diag(c, unit->information.externallyDefinedInfo->u.diag1);
-        } 
-    }
-}
-
-
-static void set_init_option(const char *name, void *clientData) {
-    ZOOM_connection c = (ZOOM_connection) clientData;
-    char buf[80];
-
-    sprintf(buf, "init_opt_%.70s", name);
-    ZOOM_connection_option_set(c, buf, "1");
-}
-
-
-static void recv_apdu(ZOOM_connection c, Z_APDU *apdu)
-{
-    Z_InitResponse *initrs;
-    
-    ZOOM_connection_set_mask(c, 0);
-    yaz_log(log_details, "%p recv_apdu apdu->which=%d", c, apdu->which);
-    switch(apdu->which)
-    {
-    case Z_APDU_initResponse:
-        yaz_log(log_api, "%p recv_apdu: Received Init response", c);
-        initrs = apdu->u.initResponse;
-        ZOOM_connection_option_set(c, "serverImplementationId",
-                                   initrs->implementationId ?
-                                   initrs->implementationId : "");
-        ZOOM_connection_option_set(c, "serverImplementationName",
-                                   initrs->implementationName ?
-                                   initrs->implementationName : "");
-        ZOOM_connection_option_set(c, "serverImplementationVersion",
-                                   initrs->implementationVersion ?
-                                   initrs->implementationVersion : "");
-        /* Set the three old options too, for old applications */
-        ZOOM_connection_option_set(c, "targetImplementationId",
-                                   initrs->implementationId ?
-                                   initrs->implementationId : "");
-        ZOOM_connection_option_set(c, "targetImplementationName",
-                                   initrs->implementationName ?
-                                   initrs->implementationName : "");
-        ZOOM_connection_option_set(c, "targetImplementationVersion",
-                                   initrs->implementationVersion ?
-                                   initrs->implementationVersion : "");
-
-        /* Make initrs->options available as ZOOM-level options */
-        yaz_init_opt_decode(initrs->options, set_init_option, (void*) c);
-
-        if (!*initrs->result)
-        {
-            Z_External *uif = initrs->userInformationField;
-
-            set_ZOOM_error(c, ZOOM_ERROR_INIT, 0); /* default error */
-
-            if (uif && uif->which == Z_External_userInfo1)
-                interpret_otherinformation_field(c, uif->u.userInfo1);
-        }
-        else
-        {
-            char *cookie =
-                yaz_oi_get_string_oid(&apdu->u.initResponse->otherInfo,
-                                      yaz_oid_userinfo_cookie, 1, 0);
-            xfree(c->cookie_in);
-            c->cookie_in = 0;
-            if (cookie)
-                c->cookie_in = xstrdup(cookie);
-            if (ODR_MASK_GET(initrs->options, Z_Options_namedResultSets) &&
-                ODR_MASK_GET(initrs->protocolVersion, Z_ProtocolVersion_3))
-                c->support_named_resultsets = 1;
-            if (c->tasks)
-            {
-                assert(c->tasks->which == ZOOM_TASK_CONNECT);
-                ZOOM_connection_remove_task(c);
-            }
-            ZOOM_connection_exec_task(c);
-        }
-        if (ODR_MASK_GET(initrs->options, Z_Options_negotiationModel))
-        {
-            NMEM tmpmem = nmem_create();
-            Z_CharSetandLanguageNegotiation *p =
-                yaz_get_charneg_record(initrs->otherInfo);
-            
-            if (p)
-            {
-                char *charset = NULL, *lang = NULL;
-                int sel;
-                
-                yaz_get_response_charneg(tmpmem, p, &charset, &lang, &sel);
-                yaz_log(log_details, "%p recv_apdu target accepted: "
-                        "charset %s, language %s, select %d",
-                        c,
-                        charset ? charset : "none", lang ? lang : "none", sel);
-                if (charset)
-                    ZOOM_connection_option_set(c, "negotiation-charset",
-                                               charset);
-                if (lang)
-                    ZOOM_connection_option_set(c, "negotiation-lang",
-                                               lang);
-
-                ZOOM_connection_option_set(
-                    c,  "negotiation-charset-in-effect-for-records",
-                    (sel != 0) ? "1" : "0");
-                nmem_destroy(tmpmem);
-            }
-        }       
-        break;
-    case Z_APDU_searchResponse:
-        yaz_log(log_api, "%p recv_apdu Search response", c);
-        handle_search_response(c, apdu->u.searchResponse);
-        if (send_sort_present(c) == zoom_complete)
-            ZOOM_connection_remove_task(c);
-        break;
-    case Z_APDU_presentResponse:
-        yaz_log(log_api, "%p recv_apdu Present response", c);
-        handle_present_response(c, apdu->u.presentResponse);
-        if (send_present(c) == zoom_complete)
-            ZOOM_connection_remove_task(c);
-        break;
-    case Z_APDU_sortResponse:
-        yaz_log(log_api, "%p recv_apdu Sort response", c);
-        sort_response(c, apdu->u.sortResponse);
-        if (send_present(c) == zoom_complete)
-            ZOOM_connection_remove_task(c);
-        break;
-    case Z_APDU_scanResponse:
-        yaz_log(log_api, "%p recv_apdu Scan response", c);
-        scan_response(c, apdu->u.scanResponse);
-        ZOOM_connection_remove_task(c);
-        break;
-    case Z_APDU_extendedServicesResponse:
-        yaz_log(log_api, "%p recv_apdu Extended Services response", c);
-        es_response(c, apdu->u.extendedServicesResponse);
-        ZOOM_connection_remove_task(c);
-        break;
-    case Z_APDU_close:
-        yaz_log(log_api, "%p recv_apdu Close PDU", c);
-        if (!ZOOM_test_reconnect(c))
-        {
-            set_ZOOM_error(c, ZOOM_ERROR_CONNECTION_LOST, c->host_port);
-            do_close(c);
-        }
-        break;
-    default:
-        yaz_log(log_api, "%p Received unknown PDU", c);
-        set_ZOOM_error(c, ZOOM_ERROR_DECODE, 0);
-        do_close(c);
-    }
-}
-
-#if YAZ_HAVE_XML2
-static zoom_ret handle_srw_response(ZOOM_connection c,
-                                    Z_SRW_searchRetrieveResponse *res)
-{
-    ZOOM_resultset resultset = 0;
-    int i;
-    NMEM nmem;
-    ZOOM_Event event;
-    int *start, *count;
-    const char *syntax, *elementSetName;
-
-    if (!c->tasks)
-        return zoom_complete;
-
-    switch(c->tasks->which)
-    {
-    case ZOOM_TASK_SEARCH:
-        resultset = c->tasks->u.search.resultset;
-        start = &c->tasks->u.search.start;
-        count = &c->tasks->u.search.count;
-        syntax = c->tasks->u.search.syntax;
-        elementSetName = c->tasks->u.search.elementSetName;        
-
-        if (!c->tasks->u.search.recv_search_fired)
-        {
-            event = ZOOM_Event_create(ZOOM_EVENT_RECV_SEARCH);
-            ZOOM_connection_put_event(c, event);
-            c->tasks->u.search.recv_search_fired = 1;
-        }
-        break;
-    case ZOOM_TASK_RETRIEVE:
-        resultset = c->tasks->u.retrieve.resultset;
-        start = &c->tasks->u.retrieve.start;
-        count = &c->tasks->u.retrieve.count;
-        syntax = c->tasks->u.retrieve.syntax;
-        elementSetName = c->tasks->u.retrieve.elementSetName;
-        break;
-    default:
-        return zoom_complete;
-    }
-
-    resultset->size = 0;
-
-    if (res->resultSetId)
-        ZOOM_resultset_option_set(resultset, "resultSetId", res->resultSetId);
-
-    yaz_log(log_details, "%p handle_srw_response got SRW response OK", c);
-
-    if (res->num_diagnostics > 0)
-    {
-        set_SRU_error(c, &res->diagnostics[0]);
-    }
-    else
-    {
-        if (res->numberOfRecords)
-            resultset->size = *res->numberOfRecords;
-        for (i = 0; i<res->num_records; i++)
-        {
-            int pos;
-            Z_SRW_record *sru_rec;
-            Z_SRW_diagnostic *diag = 0;
-            int num_diag;
-            
-            Z_NamePlusRecord *npr = (Z_NamePlusRecord *)
-                odr_malloc(c->odr_in, sizeof(Z_NamePlusRecord));
-            
-            if (res->records[i].recordPosition && 
-                *res->records[i].recordPosition > 0)
-                pos = *res->records[i].recordPosition - 1;
-            else
-                pos = *start + i;
-            
-            sru_rec = &res->records[i];
-            
-            npr->databaseName = 0;
-            npr->which = Z_NamePlusRecord_databaseRecord;
-            npr->u.databaseRecord = (Z_External *)
-                odr_malloc(c->odr_in, sizeof(Z_External));
-            npr->u.databaseRecord->descriptor = 0;
-            npr->u.databaseRecord->direct_reference =
-                odr_oiddup(c->odr_in, yaz_oid_recsyn_xml);
-            npr->u.databaseRecord->which = Z_External_octet;
-            
-            npr->u.databaseRecord->u.octet_aligned = (Odr_oct *)
-                odr_malloc(c->odr_in, sizeof(Odr_oct));
-            npr->u.databaseRecord->u.octet_aligned->buf = (unsigned char*)
-                sru_rec->recordData_buf;
-            npr->u.databaseRecord->u.octet_aligned->len = 
-                npr->u.databaseRecord->u.octet_aligned->size = 
-                sru_rec->recordData_len;
-            
-            if (sru_rec->recordSchema 
-                && !strcmp(sru_rec->recordSchema,
-                           "info:srw/schema/1/diagnostics-v1.1"))
-            {
-                sru_decode_surrogate_diagnostics(sru_rec->recordData_buf,
-                                                 sru_rec->recordData_len,
-                                                 &diag, &num_diag,
-                                                 resultset->odr);
-            }
-            record_cache_add(resultset, npr, pos, syntax, elementSetName,
-                             sru_rec->recordSchema, diag);
-        }
-        *count -= i;
-        *start += i;
-        if (*count + *start > resultset->size)
-            *count = resultset->size - *start;
-        if (*count < 0)
-            *count = 0;
-        
-        nmem = odr_extract_mem(c->odr_in);
-        nmem_transfer(odr_getmem(resultset->odr), nmem);
-        nmem_destroy(nmem);
-
-        if (*count > 0)
-            return ZOOM_connection_srw_send_search(c);
-    }
-    return zoom_complete;
-}
-#endif
-
-#if YAZ_HAVE_XML2
-static void handle_srw_scan_response(ZOOM_connection c,
-                                     Z_SRW_scanResponse *res)
-{
-    NMEM nmem = odr_extract_mem(c->odr_in);
-    ZOOM_scanset scan;
-
-    if (!c->tasks || c->tasks->which != ZOOM_TASK_SCAN)
-        return;
-    scan = c->tasks->u.scan.scan;
-
-    if (res->num_diagnostics > 0)
-        set_SRU_error(c, &res->diagnostics[0]);
-
-    scan->scan_response = 0;
-    scan->srw_scan_response = res;
-    nmem_transfer(odr_getmem(scan->odr), nmem);
-
-    ZOOM_options_set_int(scan->options, "number", res->num_terms);
-    nmem_destroy(nmem);
-}
-#endif
-
 #if YAZ_HAVE_XML2
 static Z_GDU *get_HTTP_Request_url(ODR odr, const char *url)
 {
@@ -4367,7 +1483,7 @@ static Z_GDU *get_HTTP_Request_url(ODR odr, const char *url)
     return p;
 }
 
-static zoom_ret send_SRW_redirect(ZOOM_connection c, const char *uri,
+static zoom_ret send_HTTP_redirect(ZOOM_connection c, const char *uri,
                                   Z_HTTP_Response *cookie_hres)
 {
     struct Z_HTTP_Header *h;
@@ -4416,9 +1532,18 @@ static zoom_ret send_SRW_redirect(ZOOM_connection c, const char *uri,
     c->buf_out = odr_getbuf(c->odr_out, &c->len_out, 0);
 
     odr_reset(c->odr_out);
-    return do_write(c);
+    return ZOOM_send_buf(c);
 }
 
+#if YAZ_HAVE_XML2
+void ZOOM_set_HTTP_error(ZOOM_connection c, int error,
+                         const char *addinfo, const char *addinfo2)
+{
+    ZOOM_set_dset_error(c, error, "HTTP", addinfo, addinfo2);
+}
+#endif
+
+
 static void handle_http(ZOOM_connection c, Z_HTTP_Response *hres)
 {
     zoom_ret cret = zoom_complete;
@@ -4429,7 +1554,7 @@ static void handle_http(ZOOM_connection c, Z_HTTP_Response *hres)
     const char *location;
 
     ZOOM_connection_set_mask(c, 0);
-    yaz_log(log_details, "%p handle_http", c);
+    yaz_log(c->log_details, "%p handle_http", c);
     
     if ((hres->code == 301 || hres->code == 302) && c->sru_mode == zoom_sru_get
         && (location = z_HTTP_header_lookup(hres->headers, "Location")))
@@ -4437,79 +1562,42 @@ static void handle_http(ZOOM_connection c, Z_HTTP_Response *hres)
         c->no_redirects++;
         if (c->no_redirects > 10)
         {
-            set_HTTP_error(c, hres->code, 0, 0);
+            ZOOM_set_HTTP_error(c, hres->code, 0, 0);
             c->no_redirects = 0;
-            do_close(c);
+            ZOOM_connection_close(c);
         }
         else
         {
             /* since redirect may change host we just reconnect. A smarter
                implementation might check whether it's the same server */
             do_connect_host(c, location, 0);
-            send_SRW_redirect(c, location, hres);
+            send_HTTP_redirect(c, location, hres);
             /* we're OK for now. Operation is not really complete */
             ret = 0;
             cret = zoom_pending;
         }
     }
     else 
-    {   /* not redirect (normal response) */
-        if (!yaz_srw_check_content_type(hres))
-            addinfo = "content-type";
-        else
-        {
-            Z_SOAP *soap_package = 0;
-            ODR o = c->odr_in;
-            Z_SOAP_Handler soap_handlers[2] = {
-                {YAZ_XMLNS_SRU_v1_1, 0, (Z_SOAP_fun) yaz_srw_codec},
-                {0, 0, 0}
-            };
-            ret = z_soap_codec(o, &soap_package,
-                               &hres->content_buf, &hres->content_len,
-                               soap_handlers);
-            if (!ret && soap_package->which == Z_SOAP_generic &&
-                soap_package->u.generic->no == 0)
-            {
-                Z_SRW_PDU *sr = (Z_SRW_PDU*) soap_package->u.generic->p;
-                
-                ZOOM_options_set(c->options, "sru_version", sr->srw_version);
-                ZOOM_options_setl(c->options, "sru_extra_response_data",
-                                  sr->extraResponseData_buf, sr->extraResponseData_len);
-                if (sr->which == Z_SRW_searchRetrieve_response)
-                    cret = handle_srw_response(c, sr->u.response);
-                else if (sr->which == Z_SRW_scan_response)
-                    handle_srw_scan_response(c, sr->u.scan_response);
-                else
-                    ret = -1;
-            }
-            else if (!ret && (soap_package->which == Z_SOAP_fault
-                              || soap_package->which == Z_SOAP_error))
-            {
-                set_HTTP_error(c, hres->code,
-                               soap_package->u.fault->fault_code,
-                               soap_package->u.fault->fault_string);
-            }
-            else
-                ret = -1;
-        }   
+    {  
+        ret = ZOOM_handle_sru(c, hres, &cret);
         if (ret == 0)
         {
             if (c->no_redirects) /* end of redirect. change hosts again */
-                do_close(c);
+                ZOOM_connection_close(c);
         }
         c->no_redirects = 0;
     }
     if (ret)
     {
         if (hres->code != 200)
-            set_HTTP_error(c, hres->code, 0, 0);
+            ZOOM_set_HTTP_error(c, hres->code, 0, 0);
         else
-            set_ZOOM_error(c, ZOOM_ERROR_DECODE, addinfo);
-        do_close(c);
+            ZOOM_set_error(c, ZOOM_ERROR_DECODE, addinfo);
+        ZOOM_connection_close(c);
     }
     if (cret == zoom_complete)
     {
-        yaz_log(YLOG_LOG, "removing tasks in handle_http");
+        yaz_log(c->log_details, "removing tasks in handle_http");
         ZOOM_connection_remove_task(c);
     }
     {
@@ -4528,7 +1616,7 @@ static void handle_http(ZOOM_connection c, Z_HTTP_Response *hres)
         }
         if (must_close)
         {
-            do_close(c);
+            ZOOM_connection_close(c);
             if (c->tasks)
             {
                 c->tasks->running = 0;
@@ -4550,15 +1638,15 @@ static int do_read(ZOOM_connection c)
     
     r = cs_get(c->cs, &c->buf_in, &c->len_in);
     more = cs_more(c->cs);
-    yaz_log(log_details, "%p do_read len=%d more=%d", c, r, more);
+    yaz_log(c->log_details, "%p do_read len=%d more=%d", c, r, more);
     if (r == 1)
         return 0;
     if (r <= 0)
     {
         if (!ZOOM_test_reconnect(c))
         {
-            set_ZOOM_error(c, ZOOM_ERROR_CONNECTION_LOST, c->host_port);
-            do_close(c);
+            ZOOM_set_error(c, ZOOM_ERROR_CONNECTION_LOST, c->host_port);
+            ZOOM_connection_close(c);
         }
     }
     else
@@ -4581,28 +1669,28 @@ static int do_read(ZOOM_connection c)
                     "ODR code %d:%d element=%s offset=%d",
                     err, x, element ? element : "<unknown>",
                     odr_offset(c->odr_in));
-            set_ZOOM_error(c, ZOOM_ERROR_DECODE, msg);
-            if (log_api)
+            ZOOM_set_error(c, ZOOM_ERROR_DECODE, msg);
+            if (c->log_api)
             {
                 FILE *ber_file = yaz_log_file();
                 if (ber_file)
                     odr_dumpBER(ber_file, c->buf_in, r);
             }
-            do_close(c);
+            ZOOM_connection_close(c);
         }
         else
         {
             if (c->odr_print)
                 z_GDU(c->odr_print, &gdu, 0, 0);
             if (gdu->which == Z_GDU_Z3950)
-                recv_apdu(c, gdu->u.z3950);
+                ZOOM_handle_Z3950_apdu(c, gdu->u.z3950);
             else if (gdu->which == Z_GDU_HTTP_Response)
             {
 #if YAZ_HAVE_XML2
                 handle_http(c, gdu->u.HTTP_Response);
 #else
-                set_ZOOM_error(c, ZOOM_ERROR_DECODE, 0);
-                do_close(c);
+                ZOOM_set_error(c, ZOOM_ERROR_DECODE, 0);
+                ZOOM_connection_close(c);
 #endif
             }
         }
@@ -4619,19 +1707,19 @@ static zoom_ret do_write_ex(ZOOM_connection c, char *buf_out, int len_out)
     event = ZOOM_Event_create(ZOOM_EVENT_SEND_DATA);
     ZOOM_connection_put_event(c, event);
 
-    yaz_log(log_details, "%p do_write_ex len=%d", c, len_out);
+    yaz_log(c->log_details, "%p do_write_ex len=%d", c, len_out);
     if ((r = cs_put(c->cs, buf_out, len_out)) < 0)
     {
-        yaz_log(log_details, "%p do_write_ex write failed", c);
+        yaz_log(c->log_details, "%p do_write_ex write failed", c);
         if (ZOOM_test_reconnect(c))
         {
             return zoom_pending;
         }
         if (c->state == STATE_CONNECTING)
-            set_ZOOM_error(c, ZOOM_ERROR_CONNECT, c->host_port);
+            ZOOM_set_error(c, ZOOM_ERROR_CONNECT, c->host_port);
         else
-            set_ZOOM_error(c, ZOOM_ERROR_CONNECTION_LOST, c->host_port);
-        do_close(c);
+            ZOOM_set_error(c, ZOOM_ERROR_CONNECTION_LOST, c->host_port);
+        ZOOM_connection_close(c);
         return zoom_complete;
     }
     else if (r == 1)
@@ -4642,19 +1730,19 @@ static zoom_ret do_write_ex(ZOOM_connection c, char *buf_out, int len_out)
         if (c->cs->io_pending & CS_WANT_READ)
             mask += ZOOM_SELECT_READ;
         ZOOM_connection_set_mask(c, mask);
-        yaz_log(log_details, "%p do_write_ex write incomplete mask=%d",
+        yaz_log(c->log_details, "%p do_write_ex write incomplete mask=%d",
                 c, c->mask);
     }
     else
     {
         ZOOM_connection_set_mask(c, ZOOM_SELECT_READ|ZOOM_SELECT_EXCEPT);
-        yaz_log(log_details, "%p do_write_ex write complete mask=%d",
+        yaz_log(c->log_details, "%p do_write_ex write complete mask=%d",
                 c, c->mask);
     }
     return zoom_pending;
 }
 
-static zoom_ret do_write(ZOOM_connection c)
+zoom_ret ZOOM_send_buf(ZOOM_connection c)
 {
     return do_write_ex(c, c->buf_out, c->len_out);
 }
@@ -4808,20 +1896,20 @@ static void ZOOM_connection_do_io(ZOOM_connection c, int mask)
 {
     ZOOM_Event event = 0;
     int r = cs_look(c->cs);
-    yaz_log(log_details, "%p ZOOM_connection_do_io mask=%d cs_look=%d",
+    yaz_log(c->log_details, "%p ZOOM_connection_do_io mask=%d cs_look=%d",
             c, mask, r);
     
     if (r == CS_NONE)
     {
         event = ZOOM_Event_create(ZOOM_EVENT_CONNECT);
-        set_ZOOM_error(c, ZOOM_ERROR_CONNECT, c->host_port);
-        do_close(c);
+        ZOOM_set_error(c, ZOOM_ERROR_CONNECT, c->host_port);
+        ZOOM_connection_close(c);
         ZOOM_connection_put_event(c, event);
     }
     else if (r == CS_CONNECT)
     {
         int ret = ret = cs_rcvconnect(c->cs);
-        yaz_log(log_details, "%p ZOOM_connection_do_io "
+        yaz_log(c->log_details, "%p ZOOM_connection_do_io "
                 "cs_rcvconnect returned %d", c, ret);
         if (ret == 1)
         {
@@ -4840,7 +1928,7 @@ static void ZOOM_connection_do_io(ZOOM_connection c, int mask)
             ZOOM_connection_put_event(c, event);
             get_cert(c);
             if (c->proto == PROTO_Z3950)
-                ZOOM_connection_send_init(c);
+                ZOOM_connection_Z3950_send_init(c);
             else
             {
                 /* no init request for SRW .. */
@@ -4853,8 +1941,8 @@ static void ZOOM_connection_do_io(ZOOM_connection c, int mask)
         }
         else
         {
-            set_ZOOM_error(c, ZOOM_ERROR_CONNECT, c->host_port);
-            do_close(c);
+            ZOOM_set_error(c, ZOOM_ERROR_CONNECT, c->host_port);
+            ZOOM_connection_close(c);
         }
     }
     else
@@ -4863,15 +1951,15 @@ static void ZOOM_connection_do_io(ZOOM_connection c, int mask)
         {
             if (!ZOOM_test_reconnect(c))
             {
-                set_ZOOM_error(c, ZOOM_ERROR_CONNECTION_LOST, c->host_port);
-                do_close(c);
+                ZOOM_set_error(c, ZOOM_ERROR_CONNECTION_LOST, c->host_port);
+                ZOOM_connection_close(c);
             }
             return;
         }
         if (mask & ZOOM_SELECT_READ)
             do_read(c);
         if (c->cs && (mask & ZOOM_SELECT_WRITE))
-            do_write(c);
+            ZOOM_send_buf(c);
     }
 }
 
@@ -4884,77 +1972,14 @@ ZOOM_API(int)
 }
 
 
-static void cql2pqf_wrbuf_puts(const char *buf, void *client_data)
-{
-    WRBUF wrbuf = (WRBUF) client_data;
-    wrbuf_puts(wrbuf, buf);
-}
-
-/*
- * Returns an xmalloc()d string containing RPN that corresponds to the
- * CQL passed in.  On error, sets the Connection object's error state
- * and returns a null pointer.
- * ### We could cache CQL parser and/or transformer in Connection.
- */
-static char *cql2pqf(ZOOM_connection c, const char *cql)
-{
-    CQL_parser parser;
-    int error;
-    const char *cqlfile;
-    cql_transform_t trans;
-    char *result = 0;
-
-    parser = cql_parser_create();
-    if ((error = cql_parser_string(parser, cql)) != 0) {
-        cql_parser_destroy(parser);
-        set_ZOOM_error(c, ZOOM_ERROR_CQL_PARSE, cql);
-        return 0;
-    }
-
-    cqlfile = ZOOM_connection_option_get(c, "cqlfile");
-    if (cqlfile == 0) 
-    {
-        set_ZOOM_error(c, ZOOM_ERROR_CQL_TRANSFORM, "no CQL transform file");
-    }
-    else if ((trans = cql_transform_open_fname(cqlfile)) == 0) 
-    {
-        char buf[512];        
-        sprintf(buf, "can't open CQL transform file '%.200s': %.200s",
-                cqlfile, strerror(errno));
-        set_ZOOM_error(c, ZOOM_ERROR_CQL_TRANSFORM, buf);
-    }
-    else 
-    {
-        WRBUF wrbuf_result = wrbuf_alloc();
-        error = cql_transform(trans, cql_parser_result(parser),
-                              cql2pqf_wrbuf_puts, wrbuf_result);
-        if (error != 0) {
-            char buf[512];
-            const char *addinfo;
-            error = cql_transform_error(trans, &addinfo);
-            sprintf(buf, "%.200s (addinfo=%.200s)", 
-                    cql_strerror(error), addinfo);
-            set_ZOOM_error(c, ZOOM_ERROR_CQL_TRANSFORM, buf);
-        }
-        else
-        {
-            result = xstrdup(wrbuf_cstr(wrbuf_result));
-        }
-        cql_transform_close(trans);
-        wrbuf_destroy(wrbuf_result);
-    }
-    cql_parser_destroy(parser);
-    return result;
-}
-
 ZOOM_API(int) ZOOM_connection_fire_event_timeout(ZOOM_connection c)
 {
     if (c->mask)
     {
         ZOOM_Event event = ZOOM_Event_create(ZOOM_EVENT_TIMEOUT);
         /* timeout and this connection was waiting */
-        set_ZOOM_error(c, ZOOM_ERROR_TIMEOUT, 0);
-        do_close(c);
+        ZOOM_set_error(c, ZOOM_ERROR_TIMEOUT, 0);
+        ZOOM_connection_close(c);
         ZOOM_connection_put_event(c, event);
     }
     return 0;
@@ -4988,7 +2013,7 @@ ZOOM_API(int)
 {
     int i;
 
-    yaz_log(log_details, "ZOOM_process_event(no=%d,cs=%p)", no, cs);
+    yaz_log(log_details0, "ZOOM_process_event(no=%d,cs=%p)", no, cs);
     
     for (i = 0; i<no; i++)
     {
@@ -5036,7 +2061,11 @@ ZOOM_API(int) ZOOM_connection_get_timeout(ZOOM_connection c)
 
 ZOOM_API(void) ZOOM_connection_close(ZOOM_connection c)
 {
-    do_close(c);
+    if (c->cs)
+        cs_close(c->cs);
+    c->cs = 0;
+    ZOOM_connection_set_mask(c, 0);
+    c->state = STATE_IDLE;
 }
 
 /*
diff --git a/src/zoom-event.c b/src/zoom-event.c
new file mode 100644 (file)
index 0000000..bc1c84e
--- /dev/null
@@ -0,0 +1,115 @@
+/* This file is part of the YAZ toolkit.
+ * Copyright (C) 1995-2010 Index Data
+ * See the file LICENSE for details.
+ */
+/**
+ * \file zoom-event.c
+ * \brief Implements ZOOM Event stuff
+ */
+
+#include <assert.h>
+#include <string.h>
+#include <errno.h>
+#include "zoom-p.h"
+
+ZOOM_API(const char *) ZOOM_get_event_str(int event)
+{
+    static const char *ar[] = {
+        "NONE",
+        "CONNECT",
+        "SEND_DATA",
+        "RECV_DATA",
+        "TIMEOUT",
+        "UNKNOWN",
+        "SEND_APDU",
+        "RECV_APDU",
+        "RECV_RECORD",
+        "RECV_SEARCH",
+        "END"
+    };
+    return ar[event];
+}
+
+struct ZOOM_Event_p {
+    int kind;
+    ZOOM_Event next;
+    ZOOM_Event prev;
+};
+
+ZOOM_Event ZOOM_Event_create(int kind)
+{
+    ZOOM_Event event = (ZOOM_Event) xmalloc(sizeof(*event));
+    event->kind = kind;
+    event->next = 0;
+    event->prev = 0;
+    return event;
+}
+
+
+ZOOM_Event ZOOM_connection_get_event(ZOOM_connection c)
+{
+    ZOOM_Event event = c->m_queue_front;
+    if (!event)
+    {
+        c->last_event = ZOOM_EVENT_NONE;
+        return 0;
+    }
+    assert(c->m_queue_back);
+    c->m_queue_front = event->prev;
+    if (c->m_queue_front)
+    {
+        assert(c->m_queue_back);
+        c->m_queue_front->next = 0;
+    }
+    else
+        c->m_queue_back = 0;
+    c->last_event = event->kind;
+    return event;
+}
+
+void ZOOM_connection_put_event(ZOOM_connection c, ZOOM_Event event)
+{
+    if (c->m_queue_back)
+    {
+        c->m_queue_back->prev = event;
+        assert(c->m_queue_front);
+    }
+    else
+    {
+        assert(!c->m_queue_front);
+        c->m_queue_front = event;
+    }
+    event->next = c->m_queue_back;
+    event->prev = 0;
+    c->m_queue_back = event;
+}
+
+void ZOOM_Event_destroy(ZOOM_Event event)
+{
+    xfree(event);
+}
+
+void ZOOM_connection_remove_events(ZOOM_connection c)
+{
+    ZOOM_Event event;
+    while ((event = ZOOM_connection_get_event(c)))
+        ZOOM_Event_destroy(event);
+}
+
+ZOOM_API(int) ZOOM_connection_peek_event(ZOOM_connection c)
+{
+    ZOOM_Event event = c->m_queue_front;
+
+    return event ? event->kind : ZOOM_EVENT_NONE;
+}
+
+
+/*
+ * Local variables:
+ * c-basic-offset: 4
+ * c-file-style: "Stroustrup"
+ * indent-tabs-mode: nil
+ * End:
+ * vim: shiftwidth=4 tabstop=8 expandtab
+ */
+
index 0f5d9da..602f57d 100644 (file)
 
 #include <yaz/xmalloc.h>
 
+struct ZOOM_options_entry {
+    char *name;
+    char *value;
+    int len;                  /* of `value', which may contain NULs */
+    struct ZOOM_options_entry *next;
+};
+
+struct ZOOM_options_p {
+    int refcount;
+    void *callback_handle;
+    ZOOM_options_callback callback_func;
+    struct ZOOM_options_entry *entries;
+    ZOOM_options parent1;
+    ZOOM_options parent2;
+};
+
 static void set_value(struct ZOOM_options_entry **e,
                       const char *value, int len)
 {
index 51125b5..b0a39fc 100644 (file)
@@ -33,7 +33,6 @@
 #include <yaz/comstack.h>
 #include <yaz/wrbuf.h>
 #include <yaz/zoom.h>
-#include <yaz/sortspec.h>
 #include <yaz/srw.h>
 #include <yaz/mutex.h>
 
 
 typedef struct ZOOM_Event_p *ZOOM_Event;
 
-struct ZOOM_query_p {
-    Z_Query *z_query;
-    Z_SortKeySpecList *sort_spec;
-    int refcount;
-    ODR odr;
-    char *query_string;
-};
-
 typedef enum {
     zoom_sru_error,
     zoom_sru_soap,
     zoom_sru_get,
-    zoom_sru_post
+    zoom_sru_post,
+    zoom_sru_solr
 } zoom_sru_mode;
     
 
@@ -116,6 +108,9 @@ struct ZOOM_connection_p {
     ZOOM_Event m_queue_back;
     zoom_sru_mode sru_mode;
     int no_redirects; /* 0 for no redirects. >0 for number of redirects */
+
+    int log_details;
+    int log_api;
 };
 
 #if ZOOM_RESULT_LISTS
@@ -125,23 +120,6 @@ struct ZOOM_resultsets_p {
 };
 #endif
 
-struct ZOOM_options_entry {
-    char *name;
-    char *value;
-    int len;                  /* of `value', which may contain NULs */
-    struct ZOOM_options_entry *next;
-};
-
-struct ZOOM_options_p {
-    int refcount;
-    void *callback_handle;
-    ZOOM_options_callback callback_func;
-    struct ZOOM_options_entry *entries;
-    ZOOM_options parent1;
-    ZOOM_options parent2;
-};
-
-
 typedef struct ZOOM_record_cache_p *ZOOM_record_cache;
 
 #define RECORD_HASH_SIZE  131
@@ -174,23 +152,6 @@ struct ZOOM_resultset_p {
     char **facets_names;
 };
 
-struct ZOOM_record_p {
-    ODR odr;
-#if SHPTR
-    struct WRBUF_shptr *record_wrbuf;
-#else
-    WRBUF wrbuf;
-#endif
-
-    Z_NamePlusRecord *npr;
-    const char *schema;
-
-    const char *diag_uri;
-    const char *diag_message;
-    const char *diag_details;
-    const char *diag_set;
-};
-
 struct facet_term_p {
     char *term;
     int frequency;
@@ -202,16 +163,6 @@ struct ZOOM_facet_field_p {
     struct facet_term_p *facet_terms;
 };
 
-
-struct ZOOM_record_cache_p {
-    struct ZOOM_record_p rec;
-    char *elementSetName;
-    char *syntax;
-    char *schema;
-    int pos;
-    ZOOM_record_cache next;
-};
-
 struct ZOOM_scanset_p {
     int refcount;
     ODR odr;
@@ -271,14 +222,65 @@ struct ZOOM_task_p {
     ZOOM_task next;
 };
 
-struct ZOOM_Event_p {
-    int kind;
-    ZOOM_Event next;
-    ZOOM_Event prev;
-};
+typedef enum {
+    zoom_pending,
+    zoom_complete
+} zoom_ret;
 
 void ZOOM_options_addref (ZOOM_options opt);
 
+void ZOOM_handle_Z3950_apdu(ZOOM_connection c, Z_APDU *apdu);
+
+void ZOOM_set_dset_error(ZOOM_connection c, int error,
+                         const char *dset,
+                         const char *addinfo, const char *addinfo2);
+
+void ZOOM_set_error(ZOOM_connection c, int error, const char *addinfo);
+
+ZOOM_Event ZOOM_Event_create(int kind);
+void ZOOM_connection_put_event(ZOOM_connection c, ZOOM_Event event);
+
+zoom_ret ZOOM_connection_Z3950_send_search(ZOOM_connection c);
+zoom_ret send_Z3950_present(ZOOM_connection c);
+zoom_ret ZOOM_connection_Z3950_send_scan(ZOOM_connection c);
+zoom_ret ZOOM_send_buf(ZOOM_connection c);
+zoom_ret send_Z3950_sort(ZOOM_connection c, ZOOM_resultset resultset);
+char **ZOOM_connection_get_databases(ZOOM_connection con, ZOOM_options options,
+                                     int *num, ODR odr);
+zoom_ret ZOOM_connection_Z3950_send_init(ZOOM_connection c);
+
+ZOOM_task ZOOM_connection_add_task(ZOOM_connection c, int which);
+void ZOOM_connection_remove_task(ZOOM_connection c);
+int ZOOM_test_reconnect(ZOOM_connection c);
+
+ZOOM_record ZOOM_record_cache_lookup(ZOOM_resultset r, int pos,
+                                     const char *syntax,
+                                     const char *elementSetName);
+void ZOOM_record_cache_add(ZOOM_resultset r, Z_NamePlusRecord *npr, 
+                           int pos,
+                           const char *syntax, const char *elementSetName,
+                           const char *schema,
+                           Z_SRW_diagnostic *diag);
+
+Z_Query *ZOOM_query_get_Z_Query(ZOOM_query s);
+Z_SortKeySpecList *ZOOM_query_get_sortspec(ZOOM_query s);
+char *ZOOM_query_get_query_string(ZOOM_query s);
+
+int ZOOM_uri_to_code(const char *uri);
+
+zoom_ret ZOOM_connection_srw_send_search(ZOOM_connection c);
+zoom_ret ZOOM_connection_srw_send_scan(ZOOM_connection c);
+
+int ZOOM_handle_sru(ZOOM_connection c, Z_HTTP_Response *hres,
+                    zoom_ret *cret);
+
+void ZOOM_set_HTTP_error(ZOOM_connection c, int error,
+                         const char *addinfo, const char *addinfo2);
+
+ZOOM_Event ZOOM_connection_get_event(ZOOM_connection c);
+void ZOOM_connection_remove_events(ZOOM_connection c);
+void ZOOM_Event_destroy(ZOOM_Event event);
+
 /*
  * Local variables:
  * c-basic-offset: 4
diff --git a/src/zoom-query.c b/src/zoom-query.c
new file mode 100644 (file)
index 0000000..1049873
--- /dev/null
@@ -0,0 +1,262 @@
+/* This file is part of the YAZ toolkit.
+ * Copyright (C) 1995-2010 Index Data
+ * See the file LICENSE for details.
+ */
+/**
+ * \file zoom-query.c
+ * \brief Implements ZOOM C query interface.
+ */
+
+#include <assert.h>
+#include <string.h>
+#include <errno.h>
+#include "zoom-p.h"
+
+#include <yaz/yaz-util.h>
+#include <yaz/xmalloc.h>
+#include <yaz/log.h>
+#include <yaz/pquery.h>
+#include <yaz/cql.h>
+#include <yaz/ccl.h>
+#include <yaz/sortspec.h>
+
+struct ZOOM_query_p {
+    Z_Query *z_query;
+    Z_SortKeySpecList *sort_spec;
+    int refcount;
+    ODR odr;
+    char *query_string;
+};
+
+Z_Query *ZOOM_query_get_Z_Query(ZOOM_query s)
+{
+    return s->z_query;
+}
+
+
+Z_SortKeySpecList *ZOOM_query_get_sortspec(ZOOM_query s)
+{
+    return s->sort_spec;
+}
+
+static void cql2pqf_wrbuf_puts(const char *buf, void *client_data)
+{
+    WRBUF wrbuf = (WRBUF) client_data;
+    wrbuf_puts(wrbuf, buf);
+}
+
+char *ZOOM_query_get_query_string(ZOOM_query s)
+{
+    return s->query_string;
+}
+
+/*
+ * Returns an xmalloc()d string containing RPN that corresponds to the
+ * CQL passed in.  On error, sets the Connection object's error state
+ * and returns a null pointer.
+ * ### We could cache CQL parser and/or transformer in Connection.
+ */
+static char *cql2pqf(ZOOM_connection c, const char *cql)
+{
+    CQL_parser parser;
+    int error;
+    const char *cqlfile;
+    cql_transform_t trans;
+    char *result = 0;
+
+    parser = cql_parser_create();
+    if ((error = cql_parser_string(parser, cql)) != 0) {
+        cql_parser_destroy(parser);
+        ZOOM_set_error(c, ZOOM_ERROR_CQL_PARSE, cql);
+        return 0;
+    }
+
+    cqlfile = ZOOM_connection_option_get(c, "cqlfile");
+    if (cqlfile == 0) 
+    {
+        ZOOM_set_error(c, ZOOM_ERROR_CQL_TRANSFORM, "no CQL transform file");
+    }
+    else if ((trans = cql_transform_open_fname(cqlfile)) == 0) 
+    {
+        char buf[512];        
+        sprintf(buf, "can't open CQL transform file '%.200s': %.200s",
+                cqlfile, strerror(errno));
+        ZOOM_set_error(c, ZOOM_ERROR_CQL_TRANSFORM, buf);
+    }
+    else 
+    {
+        WRBUF wrbuf_result = wrbuf_alloc();
+        error = cql_transform(trans, cql_parser_result(parser),
+                              cql2pqf_wrbuf_puts, wrbuf_result);
+        if (error != 0) {
+            char buf[512];
+            const char *addinfo;
+            error = cql_transform_error(trans, &addinfo);
+            sprintf(buf, "%.200s (addinfo=%.200s)", 
+                    cql_strerror(error), addinfo);
+            ZOOM_set_error(c, ZOOM_ERROR_CQL_TRANSFORM, buf);
+        }
+        else
+        {
+            result = xstrdup(wrbuf_cstr(wrbuf_result));
+        }
+        cql_transform_close(trans);
+        wrbuf_destroy(wrbuf_result);
+    }
+    cql_parser_destroy(parser);
+    return result;
+}
+
+
+ZOOM_API(ZOOM_query)
+    ZOOM_query_create(void)
+{
+    ZOOM_query s = (ZOOM_query) xmalloc(sizeof(*s));
+
+    s->refcount = 1;
+    s->z_query = 0;
+    s->sort_spec = 0;
+    s->odr = odr_createmem(ODR_ENCODE);
+    s->query_string = 0;
+
+    return s;
+}
+
+ZOOM_API(void)
+    ZOOM_query_destroy(ZOOM_query s)
+{
+    if (!s)
+        return;
+
+    (s->refcount)--;
+    if (s->refcount == 0)
+    {
+        odr_destroy(s->odr);
+        xfree(s);
+    }
+}
+
+ZOOM_API(void)
+    ZOOM_query_addref(ZOOM_query s)
+{
+    s->refcount++;
+}
+
+ZOOM_API(int)
+    ZOOM_query_prefix(ZOOM_query s, const char *str)
+{
+    s->query_string = odr_strdup(s->odr, str);
+    s->z_query = (Z_Query *) odr_malloc(s->odr, sizeof(*s->z_query));
+    s->z_query->which = Z_Query_type_1;
+    s->z_query->u.type_1 =  p_query_rpn(s->odr, str);
+    if (!s->z_query->u.type_1)
+    {
+        s->z_query = 0;
+        return -1;
+    }
+    return 0;
+}
+
+ZOOM_API(int)
+    ZOOM_query_cql(ZOOM_query s, const char *str)
+{
+    Z_External *ext;
+
+    s->query_string = odr_strdup(s->odr, str);
+
+    ext = (Z_External *) odr_malloc(s->odr, sizeof(*ext));
+    ext->direct_reference = odr_oiddup(s->odr, yaz_oid_userinfo_cql);
+    ext->indirect_reference = 0;
+    ext->descriptor = 0;
+    ext->which = Z_External_CQL;
+    ext->u.cql = s->query_string;
+    
+    s->z_query = (Z_Query *) odr_malloc(s->odr, sizeof(*s->z_query));
+    s->z_query->which = Z_Query_type_104;
+    s->z_query->u.type_104 =  ext;
+
+    return 0;
+}
+
+/*
+ * Translate the CQL string client-side into RPN which is passed to
+ * the server.  This is useful for server's that don't themselves
+ * support CQL, for which ZOOM_query_cql() is useless.  `conn' is used
+ * only as a place to stash diagnostics if compilation fails; if this
+ * information is not needed, a null pointer may be used.
+ */
+ZOOM_API(int)
+    ZOOM_query_cql2rpn(ZOOM_query s, const char *str, ZOOM_connection conn)
+{
+    char *rpn;
+    int ret;
+    ZOOM_connection freeme = 0;
+
+    if (conn == 0)
+        conn = freeme = ZOOM_connection_create(0);
+
+    rpn = cql2pqf(conn, str);
+    if (freeme != 0)
+        ZOOM_connection_destroy(freeme);
+    if (rpn == 0)
+        return -1;
+
+    ret = ZOOM_query_prefix(s, rpn);
+    xfree(rpn);
+    return ret;
+}
+
+/*
+ * Analogous in every way to ZOOM_query_cql2rpn(), except that there
+ * is no analogous ZOOM_query_ccl() that just sends uninterpreted CCL
+ * to the server, as the YAZ GFS doesn't know how to handle this.
+ */
+ZOOM_API(int)
+    ZOOM_query_ccl2rpn(ZOOM_query s, const char *str, const char *config,
+                       int *ccl_error, const char **error_string,
+                       int *error_pos)
+{
+    int ret;
+    struct ccl_rpn_node *rpn;
+    CCL_bibset bibset = ccl_qual_mk();
+
+    if (config)
+        ccl_qual_buf(bibset, config);
+
+    rpn = ccl_find_str(bibset, str, ccl_error, error_pos);
+    if (!rpn)
+    {
+        *error_string = ccl_err_msg(*ccl_error);
+        ret = -1;
+    }
+    else
+    {
+        WRBUF wr = wrbuf_alloc();
+        ccl_pquery(wr, rpn);
+        ccl_rpn_delete(rpn);
+        ret = ZOOM_query_prefix(s, wrbuf_cstr(wr));
+        wrbuf_destroy(wr);
+    }
+    ccl_qual_rm(&bibset);
+    return ret;
+}
+
+ZOOM_API(int)
+    ZOOM_query_sortby(ZOOM_query s, const char *criteria)
+{
+    s->sort_spec = yaz_sort_spec(s->odr, criteria);
+    if (!s->sort_spec)
+        return -1;
+    return 0;
+}
+
+
+/*
+ * Local variables:
+ * c-basic-offset: 4
+ * c-file-style: "Stroustrup"
+ * indent-tabs-mode: nil
+ * End:
+ * vim: shiftwidth=4 tabstop=8 expandtab
+ */
+
diff --git a/src/zoom-record-cache.c b/src/zoom-record-cache.c
new file mode 100644 (file)
index 0000000..9bc4e15
--- /dev/null
@@ -0,0 +1,314 @@
+/* This file is part of the YAZ toolkit.
+ * Copyright (C) 1995-2010 Index Data
+ * See the file LICENSE for details.
+ */
+/**
+ * \file zoom-record-cache.c
+ * \brief Implements ZOOM record caching
+ */
+
+#include <assert.h>
+#include <string.h>
+#include <errno.h>
+#include "zoom-p.h"
+
+#include <yaz/diagbib1.h>
+#include <yaz/record_render.h>
+#include <yaz/shptr.h>
+
+#if SHPTR
+YAZ_SHPTR_TYPE(WRBUF)
+#endif
+
+struct ZOOM_record_p {
+    ODR odr;
+#if SHPTR
+    struct WRBUF_shptr *record_wrbuf;
+#else
+    WRBUF wrbuf;
+#endif
+
+    Z_NamePlusRecord *npr;
+    const char *schema;
+
+    const char *diag_uri;
+    const char *diag_message;
+    const char *diag_details;
+    const char *diag_set;
+};
+
+struct ZOOM_record_cache_p {
+    struct ZOOM_record_p rec;
+    char *elementSetName;
+    char *syntax;
+    char *schema;
+    int pos;
+    ZOOM_record_cache next;
+};
+
+
+static int strcmp_null(const char *v1, const char *v2)
+{
+    if (!v1 && !v2)
+        return 0;
+    if (!v1 || !v2)
+        return -1;
+    return strcmp(v1, v2);
+}
+
+static size_t record_hash(int pos)
+{
+    if (pos < 0)
+        pos = 0;
+    return pos % RECORD_HASH_SIZE;
+}
+
+void ZOOM_record_cache_add(ZOOM_resultset r, Z_NamePlusRecord *npr, 
+                           int pos,
+                           const char *syntax, const char *elementSetName,
+                           const char *schema,
+                           Z_SRW_diagnostic *diag)
+{
+    ZOOM_record_cache rc = 0;
+    
+    ZOOM_Event event = ZOOM_Event_create(ZOOM_EVENT_RECV_RECORD);
+    ZOOM_connection_put_event(r->connection, event);
+
+    for (rc = r->record_hash[record_hash(pos)]; rc; rc = rc->next)
+    {
+        if (pos == rc->pos 
+            && strcmp_null(r->schema, rc->schema) == 0
+            && strcmp_null(elementSetName,rc->elementSetName) == 0
+            && strcmp_null(syntax, rc->syntax) == 0)
+            break;
+    }
+    if (!rc)
+    {
+        rc = (ZOOM_record_cache) odr_malloc(r->odr, sizeof(*rc));
+        rc->rec.odr = 0;
+#if SHPTR
+        YAZ_SHPTR_INC(r->record_wrbuf);
+        rc->rec.record_wrbuf = r->record_wrbuf;
+#else
+        rc->rec.wrbuf = 0;
+#endif
+        rc->elementSetName = odr_strdup_null(r->odr, elementSetName);
+        
+        rc->syntax = odr_strdup_null(r->odr, syntax);
+        
+        rc->schema = odr_strdup_null(r->odr, r->schema);
+
+        rc->pos = pos;
+        rc->next = r->record_hash[record_hash(pos)];
+        r->record_hash[record_hash(pos)] = rc;
+    }
+    rc->rec.npr = npr;
+    rc->rec.schema = odr_strdup_null(r->odr, schema);
+    rc->rec.diag_set = 0;
+    rc->rec.diag_uri = 0;
+    rc->rec.diag_message = 0;
+    rc->rec.diag_details = 0;
+    if (diag)
+    {
+        if (diag->uri)
+        {
+            char *cp;
+            rc->rec.diag_set = odr_strdup(r->odr, diag->uri);
+            if ((cp = strrchr(rc->rec.diag_set, '/')))
+                *cp = '\0';
+            rc->rec.diag_uri = odr_strdup(r->odr, diag->uri);
+        }
+        rc->rec.diag_message = odr_strdup_null(r->odr, diag->message);            
+        rc->rec.diag_details = odr_strdup_null(r->odr, diag->details);
+    }
+}
+
+ZOOM_record ZOOM_record_cache_lookup(ZOOM_resultset r, int pos,
+                                     const char *syntax,
+                                     const char *elementSetName)
+{
+    ZOOM_record_cache rc;
+    
+    for (rc = r->record_hash[record_hash(pos)]; rc; rc = rc->next)
+    {
+        if (pos == rc->pos)
+        {
+            if (strcmp_null(r->schema, rc->schema))
+                continue;
+            if (strcmp_null(elementSetName,rc->elementSetName))
+                continue;
+            if (strcmp_null(syntax, rc->syntax))
+                continue;
+            return &rc->rec;
+        }
+    }
+    return 0;
+}
+
+ZOOM_API(ZOOM_record)
+    ZOOM_record_clone(ZOOM_record srec)
+{
+    char *buf;
+    int size;
+    ODR odr_enc;
+    ZOOM_record nrec;
+
+    odr_enc = odr_createmem(ODR_ENCODE);
+    if (!z_NamePlusRecord(odr_enc, &srec->npr, 0, 0))
+        return 0;
+    buf = odr_getbuf(odr_enc, &size, 0);
+    
+    nrec = (ZOOM_record) xmalloc(sizeof(*nrec));
+    nrec->odr = odr_createmem(ODR_DECODE);
+#if SHPTR
+    nrec->record_wrbuf = 0;
+#else
+    nrec->wrbuf = 0;
+#endif
+    odr_setbuf(nrec->odr, buf, size, 0);
+    z_NamePlusRecord(nrec->odr, &nrec->npr, 0, 0);
+    
+    nrec->schema = odr_strdup_null(nrec->odr, srec->schema);
+    nrec->diag_uri = odr_strdup_null(nrec->odr, srec->diag_uri);
+    nrec->diag_message = odr_strdup_null(nrec->odr, srec->diag_message);
+    nrec->diag_details = odr_strdup_null(nrec->odr, srec->diag_details);
+    nrec->diag_set = odr_strdup_null(nrec->odr, srec->diag_set);
+    odr_destroy(odr_enc);
+    return nrec;
+}
+
+static void ZOOM_record_release(ZOOM_record rec)
+{
+    if (!rec)
+        return;
+
+#if SHPTR
+    if (rec->record_wrbuf)
+        YAZ_SHPTR_DEC(rec->record_wrbuf, wrbuf_destroy);
+#else
+    if (rec->wrbuf)
+        wrbuf_destroy(rec->wrbuf);
+#endif
+
+    if (rec->odr)
+        odr_destroy(rec->odr);
+}
+
+ZOOM_API(void)
+    ZOOM_resultset_cache_reset(ZOOM_resultset r)
+{
+    int i;
+    for (i = 0; i<RECORD_HASH_SIZE; i++)
+    {
+        ZOOM_record_cache rc;
+        for (rc = r->record_hash[i]; rc; rc = rc->next)
+        {
+            ZOOM_record_release(&rc->rec);
+        }
+        r->record_hash[i] = 0;
+    }
+}
+
+
+ZOOM_API(const char *)
+    ZOOM_record_get(ZOOM_record rec, const char *type_spec, int *len)
+{
+    WRBUF wrbuf;
+    
+    if (len)
+        *len = 0; /* default return */
+        
+    if (!rec || !rec->npr)
+        return 0;
+
+#if SHPTR
+    if (!rec->record_wrbuf)
+    {
+        WRBUF w = wrbuf_alloc();
+        YAZ_SHPTR_INIT(rec->record_wrbuf, w);
+    }
+    wrbuf = rec->record_wrbuf->ptr;
+#else
+    if (!rec->wrbuf)
+        rec->wrbuf = wrbuf_alloc();
+    wrbuf = rec->wrbuf;
+#endif
+    return yaz_record_render(rec->npr, rec->schema, wrbuf, type_spec, len);
+}
+
+ZOOM_API(int)
+    ZOOM_record_error(ZOOM_record rec, const char **cp,
+                      const char **addinfo, const char **diagset)
+{
+    Z_NamePlusRecord *npr;
+    
+    if (!rec)
+        return 0;
+
+    npr = rec->npr;
+    if (rec->diag_uri)
+    {
+        if (cp)
+            *cp = rec->diag_message;
+        if (addinfo)
+            *addinfo = rec->diag_details;
+        if (diagset)
+            *diagset = rec->diag_set;
+        return ZOOM_uri_to_code(rec->diag_uri);
+    }
+    if (npr && npr->which == Z_NamePlusRecord_surrogateDiagnostic)
+    {
+        Z_DiagRec *diag_rec = npr->u.surrogateDiagnostic;
+        int error = YAZ_BIB1_UNSPECIFIED_ERROR;
+        const char *add = 0;
+
+        if (diag_rec->which == Z_DiagRec_defaultFormat)
+        {
+            Z_DefaultDiagFormat *ddf = diag_rec->u.defaultFormat;
+            oid_class oclass;
+    
+            error = *ddf->condition;
+            switch (ddf->which)
+            {
+            case Z_DefaultDiagFormat_v2Addinfo:
+                add = ddf->u.v2Addinfo;
+                break;
+            case Z_DefaultDiagFormat_v3Addinfo:
+                add = ddf->u.v3Addinfo;
+                break;
+            }
+            if (diagset)
+                *diagset =
+                    yaz_oid_to_string(yaz_oid_std(),
+                                      ddf->diagnosticSetId, &oclass);
+        }
+        else
+        {
+            if (diagset)
+                *diagset = "Bib-1";
+        }
+        if (addinfo)
+            *addinfo = add ? add : "";
+        if (cp)
+            *cp = diagbib1_str(error);
+        return error;
+    }
+    return 0;
+}
+
+ZOOM_API(void)
+    ZOOM_record_destroy(ZOOM_record rec)
+{
+    ZOOM_record_release(rec);
+    xfree(rec);
+}
+
+/*
+ * Local variables:
+ * c-basic-offset: 4
+ * c-file-style: "Stroustrup"
+ * indent-tabs-mode: nil
+ * End:
+ * vim: shiftwidth=4 tabstop=8 expandtab
+ */
+
diff --git a/src/zoom-sru.c b/src/zoom-sru.c
new file mode 100644 (file)
index 0000000..2319387
--- /dev/null
@@ -0,0 +1,443 @@
+/* This file is part of the YAZ toolkit.
+ * Copyright (C) 1995-2010 Index Data
+ * See the file LICENSE for details.
+ */
+/**
+ * \file zoom-sru.c
+ * \brief Implements ZOOM SRU
+ */
+
+#include <assert.h>
+#include <string.h>
+#include <errno.h>
+#include "zoom-p.h"
+
+#include <yaz/log.h>
+
+#if YAZ_HAVE_XML2
+static void set_SRU_error(ZOOM_connection c, Z_SRW_diagnostic *d)
+{
+    const char *uri = d->uri;
+    if (uri)
+        ZOOM_set_dset_error(c, ZOOM_uri_to_code(uri), uri, d->details, 0);
+}
+#endif
+
+
+#if YAZ_HAVE_XML2
+static zoom_ret send_srw(ZOOM_connection c, Z_SRW_PDU *sr)
+{
+    Z_GDU *gdu;
+    ZOOM_Event event;
+    const char *database =  ZOOM_options_get(c->options, "databaseName");
+    char *fdatabase = 0;
+    
+    if (database)
+        fdatabase = yaz_encode_sru_dbpath_odr(c->odr_out, database);
+    gdu = z_get_HTTP_Request_host_path(c->odr_out, c->host_port,
+                                       fdatabase ? fdatabase : c->path);
+
+    if (c->sru_mode == zoom_sru_get)
+    {
+        yaz_sru_get_encode(gdu->u.HTTP_Request, sr, c->odr_out, c->charset);
+    }
+    else if (c->sru_mode == zoom_sru_post)
+    {
+        yaz_sru_post_encode(gdu->u.HTTP_Request, sr, c->odr_out, c->charset);
+    }
+    else if (c->sru_mode == zoom_sru_soap)
+    {
+        yaz_sru_soap_encode(gdu->u.HTTP_Request, sr, c->odr_out, c->charset);
+    }
+    else if (c->sru_mode == zoom_sru_solr)
+    {
+        yaz_solr_encode_request(gdu->u.HTTP_Request, sr, c->odr_out, c->charset);
+    }
+    if (!z_GDU(c->odr_out, &gdu, 0, 0))
+        return zoom_complete;
+    if (c->odr_print)
+        z_GDU(c->odr_print, &gdu, 0, 0);
+    c->buf_out = odr_getbuf(c->odr_out, &c->len_out, 0);
+        
+    event = ZOOM_Event_create(ZOOM_EVENT_SEND_APDU);
+    ZOOM_connection_put_event(c, event);
+    odr_reset(c->odr_out);
+    return ZOOM_send_buf(c);
+}
+#endif
+
+#if YAZ_HAVE_XML2
+static Z_SRW_PDU *ZOOM_srw_get_pdu(ZOOM_connection c, int type)
+{
+    Z_SRW_PDU *sr = yaz_srw_get_pdu(c->odr_out, type, c->sru_version);
+    sr->username = c->user;
+    sr->password = c->password;
+    return sr;
+}
+#endif
+
+#if YAZ_HAVE_XML2
+zoom_ret ZOOM_connection_srw_send_scan(ZOOM_connection c)
+{
+    ZOOM_scanset scan;
+    Z_SRW_PDU *sr = 0;
+    const char *option_val = 0;
+    Z_Query *z_query;
+
+    if (!c->tasks)
+        return zoom_complete;
+    assert (c->tasks->which == ZOOM_TASK_SCAN);
+    scan = c->tasks->u.scan.scan;
+        
+    sr = ZOOM_srw_get_pdu(c, Z_SRW_scan_request);
+
+    z_query = ZOOM_query_get_Z_Query(scan->query);
+    /* SRU scan can only carry CQL and PQF */
+    if (z_query->which == Z_Query_type_104)
+    {
+        sr->u.scan_request->query_type = Z_SRW_query_type_cql;
+        sr->u.scan_request->scanClause.cql =
+            ZOOM_query_get_query_string(scan->query);
+    }
+    else if (z_query->which == Z_Query_type_1
+             || z_query->which == Z_Query_type_101)
+    {
+        sr->u.scan_request->query_type = Z_SRW_query_type_pqf;
+        sr->u.scan_request->scanClause.pqf =
+            ZOOM_query_get_query_string(scan->query);
+    }
+    else
+    {
+        ZOOM_set_error(c, ZOOM_ERROR_UNSUPPORTED_QUERY, 0);
+        return zoom_complete;
+    }
+
+    sr->u.scan_request->maximumTerms = odr_intdup(
+        c->odr_out, ZOOM_options_get_int(scan->options, "number", 10));
+    
+    sr->u.scan_request->responsePosition = odr_intdup(
+        c->odr_out, ZOOM_options_get_int(scan->options, "position", 1));
+    
+    option_val = ZOOM_options_get(scan->options, "extraArgs");
+    yaz_encode_sru_extra(sr, c->odr_out, option_val);
+    return send_srw(c, sr);
+}
+#else
+zoom_ret ZOOM_connection_srw_send_scan(ZOOM_connection c)
+{
+    return zoom_complete;
+}
+#endif
+
+#if YAZ_HAVE_XML2
+zoom_ret ZOOM_connection_srw_send_search(ZOOM_connection c)
+{
+    int i;
+    int *start, *count;
+    ZOOM_resultset resultset = 0;
+    Z_SRW_PDU *sr = 0;
+    const char *option_val = 0;
+    Z_Query *z_query;
+
+    if (c->error)                  /* don't continue on error */
+        return zoom_complete;
+    assert(c->tasks);
+    switch(c->tasks->which)
+    {
+    case ZOOM_TASK_SEARCH:
+        resultset = c->tasks->u.search.resultset;
+        if (!resultset->setname)
+            resultset->setname = xstrdup("default");
+        ZOOM_options_set(resultset->options, "setname", resultset->setname);
+        start = &c->tasks->u.search.start;
+        count = &c->tasks->u.search.count;
+        break;
+    case ZOOM_TASK_RETRIEVE:
+        resultset = c->tasks->u.retrieve.resultset;
+
+        start = &c->tasks->u.retrieve.start;
+        count = &c->tasks->u.retrieve.count;
+
+        if (*start >= resultset->size)
+            return zoom_complete;
+        if (*start + *count > resultset->size)
+            *count = resultset->size - *start;
+
+        for (i = 0; i < *count; i++)
+        {
+            ZOOM_record rec =
+                ZOOM_record_cache_lookup(resultset, i + *start,
+                                         c->tasks->u.retrieve.syntax,
+                                         c->tasks->u.retrieve.elementSetName);
+            if (!rec)
+                break;
+            else
+            {
+                ZOOM_Event event = ZOOM_Event_create(ZOOM_EVENT_RECV_RECORD);
+                ZOOM_connection_put_event(c, event);
+            }
+        }
+        *start += i;
+        *count -= i;
+
+        if (*count == 0)
+            return zoom_complete;
+        break;
+    default:
+        return zoom_complete;
+    }
+    assert(resultset->query);
+        
+    sr = ZOOM_srw_get_pdu(c, Z_SRW_searchRetrieve_request);
+    z_query = ZOOM_query_get_Z_Query(resultset->query);
+
+    if (z_query->which == Z_Query_type_104
+        && z_query->u.type_104->which == Z_External_CQL)
+    {
+        sr->u.request->query_type = Z_SRW_query_type_cql;
+        sr->u.request->query.cql = z_query->u.type_104->u.cql;
+    }
+    else if (z_query->which == Z_Query_type_1 && z_query->u.type_1)
+    {
+        sr->u.request->query_type = Z_SRW_query_type_pqf;
+        sr->u.request->query.pqf =
+            ZOOM_query_get_query_string(resultset->query);
+    }
+    else
+    {
+        ZOOM_set_error(c, ZOOM_ERROR_UNSUPPORTED_QUERY, 0);
+        return zoom_complete;
+    }
+    sr->u.request->startRecord = odr_intdup(c->odr_out, *start + 1);
+    sr->u.request->maximumRecords = odr_intdup(
+        c->odr_out, (resultset->step > 0 && resultset->step < *count) ? 
+        resultset->step : *count);
+    sr->u.request->recordSchema = resultset->schema;
+    
+    option_val = ZOOM_resultset_option_get(resultset, "recordPacking");
+    if (option_val)
+        sr->u.request->recordPacking = odr_strdup(c->odr_out, option_val);
+
+    option_val = ZOOM_resultset_option_get(resultset, "extraArgs");
+    yaz_encode_sru_extra(sr, c->odr_out, option_val);
+    return send_srw(c, sr);
+}
+#else
+zoom_ret ZOOM_connection_srw_send_search(ZOOM_connection c)
+{
+    return zoom_complete;
+}
+#endif
+
+#if YAZ_HAVE_XML2
+static zoom_ret handle_srw_response(ZOOM_connection c,
+                                    Z_SRW_searchRetrieveResponse *res)
+{
+    ZOOM_resultset resultset = 0;
+    int i;
+    NMEM nmem;
+    ZOOM_Event event;
+    int *start, *count;
+    const char *syntax, *elementSetName;
+
+    if (!c->tasks)
+        return zoom_complete;
+
+    switch(c->tasks->which)
+    {
+    case ZOOM_TASK_SEARCH:
+        resultset = c->tasks->u.search.resultset;
+        start = &c->tasks->u.search.start;
+        count = &c->tasks->u.search.count;
+        syntax = c->tasks->u.search.syntax;
+        elementSetName = c->tasks->u.search.elementSetName;        
+
+        if (!c->tasks->u.search.recv_search_fired)
+        {
+            event = ZOOM_Event_create(ZOOM_EVENT_RECV_SEARCH);
+            ZOOM_connection_put_event(c, event);
+            c->tasks->u.search.recv_search_fired = 1;
+        }
+        break;
+    case ZOOM_TASK_RETRIEVE:
+        resultset = c->tasks->u.retrieve.resultset;
+        start = &c->tasks->u.retrieve.start;
+        count = &c->tasks->u.retrieve.count;
+        syntax = c->tasks->u.retrieve.syntax;
+        elementSetName = c->tasks->u.retrieve.elementSetName;
+        break;
+    default:
+        return zoom_complete;
+    }
+
+    resultset->size = 0;
+
+    if (res->resultSetId)
+        ZOOM_resultset_option_set(resultset, "resultSetId", res->resultSetId);
+
+    yaz_log(c->log_details, "%p handle_srw_response got SRW response OK", c);
+
+    if (res->num_diagnostics > 0)
+    {
+        set_SRU_error(c, &res->diagnostics[0]);
+    }
+    else
+    {
+        if (res->numberOfRecords)
+            resultset->size = *res->numberOfRecords;
+        for (i = 0; i<res->num_records; i++)
+        {
+            int pos;
+            Z_SRW_record *sru_rec;
+            Z_SRW_diagnostic *diag = 0;
+            int num_diag;
+            
+            Z_NamePlusRecord *npr = (Z_NamePlusRecord *)
+                odr_malloc(c->odr_in, sizeof(Z_NamePlusRecord));
+            
+            if (res->records[i].recordPosition && 
+                *res->records[i].recordPosition > 0)
+                pos = *res->records[i].recordPosition - 1;
+            else
+                pos = *start + i;
+            
+            sru_rec = &res->records[i];
+            
+            npr->databaseName = 0;
+            npr->which = Z_NamePlusRecord_databaseRecord;
+            npr->u.databaseRecord = (Z_External *)
+                odr_malloc(c->odr_in, sizeof(Z_External));
+            npr->u.databaseRecord->descriptor = 0;
+            npr->u.databaseRecord->direct_reference =
+                odr_oiddup(c->odr_in, yaz_oid_recsyn_xml);
+            npr->u.databaseRecord->which = Z_External_octet;
+            
+            npr->u.databaseRecord->u.octet_aligned = (Odr_oct *)
+                odr_malloc(c->odr_in, sizeof(Odr_oct));
+            npr->u.databaseRecord->u.octet_aligned->buf = (unsigned char*)
+                sru_rec->recordData_buf;
+            npr->u.databaseRecord->u.octet_aligned->len = 
+                npr->u.databaseRecord->u.octet_aligned->size = 
+                sru_rec->recordData_len;
+            
+            if (sru_rec->recordSchema 
+                && !strcmp(sru_rec->recordSchema,
+                           "info:srw/schema/1/diagnostics-v1.1"))
+            {
+                sru_decode_surrogate_diagnostics(sru_rec->recordData_buf,
+                                                 sru_rec->recordData_len,
+                                                 &diag, &num_diag,
+                                                 resultset->odr);
+            }
+            ZOOM_record_cache_add(resultset, npr, pos, syntax, elementSetName,
+                                  sru_rec->recordSchema, diag);
+        }
+        *count -= i;
+        *start += i;
+        if (*count + *start > resultset->size)
+            *count = resultset->size - *start;
+        if (*count < 0)
+            *count = 0;
+        
+        nmem = odr_extract_mem(c->odr_in);
+        nmem_transfer(odr_getmem(resultset->odr), nmem);
+        nmem_destroy(nmem);
+
+        if (*count > 0)
+            return ZOOM_connection_srw_send_search(c);
+    }
+    return zoom_complete;
+}
+#endif
+
+#if YAZ_HAVE_XML2
+static void handle_srw_scan_response(ZOOM_connection c,
+                                     Z_SRW_scanResponse *res)
+{
+    NMEM nmem = odr_extract_mem(c->odr_in);
+    ZOOM_scanset scan;
+
+    if (!c->tasks || c->tasks->which != ZOOM_TASK_SCAN)
+        return;
+    scan = c->tasks->u.scan.scan;
+
+    if (res->num_diagnostics > 0)
+        set_SRU_error(c, &res->diagnostics[0]);
+
+    scan->scan_response = 0;
+    scan->srw_scan_response = res;
+    nmem_transfer(odr_getmem(scan->odr), nmem);
+
+    ZOOM_options_set_int(scan->options, "number", res->num_terms);
+    nmem_destroy(nmem);
+}
+#endif
+
+int ZOOM_handle_sru(ZOOM_connection c, Z_HTTP_Response *hres,
+                    zoom_ret *cret)
+{
+    int ret = 0;
+    const char *addinfo = 0;
+
+    /* not redirect (normal response) */
+    if (!yaz_srw_check_content_type(hres))
+    {
+        addinfo = "content-type";
+        ret = -1;
+    }
+    else if (c->sru_mode == zoom_sru_solr)
+    {
+        Z_SRW_PDU *sr;
+        ret = yaz_solr_decode_response(c->odr_in, hres, &sr);
+        if (ret == 0)
+            if (sr->which == Z_SRW_searchRetrieve_response)
+                *cret = handle_srw_response(c, sr->u.response);
+    }
+    else
+    {
+        Z_SOAP *soap_package = 0;
+        ODR o = c->odr_in;
+        Z_SOAP_Handler soap_handlers[2] = {
+            {YAZ_XMLNS_SRU_v1_1, 0, (Z_SOAP_fun) yaz_srw_codec},
+            {0, 0, 0}
+        };
+        ret = z_soap_codec(o, &soap_package,
+                           &hres->content_buf, &hres->content_len,
+                           soap_handlers);
+        if (!ret && soap_package->which == Z_SOAP_generic &&
+            soap_package->u.generic->no == 0)
+        {
+            Z_SRW_PDU *sr = (Z_SRW_PDU*) soap_package->u.generic->p;
+            
+            ZOOM_options_set(c->options, "sru_version", sr->srw_version);
+            ZOOM_options_setl(c->options, "sru_extra_response_data",
+                              sr->extraResponseData_buf, sr->extraResponseData_len);
+            if (sr->which == Z_SRW_searchRetrieve_response)
+                *cret = handle_srw_response(c, sr->u.response);
+            else if (sr->which == Z_SRW_scan_response)
+                handle_srw_scan_response(c, sr->u.scan_response);
+            else
+                ret = -1;
+        }
+        else if (!ret && (soap_package->which == Z_SOAP_fault
+                          || soap_package->which == Z_SOAP_error))
+        {
+            ZOOM_set_HTTP_error(c, hres->code,
+                                soap_package->u.fault->fault_code,
+                                soap_package->u.fault->fault_string);
+        }
+        else
+            ret = -1;
+    }   
+    return ret;
+}
+
+/*
+ * Local variables:
+ * c-basic-offset: 4
+ * c-file-style: "Stroustrup"
+ * indent-tabs-mode: nil
+ * End:
+ * vim: shiftwidth=4 tabstop=8 expandtab
+ */
+
diff --git a/src/zoom-z3950.c b/src/zoom-z3950.c
new file mode 100644 (file)
index 0000000..bbd2e2a
--- /dev/null
@@ -0,0 +1,1776 @@
+/* This file is part of the YAZ toolkit.
+ * Copyright (C) 1995-2010 Index Data
+ * See the file LICENSE for details.
+ */
+/**
+ * \file zoom-z3950.c
+ * \brief Implements ZOOM Z39.50 handling
+ */
+
+#include <assert.h>
+#include <string.h>
+#include <errno.h>
+#include "zoom-p.h"
+
+#include <yaz/yaz-util.h>
+#include <yaz/xmalloc.h>
+#include <yaz/otherinfo.h>
+#include <yaz/log.h>
+#include <yaz/pquery.h>
+#include <yaz/marcdisp.h>
+#include <yaz/diagbib1.h>
+#include <yaz/charneg.h>
+#include <yaz/ill.h>
+#include <yaz/query-charset.h>
+#include <yaz/copy_types.h>
+#include <yaz/snprintf.h>
+#include <yaz/facet.h>
+
+#include <yaz/shptr.h>
+
+/*
+ * This wrapper is just for logging failed lookups.  It would be nicer
+ * if it could cause failure when a lookup fails, but that's hard.
+ */
+static Odr_oid *zoom_yaz_str_to_z3950oid(ZOOM_connection c,
+                                     oid_class oid_class, const char *str) {
+    Odr_oid *res = yaz_string_to_oid_odr(yaz_oid_std(), oid_class, str,
+                                     c->odr_out);
+    if (res == 0)
+        yaz_log(YLOG_WARN, "%p OID lookup (%d, '%s') failed",
+                c, (int) oid_class, str);
+    return res;
+}
+
+static Z_APDU *create_es_package(ZOOM_package p, const Odr_oid *oid)
+{
+    const char *str;
+    Z_APDU *apdu = zget_APDU(p->odr_out, Z_APDU_extendedServicesRequest);
+    Z_ExtendedServicesRequest *req = apdu->u.extendedServicesRequest;
+    
+    str = ZOOM_options_get(p->options, "package-name");
+    if (str && *str)
+        req->packageName = odr_strdup(p->odr_out, str);
+    
+    str = ZOOM_options_get(p->options, "user-id");
+    if (str)
+        req->userId = odr_strdup_null(p->odr_out, str);
+    
+    req->packageType = odr_oiddup(p->odr_out, oid);
+
+    str = ZOOM_options_get(p->options, "function");
+    if (str)
+    {
+        if (!strcmp (str, "create"))
+            *req->function = Z_ExtendedServicesRequest_create;
+        if (!strcmp (str, "delete"))
+            *req->function = Z_ExtendedServicesRequest_delete;
+        if (!strcmp (str, "modify"))
+            *req->function = Z_ExtendedServicesRequest_modify;
+    }
+
+    str = ZOOM_options_get(p->options, "waitAction");
+    if (str)
+    {
+        if (!strcmp (str, "wait"))
+            *req->waitAction = Z_ExtendedServicesRequest_wait;
+        if (!strcmp (str, "waitIfPossible"))
+            *req->waitAction = Z_ExtendedServicesRequest_waitIfPossible;
+        if (!strcmp (str, "dontWait"))
+            *req->waitAction = Z_ExtendedServicesRequest_dontWait;
+        if (!strcmp (str, "dontReturnPackage"))
+            *req->waitAction = Z_ExtendedServicesRequest_dontReturnPackage;
+    }
+    return apdu;
+}
+
+static const char *ill_array_lookup(void *clientData, const char *idx)
+{
+    ZOOM_package p = (ZOOM_package) clientData;
+    return ZOOM_options_get(p->options, idx+4);
+}
+
+static Z_External *encode_ill_request(ZOOM_package p)
+{
+    ODR out = p->odr_out;
+    ILL_Request *req;
+    Z_External *r = 0;
+    struct ill_get_ctl ctl;
+        
+    ctl.odr = p->odr_out;
+    ctl.clientData = p;
+    ctl.f = ill_array_lookup;
+        
+    req = ill_get_ILLRequest(&ctl, "ill", 0);
+        
+    if (!ill_Request(out, &req, 0, 0))
+    {
+        int ill_request_size;
+        char *ill_request_buf = odr_getbuf(out, &ill_request_size, 0);
+        if (ill_request_buf)
+            odr_setbuf(out, ill_request_buf, ill_request_size, 1);
+        return 0;
+    }
+    else
+    {
+        int illRequest_size = 0;
+        char *illRequest_buf = odr_getbuf(out, &illRequest_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;
+        r->descriptor = 0;
+        r->which = Z_External_single;
+                
+        r->u.single_ASN1_type =
+            odr_create_Odr_oct(out,
+                               (unsigned char *)illRequest_buf,
+                               illRequest_size);
+    }
+    return r;
+}
+
+static Z_ItemOrder *encode_item_order(ZOOM_package p)
+{
+    Z_ItemOrder *req = (Z_ItemOrder *) odr_malloc(p->odr_out, sizeof(*req));
+    const char *str;
+    int len;
+    
+    req->which = Z_IOItemOrder_esRequest;
+    req->u.esRequest = (Z_IORequest *) 
+        odr_malloc(p->odr_out,sizeof(Z_IORequest));
+
+    /* to keep part ... */
+    req->u.esRequest->toKeep = (Z_IOOriginPartToKeep *)
+        odr_malloc(p->odr_out,sizeof(Z_IOOriginPartToKeep));
+    req->u.esRequest->toKeep->supplDescription = 0;
+    req->u.esRequest->toKeep->contact = (Z_IOContact *)
+        odr_malloc(p->odr_out, sizeof(*req->u.esRequest->toKeep->contact));
+        
+    str = ZOOM_options_get(p->options, "contact-name");
+    req->u.esRequest->toKeep->contact->name =
+        odr_strdup_null(p->odr_out, str);
+        
+    str = ZOOM_options_get(p->options, "contact-phone");
+    req->u.esRequest->toKeep->contact->phone =
+        odr_strdup_null(p->odr_out, str);
+        
+    str = ZOOM_options_get(p->options, "contact-email");
+    req->u.esRequest->toKeep->contact->email =
+        odr_strdup_null(p->odr_out, str);
+        
+    req->u.esRequest->toKeep->addlBilling = 0;
+        
+    /* not to keep part ... */
+    req->u.esRequest->notToKeep = (Z_IOOriginPartNotToKeep *)
+        odr_malloc(p->odr_out,sizeof(Z_IOOriginPartNotToKeep));
+        
+    str = ZOOM_options_get(p->options, "itemorder-setname");
+    if (!str)
+        str = "default";
+
+    if (!*str) 
+        req->u.esRequest->notToKeep->resultSetItem = 0;
+    else
+    {
+        req->u.esRequest->notToKeep->resultSetItem = (Z_IOResultSetItem *)
+            odr_malloc(p->odr_out, sizeof(Z_IOResultSetItem));
+
+        req->u.esRequest->notToKeep->resultSetItem->resultSetId =
+            odr_strdup(p->odr_out, str);
+        req->u.esRequest->notToKeep->resultSetItem->item =
+            odr_intdup(p->odr_out, 0);
+        
+        str = ZOOM_options_get(p->options, "itemorder-item");
+        *req->u.esRequest->notToKeep->resultSetItem->item =
+            (str ? atoi(str) : 1);
+    }
+
+    str = ZOOM_options_getl(p->options, "doc", &len);
+    if (str)
+    {
+        req->u.esRequest->notToKeep->itemRequest =
+            z_ext_record_xml(p->odr_out, str, len);
+    }
+    else
+        req->u.esRequest->notToKeep->itemRequest = encode_ill_request(p);
+    
+    return req;
+}
+
+Z_APDU *create_admin_package(ZOOM_package p, int type, 
+                             Z_ESAdminOriginPartToKeep **toKeepP,
+                             Z_ESAdminOriginPartNotToKeep **notToKeepP)
+{
+    Z_APDU *apdu = create_es_package(p, yaz_oid_extserv_admin);
+    if (apdu)
+    {
+        Z_ESAdminOriginPartToKeep  *toKeep;
+        Z_ESAdminOriginPartNotToKeep  *notToKeep;
+        Z_External *r = (Z_External *) odr_malloc(p->odr_out, sizeof(*r));
+        const char *first_db = "Default";
+        int num_db;
+        char **db = ZOOM_connection_get_databases(p->connection,
+                                                  p->options, &num_db,
+                                                  p->odr_out);
+        if (num_db > 0)
+            first_db = db[0];
+            
+        r->direct_reference = odr_oiddup(p->odr_out, yaz_oid_extserv_admin);
+        r->descriptor = 0;
+        r->indirect_reference = 0;
+        r->which = Z_External_ESAdmin;
+        
+        r->u.adminService = (Z_Admin *)
+            odr_malloc(p->odr_out, sizeof(*r->u.adminService));
+        r->u.adminService->which = Z_Admin_esRequest;
+        r->u.adminService->u.esRequest = (Z_AdminEsRequest *)
+            odr_malloc(p->odr_out, sizeof(*r->u.adminService->u.esRequest));
+        
+        toKeep = r->u.adminService->u.esRequest->toKeep =
+            (Z_ESAdminOriginPartToKeep *) 
+            odr_malloc(p->odr_out, sizeof(*r->u.adminService->u.esRequest->toKeep));
+        toKeep->which = type;
+        toKeep->databaseName = odr_strdup(p->odr_out, first_db);
+        toKeep->u.create = odr_nullval();
+        apdu->u.extendedServicesRequest->taskSpecificParameters = r;
+        
+        r->u.adminService->u.esRequest->notToKeep = notToKeep =
+            (Z_ESAdminOriginPartNotToKeep *)
+            odr_malloc(p->odr_out,
+                       sizeof(*r->u.adminService->u.esRequest->notToKeep));
+        notToKeep->which = Z_ESAdminOriginPartNotToKeep_recordsWillFollow;
+        notToKeep->u.recordsWillFollow = odr_nullval();
+        if (toKeepP)
+            *toKeepP = toKeep;
+        if (notToKeepP)
+            *notToKeepP = notToKeep;
+    }
+    return apdu;
+}
+
+static Z_APDU *create_xmlupdate_package(ZOOM_package p)
+{
+    Z_APDU *apdu = create_es_package(p, yaz_oid_extserv_xml_es);
+    Z_ExtendedServicesRequest *req = apdu->u.extendedServicesRequest;
+    Z_External *ext = (Z_External *) odr_malloc(p->odr_out, sizeof(*ext));
+    int len;
+    const char *doc = ZOOM_options_getl(p->options, "doc", &len);
+
+    if (!doc)
+    {
+        doc = "";
+        len = 0;
+    }
+
+    req->taskSpecificParameters = ext;
+    ext->direct_reference = req->packageType;
+    ext->descriptor = 0;
+    ext->indirect_reference = 0;
+    
+    ext->which = Z_External_octet;
+    ext->u.single_ASN1_type =
+        odr_create_Odr_oct(p->odr_out, (const unsigned char *) doc, len);
+    return apdu;
+}
+
+static Z_APDU *create_update_package(ZOOM_package p)
+{
+    Z_APDU *apdu = 0;
+    const char *first_db = "Default";
+    int num_db;
+    char **db = ZOOM_connection_get_databases(p->connection, p->options,
+                                              &num_db, p->odr_out);
+    const char *action = ZOOM_options_get(p->options, "action");
+    int recordIdOpaque_len;
+    const char *recordIdOpaque = ZOOM_options_getl(p->options, "recordIdOpaque",
+        &recordIdOpaque_len);
+    const char *recordIdNumber = ZOOM_options_get(p->options, "recordIdNumber");
+    int record_len;
+    const char *record_buf = ZOOM_options_getl(p->options, "record",
+        &record_len);
+    int recordOpaque_len;
+    const char *recordOpaque_buf = ZOOM_options_getl(p->options, "recordOpaque",
+        &recordOpaque_len);
+    const char *syntax_str = ZOOM_options_get(p->options, "syntax");
+    const char *version = ZOOM_options_get(p->options, "updateVersion");
+
+    const char *correlationInfo_note =
+        ZOOM_options_get(p->options, "correlationInfo.note");
+    const char *correlationInfo_id =
+        ZOOM_options_get(p->options, "correlationInfo.id");
+    int action_no = -1;
+    Odr_oid *syntax_oid = 0;
+    const Odr_oid *package_oid = yaz_oid_extserv_database_update;
+
+    if (!version)
+        version = "3";
+    if (!syntax_str)
+        syntax_str = "xml";
+    if (!record_buf && !recordOpaque_buf)
+    {
+        record_buf = "void";
+        record_len = 4;
+        syntax_str = "SUTRS";
+    }
+
+    if (syntax_str)
+    {
+        syntax_oid = yaz_string_to_oid_odr(yaz_oid_std(),
+                                           CLASS_RECSYN, syntax_str,
+                                           p->odr_out);
+    }
+    if (!syntax_oid)
+        return 0;
+
+    if (num_db > 0)
+        first_db = db[0];
+    
+    switch(*version)
+    {
+    case '1':
+        package_oid = yaz_oid_extserv_database_update_first_version;
+        /* old update does not support specialUpdate */
+        if (!action)
+            action = "recordInsert";
+        break;
+    case '2':
+        if (!action)
+            action = "specialUpdate";
+        package_oid = yaz_oid_extserv_database_update_second_version;
+        break;
+    case '3':
+        if (!action)
+            action = "specialUpdate";
+        package_oid = yaz_oid_extserv_database_update;
+        break;
+    default:
+        return 0;
+    }
+    
+    if (!strcmp(action, "recordInsert"))
+        action_no = Z_IUOriginPartToKeep_recordInsert;
+    else if (!strcmp(action, "recordReplace"))
+        action_no = Z_IUOriginPartToKeep_recordReplace;
+    else if (!strcmp(action, "recordDelete"))
+        action_no = Z_IUOriginPartToKeep_recordDelete;
+    else if (!strcmp(action, "elementUpdate"))
+        action_no = Z_IUOriginPartToKeep_elementUpdate;
+    else if (!strcmp(action, "specialUpdate"))
+        action_no = Z_IUOriginPartToKeep_specialUpdate;
+    else
+        return 0;
+
+    apdu = create_es_package(p, package_oid);
+    if (apdu)
+    {
+        Z_IUOriginPartToKeep *toKeep;
+        Z_IUSuppliedRecords *notToKeep;
+        Z_External *r = (Z_External *)
+            odr_malloc(p->odr_out, sizeof(*r));
+        const char *elementSetName =
+            ZOOM_options_get(p->options, "elementSetName");
+        
+        apdu->u.extendedServicesRequest->taskSpecificParameters = r;
+        
+        r->direct_reference = odr_oiddup(p->odr_out, package_oid);
+        r->descriptor = 0;
+        r->which = Z_External_update;
+        r->indirect_reference = 0;
+        r->u.update = (Z_IUUpdate *)
+            odr_malloc(p->odr_out, sizeof(*r->u.update));
+        
+        r->u.update->which = Z_IUUpdate_esRequest;
+        r->u.update->u.esRequest = (Z_IUUpdateEsRequest *)
+            odr_malloc(p->odr_out, sizeof(*r->u.update->u.esRequest));
+        toKeep = r->u.update->u.esRequest->toKeep = 
+            (Z_IUOriginPartToKeep *)
+            odr_malloc(p->odr_out, sizeof(*toKeep));
+        
+        toKeep->databaseName = odr_strdup(p->odr_out, first_db);
+        toKeep->schema = 0;
+        
+        toKeep->elementSetName = odr_strdup_null(p->odr_out, elementSetName);
+            
+        toKeep->actionQualifier = 0;
+        toKeep->action = odr_intdup(p->odr_out, action_no);
+        
+        notToKeep = r->u.update->u.esRequest->notToKeep = 
+            (Z_IUSuppliedRecords *)
+            odr_malloc(p->odr_out, sizeof(*notToKeep));
+        notToKeep->num = 1;
+        notToKeep->elements = (Z_IUSuppliedRecords_elem **)
+            odr_malloc(p->odr_out, sizeof(*notToKeep->elements));
+        notToKeep->elements[0] = (Z_IUSuppliedRecords_elem *)
+            odr_malloc(p->odr_out, sizeof(**notToKeep->elements));
+        notToKeep->elements[0]->which = Z_IUSuppliedRecords_elem_opaque;
+        if (recordIdOpaque)
+        {
+            notToKeep->elements[0]->u.opaque = 
+                odr_create_Odr_oct(p->odr_out,
+                                   (const unsigned char *) recordIdOpaque,
+                                   recordIdOpaque_len);
+        }
+        else if (recordIdNumber)
+        {
+            notToKeep->elements[0]->which = Z_IUSuppliedRecords_elem_number;
+            
+            notToKeep->elements[0]->u.number =
+                odr_intdup(p->odr_out, atoi(recordIdNumber));
+        }
+        else
+            notToKeep->elements[0]->u.opaque = 0;
+        notToKeep->elements[0]->supplementalId = 0;
+        if (correlationInfo_note || correlationInfo_id)
+        {
+            Z_IUCorrelationInfo *ci;
+            ci = notToKeep->elements[0]->correlationInfo =
+                (Z_IUCorrelationInfo *) odr_malloc(p->odr_out, sizeof(*ci));
+            ci->note = odr_strdup_null(p->odr_out, correlationInfo_note);
+            ci->id = correlationInfo_id ?
+                odr_intdup(p->odr_out, atoi(correlationInfo_id)) : 0;
+        }
+        else
+            notToKeep->elements[0]->correlationInfo = 0;
+        if (recordOpaque_buf)
+        {
+            notToKeep->elements[0]->record =
+                z_ext_record_oid_any(p->odr_out, syntax_oid,
+                                 recordOpaque_buf, recordOpaque_len);
+        }
+        else
+        {
+            notToKeep->elements[0]->record =
+                z_ext_record_oid(p->odr_out, syntax_oid,
+                                 record_buf, record_len);
+        }
+    }
+    if (0 && apdu)
+    {
+        ODR print = odr_createmem(ODR_PRINT);
+
+        z_APDU(print, &apdu, 0, 0);
+        odr_destroy(print);
+    }
+    return apdu;
+}
+
+
+static void otherInfo_attach(ZOOM_connection c, Z_APDU *a, ODR out)
+{
+    int i;
+    for (i = 0; i<200; i++)
+    {
+        size_t len;
+        Odr_oid *oid;
+        Z_OtherInformation **oi;
+        char buf[80];
+        const char *val;
+        const char *cp;
+
+        sprintf(buf, "otherInfo%d", i);
+        val = ZOOM_options_get(c->options, buf);
+        if (!val)
+            break;
+        cp = strchr(val, ':');
+        if (!cp)
+            continue;
+        len = cp - val;
+        if (len >= sizeof(buf))
+            len = sizeof(buf)-1;
+        memcpy(buf, val, len);
+        buf[len] = '\0';
+        
+        oid = yaz_string_to_oid_odr(yaz_oid_std(), CLASS_USERINFO,
+                                    buf, out);
+        if (!oid)
+            continue;
+        
+        yaz_oi_APDU(a, &oi);
+        yaz_oi_set_string_oid(oi, out, oid, 1, cp+1);
+    }
+}
+
+
+
+static int encode_APDU(ZOOM_connection c, Z_APDU *a, ODR out)
+{
+    assert(a);
+    if (c->cookie_out)
+    {
+        Z_OtherInformation **oi;
+        yaz_oi_APDU(a, &oi);
+        yaz_oi_set_string_oid(oi, out, yaz_oid_userinfo_cookie, 
+                              1, c->cookie_out);
+    }
+    if (c->client_IP)
+    {
+        Z_OtherInformation **oi;
+        yaz_oi_APDU(a, &oi);
+        yaz_oi_set_string_oid(oi, out, yaz_oid_userinfo_client_ip, 
+                              1, c->client_IP);
+    }
+    otherInfo_attach(c, a, out);
+    if (!z_APDU(out, &a, 0, 0))
+    {
+        FILE *outf = fopen("/tmp/apdu.txt", "a");
+        if (a && outf)
+        {
+            ODR odr_pr = odr_createmem(ODR_PRINT);
+            fprintf(outf, "a=%p\n", a);
+            odr_setprint(odr_pr, outf);
+            z_APDU(odr_pr, &a, 0, 0);
+            odr_destroy(odr_pr);
+        }
+        yaz_log(c->log_api, "%p encoding_APDU: encoding failed", c);
+        ZOOM_set_error(c, ZOOM_ERROR_ENCODE, 0);
+        odr_reset(out);
+        return -1;
+    }
+    if (c->odr_print)
+        z_APDU(c->odr_print, &a, 0, 0);
+    yaz_log(c->log_details, "%p encoding_APDU encoding OK", c);
+    return 0;
+}
+
+static zoom_ret send_APDU(ZOOM_connection c, Z_APDU *a)
+{
+    ZOOM_Event event;
+    assert(a);
+    if (encode_APDU(c, a, c->odr_out))
+        return zoom_complete;
+    yaz_log(c->log_details, "%p send APDU type=%d", c, a->which);
+    c->buf_out = odr_getbuf(c->odr_out, &c->len_out, 0);
+    event = ZOOM_Event_create(ZOOM_EVENT_SEND_APDU);
+    ZOOM_connection_put_event(c, event);
+    odr_reset(c->odr_out);
+    return ZOOM_send_buf(c);
+}
+
+zoom_ret ZOOM_connection_Z3950_send_init(ZOOM_connection c)
+{
+    Z_APDU *apdu = zget_APDU(c->odr_out, Z_APDU_initRequest);
+    Z_InitRequest *ireq = apdu->u.initRequest;
+    Z_IdAuthentication *auth = (Z_IdAuthentication *)
+        odr_malloc(c->odr_out, sizeof(*auth));
+
+    ODR_MASK_SET(ireq->options, Z_Options_search);
+    ODR_MASK_SET(ireq->options, Z_Options_present);
+    ODR_MASK_SET(ireq->options, Z_Options_scan);
+    ODR_MASK_SET(ireq->options, Z_Options_sort);
+    ODR_MASK_SET(ireq->options, Z_Options_extendedServices);
+    ODR_MASK_SET(ireq->options, Z_Options_namedResultSets);
+    
+    ODR_MASK_SET(ireq->protocolVersion, Z_ProtocolVersion_1);
+    ODR_MASK_SET(ireq->protocolVersion, Z_ProtocolVersion_2);
+    ODR_MASK_SET(ireq->protocolVersion, Z_ProtocolVersion_3);
+    
+    ireq->implementationId =
+        odr_prepend(c->odr_out,
+                    ZOOM_options_get(c->options, "implementationId"),
+                    ireq->implementationId);
+    
+    ireq->implementationName = 
+        odr_prepend(c->odr_out,
+                    ZOOM_options_get(c->options, "implementationName"),
+                    odr_prepend(c->odr_out, "ZOOM-C",
+                                ireq->implementationName));
+    
+    ireq->implementationVersion = 
+        odr_prepend(c->odr_out,
+                    ZOOM_options_get(c->options, "implementationVersion"),
+                                ireq->implementationVersion);
+    
+    *ireq->maximumRecordSize = c->maximum_record_size;
+    *ireq->preferredMessageSize = c->preferred_message_size;
+    
+    if (c->group || c->password)
+    {
+        Z_IdPass *pass = (Z_IdPass *) odr_malloc(c->odr_out, sizeof(*pass));
+        pass->groupId = odr_strdup_null(c->odr_out, c->group);
+        pass->userId = odr_strdup_null(c->odr_out, c->user);
+        pass->password = odr_strdup_null(c->odr_out, c->password);
+        auth->which = Z_IdAuthentication_idPass;
+        auth->u.idPass = pass;
+        ireq->idAuthentication = auth;
+    }
+    else if (c->user)
+    {
+        auth->which = Z_IdAuthentication_open;
+        auth->u.open = odr_strdup(c->odr_out, c->user);
+        ireq->idAuthentication = auth;
+    }
+    if (c->proxy)
+    {
+        yaz_oi_set_string_oid(&ireq->otherInfo, c->odr_out,
+                              yaz_oid_userinfo_proxy, 1, c->host_port);
+    }
+    if (c->charset || c->lang)
+    {
+        Z_OtherInformation **oi;
+        Z_OtherInformationUnit *oi_unit;
+        
+        yaz_oi_APDU(apdu, &oi);
+        
+        if ((oi_unit = yaz_oi_update(oi, c->odr_out, NULL, 0, 0)))
+        {
+            ODR_MASK_SET(ireq->options, Z_Options_negotiationModel);
+            oi_unit->which = Z_OtherInfo_externallyDefinedInfo;
+            oi_unit->information.externallyDefinedInfo =
+                yaz_set_proposal_charneg_list(c->odr_out, " ",
+                                              c->charset, c->lang, 1);
+        }
+    }
+    assert(apdu);
+    return send_APDU(c, apdu);
+}
+
+zoom_ret ZOOM_connection_Z3950_send_search(ZOOM_connection c)
+{
+    ZOOM_resultset r;
+    int lslb, ssub, mspn;
+    const char *syntax;
+    Z_APDU *apdu = zget_APDU(c->odr_out, Z_APDU_searchRequest);
+    Z_SearchRequest *search_req = apdu->u.searchRequest;
+    const char *elementSetName;
+    const char *smallSetElementSetName;
+    const char *mediumSetElementSetName;
+    const char *facets;
+
+    assert(c->tasks);
+    assert(c->tasks->which == ZOOM_TASK_SEARCH);
+
+    r = c->tasks->u.search.resultset;
+
+    yaz_log(c->log_details, "%p ZOOM_connection_send_search set=%p", c, r);
+
+    elementSetName =
+        ZOOM_options_get(r->options, "elementSetName");
+    smallSetElementSetName  =
+        ZOOM_options_get(r->options, "smallSetElementSetName");
+    mediumSetElementSetName =
+        ZOOM_options_get(r->options, "mediumSetElementSetName");
+
+    if (!smallSetElementSetName)
+        smallSetElementSetName = elementSetName;
+
+    if (!mediumSetElementSetName)
+        mediumSetElementSetName = elementSetName;
+
+    facets = ZOOM_options_get(r->options, "facets");
+    if (facets) {
+        Z_FacetList *facet_list = yaz_pqf_parse_facet_list(c->odr_out, facets);
+        if (facet_list) {
+            Z_OtherInformation **oi;
+            yaz_oi_APDU(apdu, &oi);
+            if (facet_list) {
+                yaz_oi_set_facetlist(oi, c->odr_out, facet_list);
+            }
+        }
+    }
+
+    assert(r);
+    assert(r->query);
+
+    /* prepare query for the search request */
+    search_req->query = ZOOM_query_get_Z_Query(r->query);
+    if (!search_req->query)
+    {
+        ZOOM_set_error(c, ZOOM_ERROR_INVALID_QUERY, 0);
+        return zoom_complete;
+    }
+    if (search_req->query->which == Z_Query_type_1 || 
+        search_req->query->which == Z_Query_type_101)
+    {
+        const char *cp = ZOOM_options_get(r->options, "rpnCharset");
+        if (cp)
+        {
+            yaz_iconv_t cd = yaz_iconv_open(cp, "UTF-8");
+            if (cd)
+            {
+                int r;
+                search_req->query = yaz_copy_Z_Query(search_req->query,
+                                                     c->odr_out);
+                
+                r = yaz_query_charset_convert_rpnquery_check(
+                    search_req->query->u.type_1,
+                    c->odr_out, cd);
+                yaz_iconv_close(cd);
+                if (r)
+                {  /* query could not be char converted */
+                    ZOOM_set_error(c, ZOOM_ERROR_INVALID_QUERY, 0);
+                    return zoom_complete;
+                }
+            }
+        }
+    }
+    search_req->databaseNames = r->databaseNames;
+    search_req->num_databaseNames = r->num_databaseNames;
+
+    /* get syntax (no need to provide unless piggyback is in effect) */
+    syntax = c->tasks->u.search.syntax;
+
+    lslb = ZOOM_options_get_int(r->options, "largeSetLowerBound", -1);
+    ssub = ZOOM_options_get_int(r->options, "smallSetUpperBound", -1);
+    mspn = ZOOM_options_get_int(r->options, "mediumSetPresentNumber", -1);
+    if (lslb != -1 && ssub != -1 && mspn != -1)
+    {
+        /* So're a Z39.50 expert? Let's hope you don't do sort */
+        *search_req->largeSetLowerBound = lslb;
+        *search_req->smallSetUpperBound = ssub;
+        *search_req->mediumSetPresentNumber = mspn;
+    }
+    else if (c->tasks->u.search.start == 0 && c->tasks->u.search.count > 0
+             && r->piggyback && !r->r_sort_spec && !r->schema)
+    {
+        /* Regular piggyback - do it unless we're going to do sort */
+        *search_req->largeSetLowerBound = 2000000000;
+        *search_req->smallSetUpperBound = 1;
+        *search_req->mediumSetPresentNumber = 
+            r->step>0 ? r->step : c->tasks->u.search.count;
+    }
+    else
+    {
+        /* non-piggyback. Need not provide elementsets or syntaxes .. */
+        smallSetElementSetName = 0;
+        mediumSetElementSetName = 0;
+        syntax = 0;
+    }
+    if (smallSetElementSetName && *smallSetElementSetName)
+    {
+        Z_ElementSetNames *esn = (Z_ElementSetNames *)
+            odr_malloc(c->odr_out, sizeof(*esn));
+        
+        esn->which = Z_ElementSetNames_generic;
+        esn->u.generic = odr_strdup(c->odr_out, smallSetElementSetName);
+        search_req->smallSetElementSetNames = esn;
+    }
+    if (mediumSetElementSetName && *mediumSetElementSetName)
+    {
+        Z_ElementSetNames *esn =(Z_ElementSetNames *)
+            odr_malloc(c->odr_out, sizeof(*esn));
+        
+        esn->which = Z_ElementSetNames_generic;
+        esn->u.generic = odr_strdup(c->odr_out, mediumSetElementSetName);
+        search_req->mediumSetElementSetNames = esn;
+    }
+    if (syntax)
+        search_req->preferredRecordSyntax =
+            zoom_yaz_str_to_z3950oid(c, CLASS_RECSYN, syntax);
+    
+    if (!r->setname)
+    {
+        if (c->support_named_resultsets)
+        {
+            char setname[14];
+            int ord;
+            /* find the lowest unused ordinal so that we re-use
+               result sets on the server. */
+            for (ord = 1; ; ord++)
+            {
+#if ZOOM_RESULT_LISTS
+                ZOOM_resultsets rsp;
+                sprintf(setname, "%d", ord);
+                for (rsp = c->resultsets; rsp; rsp = rsp->next)
+                    if (rsp->resultset->setname && !strcmp(rsp->resultset->setname, setname))
+                        break;
+                if (!rsp)
+                    break;
+#else
+                ZOOM_resultset rp;
+                sprintf(setname, "%d", ord);
+                for (rp = c->resultsets; rp; rp = rp->next)
+                    if (rp->setname && !strcmp(rp->setname, setname))
+                        break;
+                if (!rp)
+                    break;
+#endif
+
+            }
+            r->setname = xstrdup(setname);
+            yaz_log(c->log_details, "%p ZOOM_connection_send_search: "
+                    "allocating set %s", c, r->setname);
+        }
+        else
+        {
+            yaz_log(c->log_details, "%p ZOOM_connection_send_search: using "
+                    "default set", c);
+            r->setname = xstrdup("default");
+        }
+        ZOOM_options_set(r->options, "setname", r->setname);
+    }
+    search_req->resultSetName = odr_strdup(c->odr_out, r->setname);
+    return send_APDU(c, apdu);
+}
+
+zoom_ret ZOOM_connection_Z3950_send_scan(ZOOM_connection c)
+{
+    ZOOM_scanset scan;
+    Z_APDU *apdu = zget_APDU(c->odr_out, Z_APDU_scanRequest);
+    Z_ScanRequest *req = apdu->u.scanRequest;
+    Z_Query *z_query;
+
+    yaz_log(c->log_details, "%p send_scan", c);
+    if (!c->tasks)
+        return zoom_complete;
+    assert (c->tasks->which == ZOOM_TASK_SCAN);
+    scan = c->tasks->u.scan.scan;
+
+    z_query = ZOOM_query_get_Z_Query(scan->query);
+
+    /* Z39.50 scan can only carry RPN */
+    if (z_query->which == Z_Query_type_1 ||
+        z_query->which == Z_Query_type_101)
+    {
+        Z_RPNQuery *rpn = z_query->u.type_1;
+        const char *cp = ZOOM_options_get(scan->options, "rpnCharset");
+        if (cp)
+        {
+            yaz_iconv_t cd = yaz_iconv_open(cp, "UTF-8");
+            if (cd)
+            {
+                rpn = yaz_copy_z_RPNQuery(rpn, c->odr_out);
+
+                yaz_query_charset_convert_rpnquery(
+                    rpn, c->odr_out, cd);
+                yaz_iconv_close(cd);
+            }
+        }
+        req->attributeSet = rpn->attributeSetId;
+        if (!req->attributeSet)
+            req->attributeSet = odr_oiddup(c->odr_out, yaz_oid_attset_bib_1);
+        if (rpn->RPNStructure->which == Z_RPNStructure_simple &&
+            rpn->RPNStructure->u.simple->which == Z_Operand_APT)
+        {
+            req->termListAndStartPoint =
+                rpn->RPNStructure->u.simple->u.attributesPlusTerm;
+        }
+        else
+        {
+            ZOOM_set_error(c, ZOOM_ERROR_INVALID_QUERY, 0);
+            return zoom_complete;
+        }
+    }
+    else
+    {
+        ZOOM_set_error(c, ZOOM_ERROR_UNSUPPORTED_QUERY, 0);
+        return zoom_complete;
+    }
+
+    *req->numberOfTermsRequested =
+        ZOOM_options_get_int(scan->options, "number", 20);
+
+    req->preferredPositionInResponse =
+        odr_intdup(c->odr_out,
+                   ZOOM_options_get_int(scan->options, "position", 1));
+
+    req->stepSize =
+        odr_intdup(c->odr_out,
+                   ZOOM_options_get_int(scan->options, "stepSize", 0));
+    
+    req->databaseNames = scan->databaseNames;
+    req->num_databaseNames = scan->num_databaseNames;
+
+    return send_APDU(c, apdu);
+}
+
+ZOOM_API(void)
+    ZOOM_package_send(ZOOM_package p, const char *type)
+{
+    Z_APDU *apdu = 0;
+    ZOOM_connection c;
+    if (!p)
+        return;
+    c = p->connection;
+    odr_reset(p->odr_out);
+    xfree(p->buf_out);
+    p->buf_out = 0;
+    if (!strcmp(type, "itemorder"))
+    {
+        apdu = create_es_package(p, yaz_oid_extserv_item_order);
+        if (apdu)
+        {
+            Z_External *r = (Z_External *) odr_malloc(p->odr_out, sizeof(*r));
+            
+            r->direct_reference = 
+                odr_oiddup(p->odr_out, yaz_oid_extserv_item_order);
+            r->descriptor = 0;
+            r->which = Z_External_itemOrder;
+            r->indirect_reference = 0;
+            r->u.itemOrder = encode_item_order(p);
+
+            apdu->u.extendedServicesRequest->taskSpecificParameters = r;
+        }
+    }
+    else if (!strcmp(type, "create"))  /* create database */
+    {
+        apdu = create_admin_package(p, Z_ESAdminOriginPartToKeep_create,
+                                    0, 0);
+    }   
+    else if (!strcmp(type, "drop"))  /* drop database */
+    {
+        apdu = create_admin_package(p, Z_ESAdminOriginPartToKeep_drop,
+                                    0, 0);
+    }
+    else if (!strcmp(type, "commit"))  /* commit changes */
+    {
+        apdu = create_admin_package(p, Z_ESAdminOriginPartToKeep_commit,
+                                    0, 0);
+    }
+    else if (!strcmp(type, "update")) /* update record(s) */
+    {
+        apdu = create_update_package(p);
+    }
+    else if (!strcmp(type, "xmlupdate"))
+    {
+        apdu = create_xmlupdate_package(p);
+    }
+    if (apdu)
+    {
+        if (encode_APDU(p->connection, apdu, p->odr_out) == 0)
+        {
+            char *buf;
+
+            ZOOM_task task = ZOOM_connection_add_task(c, ZOOM_TASK_PACKAGE);
+            task->u.package = p;
+            buf = odr_getbuf(p->odr_out, &p->len_out, 0);
+            p->buf_out = (char *) xmalloc(p->len_out);
+            memcpy(p->buf_out, buf, p->len_out);
+            
+            (p->refcount)++;
+            if (!c->async)
+            {
+                while (ZOOM_event(1, &c))
+                    ;
+            }
+        }
+    }
+}
+
+
+static void handle_Z3950_records(ZOOM_connection c, Z_Records *sr,
+                                 int present_phase);
+
+static void response_default_diag(ZOOM_connection c, Z_DefaultDiagFormat *r)
+{
+    char oid_name_buf[OID_STR_MAX];
+    const char *oid_name;
+    char *addinfo = 0;
+
+    oid_name = yaz_oid_to_string_buf(r->diagnosticSetId, 0, oid_name_buf);
+    switch (r->which)
+    {
+    case Z_DefaultDiagFormat_v2Addinfo:
+        addinfo = r->u.v2Addinfo;
+        break;
+    case Z_DefaultDiagFormat_v3Addinfo:
+        addinfo = r->u.v3Addinfo;
+        break;
+    }
+    xfree(c->addinfo);
+    c->addinfo = 0;
+    ZOOM_set_dset_error(c, *r->condition, oid_name, addinfo, 0);
+}
+
+static void response_diag(ZOOM_connection c, Z_DiagRec *p)
+{
+    if (p->which != Z_DiagRec_defaultFormat)
+        ZOOM_set_error(c, ZOOM_ERROR_DECODE, 0);
+    else
+        response_default_diag(c, p->u.defaultFormat);
+}
+
+static int es_response_taskpackage_update(ZOOM_connection c,
+               Z_IUUpdateTaskPackage *utp)
+{
+       if (utp && utp->targetPart)
+       {
+               Z_IUTargetPart *targetPart = utp->targetPart;
+               switch ( *targetPart->updateStatus ) {
+                       case Z_IUTargetPart_success:
+                               ZOOM_options_set(c->tasks->u.package->options,"updateStatus", "success");
+                               break;
+                       case Z_IUTargetPart_partial:
+                               ZOOM_options_set(c->tasks->u.package->options,"updateStatus", "partial");
+                               break;
+                       case Z_IUTargetPart_failure:
+                               ZOOM_options_set(c->tasks->u.package->options,"updateStatus", "failure");
+                               if (targetPart->globalDiagnostics && targetPart->num_globalDiagnostics > 0)
+                                       response_diag(c, targetPart->globalDiagnostics[0]);
+                               break;
+               }
+               // NOTE: Individual record status, surrogate diagnostics, and supplemental diagnostics ARE NOT REPORTED.
+       }
+    return 1;
+}
+
+static int es_response_taskpackage(ZOOM_connection c,
+                                   Z_TaskPackage *taskPackage)
+{
+       // targetReference
+       Odr_oct *id = taskPackage->targetReference;
+       if (id)
+               ZOOM_options_setl(c->tasks->u.package->options,
+                                                       "targetReference", (char*) id->buf, id->len);
+       
+       // taskStatus
+       switch ( *taskPackage->taskStatus ) {
+               case Z_TaskPackage_pending:
+                       ZOOM_options_set(c->tasks->u.package->options,"taskStatus", "pending");
+                       break;
+               case Z_TaskPackage_active:
+                       ZOOM_options_set(c->tasks->u.package->options,"taskStatus", "active");
+                       break;
+               case Z_TaskPackage_complete:
+                       ZOOM_options_set(c->tasks->u.package->options,"taskStatus", "complete");
+                       break;
+               case Z_TaskPackage_aborted:
+                       ZOOM_options_set(c->tasks->u.package->options,"taskStatus", "aborted");
+                       if ( taskPackage->num_packageDiagnostics && taskPackage->packageDiagnostics )
+                               response_diag(c, taskPackage->packageDiagnostics[0]);
+                       break;
+       }
+       
+       // taskSpecificParameters
+       // NOTE: Only Update implemented, no others.
+       if ( taskPackage->taskSpecificParameters->which == Z_External_update ) {
+                       Z_IUUpdateTaskPackage *utp = taskPackage->taskSpecificParameters->u.update->u.taskPackage;
+                       es_response_taskpackage_update(c, utp);
+       }
+       return 1;
+}
+
+
+static int handle_Z3950_es_response(ZOOM_connection c,
+                                    Z_ExtendedServicesResponse *res)
+{
+    if (!c->tasks || c->tasks->which != ZOOM_TASK_PACKAGE)
+        return 0;
+    switch (*res->operationStatus) {
+        case Z_ExtendedServicesResponse_done:
+            ZOOM_options_set(c->tasks->u.package->options,"operationStatus", "done");
+            break;
+        case Z_ExtendedServicesResponse_accepted:
+            ZOOM_options_set(c->tasks->u.package->options,"operationStatus", "accepted");
+            break;
+        case Z_ExtendedServicesResponse_failure:
+            ZOOM_options_set(c->tasks->u.package->options,"operationStatus", "failure");
+            if (res->diagnostics && res->num_diagnostics > 0)
+                response_diag(c, res->diagnostics[0]);
+            break;
+    }
+    if (res->taskPackage &&
+        res->taskPackage->which == Z_External_extendedService)
+    {
+        Z_TaskPackage *taskPackage = res->taskPackage->u.extendedService;
+        es_response_taskpackage(c, taskPackage);
+    }
+    if (res->taskPackage && 
+        res->taskPackage->which == Z_External_octet)
+    {
+        Odr_oct *doc = res->taskPackage->u.octet_aligned;
+        ZOOM_options_setl(c->tasks->u.package->options,
+                          "xmlUpdateDoc", (char*) doc->buf, doc->len);
+    }
+    return 1;
+}
+
+static void interpret_init_diag(ZOOM_connection c,
+                                Z_DiagnosticFormat *diag)
+{
+    if (diag->num > 0)
+    {
+        Z_DiagnosticFormat_s *ds = diag->elements[0];
+        if (ds->which == Z_DiagnosticFormat_s_defaultDiagRec)
+            response_default_diag(c, ds->u.defaultDiagRec);
+    }
+}
+
+
+static void interpret_otherinformation_field(ZOOM_connection c,
+                                             Z_OtherInformation *ui)
+{
+    int i;
+    for (i = 0; i < ui->num_elements; i++)
+    {
+        Z_OtherInformationUnit *unit = ui->list[i];
+        if (unit->which == Z_OtherInfo_externallyDefinedInfo &&
+            unit->information.externallyDefinedInfo &&
+            unit->information.externallyDefinedInfo->which ==
+            Z_External_diag1) 
+        {
+            interpret_init_diag(c, unit->information.externallyDefinedInfo->u.diag1);
+        } 
+    }
+}
+
+static char *get_term_cstr(ODR odr, Z_Term *term) {
+
+    switch (term->which) {
+    case Z_Term_general:
+            return odr_strdupn(odr, (const char *) term->u.general->buf, (size_t) term->u.general->len);
+        break;
+    case Z_Term_characterString:
+        return odr_strdup(odr, term->u.characterString);
+    }
+    return 0;
+}
+
+static ZOOM_facet_field get_zoom_facet_field(ODR odr, Z_FacetField *facet) {
+    int term_index;
+    struct yaz_facet_attr attr_values;
+    ZOOM_facet_field facet_field = odr_malloc(odr, sizeof(*facet_field));
+    yaz_facet_attr_init(&attr_values);
+    yaz_facet_attr_get_z_attributes(facet->attributes, &attr_values);
+    facet_field->facet_name = odr_strdup(odr, attr_values.useattr);
+    facet_field->num_terms = facet->num_terms;
+    yaz_log(YLOG_DEBUG, "ZOOM_facet_field %s %d terms %d", attr_values.useattr, attr_values.limit, facet->num_terms);
+    facet_field->facet_terms = odr_malloc(odr, facet_field->num_terms * sizeof(*facet_field->facet_terms));
+    for (term_index = 0 ; term_index < facet->num_terms; term_index++) {
+        Z_FacetTerm *facetTerm = facet->terms[term_index];
+        facet_field->facet_terms[term_index].frequency = *facetTerm->count;
+        facet_field->facet_terms[term_index].term = get_term_cstr(odr, facetTerm->term);
+        yaz_log(YLOG_DEBUG, "    term[%d] %s %d",
+                term_index, facet_field->facet_terms[term_index].term, facet_field->facet_terms[term_index].frequency);
+    }
+    return facet_field;
+}
+
+static void handle_facet_result(ZOOM_connection c, ZOOM_resultset r,
+                                Z_OtherInformation *o)
+{
+    int i;
+    for (i = 0; o && i < o->num_elements; i++)
+    {
+        if (o->list[i]->which == Z_OtherInfo_externallyDefinedInfo)
+        {
+            Z_External *ext = o->list[i]->information.externallyDefinedInfo;
+            if (ext->which == Z_External_userFacets)
+            {
+                int j;
+                Z_FacetList *fl = ext->u.facetList;
+                r->num_facets   = fl->num;
+                yaz_log(YLOG_DEBUG, "Facets found: %d", fl->num);
+                r->facets       =  odr_malloc(r->odr, r->num_facets * sizeof(*r->facets));
+                r->facets_names =  odr_malloc(r->odr, r->num_facets * sizeof(*r->facets_names));
+                for (j = 0; j < fl->num; j++)
+                {
+                    r->facets[j] = get_zoom_facet_field(r->odr, fl->elements[j]);
+                    if (!r->facets[j])
+                        yaz_log(YLOG_DEBUG, "Facet field missing on index %d !", j);
+                    r->facets_names[j] = (char *) ZOOM_facet_field_name(r->facets[j]);
+                }
+            }
+        }
+    }
+}
+
+static void handle_queryExpressionTerm(ZOOM_options opt, const char *name,
+                                       Z_Term *term)
+{
+    switch (term->which)
+    {
+    case Z_Term_general:
+        ZOOM_options_setl(opt, name,
+                          (const char *)(term->u.general->buf), 
+                          term->u.general->len);
+        break;
+    case Z_Term_characterString:
+        ZOOM_options_set(opt, name, term->u.characterString);
+        break;
+    case Z_Term_numeric:
+        ZOOM_options_set_int(opt, name, *term->u.numeric);
+        break;
+    }
+}
+
+static void handle_queryExpression(ZOOM_options opt, const char *name,
+                                   Z_QueryExpression *exp)
+{
+    char opt_name[80];
+    
+    switch (exp->which)
+    {
+    case Z_QueryExpression_term:
+        if (exp->u.term && exp->u.term->queryTerm)
+        {
+            sprintf(opt_name, "%s.term", name);
+            handle_queryExpressionTerm(opt, opt_name, exp->u.term->queryTerm);
+        }
+        break;
+    case Z_QueryExpression_query:
+        break;
+    }
+}
+
+
+static void handle_search_result(ZOOM_connection c, ZOOM_resultset resultset,
+                                Z_OtherInformation *o)
+{
+    int i;
+    for (i = 0; o && i < o->num_elements; i++)
+    {
+        if (o->list[i]->which == Z_OtherInfo_externallyDefinedInfo)
+        {
+            Z_External *ext = o->list[i]->information.externallyDefinedInfo;
+            
+            if (ext->which == Z_External_searchResult1)
+            {
+                int j;
+                Z_SearchInfoReport *sr = ext->u.searchResult1;
+                
+                if (sr->num)
+                    ZOOM_options_set_int(
+                        resultset->options, "searchresult.size", sr->num);
+
+                for (j = 0; j < sr->num; j++)
+                {
+                    Z_SearchInfoReport_s *ent =
+                        ext->u.searchResult1->elements[j];
+                    char pref[80];
+                    
+                    sprintf(pref, "searchresult.%d", j);
+
+                    if (ent->subqueryId)
+                    {
+                        char opt_name[80];
+                        sprintf(opt_name, "%s.id", pref);
+                        ZOOM_options_set(resultset->options, opt_name,
+                                         ent->subqueryId);
+                    }
+                    if (ent->subqueryExpression)
+                    {
+                        char opt_name[80];
+                        sprintf(opt_name, "%s.subquery", pref);
+                        handle_queryExpression(resultset->options, opt_name,
+                                               ent->subqueryExpression);
+                    }
+                    if (ent->subqueryInterpretation)
+                    {
+                        char opt_name[80];
+                        sprintf(opt_name, "%s.interpretation", pref);
+                        handle_queryExpression(resultset->options, opt_name,
+                                               ent->subqueryInterpretation);
+                    }
+                    if (ent->subqueryRecommendation)
+                    {
+                        char opt_name[80];
+                        sprintf(opt_name, "%s.recommendation", pref);
+                        handle_queryExpression(resultset->options, opt_name,
+                                               ent->subqueryRecommendation);
+                    }
+                    if (ent->subqueryCount)
+                    {
+                        char opt_name[80];
+                        sprintf(opt_name, "%s.count", pref);
+                        ZOOM_options_set_int(resultset->options, opt_name,
+                                             *ent->subqueryCount);
+                    }                                             
+                }
+            }
+        }
+    }
+}
+
+static void handle_Z3950_search_response(ZOOM_connection c,
+                                         Z_SearchResponse *sr)
+{
+    ZOOM_resultset resultset;
+    ZOOM_Event event;
+
+    if (!c->tasks || c->tasks->which != ZOOM_TASK_SEARCH)
+        return ;
+
+    event = ZOOM_Event_create(ZOOM_EVENT_RECV_SEARCH);
+    ZOOM_connection_put_event(c, event);
+
+    resultset = c->tasks->u.search.resultset;
+
+    if (sr->resultSetStatus)
+    {
+        ZOOM_options_set_int(resultset->options, "resultSetStatus",
+                             *sr->resultSetStatus);
+    }
+    if (sr->presentStatus)
+    {
+        ZOOM_options_set_int(resultset->options, "presentStatus",
+                             *sr->presentStatus);
+    }
+    handle_search_result(c, resultset, sr->additionalSearchInfo);
+
+    handle_facet_result(c, resultset, sr->additionalSearchInfo);
+
+    resultset->size = *sr->resultCount;
+    handle_Z3950_records(c, sr->records, 0);
+}
+
+static void handle_Z3950_sort_response(ZOOM_connection c, Z_SortResponse *res)
+{
+    if (res->diagnostics && res->num_diagnostics > 0)
+        response_diag(c, res->diagnostics[0]);
+}
+
+static int handle_Z3950_scan_response(ZOOM_connection c, Z_ScanResponse *res)
+{
+    NMEM nmem = odr_extract_mem(c->odr_in);
+    ZOOM_scanset scan;
+
+    if (!c->tasks || c->tasks->which != ZOOM_TASK_SCAN)
+        return 0;
+    scan = c->tasks->u.scan.scan;
+
+    if (res->entries && res->entries->nonsurrogateDiagnostics)
+        response_diag(c, res->entries->nonsurrogateDiagnostics[0]);
+    scan->scan_response = res;
+    scan->srw_scan_response = 0;
+    nmem_transfer(odr_getmem(scan->odr), nmem);
+    if (res->stepSize)
+        ZOOM_options_set_int(scan->options, "stepSize", *res->stepSize);
+    if (res->positionOfTerm)
+        ZOOM_options_set_int(scan->options, "position", *res->positionOfTerm);
+    if (res->scanStatus)
+        ZOOM_options_set_int(scan->options, "scanStatus", *res->scanStatus);
+    if (res->numberOfEntriesReturned)
+        ZOOM_options_set_int(scan->options, "number",
+                             *res->numberOfEntriesReturned);
+    nmem_destroy(nmem);
+    return 1;
+}
+
+static void handle_Z3950_records(ZOOM_connection c, Z_Records *sr,
+                                 int present_phase)
+{
+    ZOOM_resultset resultset;
+    int *start, *count;
+    const char *syntax = 0, *elementSetName = 0;
+
+    if (!c->tasks)
+        return ;
+    switch (c->tasks->which)
+    {
+    case ZOOM_TASK_SEARCH:
+        resultset = c->tasks->u.search.resultset;
+        start = &c->tasks->u.search.start;
+        count = &c->tasks->u.search.count;
+        syntax = c->tasks->u.search.syntax;
+        elementSetName = c->tasks->u.search.elementSetName;
+        break;
+    case ZOOM_TASK_RETRIEVE:
+        resultset = c->tasks->u.retrieve.resultset;        
+        start = &c->tasks->u.retrieve.start;
+        count = &c->tasks->u.retrieve.count;
+        syntax = c->tasks->u.retrieve.syntax;
+        elementSetName = c->tasks->u.retrieve.elementSetName;
+        break;
+    default:
+        return;
+    }
+    if (sr && sr->which == Z_Records_NSD)
+        response_default_diag(c, sr->u.nonSurrogateDiagnostic);
+    else if (sr && sr->which == Z_Records_multipleNSD)
+    {
+        if (sr->u.multipleNonSurDiagnostics->num_diagRecs >= 1)
+            response_diag(c, sr->u.multipleNonSurDiagnostics->diagRecs[0]);
+        else
+            ZOOM_set_error(c, ZOOM_ERROR_DECODE, 0);
+    }
+    else 
+    {
+        if (*count + *start > resultset->size)
+            *count = resultset->size - *start;
+        if (*count < 0)
+            *count = 0;
+        if (sr && sr->which == Z_Records_DBOSD)
+        {
+            int i;
+            NMEM nmem = odr_extract_mem(c->odr_in);
+            Z_NamePlusRecordList *p =
+                sr->u.databaseOrSurDiagnostics;
+            for (i = 0; i<p->num_records; i++)
+            {
+                ZOOM_record_cache_add(resultset, p->records[i], i + *start,
+                                      syntax, elementSetName,
+                                      elementSetName, 0);
+            }
+            *count -= i;
+            if (*count < 0)
+                *count = 0;
+            *start += i;
+            yaz_log(c->log_details, 
+                    "handle_records resultset=%p start=%d count=%d",
+                    resultset, *start, *count);
+
+            /* transfer our response to search_nmem .. we need it later */
+            nmem_transfer(odr_getmem(resultset->odr), nmem);
+            nmem_destroy(nmem);
+            if (present_phase && p->num_records == 0)
+            {
+                /* present response and we didn't get any records! */
+                Z_NamePlusRecord *myrec = 
+                    zget_surrogateDiagRec(
+                        resultset->odr, 0, 
+                        YAZ_BIB1_SYSTEM_ERROR_IN_PRESENTING_RECORDS,
+                        "ZOOM C generated. Present phase and no records");
+                ZOOM_record_cache_add(resultset, myrec, *start,
+                                      syntax, elementSetName, 0, 0);
+            }
+        }
+        else if (present_phase)
+        {
+            /* present response and we didn't get any records! */
+            Z_NamePlusRecord *myrec = 
+                zget_surrogateDiagRec(
+                    resultset->odr, 0,
+                    YAZ_BIB1_SYSTEM_ERROR_IN_PRESENTING_RECORDS,
+                    "ZOOM C generated: Present response and no records");
+            ZOOM_record_cache_add(resultset, myrec, *start,
+                                  syntax, elementSetName, 0, 0);
+        }
+    }
+}
+
+static void handle_Z3950_present_response(ZOOM_connection c,
+                                          Z_PresentResponse *pr)
+{
+    handle_Z3950_records(c, pr->records, 1);
+}
+
+static void set_init_option(const char *name, void *clientData)
+{
+    ZOOM_connection c = (ZOOM_connection) clientData;
+    char buf[80];
+
+    sprintf(buf, "init_opt_%.70s", name);
+    ZOOM_connection_option_set(c, buf, "1");
+}
+
+
+zoom_ret send_Z3950_sort(ZOOM_connection c, ZOOM_resultset resultset)
+{
+    if (c->error)
+        resultset->r_sort_spec = 0;
+    if (resultset->r_sort_spec)
+    {
+        Z_APDU *apdu = zget_APDU(c->odr_out, Z_APDU_sortRequest);
+        Z_SortRequest *req = apdu->u.sortRequest;
+        
+        req->num_inputResultSetNames = 1;
+        req->inputResultSetNames = (Z_InternationalString **)
+            odr_malloc(c->odr_out, sizeof(*req->inputResultSetNames));
+        req->inputResultSetNames[0] =
+            odr_strdup(c->odr_out, resultset->setname);
+        req->sortedResultSetName = odr_strdup(c->odr_out, resultset->setname);
+        req->sortSequence = resultset->r_sort_spec;
+        resultset->r_sort_spec = 0;
+        return send_APDU(c, apdu);
+    }
+    return zoom_complete;
+}
+
+zoom_ret send_Z3950_present(ZOOM_connection c)
+{
+    Z_APDU *apdu = 0;
+    Z_PresentRequest *req = 0;
+    int i = 0;
+    const char *syntax = 0;
+    const char *elementSetName = 0;
+    ZOOM_resultset  resultset;
+    int *start, *count;
+
+    if (!c->tasks)
+    {
+        yaz_log(c->log_details, "%p send_present no tasks", c);
+        return zoom_complete;
+    }
+    
+    switch (c->tasks->which)
+    {
+    case ZOOM_TASK_SEARCH:
+        resultset = c->tasks->u.search.resultset;
+        start = &c->tasks->u.search.start;
+        count = &c->tasks->u.search.count;
+        syntax = c->tasks->u.search.syntax;
+        elementSetName = c->tasks->u.search.elementSetName;
+        break;
+    case ZOOM_TASK_RETRIEVE:
+        resultset = c->tasks->u.retrieve.resultset;
+        start = &c->tasks->u.retrieve.start;
+        count = &c->tasks->u.retrieve.count;
+        syntax = c->tasks->u.retrieve.syntax;
+        elementSetName = c->tasks->u.retrieve.elementSetName;
+        break;
+    default:
+        return zoom_complete;
+    }
+    yaz_log(c->log_details, "%p send_present start=%d count=%d",
+            c, *start, *count);
+
+    if (*start < 0 || *count < 0 || *start + *count > resultset->size)
+    {
+        ZOOM_set_dset_error(c, YAZ_BIB1_PRESENT_REQUEST_OUT_OF_RANGE, "Bib-1",
+                       "", 0);
+    }
+    if (c->error)                  /* don't continue on error */
+        return zoom_complete;
+    yaz_log(c->log_details, "send_present resultset=%p start=%d count=%d",
+            resultset, *start, *count);
+
+    for (i = 0; i < *count; i++)
+    {
+        ZOOM_record rec =
+            ZOOM_record_cache_lookup(resultset, i + *start,
+                                     syntax, elementSetName);
+        if (!rec)
+            break;
+        else
+        {
+            ZOOM_Event event = ZOOM_Event_create(ZOOM_EVENT_RECV_RECORD);
+            ZOOM_connection_put_event(c, event);
+        }
+    }
+    *start += i;
+    *count -= i;
+
+    if (*count == 0)
+    {
+        yaz_log(c->log_details, "%p send_present skip=%d no more to fetch", c, i);
+        return zoom_complete;
+    }
+
+    apdu = zget_APDU(c->odr_out, Z_APDU_presentRequest);
+    req = apdu->u.presentRequest;
+
+    if (i)
+        yaz_log(c->log_details, "%p send_present skip=%d", c, i);
+
+    *req->resultSetStartPoint = *start + 1;
+
+    if (resultset->step > 0 && resultset->step < *count)
+        *req->numberOfRecordsRequested = resultset->step;
+    else
+        *req->numberOfRecordsRequested = *count;
+    
+    if (*req->numberOfRecordsRequested + *start > resultset->size)
+        *req->numberOfRecordsRequested = resultset->size - *start;
+    assert(*req->numberOfRecordsRequested > 0);
+
+    if (syntax && *syntax)
+        req->preferredRecordSyntax =
+            zoom_yaz_str_to_z3950oid(c, CLASS_RECSYN, syntax);
+
+    if (resultset->schema && *resultset->schema)
+    {
+        Z_RecordComposition *compo = (Z_RecordComposition *)
+            odr_malloc(c->odr_out, sizeof(*compo));
+
+        req->recordComposition = compo;
+        compo->which = Z_RecordComp_complex;
+        compo->u.complex = (Z_CompSpec *)
+            odr_malloc(c->odr_out, sizeof(*compo->u.complex));
+        compo->u.complex->selectAlternativeSyntax = (bool_t *) 
+            odr_malloc(c->odr_out, sizeof(bool_t));
+        *compo->u.complex->selectAlternativeSyntax = 0;
+
+        compo->u.complex->generic = (Z_Specification *)
+            odr_malloc(c->odr_out, sizeof(*compo->u.complex->generic));
+
+        compo->u.complex->generic->which = Z_Schema_oid;
+        compo->u.complex->generic->schema.oid = (Odr_oid *)
+            zoom_yaz_str_to_z3950oid (c, CLASS_SCHEMA, resultset->schema);
+
+        if (!compo->u.complex->generic->schema.oid)
+        {
+            /* OID wasn't a schema! Try record syntax instead. */
+
+            compo->u.complex->generic->schema.oid = (Odr_oid *)
+                zoom_yaz_str_to_z3950oid (c, CLASS_RECSYN, resultset->schema);
+        }
+        if (elementSetName && *elementSetName)
+        {
+            compo->u.complex->generic->elementSpec = (Z_ElementSpec *)
+                odr_malloc(c->odr_out, sizeof(Z_ElementSpec));
+            compo->u.complex->generic->elementSpec->which =
+                Z_ElementSpec_elementSetName;
+            compo->u.complex->generic->elementSpec->u.elementSetName =
+                odr_strdup(c->odr_out, elementSetName);
+        }
+        else
+            compo->u.complex->generic->elementSpec = 0;
+        compo->u.complex->num_dbSpecific = 0;
+        compo->u.complex->dbSpecific = 0;
+        compo->u.complex->num_recordSyntax = 0;
+        compo->u.complex->recordSyntax = 0;
+    }
+    else if (elementSetName && *elementSetName)
+    {
+        Z_ElementSetNames *esn = (Z_ElementSetNames *)
+            odr_malloc(c->odr_out, sizeof(*esn));
+        Z_RecordComposition *compo = (Z_RecordComposition *)
+            odr_malloc(c->odr_out, sizeof(*compo));
+        
+        esn->which = Z_ElementSetNames_generic;
+        esn->u.generic = odr_strdup(c->odr_out, elementSetName);
+        compo->which = Z_RecordComp_simple;
+        compo->u.simple = esn;
+        req->recordComposition = compo;
+    }
+    req->resultSetId = odr_strdup(c->odr_out, resultset->setname);
+    return send_APDU(c, apdu);
+}
+
+static zoom_ret send_Z3950_sort_present(ZOOM_connection c)
+{
+    zoom_ret r = zoom_complete;
+
+    if (c->tasks && c->tasks->which == ZOOM_TASK_SEARCH)
+        r = send_Z3950_sort(c, c->tasks->u.search.resultset);
+    if (r == zoom_complete)
+        r = send_Z3950_present(c);
+    return r;
+}
+
+void ZOOM_handle_Z3950_apdu(ZOOM_connection c, Z_APDU *apdu)
+{
+    Z_InitResponse *initrs;
+    
+    ZOOM_connection_set_mask(c, 0);
+    yaz_log(c->log_details, "%p handle_Z3950_apdu apdu->which=%d",
+            c, apdu->which);
+    switch (apdu->which)
+    {
+    case Z_APDU_initResponse:
+        yaz_log(c->log_api, "%p handle_Z3950_apdu: Received Init response", c);
+        initrs = apdu->u.initResponse;
+        ZOOM_connection_option_set(c, "serverImplementationId",
+                                   initrs->implementationId ?
+                                   initrs->implementationId : "");
+        ZOOM_connection_option_set(c, "serverImplementationName",
+                                   initrs->implementationName ?
+                                   initrs->implementationName : "");
+        ZOOM_connection_option_set(c, "serverImplementationVersion",
+                                   initrs->implementationVersion ?
+                                   initrs->implementationVersion : "");
+        /* Set the three old options too, for old applications */
+        ZOOM_connection_option_set(c, "targetImplementationId",
+                                   initrs->implementationId ?
+                                   initrs->implementationId : "");
+        ZOOM_connection_option_set(c, "targetImplementationName",
+                                   initrs->implementationName ?
+                                   initrs->implementationName : "");
+        ZOOM_connection_option_set(c, "targetImplementationVersion",
+                                   initrs->implementationVersion ?
+                                   initrs->implementationVersion : "");
+
+        /* Make initrs->options available as ZOOM-level options */
+        yaz_init_opt_decode(initrs->options, set_init_option, (void*) c);
+
+        if (!*initrs->result)
+        {
+            Z_External *uif = initrs->userInformationField;
+
+            ZOOM_set_error(c, ZOOM_ERROR_INIT, 0); /* default error */
+
+            if (uif && uif->which == Z_External_userInfo1)
+                interpret_otherinformation_field(c, uif->u.userInfo1);
+        }
+        else
+        {
+            char *cookie =
+                yaz_oi_get_string_oid(&apdu->u.initResponse->otherInfo,
+                                      yaz_oid_userinfo_cookie, 1, 0);
+            xfree(c->cookie_in);
+            c->cookie_in = 0;
+            if (cookie)
+                c->cookie_in = xstrdup(cookie);
+            if (ODR_MASK_GET(initrs->options, Z_Options_namedResultSets) &&
+                ODR_MASK_GET(initrs->protocolVersion, Z_ProtocolVersion_3))
+                c->support_named_resultsets = 1;
+            if (c->tasks)
+            {
+                assert(c->tasks->which == ZOOM_TASK_CONNECT);
+                ZOOM_connection_remove_task(c);
+            }
+            ZOOM_connection_exec_task(c);
+        }
+        if (ODR_MASK_GET(initrs->options, Z_Options_negotiationModel))
+        {
+            NMEM tmpmem = nmem_create();
+            Z_CharSetandLanguageNegotiation *p =
+                yaz_get_charneg_record(initrs->otherInfo);
+            
+            if (p)
+            {
+                char *charset = NULL, *lang = NULL;
+                int sel;
+                
+                yaz_get_response_charneg(tmpmem, p, &charset, &lang, &sel);
+                yaz_log(c->log_details, "%p handle_Z3950_apdu target accepted: "
+                        "charset %s, language %s, select %d",
+                        c,
+                        charset ? charset : "none", lang ? lang : "none", sel);
+                if (charset)
+                    ZOOM_connection_option_set(c, "negotiation-charset",
+                                               charset);
+                if (lang)
+                    ZOOM_connection_option_set(c, "negotiation-lang",
+                                               lang);
+
+                ZOOM_connection_option_set(
+                    c,  "negotiation-charset-in-effect-for-records",
+                    (sel != 0) ? "1" : "0");
+                nmem_destroy(tmpmem);
+            }
+        }       
+        break;
+    case Z_APDU_searchResponse:
+        yaz_log(c->log_api, "%p handle_Z3950_apdu Search response", c);
+        handle_Z3950_search_response(c, apdu->u.searchResponse);
+        if (send_Z3950_sort_present(c) == zoom_complete)
+            ZOOM_connection_remove_task(c);
+        break;
+    case Z_APDU_presentResponse:
+        yaz_log(c->log_api, "%p handle_Z3950_apdu Present response", c);
+        handle_Z3950_present_response(c, apdu->u.presentResponse);
+        if (send_Z3950_present(c) == zoom_complete)
+            ZOOM_connection_remove_task(c);
+        break;
+    case Z_APDU_sortResponse:
+        yaz_log(c->log_api, "%p handle_Z3950_apdu Sort response", c);
+        handle_Z3950_sort_response(c, apdu->u.sortResponse);
+        if (send_Z3950_present(c) == zoom_complete)
+            ZOOM_connection_remove_task(c);
+        break;
+    case Z_APDU_scanResponse:
+        yaz_log(c->log_api, "%p handle_Z3950_apdu Scan response", c);
+        handle_Z3950_scan_response(c, apdu->u.scanResponse);
+        ZOOM_connection_remove_task(c);
+        break;
+    case Z_APDU_extendedServicesResponse:
+        yaz_log(c->log_api, "%p handle_Z3950_apdu Extended Services response", c);
+        handle_Z3950_es_response(c, apdu->u.extendedServicesResponse);
+        ZOOM_connection_remove_task(c);
+        break;
+    case Z_APDU_close:
+        yaz_log(c->log_api, "%p handle_Z3950_apdu Close PDU", c);
+        if (!ZOOM_test_reconnect(c))
+        {
+            ZOOM_set_error(c, ZOOM_ERROR_CONNECTION_LOST, c->host_port);
+            ZOOM_connection_close(c);
+        }
+        break;
+    default:
+        yaz_log(c->log_api, "%p Received unknown PDU", c);
+        ZOOM_set_error(c, ZOOM_ERROR_DECODE, 0);
+        ZOOM_connection_close(c);
+    }
+}
+
+/*
+ * Local variables:
+ * c-basic-offset: 4
+ * c-file-style: "Stroustrup"
+ * indent-tabs-mode: nil
+ * End:
+ * vim: shiftwidth=4 tabstop=8 expandtab
+ */
+
index da02fd6..23b2f12 100644 (file)
@@ -6,7 +6,8 @@ check_PROGRAMS = test_xmalloc test_iconv test_nmem test_matchstr test_wrbuf \
  test_soap1 test_soap2 test_odrstack test_log_thread test_xmlquery test_pquery \
  test_comstack test_filepath test_record_conv test_retrieval test_tpath \
  test_timing test_query_charset test_oid test_icu test_match_glob \
- test_rpn2cql test_json test_xml_include test_file_glob test_shared_ptr
+ test_rpn2cql test_rpn2solr test_json test_xml_include test_file_glob \
+ test_shared_ptr
 
 check_SCRIPTS = tstmarc.sh tstmarccol.sh tstcql2xcql.sh tstcql2pqf.sh tsticu.sh
 
@@ -77,6 +78,7 @@ test_query_charset_SOURCES = test_query_charset.c
 test_icu_SOURCES = test_icu.c
 test_match_glob_SOURCES = test_match_glob.c
 test_rpn2cql_SOURCES = test_rpn2cql.c
+test_rpn2solr_SOURCES = test_rpn2solr.c
 test_json_SOURCES = test_json.c
 test_xml_include_SOURCES = test_xml_include.c
 test_file_glob_SOURCES = test_file_glob.c
diff --git a/test/test_rpn2solr.c b/test/test_rpn2solr.c
new file mode 100644 (file)
index 0000000..029552c
--- /dev/null
@@ -0,0 +1,129 @@
+/* This file is part of the YAZ toolkit.
+ * Copyright (C) 1995-2010 Index Data
+ * See the file LICENSE for details.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <yaz/test.h>
+#include <yaz/log.h>
+#include <yaz/rpn2solr.h>
+#include <yaz/wrbuf.h>
+#include <yaz/pquery.h>
+
+static int compare(solr_transform_t ct, const char *pqf, const char *solr)
+{
+    int ret = 0;
+    ODR odr = odr_createmem(ODR_ENCODE);
+    WRBUF w = wrbuf_alloc();
+    Z_RPNQuery *q = p_query_rpn(odr, pqf);
+
+    if (q)
+    {
+        int r = solr_transform_rpn2solr_wrbuf(ct, w, q);
+
+        if (r != 0)
+        {
+            /* transform error */
+            yaz_log(YLOG_LOG, "%s -> Error %d", pqf, r);
+            if (!solr) /* also expected error? */
+                ret = 1;
+        }
+        else if (r == 0)
+        {
+            yaz_log(YLOG_LOG, "%s -> %s", pqf, wrbuf_cstr(w));
+            if (solr && !strcmp(wrbuf_cstr(w), solr))
+            {
+                ret = 1;
+            }
+        }
+    }
+    wrbuf_destroy(w);
+    odr_destroy(odr);
+    return ret;
+}
+
+static void tst1(void)
+{
+    solr_transform_t ct = solr_transform_create();
+
+    YAZ_CHECK(compare(ct, "abc", "abc"));
+    YAZ_CHECK(compare(ct, "\"a b c\"", "\"a b c\""));
+#if 0
+/* Invalid PQF, so this will never work */
+    YAZ_CHECK(compare(ct, "a b", "a b"));
+#endif
+    YAZ_CHECK(compare(ct, "@not a b", "a AND NOT b"));
+    YAZ_CHECK(compare(ct, "@and @or a b c", "(a OR b) AND c"));
+    YAZ_CHECK(compare(ct, "@and a b", "a AND b"));
+    YAZ_CHECK(compare(ct, "@or a b", "a OR b"));
+    YAZ_CHECK(compare(ct, "@attr 1=field abc", "field:abc"));
+    YAZ_CHECK(compare(ct, "@attr 1=field \"a b c\"", "field:\"a b c\""));
+    YAZ_CHECK(compare(ct, "@attr 1=4 abc", 0)); /* should fail */
+
+    solr_transform_define_pattern(ct, "index.title", "1=4");
+    YAZ_CHECK(compare(ct, "@attr 1=4 abc", "title:abc"));
+
+    solr_transform_define_pattern(ct, "index.foo", "1=bar");
+    YAZ_CHECK(compare(ct, "@attr 1=bar abc", "foo:abc"));
+    /*
+    YAZ_CHECK(compare(ct, "@or @attr 1=1016 water @attr 7=1 @attr 1=4 0", "any:water rank:??");
+     */
+
+    solr_transform_close(ct);
+}
+
+static void tst2(void)
+{
+    WRBUF w = wrbuf_alloc();
+    solr_transform_t ct = 0;
+    const char *srcdir = getenv("srcdir");
+    if (srcdir)
+    {
+        wrbuf_puts(w, srcdir);
+        wrbuf_puts(w, "/");
+    }
+    wrbuf_puts(w, "../etc/pqf.properties");
+    
+    ct = solr_transform_open_fname(wrbuf_cstr(w));
+    YAZ_CHECK(compare(ct, "@attr 1=4 abc", "dc.title:abc"));
+#if 0
+    YAZ_CHECK(compare(ct, "@attr 1=4 @attr 4=108 abc", "dc.title:abc"));
+#endif
+
+    YAZ_CHECK(compare(ct, "@attr 1=4 @attr 3=1 @attr 6=1 abc", "dc.title:abc"));
+    YAZ_CHECK(compare(ct, "@attr 1=4 @attr 4=1 @attr 6=1 abc", "dc.title:abc"));
+#if 0
+    YAZ_CHECK(compare(ct, "@attr 1=1016 abc", "abc"));
+    YAZ_CHECK(compare(ct, "@attr 2=1 @attr 1=30 1980", "dc.date:[* to 1980]"));
+    YAZ_CHECK(compare(ct, "@attr 1=30 @attr 2=3 1980", "dc.date:1980"));
+    YAZ_CHECK(compare(ct, "@attr 1=30 @attr 2=5 1980", "dc.date:[* to 1980]"));
+    YAZ_CHECK(compare(ct, "@attr 1=30 @attr 2=2 1980", "dc.date:[* to 1980]"));
+    YAZ_CHECK(compare(ct, "@attr 1=30 @attr 2=4 1980", "dc.date:[1980 to *]"));
+
+    YAZ_CHECK(compare(ct, "@attr 2=103 @attr 1=_ALLRECORDS 1", "solr.allRecords=1"));
+#endif
+    YAZ_CHECK(compare(ct, "@attr 1=500 abc", 0));
+    solr_transform_close(ct);
+    wrbuf_destroy(w);
+}
+
+int main (int argc, char **argv)
+{
+    YAZ_CHECK_INIT(argc, argv);
+    YAZ_CHECK_LOG();
+    tst1();
+    tst2();
+    YAZ_CHECK_TERM;
+}
+
+/*
+ * Local variables:
+ * c-basic-offset: 4
+ * c-file-style: "Stroustrup"
+ * indent-tabs-mode: nil
+ * End:
+ * vim: shiftwidth=4 tabstop=8 expandtab
+ */
+
index 5405807..cebd865 100644 (file)
@@ -470,9 +470,16 @@ MISC_OBJS= \
    $(OBJDIR)\opacdisp.obj \
    $(OBJDIR)\zgdu.obj \
    $(OBJDIR)\soap.obj \
+   $(OBJDIR)\solr.obj \
    $(OBJDIR)\srw.obj \
    $(OBJDIR)\srwutil.obj \
    $(OBJDIR)\zoom-c.obj \
+   $(OBJDIR)\zoom-event.obj \
+   $(OBJDIR)\zoom-record-cache.obj \
+   $(OBJDIR)\zoom-z3950.obj \
+   $(OBJDIR)\zoom-sru.obj \
+   $(OBJDIR)\zoom-query.obj \
+   $(OBJDIR)\record-render.obj \
    $(OBJDIR)\facet.obj \
    $(OBJDIR)\zoom-opt.obj \
    $(OBJDIR)\zoom-socket.obj \