6f54b296afd02750a300c5a3ec01a052e5fece03
[yaz-moved-to-github.git] / src / srwutil.c
1 /* This file is part of the YAZ toolkit.
2  * Copyright (C) 1995-2011 Index Data
3  * See the file LICENSE for details.
4  */
5 /**
6  * \file srwutil.c
7  * \brief Implements SRW/SRU utilities.
8  */
9 #if HAVE_CONFIG_H
10 #include <config.h>
11 #endif
12
13 #include <stdlib.h>
14 #include <assert.h>
15 #include <yaz/srw.h>
16 #include <yaz/matchstr.h>
17 #include <yaz/yaz-iconv.h>
18 #include "sru-p.h"
19
20 static char *yaz_decode_sru_dbpath_odr(ODR n, const char *uri, size_t len)
21 {
22     return odr_strdupn(n, uri, len);
23 }
24
25 void yaz_encode_sru_dbpath_buf(char *dst, const char *db)
26 {
27     assert(db);
28     *dst = '/';
29     strcpy(dst+1, db);
30 }
31
32 char *yaz_encode_sru_dbpath_odr(ODR out, const char *db)
33 {
34     char *dst = odr_malloc(out, 3 * strlen(db) + 2);
35     yaz_encode_sru_dbpath_buf(dst, db);
36     return dst;
37 }
38
39 Z_AttributeList *yaz_use_attribute_create(ODR o, const char *name)
40 {
41     Z_AttributeList *attributes= (Z_AttributeList *)
42         odr_malloc(o, sizeof(*attributes));
43     Z_AttributeElement ** elements;
44     attributes->num_attributes = 1;
45     elements = (Z_AttributeElement**)
46         odr_malloc (o, attributes->num_attributes * sizeof(*elements));
47     elements[0] = (Z_AttributeElement*) odr_malloc(o,sizeof(**elements));
48     elements[0]->attributeType = odr_intdup(o, 1);
49     elements[0]->attributeSet = odr_nullval();
50     elements[0]->which = Z_AttributeValue_complex;
51     elements[0]->value.complex = (Z_ComplexAttribute *)
52         odr_malloc(o, sizeof(Z_ComplexAttribute));
53     elements[0]->value.complex->num_list = 1;
54     elements[0]->value.complex->list = (Z_StringOrNumeric **)
55         odr_malloc(o, 1 * sizeof(Z_StringOrNumeric *));
56     elements[0]->value.complex->list[0] = (Z_StringOrNumeric *)
57         odr_malloc(o, sizeof(Z_StringOrNumeric));
58     elements[0]->value.complex->list[0]->which = Z_StringOrNumeric_string;
59     elements[0]->value.complex->list[0]->u.string = odr_strdup(o, name);
60     elements[0]->value.complex->semanticAction = 0;
61     elements[0]->value.complex->num_semanticAction = 0;
62     attributes->attributes = elements;
63     return attributes;
64 }
65
66 #if YAZ_HAVE_XML2
67 const char *yaz_element_attribute_value_get(xmlNodePtr ptr,
68                                             const char *node_name,
69                                             const char *attribute_name)
70 {
71     struct _xmlAttr *attr;
72     // check if the node name matches
73     if (strcmp((const char*) ptr->name, node_name))
74         return 0;
75     // check if the attribute name and return the value
76     for (attr = ptr->properties; attr; attr = attr->next)
77         if (attr->children && attr->children->type == XML_TEXT_NODE)
78         {
79             if (!strcmp((const char *) attr->name, attribute_name))
80                 return (const char *) attr->children->content;
81         }
82     return 0;
83 }
84 #endif
85
86 static int yaz_base64decode(const char *in, char *out)
87 {
88     const char *map = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
89         "abcdefghijklmnopqrstuvwxyz0123456789+/";
90     int olen = 0;
91     int len = strlen(in);
92
93     while (len >= 4)
94     {
95         char i0, i1, i2, i3;
96         char *p;
97
98         if (!(p = strchr(map, in[0])))
99             return 0;
100         i0 = p - map;
101         len--;
102         if (!(p = strchr(map, in[1])))
103             return 0;
104         i1 = p - map;
105         len--;
106         *(out++) = i0 << 2 | i1 >> 4;
107         olen++;
108         if (in[2] == '=')
109             break;
110         if (!(p = strchr(map, in[2])))
111             return 0;
112         i2 = p - map;
113         len--;
114         *(out++) = i1 << 4 | i2 >> 2;
115         olen++;
116         if (in[3] == '=')
117             break;
118         if (!(p = strchr(map, in[3])))
119             return 0;
120         i3 = p - map;
121         len--;
122         *(out++) = i2 << 6 | i3;
123         olen++;
124
125         in += 4;
126     }
127
128     *out = '\0';
129     return olen;
130 }
131
132 int yaz_srw_check_content_type(Z_HTTP_Response *hres)
133 {
134     const char *content_type = z_HTTP_header_lookup(hres->headers,
135                                                     "Content-Type");
136     if (content_type)
137     {
138         if (!yaz_strcmp_del("text/xml", content_type, "; "))
139             return 1;
140         if (!yaz_strcmp_del("application/xml", content_type, "; "))
141             return 1;
142     }
143     return 0;
144 }
145
146 /**
147  * Look for authentication tokens in HTTP Basic parameters or in x-username/x-password
148  * parameters. Added by SH.
149  */
150 static void yaz_srw_decodeauth(Z_SRW_PDU *sr, Z_HTTP_Request *hreq,
151                                char *username, char *password, ODR decode)
152 {
153     const char *basic = z_HTTP_header_lookup(hreq->headers, "Authorization");
154
155     if (username)
156         sr->username = username;
157     if (password)
158         sr->password = password;
159
160     if (basic)
161     {
162         int len, olen;
163         char out[256];
164         char ubuf[256] = "", pbuf[256] = "", *p;
165         if (strncmp(basic, "Basic ", 6))
166             return;
167         basic += 6;
168         len = strlen(basic);
169         if (!len || len > 256)
170             return;
171         olen = yaz_base64decode(basic, out);
172         /* Format of out should be username:password at this point */
173         strcpy(ubuf, out);
174         if ((p = strchr(ubuf, ':')))
175         {
176             *(p++) = '\0';
177             if (*p)
178                 strcpy(pbuf, p);
179         }
180         if (*ubuf)
181             sr->username = odr_strdup(decode, ubuf);
182         if (*pbuf)
183             sr->password = odr_strdup(decode, pbuf);
184     }
185 }
186
187 void yaz_uri_val_int(const char *path, const char *name, ODR o, Odr_int **intp)
188 {
189     const char *v = yaz_uri_val(path, name, o);
190     if (v)
191         *intp = odr_intdup(o, atoi(v));
192 }
193
194 void yaz_mk_srw_diagnostic(ODR o, Z_SRW_diagnostic *d, 
195                            const char *uri, const char *message,
196                            const char *details)
197 {
198     d->uri = odr_strdup(o, uri);
199     if (message)
200         d->message = odr_strdup(o, message);
201     else
202         d->message = 0;
203     if (details)
204         d->details = odr_strdup(o, details);
205     else
206         d->details = 0;
207 }
208
209 void yaz_mk_std_diagnostic(ODR o, Z_SRW_diagnostic *d, 
210                            int code, const char *details)
211 {
212     char uri[40];
213     
214     sprintf(uri, "info:srw/diagnostic/1/%d", code);
215     yaz_mk_srw_diagnostic(o, d, uri, 0, details);
216 }
217
218 void yaz_add_srw_diagnostic_uri(ODR o, Z_SRW_diagnostic **d,
219                                 int *num, const char *uri,
220                                 const char *message, const char *details)
221 {
222     Z_SRW_diagnostic *d_new;
223     d_new = (Z_SRW_diagnostic *) odr_malloc (o, (*num + 1)* sizeof(**d));
224     if (*num)
225         memcpy (d_new, *d, *num *sizeof(**d));
226     *d = d_new;
227
228     yaz_mk_srw_diagnostic(o, *d + *num, uri, message, details);
229     (*num)++;
230 }
231
232 void yaz_add_srw_diagnostic(ODR o, Z_SRW_diagnostic **d,
233                             int *num, int code, const char *addinfo)
234 {
235     char uri[40];
236     
237     sprintf(uri, "info:srw/diagnostic/1/%d", code);
238     yaz_add_srw_diagnostic_uri(o, d, num, uri, 0, addinfo);
239 }
240
241
242 void yaz_add_sru_update_diagnostic(ODR o, Z_SRW_diagnostic **d,
243                                    int *num, int code, const char *addinfo)
244 {
245     char uri[40];
246     
247     sprintf(uri, "info:srw/diagnostic/12/%d", code);
248     yaz_add_srw_diagnostic_uri(o, d, num, uri, 0, addinfo);
249 }
250
251
252 void yaz_mk_sru_surrogate(ODR o, Z_SRW_record *record, int pos,
253                           int code, const char *details)
254 {
255     const char *message = yaz_diag_srw_str(code);
256     int len = 200;
257     if (message)
258         len += strlen(message);
259     if (details)
260         len += strlen(details);
261     
262     record->recordData_buf = (char *) odr_malloc(o, len);
263     
264     sprintf(record->recordData_buf, "<diagnostic "
265             "xmlns=\"http://www.loc.gov/zing/srw/diagnostic/\">\n"
266             " <uri>info:srw/diagnostic/1/%d</uri>\n", code);
267     if (details)
268         sprintf(record->recordData_buf + strlen(record->recordData_buf),
269                 " <details>%s</details>\n", details);
270     if (message)
271         sprintf(record->recordData_buf + strlen(record->recordData_buf),
272                 " <message>%s</message>\n", message);
273     sprintf(record->recordData_buf + strlen(record->recordData_buf),
274             "</diagnostic>\n");
275     record->recordData_len = strlen(record->recordData_buf);
276     record->recordPosition = odr_intdup(o, pos);
277     record->recordSchema = "info:srw/schema/1/diagnostics-v1.1";
278 }
279
280 static void grab_charset(ODR o, const char *content_type, char **charset)
281 {
282     if (charset)
283     { 
284         const char *charset_p = 0;
285         if (content_type && (charset_p = strstr(content_type, "; charset=")))
286         {
287             int i = 0;
288             charset_p += 10;
289             while (i < 20 && charset_p[i] &&
290                    !strchr("; \n\r", charset_p[i]))
291                 i++;
292             *charset = (char*) odr_malloc(o, i+1);
293             memcpy(*charset, charset_p, i);
294             (*charset)[i] = '\0';
295         }
296     }
297 }
298
299 int yaz_srw_decode(Z_HTTP_Request *hreq, Z_SRW_PDU **srw_pdu,
300                    Z_SOAP **soap_package, ODR decode, char **charset)
301 {
302     if (!strcmp(hreq->method, "POST"))
303     {
304         const char *content_type = z_HTTP_header_lookup(hreq->headers,
305                                                         "Content-Type");
306         if (content_type && 
307             (!yaz_strcmp_del("text/xml", content_type, "; ") ||
308              !yaz_strcmp_del("application/soap+xml", content_type, "; ") ||
309              !yaz_strcmp_del("text/plain", content_type, "; ")))
310         {
311             char *db = "Default";
312             const char *p0 = hreq->path, *p1;
313             int ret = -1;
314             
315             static Z_SOAP_Handler soap_handlers[4] = {
316 #if YAZ_HAVE_XML2
317                 { YAZ_XMLNS_SRU_v1_1, 0, (Z_SOAP_fun) yaz_srw_codec },
318                 { YAZ_XMLNS_SRU_v1_0, 0, (Z_SOAP_fun) yaz_srw_codec },
319                 { YAZ_XMLNS_UPDATE_v0_9, 0, (Z_SOAP_fun) yaz_ucp_codec },
320 #endif
321                 {0, 0, 0}
322             };
323             
324             if (*p0 == '/')
325                 p0++;
326             p1 = strchr(p0, '?');
327             if (!p1)
328                 p1 = p0 + strlen(p0);
329             if (p1 != p0)
330                 db = yaz_decode_sru_dbpath_odr(decode, p0, p1 - p0);
331             grab_charset(decode, content_type, charset);
332
333             ret = z_soap_codec(decode, soap_package, 
334                                &hreq->content_buf, &hreq->content_len,
335                                soap_handlers);
336             if (ret == 0 && (*soap_package)->which == Z_SOAP_generic)
337             {
338                 *srw_pdu = (Z_SRW_PDU*) (*soap_package)->u.generic->p;
339                 yaz_srw_decodeauth(*srw_pdu, hreq, 0, 0, decode);
340                 
341                 if ((*srw_pdu)->which == Z_SRW_searchRetrieve_request &&
342                     (*srw_pdu)->u.request->database == 0)
343                     (*srw_pdu)->u.request->database = db;
344
345                 if ((*srw_pdu)->which == Z_SRW_explain_request &&
346                     (*srw_pdu)->u.explain_request->database == 0)
347                     (*srw_pdu)->u.explain_request->database = db;
348
349                 if ((*srw_pdu)->which == Z_SRW_scan_request &&
350                     (*srw_pdu)->u.scan_request->database == 0)
351                     (*srw_pdu)->u.scan_request->database = db;
352
353                 if ((*srw_pdu)->which == Z_SRW_update_request &&
354                     (*srw_pdu)->u.update_request->database == 0)
355                     (*srw_pdu)->u.update_request->database = db;
356
357                 return 0;
358             }
359             return 1;
360         }
361     }
362     return 2;
363 }
364
365 #if YAZ_HAVE_XML2
366 static int yaz_sru_decode_integer(ODR odr, const char *pname, 
367                                   const char *valstr, Odr_int **valp,
368                                   Z_SRW_diagnostic **diag, int *num_diag,
369                                   int min_value)
370 {
371     int ival;
372     if (!valstr)
373         return 0;
374     if (sscanf(valstr, "%d", &ival) != 1)
375     {
376         yaz_add_srw_diagnostic(odr, diag, num_diag,
377                                YAZ_SRW_UNSUPP_PARAMETER_VALUE, pname);
378         return 0;
379     }
380     if (min_value >= 0 && ival < min_value)
381     {
382         yaz_add_srw_diagnostic(odr, diag, num_diag,
383                                YAZ_SRW_UNSUPP_PARAMETER_VALUE, pname);
384         return 0;
385     }
386     *valp = odr_intdup(odr, ival);
387     return 1;
388 }
389 #endif
390
391 /**
392    http://www.loc.gov/z3950/agency/zing/srw/service.html
393 */ 
394 int yaz_sru_decode(Z_HTTP_Request *hreq, Z_SRW_PDU **srw_pdu,
395                    Z_SOAP **soap_package, ODR decode, char **charset,
396                    Z_SRW_diagnostic **diag, int *num_diag)
397 {
398 #if YAZ_HAVE_XML2
399     static Z_SOAP_Handler soap_handlers[2] = {
400         {YAZ_XMLNS_SRU_v1_1, 0, (Z_SOAP_fun) yaz_srw_codec},
401         {0, 0, 0}
402     };
403 #endif
404     const char *content_type = z_HTTP_header_lookup(hreq->headers,
405                                                     "Content-Type");
406
407     /*
408       SRU GET: ignore content type.
409       SRU POST: we support "application/x-www-form-urlencoded";
410       not  "multipart/form-data" .
411     */
412     if (!strcmp(hreq->method, "GET")
413         || 
414         (!strcmp(hreq->method, "POST") && content_type &&
415          !yaz_strcmp_del("application/x-www-form-urlencoded",
416                          content_type, "; ")))
417     {
418         char *db = "Default";
419         const char *p0 = hreq->path, *p1;
420 #if YAZ_HAVE_XML2
421         const char *operation = 0;
422         char *version = 0;
423         char *query = 0;
424         char *pQuery = 0;
425         char *username = 0;
426         char *password = 0;
427         char *sortKeys = 0;
428         char *stylesheet = 0;
429         char *scanClause = 0;
430         char *pScanClause = 0;
431         char *recordXPath = 0;
432         char *recordSchema = 0;
433         char *recordPacking = "xml";  /* xml packing is default for SRU */
434         char *maximumRecords = 0;
435         char *startRecord = 0;
436         char *maximumTerms = 0;
437         char *responsePosition = 0;
438         char *extraRequestData = 0;
439         Z_SRW_extra_arg *extra_args = 0;
440 #endif
441         char **uri_name;
442         char **uri_val;
443
444         grab_charset(decode, content_type, charset);
445         if (charset && *charset == 0 && !strcmp(hreq->method, "GET"))
446             *charset = "UTF-8";
447
448         if (*p0 == '/')
449             p0++;
450         p1 = strchr(p0, '?');
451         if (!p1)
452             p1 = p0 + strlen(p0);
453         if (p1 != p0)
454             db = yaz_decode_sru_dbpath_odr(decode, p0, p1 - p0);
455         if (!strcmp(hreq->method, "POST"))
456             p1 = hreq->content_buf;
457         yaz_uri_to_array(p1, decode, &uri_name, &uri_val);
458 #if YAZ_HAVE_XML2
459         if (uri_name)
460         {
461             int i;
462             for (i = 0; uri_name[i]; i++)
463             {
464                 char *n = uri_name[i];
465                 char *v = uri_val[i];
466                 if (!strcmp(n, "query"))
467                     query = v;
468                 else if (!strcmp(n, "x-pquery"))
469                     pQuery = v;
470                 else if (!strcmp(n, "x-username"))
471                     username = v;
472                 else if (!strcmp(n, "x-password"))
473                     password = v;
474                 else if (!strcmp(n, "operation"))
475                     operation = v;
476                 else if (!strcmp(n, "stylesheet"))
477                     stylesheet = v;
478                 else if (!strcmp(n, "sortKeys"))
479                     sortKeys = v;
480                 else if (!strcmp(n, "recordXPath"))
481                     recordXPath = v;
482                 else if (!strcmp(n, "recordSchema"))
483                     recordSchema = v;
484                 else if (!strcmp(n, "recordPacking"))
485                     recordPacking = v;
486                 else if (!strcmp(n, "version"))
487                     version = v;
488                 else if (!strcmp(n, "scanClause"))
489                     scanClause = v;
490                 else if (!strcmp(n, "x-pScanClause"))
491                     pScanClause = v;
492                 else if (!strcmp(n, "maximumRecords"))
493                     maximumRecords = v;
494                 else if (!strcmp(n, "startRecord"))
495                     startRecord = v;
496                 else if (!strcmp(n, "maximumTerms"))
497                     maximumTerms = v;
498                 else if (!strcmp(n, "responsePosition"))
499                     responsePosition = v;
500                 else if (!strcmp(n, "extraRequestData"))
501                     extraRequestData = v;
502                 else if (n[0] == 'x' && n[1] == '-')
503                 {
504                     Z_SRW_extra_arg **l = &extra_args;
505                     while (*l)
506                         l = &(*l)->next;
507                     *l = (Z_SRW_extra_arg *) odr_malloc(decode, sizeof(**l));
508                     (*l)->name = odr_strdup(decode, n);
509                     (*l)->value = odr_strdup(decode, v);
510                     (*l)->next = 0;
511                 }
512                 else
513                     yaz_add_srw_diagnostic(decode, diag, num_diag,
514                                            YAZ_SRW_UNSUPP_PARAMETER, n);
515             }
516         }
517         if (!version)
518         {
519             if (uri_name)
520                 yaz_add_srw_diagnostic(
521                     decode, diag, num_diag,
522                     YAZ_SRW_MANDATORY_PARAMETER_NOT_SUPPLIED, "version");
523             version = "1.1";
524         }
525
526         version = yaz_negotiate_sru_version(version);
527
528         if (!version)
529         {   /* negotiation failed. */
530             yaz_add_srw_diagnostic(decode, diag, num_diag,
531                                    YAZ_SRW_UNSUPP_VERSION, "1.2");
532             version = "1.2";
533         }
534         
535         if (!operation)
536         {
537             if (uri_name)
538                 yaz_add_srw_diagnostic(
539                     decode, diag, num_diag, 
540                     YAZ_SRW_MANDATORY_PARAMETER_NOT_SUPPLIED, "operation");
541             operation = "explain";
542         }
543         if (!strcmp(operation, "searchRetrieve"))
544         {
545             Z_SRW_PDU *sr = yaz_srw_get(decode, Z_SRW_searchRetrieve_request);
546
547             sr->srw_version = version;
548             sr->extra_args = extra_args;
549             *srw_pdu = sr;
550             yaz_srw_decodeauth(sr, hreq, username, password, decode);
551             if (query)
552             {
553                 sr->u.request->query_type = Z_SRW_query_type_cql;
554                 sr->u.request->query.cql = query;
555             }
556             else if (pQuery)
557             {
558                 sr->u.request->query_type = Z_SRW_query_type_pqf;
559                 sr->u.request->query.pqf = pQuery;
560             }
561             else
562                 yaz_add_srw_diagnostic(
563                     decode, diag, num_diag, 
564                     YAZ_SRW_MANDATORY_PARAMETER_NOT_SUPPLIED, "query");
565
566             if (sortKeys)
567             {
568                 sr->u.request->sort_type = Z_SRW_sort_type_sort;
569                 sr->u.request->sort.sortKeys = sortKeys;
570             }
571             sr->u.request->recordXPath = recordXPath;
572             sr->u.request->recordSchema = recordSchema;
573             sr->u.request->recordPacking = recordPacking;
574             sr->u.request->stylesheet = stylesheet;
575
576             yaz_sru_decode_integer(decode, "maximumRecords", maximumRecords, 
577                                    &sr->u.request->maximumRecords, 
578                                    diag, num_diag, 0);
579             
580             yaz_sru_decode_integer(decode, "startRecord", startRecord, 
581                                    &sr->u.request->startRecord,
582                                    diag, num_diag, 1);
583
584             sr->u.request->database = db;
585
586             (*soap_package) = (Z_SOAP *)
587                 odr_malloc(decode, sizeof(**soap_package));
588             (*soap_package)->which = Z_SOAP_generic;
589             
590             (*soap_package)->u.generic = (Z_SOAP_Generic *)
591                 odr_malloc(decode, sizeof(*(*soap_package)->u.generic));
592             
593             (*soap_package)->u.generic->p = sr;
594             (*soap_package)->u.generic->ns = soap_handlers[0].ns;
595             (*soap_package)->u.generic->no = 0;
596             
597             (*soap_package)->ns = "SRU";
598
599             return 0;
600         }
601         else if (!strcmp(operation, "explain"))
602         {
603             /* Transfer SRU explain parameters to common struct */
604             /* http://www.loc.gov/z3950/agency/zing/srw/explain.html */
605             Z_SRW_PDU *sr = yaz_srw_get(decode, Z_SRW_explain_request);
606
607             sr->srw_version = version;
608             sr->extra_args = extra_args;
609             yaz_srw_decodeauth(sr, hreq, username, password, decode);
610             *srw_pdu = sr;
611             sr->u.explain_request->recordPacking = recordPacking;
612             sr->u.explain_request->database = db;
613
614             sr->u.explain_request->stylesheet = stylesheet;
615
616             (*soap_package) = (Z_SOAP *)
617                 odr_malloc(decode, sizeof(**soap_package));
618             (*soap_package)->which = Z_SOAP_generic;
619             
620             (*soap_package)->u.generic = (Z_SOAP_Generic *)
621                 odr_malloc(decode, sizeof(*(*soap_package)->u.generic));
622             
623             (*soap_package)->u.generic->p = sr;
624             (*soap_package)->u.generic->ns = soap_handlers[0].ns;
625             (*soap_package)->u.generic->no = 0;
626             
627             (*soap_package)->ns = "SRU";
628
629             return 0;
630         }
631         else if (!strcmp(operation, "scan"))
632         {
633             /* Transfer SRU scan parameters to common struct */
634             /* http://www.loc.gov/z3950/agency/zing/srw/scan.html */
635             Z_SRW_PDU *sr = yaz_srw_get(decode, Z_SRW_scan_request);
636
637             sr->srw_version = version;
638             sr->extra_args = extra_args;
639             *srw_pdu = sr;
640             yaz_srw_decodeauth(sr, hreq, username, password, decode);
641
642             if (scanClause)
643             {
644                 sr->u.scan_request->query_type = Z_SRW_query_type_cql;
645                 sr->u.scan_request->scanClause.cql = scanClause;
646             }
647             else if (pScanClause)
648             {
649                 sr->u.scan_request->query_type = Z_SRW_query_type_pqf;
650                 sr->u.scan_request->scanClause.pqf = pScanClause;
651             }
652             else
653                 yaz_add_srw_diagnostic(
654                     decode, diag, num_diag, 
655                     YAZ_SRW_MANDATORY_PARAMETER_NOT_SUPPLIED, "scanClause");
656             sr->u.scan_request->database = db;
657             
658             yaz_sru_decode_integer(decode, "maximumTerms",
659                                    maximumTerms, 
660                                    &sr->u.scan_request->maximumTerms,
661                                    diag, num_diag, 0);
662             
663             yaz_sru_decode_integer(decode, "responsePosition",
664                                    responsePosition, 
665                                    &sr->u.scan_request->responsePosition,
666                                    diag, num_diag, 0);
667
668             sr->u.scan_request->stylesheet = stylesheet;
669
670             (*soap_package) = (Z_SOAP *)
671                 odr_malloc(decode, sizeof(**soap_package));
672             (*soap_package)->which = Z_SOAP_generic;
673             
674             (*soap_package)->u.generic = (Z_SOAP_Generic *)
675                 odr_malloc(decode, sizeof(*(*soap_package)->u.generic));
676             
677             (*soap_package)->u.generic->p = sr;
678             (*soap_package)->u.generic->ns = soap_handlers[0].ns;
679             (*soap_package)->u.generic->no = 0;
680             
681             (*soap_package)->ns = "SRU";
682
683             return 0;
684         }
685         else
686         {
687             /* unsupported operation ... */
688             /* Act as if we received a explain request and throw diagnostic. */
689
690             Z_SRW_PDU *sr = yaz_srw_get(decode, Z_SRW_explain_request);
691
692             sr->srw_version = version;
693             *srw_pdu = sr;
694             sr->u.explain_request->recordPacking = recordPacking;
695             sr->u.explain_request->database = db;
696
697             sr->u.explain_request->stylesheet = stylesheet;
698
699             (*soap_package) = (Z_SOAP *)
700                 odr_malloc(decode, sizeof(**soap_package));
701             (*soap_package)->which = Z_SOAP_generic;
702             
703             (*soap_package)->u.generic = (Z_SOAP_Generic *)
704                 odr_malloc(decode, sizeof(*(*soap_package)->u.generic));
705             
706             (*soap_package)->u.generic->p = sr;
707             (*soap_package)->u.generic->ns = soap_handlers[0].ns;
708             (*soap_package)->u.generic->no = 0;
709             
710             (*soap_package)->ns = "SRU";
711
712             yaz_add_srw_diagnostic(decode, diag, num_diag, 
713                                    YAZ_SRW_UNSUPP_OPERATION, operation);
714             return 0;
715         }
716 #endif
717         return 1;
718     }
719     return 2;
720 }
721
722 Z_SRW_extra_record *yaz_srw_get_extra_record(ODR o)
723 {
724     Z_SRW_extra_record *res = (Z_SRW_extra_record *)
725         odr_malloc(o, sizeof(*res));
726
727     res->extraRecordData_buf = 0;
728     res->extraRecordData_len = 0;
729     res->recordIdentifier = 0;
730     return res;
731 }
732
733
734 Z_SRW_record *yaz_srw_get_records(ODR o, int n)
735 {
736     Z_SRW_record *res = (Z_SRW_record *) odr_malloc(o, n * sizeof(*res));
737     int i;
738
739     for (i = 0; i<n; i++)
740     {
741         res[i].recordSchema = 0;
742         res[i].recordPacking = Z_SRW_recordPacking_string;
743         res[i].recordData_buf = 0;
744         res[i].recordData_len = 0;
745         res[i].recordPosition = 0;
746     }
747     return res;
748 }
749
750 Z_SRW_record *yaz_srw_get_record(ODR o)
751 {
752     return yaz_srw_get_records(o, 1);
753 }
754
755 static Z_SRW_PDU *yaz_srw_get_core_ver(ODR o, const char *version)
756 {
757     Z_SRW_PDU *p = (Z_SRW_PDU *) odr_malloc(o, sizeof(*p));
758     p->srw_version = odr_strdup(o, version);
759     p->username = 0;
760     p->password = 0;
761     p->extra_args = 0;
762     p->extraResponseData_buf = 0;
763     p->extraResponseData_len = 0;
764     return p;
765 }
766
767 Z_SRW_PDU *yaz_srw_get_core_v_1_1(ODR o)
768 {
769     return yaz_srw_get_core_ver(o, "1.1");
770 }
771
772 Z_SRW_PDU *yaz_srw_get(ODR o, int which)
773 {
774     return yaz_srw_get_pdu(o, which, "1.1");
775 }
776
777 Z_SRW_PDU *yaz_srw_get_pdu(ODR o, int which, const char *version)
778 {
779     Z_SRW_PDU *sr = yaz_srw_get_core_ver(o, version);
780
781     sr->which = which;
782     switch(which)
783     {
784     case Z_SRW_searchRetrieve_request:
785         sr->u.request = (Z_SRW_searchRetrieveRequest *)
786             odr_malloc(o, sizeof(*sr->u.request));
787         sr->u.request->query_type = Z_SRW_query_type_cql;
788         sr->u.request->query.cql = 0;
789         sr->u.request->sort_type = Z_SRW_sort_type_none;
790         sr->u.request->sort.none = 0;
791         sr->u.request->startRecord = 0;
792         sr->u.request->maximumRecords = 0;
793         sr->u.request->recordSchema = 0;
794         sr->u.request->recordPacking = 0;
795         sr->u.request->recordXPath = 0;
796         sr->u.request->database = 0;
797         sr->u.request->resultSetTTL = 0;
798         sr->u.request->stylesheet = 0;
799         sr->u.request->facetList = 0;
800         break;
801     case Z_SRW_searchRetrieve_response:
802         sr->u.response = (Z_SRW_searchRetrieveResponse *)
803             odr_malloc(o, sizeof(*sr->u.response));
804         sr->u.response->numberOfRecords = 0;
805         sr->u.response->resultSetId = 0;
806         sr->u.response->resultSetIdleTime = 0;
807         sr->u.response->records = 0;
808         sr->u.response->num_records = 0;
809         sr->u.response->diagnostics = 0;
810         sr->u.response->num_diagnostics = 0;
811         sr->u.response->nextRecordPosition = 0;
812         sr->u.response->extra_records = 0;
813         sr->u.response->facetList = 0;
814         break;
815     case Z_SRW_explain_request:
816         sr->u.explain_request = (Z_SRW_explainRequest *)
817             odr_malloc(o, sizeof(*sr->u.explain_request));
818         sr->u.explain_request->recordPacking = 0;
819         sr->u.explain_request->database = 0;
820         sr->u.explain_request->stylesheet = 0;
821         break;
822     case Z_SRW_explain_response:
823         sr->u.explain_response = (Z_SRW_explainResponse *)
824             odr_malloc(o, sizeof(*sr->u.explain_response));
825         sr->u.explain_response->record.recordData_buf = 0;
826         sr->u.explain_response->record.recordData_len = 0;
827         sr->u.explain_response->record.recordSchema = 0;
828         sr->u.explain_response->record.recordPosition = 0;
829         sr->u.explain_response->record.recordPacking =
830             Z_SRW_recordPacking_string;
831         sr->u.explain_response->diagnostics = 0;
832         sr->u.explain_response->num_diagnostics = 0;
833         sr->u.explain_response->extra_record = 0;
834         break;
835     case Z_SRW_scan_request:
836         sr->u.scan_request = (Z_SRW_scanRequest *)
837             odr_malloc(o, sizeof(*sr->u.scan_request));
838         sr->u.scan_request->database = 0;
839         sr->u.scan_request->stylesheet = 0;
840         sr->u.scan_request->maximumTerms = 0;
841         sr->u.scan_request->responsePosition = 0;
842         sr->u.scan_request->query_type = Z_SRW_query_type_cql;
843         sr->u.scan_request->scanClause.cql = 0;
844         break;
845     case Z_SRW_scan_response:
846         sr->u.scan_response = (Z_SRW_scanResponse *)
847             odr_malloc(o, sizeof(*sr->u.scan_response));
848         sr->u.scan_response->terms = 0;
849         sr->u.scan_response->num_terms = 0;
850         sr->u.scan_response->diagnostics = 0;
851         sr->u.scan_response->num_diagnostics = 0;
852         break;
853     case Z_SRW_update_request:
854         sr->u.update_request = (Z_SRW_updateRequest *)
855             odr_malloc(o, sizeof(*sr->u.update_request));
856         sr->u.update_request->database = 0;
857         sr->u.update_request->stylesheet = 0;
858         sr->u.update_request->record = 0;
859         sr->u.update_request->recordId = 0;
860         sr->u.update_request->recordVersions = 0;
861         sr->u.update_request->num_recordVersions = 0;
862         sr->u.update_request->extra_record = 0;
863         sr->u.update_request->extraRequestData_buf = 0;
864         sr->u.update_request->extraRequestData_len = 0;
865         sr->u.request->database = 0;
866         break;
867     case Z_SRW_update_response:
868         sr->u.update_response = (Z_SRW_updateResponse *)
869             odr_malloc(o, sizeof(*sr->u.update_response));
870         sr->u.update_response->operationStatus = 0;
871         sr->u.update_response->recordId = 0;
872         sr->u.update_response->recordVersions = 0;
873         sr->u.update_response->num_recordVersions = 0;
874         sr->u.update_response->record = 0;
875         sr->u.update_response->extra_record = 0;
876         sr->u.update_response->extraResponseData_buf = 0;
877         sr->u.update_response->extraResponseData_len = 0;
878         sr->u.update_response->diagnostics = 0;
879         sr->u.update_response->num_diagnostics = 0;
880     }
881     return sr;
882 }
883
884 /* bib1:srw */
885 static int bib1_srw_map[] = {
886     1, 1,
887     2, 2,
888     3, 11,
889     4, 35,
890     5, 12,
891     6, 38,
892     7, 30,
893     8, 32,
894     9, 29,
895     108, 10,  /* Malformed query : Syntax error */
896     10, 10,
897     11, 12,
898     11, 23,
899     12, 60,
900     13, 61,
901     13, 62,
902     14, 63,
903     14, 64,
904     14, 65,
905     15, 68,
906     15, 69,
907     16, 70,
908     17, 70,
909     18, 50,
910     19, 55,
911     20, 56, 
912     21, 52,
913     22, 50,
914     23, 3,
915     24, 66,
916     25, 66,
917     26, 66,
918     27, 51,
919     28, 52,
920     29, 52,
921     30, 51,
922     31, 57,
923     32, 58,
924     33, 59,
925     100, 1, /* bad map */
926     101, 3,
927     102, 3,
928     103, 3,
929     104, 3,
930     105, 3, 
931     106, 66,
932     107, 11,
933     108, 13,
934     108, 14,
935     108, 25,
936     108, 26,
937     108, 27,
938     108, 45,
939         
940     109, 2,
941     110, 37,
942     111, 1,
943     112, 58,
944     113, 10,
945     114, 16,
946     115, 16,
947     116, 16,
948     117, 19,
949     117, 20,
950     118, 22,
951     119, 32,
952     119, 31,
953     120, 28,
954     121, 15,
955     122, 32,
956     123, 22,
957     123, 17,
958     123, 18,
959     124, 24,
960     125, 36,
961     126, 36, 
962     127, 36,
963     128, 51,
964     129, 39,
965     130, 43,
966     131, 40,
967     132, 42,
968     201, 44,
969     201, 33,
970     201, 34,
971     202, 41,
972     203, 43,
973     205, 1,  /* bad map */
974     206, 1,  /* bad map */
975     207, 89,
976     208, 1,  /* bad map */
977     209, 80,
978     210, 80,
979     210, 81,
980     211, 84,
981     212, 85,
982     213, 92,
983     214, 90,
984     215, 91,
985     216, 92,
986     217, 63,
987     218, 1,  /* bad map */
988     219, 1,  /* bad map */
989     220, 1,  /* bad map */
990     221, 1,  /* bad map */
991     222, 3,
992     223, 1,  /* bad map */
993     224, 1,  /* bad map */
994     225, 1,  /* bad map */
995     226, 1,  /* bad map */
996     227, 66,
997     228, 1,  /* bad map */
998     229, 36,
999     230, 83,
1000     231, 89,
1001     232, 1,
1002     233, 1, /* bad map */
1003     234, 1, /* bad map */
1004     235, 2,
1005     236, 3, 
1006     237, 82,
1007     238, 67,
1008     239, 66,
1009     240, 1, /* bad map */
1010     241, 1, /* bad map */
1011     242, 70,
1012     243, 1, /* bad map */
1013     244, 66,
1014     245, 10,
1015     246, 10,
1016     247, 10,
1017     1001, 1, /* bad map */
1018     1002, 1, /* bad map */
1019     1003, 1, /* bad map */
1020     1004, 1, /* bad map */
1021     1005, 1, /* bad map */
1022     1006, 1, /* bad map */
1023     1007, 100,
1024     1008, 1, 
1025     1009, 1,
1026     1010, 3,
1027     1011, 3,
1028     1012, 3,
1029     1013, 3,
1030     1014, 3,
1031     1015, 3,
1032     1015, 3,
1033     1016, 3,
1034     1017, 3,
1035     1018, 2,
1036     1019, 2,
1037     1020, 2,
1038     1021, 3,
1039     1022, 3,
1040     1023, 3,
1041     1024, 16,
1042     1025, 3,
1043     1026, 64,
1044     1027, 1,
1045     1028, 65,
1046     1029, 1,
1047     1040, 1,
1048     /* 1041-1065 */
1049     1066, 66,
1050     1066, 67,
1051     0
1052 };
1053
1054 /*
1055  * This array contains overrides for when the first occurrence of a
1056  * particular SRW error in the array above does not correspond with
1057  * the best back-translation of that SRW error.
1058  */
1059 static int srw_bib1_map[] = {
1060     66, 238,
1061     /* No doubt there are many more */
1062     0
1063 };
1064
1065
1066 int yaz_diag_bib1_to_srw (int code)
1067 {
1068     const int *p = bib1_srw_map;
1069     while (*p)
1070     {
1071         if (code == p[0])
1072             return p[1];
1073         p += 2;
1074     }
1075     return 1;
1076 }
1077
1078 int yaz_diag_srw_to_bib1(int code)
1079 {
1080     /* Check explicit reverse-map first */
1081     const int *p = srw_bib1_map;
1082     while (*p)
1083     {
1084         if (code == p[0])
1085             return p[1];
1086         p += 2;
1087     }
1088
1089     /* Fall back on reverse lookup in main map */
1090     p = bib1_srw_map;
1091     while (*p)
1092     {
1093         if (code == p[1])
1094             return p[0];
1095         p += 2;
1096     }
1097     return 1;
1098 }
1099
1100 void yaz_add_name_value_int(ODR o, char **name, char **value, int *i,
1101                             char *a_name, Odr_int *val)
1102 {
1103     if (val)
1104     {
1105         name[*i] = a_name;
1106         value[*i] = (char *) odr_malloc(o, 40);
1107         sprintf(value[*i], ODR_INT_PRINTF, *val);
1108         (*i)++;
1109     }
1110 }
1111
1112 void yaz_add_name_value_str(ODR o, char **name, char **value,  int *i,
1113                             char *a_name, char *val)
1114 {
1115     if (val)
1116     {
1117         name[*i] = a_name;
1118         value[*i] = val;
1119         (*i)++;
1120     }
1121 }
1122
1123 static int yaz_get_sru_parms(const Z_SRW_PDU *srw_pdu, ODR encode,
1124                              char **name, char **value, int max_names)
1125 {
1126     int i = 0;
1127     yaz_add_name_value_str(encode, name, value, &i, "version", srw_pdu->srw_version);
1128     name[i] = "operation";
1129     switch(srw_pdu->which)
1130     {
1131     case Z_SRW_searchRetrieve_request:
1132         value[i++] = "searchRetrieve";
1133         switch(srw_pdu->u.request->query_type)
1134         {
1135         case Z_SRW_query_type_cql:
1136             yaz_add_name_value_str(encode, name, value, &i, "query",
1137                                    srw_pdu->u.request->query.cql);
1138             break;
1139         case Z_SRW_query_type_pqf:
1140             yaz_add_name_value_str(encode, name, value, &i, "x-pquery",
1141                                    srw_pdu->u.request->query.pqf);
1142             break;
1143         case Z_SRW_query_type_xcql:
1144             yaz_add_name_value_str(encode, name, value, &i, "x-cql",
1145                                    srw_pdu->u.request->query.xcql);
1146             break;
1147         }
1148         switch(srw_pdu->u.request->sort_type)
1149         {
1150         case Z_SRW_sort_type_none:
1151             break;
1152         case Z_SRW_sort_type_sort:            
1153             yaz_add_name_value_str(encode, name, value, &i, "sortKeys",
1154                                    srw_pdu->u.request->sort.sortKeys);
1155             break;
1156         }
1157         yaz_add_name_value_int(encode, name, value, &i, "startRecord", 
1158                                srw_pdu->u.request->startRecord);
1159         yaz_add_name_value_int(encode, name, value, &i, "maximumRecords", 
1160                                srw_pdu->u.request->maximumRecords);
1161         yaz_add_name_value_str(encode, name, value, &i, "recordSchema",
1162                                srw_pdu->u.request->recordSchema);
1163         yaz_add_name_value_str(encode, name, value, &i, "recordPacking",
1164                                srw_pdu->u.request->recordPacking);
1165         yaz_add_name_value_str(encode, name, value, &i, "recordXPath",
1166                                srw_pdu->u.request->recordXPath);
1167         yaz_add_name_value_str(encode, name, value, &i, "stylesheet",
1168                                srw_pdu->u.request->stylesheet);
1169         yaz_add_name_value_int(encode, name, value, &i, "resultSetTTL", 
1170                                srw_pdu->u.request->resultSetTTL);
1171         break;
1172     case Z_SRW_explain_request:
1173         value[i++] = "explain";
1174         yaz_add_name_value_str(encode, name, value, &i, "stylesheet",
1175                                srw_pdu->u.explain_request->stylesheet);
1176         break;
1177     case Z_SRW_scan_request:
1178         value[i++] = "scan";
1179
1180         switch(srw_pdu->u.scan_request->query_type)
1181         {
1182         case Z_SRW_query_type_cql:
1183             yaz_add_name_value_str(encode, name, value, &i, "scanClause",
1184                                    srw_pdu->u.scan_request->scanClause.cql);
1185             break;
1186         case Z_SRW_query_type_pqf:
1187             yaz_add_name_value_str(encode, name, value, &i, "x-pScanClause",
1188                                    srw_pdu->u.scan_request->scanClause.pqf);
1189             break;
1190         case Z_SRW_query_type_xcql:
1191             yaz_add_name_value_str(encode, name, value, &i, "x-cqlScanClause",
1192                                    srw_pdu->u.scan_request->scanClause.xcql);
1193             break;
1194         }
1195         yaz_add_name_value_int(encode, name, value, &i, "responsePosition", 
1196                                srw_pdu->u.scan_request->responsePosition);
1197         yaz_add_name_value_int(encode, name, value, &i, "maximumTerms", 
1198                                srw_pdu->u.scan_request->maximumTerms);
1199         yaz_add_name_value_str(encode, name, value, &i, "stylesheet",
1200                                srw_pdu->u.scan_request->stylesheet);
1201         break;
1202     case Z_SRW_update_request:
1203         value[i++] = "update";
1204         break;
1205     default:
1206         return -1;
1207     }
1208     if (srw_pdu->extra_args)
1209     {
1210         Z_SRW_extra_arg *ea = srw_pdu->extra_args;
1211         for (; ea && i < max_names-1; ea = ea->next)
1212         {
1213             name[i] = ea->name;
1214             value[i] = ea->value;
1215             i++;
1216         }
1217     }
1218     name[i++] = 0;
1219
1220     return 0;
1221 }
1222
1223 int yaz_sru_get_encode(Z_HTTP_Request *hreq, Z_SRW_PDU *srw_pdu,
1224                        ODR encode, const char *charset)
1225 {
1226     char *name[30], *value[30]; /* definite upper limit for SRU params */
1227     char *uri_args;
1228     char *path;
1229
1230     z_HTTP_header_add_basic_auth(encode, &hreq->headers, 
1231                                  srw_pdu->username, srw_pdu->password);
1232     if (yaz_get_sru_parms(srw_pdu, encode, name, value, 30))
1233         return -1;
1234     yaz_array_to_uri(&uri_args, encode, name, value);
1235
1236     hreq->method = "GET";
1237     
1238     path = (char *)
1239         odr_malloc(encode, strlen(hreq->path) + strlen(uri_args) + 4);
1240
1241     sprintf(path, "%s?%s", hreq->path, uri_args);
1242     yaz_log(YLOG_DEBUG, "SRU HTTP Get Request %s", path);
1243     hreq->path = path;
1244
1245     z_HTTP_header_add_content_type(encode, &hreq->headers,
1246                                    "text/xml", charset);
1247     return 0;
1248 }
1249
1250 int yaz_sru_post_encode(Z_HTTP_Request *hreq, Z_SRW_PDU *srw_pdu,
1251                         ODR encode, const char *charset)
1252 {
1253     char *name[30], *value[30]; /* definite upper limit for SRU params */
1254     char *uri_args;
1255
1256     z_HTTP_header_add_basic_auth(encode, &hreq->headers, 
1257                                  srw_pdu->username, srw_pdu->password);
1258     if (yaz_get_sru_parms(srw_pdu, encode, name, value, 30))
1259         return -1;
1260
1261     yaz_array_to_uri(&uri_args, encode, name, value);
1262
1263     hreq->method = "POST";
1264     
1265     hreq->content_buf = uri_args;
1266     hreq->content_len = strlen(uri_args);
1267
1268     z_HTTP_header_add_content_type(encode, &hreq->headers,
1269                                    "application/x-www-form-urlencoded",
1270                                    charset);
1271     return 0;
1272 }
1273
1274 int yaz_sru_soap_encode(Z_HTTP_Request *hreq, Z_SRW_PDU *srw_pdu,
1275                         ODR odr, const char *charset)
1276 {
1277     Z_SOAP_Handler handlers[3] = {
1278 #if YAZ_HAVE_XML2
1279         {YAZ_XMLNS_SRU_v1_1, 0, (Z_SOAP_fun) yaz_srw_codec},
1280         {YAZ_XMLNS_UPDATE_v0_9, 0, (Z_SOAP_fun) yaz_ucp_codec},
1281 #endif
1282         {0, 0, 0}
1283     };
1284     Z_SOAP *p = (Z_SOAP*) odr_malloc(odr, sizeof(*p));
1285
1286     z_HTTP_header_add_basic_auth(odr, &hreq->headers, 
1287                                  srw_pdu->username, srw_pdu->password);
1288     z_HTTP_header_add_content_type(odr,
1289                                    &hreq->headers,
1290                                    "text/xml", charset);
1291     
1292     z_HTTP_header_add(odr, &hreq->headers,
1293                       "SOAPAction", "\"\"");
1294     p->which = Z_SOAP_generic;
1295     p->u.generic = (Z_SOAP_Generic *) odr_malloc(odr, sizeof(*p->u.generic));
1296     p->u.generic->no = 0;
1297     p->u.generic->ns = 0;
1298     p->u.generic->p = srw_pdu;
1299     p->ns = "http://schemas.xmlsoap.org/soap/envelope/";
1300
1301 #if YAZ_HAVE_XML2
1302     if (srw_pdu->which == Z_SRW_update_request ||
1303         srw_pdu->which == Z_SRW_update_response)
1304         p->u.generic->no = 1; /* second handler */
1305 #endif
1306     return z_soap_codec_enc(odr, &p,
1307                             &hreq->content_buf,
1308                             &hreq->content_len, handlers,
1309                             charset);
1310 }
1311
1312 Z_SRW_recordVersion *yaz_srw_get_record_versions(ODR odr, int num)
1313 {
1314     Z_SRW_recordVersion *ver 
1315         = (Z_SRW_recordVersion *) odr_malloc(odr,num * sizeof(*ver));
1316     int i;
1317     for (i = 0; i < num; ++i)
1318     {
1319         ver[i].versionType = 0;
1320         ver[i].versionValue = 0;
1321     }
1322     return ver;
1323 }
1324
1325 const char *yaz_srw_pack_to_str(int pack)
1326 {
1327     switch(pack)
1328     {
1329     case Z_SRW_recordPacking_string:
1330         return "string";
1331     case Z_SRW_recordPacking_XML:
1332         return "xml";
1333     case Z_SRW_recordPacking_URL:
1334         return "url";
1335     }
1336     return 0;
1337 }
1338
1339 int yaz_srw_str_to_pack(const char *str)
1340 {
1341     if (!yaz_matchstr(str, "string"))
1342         return Z_SRW_recordPacking_string;
1343     if (!yaz_matchstr(str, "xml"))
1344         return Z_SRW_recordPacking_XML;
1345     if (!yaz_matchstr(str, "url"))
1346         return Z_SRW_recordPacking_URL;
1347     return -1;
1348 }
1349
1350 void yaz_encode_sru_extra(Z_SRW_PDU *sr, ODR odr, const char *extra_args)
1351 {
1352     if (extra_args)
1353     {
1354         char **name;
1355         char **val;
1356         Z_SRW_extra_arg **ea = &sr->extra_args;
1357         yaz_uri_to_array(extra_args, odr, &name, &val);
1358
1359         while (*name)
1360         {
1361             *ea = (Z_SRW_extra_arg *) odr_malloc(odr, sizeof(**ea));
1362             (*ea)->name = *name;
1363             (*ea)->value = *val;
1364             ea = &(*ea)->next;
1365             val++;
1366             name++;
1367         }
1368         *ea = 0;
1369     }
1370 }
1371
1372
1373 /*
1374  * Local variables:
1375  * c-basic-offset: 4
1376  * c-file-style: "Stroustrup"
1377  * indent-tabs-mode: nil
1378  * End:
1379  * vim: shiftwidth=4 tabstop=8 expandtab
1380  */
1381