Yaz layer moved to new sub directory. zaccess aligned with new
[egate.git] / zlayer-yaz / zaccess.c
1 /*
2  * Copyright (c) 1995, the EUROPAGATE consortium (see below).
3  *
4  * The EUROPAGATE consortium members are:
5  *
6  *    University College Dublin
7  *    Danmarks Teknologiske Videnscenter
8  *    An Chomhairle Leabharlanna
9  *    Consejo Superior de Investigaciones Cientificas
10  *
11  * Permission to use, copy, modify, distribute, and sell this software and
12  * its documentation, in whole or in part, for any purpose, is hereby granted,
13  * provided that:
14  *
15  * 1. This copyright and permission notice appear in all copies of the
16  * software and its documentation. Notices of copyright or attribution
17  * which appear at the beginning of any file must remain unchanged.
18  *
19  * 2. The names of EUROPAGATE or the project partners may not be used to
20  * endorse or promote products derived from this software without specific
21  * prior written permission.
22  *
23  * 3. Users of this software (implementors and gateway operators) agree to
24  * inform the EUROPAGATE consortium of their use of the software. This
25  * information will be used to evaluate the EUROPAGATE project and the
26  * software, and to plan further developments. The consortium may use
27  * the information in later publications.
28  * 
29  * 4. Users of this software agree to make their best efforts, when
30  * documenting their use of the software, to acknowledge the EUROPAGATE
31  * consortium, and the role played by the software in their work.
32  *
33  * THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT WARRANTY OF ANY KIND,
34  * EXPRESS, IMPLIED, OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
35  * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
36  * IN NO EVENT SHALL THE EUROPAGATE CONSORTIUM OR ITS MEMBERS BE LIABLE
37  * FOR ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF
38  * ANY KIND, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA
39  * OR PROFITS, WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND
40  * ON ANY THEORY OF LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE
41  * USE OR PERFORMANCE OF THIS SOFTWARE.
42  *
43  */
44 /*
45  * Europagate, 1995
46  *
47  * Z39.50 API for the Email gateway - YAZ version
48  *
49  * $Log: zaccess.c,v $
50  * Revision 1.1  1995/07/03 08:21:31  adam
51  * Yaz layer moved to new sub directory. zaccess aligned with new
52  * YAZ version (1.0b).
53  *
54  * Revision 1.10  1995/05/16  09:41:46  adam
55  * LICENSE. Uses new versions of odr_{set,get}buf.
56  *
57  * Revision 1.9  1995/05/03  10:15:43  quinn
58  * Fixed bug in the get_record loop - crashed when diagrec was received.
59  *
60  * Revision 1.8  1995/04/28  14:18:38  quinn
61  * *** empty log message ***
62  *
63  * Revision 1.7  1995/04/21  16:29:32  quinn
64  * Fixed bugs.
65  *
66  * Revision 1.6  1995/04/21  12:58:36  adam
67  * Bug fix.
68  *
69  * Revision 1.5  1995/04/20  15:25:32  quinn
70  * Asynch. API
71  *
72  * Revision 1.4  1995/04/19  16:02:28  adam
73  * Record type hack.
74  *
75  * Revision 1.3  1995/04/19  12:55:15  quinn
76  * Added auth.
77  *
78  * Revision 1.2  1995/04/19  09:24:02  quinn
79  * Fixed bug in zass_open - variable initialized after use
80  *
81  * Revision 1.1  1995/04/17  11:26:53  quinn
82  * Added YAZ version of zaccess
83  *
84  */
85
86 /*
87  * Interface to the YAZ Z39.50 toolkit.
88  */
89
90 #include <stdlib.h>
91 #include <assert.h>
92 #include <ctype.h>
93
94 #include <gw-log.h>
95 #include <proto.h>
96 #include <comstack.h>
97 #include <tcpip.h>
98 #ifdef USE_XTIMOSI
99 #include <xmosi.h>
100 #endif
101 #include <oid.h>
102
103 #include <ccl.h>
104 #include <zaccess.h>
105
106 struct zass    /* Z-assoc */
107 {
108     COMSTACK ass;              /* comstack association handle */
109     ODR encode;
110     ODR decode;
111     int fd;                         /* low-level socket (for select only) */
112     int nonblocking;
113     int maxrecordsize;
114     int preferredmessagesize;
115     char *inbuf;
116     int inbuflen;
117 };
118
119 static Z_APDU *get_apdu(struct zass *z, int *complete)
120 {
121     int res;
122     Z_APDU *ap;
123
124     if ((res = cs_get(z->ass, &z->inbuf, &z->inbuflen)) <= 0)
125     {
126         gw_log(GW_LOG_WARN, ZASS_TYPE, "cs_get failed");
127         if (complete)
128             *complete = 1;
129         return 0;
130     }
131     else if (res == 1)
132     {
133         if (complete)
134             *complete = 0;
135         return 0;
136     }
137     odr_reset(z->decode);
138     odr_setbuf(z->decode, z->inbuf, res, 0);
139     if (!z_APDU(z->decode, &ap, 0))
140     {
141         gw_log(GW_LOG_WARN, ZASS_TYPE, "decode: %s",
142             odr_errlist[odr_geterror(z->decode)]);
143         if (complete)
144             *complete = 0;
145         return 0;
146     }
147     if (complete)
148         *complete = 1;
149     return ap;
150 }
151
152 static int send_apdu(struct zass *z, Z_APDU *a)
153 {
154     char *buf;
155     int len;
156
157     if (!z_APDU(z->encode, &a, 0))
158     {
159         gw_log(GW_LOG_FATAL, ZASS_TYPE, "encoding initreq");
160         return -1;
161     }
162     buf = odr_getbuf(z->encode, &len, NULL);
163     if (cs_put(z->ass, buf, len) < 0)
164     {
165         gw_log(GW_LOG_FATAL, ZASS_TYPE, "cs_put");
166         return -1;
167     }
168     odr_reset(z->encode);  /* release odr_allocated structures */
169     return 0;
170 }
171
172 static int send_initreq(struct zass *p, char *auth)
173 {
174     Z_APDU *apdu;
175     Z_InitRequest *req;
176     char name[512];
177     Z_IdAuthentication idauth;
178
179     apdu = zget_APDU (p->encode, Z_APDU_initRequest);
180     req = apdu->u.initRequest;
181     req->preferredMessageSize = &p->preferredmessagesize;
182     req->maximumRecordSize = &p->maxrecordsize;
183
184     if (auth)
185     {
186         req->idAuthentication = &idauth;
187         idauth.which = Z_IdAuthentication_open;
188         idauth.u.open = auth;
189     }
190     else
191         req->idAuthentication = 0;
192     sprintf(name, "%s (YAZ protocol layer)", ZASS_NAME);
193     req->implementationName = name;
194     req->implementationVersion = ZASS_VERSION;
195     if (send_apdu(p, apdu) < 0)
196         return -1;
197     return 0;
198 }
199
200 int zass_fileno(ZASS a)
201 {
202     return a->fd;
203 }
204
205 int zass_openresult(ZASS p, int *complete)
206 {
207     Z_APDU *ap;
208     Z_InitResponse *res;
209
210     if (!(ap = get_apdu(p, complete)))
211         return -1;
212     if (ap->which != Z_APDU_initResponse)
213     {
214         gw_log(GW_LOG_WARN, ZASS_TYPE, "bad APDU");
215         return -1;
216     }
217     res = ap->u.initResponse;
218     if (!*res->result)
219     {
220         gw_log(GW_LOG_WARN, ZASS_TYPE, "Negative result on initres");
221         return -1;
222     }
223     p->preferredmessagesize = *res->preferredMessageSize;
224     p->maxrecordsize = *res->maximumRecordSize;
225     if (res->implementationId)
226         gw_log(GW_LOG_NOTICE, ZASS_TYPE, "imp. ID  : %s",
227             res->implementationId);
228     if (res->implementationName)
229         gw_log(GW_LOG_NOTICE, ZASS_TYPE, "imp. ID  : %s",
230             res->implementationName);
231     if (res->implementationVersion)
232         gw_log(GW_LOG_NOTICE, ZASS_TYPE, "imp. ID  : %s",
233             res->implementationVersion);
234     gw_log(ZASS_DEBUG, ZASS_TYPE, "Initialized ok");
235     return 0;
236 }
237
238 ZASS zass_open(char *host, int port, int *complete, char *auth)
239 {
240     struct zass *p;
241     char addstr[512];
242     void *address;
243
244     if (!(p = malloc(sizeof(*p))))
245     {
246         gw_log(GW_LOG_FATAL|GW_LOG_ERRNO, ZASS_TYPE, "malloc");
247         return 0;
248     }
249     if (complete)
250     {
251         *complete = 1;
252         p->nonblocking = 1;
253     }
254     else
255         p->nonblocking = 0;
256     if (!(p->encode = odr_createmem(ODR_ENCODE)) ||
257         !(p->decode = odr_createmem(ODR_DECODE)))
258     {
259         gw_log(GW_LOG_FATAL|GW_LOG_ERRNO, ZASS_TYPE, "odr_createmem");
260         return 0;
261     }
262     p->maxrecordsize = ZASS_MAXRECORDSIZE;
263     p->preferredmessagesize = ZASS_PREFERREDMESSAGESIZE;
264     if (!(p->ass = cs_create(tcpip_type, 1, CS_Z3950)))
265     {
266         gw_log(GW_LOG_FATAL|GW_LOG_ERRNO, ZASS_TYPE, "cs_create");
267         return 0;
268     }
269     p->inbuf = 0;
270     p->inbuflen = 0;
271     sprintf(addstr, "%s:%d", host, port);
272     if (!(address = tcpip_strtoaddr(addstr)))
273     {
274         gw_log(GW_LOG_FATAL, ZASS_TYPE, "failed to resolve %s", addstr);
275         return 0;
276     }
277     p->fd = cs_fileno(p->ass);
278     gw_log(ZASS_DEBUG, ZASS_TYPE, "Connecting to %s", addstr);
279     if (cs_connect(p->ass, address) < 0)
280     {
281         gw_log(GW_LOG_FATAL, ZASS_TYPE, "Failed to connect to %s", addstr);
282         return 0;
283     }
284     gw_log(ZASS_DEBUG, ZASS_TYPE, "connected ok... initializing");
285     if (send_initreq(p, auth) < 0)
286     {
287         gw_log(GW_LOG_FATAL, ZASS_TYPE, "Failed to send init");
288         return 0;
289     }
290     if (p->nonblocking)
291     {
292         *complete = 0;
293         return p;
294     }
295     if (zass_openresult(p, complete) < 0)
296         return 0;
297     return p; /* all done */
298 }
299
300 /*
301  * Convert the egate (ccl2rpn) version of RPN to YAZ RPN.
302  */
303 static Z_RPNStructure *rpn2rpn(ODR o, struct ccl_rpn_node *q)
304 {
305     Z_RPNStructure *r = odr_malloc(o, sizeof(*r));
306     Z_AttributesPlusTerm *t;
307     struct ccl_rpn_attr *i;
308     int len;
309     static int op[] = { Z_Operator_and, Z_Operator_or, Z_Operator_and_not };
310
311     switch (q->kind)
312     {
313         case CCL_RPN_TERM:
314             r->which = Z_RPNStructure_simple;
315             r->u.simple = odr_malloc(o, sizeof(Z_Operand));
316             r->u.simple->which = Z_Operand_APT;
317             r->u.simple->u.attributesPlusTerm = t = odr_malloc(o, sizeof(*t));
318             t->term = odr_malloc(o, sizeof(Z_Term));
319             t->term->which = Z_Term_general;
320             t->term->u.general = odr_malloc(o, sizeof(Odr_oct));
321             t->term->u.general->len = t->term->u.general->size =
322                 strlen(q->u.t.term);
323             t->term->u.general->buf = odr_malloc(o, t->term->u.general->size);
324             memcpy(t->term->u.general->buf, q->u.t.term,
325                 t->term->u.general->len);
326             t->num_attributes = 0;
327             t->attributeList = odr_malloc(o, sizeof(Z_AttributeElement*) * 100);
328             for (i = q->u.t.attr_list; i && t->num_attributes < 100;
329                 i = i->next)
330             {
331                 Z_AttributeElement *a;
332                 
333                 t->attributeList[t->num_attributes++] = a =
334                     odr_malloc(o, sizeof(*a));
335                 a->attributeType = odr_malloc(o, sizeof(int));
336                 *a->attributeType = i->type;
337 #ifdef Z_95
338                 a->attributeSet = 0;
339                 a->which = Z_AttributeValue_numeric;
340                 a->value.numeric = odr_malloc(o, sizeof(*a->value.numeric));
341                 *a->value.numeric = i->value;
342 #else
343                 a->attributeValue = odr_malloc(o, sizeof(*a->attributeValue));
344                 *a->attributeValue = i->value;
345 #endif
346             }
347             return r;
348         case CCL_RPN_SET:
349             r->which = Z_RPNStructure_simple;
350             r->u.simple = odr_malloc(o, sizeof(Z_Operand));
351             r->u.simple->which = Z_Operand_resultSetId;
352             r->u.simple->u.resultSetId = odr_malloc(o, len =
353                 strlen(q->u.setname) + 1);
354             memcpy(r->u.simple->u.resultSetId, q->u.setname, len);
355             return r;
356         case CCL_RPN_AND: case CCL_RPN_OR: case CCL_RPN_NOT:
357             r->which = Z_RPNStructure_complex;
358             r->u.complex = odr_malloc(o, sizeof(Z_Complex));
359             if (!(r->u.complex->s1 = rpn2rpn(o, q->u.p[0])) ||
360                 !(r->u.complex->s2 = rpn2rpn(o, q->u.p[1])))
361                     return 0;
362             r->u.complex->operator = odr_malloc(o, sizeof(Z_Operator));
363             r->u.complex->operator->which = op[q->kind];
364             r->u.complex->operator->u.and = "";
365             return r;
366         default:
367             gw_log(GW_LOG_FATAL, ZASS_TYPE, "Bad operator in RPN");
368             return 0;
369     }
370 }
371
372 const struct zass_searchent *zass_searchresult(ZASS a, int *complete)
373 {
374     static struct zass_searchent r;
375     Z_APDU *apdu;
376     Z_SearchResponse *res;
377
378     if (!(apdu = get_apdu(a, complete)))
379         return 0;
380     if (apdu->which != Z_APDU_searchResponse)
381     {
382         gw_log(GW_LOG_FATAL, ZASS_TYPE, "Expected searchresponse, got #%d",
383             apdu->which);
384         return 0;
385     }
386     res = apdu->u.searchResponse;
387     r.status = *res->searchStatus;
388     r.num = *res->resultCount;
389     r.status = res->resultSetStatus ? *res->resultSetStatus : 0;
390     r.errcode = -1;
391     *r.errstring = '\0';
392     if (res->records)
393     {
394         if (res->records->which != Z_Records_NSD)
395             gw_log(GW_LOG_WARN, ZASS_TYPE, "Unexpected record types in SchR");
396         else
397         {
398             oident *id;
399             Z_DiagRec *dr = res->records->u.nonSurrogateDiagnostic;
400
401 #ifdef Z_95
402             if (dr->which != Z_DiagRec_defaultFormat || 
403                 !(id = oid_getentbyoid 
404                        (dr->u.defaultFormat->diagnosticSetId)) ||
405                 id->class != CLASS_DIAGSET || 
406                 id->value != VAL_BIB1)
407                     gw_log(GW_LOG_WARN, ZASS_TYPE,
408                         "Missing or unknown diagset - ignoring error!");
409             else
410             {
411                 r.errcode = *dr->u.defaultFormat->condition;
412                 if (dr->u.defaultFormat->addinfo)
413                 {
414                     strncpy(r.errstring, dr->u.defaultFormat->addinfo, 512);
415                     r.errstring[511] = '\0';
416                 }
417             }
418 #else
419             if (!(id = oid_getentbyoid(dr->diagnosticSetId)) ||
420                 id->class != CLASS_DIAGSET || id->value != VAL_BIB1)
421                     gw_log(GW_LOG_WARN, ZASS_TYPE,
422                         "Missing or unknown diagset - ignoring error!");
423             else
424             {
425                 r.errcode = *dr->condition;
426                 if (dr->addinfo)
427                 {
428                     strncpy(r.errstring, dr->addinfo, 512);
429                     r.errstring[511] = '\0';
430                 }
431             }
432 #endif
433         }
434     }
435     return &r;
436 }
437
438 const struct zass_searchent *zass_search(ZASS a, struct ccl_rpn_node *query,
439     char *resname, char *databases, int *complete)
440 {
441     Z_Query q;
442     Z_RPNQuery rpnq;
443     Z_APDU *apdu;
444     Z_SearchRequest *req;
445     oident bib1;
446     int smallSetUpperBound = 0;
447     int largeSetLowerBound = 1;
448     int mediumSetPresentNumber = 0;
449     int replaceIndicator = 1;
450     char *datab[100];
451
452     apdu = zget_APDU (a->encode, Z_APDU_searchRequest);
453     req = apdu->u.searchRequest;
454
455     req->smallSetUpperBound = &smallSetUpperBound;
456     req->largeSetLowerBound = &largeSetLowerBound;
457     req->mediumSetPresentNumber = &mediumSetPresentNumber;
458     req->replaceIndicator = &replaceIndicator;
459     req->resultSetName = resname;
460     req->num_databaseNames = 0;
461     req->databaseNames = datab;
462     while (*databases && req->num_databaseNames < 100)
463     {
464         char *p = databases;
465         int more;
466
467         while (*p && !isspace(*p))
468             p++;
469         if (isspace(*p))
470             more = 1;
471         else
472             more = 0;
473         *p = '\0';
474         if (p - databases)
475         {
476             req->databaseNames[req->num_databaseNames] = odr_malloc(a->encode,
477                 (p - databases) + 1);
478             strcpy(req->databaseNames[req->num_databaseNames++], databases);
479         }
480         databases = p + more;
481     }
482     req->query = &q;
483     q.which = Z_Query_type_1;
484     q.u.type_1 = &rpnq;
485     bib1.proto = PROTO_Z3950;
486     bib1.class = CLASS_ATTSET;
487     bib1.value = VAL_BIB1;
488     rpnq.attributeSetId = oid_getoidbyent(&bib1);
489
490     if (complete)
491         *complete = 1;
492     if (!(rpnq.RPNStructure = rpn2rpn(a->encode, query)))
493         return 0;
494     if (send_apdu(a, apdu) < 0)
495         return 0;
496     if (complete)
497     {
498         *complete = 0;
499         return 0;
500     }
501     else
502         return zass_searchresult(a, complete);
503 }
504
505 /*
506  * Triple indirection - that's kinda heavy. We'll fix it later.
507  * There are worse things around, though. Like ZDist.
508  */
509 void get_diagrec(zass_record ***p, Z_DiagRec *r)
510 {
511     **p = malloc(sizeof(***p));
512     (**p)->next = 0;
513 #ifdef Z_95
514     (**p)->errstring[0] = '\0';
515     if (r->which == Z_DiagRec_defaultFormat)
516     {
517         (**p)->errcode = *r->u.defaultFormat->condition;
518         if (r->u.defaultFormat->addinfo)
519         {
520             strncpy((**p)->errstring, r->u.defaultFormat->addinfo, 200);
521             (**p)->errstring[200] = 0;
522         }
523     }
524     else
525         (**p)->errcode = -1;
526 #else
527     (**p)->errcode = *r->condition;
528     if (r->addinfo)
529     {
530         strncpy((**p)->errstring, r->addinfo, 200);
531         (**p)->errstring[200] = 0;
532     }
533     else
534         (**p)->errstring[0] = '\0';
535 #endif
536     (**p)->which = ZASS_REC_DIAG;
537     *p = &(**p)->next;
538 }
539
540 void get_responserecords(zass_record ***p, Z_NamePlusRecordList *recs)
541 {
542     int i;
543
544     for (i = 0; i < recs->num_records; i++)
545     {
546         Z_NamePlusRecord *record;
547
548         record = recs->records[i];
549         if (record->which == Z_NamePlusRecord_surrogateDiagnostic)
550             get_diagrec(p, record->u.surrogateDiagnostic);
551         else
552         {
553             Z_DatabaseRecord *r = record->u.databaseRecord;
554             oident *recform;
555
556             **p = malloc(sizeof(***p));
557             (**p)->next = 0;
558
559             if (!(recform = oid_getentbyoid(r->direct_reference)) ||
560                  recform->class != CLASS_RECSYN)
561             {
562                 gw_log(GW_LOG_WARN, ZASS_TYPE, "Unknown or bad record syntax");
563                 (**p)->which = ZASS_REC_UNKNOWN;
564             }
565             else
566                 switch (recform->value)
567                 {
568                     case VAL_USMARC: (**p)->which = ZASS_REC_USMARC; break;
569                     default:
570                         gw_log(GW_LOG_WARN, ZASS_TYPE, "Unknown recsyn");
571                         (**p)->which = ZASS_REC_UNKNOWN;
572                 }
573             if (r->which != ODR_EXTERNAL_octet)
574             {
575                 gw_log(GW_LOG_WARN, ZASS_TYPE, "Record wasn't octet-aligned");
576                 (**p)->record = 0;
577             }
578             else
579             {
580                 if (!((**p)->record = malloc(r->u.octet_aligned->len + 1)))
581                 {
582                     gw_log(GW_LOG_FATAL, ZASS_TYPE, "malloc");
583                     return;
584                 }
585                 memcpy((**p)->record, r->u.octet_aligned->buf,
586                     r->u.octet_aligned->len);
587                 (**p)->record[r->u.octet_aligned->len] = '\0';
588                 gw_log(ZASS_DEBUG, ZASS_TYPE, "Got a record of %d bytes",
589                     r->u.octet_aligned->len);
590             }
591             (*p) = &(**p)->next;
592         }
593     }
594 }
595
596 static void zass_records_free(zass_record *p)
597 {
598 }
599
600 static int send_present(ZASS a, char *name, int start, int num,
601     enum oid_value form)
602 {
603     Z_APDU *apdu;
604     Z_PresentRequest *req;
605 #if 0
606     oident recsyn;
607 #endif
608
609     apdu = zget_APDU (a->encode, Z_APDU_presentRequest);
610     req = apdu->u.presentRequest;
611
612     req->resultSetId = name;
613     req->resultSetStartPoint = &start;
614     req->numberOfRecordsRequested = &num;
615 #if 0
616     recsyn.proto = PROTO_Z3950;
617     recsyn.class = CLASS_RECSYN;
618     recsyn.value = form;
619     req->preferredRecordSyntax = oid_getoidbyent(&recsyn);
620 #else
621     req->preferredRecordSyntax = 0;
622 #endif
623     return send_apdu(a, apdu);
624 }
625
626 /*
627  * Note that 1== first record.
628  * TODO: make this baby operate in nonblocking mode, too.
629  */
630 const struct zass_presentent *zass_present(ZASS a, char *resname, int start,
631     int num, int *complete)
632 {
633     static struct zass_presentent r;
634     zass_record **rec = &r.records;
635
636     if (complete)
637         *complete = 1;
638     r.num = 0;
639     if (r.records)
640     {
641         zass_records_free(r.records);
642         r.records = 0;
643     }
644     do
645     {
646         Z_APDU *apdu;
647         Z_PresentResponse *res;
648
649         gw_log(ZASS_DEBUG, ZASS_TYPE,
650             "Fetching %d records from # %d", num - r.num, start);
651         if (send_present(a, resname, start, num - r.num, VAL_USMARC) < 0)
652             return 0;
653         if (!(apdu = get_apdu(a, complete)))
654         {
655             if (complete)
656                 *complete = 1;
657             return 0;
658         }
659         if (apdu->which != Z_APDU_presentResponse)
660         {
661             gw_log(GW_LOG_FATAL, ZASS_TYPE, "Expected presentresponse, got #%d",
662                 apdu->which);
663             return 0;
664         }
665         res = apdu->u.presentResponse;
666         r.presentstatus = *res->presentStatus;
667         r.num += *res->numberOfRecordsReturned;
668         if (*res->numberOfRecordsReturned == 0)
669         {
670             gw_log(GW_LOG_WARN, ZASS_TYPE, "Got 0 records from target");
671             return 0;
672         }
673         r.nextpos = *res->nextResultSetPosition;
674         start = r.nextpos;
675         switch (res->records->which)
676         {
677             case Z_Records_DBOSD:
678                 get_responserecords(&rec,
679                     res->records->u.databaseOrSurDiagnostics);
680                 break;
681             case Z_Records_NSD:
682                 get_diagrec(&rec, res->records->u.nonSurrogateDiagnostic);
683                 break;
684             default:
685                 gw_log(GW_LOG_WARN, ZASS_TYPE, "Bad tag in response rec.");
686                 return 0;
687         }
688     }
689     while (num - r.num && start);
690     return &r;
691 }
692
693 const struct zass_presentent *zass_presentresult(ZASS a, int *complete)
694 {
695     *complete = 1;
696     return 0;
697 }