bb4e62ec03c03c4c13be1b50b9fd193878ee7400
[yaz-moved-to-github.git] / zoom / zoom-c.c
1 /*
2  * $Id: zoom-c.c,v 1.27 2002-05-14 13:42:26 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
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     
899     *len = 0; /* if return 0 */
900     
901     if (!rec)
902         return 0;
903     npr = rec->npr;
904     if (!npr)
905         return 0;
906     if (!strcmp (type, "database"))
907     {
908         *len = strlen(npr->databaseName)+1;
909         return npr->databaseName;
910     }
911     else if (!strcmp (type, "syntax"))
912     {
913         if (npr->which == Z_NamePlusRecord_databaseRecord)
914         {
915             Z_External *r = (Z_External *) npr->u.databaseRecord;
916             oident *ent = oid_getentbyoid(r->direct_reference);
917             if (ent)
918             {
919                 *len = strlen(ent->desc)+1;
920                 return ent->desc;
921             }
922         }
923         return "none";
924     }
925     else if (!strcmp (type, "render") && 
926              npr->which == Z_NamePlusRecord_databaseRecord)
927     {
928         Z_External *r = (Z_External *) npr->u.databaseRecord;
929         oident *ent = oid_getentbyoid(r->direct_reference);
930         
931         if (r->which == Z_External_sutrs)
932         {
933             *len = r->u.sutrs->len;
934             return (const char *) r->u.sutrs->buf;
935         }
936         else if (r->which == Z_External_octet)
937         {
938             switch (ent->value)
939             {
940             case VAL_SOIF:
941             case VAL_HTML:
942             case VAL_SUTRS:
943                 break;
944             case VAL_TEXT_XML:
945             case VAL_APPLICATION_XML:
946                 break;
947             default:
948                 if (!rec->wrbuf_marc)
949                     rec->wrbuf_marc = wrbuf_alloc();
950                 wrbuf_rewind (rec->wrbuf_marc);
951                 if (yaz_marc_decode ((const char *)
952                                      r->u.octet_aligned->buf,
953                                      rec->wrbuf_marc, 0,
954                                      r->u.octet_aligned->len,
955                                      0) > 0)
956                 {
957                     *len = wrbuf_len(rec->wrbuf_marc);
958                     return wrbuf_buf(rec->wrbuf_marc);
959                 }
960             }
961             *len = r->u.octet_aligned->len;
962             return (const char *) r->u.octet_aligned->buf;
963         }
964         else if (r->which == Z_External_grs1)
965         {
966             *len = 5;
967             return "GRS-1";
968         }
969         return 0;
970     }
971     else if (!strcmp (type, "xml") && 
972              npr->which == Z_NamePlusRecord_databaseRecord)
973     {
974         Z_External *r = (Z_External *) npr->u.databaseRecord;
975         oident *ent = oid_getentbyoid(r->direct_reference);
976         
977         if (r->which == Z_External_sutrs)
978         {
979             *len = r->u.sutrs->len;
980             return (const char *) r->u.sutrs->buf;
981         }
982         else if (r->which == Z_External_octet)
983         {
984             switch (ent->value)
985             {
986             case VAL_SOIF:
987             case VAL_HTML:
988             case VAL_SUTRS:
989                 break;
990             case VAL_TEXT_XML:
991             case VAL_APPLICATION_XML:
992                 break;
993             default:
994                 if (!rec->wrbuf_marc)
995                     rec->wrbuf_marc = wrbuf_alloc();
996                 wrbuf_rewind (rec->wrbuf_marc);
997                 if (yaz_marc_decode ((const char *)
998                                      r->u.octet_aligned->buf,
999                                      rec->wrbuf_marc, 0,
1000                                      r->u.octet_aligned->len,
1001                                      1) > 0)
1002                 {
1003                     *len = wrbuf_len(rec->wrbuf_marc);
1004                     return wrbuf_buf(rec->wrbuf_marc);
1005                 }
1006             }
1007             *len = r->u.octet_aligned->len;
1008             return (const char *) r->u.octet_aligned->buf;
1009         }
1010         else if (r->which == Z_External_grs1)
1011         {
1012             *len = 5;
1013             return "GRS-1";
1014         }
1015         return 0;
1016     }
1017     else if (!strcmp (type, "raw"))
1018     {
1019         if (npr->which == Z_NamePlusRecord_databaseRecord)
1020         {
1021             Z_External *r = (Z_External *) npr->u.databaseRecord;
1022             
1023             if (r->which == Z_External_sutrs)
1024             {
1025                 *len = r->u.sutrs->len;
1026                 return (const char *) r->u.sutrs->buf;
1027             }
1028             else if (r->which == Z_External_octet)
1029             {
1030                 *len = r->u.octet_aligned->len;
1031                 return (const char *) r->u.octet_aligned->buf;
1032             }
1033             else /* grs-1, explain, ... */
1034             {
1035                 *len = -1;
1036                 return (const char *) npr->u.databaseRecord;
1037             }
1038         }
1039         return 0;
1040     }
1041     return 0;
1042 }
1043
1044 static void record_cache_add (ZOOM_resultset r,
1045                               Z_NamePlusRecord *npr,
1046                               int pos,
1047                               const char *elementSetName)
1048 {
1049     ZOOM_record_cache rc;
1050
1051     for (rc = r->record_cache; rc; rc = rc->next)
1052     {
1053         if (pos == rc->pos)
1054         {
1055             if ((!elementSetName && !rc->elementSetName)
1056                 || (elementSetName && rc->elementSetName &&
1057                     !strcmp (elementSetName, rc->elementSetName)))
1058             {
1059                 /* not destroying rc->npr (it's handled by nmem )*/
1060                 rc->rec.npr = npr;
1061                 /* keeping wrbuf_marc too */
1062                 return;
1063             }
1064         }
1065     }
1066     rc = (ZOOM_record_cache) odr_malloc (r->odr, sizeof(*rc));
1067     rc->rec.npr = npr; 
1068     rc->rec.odr = 0;
1069     rc->rec.wrbuf_marc = 0;
1070     if (elementSetName)
1071         rc->elementSetName = odr_strdup (r->odr, elementSetName);
1072     else
1073         rc->elementSetName = 0;
1074     rc->pos = pos;
1075     rc->next = r->record_cache;
1076     r->record_cache = rc;
1077 }
1078
1079 static ZOOM_record record_cache_lookup (ZOOM_resultset r,
1080                                          int pos,
1081                                          const char *elementSetName)
1082 {
1083     ZOOM_record_cache rc;
1084
1085     for (rc = r->record_cache; rc; rc = rc->next)
1086     {
1087         if (pos == rc->pos)
1088         {
1089             if ((!elementSetName && !rc->elementSetName)
1090                 || (elementSetName && rc->elementSetName &&
1091                     !strcmp (elementSetName, rc->elementSetName)))
1092                 return &rc->rec;
1093         }
1094     }
1095     return 0;
1096 }
1097                                              
1098 static void handle_records (ZOOM_connection c, Z_Records *sr,
1099                             int present_phase)
1100 {
1101     ZOOM_resultset resultset;
1102
1103     if (!c->tasks)
1104         return ;
1105     switch (c->tasks->which)
1106     {
1107     case ZOOM_TASK_SEARCH:
1108         resultset = c->tasks->u.search.resultset;
1109         break;
1110     case ZOOM_TASK_RETRIEVE:
1111         resultset = c->tasks->u.retrieve.resultset;        
1112         break;
1113     default:
1114         return;
1115     }
1116     if (sr && sr->which == Z_Records_NSD)
1117     {
1118         Z_DiagRec dr, *dr_p = &dr;
1119         dr.which = Z_DiagRec_defaultFormat;
1120         dr.u.defaultFormat = sr->u.nonSurrogateDiagnostic;
1121         
1122         response_diag (c, dr_p);
1123     }
1124     else if (sr && sr->which == Z_Records_multipleNSD)
1125     {
1126         if (sr->u.multipleNonSurDiagnostics->num_diagRecs >= 1)
1127             response_diag(c, sr->u.multipleNonSurDiagnostics->diagRecs[0]);
1128         else
1129             c->error = ZOOM_ERROR_DECODE;
1130     }
1131     else 
1132     {
1133         if (resultset->count + resultset->start > resultset->size)
1134             resultset->count = resultset->size - resultset->start;
1135         if (resultset->count < 0)
1136             resultset->count = 0;
1137         if (sr && sr->which == Z_Records_DBOSD)
1138         {
1139             int i;
1140             NMEM nmem = odr_extract_mem (c->odr_in);
1141             Z_NamePlusRecordList *p =
1142                 sr->u.databaseOrSurDiagnostics;
1143             for (i = 0; i<p->num_records; i++)
1144             {
1145                 record_cache_add (resultset, p->records[i],
1146                                   i+ resultset->start, 0);
1147             }
1148             /* transfer our response to search_nmem .. we need it later */
1149             nmem_transfer (resultset->odr->mem, nmem);
1150             nmem_destroy (nmem);
1151             if (present_phase && p->num_records == 0)
1152             {
1153                 /* present response and we didn't get any records! */
1154                 c->error = ZOOM_ERROR_DECODE;
1155             }
1156         }
1157         else if (present_phase)
1158         {
1159             /* present response and we didn't get any records! */
1160             c->error = ZOOM_ERROR_DECODE;
1161         }
1162     }
1163 }
1164
1165 static void handle_present_response (ZOOM_connection c, Z_PresentResponse *pr)
1166 {
1167     handle_records (c, pr->records, 1);
1168 }
1169
1170 static void handle_search_response (ZOOM_connection c, Z_SearchResponse *sr)
1171 {
1172     ZOOM_resultset resultset;
1173
1174     yaz_log (LOG_DEBUG, "got search response");
1175
1176     if (!c->tasks || c->tasks->which != ZOOM_TASK_SEARCH)
1177         return ;
1178
1179     resultset = c->tasks->u.search.resultset;
1180
1181     resultset->size = *sr->resultCount;
1182     handle_records (c, sr->records, 0);
1183 }
1184
1185 static void sort_response (ZOOM_connection c, Z_SortResponse *res)
1186 {
1187     if (res->diagnostics && res->num_diagnostics > 0)
1188         response_diag (c, res->diagnostics[0]);
1189 }
1190
1191 static int scan_response (ZOOM_connection c, Z_ScanResponse *res)
1192 {
1193     NMEM nmem = odr_extract_mem (c->odr_in);
1194     ZOOM_scanset scan;
1195
1196     if (!c->tasks || c->tasks->which != ZOOM_TASK_SCAN)
1197         return 0;
1198     scan = c->tasks->u.scan.scan;
1199
1200     if (res->entries && res->entries->nonsurrogateDiagnostics)
1201         response_diag(c, res->entries->nonsurrogateDiagnostics[0]);
1202     scan->scan_response = res;
1203     nmem_transfer (scan->odr->mem, nmem);
1204     if (res->stepSize)
1205         ZOOM_options_set_int (scan->options, "stepSize", *res->stepSize);
1206     if (res->positionOfTerm)
1207         ZOOM_options_set_int (scan->options, "position", *res->positionOfTerm);
1208     if (res->scanStatus)
1209         ZOOM_options_set_int (scan->options, "scanStatus", *res->scanStatus);
1210     if (res->numberOfEntriesReturned)
1211         ZOOM_options_set_int (scan->options, "number",
1212                               *res->numberOfEntriesReturned);
1213     nmem_destroy (nmem);
1214     return 1;
1215 }
1216
1217 static int send_sort (ZOOM_connection c)
1218 {
1219     ZOOM_resultset  resultset;
1220
1221     if (!c->tasks || c->tasks->which != ZOOM_TASK_SEARCH)
1222         return 0;
1223
1224     resultset = c->tasks->u.search.resultset;
1225
1226     if (c->error)
1227     {
1228         resultset->r_sort_spec = 0;
1229         return 0;
1230     }
1231     if (resultset->r_sort_spec)
1232     {
1233         Z_APDU *apdu = zget_APDU(c->odr_out, Z_APDU_sortRequest);
1234         Z_SortRequest *req = apdu->u.sortRequest;
1235         
1236         req->num_inputResultSetNames = 1;
1237         req->inputResultSetNames = (Z_InternationalString **)
1238             odr_malloc (c->odr_out, sizeof(*req->inputResultSetNames));
1239         req->inputResultSetNames[0] =
1240             odr_strdup (c->odr_out, resultset->setname);
1241         req->sortedResultSetName = odr_strdup (c->odr_out, resultset->setname);
1242         req->sortSequence = resultset->r_sort_spec;
1243         resultset->r_sort_spec = 0;
1244         send_APDU (c, apdu);
1245         return 1;
1246     }
1247     return 0;
1248 }
1249
1250 static int send_present (ZOOM_connection c)
1251 {
1252     Z_APDU *apdu = zget_APDU(c->odr_out, Z_APDU_presentRequest);
1253     Z_PresentRequest *req = apdu->u.presentRequest;
1254     int i = 0;
1255     const char *syntax = 
1256         ZOOM_options_get (c->options, "preferredRecordSyntax");
1257     const char *element =
1258         ZOOM_options_get (c->options, "elementSetName");
1259     const char *schema =
1260         ZOOM_options_get (c->options, "schema");
1261     ZOOM_resultset  resultset;
1262
1263     if (!c->tasks)
1264         return 0;
1265
1266     switch (c->tasks->which)
1267     {
1268     case ZOOM_TASK_SEARCH:
1269         resultset = c->tasks->u.search.resultset;
1270         break;
1271     case ZOOM_TASK_RETRIEVE:
1272         resultset = c->tasks->u.retrieve.resultset;
1273         resultset->start = c->tasks->u.retrieve.start;
1274         resultset->count = c->tasks->u.retrieve.count;
1275
1276         if (resultset->start >= resultset->size)
1277             return 0;
1278         if (resultset->start + resultset->count > resultset->size)
1279             resultset->count = resultset->size - resultset->start;
1280         break;
1281     default:
1282         return 0;
1283     }
1284
1285     if (c->error)                  /* don't continue on error */
1286         return 0;
1287     if (resultset->start < 0)
1288         return 0;
1289     for (i = 0; i<resultset->count; i++)
1290     {
1291         ZOOM_record rec =
1292             record_cache_lookup (resultset, i + resultset->start, 0);
1293         if (!rec)
1294             break;
1295     }
1296     if (i == resultset->count)
1297         return 0;
1298
1299     resultset->start += i;
1300     resultset->count -= i;
1301     *req->resultSetStartPoint = resultset->start + 1;
1302     *req->numberOfRecordsRequested = resultset->count;
1303     assert (*req->numberOfRecordsRequested > 0);
1304
1305     if (syntax && *syntax)
1306         req->preferredRecordSyntax =
1307             yaz_str_to_z3950oid (c->odr_out, CLASS_RECSYN, syntax);
1308
1309     if (schema && *schema)
1310     {
1311         Z_RecordComposition *compo = (Z_RecordComposition *)
1312             odr_malloc (c->odr_out, sizeof(*compo));
1313
1314         req->recordComposition = compo;
1315         compo->which = Z_RecordComp_complex;
1316         compo->u.complex = (Z_CompSpec *)
1317             odr_malloc(c->odr_out, sizeof(*compo->u.complex));
1318         compo->u.complex->selectAlternativeSyntax = (bool_t *) 
1319             odr_malloc(c->odr_out, sizeof(bool_t));
1320         *compo->u.complex->selectAlternativeSyntax = 0;
1321
1322         compo->u.complex->generic = (Z_Specification *)
1323             odr_malloc(c->odr_out, sizeof(*compo->u.complex->generic));
1324
1325         compo->u.complex->generic->schema = (Odr_oid *)
1326             yaz_str_to_z3950oid (c->odr_out, CLASS_SCHEMA, schema);
1327
1328         if (!compo->u.complex->generic->schema)
1329         {
1330             /* OID wasn't a schema! Try record syntax instead. */
1331
1332             compo->u.complex->generic->schema = (Odr_oid *)
1333                 yaz_str_to_z3950oid (c->odr_out, CLASS_RECSYN, schema);
1334         }
1335         if (element && *element)
1336         {
1337             compo->u.complex->generic->elementSpec = (Z_ElementSpec *)
1338                 odr_malloc(c->odr_out, sizeof(Z_ElementSpec));
1339             compo->u.complex->generic->elementSpec->which =
1340                 Z_ElementSpec_elementSetName;
1341             compo->u.complex->generic->elementSpec->u.elementSetName =
1342                 odr_strdup (c->odr_out, element);
1343         }
1344         else
1345             compo->u.complex->generic->elementSpec = 0;
1346         compo->u.complex->num_dbSpecific = 0;
1347         compo->u.complex->dbSpecific = 0;
1348         compo->u.complex->num_recordSyntax = 0;
1349         compo->u.complex->recordSyntax = 0;
1350     }
1351     else if (element && *element)
1352     {
1353         Z_ElementSetNames *esn = (Z_ElementSetNames *)
1354             odr_malloc (c->odr_out, sizeof(*esn));
1355         Z_RecordComposition *compo = (Z_RecordComposition *)
1356             odr_malloc (c->odr_out, sizeof(*compo));
1357         
1358         esn->which = Z_ElementSetNames_generic;
1359         esn->u.generic = odr_strdup (c->odr_out, element);
1360         compo->which = Z_RecordComp_simple;
1361         compo->u.simple = esn;
1362         req->recordComposition = compo;
1363     }
1364     req->resultSetId = odr_strdup(c->odr_out, resultset->setname);
1365     send_APDU (c, apdu);
1366     return 1;
1367 }
1368
1369 ZOOM_scanset ZOOM_connection_scan (ZOOM_connection c, const char *start)
1370 {
1371     ZOOM_scanset scan = (ZOOM_scanset) xmalloc (sizeof(*scan));
1372
1373     scan->connection = c;
1374     scan->odr = odr_createmem (ODR_DECODE);
1375     scan->options = ZOOM_options_create_with_parent (c->options);
1376     scan->refcount = 1;
1377     scan->scan_response = 0;
1378
1379     if ((scan->termListAndStartPoint =
1380          p_query_scan(scan->odr, PROTO_Z3950, &scan->attributeSet,
1381                       start)))
1382     {
1383         ZOOM_task task = ZOOM_connection_add_task (c, ZOOM_TASK_SCAN);
1384         task->u.scan.scan = scan;
1385         
1386         (scan->refcount)++;
1387         if (!c->async)
1388         {
1389             while (ZOOM_event (1, &c))
1390                 ;
1391         }
1392     }
1393     return scan;
1394 }
1395
1396 void ZOOM_scanset_destroy (ZOOM_scanset scan)
1397 {
1398     if (!scan)
1399         return;
1400     (scan->refcount)--;
1401     if (scan->refcount == 0)
1402     {
1403         odr_destroy (scan->odr);
1404         
1405         ZOOM_options_destroy (scan->options);
1406         xfree (scan);
1407     }
1408 }
1409
1410 int send_scan (ZOOM_connection c)
1411 {
1412     ZOOM_scanset scan;
1413     Z_APDU *apdu = zget_APDU(c->odr_out, Z_APDU_scanRequest);
1414     Z_ScanRequest *req = apdu->u.scanRequest;
1415     if (!c->tasks)
1416         return 0;
1417     assert (c->tasks->which == ZOOM_TASK_SCAN);
1418     scan = c->tasks->u.scan.scan;
1419
1420     req->termListAndStartPoint = scan->termListAndStartPoint;
1421     req->attributeSet = scan->attributeSet;
1422
1423     *req->numberOfTermsRequested =
1424         ZOOM_options_get_int(scan->options, "number", 10);
1425
1426     req->preferredPositionInResponse =
1427         odr_intdup (c->odr_out,
1428                     ZOOM_options_get_int(scan->options, "position", 1));
1429
1430     req->stepSize =
1431         odr_intdup (c->odr_out,
1432                     ZOOM_options_get_int(scan->options, "stepSize", 0));
1433     
1434     req->databaseNames = set_DatabaseNames (c, scan->options, 
1435                                             &req->num_databaseNames);
1436
1437     send_APDU (c, apdu);
1438
1439     return 1;
1440 }
1441
1442 size_t ZOOM_scanset_size (ZOOM_scanset scan)
1443 {
1444     if (!scan || !scan->scan_response || !scan->scan_response->entries)
1445         return 0;
1446     return scan->scan_response->entries->num_entries;
1447 }
1448
1449 const char *ZOOM_scanset_term (ZOOM_scanset scan, size_t pos,
1450                                int *occ, int *len)
1451 {
1452     const char *term = 0;
1453     size_t noent = ZOOM_scanset_size (scan);
1454     Z_ScanResponse *res = scan->scan_response;
1455     
1456     *len = 0;
1457     *occ = 0;
1458     if (pos >= noent)
1459         return 0;
1460     if (res->entries->entries[pos]->which == Z_Entry_termInfo)
1461     {
1462         Z_TermInfo *t = res->entries->entries[pos]->u.termInfo;
1463         
1464         if (t->term->which == Z_Term_general)
1465         {
1466             term = (const char *) t->term->u.general->buf;
1467             *len = t->term->u.general->len;
1468         }
1469         *occ = t->globalOccurrences ? *t->globalOccurrences : 0;
1470     }
1471     return term;
1472 }
1473
1474 const char *ZOOM_scanset_option_get (ZOOM_scanset scan, const char *key)
1475 {
1476     return ZOOM_options_get (scan->options, key);
1477 }
1478
1479 void ZOOM_scanset_option_set (ZOOM_scanset scan, const char *key,
1480                               const char *val)
1481 {
1482     ZOOM_options_set (scan->options, key, val);
1483 }
1484
1485 static int ZOOM_connection_exec_task (ZOOM_connection c)
1486 {
1487     ZOOM_task task = c->tasks;
1488
1489     yaz_log (LOG_DEBUG, "ZOOM_connection_exec_task");
1490     if (!task)
1491         return 0;
1492     if (c->error != ZOOM_ERROR_NONE ||
1493         (!c->cs && task->which != ZOOM_TASK_CONNECT))
1494     {
1495         ZOOM_connection_remove_tasks (c);
1496         return 0;
1497     }
1498     yaz_log (LOG_DEBUG, "ZOOM_connection_exec_task type=%d", task->which);
1499     if (task->running)
1500         return 0;
1501     task->running = 1;
1502     switch (task->which)
1503     {
1504     case ZOOM_TASK_SEARCH:
1505         /* see if search hasn't been sent yet. */
1506         if (ZOOM_connection_send_search (c))
1507             return 1;
1508         break;
1509     case ZOOM_TASK_RETRIEVE:
1510         if (send_present (c))
1511             return 1;
1512         break;
1513     case ZOOM_TASK_CONNECT:
1514         if (do_connect(c))
1515             return 1;
1516         break;
1517     case ZOOM_TASK_SCAN:
1518         if (send_scan(c))
1519             return 1;
1520     }
1521     ZOOM_connection_remove_task (c);
1522     return 0;
1523 }
1524
1525 static int send_sort_present (ZOOM_connection c)
1526 {
1527     int r = send_sort (c);
1528     if (!r)
1529         r = send_present (c);
1530     return r;
1531 }
1532
1533 static void handle_apdu (ZOOM_connection c, Z_APDU *apdu)
1534 {
1535     Z_InitResponse *initrs;
1536     
1537     yaz_log (LOG_DEBUG, "hande_apdu type=%d", apdu->which);
1538     c->mask = 0;
1539     switch(apdu->which)
1540     {
1541     case Z_APDU_initResponse:
1542         initrs = apdu->u.initResponse;
1543         if (!*initrs->result)
1544         {
1545             c->error = ZOOM_ERROR_INIT;
1546         }
1547         else
1548         {
1549             char *cookie =
1550                 yaz_oi_get_string_oidval (&apdu->u.initResponse->otherInfo,
1551                                           VAL_COOKIE, 1, 0);
1552             xfree (c->cookie_in);
1553             c->cookie_in = 0;
1554             if (cookie)
1555                 c->cookie_in = xstrdup(cookie);
1556             if (ODR_MASK_GET(initrs->options, Z_Options_namedResultSets) &&
1557                 ODR_MASK_GET(initrs->protocolVersion, Z_ProtocolVersion_3))
1558                 c->support_named_resultsets = 1;
1559             if (c->tasks)
1560             {
1561                 assert (c->tasks->which == ZOOM_TASK_CONNECT);
1562                 ZOOM_connection_remove_task (c);
1563             }
1564             ZOOM_connection_exec_task (c);
1565         }
1566         break;
1567     case Z_APDU_searchResponse:
1568         handle_search_response (c, apdu->u.searchResponse);
1569         if (!send_sort_present (c))
1570             ZOOM_connection_remove_task (c);
1571         break;
1572     case Z_APDU_presentResponse:
1573         handle_present_response (c, apdu->u.presentResponse);
1574         if (!send_present (c))
1575             ZOOM_connection_remove_task (c);
1576         break;
1577     case Z_APDU_sortResponse:
1578         sort_response (c, apdu->u.sortResponse);
1579         if (!send_present (c))
1580             ZOOM_connection_remove_task (c);
1581         break;
1582     case Z_APDU_scanResponse:
1583         scan_response (c, apdu->u.scanResponse);
1584         ZOOM_connection_remove_task (c);
1585     }
1586 }
1587
1588 static int do_read (ZOOM_connection c)
1589 {
1590     int r;
1591     Z_APDU *apdu;
1592     ZOOM_Event event;
1593     
1594     event = ZOOM_Event_create (ZOOM_EVENT_RECV_DATA);
1595     ZOOM_connection_put_event (c, event);
1596     
1597     r = cs_get (c->cs, &c->buf_in, &c->len_in);
1598     if (r == 1)
1599         return 0;
1600     if (r <= 0)
1601     {
1602         c->error= ZOOM_ERROR_CONNECTION_LOST;
1603         do_close (c);
1604     }
1605     else
1606     {
1607         ZOOM_Event event;
1608         odr_reset (c->odr_in);
1609         odr_setbuf (c->odr_in, c->buf_in, r, 0);
1610         event = ZOOM_Event_create (ZOOM_EVENT_RECV_APDU);
1611         ZOOM_connection_put_event (c, event);
1612         if (!z_APDU (c->odr_in, &apdu, 0, 0))
1613         {
1614             c->error = ZOOM_ERROR_DECODE;
1615             do_close (c);
1616         }
1617         else
1618         {
1619             handle_apdu (c, apdu);
1620         }
1621     }
1622     return 1;
1623 }
1624
1625 static int do_write_ex (ZOOM_connection c, char *buf_out, int len_out)
1626 {
1627     int r;
1628     ZOOM_Event event;
1629     
1630     event = ZOOM_Event_create(ZOOM_EVENT_SEND_DATA);
1631     ZOOM_connection_put_event (c, event);
1632
1633     if ((r=cs_put (c->cs, buf_out, len_out)) < 0)
1634     {
1635         if (c->state == STATE_CONNECTING)
1636             c->error = ZOOM_ERROR_CONNECT;
1637         else
1638             c->error = ZOOM_ERROR_CONNECTION_LOST;
1639         do_close (c);
1640         return 1;
1641     }
1642     else if (r == 1)
1643     {    
1644         c->mask = ZOOM_SELECT_EXCEPT;
1645         if (c->cs->io_pending & CS_WANT_WRITE)
1646             c->mask += ZOOM_SELECT_WRITE;
1647         if (c->cs->io_pending & CS_WANT_READ)
1648             c->mask += ZOOM_SELECT_READ;
1649         yaz_log (LOG_DEBUG, "do_write_ex 1 mask=%d", c->mask);
1650     }
1651     else
1652     {
1653         c->mask = ZOOM_SELECT_READ|ZOOM_SELECT_EXCEPT;
1654         yaz_log (LOG_DEBUG, "do_write_ex 2 mask=%d", c->mask);
1655     }
1656     return 0;
1657 }
1658
1659 static int do_write(ZOOM_connection c)
1660 {
1661     return do_write_ex (c, c->buf_out, c->len_out);
1662 }
1663
1664
1665 const char *ZOOM_connection_option_get (ZOOM_connection c, const char *key)
1666 {
1667     return ZOOM_options_get (c->options, key);
1668 }
1669
1670 void ZOOM_connection_option_set (ZOOM_connection c, const char *key,
1671                                   const char *val)
1672 {
1673     ZOOM_options_set (c->options, key, val);
1674 }
1675
1676 const char *ZOOM_resultset_option_get (ZOOM_resultset r, const char *key)
1677 {
1678     return ZOOM_options_get (r->options, key);
1679 }
1680
1681 void ZOOM_resultset_option_set (ZOOM_resultset r, const char *key,
1682                                   const char *val)
1683 {
1684     ZOOM_options_set (r->options, key, val);
1685 }
1686
1687
1688 int ZOOM_connection_errcode (ZOOM_connection c)
1689 {
1690     return ZOOM_connection_error (c, 0, 0);
1691 }
1692
1693 const char *ZOOM_connection_errmsg (ZOOM_connection c)
1694 {
1695     const char *msg;
1696     ZOOM_connection_error (c, &msg, 0);
1697     return msg;
1698 }
1699
1700 const char *ZOOM_connection_addinfo (ZOOM_connection c)
1701 {
1702     const char *addinfo;
1703     ZOOM_connection_error (c, 0, &addinfo);
1704     return addinfo;
1705 }
1706
1707 int ZOOM_connection_error (ZOOM_connection c, const char **cp,
1708                             const char **addinfo)
1709 {
1710     int error = c->error;
1711     if (cp)
1712     {
1713         switch (error)
1714         {
1715         case ZOOM_ERROR_NONE:
1716             *cp = "No error"; break;
1717         case ZOOM_ERROR_CONNECT:
1718             *cp = "Connect failed"; break;
1719         case ZOOM_ERROR_MEMORY:
1720             *cp = "Out of memory"; break;
1721         case ZOOM_ERROR_ENCODE:
1722             *cp = "Encoding failed"; break;
1723         case ZOOM_ERROR_DECODE:
1724             *cp = "Decoding failed"; break;
1725         case ZOOM_ERROR_CONNECTION_LOST:
1726             *cp = "Connection lost"; break;
1727         case ZOOM_ERROR_INIT:
1728             *cp = "Init rejected"; break;
1729         case ZOOM_ERROR_INTERNAL:
1730             *cp = "Internal failure"; break;
1731         case ZOOM_ERROR_TIMEOUT:
1732             *cp = "Timeout"; break;
1733         default:
1734             *cp = diagbib1_str (error);
1735         }
1736     }
1737     if (addinfo)
1738     {
1739         if (c->addinfo)
1740             *addinfo = c->addinfo;
1741         else
1742             *addinfo = "";
1743     }
1744     return c->error;
1745 }
1746
1747 int ZOOM_connection_do_io(ZOOM_connection c, int mask)
1748 {
1749     ZOOM_Event event = 0;
1750     int r = cs_look(c->cs);
1751     yaz_log (LOG_DEBUG, "ZOOM_connection_do_io c=%p mask=%d cs_look=%d",
1752              c, mask, r);
1753     
1754     if (r == CS_NONE)
1755     {
1756         event = ZOOM_Event_create (ZOOM_EVENT_CONNECT);
1757         c->error = ZOOM_ERROR_CONNECT;
1758         do_close (c);
1759         ZOOM_connection_put_event (c, event);
1760     }
1761     else if (r == CS_CONNECT)
1762     {
1763         int ret;
1764         event = ZOOM_Event_create (ZOOM_EVENT_CONNECT);
1765
1766         ret = cs_rcvconnect (c->cs);
1767         yaz_log (LOG_DEBUG, "cs_rcvconnect returned %d", ret);
1768         if (ret == 1)
1769         {
1770             c->mask = ZOOM_SELECT_EXCEPT;
1771             if (c->cs->io_pending & CS_WANT_WRITE)
1772                 c->mask += ZOOM_SELECT_WRITE;
1773             if (c->cs->io_pending & CS_WANT_READ)
1774                 c->mask += ZOOM_SELECT_READ;
1775             ZOOM_connection_put_event (c, event);
1776         }
1777         else if (ret == 0)
1778         {
1779             ZOOM_connection_put_event (c, event);
1780             ZOOM_connection_send_init (c);
1781             c->state = STATE_ESTABLISHED;
1782         }
1783         else
1784         {
1785             c->error = ZOOM_ERROR_CONNECT;
1786             do_close (c);
1787             ZOOM_connection_put_event (c, event);
1788         }
1789     }
1790     else
1791     {
1792         if (mask & ZOOM_SELECT_READ)
1793             do_read (c);
1794         if (c->cs && (mask & ZOOM_SELECT_WRITE))
1795             do_write (c);
1796     }
1797     return 1;
1798 }
1799
1800 int ZOOM_connection_last_event(ZOOM_connection cs)
1801 {
1802     if (!cs)
1803         return ZOOM_EVENT_NONE;
1804     return cs->last_event;
1805 }
1806
1807 int ZOOM_event (int no, ZOOM_connection *cs)
1808 {
1809 #if HAVE_SYS_POLL_H
1810     struct pollfd pollfds[1024];
1811     ZOOM_connection poll_cs[1024];
1812 #else
1813     struct timeval tv;
1814     fd_set input, output, except;
1815 #endif
1816     int i, r, nfds;
1817     int max_fd = 0;
1818
1819     for (i = 0; i<no; i++)
1820     {
1821         ZOOM_connection c = cs[i];
1822         ZOOM_Event event;
1823         if (c && (event = ZOOM_connection_get_event(c)))
1824         {
1825             ZOOM_Event_destroy (event);
1826             return i+1;
1827         }
1828     }
1829     for (i = 0; i<no; i++)
1830     {
1831         ZOOM_connection c = cs[i];
1832         ZOOM_Event event;
1833         if (c && ZOOM_connection_exec_task (c))
1834         {
1835             if ((event = ZOOM_connection_get_event(c)))
1836             {
1837                 ZOOM_Event_destroy (event);
1838                 return i+1;
1839             }
1840         }
1841     }
1842 #if HAVE_SYS_POLL_H
1843
1844 #else
1845     tv.tv_sec = 15;
1846     tv.tv_usec = 0;
1847     
1848     FD_ZERO (&input);
1849     FD_ZERO (&output);
1850     FD_ZERO (&except);
1851 #endif
1852     nfds = 0;
1853     for (i = 0; i<no; i++)
1854     {
1855         ZOOM_connection c = cs[i];
1856         int fd, mask;
1857         
1858         if (!c)
1859             continue;
1860         fd = z3950_connection_socket(c);
1861         mask = z3950_connection_mask(c);
1862
1863         if (fd == -1)
1864             continue;
1865         if (max_fd < fd)
1866             max_fd = fd;
1867
1868 #if HAVE_SYS_POLL_H
1869         if (mask)
1870         {
1871             short poll_events = 0;
1872
1873             if (mask & ZOOM_SELECT_READ)
1874                 poll_events += POLLIN;
1875             if (mask & ZOOM_SELECT_WRITE)
1876                 poll_events += POLLOUT;
1877             if (mask & ZOOM_SELECT_EXCEPT)
1878                 poll_events += POLLERR;
1879             pollfds[nfds].fd = fd;
1880             pollfds[nfds].events = poll_events;
1881             pollfds[nfds].revents = 0;
1882             poll_cs[nfds] = c;
1883             nfds++;
1884         }
1885 #else
1886         if (mask & ZOOM_SELECT_READ)
1887         {
1888             FD_SET (fd, &input);
1889             nfds++;
1890         }
1891         if (mask & ZOOM_SELECT_WRITE)
1892         {
1893             FD_SET (fd, &output);
1894             nfds++;
1895         }
1896         if (mask & ZOOM_SELECT_EXCEPT)
1897         {
1898             FD_SET (fd, &except);
1899             nfds++;
1900         }
1901 #endif
1902     }
1903     if (!nfds)
1904         return 0;
1905 #if HAVE_SYS_POLL_H
1906     r = poll (pollfds, nfds, 15000);
1907     for (i = 0; i<nfds; i++)
1908     {
1909         ZOOM_connection c = poll_cs[i];
1910         if (r && c->mask)
1911         {
1912             int mask = 0;
1913             if (pollfds[i].revents & POLLIN)
1914                 mask += ZOOM_SELECT_READ;
1915             if (pollfds[i].revents & POLLOUT)
1916                 mask += ZOOM_SELECT_WRITE;
1917             if (pollfds[i].revents & POLLERR)
1918                 mask += ZOOM_SELECT_EXCEPT;
1919             if (mask)
1920                 ZOOM_connection_do_io(c, mask);
1921         }
1922         else if (r == 0 && c->mask)
1923         {
1924             ZOOM_Event event = ZOOM_Event_create(ZOOM_EVENT_TIMEOUT);
1925             /* timeout and this connection was waiting */
1926             c->error = ZOOM_ERROR_TIMEOUT;
1927             do_close (c);
1928             ZOOM_connection_put_event(c, event);
1929         }
1930     }
1931 #else
1932     yaz_log (LOG_DEBUG, "select start");
1933     r = select (max_fd+1, &input, &output, &except, &tv);
1934     yaz_log (LOG_DEBUG, "select stop, returned r=%d", r);
1935     for (i = 0; i<no; i++)
1936     {
1937         ZOOM_connection c = cs[i];
1938         int fd, mask;
1939
1940         if (!c)
1941             continue;
1942         fd = z3950_connection_socket(c);
1943         mask = 0;
1944         if (r && c->mask)
1945         {
1946             /* no timeout and real socket */
1947             if (FD_ISSET(fd, &input))
1948                 mask += ZOOM_SELECT_READ;
1949             if (FD_ISSET(fd, &output))
1950                 mask += ZOOM_SELECT_WRITE;
1951             if (FD_ISSET(fd, &except))
1952                 mask += ZOOM_SELECT_EXCEPT;
1953             if (mask)
1954                 ZOOM_connection_do_io(c, mask);
1955         }
1956         if (r == 0 && c->mask)
1957         {
1958             ZOOM_Event event = ZOOM_Event_create(ZOOM_EVENT_TIMEOUT);
1959             /* timeout and this connection was waiting */
1960             c->error = ZOOM_ERROR_TIMEOUT;
1961             do_close (c);
1962             yaz_log (LOG_DEBUG, "timeout");
1963             ZOOM_connection_put_event(c, event);
1964         }
1965     }
1966 #endif
1967     for (i = 0; i<no; i++)
1968     {
1969         ZOOM_connection c = cs[i];
1970         ZOOM_Event event;
1971         if (c && (event = ZOOM_connection_get_event(c)))
1972         {
1973             ZOOM_Event_destroy (event);
1974             return i+1;
1975         }
1976     }
1977     return 0;
1978 }