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