Changed the SRU update structures and codecs to reflect the SRU pre 1.0
[yaz-moved-to-github.git] / src / srw.c
1 /*
2  * Copyright (C) 1995-2006, Index Data ApS
3  * See the file LICENSE for details.
4  *
5  * $Id: srw.c,v 1.52 2006-12-06 21:35:58 adam Exp $
6  */
7 /**
8  * \file srw.c
9  * \brief Implements SRW/SRU package encoding and decoding
10  */
11
12 #include <yaz/srw.h>
13 #if YAZ_HAVE_XML2
14 #include <libxml/parser.h>
15 #include <libxml/tree.h>
16 #include <assert.h>
17
18 static void add_XML_n(xmlNodePtr ptr, const char *elem, char *val, int len,
19                       xmlNsPtr ns_ptr)
20 {
21     if (val)
22     {
23         xmlDocPtr doc = xmlParseMemory(val,len);
24         if (doc)
25         {
26             xmlNodePtr c = xmlNewChild(ptr, ns_ptr, BAD_CAST elem, 0);
27             xmlNodePtr t = xmlDocGetRootElement(doc);
28             xmlAddChild(c, xmlCopyNode(t,1));
29             xmlFreeDoc(doc);
30         }
31     }
32 }
33
34 xmlNodePtr add_xsd_string_n(xmlNodePtr ptr, const char *elem, const char *val,
35                             int len)
36 {
37     if (val)
38     {
39         xmlNodePtr c = xmlNewChild(ptr, 0, BAD_CAST elem, 0);
40         xmlNodePtr t = xmlNewTextLen(BAD_CAST val, len);
41         xmlAddChild(c, t);
42         return t;
43     }
44     return 0;
45 }
46
47 xmlNodePtr add_xsd_string_ns(xmlNodePtr ptr, const char *elem, const char *val,
48                              xmlNsPtr ns_ptr)
49 {
50     if (val)
51     {
52         xmlNodePtr c = xmlNewChild(ptr, ns_ptr, BAD_CAST elem, 0);
53         xmlNodePtr t = xmlNewText(BAD_CAST val);
54         xmlAddChild(c, t);
55         return t;
56     }
57     return 0;
58 }
59
60 xmlNodePtr add_xsd_string(xmlNodePtr ptr, const char *elem, const char *val)
61 {
62     return add_xsd_string_ns(ptr, elem, val, 0);
63 }
64
65 static void add_xsd_integer(xmlNodePtr ptr, const char *elem, const int *val)
66 {
67     if (val)
68     {
69         char str[30];
70         sprintf(str, "%d", *val);
71         xmlNewTextChild(ptr, 0, BAD_CAST elem, BAD_CAST str);
72     }
73 }
74
75 static int match_element(xmlNodePtr ptr, const char *elem)
76 {
77     if (ptr->type == XML_ELEMENT_NODE && !xmlStrcmp(ptr->name, BAD_CAST elem))
78     {
79         return 1;
80     }
81     return 0;
82 }
83
84 #define CHECK_TYPE 0
85
86 static int match_xsd_string_n(xmlNodePtr ptr, const char *elem, ODR o,
87                               char **val, int *len)
88 {
89 #if CHECK_TYPE
90     struct _xmlAttr *attr;
91 #endif
92     if (!match_element(ptr, elem))
93         return 0;
94 #if CHECK_TYPE
95     for (attr = ptr->properties; attr; attr = attr->next)
96         if (!strcmp(attr->name, "type") &&
97             attr->children && attr->children->type == XML_TEXT_NODE)
98         {
99             const char *t = strchr(attr->children->content, ':');
100             if (t)
101                 t = t + 1;
102             else
103                 t = attr->children->content;
104             if (!strcmp(t, "string"))
105                 break;
106         }
107     if (!attr)
108         return 0;
109 #endif
110     ptr = ptr->children;
111     if (!ptr || ptr->type != XML_TEXT_NODE)
112     {
113         *val = "";
114         return 1;
115     }
116     *val = odr_strdup(o, (const char *) ptr->content);
117     if (len)
118         *len = xmlStrlen(ptr->content);
119     return 1;
120 }
121
122
123 static int match_xsd_string(xmlNodePtr ptr, const char *elem, ODR o,
124                             char **val)
125 {
126     return match_xsd_string_n(ptr, elem, o, val, 0);
127 }
128
129
130 /** \brief fixes NS for root node of record data (bug #740) */
131 static void fixup_xmlns(xmlNodePtr ptr, ODR o)
132 {
133     /* should go towards root and collect NS not defined in the record here! */
134     xmlNodePtr p = ptr;
135
136     while (p)
137     {
138         assert(p->type == XML_ELEMENT_NODE);
139
140         p = p->parent;
141         while (p && p->type != XML_ELEMENT_NODE)
142             p = p->prev;
143         if (p)
144         {
145             xmlNsPtr ns = p->ns;
146             for (; ns; ns = ns->next)
147             {
148                 xmlNsPtr n;
149                 for (n = ptr->nsDef; n; n = n->next)
150                     if ((n->prefix == 0 && ns->prefix == 0)
151                         || (n->prefix && ns->prefix 
152                             && !strcmp((const char *) n->prefix,
153                                        (const char *) ns->prefix)))
154                     {
155                         break;
156                     }
157                 if (!n)
158                 {
159                     xmlNsPtr new_ns = xmlCopyNamespace(ns);
160                     
161                     new_ns->next = ptr->nsDef;
162                     ptr->nsDef = new_ns;
163                 }
164             }
165         }
166     }
167 }
168
169 static int match_xsd_XML_n(xmlNodePtr ptr, const char *elem, ODR o,
170                            char **val, int *len)
171 {
172     xmlBufferPtr buf;
173
174     if (!match_element(ptr, elem))
175         return 0;
176
177     ptr = ptr->children;
178     while (ptr && (ptr->type == XML_TEXT_NODE || ptr->type == XML_COMMENT_NODE))
179         ptr = ptr->next;
180     if (!ptr)
181         return 0;
182
183     fixup_xmlns(ptr, o);
184
185     buf = xmlBufferCreate();
186
187     xmlNodeDump(buf, ptr->doc, ptr, 0, 0);
188     
189     *val = odr_malloc(o, buf->use+1);
190     memcpy (*val, buf->content, buf->use);
191     (*val)[buf->use] = '\0';
192
193     if (len)
194         *len = buf->use;
195
196     xmlBufferFree(buf);
197
198     return 1;
199 }
200                      
201 static int match_xsd_integer(xmlNodePtr ptr, const char *elem, ODR o, int **val)
202 {
203 #if CHECK_TYPE
204     struct _xmlAttr *attr;
205 #endif
206     if (!match_element(ptr, elem))
207         return 0;
208 #if CHECK_TYPE
209     for (attr = ptr->properties; attr; attr = attr->next)
210         if (!strcmp(attr->name, "type") &&
211             attr->children && attr->children->type == XML_TEXT_NODE)
212         {
213             const char *t = strchr(attr->children->content, ':');
214             if (t)
215                 t = t + 1;
216             else
217                 t = attr->children->content;
218             if (!strcmp(t, "integer"))
219                 break;
220         }
221     if (!attr)
222         return 0;
223 #endif
224     ptr = ptr->children;
225     if (!ptr || ptr->type != XML_TEXT_NODE)
226         return 0;
227     *val = odr_intdup(o, atoi((const char *) ptr->content));
228     return 1;
229 }
230
231 static int yaz_srw_record(ODR o, xmlNodePtr pptr, Z_SRW_record *rec,
232                           Z_SRW_extra_record **extra,
233                           void *client_data, const char *ns)
234 {
235     if (o->direction == ODR_DECODE)
236     {
237         Z_SRW_extra_record ex;
238
239         char *spack = 0;
240         int pack = Z_SRW_recordPacking_string;
241         xmlNodePtr ptr;
242         xmlNodePtr data_ptr = 0;
243         rec->recordSchema = 0;
244         rec->recordData_buf = 0;
245         rec->recordData_len = 0;
246         rec->recordPosition = 0;
247         *extra = 0;
248
249         ex.extraRecordData_buf = 0;
250         ex.extraRecordData_len = 0;
251         ex.recordIdentifier = 0;
252
253         for (ptr = pptr->children; ptr; ptr = ptr->next)
254         {
255             
256             if (match_xsd_string(ptr, "recordSchema", o, 
257                                  &rec->recordSchema))
258                 ;
259             else if (match_xsd_string(ptr, "recordPacking", o, &spack))
260             {
261                 if (spack && !strcmp(spack, "xml"))
262                     pack = Z_SRW_recordPacking_XML;
263                 if (spack && !strcmp(spack, "url"))
264                     pack = Z_SRW_recordPacking_URL;
265                 if (spack && !strcmp(spack, "string"))
266                     pack = Z_SRW_recordPacking_string;
267             }
268             else if (match_xsd_integer(ptr, "recordPosition", o, 
269                                        &rec->recordPosition))
270                 ;
271             else if (match_element(ptr, "recordData"))
272             {
273                 /* save position of Data until after the loop
274                    then we will know the packing (hopefully), and
275                    unpacking is done once
276                 */
277                 data_ptr = ptr;
278             }
279             else if (match_xsd_XML_n(ptr, "extraRecordData", o, 
280                                      &ex.extraRecordData_buf,
281                                      &ex.extraRecordData_len) )
282                 ;
283             else if (match_xsd_string(ptr, "recordIdentifier", o, 
284                                       &ex.recordIdentifier))
285                 ;
286
287         }
288         if (data_ptr)
289         {
290             switch(pack)
291             {
292             case Z_SRW_recordPacking_XML:
293                 match_xsd_XML_n(data_ptr, "recordData", o, 
294                                 &rec->recordData_buf, &rec->recordData_len);
295                 break;
296             case Z_SRW_recordPacking_URL:
297                 /* just store it as a string.
298                    leave it to the backend to collect the document */
299                 match_xsd_string_n(data_ptr, "recordData", o, 
300                                    &rec->recordData_buf, &rec->recordData_len);
301                 break;
302             case Z_SRW_recordPacking_string:
303                 match_xsd_string_n(data_ptr, "recordData", o, 
304                                    &rec->recordData_buf, &rec->recordData_len);
305                 break;
306             }
307         }
308         rec->recordPacking = pack;
309         if (ex.extraRecordData_buf || ex.recordIdentifier)
310         {
311             *extra = (Z_SRW_extra_record *)
312                 odr_malloc(o, sizeof(Z_SRW_extra_record));
313             memcpy(*extra, &ex, sizeof(Z_SRW_extra_record));
314         }
315     }
316     else if (o->direction == ODR_ENCODE)
317     {
318         xmlNodePtr ptr = pptr;
319         int pack = rec->recordPacking;
320         add_xsd_string(ptr, "recordSchema", rec->recordSchema);
321
322         switch(pack)
323         {
324         case Z_SRW_recordPacking_string:
325             add_xsd_string(ptr, "recordPacking", "string");
326             add_xsd_string_n(ptr, "recordData", rec->recordData_buf,
327                              rec->recordData_len);
328             break;
329         case Z_SRW_recordPacking_XML:
330             add_xsd_string(ptr, "recordPacking", "xml");
331             add_XML_n(ptr, "recordData", rec->recordData_buf,
332                       rec->recordData_len, 0);
333             break;
334         case Z_SRW_recordPacking_URL:
335             add_xsd_string(ptr, "recordPacking", "url");
336             add_xsd_string_n(ptr, "recordData", rec->recordData_buf,
337                              rec->recordData_len);
338             break;
339         }
340         if (rec->recordPosition)
341             add_xsd_integer(ptr, "recordPosition", rec->recordPosition );
342         if (extra && *extra)
343         {
344             if ((*extra)->recordIdentifier)
345                 add_xsd_string(ptr, "recordIdentifier",
346                                (*extra)->recordIdentifier);
347             if ((*extra)->extraRecordData_buf)
348                 add_XML_n(ptr, "extraRecordData",
349                           (*extra)->extraRecordData_buf,
350                           (*extra)->extraRecordData_len, 0);
351         }
352     }
353     return 0;
354 }
355
356 static int yaz_srw_records(ODR o, xmlNodePtr pptr, Z_SRW_record **recs,
357                            Z_SRW_extra_record ***extra,
358                            int *num, void *client_data, const char *ns)
359 {
360     if (o->direction == ODR_DECODE)
361     {
362         int i;
363         xmlNodePtr ptr;
364         *num = 0;
365         for (ptr = pptr->children; ptr; ptr = ptr->next)
366         {
367             if (ptr->type == XML_ELEMENT_NODE &&
368                 !xmlStrcmp(ptr->name, BAD_CAST "record"))
369                 (*num)++;
370         }
371         if (!*num)
372             return 1;
373         *recs = (Z_SRW_record *) odr_malloc(o, *num * sizeof(**recs));
374         *extra = (Z_SRW_extra_record **) odr_malloc(o, *num * sizeof(**extra));
375         for (i = 0, ptr = pptr->children; ptr; ptr = ptr->next)
376         {
377             if (ptr->type == XML_ELEMENT_NODE &&
378                 !xmlStrcmp(ptr->name, BAD_CAST "record"))
379             {
380                 yaz_srw_record(o, ptr, *recs + i, *extra + i, client_data, ns);
381                 i++;
382             }
383         }
384     }
385     else if (o->direction == ODR_ENCODE)
386     {
387         int i;
388         for (i = 0; i < *num; i++)
389         {
390             xmlNodePtr rptr = xmlNewChild(pptr, 0, BAD_CAST "record",
391                                           0);
392             yaz_srw_record(o, rptr, (*recs)+i, (*extra ? *extra + i : 0),
393                            client_data, ns);
394         }
395     }
396     return 0;
397 }
398
399 static int yaz_srw_version(ODR o, xmlNodePtr pptr, Z_SRW_recordVersion *rec,
400                            void *client_data, const char *ns)
401 {
402     if (o->direction == ODR_DECODE)
403     {
404         xmlNodePtr ptr;
405         rec->versionType = 0;
406         rec->versionValue = 0;
407         for (ptr = pptr->children; ptr; ptr = ptr->next)
408         {
409             
410             if (match_xsd_string(ptr, "versionType", o, 
411                                  &rec->versionType))
412                 ;
413             else if (match_xsd_string(ptr, "versionValue", o, 
414                                       &rec->versionValue))
415                 ;
416         }
417     }
418     else if (o->direction == ODR_ENCODE)
419         {
420         xmlNodePtr ptr = pptr;
421         add_xsd_string(ptr, "versionType", rec->versionType);
422         add_xsd_string(ptr, "versionValue", rec->versionValue);
423     }
424     return 0;
425 }
426
427 static int yaz_srw_versions(ODR o, xmlNodePtr pptr, 
428                             Z_SRW_recordVersion **vers,
429                             int *num, void *client_data, const char *ns)
430 {
431     if (o->direction == ODR_DECODE)
432     {
433         int i;
434         xmlNodePtr ptr;
435         *num = 0;
436         for (ptr = pptr->children; ptr; ptr = ptr->next)
437         {
438             if (ptr->type == XML_ELEMENT_NODE &&
439                 !xmlStrcmp(ptr->name, BAD_CAST "recordVersion"))
440                 (*num)++;
441         }
442         if (!*num)
443             return 1;
444         *vers = (Z_SRW_recordVersion *) odr_malloc(o, *num * sizeof(**vers));
445         for (i = 0, ptr = pptr->children; ptr; ptr = ptr->next)
446         {
447             if (ptr->type == XML_ELEMENT_NODE &&
448                 !xmlStrcmp(ptr->name, BAD_CAST "recordVersion"))
449             {
450                 yaz_srw_version(o, ptr, *vers + i, client_data, ns);
451                 i++;
452             }
453         }
454     }
455     else if (o->direction == ODR_ENCODE)
456     {
457         int i;
458         for (i = 0; i < *num; i++)
459             {
460             xmlNodePtr rptr = xmlNewChild(pptr, 0, BAD_CAST "version",
461                                           0);
462             yaz_srw_version(o, rptr, (*vers)+i, client_data, ns);
463         }
464     }
465     return 0;
466 }
467
468 static int yaz_srw_diagnostics(ODR o, xmlNodePtr pptr, Z_SRW_diagnostic **recs,
469                                int *num, void *client_data, const char *ns)
470 {
471     if (o->direction == ODR_DECODE)
472     {
473         int i;
474         xmlNodePtr ptr;
475         *num = 0;
476         for (ptr = pptr->children; ptr; ptr = ptr->next)
477         {
478             if (ptr->type == XML_ELEMENT_NODE &&
479                 !xmlStrcmp(ptr->name, BAD_CAST "diagnostic"))
480                 (*num)++;
481         }
482         if (!*num)
483             return 1;
484         *recs = (Z_SRW_diagnostic *) odr_malloc(o, *num * sizeof(**recs));
485         for (i = 0; i < *num; i++)
486         {
487             (*recs)[i].uri = 0;
488             (*recs)[i].details = 0;
489             (*recs)[i].message = 0;
490         } 
491         for (i = 0, ptr = pptr->children; ptr; ptr = ptr->next)
492         {
493             if (ptr->type == XML_ELEMENT_NODE &&
494                 !xmlStrcmp(ptr->name, BAD_CAST "diagnostic"))
495             {
496                 xmlNodePtr rptr;
497                 (*recs)[i].uri = 0;
498                 (*recs)[i].details = 0;
499                 (*recs)[i].message = 0;
500                 for (rptr = ptr->children; rptr; rptr = rptr->next)
501                 {
502                     if (match_xsd_string(rptr, "uri", o, 
503                                          &(*recs)[i].uri))
504                         ;
505                     else if (match_xsd_string(rptr, "details", o, 
506                                               &(*recs)[i].details))
507                         ;
508                     else if (match_xsd_string(rptr, "message", o, 
509                                               &(*recs)[i].message))
510                         ;
511                 }
512                 i++;
513             }
514         }
515     }
516     else if (o->direction == ODR_ENCODE)
517     {
518         int i;
519         xmlNsPtr ns_diag =
520             xmlNewNs(pptr, BAD_CAST YAZ_XMLNS_DIAG_v1_1, BAD_CAST "diag" );
521         for (i = 0; i < *num; i++)
522         {
523             const char *std_diag = "info:srw/diagnostic/1/";
524             const char *ucp_diag = "info:srw/diagnostic/12/";
525             xmlNodePtr rptr = xmlNewChild(pptr, ns_diag,
526                                           BAD_CAST "diagnostic", 0);
527             add_xsd_string(rptr, "uri", (*recs)[i].uri);
528             if ((*recs)[i].message)
529                 add_xsd_string(rptr, "message", (*recs)[i].message);
530             else if ((*recs)[i].uri )
531             {
532                 if (!strncmp((*recs)[i].uri, std_diag, strlen(std_diag)))
533                 {
534                     int no = atoi((*recs)[i].uri + strlen(std_diag));
535                     const char *message = yaz_diag_srw_str(no);
536                     if (message)
537                         add_xsd_string(rptr, "message", message);
538                 }
539                 else if (!strncmp((*recs)[i].uri, ucp_diag, strlen(ucp_diag)))
540                 {
541                     int no = atoi((*recs)[i].uri + strlen(ucp_diag));
542                     const char *message = yaz_diag_sru_update_str(no);
543                     if (message)
544                         add_xsd_string(rptr, "message", message);
545                 }
546             }
547             add_xsd_string(rptr, "details", (*recs)[i].details);
548         }
549     }
550     return 0;
551 }
552
553 static int yaz_srw_term(ODR o, xmlNodePtr pptr, Z_SRW_scanTerm *term,
554                         void *client_data, const char *ns)
555 {
556     if (o->direction == ODR_DECODE)
557     {
558         xmlNodePtr ptr;
559         term->value = 0;
560         term->numberOfRecords = 0;
561         term->displayTerm = 0;
562         term->whereInList = 0;
563         for (ptr = pptr->children; ptr; ptr = ptr->next)
564         {
565             if (match_xsd_string(ptr, "value", o,  &term->value))
566                 ;
567             else if (match_xsd_integer(ptr, "numberOfRecords", o, 
568                                    &term->numberOfRecords))
569                 ;
570             else if (match_xsd_string(ptr, "displayTerm", o, 
571                                       &term->displayTerm))
572                 ;
573             else if (match_xsd_string(ptr, "whereInList", o, 
574                                       &term->whereInList))
575                 ;
576         }
577     }
578     else if (o->direction == ODR_ENCODE)
579     {
580         xmlNodePtr ptr = pptr;
581         add_xsd_string(ptr, "value", term->value);
582         add_xsd_integer(ptr, "numberOfRecords", term->numberOfRecords);
583         add_xsd_string(ptr, "displayTerm", term->displayTerm);
584         add_xsd_string(ptr, "whereInList", term->whereInList);
585     }
586     return 0;
587 }
588
589 static int yaz_srw_terms(ODR o, xmlNodePtr pptr, Z_SRW_scanTerm **terms,
590                          int *num, void *client_data, const char *ns)
591 {
592     if (o->direction == ODR_DECODE)
593     {
594         int i;
595         xmlNodePtr ptr;
596         *num = 0;
597         for (ptr = pptr->children; ptr; ptr = ptr->next)
598         {
599             if (ptr->type == XML_ELEMENT_NODE &&
600                 !xmlStrcmp(ptr->name, BAD_CAST "term"))
601                 (*num)++;
602         }
603         if (!*num)
604             return 1;
605         *terms = (Z_SRW_scanTerm *) odr_malloc(o, *num * sizeof(**terms));
606         for (i = 0, ptr = pptr->children; ptr; ptr = ptr->next, i++)
607         {
608             if (ptr->type == XML_ELEMENT_NODE &&
609                 !xmlStrcmp(ptr->name, BAD_CAST "term"))
610                 yaz_srw_term(o, ptr, (*terms)+i, client_data, ns);
611         }
612     }
613     else if (o->direction == ODR_ENCODE)
614     {
615         int i;
616         for (i = 0; i < *num; i++)
617         {
618             xmlNodePtr rptr = xmlNewChild(pptr, 0, BAD_CAST "term", 0);
619             yaz_srw_term(o, rptr, (*terms)+i, client_data, ns);
620         }
621     }
622     return 0;
623 }
624
625 int yaz_srw_codec(ODR o, void * vptr, Z_SRW_PDU **handler_data,
626                   void *client_data, const char *ns)
627 {
628     xmlNodePtr pptr = (xmlNodePtr) vptr;
629     if (o->direction == ODR_DECODE)
630     {
631         Z_SRW_PDU **p = handler_data;
632         xmlNodePtr method = pptr->children;
633
634         while (method && method->type == XML_TEXT_NODE)
635             method = method->next;
636         
637         if (!method)
638             return -1;
639         if (method->type != XML_ELEMENT_NODE)
640             return -1;
641
642         *p = yaz_srw_get_core_v_1_1(o);
643         
644         if (!xmlStrcmp(method->name, BAD_CAST "searchRetrieveRequest"))
645         {
646             xmlNodePtr ptr = method->children;
647             Z_SRW_searchRetrieveRequest *req;
648
649             (*p)->which = Z_SRW_searchRetrieve_request;
650             req = (*p)->u.request = (Z_SRW_searchRetrieveRequest *)
651                 odr_malloc(o, sizeof(*req));
652             req->query_type = Z_SRW_query_type_cql;
653             req->query.cql = 0;
654             req->sort_type = Z_SRW_sort_type_none;
655             req->sort.none = 0;
656             req->startRecord = 0;
657             req->maximumRecords = 0;
658             req->recordSchema = 0;
659             req->recordPacking = 0;
660             req->recordXPath = 0;
661             req->resultSetTTL = 0;
662             req->stylesheet = 0;
663             req->database = 0;
664
665             for (; ptr; ptr = ptr->next)
666             {
667                 if (match_xsd_string(ptr, "version", o,
668                                      &(*p)->srw_version))
669                     ;
670                 else if (match_xsd_string(ptr, "query", o, 
671                                      &req->query.cql))
672                     req->query_type = Z_SRW_query_type_cql;
673                 else if (match_xsd_string(ptr, "pQuery", o, 
674                                      &req->query.pqf))
675                     req->query_type = Z_SRW_query_type_pqf;
676                 else if (match_xsd_string(ptr, "xQuery", o, 
677                                      &req->query.xcql))
678                     req->query_type = Z_SRW_query_type_xcql;
679                 else if (match_xsd_integer(ptr, "startRecord", o,
680                                            &req->startRecord))
681                     ;
682                 else if (match_xsd_integer(ptr, "maximumRecords", o,
683                                            &req->maximumRecords))
684                     ;
685                 else if (match_xsd_string(ptr, "recordPacking", o,
686                                           &req->recordPacking))
687                     ;
688                 else if (match_xsd_string(ptr, "recordSchema", o, 
689                                           &req->recordSchema))
690                     ;
691                 else if (match_xsd_string(ptr, "recordXPath", o,
692                                           &req->recordXPath))
693                     ;
694                 else if (match_xsd_integer(ptr, "resultSetTTL", o,
695                                            &req->resultSetTTL))
696                     ;
697                 else if (match_xsd_string(ptr, "sortKeys", o, 
698                                           &req->sort.sortKeys))
699                     req->sort_type = Z_SRW_sort_type_sort;
700                 else if (match_xsd_string(ptr, "stylesheet", o,
701                                            &req->stylesheet))
702                     ;
703                 else if (match_xsd_string(ptr, "database", o,
704                                            &req->database))
705                     ;
706             }
707             if (!req->query.cql && !req->query.pqf && !req->query.xcql)
708             {
709                 /* should put proper diagnostic here */
710                 return -1;
711             }
712         }
713         else if (!xmlStrcmp(method->name, BAD_CAST "searchRetrieveResponse"))
714         {
715             xmlNodePtr ptr = method->children;
716             Z_SRW_searchRetrieveResponse *res;
717
718             (*p)->which = Z_SRW_searchRetrieve_response;
719             res = (*p)->u.response = (Z_SRW_searchRetrieveResponse *)
720                 odr_malloc(o, sizeof(*res));
721
722             res->numberOfRecords = 0;
723             res->resultSetId = 0;
724             res->resultSetIdleTime = 0;
725             res->records = 0;
726             res->num_records = 0;
727             res->diagnostics = 0;
728             res->num_diagnostics = 0;
729             res->nextRecordPosition = 0;
730
731             for (; ptr; ptr = ptr->next)
732             {
733                 if (match_xsd_string(ptr, "version", o,
734                                      &(*p)->srw_version))
735                     ;
736                 else if (match_xsd_integer(ptr, "numberOfRecords", o, 
737                                       &res->numberOfRecords))
738                     ;
739                 else if (match_xsd_string(ptr, "resultSetId", o, 
740                                           &res->resultSetId))
741                     ;
742                 else if (match_xsd_integer(ptr, "resultSetIdleTime", o, 
743                                            &res->resultSetIdleTime))
744                     ;
745                 else if (match_element(ptr, "records"))
746                     yaz_srw_records(o, ptr, &res->records,
747                                     &res->extra_records,
748                                     &res->num_records, client_data, ns);
749                 else if (match_xsd_integer(ptr, "nextRecordPosition", o,
750                                            &res->nextRecordPosition))
751                     ;
752                 else if (match_element(ptr, "diagnostics"))
753                     yaz_srw_diagnostics(o, ptr, &res->diagnostics,
754                                         &res->num_diagnostics,
755                                         client_data, ns);
756             }
757         }
758         else if (!xmlStrcmp(method->name, BAD_CAST "explainRequest"))
759         {
760             Z_SRW_explainRequest *req;
761             xmlNodePtr ptr = method->children;
762             
763             (*p)->which = Z_SRW_explain_request;
764             req = (*p)->u.explain_request = (Z_SRW_explainRequest *)
765                 odr_malloc(o, sizeof(*req));
766             req->recordPacking = 0;
767             req->database = 0;
768             req->stylesheet = 0;
769             for (; ptr; ptr = ptr->next)
770             {
771                 if (match_xsd_string(ptr, "version", o,
772                                            &(*p)->srw_version))
773                     ;
774                 else if (match_xsd_string(ptr, "stylesheet", o,
775                                           &req->stylesheet))
776                     ;
777                 else if (match_xsd_string(ptr, "recordPacking", o,
778                                      &req->recordPacking))
779                     ;
780                 else if (match_xsd_string(ptr, "database", o,
781                                      &req->database))
782                     ;
783             }
784         }
785         else if (!xmlStrcmp(method->name, BAD_CAST "explainResponse"))
786         {
787             Z_SRW_explainResponse *res;
788             xmlNodePtr ptr = method->children;
789
790             (*p)->which = Z_SRW_explain_response;
791             res = (*p)->u.explain_response = (Z_SRW_explainResponse*)
792                 odr_malloc(o, sizeof(*res));
793             res->diagnostics = 0;
794             res->num_diagnostics = 0;
795             res->record.recordSchema = 0;
796             res->record.recordData_buf = 0;
797             res->record.recordData_len = 0;
798             res->record.recordPosition = 0;
799
800             for (; ptr; ptr = ptr->next)
801             {
802                 if (match_xsd_string(ptr, "version", o,
803                                            &(*p)->srw_version))
804                     ;
805                 else if (match_element(ptr, "record"))
806                     yaz_srw_record(o, ptr, &res->record, &res->extra_record,
807                                    client_data, ns);
808                 else if (match_element(ptr, "diagnostics"))
809                     yaz_srw_diagnostics(o, ptr, &res->diagnostics,
810                                         &res->num_diagnostics,
811                                         client_data, ns);
812                 ;
813             }
814         }
815         else if (!xmlStrcmp(method->name, BAD_CAST "scanRequest"))
816         {
817             Z_SRW_scanRequest *req;
818             xmlNodePtr ptr = method->children;
819
820             (*p)->which = Z_SRW_scan_request;
821             req = (*p)->u.scan_request = (Z_SRW_scanRequest *)
822                 odr_malloc(o, sizeof(*req));
823             req->query_type = Z_SRW_query_type_cql;
824             req->scanClause.cql = 0;
825             req->responsePosition = 0;
826             req->maximumTerms = 0;
827             req->stylesheet = 0;
828             req->database = 0;
829             
830             for (; ptr; ptr = ptr->next)
831             {
832                 if (match_xsd_string(ptr, "version", o,
833                                      &(*p)->srw_version))
834                     ;
835                 else if (match_xsd_string(ptr, "scanClause", o,
836                                      &req->scanClause.cql))
837                     ;
838                 else if (match_xsd_string(ptr, "pScanClause", o,
839                                           &req->scanClause.pqf))
840                 {
841                     req->query_type = Z_SRW_query_type_pqf;
842                 }
843                 else if (match_xsd_integer(ptr, "responsePosition", o,
844                                            &req->responsePosition))
845                     ;
846                 else if (match_xsd_integer(ptr, "maximumTerms", o,
847                                            &req->maximumTerms))
848                     ;
849                 else if (match_xsd_string(ptr, "stylesheet", o,
850                                           &req->stylesheet))
851                     ;
852                 else if (match_xsd_string(ptr, "database", o,
853                                           &req->database))
854                     ;
855             }
856         }
857         else if (!xmlStrcmp(method->name, BAD_CAST "scanResponse"))
858         {
859             Z_SRW_scanResponse *res;
860             xmlNodePtr ptr = method->children;
861
862             (*p)->which = Z_SRW_scan_response;
863             res = (*p)->u.scan_response = (Z_SRW_scanResponse *)
864                 odr_malloc(o, sizeof(*res));
865             res->terms = 0;
866             res->num_terms = 0;
867             res->diagnostics = 0;
868             res->num_diagnostics = 0;
869             
870             for (; ptr; ptr = ptr->next)
871             {
872                 if (match_xsd_string(ptr, "version", o,
873                                      &(*p)->srw_version))
874                     ;
875                 else if (match_element(ptr, "terms"))
876                     yaz_srw_terms(o, ptr, &res->terms,
877                                   &res->num_terms, client_data,
878                                   ns);
879                 else if (match_element(ptr, "diagnostics"))
880                     yaz_srw_diagnostics(o, ptr, &res->diagnostics,
881                                         &res->num_diagnostics,
882                                         client_data, ns);
883             }
884         }
885         else
886         {
887             *p = 0;
888             return -1;
889         }
890     }
891     else if (o->direction == ODR_ENCODE)
892     {
893         Z_SRW_PDU **p = handler_data;
894         xmlNsPtr ns_srw;
895         
896         if ((*p)->which == Z_SRW_searchRetrieve_request)
897         {
898             Z_SRW_searchRetrieveRequest *req = (*p)->u.request;
899             xmlNodePtr ptr = xmlNewChild(pptr, 0,
900                                          BAD_CAST "searchRetrieveRequest", 0);
901             ns_srw = xmlNewNs(ptr, BAD_CAST ns, BAD_CAST "zs");
902             xmlSetNs(ptr, ns_srw);
903
904             if ((*p)->srw_version)
905                 add_xsd_string(ptr, "version", (*p)->srw_version);
906             switch(req->query_type)
907             {
908             case Z_SRW_query_type_cql:
909                 add_xsd_string(ptr, "query", req->query.cql);
910                 break;
911             case Z_SRW_query_type_xcql:
912                 add_xsd_string(ptr, "xQuery", req->query.xcql);
913                 break;
914             case Z_SRW_query_type_pqf:
915                 add_xsd_string(ptr, "pQuery", req->query.pqf);
916                 break;
917             }
918             add_xsd_integer(ptr, "startRecord", req->startRecord);
919             add_xsd_integer(ptr, "maximumRecords", req->maximumRecords);
920             add_xsd_string(ptr, "recordPacking", req->recordPacking);
921             add_xsd_string(ptr, "recordSchema", req->recordSchema);
922             add_xsd_string(ptr, "recordXPath", req->recordXPath);
923             add_xsd_integer(ptr, "resultSetTTL", req->resultSetTTL);
924             switch(req->sort_type)
925             {
926             case Z_SRW_sort_type_none:
927                 break;
928             case Z_SRW_sort_type_sort:
929                 add_xsd_string(ptr, "sortKeys", req->sort.sortKeys);
930                 break;
931             case Z_SRW_sort_type_xSort:
932                 add_xsd_string(ptr, "xSortKeys", req->sort.xSortKeys);
933                 break;
934             }
935             add_xsd_string(ptr, "stylesheet", req->stylesheet);
936             add_xsd_string(ptr, "database", req->database);
937         }
938         else if ((*p)->which == Z_SRW_searchRetrieve_response)
939         {
940             Z_SRW_searchRetrieveResponse *res = (*p)->u.response;
941             xmlNodePtr ptr = xmlNewChild(pptr, 0,
942                                          BAD_CAST "searchRetrieveResponse", 0);
943             ns_srw = xmlNewNs(ptr, BAD_CAST ns, BAD_CAST "zs");
944             xmlSetNs(ptr, ns_srw);
945
946             if ((*p)->srw_version)
947                 add_xsd_string(ptr, "version", (*p)->srw_version);
948             add_xsd_integer(ptr, "numberOfRecords", res->numberOfRecords);
949             add_xsd_string(ptr, "resultSetId", res->resultSetId);
950             add_xsd_integer(ptr, "resultSetIdleTime", res->resultSetIdleTime);
951             if (res->num_records)
952             {
953                 xmlNodePtr rptr = xmlNewChild(ptr, 0, BAD_CAST "records", 0);
954                 yaz_srw_records(o, rptr, &res->records, &res->extra_records,
955                                 &res->num_records,
956                                 client_data, ns);
957             }
958             add_xsd_integer(ptr, "nextRecordPosition",
959                             res->nextRecordPosition);
960             if (res->num_diagnostics)
961             {
962                 xmlNodePtr rptr = xmlNewChild(ptr, 0, BAD_CAST "diagnostics",
963                                               0);
964                 yaz_srw_diagnostics(o, rptr, &res->diagnostics,
965                                     &res->num_diagnostics, client_data, ns);
966             }
967         }
968         else if ((*p)->which == Z_SRW_explain_request)
969         {
970             Z_SRW_explainRequest *req = (*p)->u.explain_request;
971             xmlNodePtr ptr = xmlNewChild(pptr, 0, BAD_CAST "explainRequest",
972                                          0);
973             ns_srw = xmlNewNs(ptr, BAD_CAST ns, BAD_CAST "zs");
974             xmlSetNs(ptr, ns_srw);
975
976             add_xsd_string(ptr, "version", (*p)->srw_version);
977             add_xsd_string(ptr, "recordPacking", req->recordPacking);
978             add_xsd_string(ptr, "stylesheet", req->stylesheet);
979             add_xsd_string(ptr, "database", req->database);
980         }
981         else if ((*p)->which == Z_SRW_explain_response)
982         {
983             Z_SRW_explainResponse *res = (*p)->u.explain_response;
984             xmlNodePtr ptr = xmlNewChild(pptr, 0, BAD_CAST "explainResponse",
985                                          0);
986             ns_srw = xmlNewNs(ptr, BAD_CAST ns, BAD_CAST "zs");
987             xmlSetNs(ptr, ns_srw);
988
989             add_xsd_string(ptr, "version", (*p)->srw_version);
990             if (1)
991             {
992                 xmlNodePtr ptr1 = xmlNewChild(ptr, 0, BAD_CAST "record", 0);
993                 yaz_srw_record(o, ptr1, &res->record, &res->extra_record,
994                                client_data, ns);
995             }
996             if (res->num_diagnostics)
997             {
998                 xmlNodePtr rptr = xmlNewChild(ptr, 0, BAD_CAST "diagnostics",
999                                               0);
1000                 yaz_srw_diagnostics(o, rptr, &res->diagnostics,
1001                                     &res->num_diagnostics, client_data, ns);
1002             }
1003         }
1004         else if ((*p)->which == Z_SRW_scan_request)
1005         {
1006             Z_SRW_scanRequest *req = (*p)->u.scan_request;
1007             xmlNodePtr ptr = xmlNewChild(pptr, 0, BAD_CAST "scanRequest", 0);
1008             ns_srw = xmlNewNs(ptr, BAD_CAST ns, BAD_CAST "zs");
1009             xmlSetNs(ptr, ns_srw);
1010
1011             add_xsd_string(ptr, "version", (*p)->srw_version);
1012             switch(req->query_type)
1013             {
1014             case Z_SRW_query_type_cql:
1015                 add_xsd_string(ptr, "scanClause", req->scanClause.cql);
1016                 break;
1017             case Z_SRW_query_type_pqf:
1018                 add_xsd_string(ptr, "pScanClause", req->scanClause.pqf);
1019                 break;
1020             }
1021             add_xsd_integer(ptr, "responsePosition", req->responsePosition);
1022             add_xsd_integer(ptr, "maximumTerms", req->maximumTerms);
1023             add_xsd_string(ptr, "stylesheet", req->stylesheet);
1024             add_xsd_string(ptr, "database", req->database);
1025         }
1026         else if ((*p)->which == Z_SRW_scan_response)
1027         {
1028             Z_SRW_scanResponse *res = (*p)->u.scan_response;
1029             xmlNodePtr ptr = xmlNewChild(pptr, 0, BAD_CAST "scanResponse", 0);
1030             ns_srw = xmlNewNs(ptr, BAD_CAST ns, BAD_CAST "zs");
1031             xmlSetNs(ptr, ns_srw);
1032
1033             add_xsd_string(ptr, "version", (*p)->srw_version);
1034
1035             if (res->num_terms)
1036             {
1037                 xmlNodePtr rptr = xmlNewChild(ptr, 0, BAD_CAST "terms", 0);
1038                 yaz_srw_terms(o, rptr, &res->terms, &res->num_terms,
1039                               client_data, ns);
1040             }
1041             if (res->num_diagnostics)
1042             {
1043                 xmlNodePtr rptr = xmlNewChild(ptr, 0, BAD_CAST "diagnostics",
1044                                               0);
1045                 yaz_srw_diagnostics(o, rptr, &res->diagnostics,
1046                                     &res->num_diagnostics, client_data, ns);
1047             }
1048         }
1049         else
1050             return -1;
1051
1052     }
1053     return 0;
1054 }
1055
1056 int yaz_ucp_codec(ODR o, void * vptr, Z_SRW_PDU **handler_data,
1057                   void *client_data, const char *ns_ucp_str)
1058 {
1059     xmlNodePtr pptr = (xmlNodePtr) vptr;
1060     const char *ns_srw_str = YAZ_XMLNS_SRU_v1_1;
1061     if (o->direction == ODR_DECODE)
1062     {
1063         Z_SRW_PDU **p = handler_data;
1064         xmlNodePtr method = pptr->children;
1065
1066         while (method && method->type == XML_TEXT_NODE)
1067             method = method->next;
1068         
1069         if (!method)
1070             return -1;
1071         if (method->type != XML_ELEMENT_NODE)
1072             return -1;
1073
1074         *p = yaz_srw_get_core_v_1_1(o);
1075         
1076         if (!xmlStrcmp(method->name, BAD_CAST "updateRequest"))
1077         {
1078             xmlNodePtr ptr = method->children;
1079             Z_SRW_updateRequest *req;
1080             char *oper = 0;
1081
1082             (*p)->which = Z_SRW_update_request;
1083             req = (*p)->u.update_request = (Z_SRW_updateRequest *)
1084                 odr_malloc(o, sizeof(*req));
1085             req->database = 0;
1086             req->operation = 0;
1087             req->recordId = 0;
1088             req->recordVersions = 0;
1089             req->num_recordVersions = 0;
1090             req->record = 0;
1091             req->extra_record = 0;
1092             req->extraRequestData_buf = 0;
1093             req->extraRequestData_len = 0;
1094             req->stylesheet = 0;
1095
1096             for (; ptr; ptr = ptr->next)
1097             {
1098                 if (match_xsd_string(ptr, "version", o,
1099                                      &(*p)->srw_version))
1100                     ;
1101                 else if (match_xsd_string(ptr, "action", o, 
1102                                           &oper)){
1103                     if ( oper ){
1104                         if ( !strcmp(oper, "info:srw/action/1/delete"))
1105                             req->operation = "delete";
1106                         else if (!strcmp(oper,"info:srw/action/1/replace" ))
1107                             req->operation = "replace";
1108                         else if ( !strcmp( oper, "info:srw/action/1/create"))
1109                             req->operation = "insert";
1110                     }
1111                 }
1112                 else if (match_xsd_string(ptr, "recordIdentifier", o,
1113                                           &req->recordId))
1114                     ;
1115                 else if (match_element(ptr, "recordVersions" ) )
1116                     yaz_srw_versions( o, ptr, &req->recordVersions,
1117                                       &req->num_recordVersions, client_data,
1118                                       ns_ucp_str);
1119                 else if (match_element(ptr, "record"))
1120                 {
1121                     req->record = yaz_srw_get_record(o);
1122                     yaz_srw_record(o, ptr, req->record, &req->extra_record,
1123                                    client_data, ns_ucp_str);
1124                 }
1125                 else if (match_xsd_string(ptr, "stylesheet", o,
1126                                            &req->stylesheet))
1127                     ;
1128                 else if (match_xsd_string(ptr, "database", o,
1129                                            &req->database))
1130                     ;
1131             }
1132         }
1133         else if (!xmlStrcmp(method->name, BAD_CAST "updateResponse"))
1134         {
1135             xmlNodePtr ptr = method->children;
1136             Z_SRW_updateResponse *res;
1137
1138             (*p)->which = Z_SRW_update_response;
1139             res = (*p)->u.update_response = (Z_SRW_updateResponse *)
1140                 odr_malloc(o, sizeof(*res));
1141
1142             res->operationStatus = 0;
1143             res->recordId = 0;
1144             res->recordVersions = 0;
1145             res->num_recordVersions = 0;
1146             res->diagnostics = 0;
1147             res->num_diagnostics = 0;
1148             res->record = 0;
1149             res->extra_record = 0;
1150             res->extraResponseData_buf = 0;
1151             res->extraResponseData_len = 0;
1152
1153             for (; ptr; ptr = ptr->next)
1154             {
1155                 if (match_xsd_string(ptr, "version", o,
1156                                      &(*p)->srw_version))
1157                     ;
1158                 else if (match_xsd_string(ptr, "operationStatus", o, 
1159                                       &res->operationStatus ))
1160                     ;
1161                 else if (match_xsd_string(ptr, "recordIdentifier", o, 
1162                                           &res->recordId))
1163                     ;
1164                 else if (match_element(ptr, "recordVersions" )) 
1165                     yaz_srw_versions(o, ptr, &res->recordVersions,
1166                                      &res->num_recordVersions,
1167                                      client_data, ns_ucp_str);
1168                 else if (match_element(ptr, "record"))
1169                 {
1170                     res->record = yaz_srw_get_record(o);
1171                     yaz_srw_record(o, ptr, res->record, &res->extra_record,
1172                                    client_data, ns_ucp_str);
1173                 }
1174                 else if (match_element(ptr, "diagnostics"))
1175                     yaz_srw_diagnostics(o, ptr, &res->diagnostics,
1176                                         &res->num_diagnostics,
1177                                         client_data, ns_ucp_str);
1178             }
1179         }
1180         else if (!xmlStrcmp(method->name, BAD_CAST "explainUpdateRequest"))
1181         {
1182         }
1183         else if (!xmlStrcmp(method->name, BAD_CAST "explainUpdateResponse"))
1184         {
1185         }
1186         else
1187         {
1188             *p = 0;
1189             return -1;
1190         }
1191     }
1192     else if (o->direction == ODR_ENCODE)
1193     {
1194         Z_SRW_PDU **p = handler_data;
1195         xmlNsPtr ns_ucp, ns_srw;
1196
1197
1198         if ((*p)->which == Z_SRW_update_request)
1199         {
1200             Z_SRW_updateRequest *req = (*p)->u.update_request;
1201             xmlNodePtr ptr = xmlNewChild(pptr, 0, BAD_CAST "updateRequest", 0);
1202             ns_ucp = xmlNewNs(ptr, BAD_CAST ns_ucp_str, BAD_CAST "zu");
1203             xmlSetNs(ptr, ns_ucp);
1204             ns_srw = xmlNewNs(ptr, BAD_CAST ns_srw_str, BAD_CAST "zs");
1205
1206             add_xsd_string_ns(ptr, "version", (*p)->srw_version, ns_srw);
1207             add_xsd_string(ptr, "action", req->operation);
1208             add_xsd_string(ptr, "recordIdentifier", req->recordId );
1209             if (req->recordVersions)
1210                 yaz_srw_versions( o, ptr, &req->recordVersions,
1211                                   &req->num_recordVersions,
1212                                   client_data, ns_ucp_str);
1213             if (req->record && req->record->recordData_len)
1214             {
1215                 xmlNodePtr rptr = xmlNewChild(ptr, 0, BAD_CAST "record", 0);
1216                 xmlSetNs(rptr, ns_srw);
1217                 yaz_srw_record(o, rptr, req->record, &req->extra_record,
1218                                client_data, ns_ucp_str);
1219             }
1220             if (req->extraRequestData_len)
1221             {
1222                 add_XML_n(ptr, "extraRequestData", 
1223                           req->extraRequestData_buf, 
1224                           req->extraRequestData_len, ns_srw);
1225             }
1226             add_xsd_string(ptr, "stylesheet", req->stylesheet);
1227             add_xsd_string(ptr, "database", req->database);
1228         }
1229         else if ((*p)->which == Z_SRW_update_response)
1230         {
1231             Z_SRW_updateResponse *res = (*p)->u.update_response;
1232             xmlNodePtr ptr = xmlNewChild(pptr, 0, (xmlChar *) 
1233                                          "updateResponse", 0);
1234             ns_ucp = xmlNewNs(ptr, BAD_CAST ns_ucp_str, BAD_CAST "zu");
1235             xmlSetNs(ptr, ns_ucp);
1236             ns_srw = xmlNewNs(ptr, BAD_CAST ns_srw_str, BAD_CAST "zs");
1237             
1238             add_xsd_string_ns(ptr, "version", (*p)->srw_version, ns_srw);
1239             add_xsd_string(ptr, "operationStatus", res->operationStatus );
1240             add_xsd_string(ptr, "recordIdentifier", res->recordId );
1241             if (res->recordVersions)
1242                 yaz_srw_versions(o, ptr, &res->recordVersions,
1243                                  &res->num_recordVersions,
1244                                  client_data, ns_ucp_str);
1245             if (res->record && res->record->recordData_len)
1246             {
1247                 xmlNodePtr rptr = xmlNewChild(ptr, 0, BAD_CAST "record", 0);
1248                 xmlSetNs(rptr, ns_srw);
1249                 yaz_srw_record(o, rptr, res->record, &res->extra_record,
1250                                client_data, ns_ucp_str);
1251             }
1252             if (res->num_diagnostics)
1253             {
1254                 xmlNsPtr ns_diag =
1255                     xmlNewNs(pptr, BAD_CAST YAZ_XMLNS_DIAG_v1_1,
1256                              BAD_CAST "diag" );
1257                 
1258                 xmlNodePtr rptr = xmlNewChild(ptr, ns_diag, BAD_CAST "diagnostics", 0);
1259                 yaz_srw_diagnostics(o, rptr, &res->diagnostics,
1260                                     &res->num_diagnostics, client_data,
1261                                     ns_ucp_str);
1262             }
1263             if (res->extraResponseData_len)
1264                 add_XML_n(ptr, "extraResponseData", 
1265                           res->extraResponseData_buf, 
1266                           res->extraResponseData_len, ns_srw);
1267         }
1268         else
1269             return -1;
1270
1271     }
1272     return 0;
1273 }
1274
1275 #endif
1276
1277
1278 /*
1279  * Local variables:
1280  * c-basic-offset: 4
1281  * indent-tabs-mode: nil
1282  * End:
1283  * vim: shiftwidth=4 tabstop=8 expandtab
1284  */
1285