7a0b17a65969092230738cf30bb4bc95d7aa92ac
[simpleserver-moved-to-github.git] / SimpleServer.xs
1 /*
2  * Copyright (c) 2000, Index Data.
3  *
4  * Permission to use, copy, modify, distribute, and sell this software and
5  * its documentation, in whole or in part, for any purpose, is hereby granted,
6  * provided that:
7  *
8  * 1. This copyright and permission notice appear in all copies of the
9  * software and its documentation. Notices of copyright or attribution
10  * which appear at the beginning of any file must remain unchanged.
11  *
12  * 2. The name of Index Data or the individual authors may not be used to
13  * endorse or promote products derived from this software without specific
14  * prior written permission.
15  *
16  * THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT WARRANTY OF ANY KIND,
17  * EXPRESS, IMPLIED, OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
18  * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
19  * IN NO EVENT SHALL INDEX DATA BE LIABLE FOR ANY SPECIAL, INCIDENTAL,
20  * INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY DAMAGES
21  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER OR
22  * NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF
23  * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
24  * OF THIS SOFTWARE.
25  */
26
27
28 #include "EXTERN.h"
29 #include "perl.h"
30 #include "XSUB.h"
31 #include <yaz/backend.h>
32 #include <yaz/log.h>
33 #include <yaz/wrbuf.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <ctype.h>
37 #ifdef ASN_COMPILED
38 #include <yaz/ill.h>
39 #endif
40
41
42 typedef struct {
43         SV *handle;
44
45         SV *init_ref;
46         SV *close_ref;
47         SV *sort_ref;
48         SV *search_ref;
49         SV *fetch_ref;
50         SV *present_ref;
51         SV *esrequest_ref;
52         SV *delete_ref;
53         SV *scan_ref;
54 } Zfront_handle;
55
56 SV *init_ref = NULL;
57 SV *close_ref = NULL;
58 SV *sort_ref = NULL;
59 SV *search_ref = NULL;
60 SV *fetch_ref = NULL;
61 SV *present_ref = NULL;
62 SV *esrequest_ref = NULL;
63 SV *delete_ref = NULL;
64 SV *scan_ref = NULL;
65 int MAX_OID = 15;
66
67 static void oid2str(Odr_oid *o, WRBUF buf)
68 {
69     for (; *o >= 0; o++) {
70         char ibuf[16];
71         sprintf(ibuf, "%d", *o);
72         wrbuf_puts(buf, ibuf);
73         if (o[1] > 0)
74             wrbuf_putc(buf, '.');
75     }
76 }
77
78
79 static int rpn2pquery(Z_RPNStructure *s, WRBUF buf)
80 {
81     switch (s->which) {
82         case Z_RPNStructure_simple: {
83             Z_Operand *o = s->u.simple;
84
85             switch (o->which) {
86                 case Z_Operand_APT: {
87                     Z_AttributesPlusTerm *at = o->u.attributesPlusTerm;
88
89                     if (at->attributes) {
90                         int i;
91                         char ibuf[16];
92
93                         for (i = 0; i < at->attributes->num_attributes; i++) {
94                             wrbuf_puts(buf, "@attr ");
95                             if (at->attributes->attributes[i]->attributeSet) {
96                                 oid2str(at->attributes->attributes[i]->attributeSet, buf);
97                                 wrbuf_putc(buf, ' ');
98                             }
99                             sprintf(ibuf, "%d=", *at->attributes->attributes[i]->attributeType);
100                             assert(at->attributes->attributes[i]->which == Z_AttributeValue_numeric);
101                             wrbuf_puts(buf, ibuf);
102                             sprintf(ibuf, "%d ", *at->attributes->attributes[i]->value.numeric);
103                             wrbuf_puts(buf, ibuf);
104                         }
105                     }
106                     switch (at->term->which) {
107                         case Z_Term_general: {
108                             wrbuf_putc(buf, '"');
109                             wrbuf_write(buf, (char*) at->term->u.general->buf, at->term->u.general->len);
110                             wrbuf_puts(buf, "\" ");
111                             break;
112                         }
113                         default: abort();
114                     }
115                     break;
116                 }
117                 default: abort();
118             }
119             break;
120         }
121         case Z_RPNStructure_complex: {
122             Z_Complex *c = s->u.complex;
123
124             switch (c->roperator->which) {
125                 case Z_Operator_and: wrbuf_puts(buf, "@and "); break;
126                 case Z_Operator_or: wrbuf_puts(buf, "@or "); break;
127                 case Z_Operator_and_not: wrbuf_puts(buf, "@not "); break;
128                 case Z_Operator_prox: abort();
129                 default: abort();
130             }
131             if (!rpn2pquery(c->s1, buf))
132                 return 0;
133             if (!rpn2pquery(c->s2, buf))
134                 return 0;
135             break;
136         }
137         default: abort();
138     }
139     return 1;
140 }
141
142
143 WRBUF zquery2pquery(Z_Query *q)
144 {
145     WRBUF buf = wrbuf_alloc();
146
147     if (q->which != Z_Query_type_1 && q->which != Z_Query_type_101) 
148         return 0;
149     if (q->u.type_1->attributeSetId) {
150         /* Output attribute set ID */
151         wrbuf_puts(buf, "@attrset ");
152         oid2str(q->u.type_1->attributeSetId, buf);
153         wrbuf_putc(buf, ' ');
154     }
155     return rpn2pquery(q->u.type_1->RPNStructure, buf) ? buf : 0;
156 }
157
158
159 int bend_sort(void *handle, bend_sort_rr *rr)
160 {
161         perl_call_sv(sort_ref, G_VOID | G_DISCARD | G_NOARGS);
162         return;
163 }
164
165
166 int bend_search(void *handle, bend_search_rr *rr)
167 {
168         HV *href;
169         AV *aref;
170         SV **temp;
171         SV *hits;
172         SV *err_code;
173         SV *err_str;
174         char *ODR_errstr;
175         STRLEN len;
176         int i;
177         char **basenames;
178         int n;
179         WRBUF query;
180         char *ptr;
181         SV *point;
182         SV *ODR_point;
183         Zfront_handle *zhandle = (Zfront_handle *)handle;
184
185         dSP;
186         ENTER;
187         SAVETMPS;
188
189         aref = newAV();
190         basenames = rr->basenames;
191         for (i = 0; i < rr->num_bases; i++)
192         {
193                 av_push(aref, newSVpv(*basenames++, 0));
194         }
195         href = newHV();         
196         hv_store(href, "SETNAME", 7, newSVpv(rr->setname, 0), 0);
197         hv_store(href, "REPL_SET", 8, newSViv(rr->replace_set), 0);
198         hv_store(href, "ERR_CODE", 8, newSViv(0), 0);
199         hv_store(href, "ERR_STR", 7, newSVpv("", 0), 0);
200         hv_store(href, "HITS", 4, newSViv(0), 0);
201         hv_store(href, "DATABASES", 9, newRV( (SV*) aref), 0);
202         hv_store(href, "HANDLE", 6, zhandle->handle, 0);
203         query = zquery2pquery(rr->query);
204         if (query)
205         {
206                 hv_store(href, "QUERY", 5, newSVpv((char *)query->buf, query->pos), 0);
207         }
208         else
209         {       
210                 rr->errcode = 108;
211         }
212         PUSHMARK(sp);
213         
214         XPUSHs(sv_2mortal(newRV( (SV*) href)));
215         
216         PUTBACK;
217
218         n = perl_call_sv(search_ref, G_SCALAR | G_DISCARD);
219
220         SPAGAIN;
221
222         temp = hv_fetch(href, "HITS", 4, 1);
223         hits = newSVsv(*temp);
224
225         temp = hv_fetch(href, "ERR_CODE", 8, 1);
226         err_code = newSVsv(*temp);
227
228         temp = hv_fetch(href, "ERR_STR", 7, 1);
229         err_str = newSVsv(*temp);
230
231         temp = hv_fetch(href, "HANDLE", 6, 1);
232         point = newSVsv(*temp);
233
234         PUTBACK;
235         FREETMPS;
236         LEAVE;
237         
238         hv_undef(href);
239         av_undef(aref);
240         rr->hits = SvIV(hits);
241         rr->errcode = SvIV(err_code);
242         ptr = SvPV(err_str, len);
243         ODR_errstr = (char *)odr_malloc(rr->stream, len + 1);
244         strcpy(ODR_errstr, ptr);
245         rr->errstring = ODR_errstr;
246 /*      ODR_point = (SV *)odr_malloc(rr->stream, sizeof(*point));
247         memcpy(ODR_point, point, sizeof(*point));
248         zhandle->handle = ODR_point;*/
249         zhandle->handle = point;
250         handle = zhandle;
251         sv_free(hits);
252         sv_free(err_code);
253         sv_free(err_str);
254         sv_free( (SV*) aref);
255         sv_free( (SV*) href);
256         /*sv_free(point);*/
257         wrbuf_free(query, 1);
258         return 0;
259 }
260
261
262 WRBUF oid2dotted(int *oid)
263 {
264
265         WRBUF buf = wrbuf_alloc();
266         int dot = 0;
267
268         for (; *oid != -1 ; oid++)
269         {
270                 char ibuf[16];
271                 if (dot)
272                 {
273                         wrbuf_putc(buf, '.');
274                 }
275                 else
276                 {
277                         dot = 1;
278                 }
279                 sprintf(ibuf, "%d", *oid);
280                 wrbuf_puts(buf, ibuf);
281         }
282         return buf;
283 }
284                 
285
286 int dotted2oid(char *dotted, int *buffer)
287 {
288         int *oid;
289         char ibuf[16];
290         char *ptr;
291         int n = 0;
292
293         ptr = ibuf;
294         oid = buffer;
295         while (*dotted)
296         {
297                 if (*dotted == '.')
298                 {
299                         n++;
300                         if (n == MAX_OID)  /* Terminate if more than MAX_OID entries */
301                         {
302                                 *oid = -1;
303                                 return -1;
304                         }
305                         *ptr = 0;
306                         sscanf(ibuf, "%d", oid++);
307                         ptr = ibuf;
308                         dotted++;
309
310                 }
311                 else
312                 {
313                         *ptr++ = *dotted++;
314                 }
315         }
316         if (n < MAX_OID)
317         {
318                 *ptr = 0;
319                 sscanf(ibuf, "%d", oid++);
320         }
321         *oid = -1;
322         return 0;
323 }
324
325
326 int bend_fetch(void *handle, bend_fetch_rr *rr)
327 {
328         HV *href;
329         SV **temp;
330         SV *basename;
331         SV *record;
332         SV *last;
333         SV *err_code;
334         SV *err_string;
335         SV *sur_flag;
336         SV *point;
337         SV *rep_form;
338         char *ptr;
339         char *ODR_record;
340         char *ODR_basename;
341         char *ODR_errstr;
342         int *ODR_oid_buf;
343         WRBUF oid_dotted;
344         Zfront_handle *zhandle = (Zfront_handle *)handle;
345
346         Z_RecordComposition *composition;
347         Z_ElementSetNames *simple;
348         STRLEN length;
349         int oid;
350
351         dSP;
352         ENTER;
353         SAVETMPS;
354
355         rr->errcode = 0;
356         href = newHV();
357         hv_store(href, "SETNAME", 7, newSVpv(rr->setname, 0), 0);
358         temp = hv_store(href, "OFFSET", 6, newSViv(rr->number), 0);
359         oid_dotted = oid2dotted(rr->request_format_raw);
360         hv_store(href, "REQ_FORM", 8, newSVpv((char *)oid_dotted->buf, oid_dotted->pos), 0);
361         hv_store(href, "REP_FORM", 8, newSVpv((char *)oid_dotted->buf, oid_dotted->pos), 0);
362         hv_store(href, "BASENAME", 8, newSVpv("", 0), 0);
363         hv_store(href, "RECORD", 6, newSVpv("", 0), 0);
364         hv_store(href, "LAST", 4, newSViv(0), 0);
365         hv_store(href, "ERR_CODE", 8, newSViv(0), 0);
366         hv_store(href, "ERR_STR", 7, newSVpv("", 0), 0);
367         hv_store(href, "SUR_FLAG", 8, newSViv(0), 0);
368         hv_store(href, "HANDLE", 6, zhandle->handle, 0);
369         if (rr->comp)
370         {
371                 composition = rr->comp;
372                 if (composition->which == Z_RecordComp_simple)
373                 {
374                         simple = composition->u.simple;
375                         if (simple->which == 1)
376                         {
377                                 hv_store(href, "COMP", 4, newSVpv(simple->u.generic, 0), 0);
378                         } 
379                         else
380                         {
381                                 rr->errcode = 26;
382                         }
383                 }
384                 else
385                 {
386                         rr->errcode = 26;
387                 }
388         }
389
390         PUSHMARK(sp);
391
392         XPUSHs(sv_2mortal(newRV( (SV*) href)));
393
394         PUTBACK;
395         
396         perl_call_sv(fetch_ref, G_SCALAR | G_DISCARD);
397
398         SPAGAIN;
399
400         temp = hv_fetch(href, "BASENAME", 8, 1);
401         basename = newSVsv(*temp);
402
403         temp = hv_fetch(href, "RECORD", 6, 1);
404         record = newSVsv(*temp);
405
406         temp = hv_fetch(href, "LAST", 4, 1);
407         last = newSVsv(*temp);
408
409         temp = hv_fetch(href, "ERR_CODE", 8, 1);
410         err_code = newSVsv(*temp);
411
412         temp = hv_fetch(href, "ERR_STR", 7, 1),
413         err_string = newSVsv(*temp);
414
415         temp = hv_fetch(href, "SUR_FLAG", 8, 1);
416         sur_flag = newSVsv(*temp);
417
418         temp = hv_fetch(href, "REP_FORM", 8, 1);
419         rep_form = newSVsv(*temp);
420
421         temp = hv_fetch(href, "HANDLE", 6, 1);
422         point = newSVsv(*temp);
423
424         PUTBACK;
425         FREETMPS;
426         LEAVE;
427
428         hv_undef(href);
429         
430         ptr = SvPV(basename, length);
431         ODR_basename = (char *)odr_malloc(rr->stream, length + 1);
432         strcpy(ODR_basename, ptr);
433         rr->basename = ODR_basename;
434
435         ptr = SvPV(rep_form, length);
436         ODR_oid_buf = (int *)odr_malloc(rr->stream, (MAX_OID + 1) * sizeof(int));
437         if (dotted2oid(ptr, ODR_oid_buf) == -1)         /* Maximum number of OID elements exceeded */
438         {
439                 printf("Net::Z3950::SimpleServer: WARNING: OID structure too long, max length is %d\n", MAX_OID);
440         }
441         rr->output_format_raw = ODR_oid_buf;    
442         
443         ptr = SvPV(record, length);
444         ODR_record = (char *)odr_malloc(rr->stream, length + 1);
445         strcpy(ODR_record, ptr);
446         rr->record = ODR_record;
447         rr->len = length;
448
449         zhandle->handle = point;
450         handle = zhandle;
451         rr->last_in_set = SvIV(last);
452         
453         if (!(rr->errcode))
454         {
455                 rr->errcode = SvIV(err_code);
456                 ptr = SvPV(err_string, length);
457                 ODR_errstr = (char *)odr_malloc(rr->stream, length + 1);
458                 strcpy(ODR_errstr, ptr);
459                 rr->errstring = ODR_errstr;
460         }
461         rr->surrogate_flag = SvIV(sur_flag);
462
463         wrbuf_free(oid_dotted, 1);
464         sv_free((SV*) href);
465         sv_free(basename);
466         sv_free(record);
467         sv_free(last);
468         sv_free(err_string);
469         sv_free(err_code),
470         sv_free(sur_flag);
471         sv_free(rep_form);
472         
473         return 0;
474 }
475
476
477 int bend_present(void *handle, bend_present_rr *rr)
478 {
479
480         int n;
481         HV *href;
482         SV **temp;
483         SV *err_code;
484         SV *err_string;
485         STRLEN len;
486         Z_RecordComposition *composition;
487         Z_ElementSetNames *simple;
488         char *ODR_errstr;
489         char *ptr;
490
491         dSP;
492         ENTER;
493         SAVETMPS;
494
495         href = newHV();
496         hv_store(href, "ERR_CODE", 8, newSViv(0), 0);
497         hv_store(href, "ERR_STR", 7, newSVpv("", 0), 0);
498         hv_store(href, "START", 5, newSViv(rr->start), 0);
499         hv_store(href, "SETNAME", 7, newSVpv(rr->setname, 0), 0);
500         hv_store(href, "NUMBER", 6, newSViv(rr->number), 0);
501         if (rr->comp)
502         {
503                 composition = rr->comp;
504                 if (composition->which == 1)
505                 {
506                         simple = composition->u.simple;
507                         if (simple->which == 1)
508                         {
509                                 hv_store(href, "COMP", 4, newSVpv(simple->u.generic, 0), 0);
510                         } 
511                         else
512                         {
513                                 rr->errcode = 26;
514                                 return 0;
515                         }
516                 }
517                 else
518                 {
519                         rr->errcode = 26;
520                         return 0;
521                 }
522         }
523
524         PUSHMARK(sp);
525         
526         XPUSHs(sv_2mortal(newRV( (SV*) href)));
527         
528         PUTBACK;
529         
530         n = perl_call_sv(present_ref, G_SCALAR | G_DISCARD);
531         
532         SPAGAIN;
533
534         temp = hv_fetch(href, "ERR_CODE", 8, 1);
535         err_code = newSVsv(*temp);
536
537         temp = hv_fetch(href, "ERR_STR", 7, 1);
538         err_string = newSVsv(*temp);
539
540         PUTBACK;
541         FREETMPS;
542         LEAVE;
543         
544         hv_undef(href);
545         rr->errcode = SvIV(err_code);
546
547         ptr = SvPV(err_string, len);
548         ODR_errstr = (char *)odr_malloc(rr->stream, len + 1);
549         strcpy(ODR_errstr, ptr);
550         rr->errstring = ODR_errstr;
551
552         sv_free(err_code);
553         sv_free(err_string);
554         sv_free( (SV*) href);
555
556         return 0;
557 }
558
559
560 int bend_esrequest(void *handle, bend_esrequest_rr *rr)
561 {
562         perl_call_sv(esrequest_ref, G_VOID | G_DISCARD | G_NOARGS);
563         return 0;
564 }
565
566
567 int bend_delete(void *handle, bend_delete_rr *rr)
568 {
569         perl_call_sv(delete_ref, G_VOID | G_DISCARD | G_NOARGS);
570         return 0;
571 }
572
573
574 int bend_scan(void *handle, bend_scan_rr *rr)
575 {
576         perl_call_sv(scan_ref, G_VOID | G_DISCARD | G_NOARGS);
577         return 0;
578 }
579
580
581 bend_initresult *bend_init(bend_initrequest *q)
582 {
583         bend_initresult *r = (bend_initresult *) odr_malloc (q->stream, sizeof(*r));
584         HV *href;
585         SV **temp;
586         SV *name;
587         SV *ver;
588         SV *err_str;
589         SV *status;
590         Zfront_handle *zhandle =  (Zfront_handle *) xmalloc (sizeof(*zhandle));
591         STRLEN len;
592         int n;
593         SV *handle;
594         /*char *name_ptr;
595         char *ver_ptr;*/
596         char *ptr;
597
598         dSP;
599         ENTER;
600         SAVETMPS;
601
602         /*q->bend_sort = bend_sort;*/
603         if (search_ref)
604         {
605                 q->bend_search = bend_search;
606         }
607         /*q->bend_present = present;*/
608         /*q->bend_esrequest = bend_esrequest;*/
609         /*q->bend_delete = bend_delete;*/
610         if (fetch_ref)
611         {
612                 q->bend_fetch = bend_fetch;
613         }
614         /*q->bend_scan = bend_scan;*/
615         href = newHV(); 
616         hv_store(href, "IMP_NAME", 8, newSVpv("", 0), 0);
617         hv_store(href, "IMP_VER", 7, newSVpv("", 0), 0);
618         hv_store(href, "ERR_CODE", 8, newSViv(0), 0);
619         hv_store(href, "HANDLE", 6, newSVsv(&sv_undef), 0);
620
621         PUSHMARK(sp);   
622
623         XPUSHs(sv_2mortal(newRV( (SV*) href)));
624
625         PUTBACK;
626
627         if (init_ref != NULL)
628         {
629                 perl_call_sv(init_ref, G_SCALAR | G_DISCARD);
630         }
631
632         SPAGAIN;
633
634         temp = hv_fetch(href, "IMP_NAME", 8, 1);
635         name = newSVsv(*temp);
636
637         temp = hv_fetch(href, "IMP_VER", 7, 1);
638         ver = newSVsv(*temp);
639
640         temp = hv_fetch(href, "ERR_CODE", 8, 1);
641         status = newSVsv(*temp);
642
643         temp = hv_fetch(href, "HANDLE", 6, 1);
644         handle= newSVsv(*temp);
645
646         hv_undef(href);
647         PUTBACK;
648         FREETMPS;
649         LEAVE;
650         zhandle->handle = handle;
651         r->errcode = SvIV(status);
652         r->handle = zhandle;
653         ptr = SvPV(name, len);
654         q->implementation_name = (char *)xmalloc(len + 1);
655         strcpy(q->implementation_name, ptr);
656 /*      q->implementation_name = SvPV(name, len);*/
657         ptr = SvPV(ver, len);
658         q->implementation_version = (char *)xmalloc(len + 1);
659         strcpy(q->implementation_version, ptr);
660         
661         return r;
662 }
663
664
665 void bend_close(void *handle)
666 {
667         HV *href;
668         Zfront_handle *zhandle = (Zfront_handle *)handle;
669         SV **temp;
670
671         dSP;
672         ENTER;
673         SAVETMPS;
674
675         if (close_ref == NULL)
676         {
677                 return;
678         }
679
680         href = newHV();
681         hv_store(href, "HANDLE", 6, zhandle->handle, 0);
682
683         PUSHMARK(sp);
684
685         XPUSHs(sv_2mortal(newRV((SV *)href)));
686
687         PUTBACK;
688         
689         perl_call_sv(close_ref, G_SCALAR | G_DISCARD);
690         
691         SPAGAIN;
692
693         PUTBACK;
694         FREETMPS;
695         LEAVE;
696
697         xfree(handle);
698         
699         return;
700 }
701
702
703 MODULE = Net::Z3950::SimpleServer       PACKAGE = Net::Z3950::SimpleServer
704
705 void
706 set_init_handler(arg)
707                 SV *arg
708         CODE:
709                 init_ref = newSVsv(arg);
710                 
711
712 void
713 set_close_handler(arg)
714                 SV *arg
715         CODE:
716                 close_ref = newSVsv(arg);
717
718
719 void
720 set_sort_handler(arg)
721                 SV *arg
722         CODE:
723                 sort_ref = newSVsv(arg);
724
725 void
726 set_search_handler(arg)
727                 SV *arg
728         CODE:
729                 search_ref = newSVsv(arg);
730
731
732 void
733 set_fetch_handler(arg)
734                 SV *arg
735         CODE:
736                 fetch_ref = newSVsv(arg);
737
738
739 void
740 set_present_handler(arg)
741                 SV *arg
742         CODE:
743                 present_ref = newSVsv(arg);
744
745
746 void
747 set_esrequest_handler(arg)
748                 SV *arg
749         CODE:
750                 esrequest_ref = newSVsv(arg);
751
752
753 void
754 set_delete_handler(arg)
755                 SV *arg
756         CODE:
757                 delete_ref = newSVsv(arg);
758
759
760 void
761 set_scan_handler(arg)
762                 SV *arg
763         CODE:
764                 scan_ref = newSVsv(arg);
765
766
767 int
768 start_server(...)
769         PREINIT:
770                 char **argv;
771                 char **argv_buf;
772                 char *ptr;
773                 int i;
774                 STRLEN len;
775         CODE:
776                 argv_buf = (char **)xmalloc((items + 1) * sizeof(char *));
777                 argv = argv_buf;
778                 for (i = 0; i < items; i++)
779                 {
780                         ptr = SvPV(ST(i), len);
781                         *argv_buf = (char *)xmalloc(len + 1);
782                         strcpy(*argv_buf++, ptr); 
783                 }
784                 *argv_buf = NULL;
785                 
786                 RETVAL = statserv_main(items, argv, bend_init, bend_close);
787         OUTPUT:
788                 RETVAL