44f182c8df92cd2b50ad4360f91e2f28fdc8d766
[simpleserver-moved-to-github.git] / SimpleServer.xs
1 /*
2  * $Id: SimpleServer.xs,v 1.28 2004-06-05 07:55:05 adam Exp $ 
3  * ----------------------------------------------------------------------
4  * 
5  * Copyright (c) 2000-2004, Index Data.
6  *
7  * Permission to use, copy, modify, distribute, and sell this software and
8  * its documentation, in whole or in part, for any purpose, is hereby granted,
9  * provided that:
10  *
11  * 1. This copyright and permission notice appear in all copies of the
12  * software and its documentation. Notices of copyright or attribution
13  * which appear at the beginning of any file must remain unchanged.
14  *
15  * 2. The name of Index Data or the individual authors may not be used to
16  * endorse or promote products derived from this software without specific
17  * prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT WARRANTY OF ANY KIND,
20  * EXPRESS, IMPLIED, OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
21  * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
22  * IN NO EVENT SHALL INDEX DATA BE LIABLE FOR ANY SPECIAL, INCIDENTAL,
23  * INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY DAMAGES
24  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER OR
25  * NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF
26  * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
27  * OF THIS SOFTWARE.
28  */
29
30 #include "EXTERN.h"
31 #include "perl.h"
32 #include "proto.h"
33 #include "embed.h"
34 #include "XSUB.h"
35 #include <yaz/backend.h>
36 #include <yaz/log.h>
37 #include <yaz/wrbuf.h>
38 #include <stdio.h>
39 #ifdef WIN32
40 #else
41 #include <unistd.h>
42 #endif
43 #include <stdlib.h>
44 #include <ctype.h>
45 #define GRS_MAX_FIELDS 500 
46 #ifdef ASN_COMPILED
47 #include <yaz/ill.h>
48 #endif
49 #ifndef sv_undef                /* To fix the problem with Perl 5.6.0 */
50 #define sv_undef PL_sv_undef
51 #endif
52
53 NMEM_MUTEX simpleserver_mutex;
54
55 typedef struct {
56         SV *handle;
57
58         SV *init_ref;
59         SV *close_ref;
60         SV *sort_ref;
61         SV *search_ref;
62         SV *fetch_ref;
63         SV *present_ref;
64         SV *esrequest_ref;
65         SV *delete_ref;
66         SV *scan_ref;
67         NMEM nmem;
68         int stop_flag;  /* is used to stop server prematurely .. */
69 } Zfront_handle;
70
71 #define ENABLE_STOP_SERVER 0
72
73 SV *init_ref = NULL;
74 SV *close_ref = NULL;
75 SV *sort_ref = NULL;
76 SV *search_ref = NULL;
77 SV *fetch_ref = NULL;
78 SV *present_ref = NULL;
79 SV *esrequest_ref = NULL;
80 SV *delete_ref = NULL;
81 SV *scan_ref = NULL;
82 PerlInterpreter *root_perl_context;
83 int MAX_OID = 15;
84
85 #define GRS_BUF_SIZE 512
86
87 CV * simpleserver_sv2cv(SV *handler) {
88     STRLEN len;
89     char *buf;
90    
91     if (SvPOK(handler)) {
92         CV *ret;
93         buf = SvPV( handler, len);
94         if ( !( ret = perl_get_cv(buf, FALSE ) ) ) {
95             fprintf( stderr, "simpleserver_sv2cv: No such handler '%s'\n\n", buf );
96             exit(1);
97         }
98         
99         return ret;
100     } else {
101         return (CV *) handler;
102     }
103 }
104
105 /* debuggin routine to check for destruction of Perl interpreters */
106 #if 0
107 int tst_clones(void)
108 {
109     int i; 
110     PerlInterpreter *parent = PERL_GET_CONTEXT;
111     for (i = 0; i<500; i++)
112     {
113         PerlInterpreter *perl_interp = perl_clone(parent, 0);
114         PERL_SET_CONTEXT( perl_interp );
115         PL_perl_destruct_level = 2;
116         PERL_SET_CONTEXT( parent );
117         perl_destruct(perl_interp);
118         perl_free(perl_interp);
119     }
120     exit (0);
121 }
122 #endif
123
124 int simpleserver_clone(void) {
125 #ifdef USE_ITHREADS
126      nmem_mutex_enter(simpleserver_mutex);
127      if (1)
128      {
129          PerlInterpreter *current = PERL_GET_CONTEXT;
130
131          /* if current is unset, then we're in a new thread with
132           * no Perl interpreter for it. So we must create one .
133           * This will only happen when threaded is used..
134           */
135          if (!current) {
136              PerlInterpreter *perl_interp;
137              PERL_SET_CONTEXT( root_perl_context );
138              perl_interp = perl_clone(root_perl_context, 0);
139              PERL_SET_CONTEXT( perl_interp );
140          }
141      }
142      nmem_mutex_leave(simpleserver_mutex);
143 #endif
144      return 0;
145 }
146
147
148 void simpleserver_free(void) {
149     nmem_mutex_enter(simpleserver_mutex);
150     if (1)
151     {
152         PerlInterpreter *current_interp = PERL_GET_CONTEXT;
153
154         /* If current Perl Interp is different from root interp, then
155          * we're in threaded mode and we must destroy.. 
156          */
157         if (current_interp != root_perl_context) {
158             PL_perl_destruct_level = 2;
159             PERL_SET_CONTEXT(root_perl_context);
160             perl_destruct(current_interp);
161             perl_free(current_interp);
162         }
163     }
164     nmem_mutex_leave(simpleserver_mutex);
165 }
166
167
168 Z_GenericRecord *read_grs1(char *str, ODR o)
169 {
170         int type, ivalue;
171         char line[GRS_BUF_SIZE+1], *buf, *ptr, *original;
172         char value[GRS_BUF_SIZE+1];
173         Z_GenericRecord *r = 0;
174
175         original = str;
176         r = (Z_GenericRecord *)odr_malloc(o, sizeof(*r));
177         r->elements = (Z_TaggedElement **) odr_malloc(o, sizeof(Z_TaggedElement*) * GRS_MAX_FIELDS);
178         r->num_elements = 0;
179         
180         for (;;)
181         {
182                 Z_TaggedElement *t;
183                 Z_ElementData *c;
184                 int len;
185         
186                 ptr = strchr(str, '\n');
187                 if (!ptr) {
188                         return r;
189                 }
190                 len = ptr - str;
191                 if (len > GRS_BUF_SIZE) {
192                     yaz_log(LOG_WARN, "GRS string too long - truncating (%d > %d)", len, GRS_BUF_SIZE);
193                     len = GRS_BUF_SIZE;
194                 }
195                 strncpy(line, str, len);
196                 line[len] = 0;
197                 buf = line;
198                 str = ptr + 1;
199                 while (*buf && isspace(*buf))
200                         buf++;
201                 if (*buf == '}') {
202                         memmove(original, str, strlen(str));
203                         return r;
204                 }
205                 if (sscanf(buf, "(%d,%[^)])", &type, value) != 2)
206                 {
207                         yaz_log(LOG_WARN, "Bad data in '%s'", buf);
208                         return r;
209                 }
210                 if (!type && *value == '0')
211                         return r;
212                 if (!(buf = strchr(buf, ')')))
213                         return r;
214                 buf++;
215                 while (*buf && isspace(*buf))
216                         buf++;
217                 if (r->num_elements >= GRS_MAX_FIELDS)
218                 {
219                         yaz_log(LOG_WARN, "Max number of GRS-1 elements exceeded [GRS_MAX_FIELDS=%d]", GRS_MAX_FIELDS);
220                         exit(0);
221                 }
222                 r->elements[r->num_elements] = t = (Z_TaggedElement *) odr_malloc(o, sizeof(Z_TaggedElement));
223                 t->tagType = (int *)odr_malloc(o, sizeof(int));
224                 *t->tagType = type;
225                 t->tagValue = (Z_StringOrNumeric *)
226                         odr_malloc(o, sizeof(Z_StringOrNumeric));
227                 if ((ivalue = atoi(value)))
228                 {
229                         t->tagValue->which = Z_StringOrNumeric_numeric;
230                         t->tagValue->u.numeric = (int *)odr_malloc(o, sizeof(int));
231                         *t->tagValue->u.numeric = ivalue;
232                 }
233                 else
234                 {
235                         t->tagValue->which = Z_StringOrNumeric_string;
236                         t->tagValue->u.string = (char *)odr_malloc(o, strlen(value)+1);
237                         strcpy(t->tagValue->u.string, value);
238                 }
239                 t->tagOccurrence = 0;
240                 t->metaData = 0;
241                 t->appliedVariant = 0;
242                 t->content = c = (Z_ElementData *)odr_malloc(o, sizeof(Z_ElementData));
243                 if (*buf == '{')
244                 {
245                         c->which = Z_ElementData_subtree;
246                         c->u.subtree = read_grs1(str, o);
247                 }
248                 else
249                 {
250                         c->which = Z_ElementData_string;
251                         c->u.string = odr_strdup(o, buf);
252                 }
253                 r->num_elements++;
254         }
255 }
256
257
258
259
260 static void oid2str(Odr_oid *o, WRBUF buf)
261 {
262     for (; *o >= 0; o++) {
263         char ibuf[16];
264         sprintf(ibuf, "%d", *o);
265         wrbuf_puts(buf, ibuf);
266         if (o[1] > 0)
267             wrbuf_putc(buf, '.');
268     }
269 }
270
271
272 static int rpn2pquery(Z_RPNStructure *s, WRBUF buf)
273 {
274     switch (s->which) {
275         case Z_RPNStructure_simple: {
276             Z_Operand *o = s->u.simple;
277
278             switch (o->which) {
279                 case Z_Operand_APT: {
280                     Z_AttributesPlusTerm *at = o->u.attributesPlusTerm;
281
282                     if (at->attributes) {
283                         int i;
284                         char ibuf[16];
285
286                         for (i = 0; i < at->attributes->num_attributes; i++) {
287                             wrbuf_puts(buf, "@attr ");
288                             if (at->attributes->attributes[i]->attributeSet) {
289                                 oid2str(at->attributes->attributes[i]->attributeSet, buf);
290                                 wrbuf_putc(buf, ' ');
291                             }
292                             sprintf(ibuf, "%d=", *at->attributes->attributes[i]->attributeType);
293                             assert(at->attributes->attributes[i]->which == Z_AttributeValue_numeric);
294                             wrbuf_puts(buf, ibuf);
295                             sprintf(ibuf, "%d ", *at->attributes->attributes[i]->value.numeric);
296                             wrbuf_puts(buf, ibuf);
297                         }
298                     }
299                     switch (at->term->which) {
300                         case Z_Term_general: {
301                             wrbuf_putc(buf, '"');
302                             wrbuf_write(buf, (char*) at->term->u.general->buf, at->term->u.general->len);
303                             wrbuf_puts(buf, "\" ");
304                             break;
305                         }
306                         default: abort();
307                     }
308                     break;
309                 }
310                 default: abort();
311             }
312             break;
313         }
314         case Z_RPNStructure_complex: {
315             Z_Complex *c = s->u.complex;
316
317             switch (c->roperator->which) {
318                 case Z_Operator_and: wrbuf_puts(buf, "@and "); break;
319                 case Z_Operator_or: wrbuf_puts(buf, "@or "); break;
320                 case Z_Operator_and_not: wrbuf_puts(buf, "@not "); break;
321                 case Z_Operator_prox: abort();
322                 default: abort();
323             }
324             if (!rpn2pquery(c->s1, buf))
325                 return 0;
326             if (!rpn2pquery(c->s2, buf))
327                 return 0;
328             break;
329         }
330         default: abort();
331     }
332     return 1;
333 }
334
335
336 WRBUF zquery2pquery(Z_Query *q)
337 {
338     WRBUF buf = wrbuf_alloc();
339
340     if (q->which != Z_Query_type_1 && q->which != Z_Query_type_101) 
341         return 0;
342     if (q->u.type_1->attributeSetId) {
343         /* Output attribute set ID */
344         wrbuf_puts(buf, "@attrset ");
345         oid2str(q->u.type_1->attributeSetId, buf);
346         wrbuf_putc(buf, ' ');
347     }
348     return rpn2pquery(q->u.type_1->RPNStructure, buf) ? buf : 0;
349 }
350
351
352 /* Lifted verbatim from Net::Z3950 yazwrap/util.c */
353 #include <stdarg.h>
354 void fatal(char *fmt, ...)
355 {
356     va_list ap;
357
358     fprintf(stderr, "FATAL (yazwrap): ");
359     va_start(ap, fmt);
360     vfprintf(stderr, fmt, ap);
361     va_end(ap);
362     fprintf(stderr, "\n");
363     abort();
364 }
365
366
367 /* Lifted verbatim from Net::Z3950 yazwrap/receive.c */
368 /*
369  * Creates a new Perl object of type `class'; the newly-created scalar
370  * that is a reference to the blessed thingy `referent' is returned.
371  */
372 static SV *newObject(char *class, SV *referent)
373 {
374     HV *stash;
375     SV *sv;
376
377     sv = newRV_noinc((SV*) referent);
378     stash = gv_stashpv(class, 0);
379     if (stash == 0)
380         fatal("attempt to create object of undefined class '%s'", class);
381     /*assert(stash != 0);*/
382     sv_bless(sv, stash);
383     return sv;
384 }
385
386
387 /* Lifted verbatim from Net::Z3950 yazwrap/receive.c */
388 static void setMember(HV *hv, char *name, SV *val)
389 {
390     /* We don't increment `val's reference count -- I think this is
391      * right because it's created with a refcount of 1, and in fact
392      * the reference via this hash is the only reference to it in
393      * general.
394      */
395     if (!hv_store(hv, name, (U32) strlen(name), val, (U32) 0))
396         fatal("couldn't store member in hash");
397 }
398
399
400 /* Lifted verbatim from Net::Z3950 yazwrap/receive.c */
401 static SV *translateOID(Odr_oid *x)
402 {
403     /* Yaz represents an OID by an int array terminated by a negative
404      * value, typically -1; we represent it as a reference to a
405      * blessed scalar string of "."-separated elements.
406      */
407     char buf[1000];
408     int i;
409
410     *buf = '\0';
411     for (i = 0; x[i] >= 0; i++) {
412         sprintf(buf + strlen(buf), "%d", (int) x[i]);
413         if (x[i+1] >- 0)
414             strcat(buf, ".");
415     }
416
417     /*
418      * ### We'd like to return a blessed scalar (string) here, but of
419      *  course you can't do that in Perl: only references can be
420      *  blessed, so we'd have to return a _reference_ to a string, and
421      *  bless _that_.  Better to do without the blessing, I think.
422      */
423     if (1) {
424         return newSVpv(buf, 0);
425     } else {
426         return newObject("Net::Z3950::APDU::OID", newSVpv(buf, 0));
427     }
428 }
429
430
431 static SV *rpn2perl(Z_RPNStructure *s)
432 {
433     SV *sv;
434     HV *hv;
435     AV *av;
436
437     switch (s->which) {
438     case Z_RPNStructure_simple: {
439         Z_Operand *o = s->u.simple;
440         Z_AttributesPlusTerm *at;
441         if (o->which != Z_Operand_APT)
442             fatal("can't handle RPN simples other than APT");
443         at = o->u.attributesPlusTerm;
444         if (at->term->which != Z_Term_general)
445             fatal("can't handle RPN terms other than general");
446
447         sv = newObject("Net::Z3950::RPN::Term", (SV*) (hv = newHV()));
448         if (at->attributes) {
449             int i;
450             SV *attrs = newObject("Net::Z3950::RPN::Attributes",
451                                   (SV*) (av = newAV()));
452             for (i = 0; i < at->attributes->num_attributes; i++) {
453                 Z_AttributeElement *elem = at->attributes->attributes[i];
454                 HV *hv2;
455                 SV *tmp = newObject("Net::Z3950::RPN::Attribute",
456                                     (SV*) (hv2 = newHV()));
457                 if (elem->attributeSet)
458                     setMember(hv2, "attributeSet",
459                               translateOID(elem->attributeSet));
460                 setMember(hv2, "attributeType",
461                           newSViv(*elem->attributeType));
462                 assert(elem->which == Z_AttributeValue_numeric);
463                 setMember(hv2, "attributeValue",
464                           newSViv(*elem->value.numeric));
465                 av_push(av, tmp);
466             }
467             setMember(hv, "attributes", attrs);
468         }
469         setMember(hv, "term", newSVpv((char*) at->term->u.general->buf,
470                                       at->term->u.general->len));
471         return sv;
472     }
473     case Z_RPNStructure_complex: {
474         SV *tmp;
475         Z_Complex *c = s->u.complex;
476         char *type = 0;         /* vacuous assignment satisfies gcc -Wall */
477         switch (c->roperator->which) {
478         case Z_Operator_and:     type = "Net::Z3950::RPN::And"; break;
479         case Z_Operator_or:      type = "Net::Z3950::RPN::Or"; break;
480         case Z_Operator_and_not: type = "Net::Z3950::RPN::AndNot"; break;
481         case Z_Operator_prox:    fatal("proximity not yet supported");
482         default: fatal("unknown RPN operator %d", (int) c->roperator->which);
483         }
484         sv = newObject(type, (SV*) (av = newAV()));
485         if ((tmp = rpn2perl(c->s1)) == 0)
486             return 0;
487         av_push(av, tmp);
488         if ((tmp = rpn2perl(c->s2)) == 0)
489             return 0;
490         av_push(av, tmp);
491         return sv;
492     }
493     default: fatal("unknown RPN node type %d", (int) s->which);
494     }
495
496     return 0;
497 }
498
499
500 static SV *zquery2perl(Z_Query *q)
501 {
502     SV *sv;
503     HV *hv;
504
505     if (q->which != Z_Query_type_1 && q->which != Z_Query_type_101) 
506         return 0;
507     sv = newObject("Net::Z3950::APDU::Query", (SV*) (hv = newHV()));
508     if (q->u.type_1->attributeSetId)
509         setMember(hv, "attributeSet",
510                   translateOID(q->u.type_1->attributeSetId));
511     setMember(hv, "query", rpn2perl(q->u.type_1->RPNStructure));
512     return sv;
513 }
514
515
516 int bend_sort(void *handle, bend_sort_rr *rr)
517 {
518         HV *href;
519         AV *aref;
520         SV **temp;
521         SV *err_code;
522         SV *err_str;
523         SV *status;
524         STRLEN len;
525         char *ptr;
526         char *ODR_err_str;
527         char **input_setnames;
528         Zfront_handle *zhandle = (Zfront_handle *)handle;
529         int i;
530         
531         dSP;
532         ENTER;
533         SAVETMPS;
534         
535         aref = newAV();
536         input_setnames = rr->input_setnames;
537         for (i = 0; i < rr->num_input_setnames; i++)
538         {
539                 av_push(aref, newSVpv(*input_setnames++, 0));
540         }
541         href = newHV();
542         hv_store(href, "INPUT", 5, newRV( (SV*) aref), 0);
543         hv_store(href, "OUTPUT", 6, newSVpv(rr->output_setname, 0), 0);
544         hv_store(href, "HANDLE", 6, zhandle->handle, 0);
545         hv_store(href, "STATUS", 6, newSViv(0), 0);
546
547         PUSHMARK(sp);
548
549         XPUSHs(sv_2mortal(newRV( (SV*) href)));
550
551         PUTBACK;
552
553         perl_call_sv(sort_ref, G_SCALAR | G_DISCARD);
554
555         SPAGAIN;
556
557         temp = hv_fetch(href, "ERR_CODE", 8, 1);
558         err_code = newSVsv(*temp);
559
560         temp = hv_fetch(href, "ERR_STR", 7, 1);
561         err_str = newSVsv(*temp);
562
563         temp = hv_fetch(href, "STATUS", 6, 1);
564         status = newSVsv(*temp);
565
566
567         
568
569         PUTBACK;
570         FREETMPS;
571         LEAVE;
572
573         hv_undef(href),
574         av_undef(aref);
575         rr->errcode = SvIV(err_code);
576         rr->sort_status = SvIV(status);
577         ptr = SvPV(err_str, len);
578         ODR_err_str = (char *)odr_malloc(rr->stream, len + 1);
579         strcpy(ODR_err_str, ptr);
580         rr->errstring = ODR_err_str;
581
582         sv_free(err_code);
583         sv_free(err_str);
584         sv_free(status);
585         
586         return 0;
587 }
588
589
590 int bend_search(void *handle, bend_search_rr *rr)
591 {
592         HV *href;
593         AV *aref;
594         SV **temp;
595         SV *hits;
596         SV *err_code;
597         SV *err_str;
598         char *ODR_errstr;
599         STRLEN len;
600         int i;
601         char **basenames;
602         int n;
603         WRBUF query;
604         char *ptr;
605         SV *point;
606         SV *ODR_point;
607         Zfront_handle *zhandle = (Zfront_handle *)handle;
608         CV* handler_cv = 0;
609
610         dSP;
611         ENTER;
612         SAVETMPS;
613
614         aref = newAV();
615         basenames = rr->basenames;
616         for (i = 0; i < rr->num_bases; i++)
617         {
618                 av_push(aref, newSVpv(*basenames++, 0));
619         }
620 #if ENABLE_STOP_SERVER
621         if (rr->num_bases == 1 && !strcmp(rr->basenames[0], "XXstop"))
622         {
623                 zhandle->stop_flag = 1;
624         }
625 #endif
626         href = newHV();         
627         hv_store(href, "SETNAME", 7, newSVpv(rr->setname, 0), 0);
628         hv_store(href, "REPL_SET", 8, newSViv(rr->replace_set), 0);
629         hv_store(href, "ERR_CODE", 8, newSViv(0), 0);
630         hv_store(href, "ERR_STR", 7, newSVpv("", 0), 0);
631         hv_store(href, "HITS", 4, newSViv(0), 0);
632         hv_store(href, "DATABASES", 9, newRV( (SV*) aref), 0);
633         hv_store(href, "HANDLE", 6, zhandle->handle, 0);
634         hv_store(href, "PID", 3, newSViv(getpid()), 0);
635         hv_store(href, "RPN", 3, zquery2perl(rr->query), 0);
636         query = zquery2pquery(rr->query);
637         if (query)
638         {
639                 hv_store(href, "QUERY", 5, newSVpv((char *)query->buf, query->pos), 0);
640         }
641         else
642         {       
643                 rr->errcode = 108;
644         }
645         PUSHMARK(sp);
646         
647         XPUSHs(sv_2mortal(newRV( (SV*) href)));
648         
649         PUTBACK;
650
651         handler_cv = simpleserver_sv2cv( search_ref );
652         perl_call_sv( (SV *) handler_cv, G_SCALAR | G_DISCARD);
653
654         SPAGAIN;
655
656         temp = hv_fetch(href, "HITS", 4, 1);
657         hits = newSVsv(*temp);
658
659         temp = hv_fetch(href, "ERR_CODE", 8, 1);
660         err_code = newSVsv(*temp);
661
662         temp = hv_fetch(href, "ERR_STR", 7, 1);
663         err_str = newSVsv(*temp);
664
665         temp = hv_fetch(href, "HANDLE", 6, 1);
666         point = newSVsv(*temp);
667
668         PUTBACK;
669         FREETMPS;
670         LEAVE;
671         
672         hv_undef(href);
673         av_undef(aref);
674         rr->hits = SvIV(hits);
675         rr->errcode = SvIV(err_code);
676         ptr = SvPV(err_str, len);
677         ODR_errstr = (char *)odr_malloc(rr->stream, len + 1);
678         strcpy(ODR_errstr, ptr);
679         rr->errstring = ODR_errstr;
680
681         zhandle->handle = point;
682         handle = zhandle;
683         sv_free(hits);
684         sv_free(err_code);
685         sv_free(err_str);
686         sv_free( (SV*) aref);
687         sv_free( (SV*) href);
688         /*sv_free(point);*/
689         wrbuf_free(query, 1);
690         return 0;
691 }
692
693
694 /* ### this is worryingly similar to oid2str() */
695 WRBUF oid2dotted(int *oid)
696 {
697
698         WRBUF buf = wrbuf_alloc();
699         int dot = 0;
700
701         for (; *oid != -1 ; oid++)
702         {
703                 char ibuf[16];
704                 if (dot)
705                 {
706                         wrbuf_putc(buf, '.');
707                 }
708                 else
709                 {
710                         dot = 1;
711                 }
712                 sprintf(ibuf, "%d", *oid);
713                 wrbuf_puts(buf, ibuf);
714         }
715         return buf;
716 }
717                 
718
719 int dotted2oid(char *dotted, int *buffer)
720 {
721         int *oid;
722         char ibuf[16];
723         char *ptr;
724         int n = 0;
725
726         ptr = ibuf;
727         oid = buffer;
728         while (*dotted)
729         {
730                 if (*dotted == '.')
731                 {
732                         n++;
733                         if (n == MAX_OID)  /* Terminate if more than MAX_OID entries */
734                         {
735                                 *oid = -1;
736                                 return -1;
737                         }
738                         *ptr = 0;
739                         sscanf(ibuf, "%d", oid++);
740                         ptr = ibuf;
741                         dotted++;
742
743                 }
744                 else
745                 {
746                         *ptr++ = *dotted++;
747                 }
748         }
749         if (n < MAX_OID)
750         {
751                 *ptr = 0;
752                 sscanf(ibuf, "%d", oid++);
753         }
754         *oid = -1;
755         return 0;
756 }
757
758
759 int bend_fetch(void *handle, bend_fetch_rr *rr)
760 {
761         HV *href;
762         SV **temp;
763         SV *basename;
764         SV *record;
765         SV *last;
766         SV *err_code;
767         SV *err_string;
768         SV *sur_flag;
769         SV *point;
770         SV *rep_form;
771         char *ptr;
772         char *ODR_record;
773         char *ODR_basename;
774         char *ODR_errstr;
775         int *ODR_oid_buf;
776         oident *oid;
777         WRBUF oid_dotted;
778         Zfront_handle *zhandle = (Zfront_handle *)handle;
779         CV* handler_cv = 0;
780
781         Z_RecordComposition *composition;
782         Z_ElementSetNames *simple;
783         STRLEN length;
784
785         dSP;
786         ENTER;
787         SAVETMPS;
788
789         rr->errcode = 0;
790         href = newHV();
791         hv_store(href, "SETNAME", 7, newSVpv(rr->setname, 0), 0);
792         temp = hv_store(href, "OFFSET", 6, newSViv(rr->number), 0);
793         oid_dotted = oid2dotted(rr->request_format_raw);
794         hv_store(href, "REQ_FORM", 8, newSVpv((char *)oid_dotted->buf, oid_dotted->pos), 0);
795         hv_store(href, "REP_FORM", 8, newSVpv((char *)oid_dotted->buf, oid_dotted->pos), 0);
796         hv_store(href, "BASENAME", 8, newSVpv("", 0), 0);
797         hv_store(href, "RECORD", 6, newSVpv("", 0), 0);
798         hv_store(href, "LAST", 4, newSViv(0), 0);
799         hv_store(href, "ERR_CODE", 8, newSViv(0), 0);
800         hv_store(href, "ERR_STR", 7, newSVpv("", 0), 0);
801         hv_store(href, "SUR_FLAG", 8, newSViv(0), 0);
802         hv_store(href, "HANDLE", 6, zhandle->handle, 0);
803         hv_store(href, "PID", 3, newSViv(getpid()), 0);
804         if (rr->comp)
805         {
806                 composition = rr->comp;
807                 if (composition->which == Z_RecordComp_simple)
808                 {
809                         simple = composition->u.simple;
810                         if (simple->which == Z_ElementSetNames_generic)
811                         {
812                                 hv_store(href, "COMP", 4, newSVpv(simple->u.generic, 0), 0);
813                         } 
814                         else
815                         {
816                                 rr->errcode = 26;
817                         }
818                 }
819                 else
820                 {
821                         rr->errcode = 26;
822                 }
823         }
824
825         PUSHMARK(sp);
826
827         XPUSHs(sv_2mortal(newRV( (SV*) href)));
828
829         PUTBACK;
830         
831         handler_cv = simpleserver_sv2cv( fetch_ref );
832         perl_call_sv( (SV *) handler_cv, G_SCALAR | G_DISCARD);
833
834         SPAGAIN;
835
836         temp = hv_fetch(href, "BASENAME", 8, 1);
837         basename = newSVsv(*temp);
838
839         temp = hv_fetch(href, "RECORD", 6, 1);
840         record = newSVsv(*temp);
841
842         temp = hv_fetch(href, "LAST", 4, 1);
843         last = newSVsv(*temp);
844
845         temp = hv_fetch(href, "ERR_CODE", 8, 1);
846         err_code = newSVsv(*temp);
847
848         temp = hv_fetch(href, "ERR_STR", 7, 1),
849         err_string = newSVsv(*temp);
850
851         temp = hv_fetch(href, "SUR_FLAG", 8, 1);
852         sur_flag = newSVsv(*temp);
853
854         temp = hv_fetch(href, "REP_FORM", 8, 1);
855         rep_form = newSVsv(*temp);
856
857         temp = hv_fetch(href, "HANDLE", 6, 1);
858         point = newSVsv(*temp);
859
860         PUTBACK;
861         FREETMPS;
862         LEAVE;
863
864         hv_undef(href);
865         
866         ptr = SvPV(basename, length);
867         ODR_basename = (char *)odr_malloc(rr->stream, length + 1);
868         strcpy(ODR_basename, ptr);
869         rr->basename = ODR_basename;
870
871         ptr = SvPV(rep_form, length);
872         ODR_oid_buf = (int *)odr_malloc(rr->stream, (MAX_OID + 1) * sizeof(int));
873         if (dotted2oid(ptr, ODR_oid_buf) == -1)         /* Maximum number of OID elements exceeded */
874         {
875                 printf("Net::Z3950::SimpleServer: WARNING: OID structure too long, max length is %d\n", MAX_OID);
876         }
877         rr->output_format_raw = ODR_oid_buf;    
878         
879         ptr = SvPV(record, length);
880         oid = oid_getentbyoid(ODR_oid_buf);
881         if (oid->value == VAL_GRS1)             /* Treat GRS-1 records separately */
882         {
883                 rr->record = (char *) read_grs1(ptr, rr->stream);
884                 rr->len = -1;
885         }
886         else
887         {
888                 ODR_record = (char *)odr_malloc(rr->stream, length + 1);
889                 strcpy(ODR_record, ptr);
890                 rr->record = ODR_record;
891                 rr->len = length;
892         }
893         zhandle->handle = point;
894         handle = zhandle;
895         rr->last_in_set = SvIV(last);
896         
897         if (!(rr->errcode))
898         {
899                 rr->errcode = SvIV(err_code);
900                 ptr = SvPV(err_string, length);
901                 ODR_errstr = (char *)odr_malloc(rr->stream, length + 1);
902                 strcpy(ODR_errstr, ptr);
903                 rr->errstring = ODR_errstr;
904         }
905         rr->surrogate_flag = SvIV(sur_flag);
906
907         wrbuf_free(oid_dotted, 1);
908         sv_free((SV*) href);
909         sv_free(basename);
910         sv_free(record);
911         sv_free(last);
912         sv_free(err_string);
913         sv_free(err_code),
914         sv_free(sur_flag);
915         sv_free(rep_form);
916         
917         return 0;
918 }
919
920
921 int bend_present(void *handle, bend_present_rr *rr)
922 {
923
924         HV *href;
925         SV **temp;
926         SV *err_code;
927         SV *err_string;
928         SV *hits;
929         SV *point;
930         STRLEN len;
931         Z_RecordComposition *composition;
932         Z_ElementSetNames *simple;
933         char *ODR_errstr;
934         char *ptr;
935         Zfront_handle *zhandle = (Zfront_handle *)handle;
936         CV* handler_cv = 0;
937
938 /*      WRBUF oid_dotted; */
939
940         dSP;
941         ENTER;
942         SAVETMPS;
943
944         href = newHV();
945         hv_store(href, "HANDLE", 6, zhandle->handle, 0);
946         hv_store(href, "ERR_CODE", 8, newSViv(0), 0);
947         hv_store(href, "ERR_STR", 7, newSVpv("", 0), 0);
948         hv_store(href, "START", 5, newSViv(rr->start), 0);
949         hv_store(href, "SETNAME", 7, newSVpv(rr->setname, 0), 0);
950         hv_store(href, "NUMBER", 6, newSViv(rr->number), 0);
951         /*oid_dotted = oid2dotted(rr->request_format_raw);
952         hv_store(href, "REQ_FORM", 8, newSVpv((char *)oid_dotted->buf, oid_dotted->pos), 0);*/
953         hv_store(href, "HITS", 4, newSViv(0), 0);
954         hv_store(href, "PID", 3, newSViv(getpid()), 0);
955         if (rr->comp)
956         {
957                 composition = rr->comp;
958                 if (composition->which == Z_RecordComp_simple)
959                 {
960                         simple = composition->u.simple;
961                         if (simple->which == Z_ElementSetNames_generic)
962                         {
963                                 hv_store(href, "COMP", 4, newSVpv(simple->u.generic, 0), 0);
964                         } 
965                         else
966                         {
967                                 rr->errcode = 26;
968                                 return 0;
969                         }
970                 }
971                 else
972                 {
973                         rr->errcode = 26;
974                         return 0;
975                 }
976         }
977
978         PUSHMARK(sp);
979         
980         XPUSHs(sv_2mortal(newRV( (SV*) href)));
981         
982         PUTBACK;
983         
984         handler_cv = simpleserver_sv2cv( present_ref );
985         perl_call_sv( (SV *) handler_cv, G_SCALAR | G_DISCARD);
986         
987         SPAGAIN;
988
989         temp = hv_fetch(href, "ERR_CODE", 8, 1);
990         err_code = newSVsv(*temp);
991
992         temp = hv_fetch(href, "ERR_STR", 7, 1);
993         err_string = newSVsv(*temp);
994
995         temp = hv_fetch(href, "HITS", 4, 1);
996         hits = newSVsv(*temp);
997
998         temp = hv_fetch(href, "HANDLE", 6, 1);
999         point = newSVsv(*temp);
1000
1001         PUTBACK;
1002         FREETMPS;
1003         LEAVE;
1004         
1005         hv_undef(href);
1006         rr->errcode = SvIV(err_code);
1007         rr->hits = SvIV(hits);
1008
1009         ptr = SvPV(err_string, len);
1010         ODR_errstr = (char *)odr_malloc(rr->stream, len + 1);
1011         strcpy(ODR_errstr, ptr);
1012         rr->errstring = ODR_errstr;
1013 /*      wrbuf_free(oid_dotted, 1);*/
1014         zhandle->handle = point;
1015         handle = zhandle;
1016         sv_free(err_code);
1017         sv_free(err_string);
1018         sv_free(hits);
1019         sv_free( (SV*) href);
1020
1021         return 0;
1022 }
1023
1024
1025 int bend_esrequest(void *handle, bend_esrequest_rr *rr)
1026 {
1027         perl_call_sv(esrequest_ref, G_VOID | G_DISCARD | G_NOARGS);
1028         return 0;
1029 }
1030
1031
1032 int bend_delete(void *handle, bend_delete_rr *rr)
1033 {
1034         perl_call_sv(delete_ref, G_VOID | G_DISCARD | G_NOARGS);
1035         return 0;
1036 }
1037
1038
1039 int bend_scan(void *handle, bend_scan_rr *rr)
1040 {
1041         HV *href;
1042         AV *aref;
1043         AV *list;
1044         AV *entries;
1045         HV *scan_item;
1046         struct scan_entry *scan_list;
1047         struct scan_entry *buffer;
1048         int *step_size = rr->step_size;
1049         int i;
1050         char **basenames;
1051         SV **temp;
1052         SV *err_code = sv_newmortal();
1053         SV *err_str = sv_newmortal();
1054         SV *point = sv_newmortal();
1055         SV *status = sv_newmortal();
1056         SV *number = sv_newmortal();
1057         char *ptr;
1058         char *ODR_errstr;
1059         STRLEN len;
1060         int term_len;
1061         SV *term_tmp;
1062         SV *entries_ref;
1063         Zfront_handle *zhandle = (Zfront_handle *)handle;
1064         CV* handler_cv = 0;
1065
1066         dSP;
1067         ENTER;
1068         SAVETMPS;
1069         href = newHV();
1070         list = newAV();
1071         if (rr->term->term->which == Z_Term_general)
1072         {
1073                 term_len = rr->term->term->u.general->len;
1074                 hv_store(href, "TERM", 4, newSVpv(rr->term->term->u.general->buf, term_len), 0);
1075         } else {
1076                 rr->errcode = 229;      /* Unsupported term type */
1077                 return 0;
1078         }
1079         hv_store(href, "STEP", 4, newSViv(*step_size), 0);
1080         hv_store(href, "NUMBER", 6, newSViv(rr->num_entries), 0);
1081         hv_store(href, "POS", 3, newSViv(rr->term_position), 0);
1082         hv_store(href, "ERR_CODE", 8, newSViv(0), 0);
1083         hv_store(href, "ERR_STR", 7, newSVpv("", 0), 0);
1084         hv_store(href, "HANDLE", 6, zhandle->handle, 0);
1085         hv_store(href, "STATUS", 6, newSViv(BEND_SCAN_SUCCESS), 0);
1086         hv_store(href, "ENTRIES", 7, newRV((SV *) list), 0);
1087         aref = newAV();
1088         basenames = rr->basenames;
1089         for (i = 0; i < rr->num_bases; i++)
1090         {
1091                 av_push(aref, newSVpv(*basenames++, 0));
1092         }
1093         hv_store(href, "DATABASES", 9, newRV( (SV*) aref), 0);
1094
1095         PUSHMARK(sp);
1096
1097         XPUSHs(sv_2mortal(newRV( (SV*) href)));
1098
1099         PUTBACK;
1100
1101         handler_cv = simpleserver_sv2cv( scan_ref );
1102         perl_call_sv( (SV *) handler_cv, G_SCALAR | G_DISCARD);
1103
1104         SPAGAIN;
1105
1106         temp = hv_fetch(href, "ERR_CODE", 8, 1);
1107         err_code = newSVsv(*temp);
1108
1109         temp = hv_fetch(href, "ERR_STR", 7, 1);
1110         err_str = newSVsv(*temp);
1111
1112         temp = hv_fetch(href, "HANDLE", 6, 1);
1113         point = newSVsv(*temp);
1114
1115         temp = hv_fetch(href, "STATUS", 6, 1);
1116         status = newSVsv(*temp);
1117         
1118         temp = hv_fetch(href, "NUMBER", 6, 1);
1119         number = newSVsv(*temp);
1120
1121         temp = hv_fetch(href, "ENTRIES", 7, 1);
1122         entries_ref = newSVsv(*temp);
1123
1124         PUTBACK;
1125         FREETMPS;
1126         LEAVE;
1127
1128         ptr = SvPV(err_str, len);
1129         ODR_errstr = (char *)odr_malloc(rr->stream, len + 1);
1130         strcpy(ODR_errstr, ptr);
1131         rr->errstring = ODR_errstr;
1132         rr->errcode = SvIV(err_code);
1133         rr->num_entries = SvIV(number);
1134         rr->status = SvIV(status);
1135         scan_list = (struct scan_entry *) odr_malloc (rr->stream, rr->num_entries * sizeof(*scan_list));
1136         buffer = scan_list;
1137         entries = (AV *)SvRV(entries_ref);
1138         for (i = 0; i < rr->num_entries; i++)
1139         {
1140                 scan_item = (HV *)SvRV(sv_2mortal(av_shift(entries)));
1141                 temp = hv_fetch(scan_item, "TERM", 4, 1);
1142                 ptr = SvPV(*temp, len);
1143                 buffer->term = (char *) odr_malloc (rr->stream, len + 1); 
1144                 strcpy(buffer->term, ptr);
1145                 temp = hv_fetch(scan_item, "OCCURRENCE", 10, 1); 
1146                 buffer->occurrences = SvIV(*temp);
1147                 buffer++;
1148                 hv_undef(scan_item);
1149         }
1150         rr->entries = scan_list;
1151         zhandle->handle = point;
1152         handle = zhandle;
1153         sv_free(err_code);
1154         sv_free(err_str);
1155         sv_free(status);
1156         sv_free(number);
1157         hv_undef(href);
1158         sv_free((SV *)href);
1159         av_undef(aref);
1160         sv_free((SV *)aref);
1161         av_undef(list);
1162         sv_free((SV *)list);
1163         av_undef(entries);
1164         /*sv_free((SV *)entries);*/
1165         sv_free(entries_ref);
1166
1167         return 0;
1168 }
1169
1170
1171 bend_initresult *bend_init(bend_initrequest *q)
1172 {
1173         int dummy = simpleserver_clone();
1174         bend_initresult *r = (bend_initresult *) odr_malloc (q->stream, sizeof(*r));
1175         HV *href;
1176         SV **temp;
1177         SV *id;
1178         SV *name;
1179         SV *ver;
1180         SV *err_str;
1181         SV *status;
1182         NMEM nmem = nmem_create();
1183         Zfront_handle *zhandle =  (Zfront_handle *) nmem_malloc (nmem,
1184                         sizeof(*zhandle));
1185         STRLEN len;
1186         int n;
1187         SV *handle;
1188         /*char *name_ptr;
1189         char *ver_ptr;*/
1190         char *ptr;
1191         char *user = NULL;
1192         char *passwd = NULL;
1193         CV* handler_cv = 0;
1194
1195         dSP;
1196         ENTER;
1197         SAVETMPS;
1198
1199         zhandle->nmem = nmem;
1200         zhandle->stop_flag = 0;
1201         /*q->bend_sort = bend_sort;*/
1202         if (search_ref)
1203         {
1204                 q->bend_search = bend_search;
1205         }
1206         if (present_ref)
1207         {
1208                 q->bend_present = bend_present;
1209         }
1210         /*q->bend_esrequest = bend_esrequest;*/
1211         /*q->bend_delete = bend_delete;*/
1212         if (fetch_ref)
1213         {
1214                 q->bend_fetch = bend_fetch;
1215         }
1216         if (scan_ref)
1217         {
1218                 q->bend_scan = bend_scan;
1219         }
1220         href = newHV(); 
1221         hv_store(href, "IMP_ID", 6, newSVpv("", 0), 0);
1222         hv_store(href, "IMP_NAME", 8, newSVpv("", 0), 0);
1223         hv_store(href, "IMP_VER", 7, newSVpv("", 0), 0);
1224         hv_store(href, "ERR_CODE", 8, newSViv(0), 0);
1225         hv_store(href, "ERR_STR", 7, newSViv(0), 0);
1226         hv_store(href, "PEER_NAME", 9, newSVpv(q->peer_name, 0), 0);
1227         hv_store(href, "HANDLE", 6, newSVsv(&sv_undef), 0);
1228         hv_store(href, "PID", 3, newSViv(getpid()), 0);
1229         if (q->auth) {
1230             if (q->auth->which == Z_IdAuthentication_open) {
1231                 char *openpass = xstrdup (q->auth->u.open);
1232                 char *cp = strchr (openpass, '/');
1233                 if (cp) {
1234                     *cp = '\0';
1235                     user = nmem_strdup (odr_getmem (q->stream), openpass);
1236                     passwd = nmem_strdup (odr_getmem (q->stream), cp + 1);
1237                 }
1238                 xfree(openpass);
1239             } else if (q->auth->which == Z_IdAuthentication_idPass) {
1240                 user = q->auth->u.idPass->userId;
1241                 passwd = q->auth->u.idPass->password;
1242             }
1243             /* ### some code paths have user/password unassigned here */
1244             hv_store(href, "USER", 4, newSVpv(user, 0), 0);
1245             hv_store(href, "PASS", 4, newSVpv(passwd, 0), 0);
1246         }
1247
1248         PUSHMARK(sp);   
1249
1250         XPUSHs(sv_2mortal(newRV( (SV*) href)));
1251
1252         PUTBACK;
1253
1254         if (init_ref != NULL)
1255         {
1256              handler_cv = simpleserver_sv2cv( init_ref );
1257              perl_call_sv( (SV *) handler_cv, G_SCALAR | G_DISCARD);
1258         }
1259
1260         SPAGAIN;
1261
1262         temp = hv_fetch(href, "IMP_ID", 6, 1);
1263         id = newSVsv(*temp);
1264
1265         temp = hv_fetch(href, "IMP_NAME", 8, 1);
1266         name = newSVsv(*temp);
1267
1268         temp = hv_fetch(href, "IMP_VER", 7, 1);
1269         ver = newSVsv(*temp);
1270
1271         temp = hv_fetch(href, "ERR_CODE", 8, 1);
1272         status = newSVsv(*temp);
1273
1274         temp = hv_fetch(href, "ERR_STR", 7, 1);
1275         err_str = newSVsv(*temp);
1276
1277         temp = hv_fetch(href, "HANDLE", 6, 1);
1278         handle= newSVsv(*temp);
1279
1280         hv_undef(href);
1281         PUTBACK;
1282         FREETMPS;
1283         LEAVE;
1284         zhandle->handle = handle;
1285         r->errcode = SvIV(status);
1286         ptr = SvPV(err_str, len);
1287         r->errstring = (char *)odr_malloc(q->stream, len + 1);
1288         strcpy(r->errstring, ptr);
1289         sv_free(err_str);
1290         r->handle = zhandle;
1291         ptr = SvPV(id, len);
1292         q->implementation_id = nmem_strdup(nmem, ptr);
1293         ptr = SvPV(name, len);
1294         q->implementation_name = nmem_strdup(nmem, ptr);
1295         ptr = SvPV(ver, len);
1296         q->implementation_version = nmem_strdup(nmem, ptr);
1297         
1298         return r;
1299 }
1300
1301
1302 void bend_close(void *handle)
1303 {
1304         HV *href;
1305         Zfront_handle *zhandle = (Zfront_handle *)handle;
1306         SV **temp;
1307         CV* handler_cv = 0;
1308         int stop_flag = 0;
1309
1310         if (close_ref)
1311         {
1312                 dSP;
1313                 ENTER;
1314                 SAVETMPS;
1315                 href = newHV();
1316                 hv_store(href, "HANDLE", 6, zhandle->handle, 0);
1317
1318                 PUSHMARK(sp);
1319
1320                 XPUSHs(sv_2mortal(newRV((SV *)href)));
1321
1322                 PUTBACK;
1323         
1324                 handler_cv = simpleserver_sv2cv( close_ref );
1325                 perl_call_sv( (SV *) handler_cv, G_SCALAR | G_DISCARD);
1326         
1327                 SPAGAIN;
1328
1329                 PUTBACK;
1330                 FREETMPS;
1331                 LEAVE;
1332         }
1333         stop_flag = zhandle->stop_flag;
1334         nmem_destroy(zhandle->nmem);
1335         simpleserver_free();
1336
1337         if (stop_flag)
1338                 exit(0);
1339         return;
1340 }
1341
1342
1343 MODULE = Net::Z3950::SimpleServer       PACKAGE = Net::Z3950::SimpleServer
1344
1345 void
1346 set_init_handler(arg)
1347                 SV *arg
1348         CODE:
1349                 init_ref = newSVsv(arg);
1350                 
1351
1352 void
1353 set_close_handler(arg)
1354                 SV *arg
1355         CODE:
1356                 close_ref = newSVsv(arg);
1357
1358
1359 void
1360 set_sort_handler(arg)
1361                 SV *arg
1362         CODE:
1363                 sort_ref = newSVsv(arg);
1364
1365 void
1366 set_search_handler(arg)
1367                 SV *arg
1368         CODE:
1369                 search_ref = newSVsv(arg);
1370
1371
1372 void
1373 set_fetch_handler(arg)
1374                 SV *arg
1375         CODE:
1376                 fetch_ref = newSVsv(arg);
1377
1378
1379 void
1380 set_present_handler(arg)
1381                 SV *arg
1382         CODE:
1383                 present_ref = newSVsv(arg);
1384
1385
1386 void
1387 set_esrequest_handler(arg)
1388                 SV *arg
1389         CODE:
1390                 esrequest_ref = newSVsv(arg);
1391
1392
1393 void
1394 set_delete_handler(arg)
1395                 SV *arg
1396         CODE:
1397                 delete_ref = newSVsv(arg);
1398
1399
1400 void
1401 set_scan_handler(arg)
1402                 SV *arg
1403         CODE:
1404                 scan_ref = newSVsv(arg);
1405
1406
1407 int
1408 start_server(...)
1409         PREINIT:
1410                 char **argv;
1411                 char **argv_buf;
1412                 char *ptr;
1413                 int i;
1414                 STRLEN len;
1415         CODE:
1416                 argv_buf = (char **)xmalloc((items + 1) * sizeof(char *));
1417                 argv = argv_buf;
1418                 for (i = 0; i < items; i++)
1419                 {
1420                         ptr = SvPV(ST(i), len);
1421                         *argv_buf = (char *)xmalloc(len + 1);
1422                         strcpy(*argv_buf++, ptr); 
1423                 }
1424                 *argv_buf = NULL;
1425                 root_perl_context = PERL_GET_CONTEXT;
1426                 nmem_mutex_create(&simpleserver_mutex);
1427 #if 0
1428                 tst_clones();
1429 #endif
1430                 
1431                 RETVAL = statserv_main(items, argv, bend_init, bend_close);
1432         OUTPUT:
1433                 RETVAL
1434
1435
1436 int
1437 ScanSuccess()
1438         CODE:
1439                 RETVAL = BEND_SCAN_SUCCESS;
1440         OUTPUT:
1441                 RETVAL
1442
1443 int
1444 ScanPartial()
1445         CODE:
1446                 RETVAL = BEND_SCAN_PARTIAL;
1447         OUTPUT:
1448                 RETVAL
1449
1450