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