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