50edbc798be11ab19443fcfd4f4e20ae32c16045
[yaz-moved-to-github.git] / ztest / ztest.c
1 /*
2  * Copyright (c) 1995-2001, Index Data.
3  * See the file LICENSE for details.
4  *
5  * NT Service interface by
6  *    Chas Woodfield, Fretwell Downing Datasystems.
7  *
8  * $Log: ztest.c,v $
9  * Revision 1.41  2001-04-05 13:08:48  adam
10  * New configure options: --enable-module.
11  *
12  * Revision 1.40  2001/03/25 21:55:13  adam
13  * Added odr_intdup. Ztest server returns TaskPackage for ItemUpdate.
14  *
15  * Revision 1.39  2001/03/12 14:40:57  adam
16  * Minor change of print of item update info.
17  *
18  * Revision 1.38  2001/02/21 13:46:54  adam
19  * C++ fixes.
20  *
21  * Revision 1.37  2001/02/20 11:25:32  adam
22  * Added ill_get_APDU and ill_get_Cancel.
23  *
24  * Revision 1.36  2001/01/30 21:34:18  adam
25  * Added step-size for Scan backend interface.
26  *
27  * Revision 1.35  2000/11/23 10:58:33  adam
28  * SSL comstack support. Separate POSIX thread support library.
29  *
30  * Revision 1.34  2000/09/04 08:58:15  adam
31  * Added prefix yaz_ for most logging utility functions.
32  *
33  * Revision 1.33  2000/08/10 08:41:26  adam
34  * Fixes for ILL.
35  *
36  * Revision 1.32  2000/04/05 07:39:55  adam
37  * Added shared library support (libtool).
38  *
39  * Revision 1.31  2000/01/31 13:15:21  adam
40  * Removed uses of assert(3). Cleanup of ODR. CCL parser update so
41  * that some characters are not surrounded by spaces in resulting term.
42  * ILL-code updates.
43  *
44  * Revision 1.30  2000/01/13 23:05:50  adam
45  * Fixed tagging for member requester-CHECKED-IN in ILL ASN.1 spec.
46  *
47  * Revision 1.29  2000/01/12 14:36:07  adam
48  * Added printing stream (ODR) for backend functions.
49  *
50  * Revision 1.28  1999/12/16 23:36:19  adam
51  * Implemented ILL protocol. Minor updates ASN.1 compiler.
52  *
53  * Revision 1.27  1999/11/30 13:47:12  adam
54  * Improved installation. Moved header files to include/yaz.
55  *
56  * Revision 1.26  1999/08/27 09:40:32  adam
57  * Renamed logf function to yaz_log. Removed VC++ project files.
58  *
59  * Revision 1.25  1999/06/01 14:29:12  adam
60  * Work on Extended Services.
61  *
62  * Revision 1.24  1999/05/27 13:07:54  adam
63  * Fix.
64  *
65  * Revision 1.23  1999/05/27 13:02:20  adam
66  * Assigned OID for old DB Update (VAL_DBUPDATE0).
67  *
68  * Revision 1.22  1999/05/26 13:49:12  adam
69  * DB Update implemented in client (very basic).
70  *
71  * Revision 1.21  1998/12/15 12:45:42  adam
72  * Minor change.
73  *
74  * Revision 1.20  1998/12/14 14:48:05  adam
75  * Fixed memory leak - happened when fetching MARC records.
76  *
77  * Revision 1.19  1998/10/20 15:16:22  adam
78  * Minor change to prevent warning.
79  *
80  * Revision 1.18  1998/10/20 15:13:45  adam
81  * Minor fix regarding output for Item Order.
82  *
83  * Revision 1.17  1998/10/18 22:33:35  quinn
84  * Added diagnostic dump of Item Order Eservice.
85  *
86  * Revision 1.16  1998/10/15 08:26:23  adam
87  * Added type cast to make C++ happy.
88  *
89  * Revision 1.15  1998/10/13 20:05:57  adam
90  * Minor change.
91  *
92  * Revision 1.14  1998/10/13 16:12:25  adam
93  * Added support for Surrogate Diagnostics for Scan Term entries.
94  *
95  * Revision 1.13  1998/08/19 16:10:09  adam
96  * Changed som member names of DeleteResultSetRequest/Response.
97  *
98  * Revision 1.12  1998/07/20 12:38:44  adam
99  * Implemented delete result set service to server API.
100  *
101  * Revision 1.11  1998/06/09 13:55:08  adam
102  * Minor changes.
103  *
104  * Revision 1.10  1998/05/27 16:55:54  adam
105  * Minor changes.
106  *
107  * Revision 1.9  1998/03/31 11:07:45  adam
108  * Furhter work on UNIverse resource report.
109  * Added Extended Services handling in frontend server.
110  *
111  * Revision 1.8  1998/02/11 11:53:36  adam
112  * Changed code so that it compiles as C++.
113  *
114  * Revision 1.7  1998/02/10 11:03:57  adam
115  * Added support for extended handlers in backend server interface.
116  *
117  * Revision 1.6  1998/01/29 13:16:02  adam
118  * Added dummy sort in test server.
119  *
120  * Revision 1.5  1997/11/07 13:31:58  adam
121  * Added NT Service name part of statserv_options_block. Moved NT
122  * service utility to server library.
123  *
124  * Revision 1.4  1997/09/17 12:10:43  adam
125  * YAZ version 1.4.
126  *
127  * Revision 1.3  1997/09/09 10:10:20  adam
128  * Another MSV5.0 port. Changed projects to include proper
129  * library/include paths.
130  * Server starts server in test-mode when no options are given.
131  *
132  * Revision 1.2  1997/09/04 13:50:31  adam
133  * Bug fix in ztest.
134  *
135  */
136
137 /*
138  * Demonstration of simple server
139  */
140
141 #include <stdio.h>
142 #include <stdlib.h>
143 #include <ctype.h>
144
145 #include <yaz/backend.h>
146 #include <yaz/log.h>
147
148 #if YAZ_MODULE_ill
149 #include <yaz/ill.h>
150 #endif
151
152 Z_GenericRecord *read_grs1(FILE *f, ODR o);
153
154 int ztest_search (void *handle, bend_search_rr *rr);
155 int ztest_sort (void *handle, bend_sort_rr *rr);
156 int ztest_present (void *handle, bend_present_rr *rr);
157 int ztest_esrequest (void *handle, bend_esrequest_rr *rr);
158 int ztest_delete (void *handle, bend_delete_rr *rr);
159
160 int ztest_search (void *handle, bend_search_rr *rr)
161 {
162     rr->hits = rand() % 22;
163     return 0;
164 }
165
166 int ztest_present (void *handle, bend_present_rr *rr)
167 {
168     return 0;
169 }
170
171 int ztest_esrequest (void *handle, bend_esrequest_rr *rr)
172 {
173     yaz_log(LOG_LOG, "function: %d", *rr->esr->function);
174     if (rr->esr->packageName)
175         yaz_log(LOG_LOG, "packagename: %s", rr->esr->packageName);
176     yaz_log(LOG_LOG, "Waitaction: %d", *rr->esr->waitAction);
177
178     if (!rr->esr->taskSpecificParameters)
179     {
180         yaz_log (LOG_WARN, "No task specific parameters");
181     }
182     else if (rr->esr->taskSpecificParameters->which == Z_External_itemOrder)
183     {
184         Z_ItemOrder *it = rr->esr->taskSpecificParameters->u.itemOrder;
185         yaz_log (LOG_LOG, "Received ItemOrder");
186         switch (it->which)
187         {
188 #ifdef ASN_COMPILED
189         case Z_IOItemOrder_esRequest:
190 #else
191         case Z_ItemOrder_esRequest:
192 #endif
193         {
194             Z_IORequest *ir = it->u.esRequest;
195             Z_IOOriginPartToKeep *k = ir->toKeep;
196             Z_IOOriginPartNotToKeep *n = ir->notToKeep;
197             
198             if (k && k->contact)
199             {
200                 if (k->contact->name)
201                     yaz_log(LOG_LOG, "contact name %s", k->contact->name);
202                 if (k->contact->phone)
203                     yaz_log(LOG_LOG, "contact phone %s", k->contact->phone);
204                 if (k->contact->email)
205                     yaz_log(LOG_LOG, "contact email %s", k->contact->email);
206             }
207             if (k->addlBilling)
208             {
209                 yaz_log(LOG_LOG, "Billing info (not shown)");
210             }
211             
212             if (n->resultSetItem)
213             {
214                 yaz_log(LOG_LOG, "resultsetItem");
215                 yaz_log(LOG_LOG, "setId: %s", n->resultSetItem->resultSetId);
216                 yaz_log(LOG_LOG, "item: %d", *n->resultSetItem->item);
217             }
218 #if YAZ_MODULE_ill
219             if (n->itemRequest)
220             {
221                 Z_External *r = (Z_External*) n->itemRequest;
222                 ILL_ItemRequest *item_req = 0;
223                 ILL_APDU *ill_apdu = 0;
224                 if (r->direct_reference)
225                 {
226                     oident *ent = oid_getentbyoid(r->direct_reference);
227                     if (ent)
228                         yaz_log(LOG_LOG, "OID %s", ent->desc);
229                     if (ent && ent->value == VAL_ISO_ILL_1)
230                     {
231                         yaz_log (LOG_LOG, "Decode ItemRequest begin");
232                         if (r->which == ODR_EXTERNAL_single)
233                         {
234                             odr_setbuf(rr->decode,
235                                        (char *) r->u.single_ASN1_type->buf,
236                                        r->u.single_ASN1_type->len, 0);
237                             
238                             if (!ill_ItemRequest (rr->decode, &item_req, 0, 0))
239                             {
240                                 yaz_log (LOG_LOG,
241                                     "Couldn't decode ItemRequest %s near %d",
242                                        odr_errmsg(odr_geterror(rr->decode)),
243                                        odr_offset(rr->decode));
244 #if 0
245                                 yaz_log(LOG_LOG, "PDU dump:");
246                                 odr_dumpBER(yaz_log_file(),
247                                      r->u.single_ASN1_type->buf,
248                                      r->u.single_ASN1_type->len);
249 #endif
250                             }
251                             else
252                                 yaz_log(LOG_LOG, "Decode ItemRequest OK");
253                             if (rr->print)
254                             {
255                                 ill_ItemRequest (rr->print, &item_req, 0,
256                                     "ItemRequest");
257                                 odr_reset (rr->print);
258                             }
259                         }
260                         if (!item_req && r->which == ODR_EXTERNAL_single)
261                         {
262                             yaz_log (LOG_LOG, "Decode ILL APDU begin");
263                             odr_setbuf(rr->decode,
264                                        (char*) r->u.single_ASN1_type->buf,
265                                        r->u.single_ASN1_type->len, 0);
266                             
267                             if (!ill_APDU (rr->decode, &ill_apdu, 0, 0))
268                             {
269                                 yaz_log (LOG_LOG,
270                                     "Couldn't decode ILL APDU %s near %d",
271                                        odr_errmsg(odr_geterror(rr->decode)),
272                                        odr_offset(rr->decode));
273                                 yaz_log(LOG_LOG, "PDU dump:");
274                                 odr_dumpBER(yaz_log_file(),
275                                      (char *) r->u.single_ASN1_type->buf,
276                                      r->u.single_ASN1_type->len);
277                             }
278                             else
279                                 yaz_log(LOG_LOG, "Decode ILL APDU OK");
280                             if (rr->print)
281                             {
282                                 ill_APDU (rr->print, &ill_apdu, 0,
283                                     "ILL APDU");
284                                 odr_reset (rr->print);
285                             }
286                         }
287                     }
288                 }
289                 if (item_req)
290                 {
291                     yaz_log (LOG_LOG, "ILL protocol version = %d",
292                              *item_req->protocol_version_num);
293                 }
294             }
295 #endif
296         }
297         break;
298         }
299     }
300     else if (rr->esr->taskSpecificParameters->which == Z_External_update)
301     {
302         Z_IUUpdate *up = rr->esr->taskSpecificParameters->u.update;
303         yaz_log (LOG_LOG, "Received DB Update");
304         if (up->which == Z_IUUpdate_esRequest)
305         {
306             Z_IUUpdateEsRequest *esRequest = up->u.esRequest;
307             Z_IUOriginPartToKeep *toKeep = esRequest->toKeep;
308             Z_IUSuppliedRecords *notToKeep = esRequest->notToKeep;
309             
310             yaz_log (LOG_LOG, "action");
311             if (toKeep->action)
312             {
313                 switch (*toKeep->action)
314                 {
315                 case Z_IUOriginPartToKeep_recordInsert:
316                     yaz_log (LOG_LOG, " recordInsert");
317                     break;
318                 case Z_IUOriginPartToKeep_recordReplace:
319                     yaz_log (LOG_LOG, " recordReplace");
320                     break;
321                 case Z_IUOriginPartToKeep_recordDelete:
322                     yaz_log (LOG_LOG, " recordDelete");
323                     break;
324                 case Z_IUOriginPartToKeep_elementUpdate:
325                     yaz_log (LOG_LOG, " elementUpdate");
326                     break;
327                 case Z_IUOriginPartToKeep_specialUpdate:
328                     yaz_log (LOG_LOG, " specialUpdate");
329                     break;
330                 default:
331                     yaz_log (LOG_LOG, " unknown (%d)", *toKeep->action);
332                 }
333             }
334             if (toKeep->databaseName)
335             {
336                 yaz_log (LOG_LOG, "database: %s", toKeep->databaseName);
337                 if (!strcmp(toKeep->databaseName, "fault"))
338                 {
339                     rr->errcode = 109;
340                     rr->errstring = toKeep->databaseName;
341                 }
342                 if (!strcmp(toKeep->databaseName, "accept"))
343                     rr->errcode = -1;
344             }
345             if (toKeep)
346             {
347                 Z_External *ext = odr_malloc (rr->stream, sizeof(*ext));
348                 Z_IUOriginPartToKeep *keep =
349                     odr_malloc (rr->stream, sizeof(*keep));
350                 Z_IUTargetPart *targetPart =
351                     odr_malloc (rr->stream, sizeof(*targetPart));
352                 rr->taskPackage = odr_malloc (rr->stream, sizeof(*rr->taskPackage));
353                 rr->taskPackage->packageType =
354                     odr_oiddup (rr->stream, rr->esr->packageType);
355                 rr->taskPackage->packageName = 0;
356                 rr->taskPackage->userId = 0;
357                 rr->taskPackage->retentionTime = 0;
358                 rr->taskPackage->permissions = 0;
359                 rr->taskPackage->description = 0;
360                 rr->taskPackage->targetReference = (Odr_oct *)
361                     odr_malloc (rr->stream, sizeof(Odr_oct));
362                 rr->taskPackage->targetReference->buf =
363                     odr_strdup (rr->stream, "123");
364                 rr->taskPackage->targetReference->len =
365                     rr->taskPackage->targetReference->size =
366                     strlen(rr->taskPackage->targetReference->buf);
367                 rr->taskPackage->creationDateTime = 0;
368                 rr->taskPackage->taskStatus = odr_intdup(rr->stream, 0);
369                 rr->taskPackage->packageDiagnostics = 0;
370                 rr->taskPackage->taskSpecificParameters = ext;
371
372                 ext->direct_reference =
373                     odr_oiddup (rr->stream, rr->esr->packageType);
374                 ext->indirect_reference = 0;
375                 ext->descriptor = 0;
376                 ext->which = Z_External_update;
377                 ext->u.update = (Z_IUUpdate *)
378                     odr_malloc (rr->stream, sizeof(*ext->u.update));
379                 ext->u.update->which = Z_IUUpdate_taskPackage;
380                 ext->u.update->u.taskPackage =  (Z_IUUpdateTaskPackage *)
381                     odr_malloc (rr->stream, sizeof(Z_IUUpdateTaskPackage));
382                 ext->u.update->u.taskPackage->originPart = keep;
383                 ext->u.update->u.taskPackage->targetPart = targetPart;
384
385                 keep->action = odr_malloc (rr->stream, sizeof(int));
386                 *keep->action = *toKeep->action;
387                 keep->databaseName =
388                     odr_strdup (rr->stream, toKeep->databaseName);
389                 keep->schema = 0;
390                 keep->elementSetName = 0;
391                 keep->actionQualifier = 0;
392
393                 targetPart->updateStatus = odr_intdup (rr->stream, 1);
394                 targetPart->num_globalDiagnostics = 0;
395                 targetPart->globalDiagnostics = odr_nullval();
396                 targetPart->num_taskPackageRecords = 0;
397                 targetPart->taskPackageRecords = odr_nullval();
398             }
399             if (notToKeep)
400             {
401                 int i;
402                 for (i = 0; i < notToKeep->num; i++)
403                 {
404                     Z_External *rec = notToKeep->elements[i]->record;
405
406                     if (rec->direct_reference)
407                     {
408                         struct oident *oident;
409                         oident = oid_getentbyoid(rec->direct_reference);
410                         if (oident)
411                             yaz_log (LOG_LOG, "record %d type %s", i,
412                                      oident->desc);
413                     }
414                     switch (rec->which)
415                     {
416                     case Z_External_sutrs:
417                         if (rec->u.octet_aligned->len > 170)
418                             yaz_log (LOG_LOG, "%d bytes:\n%.168s ...",
419                                      rec->u.sutrs->len,
420                                      rec->u.sutrs->buf);
421                         else
422                             yaz_log (LOG_LOG, "%d bytes:\n%s",
423                                      rec->u.sutrs->len,
424                                      rec->u.sutrs->buf);
425                         break;
426                     case Z_External_octet        :
427                         if (rec->u.octet_aligned->len > 170)
428                             yaz_log (LOG_LOG, "%d bytes:\n%.168s ...",
429                                      rec->u.octet_aligned->len,
430                                      rec->u.octet_aligned->buf);
431                         else
432                             yaz_log (LOG_LOG, "%d bytes\n%s",
433                                      rec->u.octet_aligned->len,
434                                      rec->u.octet_aligned->buf);
435                     }
436                 }
437             }
438         }
439     }
440     else
441     {
442         yaz_log (LOG_WARN, "Unknown Extended Service(%d)",
443                  rr->esr->taskSpecificParameters->which);
444         
445     }
446     return 0;
447 }
448
449 int ztest_delete (void *handle, bend_delete_rr *rr)
450 {
451     if (rr->num_setnames == 1 && !strcmp (rr->setnames[0], "1"))
452         rr->delete_status = Z_DeleteStatus_success;
453     else
454         rr->delete_status = Z_DeleteStatus_resultSetDidNotExist;
455     return 0;
456 }
457
458 /* Our sort handler really doesn't sort... */
459 int ztest_sort (void *handle, bend_sort_rr *rr)
460 {
461     rr->errcode = 0;
462     rr->sort_status = Z_SortStatus_success;
463     return 0;
464 }
465
466 static int atoin (const char *buf, int n)
467 {
468     int val = 0;
469     while (--n >= 0)
470     {
471         if (isdigit(*buf))
472             val = val*10 + (*buf - '0');
473         buf++;
474     }
475     return val;
476 }
477
478 char *marc_read(FILE *inf, ODR odr)
479 {
480     char length_str[5];
481     size_t size;
482     char *buf;
483
484     if (fread (length_str, 1, 5, inf) != 5)
485         return NULL;
486     size = atoin (length_str, 5);
487     if (size <= 6)
488         return NULL;
489     if (!(buf = (char*) odr_malloc (odr, size+1)))
490         return NULL;
491     if (fread (buf+5, 1, size-5, inf) != (size-5))
492     {
493         xfree (buf);
494         return NULL;
495     }
496     memcpy (buf, length_str, 5);
497     buf[size] = '\0';
498     return buf;
499 }
500
501 static char *dummy_database_record (int num, ODR odr)
502 {
503     FILE *inf = fopen ("dummy-records", "r");
504     char *buf = 0;
505
506     if (!inf)
507         return NULL;
508     while (--num >= 0)
509     {
510         if (num == 98)
511         {
512             buf = (char*) odr_malloc(odr, 2101);
513             memset(buf, 'A', 2100);
514             buf[2100] = '\0';
515             break;
516         }
517         else
518             buf = marc_read (inf, odr);
519         if (!num || !buf)
520             break;
521     }
522     fclose(inf);
523     if (num < 0)
524         return 0;
525     return buf;
526 }
527
528 static Z_GenericRecord *dummy_grs_record (int num, ODR o)
529 {
530     FILE *f = fopen("dummy-grs", "r");
531     char line[512];
532     Z_GenericRecord *r = 0;
533     int n;
534
535     if (!f)
536         return 0;
537     while (fgets(line, 512, f))
538         if (*line == '#' && sscanf(line, "#%d", &n) == 1 && n == num)
539         {
540             r = read_grs1(f, o);
541             break;
542         }
543     fclose(f);
544     return r;
545 }
546
547 int ztest_fetch(void *handle, bend_fetch_rr *r)
548 {
549     char *cp;
550     r->errstring = 0;
551     r->last_in_set = 0;
552     r->basename = "DUMMY";
553     r->output_format = r->request_format;  
554     if (r->request_format == VAL_SUTRS)
555     {
556 #if 0
557 /* this section returns a huge record (for testing non-blocking write, etc) */
558         r->len = 980000;
559         r->record = odr_malloc (r->stream, r->len);
560         memset (r->record, 'x', r->len);
561 #else
562 /* this section returns a small record */
563         char buf[100];
564
565         sprintf(buf, "This is dummy SUTRS record number %d\n", r->number);
566
567         r->len = strlen(buf);
568         r->record = (char *) odr_malloc (r->stream, r->len+1);
569         strcpy(r->record, buf);
570 #endif
571     }
572     else if (r->request_format == VAL_GRS1)
573     {
574         r->len = -1;
575         r->record = (char*) dummy_grs_record(r->number, r->stream);
576         if (!r->record)
577         {
578             r->errcode = 13;
579             return 0;
580         }
581     }
582     else if ((cp = dummy_database_record(r->number, r->stream)))
583     {
584         r->len = strlen(cp);
585         r->record = cp;
586         r->output_format = VAL_USMARC;
587     }
588     else
589     {
590         r->errcode = 13;
591         return 0;
592     }
593     r->errcode = 0;
594     return 0;
595 }
596
597 /*
598  * silly dummy-scan what reads words from a file.
599  */
600 int ztest_scan(void *handle, bend_scan_rr *q)
601 {
602     static FILE *f = 0;
603     static struct scan_entry list[200];
604     static char entries[200][80];
605     int hits[200];
606     char term[80], *p;
607     int i, pos;
608     int term_position_req = q->term_position;
609     int num_entries_req = q->num_entries;
610
611     q->errcode = 0;
612     q->errstring = 0;
613     q->entries = list;
614     q->status = BEND_SCAN_SUCCESS;
615     if (!f && !(f = fopen("dummy-words", "r")))
616     {
617         perror("dummy-words");
618         exit(1);
619     }
620     if (q->term->term->which != Z_Term_general)
621     {
622         q->errcode = 229; /* unsupported term type */
623         return 0;
624     }
625     if (*q->step_size != 0)
626     {
627         q->errcode = 205; /*Only zero step size supported for Scan */
628         return 0;
629     }
630     if (q->term->term->u.general->len >= 80)
631     {
632         q->errcode = 11; /* term too long */
633         return 0;
634     }
635     if (q->num_entries > 200)
636     {
637         q->errcode = 31;
638         return 0;
639     }
640     memcpy(term, q->term->term->u.general->buf, q->term->term->u.general->len);
641     term[q->term->term->u.general->len] = '\0';
642     for (p = term; *p; p++)
643         if (islower(*p))
644             *p = toupper(*p);
645
646     fseek(f, 0, SEEK_SET);
647     q->num_entries = 0;
648
649     for (i = 0, pos = 0; fscanf(f, " %79[^:]:%d", entries[pos], &hits[pos]) == 2;
650         i++, pos < 199 ? pos++ : (pos = 0))
651     {
652         if (!q->num_entries && strcmp(entries[pos], term) >= 0) /* s-point fnd */
653         {
654             if ((q->term_position = term_position_req) > i + 1)
655             {
656                 q->term_position = i + 1;
657                 q->status = BEND_SCAN_PARTIAL;
658             }
659             for (; q->num_entries < q->term_position; q->num_entries++)
660             {
661                 int po;
662
663                 po = pos - q->term_position + q->num_entries+1; /* find pos */
664                 if (po < 0)
665                     po += 200;
666
667                 if (!strcmp (term, "SD") && q->num_entries == 2)
668                 {
669                     list[q->num_entries].term = entries[pos];
670                     list[q->num_entries].occurrences = -1;
671                     list[q->num_entries].errcode = 233;
672                     list[q->num_entries].errstring = "SD for Scan Term";
673                 }
674                 else
675                 {
676                     list[q->num_entries].term = entries[po];
677                     list[q->num_entries].occurrences = hits[po];
678                 }
679             }
680         }
681         else if (q->num_entries)
682         {
683             list[q->num_entries].term = entries[pos];
684             list[q->num_entries].occurrences = hits[pos];
685             q->num_entries++;
686         }
687         if (q->num_entries >= num_entries_req)
688             break;
689     }
690     if (feof(f))
691         q->status = BEND_SCAN_PARTIAL;
692     return 0;
693 }
694
695 bend_initresult *bend_init(bend_initrequest *q)
696 {
697     bend_initresult *r = (bend_initresult *) odr_malloc (q->stream, sizeof(*r));
698     static char *dummy = "Hej fister";
699
700     r->errcode = 0;
701     r->errstring = 0;
702     r->handle = dummy;
703     q->bend_sort = ztest_sort;              /* register sort handler */
704     q->bend_search = ztest_search;          /* register search handler */
705     q->bend_present = ztest_present;        /* register present handle */
706     q->bend_esrequest = ztest_esrequest;
707     q->bend_delete = ztest_delete;
708     q->bend_fetch = ztest_fetch;
709     q->bend_scan = ztest_scan;
710     return r;
711 }
712
713 void bend_close(void *handle)
714 {
715     return;
716 }
717
718 int main(int argc, char **argv)
719 {
720     return statserv_main(argc, argv, bend_init, bend_close);
721 }