--- /dev/null
+/* $Id: srw-gateway.c,v 1.1 2003-01-06 08:20:28 adam Exp $
+ Copyright (C) 2002-2003
+ Index Data Aps
+
+This file is part of the YAZ toolkit.
+
+See the file LICENSE.
+*/
+
+/*
+ * TODO:
+ *
+ * TTL for targets. Separate thread for cleanup.
+ * External target config and aliases.
+ */
+
+/* note that soapH.h defines _REENTRANT so we check for it here */
+#ifdef _REENTRANT
+#include <pthread.h>
+#define USE_THREADS 1
+#else
+#define USE_THREADS 0
+#endif
+
+#include <yaz/srw-util.h>
+#include <yaz/xmalloc.h>
+#include <yaz/zoom.h>
+#include <yaz/log.h>
+#include <yaz/options.h>
+#include <yaz/wrbuf.h>
+
+#define RESULT_SETS 0
+#define SRW_DEBUG 1
+
+struct tset {
+ ZOOM_resultset m_r;
+ long m_expiry_sec; /* date of creation */
+ int m_idle_time;
+ char *m_query;
+ char *m_schema;
+ struct tset *m_next;
+};
+
+struct target {
+ ZOOM_connection m_c;
+ char *m_name;
+ int m_in_use;
+ struct tset *m_sets;
+ struct target *next;
+};
+
+struct srw_prop {
+ int optimize_level;
+ int idle_time;
+ int max_sets;
+ xslt_maps maps;
+};
+
+static cql_transform_t cql_transform_handle = 0;
+static struct target *target_list = 0;
+#if USE_THREADS
+static pthread_mutex_t target_mutex = PTHREAD_MUTEX_INITIALIZER;
+#define mylock(x) pthread_mutex_lock(x)
+#define myunlock(x) pthread_mutex_unlock(x)
+#else
+#define mylock(x)
+#define myunlock(x)
+#endif
+
+#define ERROR_NO_TARGET -1
+#define ERROR_BAD_CQL 10
+
+static int diag_bib1_to_srw (int code)
+{
+ static int map[] = {
+ 1, 1,
+ 2, 2,
+ 3, 11,
+ 4, 35,
+ 5, 12,
+ 6, 30,
+ 7, 30,
+ 8, 32,
+ 9, 29,
+ 10, 10,
+ 11, 12,
+ 13, 61,
+ 14, 63,
+ 15, 68,
+ 16, 70,
+ 17, 70,
+ 18, 50,
+ 19, 55,
+ 20, 56,
+ 21, 52,
+ 22, 50,
+ /* 23-26 no map */
+ 27, 51,
+ 28, 52,
+ 29, 52,
+ 30, 51,
+ 31, 52,
+ 32, 52,
+ 33, 52,
+ /* 100 -105 */
+ 106, 66,
+ 107, 11,
+ 108, 10,
+ 109, 2,
+ 110, 37,
+ /* 111- 112 */
+ 113, 10,
+ 114, 16,
+ 115, 16,
+ 116, 16,
+ 117, 19,
+ 118, 22,
+ 119, 32,
+ 120, 28,
+ 121, 15,
+ 122, 32,
+ 123, 22,
+ 124, 24,
+ 125, 36,
+ 126, 36,
+ 127, 36,
+ 128, 51,
+ 129, 39,
+ 130, 43,
+ 131, 40,
+ 132, 42,
+ 201, 44,
+ 202, 41,
+ 203, 43,
+ /* 205 */
+ 0
+ };
+ const int *p = map;
+ while (*p)
+ {
+ if (code == *p)
+ return p[1];
+ p += 2;
+ }
+ return 0;
+}
+
+static int searchRetrieve(void *userinfo,
+ struct soap * soap,
+ xsd__string *query,
+ struct xcql__operandType *xQuery,
+ xsd__string *sortKeys,
+ struct xsort__xSortKeysType *xSortKeys,
+ xsd__integer *startRecord,
+ xsd__integer *maximumRecords,
+ xsd__string *recordSchema,
+ xsd__string *recordPacking,
+ struct zs__searchRetrieveResponse *res);
+static int explain (void *userinfo,
+ struct soap *soap,
+ struct zs__explainResponse *explainResponse);
+
+struct target *target_use (const char *action, const char *query,
+ const char *schema, struct tset **set,
+ struct srw_prop *prop)
+{
+ char name[80];
+ struct target *l = 0;
+ struct timeval tv;
+ struct tset **ssp = 0;
+ long now;
+ int no_sets = 0;
+
+ gettimeofday(&tv, 0);
+ now = tv.tv_sec;
+
+ if (strlen(action) >= 80)
+ action = "localhost:210";
+ if (strchr(action, '/') || strchr(action, ':'))
+ strcpy (name, action);
+ else
+ {
+ strcpy (name, "localhost/");
+ if (*action == '\0')
+ strcat (name, "Default");
+ else
+ strcat (name, action);
+ }
+
+ /* See if we have the target and the same query */
+ if (query)
+ for (l = target_list; l; l = l->next)
+ if (!l->m_in_use && !strcmp (l->m_name, name))
+ {
+ struct tset *s = l->m_sets;
+ for (; s; s = s->m_next)
+ if (!strcmp(s->m_query, query) &&
+ !strcmp (s->m_schema, schema))
+ {
+ *set = s;
+ return l;
+ }
+ }
+
+ /* OK, see if we have the target, then.. */
+ for (l = target_list; l; l = l->next)
+ if (!strcmp (l->m_name, name) && !l->m_in_use)
+ {
+ struct tset *s = l->m_sets;
+ for (; s; s = s->m_next)
+ /* if m_expiry_sec is 0, the condition is true below */
+ if (s->m_expiry_sec < now)
+ {
+ xfree (s->m_query);
+ s->m_query = xstrdup("");
+ xfree (s->m_schema);
+ s->m_schema = xstrdup("");
+ ZOOM_resultset_destroy(s->m_r);
+ s->m_r = 0;
+ *set = s;
+ return l;
+ }
+ break;
+ }
+ if (!l)
+ {
+ /* allright. Have to make a new one */
+ l = xmalloc (sizeof(*l));
+ l->m_name = xstrdup (name);
+ l->m_in_use = 1;
+ l->m_c = 0;
+ l->m_sets = 0;
+ l->next = target_list;
+ target_list = l;
+ }
+ for (ssp = &l->m_sets; *ssp; ssp = &(*ssp)->m_next)
+ no_sets++;
+ *ssp = xmalloc(sizeof(**ssp));
+ (*ssp)->m_next = 0;
+ (*ssp)->m_query = xstrdup("");
+ (*ssp)->m_schema = xstrdup("");
+ (*ssp)->m_r = 0;
+ (*ssp)->m_expiry_sec = 0;
+ (*ssp)->m_idle_time = (no_sets >= prop->max_sets ? 0 : prop->idle_time);
+ *set = *ssp;
+ return l;
+}
+
+static void target_destroy (struct target *t)
+{
+ struct target **tp;
+
+ mylock(&target_mutex);
+
+ for (tp = &target_list; *tp; tp = &(*tp)->next)
+ if (*tp == t)
+ {
+ struct tset *s = t->m_sets;
+ while (s)
+ {
+ struct tset *s_next = s->m_next;
+ xfree (s->m_query);
+ xfree (s->m_schema);
+ ZOOM_resultset_destroy (s->m_r);
+ xfree (s);
+ s = s_next;
+ }
+
+ *tp = t->next;
+
+ ZOOM_connection_destroy (t->m_c);
+
+ xfree (t->m_name);
+ xfree (t);
+ break;
+ }
+ myunlock(&target_mutex);
+}
+
+static void target_leave (struct target *l)
+{
+ mylock(&target_mutex);
+ l->m_in_use = 0;
+
+ if (1)
+ {
+ struct tset *s = l->m_sets;
+ for (; s; s = s->m_next)
+ yaz_log(LOG_LOG, " set %s q=%s",
+ (s->m_r ? ZOOM_resultset_option_get(s->m_r,"setname"):""),
+ s->m_query);
+ }
+ myunlock(&target_mutex);
+}
+
+static void standalone(struct soap *soap, const char *host, int port,
+ int max_thr, struct srw_prop *properties)
+{
+ struct soap **soap_thr = malloc (sizeof(*soap_thr) * max_thr);
+#if USE_THREADS
+ pthread_t *tid = malloc (sizeof(pthread_t) * max_thr);
+#endif
+ int m, s, i;
+ int cno = 0;
+ int stop = 0;
+ char fname[40];
+
+ m = soap_bind(soap, 0, port, 100);
+ if (m < 0)
+ {
+ yaz_log (LOG_WARN|LOG_ERRNO, "Cannot bind to %d", port);
+ stop = 1;
+ }
+
+ for (i = 0; i<max_thr; i++)
+ soap_thr[i] = 0;
+
+ while (!stop)
+ {
+ for (i = 0; i<max_thr; i++)
+ {
+ s = soap_accept(soap);
+ if (s < 0)
+ break;
+ cno++;
+ if (!soap_thr[i])
+ {
+ soap_thr[i] = soap_new();
+ if (!soap_thr[i])
+ {
+ stop = 1;
+ break;
+ }
+ }
+ else
+ {
+#if USE_THREADS
+ if (max_thr > 1) /* static mode for max_thr <= 1 */
+ pthread_join(tid[i], 0);
+#endif
+ soap_end(soap_thr[i]);
+ }
+
+#if SRW_DEBUG
+ sprintf (fname, "srw.recv.%05d.log", cno);
+ remove (fname);
+ soap_set_recv_logfile(soap_thr[i], fname);
+
+ sprintf (fname, "srw.sent.%05d.log", cno);
+ remove (fname);
+ soap_set_sent_logfile(soap_thr[i], fname);
+
+ sprintf (fname, "srw.test.%05d.log", cno);
+ remove (fname);
+ soap_set_test_logfile(soap_thr[i], fname);
+
+ yaz_log (LOG_LOG, "starting session %d %ld.%ld.%ld.%ld", cno,
+ (long) (soap->ip>>24) & 0xff,
+ (long) (soap->ip>>16) & 0xff,
+ (long) (soap->ip>>8) & 0xff,
+ (long) soap->ip & 0xff);
+#endif
+ soap_thr[i]->encodingStyle = 0;
+ soap_thr[i]->socket = s;
+ soap_thr[i]->user = properties;
+#if USE_THREADS
+ if (max_thr <= 1)
+ yaz_srw_serve(soap_thr[i], properties,
+ searchRetrieve, explain); /* static mode .. */
+ else
+ pthread_create(&tid[i], 0, (void*(*)(void*))soap_serve,
+ soap_thr[i]);
+#else
+ yaz_srw_serve(soap_thr[i], properties,
+ searchRetrieve, explain); /* static mode .. */
+#endif
+ }
+ }
+#if USE_THREADS
+ free (tid);
+#endif
+ free (soap_thr);
+}
+
+static void reconnect (struct target *t)
+{
+ struct tset *s;
+
+ for (s = t->m_sets; s; s = s->m_next)
+ {
+ ZOOM_resultset_destroy(s->m_r);
+ s->m_r = 0;
+ }
+ ZOOM_connection_destroy (t->m_c);
+
+ t->m_c = ZOOM_connection_create (0);
+ ZOOM_connection_connect (t->m_c, t->m_name, 0);
+}
+
+int explain (void *userinfo,
+ struct soap *soap,
+ struct zs__explainResponse *explainResponse)
+{
+ explainResponse->Explain =
+ "<explain>\n"
+ " <!-- not implemented -->\n"
+ "</explain>\n";
+ return SOAP_OK;
+}
+
+int fetchone(struct soap *soap, struct srw_prop *properties,
+ ZOOM_record zrec, const char *schema,
+ char **rec_data, char **rec_schema)
+{
+ xslt_map_result res;
+ int xml_len;
+ const char *xml_rec = ZOOM_record_get(zrec, "xml", &xml_len);
+ if (!xml_rec)
+ {
+ return 65;
+ }
+ if (!strcmp(schema, "MARC21"))
+ {
+ *rec_data = soap_malloc (soap, xml_len+1);
+ memcpy (*rec_data, xml_rec, xml_len);
+ (*rec_data)[xml_len] = 0;
+ *rec_schema = "http://www.loc.gov/marcxml/";
+ }
+ else if ((res = xslt_map (properties->maps, "MARC21",
+ schema, xml_rec, xml_len)))
+ {
+ int len = xslt_map_result_len(res);
+ char *buf = xslt_map_result_buf(res);
+
+ *rec_data = soap_malloc (soap, len+1);
+ memcpy (*rec_data, buf, len);
+ (*rec_data)[len] = 0;
+
+ *rec_schema = soap_malloc(soap,
+ strlen(xslt_map_result_schema(res)) + 1);
+ strcpy(*rec_schema, xslt_map_result_schema(res));
+
+ xslt_map_free (res);
+ }
+ else
+ {
+ *rec_data = soap_malloc(soap, strlen(schema)+1);
+ strcpy(*rec_data, schema);
+ return 66;
+ }
+ return 0;
+}
+
+int searchRetrieve(void *userinfo,
+ struct soap * soap,
+ xsd__string *query,
+ struct xcql__operandType *xQuery,
+ xsd__string *sortKeys,
+ struct xsort__xSortKeysType *xSortKeys,
+ xsd__integer *startRecord,
+ xsd__integer *maximumRecords,
+ xsd__string *recordSchema,
+ xsd__string *recordPacking,
+ struct zs__searchRetrieveResponse *res)
+{
+ const char *msg = 0, *addinfo = 0;
+ const char *schema = recordSchema ? *recordSchema : "";
+ struct target *t = 0;
+ struct tset *s = 0;
+ int error = 0;
+ struct srw_prop *properties = (struct srw_prop*) userinfo;
+ WRBUF wr_log = wrbuf_alloc();
+
+ char pqf_buf[1024];
+ char zurl[81];
+
+ *pqf_buf = '\0';
+ *zurl = '\0';
+ yaz_log (LOG_LOG, "HTTP: %s", soap->endpoint);
+ if (*soap->endpoint)
+ {
+ const char *cp = strstr(soap->endpoint, "//");
+ if (cp)
+ cp = cp+2; /* skip method// */
+ else
+ cp = soap->endpoint;
+ cp = strstr(cp, "/");
+ if (cp)
+ {
+ size_t len;
+ cp++;
+ len = strlen(cp);
+ if (len > 80)
+ len = 80;
+ if (len)
+ memcpy (zurl, cp, len);
+ zurl[len] = '\0';
+ }
+ }
+ else
+ {
+ const char *cp = getenv("PATH_INFO");
+ if (cp && cp[0] && cp[1])
+ {
+ size_t len;
+ cp++; /* skip / */
+ len = strlen(cp);
+ if (len > 80)
+ len = 80;
+ if (len)
+ memcpy (zurl, cp, len);
+ zurl[len] = '\0';
+ }
+ }
+ if (query)
+ {
+ CQL_parser cql_parser = cql_parser_create();
+ int r = cql_parser_string(cql_parser, *query);
+
+ if (r)
+ {
+ yaz_log (LOG_LOG, "cql failed: %s", *query);
+ error = ERROR_BAD_CQL;
+ }
+ else
+ {
+ struct cql_node *tree = cql_parser_result(cql_parser);
+ error = cql_transform_buf (cql_transform_handle, tree,
+ pqf_buf, sizeof(pqf_buf));
+ if (error)
+ cql_transform_error(cql_transform_handle, &addinfo);
+ cql_parser_destroy(cql_parser);
+ yaz_log (LOG_LOG, "cql OK: %s", *query);
+ }
+ }
+ else if (xQuery)
+ {
+ struct cql_node *tree = xcql_to_cqlnode(xQuery);
+ yaz_log (LOG_LOG, "xcql");
+ cql_transform_buf (cql_transform_handle, tree,
+ pqf_buf, sizeof(pqf_buf));
+ cql_node_destroy(tree);
+ }
+ if (!error)
+ {
+ mylock(&target_mutex);
+ t = target_use (zurl, *pqf_buf ? pqf_buf : 0, schema, &s, properties);
+ myunlock(&target_mutex);
+ }
+
+ if (!error && !t->m_c)
+ {
+ reconnect(t);
+ if (ZOOM_connection_error (t->m_c, &msg, &addinfo))
+ {
+ yaz_log (LOG_LOG, "%s: connect failed", t->m_name);
+ error = ERROR_NO_TARGET;
+ }
+ else
+ yaz_log (LOG_LOG, "%s: connect ok", t->m_name);
+ }
+ if (!error && t->m_c && *pqf_buf)
+ {
+ if (properties->optimize_level <=1 ||
+ strcmp (pqf_buf, s->m_query) ||
+ strcmp (schema, s->m_schema))
+ {
+ /* not the same query: remove result set .. */
+ ZOOM_resultset_destroy (s->m_r);
+ s->m_r = 0;
+ }
+ else
+ {
+ /* same query: retrieve (instead of doing search) */
+ if (maximumRecords && *maximumRecords > 0)
+ {
+ int start = startRecord ? *startRecord : 1;
+ yaz_log (LOG_LOG, "%s: present start=%d count=%d pqf=%s",
+ t->m_name, start, *maximumRecords, pqf_buf);
+ wrbuf_printf (wr_log, "%s: present start=%d count=%d pqf=%s",
+ t->m_name, start, *maximumRecords, pqf_buf);
+ ZOOM_resultset_records (s->m_r, 0, start-1, *maximumRecords);
+ error = ZOOM_connection_error (t->m_c, &msg, &addinfo);
+ if (error == ZOOM_ERROR_CONNECTION_LOST ||
+ error == ZOOM_ERROR_CONNECT)
+ {
+ reconnect (t);
+ if ((error = ZOOM_connection_error (t->m_c, &msg,
+ &addinfo)))
+ {
+ yaz_log (LOG_LOG, "%s: connect failed", t->m_name);
+ error = ERROR_NO_TARGET;
+ }
+ }
+ else if (error)
+ {
+ yaz_log (LOG_LOG, "%s: present failed bib1-code=%d",
+ t->m_name, error);
+ error = diag_bib1_to_srw(error);
+ }
+ }
+ else
+ {
+ yaz_log (LOG_LOG, "%s: matched search pqf=%s",
+ t->m_name, pqf_buf);
+ wrbuf_printf (wr_log, "%s: matched search pqf=%s",
+ t->m_name, pqf_buf);
+ }
+ }
+ if (!error && !s->m_r)
+ { /* no result set usable. We must search ... */
+ int pass;
+ for (pass = 0; pass < 2; pass++)
+ {
+ char val[30];
+ int start = startRecord ? *startRecord : 1;
+ int count = maximumRecords ? *maximumRecords : 0;
+
+ sprintf (val, "%d", start-1);
+ ZOOM_connection_option_set (t->m_c, "start", val);
+
+ sprintf (val, "%d", count);
+ ZOOM_connection_option_set (t->m_c, "count", val);
+
+ ZOOM_connection_option_set (t->m_c, "preferredRecordSyntax",
+ "usmarc");
+
+ xfree (s->m_query);
+ s->m_query = xstrdup (pqf_buf);
+
+ xfree (s->m_schema);
+ s->m_schema = xstrdup (schema);
+
+ yaz_log (LOG_LOG, "%s: search start=%d count=%d pqf=%s",
+ t->m_name, start, count, pqf_buf);
+
+ wrbuf_printf (wr_log, "%s: search start=%d count=%d pqf=%s",
+ t->m_name, start, count, pqf_buf);
+
+ s->m_r = ZOOM_connection_search_pqf (t->m_c, s->m_query);
+
+ error = ZOOM_connection_error (t->m_c, &msg, &addinfo);
+ if (!error)
+ break;
+ if (error != ZOOM_ERROR_CONNECTION_LOST &&
+ error != ZOOM_ERROR_CONNECT)
+ {
+ yaz_log (LOG_LOG, "%s: search failed bib1-code=%d",
+ t->m_name, error);
+ error = diag_bib1_to_srw(error);
+ break;
+ }
+ yaz_log (LOG_LOG, "%s: reconnect (search again)", t->m_name);
+
+ /* try once more */
+ reconnect(t);
+
+ error = ZOOM_connection_error (t->m_c, &msg, &addinfo);
+
+ if (error)
+ {
+ error = ERROR_NO_TARGET;
+ break;
+ }
+ }
+ }
+ }
+
+ if (!error && t->m_c && s->m_r)
+ {
+ yaz_log (LOG_LOG, "%s: %d hits", t->m_name,
+ ZOOM_resultset_size(s->m_r));
+ res->numberOfRecords = ZOOM_resultset_size(s->m_r);
+
+ if (maximumRecords)
+ {
+ int i, j = 0;
+ int offset = startRecord ? *startRecord -1 : 0;
+ res->records.record =
+ soap_malloc(soap, sizeof(*res->records.record) *
+ *maximumRecords);
+
+ for (i = 0; i < *maximumRecords; i++)
+ {
+ char *rec_data = 0;
+ char *rec_schema = 0;
+ ZOOM_record zrec = ZOOM_resultset_record (s->m_r, offset + i);
+ if (!zrec)
+ {
+ error = 65;
+ addinfo = schema;
+ break;
+ }
+ error = fetchone(soap, properties, zrec, schema,
+ &rec_data, &rec_schema);
+ if (error)
+ {
+ addinfo = rec_data;
+ break;
+ }
+ res->records.record[j] =
+ soap_malloc(soap, sizeof(**res->records.record));
+ res->records.record[j]->recordData = rec_data;
+ res->records.record[j]->recordSchema = rec_schema;
+ j++;
+ }
+ res->records.__sizeRecords = j;
+ }
+ else
+ res->numberOfRecords = 0;
+ }
+ if (error)
+ {
+ if (s)
+ {
+ ZOOM_resultset_destroy (s->m_r);
+ s->m_r = 0;
+ }
+ if (error == ERROR_NO_TARGET)
+ {
+ addinfo = zurl;
+ ZOOM_connection_destroy (t->m_c);
+ t->m_c = 0;
+ }
+ else
+ {
+ res->diagnostics.__sizeDiagnostics = 1;
+ res->diagnostics.diagnostic =
+ soap_malloc (soap, sizeof(*res->diagnostics.diagnostic));
+ res->diagnostics.diagnostic[0] =
+ soap_malloc (soap, sizeof(**res->diagnostics.diagnostic));
+
+ res->diagnostics.diagnostic[0]->code = error;
+ if (addinfo)
+ {
+ res->diagnostics.diagnostic[0]->details =
+ soap_malloc (soap, strlen(addinfo) + 1);
+ strcpy (res->diagnostics.diagnostic[0]->details, addinfo);
+ }
+ else
+ res->diagnostics.diagnostic[0]->details = 0;
+ }
+ }
+ else
+ {
+ if (s->m_r)
+ {
+ struct timeval tv;
+ const char *setname = ZOOM_resultset_option_get(s->m_r, "setname");
+ if (strcmp(setname, "default") && s->m_idle_time)
+ {
+ res->resultSetId = soap_malloc(soap, strlen(setname));
+ strcpy(res->resultSetId, setname);
+ res->resultSetIdleTime = s->m_idle_time;
+ gettimeofday(&tv, 0);
+ s->m_expiry_sec = res->resultSetIdleTime + tv.tv_sec + 2;
+ } else {
+ s->m_expiry_sec = 0;
+ }
+ }
+ }
+
+ if (t)
+ {
+ if (properties->optimize_level > 0)
+ target_leave(t);
+ else
+ target_destroy(t);
+ }
+ wrbuf_free(wr_log, 1);
+ if (error == ERROR_NO_TARGET)
+ return soap_receiver_fault(soap, "Cannot connect to Z39.50 target", 0);
+ return SOAP_OK;
+}
+
+int main(int argc, char **argv)
+{
+ struct soap soap;
+ int ret;
+ int port = 0;
+ int no_threads = 40;
+ char *arg;
+ const char *host = 0;
+ struct srw_prop properties;
+
+ properties.optimize_level = 2;
+ properties.idle_time = 300;
+ properties.max_sets = 30;
+ properties.maps = 0;
+
+ while ((ret = options("dO:T:l:hVp:s:x:i:", argv, argc, &arg)) != -2)
+ {
+ switch(ret)
+ {
+ case 0:
+ port = atoi(arg);
+ break;
+ case 'O':
+ properties.optimize_level = atoi(arg);
+ break;
+ case 'T':
+ no_threads = atoi(arg);
+ if (no_threads < 1 || no_threads > 200)
+ no_threads = 40;
+ break;
+ case 's':
+ if (!properties.maps)
+ {
+ properties.maps = xslt_maps_create();
+ if (xslt_maps_file(properties.maps, arg))
+ {
+ fprintf (stderr, "maps file %s could not be opened\n",
+ arg);
+ exit(1);
+ }
+ }
+ break;
+ case 'l':
+ yaz_log_init_file(arg);
+ break;
+ case 'V':
+ puts ("Version: $Id: srw-gateway.c,v 1.1 2003-01-06 08:20:28 adam Exp $"
+#if SRW_DEBUG
+ " DEBUG"
+#endif
+ );
+ exit (0);
+ case 'p':
+ if (cql_transform_handle == 0)
+ cql_transform_handle = cql_transform_open_fname(arg);
+ break;
+ case 'x':
+ properties.max_sets = atoi(arg);
+ break;
+ case 'i':
+ properties.idle_time = atoi(arg);
+ break;
+ case 'h':
+ printf ("srw-gateway [options] <port>\n");
+ printf (" port port for standalone service; If port is omitted, CGI is used.\n");
+ printf (" -O n optimize level. >= 1 cache connections, >=2 cache result sets.\n");
+#if USE_THREADS
+ printf (" -T n number of threads.\n");
+#else
+ printf (" -T unsupported in this version.\n");
+#endif
+ printf (" -l file log to file (instead of stderr).\n");
+ printf (" -p file PQF properties.\n");
+ printf (" -s file schema maps.\n");
+ printf (" -i time idle time.\n");
+ printf (" -x sets live sets.\n");
+ printf (" -V show version.\n");
+ exit (1);
+ default:
+ fprintf (stderr, "srw-gateway: bad option -%s ; use -h for help\n",
+ arg);
+ exit (1);
+ break;
+
+ }
+ }
+ if (!cql_transform_handle)
+ cql_transform_handle = cql_transform_open_fname("pqf.properties");
+ if (!properties.maps)
+ {
+ properties.maps = xslt_maps_create();
+ xslt_maps_file(properties.maps, "maps.xml");
+ }
+ soap.encodingStyle = 0;
+ if (port == 0 && getenv("QUERY_STRING"))
+ {
+ properties.optimize_level = 0;
+
+ yaz_log_init_file("srw.log");
+ yaz_log (LOG_LOG, "CGI begin");
+ soap_init(&soap);
+ soap.user = &properties;
+
+ yaz_srw_serve(&soap, &properties, searchRetrieve, explain);
+
+ soap_end(&soap);
+ yaz_log (LOG_LOG, "CGI end");
+ }
+ else if (port)
+ {
+ if (!cql_transform_handle)
+ {
+ fprintf(stderr, "no properties file; use option -p to specify\n");
+ exit (1);
+ }
+ yaz_log (LOG_LOG, "standalone service on port %d", port);
+
+ soap_init(&soap);
+
+ standalone(&soap, host, port, no_threads, &properties);
+ }
+ else
+ {
+ fprintf(stderr, "srw-gateway: no port specified. Use -h for help\n");
+ }
+ xslt_maps_free(properties.maps);
+ return 0;
+}