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