Minor adjustments and reformat of dummy MARC
[yaz-moved-to-github.git] / ztest / ztest.c
1 /* This file is part of the YAZ toolkit.
2  * Copyright (C) 1995-2010 Index Data
3  * See the file LICENSE for details.
4  */
5
6 /** \file
7  * \brief yaz-ztest Generic Frontend Server
8  */
9
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <ctype.h>
13 #if HAVE_UNISTD_H
14 #include <unistd.h>
15 #endif
16
17 #include <yaz/log.h>
18 #include <yaz/backend.h>
19 #include <yaz/ill.h>
20 #include <yaz/diagbib1.h>
21
22 #include "ztest.h"
23
24 static int log_level=0;
25 static int log_level_set=0;
26
27 struct result_set {
28     char *name;
29     char *db;
30     Odr_int hits;
31     struct result_set *next;
32 };
33
34 struct session_handle {
35     struct result_set *result_sets;
36 };
37
38 int ztest_search(void *handle, bend_search_rr *rr);
39 int ztest_sort(void *handle, bend_sort_rr *rr);
40 int ztest_present(void *handle, bend_present_rr *rr);
41 int ztest_esrequest(void *handle, bend_esrequest_rr *rr);
42 int ztest_delete(void *handle, bend_delete_rr *rr);
43
44 static struct result_set *get_set(struct session_handle *sh, const char *name)
45 {
46     struct result_set *set = sh->result_sets;
47     for (; set; set = set->next)
48         if (!strcmp(name, set->name))
49             return set;
50     return 0;
51 }
52
53 static void remove_sets(struct session_handle *sh)
54 {
55     struct result_set *set = sh->result_sets;
56     while (set)
57     {
58         struct result_set *set_next = set->next;
59         xfree(set->name);
60         xfree(set->db);
61         xfree(set);
62         set = set_next;
63     }
64     sh->result_sets = 0;
65 }
66
67 /** \brief use term value as hit count 
68     \param s RPN structure
69     \return >= 0: search term number or -1: not found
70    
71     Traverse RPN tree 'in order' and use term value as hit count.
72     Only terms  that looks a numeric is used.. Returns -1 if
73     no sub tree has a hit count term
74 */
75 static Odr_int get_term_hit(Z_RPNStructure *s)
76 {
77     Odr_int h = -1;
78     switch(s->which)
79     {
80     case Z_RPNStructure_simple:
81         if (s->u.simple->which == Z_Operand_APT)
82         {
83             Z_AttributesPlusTerm *apt = s->u.simple->u.attributesPlusTerm;
84             if (apt->term->which == Z_Term_general)
85             {
86                 Odr_oct *oct = apt->term->u.general;
87                 if (oct->len > 0 && oct->buf[0] >= '0' && oct->buf[0] <= '9')
88                 {
89                     WRBUF hits_str = wrbuf_alloc();
90                     wrbuf_write(hits_str, (const char *) oct->buf, oct->len);
91                     h = odr_atoi(wrbuf_cstr(hits_str));
92                     wrbuf_destroy(hits_str);
93                 }
94             }
95         }
96         break;
97     case Z_RPNStructure_complex:
98         h = get_term_hit(s->u.complex->s1);
99         if (h == -1)
100             h = get_term_hit(s->u.complex->s2);
101         break;
102     }
103     return h;
104 }
105
106 /** \brief gets hit count for numeric terms in RPN queries
107     \param q RPN Query
108     \return number of hits (random or number for term)
109     
110     This is just for testing.. A real database of course uses
111     the content of a database to establish a value.. In our case, we
112     have a way to trigger a certain hit count. Good for testing of
113     client applications etc
114 */
115 static Odr_int get_hit_count(Z_Query *q)
116 {
117     Odr_int h = -1;
118     if (q->which == Z_Query_type_1 || q->which == Z_Query_type_101)
119         h = get_term_hit(q->u.type_1->RPNStructure);
120     if (h == -1)
121         h = rand() % 24;
122     return h;
123 }
124
125 /** \brief checks if it's a dummy Slow database
126     \param basename database name to check
127     \param association backend association (or NULL if not available)
128     \retval 1 is slow database
129     \retval 0 is not a slow database
130
131     The Slow database is for testing.. It allows us to simulate
132     a slow server...
133 */
134 static int check_slow(const char *basename, bend_association association)
135 {
136     if (strncmp(basename, "Slow", 4) == 0)
137     {
138 #if HAVE_UNISTD_H
139         int i, w = 3;
140         if (basename[4])
141             sscanf(basename+4, "%d", &w);
142         /* wait up to 3 seconds and check if connection is still alive */
143         for (i = 0; i < w; i++)
144         {
145             if (association && !bend_assoc_is_alive(association))
146             {
147                 yaz_log(YLOG_LOG, "search aborted");
148                 break;
149             }
150             sleep(1);
151         }
152 #endif
153         return 1;
154     }
155     return 0;
156 }
157
158 int ztest_search(void *handle, bend_search_rr *rr)
159 {
160     struct session_handle *sh = (struct session_handle*) handle;
161     struct result_set *new_set;
162
163     if (rr->num_bases != 1)
164     {
165         rr->errcode = YAZ_BIB1_COMBI_OF_SPECIFIED_DATABASES_UNSUPP;
166         return 0;
167     }
168     /* Allow Default, db.* and Slow */
169     if (!yaz_matchstr(rr->basenames[0], "Default"))
170         ;  /* Default is OK in our test */
171     else if (!strncmp(rr->basenames[0], "db", 2))
172         ;  /* db.* is OK in our test */
173     else if (check_slow(rr->basenames[0], rr->association))
174     {
175         rr->estimated_hit_count = 1;
176     }
177     else
178     {
179         rr->errcode = YAZ_BIB1_DATABASE_UNAVAILABLE;
180         rr->errstring = rr->basenames[0];
181         return 0;
182     }
183
184     new_set = get_set(sh, rr->setname);    
185     if (new_set)
186     {
187         if (!rr->replace_set)
188         {
189             rr->errcode = YAZ_BIB1_RESULT_SET_EXISTS_AND_REPLACE_INDICATOR_OFF;
190             return 0;
191         }
192         xfree(new_set->db);
193     }
194     else
195     {
196         new_set = xmalloc(sizeof(*new_set));
197         new_set->next = sh->result_sets;
198         sh->result_sets = new_set;
199         new_set->name = xstrdup(rr->setname);
200     }
201
202     if (rr->extra_args)
203     {
204         Z_SRW_extra_arg *a;
205         WRBUF response_xml = wrbuf_alloc();
206         wrbuf_puts(response_xml, "<extra>");
207         for (a = rr->extra_args; a; a = a->next)
208         {
209             wrbuf_puts(response_xml, "<extra name=\"");
210             wrbuf_xmlputs(response_xml, a->name);
211             wrbuf_puts(response_xml, "\"");
212             if (a->value)
213             {
214                 wrbuf_puts(response_xml, " value=\"");
215                 wrbuf_xmlputs(response_xml, a->value);
216                 wrbuf_puts(response_xml, "\"");
217             }
218             wrbuf_puts(response_xml, "/>");
219         }
220         wrbuf_puts(response_xml, "</extra>");
221         rr->extra_response_data =
222             odr_strdup(rr->stream, wrbuf_cstr(response_xml));
223         wrbuf_destroy(response_xml);
224     }
225     rr->hits = get_hit_count(rr->query);
226
227     new_set->hits = rr->hits;
228     new_set->db = xstrdup(rr->basenames[0]);
229     
230     return 0;
231 }
232
233
234 /* this huge function handles extended services */
235 int ztest_esrequest(void *handle, bend_esrequest_rr *rr)
236 {
237     /* user-defined handle - created in bend_init */
238     int *counter = (int*) handle;  
239
240     yaz_log(log_level, "ESRequest no %d", *counter);
241
242     (*counter)++;
243
244     if (rr->esr->packageName)
245         yaz_log(log_level, "packagename: %s", rr->esr->packageName);
246     yaz_log(log_level, "Waitaction: " ODR_INT_PRINTF, *rr->esr->waitAction);
247
248
249     yaz_log(log_level, "function: " ODR_INT_PRINTF, *rr->esr->function);
250
251     if (!rr->esr->taskSpecificParameters)
252     {
253         yaz_log(log_level, "No task specific parameters");
254     }
255     else if (rr->esr->taskSpecificParameters->which == Z_External_itemOrder)
256     {
257         Z_ItemOrder *it = rr->esr->taskSpecificParameters->u.itemOrder;
258         yaz_log(log_level, "Received ItemOrder");
259         if (it->which == Z_IOItemOrder_esRequest)
260         {
261             Z_IORequest *ir = it->u.esRequest;
262             Z_IOOriginPartToKeep *k = ir->toKeep;
263             Z_IOOriginPartNotToKeep *n = ir->notToKeep;
264             const char *xml_in_response = 0;
265             
266             if (k && k->contact)
267             {
268                 if (k->contact->name)
269                     yaz_log(log_level, "contact name %s", k->contact->name);
270                 if (k->contact->phone)
271                     yaz_log(log_level, "contact phone %s", k->contact->phone);
272                 if (k->contact->email)
273                     yaz_log(log_level, "contact email %s", k->contact->email);
274             }
275             if (k->addlBilling)
276             {
277                 yaz_log(log_level, "Billing info (not shown)");
278             }
279             
280             if (n->resultSetItem)
281             {
282                 yaz_log(log_level, "resultsetItem");
283                 yaz_log(log_level, "setId: %s", n->resultSetItem->resultSetId);
284                 yaz_log(log_level, "item: " ODR_INT_PRINTF, *n->resultSetItem->item);
285             }
286             if (n->itemRequest)
287             {
288                 Z_External *r = (Z_External*) n->itemRequest;
289                 ILL_ItemRequest *item_req = 0;
290                 ILL_APDU *ill_apdu = 0;
291                 if (r->direct_reference)
292                 {
293                     char oid_name_str[OID_STR_MAX];
294                     oid_class oclass;
295                     const char *oid_name = 
296                         yaz_oid_to_string_buf(r->direct_reference,
297                                               &oclass, oid_name_str);
298                     if (oid_name)
299                         yaz_log(log_level, "OID %s", oid_name);
300                     if (!oid_oidcmp(r->direct_reference, yaz_oid_recsyn_xml))
301                     {
302                         yaz_log(log_level, "ILL XML request");
303                         if (r->which == Z_External_octet)
304                             yaz_log(log_level, "%.*s",
305                                     r->u.octet_aligned->len,
306                                     r->u.octet_aligned->buf); 
307                         xml_in_response = "<dummy>x</dummy>";
308                     }
309                     if (!oid_oidcmp(r->direct_reference, 
310                                     yaz_oid_general_isoill_1))
311                     {
312                         yaz_log(log_level, "Decode ItemRequest begin");
313                         if (r->which == ODR_EXTERNAL_single)
314                         {
315                             odr_setbuf(rr->decode,
316                                        (char *) r->u.single_ASN1_type->buf,
317                                        r->u.single_ASN1_type->len, 0);
318                             
319                             if (!ill_ItemRequest(rr->decode, &item_req, 0, 0))
320                             {
321                                 yaz_log(log_level,
322                                         "Couldn't decode ItemRequest %s near %ld",
323                                         odr_errmsg(odr_geterror(rr->decode)),
324                                         (long) odr_offset(rr->decode));
325                             }
326                             else
327                                 yaz_log(log_level, "Decode ItemRequest OK");
328                             if (rr->print)
329                             {
330                                 ill_ItemRequest(rr->print, &item_req, 0,
331                                                 "ItemRequest");
332                                 odr_reset(rr->print);
333                             }
334                         }
335                         if (!item_req && r->which == ODR_EXTERNAL_single)
336                         {
337                             yaz_log(log_level, "Decode ILL APDU begin");
338                             odr_setbuf(rr->decode,
339                                        (char*) r->u.single_ASN1_type->buf,
340                                        r->u.single_ASN1_type->len, 0);
341                             
342                             if (!ill_APDU(rr->decode, &ill_apdu, 0, 0))
343                             {
344                                 yaz_log(log_level,
345                                         "Couldn't decode ILL APDU %s near %ld",
346                                         odr_errmsg(odr_geterror(rr->decode)),
347                                         (long) odr_offset(rr->decode));
348                                 yaz_log(log_level, "PDU dump:");
349                                 odr_dumpBER(yaz_log_file(),
350                                             (char *) r->u.single_ASN1_type->buf,
351                                             r->u.single_ASN1_type->len);
352                             }
353                             else
354                                 yaz_log(log_level, "Decode ILL APDU OK");
355                             if (rr->print)
356                             {
357                                 ill_APDU(rr->print, &ill_apdu, 0,
358                                          "ILL APDU");
359                                 odr_reset(rr->print);
360                             }
361                         }
362                     }
363                 }
364                 if (item_req)
365                 {
366                     yaz_log(log_level, "ILL protocol version = "
367                             ODR_INT_PRINTF,
368                             *item_req->protocol_version_num);
369                 }
370             }
371             if (k)
372             {
373
374                 Z_External *ext = (Z_External *)
375                     odr_malloc(rr->stream, sizeof(*ext));
376                 Z_IUOriginPartToKeep *keep = (Z_IUOriginPartToKeep *)
377                     odr_malloc(rr->stream, sizeof(*keep));
378                 Z_IOTargetPart *targetPart = (Z_IOTargetPart *)
379                     odr_malloc(rr->stream, sizeof(*targetPart));
380
381                 rr->taskPackage = (Z_TaskPackage *)
382                     odr_malloc(rr->stream, sizeof(*rr->taskPackage));
383                 rr->taskPackage->packageType =
384                     odr_oiddup(rr->stream, rr->esr->packageType);
385                 rr->taskPackage->packageName = 0;
386                 rr->taskPackage->userId = 0;
387                 rr->taskPackage->retentionTime = 0;
388                 rr->taskPackage->permissions = 0;
389                 rr->taskPackage->description = 0;
390                 rr->taskPackage->targetReference = (Odr_oct *)
391                     odr_malloc(rr->stream, sizeof(Odr_oct));
392                 rr->taskPackage->targetReference->buf =
393                     (unsigned char *) odr_strdup(rr->stream, "911");
394                 rr->taskPackage->targetReference->len =
395                     rr->taskPackage->targetReference->size =
396                     strlen((char *) (rr->taskPackage->targetReference->buf));
397                 rr->taskPackage->creationDateTime = 0;
398                 rr->taskPackage->taskStatus = odr_intdup(rr->stream, 0);
399                 rr->taskPackage->packageDiagnostics = 0;
400                 rr->taskPackage->taskSpecificParameters = ext;
401
402                 ext->direct_reference =
403                     odr_oiddup(rr->stream, rr->esr->packageType);
404                 ext->indirect_reference = 0;
405                 ext->descriptor = 0;
406                 ext->which = Z_External_itemOrder;
407                 ext->u.itemOrder = (Z_ItemOrder *)
408                     odr_malloc(rr->stream, sizeof(*ext->u.update));
409                 ext->u.itemOrder->which = Z_IOItemOrder_taskPackage;
410                 ext->u.itemOrder->u.taskPackage =  (Z_IOTaskPackage *)
411                     odr_malloc(rr->stream, sizeof(Z_IOTaskPackage));
412                 ext->u.itemOrder->u.taskPackage->originPart = k;
413                 ext->u.itemOrder->u.taskPackage->targetPart = targetPart;
414
415                 if (xml_in_response)
416                     targetPart->itemRequest =
417                         z_ext_record_xml(rr->stream, xml_in_response,
418                                          strlen(xml_in_response));
419                 else
420                     targetPart->itemRequest = 0;
421                     
422                 targetPart->statusOrErrorReport = 0;
423                 targetPart->auxiliaryStatus = 0;
424             }
425         }
426     }
427     else if (rr->esr->taskSpecificParameters->which == Z_External_update)
428     {
429         Z_IUUpdate *up = rr->esr->taskSpecificParameters->u.update;
430         yaz_log(log_level, "Received DB Update");
431         if (up->which == Z_IUUpdate_esRequest)
432         {
433             Z_IUUpdateEsRequest *esRequest = up->u.esRequest;
434             Z_IUOriginPartToKeep *toKeep = esRequest->toKeep;
435             Z_IUSuppliedRecords *notToKeep = esRequest->notToKeep;
436             
437             yaz_log(log_level, "action");
438             if (toKeep->action)
439             {
440                 switch (*toKeep->action)
441                 {
442                 case Z_IUOriginPartToKeep_recordInsert:
443                     yaz_log(log_level, " recordInsert");
444                     break;
445                 case Z_IUOriginPartToKeep_recordReplace:
446                     yaz_log(log_level, " recordReplace");
447                     break;
448                 case Z_IUOriginPartToKeep_recordDelete:
449                     yaz_log(log_level, " recordDelete");
450                     break;
451                 case Z_IUOriginPartToKeep_elementUpdate:
452                     yaz_log(log_level, " elementUpdate");
453                     break;
454                 case Z_IUOriginPartToKeep_specialUpdate:
455                     yaz_log(log_level, " specialUpdate");
456                     break;
457                 default:
458                     yaz_log(log_level, " unknown (" ODR_INT_PRINTF ")",
459                             *toKeep->action);
460                 }
461             }
462             if (toKeep->databaseName)
463             {
464                 yaz_log(log_level, "database: %s", toKeep->databaseName);
465                 if (!strcmp(toKeep->databaseName, "fault"))
466                 {
467                     rr->errcode = YAZ_BIB1_DATABASE_UNAVAILABLE;
468                     rr->errstring = toKeep->databaseName;
469                 }
470                 if (!strcmp(toKeep->databaseName, "accept"))
471                     rr->errcode = -1;
472             }
473             if (toKeep)
474             {
475                 Z_External *ext = (Z_External *)
476                     odr_malloc(rr->stream, sizeof(*ext));
477                 Z_IUOriginPartToKeep *keep = (Z_IUOriginPartToKeep *)
478                     odr_malloc(rr->stream, sizeof(*keep));
479                 Z_IUTargetPart *targetPart = (Z_IUTargetPart *)
480                     odr_malloc(rr->stream, sizeof(*targetPart));
481
482                 rr->taskPackage = (Z_TaskPackage *)
483                     odr_malloc(rr->stream, sizeof(*rr->taskPackage));
484                 rr->taskPackage->packageType =
485                     odr_oiddup(rr->stream, rr->esr->packageType);
486                 rr->taskPackage->packageName = 0;
487                 rr->taskPackage->userId = 0;
488                 rr->taskPackage->retentionTime = 0;
489                 rr->taskPackage->permissions = 0;
490                 rr->taskPackage->description = 0;
491                 rr->taskPackage->targetReference = (Odr_oct *)
492                     odr_malloc(rr->stream, sizeof(Odr_oct));
493                 rr->taskPackage->targetReference->buf =
494                     (unsigned char *) odr_strdup(rr->stream, "123");
495                 rr->taskPackage->targetReference->len =
496                     rr->taskPackage->targetReference->size =
497                     strlen((char *) (rr->taskPackage->targetReference->buf));
498                 rr->taskPackage->creationDateTime = 0;
499                 rr->taskPackage->taskStatus = odr_intdup(rr->stream, 0);
500                 rr->taskPackage->packageDiagnostics = 0;
501                 rr->taskPackage->taskSpecificParameters = ext;
502
503                 ext->direct_reference =
504                     odr_oiddup(rr->stream, rr->esr->packageType);
505                 ext->indirect_reference = 0;
506                 ext->descriptor = 0;
507                 ext->which = Z_External_update;
508                 ext->u.update = (Z_IUUpdate *)
509                     odr_malloc(rr->stream, sizeof(*ext->u.update));
510                 ext->u.update->which = Z_IUUpdate_taskPackage;
511                 ext->u.update->u.taskPackage =  (Z_IUUpdateTaskPackage *)
512                     odr_malloc(rr->stream, sizeof(Z_IUUpdateTaskPackage));
513                 ext->u.update->u.taskPackage->originPart = keep;
514                 ext->u.update->u.taskPackage->targetPart = targetPart;
515
516                 keep->action = odr_intdup(rr->stream, *toKeep->action);
517                 keep->databaseName =
518                     odr_strdup(rr->stream, toKeep->databaseName);
519                 keep->schema = 0;
520                 keep->elementSetName = 0;
521                 keep->actionQualifier = 0;
522
523                 targetPart->updateStatus = odr_intdup(rr->stream, 1);
524                 targetPart->num_globalDiagnostics = 0;
525                 targetPart->globalDiagnostics = (Z_DiagRec **) odr_nullval();
526                 targetPart->num_taskPackageRecords = 1;
527                 targetPart->taskPackageRecords = 
528                     (Z_IUTaskPackageRecordStructure **)
529                     odr_malloc(rr->stream,
530                                sizeof(Z_IUTaskPackageRecordStructure *));
531                 targetPart->taskPackageRecords[0] =
532                     (Z_IUTaskPackageRecordStructure *)
533                     odr_malloc(rr->stream,
534                                sizeof(Z_IUTaskPackageRecordStructure));
535                 
536                 targetPart->taskPackageRecords[0]->which =
537                     Z_IUTaskPackageRecordStructure_record;
538                 targetPart->taskPackageRecords[0]->u.record = 
539                     z_ext_record_sutrs(rr->stream, "test", 4);
540                 targetPart->taskPackageRecords[0]->correlationInfo = 0; 
541                 targetPart->taskPackageRecords[0]->recordStatus =
542                     odr_intdup(rr->stream,
543                                Z_IUTaskPackageRecordStructure_success);  
544                 targetPart->taskPackageRecords[0]->num_supplementalDiagnostics
545                     = 0;
546
547                 targetPart->taskPackageRecords[0]->supplementalDiagnostics = 0;
548             }
549             if (notToKeep)
550             {
551                 int i;
552                 for (i = 0; i < notToKeep->num; i++)
553                 {
554                     Z_External *rec = notToKeep->elements[i]->record;
555
556                     if (rec->direct_reference)
557                     {
558                         char oid_name_str[OID_STR_MAX];
559                         const char *oid_name 
560                             = oid_name = yaz_oid_to_string_buf(
561                                 rec->direct_reference, 0,
562                                 oid_name_str);
563                         if (oid_name)
564                             yaz_log(log_level, "record %d type %s", i,
565                                     oid_name);
566                     }
567                     switch (rec->which)
568                     {
569                     case Z_External_sutrs:
570                         if (rec->u.octet_aligned->len > 170)
571                             yaz_log(log_level, "%d bytes:\n%.168s ...",
572                                     rec->u.sutrs->len,
573                                     rec->u.sutrs->buf);
574                         else
575                             yaz_log(log_level, "%d bytes:\n%s",
576                                     rec->u.sutrs->len,
577                                     rec->u.sutrs->buf);
578                         break;
579                     case Z_External_octet        :
580                         if (rec->u.octet_aligned->len > 170)
581                             yaz_log(log_level, "%d bytes:\n%.168s ...",
582                                     rec->u.octet_aligned->len,
583                                     rec->u.octet_aligned->buf);
584                         else
585                             yaz_log(log_level, "%d bytes\n%s",
586                                     rec->u.octet_aligned->len,
587                                     rec->u.octet_aligned->buf);
588                     }
589                 }
590             }
591         }
592     }
593     return 0;
594 }
595
596 /* result set delete */
597 int ztest_delete(void *handle, bend_delete_rr *rr)
598 {
599     if (rr->num_setnames == 1 && !strcmp(rr->setnames[0], "1"))
600         rr->delete_status = Z_DeleteStatus_success;
601     else
602         rr->delete_status = Z_DeleteStatus_resultSetDidNotExist;
603     return 0;
604 }
605
606 /* Our sort handler really doesn't sort... */
607 int ztest_sort(void *handle, bend_sort_rr *rr)
608 {
609     rr->errcode = 0;
610     rr->sort_status = Z_SortResponse_success;
611     return 0;
612 }
613
614
615 /* present request handler */
616 int ztest_present(void *handle, bend_present_rr *rr)
617 {
618     return 0;
619 }
620
621 /* retrieval of a single record (present, and piggy back search) */
622 int ztest_fetch(void *handle, bend_fetch_rr *r)
623 {
624     struct session_handle *sh = (struct session_handle*) handle;
625     char *cp;
626     const Odr_oid *oid = r->request_format;
627     struct result_set *set = get_set(sh, r->setname);    
628
629     if (!set)
630     {
631         r->errcode = YAZ_BIB1_SPECIFIED_RESULT_SET_DOES_NOT_EXIST;
632         r->errstring = odr_strdup(r->stream, r->setname);
633         return 0;
634     }
635     r->last_in_set = 0;
636     r->basename = set->db;
637     r->output_format = r->request_format;
638
639     if (r->number < 1 || r->number > set->hits)
640     {
641         r->errcode = YAZ_BIB1_PRESENT_REQUEST_OUT_OF_RANGE;
642         return 0;
643     }
644     if (!oid || yaz_oid_is_iso2709(oid))
645     {
646         cp = dummy_marc_record(r->number, r->stream);
647         if (!cp)
648         {
649             r->errcode = YAZ_BIB1_SYSTEM_ERROR_IN_PRESENTING_RECORDS;
650             r->surrogate_flag = 1;
651             return 0;
652         }
653         else
654         {
655             r->len = strlen(cp);
656             r->record = cp;
657             r->output_format = odr_oiddup(r->stream, yaz_oid_recsyn_usmarc);
658         }
659     }
660     else if (!oid_oidcmp(oid, yaz_oid_recsyn_opac))
661     {
662         cp = dummy_marc_record(r->number, r->stream);
663         if (!cp)
664         {
665             r->errcode = YAZ_BIB1_SYSTEM_ERROR_IN_PRESENTING_RECORDS;
666             r->surrogate_flag = 1;
667             return 0;
668         }
669         r->record = (char *) dummy_opac(r->number, r->stream, cp);
670         r->len = -1;
671     }
672     else if (!oid_oidcmp(oid, yaz_oid_recsyn_sutrs))
673     {
674         /* this section returns a small record */
675         char buf[100];
676         
677         sprintf(buf, "This is dummy SUTRS record number %d\n", r->number);
678
679         r->len = strlen(buf);
680         r->record = (char *) odr_malloc(r->stream, r->len+1);
681         strcpy(r->record, buf);
682     }
683     else if (!oid_oidcmp(oid, yaz_oid_recsyn_grs_1))
684     {
685         r->len = -1;
686         r->record = (char*) dummy_grs_record(r->number, r->stream);
687         if (!r->record)
688         {
689             r->errcode = YAZ_BIB1_SYSTEM_ERROR_IN_PRESENTING_RECORDS;
690             r->surrogate_flag = 1;
691             return 0;
692         }
693     }
694     else if (!oid_oidcmp(oid, yaz_oid_recsyn_postscript))
695     {
696         char fname[20];
697         FILE *f;
698         long size;
699
700         sprintf(fname, "part.%d.ps", r->number);
701         f = fopen(fname, "rb");
702         if (!f)
703         {
704             r->errcode = YAZ_BIB1_SYSTEM_ERROR_IN_PRESENTING_RECORDS;
705             r->surrogate_flag = 1;
706             return 0;
707         }
708         fseek(f, 0L, SEEK_END);
709         size = ftell(f);
710         if (size <= 0 || size >= 5000000)
711         {
712             r->errcode = YAZ_BIB1_SYSTEM_ERROR_IN_PRESENTING_RECORDS;
713             r->surrogate_flag = 1;
714         }
715         fseek(f, 0L, SEEK_SET);
716         r->record = (char*) odr_malloc(r->stream, size);
717         r->len = size;
718         if (fread(r->record, size, 1, f) != 1)
719         {
720             r->errcode = YAZ_BIB1_SYSTEM_ERROR_IN_PRESENTING_RECORDS;
721             r->surrogate_flag = 1;
722         }
723         fclose(f);
724     }
725     else if (!oid_oidcmp(oid, yaz_oid_recsyn_xml))
726     {
727         if ((cp = dummy_xml_record(r->number, r->stream)))
728         {
729             r->len = strlen(cp);
730             r->record = cp;
731         }
732         else 
733         {
734             r->errcode = YAZ_BIB1_SYSTEM_ERROR_IN_PRESENTING_RECORDS;
735             r->surrogate_flag = 1;
736             return 0;
737         }
738     }
739     else
740     {
741         char buf[OID_STR_MAX];
742         r->errcode = YAZ_BIB1_RECORD_SYNTAX_UNSUPP;
743         r->errstring = odr_strdup(r->stream, oid_oid_to_dotstring(oid, buf));
744         return 0;
745     }
746     r->errcode = 0;
747     return 0;
748 }
749
750 /*
751  * silly dummy-scan what reads words from a file.
752  */
753 int ztest_scan(void *handle, bend_scan_rr *q)
754 {
755     static FILE *f = 0;
756     static struct scan_entry list[200];
757     static char entries[200][80];
758     int hits[200];
759     char term[80], *p;
760     int i, pos;
761     int term_position_req = q->term_position;
762     int num_entries_req = q->num_entries;
763
764     /* Throw Database unavailable if other than Default or Slow */
765     if (!yaz_matchstr(q->basenames[0], "Default"))
766         ;  /* Default is OK in our test */
767     else if (check_slow(q->basenames[0], 0 /* no assoc for scan */))
768         ;
769     else
770     {
771         q->errcode = YAZ_BIB1_DATABASE_UNAVAILABLE;
772         q->errstring = q->basenames[0];
773         return 0;
774     }
775
776     q->errcode = 0;
777     q->errstring = 0;
778     q->entries = list;
779     q->status = BEND_SCAN_SUCCESS;
780     if (!f && !(f = fopen("dummy-words", "r")))
781     {
782         perror("dummy-words");
783         exit(1);
784     }
785     if (q->num_entries > 200)
786     {
787         q->errcode = YAZ_BIB1_RESOURCES_EXHAUSTED_NO_RESULTS_AVAILABLE;
788         return 0;
789     }
790     if (q->term)
791     {
792         int len;
793         if (q->term->term->which != Z_Term_general)
794         {
795             q->errcode = YAZ_BIB1_TERM_TYPE_UNSUPP;
796             return 0;
797         }
798         if (*q->step_size != 0)
799         {
800             q->errcode = YAZ_BIB1_ONLY_ZERO_STEP_SIZE_SUPPORTED_FOR_SCAN;
801             return 0;
802         }
803         len = q->term->term->u.general->len;
804         if (len >= (int ) sizeof(term))
805             len = sizeof(term)-1;
806         memcpy(term, q->term->term->u.general->buf, len);
807         term[len] = '\0';
808     }
809     else if (q->scanClause)
810     {
811         strncpy(term, q->scanClause, sizeof(term)-1);
812         term[sizeof(term)-1] = '\0';
813     }
814     else
815         strcpy(term, "0");
816
817     for (p = term; *p; p++)
818         if (islower(*(unsigned char *) p))
819             *p = toupper(*p);
820
821     fseek(f, 0, SEEK_SET);
822     q->num_entries = 0;
823
824     for (i = 0, pos = 0; fscanf(f, " %79[^:]:%d", entries[pos], &hits[pos]) == 2;
825          i++, pos < 199 ? pos++ : (pos = 0))
826     {
827         if (!q->num_entries && strcmp(entries[pos], term) >= 0) /* s-point fnd */
828         {
829             if ((q->term_position = term_position_req) > i + 1)
830             {
831                 q->term_position = i + 1;
832                 q->status = BEND_SCAN_PARTIAL;
833             }
834             for (; q->num_entries < q->term_position; q->num_entries++)
835             {
836                 int po;
837
838                 po = pos - q->term_position + q->num_entries+1; /* find pos */
839                 if (po < 0)
840                     po += 200;
841
842                 if (!strcmp(term, "SD") && q->num_entries == 2)
843                 {
844                     list[q->num_entries].term = entries[pos];
845                     list[q->num_entries].occurrences = -1;
846                     list[q->num_entries].errcode =
847                         YAZ_BIB1_SCAN_UNSUPP_VALUE_OF_POSITION_IN_RESPONSE;
848                     list[q->num_entries].errstring = "SD for Scan Term";
849                 }
850                 else
851                 {
852                     list[q->num_entries].term = entries[po];
853                     list[q->num_entries].occurrences = hits[po];
854                 }
855             }
856         }
857         else if (q->num_entries)
858         {
859             list[q->num_entries].term = entries[pos];
860             list[q->num_entries].occurrences = hits[pos];
861             q->num_entries++;
862         }
863         if (q->num_entries >= num_entries_req)
864             break;
865     }
866     if (feof(f))
867         q->status = BEND_SCAN_PARTIAL;
868     return 0;
869 }
870
871 int ztest_explain(void *handle, bend_explain_rr *rr)
872 {
873     if (rr->database && !strcmp(rr->database, "Default"))
874     {
875         rr->explain_buf = "<explain>\n"
876             "\t<serverInfo>\n"
877             "\t\t<host>localhost</host>\n"
878             "\t\t<port>210</port>\n"
879             "\t</serverInfo>\n"
880             "</explain>\n";
881     }
882     return 0;
883 }
884
885 int ztest_update(void *handle, bend_update_rr *rr)
886 {
887     rr->operation_status = "success";
888     return 0;
889 }
890
891 bend_initresult *bend_init(bend_initrequest *q)
892 {
893     bend_initresult *r = (bend_initresult *)
894         odr_malloc(q->stream, sizeof(*r));
895     struct session_handle *sh = xmalloc(sizeof(*sh));
896
897     sh->result_sets = 0;
898
899     if (!log_level_set)
900     {
901         log_level=yaz_log_module_level("ztest");
902         log_level_set=1;
903     }
904
905     r->errcode = 0;
906     r->errstring = 0;
907     r->handle = sh;                         /* tell GFS about our handle */
908     q->bend_sort = ztest_sort;              /* register sort handler */
909     q->bend_search = ztest_search;          /* register search handler */
910     q->bend_present = ztest_present;        /* register present handle */
911     q->bend_esrequest = ztest_esrequest;
912     q->bend_delete = ztest_delete;
913     q->bend_fetch = ztest_fetch;
914     q->bend_scan = ztest_scan;
915 #if 0
916     q->bend_explain = ztest_explain;
917 #endif
918     q->bend_srw_scan = ztest_scan;
919     q->bend_srw_update = ztest_update;
920
921     q->query_charset = "ISO-8859-1";
922     q->records_in_same_charset = 0;
923
924     return r;
925 }
926
927 void bend_close(void *handle)
928 {
929     struct session_handle *sh = (struct session_handle*) handle;
930     remove_sets(sh);
931     xfree(sh);              /* release our session */
932     return;
933 }
934
935 int main(int argc, char **argv)
936 {
937     return statserv_main(argc, argv, bend_init, bend_close);
938 }
939 /*
940  * Local variables:
941  * c-basic-offset: 4
942  * c-file-style: "Stroustrup"
943  * indent-tabs-mode: nil
944  * End:
945  * vim: shiftwidth=4 tabstop=8 expandtab
946  */
947