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