Use __stdcall convention for ZOOM on WIN32
[yaz-moved-to-github.git] / zoom / zoom-c.c
1 /*
2  * $Id: zoom-c.c,v 1.28 2002-05-17 12:48:30 adam Exp $
3  *
4  * ZOOM layer for C, connections, result sets, queries.
5  */
6 #include <assert.h>
7 #include <yaz/xmalloc.h>
8 #include <yaz/otherinfo.h>
9 #include <yaz/log.h>
10 #include <yaz/pquery.h>
11 #include <yaz/diagbib1.h>
12
13 #include "zoom-p.h"
14
15 #if HAVE_SYS_POLL_H
16 #include <sys/poll.h>
17 #endif
18
19 static int ZOOM_connection_send_init (ZOOM_connection c);
20
21 static ZOOM_Event ZOOM_Event_create (int kind)
22 {
23     ZOOM_Event event = (ZOOM_Event) xmalloc (sizeof(*event));
24     event->kind = kind;
25     event->next = 0;
26     event->prev = 0;
27     return event;
28 }
29
30 static void ZOOM_Event_destroy (ZOOM_Event event)
31 {
32     xfree (event);
33 }
34
35 static void ZOOM_connection_put_event (ZOOM_connection c, ZOOM_Event event)
36 {
37     if (c->m_queue_back)
38     {
39         c->m_queue_back->prev = event;
40         assert (c->m_queue_front);
41     }
42     else
43     {
44         assert (!c->m_queue_front);
45         c->m_queue_front = event;
46     }
47     event->next = c->m_queue_back;
48     event->prev = 0;
49     c->m_queue_back = event;
50 }
51
52 static ZOOM_Event ZOOM_connection_get_event(ZOOM_connection c)
53 {
54     ZOOM_Event event = c->m_queue_front;
55     if (!event)
56         return 0;
57     assert (c->m_queue_back);
58     c->m_queue_front = event->prev;
59     if (c->m_queue_front)
60     {
61         assert (c->m_queue_back);
62         c->m_queue_front->next = 0;
63     }
64     else
65         c->m_queue_back = 0;
66     c->last_event = event->kind;
67     return event;
68 }
69
70 static void clear_error (ZOOM_connection c)
71 {
72
73     switch (c->error)
74     {
75     case ZOOM_ERROR_CONNECT:
76     case ZOOM_ERROR_MEMORY:
77     case ZOOM_ERROR_DECODE:
78     case ZOOM_ERROR_CONNECTION_LOST:
79     case ZOOM_ERROR_INIT:
80     case ZOOM_ERROR_INTERNAL:
81         break;
82     default:
83         c->error = ZOOM_ERROR_NONE;
84         xfree (c->addinfo);
85         c->addinfo = 0;
86     }
87 }
88
89 ZOOM_task ZOOM_connection_add_task (ZOOM_connection c, int which)
90 {
91     ZOOM_task *taskp = &c->tasks;
92     while (*taskp)
93         taskp = &(*taskp)->next;
94     *taskp = (ZOOM_task) xmalloc (sizeof(**taskp));
95     (*taskp)->running = 0;
96     (*taskp)->which = which;
97     (*taskp)->next = 0;
98     clear_error (c);
99     return *taskp;
100 }
101
102 void ZOOM_connection_remove_task (ZOOM_connection c)
103 {
104     ZOOM_task task = c->tasks;
105
106     if (task)
107     {
108         c->tasks = task->next;
109         switch (task->which)
110         {
111         case ZOOM_TASK_SEARCH:
112             ZOOM_resultset_destroy (task->u.search.resultset);
113             break;
114         case ZOOM_TASK_RETRIEVE:
115             ZOOM_resultset_destroy (task->u.retrieve.resultset);
116             break;
117         case ZOOM_TASK_CONNECT:
118             break;
119         case ZOOM_TASK_SCAN:
120             ZOOM_scanset_destroy (task->u.scan.scan);
121             break;
122         default:
123             assert (0);
124         }
125         xfree (task);
126     }
127 }
128
129 void ZOOM_connection_remove_tasks (ZOOM_connection c)
130 {
131     while (c->tasks)
132         ZOOM_connection_remove_task(c);
133 }
134
135 static ZOOM_record record_cache_lookup (ZOOM_resultset r,
136                                          int pos,
137                                          const char *elementSetName);
138
139 ZOOM_API(ZOOM_connection)
140 ZOOM_connection_create (ZOOM_options options)
141 {
142     ZOOM_connection c = (ZOOM_connection) xmalloc (sizeof(*c));
143
144     c->cs = 0;
145     c->mask = 0;
146     c->state = STATE_IDLE;
147     c->error = ZOOM_ERROR_NONE;
148     c->addinfo = 0;
149     c->buf_in = 0;
150     c->len_in = 0;
151     c->buf_out = 0;
152     c->len_out = 0;
153     c->resultsets = 0;
154
155     c->options = ZOOM_options_create_with_parent(options);
156
157     c->host_port = 0;
158     c->proxy = 0;
159
160     c->cookie_out = 0;
161     c->cookie_in = 0;
162     c->tasks = 0;
163
164     c->odr_in = odr_createmem (ODR_DECODE);
165     c->odr_out = odr_createmem (ODR_ENCODE);
166
167     c->async = 0;
168     c->support_named_resultsets = 0;
169     c->last_event = ZOOM_EVENT_NONE;
170
171     c->m_queue_front = 0;
172     c->m_queue_back = 0;
173     return c;
174 }
175
176 /* set database names. Take local databases (if set); otherwise
177    take databases given in ZURL (if set); otherwise use Default */
178 static char **set_DatabaseNames (ZOOM_connection con, ZOOM_options options,
179                                  int *num)
180 {
181     char **databaseNames;
182     const char *c;
183     int no = 2;
184     const char *cp = ZOOM_options_get (options, "databaseName");
185     
186     if (!cp || !*cp)
187     {
188         cp = strchr (con->host_port, '/');
189         if (cp)
190             cp++;
191         }
192     if (cp)
193     {
194         c = cp;
195         while ((c = strchr(c, '+')))
196         {
197             c++;
198             no++;
199         }
200     }
201     else
202         cp = "Default";
203     databaseNames = (char**)
204         odr_malloc (con->odr_out, no * sizeof(*databaseNames));
205     no = 0;
206     while (*cp)
207     {
208         c = strchr (cp, '+');
209         if (!c)
210             c = cp + strlen(cp);
211         else if (c == cp)
212         {
213             cp++;
214             continue;
215         }
216         /* cp ptr to first char of db name, c is char
217            following db name */
218         databaseNames[no] = (char*) odr_malloc (con->odr_out, 1+c-cp);
219         memcpy (databaseNames[no], cp, c-cp);
220         databaseNames[no++][c-cp] = '\0';
221         cp = c;
222         if (*cp)
223             cp++;
224     }
225     databaseNames[no] = NULL;
226     *num = no;
227     return databaseNames;
228 }
229
230 ZOOM_API(ZOOM_connection)
231 ZOOM_connection_new (const char *host, int portnum)
232 {
233     ZOOM_connection c = ZOOM_connection_create (0);
234
235     ZOOM_connection_connect (c, host, portnum);
236     return c;
237 }
238
239 ZOOM_API(void)
240 ZOOM_connection_connect(ZOOM_connection c,
241                                 const char *host, int portnum)
242 {
243     const char *val;
244     ZOOM_task task;
245
246     val = ZOOM_options_get (c->options, "proxy");
247     if (val && *val)
248         c->proxy = xstrdup (val);
249     else
250         c->proxy = 0;
251
252     if (portnum)
253     {
254         char hostn[128];
255         sprintf (hostn, "%.80s:%d", host, portnum);
256         c->host_port = xstrdup(hostn);
257     }
258     else
259         c->host_port = xstrdup(host);
260
261     ZOOM_options_set(c->options, "host", c->host_port);
262
263     c->async = ZOOM_options_get_bool (c->options, "async", 0);
264  
265     c->error = ZOOM_ERROR_NONE;
266
267     task = ZOOM_connection_add_task (c, ZOOM_TASK_CONNECT);
268
269     if (!c->async)
270     {
271         while (ZOOM_event (1, &c))
272             ;
273     }
274 }
275
276 ZOOM_API(ZOOM_query)
277 ZOOM_query_create(void)
278 {
279     ZOOM_query s = (ZOOM_query) xmalloc (sizeof(*s));
280
281     s->refcount = 1;
282     s->query = 0;
283     s->sort_spec = 0;
284     s->odr = odr_createmem (ODR_ENCODE);
285
286     return s;
287 }
288
289 ZOOM_API(void)
290 ZOOM_query_destroy(ZOOM_query s)
291 {
292     if (!s)
293         return;
294
295     (s->refcount)--;
296     yaz_log (LOG_DEBUG, "ZOOM_query_destroy count=%d", s->refcount);
297     if (s->refcount == 0)
298     {
299         odr_destroy (s->odr);
300         xfree (s);
301     }
302 }
303
304 ZOOM_API(int)
305 ZOOM_query_prefix(ZOOM_query s, const char *str)
306 {
307     s->query = (Z_Query *) odr_malloc (s->odr, sizeof(*s->query));
308     s->query->which = Z_Query_type_1;
309     s->query->u.type_1 =  p_query_rpn(s->odr, PROTO_Z3950, str);
310     if (!s->query->u.type_1)
311         return -1;
312     return 0;
313 }
314
315 ZOOM_API(int)
316 ZOOM_query_sortby(ZOOM_query s, const char *criteria)
317 {
318     s->sort_spec = yaz_sort_spec (s->odr, criteria);
319     if (!s->sort_spec)
320         return -1;
321     return 0;
322 }
323
324 static int do_write(ZOOM_connection c);
325
326 ZOOM_API(void)
327 ZOOM_connection_destroy(ZOOM_connection c)
328 {
329     ZOOM_resultset r;
330     if (!c)
331         return;
332     if (c->cs)
333         cs_close (c->cs);
334     for (r = c->resultsets; r; r = r->next)
335         r->connection = 0;
336
337     xfree (c->buf_in);
338     xfree (c->addinfo);
339     odr_destroy (c->odr_in);
340     odr_destroy (c->odr_out);
341     ZOOM_options_destroy (c->options);
342     ZOOM_connection_remove_tasks (c);
343     xfree (c->host_port);
344     xfree (c->proxy);
345     xfree (c->cookie_out);
346     xfree (c->cookie_in);
347     xfree (c);
348 }
349
350 void ZOOM_resultset_addref (ZOOM_resultset r)
351 {
352     if (r)
353         (r->refcount)++;
354 }
355 ZOOM_resultset ZOOM_resultset_create ()
356 {
357     ZOOM_resultset r = (ZOOM_resultset) xmalloc (sizeof(*r));
358
359     r->refcount = 1;
360     r->size = 0;
361     r->odr = odr_createmem (ODR_ENCODE);
362     r->start = 0;
363     r->piggyback = 1;
364     r->setname = 0;
365     r->count = 0;
366     r->record_cache = 0;
367     r->r_sort_spec = 0;
368     r->r_query = 0;
369     r->search = 0;
370     r->connection = 0;
371     r->next = 0;
372     return r;
373 }
374
375 ZOOM_API(ZOOM_resultset)
376 ZOOM_connection_search_pqf(ZOOM_connection c, const char *q)
377 {
378     ZOOM_resultset r;
379     ZOOM_query s = ZOOM_query_create();
380
381     ZOOM_query_prefix (s, q);
382
383     r = ZOOM_connection_search (c, s);
384     ZOOM_query_destroy (s);
385     return r;
386 }
387
388 ZOOM_API(ZOOM_resultset)
389 ZOOM_connection_search(ZOOM_connection c, ZOOM_query q)
390 {
391     ZOOM_resultset r = ZOOM_resultset_create ();
392     ZOOM_task task;
393     const char *cp;
394
395     r->r_sort_spec = q->sort_spec;
396     r->r_query = q->query;
397     r->search = q;
398
399     r->options = ZOOM_options_create_with_parent(c->options);
400
401     r->start = ZOOM_options_get_int(r->options, "start", 0);
402     r->count = ZOOM_options_get_int(r->options, "count", 0);
403     r->piggyback = ZOOM_options_get_bool (r->options, "piggyback", 1);
404     cp = ZOOM_options_get (r->options, "setname");
405     if (cp)
406         r->setname = xstrdup (cp);
407     
408     r->connection = c;
409
410     r->next = c->resultsets;
411     c->resultsets = r;
412
413     task = ZOOM_connection_add_task (c, ZOOM_TASK_SEARCH);
414     task->u.search.resultset = r;
415     ZOOM_resultset_addref (r);  
416
417     (q->refcount)++;
418
419     if (!c->async)
420     {
421         while (ZOOM_event (1, &c))
422             ;
423     }
424     return r;
425 }
426
427 ZOOM_API(void)
428 ZOOM_resultset_destroy(ZOOM_resultset r)
429 {
430     if (!r)
431         return;
432     (r->refcount)--;
433     yaz_log (LOG_DEBUG, "destroy r = %p count=%d", r, r->refcount);
434     if (r->refcount == 0)
435     {
436         ZOOM_record_cache rc;
437
438         for (rc = r->record_cache; rc; rc = rc->next)
439             if (rc->rec.wrbuf_marc)
440                 wrbuf_free (rc->rec.wrbuf_marc, 1);
441         if (r->connection)
442         {
443             /* remove ourselves from the resultsets in connection */
444             ZOOM_resultset *rp = &r->connection->resultsets;
445             while (1)
446             {
447                 assert (*rp);   /* we must be in this list!! */
448                 if (*rp == r)
449                 {   /* OK, we're here - take us out of it */
450                     *rp = (*rp)->next;
451                     break;
452                 }
453                 rp = &(*rp)->next;
454             }
455         }
456         ZOOM_query_destroy (r->search);
457         ZOOM_options_destroy (r->options);
458         odr_destroy (r->odr);
459         xfree (r->setname);
460         xfree (r);
461     }
462 }
463
464 ZOOM_API(size_t)
465 ZOOM_resultset_size (ZOOM_resultset r)
466 {
467     return r->size;
468 }
469
470 static void do_close (ZOOM_connection c)
471 {
472     if (c->cs)
473         cs_close(c->cs);
474     c->cs = 0;
475     c->mask = 0;
476     c->state = STATE_IDLE;
477 }
478
479 static void ZOOM_resultset_retrieve (ZOOM_resultset r,
480                                       int force_sync, int start, int count)
481 {
482     ZOOM_task task;
483     ZOOM_connection c;
484
485     if (!r)
486         return;
487     c = r->connection;
488     if (!c)
489         return;
490     task = ZOOM_connection_add_task (c, ZOOM_TASK_RETRIEVE);
491     task->u.retrieve.resultset = r;
492     task->u.retrieve.start = start;
493     task->u.retrieve.count = count;
494
495     ZOOM_resultset_addref (r);
496
497     if (!r->connection->async || force_sync)
498         while (r->connection && ZOOM_event (1, &r->connection))
499             ;
500 }
501
502 ZOOM_API(void)
503 ZOOM_resultset_records (ZOOM_resultset r, ZOOM_record *recs,
504                                 size_t start, size_t count)
505 {
506     int force_present = 0;
507
508     if (!r)
509         return ;
510     if (count && recs)
511         force_present = 1;
512     ZOOM_resultset_retrieve (r, force_present, start, count);
513     if (force_present)
514     {
515         size_t i;
516         for (i = 0; i< count; i++)
517             recs[i] = ZOOM_resultset_record_immediate (r, i+start);
518     }
519 }
520
521 static int do_connect (ZOOM_connection c)
522 {
523     void *add;
524     const char *effective_host;
525
526     if (c->proxy)
527         effective_host = c->proxy;
528     else
529         effective_host = c->host_port;
530
531     yaz_log (LOG_DEBUG, "do_connect host=%s", effective_host);
532
533     assert (!c->cs);
534     c->cs = cs_create_host (effective_host, 0, &add);
535
536     if (c->cs)
537     {
538         int ret = cs_connect (c->cs, add);
539         yaz_log (LOG_DEBUG, "cs_connect returned %d", ret);
540         if (ret == 0)
541         {
542             ZOOM_Event event = ZOOM_Event_create(ZOOM_EVENT_CONNECT);
543             ZOOM_connection_put_event(c, event);
544             ZOOM_connection_send_init(c);
545             c->state = STATE_ESTABLISHED;
546             return 1;
547         }
548         else if (ret > 0)
549         {
550             c->state = STATE_CONNECTING; 
551             c->mask = ZOOM_SELECT_EXCEPT;
552             if (c->cs->io_pending & CS_WANT_WRITE)
553                 c->mask += ZOOM_SELECT_WRITE;
554             if (c->cs->io_pending & CS_WANT_READ)
555                 c->mask += ZOOM_SELECT_READ;
556             return 1;
557         }
558     }
559     c->state = STATE_IDLE;
560     c->error = ZOOM_ERROR_CONNECT;
561     return 0;
562 }
563
564 int z3950_connection_socket(ZOOM_connection c)
565 {
566     if (c->cs)
567         return cs_fileno(c->cs);
568     return -1;
569 }
570
571 int z3950_connection_mask(ZOOM_connection c)
572 {
573     if (c->cs)
574         return c->mask;
575     return 0;
576 }
577
578 static int encode_APDU(ZOOM_connection c, Z_APDU *a, ODR out)
579 {
580     char str[120];
581
582     assert (a);
583     sprintf (str, "send_APDU t=%p type=%d", c, a->which);
584     if (c->cookie_out)
585     {
586         Z_OtherInformation **oi;
587         yaz_oi_APDU(a, &oi);
588         yaz_oi_set_string_oidval(oi, out, VAL_COOKIE, 1, c->cookie_out);
589     }
590     if (!z_APDU(out, &a, 0, 0))
591     {
592         FILE *outf = fopen("/tmp/apdu.txt", "w");
593         if (outf)
594         {
595             ODR odr_pr = odr_createmem(ODR_PRINT);
596             fprintf (outf, "a=%p\n", a);
597             odr_setprint(odr_pr, outf);
598             z_APDU(odr_pr, &a, 0, 0);
599             odr_destroy(odr_pr);
600             fclose (outf);
601         }
602         c->error = ZOOM_ERROR_ENCODE;
603         do_close (c);
604         return -1;
605     }
606     
607     return 0;
608 }
609
610 static int send_APDU (ZOOM_connection c, Z_APDU *a)
611 {
612     ZOOM_Event event;
613     assert (a);
614     if (encode_APDU(c, a, c->odr_out))
615         return -1;
616     c->buf_out = odr_getbuf(c->odr_out, &c->len_out, 0);
617     event = ZOOM_Event_create (ZOOM_EVENT_SEND_APDU);
618     ZOOM_connection_put_event (c, event);
619     odr_reset(c->odr_out);
620     do_write (c);
621     return 0;   
622 }
623
624 static int ZOOM_connection_send_init (ZOOM_connection c)
625 {
626     const char *impname;
627     Z_APDU *apdu = zget_APDU(c->odr_out, Z_APDU_initRequest);
628     Z_InitRequest *ireq = apdu->u.initRequest;
629     Z_IdAuthentication *auth = (Z_IdAuthentication *)
630         odr_malloc(c->odr_out, sizeof(*auth));
631     const char *auth_groupId = ZOOM_options_get (c->options, "group");
632     const char *auth_userId = ZOOM_options_get (c->options, "user");
633     const char *auth_password = ZOOM_options_get (c->options, "pass");
634     
635     ODR_MASK_SET(ireq->options, Z_Options_search);
636     ODR_MASK_SET(ireq->options, Z_Options_present);
637     ODR_MASK_SET(ireq->options, Z_Options_scan);
638     ODR_MASK_SET(ireq->options, Z_Options_sort);
639     ODR_MASK_SET(ireq->options, Z_Options_extendedServices);
640     ODR_MASK_SET(ireq->options, Z_Options_namedResultSets);
641     
642     ODR_MASK_SET(ireq->protocolVersion, Z_ProtocolVersion_1);
643     ODR_MASK_SET(ireq->protocolVersion, Z_ProtocolVersion_2);
644     ODR_MASK_SET(ireq->protocolVersion, Z_ProtocolVersion_3);
645     
646     impname = ZOOM_options_get (c->options, "implementationName");
647     ireq->implementationName =
648         (char *) odr_malloc (c->odr_out, 15 + (impname ? strlen(impname) : 0));
649     strcpy (ireq->implementationName, "");
650     if (impname)
651     {
652         strcat (ireq->implementationName, impname);
653         strcat (ireq->implementationName, "/");
654     }                                          
655     strcat (ireq->implementationName, "ZOOM-C/YAZ");
656     
657     *ireq->maximumRecordSize =
658         ZOOM_options_get_int (c->options, "maximumRecordSize", 1024*1024);
659     *ireq->preferredMessageSize =
660         ZOOM_options_get_int (c->options, "preferredMessageSize", 1024*1024);
661     
662     if (auth_groupId || auth_password)
663     {
664         Z_IdPass *pass = (Z_IdPass *) odr_malloc(c->odr_out, sizeof(*pass));
665         int i = 0;
666         pass->groupId = 0;
667         if (auth_groupId && *auth_groupId)
668         {
669             pass->groupId = (char *)
670                 odr_malloc(c->odr_out, strlen(auth_groupId)+1);
671             strcpy(pass->groupId, auth_groupId);
672             i++;
673         }
674         pass->userId = 0;
675         if (auth_userId && *auth_userId)
676         {
677             pass->userId = (char *)
678                 odr_malloc(c->odr_out, strlen(auth_userId)+1);
679             strcpy(pass->userId, auth_userId);
680             i++;
681         }
682         pass->password = 0;
683         if (auth_password && *auth_password)
684         {
685             pass->password = (char *)
686                 odr_malloc(c->odr_out, strlen(auth_password)+1);
687             strcpy(pass->password, auth_password);
688             i++;
689         }
690         if (i)
691         {
692             auth->which = Z_IdAuthentication_idPass;
693             auth->u.idPass = pass;
694             ireq->idAuthentication = auth;
695         }
696     }
697     else if (auth_userId)
698     {
699         auth->which = Z_IdAuthentication_open;
700         auth->u.open = (char *)
701             odr_malloc(c->odr_out, strlen(auth_userId)+1);
702         strcpy(auth->u.open, auth_userId);
703         ireq->idAuthentication = auth;
704     }
705     if (c->proxy)
706         yaz_oi_set_string_oidval(&ireq->otherInfo, c->odr_out,
707                                  VAL_PROXY, 1, c->host_port);
708     assert (apdu);
709     send_APDU (c, apdu);
710     
711     return 0;
712 }
713
714 static int ZOOM_connection_send_search (ZOOM_connection c)
715 {
716     ZOOM_resultset r;
717     int lslb, ssub, mspn;
718     const char *syntax;
719     Z_APDU *apdu = zget_APDU(c->odr_out, Z_APDU_searchRequest);
720     Z_SearchRequest *search_req = apdu->u.searchRequest;
721     const char *elementSetName;
722     const char *smallSetElementSetName;
723     const char *mediumSetElementSetName;
724     const char *schema;
725
726     assert (c->tasks);
727     assert (c->tasks->which == ZOOM_TASK_SEARCH);
728
729     r = c->tasks->u.search.resultset;
730
731     elementSetName =
732         ZOOM_options_get (r->options, "elementSetName");
733     smallSetElementSetName  =
734         ZOOM_options_get (r->options, "smallSetElementSetName");
735     mediumSetElementSetName =
736         ZOOM_options_get (r->options, "mediumSetElementSetName");
737     schema =
738         ZOOM_options_get (r->options, "schema");
739
740     if (!smallSetElementSetName)
741         smallSetElementSetName = elementSetName;
742
743     if (!mediumSetElementSetName)
744         mediumSetElementSetName = elementSetName;
745
746     assert (r);
747     assert (r->r_query);
748
749     /* prepare query for the search request */
750     search_req->query = r->r_query;
751
752     search_req->databaseNames =
753         set_DatabaseNames (c, r->options, &search_req->num_databaseNames);
754
755     /* get syntax (no need to provide unless piggyback is in effect) */
756     syntax = ZOOM_options_get (r->options, "preferredRecordSyntax");
757
758     lslb = ZOOM_options_get_int (r->options, "largeSetLowerBound", -1);
759     ssub = ZOOM_options_get_int (r->options, "smallSetUpperBound", -1);
760     mspn = ZOOM_options_get_int (r->options, "mediumSetPresentNumber", -1);
761     if (lslb != -1 && ssub != -1 && mspn != -1)
762     {
763         /* So're a Z39.50 expert? Let's hope you don't do sort */
764         *search_req->largeSetLowerBound = lslb;
765         *search_req->smallSetUpperBound = ssub;
766         *search_req->mediumSetPresentNumber = mspn;
767     }
768     else if (r->start == 0 && r->count > 0
769              && r->piggyback && !r->r_sort_spec && !schema)
770     {
771         /* Regular piggyback - do it unless we're going to do sort */
772         *search_req->largeSetLowerBound = 2000000000;
773         *search_req->smallSetUpperBound = r->count;
774         *search_req->mediumSetPresentNumber = r->count;
775         smallSetElementSetName = 0;  /* no need to provide this */
776     }
777     else
778     {
779         /* non-piggyback. Need not provide elementsets or syntaxes .. */
780         smallSetElementSetName = 0;
781         mediumSetElementSetName = 0;
782         syntax = 0;
783     }
784     if (smallSetElementSetName && *smallSetElementSetName)
785     {
786         Z_ElementSetNames *esn = (Z_ElementSetNames *)
787             odr_malloc (c->odr_out, sizeof(*esn));
788         
789         esn->which = Z_ElementSetNames_generic;
790         esn->u.generic = odr_strdup (c->odr_out, smallSetElementSetName);
791         search_req->smallSetElementSetNames = esn;
792     }
793     if (mediumSetElementSetName && *mediumSetElementSetName)
794     {
795         Z_ElementSetNames *esn = (Z_ElementSetNames *)
796             odr_malloc (c->odr_out, sizeof(*esn));
797         
798         esn->which = Z_ElementSetNames_generic;
799         esn->u.generic = odr_strdup (c->odr_out, mediumSetElementSetName);
800         search_req->mediumSetElementSetNames = esn;
801     }
802     if (syntax)
803         search_req->preferredRecordSyntax =
804             yaz_str_to_z3950oid (c->odr_out, CLASS_RECSYN, syntax);
805     
806     if (!r->setname)
807     {
808         if (c->support_named_resultsets)
809         {
810             char setname[14];
811             int ord;
812             /* find the lowest unused ordinal so that we re-use
813                result sets on the server. */
814             for (ord = 1; ; ord++)
815             {
816                 ZOOM_resultset rp;
817                 sprintf (setname, "%d", ord);
818                 for (rp = c->resultsets; rp; rp = rp->next)
819                     if (rp->setname && !strcmp (rp->setname, setname))
820                         break;
821                 if (!rp)
822                     break;
823             }
824             r->setname = xstrdup (setname);
825             yaz_log (LOG_DEBUG, "allocating %s", r->setname);
826         }
827         else
828             r->setname = xstrdup ("default");
829         ZOOM_options_set (r->options, "setname", r->setname);
830     }
831     search_req->resultSetName = odr_strdup(c->odr_out, r->setname);
832     /* send search request */
833     send_APDU (c, apdu);
834     r->r_query = 0;
835     return 1;
836 }
837
838 static void response_diag (ZOOM_connection c, Z_DiagRec *p)
839 {
840     Z_DefaultDiagFormat *r;
841     char *addinfo = 0;
842     
843     xfree (c->addinfo);
844     c->addinfo = 0;
845     if (p->which != Z_DiagRec_defaultFormat)
846     {
847         c->error = ZOOM_ERROR_DECODE;
848         return;
849     }
850     r = p->u.defaultFormat;
851     switch (r->which)
852     {
853     case Z_DefaultDiagFormat_v2Addinfo:
854         addinfo = r->u.v2Addinfo;
855         break;
856     case Z_DefaultDiagFormat_v3Addinfo:
857         addinfo = r->u.v3Addinfo;
858         break;
859     }
860     if (addinfo)
861         c->addinfo = xstrdup (addinfo);
862     c->error = *r->condition;
863 }
864
865 ZOOM_API(ZOOM_record)
866 ZOOM_record_clone (ZOOM_record srec)
867 {
868     char *buf;
869     int size;
870     ODR odr_enc;
871     ZOOM_record nrec;
872
873     odr_enc = odr_createmem(ODR_ENCODE);
874     if (!z_NamePlusRecord (odr_enc, &srec->npr, 0, 0))
875         return 0;
876     buf = odr_getbuf (odr_enc, &size, 0);
877     
878     nrec = (ZOOM_record) xmalloc (sizeof(*nrec));
879     nrec->odr = odr_createmem(ODR_DECODE);
880     nrec->wrbuf_marc = 0;
881     odr_setbuf (nrec->odr, buf, size, 0);
882     z_NamePlusRecord (nrec->odr, &nrec->npr, 0, 0);
883     
884     odr_destroy (odr_enc);
885     return nrec;
886 }
887
888 ZOOM_API(ZOOM_record)
889 ZOOM_resultset_record_immediate (ZOOM_resultset s,size_t pos)
890 {
891     return record_cache_lookup (s, pos, 0);
892 }
893
894 ZOOM_API(ZOOM_record)
895 ZOOM_resultset_record (ZOOM_resultset r, size_t pos)
896 {
897     ZOOM_resultset_retrieve (r, 1, pos, 1);
898     return ZOOM_resultset_record_immediate (r, pos);
899 }
900
901 ZOOM_API(void)
902 ZOOM_record_destroy (ZOOM_record rec)
903 {
904     if (!rec)
905         return;
906     if (rec->wrbuf_marc)
907         wrbuf_free (rec->wrbuf_marc, 1);
908     odr_destroy (rec->odr);
909     xfree (rec);
910 }
911
912 ZOOM_API(const char *)
913 ZOOM_record_get (ZOOM_record rec, const char *type, int *len)
914 {
915     Z_NamePlusRecord *npr;
916     
917     *len = 0; /* if return 0 */
918     
919     if (!rec)
920         return 0;
921     npr = rec->npr;
922     if (!npr)
923         return 0;
924     if (!strcmp (type, "database"))
925     {
926         *len = strlen(npr->databaseName)+1;
927         return npr->databaseName;
928     }
929     else if (!strcmp (type, "syntax"))
930     {
931         if (npr->which == Z_NamePlusRecord_databaseRecord)
932         {
933             Z_External *r = (Z_External *) npr->u.databaseRecord;
934             oident *ent = oid_getentbyoid(r->direct_reference);
935             if (ent)
936             {
937                 *len = strlen(ent->desc)+1;
938                 return ent->desc;
939             }
940         }
941         return "none";
942     }
943     else if (!strcmp (type, "render") && 
944              npr->which == Z_NamePlusRecord_databaseRecord)
945     {
946         Z_External *r = (Z_External *) npr->u.databaseRecord;
947         oident *ent = oid_getentbyoid(r->direct_reference);
948         
949         if (r->which == Z_External_sutrs)
950         {
951             *len = r->u.sutrs->len;
952             return (const char *) r->u.sutrs->buf;
953         }
954         else if (r->which == Z_External_octet)
955         {
956             switch (ent->value)
957             {
958             case VAL_SOIF:
959             case VAL_HTML:
960             case VAL_SUTRS:
961                 break;
962             case VAL_TEXT_XML:
963             case VAL_APPLICATION_XML:
964                 break;
965             default:
966                 if (!rec->wrbuf_marc)
967                     rec->wrbuf_marc = wrbuf_alloc();
968                 wrbuf_rewind (rec->wrbuf_marc);
969                 if (yaz_marc_decode ((const char *)
970                                      r->u.octet_aligned->buf,
971                                      rec->wrbuf_marc, 0,
972                                      r->u.octet_aligned->len,
973                                      0) > 0)
974                 {
975                     *len = wrbuf_len(rec->wrbuf_marc);
976                     return wrbuf_buf(rec->wrbuf_marc);
977                 }
978             }
979             *len = r->u.octet_aligned->len;
980             return (const char *) r->u.octet_aligned->buf;
981         }
982         else if (r->which == Z_External_grs1)
983         {
984             *len = 5;
985             return "GRS-1";
986         }
987         return 0;
988     }
989     else if (!strcmp (type, "xml") && 
990              npr->which == Z_NamePlusRecord_databaseRecord)
991     {
992         Z_External *r = (Z_External *) npr->u.databaseRecord;
993         oident *ent = oid_getentbyoid(r->direct_reference);
994         
995         if (r->which == Z_External_sutrs)
996         {
997             *len = r->u.sutrs->len;
998             return (const char *) r->u.sutrs->buf;
999         }
1000         else if (r->which == Z_External_octet)
1001         {
1002             switch (ent->value)
1003             {
1004             case VAL_SOIF:
1005             case VAL_HTML:
1006             case VAL_SUTRS:
1007                 break;
1008             case VAL_TEXT_XML:
1009             case VAL_APPLICATION_XML:
1010                 break;
1011             default:
1012                 if (!rec->wrbuf_marc)
1013                     rec->wrbuf_marc = wrbuf_alloc();
1014                 wrbuf_rewind (rec->wrbuf_marc);
1015                 if (yaz_marc_decode ((const char *)
1016                                      r->u.octet_aligned->buf,
1017                                      rec->wrbuf_marc, 0,
1018                                      r->u.octet_aligned->len,
1019                                      1) > 0)
1020                 {
1021                     *len = wrbuf_len(rec->wrbuf_marc);
1022                     return wrbuf_buf(rec->wrbuf_marc);
1023                 }
1024             }
1025             *len = r->u.octet_aligned->len;
1026             return (const char *) r->u.octet_aligned->buf;
1027         }
1028         else if (r->which == Z_External_grs1)
1029         {
1030             *len = 5;
1031             return "GRS-1";
1032         }
1033         return 0;
1034     }
1035     else if (!strcmp (type, "raw"))
1036     {
1037         if (npr->which == Z_NamePlusRecord_databaseRecord)
1038         {
1039             Z_External *r = (Z_External *) npr->u.databaseRecord;
1040             
1041             if (r->which == Z_External_sutrs)
1042             {
1043                 *len = r->u.sutrs->len;
1044                 return (const char *) r->u.sutrs->buf;
1045             }
1046             else if (r->which == Z_External_octet)
1047             {
1048                 *len = r->u.octet_aligned->len;
1049                 return (const char *) r->u.octet_aligned->buf;
1050             }
1051             else /* grs-1, explain, ... */
1052             {
1053                 *len = -1;
1054                 return (const char *) npr->u.databaseRecord;
1055             }
1056         }
1057         return 0;
1058     }
1059     return 0;
1060 }
1061
1062 static void record_cache_add (ZOOM_resultset r,
1063                               Z_NamePlusRecord *npr,
1064                               int pos,
1065                               const char *elementSetName)
1066 {
1067     ZOOM_record_cache rc;
1068
1069     for (rc = r->record_cache; rc; rc = rc->next)
1070     {
1071         if (pos == rc->pos)
1072         {
1073             if ((!elementSetName && !rc->elementSetName)
1074                 || (elementSetName && rc->elementSetName &&
1075                     !strcmp (elementSetName, rc->elementSetName)))
1076             {
1077                 /* not destroying rc->npr (it's handled by nmem )*/
1078                 rc->rec.npr = npr;
1079                 /* keeping wrbuf_marc too */
1080                 return;
1081             }
1082         }
1083     }
1084     rc = (ZOOM_record_cache) odr_malloc (r->odr, sizeof(*rc));
1085     rc->rec.npr = npr; 
1086     rc->rec.odr = 0;
1087     rc->rec.wrbuf_marc = 0;
1088     if (elementSetName)
1089         rc->elementSetName = odr_strdup (r->odr, elementSetName);
1090     else
1091         rc->elementSetName = 0;
1092     rc->pos = pos;
1093     rc->next = r->record_cache;
1094     r->record_cache = rc;
1095 }
1096
1097 static ZOOM_record record_cache_lookup (ZOOM_resultset r,
1098                                          int pos,
1099                                          const char *elementSetName)
1100 {
1101     ZOOM_record_cache rc;
1102
1103     for (rc = r->record_cache; rc; rc = rc->next)
1104     {
1105         if (pos == rc->pos)
1106         {
1107             if ((!elementSetName && !rc->elementSetName)
1108                 || (elementSetName && rc->elementSetName &&
1109                     !strcmp (elementSetName, rc->elementSetName)))
1110                 return &rc->rec;
1111         }
1112     }
1113     return 0;
1114 }
1115                                              
1116 static void handle_records (ZOOM_connection c, Z_Records *sr,
1117                             int present_phase)
1118 {
1119     ZOOM_resultset resultset;
1120
1121     if (!c->tasks)
1122         return ;
1123     switch (c->tasks->which)
1124     {
1125     case ZOOM_TASK_SEARCH:
1126         resultset = c->tasks->u.search.resultset;
1127         break;
1128     case ZOOM_TASK_RETRIEVE:
1129         resultset = c->tasks->u.retrieve.resultset;        
1130         break;
1131     default:
1132         return;
1133     }
1134     if (sr && sr->which == Z_Records_NSD)
1135     {
1136         Z_DiagRec dr, *dr_p = &dr;
1137         dr.which = Z_DiagRec_defaultFormat;
1138         dr.u.defaultFormat = sr->u.nonSurrogateDiagnostic;
1139         
1140         response_diag (c, dr_p);
1141     }
1142     else if (sr && sr->which == Z_Records_multipleNSD)
1143     {
1144         if (sr->u.multipleNonSurDiagnostics->num_diagRecs >= 1)
1145             response_diag(c, sr->u.multipleNonSurDiagnostics->diagRecs[0]);
1146         else
1147             c->error = ZOOM_ERROR_DECODE;
1148     }
1149     else 
1150     {
1151         if (resultset->count + resultset->start > resultset->size)
1152             resultset->count = resultset->size - resultset->start;
1153         if (resultset->count < 0)
1154             resultset->count = 0;
1155         if (sr && sr->which == Z_Records_DBOSD)
1156         {
1157             int i;
1158             NMEM nmem = odr_extract_mem (c->odr_in);
1159             Z_NamePlusRecordList *p =
1160                 sr->u.databaseOrSurDiagnostics;
1161             for (i = 0; i<p->num_records; i++)
1162             {
1163                 record_cache_add (resultset, p->records[i],
1164                                   i+ resultset->start, 0);
1165             }
1166             /* transfer our response to search_nmem .. we need it later */
1167             nmem_transfer (resultset->odr->mem, nmem);
1168             nmem_destroy (nmem);
1169             if (present_phase && p->num_records == 0)
1170             {
1171                 /* present response and we didn't get any records! */
1172                 c->error = ZOOM_ERROR_DECODE;
1173             }
1174         }
1175         else if (present_phase)
1176         {
1177             /* present response and we didn't get any records! */
1178             c->error = ZOOM_ERROR_DECODE;
1179         }
1180     }
1181 }
1182
1183 static void handle_present_response (ZOOM_connection c, Z_PresentResponse *pr)
1184 {
1185     handle_records (c, pr->records, 1);
1186 }
1187
1188 static void handle_search_response (ZOOM_connection c, Z_SearchResponse *sr)
1189 {
1190     ZOOM_resultset resultset;
1191
1192     yaz_log (LOG_DEBUG, "got search response");
1193
1194     if (!c->tasks || c->tasks->which != ZOOM_TASK_SEARCH)
1195         return ;
1196
1197     resultset = c->tasks->u.search.resultset;
1198
1199     resultset->size = *sr->resultCount;
1200     handle_records (c, sr->records, 0);
1201 }
1202
1203 static void sort_response (ZOOM_connection c, Z_SortResponse *res)
1204 {
1205     if (res->diagnostics && res->num_diagnostics > 0)
1206         response_diag (c, res->diagnostics[0]);
1207 }
1208
1209 static int scan_response (ZOOM_connection c, Z_ScanResponse *res)
1210 {
1211     NMEM nmem = odr_extract_mem (c->odr_in);
1212     ZOOM_scanset scan;
1213
1214     if (!c->tasks || c->tasks->which != ZOOM_TASK_SCAN)
1215         return 0;
1216     scan = c->tasks->u.scan.scan;
1217
1218     if (res->entries && res->entries->nonsurrogateDiagnostics)
1219         response_diag(c, res->entries->nonsurrogateDiagnostics[0]);
1220     scan->scan_response = res;
1221     nmem_transfer (scan->odr->mem, nmem);
1222     if (res->stepSize)
1223         ZOOM_options_set_int (scan->options, "stepSize", *res->stepSize);
1224     if (res->positionOfTerm)
1225         ZOOM_options_set_int (scan->options, "position", *res->positionOfTerm);
1226     if (res->scanStatus)
1227         ZOOM_options_set_int (scan->options, "scanStatus", *res->scanStatus);
1228     if (res->numberOfEntriesReturned)
1229         ZOOM_options_set_int (scan->options, "number",
1230                               *res->numberOfEntriesReturned);
1231     nmem_destroy (nmem);
1232     return 1;
1233 }
1234
1235 static int send_sort (ZOOM_connection c)
1236 {
1237     ZOOM_resultset  resultset;
1238
1239     if (!c->tasks || c->tasks->which != ZOOM_TASK_SEARCH)
1240         return 0;
1241
1242     resultset = c->tasks->u.search.resultset;
1243
1244     if (c->error)
1245     {
1246         resultset->r_sort_spec = 0;
1247         return 0;
1248     }
1249     if (resultset->r_sort_spec)
1250     {
1251         Z_APDU *apdu = zget_APDU(c->odr_out, Z_APDU_sortRequest);
1252         Z_SortRequest *req = apdu->u.sortRequest;
1253         
1254         req->num_inputResultSetNames = 1;
1255         req->inputResultSetNames = (Z_InternationalString **)
1256             odr_malloc (c->odr_out, sizeof(*req->inputResultSetNames));
1257         req->inputResultSetNames[0] =
1258             odr_strdup (c->odr_out, resultset->setname);
1259         req->sortedResultSetName = odr_strdup (c->odr_out, resultset->setname);
1260         req->sortSequence = resultset->r_sort_spec;
1261         resultset->r_sort_spec = 0;
1262         send_APDU (c, apdu);
1263         return 1;
1264     }
1265     return 0;
1266 }
1267
1268 static int send_present (ZOOM_connection c)
1269 {
1270     Z_APDU *apdu = zget_APDU(c->odr_out, Z_APDU_presentRequest);
1271     Z_PresentRequest *req = apdu->u.presentRequest;
1272     int i = 0;
1273     const char *syntax = 
1274         ZOOM_options_get (c->options, "preferredRecordSyntax");
1275     const char *element =
1276         ZOOM_options_get (c->options, "elementSetName");
1277     const char *schema =
1278         ZOOM_options_get (c->options, "schema");
1279     ZOOM_resultset  resultset;
1280
1281     if (!c->tasks)
1282         return 0;
1283
1284     switch (c->tasks->which)
1285     {
1286     case ZOOM_TASK_SEARCH:
1287         resultset = c->tasks->u.search.resultset;
1288         break;
1289     case ZOOM_TASK_RETRIEVE:
1290         resultset = c->tasks->u.retrieve.resultset;
1291         resultset->start = c->tasks->u.retrieve.start;
1292         resultset->count = c->tasks->u.retrieve.count;
1293
1294         if (resultset->start >= resultset->size)
1295             return 0;
1296         if (resultset->start + resultset->count > resultset->size)
1297             resultset->count = resultset->size - resultset->start;
1298         break;
1299     default:
1300         return 0;
1301     }
1302
1303     if (c->error)                  /* don't continue on error */
1304         return 0;
1305     if (resultset->start < 0)
1306         return 0;
1307     for (i = 0; i<resultset->count; i++)
1308     {
1309         ZOOM_record rec =
1310             record_cache_lookup (resultset, i + resultset->start, 0);
1311         if (!rec)
1312             break;
1313     }
1314     if (i == resultset->count)
1315         return 0;
1316
1317     resultset->start += i;
1318     resultset->count -= i;
1319     *req->resultSetStartPoint = resultset->start + 1;
1320     *req->numberOfRecordsRequested = resultset->count;
1321     assert (*req->numberOfRecordsRequested > 0);
1322
1323     if (syntax && *syntax)
1324         req->preferredRecordSyntax =
1325             yaz_str_to_z3950oid (c->odr_out, CLASS_RECSYN, syntax);
1326
1327     if (schema && *schema)
1328     {
1329         Z_RecordComposition *compo = (Z_RecordComposition *)
1330             odr_malloc (c->odr_out, sizeof(*compo));
1331
1332         req->recordComposition = compo;
1333         compo->which = Z_RecordComp_complex;
1334         compo->u.complex = (Z_CompSpec *)
1335             odr_malloc(c->odr_out, sizeof(*compo->u.complex));
1336         compo->u.complex->selectAlternativeSyntax = (bool_t *) 
1337             odr_malloc(c->odr_out, sizeof(bool_t));
1338         *compo->u.complex->selectAlternativeSyntax = 0;
1339
1340         compo->u.complex->generic = (Z_Specification *)
1341             odr_malloc(c->odr_out, sizeof(*compo->u.complex->generic));
1342
1343         compo->u.complex->generic->schema = (Odr_oid *)
1344             yaz_str_to_z3950oid (c->odr_out, CLASS_SCHEMA, schema);
1345
1346         if (!compo->u.complex->generic->schema)
1347         {
1348             /* OID wasn't a schema! Try record syntax instead. */
1349
1350             compo->u.complex->generic->schema = (Odr_oid *)
1351                 yaz_str_to_z3950oid (c->odr_out, CLASS_RECSYN, schema);
1352         }
1353         if (element && *element)
1354         {
1355             compo->u.complex->generic->elementSpec = (Z_ElementSpec *)
1356                 odr_malloc(c->odr_out, sizeof(Z_ElementSpec));
1357             compo->u.complex->generic->elementSpec->which =
1358                 Z_ElementSpec_elementSetName;
1359             compo->u.complex->generic->elementSpec->u.elementSetName =
1360                 odr_strdup (c->odr_out, element);
1361         }
1362         else
1363             compo->u.complex->generic->elementSpec = 0;
1364         compo->u.complex->num_dbSpecific = 0;
1365         compo->u.complex->dbSpecific = 0;
1366         compo->u.complex->num_recordSyntax = 0;
1367         compo->u.complex->recordSyntax = 0;
1368     }
1369     else if (element && *element)
1370     {
1371         Z_ElementSetNames *esn = (Z_ElementSetNames *)
1372             odr_malloc (c->odr_out, sizeof(*esn));
1373         Z_RecordComposition *compo = (Z_RecordComposition *)
1374             odr_malloc (c->odr_out, sizeof(*compo));
1375         
1376         esn->which = Z_ElementSetNames_generic;
1377         esn->u.generic = odr_strdup (c->odr_out, element);
1378         compo->which = Z_RecordComp_simple;
1379         compo->u.simple = esn;
1380         req->recordComposition = compo;
1381     }
1382     req->resultSetId = odr_strdup(c->odr_out, resultset->setname);
1383     send_APDU (c, apdu);
1384     return 1;
1385 }
1386
1387 ZOOM_API(ZOOM_scanset)
1388 ZOOM_connection_scan (ZOOM_connection c, const char *start)
1389 {
1390     ZOOM_scanset scan = (ZOOM_scanset) xmalloc (sizeof(*scan));
1391
1392     scan->connection = c;
1393     scan->odr = odr_createmem (ODR_DECODE);
1394     scan->options = ZOOM_options_create_with_parent (c->options);
1395     scan->refcount = 1;
1396     scan->scan_response = 0;
1397
1398     if ((scan->termListAndStartPoint =
1399          p_query_scan(scan->odr, PROTO_Z3950, &scan->attributeSet,
1400                       start)))
1401     {
1402         ZOOM_task task = ZOOM_connection_add_task (c, ZOOM_TASK_SCAN);
1403         task->u.scan.scan = scan;
1404         
1405         (scan->refcount)++;
1406         if (!c->async)
1407         {
1408             while (ZOOM_event (1, &c))
1409                 ;
1410         }
1411     }
1412     return scan;
1413 }
1414
1415 ZOOM_API(void)
1416 ZOOM_scanset_destroy (ZOOM_scanset scan)
1417 {
1418     if (!scan)
1419         return;
1420     (scan->refcount)--;
1421     if (scan->refcount == 0)
1422     {
1423         odr_destroy (scan->odr);
1424         
1425         ZOOM_options_destroy (scan->options);
1426         xfree (scan);
1427     }
1428 }
1429
1430 static int send_scan (ZOOM_connection c)
1431 {
1432     ZOOM_scanset scan;
1433     Z_APDU *apdu = zget_APDU(c->odr_out, Z_APDU_scanRequest);
1434     Z_ScanRequest *req = apdu->u.scanRequest;
1435     if (!c->tasks)
1436         return 0;
1437     assert (c->tasks->which == ZOOM_TASK_SCAN);
1438     scan = c->tasks->u.scan.scan;
1439
1440     req->termListAndStartPoint = scan->termListAndStartPoint;
1441     req->attributeSet = scan->attributeSet;
1442
1443     *req->numberOfTermsRequested =
1444         ZOOM_options_get_int(scan->options, "number", 10);
1445
1446     req->preferredPositionInResponse =
1447         odr_intdup (c->odr_out,
1448                     ZOOM_options_get_int(scan->options, "position", 1));
1449
1450     req->stepSize =
1451         odr_intdup (c->odr_out,
1452                     ZOOM_options_get_int(scan->options, "stepSize", 0));
1453     
1454     req->databaseNames = set_DatabaseNames (c, scan->options, 
1455                                             &req->num_databaseNames);
1456
1457     send_APDU (c, apdu);
1458
1459     return 1;
1460 }
1461
1462 ZOOM_API(size_t)
1463 ZOOM_scanset_size (ZOOM_scanset scan)
1464 {
1465     if (!scan || !scan->scan_response || !scan->scan_response->entries)
1466         return 0;
1467     return scan->scan_response->entries->num_entries;
1468 }
1469
1470 ZOOM_API(const char *)
1471 ZOOM_scanset_term (ZOOM_scanset scan, size_t pos,
1472                                int *occ, int *len)
1473 {
1474     const char *term = 0;
1475     size_t noent = ZOOM_scanset_size (scan);
1476     Z_ScanResponse *res = scan->scan_response;
1477     
1478     *len = 0;
1479     *occ = 0;
1480     if (pos >= noent)
1481         return 0;
1482     if (res->entries->entries[pos]->which == Z_Entry_termInfo)
1483     {
1484         Z_TermInfo *t = res->entries->entries[pos]->u.termInfo;
1485         
1486         if (t->term->which == Z_Term_general)
1487         {
1488             term = (const char *) t->term->u.general->buf;
1489             *len = t->term->u.general->len;
1490         }
1491         *occ = t->globalOccurrences ? *t->globalOccurrences : 0;
1492     }
1493     return term;
1494 }
1495
1496 ZOOM_API(const char *)
1497 ZOOM_scanset_option_get (ZOOM_scanset scan, const char *key)
1498 {
1499     return ZOOM_options_get (scan->options, key);
1500 }
1501
1502 ZOOM_API(void)
1503 ZOOM_scanset_option_set (ZOOM_scanset scan, const char *key,
1504                               const char *val)
1505 {
1506     ZOOM_options_set (scan->options, key, val);
1507 }
1508
1509 static int ZOOM_connection_exec_task (ZOOM_connection c)
1510 {
1511     ZOOM_task task = c->tasks;
1512
1513     yaz_log (LOG_DEBUG, "ZOOM_connection_exec_task");
1514     if (!task)
1515         return 0;
1516     if (c->error != ZOOM_ERROR_NONE ||
1517         (!c->cs && task->which != ZOOM_TASK_CONNECT))
1518     {
1519         ZOOM_connection_remove_tasks (c);
1520         return 0;
1521     }
1522     yaz_log (LOG_DEBUG, "ZOOM_connection_exec_task type=%d", task->which);
1523     if (task->running)
1524         return 0;
1525     task->running = 1;
1526     switch (task->which)
1527     {
1528     case ZOOM_TASK_SEARCH:
1529         /* see if search hasn't been sent yet. */
1530         if (ZOOM_connection_send_search (c))
1531             return 1;
1532         break;
1533     case ZOOM_TASK_RETRIEVE:
1534         if (send_present (c))
1535             return 1;
1536         break;
1537     case ZOOM_TASK_CONNECT:
1538         if (do_connect(c))
1539             return 1;
1540         break;
1541     case ZOOM_TASK_SCAN:
1542         if (send_scan(c))
1543             return 1;
1544     }
1545     ZOOM_connection_remove_task (c);
1546     return 0;
1547 }
1548
1549 static int send_sort_present (ZOOM_connection c)
1550 {
1551     int r = send_sort (c);
1552     if (!r)
1553         r = send_present (c);
1554     return r;
1555 }
1556
1557 static void handle_apdu (ZOOM_connection c, Z_APDU *apdu)
1558 {
1559     Z_InitResponse *initrs;
1560     
1561     yaz_log (LOG_DEBUG, "hande_apdu type=%d", apdu->which);
1562     c->mask = 0;
1563     switch(apdu->which)
1564     {
1565     case Z_APDU_initResponse:
1566         initrs = apdu->u.initResponse;
1567         if (!*initrs->result)
1568         {
1569             c->error = ZOOM_ERROR_INIT;
1570         }
1571         else
1572         {
1573             char *cookie =
1574                 yaz_oi_get_string_oidval (&apdu->u.initResponse->otherInfo,
1575                                           VAL_COOKIE, 1, 0);
1576             xfree (c->cookie_in);
1577             c->cookie_in = 0;
1578             if (cookie)
1579                 c->cookie_in = xstrdup(cookie);
1580             if (ODR_MASK_GET(initrs->options, Z_Options_namedResultSets) &&
1581                 ODR_MASK_GET(initrs->protocolVersion, Z_ProtocolVersion_3))
1582                 c->support_named_resultsets = 1;
1583             if (c->tasks)
1584             {
1585                 assert (c->tasks->which == ZOOM_TASK_CONNECT);
1586                 ZOOM_connection_remove_task (c);
1587             }
1588             ZOOM_connection_exec_task (c);
1589         }
1590         break;
1591     case Z_APDU_searchResponse:
1592         handle_search_response (c, apdu->u.searchResponse);
1593         if (!send_sort_present (c))
1594             ZOOM_connection_remove_task (c);
1595         break;
1596     case Z_APDU_presentResponse:
1597         handle_present_response (c, apdu->u.presentResponse);
1598         if (!send_present (c))
1599             ZOOM_connection_remove_task (c);
1600         break;
1601     case Z_APDU_sortResponse:
1602         sort_response (c, apdu->u.sortResponse);
1603         if (!send_present (c))
1604             ZOOM_connection_remove_task (c);
1605         break;
1606     case Z_APDU_scanResponse:
1607         scan_response (c, apdu->u.scanResponse);
1608         ZOOM_connection_remove_task (c);
1609     }
1610 }
1611
1612 static int do_read (ZOOM_connection c)
1613 {
1614     int r;
1615     Z_APDU *apdu;
1616     ZOOM_Event event;
1617     
1618     event = ZOOM_Event_create (ZOOM_EVENT_RECV_DATA);
1619     ZOOM_connection_put_event (c, event);
1620     
1621     r = cs_get (c->cs, &c->buf_in, &c->len_in);
1622     if (r == 1)
1623         return 0;
1624     if (r <= 0)
1625     {
1626         c->error= ZOOM_ERROR_CONNECTION_LOST;
1627         do_close (c);
1628     }
1629     else
1630     {
1631         ZOOM_Event event;
1632         odr_reset (c->odr_in);
1633         odr_setbuf (c->odr_in, c->buf_in, r, 0);
1634         event = ZOOM_Event_create (ZOOM_EVENT_RECV_APDU);
1635         ZOOM_connection_put_event (c, event);
1636         if (!z_APDU (c->odr_in, &apdu, 0, 0))
1637         {
1638             c->error = ZOOM_ERROR_DECODE;
1639             do_close (c);
1640         }
1641         else
1642         {
1643             handle_apdu (c, apdu);
1644         }
1645     }
1646     return 1;
1647 }
1648
1649 static int do_write_ex (ZOOM_connection c, char *buf_out, int len_out)
1650 {
1651     int r;
1652     ZOOM_Event event;
1653     
1654     event = ZOOM_Event_create(ZOOM_EVENT_SEND_DATA);
1655     ZOOM_connection_put_event (c, event);
1656
1657     if ((r=cs_put (c->cs, buf_out, len_out)) < 0)
1658     {
1659         if (c->state == STATE_CONNECTING)
1660             c->error = ZOOM_ERROR_CONNECT;
1661         else
1662             c->error = ZOOM_ERROR_CONNECTION_LOST;
1663         do_close (c);
1664         return 1;
1665     }
1666     else if (r == 1)
1667     {    
1668         c->mask = ZOOM_SELECT_EXCEPT;
1669         if (c->cs->io_pending & CS_WANT_WRITE)
1670             c->mask += ZOOM_SELECT_WRITE;
1671         if (c->cs->io_pending & CS_WANT_READ)
1672             c->mask += ZOOM_SELECT_READ;
1673         yaz_log (LOG_DEBUG, "do_write_ex 1 mask=%d", c->mask);
1674     }
1675     else
1676     {
1677         c->mask = ZOOM_SELECT_READ|ZOOM_SELECT_EXCEPT;
1678         yaz_log (LOG_DEBUG, "do_write_ex 2 mask=%d", c->mask);
1679     }
1680     return 0;
1681 }
1682
1683 static int do_write(ZOOM_connection c)
1684 {
1685     return do_write_ex (c, c->buf_out, c->len_out);
1686 }
1687
1688
1689 ZOOM_API(const char *)
1690 ZOOM_connection_option_get (ZOOM_connection c, const char *key)
1691 {
1692     return ZOOM_options_get (c->options, key);
1693 }
1694
1695 ZOOM_API(void)
1696 ZOOM_connection_option_set (ZOOM_connection c, const char *key,
1697                                   const char *val)
1698 {
1699     ZOOM_options_set (c->options, key, val);
1700 }
1701
1702 ZOOM_API(const char *)
1703 ZOOM_resultset_option_get (ZOOM_resultset r, const char *key)
1704 {
1705     return ZOOM_options_get (r->options, key);
1706 }
1707
1708 ZOOM_API(void)
1709 ZOOM_resultset_option_set (ZOOM_resultset r, const char *key,
1710                                   const char *val)
1711 {
1712     ZOOM_options_set (r->options, key, val);
1713 }
1714
1715
1716 ZOOM_API(int)
1717 ZOOM_connection_errcode (ZOOM_connection c)
1718 {
1719     return ZOOM_connection_error (c, 0, 0);
1720 }
1721
1722 ZOOM_API(const char *)
1723 ZOOM_connection_errmsg (ZOOM_connection c)
1724 {
1725     const char *msg;
1726     ZOOM_connection_error (c, &msg, 0);
1727     return msg;
1728 }
1729
1730 ZOOM_API(const char *)
1731 ZOOM_connection_addinfo (ZOOM_connection c)
1732 {
1733     const char *addinfo;
1734     ZOOM_connection_error (c, 0, &addinfo);
1735     return addinfo;
1736 }
1737
1738 ZOOM_API(int)
1739 ZOOM_connection_error (ZOOM_connection c, const char **cp,
1740                             const char **addinfo)
1741 {
1742     int error = c->error;
1743     if (cp)
1744     {
1745         switch (error)
1746         {
1747         case ZOOM_ERROR_NONE:
1748             *cp = "No error"; break;
1749         case ZOOM_ERROR_CONNECT:
1750             *cp = "Connect failed"; break;
1751         case ZOOM_ERROR_MEMORY:
1752             *cp = "Out of memory"; break;
1753         case ZOOM_ERROR_ENCODE:
1754             *cp = "Encoding failed"; break;
1755         case ZOOM_ERROR_DECODE:
1756             *cp = "Decoding failed"; break;
1757         case ZOOM_ERROR_CONNECTION_LOST:
1758             *cp = "Connection lost"; break;
1759         case ZOOM_ERROR_INIT:
1760             *cp = "Init rejected"; break;
1761         case ZOOM_ERROR_INTERNAL:
1762             *cp = "Internal failure"; break;
1763         case ZOOM_ERROR_TIMEOUT:
1764             *cp = "Timeout"; break;
1765         default:
1766             *cp = diagbib1_str (error);
1767         }
1768     }
1769     if (addinfo)
1770     {
1771         if (c->addinfo)
1772             *addinfo = c->addinfo;
1773         else
1774             *addinfo = "";
1775     }
1776     return c->error;
1777 }
1778
1779 static int ZOOM_connection_do_io(ZOOM_connection c, int mask)
1780 {
1781     ZOOM_Event event = 0;
1782     int r = cs_look(c->cs);
1783     yaz_log (LOG_DEBUG, "ZOOM_connection_do_io c=%p mask=%d cs_look=%d",
1784              c, mask, r);
1785     
1786     if (r == CS_NONE)
1787     {
1788         event = ZOOM_Event_create (ZOOM_EVENT_CONNECT);
1789         c->error = ZOOM_ERROR_CONNECT;
1790         do_close (c);
1791         ZOOM_connection_put_event (c, event);
1792     }
1793     else if (r == CS_CONNECT)
1794     {
1795         int ret;
1796         event = ZOOM_Event_create (ZOOM_EVENT_CONNECT);
1797
1798         ret = cs_rcvconnect (c->cs);
1799         yaz_log (LOG_DEBUG, "cs_rcvconnect returned %d", ret);
1800         if (ret == 1)
1801         {
1802             c->mask = ZOOM_SELECT_EXCEPT;
1803             if (c->cs->io_pending & CS_WANT_WRITE)
1804                 c->mask += ZOOM_SELECT_WRITE;
1805             if (c->cs->io_pending & CS_WANT_READ)
1806                 c->mask += ZOOM_SELECT_READ;
1807             ZOOM_connection_put_event (c, event);
1808         }
1809         else if (ret == 0)
1810         {
1811             ZOOM_connection_put_event (c, event);
1812             ZOOM_connection_send_init (c);
1813             c->state = STATE_ESTABLISHED;
1814         }
1815         else
1816         {
1817             c->error = ZOOM_ERROR_CONNECT;
1818             do_close (c);
1819             ZOOM_connection_put_event (c, event);
1820         }
1821     }
1822     else
1823     {
1824         if (mask & ZOOM_SELECT_READ)
1825             do_read (c);
1826         if (c->cs && (mask & ZOOM_SELECT_WRITE))
1827             do_write (c);
1828     }
1829     return 1;
1830 }
1831
1832 ZOOM_API(int)
1833 ZOOM_connection_last_event(ZOOM_connection cs)
1834 {
1835     if (!cs)
1836         return ZOOM_EVENT_NONE;
1837     return cs->last_event;
1838 }
1839
1840 ZOOM_API(int)
1841 ZOOM_event (int no, ZOOM_connection *cs)
1842 {
1843 #if HAVE_SYS_POLL_H
1844     struct pollfd pollfds[1024];
1845     ZOOM_connection poll_cs[1024];
1846 #else
1847     struct timeval tv;
1848     fd_set input, output, except;
1849 #endif
1850     int i, r, nfds;
1851     int max_fd = 0;
1852
1853     for (i = 0; i<no; i++)
1854     {
1855         ZOOM_connection c = cs[i];
1856         ZOOM_Event event;
1857         if (c && (event = ZOOM_connection_get_event(c)))
1858         {
1859             ZOOM_Event_destroy (event);
1860             return i+1;
1861         }
1862     }
1863     for (i = 0; i<no; i++)
1864     {
1865         ZOOM_connection c = cs[i];
1866         ZOOM_Event event;
1867         if (c && ZOOM_connection_exec_task (c))
1868         {
1869             if ((event = ZOOM_connection_get_event(c)))
1870             {
1871                 ZOOM_Event_destroy (event);
1872                 return i+1;
1873             }
1874         }
1875     }
1876 #if HAVE_SYS_POLL_H
1877
1878 #else
1879     tv.tv_sec = 15;
1880     tv.tv_usec = 0;
1881     
1882     FD_ZERO (&input);
1883     FD_ZERO (&output);
1884     FD_ZERO (&except);
1885 #endif
1886     nfds = 0;
1887     for (i = 0; i<no; i++)
1888     {
1889         ZOOM_connection c = cs[i];
1890         int fd, mask;
1891         
1892         if (!c)
1893             continue;
1894         fd = z3950_connection_socket(c);
1895         mask = z3950_connection_mask(c);
1896
1897         if (fd == -1)
1898             continue;
1899         if (max_fd < fd)
1900             max_fd = fd;
1901
1902 #if HAVE_SYS_POLL_H
1903         if (mask)
1904         {
1905             short poll_events = 0;
1906
1907             if (mask & ZOOM_SELECT_READ)
1908                 poll_events += POLLIN;
1909             if (mask & ZOOM_SELECT_WRITE)
1910                 poll_events += POLLOUT;
1911             if (mask & ZOOM_SELECT_EXCEPT)
1912                 poll_events += POLLERR;
1913             pollfds[nfds].fd = fd;
1914             pollfds[nfds].events = poll_events;
1915             pollfds[nfds].revents = 0;
1916             poll_cs[nfds] = c;
1917             nfds++;
1918         }
1919 #else
1920         if (mask & ZOOM_SELECT_READ)
1921         {
1922             FD_SET (fd, &input);
1923             nfds++;
1924         }
1925         if (mask & ZOOM_SELECT_WRITE)
1926         {
1927             FD_SET (fd, &output);
1928             nfds++;
1929         }
1930         if (mask & ZOOM_SELECT_EXCEPT)
1931         {
1932             FD_SET (fd, &except);
1933             nfds++;
1934         }
1935 #endif
1936     }
1937     if (!nfds)
1938         return 0;
1939 #if HAVE_SYS_POLL_H
1940     r = poll (pollfds, nfds, 15000);
1941     for (i = 0; i<nfds; i++)
1942     {
1943         ZOOM_connection c = poll_cs[i];
1944         if (r && c->mask)
1945         {
1946             int mask = 0;
1947             if (pollfds[i].revents & POLLIN)
1948                 mask += ZOOM_SELECT_READ;
1949             if (pollfds[i].revents & POLLOUT)
1950                 mask += ZOOM_SELECT_WRITE;
1951             if (pollfds[i].revents & POLLERR)
1952                 mask += ZOOM_SELECT_EXCEPT;
1953             if (mask)
1954                 ZOOM_connection_do_io(c, mask);
1955         }
1956         else if (r == 0 && c->mask)
1957         {
1958             ZOOM_Event event = ZOOM_Event_create(ZOOM_EVENT_TIMEOUT);
1959             /* timeout and this connection was waiting */
1960             c->error = ZOOM_ERROR_TIMEOUT;
1961             do_close (c);
1962             ZOOM_connection_put_event(c, event);
1963         }
1964     }
1965 #else
1966     yaz_log (LOG_DEBUG, "select start");
1967     r = select (max_fd+1, &input, &output, &except, &tv);
1968     yaz_log (LOG_DEBUG, "select stop, returned r=%d", r);
1969     for (i = 0; i<no; i++)
1970     {
1971         ZOOM_connection c = cs[i];
1972         int fd, mask;
1973
1974         if (!c)
1975             continue;
1976         fd = z3950_connection_socket(c);
1977         mask = 0;
1978         if (r && c->mask)
1979         {
1980             /* no timeout and real socket */
1981             if (FD_ISSET(fd, &input))
1982                 mask += ZOOM_SELECT_READ;
1983             if (FD_ISSET(fd, &output))
1984                 mask += ZOOM_SELECT_WRITE;
1985             if (FD_ISSET(fd, &except))
1986                 mask += ZOOM_SELECT_EXCEPT;
1987             if (mask)
1988                 ZOOM_connection_do_io(c, mask);
1989         }
1990         if (r == 0 && c->mask)
1991         {
1992             ZOOM_Event event = ZOOM_Event_create(ZOOM_EVENT_TIMEOUT);
1993             /* timeout and this connection was waiting */
1994             c->error = ZOOM_ERROR_TIMEOUT;
1995             do_close (c);
1996             yaz_log (LOG_DEBUG, "timeout");
1997             ZOOM_connection_put_event(c, event);
1998         }
1999     }
2000 #endif
2001     for (i = 0; i<no; i++)
2002     {
2003         ZOOM_connection c = cs[i];
2004         ZOOM_Event event;
2005         if (c && (event = ZOOM_connection_get_event(c)))
2006         {
2007             ZOOM_Event_destroy (event);
2008             return i+1;
2009         }
2010     }
2011     return 0;
2012 }