Using yaz_log instead of logf
[yaz-moved-to-github.git] / srwapps / srw-gateway.c
1 /* $Id: srw-gateway.c,v 1.1 2003-01-06 08:20:28 adam Exp $
2    Copyright (C) 2002-2003
3    Index Data Aps
4
5 This file is part of the YAZ toolkit.
6
7 See the file LICENSE.
8 */
9
10 /*
11  * TODO:
12  *
13  * TTL for targets. Separate thread for cleanup.
14  * External target config and aliases.
15  */
16
17 /* note that soapH.h defines _REENTRANT so we check for it here */
18 #ifdef _REENTRANT
19 #include <pthread.h>
20 #define USE_THREADS 1
21 #else
22 #define USE_THREADS 0
23 #endif
24
25 #include <yaz/srw-util.h>
26 #include <yaz/xmalloc.h>
27 #include <yaz/zoom.h>
28 #include <yaz/log.h>
29 #include <yaz/options.h>
30 #include <yaz/wrbuf.h>
31
32 #define RESULT_SETS 0
33 #define SRW_DEBUG 1
34
35 struct tset {
36     ZOOM_resultset m_r;
37     long m_expiry_sec;   /* date of creation */
38     int m_idle_time;
39     char *m_query;
40     char *m_schema;
41     struct tset *m_next;
42 };
43
44 struct target {
45     ZOOM_connection m_c;
46     char *m_name;
47     int m_in_use;
48     struct tset *m_sets;
49     struct target *next;
50 };
51
52 struct srw_prop {
53     int optimize_level;
54     int idle_time;
55     int max_sets;
56     xslt_maps maps;
57 };
58
59 static cql_transform_t cql_transform_handle = 0;
60 static struct target *target_list = 0;
61 #if USE_THREADS
62 static pthread_mutex_t target_mutex = PTHREAD_MUTEX_INITIALIZER;
63 #define mylock(x) pthread_mutex_lock(x)
64 #define myunlock(x) pthread_mutex_unlock(x)
65 #else
66 #define mylock(x)
67 #define myunlock(x)
68 #endif
69
70 #define ERROR_NO_TARGET -1
71 #define ERROR_BAD_CQL   10
72
73 static int diag_bib1_to_srw (int code)
74 {
75     static int map[] = {
76         1, 1,
77         2, 2,
78         3, 11,
79         4, 35,
80         5, 12,
81         6, 30,
82         7, 30,
83         8, 32,
84         9, 29,
85         10, 10,
86         11, 12,
87         13, 61,
88         14, 63,
89         15, 68,
90         16, 70,
91         17, 70,
92         18, 50,
93         19, 55,
94         20, 56, 
95         21, 52,
96         22, 50,
97         /* 23-26 no map */
98         27, 51,
99         28, 52,
100         29, 52,
101         30, 51,
102         31, 52,
103         32, 52,
104         33, 52,
105         /* 100 -105 */
106         106, 66,
107         107, 11,
108         108, 10,
109         109, 2,
110         110, 37,
111         /* 111- 112 */
112         113, 10,
113         114, 16,
114         115, 16,
115         116, 16,
116         117, 19,
117         118, 22,
118         119, 32,
119         120, 28,
120         121, 15,
121         122, 32,
122         123, 22,
123         124, 24,
124         125, 36,
125         126, 36, 
126         127, 36,
127         128, 51,
128         129, 39,
129         130, 43,
130         131, 40,
131         132, 42,
132         201, 44,
133         202, 41,
134         203, 43,
135         /* 205 */
136         0
137     };
138     const int *p = map;
139     while (*p)
140     {
141         if (code == *p)
142             return p[1];
143         p += 2;
144     }
145     return 0;
146 }
147
148 static int searchRetrieve(void *userinfo,
149                           struct soap * soap,
150                           xsd__string  *query,
151                           struct xcql__operandType *xQuery,     
152                           xsd__string *sortKeys,
153                           struct xsort__xSortKeysType *xSortKeys,
154                           xsd__integer *startRecord,
155                           xsd__integer *maximumRecords,
156                           xsd__string *recordSchema,
157                           xsd__string *recordPacking,
158                           struct zs__searchRetrieveResponse *res);
159 static int explain (void *userinfo,
160                     struct soap *soap,
161                     struct zs__explainResponse *explainResponse);
162
163 struct target *target_use (const char *action, const char *query,
164                            const char *schema, struct tset **set,
165                            struct srw_prop *prop)
166 {
167     char name[80];
168     struct target *l = 0;
169     struct timeval tv;
170     struct tset **ssp = 0;
171     long now;
172     int no_sets = 0;
173
174     gettimeofday(&tv, 0);
175     now = tv.tv_sec;
176
177     if (strlen(action) >= 80)
178         action = "localhost:210";
179     if (strchr(action, '/') || strchr(action, ':'))
180         strcpy (name, action);
181     else
182     {
183         strcpy (name, "localhost/");
184         if (*action == '\0')
185             strcat (name, "Default");
186         else
187             strcat (name, action);
188     }
189
190     /* See if we have the target and the same query */
191     if (query)
192         for (l = target_list; l; l = l->next)
193             if (!l->m_in_use && !strcmp (l->m_name, name))
194             {
195                 struct tset *s = l->m_sets;
196                 for (; s; s = s->m_next)
197                     if (!strcmp(s->m_query, query) && 
198                         !strcmp (s->m_schema, schema))
199                     {
200                         *set = s;
201                         return l;
202                     }
203             }
204     
205     /* OK, see if we have the target, then.. */
206     for (l = target_list; l; l = l->next)
207         if (!strcmp (l->m_name, name) && !l->m_in_use)
208         {
209             struct tset *s = l->m_sets;
210             for (; s; s = s->m_next)
211                 /* if m_expiry_sec is 0, the condition is true below */
212                 if (s->m_expiry_sec < now)
213                 {
214                     xfree (s->m_query);
215                     s->m_query = xstrdup("");
216                     xfree (s->m_schema);
217                     s->m_schema = xstrdup("");
218                     ZOOM_resultset_destroy(s->m_r);
219                     s->m_r = 0;
220                     *set = s;
221                     return l;
222                 }
223             break;
224         }
225     if (!l)
226     {
227         /* allright. Have to make a new one */
228         l = xmalloc (sizeof(*l));
229         l->m_name = xstrdup (name);
230         l->m_in_use = 1;
231         l->m_c = 0;
232         l->m_sets = 0;
233         l->next = target_list;
234         target_list = l;
235     }
236     for (ssp = &l->m_sets; *ssp; ssp = &(*ssp)->m_next)
237         no_sets++;
238     *ssp = xmalloc(sizeof(**ssp));
239     (*ssp)->m_next = 0;
240     (*ssp)->m_query = xstrdup("");
241     (*ssp)->m_schema = xstrdup("");
242     (*ssp)->m_r = 0;
243     (*ssp)->m_expiry_sec = 0;
244     (*ssp)->m_idle_time = (no_sets >= prop->max_sets ? 0 : prop->idle_time);
245     *set = *ssp;
246     return l;
247 }
248
249 static void target_destroy (struct target *t)
250 {
251     struct target **tp;
252
253     mylock(&target_mutex);
254
255     for (tp = &target_list; *tp; tp = &(*tp)->next)
256         if (*tp == t)
257         {
258             struct tset *s = t->m_sets;
259             while (s)
260             {
261                 struct tset *s_next = s->m_next;
262                 xfree (s->m_query);
263                 xfree (s->m_schema);
264                 ZOOM_resultset_destroy (s->m_r);
265                 xfree (s);
266                 s = s_next;
267             }
268
269             *tp = t->next;
270
271             ZOOM_connection_destroy (t->m_c);
272
273             xfree (t->m_name);
274             xfree (t);
275             break;
276         }
277     myunlock(&target_mutex);
278 }
279     
280 static void target_leave (struct target *l)
281 {
282     mylock(&target_mutex);
283     l->m_in_use = 0;
284
285     if (1)
286     {
287         struct tset *s = l->m_sets;
288         for (; s; s = s->m_next)
289             yaz_log(LOG_LOG, " set %s q=%s", 
290                     (s->m_r ? ZOOM_resultset_option_get(s->m_r,"setname"):""),
291                     s->m_query);
292     }
293     myunlock(&target_mutex);
294 }
295
296 static void standalone(struct soap *soap, const char *host, int port,
297                        int max_thr, struct srw_prop *properties)
298 {
299     struct soap **soap_thr = malloc (sizeof(*soap_thr) * max_thr);
300 #if USE_THREADS
301     pthread_t *tid = malloc (sizeof(pthread_t) * max_thr);
302 #endif
303     int m, s, i;
304     int cno = 0;
305     int stop = 0;
306     char fname[40];
307     
308     m = soap_bind(soap, 0, port, 100);
309     if (m < 0)
310     {
311         yaz_log (LOG_WARN|LOG_ERRNO, "Cannot bind to %d", port);
312         stop = 1;
313     }
314
315     for (i = 0; i<max_thr; i++)
316         soap_thr[i] = 0;
317
318     while (!stop)
319     {
320         for (i = 0; i<max_thr; i++)
321         {
322             s = soap_accept(soap);
323             if (s < 0)
324                 break;
325             cno++;
326             if (!soap_thr[i])
327             {
328                 soap_thr[i] = soap_new();
329                 if (!soap_thr[i])
330                 {
331                     stop = 1;
332                     break;
333                 }
334             }
335             else
336             {
337 #if USE_THREADS
338                 if (max_thr > 1)          /* static mode for max_thr <= 1 */
339                     pthread_join(tid[i], 0);
340 #endif
341                 soap_end(soap_thr[i]);
342             }
343
344 #if SRW_DEBUG
345             sprintf (fname, "srw.recv.%05d.log", cno);
346             remove (fname);
347             soap_set_recv_logfile(soap_thr[i], fname);
348             
349             sprintf (fname, "srw.sent.%05d.log", cno);
350             remove (fname);
351             soap_set_sent_logfile(soap_thr[i], fname);
352             
353             sprintf (fname, "srw.test.%05d.log", cno);
354             remove (fname);
355             soap_set_test_logfile(soap_thr[i], fname);
356
357             yaz_log (LOG_LOG, "starting session %d %ld.%ld.%ld.%ld", cno,
358                      (long) (soap->ip>>24) & 0xff,
359                      (long) (soap->ip>>16) & 0xff,
360                      (long) (soap->ip>>8) & 0xff,
361                      (long) soap->ip & 0xff);
362 #endif
363             soap_thr[i]->encodingStyle = 0;
364             soap_thr[i]->socket = s;
365             soap_thr[i]->user = properties;
366 #if USE_THREADS
367             if (max_thr <= 1)
368                 yaz_srw_serve(soap_thr[i], properties,
369                               searchRetrieve, explain);  /* static mode .. */
370             else
371                 pthread_create(&tid[i], 0, (void*(*)(void*))soap_serve,
372                                soap_thr[i]);
373 #else
374             yaz_srw_serve(soap_thr[i], properties,
375                           searchRetrieve, explain);  /* static mode .. */
376 #endif
377         }
378     }
379 #if USE_THREADS
380     free (tid);
381 #endif
382     free (soap_thr);
383 }
384
385 static void reconnect (struct target *t)
386 {
387     struct tset *s;
388
389     for (s = t->m_sets; s; s = s->m_next)
390     {
391         ZOOM_resultset_destroy(s->m_r);
392         s->m_r = 0;
393     }
394     ZOOM_connection_destroy (t->m_c);
395
396     t->m_c = ZOOM_connection_create (0);
397     ZOOM_connection_connect (t->m_c, t->m_name, 0);
398 }
399
400 int explain (void *userinfo,
401              struct soap *soap,
402              struct zs__explainResponse *explainResponse)
403 {
404     explainResponse->Explain = 
405         "<explain>\n"
406         "  <!-- not implemented -->\n"
407         "</explain>\n";
408     return SOAP_OK;
409 }
410
411 int fetchone(struct soap *soap, struct srw_prop *properties,
412              ZOOM_record zrec, const char *schema,
413              char **rec_data, char **rec_schema)
414 {
415     xslt_map_result res;
416     int xml_len;
417     const char *xml_rec = ZOOM_record_get(zrec, "xml", &xml_len);
418     if (!xml_rec)
419     {
420         return 65;
421     }
422     if (!strcmp(schema, "MARC21"))
423     {
424         *rec_data = soap_malloc (soap, xml_len+1);
425         memcpy (*rec_data, xml_rec, xml_len);
426         (*rec_data)[xml_len] = 0;
427         *rec_schema = "http://www.loc.gov/marcxml/";
428     }
429     else if ((res = xslt_map (properties->maps, "MARC21",
430                               schema, xml_rec, xml_len)))
431     {
432         int len = xslt_map_result_len(res);
433         char *buf = xslt_map_result_buf(res);
434
435         *rec_data = soap_malloc (soap, len+1);
436         memcpy (*rec_data, buf, len);
437         (*rec_data)[len] = 0;
438         
439         *rec_schema = soap_malloc(soap,
440                                   strlen(xslt_map_result_schema(res)) + 1);
441         strcpy(*rec_schema, xslt_map_result_schema(res));
442         
443         xslt_map_free (res);
444     }
445     else
446     {
447         *rec_data = soap_malloc(soap, strlen(schema)+1);
448         strcpy(*rec_data, schema);
449         return 66;
450     }
451     return 0;
452 }
453                 
454 int searchRetrieve(void *userinfo,
455                    struct soap * soap,
456                    xsd__string  *query,
457                    struct xcql__operandType *xQuery,    
458                    xsd__string *sortKeys,
459                    struct xsort__xSortKeysType *xSortKeys,
460                    xsd__integer *startRecord,
461                    xsd__integer *maximumRecords,
462                    xsd__string *recordSchema,
463                    xsd__string *recordPacking,
464                    struct zs__searchRetrieveResponse *res)
465 {
466     const char *msg = 0, *addinfo = 0;
467     const char *schema = recordSchema ? *recordSchema : "";
468     struct target *t = 0;
469     struct tset *s = 0;
470     int error = 0;
471     struct srw_prop *properties = (struct srw_prop*) userinfo;
472     WRBUF wr_log = wrbuf_alloc();
473
474     char pqf_buf[1024];
475     char zurl[81];
476
477     *pqf_buf = '\0';
478     *zurl = '\0';
479     yaz_log (LOG_LOG, "HTTP: %s", soap->endpoint);
480     if (*soap->endpoint)
481     {
482         const char *cp = strstr(soap->endpoint, "//");
483         if (cp)
484             cp = cp+2;             /* skip method// */
485         else
486             cp = soap->endpoint;
487         cp = strstr(cp, "/"); 
488         if (cp)
489         {
490             size_t len;
491             cp++;
492             len = strlen(cp);
493             if (len > 80)
494                 len = 80;
495             if (len)
496                 memcpy (zurl, cp, len);
497             zurl[len] = '\0';
498         }
499     }
500     else
501     {
502         const char *cp = getenv("PATH_INFO");
503         if (cp && cp[0] && cp[1])
504         {
505             size_t len;
506             cp++;  /* skip / */
507             len = strlen(cp);
508             if (len > 80)
509                 len = 80;
510             if (len)
511                 memcpy (zurl, cp, len);
512             zurl[len] = '\0';
513         }
514     }
515     if (query)
516     {
517         CQL_parser cql_parser = cql_parser_create();
518         int r = cql_parser_string(cql_parser, *query);
519
520         if (r)
521         {
522             yaz_log (LOG_LOG, "cql failed: %s", *query);
523             error = ERROR_BAD_CQL;
524         }
525         else
526         {
527             struct cql_node *tree = cql_parser_result(cql_parser);
528             error = cql_transform_buf (cql_transform_handle, tree,
529                                        pqf_buf, sizeof(pqf_buf));
530             if (error)
531                 cql_transform_error(cql_transform_handle, &addinfo);
532             cql_parser_destroy(cql_parser);
533             yaz_log (LOG_LOG, "cql OK: %s", *query);
534         }
535     }
536     else if (xQuery)
537     {
538         struct cql_node *tree = xcql_to_cqlnode(xQuery);
539         yaz_log (LOG_LOG, "xcql");
540         cql_transform_buf (cql_transform_handle, tree,
541                            pqf_buf, sizeof(pqf_buf));
542         cql_node_destroy(tree);
543     }
544     if (!error)
545     {
546         mylock(&target_mutex);
547         t = target_use (zurl, *pqf_buf ? pqf_buf : 0, schema, &s, properties);
548         myunlock(&target_mutex);
549     }
550
551     if (!error && !t->m_c)
552     {
553         reconnect(t);
554         if (ZOOM_connection_error (t->m_c, &msg, &addinfo))
555         {
556             yaz_log (LOG_LOG, "%s: connect failed", t->m_name);
557             error = ERROR_NO_TARGET;
558         }
559         else
560             yaz_log (LOG_LOG, "%s: connect ok", t->m_name);
561     }
562     if (!error && t->m_c &&  *pqf_buf)
563     {
564         if (properties->optimize_level <=1 ||
565             strcmp (pqf_buf, s->m_query) ||
566             strcmp (schema, s->m_schema))
567         {
568             /* not the same query: remove result set .. */
569             ZOOM_resultset_destroy (s->m_r);
570             s->m_r = 0;
571         }
572         else
573         {
574             /* same query: retrieve (instead of doing search) */
575             if (maximumRecords && *maximumRecords > 0)
576             {
577                 int start = startRecord ? *startRecord : 1;
578                 yaz_log (LOG_LOG, "%s: present start=%d count=%d pqf=%s",
579                          t->m_name, start, *maximumRecords, pqf_buf);
580                 wrbuf_printf (wr_log, "%s: present start=%d count=%d pqf=%s",
581                               t->m_name, start, *maximumRecords, pqf_buf);
582                 ZOOM_resultset_records (s->m_r, 0, start-1, *maximumRecords);
583                 error = ZOOM_connection_error (t->m_c, &msg, &addinfo);
584                 if (error == ZOOM_ERROR_CONNECTION_LOST ||
585                     error == ZOOM_ERROR_CONNECT)
586                 {
587                     reconnect (t);
588                     if ((error = ZOOM_connection_error (t->m_c, &msg,
589                                                      &addinfo)))
590                     {
591                         yaz_log (LOG_LOG, "%s: connect failed", t->m_name);
592                         error = ERROR_NO_TARGET;
593                     }
594                 }
595                 else if (error)
596                 {
597                     yaz_log (LOG_LOG, "%s: present failed bib1-code=%d",
598                              t->m_name, error);
599                     error = diag_bib1_to_srw(error);
600                 }
601             }
602             else
603             {
604                 yaz_log (LOG_LOG, "%s: matched search pqf=%s",
605                          t->m_name, pqf_buf);
606                 wrbuf_printf (wr_log, "%s: matched search pqf=%s",
607                          t->m_name, pqf_buf);
608             }
609         }
610         if (!error && !s->m_r) 
611         {   /* no result set usable. We must search ... */
612             int pass;
613             for (pass = 0; pass < 2; pass++)
614             {
615                 char val[30];
616                 int start = startRecord ? *startRecord : 1;
617                 int count = maximumRecords ? *maximumRecords : 0;
618                 
619                 sprintf (val, "%d", start-1);
620                 ZOOM_connection_option_set (t->m_c, "start", val);
621                 
622                 sprintf (val, "%d", count);
623                 ZOOM_connection_option_set (t->m_c, "count", val);
624                 
625                 ZOOM_connection_option_set (t->m_c, "preferredRecordSyntax", 
626                                             "usmarc");
627                 
628                 xfree (s->m_query);
629                 s->m_query = xstrdup (pqf_buf);
630
631                 xfree (s->m_schema);
632                 s->m_schema = xstrdup (schema);
633                 
634                 yaz_log (LOG_LOG, "%s: search start=%d count=%d pqf=%s",
635                          t->m_name, start, count, pqf_buf);
636                 
637                 wrbuf_printf (wr_log, "%s: search start=%d count=%d pqf=%s",
638                               t->m_name, start, count, pqf_buf);
639                 
640                 s->m_r = ZOOM_connection_search_pqf (t->m_c, s->m_query);
641                 
642                 error = ZOOM_connection_error (t->m_c, &msg, &addinfo);
643                 if (!error)
644                     break;
645                 if (error != ZOOM_ERROR_CONNECTION_LOST &&
646                     error != ZOOM_ERROR_CONNECT)
647                 {
648                     yaz_log (LOG_LOG, "%s: search failed bib1-code=%d",
649                              t->m_name, error);
650                     error = diag_bib1_to_srw(error);
651                     break;
652                 }
653                 yaz_log (LOG_LOG, "%s: reconnect (search again)", t->m_name);
654
655                 /* try once more */
656                 reconnect(t);
657
658                 error = ZOOM_connection_error (t->m_c, &msg, &addinfo);
659
660                 if (error)
661                 {
662                     error = ERROR_NO_TARGET;
663                     break;
664                 }
665             }
666         }
667     }
668     
669     if (!error && t->m_c && s->m_r)
670     {
671         yaz_log (LOG_LOG, "%s: %d hits", t->m_name,
672                  ZOOM_resultset_size(s->m_r));
673         res->numberOfRecords = ZOOM_resultset_size(s->m_r);
674         
675         if (maximumRecords)
676         {
677             int i, j = 0;
678             int offset = startRecord ? *startRecord -1 : 0;
679             res->records.record =
680                 soap_malloc(soap, sizeof(*res->records.record) *
681                             *maximumRecords);
682             
683             for (i = 0; i < *maximumRecords; i++)
684             {
685                 char *rec_data = 0;
686                 char *rec_schema = 0;
687                 ZOOM_record zrec = ZOOM_resultset_record (s->m_r, offset + i);
688                 if (!zrec)
689                 {
690                     error = 65;
691                     addinfo = schema;
692                     break;
693                 }
694                 error = fetchone(soap, properties, zrec, schema,
695                                  &rec_data, &rec_schema);
696                 if (error)
697                 {
698                     addinfo = rec_data;
699                     break;
700                 }
701                 res->records.record[j] = 
702                     soap_malloc(soap, sizeof(**res->records.record));
703                 res->records.record[j]->recordData = rec_data;
704                 res->records.record[j]->recordSchema = rec_schema;
705                 j++;
706             }
707             res->records.__sizeRecords = j;
708         }
709         else
710             res->numberOfRecords = 0;
711     }
712     if (error)
713     {
714         if (s)
715         {
716             ZOOM_resultset_destroy (s->m_r);
717             s->m_r = 0;
718         }
719         if (error == ERROR_NO_TARGET)
720         {
721             addinfo = zurl;
722             ZOOM_connection_destroy (t->m_c);
723             t->m_c = 0;
724         }
725         else
726         {
727             res->diagnostics.__sizeDiagnostics = 1;
728             res->diagnostics.diagnostic =
729                 soap_malloc (soap, sizeof(*res->diagnostics.diagnostic));
730             res->diagnostics.diagnostic[0] =
731                 soap_malloc (soap, sizeof(**res->diagnostics.diagnostic));
732             
733             res->diagnostics.diagnostic[0]->code = error;
734             if (addinfo)
735             {
736                 res->diagnostics.diagnostic[0]->details =
737                     soap_malloc (soap, strlen(addinfo) + 1);
738                 strcpy (res->diagnostics.diagnostic[0]->details, addinfo);
739             }
740             else
741                 res->diagnostics.diagnostic[0]->details = 0;
742         }
743     }
744     else
745     {
746         if (s->m_r)
747         {
748             struct timeval tv;
749             const char *setname = ZOOM_resultset_option_get(s->m_r, "setname");
750             if (strcmp(setname, "default") && s->m_idle_time)
751             {
752                 res->resultSetId = soap_malloc(soap, strlen(setname));
753                 strcpy(res->resultSetId, setname);
754                 res->resultSetIdleTime = s->m_idle_time;
755                 gettimeofday(&tv, 0);
756                 s->m_expiry_sec = res->resultSetIdleTime + tv.tv_sec + 2;
757             } else {
758                 s->m_expiry_sec = 0;
759             }
760         }
761     }
762
763     if (t)
764     {
765         if (properties->optimize_level > 0)
766             target_leave(t);
767         else
768             target_destroy(t);
769     }
770     wrbuf_free(wr_log, 1);
771     if (error == ERROR_NO_TARGET)
772         return soap_receiver_fault(soap, "Cannot connect to Z39.50 target", 0);
773     return SOAP_OK;
774 }
775
776 int main(int argc, char **argv)
777 {
778     struct soap soap;
779     int ret;
780     int port = 0;
781     int no_threads = 40;
782     char *arg;
783     const char *host = 0;
784     struct srw_prop properties;
785
786     properties.optimize_level = 2;
787     properties.idle_time = 300;
788     properties.max_sets = 30;
789     properties.maps = 0;
790             
791     while ((ret = options("dO:T:l:hVp:s:x:i:", argv, argc, &arg)) != -2)
792     {
793         switch(ret)
794         {
795         case 0:
796             port = atoi(arg);
797             break;
798         case 'O':
799             properties.optimize_level = atoi(arg);
800             break;
801         case 'T':
802             no_threads = atoi(arg);
803             if (no_threads < 1 || no_threads > 200)
804                 no_threads = 40;
805             break;
806         case 's':
807             if (!properties.maps)
808             {
809                 properties.maps = xslt_maps_create();
810                 if (xslt_maps_file(properties.maps, arg))
811                 {
812                     fprintf (stderr, "maps file %s could not be opened\n",
813                              arg);
814                     exit(1);
815                 }
816             }
817             break;
818         case 'l':
819             yaz_log_init_file(arg);
820             break;
821         case 'V':
822             puts ("Version: $Id: srw-gateway.c,v 1.1 2003-01-06 08:20:28 adam Exp $"
823 #if SRW_DEBUG
824             " DEBUG"
825 #endif
826                 );
827             exit (0);
828         case 'p':
829             if (cql_transform_handle == 0)
830                 cql_transform_handle = cql_transform_open_fname(arg);
831             break;
832         case 'x':
833             properties.max_sets = atoi(arg);
834             break;
835         case 'i':
836             properties.idle_time = atoi(arg);
837             break;
838         case 'h':
839             printf ("srw-gateway [options] <port>\n");
840             printf ("  port  port for standalone service; If port is omitted, CGI is used.\n");
841             printf ("  -O n     optimize level. >= 1 cache connections, >=2 cache result sets.\n");
842 #if USE_THREADS
843             printf ("  -T n     number of threads.\n");
844 #else
845             printf ("  -T       unsupported in this version.\n");
846 #endif
847             printf ("  -l file  log to file (instead of stderr).\n");
848             printf ("  -p file  PQF properties.\n");
849             printf ("  -s file  schema maps.\n");
850             printf ("  -i time  idle time.\n");
851             printf ("  -x sets  live sets.\n");
852             printf ("  -V       show version.\n");
853             exit (1);
854         default:
855             fprintf (stderr, "srw-gateway: bad option -%s ; use -h for help\n",
856                      arg);
857             exit (1);
858             break;
859             
860         }
861     }
862     if (!cql_transform_handle)
863         cql_transform_handle = cql_transform_open_fname("pqf.properties");
864     if (!properties.maps)
865     {
866         properties.maps = xslt_maps_create();
867         xslt_maps_file(properties.maps, "maps.xml");
868     }
869     soap.encodingStyle = 0;
870     if (port == 0 && getenv("QUERY_STRING"))
871     {
872         properties.optimize_level = 0;
873
874         yaz_log_init_file("srw.log");
875         yaz_log (LOG_LOG, "CGI begin");
876         soap_init(&soap);
877         soap.user = &properties;
878
879         yaz_srw_serve(&soap, &properties, searchRetrieve, explain);
880
881         soap_end(&soap);
882         yaz_log (LOG_LOG, "CGI end");
883     }
884     else if (port)
885     {
886         if (!cql_transform_handle)
887         {
888             fprintf(stderr, "no properties file; use option -p to specify\n");
889             exit (1);
890         }
891         yaz_log (LOG_LOG, "standalone service on port %d", port);
892         
893         soap_init(&soap);
894
895         standalone(&soap, host, port, no_threads, &properties);
896     }
897     else
898     {
899         fprintf(stderr, "srw-gateway: no port specified. Use -h for help\n");
900     }
901     xslt_maps_free(properties.maps);
902     return 0;
903 }