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