f7cbd0ac05e0405a10b7537fc4c0f32d8e5b32d3
[yaz-moved-to-github.git] / src / zoom-c.c
1 /*
2  * Copyright (C) 1995-2005, Index Data ApS
3  * See the file LICENSE for details.
4  *
5  * $Id: zoom-c.c,v 1.79 2006-06-16 10:20:17 adam Exp $
6  */
7 /**
8  * \file zoom-c.c
9  * \brief Implements ZOOM C interface.
10  */
11
12 #include <assert.h>
13 #include <string.h>
14 #include <errno.h>
15 #include "zoom-p.h"
16
17 #include <yaz/yaz-util.h>
18 #include <yaz/xmalloc.h>
19 #include <yaz/otherinfo.h>
20 #include <yaz/log.h>
21 #include <yaz/pquery.h>
22 #include <yaz/marcdisp.h>
23 #include <yaz/diagbib1.h>
24 #include <yaz/charneg.h>
25 #include <yaz/ill.h>
26 #include <yaz/srw.h>
27 #include <yaz/cql.h>
28 #include <yaz/ccl.h>
29
30 #if HAVE_SYS_TYPES_H
31 #include <sys/types.h>
32 #endif
33 #if HAVE_SYS_TIME_H
34 #include <sys/time.h>
35 #endif
36 #if HAVE_SYS_POLL_H
37 #include <sys/poll.h>
38 #endif
39 #if HAVE_SYS_SELECT_H
40 #include <sys/select.h>
41 #endif
42 #ifdef WIN32
43 #include <winsock.h>
44 #endif
45
46 static int log_api = 0;
47 static int log_details = 0;
48
49 typedef enum {
50     zoom_pending,
51     zoom_complete
52 } zoom_ret;
53
54 static zoom_ret ZOOM_connection_send_init (ZOOM_connection c);
55 static zoom_ret do_write_ex (ZOOM_connection c, char *buf_out, int len_out);
56 static char *cql2pqf(ZOOM_connection c, const char *cql);
57
58 static void initlog()
59 {
60     static int log_level_initialized = 0;
61     if (!log_level_initialized)
62     {
63         log_api = yaz_log_module_level("zoom");
64         log_details = yaz_log_module_level("zoomdetails");
65         log_level_initialized = 1;
66     }
67 }
68
69 static ZOOM_Event ZOOM_Event_create (int kind)
70 {
71     ZOOM_Event event = (ZOOM_Event) xmalloc (sizeof(*event));
72     event->kind = kind;
73     event->next = 0;
74     event->prev = 0;
75     return event;
76 }
77
78 static void ZOOM_Event_destroy (ZOOM_Event event)
79 {
80     xfree (event);
81 }
82
83 static void ZOOM_connection_put_event (ZOOM_connection c, ZOOM_Event event)
84 {
85     if (c->m_queue_back)
86     {
87         c->m_queue_back->prev = event;
88         assert (c->m_queue_front);
89     }
90     else
91     {
92         assert (!c->m_queue_front);
93         c->m_queue_front = event;
94     }
95     event->next = c->m_queue_back;
96     event->prev = 0;
97     c->m_queue_back = event;
98 }
99
100 static ZOOM_Event ZOOM_connection_get_event(ZOOM_connection c)
101 {
102     ZOOM_Event event = c->m_queue_front;
103     if (!event)
104     {
105         c->last_event = ZOOM_EVENT_NONE;
106         return 0;
107     }
108     assert (c->m_queue_back);
109     c->m_queue_front = event->prev;
110     if (c->m_queue_front)
111     {
112         assert (c->m_queue_back);
113         c->m_queue_front->next = 0;
114     }
115     else
116         c->m_queue_back = 0;
117     c->last_event = event->kind;
118     return event;
119 }
120
121
122 static void set_dset_error (ZOOM_connection c, int error,
123                             const char *dset,
124                             const char *addinfo, const char *addinfo2)
125 {
126     char *cp;
127
128     xfree (c->addinfo);
129     c->addinfo = 0;
130     c->error = error;
131     if (!c->diagset || strcmp(dset, c->diagset))
132     {
133         xfree(c->diagset);
134         c->diagset = xstrdup(dset);
135         /* remove integer part from SRW diagset .. */
136         if ((cp = strrchr(c->diagset, '/')))
137            *cp = '\0';
138     }
139     if (addinfo && addinfo2)
140     {
141         c->addinfo = (char*) xmalloc(strlen(addinfo) + strlen(addinfo2) + 2);
142         strcpy(c->addinfo, addinfo);
143         strcat(c->addinfo, addinfo2);
144     }
145     else if (addinfo)
146         c->addinfo = xstrdup(addinfo);
147     if (error)
148         yaz_log(log_api, "%p set_dset_error %s %s:%d %s %s",
149                 c, c->host_port ? c->host_port : "<>", dset, error,
150                 addinfo ? addinfo : "",
151                 addinfo2 ? addinfo2 : "");
152 }
153
154 #if HAVE_XML2
155 static void set_HTTP_error (ZOOM_connection c, int error,
156                             const char *addinfo, const char *addinfo2)
157 {
158     set_dset_error(c, error, "HTTP", addinfo, addinfo2);
159 }
160 #endif
161
162 static void set_ZOOM_error (ZOOM_connection c, int error,
163                             const char *addinfo)
164 {
165     set_dset_error(c, error, "ZOOM", addinfo, 0);
166 }
167
168 static void clear_error (ZOOM_connection c)
169 {
170     /*
171      * If an error is tied to an operation then it's ok to clear: for
172      * example, a diagnostic returned from a search is cleared by a
173      * subsequent search.  However, problems such as Connection Lost
174      * or Init Refused are not cleared, because they are not
175      * recoverable: doing another search doesn't help.
176      */
177     switch (c->error)
178     {
179     case ZOOM_ERROR_CONNECT:
180     case ZOOM_ERROR_MEMORY:
181     case ZOOM_ERROR_DECODE:
182     case ZOOM_ERROR_CONNECTION_LOST:
183     case ZOOM_ERROR_INIT:
184     case ZOOM_ERROR_INTERNAL:
185     case ZOOM_ERROR_UNSUPPORTED_PROTOCOL:
186         break;
187     default:
188         set_ZOOM_error(c, ZOOM_ERROR_NONE, 0);
189     }
190 }
191
192 ZOOM_task ZOOM_connection_add_task (ZOOM_connection c, int which)
193 {
194     ZOOM_task *taskp = &c->tasks;
195     while (*taskp)
196         taskp = &(*taskp)->next;
197     *taskp = (ZOOM_task) xmalloc (sizeof(**taskp));
198     (*taskp)->running = 0;
199     (*taskp)->which = which;
200     (*taskp)->next = 0;
201     clear_error (c);
202     return *taskp;
203 }
204
205 ZOOM_task ZOOM_connection_insert_task (ZOOM_connection c, int which)
206 {
207     ZOOM_task task = (ZOOM_task) xmalloc (sizeof(*task));
208
209     task->next = c->tasks;
210     c->tasks = task;
211
212     task->running = 0;
213     task->which = which;
214     clear_error (c);
215     return task;
216 }
217
218 void ZOOM_connection_remove_task (ZOOM_connection c)
219 {
220     ZOOM_task task = c->tasks;
221
222     if (task)
223     {
224         c->tasks = task->next;
225         switch (task->which)
226         {
227         case ZOOM_TASK_SEARCH:
228             ZOOM_resultset_destroy (task->u.search.resultset);
229             break;
230         case ZOOM_TASK_RETRIEVE:
231             ZOOM_resultset_destroy (task->u.retrieve.resultset);
232             break;
233         case ZOOM_TASK_CONNECT:
234             break;
235         case ZOOM_TASK_SCAN:
236             ZOOM_scanset_destroy (task->u.scan.scan);
237             break;
238         case ZOOM_TASK_PACKAGE:
239             ZOOM_package_destroy (task->u.package);
240             break;
241         case ZOOM_TASK_SORT:
242             ZOOM_resultset_destroy (task->u.sort.resultset);
243             ZOOM_query_destroy(task->u.sort.q);
244             break;
245         default:
246             assert (0);
247         }
248         xfree (task);
249
250         if (!c->tasks)
251         {
252             ZOOM_Event event = ZOOM_Event_create(ZOOM_EVENT_END);
253             ZOOM_connection_put_event(c, event);
254         }
255     }
256 }
257
258 static int ZOOM_connection_exec_task (ZOOM_connection c);
259
260 void ZOOM_connection_remove_tasks (ZOOM_connection c)
261 {
262     while (c->tasks)
263         ZOOM_connection_remove_task(c);
264 }
265
266 static ZOOM_record record_cache_lookup (ZOOM_resultset r, int pos);
267
268 ZOOM_API(ZOOM_connection)
269 ZOOM_connection_create (ZOOM_options options)
270 {
271     ZOOM_connection c = (ZOOM_connection) xmalloc (sizeof(*c));
272
273     initlog();
274
275     yaz_log(log_api, "%p ZOOM_connection_create", c);
276
277     c->proto = PROTO_Z3950;
278     c->cs = 0;
279     c->mask = 0;
280     c->reconnect_ok = 0;
281     c->state = STATE_IDLE;
282     c->addinfo = 0;
283     c->diagset = 0;
284     set_ZOOM_error(c, ZOOM_ERROR_NONE, 0);
285     c->buf_in = 0;
286     c->len_in = 0;
287     c->buf_out = 0;
288     c->len_out = 0;
289     c->resultsets = 0;
290
291     c->options = ZOOM_options_create_with_parent(options);
292
293     c->host_port = 0;
294     c->path = 0;
295     c->proxy = 0;
296     
297     c->charset = c->lang = 0;
298
299     c->cookie_out = 0;
300     c->cookie_in = 0;
301     c->client_IP = 0;
302     c->tasks = 0;
303
304     c->odr_in = odr_createmem (ODR_DECODE);
305     c->odr_out = odr_createmem (ODR_ENCODE);
306
307     c->async = 0;
308     c->support_named_resultsets = 0;
309     c->last_event = ZOOM_EVENT_NONE;
310
311     c->m_queue_front = 0;
312     c->m_queue_back = 0;
313     return c;
314 }
315
316 /* set database names. Take local databases (if set); otherwise
317    take databases given in ZURL (if set); otherwise use Default */
318 static char **set_DatabaseNames (ZOOM_connection con, ZOOM_options options,
319                                  int *num)
320 {
321     char **databaseNames;
322     const char *cp = ZOOM_options_get (options, "databaseName");
323     
324     if ((!cp || !*cp) && con->host_port)
325     {
326         if (strncmp (con->host_port, "unix:", 5) == 0)
327             cp = strchr(con->host_port+5, ':');
328         else
329             cp = strchr(con->host_port, '/');
330         if (cp)
331             cp++;
332     }
333     if (!cp)
334         cp = "Default";
335     nmem_strsplit(con->odr_out->mem, "+", cp,  &databaseNames, num);
336     return databaseNames;
337 }
338
339 ZOOM_API(ZOOM_connection)
340 ZOOM_connection_new (const char *host, int portnum)
341 {
342     ZOOM_connection c = ZOOM_connection_create (0);
343
344     ZOOM_connection_connect (c, host, portnum);
345     return c;
346 }
347
348 static zoom_sru_mode get_sru_mode_from_string(const char *s)
349 {
350     if (!s || !*s)
351         return zoom_sru_soap;
352     if (!yaz_matchstr(s, "soap"))
353         return zoom_sru_soap;
354     else if (!yaz_matchstr(s, "get"))
355         return zoom_sru_get;
356     else if (!yaz_matchstr(s, "post"))
357         return zoom_sru_post;
358     return zoom_sru_error;
359 }
360
361 ZOOM_API(void)
362 ZOOM_connection_connect(ZOOM_connection c,
363                         const char *host, int portnum)
364 {
365     const char *val;
366     ZOOM_task task;
367
368     initlog();
369
370     yaz_log(log_api, "%p ZOOM_connection_connect host=%s portnum=%d",
371             c, host, portnum);
372
373     if (c->cs)
374     {
375         yaz_log(log_details, "%p ZOOM_connection_connect reconnect ok", c);
376         c->reconnect_ok = 1;
377         return;
378     }
379     yaz_log(log_details, "%p ZOOM_connection_connect connect", c);
380     xfree (c->proxy);
381     val = ZOOM_options_get (c->options, "proxy");
382     if (val && *val)
383     {
384         yaz_log(log_details, "%p ZOOM_connection_connect proxy=%s", c, val);
385         c->proxy = xstrdup (val);
386     }
387     else
388         c->proxy = 0;
389
390     xfree (c->charset);
391     val = ZOOM_options_get (c->options, "charset");
392     if (val && *val)
393     {
394         yaz_log(log_details, "%p ZOOM_connection_connect charset=%s", c, val);
395         c->charset = xstrdup (val);
396     }
397     else
398         c->charset = 0;
399
400     xfree (c->lang);
401     val = ZOOM_options_get (c->options, "lang");
402     if (val && *val)
403     {
404         yaz_log(log_details, "%p ZOOM_connection_connect lang=%s", c, val);
405         c->lang = xstrdup (val);
406     }
407     else
408         c->lang = 0;
409
410     val = ZOOM_options_get (c->options, "sru");
411     c->sru_mode = get_sru_mode_from_string(val);
412
413     xfree (c->host_port);
414     if (portnum)
415     {
416         char hostn[128];
417         sprintf (hostn, "%.80s:%d", host, portnum);
418         c->host_port = xstrdup(hostn);
419     }
420     else
421         c->host_port = xstrdup(host);
422
423     {
424         /*
425          * If the "<scheme>:" part of the host string is preceded by one
426          * or more comma-separated <name>=<value> pairs, these are taken
427          * to be options to be set on the connection object.  Among other
428          * applications, this facility can be used to embed authentication
429          * in a host string:
430          *          user=admin,password=secret,tcp:localhost:9999
431          */
432         char *remainder = c->host_port;
433         char *pcolon = strchr(remainder, ':');
434         char *pcomma;
435         char *pequals;
436         while ((pcomma = strchr(remainder, ',')) != 0 &&
437                (pcolon == 0 || pcomma < pcolon)) {
438             *pcomma = '\0';
439             if ((pequals = strchr(remainder, '=')) != 0) {
440                 *pequals = '\0';
441                 /*printf("# setting '%s'='%s'\n", remainder, pequals+1);*/
442                 ZOOM_connection_option_set(c, remainder, pequals+1);
443             }
444             remainder = pcomma+1;
445         }
446
447         if (remainder != c->host_port) {
448             xfree(c->host_port);
449             c->host_port = xstrdup(remainder);
450             /*printf("# reset hp='%s'\n", remainder);*/
451         }
452     }
453
454     ZOOM_options_set(c->options, "host", c->host_port);
455
456     val = ZOOM_options_get (c->options, "cookie");
457     if (val && *val)
458     { 
459         yaz_log(log_details, "%p ZOOM_connection_connect cookie=%s", c, val);
460         c->cookie_out = xstrdup (val);
461     }
462
463     val = ZOOM_options_get (c->options, "clientIP");
464     if (val && *val)
465     {
466         yaz_log(log_details, "%p ZOOM_connection_connect clientIP=%s",
467                 c, val);
468         c->client_IP = xstrdup (val);
469     }
470
471     c->async = ZOOM_options_get_bool (c->options, "async", 0);
472     yaz_log(log_details, "%p ZOOM_connection_connect async=%d", c, c->async);
473  
474     set_ZOOM_error(c, ZOOM_ERROR_NONE, 0);
475
476     task = ZOOM_connection_add_task (c, ZOOM_TASK_CONNECT);
477
478     if (!c->async)
479     {
480         while (ZOOM_event (1, &c))
481             ;
482     }
483 }
484
485 ZOOM_API(ZOOM_query)
486 ZOOM_query_create(void)
487 {
488     ZOOM_query s = (ZOOM_query) xmalloc (sizeof(*s));
489
490     yaz_log(log_details, "%p ZOOM_query_create", s);
491     s->refcount = 1;
492     s->z_query = 0;
493     s->sort_spec = 0;
494     s->odr = odr_createmem (ODR_ENCODE);
495     s->query_string = 0;
496
497     return s;
498 }
499
500 ZOOM_API(void)
501 ZOOM_query_destroy(ZOOM_query s)
502 {
503     if (!s)
504         return;
505
506     (s->refcount)--;
507     yaz_log(log_details, "%p ZOOM_query_destroy count=%d", s, s->refcount);
508     if (s->refcount == 0)
509     {
510         odr_destroy (s->odr);
511         xfree (s);
512     }
513 }
514
515 ZOOM_API(int)
516 ZOOM_query_prefix(ZOOM_query s, const char *str)
517 {
518     s->query_string = odr_strdup(s->odr, str);
519     s->z_query = (Z_Query *) odr_malloc (s->odr, sizeof(*s->z_query));
520     s->z_query->which = Z_Query_type_1;
521     s->z_query->u.type_1 =  p_query_rpn(s->odr, PROTO_Z3950, str);
522     if (!s->z_query->u.type_1)
523     {
524         yaz_log(log_details, "%p ZOOM_query_prefix str=%s failed", s, str);
525         s->z_query = 0;
526         return -1;
527     }
528     yaz_log(log_details, "%p ZOOM_query_prefix str=%s", s, str);
529     return 0;
530 }
531
532 ZOOM_API(int)
533 ZOOM_query_cql(ZOOM_query s, const char *str)
534 {
535     Z_External *ext;
536
537     s->query_string = odr_strdup(s->odr, str);
538
539     ext = (Z_External *) odr_malloc(s->odr, sizeof(*ext));
540     ext->direct_reference = odr_getoidbystr(s->odr, "1.2.840.10003.16.2");
541     ext->indirect_reference = 0;
542     ext->descriptor = 0;
543     ext->which = Z_External_CQL;
544     ext->u.cql = s->query_string;
545     
546     s->z_query = (Z_Query *) odr_malloc (s->odr, sizeof(*s->z_query));
547     s->z_query->which = Z_Query_type_104;
548     s->z_query->u.type_104 =  ext;
549
550     yaz_log(log_details, "%p ZOOM_query_cql str=%s", s, str);
551
552     return 0;
553 }
554
555 /*
556  * Translate the CQL string client-side into RPN which is passed to
557  * the server.  This is useful for server's that don't themselves
558  * support CQL, for which ZOOM_query_cql() is useless.  `conn' is used
559  * only as a place to stash diagnostics if compilation fails; if this
560  * information is not needed, a null pointer may be used.
561  */
562 ZOOM_API(int)
563 ZOOM_query_cql2rpn(ZOOM_query s, const char *str, ZOOM_connection conn)
564 {
565     char *rpn;
566     int ret;
567     ZOOM_connection freeme = 0;
568
569     yaz_log(log_details, "%p ZOOM_query_cql2rpn str=%s conn=%p", s, str, conn);
570     if (conn == 0)
571         conn = freeme = ZOOM_connection_create(0);
572
573     rpn = cql2pqf(conn, str);
574     if (freeme != 0)
575         ZOOM_connection_destroy(freeme);
576     if (rpn == 0)
577         return -1;
578
579     ret = ZOOM_query_prefix(s, rpn);
580     xfree(rpn);
581     return ret;
582 }
583
584 /*
585  * Analogous in every way to ZOOM_query_cql2rpn(), except that there
586  * is no analogous ZOOM_query_ccl() that just sends uninterpreted CCL
587  * to the server, as the YAZ GFS doesn't know how to handle this.
588  */
589 ZOOM_API(int)
590     ZOOM_query_ccl2rpn(ZOOM_query s, const char *str, const char *config,
591                        int *ccl_error, const char **error_string,
592                        int *error_pos)
593 {
594     int ret;
595     struct ccl_rpn_node *rpn;
596     CCL_bibset bibset = ccl_qual_mk();
597
598     if (config)
599         ccl_qual_buf(bibset, config);
600
601     rpn = ccl_find_str(bibset, str, ccl_error, error_pos);
602     if (!rpn)
603     {
604         *error_string = ccl_err_msg(*ccl_error);
605         ret = -1;
606     }
607     else
608     {
609         WRBUF wr = wrbuf_alloc();
610         ccl_pquery(wr, rpn);
611         ccl_rpn_delete(rpn);
612         ret = ZOOM_query_prefix(s, wrbuf_buf(wr));
613         wrbuf_free(wr, 1);
614     }
615     ccl_qual_rm(&bibset);
616     return ret;
617 }
618
619 ZOOM_API(int)
620 ZOOM_query_sortby(ZOOM_query s, const char *criteria)
621 {
622     s->sort_spec = yaz_sort_spec (s->odr, criteria);
623     if (!s->sort_spec)
624     {
625         yaz_log(log_details, "%p ZOOM_query_sortby criteria=%s failed",
626                 s, criteria);
627         return -1;
628     }
629     yaz_log(log_details, "%p ZOOM_query_sortby criteria=%s", s, criteria);
630     return 0;
631 }
632
633 static zoom_ret do_write(ZOOM_connection c);
634
635 ZOOM_API(void)
636 ZOOM_connection_destroy(ZOOM_connection c)
637 {
638     ZOOM_resultset r;
639     if (!c)
640         return;
641     yaz_log(log_api, "%p ZOOM_connection_destroy", c);
642     if (c->cs)
643         cs_close (c->cs);
644     for (r = c->resultsets; r; r = r->next)
645         r->connection = 0;
646
647     xfree (c->buf_in);
648     xfree (c->addinfo);
649     xfree (c->diagset);
650     odr_destroy (c->odr_in);
651     odr_destroy (c->odr_out);
652     ZOOM_options_destroy (c->options);
653     ZOOM_connection_remove_tasks (c);
654     xfree (c->host_port);
655     xfree (c->path);
656     xfree (c->proxy);
657     xfree (c->charset);
658     xfree (c->lang);
659     xfree (c->cookie_out);
660     xfree (c->cookie_in);
661     xfree (c->client_IP);
662     xfree (c);
663 }
664
665 void ZOOM_resultset_addref (ZOOM_resultset r)
666 {
667     if (r)
668     {
669         (r->refcount)++;
670         yaz_log(log_details, "%p ZOOM_resultset_addref count=%d",
671                 r, r->refcount);
672     }
673 }
674
675 ZOOM_resultset ZOOM_resultset_create ()
676 {
677     int i;
678     ZOOM_resultset r = (ZOOM_resultset) xmalloc (sizeof(*r));
679
680     initlog();
681
682     yaz_log(log_details, "%p ZOOM_resultset_create", r);
683     r->refcount = 1;
684     r->size = 0;
685     r->odr = odr_createmem (ODR_ENCODE);
686     r->start = 0;
687     r->piggyback = 1;
688     r->setname = 0;
689     r->schema = 0;
690     r->count = 0;
691     r->step = 0;
692     for (i = 0; i<RECORD_HASH_SIZE; i++)
693         r->record_hash[i] = 0;
694     r->r_sort_spec = 0;
695     r->query = 0;
696     r->connection = 0;
697     r->next = 0;
698     return r;
699 }
700
701 ZOOM_API(ZOOM_resultset)
702 ZOOM_connection_search_pqf(ZOOM_connection c, const char *q)
703 {
704     ZOOM_resultset r;
705     ZOOM_query s = ZOOM_query_create();
706
707     ZOOM_query_prefix (s, q);
708
709     r = ZOOM_connection_search (c, s);
710     ZOOM_query_destroy (s);
711     return r;
712 }
713
714 ZOOM_API(ZOOM_resultset)
715 ZOOM_connection_search(ZOOM_connection c, ZOOM_query q)
716 {
717     ZOOM_resultset r = ZOOM_resultset_create ();
718     ZOOM_task task;
719     const char *cp;
720
721     yaz_log(log_api, "%p ZOOM_connection_search set %p query %p", c, r, q);
722     r->r_sort_spec = q->sort_spec;
723     r->query = q;
724
725     r->options = ZOOM_options_create_with_parent(c->options);
726
727     r->start = ZOOM_options_get_int(r->options, "start", 0);
728     r->count = ZOOM_options_get_int(r->options, "count", 0);
729     {
730         /* If "presentChunk" is defined use that; otherwise "step" */
731         const char *cp = ZOOM_options_get (r->options, "presentChunk");
732         r->step = ZOOM_options_get_int(r->options,
733                                        (cp != 0 ? "presentChunk": "step"), 0);
734     }
735     r->piggyback = ZOOM_options_get_bool (r->options, "piggyback", 1);
736     cp = ZOOM_options_get (r->options, "setname");
737     if (cp)
738         r->setname = xstrdup(cp);
739     cp = ZOOM_options_get (r->options, "schema");
740     if (cp)
741         r->schema = xstrdup(cp);
742     
743     r->connection = c;
744
745     r->next = c->resultsets;
746     c->resultsets = r;
747
748     if (c->host_port && c->proto == PROTO_HTTP)
749     {
750         if (!c->cs)
751         {
752             yaz_log(log_details, "ZOOM_connection_search: no comstack");
753             ZOOM_connection_add_task(c, ZOOM_TASK_CONNECT);
754         }
755         else
756         {
757             yaz_log(log_details, "ZOOM_connection_search: reconnect");
758             c->reconnect_ok = 1;
759         }
760     }
761
762     task = ZOOM_connection_add_task (c, ZOOM_TASK_SEARCH);
763     task->u.search.resultset = r;
764     ZOOM_resultset_addref (r);  
765
766     (q->refcount)++;
767
768     if (!c->async)
769     {
770         while (ZOOM_event (1, &c))
771             ;
772     }
773     return r;
774 }
775
776 /*
777  * This is the old result-set sorting API, which is maintained only
778  * for the sake of binary compatibility.  There is no reason ever to
779  * use this rather than ZOOM_resultset_sort1().
780  */
781 ZOOM_API(void)
782 ZOOM_resultset_sort(ZOOM_resultset r,
783                     const char *sort_type, const char *sort_spec)
784 {
785     (void) ZOOM_resultset_sort1(r, sort_type, sort_spec);
786 }
787
788 ZOOM_API(int)
789 ZOOM_resultset_sort1(ZOOM_resultset r,
790                      const char *sort_type, const char *sort_spec)
791 {
792     ZOOM_connection c = r->connection;
793     ZOOM_task task;
794     ZOOM_query newq;
795
796     newq = ZOOM_query_create();
797     if (ZOOM_query_sortby(newq, sort_spec) < 0)
798         return -1;
799
800     yaz_log(log_api, "%p ZOOM_resultset_sort r=%p sort_type=%s sort_spec=%s",
801             r, r, sort_type, sort_spec);
802     if (!c)
803         return 0;
804
805     if (c->host_port && c->proto == PROTO_HTTP)
806     {
807         if (!c->cs)
808         {
809             yaz_log(log_details, "%p ZOOM_resultset_sort: no comstack", r);
810             ZOOM_connection_add_task(c, ZOOM_TASK_CONNECT);
811         }
812         else
813         {
814             yaz_log(log_details, "%p ZOOM_resultset_sort: prepare reconnect",
815                     r);
816             c->reconnect_ok = 1;
817         }
818     }
819     
820     ZOOM_resultset_cache_reset(r);
821     task = ZOOM_connection_add_task (c, ZOOM_TASK_SORT);
822     task->u.sort.resultset = r;
823     task->u.sort.q = newq;
824
825     ZOOM_resultset_addref (r);  
826
827     if (!c->async)
828     {
829         while (ZOOM_event (1, &c))
830             ;
831     }
832
833     return 0;
834 }
835
836 ZOOM_API(void)
837     ZOOM_resultset_cache_reset(ZOOM_resultset r)
838 {
839     int i;
840     for (i = 0; i<RECORD_HASH_SIZE; i++)
841     {
842         ZOOM_record_cache rc;
843         for (rc = r->record_hash[i]; rc; rc = rc->next)
844         {
845             if (rc->rec.wrbuf_marc)
846                 wrbuf_free (rc->rec.wrbuf_marc, 1);
847             if (rc->rec.wrbuf_iconv)
848                 wrbuf_free (rc->rec.wrbuf_iconv, 1);
849             if (rc->rec.wrbuf_opac)
850                 wrbuf_free (rc->rec.wrbuf_opac, 1);
851         }
852         r->record_hash[i] = 0;
853     }
854 }
855
856 ZOOM_API(void)
857 ZOOM_resultset_destroy(ZOOM_resultset r)
858 {
859     if (!r)
860         return;
861     (r->refcount)--;
862     yaz_log(log_details, "%p ZOOM_resultset_destroy r=%p count=%d",
863             r, r, r->refcount);
864     if (r->refcount == 0)
865     {
866         ZOOM_resultset_cache_reset(r);
867
868         if (r->connection)
869         {
870             /* remove ourselves from the resultsets in connection */
871             ZOOM_resultset *rp = &r->connection->resultsets;
872             while (1)
873             {
874                 assert (*rp);   /* we must be in this list!! */
875                 if (*rp == r)
876                 {   /* OK, we're here - take us out of it */
877                     *rp = (*rp)->next;
878                     break;
879                 }
880                 rp = &(*rp)->next;
881             }
882         }
883         ZOOM_query_destroy (r->query);
884         ZOOM_options_destroy (r->options);
885         odr_destroy (r->odr);
886         xfree (r->setname);
887         xfree (r->schema);
888         xfree (r);
889     }
890 }
891
892 ZOOM_API(size_t)
893 ZOOM_resultset_size (ZOOM_resultset r)
894 {
895     return r->size;
896 }
897
898 static void do_close (ZOOM_connection c)
899 {
900     if (c->cs)
901         cs_close(c->cs);
902     c->cs = 0;
903     c->mask = 0;
904     c->state = STATE_IDLE;
905 }
906
907 static void ZOOM_resultset_retrieve (ZOOM_resultset r,
908                                      int force_sync, int start, int count)
909 {
910     ZOOM_task task;
911     ZOOM_connection c;
912     const char *cp;
913
914     if (!r)
915         return;
916     yaz_log(log_details, "%p ZOOM_resultset_retrieve force_sync=%d start=%d"
917             " count=%d", r, force_sync, start, count);
918     c = r->connection;
919     if (!c)
920         return;
921
922     if (c->host_port && c->proto == PROTO_HTTP)
923     {
924         if (!c->cs)
925         {
926             yaz_log(log_details, "%p ZOOM_resultset_retrieve: no comstack", r);
927             ZOOM_connection_add_task(c, ZOOM_TASK_CONNECT);
928         }
929         else
930         {
931             yaz_log(log_details, "%p ZOOM_resultset_retrieve: prepare "
932                     "reconnect", r);
933             c->reconnect_ok = 1;
934         }
935     }
936     task = ZOOM_connection_add_task (c, ZOOM_TASK_RETRIEVE);
937     task->u.retrieve.resultset = r;
938     task->u.retrieve.start = start;
939     task->u.retrieve.count = count;
940
941     cp = ZOOM_options_get (r->options, "schema");
942     if (cp)
943     {
944         if (!r->schema || strcmp(r->schema, cp))
945         {
946             xfree(r->schema);
947             r->schema = xstrdup(cp);
948         }
949     }
950
951     ZOOM_resultset_addref (r);
952
953     if (!r->connection->async || force_sync)
954         while (r->connection && ZOOM_event (1, &r->connection))
955             ;
956 }
957
958 ZOOM_API(void)
959 ZOOM_resultset_records (ZOOM_resultset r, ZOOM_record *recs,
960                         size_t start, size_t count)
961 {
962     int force_present = 0;
963
964     if (!r)
965         return ;
966     yaz_log(log_api, "%p ZOOM_resultset_records r=%p start=%ld count=%ld",
967             r, r, (long) start, (long) count);
968     if (count && recs)
969         force_present = 1;
970     ZOOM_resultset_retrieve (r, force_present, start, count);
971     if (force_present)
972     {
973         size_t i;
974         for (i = 0; i< count; i++)
975             recs[i] = ZOOM_resultset_record_immediate (r, i+start);
976     }
977 }
978
979 static void get_cert(ZOOM_connection c)
980 {
981     char *cert_buf;
982     int cert_len;
983     
984     if (cs_get_peer_certificate_x509(c->cs, &cert_buf, &cert_len))
985     {
986         ZOOM_connection_option_setl(c, "sslPeerCert",
987                                     cert_buf, cert_len);
988         xfree(cert_buf);
989     }
990 }
991
992 static zoom_ret do_connect (ZOOM_connection c)
993 {
994     void *add;
995     const char *effective_host;
996
997     if (c->proxy)
998         effective_host = c->proxy;
999     else
1000         effective_host = c->host_port;
1001
1002     yaz_log(log_details, "%p do_connect effective_host=%s", c, effective_host);
1003
1004     if (c->cs)
1005         cs_close(c->cs);
1006     c->cs = cs_create_host (effective_host, 0, &add);
1007
1008     if (c->cs && c->cs->protocol == PROTO_HTTP)
1009     {
1010 #if HAVE_XML2
1011         const char *path = 0;
1012
1013         c->proto = PROTO_HTTP;
1014         cs_get_host_args(c->host_port, &path);
1015         xfree(c->path);
1016         c->path = (char*) xmalloc(strlen(path)+2);
1017         c->path[0] = '/';
1018         strcpy (c->path+1, path);
1019 #else
1020         set_ZOOM_error(c, ZOOM_ERROR_UNSUPPORTED_PROTOCOL, "SRW");
1021         do_close(c);
1022         return zoom_complete;
1023 #endif
1024     }
1025     if (c->cs)
1026     {
1027         int ret = cs_connect (c->cs, add);
1028         if (ret == 0)
1029         {
1030             ZOOM_Event event = ZOOM_Event_create(ZOOM_EVENT_CONNECT);
1031             ZOOM_connection_put_event(c, event);
1032             get_cert(c);
1033             if (c->proto == PROTO_Z3950)
1034                 ZOOM_connection_send_init(c);
1035             else
1036             {
1037                 /* no init request for SRW .. */
1038                 assert (c->tasks->which == ZOOM_TASK_CONNECT);
1039                 ZOOM_connection_remove_task (c);
1040                 c->mask = 0;
1041                 ZOOM_connection_exec_task (c);
1042             }
1043             c->state = STATE_ESTABLISHED;
1044             return zoom_pending;
1045         }
1046         else if (ret > 0)
1047         {
1048             c->state = STATE_CONNECTING; 
1049             c->mask = ZOOM_SELECT_EXCEPT;
1050             if (c->cs->io_pending & CS_WANT_WRITE)
1051                 c->mask += ZOOM_SELECT_WRITE;
1052             if (c->cs->io_pending & CS_WANT_READ)
1053                 c->mask += ZOOM_SELECT_READ;
1054             return zoom_pending;
1055         }
1056     }
1057     c->state = STATE_IDLE;
1058     set_ZOOM_error(c, ZOOM_ERROR_CONNECT, c->host_port);
1059     return zoom_complete;
1060 }
1061
1062 int z3950_connection_socket(ZOOM_connection c)
1063 {
1064     if (c->cs)
1065         return cs_fileno(c->cs);
1066     return -1;
1067 }
1068
1069 int z3950_connection_mask(ZOOM_connection c)
1070 {
1071     if (c->cs)
1072         return c->mask;
1073     return 0;
1074 }
1075
1076 static void otherInfo_attach (ZOOM_connection c, Z_APDU *a, ODR out)
1077 {
1078     int i;
1079     for (i = 0; i<200; i++)
1080     {
1081         size_t len;
1082         Z_OtherInformation **oi;
1083         char buf[80];
1084         const char *val;
1085         const char *cp;
1086         int oidval;
1087
1088         sprintf (buf, "otherInfo%d", i);
1089         val = ZOOM_options_get (c->options, buf);
1090         if (!val)
1091             break;
1092         cp = strchr(val, ':');
1093         if (!cp)
1094             continue;
1095         len = cp - val;
1096         if (len >= sizeof(buf))
1097             len = sizeof(buf)-1;
1098         memcpy (buf, val, len);
1099         buf[len] = '\0';
1100         oidval = oid_getvalbyname (buf);
1101         if (oidval == VAL_NONE)
1102             continue;
1103         
1104         yaz_oi_APDU(a, &oi);
1105         yaz_oi_set_string_oidval(oi, out, oidval, 1, cp+1);
1106     }
1107 }
1108
1109 static int encode_APDU(ZOOM_connection c, Z_APDU *a, ODR out)
1110 {
1111     assert (a);
1112     if (c->cookie_out)
1113     {
1114         Z_OtherInformation **oi;
1115         yaz_oi_APDU(a, &oi);
1116         yaz_oi_set_string_oidval(oi, out, VAL_COOKIE, 1, c->cookie_out);
1117     }
1118     if (c->client_IP)
1119     {
1120         Z_OtherInformation **oi;
1121         yaz_oi_APDU(a, &oi);
1122         yaz_oi_set_string_oidval(oi, out, VAL_CLIENT_IP, 1, c->client_IP);
1123     }
1124     otherInfo_attach (c, a, out);
1125     if (!z_APDU(out, &a, 0, 0))
1126     {
1127         FILE *outf = fopen("/tmp/apdu.txt", "a");
1128         if (a && outf)
1129         {
1130             ODR odr_pr = odr_createmem(ODR_PRINT);
1131             fprintf (outf, "a=%p\n", a);
1132             odr_setprint(odr_pr, outf);
1133             z_APDU(odr_pr, &a, 0, 0);
1134             odr_destroy(odr_pr);
1135         }
1136         yaz_log(log_api, "%p encoding_APDU: encoding failed", c);
1137         set_ZOOM_error(c, ZOOM_ERROR_ENCODE, 0);
1138         odr_reset(out);
1139         return -1;
1140     }
1141     yaz_log(log_details, "%p encoding_APDU encoding OK", c);
1142     return 0;
1143 }
1144
1145 static zoom_ret send_APDU (ZOOM_connection c, Z_APDU *a)
1146 {
1147     ZOOM_Event event;
1148     assert (a);
1149     if (encode_APDU(c, a, c->odr_out))
1150         return zoom_complete;
1151     yaz_log(log_details, "%p send APDU type=%d", c, a->which);
1152     c->buf_out = odr_getbuf(c->odr_out, &c->len_out, 0);
1153     event = ZOOM_Event_create (ZOOM_EVENT_SEND_APDU);
1154     ZOOM_connection_put_event (c, event);
1155     odr_reset(c->odr_out);
1156     return do_write (c);
1157 }
1158
1159 /* returns 1 if PDU was sent OK (still pending )
1160            0 if PDU was not sent OK (nothing to wait for) 
1161 */
1162
1163 static zoom_ret ZOOM_connection_send_init (ZOOM_connection c)
1164 {
1165     Z_APDU *apdu = zget_APDU(c->odr_out, Z_APDU_initRequest);
1166     Z_InitRequest *ireq = apdu->u.initRequest;
1167     Z_IdAuthentication *auth = (Z_IdAuthentication *)
1168         odr_malloc(c->odr_out, sizeof(*auth));
1169     const char *auth_groupId = ZOOM_options_get (c->options, "group");
1170     const char *auth_userId = ZOOM_options_get (c->options, "user");
1171     const char *auth_password = ZOOM_options_get (c->options, "password");
1172     char *version;
1173
1174     /* support the pass for backwards compatibility */
1175     if (!auth_password)
1176         auth_password = ZOOM_options_get (c->options, "pass");
1177         
1178     ODR_MASK_SET(ireq->options, Z_Options_search);
1179     ODR_MASK_SET(ireq->options, Z_Options_present);
1180     ODR_MASK_SET(ireq->options, Z_Options_scan);
1181     ODR_MASK_SET(ireq->options, Z_Options_sort);
1182     ODR_MASK_SET(ireq->options, Z_Options_extendedServices);
1183     ODR_MASK_SET(ireq->options, Z_Options_namedResultSets);
1184     
1185     ODR_MASK_SET(ireq->protocolVersion, Z_ProtocolVersion_1);
1186     ODR_MASK_SET(ireq->protocolVersion, Z_ProtocolVersion_2);
1187     ODR_MASK_SET(ireq->protocolVersion, Z_ProtocolVersion_3);
1188
1189     /* Index Data's Z39.50 Implementor Id is 81 */
1190     ireq->implementationId = odr_prepend(c->odr_out,
1191         ZOOM_options_get(c->options, "implementationId"),
1192         odr_prepend(c->odr_out, "81", ireq->implementationId));
1193
1194     ireq->implementationName = odr_prepend(c->odr_out,
1195         ZOOM_options_get(c->options, "implementationName"),
1196         odr_prepend(c->odr_out, "ZOOM-C", ireq->implementationName));
1197
1198     version = odr_strdup(c->odr_out, "$Revision: 1.79 $");
1199     if (strlen(version) > 10)   /* check for unexpanded CVS strings */
1200         version[strlen(version)-2] = '\0';
1201     ireq->implementationVersion = odr_prepend(c->odr_out,
1202         ZOOM_options_get(c->options, "implementationVersion"),
1203         odr_prepend(c->odr_out, &version[11], ireq->implementationVersion));
1204
1205     *ireq->maximumRecordSize =
1206         ZOOM_options_get_int (c->options, "maximumRecordSize", 1024*1024);
1207     *ireq->preferredMessageSize =
1208         ZOOM_options_get_int (c->options, "preferredMessageSize", 1024*1024);
1209     
1210     if (auth_groupId || auth_password)
1211     {
1212         Z_IdPass *pass = (Z_IdPass *) odr_malloc(c->odr_out, sizeof(*pass));
1213         int i = 0;
1214         pass->groupId = 0;
1215         if (auth_groupId && *auth_groupId)
1216         {
1217             pass->groupId = (char *)
1218                 odr_malloc(c->odr_out, strlen(auth_groupId)+1);
1219             strcpy(pass->groupId, auth_groupId);
1220             i++;
1221         }
1222         pass->userId = 0;
1223         if (auth_userId && *auth_userId)
1224         {
1225             pass->userId = (char *)
1226                 odr_malloc(c->odr_out, strlen(auth_userId)+1);
1227             strcpy(pass->userId, auth_userId);
1228             i++;
1229         }
1230         pass->password = 0;
1231         if (auth_password && *auth_password)
1232         {
1233             pass->password = (char *)
1234                 odr_malloc(c->odr_out, strlen(auth_password)+1);
1235             strcpy(pass->password, auth_password);
1236             i++;
1237         }
1238         if (i)
1239         {
1240             auth->which = Z_IdAuthentication_idPass;
1241             auth->u.idPass = pass;
1242             ireq->idAuthentication = auth;
1243         }
1244     }
1245     else if (auth_userId)
1246     {
1247         auth->which = Z_IdAuthentication_open;
1248         auth->u.open = (char *)
1249             odr_malloc(c->odr_out, strlen(auth_userId)+1);
1250         strcpy(auth->u.open, auth_userId);
1251         ireq->idAuthentication = auth;
1252     }
1253     if (c->proxy)
1254         yaz_oi_set_string_oidval(&ireq->otherInfo, c->odr_out,
1255                                  VAL_PROXY, 1, c->host_port);
1256     if (c->charset || c->lang)
1257     {
1258         Z_OtherInformation **oi;
1259         Z_OtherInformationUnit *oi_unit;
1260         
1261         yaz_oi_APDU(apdu, &oi);
1262         
1263         if ((oi_unit = yaz_oi_update(oi, c->odr_out, NULL, 0, 0)))
1264         {
1265             ODR_MASK_SET(ireq->options, Z_Options_negotiationModel);
1266             oi_unit->which = Z_OtherInfo_externallyDefinedInfo;
1267             oi_unit->information.externallyDefinedInfo =
1268                 yaz_set_proposal_charneg_list(c->odr_out, " ",
1269                                               c->charset, c->lang, 1);
1270         }
1271     }
1272     assert (apdu);
1273     return send_APDU (c, apdu);
1274 }
1275
1276 #if HAVE_XML2
1277 static zoom_ret send_srw (ZOOM_connection c, Z_SRW_PDU *sr)
1278 {
1279     Z_GDU *gdu;
1280     ZOOM_Event event;
1281
1282     gdu = z_get_HTTP_Request_host_path(c->odr_out, c->host_port, c->path);
1283
1284     if (c->sru_mode == zoom_sru_get)
1285     {
1286         yaz_sru_get_encode(gdu->u.HTTP_Request, sr, c->odr_out, c->charset);
1287     }
1288     else if (c->sru_mode == zoom_sru_post)
1289     {
1290         yaz_sru_post_encode(gdu->u.HTTP_Request, sr, c->odr_out, c->charset);
1291     }
1292     else if (c->sru_mode == zoom_sru_soap)
1293     {
1294         yaz_sru_post_encode(gdu->u.HTTP_Request, sr, c->odr_out, c->charset);
1295     }
1296     if (!z_GDU(c->odr_out, &gdu, 0, 0))
1297         return zoom_complete;
1298     c->buf_out = odr_getbuf(c->odr_out, &c->len_out, 0);
1299         
1300     event = ZOOM_Event_create (ZOOM_EVENT_SEND_APDU);
1301     ZOOM_connection_put_event (c, event);
1302     odr_reset(c->odr_out);
1303     return do_write (c);
1304 }
1305 #endif
1306
1307 #if HAVE_XML2
1308 static zoom_ret ZOOM_connection_srw_send_search(ZOOM_connection c)
1309 {
1310     int i;
1311     ZOOM_resultset resultset = 0;
1312     Z_SRW_PDU *sr = 0;
1313     const char *recordPacking = 0;
1314
1315     if (c->error)                  /* don't continue on error */
1316         return zoom_complete;
1317     assert (c->tasks);
1318     if (c->tasks->which == ZOOM_TASK_SEARCH)
1319     {
1320         resultset = c->tasks->u.search.resultset;
1321         resultset->setname = xstrdup ("default");
1322         ZOOM_options_set (resultset->options, "setname", resultset->setname);
1323     }
1324     else if(c->tasks->which == ZOOM_TASK_RETRIEVE)
1325     {
1326         resultset = c->tasks->u.retrieve.resultset;
1327
1328         resultset->start = c->tasks->u.retrieve.start;
1329         resultset->count = c->tasks->u.retrieve.count;
1330         
1331         if (resultset->start >= resultset->size)
1332             return zoom_complete;
1333         if (resultset->start + resultset->count > resultset->size)
1334             resultset->count = resultset->size - resultset->start;
1335
1336         for (i = 0; i<resultset->count; i++)
1337         {
1338             ZOOM_record rec =
1339                 record_cache_lookup (resultset, i + resultset->start);
1340             if (!rec)
1341                 break;
1342             else
1343             {
1344                 ZOOM_Event event = ZOOM_Event_create(ZOOM_EVENT_RECV_RECORD);
1345                 ZOOM_connection_put_event(c, event);
1346             }
1347         }
1348         if (i == resultset->count)
1349             return zoom_complete;
1350     }
1351     assert(resultset->query);
1352         
1353     sr = yaz_srw_get(c->odr_out, Z_SRW_searchRetrieve_request);
1354
1355     if (resultset->query->z_query->which == Z_Query_type_104
1356         && resultset->query->z_query->u.type_104->which == Z_External_CQL)
1357     {
1358
1359         sr->u.request->query_type = Z_SRW_query_type_cql;
1360         sr->u.request->query.cql =resultset->query->z_query->u.type_104->u.cql;
1361     }
1362     else if (resultset->query->z_query->which == Z_Query_type_1 &&
1363              resultset->query->z_query->u.type_1)
1364     {
1365         sr->u.request->query_type = Z_SRW_query_type_pqf;
1366         sr->u.request->query.pqf = resultset->query->query_string;
1367     }
1368     else
1369     {
1370         set_ZOOM_error(c, ZOOM_ERROR_UNSUPPORTED_QUERY, 0);
1371         return zoom_complete;
1372     }
1373     sr->u.request->startRecord = odr_intdup (c->odr_out, resultset->start + 1);
1374     sr->u.request->maximumRecords = odr_intdup (
1375         c->odr_out, resultset->step>0 ? resultset->step : resultset->count);
1376     sr->u.request->recordSchema = resultset->schema;
1377
1378     recordPacking = ZOOM_resultset_option_get (resultset, "recordPacking");
1379
1380     if (recordPacking)
1381         sr->u.request->recordPacking = odr_strdup(c->odr_out, recordPacking);
1382     
1383     return send_srw(c, sr);
1384 }
1385 #else
1386 static zoom_ret ZOOM_connection_srw_send_search(ZOOM_connection c)
1387 {
1388     return zoom_complete;
1389 }
1390 #endif
1391
1392 static zoom_ret ZOOM_connection_send_search (ZOOM_connection c)
1393 {
1394     ZOOM_resultset r;
1395     int lslb, ssub, mspn;
1396     const char *syntax;
1397     Z_APDU *apdu = zget_APDU(c->odr_out, Z_APDU_searchRequest);
1398     Z_SearchRequest *search_req = apdu->u.searchRequest;
1399     const char *elementSetName;
1400     const char *smallSetElementSetName;
1401     const char *mediumSetElementSetName;
1402
1403     assert (c->tasks);
1404     assert (c->tasks->which == ZOOM_TASK_SEARCH);
1405
1406     r = c->tasks->u.search.resultset;
1407
1408     yaz_log(log_details, "%p ZOOM_connection_send_search set=%p", c, r);
1409
1410     elementSetName =
1411         ZOOM_options_get (r->options, "elementSetName");
1412     smallSetElementSetName  =
1413         ZOOM_options_get (r->options, "smallSetElementSetName");
1414     mediumSetElementSetName =
1415         ZOOM_options_get (r->options, "mediumSetElementSetName");
1416
1417     if (!smallSetElementSetName)
1418         smallSetElementSetName = elementSetName;
1419
1420     if (!mediumSetElementSetName)
1421         mediumSetElementSetName = elementSetName;
1422
1423     assert (r);
1424     assert (r->query);
1425
1426     /* prepare query for the search request */
1427     search_req->query = r->query->z_query;
1428     if (!search_req->query)
1429     {
1430         set_ZOOM_error(c, ZOOM_ERROR_INVALID_QUERY, 0);
1431         return zoom_complete;
1432     }
1433
1434     search_req->databaseNames =
1435         set_DatabaseNames (c, r->options, &search_req->num_databaseNames);
1436
1437     /* get syntax (no need to provide unless piggyback is in effect) */
1438     syntax = ZOOM_options_get (r->options, "preferredRecordSyntax");
1439
1440     lslb = ZOOM_options_get_int (r->options, "largeSetLowerBound", -1);
1441     ssub = ZOOM_options_get_int (r->options, "smallSetUpperBound", -1);
1442     mspn = ZOOM_options_get_int (r->options, "mediumSetPresentNumber", -1);
1443     if (lslb != -1 && ssub != -1 && mspn != -1)
1444     {
1445         /* So're a Z39.50 expert? Let's hope you don't do sort */
1446         *search_req->largeSetLowerBound = lslb;
1447         *search_req->smallSetUpperBound = ssub;
1448         *search_req->mediumSetPresentNumber = mspn;
1449     }
1450     else if (r->start == 0 && r->count > 0
1451              && r->piggyback && !r->r_sort_spec && !r->schema)
1452     {
1453         /* Regular piggyback - do it unless we're going to do sort */
1454         *search_req->largeSetLowerBound = 2000000000;
1455         *search_req->smallSetUpperBound = 1;
1456         *search_req->mediumSetPresentNumber = r->step>0 ? r->step : r->count;
1457     }
1458     else
1459     {
1460         /* non-piggyback. Need not provide elementsets or syntaxes .. */
1461         smallSetElementSetName = 0;
1462         mediumSetElementSetName = 0;
1463         syntax = 0;
1464     }
1465     if (smallSetElementSetName && *smallSetElementSetName)
1466     {
1467         Z_ElementSetNames *esn = (Z_ElementSetNames *)
1468             odr_malloc (c->odr_out, sizeof(*esn));
1469         
1470         esn->which = Z_ElementSetNames_generic;
1471         esn->u.generic = odr_strdup (c->odr_out, smallSetElementSetName);
1472         search_req->smallSetElementSetNames = esn;
1473     }
1474     if (mediumSetElementSetName && *mediumSetElementSetName)
1475     {
1476         Z_ElementSetNames *esn = (Z_ElementSetNames *)
1477             odr_malloc (c->odr_out, sizeof(*esn));
1478         
1479         esn->which = Z_ElementSetNames_generic;
1480         esn->u.generic = odr_strdup (c->odr_out, mediumSetElementSetName);
1481         search_req->mediumSetElementSetNames = esn;
1482     }
1483     if (syntax)
1484         search_req->preferredRecordSyntax =
1485             yaz_str_to_z3950oid (c->odr_out, CLASS_RECSYN, syntax);
1486     
1487     if (!r->setname)
1488     {
1489         if (c->support_named_resultsets)
1490         {
1491             char setname[14];
1492             int ord;
1493             /* find the lowest unused ordinal so that we re-use
1494                result sets on the server. */
1495             for (ord = 1; ; ord++)
1496             {
1497                 ZOOM_resultset rp;
1498                 sprintf (setname, "%d", ord);
1499                 for (rp = c->resultsets; rp; rp = rp->next)
1500                     if (rp->setname && !strcmp (rp->setname, setname))
1501                         break;
1502                 if (!rp)
1503                     break;
1504             }
1505             r->setname = xstrdup (setname);
1506             yaz_log(log_details, "%p ZOOM_connection_send_search: allocating "
1507                     "set %s", c, r->setname);
1508         }
1509         else
1510         {
1511             yaz_log(log_details, "%p ZOOM_connection_send_search: using "
1512                     "default set", c);
1513             r->setname = xstrdup ("default");
1514         }
1515         ZOOM_options_set (r->options, "setname", r->setname);
1516     }
1517     search_req->resultSetName = odr_strdup(c->odr_out, r->setname);
1518     return send_APDU (c, apdu);
1519 }
1520
1521 static void response_default_diag(ZOOM_connection c, Z_DefaultDiagFormat *r)
1522 {
1523     int oclass;
1524     char *addinfo = 0;
1525
1526     switch (r->which)
1527     {
1528     case Z_DefaultDiagFormat_v2Addinfo:
1529         addinfo = r->u.v2Addinfo;
1530         break;
1531     case Z_DefaultDiagFormat_v3Addinfo:
1532         addinfo = r->u.v3Addinfo;
1533         break;
1534     }
1535     xfree (c->addinfo);
1536     c->addinfo = 0;
1537     set_dset_error(c, *r->condition,
1538                    yaz_z3950oid_to_str(r->diagnosticSetId, &oclass),
1539                    addinfo, 0);
1540 }
1541
1542 static void response_diag(ZOOM_connection c, Z_DiagRec *p)
1543 {
1544     if (p->which != Z_DiagRec_defaultFormat)
1545         set_ZOOM_error(c, ZOOM_ERROR_DECODE, 0);
1546     else
1547         response_default_diag(c, p->u.defaultFormat);
1548 }
1549
1550 ZOOM_API(ZOOM_record)
1551 ZOOM_record_clone (ZOOM_record srec)
1552 {
1553     char *buf;
1554     int size;
1555     ODR odr_enc;
1556     ZOOM_record nrec;
1557
1558     odr_enc = odr_createmem(ODR_ENCODE);
1559     if (!z_NamePlusRecord (odr_enc, &srec->npr, 0, 0))
1560         return 0;
1561     buf = odr_getbuf (odr_enc, &size, 0);
1562     
1563     nrec = (ZOOM_record) xmalloc (sizeof(*nrec));
1564     nrec->odr = odr_createmem(ODR_DECODE);
1565     nrec->wrbuf_marc = 0;
1566     nrec->wrbuf_iconv = 0;
1567     nrec->wrbuf_opac = 0;
1568     odr_setbuf (nrec->odr, buf, size, 0);
1569     z_NamePlusRecord (nrec->odr, &nrec->npr, 0, 0);
1570     
1571     odr_destroy (odr_enc);
1572     return nrec;
1573 }
1574
1575 ZOOM_API(ZOOM_record)
1576 ZOOM_resultset_record_immediate (ZOOM_resultset s,size_t pos)
1577 {
1578     return record_cache_lookup (s, pos);
1579 }
1580
1581 ZOOM_API(ZOOM_record)
1582 ZOOM_resultset_record (ZOOM_resultset r, size_t pos)
1583 {
1584     ZOOM_record rec = ZOOM_resultset_record_immediate(r, pos);
1585
1586     if (!rec)
1587     {
1588         /*
1589          * MIKE: I think force_sync should always be zero, but I don't
1590          * want to make this change until I get the go-ahead from
1591          * Adam, in case something depends on the old synchronous
1592          * behaviour.
1593          */
1594         int force_sync = 1;
1595         if (getenv("ZOOM_RECORD_NO_FORCE_SYNC")) force_sync = 0;
1596         ZOOM_resultset_retrieve (r, force_sync, pos, 1);
1597         rec = ZOOM_resultset_record_immediate (r, pos);
1598     }
1599     return rec;
1600 }
1601
1602 ZOOM_API(void)
1603 ZOOM_record_destroy (ZOOM_record rec)
1604 {
1605     if (!rec)
1606         return;
1607     if (rec->wrbuf_marc)
1608         wrbuf_free (rec->wrbuf_marc, 1);
1609     if (rec->wrbuf_iconv)
1610         wrbuf_free (rec->wrbuf_iconv, 1);
1611     if (rec->wrbuf_opac)
1612         wrbuf_free (rec->wrbuf_opac, 1);
1613     odr_destroy (rec->odr);
1614     xfree (rec);
1615 }
1616
1617 static const char *marc_iconv_return(ZOOM_record rec, int marc_type,
1618                                      int *len,
1619                                      const char *buf, int sz,
1620                                      const char *record_charset)
1621 {
1622     char to[40];
1623     char from[40];
1624     yaz_iconv_t cd = 0;
1625     yaz_marc_t mt = yaz_marc_create();
1626
1627     *from = '\0';
1628     strcpy(to, "UTF-8");
1629     if (record_charset && *record_charset)
1630     {
1631         /* Use "from,to" or just "from" */
1632         const char *cp = strchr(record_charset, ',');
1633         int clen = strlen(record_charset);
1634         if (cp && cp[1])
1635         {
1636             strncpy( to, cp+1, sizeof(to)-1);
1637             to[sizeof(to)-1] = '\0';
1638             clen = cp - record_charset;
1639         }
1640         if (clen > sizeof(from)-1)
1641             clen = sizeof(from)-1;
1642         
1643         if (clen)
1644             strncpy(from, record_charset, clen);
1645         from[clen] = '\0';
1646     }
1647
1648     if (*from && *to)
1649     {
1650         cd = yaz_iconv_open(to, from);
1651         yaz_marc_iconv(mt, cd);
1652     }
1653
1654     yaz_marc_xml(mt, marc_type);
1655     if (!rec->wrbuf_marc)
1656         rec->wrbuf_marc = wrbuf_alloc();
1657     wrbuf_rewind (rec->wrbuf_marc);
1658     if (yaz_marc_decode_wrbuf (mt, buf, sz, rec->wrbuf_marc) > 0)
1659     {
1660         yaz_marc_destroy(mt);
1661         if (cd)
1662             yaz_iconv_close(cd);
1663         if (len)
1664             *len = wrbuf_len(rec->wrbuf_marc);
1665         return wrbuf_buf(rec->wrbuf_marc);
1666     }
1667     yaz_marc_destroy(mt);
1668     if (cd)
1669         yaz_iconv_close(cd);
1670     return 0;
1671 }
1672
1673 static const char *record_iconv_return(ZOOM_record rec, int *len,
1674                                        const char *buf, int sz,
1675                                        const char *record_charset)
1676 {
1677     char to[40];
1678     char from[40];
1679     yaz_iconv_t cd = 0;
1680
1681     *from = '\0';
1682     strcpy(to, "UTF-8");
1683     if (record_charset && *record_charset)
1684     {
1685         /* Use "from,to" or just "from" */
1686         const char *cp = strchr(record_charset, ',');
1687         int clen = strlen(record_charset);
1688         if (cp && cp[1])
1689         {
1690             strncpy( to, cp+1, sizeof(to)-1);
1691             to[sizeof(to)-1] = '\0';
1692             clen = cp - record_charset;
1693         }
1694         if (clen > sizeof(from)-1)
1695             clen = sizeof(from)-1;
1696         
1697         if (clen)
1698             strncpy(from, record_charset, clen);
1699         from[clen] = '\0';
1700     }
1701
1702     if (*from && *to && (cd = yaz_iconv_open(to, from)))
1703     {
1704         char outbuf[12];
1705         size_t inbytesleft = sz;
1706         const char *inp = buf;
1707         
1708         if (!rec->wrbuf_iconv)
1709             rec->wrbuf_iconv = wrbuf_alloc();
1710
1711         wrbuf_rewind(rec->wrbuf_iconv);
1712
1713         while (inbytesleft)
1714         {
1715             size_t outbytesleft = sizeof(outbuf);
1716             char *outp = outbuf;
1717             size_t r = yaz_iconv (cd, (char**) &inp,
1718                                   &inbytesleft, 
1719                                   &outp, &outbytesleft);
1720             if (r == (size_t) (-1))
1721             {
1722                 int e = yaz_iconv_error(cd);
1723                 if (e != YAZ_ICONV_E2BIG)
1724                     break;
1725             }
1726             wrbuf_write(rec->wrbuf_iconv, outbuf, outp - outbuf);
1727         }
1728         wrbuf_puts(rec->wrbuf_iconv, "");
1729         buf = wrbuf_buf(rec->wrbuf_iconv);
1730         sz = wrbuf_len(rec->wrbuf_iconv);
1731         yaz_iconv_close(cd);
1732     }
1733     if (len)
1734         *len = sz;
1735     return buf;
1736 }
1737
1738 ZOOM_API(const char *)
1739 ZOOM_record_get (ZOOM_record rec, const char *type_spec, int *len)
1740 {
1741     char type[40];
1742     char charset[40];
1743     char xpath[512];
1744     const char *cp;
1745     int i;
1746     Z_NamePlusRecord *npr;
1747     
1748     if (len)
1749         *len = 0; /* default return */
1750         
1751     if (!rec)
1752         return 0;
1753     npr = rec->npr;
1754     if (!npr)
1755         return 0;
1756
1757     cp = type_spec;
1758     for (i = 0; cp[i] && i < sizeof(type)-1; i++)
1759     {
1760         if (cp[i] == ';' || cp[i] == ' ')
1761             break;
1762         type[i] = cp[i];
1763     }
1764     type[i] = '\0';
1765     charset[0] = '\0';
1766     while (type_spec[i] == ';')
1767     {
1768         i++;
1769         while (type_spec[i] == ' ')
1770             i++;
1771         if (!strncmp(type_spec+i, "charset=", 8))
1772         {
1773             int j = 0;
1774             i = i + 8; /* skip charset= */
1775             for (j = 0; type_spec[i]  && j < sizeof(charset)-1; i++, j++)
1776             {
1777                 if (type_spec[i] == ';' || type_spec[i] == ' ')
1778                     break;
1779                 charset[j] = cp[i];
1780             }
1781             charset[j] = '\0';
1782         }
1783         else if (!strncmp(type_spec+i, "xpath=", 6))
1784         {
1785             int j = 0; 
1786             i = i + 6;
1787             for (j = 0; type_spec[i] && j < sizeof(xpath)-1; i++, j++)
1788                 xpath[j] = cp[i];
1789             xpath[j] = '\0';
1790         } 
1791         while (type_spec[i] == ' ')
1792             i++;
1793     }
1794     if (!strcmp (type, "database"))
1795     {
1796         if (len)
1797             *len = (npr->databaseName ? strlen(npr->databaseName) : 0);
1798         return npr->databaseName;
1799     }
1800     else if (!strcmp (type, "syntax"))
1801     {
1802         const char *desc = 0;   
1803         if (npr->which == Z_NamePlusRecord_databaseRecord)
1804         {
1805             Z_External *r = (Z_External *) npr->u.databaseRecord;
1806             oident *ent = oid_getentbyoid(r->direct_reference);
1807             if (ent)
1808                 desc = ent->desc;
1809         }
1810         if (!desc)
1811             desc = "none";
1812         if (len)
1813             *len = strlen(desc);
1814         return desc;
1815     }
1816     if (npr->which != Z_NamePlusRecord_databaseRecord)
1817         return 0;
1818
1819     /* from now on - we have a database record .. */
1820     if (!strcmp (type, "render"))
1821     {
1822         Z_External *r = (Z_External *) npr->u.databaseRecord;
1823         oident *ent = oid_getentbyoid(r->direct_reference);
1824
1825         /* render bibliographic record .. */
1826         if (r->which == Z_External_OPAC)
1827         {
1828             r = r->u.opac->bibliographicRecord;
1829             if (!r)
1830                 return 0;
1831             ent = oid_getentbyoid(r->direct_reference);
1832         }
1833         if (r->which == Z_External_sutrs)
1834             return record_iconv_return(rec, len,
1835                                        (char*) r->u.sutrs->buf,
1836                                        r->u.sutrs->len,
1837                                        charset);
1838         else if (r->which == Z_External_octet)
1839         {
1840             const char *ret_buf;
1841             switch (ent->value)
1842             {
1843             case VAL_SOIF:
1844             case VAL_HTML:
1845             case VAL_SUTRS:
1846                 break;
1847             case VAL_TEXT_XML:
1848             case VAL_APPLICATION_XML:
1849                 break;
1850             default:
1851                 ret_buf = marc_iconv_return(
1852                     rec, YAZ_MARC_LINE, len,
1853                     (const char *) r->u.octet_aligned->buf,
1854                     r->u.octet_aligned->len,
1855                     charset);
1856                 if (ret_buf)
1857                     return ret_buf;
1858             }
1859             return record_iconv_return(rec, len,
1860                                        (const char *) r->u.octet_aligned->buf,
1861                                        r->u.octet_aligned->len,
1862                                        charset);
1863         }
1864         else if (r->which == Z_External_grs1)
1865         {
1866             if (!rec->wrbuf_marc)
1867                 rec->wrbuf_marc = wrbuf_alloc();
1868             wrbuf_rewind (rec->wrbuf_marc);
1869             yaz_display_grs1(rec->wrbuf_marc, r->u.grs1, 0);
1870             return record_iconv_return(rec, len,
1871                                        wrbuf_buf(rec->wrbuf_marc),
1872                                        wrbuf_len(rec->wrbuf_marc),
1873                                        charset);
1874         }
1875         return 0;
1876     }
1877     else if (!strcmp (type, "xml"))
1878     {
1879         Z_External *r = (Z_External *) npr->u.databaseRecord;
1880         oident *ent = oid_getentbyoid(r->direct_reference);
1881
1882         /* render bibliographic record .. */
1883         if (r->which == Z_External_OPAC)
1884         {
1885             r = r->u.opac->bibliographicRecord;
1886             if (!r)
1887                 return 0;
1888             ent = oid_getentbyoid(r->direct_reference);
1889         }
1890         
1891         if (r->which == Z_External_sutrs)
1892             return record_iconv_return(rec, len,
1893                                        (const char *) r->u.sutrs->buf,
1894                                        r->u.sutrs->len,
1895                                        charset);
1896         else if (r->which == Z_External_octet)
1897         {
1898             const char *ret_buf;
1899             int marc_decode_type = YAZ_MARC_MARCXML;
1900
1901             switch (ent->value)
1902             {
1903             case VAL_SOIF:
1904             case VAL_HTML:
1905             case VAL_SUTRS:
1906                 break;
1907             case VAL_TEXT_XML:
1908             case VAL_APPLICATION_XML:
1909                 break;
1910             default:
1911                 ret_buf = marc_iconv_return(
1912                     rec, marc_decode_type, len,
1913                     (const char *) r->u.octet_aligned->buf,
1914                     r->u.octet_aligned->len,
1915                     charset);
1916                 if (ret_buf)
1917                     return ret_buf;
1918             }
1919             return record_iconv_return(rec, len,
1920                                        (const char *) r->u.octet_aligned->buf,
1921                                        r->u.octet_aligned->len,
1922                                        charset);
1923         }
1924         else if (r->which == Z_External_grs1)
1925         {
1926             if (len) *len = 5;
1927             return "GRS-1";
1928         }
1929         return 0;
1930     }
1931     else if (!strcmp (type, "raw"))
1932     {
1933         Z_External *r = (Z_External *) npr->u.databaseRecord;
1934         
1935         if (r->which == Z_External_sutrs)
1936         {
1937             if (len) *len = r->u.sutrs->len;
1938             return (const char *) r->u.sutrs->buf;
1939         }
1940         else if (r->which == Z_External_octet)
1941         {
1942             if (len) *len = r->u.octet_aligned->len;
1943             return (const char *) r->u.octet_aligned->buf;
1944         }
1945         else /* grs-1, explain, OPAC, ... */
1946         {
1947             if (len) *len = -1;
1948             return (const char *) npr->u.databaseRecord;
1949         }
1950         return 0;
1951     }
1952     else if (!strcmp (type, "ext"))
1953     {
1954         if (len) *len = -1;
1955         return (const char *) npr->u.databaseRecord;
1956     }
1957     else if (!strcmp (type, "opac"))
1958              
1959     {
1960         Z_External *r = (Z_External *) npr->u.databaseRecord;
1961         if (r->which == Z_External_OPAC)
1962         {
1963             if (!rec->wrbuf_opac)
1964                 rec->wrbuf_opac = wrbuf_alloc();
1965             wrbuf_rewind (rec->wrbuf_opac);
1966             yaz_display_OPAC(rec->wrbuf_opac, r->u.opac, 0);
1967             return record_iconv_return(rec, len,
1968                                        wrbuf_buf(rec->wrbuf_opac),
1969                                        wrbuf_len(rec->wrbuf_opac),
1970                                        charset);
1971         }
1972     }
1973     return 0;
1974 }
1975
1976 static int strcmp_null(const char *v1, const char *v2)
1977 {
1978     if (!v1 && !v2)
1979         return 0;
1980     if (!v1 || !v2)
1981         return -1;
1982     return strcmp(v1, v2);
1983 }
1984
1985 static size_t record_hash(int pos)
1986 {
1987     if (pos < 0)
1988         pos = 0;
1989     return pos % RECORD_HASH_SIZE;
1990 }
1991
1992 static void record_cache_add (ZOOM_resultset r, Z_NamePlusRecord *npr, 
1993                               int pos)
1994 {
1995     ZOOM_record_cache rc;
1996     const char *elementSetName =
1997         ZOOM_resultset_option_get (r, "elementSetName");
1998     const char *syntax = 
1999         ZOOM_resultset_option_get (r, "preferredRecordSyntax");
2000     
2001     ZOOM_Event event = ZOOM_Event_create(ZOOM_EVENT_RECV_RECORD);
2002     ZOOM_connection_put_event(r->connection, event);
2003
2004     for (rc = r->record_hash[record_hash(pos)]; rc; rc = rc->next)
2005     {
2006         if (pos == rc->pos)
2007         {
2008             if (strcmp_null(r->schema, rc->schema))
2009                 continue;
2010             if (strcmp_null(elementSetName,rc->elementSetName))
2011                 continue;
2012             if (strcmp_null(syntax, rc->syntax))
2013                 continue;
2014             /* not destroying rc->npr (it's handled by nmem )*/
2015             rc->rec.npr = npr;
2016             /* keeping wrbuf_marc too */
2017             return;
2018         }
2019     }
2020     rc = (ZOOM_record_cache) odr_malloc (r->odr, sizeof(*rc));
2021     rc->rec.npr = npr; 
2022     rc->rec.odr = 0;
2023     rc->rec.wrbuf_marc = 0;
2024     rc->rec.wrbuf_iconv = 0;
2025     rc->rec.wrbuf_opac = 0;
2026     if (elementSetName)
2027         rc->elementSetName = odr_strdup (r->odr, elementSetName);
2028     else
2029         rc->elementSetName = 0;
2030
2031     if (syntax)
2032         rc->syntax = odr_strdup (r->odr, syntax);
2033     else
2034         rc->syntax = 0;
2035
2036     if (r->schema)
2037         rc->schema = odr_strdup (r->odr, r->schema);
2038     else
2039         rc->schema = 0;
2040
2041     rc->pos = pos;
2042     rc->next = r->record_hash[record_hash(pos)];
2043     r->record_hash[record_hash(pos)] = rc;
2044 }
2045
2046 static ZOOM_record record_cache_lookup (ZOOM_resultset r, int pos)
2047 {
2048     ZOOM_record_cache rc;
2049     const char *elementSetName =
2050         ZOOM_resultset_option_get (r, "elementSetName");
2051     const char *syntax = 
2052         ZOOM_resultset_option_get (r, "preferredRecordSyntax");
2053     
2054     for (rc = r->record_hash[record_hash(pos)]; rc; rc = rc->next)
2055     {
2056         if (pos == rc->pos)
2057         {
2058             if (strcmp_null(r->schema, rc->schema))
2059                 continue;
2060             if (strcmp_null(elementSetName,rc->elementSetName))
2061                 continue;
2062             if (strcmp_null(syntax, rc->syntax))
2063                 continue;
2064             return &rc->rec;
2065         }
2066     }
2067     return 0;
2068 }
2069                                              
2070 static void handle_records (ZOOM_connection c, Z_Records *sr,
2071                             int present_phase)
2072 {
2073     ZOOM_resultset resultset;
2074
2075     if (!c->tasks)
2076         return ;
2077     switch (c->tasks->which)
2078     {
2079     case ZOOM_TASK_SEARCH:
2080         resultset = c->tasks->u.search.resultset;
2081         break;
2082     case ZOOM_TASK_RETRIEVE:
2083         resultset = c->tasks->u.retrieve.resultset;        
2084         break;
2085     default:
2086         return;
2087     }
2088     if (sr && sr->which == Z_Records_NSD)
2089         response_default_diag(c, sr->u.nonSurrogateDiagnostic);
2090     else if (sr && sr->which == Z_Records_multipleNSD)
2091     {
2092         if (sr->u.multipleNonSurDiagnostics->num_diagRecs >= 1)
2093             response_diag(c, sr->u.multipleNonSurDiagnostics->diagRecs[0]);
2094         else
2095             set_ZOOM_error(c, ZOOM_ERROR_DECODE, 0);
2096     }
2097     else 
2098     {
2099         if (resultset->count + resultset->start > resultset->size)
2100             resultset->count = resultset->size - resultset->start;
2101         if (resultset->count < 0)
2102             resultset->count = 0;
2103         if (sr && sr->which == Z_Records_DBOSD)
2104         {
2105             int i;
2106             NMEM nmem = odr_extract_mem (c->odr_in);
2107             Z_NamePlusRecordList *p =
2108                 sr->u.databaseOrSurDiagnostics;
2109             for (i = 0; i<p->num_records; i++)
2110             {
2111                 record_cache_add (resultset, p->records[i],
2112                                   i+ resultset->start);
2113             }
2114             /* transfer our response to search_nmem .. we need it later */
2115             nmem_transfer (resultset->odr->mem, nmem);
2116             nmem_destroy (nmem);
2117             if (present_phase && p->num_records == 0)
2118             {
2119                 /* present response and we didn't get any records! */
2120                 Z_NamePlusRecord *myrec = 
2121                     zget_surrogateDiagRec(resultset->odr, 0, 14, 0);
2122                 record_cache_add(resultset, myrec, resultset->start);
2123             }
2124         }
2125         else if (present_phase)
2126         {
2127             /* present response and we didn't get any records! */
2128             Z_NamePlusRecord *myrec = 
2129                 zget_surrogateDiagRec(resultset->odr, 0, 14, 0);
2130             record_cache_add(resultset, myrec, resultset->start);
2131         }
2132     }
2133 }
2134
2135 static void handle_present_response (ZOOM_connection c, Z_PresentResponse *pr)
2136 {
2137     handle_records (c, pr->records, 1);
2138 }
2139
2140 static void handle_queryExpressionTerm(ZOOM_options opt, const char *name,
2141                                        Z_Term *term)
2142 {
2143     switch (term->which)
2144     {
2145     case Z_Term_general:
2146         ZOOM_options_setl(opt, name,
2147                           (const char *)(term->u.general->buf), 
2148                           term->u.general->len);
2149         break;
2150     case Z_Term_characterString:
2151         ZOOM_options_set(opt, name, term->u.characterString);
2152         break;
2153     case Z_Term_numeric:
2154         ZOOM_options_set_int(opt, name, *term->u.numeric);
2155         break;
2156     }
2157 }
2158
2159 static void handle_queryExpression(ZOOM_options opt, const char *name,
2160                                    Z_QueryExpression *exp)
2161 {
2162     char opt_name[80];
2163     
2164     switch (exp->which)
2165     {
2166     case Z_QueryExpression_term:
2167         if (exp->u.term && exp->u.term->queryTerm)
2168         {
2169             sprintf(opt_name, "%s.term", name);
2170             handle_queryExpressionTerm(opt, opt_name, exp->u.term->queryTerm);
2171         }
2172         break;
2173     case Z_QueryExpression_query:
2174         break;
2175     }
2176 }
2177
2178 static void handle_searchResult(ZOOM_connection c, ZOOM_resultset resultset,
2179                                 Z_OtherInformation *o)
2180 {
2181     int i;
2182     for (i = 0; o && i < o->num_elements; i++)
2183     {
2184         if (o->list[i]->which == Z_OtherInfo_externallyDefinedInfo)
2185         {
2186             Z_External *ext = o->list[i]->information.externallyDefinedInfo;
2187             
2188             if (ext->which == Z_External_searchResult1)
2189             {
2190                 int j;
2191                 Z_SearchInfoReport *sr = ext->u.searchResult1;
2192                 
2193                 if (sr->num)
2194                     ZOOM_options_set_int(
2195                         resultset->options, "searchresult.size", sr->num);
2196
2197                 for (j = 0; j < sr->num; j++)
2198                 {
2199                     Z_SearchInfoReport_s *ent =
2200                         ext->u.searchResult1->elements[j];
2201                     char pref[80];
2202                     
2203                     sprintf(pref, "searchresult.%d", j);
2204
2205                     if (ent->subqueryId)
2206                     {
2207                         char opt_name[80];
2208                         sprintf(opt_name, "%s.id", pref);
2209                         ZOOM_options_set(resultset->options, opt_name,
2210                                          ent->subqueryId);
2211                     }
2212                     if (ent->subqueryExpression)
2213                     {
2214                         char opt_name[80];
2215                         sprintf(opt_name, "%s.subquery", pref);
2216                         handle_queryExpression(resultset->options, opt_name,
2217                                                ent->subqueryExpression);
2218                     }
2219                     if (ent->subqueryInterpretation)
2220                     {
2221                         char opt_name[80];
2222                         sprintf(opt_name, "%s.interpretation", pref);
2223                         handle_queryExpression(resultset->options, opt_name,
2224                                                ent->subqueryInterpretation);
2225                     }
2226                     if (ent->subqueryRecommendation)
2227                     {
2228                         char opt_name[80];
2229                         sprintf(opt_name, "%s.recommendation", pref);
2230                         handle_queryExpression(resultset->options, opt_name,
2231                                                ent->subqueryRecommendation);
2232                     }
2233                     if (ent->subqueryCount)
2234                     {
2235                         char opt_name[80];
2236                         sprintf(opt_name, "%s.count", pref);
2237                         ZOOM_options_set_int(resultset->options, opt_name,
2238                                              *ent->subqueryCount);
2239                     }                                             
2240                 }
2241             }
2242         }
2243     }
2244 }
2245
2246 static void handle_search_response (ZOOM_connection c, Z_SearchResponse *sr)
2247 {
2248     ZOOM_resultset resultset;
2249     ZOOM_Event event;
2250
2251     if (!c->tasks || c->tasks->which != ZOOM_TASK_SEARCH)
2252         return ;
2253
2254     event = ZOOM_Event_create(ZOOM_EVENT_RECV_SEARCH);
2255     ZOOM_connection_put_event(c, event);
2256
2257     resultset = c->tasks->u.search.resultset;
2258
2259     handle_searchResult(c, resultset, sr->additionalSearchInfo);
2260
2261     resultset->size = *sr->resultCount;
2262     handle_records (c, sr->records, 0);
2263 }
2264
2265 static void sort_response (ZOOM_connection c, Z_SortResponse *res)
2266 {
2267     if (res->diagnostics && res->num_diagnostics > 0)
2268         response_diag (c, res->diagnostics[0]);
2269 }
2270
2271 static int scan_response (ZOOM_connection c, Z_ScanResponse *res)
2272 {
2273     NMEM nmem = odr_extract_mem (c->odr_in);
2274     ZOOM_scanset scan;
2275
2276     if (!c->tasks || c->tasks->which != ZOOM_TASK_SCAN)
2277         return 0;
2278     scan = c->tasks->u.scan.scan;
2279
2280     if (res->entries && res->entries->nonsurrogateDiagnostics)
2281         response_diag(c, res->entries->nonsurrogateDiagnostics[0]);
2282     scan->scan_response = res;
2283     nmem_transfer (scan->odr->mem, nmem);
2284     if (res->stepSize)
2285         ZOOM_options_set_int (scan->options, "stepSize", *res->stepSize);
2286     if (res->positionOfTerm)
2287         ZOOM_options_set_int (scan->options, "position", *res->positionOfTerm);
2288     if (res->scanStatus)
2289         ZOOM_options_set_int (scan->options, "scanStatus", *res->scanStatus);
2290     if (res->numberOfEntriesReturned)
2291         ZOOM_options_set_int (scan->options, "number",
2292                               *res->numberOfEntriesReturned);
2293     nmem_destroy (nmem);
2294     return 1;
2295 }
2296
2297 static zoom_ret send_sort (ZOOM_connection c,
2298                            ZOOM_resultset resultset)
2299 {
2300     if (c->error)
2301         resultset->r_sort_spec = 0;
2302     if (resultset->r_sort_spec)
2303     {
2304         Z_APDU *apdu = zget_APDU(c->odr_out, Z_APDU_sortRequest);
2305         Z_SortRequest *req = apdu->u.sortRequest;
2306         
2307         req->num_inputResultSetNames = 1;
2308         req->inputResultSetNames = (Z_InternationalString **)
2309             odr_malloc (c->odr_out, sizeof(*req->inputResultSetNames));
2310         req->inputResultSetNames[0] =
2311             odr_strdup (c->odr_out, resultset->setname);
2312         req->sortedResultSetName = odr_strdup (c->odr_out, resultset->setname);
2313         req->sortSequence = resultset->r_sort_spec;
2314         resultset->r_sort_spec = 0;
2315         return send_APDU (c, apdu);
2316     }
2317     return zoom_complete;
2318 }
2319
2320 static zoom_ret send_present(ZOOM_connection c)
2321 {
2322     Z_APDU *apdu = 0;
2323     Z_PresentRequest *req = 0;
2324     int i = 0;
2325     const char *syntax = 0;
2326     const char *elementSetName = 0;
2327     ZOOM_resultset  resultset;
2328
2329     if (!c->tasks)
2330     {
2331         yaz_log(log_details, "%p send_present no tasks", c);
2332         return zoom_complete;
2333     }
2334     switch (c->tasks->which)
2335     {
2336     case ZOOM_TASK_SEARCH:
2337         resultset = c->tasks->u.search.resultset;
2338         break;
2339     case ZOOM_TASK_RETRIEVE:
2340         resultset = c->tasks->u.retrieve.resultset;
2341         resultset->start = c->tasks->u.retrieve.start;
2342         resultset->count = c->tasks->u.retrieve.count;
2343
2344         if (resultset->start >= resultset->size)
2345         {
2346             yaz_log(log_details, "%p send_present start=%d >= size=%d",
2347                     c, resultset->start, resultset->size);
2348             return zoom_complete;
2349         }
2350         if (resultset->start + resultset->count > resultset->size)
2351             resultset->count = resultset->size - resultset->start;
2352         break;
2353     default:
2354         return zoom_complete;
2355     }
2356     yaz_log(log_details, "%p send_present start=%d count=%d",
2357             c, resultset->start, resultset->count);
2358
2359     syntax = ZOOM_resultset_option_get (resultset, "preferredRecordSyntax");
2360     elementSetName = ZOOM_resultset_option_get (resultset, "elementSetName");
2361
2362     if (c->error)                  /* don't continue on error */
2363         return zoom_complete;
2364     if (resultset->start < 0)
2365         return zoom_complete;
2366     for (i = 0; i<resultset->count; i++)
2367     {
2368         ZOOM_record rec =
2369             record_cache_lookup (resultset, i + resultset->start);
2370         if (!rec)
2371             break;
2372         else
2373         {
2374             ZOOM_Event event = ZOOM_Event_create(ZOOM_EVENT_RECV_RECORD);
2375             ZOOM_connection_put_event(c, event);
2376         }
2377     }
2378     if (i == resultset->count)
2379     {
2380         yaz_log(log_details, "%p send_present skip=%d no more to fetch", c, i);
2381         return zoom_complete;
2382     }
2383
2384     apdu = zget_APDU(c->odr_out, Z_APDU_presentRequest);
2385     req = apdu->u.presentRequest;
2386
2387     resultset->start += i;
2388     resultset->count -= i;
2389
2390     if (i)
2391         yaz_log(log_details, "%p send_present skip=%d", c, i);
2392
2393     *req->resultSetStartPoint = resultset->start + 1;
2394     *req->numberOfRecordsRequested = resultset->step>0 ?
2395         resultset->step : resultset->count;
2396     if (*req->numberOfRecordsRequested + resultset->start > resultset->size)
2397         *req->numberOfRecordsRequested = resultset->size -  resultset->start;
2398     assert (*req->numberOfRecordsRequested > 0);
2399
2400     if (syntax && *syntax)
2401         req->preferredRecordSyntax =
2402             yaz_str_to_z3950oid (c->odr_out, CLASS_RECSYN, syntax);
2403
2404     if (resultset->schema && *resultset->schema)
2405     {
2406         Z_RecordComposition *compo = (Z_RecordComposition *)
2407             odr_malloc (c->odr_out, sizeof(*compo));
2408
2409         req->recordComposition = compo;
2410         compo->which = Z_RecordComp_complex;
2411         compo->u.complex = (Z_CompSpec *)
2412             odr_malloc(c->odr_out, sizeof(*compo->u.complex));
2413         compo->u.complex->selectAlternativeSyntax = (bool_t *) 
2414             odr_malloc(c->odr_out, sizeof(bool_t));
2415         *compo->u.complex->selectAlternativeSyntax = 0;
2416
2417         compo->u.complex->generic = (Z_Specification *)
2418             odr_malloc(c->odr_out, sizeof(*compo->u.complex->generic));
2419
2420         compo->u.complex->generic->which = Z_Schema_oid;
2421         compo->u.complex->generic->schema.oid = (Odr_oid *)
2422             yaz_str_to_z3950oid (c->odr_out, CLASS_SCHEMA, resultset->schema);
2423
2424         if (!compo->u.complex->generic->schema.oid)
2425         {
2426             /* OID wasn't a schema! Try record syntax instead. */
2427
2428             compo->u.complex->generic->schema.oid = (Odr_oid *)
2429                 yaz_str_to_z3950oid (c->odr_out, CLASS_RECSYN, resultset->schema);
2430         }
2431         if (elementSetName && *elementSetName)
2432         {
2433             compo->u.complex->generic->elementSpec = (Z_ElementSpec *)
2434                 odr_malloc(c->odr_out, sizeof(Z_ElementSpec));
2435             compo->u.complex->generic->elementSpec->which =
2436                 Z_ElementSpec_elementSetName;
2437             compo->u.complex->generic->elementSpec->u.elementSetName =
2438                 odr_strdup (c->odr_out, elementSetName);
2439         }
2440         else
2441             compo->u.complex->generic->elementSpec = 0;
2442         compo->u.complex->num_dbSpecific = 0;
2443         compo->u.complex->dbSpecific = 0;
2444         compo->u.complex->num_recordSyntax = 0;
2445         compo->u.complex->recordSyntax = 0;
2446     }
2447     else if (elementSetName && *elementSetName)
2448     {
2449         Z_ElementSetNames *esn = (Z_ElementSetNames *)
2450             odr_malloc (c->odr_out, sizeof(*esn));
2451         Z_RecordComposition *compo = (Z_RecordComposition *)
2452             odr_malloc (c->odr_out, sizeof(*compo));
2453         
2454         esn->which = Z_ElementSetNames_generic;
2455         esn->u.generic = odr_strdup (c->odr_out, elementSetName);
2456         compo->which = Z_RecordComp_simple;
2457         compo->u.simple = esn;
2458         req->recordComposition = compo;
2459     }
2460     req->resultSetId = odr_strdup(c->odr_out, resultset->setname);
2461     return send_APDU (c, apdu);
2462 }
2463
2464 ZOOM_API(ZOOM_scanset)
2465 ZOOM_connection_scan (ZOOM_connection c, const char *start)
2466 {
2467     ZOOM_scanset s;
2468     ZOOM_query q = ZOOM_query_create();
2469
2470     ZOOM_query_prefix (q, start);
2471
2472     s = ZOOM_connection_scan1(c, q);
2473     ZOOM_query_destroy (q);
2474     return s;
2475
2476 }
2477
2478 ZOOM_API(ZOOM_scanset)
2479 ZOOM_connection_scan1 (ZOOM_connection c, ZOOM_query q)
2480 {
2481     ZOOM_scanset scan = (ZOOM_scanset) xmalloc (sizeof(*scan));
2482     char *start;
2483     char *freeme = 0;
2484
2485     scan->connection = c;
2486     scan->odr = odr_createmem (ODR_DECODE);
2487     scan->options = ZOOM_options_create_with_parent (c->options);
2488     scan->refcount = 1;
2489     scan->scan_response = 0;
2490
2491     /*
2492      * We need to check the query-type, so we can recognise CQL and
2493      * CCL and compile them into a form that we can use here.  The
2494      * ZOOM_query structure has no explicit `type' member, but
2495      * inspection of the ZOOM_query_prefix() and ZOOM_query_cql()
2496      * functions shows how the structure is set up in each case.
2497      */
2498     if (q->z_query->which == Z_Query_type_1) {
2499         yaz_log(log_api, "%p ZOOM_connection_scan1 q=%p PQF '%s'",
2500                 c, q, q->query_string);
2501         start = q->query_string;
2502     } else if (q->z_query->which == Z_Query_type_104) {
2503         yaz_log(log_api, "%p ZOOM_connection_scan1 q=%p CQL '%s'",
2504                 c, q, q->query_string);
2505         start = freeme = cql2pqf(c, q->query_string);
2506         if (start == 0)
2507             return 0;
2508     } else {
2509         yaz_log(YLOG_FATAL, "%p ZOOM_connection_scan1 q=%p unknown type '%s'",
2510                 c, q, q->query_string);
2511         abort();
2512     }
2513
2514     scan->termListAndStartPoint =
2515         p_query_scan(scan->odr, PROTO_Z3950, &scan->attributeSet, start);
2516     xfree (freeme);
2517     if (scan->termListAndStartPoint != 0)
2518     {
2519         ZOOM_task task = ZOOM_connection_add_task (c, ZOOM_TASK_SCAN);
2520         task->u.scan.scan = scan;
2521         
2522         (scan->refcount)++;
2523         if (!c->async)
2524         {
2525             while (ZOOM_event (1, &c))
2526                 ;
2527         }
2528     }
2529     return scan;
2530 }
2531
2532 ZOOM_API(void)
2533 ZOOM_scanset_destroy (ZOOM_scanset scan)
2534 {
2535     if (!scan)
2536         return;
2537     (scan->refcount)--;
2538     if (scan->refcount == 0)
2539     {
2540         odr_destroy (scan->odr);
2541         
2542         ZOOM_options_destroy (scan->options);
2543         xfree (scan);
2544     }
2545 }
2546
2547 static zoom_ret send_package (ZOOM_connection c)
2548 {
2549     ZOOM_Event event;
2550
2551     yaz_log(log_details, "%p send_package", c);
2552     if (!c->tasks)
2553         return zoom_complete;
2554     assert (c->tasks->which == ZOOM_TASK_PACKAGE);
2555     
2556     event = ZOOM_Event_create (ZOOM_EVENT_SEND_APDU);
2557     ZOOM_connection_put_event (c, event);
2558     
2559     c->buf_out = c->tasks->u.package->buf_out;
2560     c->len_out = c->tasks->u.package->len_out;
2561
2562     return do_write(c);
2563 }
2564
2565 static zoom_ret send_scan (ZOOM_connection c)
2566 {
2567     ZOOM_scanset scan;
2568     Z_APDU *apdu = zget_APDU(c->odr_out, Z_APDU_scanRequest);
2569     Z_ScanRequest *req = apdu->u.scanRequest;
2570
2571     yaz_log(log_details, "%p send_scan", c);
2572     if (!c->tasks)
2573         return zoom_complete;
2574     assert (c->tasks->which == ZOOM_TASK_SCAN);
2575     scan = c->tasks->u.scan.scan;
2576
2577     req->termListAndStartPoint = scan->termListAndStartPoint;
2578     req->attributeSet = scan->attributeSet;
2579
2580     *req->numberOfTermsRequested =
2581         ZOOM_options_get_int(scan->options, "number", 10);
2582
2583     req->preferredPositionInResponse =
2584         odr_intdup (c->odr_out,
2585                     ZOOM_options_get_int(scan->options, "position", 1));
2586
2587     req->stepSize =
2588         odr_intdup (c->odr_out,
2589                     ZOOM_options_get_int(scan->options, "stepSize", 0));
2590     
2591     req->databaseNames = set_DatabaseNames (c, scan->options, 
2592                                             &req->num_databaseNames);
2593
2594     return send_APDU (c, apdu);
2595 }
2596
2597 ZOOM_API(size_t)
2598 ZOOM_scanset_size (ZOOM_scanset scan)
2599 {
2600     if (!scan || !scan->scan_response || !scan->scan_response->entries)
2601         return 0;
2602     return scan->scan_response->entries->num_entries;
2603 }
2604
2605 ZOOM_API(const char *)
2606 ZOOM_scanset_term (ZOOM_scanset scan, size_t pos,
2607                    int *occ, int *len)
2608 {
2609     const char *term = 0;
2610     size_t noent = ZOOM_scanset_size (scan);
2611     Z_ScanResponse *res = scan->scan_response;
2612     
2613     *len = 0;
2614     *occ = 0;
2615     if (pos >= noent)
2616         return 0;
2617     if (res->entries->entries[pos]->which == Z_Entry_termInfo)
2618     {
2619         Z_TermInfo *t = res->entries->entries[pos]->u.termInfo;
2620         
2621         if (t->term->which == Z_Term_general)
2622         {
2623             term = (const char *) t->term->u.general->buf;
2624             *len = t->term->u.general->len;
2625         }
2626         *occ = t->globalOccurrences ? *t->globalOccurrences : 0;
2627     }
2628     return term;
2629 }
2630
2631 ZOOM_API(const char *)
2632 ZOOM_scanset_display_term (ZOOM_scanset scan, size_t pos,
2633                            int *occ, int *len)
2634 {
2635     const char *term = 0;
2636     size_t noent = ZOOM_scanset_size (scan);
2637     Z_ScanResponse *res = scan->scan_response;
2638     
2639     *len = 0;
2640     *occ = 0;
2641     if (pos >= noent)
2642         return 0;
2643     if (res->entries->entries[pos]->which == Z_Entry_termInfo)
2644     {
2645         Z_TermInfo *t = res->entries->entries[pos]->u.termInfo;
2646
2647         if (t->displayTerm)
2648         {
2649             term = t->displayTerm;
2650             *len = strlen(term);
2651         }
2652         else if (t->term->which == Z_Term_general)
2653         {
2654             term = (const char *) t->term->u.general->buf;
2655             *len = t->term->u.general->len;
2656         }
2657         *occ = t->globalOccurrences ? *t->globalOccurrences : 0;
2658     }
2659     return term;
2660 }
2661
2662 ZOOM_API(const char *)
2663 ZOOM_scanset_option_get (ZOOM_scanset scan, const char *key)
2664 {
2665     return ZOOM_options_get (scan->options, key);
2666 }
2667
2668 ZOOM_API(void)
2669 ZOOM_scanset_option_set (ZOOM_scanset scan, const char *key,
2670                               const char *val)
2671 {
2672     ZOOM_options_set (scan->options, key, val);
2673 }
2674
2675 static Z_APDU *create_es_package (ZOOM_package p, int type)
2676 {
2677     const char *str;
2678     Z_APDU *apdu = zget_APDU(p->odr_out, Z_APDU_extendedServicesRequest);
2679     Z_ExtendedServicesRequest *req = apdu->u.extendedServicesRequest;
2680     
2681     *req->function = Z_ExtendedServicesRequest_create;
2682     
2683     str = ZOOM_options_get(p->options, "package-name");
2684     if (str && *str)
2685         req->packageName = nmem_strdup (p->odr_out->mem, str);
2686     
2687     str = ZOOM_options_get(p->options, "user-id");
2688     if (str)
2689         req->userId = nmem_strdup (p->odr_out->mem, str);
2690     
2691     req->packageType = yaz_oidval_to_z3950oid(p->odr_out, CLASS_EXTSERV,
2692                                               type);
2693
2694     str = ZOOM_options_get(p->options, "function");
2695     if (str)
2696     {
2697         if (!strcmp (str, "create"))
2698             *req->function = 1;
2699         if (!strcmp (str, "delete"))
2700             *req->function = 2;
2701         if (!strcmp (str, "modify"))
2702             *req->function = 3;
2703     }
2704     return apdu;
2705 }
2706
2707 static const char *ill_array_lookup (void *clientData, const char *idx)
2708 {
2709     ZOOM_package p = (ZOOM_package) clientData;
2710     return ZOOM_options_get (p->options, idx+4);
2711 }
2712
2713 static Z_External *encode_ill_request (ZOOM_package p)
2714 {
2715     ODR out = p->odr_out;
2716     ILL_Request *req;
2717     Z_External *r = 0;
2718     struct ill_get_ctl ctl;
2719         
2720     ctl.odr = p->odr_out;
2721     ctl.clientData = p;
2722     ctl.f = ill_array_lookup;
2723         
2724     req = ill_get_ILLRequest(&ctl, "ill", 0);
2725         
2726     if (!ill_Request (out, &req, 0, 0))
2727     {
2728         int ill_request_size;
2729         char *ill_request_buf = odr_getbuf (out, &ill_request_size, 0);
2730         if (ill_request_buf)
2731             odr_setbuf (out, ill_request_buf, ill_request_size, 1);
2732         return 0;
2733     }
2734     else
2735     {
2736         oident oid;
2737         int illRequest_size = 0;
2738         char *illRequest_buf = odr_getbuf (out, &illRequest_size, 0);
2739                 
2740         oid.proto = PROTO_GENERAL;
2741         oid.oclass = CLASS_GENERAL;
2742         oid.value = VAL_ISO_ILL_1;
2743                 
2744         r = (Z_External *) odr_malloc (out, sizeof(*r));
2745         r->direct_reference = odr_oiddup(out,oid_getoidbyent(&oid)); 
2746         r->indirect_reference = 0;
2747         r->descriptor = 0;
2748         r->which = Z_External_single;
2749                 
2750         r->u.single_ASN1_type =
2751             odr_create_Odr_oct(out,
2752                                (unsigned char *)illRequest_buf,
2753                                illRequest_size);
2754     }
2755     return r;
2756 }
2757
2758 static Z_ItemOrder *encode_item_order(ZOOM_package p)
2759 {
2760     Z_ItemOrder *req = (Z_ItemOrder *) odr_malloc (p->odr_out, sizeof(*req));
2761     const char *str;
2762     
2763     req->which = Z_IOItemOrder_esRequest;
2764     req->u.esRequest = (Z_IORequest *) 
2765         odr_malloc(p->odr_out,sizeof(Z_IORequest));
2766
2767     /* to keep part ... */
2768     req->u.esRequest->toKeep = (Z_IOOriginPartToKeep *)
2769         odr_malloc(p->odr_out,sizeof(Z_IOOriginPartToKeep));
2770     req->u.esRequest->toKeep->supplDescription = 0;
2771     req->u.esRequest->toKeep->contact = (Z_IOContact *)
2772         odr_malloc (p->odr_out, sizeof(*req->u.esRequest->toKeep->contact));
2773         
2774     str = ZOOM_options_get(p->options, "contact-name");
2775     req->u.esRequest->toKeep->contact->name = str ?
2776         nmem_strdup (p->odr_out->mem, str) : 0;
2777         
2778     str = ZOOM_options_get(p->options, "contact-phone");
2779     req->u.esRequest->toKeep->contact->phone = str ?
2780         nmem_strdup (p->odr_out->mem, str) : 0;
2781         
2782     str = ZOOM_options_get(p->options, "contact-email");
2783     req->u.esRequest->toKeep->contact->email = str ?
2784         nmem_strdup (p->odr_out->mem, str) : 0;
2785         
2786     req->u.esRequest->toKeep->addlBilling = 0;
2787         
2788     /* not to keep part ... */
2789     req->u.esRequest->notToKeep = (Z_IOOriginPartNotToKeep *)
2790         odr_malloc(p->odr_out,sizeof(Z_IOOriginPartNotToKeep));
2791         
2792     str = ZOOM_options_get(p->options, "itemorder-setname");
2793     if (!str)
2794         str = "default";
2795
2796     if (!*str) 
2797         req->u.esRequest->notToKeep->resultSetItem = 0;
2798     else
2799     {
2800         req->u.esRequest->notToKeep->resultSetItem = (Z_IOResultSetItem *)
2801            odr_malloc(p->odr_out, sizeof(Z_IOResultSetItem));
2802
2803         req->u.esRequest->notToKeep->resultSetItem->resultSetId =
2804            nmem_strdup (p->odr_out->mem, str);
2805         req->u.esRequest->notToKeep->resultSetItem->item =
2806             (int *) odr_malloc(p->odr_out, sizeof(int));
2807         
2808         str = ZOOM_options_get(p->options, "itemorder-item");
2809         *req->u.esRequest->notToKeep->resultSetItem->item =
2810             (str ? atoi(str) : 1);
2811     }
2812     req->u.esRequest->notToKeep->itemRequest = encode_ill_request(p);
2813     
2814     return req;
2815 }
2816
2817 Z_APDU *create_admin_package(ZOOM_package p, int type, 
2818                              Z_ESAdminOriginPartToKeep **toKeepP,
2819                              Z_ESAdminOriginPartNotToKeep **notToKeepP)
2820 {
2821     Z_APDU *apdu = create_es_package (p, VAL_ADMINSERVICE);
2822     if (apdu)
2823     {
2824         Z_ESAdminOriginPartToKeep  *toKeep;
2825         Z_ESAdminOriginPartNotToKeep  *notToKeep;
2826         Z_External *r = (Z_External *) odr_malloc (p->odr_out, sizeof(*r));
2827         const char *first_db = "Default";
2828         int num_db;
2829         char **db = set_DatabaseNames(p->connection, p->options, &num_db);
2830         if (num_db > 0)
2831             first_db = db[0];
2832             
2833         r->direct_reference =
2834             yaz_oidval_to_z3950oid(p->odr_out, CLASS_EXTSERV,
2835                                    VAL_ADMINSERVICE);
2836         r->descriptor = 0;
2837         r->indirect_reference = 0;
2838         r->which = Z_External_ESAdmin;
2839         
2840         r->u.adminService = (Z_Admin *)
2841             odr_malloc(p->odr_out, sizeof(*r->u.adminService));
2842         r->u.adminService->which = Z_Admin_esRequest;
2843         r->u.adminService->u.esRequest = (Z_AdminEsRequest *)
2844             odr_malloc(p->odr_out, sizeof(*r->u.adminService->u.esRequest));
2845         
2846         toKeep = r->u.adminService->u.esRequest->toKeep =
2847             (Z_ESAdminOriginPartToKeep *) 
2848             odr_malloc(p->odr_out, sizeof(*r->u.adminService->u.esRequest->toKeep));
2849         toKeep->which = type;
2850         toKeep->databaseName = odr_strdup(p->odr_out, first_db);
2851         toKeep->u.create = odr_nullval();
2852         apdu->u.extendedServicesRequest->taskSpecificParameters = r;
2853         
2854         r->u.adminService->u.esRequest->notToKeep = notToKeep =
2855             (Z_ESAdminOriginPartNotToKeep *)
2856             odr_malloc(p->odr_out,
2857                        sizeof(*r->u.adminService->u.esRequest->notToKeep));
2858         notToKeep->which = Z_ESAdminOriginPartNotToKeep_recordsWillFollow;
2859         notToKeep->u.recordsWillFollow = odr_nullval();
2860         if (toKeepP)
2861             *toKeepP = toKeep;
2862         if (notToKeepP)
2863             *notToKeepP = notToKeep;
2864     }
2865     return apdu;
2866 }
2867
2868 static Z_APDU *create_xmlupdate_package(ZOOM_package p)
2869 {
2870     Z_APDU *apdu = create_es_package(p, VAL_XMLES);
2871     Z_ExtendedServicesRequest *req = apdu->u.extendedServicesRequest;
2872     Z_External *ext = (Z_External *) odr_malloc(p->odr_out, sizeof(*ext));
2873     const char *doc = ZOOM_options_get(p->options, "doc");
2874
2875     if (!doc)
2876         doc = "";
2877
2878     req->taskSpecificParameters = ext;
2879     ext->direct_reference = req->packageType;
2880     ext->descriptor = 0;
2881     ext->indirect_reference = 0;
2882     
2883     ext->which = Z_External_octet;
2884     ext->u.single_ASN1_type =
2885         odr_create_Odr_oct(p->odr_out, (const unsigned char *) doc,
2886                            strlen(doc));
2887     return apdu;
2888 }
2889
2890 static Z_APDU *create_update_package(ZOOM_package p)
2891 {
2892     Z_APDU *apdu = 0;
2893     const char *first_db = "Default";
2894     int num_db;
2895     char **db = set_DatabaseNames(p->connection, p->options, &num_db);
2896     const char *action = ZOOM_options_get(p->options, "action");
2897     const char *recordIdOpaque = ZOOM_options_get(p->options, "recordIdOpaque");
2898     const char *recordIdNumber = ZOOM_options_get(p->options, "recordIdNumber");
2899     const char *record_buf = ZOOM_options_get(p->options, "record");
2900     const char *syntax_str = ZOOM_options_get(p->options, "syntax");
2901     int syntax_oid = VAL_NONE;
2902     int action_no = -1;
2903     
2904     if (syntax_str)
2905         syntax_oid = oid_getvalbyname(syntax_str);
2906     if (!record_buf)
2907     {
2908         record_buf = "void";
2909         syntax_oid = VAL_SUTRS;
2910     }
2911     if (syntax_oid == VAL_NONE)
2912         syntax_oid = VAL_TEXT_XML;
2913     
2914     if (num_db > 0)
2915         first_db = db[0];
2916     
2917     if (!action)
2918         action = "specialUpdate";
2919     
2920     if (!strcmp(action, "recordInsert"))
2921         action_no = Z_IUOriginPartToKeep_recordInsert;
2922     else if (!strcmp(action, "recordReplace"))
2923         action_no = Z_IUOriginPartToKeep_recordReplace;
2924     else if (!strcmp(action, "recordDelete"))
2925         action_no = Z_IUOriginPartToKeep_recordDelete;
2926     else if (!strcmp(action, "elementUpdate"))
2927         action_no = Z_IUOriginPartToKeep_elementUpdate;
2928     else if (!strcmp(action, "specialUpdate"))
2929         action_no = Z_IUOriginPartToKeep_specialUpdate;
2930     else
2931         return 0;
2932
2933     apdu = create_es_package (p, VAL_DBUPDATE);
2934     if (apdu)
2935     {
2936         Z_IUOriginPartToKeep *toKeep;
2937         Z_IUSuppliedRecords *notToKeep;
2938         Z_External *r = (Z_External *)
2939             odr_malloc (p->odr_out, sizeof(*r));
2940         
2941         apdu->u.extendedServicesRequest->taskSpecificParameters = r;
2942         
2943         r->direct_reference =
2944             yaz_oidval_to_z3950oid(p->odr_out, CLASS_EXTSERV,
2945                                    VAL_DBUPDATE);
2946         r->descriptor = 0;
2947         r->which = Z_External_update;
2948         r->indirect_reference = 0;
2949         r->u.update = (Z_IUUpdate *)
2950             odr_malloc(p->odr_out, sizeof(*r->u.update));
2951         
2952         r->u.update->which = Z_IUUpdate_esRequest;
2953         r->u.update->u.esRequest = (Z_IUUpdateEsRequest *)
2954             odr_malloc(p->odr_out, sizeof(*r->u.update->u.esRequest));
2955         toKeep = r->u.update->u.esRequest->toKeep = 
2956             (Z_IUOriginPartToKeep *)
2957             odr_malloc(p->odr_out, sizeof(*toKeep));
2958         
2959         toKeep->databaseName = odr_strdup(p->odr_out, first_db);
2960         toKeep->schema = 0;
2961         toKeep->elementSetName = 0;
2962         toKeep->actionQualifier = 0;
2963         toKeep->action = odr_intdup(p->odr_out, action_no);
2964         
2965         notToKeep = r->u.update->u.esRequest->notToKeep = 
2966             (Z_IUSuppliedRecords *)
2967             odr_malloc(p->odr_out, sizeof(*notToKeep));
2968         notToKeep->num = 1;
2969         notToKeep->elements = (Z_IUSuppliedRecords_elem **)
2970             odr_malloc(p->odr_out, sizeof(*notToKeep->elements));
2971         notToKeep->elements[0] = (Z_IUSuppliedRecords_elem *)
2972             odr_malloc(p->odr_out, sizeof(**notToKeep->elements));
2973         notToKeep->elements[0]->which = Z_IUSuppliedRecords_elem_opaque;
2974         if (recordIdOpaque)
2975         {
2976             notToKeep->elements[0]->u.opaque = 
2977                 odr_create_Odr_oct(p->odr_out,
2978                                    (const unsigned char *) recordIdOpaque,
2979                                    strlen(recordIdOpaque));
2980         }
2981         else if (recordIdNumber)
2982         {
2983             notToKeep->elements[0]->which = Z_IUSuppliedRecords_elem_number;
2984             
2985             notToKeep->elements[0]->u.number =
2986                 odr_intdup(p->odr_out, atoi(recordIdNumber));
2987         }
2988         else
2989             notToKeep->elements[0]->u.opaque = 0;
2990         notToKeep->elements[0]->supplementalId = 0;
2991         notToKeep->elements[0]->correlationInfo = 0;
2992         notToKeep->elements[0]->record =
2993             z_ext_record(p->odr_out, syntax_oid,
2994                          record_buf, strlen(record_buf));
2995     }
2996     if (0 && apdu)
2997     {
2998        ODR print = odr_createmem(ODR_PRINT);
2999
3000        z_APDU(print, &apdu, 0, 0);
3001        odr_destroy(print);
3002     }
3003     return apdu;
3004 }
3005
3006 ZOOM_API(void)
3007     ZOOM_package_send (ZOOM_package p, const char *type)
3008 {
3009     Z_APDU *apdu = 0;
3010     ZOOM_connection c;
3011     if (!p)
3012         return;
3013     c = p->connection;
3014     odr_reset (p->odr_out);
3015     xfree (p->buf_out);
3016     p->buf_out = 0;
3017     if (!strcmp(type, "itemorder"))
3018     {
3019         apdu = create_es_package (p, VAL_ITEMORDER);
3020         if (apdu)
3021         {
3022             Z_External *r = (Z_External *) odr_malloc (p->odr_out, sizeof(*r));
3023             
3024             r->direct_reference =
3025                 yaz_oidval_to_z3950oid(p->odr_out, CLASS_EXTSERV,
3026                                        VAL_ITEMORDER);
3027             r->descriptor = 0;
3028             r->which = Z_External_itemOrder;
3029             r->indirect_reference = 0;
3030             r->u.itemOrder = encode_item_order (p);
3031
3032             apdu->u.extendedServicesRequest->taskSpecificParameters = r;
3033         }
3034     }
3035     else if (!strcmp(type, "create"))  /* create database */
3036     {
3037         apdu = create_admin_package(p, Z_ESAdminOriginPartToKeep_create,
3038                                     0, 0);
3039     }   
3040     else if (!strcmp(type, "drop"))  /* drop database */
3041     {
3042         apdu = create_admin_package(p, Z_ESAdminOriginPartToKeep_drop,
3043                                     0, 0);
3044     }
3045     else if (!strcmp(type, "commit"))  /* commit changes */
3046     {
3047         apdu = create_admin_package(p, Z_ESAdminOriginPartToKeep_commit,
3048                                     0, 0);
3049     }
3050     else if (!strcmp(type, "update")) /* update record(s) */
3051     {
3052         apdu = create_update_package(p);
3053     }
3054     else if (!strcmp(type, "xmlupdate"))
3055     {
3056         apdu = create_xmlupdate_package(p);
3057     }
3058     if (apdu)
3059     {
3060         if (encode_APDU(p->connection, apdu, p->odr_out) == 0)
3061         {
3062             char *buf;
3063
3064             ZOOM_task task = ZOOM_connection_add_task (c, ZOOM_TASK_PACKAGE);
3065             task->u.package = p;
3066             buf = odr_getbuf(p->odr_out, &p->len_out, 0);
3067             p->buf_out = (char *) xmalloc (p->len_out);
3068             memcpy (p->buf_out, buf, p->len_out);
3069             
3070             (p->refcount)++;
3071             if (!c->async)
3072             {
3073                 while (ZOOM_event (1, &c))
3074                     ;
3075             }
3076         }
3077     }
3078 }
3079
3080 ZOOM_API(ZOOM_package)
3081     ZOOM_connection_package (ZOOM_connection c, ZOOM_options options)
3082 {
3083     ZOOM_package p = (ZOOM_package) xmalloc (sizeof(*p));
3084
3085     p->connection = c;
3086     p->odr_out = odr_createmem (ODR_ENCODE);
3087     p->options = ZOOM_options_create_with_parent2 (options, c->options);
3088     p->refcount = 1;
3089     p->buf_out = 0;
3090     p->len_out = 0;
3091     return p;
3092 }
3093
3094 ZOOM_API(void)
3095     ZOOM_package_destroy(ZOOM_package p)
3096 {
3097     if (!p)
3098         return;
3099     (p->refcount)--;
3100     if (p->refcount == 0)
3101     {
3102         odr_destroy (p->odr_out);
3103         xfree (p->buf_out);
3104         
3105         ZOOM_options_destroy (p->options);
3106         xfree (p);
3107     }
3108 }
3109
3110 ZOOM_API(const char *)
3111 ZOOM_package_option_get (ZOOM_package p, const char *key)
3112 {
3113     return ZOOM_options_get (p->options, key);
3114 }
3115
3116
3117 ZOOM_API(void)
3118 ZOOM_package_option_set (ZOOM_package p, const char *key,
3119                               const char *val)
3120 {
3121     ZOOM_options_set (p->options, key, val);
3122 }
3123
3124 static int ZOOM_connection_exec_task (ZOOM_connection c)
3125 {
3126     ZOOM_task task = c->tasks;
3127     zoom_ret ret = zoom_complete;
3128
3129     if (!task)
3130         return 0;
3131     yaz_log(log_details, "%p ZOOM_connection_exec_task type=%d run=%d",
3132              c, task->which, task->running);
3133     if (c->error != ZOOM_ERROR_NONE)
3134     {
3135         yaz_log(log_details, "%p ZOOM_connection_exec_task "
3136                 "removing tasks because of error = %d", c, c->error);
3137         ZOOM_connection_remove_tasks (c);
3138         return 0;
3139     }
3140     if (task->running)
3141     {
3142         yaz_log(log_details, "%p ZOOM_connection_exec_task "
3143                  "task already running", c);
3144         return 0;
3145     }
3146     task->running = 1;
3147     ret = zoom_complete;
3148     if (c->cs || task->which == ZOOM_TASK_CONNECT)
3149     {
3150         switch (task->which)
3151         {
3152         case ZOOM_TASK_SEARCH:
3153             if (c->proto == PROTO_HTTP)
3154                 ret = ZOOM_connection_srw_send_search(c);
3155             else
3156                 ret = ZOOM_connection_send_search(c);
3157             break;
3158         case ZOOM_TASK_RETRIEVE:
3159             if (c->proto == PROTO_HTTP)
3160                 ret = ZOOM_connection_srw_send_search(c);
3161             else
3162                 ret = send_present (c);
3163             break;
3164         case ZOOM_TASK_CONNECT:
3165             ret = do_connect(c);
3166             break;
3167         case ZOOM_TASK_SCAN:
3168             ret = send_scan(c);
3169             break;
3170         case ZOOM_TASK_PACKAGE:
3171             ret = send_package(c);
3172             break;
3173         case ZOOM_TASK_SORT:
3174             c->tasks->u.sort.resultset->r_sort_spec = 
3175                 c->tasks->u.sort.q->sort_spec;
3176             ret = send_sort(c, c->tasks->u.sort.resultset);
3177             break;
3178         }
3179     }
3180     else
3181     {
3182         yaz_log(log_details, "%p ZOOM_connection_exec_task "
3183                 "remove tasks because no connection exist", c);
3184         ZOOM_connection_remove_tasks (c);
3185     }
3186     if (ret == zoom_complete)
3187     {
3188         yaz_log(log_details, "%p ZOOM_connection_exec_task "
3189                  "task removed (complete)", c);
3190         ZOOM_connection_remove_task (c);
3191         return 0;
3192     }
3193     yaz_log(log_details, "%p ZOOM_connection_exec_task "
3194             "task pending", c);
3195     return 1;
3196 }
3197
3198 static zoom_ret send_sort_present (ZOOM_connection c)
3199 {
3200     zoom_ret r = zoom_complete;
3201
3202     if (c->tasks && c->tasks->which == ZOOM_TASK_SEARCH)
3203         r = send_sort (c, c->tasks->u.search.resultset);
3204     if (r == zoom_complete)
3205         r = send_present (c);
3206     return r;
3207 }
3208
3209 static int es_response (ZOOM_connection c,
3210                         Z_ExtendedServicesResponse *res)
3211 {
3212     if (!c->tasks || c->tasks->which != ZOOM_TASK_PACKAGE)
3213         return 0;
3214     if (res->diagnostics && res->num_diagnostics > 0)
3215         response_diag(c, res->diagnostics[0]);
3216     if (res->taskPackage &&
3217         res->taskPackage->which == Z_External_extendedService)
3218     {
3219         Z_TaskPackage *taskPackage = res->taskPackage->u.extendedService;
3220         Odr_oct *id = taskPackage->targetReference;
3221         
3222         if (id)
3223             ZOOM_options_setl (c->tasks->u.package->options,
3224                                "targetReference", (char*) id->buf, id->len);
3225     }
3226     if (res->taskPackage && 
3227         res->taskPackage->which == Z_External_octet)
3228     {
3229         Odr_oct *doc = res->taskPackage->u.octet_aligned;
3230         ZOOM_options_setl (c->tasks->u.package->options,
3231                            "xmlUpdateDoc", (char*) doc->buf, doc->len);
3232     }
3233     return 1;
3234 }
3235
3236 static void interpret_init_diag(ZOOM_connection c,
3237                                 Z_DiagnosticFormat *diag)
3238 {
3239     if (diag->num > 0)
3240     {
3241         Z_DiagnosticFormat_s *ds = diag->elements[0];
3242         if (ds->which == Z_DiagnosticFormat_s_defaultDiagRec)
3243             response_default_diag(c, ds->u.defaultDiagRec);
3244     }
3245 }
3246
3247
3248 static void interpret_otherinformation_field(ZOOM_connection c,
3249                                              Z_OtherInformation *ui)
3250 {
3251     int i;
3252     for (i = 0; i < ui->num_elements; i++)
3253     {
3254         Z_OtherInformationUnit *unit = ui->list[i];
3255         if (unit->which == Z_OtherInfo_externallyDefinedInfo &&
3256             unit->information.externallyDefinedInfo &&
3257             unit->information.externallyDefinedInfo->which ==
3258             Z_External_diag1) 
3259         {
3260             interpret_init_diag(c, unit->information.externallyDefinedInfo->u.diag1);
3261         } 
3262     }
3263 }
3264
3265 static void recv_apdu (ZOOM_connection c, Z_APDU *apdu)
3266 {
3267     Z_InitResponse *initrs;
3268     
3269     c->mask = 0;
3270     yaz_log(log_details, "%p recv_apdu apdu->which=%d", c, apdu->which);
3271     switch(apdu->which)
3272     {
3273     case Z_APDU_initResponse:
3274         yaz_log(log_api, "%p recv_apd: Received Init response", c);
3275         initrs = apdu->u.initResponse;
3276         ZOOM_connection_option_set(c, "serverImplementationId",
3277                                    initrs->implementationId ?
3278                                    initrs->implementationId : "");
3279         ZOOM_connection_option_set(c, "serverImplementationName",
3280                                    initrs->implementationName ?
3281                                    initrs->implementationName : "");
3282         ZOOM_connection_option_set(c, "serverImplementationVersion",
3283                                    initrs->implementationVersion ?
3284                                    initrs->implementationVersion : "");
3285         /* Set the three old options too, for old applications */
3286         ZOOM_connection_option_set(c, "targetImplementationId",
3287                                    initrs->implementationId ?
3288                                    initrs->implementationId : "");
3289         ZOOM_connection_option_set(c, "targetImplementationName",
3290                                    initrs->implementationName ?
3291                                    initrs->implementationName : "");
3292         ZOOM_connection_option_set(c, "targetImplementationVersion",
3293                                    initrs->implementationVersion ?
3294                                    initrs->implementationVersion : "");
3295         if (!*initrs->result)
3296         {
3297             Z_External *uif = initrs->userInformationField;
3298
3299             set_ZOOM_error(c, ZOOM_ERROR_INIT, 0); /* default error */
3300
3301             if (uif && uif->which == Z_External_userInfo1)
3302                 interpret_otherinformation_field(c, uif->u.userInfo1);
3303         }
3304         else
3305         {
3306             char *cookie =
3307                 yaz_oi_get_string_oidval (&apdu->u.initResponse->otherInfo,
3308                                           VAL_COOKIE, 1, 0);
3309             xfree (c->cookie_in);
3310             c->cookie_in = 0;
3311             if (cookie)
3312                 c->cookie_in = xstrdup(cookie);
3313             if (ODR_MASK_GET(initrs->options, Z_Options_namedResultSets) &&
3314                 ODR_MASK_GET(initrs->protocolVersion, Z_ProtocolVersion_3))
3315                 c->support_named_resultsets = 1;
3316             if (c->tasks)
3317             {
3318                 assert (c->tasks->which == ZOOM_TASK_CONNECT);
3319                 ZOOM_connection_remove_task (c);
3320             }
3321             ZOOM_connection_exec_task (c);
3322         }
3323         if (ODR_MASK_GET(initrs->options, Z_Options_negotiationModel))
3324         {
3325             NMEM tmpmem = nmem_create();
3326             Z_CharSetandLanguageNegotiation *p =
3327                 yaz_get_charneg_record(initrs->otherInfo);
3328             
3329             if (p)
3330             {
3331                 char *charset = NULL, *lang = NULL;
3332                 int sel;
3333                 
3334                 yaz_get_response_charneg(tmpmem, p, &charset, &lang, &sel);
3335                 yaz_log(log_details, "%p recv_apdu target accepted: "
3336                         "charset %s, language %s, select %d",
3337                         c,
3338                         charset ? charset : "none", lang ? lang : "none", sel);
3339                 if (charset)
3340                     ZOOM_connection_option_set (c, "negotiation-charset",
3341                                                 charset);
3342                 if (lang)
3343                     ZOOM_connection_option_set (c, "negotiation-lang",
3344                                                 lang);
3345
3346                 ZOOM_connection_option_set (
3347                     c,  "negotiation-charset-in-effect-for-records",
3348                     (sel != 0) ? "1" : "0");
3349                 nmem_destroy(tmpmem);
3350             }
3351         }       
3352         break;
3353     case Z_APDU_searchResponse:
3354         yaz_log(log_api, "%p recv_apdu Search response", c);
3355         handle_search_response (c, apdu->u.searchResponse);
3356         if (send_sort_present (c) == zoom_complete)
3357             ZOOM_connection_remove_task (c);
3358         break;
3359     case Z_APDU_presentResponse:
3360         yaz_log(log_api, "%p recv_apdu Present response", c);
3361         handle_present_response (c, apdu->u.presentResponse);
3362         if (send_present (c) == zoom_complete)
3363             ZOOM_connection_remove_task (c);
3364         break;
3365     case Z_APDU_sortResponse:
3366         yaz_log(log_api, "%p recv_apdu Sort response", c);
3367         sort_response (c, apdu->u.sortResponse);
3368         if (send_present (c) == zoom_complete)
3369             ZOOM_connection_remove_task (c);
3370         break;
3371     case Z_APDU_scanResponse:
3372         yaz_log(log_api, "%p recv_apdu Scan response", c);
3373         scan_response (c, apdu->u.scanResponse);
3374         ZOOM_connection_remove_task (c);
3375         break;
3376     case Z_APDU_extendedServicesResponse:
3377         yaz_log(log_api, "%p recv_apdu Extended Services response", c);
3378         es_response (c, apdu->u.extendedServicesResponse);
3379         ZOOM_connection_remove_task (c);
3380         break;
3381     case Z_APDU_close:
3382         yaz_log(log_api, "%p recv_apdu Close PDU", c);
3383         if (c->reconnect_ok)
3384         {
3385             do_close(c);
3386             c->tasks->running = 0;
3387             ZOOM_connection_insert_task (c, ZOOM_TASK_CONNECT);
3388         }
3389         else
3390         {
3391             set_ZOOM_error(c, ZOOM_ERROR_CONNECTION_LOST, c->host_port);
3392             do_close(c);
3393         }
3394         break;
3395     default:
3396         yaz_log(log_api, "%p Received unknown PDU", c);
3397         set_ZOOM_error(c, ZOOM_ERROR_DECODE, 0);
3398         do_close(c);
3399     }
3400 }
3401
3402 #if HAVE_XML2
3403 static void handle_srw_response(ZOOM_connection c,
3404                                 Z_SRW_searchRetrieveResponse *res)
3405 {
3406     ZOOM_resultset resultset = 0;
3407     int i;
3408     NMEM nmem;
3409     ZOOM_Event event;
3410
3411     if (!c->tasks)
3412         return;
3413
3414     if (c->tasks->which == ZOOM_TASK_SEARCH)
3415         resultset = c->tasks->u.search.resultset;
3416     else if (c->tasks->which == ZOOM_TASK_RETRIEVE)
3417         resultset = c->tasks->u.retrieve.resultset;
3418     else
3419         return ;
3420
3421     event = ZOOM_Event_create(ZOOM_EVENT_RECV_SEARCH);
3422     ZOOM_connection_put_event(c, event);
3423
3424     resultset->size = 0;
3425
3426     yaz_log(log_details, "%p handle_srw_response got SRW response OK", c);
3427     
3428     if (res->numberOfRecords)
3429         resultset->size = *res->numberOfRecords;
3430
3431     for (i = 0; i<res->num_records; i++)
3432     {
3433         int pos;
3434
3435         Z_NamePlusRecord *npr = (Z_NamePlusRecord *)
3436             odr_malloc(c->odr_in, sizeof(Z_NamePlusRecord));
3437
3438         if (res->records[i].recordPosition && 
3439             *res->records[i].recordPosition > 0)
3440             pos = *res->records[i].recordPosition - 1;
3441         else
3442             pos = resultset->start + i;
3443         
3444         npr->databaseName = 0;
3445         npr->which = Z_NamePlusRecord_databaseRecord;
3446         npr->u.databaseRecord = (Z_External *)
3447             odr_malloc(c->odr_in, sizeof(Z_External));
3448         npr->u.databaseRecord->descriptor = 0;
3449         npr->u.databaseRecord->direct_reference =
3450             yaz_oidval_to_z3950oid(c->odr_in, CLASS_RECSYN, VAL_TEXT_XML);
3451         npr->u.databaseRecord->which = Z_External_octet;
3452
3453         npr->u.databaseRecord->u.octet_aligned = (Odr_oct *)
3454             odr_malloc(c->odr_in, sizeof(Odr_oct));
3455         npr->u.databaseRecord->u.octet_aligned->buf = (unsigned char*)
3456             res->records[i].recordData_buf;
3457         npr->u.databaseRecord->u.octet_aligned->len = 
3458             npr->u.databaseRecord->u.octet_aligned->size = 
3459             res->records[i].recordData_len;
3460         record_cache_add (resultset, npr, pos);
3461     }
3462     if (res->num_diagnostics > 0)
3463     {
3464         const char *uri = res->diagnostics[0].uri;
3465         if (uri)
3466         {
3467             int code = 0;       
3468             const char *cp;
3469             if ((cp = strrchr(uri, '/')))
3470                 code = atoi(cp+1);
3471             set_dset_error(c, code, uri,
3472                            res->diagnostics[0].details, 0);
3473         }
3474     }
3475     nmem = odr_extract_mem(c->odr_in);
3476     nmem_transfer(resultset->odr->mem, nmem);
3477     nmem_destroy(nmem);
3478 }
3479 #endif
3480
3481 #if HAVE_XML2
3482 static void handle_http(ZOOM_connection c, Z_HTTP_Response *hres)
3483 {
3484     int ret = -1;
3485     const char *content_type = z_HTTP_header_lookup(hres->headers,
3486                                                     "Content-Type");
3487     const char *connection_head = z_HTTP_header_lookup(hres->headers,
3488                                                        "Connection");
3489     c->mask = 0;
3490     yaz_log(log_details, "%p handle_http", c);
3491
3492     if (content_type && !yaz_strcmp_del("text/xml", content_type, "; "))
3493     {
3494         Z_SOAP *soap_package = 0;
3495         ODR o = c->odr_in;
3496         Z_SOAP_Handler soap_handlers[2] = {
3497             {"http://www.loc.gov/zing/srw/", 0,
3498              (Z_SOAP_fun) yaz_srw_codec},
3499             {0, 0, 0}
3500         };
3501         ret = z_soap_codec(o, &soap_package,
3502                            &hres->content_buf, &hres->content_len,
3503                            soap_handlers);
3504         if (!ret && soap_package->which == Z_SOAP_generic &&
3505             soap_package->u.generic->no == 0)
3506         {
3507             Z_SRW_PDU *sr = (Z_SRW_PDU*) soap_package->u.generic->p;
3508             if (sr->which == Z_SRW_searchRetrieve_response)
3509                 handle_srw_response(c, sr->u.response);
3510             else
3511                 ret = -1;
3512         }
3513         else if (!ret && (soap_package->which == Z_SOAP_fault
3514                           || soap_package->which == Z_SOAP_error))
3515         {
3516             set_HTTP_error(c, hres->code,
3517                            soap_package->u.fault->fault_code,
3518                            soap_package->u.fault->fault_string);
3519         }
3520         else
3521             ret = -1;
3522     }
3523     if (ret)
3524     {
3525         if (hres->code != 200)
3526             set_HTTP_error(c, hres->code, 0, 0);
3527         else
3528             set_ZOOM_error(c, ZOOM_ERROR_DECODE, 0);
3529         do_close (c);
3530     }
3531     ZOOM_connection_remove_task(c);
3532     if (!strcmp(hres->version, "1.0"))
3533     {
3534         /* HTTP 1.0: only if Keep-Alive we stay alive.. */
3535         if (!connection_head || strcmp(connection_head, "Keep-Alive"))
3536             do_close(c);
3537     }
3538     else 
3539     {
3540         /* HTTP 1.1: only if no close we stay alive .. */
3541         if (connection_head && !strcmp(connection_head, "close"))
3542             do_close(c);
3543     }
3544 }
3545 #endif
3546
3547 static int do_read (ZOOM_connection c)
3548 {
3549     int r, more;
3550     ZOOM_Event event;
3551     
3552     event = ZOOM_Event_create (ZOOM_EVENT_RECV_DATA);
3553     ZOOM_connection_put_event (c, event);
3554     
3555     r = cs_get (c->cs, &c->buf_in, &c->len_in);
3556     more = cs_more(c->cs);
3557     yaz_log(log_details, "%p do_read len=%d more=%d", c, r, more);
3558     if (r == 1)
3559         return 0;
3560     if (r <= 0)
3561     {
3562         if (c->reconnect_ok)
3563         {
3564             do_close (c);
3565             c->reconnect_ok = 0;
3566             yaz_log(log_details, "%p do_read reconnect read", c);
3567             c->tasks->running = 0;
3568             ZOOM_connection_insert_task (c, ZOOM_TASK_CONNECT);
3569         }
3570         else
3571         {
3572             set_ZOOM_error(c, ZOOM_ERROR_CONNECTION_LOST, c->host_port);
3573             do_close (c);
3574         }
3575     }
3576     else
3577     {
3578         Z_GDU *gdu;
3579         ZOOM_Event event;
3580
3581         odr_reset (c->odr_in);
3582         odr_setbuf (c->odr_in, c->buf_in, r, 0);
3583         event = ZOOM_Event_create (ZOOM_EVENT_RECV_APDU);
3584         ZOOM_connection_put_event (c, event);
3585
3586         if (!z_GDU (c->odr_in, &gdu, 0, 0))
3587         {
3588             int x;
3589             int err = odr_geterrorx(c->odr_in, &x);
3590             char msg[60];
3591             const char *element = odr_getelement(c->odr_in);
3592             sprintf (msg, "ODR code %d:%d element=%-20s",
3593                      err, x, element ? element : "<unknown>");
3594             set_ZOOM_error(c, ZOOM_ERROR_DECODE, msg);
3595             do_close (c);
3596         }
3597         else if (gdu->which == Z_GDU_Z3950)
3598             recv_apdu (c, gdu->u.z3950);
3599         else if (gdu->which == Z_GDU_HTTP_Response)
3600         {
3601 #if HAVE_XML2
3602             handle_http (c, gdu->u.HTTP_Response);
3603 #else
3604             set_ZOOM_error(c, ZOOM_ERROR_DECODE, 0);
3605             do_close (c);
3606 #endif
3607         }
3608         c->reconnect_ok = 0;
3609     }
3610     return 1;
3611 }
3612
3613 static zoom_ret do_write_ex (ZOOM_connection c, char *buf_out, int len_out)
3614 {
3615     int r;
3616     ZOOM_Event event;
3617     
3618     event = ZOOM_Event_create(ZOOM_EVENT_SEND_DATA);
3619     ZOOM_connection_put_event (c, event);
3620
3621     yaz_log(log_details, "%p do_write_ex len=%d", c, len_out);
3622     if ((r = cs_put(c->cs, buf_out, len_out)) < 0)
3623     {
3624         yaz_log(log_details, "%p do_write_ex write failed", c);
3625         if (c->reconnect_ok)
3626         {
3627             do_close (c);
3628             c->reconnect_ok = 0;
3629             yaz_log(log_details, "%p do_write_ex reconnect write", c);
3630             c->tasks->running = 0;
3631             ZOOM_connection_insert_task (c, ZOOM_TASK_CONNECT);
3632             return zoom_pending;
3633         }
3634         if (c->state == STATE_CONNECTING)
3635             set_ZOOM_error(c, ZOOM_ERROR_CONNECT, c->host_port);
3636         else
3637             set_ZOOM_error(c, ZOOM_ERROR_CONNECTION_LOST, c->host_port);
3638         do_close (c);
3639         return zoom_complete;
3640     }
3641     else if (r == 1)
3642     {    
3643         c->mask = ZOOM_SELECT_EXCEPT;
3644         if (c->cs->io_pending & CS_WANT_WRITE)
3645             c->mask += ZOOM_SELECT_WRITE;
3646         if (c->cs->io_pending & CS_WANT_READ)
3647             c->mask += ZOOM_SELECT_READ;
3648         yaz_log(log_details, "%p do_write_ex write incomplete mask=%d",
3649                 c, c->mask);
3650     }
3651     else
3652     {
3653         c->mask = ZOOM_SELECT_READ|ZOOM_SELECT_EXCEPT;
3654         yaz_log(log_details, "%p do_write_ex write complete mask=%d",
3655                 c, c->mask);
3656     }
3657     return zoom_pending;
3658 }
3659
3660 static zoom_ret do_write(ZOOM_connection c)
3661 {
3662     return do_write_ex (c, c->buf_out, c->len_out);
3663 }
3664
3665
3666 ZOOM_API(const char *)
3667 ZOOM_connection_option_get (ZOOM_connection c, const char *key)
3668 {
3669     return ZOOM_options_get (c->options, key);
3670 }
3671
3672 ZOOM_API(const char *)
3673 ZOOM_connection_option_getl (ZOOM_connection c, const char *key, int *lenp)
3674 {
3675     return ZOOM_options_getl (c->options, key, lenp);
3676 }
3677
3678 ZOOM_API(void)
3679 ZOOM_connection_option_set (ZOOM_connection c, const char *key,
3680                             const char *val)
3681 {
3682     ZOOM_options_set (c->options, key, val);
3683 }
3684
3685 ZOOM_API(void)
3686 ZOOM_connection_option_setl (ZOOM_connection c, const char *key,
3687                              const char *val, int len)
3688 {
3689     ZOOM_options_setl (c->options, key, val, len);
3690 }
3691
3692 ZOOM_API(const char *)
3693 ZOOM_resultset_option_get (ZOOM_resultset r, const char *key)
3694 {
3695     return ZOOM_options_get (r->options, key);
3696 }
3697
3698 ZOOM_API(void)
3699 ZOOM_resultset_option_set (ZOOM_resultset r, const char *key,
3700                                   const char *val)
3701 {
3702     ZOOM_options_set (r->options, key, val);
3703 }
3704
3705
3706 ZOOM_API(int)
3707 ZOOM_connection_errcode (ZOOM_connection c)
3708 {
3709     return ZOOM_connection_error (c, 0, 0);
3710 }
3711
3712 ZOOM_API(const char *)
3713 ZOOM_connection_errmsg (ZOOM_connection c)
3714 {
3715     const char *msg;
3716     ZOOM_connection_error (c, &msg, 0);
3717     return msg;
3718 }
3719
3720 ZOOM_API(const char *)
3721 ZOOM_connection_addinfo (ZOOM_connection c)
3722 {
3723     const char *addinfo;
3724     ZOOM_connection_error (c, 0, &addinfo);
3725     return addinfo;
3726 }
3727
3728 ZOOM_API(const char *)
3729 ZOOM_connection_diagset (ZOOM_connection c)
3730 {
3731     const char *diagset;
3732     ZOOM_connection_error_x (c, 0, 0, &diagset);
3733     return diagset;
3734 }
3735
3736 ZOOM_API(const char *)
3737 ZOOM_diag_str (int error)
3738 {
3739     switch (error)
3740     {
3741     case ZOOM_ERROR_NONE:
3742         return "No error";
3743     case ZOOM_ERROR_CONNECT:
3744         return "Connect failed";
3745     case ZOOM_ERROR_MEMORY:
3746         return "Out of memory";
3747     case ZOOM_ERROR_ENCODE:
3748         return "Encoding failed";
3749     case ZOOM_ERROR_DECODE:
3750         return "Decoding failed";
3751     case ZOOM_ERROR_CONNECTION_LOST:
3752         return "Connection lost";
3753     case ZOOM_ERROR_INIT:
3754         return "Init rejected";
3755     case ZOOM_ERROR_INTERNAL:
3756         return "Internal failure";
3757     case ZOOM_ERROR_TIMEOUT:
3758         return "Timeout";
3759     case ZOOM_ERROR_UNSUPPORTED_PROTOCOL:
3760         return "Unsupported protocol";
3761     case ZOOM_ERROR_UNSUPPORTED_QUERY:
3762         return "Unsupported query type";
3763     case ZOOM_ERROR_INVALID_QUERY:
3764         return "Invalid query";
3765     case ZOOM_ERROR_CQL_PARSE:
3766         return "CQL parsing error";
3767     case ZOOM_ERROR_CQL_TRANSFORM:
3768         return "CQL transformation error";
3769     case ZOOM_ERROR_CCL_CONFIG:
3770         return "CCL configuration error";
3771     case ZOOM_ERROR_CCL_PARSE:
3772         return "CCL parsing error";
3773     default:
3774         return diagbib1_str (error);
3775     }
3776 }
3777
3778 ZOOM_API(int)
3779 ZOOM_connection_error_x (ZOOM_connection c, const char **cp,
3780                          const char **addinfo, const char **diagset)
3781 {
3782     int error = c->error;
3783     if (cp)
3784     {
3785         if (!c->diagset || !strcmp(c->diagset, "ZOOM"))
3786             *cp = ZOOM_diag_str(error);
3787         else if (!strcmp(c->diagset, "HTTP"))
3788             *cp = z_HTTP_errmsg(c->error);
3789         else if (!strcmp(c->diagset, "Bib-1"))
3790             *cp = ZOOM_diag_str(error);
3791         else if (!strcmp(c->diagset, "info:srw/diagnostic/1"))
3792             *cp = yaz_diag_srw_str(c->error);
3793         else
3794             *cp = "Unknown error and diagnostic set";
3795     }
3796     if (addinfo)
3797         *addinfo = c->addinfo ? c->addinfo : "";
3798     if (diagset)
3799         *diagset = c->diagset ? c->diagset : "";
3800     return c->error;
3801 }
3802
3803 ZOOM_API(int)
3804 ZOOM_connection_error (ZOOM_connection c, const char **cp,
3805                        const char **addinfo)
3806 {
3807     return ZOOM_connection_error_x(c, cp, addinfo, 0);
3808 }
3809
3810 static int ZOOM_connection_do_io(ZOOM_connection c, int mask)
3811 {
3812     ZOOM_Event event = 0;
3813     int r = cs_look(c->cs);
3814     yaz_log(log_details, "%p ZOOM_connection_do_io mask=%d cs_look=%d",
3815              c, mask, r);
3816     
3817     if (r == CS_NONE)
3818     {
3819         event = ZOOM_Event_create (ZOOM_EVENT_CONNECT);
3820         set_ZOOM_error(c, ZOOM_ERROR_CONNECT, c->host_port);
3821         do_close (c);
3822         ZOOM_connection_put_event (c, event);
3823     }
3824     else if (r == CS_CONNECT)
3825     {
3826         int ret;
3827         event = ZOOM_Event_create (ZOOM_EVENT_CONNECT);
3828
3829         ret = cs_rcvconnect (c->cs);
3830         yaz_log(log_details, "%p ZOOM_connection_do_io "
3831                 "cs_rcvconnect returned %d", c, ret);
3832         if (ret == 1)
3833         {
3834             c->mask = ZOOM_SELECT_EXCEPT;
3835             if (c->cs->io_pending & CS_WANT_WRITE)
3836                 c->mask += ZOOM_SELECT_WRITE;
3837             if (c->cs->io_pending & CS_WANT_READ)
3838                 c->mask += ZOOM_SELECT_READ;
3839             ZOOM_connection_put_event (c, event);
3840         }
3841         else if (ret == 0)
3842         {
3843             ZOOM_connection_put_event (c, event);
3844             get_cert(c);
3845             if (c->proto == PROTO_Z3950)
3846                 ZOOM_connection_send_init(c);
3847             else
3848             {
3849                 /* no init request for SRW .. */
3850                 assert (c->tasks->which == ZOOM_TASK_CONNECT);
3851                 ZOOM_connection_remove_task (c);
3852                 c->mask = 0;
3853                 ZOOM_connection_exec_task (c);
3854             }
3855             c->state = STATE_ESTABLISHED;
3856         }
3857         else
3858         {
3859             set_ZOOM_error(c, ZOOM_ERROR_CONNECT, c->host_port);
3860             do_close (c);
3861             ZOOM_connection_put_event (c, event);
3862         }
3863     }
3864     else
3865     {
3866         if (mask & ZOOM_SELECT_READ)
3867             do_read (c);
3868         if (c->cs && (mask & ZOOM_SELECT_WRITE))
3869             do_write (c);
3870     }
3871     return 1;
3872 }
3873
3874 ZOOM_API(int)
3875 ZOOM_connection_last_event(ZOOM_connection cs)
3876 {
3877     if (!cs)
3878         return ZOOM_EVENT_NONE;
3879     return cs->last_event;
3880 }
3881
3882 ZOOM_API(int)
3883 ZOOM_event (int no, ZOOM_connection *cs)
3884 {
3885     int timeout = 30;      /* default timeout in seconds */
3886     int timeout_set = 0;   /* whether it was overriden at all */
3887 #if HAVE_SYS_POLL_H
3888     struct pollfd pollfds[1024];
3889     ZOOM_connection poll_cs[1024];
3890 #else
3891     struct timeval tv;
3892     fd_set input, output, except;
3893 #endif
3894     int i, r, nfds;
3895     int max_fd = 0;
3896
3897     for (i = 0; i<no; i++)
3898     {
3899         ZOOM_connection c = cs[i];
3900         ZOOM_Event event;
3901         if (c && (event = ZOOM_connection_get_event(c)))
3902         {
3903             ZOOM_Event_destroy (event);
3904             return i+1;
3905         }
3906     }
3907     for (i = 0; i<no; i++)
3908     {
3909         ZOOM_connection c = cs[i];
3910         ZOOM_Event event;
3911         if (c && ZOOM_connection_exec_task (c))
3912         {
3913             if ((event = ZOOM_connection_get_event(c)))
3914             {
3915                 ZOOM_Event_destroy (event);
3916                 return i+1;
3917             }
3918         }
3919     }
3920 #if HAVE_SYS_POLL_H
3921
3922 #else
3923     FD_ZERO (&input);
3924     FD_ZERO (&output);
3925     FD_ZERO (&except);
3926 #endif
3927     nfds = 0;
3928     for (i = 0; i<no; i++)
3929     {
3930         ZOOM_connection c = cs[i];
3931         int fd, mask;
3932         int this_timeout;
3933         
3934         if (!c)
3935             continue;
3936         fd = z3950_connection_socket(c);
3937         mask = z3950_connection_mask(c);
3938
3939         if (fd == -1)
3940             continue;
3941         if (max_fd < fd)
3942             max_fd = fd;
3943         
3944         /* -1 is used for indefinite timeout (no timeout), so -2 here. */
3945         this_timeout = ZOOM_options_get_int (c->options, "timeout", -2);
3946         if (this_timeout != -2)
3947         {
3948             /* ensure the minimum timeout is used */
3949             if (!timeout_set)
3950                 timeout = this_timeout;
3951             else if (this_timeout != -1 && this_timeout < timeout)
3952                 timeout = this_timeout;
3953             timeout_set = 1;
3954         }               
3955 #if HAVE_SYS_POLL_H
3956         if (mask)
3957         {
3958             short poll_events = 0;
3959
3960             if (mask & ZOOM_SELECT_READ)
3961                 poll_events += POLLIN;
3962             if (mask & ZOOM_SELECT_WRITE)
3963                 poll_events += POLLOUT;
3964             if (mask & ZOOM_SELECT_EXCEPT)
3965                 poll_events += POLLERR;
3966             pollfds[nfds].fd = fd;
3967             pollfds[nfds].events = poll_events;
3968             pollfds[nfds].revents = 0;
3969             poll_cs[nfds] = c;
3970             nfds++;
3971         }
3972 #else
3973         if (mask & ZOOM_SELECT_READ)
3974         {
3975             FD_SET (fd, &input);
3976             nfds++;
3977         }
3978         if (mask & ZOOM_SELECT_WRITE)
3979         {
3980             FD_SET (fd, &output);
3981             nfds++;
3982         }
3983         if (mask & ZOOM_SELECT_EXCEPT)
3984         {
3985             FD_SET (fd, &except);
3986             nfds++;
3987         }
3988 #endif
3989     }
3990     if (!nfds)
3991         return 0;
3992
3993 #if HAVE_SYS_POLL_H
3994     r = poll (pollfds, nfds, (timeout == -1 ? -1 : timeout * 1000));
3995     for (i = 0; i<nfds; i++)
3996     {
3997         ZOOM_connection c = poll_cs[i];
3998         if (r && c->mask)
3999         {
4000             int mask = 0;
4001             if (pollfds[i].revents & POLLIN)
4002                 mask += ZOOM_SELECT_READ;
4003             if (pollfds[i].revents & POLLOUT)
4004                 mask += ZOOM_SELECT_WRITE;
4005             if (pollfds[i].revents & POLLERR)
4006                 mask += ZOOM_SELECT_EXCEPT;
4007             if (mask)
4008                 ZOOM_connection_do_io(c, mask);
4009         }
4010         else if (r == 0 && c->mask)
4011         {
4012             ZOOM_Event event = ZOOM_Event_create(ZOOM_EVENT_TIMEOUT);
4013             /* timeout and this connection was waiting */
4014             set_ZOOM_error(c, ZOOM_ERROR_TIMEOUT, 0);
4015             do_close (c);
4016             ZOOM_connection_put_event(c, event);
4017         }
4018     }
4019 #else
4020     tv.tv_sec = timeout;
4021     tv.tv_usec = 0;
4022     r = select (max_fd+1, &input, &output, &except, (timeout == -1 ? 0 : &tv));
4023     for (i = 0; i<no; i++)
4024     {
4025         ZOOM_connection c = cs[i];
4026         int fd, mask;
4027
4028         if (!c)
4029             continue;
4030         fd = z3950_connection_socket(c);
4031         mask = 0;
4032         if (r && c->mask)
4033         {
4034             /* no timeout and real socket */
4035             if (FD_ISSET(fd, &input))
4036                 mask += ZOOM_SELECT_READ;
4037             if (FD_ISSET(fd, &output))
4038                 mask += ZOOM_SELECT_WRITE;
4039             if (FD_ISSET(fd, &except))
4040                 mask += ZOOM_SELECT_EXCEPT;
4041             if (mask)
4042                 ZOOM_connection_do_io(c, mask);
4043         }
4044         if (r == 0 && c->mask)
4045         {
4046             ZOOM_Event event = ZOOM_Event_create(ZOOM_EVENT_TIMEOUT);
4047             /* timeout and this connection was waiting */
4048             set_ZOOM_error(c, ZOOM_ERROR_TIMEOUT, 0);
4049             do_close (c);
4050             ZOOM_connection_put_event(c, event);
4051         }
4052     }
4053 #endif
4054     for (i = 0; i<no; i++)
4055     {
4056         ZOOM_connection c = cs[i];
4057         ZOOM_Event event;
4058         if (c && (event = ZOOM_connection_get_event(c)))
4059         {
4060             ZOOM_Event_destroy (event);
4061             return i+1;
4062         }
4063     }
4064     return 0;
4065 }
4066
4067
4068 /*
4069  * Returns an xmalloc()d string containing RPN that corresponds to the
4070  * CQL passed in.  On error, sets the Connection object's error state
4071  * and returns a null pointer.
4072  * ### We could cache CQL parser and/or transformer in Connection.
4073  */
4074 static char *cql2pqf(ZOOM_connection c, const char *cql)
4075 {
4076     CQL_parser parser;
4077     int error;
4078     struct cql_node *node;
4079     const char *cqlfile;
4080     static cql_transform_t trans;
4081     char pqfbuf[512];
4082
4083     parser = cql_parser_create();
4084     if ((error = cql_parser_string(parser, cql)) != 0) {
4085         cql_parser_destroy(parser);
4086         set_ZOOM_error(c, ZOOM_ERROR_CQL_PARSE, cql);
4087         return 0;
4088     }
4089
4090     node = cql_parser_result(parser);
4091     /* ### Do not call cql_parser_destroy() yet: it destroys `node'! */
4092
4093     cqlfile = ZOOM_connection_option_get(c, "cqlfile");
4094     if (cqlfile == 0) {
4095         cql_parser_destroy(parser);
4096         cql_node_destroy(node);
4097         set_ZOOM_error(c, ZOOM_ERROR_CQL_TRANSFORM, "no CQL transform file");
4098         return 0;
4099     }
4100
4101     if ((trans = cql_transform_open_fname(cqlfile)) == 0) {
4102         char buf[512];        
4103         cql_parser_destroy(parser);
4104         cql_node_destroy(node);
4105         sprintf(buf, "can't open CQL transform file '%.200s': %.200s",
4106                 cqlfile, strerror(errno));
4107         set_ZOOM_error(c, ZOOM_ERROR_CQL_TRANSFORM, buf);
4108         return 0;
4109     }
4110
4111     error = cql_transform_buf(trans, node, pqfbuf, sizeof pqfbuf);
4112     cql_parser_destroy(parser);
4113     cql_node_destroy(node);
4114     if (error != 0) {
4115         char buf[512];
4116         const char *addinfo;
4117         error = cql_transform_error(trans, &addinfo);
4118         cql_transform_close(trans);
4119         sprintf(buf, "%.200s (addinfo=%.200s)", cql_strerror(error), addinfo);
4120         set_ZOOM_error(c, ZOOM_ERROR_CQL_TRANSFORM, buf);
4121         return 0;
4122     }
4123
4124     cql_transform_close(trans);
4125     return xstrdup(pqfbuf);
4126 }
4127
4128 /*
4129  * Local variables:
4130  * c-basic-offset: 4
4131  * indent-tabs-mode: nil
4132  * End:
4133  * vim: shiftwidth=4 tabstop=8 expandtab
4134  */
4135