Client prevents "present request out of range".
[yaz-moved-to-github.git] / zoom / zoom-c.c
1 /*
2  * $Id: zoom-c.c,v 1.8 2001-11-15 21:59:40 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 Z3950_Event Z3950_Event_create (int kind)
20 {
21     Z3950_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 Z3950_Event_destroy (Z3950_Event event)
29 {
30     xfree (event);
31 }
32
33 static void Z3950_connection_put_event (Z3950_connection c, Z3950_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 Z3950_Event Z3950_connection_get_event(Z3950_connection c)
52 {
53     // get from front of queue
54     Z3950_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 (Z3950_connection c)
70 {
71     c->error = Z3950_ERROR_NONE;
72     xfree (c->addinfo);
73     c->addinfo = 0;
74 }
75
76 Z3950_task Z3950_connection_add_task (Z3950_connection c, int which)
77 {
78     Z3950_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)->u.resultset = 0;  /* one null pointer there at least */
85     (*taskp)->next = 0;
86     clear_error (c);
87     return *taskp;
88 }
89
90 void Z3950_connection_remove_task (Z3950_connection c)
91 {
92     Z3950_task task = c->tasks;
93
94     if (task)
95     {
96         c->tasks = task->next;
97         switch (task->which)
98         {
99         case Z3950_TASK_SEARCH:
100             Z3950_resultset_destroy (task->u.resultset);
101             break;
102         case Z3950_TASK_RETRIEVE:
103             Z3950_resultset_destroy (task->u.resultset);
104             break;
105         case Z3950_TASK_CONNECT:
106             break;
107         default:
108             assert (0);
109         }
110         xfree (task);
111     }
112 }
113
114 void Z3950_connection_remove_tasks (Z3950_connection c)
115 {
116     while (c->tasks)
117         Z3950_connection_remove_task(c);
118 }
119
120 static Z3950_record record_cache_lookup (Z3950_resultset r,
121                                          int pos,
122                                          const char *elementSetName);
123
124 Z3950_connection Z3950_connection_create (Z3950_options options)
125 {
126     Z3950_connection c = xmalloc (sizeof(*c));
127
128     c->cs = 0;
129     c->mask = 0;
130     c->state = STATE_IDLE;
131     c->error = Z3950_ERROR_NONE;
132     c->addinfo = 0;
133     c->buf_in = 0;
134     c->len_in = 0;
135     c->buf_out = 0;
136     c->len_out = 0;
137     c->resultsets = 0;
138
139     c->options = Z3950_options_create_with_parent(options);
140
141     c->host_port = 0;
142     c->proxy = 0;
143
144     c->cookie_out = 0;
145     c->cookie_in = 0;
146     c->tasks = 0;
147
148     c->odr_in = odr_createmem (ODR_DECODE);
149     c->odr_out = odr_createmem (ODR_ENCODE);
150
151     c->async = 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 (Z3950_connection con, int *num)
161 {
162     char **databaseNames;
163     const char *c;
164     int no = 2;
165     const char *cp = Z3950_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 Z3950_connection Z3950_connection_new (const char *host, int portnum)
211 {
212     Z3950_connection c = Z3950_connection_create (0);
213
214     Z3950_connection_connect (c, host, portnum);
215     return c;
216 }
217
218 void Z3950_connection_connect(Z3950_connection c,
219                               const char *host, int portnum)
220 {
221     const char *val;
222     Z3950_task task;
223
224     val = Z3950_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     c->async = Z3950_options_get_bool (c->options, "async", 0);
240     
241     task = Z3950_connection_add_task (c, Z3950_TASK_CONNECT);
242
243     if (!c->async)
244     {
245         while (Z3950_event (1, &c))
246             ;
247     }
248 }
249
250 Z3950_query Z3950_query_create(void)
251 {
252     Z3950_query s = xmalloc (sizeof(*s));
253
254     s->refcount = 1;
255     s->query = 0;
256     s->sort_spec = 0;
257     s->odr = odr_createmem (ODR_ENCODE);
258
259     return s;
260 }
261
262 const char *Z3950_connection_host (Z3950_connection c)
263 {
264     return c->host_port;
265 }
266
267 void Z3950_query_destroy(Z3950_query s)
268 {
269     if (!s)
270         return;
271
272     (s->refcount)--;
273     yaz_log (LOG_DEBUG, "Z3950_query_destroy count=%d", s->refcount);
274     if (s->refcount == 0)
275     {
276         odr_destroy (s->odr);
277         xfree (s);
278     }
279 }
280
281 int Z3950_query_prefix(Z3950_query s, const char *str)
282 {
283     s->query = odr_malloc (s->odr, sizeof(*s->query));
284     s->query->which = Z_Query_type_1;
285     s->query->u.type_1 =  p_query_rpn(s->odr, PROTO_Z3950, str);
286     if (!s->query->u.type_1)
287         return -1;
288     return 0;
289 }
290
291 int Z3950_query_sortby(Z3950_query s, const char *criteria)
292 {
293     s->sort_spec = yaz_sort_spec (s->odr, criteria);
294     if (!s->sort_spec)
295         return -1;
296     return 0;
297 }
298
299 static int do_write(Z3950_connection c);
300
301 void Z3950_connection_destroy(Z3950_connection c)
302 {
303     Z3950_resultset r;
304     if (!c)
305         return;
306     if (c->cs)
307         cs_close (c->cs);
308     for (r = c->resultsets; r; r = r->next)
309         r->connection = 0;
310
311     xfree (c->buf_in);
312     xfree (c->addinfo);
313     odr_destroy (c->odr_in);
314     odr_destroy (c->odr_out);
315     Z3950_options_destroy (c->options);
316     Z3950_connection_remove_tasks (c);
317     xfree (c->host_port);
318     xfree (c);
319 }
320
321 void Z3950_resultset_addref (Z3950_resultset r)
322 {
323     if (r)
324         (r->refcount)++;
325 }
326 Z3950_resultset Z3950_resultset_create ()
327 {
328     Z3950_resultset r = xmalloc (sizeof(*r));
329
330     r->refcount = 1;
331     r->size = 0;
332     r->odr = odr_createmem (ODR_ENCODE);
333     r->start = 0;
334     r->piggyback = 1;
335     r->count = 0;
336     r->record_cache = 0;
337     r->r_sort_spec = 0;
338     r->r_query = 0;
339     r->search = 0;
340     r->connection = 0;
341     r->next = 0;
342     return r;
343 }
344
345 Z3950_resultset Z3950_connection_search_pqf(Z3950_connection c, const char *q)
346 {
347     Z3950_resultset r;
348     Z3950_query s = Z3950_query_create();
349
350     Z3950_query_prefix (s, q);
351
352     r = Z3950_connection_search (c, s);
353     Z3950_query_destroy (s);
354     return r;
355 }
356
357 Z3950_resultset Z3950_connection_search(Z3950_connection c, Z3950_query q)
358 {
359     Z3950_resultset r = Z3950_resultset_create ();
360     Z3950_task task;
361
362     r->r_sort_spec = q->sort_spec;
363     r->r_query = q->query;
364     r->search = q;
365
366     r->options = Z3950_options_create_with_parent(c->options);
367
368     r->start = Z3950_options_get_int(r->options, "start", 0);
369     r->count = Z3950_options_get_int(r->options, "count", 0);
370     r->piggyback = Z3950_options_get_bool (r->options, "piggyback", 1);
371     r->connection = c;
372
373     r->next = c->resultsets;
374     c->resultsets = r;
375
376     task = Z3950_connection_add_task (c, Z3950_TASK_SEARCH);
377     task->u.resultset = r;
378     Z3950_resultset_addref (r);  
379
380     (q->refcount)++;
381
382     if (!c->async)
383     {
384         while (Z3950_event (1, &c))
385             ;
386     }
387     return r;
388 }
389
390 void Z3950_resultset_destroy(Z3950_resultset r)
391 {
392     if (!r)
393         return;
394     (r->refcount)--;
395     yaz_log (LOG_DEBUG, "destroy r = %p count=%d", r, r->refcount);
396     if (r->refcount == 0)
397     {
398         if (r->connection)
399         {
400             /* remove ourselves from the resultsets in connection */
401             Z3950_resultset *rp = &r->connection->resultsets;
402             while (1)
403             {
404                 assert (*rp);   /* we must be in this list!! */
405                 if (*rp == r)
406                 {   /* OK, we're here - take us out of it */
407                     *rp = (*rp)->next;
408                     break;
409                 }
410                 rp = &(*rp)->next;
411             }
412         }
413         Z3950_query_destroy (r->search);
414         Z3950_options_destroy (r->options);
415         odr_destroy (r->odr);
416         xfree (r);
417     }
418 }
419
420 size_t Z3950_resultset_size (Z3950_resultset r)
421 {
422     return r->size;
423 }
424
425 static void do_close (Z3950_connection c)
426 {
427     if (c->cs)
428         cs_close(c->cs);
429     c->cs = 0;
430     c->mask = 0;
431     c->state = STATE_IDLE;
432 }
433
434 static void Z3950_resultset_retrieve (Z3950_resultset r,
435                                       int force_sync, int start, int count)
436 {
437     Z3950_task task;
438     Z3950_connection c;
439
440     if (!r)
441         return;
442     c = r->connection;
443     if (!c)
444         return;
445     if (start >= r->size)
446         return;
447
448     if (start + count > r->size)
449         count = r->size - start;
450
451     task = Z3950_connection_add_task (c, Z3950_TASK_RETRIEVE);
452     task->u.resultset = r;
453     Z3950_resultset_addref (r);
454
455     r->start = start;
456     r->count = count;
457
458     if (!r->connection->async || force_sync)
459         while (r->connection && Z3950_event (1, &r->connection))
460             ;
461 }
462
463 void Z3950_resultset_records (Z3950_resultset r, Z3950_record *recs,
464                               size_t start, size_t count)
465 {
466     int force_present = 0;
467
468     if (!r)
469         return ;
470     if (count && recs)
471         force_present = 1;
472     Z3950_resultset_retrieve (r, force_present, start, count);
473     if (force_present)
474     {
475         size_t i;
476         for (i = 0; i< count; i++)
477             recs[i] = Z3950_resultset_record_immediate (r, i+start);
478     }
479 }
480
481 static int do_connect (Z3950_connection c)
482 {
483     void *add;
484     const char *effective_host;
485
486     if (c->proxy)
487         effective_host = c->proxy;
488     else
489         effective_host = c->host_port;
490
491     yaz_log (LOG_DEBUG, "do_connect host=%s", effective_host);
492
493     assert (!c->cs);
494     c->cs = cs_create_host (effective_host, 0, &add);
495
496     if (c->cs)
497     {
498         int ret = cs_connect (c->cs, add);
499         yaz_log (LOG_DEBUG, "cs_connect returned %d", ret);
500         if (ret >= 0)
501         {
502             c->state = STATE_CONNECTING; 
503             c->mask = Z3950_SELECT_READ | Z3950_SELECT_WRITE | 
504                 Z3950_SELECT_EXCEPT;
505             return 1;
506         }
507     }
508     c->state = STATE_IDLE;
509     c->error = Z3950_ERROR_CONNECT;
510     return 0;
511 }
512
513 int z3950_connection_socket(Z3950_connection c)
514 {
515     if (c->cs)
516         return cs_fileno(c->cs);
517     return -1;
518 }
519
520 int z3950_connection_mask(Z3950_connection c)
521 {
522     if (c->cs)
523         return c->mask;
524     return 0;
525 }
526
527 static int encode_APDU(Z3950_connection c, Z_APDU *a, ODR out)
528 {
529     char str[120];
530
531     assert (a);
532     sprintf (str, "send_APDU t=%p type=%d", c, a->which);
533     if (c->cookie_out)
534     {
535         Z_OtherInformation **oi;
536         yaz_oi_APDU(a, &oi);
537         yaz_oi_set_string_oidval(oi, out, VAL_COOKIE, 1, c->cookie_out);
538     }
539     if (!z_APDU(out, &a, 0, 0))
540     {
541         FILE *outf = fopen("/tmp/apdu.txt", "w");
542         if (outf)
543         {
544             ODR odr_pr = odr_createmem(ODR_PRINT);
545             fprintf (outf, "a=%p\n", a);
546             odr_setprint(odr_pr, outf);
547             z_APDU(odr_pr, &a, 0, 0);
548             odr_destroy(odr_pr);
549             fclose (outf);
550         }
551         c->error = Z3950_ERROR_ENCODE;
552         do_close (c);
553         return -1;
554     }
555     return 0;
556 }
557
558 static int send_APDU (Z3950_connection c, Z_APDU *a)
559 {
560     assert (a);
561     if (encode_APDU(c, a, c->odr_out))
562         return -1;
563     c->buf_out = odr_getbuf(c->odr_out, &c->len_out, 0);
564     odr_reset(c->odr_out);
565     do_write (c);
566     return 0;   
567 }
568
569 static int Z3950_connection_send_init (Z3950_connection c)
570 {
571     const char *impname;
572     Z_APDU *apdu = zget_APDU(c->odr_out, Z_APDU_initRequest);
573     Z_InitRequest *ireq = apdu->u.initRequest;
574     Z_IdAuthentication *auth = odr_malloc(c->odr_out, sizeof(*auth));
575     const char *auth_groupId = Z3950_options_get (c->options, "group");
576     const char *auth_userId = Z3950_options_get (c->options, "user");
577     const char *auth_password = Z3950_options_get (c->options, "pass");
578     
579     ODR_MASK_SET(ireq->options, Z_Options_search);
580     ODR_MASK_SET(ireq->options, Z_Options_present);
581     ODR_MASK_SET(ireq->options, Z_Options_scan);
582     ODR_MASK_SET(ireq->options, Z_Options_sort);
583 #if 0
584     ODR_MASK_SET(ireq->options, Z_Options_extendedServices);
585     ODR_MASK_SET(ireq->options, Z_Options_namedResultSets);
586 #endif
587     
588     ODR_MASK_SET(ireq->protocolVersion, Z_ProtocolVersion_1);
589     ODR_MASK_SET(ireq->protocolVersion, Z_ProtocolVersion_2);
590     ODR_MASK_SET(ireq->protocolVersion, Z_ProtocolVersion_3);
591     
592     impname = Z3950_options_get (c->options, "implementationName");
593     ireq->implementationName =
594         odr_malloc (c->odr_out, 15 + (impname ? strlen(impname) : 0));
595     strcpy (ireq->implementationName, "");
596     if (impname)
597     {
598         strcat (ireq->implementationName, impname);
599         strcat (ireq->implementationName, "/");
600     }                                          
601     strcat (ireq->implementationName, "ZOOM-C/YAZ");
602     
603     *ireq->maximumRecordSize =
604         Z3950_options_get_int (c->options, "maximumRecordSize", 1024*1024);
605     *ireq->preferredMessageSize =
606         Z3950_options_get_int (c->options, "preferredMessageSize", 1024*1024);
607     
608     if (auth_groupId || auth_password)
609     {
610         Z_IdPass *pass = odr_malloc(c->odr_out, sizeof(*pass));
611         int i = 0;
612         pass->groupId = 0;
613         if (auth_groupId && *auth_groupId)
614         {
615             pass->groupId = odr_malloc(c->odr_out, strlen(auth_groupId)+1);
616             strcpy(pass->groupId, auth_groupId);
617             i++;
618         }
619         pass->userId = 0;
620         if (auth_userId && *auth_userId)
621         {
622             pass->userId = odr_malloc(c->odr_out, strlen(auth_userId)+1);
623             strcpy(pass->userId, auth_userId);
624             i++;
625         }
626         pass->password = 0;
627         if (auth_password && *auth_password)
628         {
629             pass->password = odr_malloc(c->odr_out, strlen(auth_password)+1);
630             strcpy(pass->password, auth_password);
631             i++;
632         }
633         if (i)
634         {
635             auth->which = Z_IdAuthentication_idPass;
636             auth->u.idPass = pass;
637             ireq->idAuthentication = auth;
638         }
639     }
640     else if (auth_userId)
641     {
642         auth->which = Z_IdAuthentication_open;
643         auth->u.open = odr_malloc(c->odr_out, strlen(auth_userId)+1);
644         strcpy(auth->u.open, auth_userId);
645         ireq->idAuthentication = auth;
646     }
647     if (c->proxy)
648         yaz_oi_set_string_oidval(&ireq->otherInfo, c->odr_out,
649                                  VAL_PROXY, 1, c->host_port);
650     assert (apdu);
651     send_APDU (c, apdu);
652     
653     return 0;
654 }
655
656 static int Z3950_connection_send_search (Z3950_connection c)
657 {
658     Z3950_resultset r;
659     int lslb, ssub, mspn;
660     const char *syntax;
661     Z_APDU *apdu = zget_APDU(c->odr_out, Z_APDU_searchRequest);
662     Z_SearchRequest *search_req = apdu->u.searchRequest;
663     const char *elementSetName;
664     const char *smallSetElementSetName;
665     const char *mediumSetElementSetName;
666
667     assert (c->tasks);
668     assert (c->tasks->which == Z3950_TASK_SEARCH);
669
670     r = c->tasks->u.resultset;
671
672     elementSetName =
673         Z3950_options_get (r->options, "elementSetName");
674     smallSetElementSetName  =
675         Z3950_options_get (r->options, "smallSetElementSetName");
676     mediumSetElementSetName =
677         Z3950_options_get (r->options, "mediumSetElementSetName");
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 = Z3950_options_get (r->options, "preferredRecordSyntax");
696
697     lslb = Z3950_options_get_int (r->options, "largeSetLowerBound", -1);
698     ssub = Z3950_options_get_int (r->options, "smallSetUpperBound", -1);
699     mspn = Z3950_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)
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     /* send search request */
744     send_APDU (c, apdu);
745     r->r_query = 0;
746     return 1;
747 }
748
749 static void response_diag (Z3950_connection c, Z_DiagRec *p)
750 {
751     Z_DefaultDiagFormat *r;
752     char *addinfo = 0;
753     
754     xfree (c->addinfo);
755     c->addinfo = 0;
756     if (p->which != Z_DiagRec_defaultFormat)
757     {
758         c->error = Z3950_ERROR_DECODE;
759         return;
760     }
761     r = p->u.defaultFormat;
762     switch (r->which)
763     {
764     case Z_DefaultDiagFormat_v2Addinfo:
765         addinfo = r->u.v2Addinfo;
766         break;
767     case Z_DefaultDiagFormat_v3Addinfo:
768         addinfo = r->u.v3Addinfo;
769         break;
770     }
771     if (addinfo)
772         c->addinfo = xstrdup (addinfo);
773     c->error = *r->condition;
774 }
775
776 Z3950_record Z3950_record_dup (const Z3950_record srec)
777 {
778     char *buf;
779     int size;
780     ODR odr_enc;
781     Z3950_record nrec;
782
783     odr_enc = odr_createmem(ODR_ENCODE);
784     if (!z_NamePlusRecord (odr_enc, &srec->npr, 0, 0))
785         return 0;
786     buf = odr_getbuf (odr_enc, &size, 0);
787     
788     nrec = xmalloc (sizeof(*nrec));
789     nrec->odr = odr_createmem(ODR_DECODE);
790     nrec->wrbuf_marc = 0;
791     odr_setbuf (nrec->odr, buf, size, 0);
792     z_NamePlusRecord (nrec->odr, &nrec->npr, 0, 0);
793     
794     odr_destroy (odr_enc);
795     return nrec;
796 }
797
798 Z3950_record Z3950_resultset_record_immediate (Z3950_resultset s,size_t pos)
799 {
800     return record_cache_lookup (s, pos, 0);
801 }
802
803 Z3950_record Z3950_resultset_record (Z3950_resultset r, size_t pos)
804 {
805     Z3950_resultset_retrieve (r, 1, pos, 1);
806     return Z3950_resultset_record_immediate (r, pos);
807 }
808
809 void Z3950_record_destroy (Z3950_record rec)
810 {
811     if (!rec)
812         return;
813     if (rec->wrbuf_marc)
814         wrbuf_free (rec->wrbuf_marc, 1);
815     odr_destroy (rec->odr);
816     xfree (rec);
817 }
818
819 void *Z3950_record_get (Z3950_record rec, const char *type, size_t *len)
820 {
821     Z_NamePlusRecord *npr;
822     if (!rec)
823         return 0;
824     npr = rec->npr;
825     if (!npr)
826         return 0;
827     if (!strcmp (type, "database"))
828     {
829         return npr->databaseName;
830     }
831     else if (!strcmp (type, "syntax"))
832     {
833         if (npr->which == Z_NamePlusRecord_databaseRecord)
834         {
835             Z_External *r = (Z_External *) npr->u.databaseRecord;
836             oident *ent = oid_getentbyoid(r->direct_reference);
837             if (ent)
838                 return ent->desc;
839         }
840         return "none";
841     }
842     else if (!strcmp (type, "render"))
843     {
844         if (npr->which == Z_NamePlusRecord_databaseRecord)
845         {
846             Z_External *r = (Z_External *) npr->u.databaseRecord;
847             oident *ent = oid_getentbyoid(r->direct_reference);
848             
849             if (r->which == Z_External_sutrs)
850             {
851                 *len = r->u.sutrs->len;
852                 return r->u.sutrs->buf;
853             }
854             else if (r->which == Z_External_octet)
855             {
856                 switch (ent->value)
857                 {
858                 case VAL_SOIF:
859                 case VAL_HTML:
860                 case VAL_SUTRS:
861                     break;
862                 case VAL_TEXT_XML:
863                 case VAL_APPLICATION_XML:
864                     break;
865                 default:
866                     if (!rec->wrbuf_marc)
867                         rec->wrbuf_marc = wrbuf_alloc();
868                     if (marc_display_wrbuf (r->u.octet_aligned->buf,
869                                         rec->wrbuf_marc, 0,
870                                             r->u.octet_aligned->len) > 0)
871                     {
872                         *len = wrbuf_len(rec->wrbuf_marc);
873                         return wrbuf_buf(rec->wrbuf_marc);
874                     }
875                 }
876                 *len = r->u.octet_aligned->len;
877                 return r->u.octet_aligned->buf;
878             }
879             else if (r->which == Z_External_grs1)
880             {
881                 *len = 5;
882                 return "GRS-1";
883             }
884         }
885         return 0;
886     }
887     else if (!strcmp (type, "raw"))
888     {
889         if (npr->which == Z_NamePlusRecord_databaseRecord)
890         {
891             *len = -1;
892             return (Z_External *) npr->u.databaseRecord;
893         }
894         return 0;
895     }
896     return 0;
897 }
898
899 static void record_cache_add (Z3950_resultset r,
900                               Z_NamePlusRecord *npr,
901                               int pos,
902                               const char *elementSetName)
903 {
904     Z3950_record_cache rc;
905
906     for (rc = r->record_cache; rc; rc = rc->next)
907     {
908         if (pos == rc->pos)
909         {
910             if ((!elementSetName && !rc->elementSetName)
911                 || (elementSetName && rc->elementSetName &&
912                     !strcmp (elementSetName, rc->elementSetName)))
913             {
914                 /* not destroying rc->npr (it's handled by nmem )*/
915                 rc->rec.npr = npr;
916                 /* keeping wrbuf_marc too */
917                 return;
918             }
919         }
920
921     }
922     rc = odr_malloc (r->odr, sizeof(*rc));
923     rc->rec.npr = npr; 
924     rc->rec.odr = 0;
925     rc->rec.wrbuf_marc = 0;
926     if (elementSetName)
927         rc->elementSetName = odr_strdup (r->odr, elementSetName);
928     else
929         rc->elementSetName = 0;
930     rc->pos = pos;
931     rc->next = r->record_cache;
932     r->record_cache = rc;
933 }
934
935 static Z3950_record record_cache_lookup (Z3950_resultset r,
936                                          int pos,
937                                          const char *elementSetName)
938 {
939     Z3950_record_cache rc;
940
941     for (rc = r->record_cache; rc; rc = rc->next)
942     {
943         if (pos == rc->pos)
944         {
945             if ((!elementSetName && !rc->elementSetName)
946                 || (elementSetName && rc->elementSetName &&
947                     !strcmp (elementSetName, rc->elementSetName)))
948                 return &rc->rec;
949         }
950     }
951     return 0;
952 }
953                                              
954 static void handle_records (Z3950_connection c, Z_Records *sr,
955                             int present_phase)
956 {
957     Z3950_resultset resultset;
958
959     if (!c->tasks)
960         return ;
961     if (c->tasks->which != Z3950_TASK_SEARCH &&
962         c->tasks->which != Z3950_TASK_RETRIEVE)
963         return ;
964     
965     resultset = c->tasks->u.resultset;
966
967     if (sr && sr->which == Z_Records_NSD)
968     {
969         Z_DiagRec dr, *dr_p = &dr;
970         dr.which = Z_DiagRec_defaultFormat;
971         dr.u.defaultFormat = sr->u.nonSurrogateDiagnostic;
972         
973         response_diag (c, dr_p);
974     }
975     else if (sr && sr->which == Z_Records_multipleNSD)
976     {
977         if (sr->u.multipleNonSurDiagnostics->num_diagRecs >= 1)
978             response_diag(c, sr->u.multipleNonSurDiagnostics->diagRecs[0]);
979         else
980             c->error = Z3950_ERROR_DECODE;
981     }
982     else 
983     {
984         if (resultset->count + resultset->start > resultset->size)
985             resultset->count = resultset->size - resultset->start;
986         if (resultset->count < 0)
987             resultset->count = 0;
988         if (sr && sr->which == Z_Records_DBOSD)
989         {
990             int i;
991             NMEM nmem = odr_extract_mem (c->odr_in);
992             Z_NamePlusRecordList *p =
993                 sr->u.databaseOrSurDiagnostics;
994             for (i = 0; i<p->num_records; i++)
995             {
996                 record_cache_add (resultset, p->records[i],
997                                   i+ resultset->start, 0);
998             }
999             /* transfer our response to search_nmem .. we need it later */
1000             nmem_transfer (resultset->odr->mem, nmem);
1001             nmem_destroy (nmem);
1002             if (present_phase && p->num_records == 0)
1003             {
1004                 /* present response and we didn't get any records! */
1005                 c->error = Z3950_ERROR_DECODE;
1006             }
1007         }
1008         else if (present_phase)
1009         {
1010             /* present response and we didn't get any records! */
1011             c->error = Z3950_ERROR_DECODE;
1012         }
1013     }
1014 }
1015
1016 static void handle_present_response (Z3950_connection c, Z_PresentResponse *pr)
1017 {
1018     handle_records (c, pr->records, 1);
1019 }
1020
1021 static void handle_search_response (Z3950_connection c, Z_SearchResponse *sr)
1022 {
1023     Z3950_resultset resultset;
1024
1025     yaz_log (LOG_DEBUG, "got search response");
1026
1027     if (!c->tasks || c->tasks->which != Z3950_TASK_SEARCH)
1028         return ;
1029
1030     resultset = c->tasks->u.resultset;
1031
1032     resultset->size = *sr->resultCount;
1033     handle_records (c, sr->records, 0);
1034 }
1035
1036 static void sort_response (Z3950_connection c, Z_SortResponse *res)
1037 {
1038     if (res->diagnostics && res->num_diagnostics > 0)
1039         response_diag (c, res->diagnostics[0]);
1040 }
1041
1042 static int send_sort (Z3950_connection c)
1043 {
1044     Z3950_resultset  resultset;
1045
1046     if (!c->tasks || c->tasks->which != Z3950_TASK_SEARCH)
1047         return 0;
1048
1049     resultset = c->tasks->u.resultset;
1050
1051     if (c->error)
1052     {
1053         resultset->r_sort_spec = 0;
1054         return 0;
1055     }
1056     if (resultset->r_sort_spec)
1057     {
1058         Z_APDU *apdu = zget_APDU(c->odr_out, Z_APDU_sortRequest);
1059         Z_SortRequest *req = apdu->u.sortRequest;
1060         
1061         req->num_inputResultSetNames = 1;
1062         req->inputResultSetNames = (Z_InternationalString **)
1063             odr_malloc (c->odr_out, sizeof(*req->inputResultSetNames));
1064         req->inputResultSetNames[0] = odr_strdup (c->odr_out, "default");
1065         req->sortedResultSetName = odr_strdup (c->odr_out, "default");
1066         req->sortSequence = resultset->r_sort_spec;
1067         resultset->r_sort_spec = 0;
1068         send_APDU (c, apdu);
1069         return 1;
1070     }
1071     return 0;
1072 }
1073
1074 static int send_present (Z3950_connection c)
1075 {
1076     Z_APDU *apdu = zget_APDU(c->odr_out, Z_APDU_presentRequest);
1077     Z_PresentRequest *req = apdu->u.presentRequest;
1078     int i = 0;
1079     const char *syntax = 
1080         Z3950_options_get (c->options, "preferredRecordSyntax");
1081     const char *element =
1082         Z3950_options_get (c->options, "elementSetName");
1083     Z3950_resultset  resultset;
1084
1085     if (!c->tasks)
1086         return 0;
1087     if (c->tasks->which != Z3950_TASK_SEARCH && 
1088         c->tasks->which != Z3950_TASK_RETRIEVE)
1089         return 0;
1090
1091     resultset = c->tasks->u.resultset;
1092     
1093     if (c->error)                  /* don't continue on error */
1094         return 0;
1095     if (resultset->start < 0)
1096         return 0;
1097     for (i = 0; i<resultset->count; i++)
1098     {
1099         Z3950_record rec =
1100             record_cache_lookup (resultset, i + resultset->start, 0);
1101         if (!rec)
1102             break;
1103     }
1104     if (i == resultset->count)
1105         return 0;
1106
1107     resultset->start += i;
1108     resultset->count -= i;
1109     *req->resultSetStartPoint = resultset->start + 1;
1110     *req->numberOfRecordsRequested = resultset->count;
1111     assert (*req->numberOfRecordsRequested > 0);
1112
1113     if (syntax && *syntax)
1114         req->preferredRecordSyntax =
1115             yaz_str_to_z3950oid (c->odr_out, CLASS_RECSYN, syntax);
1116
1117     if (element && *element)
1118     {
1119         Z_ElementSetNames *esn = odr_malloc (c->odr_out, sizeof(*esn));
1120         Z_RecordComposition *compo = odr_malloc (c->odr_out, sizeof(*compo));
1121         
1122         esn->which = Z_ElementSetNames_generic;
1123         esn->u.generic = odr_strdup (c->odr_out, element);
1124         compo->which = Z_RecordComp_simple;
1125         compo->u.simple = esn;
1126         req->recordComposition = compo;
1127     }
1128     send_APDU (c, apdu);
1129     return 1;
1130 }
1131
1132 static int Z3950_connection_exec_task (Z3950_connection c)
1133 {
1134     Z3950_task task = c->tasks;
1135
1136     yaz_log (LOG_LOG, "Z3950_connection_exec_task");
1137     if (!task)
1138         return 0;
1139     if (c->error != Z3950_ERROR_NONE ||
1140         (!c->cs && task->which != Z3950_TASK_CONNECT))
1141     {
1142         Z3950_connection_remove_tasks (c);
1143         return 0;
1144     }
1145     yaz_log (LOG_DEBUG, "Z3950_connection_exec_task type=%d", task->which);
1146     if (task->running)
1147         return 0;
1148     task->running = 1;
1149     switch (task->which)
1150     {
1151     case Z3950_TASK_SEARCH:
1152         /* see if search hasn't been sent yet. */
1153         if (Z3950_connection_send_search (c))
1154             return 1;
1155         break;
1156     case Z3950_TASK_RETRIEVE:
1157         if (send_present (c))
1158             return 1;
1159         break;
1160     case Z3950_TASK_CONNECT:
1161         if (do_connect(c))
1162             return 1;
1163     }
1164     Z3950_connection_remove_task (c);
1165     return 0;
1166 }
1167
1168 static int send_sort_present (Z3950_connection c)
1169 {
1170     int r = send_sort (c);
1171     if (!r)
1172         r = send_present (c);
1173     return r;
1174 }
1175
1176 static void handle_apdu (Z3950_connection c, Z_APDU *apdu)
1177 {
1178     Z_InitResponse *initrs;
1179     
1180     yaz_log (LOG_DEBUG, "hande_apdu type=%d", apdu->which);
1181     c->mask = 0;
1182     switch(apdu->which)
1183     {
1184     case Z_APDU_initResponse:
1185         initrs = apdu->u.initResponse;
1186         if (!*initrs->result)
1187         {
1188             c->error = Z3950_ERROR_INIT;
1189         }
1190         else
1191         {
1192             char *cookie =
1193                 yaz_oi_get_string_oidval (&apdu->u.initResponse->otherInfo,
1194                                           VAL_COOKIE, 1, 0);
1195             xfree (c->cookie_in);
1196             c->cookie_in = 0;
1197             if (cookie)
1198                 c->cookie_in = xstrdup(cookie);
1199             if (c->tasks)
1200             {
1201                 assert (c->tasks->which == Z3950_TASK_CONNECT);
1202                 Z3950_connection_remove_task (c);
1203             }
1204             Z3950_connection_exec_task (c);
1205         }
1206         break;
1207     case Z_APDU_searchResponse:
1208         handle_search_response (c, apdu->u.searchResponse);
1209         if (!send_sort_present (c))
1210             Z3950_connection_remove_task (c);
1211         break;
1212     case Z_APDU_presentResponse:
1213         handle_present_response (c, apdu->u.presentResponse);
1214         if (!send_present (c))
1215             Z3950_connection_remove_task (c);
1216         break;
1217     case Z_APDU_sortResponse:
1218         sort_response (c, apdu->u.sortResponse);
1219         if (!send_present (c))
1220             Z3950_connection_remove_task (c);
1221     }
1222 }
1223
1224 static int do_read (Z3950_connection c)
1225 {
1226     int r;
1227     Z_APDU *apdu;
1228     
1229     r = cs_get (c->cs, &c->buf_in, &c->len_in);
1230     if (r == 1)
1231         return 0;
1232     if (r <= 0)
1233     {
1234         c->error= Z3950_ERROR_CONNECTION_LOST;
1235         do_close (c);
1236     }
1237     else
1238     {
1239         odr_reset (c->odr_in);
1240         odr_setbuf (c->odr_in, c->buf_in, r, 0);
1241         if (!z_APDU (c->odr_in, &apdu, 0, 0))
1242         {
1243             c->error = Z3950_ERROR_DECODE;
1244             do_close (c);
1245         }
1246         else
1247         {
1248             handle_apdu (c, apdu);
1249         }
1250     }
1251     return 1;
1252 }
1253
1254 static int do_write_ex (Z3950_connection c, char *buf_out, int len_out)
1255 {
1256     int r;
1257     
1258     if ((r=cs_put (c->cs, buf_out, len_out)) < 0)
1259     {
1260         if (c->state == STATE_CONNECTING)
1261             c->error = Z3950_ERROR_CONNECT;
1262         else
1263             c->error = Z3950_ERROR_CONNECTION_LOST;
1264         do_close (c);
1265         return 1;
1266     }
1267     else if (r == 1)
1268     {
1269         c->state = STATE_ESTABLISHED;
1270         c->mask = Z3950_SELECT_READ|Z3950_SELECT_WRITE|Z3950_SELECT_EXCEPT;
1271     }
1272     else
1273     {
1274         c->state = STATE_ESTABLISHED;
1275         c->mask = Z3950_SELECT_READ|Z3950_SELECT_EXCEPT;
1276     }
1277     return 0;
1278 }
1279
1280 static int do_write(Z3950_connection c)
1281 {
1282     return do_write_ex (c, c->buf_out, c->len_out);
1283 }
1284
1285
1286 const char *Z3950_connection_option_get (Z3950_connection c, const char *key)
1287 {
1288     return Z3950_options_get (c->options, key);
1289 }
1290
1291 void Z3950_connection_option_set (Z3950_connection c, const char *key,
1292                                   const char *val)
1293 {
1294     Z3950_options_set (c->options, key, val);
1295 }
1296
1297 const char *Z3950_resultset_option_get (Z3950_resultset r, const char *key)
1298 {
1299     return Z3950_options_get (r->options, key);
1300 }
1301
1302 void Z3950_resultset_option_set (Z3950_resultset r, const char *key,
1303                                   const char *val)
1304 {
1305     Z3950_options_set (r->options, key, val);
1306 }
1307
1308
1309 int Z3950_connection_errcode (Z3950_connection c)
1310 {
1311     return Z3950_connection_error (c, 0, 0);
1312 }
1313
1314 const char *Z3950_connection_errmsg (Z3950_connection c)
1315 {
1316     const char *msg;
1317     Z3950_connection_error (c, &msg, 0);
1318     return msg;
1319 }
1320
1321 const char *Z3950_connection_addinfo (Z3950_connection c)
1322 {
1323     const char *addinfo;
1324     Z3950_connection_error (c, 0, &addinfo);
1325     return addinfo;
1326 }
1327
1328 int Z3950_connection_error (Z3950_connection c, const char **cp,
1329                             const char **addinfo)
1330 {
1331     int error = c->error;
1332     if (cp)
1333     {
1334         switch (error)
1335         {
1336         case Z3950_ERROR_NONE:
1337             *cp = "No error"; break;
1338         case Z3950_ERROR_CONNECT:
1339             *cp = "Connect failed"; break;
1340         case Z3950_ERROR_MEMORY:
1341             *cp = "Out of memory"; break;
1342         case Z3950_ERROR_ENCODE:
1343             *cp = "Encoding failed"; break;
1344         case Z3950_ERROR_DECODE:
1345             *cp = "Decoding failed"; break;
1346         case Z3950_ERROR_CONNECTION_LOST:
1347             *cp = "Connection lost"; break;
1348         case Z3950_ERROR_INIT:
1349             *cp = "Init rejected"; break;
1350         case Z3950_ERROR_INTERNAL:
1351             *cp = "Internal failure"; break;
1352         case Z3950_ERROR_TIMEOUT:
1353             *cp = "Timeout"; break;
1354         default:
1355             *cp = diagbib1_str (error);
1356         }
1357     }
1358     if (addinfo)
1359     {
1360         if (c->addinfo)
1361             *addinfo = c->addinfo;
1362         else
1363             *addinfo = "";
1364     }
1365     return c->error;
1366 }
1367
1368 int Z3950_connection_do_io(Z3950_connection c, int mask)
1369 {
1370     Z3950_Event event;
1371 #if 0
1372     int r = cs_look(c->cs);
1373     yaz_log (LOG_LOG, "Z3950_connection_do_io c=%p mask=%d cs_look=%d",
1374              c, mask, r);
1375     
1376     if (r == CS_NONE)
1377     {
1378         c->error = Z3950_ERROR_CONNECT;
1379         do_close (c);
1380     }
1381     else if (r == CS_CONNECT)
1382     {
1383         yaz_log (LOG_LOG, "calling rcvconnect");
1384         if (cs_rcvconnect (c->cs) < 0)
1385         {
1386             c->error = Z3950_ERROR_CONNECT;
1387             do_close (c);
1388         }
1389         else
1390             Z3950_connection_send_init (c);
1391     }
1392     else
1393     {
1394         if (mask & Z3950_SELECT_READ)
1395             do_read (c);
1396         if (c->cs && (mask & Z3950_SELECT_WRITE))
1397             do_write (c);
1398     }   
1399 #else
1400     yaz_log (LOG_DEBUG, "Z3950_connection_do_io c=%p mask=%d", c, mask);
1401     if (c->state == STATE_CONNECTING)
1402     {
1403         if (mask & Z3950_SELECT_WRITE)
1404             Z3950_connection_send_init (c);
1405         else
1406         {
1407             c->error = Z3950_ERROR_CONNECT;
1408             do_close (c);
1409         }
1410     }
1411     else if (c->state == STATE_ESTABLISHED)
1412     {
1413         if (mask & Z3950_SELECT_READ)
1414             do_read (c);
1415         if (c->cs && (mask & Z3950_SELECT_WRITE))
1416             do_write (c);
1417     }
1418     else
1419     {
1420         c->error = Z3950_ERROR_INTERNAL;
1421         do_close (c);
1422     }
1423 #endif
1424     event = Z3950_Event_create (1);
1425     Z3950_connection_put_event (c, event);
1426     return 1;
1427 }
1428
1429
1430 int Z3950_event (int no, Z3950_connection *cs)
1431 {
1432 #if HAVE_SYS_POLL_H
1433     struct pollfd pollfds[1024];
1434     Z3950_connection poll_cs[1024];
1435 #else
1436     struct timeval tv;
1437     fd_set input, output, except;
1438 #endif
1439     int i, r, nfds;
1440     int max_fd = 0;
1441
1442     for (i = 0; i<no; i++)
1443     {
1444         Z3950_connection c = cs[i];
1445         Z3950_Event event;
1446         if (c && (event = Z3950_connection_get_event(c)))
1447         {
1448             Z3950_Event_destroy (event);
1449             return i+1;
1450         }
1451     }
1452     for (i = 0; i<no; i++)
1453     {
1454         Z3950_connection c = cs[i];
1455         if (c && Z3950_connection_exec_task (c))
1456             return i+1;
1457     }
1458 #if HAVE_SYS_POLL_H
1459
1460 #else
1461     tv.tv_sec = 15;
1462     tv.tv_usec = 0;
1463     
1464     FD_ZERO (&input);
1465     FD_ZERO (&output);
1466     FD_ZERO (&except);
1467 #endif
1468     nfds = 0;
1469     for (i = 0; i<no; i++)
1470     {
1471         Z3950_connection c = cs[i];
1472         int fd, mask;
1473         
1474         if (!c)
1475             continue;
1476         fd = z3950_connection_socket(c);
1477         mask = z3950_connection_mask(c);
1478
1479         if (fd == -1)
1480             continue;
1481         if (max_fd < fd)
1482             max_fd = fd;
1483
1484 #if HAVE_SYS_POLL_H
1485         if (mask)
1486         {
1487             short poll_events = 0;
1488
1489             if (mask & Z3950_SELECT_READ)
1490                 poll_events += POLLIN;
1491             if (mask & Z3950_SELECT_WRITE)
1492                 poll_events += POLLOUT;
1493             if (mask & Z3950_SELECT_EXCEPT)
1494                 poll_events += POLLERR;
1495             pollfds[nfds].fd = fd;
1496             pollfds[nfds].events = poll_events;
1497             pollfds[nfds].revents = 0;
1498             poll_cs[nfds] = c;
1499             nfds++;
1500         }
1501 #else
1502         if (mask & Z3950_SELECT_READ)
1503         {
1504             FD_SET (fd, &input);
1505             nfds++;
1506         }
1507         if (mask & Z3950_SELECT_WRITE)
1508         {
1509             FD_SET (fd, &output);
1510             nfds++;
1511         }
1512         if (mask & Z3950_SELECT_EXCEPT)
1513         {
1514             FD_SET (fd, &except);
1515             nfds++;
1516         }
1517 #endif
1518     }
1519     if (!nfds)
1520         return 0;
1521 #if HAVE_SYS_POLL_H
1522     yaz_log (LOG_LOG, "poll start");
1523     r = poll (pollfds, nfds, 15000);
1524     yaz_log (LOG_LOG, "poll stop, returned r=%d", r);
1525     for (i = 0; i<nfds; i++)
1526     {
1527         Z3950_connection c = poll_cs[i];
1528         if (r && c->mask)
1529         {
1530             int mask = 0;
1531             if (pollfds[i].revents & POLLIN)
1532                 mask += Z3950_SELECT_READ;
1533             if (pollfds[i].revents & POLLOUT)
1534                 mask += Z3950_SELECT_WRITE;
1535             if (pollfds[i].revents & POLLERR)
1536                 mask += Z3950_SELECT_EXCEPT;
1537             if (mask)
1538                 Z3950_connection_do_io(c, mask);
1539         }
1540         else if (r == 0 && c->mask)
1541         {
1542             Z3950_Event event = Z3950_Event_create(0);
1543             /* timeout and this connection was waiting */
1544             c->error = Z3950_ERROR_TIMEOUT;
1545             do_close (c);
1546             Z3950_connection_put_event(c, event);
1547         }
1548     }
1549 #else
1550     yaz_log (LOG_DEBUG, "select start");
1551     r = select (max_fd+1, &input, &output, &except, &tv);
1552     yaz_log (LOG_DEBUG, "select stop, returned r=%d", r);
1553     for (i = 0; i<no; i++)
1554     {
1555         Z3950_connection c = cs[i];
1556         int fd, mask;
1557
1558         if (!c)
1559             continue;
1560         fd = z3950_connection_socket(c);
1561         mask = 0;
1562         if (r && c->mask)
1563         {
1564             /* no timeout and real socket */
1565             if (FD_ISSET(fd, &input))
1566                 mask += Z3950_SELECT_READ;
1567             if (FD_ISSET(fd, &output))
1568                 mask += Z3950_SELECT_WRITE;
1569             if (FD_ISSET(fd, &except))
1570                 mask += Z3950_SELECT_EXCEPT;
1571             if (mask)
1572                 Z3950_connection_do_io(c, mask);
1573         }
1574         if (r == 0 && c->mask)
1575         {
1576             Z3950_Event event = Z3950_Event_create(0);
1577             /* timeout and this connection was waiting */
1578             c->error = Z3950_ERROR_TIMEOUT;
1579             do_close (c);
1580             yaz_log (LOG_LOG, "timeout");
1581             Z3950_connection_put_event(c, event);
1582         }
1583     }
1584 #endif
1585     for (i = 0; i<no; i++)
1586     {
1587         Z3950_connection c = cs[i];
1588         Z3950_Event event;
1589         if (c && (event = Z3950_connection_get_event(c)))
1590         {
1591             Z3950_Event_destroy (event);
1592             return i+1;
1593         }
1594     }
1595     return 0;
1596 }