ZOOM Task structure includes start and count for retrieval.
[yaz-moved-to-github.git] / zoom / zoom-c.c
1 /*
2  * $Id: zoom-c.c,v 1.13 2001-11-28 23:00:19 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
444     if (!r)
445         return;
446     c = r->connection;
447     if (!c)
448         return;
449     task = ZOOM_connection_add_task (c, ZOOM_TASK_RETRIEVE);
450     task->u.retrieve.resultset = r;
451     task->u.retrieve.start = start;
452     task->u.retrieve.count = count;
453
454     ZOOM_resultset_addref (r);
455
456     if (!r->connection->async || force_sync)
457         while (r->connection && ZOOM_event (1, &r->connection))
458             ;
459 }
460
461 void ZOOM_resultset_records (ZOOM_resultset r, ZOOM_record *recs,
462                               size_t start, size_t count)
463 {
464     int force_present = 0;
465
466     if (!r)
467         return ;
468     if (count && recs)
469         force_present = 1;
470     ZOOM_resultset_retrieve (r, force_present, start, count);
471     if (force_present)
472     {
473         size_t i;
474         for (i = 0; i< count; i++)
475             recs[i] = ZOOM_resultset_record_immediate (r, i+start);
476     }
477 }
478
479 static int do_connect (ZOOM_connection c)
480 {
481     void *add;
482     const char *effective_host;
483
484     if (c->proxy)
485         effective_host = c->proxy;
486     else
487         effective_host = c->host_port;
488
489     yaz_log (LOG_DEBUG, "do_connect host=%s", effective_host);
490
491     assert (!c->cs);
492     c->cs = cs_create_host (effective_host, 0, &add);
493
494     if (c->cs)
495     {
496         int ret = cs_connect (c->cs, add);
497         yaz_log (LOG_DEBUG, "cs_connect returned %d", ret);
498         if (ret >= 0)
499         {
500             c->state = STATE_CONNECTING; 
501             c->mask = ZOOM_SELECT_READ | ZOOM_SELECT_WRITE | 
502                 ZOOM_SELECT_EXCEPT;
503             return 1;
504         }
505     }
506     c->state = STATE_IDLE;
507     c->error = ZOOM_ERROR_CONNECT;
508     return 0;
509 }
510
511 int z3950_connection_socket(ZOOM_connection c)
512 {
513     if (c->cs)
514         return cs_fileno(c->cs);
515     return -1;
516 }
517
518 int z3950_connection_mask(ZOOM_connection c)
519 {
520     if (c->cs)
521         return c->mask;
522     return 0;
523 }
524
525 static int encode_APDU(ZOOM_connection c, Z_APDU *a, ODR out)
526 {
527     char str[120];
528
529     assert (a);
530     sprintf (str, "send_APDU t=%p type=%d", c, a->which);
531     if (c->cookie_out)
532     {
533         Z_OtherInformation **oi;
534         yaz_oi_APDU(a, &oi);
535         yaz_oi_set_string_oidval(oi, out, VAL_COOKIE, 1, c->cookie_out);
536     }
537     if (!z_APDU(out, &a, 0, 0))
538     {
539         FILE *outf = fopen("/tmp/apdu.txt", "w");
540         if (outf)
541         {
542             ODR odr_pr = odr_createmem(ODR_PRINT);
543             fprintf (outf, "a=%p\n", a);
544             odr_setprint(odr_pr, outf);
545             z_APDU(odr_pr, &a, 0, 0);
546             odr_destroy(odr_pr);
547             fclose (outf);
548         }
549         c->error = ZOOM_ERROR_ENCODE;
550         do_close (c);
551         return -1;
552     }
553     return 0;
554 }
555
556 static int send_APDU (ZOOM_connection c, Z_APDU *a)
557 {
558     assert (a);
559     if (encode_APDU(c, a, c->odr_out))
560         return -1;
561     c->buf_out = odr_getbuf(c->odr_out, &c->len_out, 0);
562     odr_reset(c->odr_out);
563     do_write (c);
564     return 0;   
565 }
566
567 static int ZOOM_connection_send_init (ZOOM_connection c)
568 {
569     const char *impname;
570     Z_APDU *apdu = zget_APDU(c->odr_out, Z_APDU_initRequest);
571     Z_InitRequest *ireq = apdu->u.initRequest;
572     Z_IdAuthentication *auth = odr_malloc(c->odr_out, sizeof(*auth));
573     const char *auth_groupId = ZOOM_options_get (c->options, "group");
574     const char *auth_userId = ZOOM_options_get (c->options, "user");
575     const char *auth_password = ZOOM_options_get (c->options, "pass");
576     
577     ODR_MASK_SET(ireq->options, Z_Options_search);
578     ODR_MASK_SET(ireq->options, Z_Options_present);
579     ODR_MASK_SET(ireq->options, Z_Options_scan);
580     ODR_MASK_SET(ireq->options, Z_Options_sort);
581     ODR_MASK_SET(ireq->options, Z_Options_extendedServices);
582     ODR_MASK_SET(ireq->options, Z_Options_namedResultSets);
583     
584     ODR_MASK_SET(ireq->protocolVersion, Z_ProtocolVersion_1);
585     ODR_MASK_SET(ireq->protocolVersion, Z_ProtocolVersion_2);
586     ODR_MASK_SET(ireq->protocolVersion, Z_ProtocolVersion_3);
587     
588     impname = ZOOM_options_get (c->options, "implementationName");
589     ireq->implementationName =
590         odr_malloc (c->odr_out, 15 + (impname ? strlen(impname) : 0));
591     strcpy (ireq->implementationName, "");
592     if (impname)
593     {
594         strcat (ireq->implementationName, impname);
595         strcat (ireq->implementationName, "/");
596     }                                          
597     strcat (ireq->implementationName, "ZOOM-C/YAZ");
598     
599     *ireq->maximumRecordSize =
600         ZOOM_options_get_int (c->options, "maximumRecordSize", 1024*1024);
601     *ireq->preferredMessageSize =
602         ZOOM_options_get_int (c->options, "preferredMessageSize", 1024*1024);
603     
604     if (auth_groupId || auth_password)
605     {
606         Z_IdPass *pass = odr_malloc(c->odr_out, sizeof(*pass));
607         int i = 0;
608         pass->groupId = 0;
609         if (auth_groupId && *auth_groupId)
610         {
611             pass->groupId = odr_malloc(c->odr_out, strlen(auth_groupId)+1);
612             strcpy(pass->groupId, auth_groupId);
613             i++;
614         }
615         pass->userId = 0;
616         if (auth_userId && *auth_userId)
617         {
618             pass->userId = odr_malloc(c->odr_out, strlen(auth_userId)+1);
619             strcpy(pass->userId, auth_userId);
620             i++;
621         }
622         pass->password = 0;
623         if (auth_password && *auth_password)
624         {
625             pass->password = odr_malloc(c->odr_out, strlen(auth_password)+1);
626             strcpy(pass->password, auth_password);
627             i++;
628         }
629         if (i)
630         {
631             auth->which = Z_IdAuthentication_idPass;
632             auth->u.idPass = pass;
633             ireq->idAuthentication = auth;
634         }
635     }
636     else if (auth_userId)
637     {
638         auth->which = Z_IdAuthentication_open;
639         auth->u.open = odr_malloc(c->odr_out, strlen(auth_userId)+1);
640         strcpy(auth->u.open, auth_userId);
641         ireq->idAuthentication = auth;
642     }
643     if (c->proxy)
644         yaz_oi_set_string_oidval(&ireq->otherInfo, c->odr_out,
645                                  VAL_PROXY, 1, c->host_port);
646     assert (apdu);
647     send_APDU (c, apdu);
648     
649     return 0;
650 }
651
652 static int ZOOM_connection_send_search (ZOOM_connection c)
653 {
654     ZOOM_resultset r;
655     int lslb, ssub, mspn;
656     const char *syntax;
657     Z_APDU *apdu = zget_APDU(c->odr_out, Z_APDU_searchRequest);
658     Z_SearchRequest *search_req = apdu->u.searchRequest;
659     const char *elementSetName;
660     const char *smallSetElementSetName;
661     const char *mediumSetElementSetName;
662
663     assert (c->tasks);
664     assert (c->tasks->which == ZOOM_TASK_SEARCH);
665
666     r = c->tasks->u.search.resultset;
667
668     elementSetName =
669         ZOOM_options_get (r->options, "elementSetName");
670     smallSetElementSetName  =
671         ZOOM_options_get (r->options, "smallSetElementSetName");
672     mediumSetElementSetName =
673         ZOOM_options_get (r->options, "mediumSetElementSetName");
674
675     if (!smallSetElementSetName)
676         smallSetElementSetName = elementSetName;
677
678     if (!mediumSetElementSetName)
679         mediumSetElementSetName = elementSetName;
680
681     assert (r);
682     assert (r->r_query);
683
684     /* prepare query for the search request */
685     search_req->query = r->r_query;
686
687     search_req->databaseNames =
688         set_DatabaseNames (c, &search_req->num_databaseNames);
689
690     /* get syntax (no need to provide unless piggyback is in effect) */
691     syntax = ZOOM_options_get (r->options, "preferredRecordSyntax");
692
693     lslb = ZOOM_options_get_int (r->options, "largeSetLowerBound", -1);
694     ssub = ZOOM_options_get_int (r->options, "smallSetUpperBound", -1);
695     mspn = ZOOM_options_get_int (r->options, "mediumSetPresentNumber", -1);
696     if (lslb != -1 && ssub != -1 && mspn != -1)
697     {
698         /* So're a Z39.50 expert? Let's hope you don't do sort */
699         *search_req->largeSetLowerBound = lslb;
700         *search_req->smallSetUpperBound = ssub;
701         *search_req->mediumSetPresentNumber = mspn;
702     }
703     else if (r->start == 0 && r->count > 0
704              && r->piggyback && !r->r_sort_spec)
705     {
706         /* Regular piggyback - do it unless we're going to do sort */
707         *search_req->largeSetLowerBound = 2000000000;
708         *search_req->smallSetUpperBound = r->count;
709         *search_req->mediumSetPresentNumber = r->count;
710         smallSetElementSetName = 0;  /* no need to provide this */
711     }
712     else
713     {
714         /* non-piggyback. Need not provide elementsets or syntaxes .. */
715         smallSetElementSetName = 0;
716         mediumSetElementSetName = 0;
717         syntax = 0;
718     }
719     if (smallSetElementSetName && *smallSetElementSetName)
720     {
721         Z_ElementSetNames *esn = odr_malloc (c->odr_out, sizeof(*esn));
722         
723         esn->which = Z_ElementSetNames_generic;
724         esn->u.generic = odr_strdup (c->odr_out, smallSetElementSetName);
725         search_req->smallSetElementSetNames = esn;
726     }
727     if (mediumSetElementSetName && *mediumSetElementSetName)
728     {
729         Z_ElementSetNames *esn = odr_malloc (c->odr_out, sizeof(*esn));
730         
731         esn->which = Z_ElementSetNames_generic;
732         esn->u.generic = odr_strdup (c->odr_out, mediumSetElementSetName);
733         search_req->mediumSetElementSetNames = esn;
734     }
735     if (syntax)
736         search_req->preferredRecordSyntax =
737             yaz_str_to_z3950oid (c->odr_out, CLASS_RECSYN, syntax);
738     
739     if (!r->setname)
740     {
741         if (c->support_named_resultsets)
742         {
743             char setname[14];
744             int ord;
745             /* find the lowest unused ordinal so that we re-use
746                result sets on the server. */
747             for (ord = 1; ; ord++)
748             {
749                 ZOOM_resultset rp;
750                 sprintf (setname, "%d", ord);
751                 for (rp = c->resultsets; rp; rp = rp->next)
752                     if (rp->setname && !strcmp (rp->setname, setname))
753                         break;
754                 if (!rp)
755                     break;
756             }
757             r->setname = xstrdup (setname);
758             yaz_log (LOG_DEBUG, "allocating %s", r->setname);
759         }
760         else
761             r->setname = xstrdup ("default");
762         ZOOM_options_set (r->options, "setname", r->setname);
763     }
764     search_req->resultSetName = odr_strdup(c->odr_out, r->setname);
765     /* send search request */
766     send_APDU (c, apdu);
767     r->r_query = 0;
768     return 1;
769 }
770
771 static void response_diag (ZOOM_connection c, Z_DiagRec *p)
772 {
773     Z_DefaultDiagFormat *r;
774     char *addinfo = 0;
775     
776     xfree (c->addinfo);
777     c->addinfo = 0;
778     if (p->which != Z_DiagRec_defaultFormat)
779     {
780         c->error = ZOOM_ERROR_DECODE;
781         return;
782     }
783     r = p->u.defaultFormat;
784     switch (r->which)
785     {
786     case Z_DefaultDiagFormat_v2Addinfo:
787         addinfo = r->u.v2Addinfo;
788         break;
789     case Z_DefaultDiagFormat_v3Addinfo:
790         addinfo = r->u.v3Addinfo;
791         break;
792     }
793     if (addinfo)
794         c->addinfo = xstrdup (addinfo);
795     c->error = *r->condition;
796 }
797
798 ZOOM_record ZOOM_record_clone (ZOOM_record srec)
799 {
800     char *buf;
801     int size;
802     ODR odr_enc;
803     ZOOM_record nrec;
804
805     odr_enc = odr_createmem(ODR_ENCODE);
806     if (!z_NamePlusRecord (odr_enc, &srec->npr, 0, 0))
807         return 0;
808     buf = odr_getbuf (odr_enc, &size, 0);
809     
810     nrec = xmalloc (sizeof(*nrec));
811     nrec->odr = odr_createmem(ODR_DECODE);
812     nrec->wrbuf_marc = 0;
813     odr_setbuf (nrec->odr, buf, size, 0);
814     z_NamePlusRecord (nrec->odr, &nrec->npr, 0, 0);
815     
816     odr_destroy (odr_enc);
817     return nrec;
818 }
819
820 ZOOM_record ZOOM_resultset_record_immediate (ZOOM_resultset s,size_t pos)
821 {
822     return record_cache_lookup (s, pos, 0);
823 }
824
825 ZOOM_record ZOOM_resultset_record (ZOOM_resultset r, size_t pos)
826 {
827     ZOOM_resultset_retrieve (r, 1, pos, 1);
828     return ZOOM_resultset_record_immediate (r, pos);
829 }
830
831 void ZOOM_record_destroy (ZOOM_record rec)
832 {
833     if (!rec)
834         return;
835     if (rec->wrbuf_marc)
836         wrbuf_free (rec->wrbuf_marc, 1);
837     odr_destroy (rec->odr);
838     xfree (rec);
839 }
840
841 void *ZOOM_record_get (ZOOM_record rec, const char *type, size_t *len)
842 {
843     Z_NamePlusRecord *npr;
844     if (!rec)
845         return 0;
846     npr = rec->npr;
847     if (!npr)
848         return 0;
849     if (!strcmp (type, "database"))
850     {
851         return npr->databaseName;
852     }
853     else if (!strcmp (type, "syntax"))
854     {
855         if (npr->which == Z_NamePlusRecord_databaseRecord)
856         {
857             Z_External *r = (Z_External *) npr->u.databaseRecord;
858             oident *ent = oid_getentbyoid(r->direct_reference);
859             if (ent)
860                 return ent->desc;
861         }
862         return "none";
863     }
864     else if (!strcmp (type, "render"))
865     {
866         if (npr->which == Z_NamePlusRecord_databaseRecord)
867         {
868             Z_External *r = (Z_External *) npr->u.databaseRecord;
869             oident *ent = oid_getentbyoid(r->direct_reference);
870             
871             if (r->which == Z_External_sutrs)
872             {
873                 *len = r->u.sutrs->len;
874                 return r->u.sutrs->buf;
875             }
876             else if (r->which == Z_External_octet)
877             {
878                 switch (ent->value)
879                 {
880                 case VAL_SOIF:
881                 case VAL_HTML:
882                 case VAL_SUTRS:
883                     break;
884                 case VAL_TEXT_XML:
885                 case VAL_APPLICATION_XML:
886                     break;
887                 default:
888                     if (!rec->wrbuf_marc)
889                         rec->wrbuf_marc = wrbuf_alloc();
890                     if (marc_display_wrbuf (r->u.octet_aligned->buf,
891                                         rec->wrbuf_marc, 0,
892                                             r->u.octet_aligned->len) > 0)
893                     {
894                         *len = wrbuf_len(rec->wrbuf_marc);
895                         return wrbuf_buf(rec->wrbuf_marc);
896                     }
897                 }
898                 *len = r->u.octet_aligned->len;
899                 return r->u.octet_aligned->buf;
900             }
901             else if (r->which == Z_External_grs1)
902             {
903                 *len = 5;
904                 return "GRS-1";
905             }
906         }
907         return 0;
908     }
909     else if (!strcmp (type, "raw"))
910     {
911         if (npr->which == Z_NamePlusRecord_databaseRecord)
912         {
913             *len = -1;
914             return (Z_External *) npr->u.databaseRecord;
915         }
916         return 0;
917     }
918     return 0;
919 }
920
921 static void record_cache_add (ZOOM_resultset r,
922                               Z_NamePlusRecord *npr,
923                               int pos,
924                               const char *elementSetName)
925 {
926     ZOOM_record_cache rc;
927
928     for (rc = r->record_cache; rc; rc = rc->next)
929     {
930         if (pos == rc->pos)
931         {
932             if ((!elementSetName && !rc->elementSetName)
933                 || (elementSetName && rc->elementSetName &&
934                     !strcmp (elementSetName, rc->elementSetName)))
935             {
936                 /* not destroying rc->npr (it's handled by nmem )*/
937                 rc->rec.npr = npr;
938                 /* keeping wrbuf_marc too */
939                 return;
940             }
941         }
942
943     }
944     rc = odr_malloc (r->odr, sizeof(*rc));
945     rc->rec.npr = npr; 
946     rc->rec.odr = 0;
947     rc->rec.wrbuf_marc = 0;
948     if (elementSetName)
949         rc->elementSetName = odr_strdup (r->odr, elementSetName);
950     else
951         rc->elementSetName = 0;
952     rc->pos = pos;
953     rc->next = r->record_cache;
954     r->record_cache = rc;
955 }
956
957 static ZOOM_record record_cache_lookup (ZOOM_resultset r,
958                                          int pos,
959                                          const char *elementSetName)
960 {
961     ZOOM_record_cache rc;
962
963     for (rc = r->record_cache; rc; rc = rc->next)
964     {
965         if (pos == rc->pos)
966         {
967             if ((!elementSetName && !rc->elementSetName)
968                 || (elementSetName && rc->elementSetName &&
969                     !strcmp (elementSetName, rc->elementSetName)))
970                 return &rc->rec;
971         }
972     }
973     return 0;
974 }
975                                              
976 static void handle_records (ZOOM_connection c, Z_Records *sr,
977                             int present_phase)
978 {
979     ZOOM_resultset resultset;
980
981     if (!c->tasks)
982         return ;
983     switch (c->tasks->which)
984     {
985     case ZOOM_TASK_SEARCH:
986         resultset = c->tasks->u.search.resultset;
987         break;
988     case ZOOM_TASK_RETRIEVE:
989         resultset = c->tasks->u.retrieve.resultset;        
990         break;
991     default:
992         return;
993     }
994     if (sr && sr->which == Z_Records_NSD)
995     {
996         Z_DiagRec dr, *dr_p = &dr;
997         dr.which = Z_DiagRec_defaultFormat;
998         dr.u.defaultFormat = sr->u.nonSurrogateDiagnostic;
999         
1000         response_diag (c, dr_p);
1001     }
1002     else if (sr && sr->which == Z_Records_multipleNSD)
1003     {
1004         if (sr->u.multipleNonSurDiagnostics->num_diagRecs >= 1)
1005             response_diag(c, sr->u.multipleNonSurDiagnostics->diagRecs[0]);
1006         else
1007             c->error = ZOOM_ERROR_DECODE;
1008     }
1009     else 
1010     {
1011         if (resultset->count + resultset->start > resultset->size)
1012             resultset->count = resultset->size - resultset->start;
1013         if (resultset->count < 0)
1014             resultset->count = 0;
1015         if (sr && sr->which == Z_Records_DBOSD)
1016         {
1017             int i;
1018             NMEM nmem = odr_extract_mem (c->odr_in);
1019             Z_NamePlusRecordList *p =
1020                 sr->u.databaseOrSurDiagnostics;
1021             for (i = 0; i<p->num_records; i++)
1022             {
1023                 record_cache_add (resultset, p->records[i],
1024                                   i+ resultset->start, 0);
1025             }
1026             /* transfer our response to search_nmem .. we need it later */
1027             nmem_transfer (resultset->odr->mem, nmem);
1028             nmem_destroy (nmem);
1029             if (present_phase && p->num_records == 0)
1030             {
1031                 /* present response and we didn't get any records! */
1032                 c->error = ZOOM_ERROR_DECODE;
1033             }
1034         }
1035         else if (present_phase)
1036         {
1037             /* present response and we didn't get any records! */
1038             c->error = ZOOM_ERROR_DECODE;
1039         }
1040     }
1041 }
1042
1043 static void handle_present_response (ZOOM_connection c, Z_PresentResponse *pr)
1044 {
1045     handle_records (c, pr->records, 1);
1046 }
1047
1048 static void handle_search_response (ZOOM_connection c, Z_SearchResponse *sr)
1049 {
1050     ZOOM_resultset resultset;
1051
1052     yaz_log (LOG_DEBUG, "got search response");
1053
1054     if (!c->tasks || c->tasks->which != ZOOM_TASK_SEARCH)
1055         return ;
1056
1057     resultset = c->tasks->u.search.resultset;
1058
1059     resultset->size = *sr->resultCount;
1060     handle_records (c, sr->records, 0);
1061 }
1062
1063 static void sort_response (ZOOM_connection c, Z_SortResponse *res)
1064 {
1065     if (res->diagnostics && res->num_diagnostics > 0)
1066         response_diag (c, res->diagnostics[0]);
1067 }
1068
1069 static int send_sort (ZOOM_connection c)
1070 {
1071     ZOOM_resultset  resultset;
1072
1073     if (!c->tasks || c->tasks->which != ZOOM_TASK_SEARCH)
1074         return 0;
1075
1076     resultset = c->tasks->u.search.resultset;
1077
1078     if (c->error)
1079     {
1080         resultset->r_sort_spec = 0;
1081         return 0;
1082     }
1083     if (resultset->r_sort_spec)
1084     {
1085         Z_APDU *apdu = zget_APDU(c->odr_out, Z_APDU_sortRequest);
1086         Z_SortRequest *req = apdu->u.sortRequest;
1087         
1088         req->num_inputResultSetNames = 1;
1089         req->inputResultSetNames = (Z_InternationalString **)
1090             odr_malloc (c->odr_out, sizeof(*req->inputResultSetNames));
1091         req->inputResultSetNames[0] =
1092             odr_strdup (c->odr_out, resultset->setname);
1093         req->sortedResultSetName = odr_strdup (c->odr_out, resultset->setname);
1094         req->sortSequence = resultset->r_sort_spec;
1095         resultset->r_sort_spec = 0;
1096         send_APDU (c, apdu);
1097         return 1;
1098     }
1099     return 0;
1100 }
1101
1102 static int send_present (ZOOM_connection c)
1103 {
1104     Z_APDU *apdu = zget_APDU(c->odr_out, Z_APDU_presentRequest);
1105     Z_PresentRequest *req = apdu->u.presentRequest;
1106     int i = 0;
1107     const char *syntax = 
1108         ZOOM_options_get (c->options, "preferredRecordSyntax");
1109     const char *element =
1110         ZOOM_options_get (c->options, "elementSetName");
1111     ZOOM_resultset  resultset;
1112
1113     if (!c->tasks)
1114         return 0;
1115
1116     switch (c->tasks->which)
1117     {
1118     case ZOOM_TASK_SEARCH:
1119         resultset = c->tasks->u.search.resultset;
1120         break;
1121     case ZOOM_TASK_RETRIEVE:
1122         resultset = c->tasks->u.retrieve.resultset;
1123         resultset->start = c->tasks->u.retrieve.start;
1124         resultset->count = c->tasks->u.retrieve.count;
1125
1126         if (resultset->start >= resultset->size)
1127             return 0;
1128         if (resultset->start + resultset->count > resultset->size)
1129             resultset->count = resultset->size - resultset->start;
1130         break;
1131     default:
1132         return 0;
1133     }
1134
1135     if (c->error)                  /* don't continue on error */
1136         return 0;
1137     if (resultset->start < 0)
1138         return 0;
1139     for (i = 0; i<resultset->count; i++)
1140     {
1141         ZOOM_record rec =
1142             record_cache_lookup (resultset, i + resultset->start, 0);
1143         if (!rec)
1144             break;
1145     }
1146     if (i == resultset->count)
1147         return 0;
1148
1149     resultset->start += i;
1150     resultset->count -= i;
1151     *req->resultSetStartPoint = resultset->start + 1;
1152     *req->numberOfRecordsRequested = resultset->count;
1153     assert (*req->numberOfRecordsRequested > 0);
1154
1155     if (syntax && *syntax)
1156         req->preferredRecordSyntax =
1157             yaz_str_to_z3950oid (c->odr_out, CLASS_RECSYN, syntax);
1158
1159     if (element && *element)
1160     {
1161         Z_ElementSetNames *esn = odr_malloc (c->odr_out, sizeof(*esn));
1162         Z_RecordComposition *compo = odr_malloc (c->odr_out, sizeof(*compo));
1163         
1164         esn->which = Z_ElementSetNames_generic;
1165         esn->u.generic = odr_strdup (c->odr_out, element);
1166         compo->which = Z_RecordComp_simple;
1167         compo->u.simple = esn;
1168         req->recordComposition = compo;
1169     }
1170     req->resultSetId = odr_strdup(c->odr_out, resultset->setname);
1171     send_APDU (c, apdu);
1172     return 1;
1173 }
1174
1175 static int ZOOM_connection_exec_task (ZOOM_connection c)
1176 {
1177     ZOOM_task task = c->tasks;
1178
1179     yaz_log (LOG_DEBUG, "ZOOM_connection_exec_task");
1180     if (!task)
1181         return 0;
1182     if (c->error != ZOOM_ERROR_NONE ||
1183         (!c->cs && task->which != ZOOM_TASK_CONNECT))
1184     {
1185         ZOOM_connection_remove_tasks (c);
1186         return 0;
1187     }
1188     yaz_log (LOG_DEBUG, "ZOOM_connection_exec_task type=%d", task->which);
1189     if (task->running)
1190         return 0;
1191     task->running = 1;
1192     switch (task->which)
1193     {
1194     case ZOOM_TASK_SEARCH:
1195         /* see if search hasn't been sent yet. */
1196         if (ZOOM_connection_send_search (c))
1197             return 1;
1198         break;
1199     case ZOOM_TASK_RETRIEVE:
1200         if (send_present (c))
1201             return 1;
1202         break;
1203     case ZOOM_TASK_CONNECT:
1204         if (do_connect(c))
1205             return 1;
1206     }
1207     ZOOM_connection_remove_task (c);
1208     return 0;
1209 }
1210
1211 static int send_sort_present (ZOOM_connection c)
1212 {
1213     int r = send_sort (c);
1214     if (!r)
1215         r = send_present (c);
1216     return r;
1217 }
1218
1219 static void handle_apdu (ZOOM_connection c, Z_APDU *apdu)
1220 {
1221     Z_InitResponse *initrs;
1222     
1223     yaz_log (LOG_DEBUG, "hande_apdu type=%d", apdu->which);
1224     c->mask = 0;
1225     switch(apdu->which)
1226     {
1227     case Z_APDU_initResponse:
1228         initrs = apdu->u.initResponse;
1229         if (!*initrs->result)
1230         {
1231             c->error = ZOOM_ERROR_INIT;
1232         }
1233         else
1234         {
1235             char *cookie =
1236                 yaz_oi_get_string_oidval (&apdu->u.initResponse->otherInfo,
1237                                           VAL_COOKIE, 1, 0);
1238             xfree (c->cookie_in);
1239             c->cookie_in = 0;
1240             if (cookie)
1241                 c->cookie_in = xstrdup(cookie);
1242             if (ODR_MASK_GET(initrs->options, Z_Options_namedResultSets))
1243                 c->support_named_resultsets = 1;
1244             if (c->tasks)
1245             {
1246                 assert (c->tasks->which == ZOOM_TASK_CONNECT);
1247                 ZOOM_connection_remove_task (c);
1248             }
1249             ZOOM_connection_exec_task (c);
1250         }
1251         break;
1252     case Z_APDU_searchResponse:
1253         handle_search_response (c, apdu->u.searchResponse);
1254         if (!send_sort_present (c))
1255             ZOOM_connection_remove_task (c);
1256         break;
1257     case Z_APDU_presentResponse:
1258         handle_present_response (c, apdu->u.presentResponse);
1259         if (!send_present (c))
1260             ZOOM_connection_remove_task (c);
1261         break;
1262     case Z_APDU_sortResponse:
1263         sort_response (c, apdu->u.sortResponse);
1264         if (!send_present (c))
1265             ZOOM_connection_remove_task (c);
1266     }
1267 }
1268
1269 static int do_read (ZOOM_connection c)
1270 {
1271     int r;
1272     Z_APDU *apdu;
1273     
1274     r = cs_get (c->cs, &c->buf_in, &c->len_in);
1275     if (r == 1)
1276         return 0;
1277     if (r <= 0)
1278     {
1279         c->error= ZOOM_ERROR_CONNECTION_LOST;
1280         do_close (c);
1281     }
1282     else
1283     {
1284         odr_reset (c->odr_in);
1285         odr_setbuf (c->odr_in, c->buf_in, r, 0);
1286         if (!z_APDU (c->odr_in, &apdu, 0, 0))
1287         {
1288             c->error = ZOOM_ERROR_DECODE;
1289             do_close (c);
1290         }
1291         else
1292         {
1293             handle_apdu (c, apdu);
1294         }
1295     }
1296     return 1;
1297 }
1298
1299 static int do_write_ex (ZOOM_connection c, char *buf_out, int len_out)
1300 {
1301     int r;
1302     
1303     if ((r=cs_put (c->cs, buf_out, len_out)) < 0)
1304     {
1305         if (c->state == STATE_CONNECTING)
1306             c->error = ZOOM_ERROR_CONNECT;
1307         else
1308             c->error = ZOOM_ERROR_CONNECTION_LOST;
1309         do_close (c);
1310         return 1;
1311     }
1312     else if (r == 1)
1313     {
1314         c->state = STATE_ESTABLISHED;
1315         c->mask = ZOOM_SELECT_READ|ZOOM_SELECT_WRITE|ZOOM_SELECT_EXCEPT;
1316     }
1317     else
1318     {
1319         c->state = STATE_ESTABLISHED;
1320         c->mask = ZOOM_SELECT_READ|ZOOM_SELECT_EXCEPT;
1321     }
1322     return 0;
1323 }
1324
1325 static int do_write(ZOOM_connection c)
1326 {
1327     return do_write_ex (c, c->buf_out, c->len_out);
1328 }
1329
1330
1331 const char *ZOOM_connection_option_get (ZOOM_connection c, const char *key)
1332 {
1333     return ZOOM_options_get (c->options, key);
1334 }
1335
1336 void ZOOM_connection_option_set (ZOOM_connection c, const char *key,
1337                                   const char *val)
1338 {
1339     ZOOM_options_set (c->options, key, val);
1340 }
1341
1342 const char *ZOOM_resultset_option_get (ZOOM_resultset r, const char *key)
1343 {
1344     return ZOOM_options_get (r->options, key);
1345 }
1346
1347 void ZOOM_resultset_option_set (ZOOM_resultset r, const char *key,
1348                                   const char *val)
1349 {
1350     ZOOM_options_set (r->options, key, val);
1351 }
1352
1353
1354 int ZOOM_connection_errcode (ZOOM_connection c)
1355 {
1356     return ZOOM_connection_error (c, 0, 0);
1357 }
1358
1359 const char *ZOOM_connection_errmsg (ZOOM_connection c)
1360 {
1361     const char *msg;
1362     ZOOM_connection_error (c, &msg, 0);
1363     return msg;
1364 }
1365
1366 const char *ZOOM_connection_addinfo (ZOOM_connection c)
1367 {
1368     const char *addinfo;
1369     ZOOM_connection_error (c, 0, &addinfo);
1370     return addinfo;
1371 }
1372
1373 int ZOOM_connection_error (ZOOM_connection c, const char **cp,
1374                             const char **addinfo)
1375 {
1376     int error = c->error;
1377     if (cp)
1378     {
1379         switch (error)
1380         {
1381         case ZOOM_ERROR_NONE:
1382             *cp = "No error"; break;
1383         case ZOOM_ERROR_CONNECT:
1384             *cp = "Connect failed"; break;
1385         case ZOOM_ERROR_MEMORY:
1386             *cp = "Out of memory"; break;
1387         case ZOOM_ERROR_ENCODE:
1388             *cp = "Encoding failed"; break;
1389         case ZOOM_ERROR_DECODE:
1390             *cp = "Decoding failed"; break;
1391         case ZOOM_ERROR_CONNECTION_LOST:
1392             *cp = "Connection lost"; break;
1393         case ZOOM_ERROR_INIT:
1394             *cp = "Init rejected"; break;
1395         case ZOOM_ERROR_INTERNAL:
1396             *cp = "Internal failure"; break;
1397         case ZOOM_ERROR_TIMEOUT:
1398             *cp = "Timeout"; break;
1399         default:
1400             *cp = diagbib1_str (error);
1401         }
1402     }
1403     if (addinfo)
1404     {
1405         if (c->addinfo)
1406             *addinfo = c->addinfo;
1407         else
1408             *addinfo = "";
1409     }
1410     return c->error;
1411 }
1412
1413 int ZOOM_connection_do_io(ZOOM_connection c, int mask)
1414 {
1415     ZOOM_Event event;
1416 #if 0
1417     int r = cs_look(c->cs);
1418     yaz_log (LOG_LOG, "ZOOM_connection_do_io c=%p mask=%d cs_look=%d",
1419              c, mask, r);
1420     
1421     if (r == CS_NONE)
1422     {
1423         c->error = ZOOM_ERROR_CONNECT;
1424         do_close (c);
1425     }
1426     else if (r == CS_CONNECT)
1427     {
1428         yaz_log (LOG_LOG, "calling rcvconnect");
1429         if (cs_rcvconnect (c->cs) < 0)
1430         {
1431             c->error = ZOOM_ERROR_CONNECT;
1432             do_close (c);
1433         }
1434         else
1435             ZOOM_connection_send_init (c);
1436     }
1437     else
1438     {
1439         if (mask & ZOOM_SELECT_READ)
1440             do_read (c);
1441         if (c->cs && (mask & ZOOM_SELECT_WRITE))
1442             do_write (c);
1443     }   
1444 #else
1445     yaz_log (LOG_DEBUG, "ZOOM_connection_do_io c=%p mask=%d", c, mask);
1446     if (c->state == STATE_CONNECTING)
1447     {
1448         if (mask & ZOOM_SELECT_WRITE)
1449             ZOOM_connection_send_init (c);
1450         else
1451         {
1452             c->error = ZOOM_ERROR_CONNECT;
1453             do_close (c);
1454         }
1455     }
1456     else if (c->state == STATE_ESTABLISHED)
1457     {
1458         if (mask & ZOOM_SELECT_READ)
1459             do_read (c);
1460         if (c->cs && (mask & ZOOM_SELECT_WRITE))
1461             do_write (c);
1462     }
1463     else
1464     {
1465         c->error = ZOOM_ERROR_INTERNAL;
1466         do_close (c);
1467     }
1468 #endif
1469     event = ZOOM_Event_create (1);
1470     ZOOM_connection_put_event (c, event);
1471     return 1;
1472 }
1473
1474
1475 int ZOOM_event (int no, ZOOM_connection *cs)
1476 {
1477 #if HAVE_SYS_POLL_H
1478     struct pollfd pollfds[1024];
1479     ZOOM_connection poll_cs[1024];
1480 #else
1481     struct timeval tv;
1482     fd_set input, output, except;
1483 #endif
1484     int i, r, nfds;
1485     int max_fd = 0;
1486
1487     for (i = 0; i<no; i++)
1488     {
1489         ZOOM_connection c = cs[i];
1490         ZOOM_Event event;
1491         if (c && (event = ZOOM_connection_get_event(c)))
1492         {
1493             ZOOM_Event_destroy (event);
1494             return i+1;
1495         }
1496     }
1497     for (i = 0; i<no; i++)
1498     {
1499         ZOOM_connection c = cs[i];
1500         if (c && ZOOM_connection_exec_task (c))
1501             return i+1;
1502     }
1503 #if HAVE_SYS_POLL_H
1504
1505 #else
1506     tv.tv_sec = 15;
1507     tv.tv_usec = 0;
1508     
1509     FD_ZERO (&input);
1510     FD_ZERO (&output);
1511     FD_ZERO (&except);
1512 #endif
1513     nfds = 0;
1514     for (i = 0; i<no; i++)
1515     {
1516         ZOOM_connection c = cs[i];
1517         int fd, mask;
1518         
1519         if (!c)
1520             continue;
1521         fd = z3950_connection_socket(c);
1522         mask = z3950_connection_mask(c);
1523
1524         if (fd == -1)
1525             continue;
1526         if (max_fd < fd)
1527             max_fd = fd;
1528
1529 #if HAVE_SYS_POLL_H
1530         if (mask)
1531         {
1532             short poll_events = 0;
1533
1534             if (mask & ZOOM_SELECT_READ)
1535                 poll_events += POLLIN;
1536             if (mask & ZOOM_SELECT_WRITE)
1537                 poll_events += POLLOUT;
1538             if (mask & ZOOM_SELECT_EXCEPT)
1539                 poll_events += POLLERR;
1540             pollfds[nfds].fd = fd;
1541             pollfds[nfds].events = poll_events;
1542             pollfds[nfds].revents = 0;
1543             poll_cs[nfds] = c;
1544             nfds++;
1545         }
1546 #else
1547         if (mask & ZOOM_SELECT_READ)
1548         {
1549             FD_SET (fd, &input);
1550             nfds++;
1551         }
1552         if (mask & ZOOM_SELECT_WRITE)
1553         {
1554             FD_SET (fd, &output);
1555             nfds++;
1556         }
1557         if (mask & ZOOM_SELECT_EXCEPT)
1558         {
1559             FD_SET (fd, &except);
1560             nfds++;
1561         }
1562 #endif
1563     }
1564     if (!nfds)
1565         return 0;
1566 #if HAVE_SYS_POLL_H
1567     yaz_log (LOG_DEBUG, "poll start");
1568     r = poll (pollfds, nfds, 15000);
1569     yaz_log (LOG_DEBUG, "poll stop, returned r=%d", r);
1570     for (i = 0; i<nfds; i++)
1571     {
1572         ZOOM_connection c = poll_cs[i];
1573         if (r && c->mask)
1574         {
1575             int mask = 0;
1576             if (pollfds[i].revents & POLLIN)
1577                 mask += ZOOM_SELECT_READ;
1578             if (pollfds[i].revents & POLLOUT)
1579                 mask += ZOOM_SELECT_WRITE;
1580             if (pollfds[i].revents & POLLERR)
1581                 mask += ZOOM_SELECT_EXCEPT;
1582             if (mask)
1583                 ZOOM_connection_do_io(c, mask);
1584         }
1585         else if (r == 0 && c->mask)
1586         {
1587             ZOOM_Event event = ZOOM_Event_create(0);
1588             /* timeout and this connection was waiting */
1589             c->error = ZOOM_ERROR_TIMEOUT;
1590             do_close (c);
1591             ZOOM_connection_put_event(c, event);
1592         }
1593     }
1594 #else
1595     yaz_log (LOG_DEBUG, "select start");
1596     r = select (max_fd+1, &input, &output, &except, &tv);
1597     yaz_log (LOG_DEBUG, "select stop, returned r=%d", r);
1598     for (i = 0; i<no; i++)
1599     {
1600         ZOOM_connection c = cs[i];
1601         int fd, mask;
1602
1603         if (!c)
1604             continue;
1605         fd = z3950_connection_socket(c);
1606         mask = 0;
1607         if (r && c->mask)
1608         {
1609             /* no timeout and real socket */
1610             if (FD_ISSET(fd, &input))
1611                 mask += ZOOM_SELECT_READ;
1612             if (FD_ISSET(fd, &output))
1613                 mask += ZOOM_SELECT_WRITE;
1614             if (FD_ISSET(fd, &except))
1615                 mask += ZOOM_SELECT_EXCEPT;
1616             if (mask)
1617                 ZOOM_connection_do_io(c, mask);
1618         }
1619         if (r == 0 && c->mask)
1620         {
1621             ZOOM_Event event = ZOOM_Event_create(0);
1622             /* timeout and this connection was waiting */
1623             c->error = ZOOM_ERROR_TIMEOUT;
1624             do_close (c);
1625             yaz_log (LOG_DEBUG, "timeout");
1626             ZOOM_connection_put_event(c, event);
1627         }
1628     }
1629 #endif
1630     for (i = 0; i<no; i++)
1631     {
1632         ZOOM_connection c = cs[i];
1633         ZOOM_Event event;
1634         if (c && (event = ZOOM_connection_get_event(c)))
1635         {
1636             ZOOM_Event_destroy (event);
1637             return i+1;
1638         }
1639     }
1640     return 0;
1641 }