3757f284c5cb5e7250db80520a6a823447b3cba8
[idzebra-moved-to-github.git] / data1 / d1_expout.c
1 /* $Id: d1_expout.c,v 1.10 2007-04-16 08:44:31 adam Exp $
2    Copyright (C) 1995-2007
3    Index Data ApS
4
5 This file is part of the Zebra server.
6
7 Zebra is free software; you can redistribute it and/or modify it under
8 the terms of the GNU General Public License as published by the Free
9 Software Foundation; either version 2, or (at your option) any later
10 version.
11
12 Zebra is distributed in the hope that it will be useful, but WITHOUT ANY
13 WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
15 for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20
21 */
22
23 /*
24  * This module converts data1 tree to Z39.50 Explain records  
25  */
26
27 #include <assert.h>
28 #include <string.h>
29 #include <stdlib.h>
30
31 #include <yaz/log.h>
32 #include <yaz/proto.h>
33 #include <yaz/oid_db.h>
34 #include <yaz/snprintf.h>
35 #include <idzebra/data1.h>
36
37 typedef struct {
38     data1_handle dh;
39     ODR o;
40     int select;
41
42     bool_t *false_value;
43     bool_t *true_value;
44 } ExpHandle;
45
46 static int is_numeric_tag (ExpHandle *eh, data1_node *c)
47 {
48     if (!c || c->which != DATA1N_tag)
49         return 0;
50     if (!c->u.tag.element)
51     {
52         yaz_log(YLOG_WARN, "Tag %s is local", c->u.tag.tag);
53         return 0;
54     }
55     if (c->u.tag.element->tag->which != DATA1T_numeric)
56     {
57         yaz_log(YLOG_WARN, "Tag %s is not numeric", c->u.tag.tag);
58         return 0;
59     }
60     if (eh->select && !c->u.tag.node_selected)
61         return 0;
62     return c->u.tag.element->tag->value.numeric;
63 }
64
65 static int is_data_tag (ExpHandle *eh, data1_node *c)
66 {
67     if (!c || c->which != DATA1N_data)
68         return 0;
69     if (eh->select && !c->u.tag.node_selected)
70         return 0;
71     return 1;
72 }
73
74 static int *f_integer(ExpHandle *eh, data1_node *c)
75 {
76     int *r;
77     char intbuf[64];
78
79     c = c->child;
80     if (!is_data_tag (eh, c) || c->u.data.len > 63)
81         return 0;
82     r = (int *)odr_malloc(eh->o, sizeof(*r));
83     sprintf(intbuf, "%.*s", c->u.data.len, c->u.data.data);
84     *r = atoi(intbuf);
85     return r;
86 }
87
88 static char *f_string(ExpHandle *eh, data1_node *c)
89 {
90     char *r;
91
92     c = c->child;
93     if (!is_data_tag (eh, c))
94         return 0;
95     r = (char *)odr_malloc(eh->o, c->u.data.len+1);
96     memcpy(r, c->u.data.data, c->u.data.len);
97     r[c->u.data.len] = '\0';
98     return r;
99 }
100
101 static bool_t *f_bool(ExpHandle *eh, data1_node *c)
102 {
103     bool_t *tf;
104     char intbuf[64];
105
106     c = c->child;
107     if (!is_data_tag (eh, c) || c->u.data.len > 63)
108         return 0;
109     tf = (int *)odr_malloc (eh->o, sizeof(*tf));
110     sprintf(intbuf, "%.*s", c->u.data.len, c->u.data.data);
111     *tf = atoi(intbuf);
112     return tf;
113 }
114
115 static Odr_oid *f_oid(ExpHandle *eh, data1_node *c, oid_class oclass)
116 {
117     char oidstr[64];
118
119     c = c->child;
120     if (!is_data_tag (eh, c) || c->u.data.len > 63)
121         return 0;
122     yaz_snprintf(oidstr, sizeof(oidstr)-1, 
123                  "%.*s", c->u.data.len, c->u.data.data);
124
125     return yaz_string_to_oid_odr(yaz_oid_std(),
126                                  CLASS_GENERAL, oidstr, eh->o);
127 }
128
129 static Z_IntUnit *f_intunit(ExpHandle *eh, data1_node *c)
130 {
131     /* fix */
132     return 0;
133 }
134
135 static Z_HumanString *f_humstring(ExpHandle *eh, data1_node *c)
136 {
137     Z_HumanString *r;
138     Z_HumanStringUnit *u;
139
140     c = c->child;
141     if (!is_data_tag (eh, c))
142         return 0;
143     r = (Z_HumanString *)odr_malloc(eh->o, sizeof(*r));
144     r->num_strings = 1;
145     r->strings = (Z_HumanStringUnit **)odr_malloc(eh->o, sizeof(Z_HumanStringUnit*));
146     r->strings[0] = u = (Z_HumanStringUnit *)odr_malloc(eh->o, sizeof(*u));
147     u->language = 0;
148     u->text = (char *)odr_malloc(eh->o, c->u.data.len+1);
149     memcpy(u->text, c->u.data.data, c->u.data.len);
150     u->text[c->u.data.len] = '\0';
151     return r;
152 }
153
154 static Z_CommonInfo *f_commonInfo(ExpHandle *eh, data1_node *n)
155 {
156     Z_CommonInfo *res = (Z_CommonInfo *)odr_malloc(eh->o, sizeof(*res));
157     data1_node *c;
158
159     res->dateAdded = 0;
160     res->dateChanged = 0;
161     res->expiry = 0;
162     res->humanStringLanguage = 0;
163     res->otherInfo = 0;
164
165     for (c = n->child; c; c = c->next)
166     {
167         switch (is_numeric_tag (eh, c))
168         {
169             case 601: res->dateAdded = f_string(eh, c); break;
170             case 602: res->dateChanged = f_string(eh, c); break;
171             case 603: res->expiry = f_string(eh, c); break;
172             case 604: res->humanStringLanguage = f_string(eh, c); break;
173         }
174     }
175     return res;
176 }
177
178 Odr_oid **f_oid_seq (ExpHandle *eh, data1_node *n, int *num, oid_class oclass)
179 {
180     Odr_oid **res;
181     data1_node *c;
182     int i;
183
184     *num = 0;
185     for (c = n->child ; c; c = c->next)
186         if (is_numeric_tag (eh, c) == 1000)
187             ++(*num);
188     if (!*num)
189         return NULL;
190     res = (int **)odr_malloc (eh->o, sizeof(*res) * (*num));
191     for (c = n->child, i = 0 ; c; c = c->next)
192         if (is_numeric_tag (eh, c) == 1000)
193             res[i++] = f_oid (eh, c, oclass);
194     return res;
195 }
196     
197 char **f_string_seq (ExpHandle *eh, data1_node *n, int *num)
198 {
199     char **res;
200     data1_node *c;
201     int i;
202
203     *num = 0;
204     for (c = n->child ; c; c = c->next)
205     {
206         if (is_numeric_tag (eh, c) != 1001)
207             continue;
208         ++(*num);
209     }
210     if (!*num)
211         return NULL;
212     res = (char **)odr_malloc (eh->o, sizeof(*res) * (*num));
213     for (c = n->child, i = 0 ; c; c = c->next)
214     {
215         if (is_numeric_tag (eh, c) != 1001)
216             continue;
217         res[i++] = f_string (eh, c);
218     }
219     return res;
220 }
221
222 Z_ProximitySupport *f_proximitySupport (ExpHandle *eh, data1_node *n)
223 {
224     Z_ProximitySupport *res = (Z_ProximitySupport *)
225         odr_malloc (eh->o, sizeof(*res));
226     res->anySupport = eh->false_value;
227     res->num_unitsSupported = 0;
228     res->unitsSupported = 0;
229     return res;
230 }
231
232 Z_RpnCapabilities *f_rpnCapabilities (ExpHandle *eh, data1_node *n)
233 {
234     Z_RpnCapabilities *res = (Z_RpnCapabilities *)
235         odr_malloc (eh->o, sizeof(*res));
236     data1_node *c;
237
238     res->num_operators = 0;
239     res->operators = NULL;
240     res->resultSetAsOperandSupported = eh->false_value;
241     res->restrictionOperandSupported = eh->false_value;
242     res->proximity = NULL;
243
244     for (c = n->child; c; c = c->next)
245     {
246         int i = 0;
247         switch (is_numeric_tag(eh, c))
248         {
249         case 550:
250             for (n = c->child; n; n = n->next)
251             {
252                 if (is_numeric_tag(eh, n) != 551)
253                     continue;
254                 (res->num_operators)++;
255             }
256             if (res->num_operators)
257                 res->operators = (int **)
258                     odr_malloc (eh->o, res->num_operators
259                                 * sizeof(*res->operators));
260             for (n = c->child; n; n = n->next)
261             {
262                 if (is_numeric_tag(eh, n) != 551)
263                     continue;
264                 res->operators[i++] = f_integer (eh, n);
265             }
266             break;
267         case 552:
268             res->resultSetAsOperandSupported = f_bool (eh, c);
269             break;
270         case 553:
271             res->restrictionOperandSupported = f_bool (eh, c);
272             break;
273         case 554:
274             res->proximity = f_proximitySupport (eh, c);
275             break;
276         }
277     }
278     return res;
279 }
280
281 Z_QueryTypeDetails *f_queryTypeDetails (ExpHandle *eh, data1_node *n)
282 {
283     Z_QueryTypeDetails *res = (Z_QueryTypeDetails *)
284         odr_malloc(eh->o, sizeof(*res));
285     data1_node *c;
286
287     res->which = Z_QueryTypeDetails_rpn;
288     res->u.rpn = 0;
289     for (c = n->child; c; c = c->next)
290     {
291         switch (is_numeric_tag(eh, c))
292         {
293         case 519:
294             res->which = Z_QueryTypeDetails_rpn;
295             res->u.rpn = f_rpnCapabilities (eh, c);
296             break;
297         case 520:
298             break;
299         case 521:
300             break;
301         }
302     }
303     return res;
304 }
305
306 static Z_AccessInfo *f_accessInfo(ExpHandle *eh, data1_node *n)
307 {
308     Z_AccessInfo *res = (Z_AccessInfo *)odr_malloc(eh->o, sizeof(*res));
309     data1_node *c;
310
311     res->num_queryTypesSupported = 0;
312     res->queryTypesSupported = 0;
313     res->num_diagnosticsSets = 0;
314     res->diagnosticsSets = 0;
315     res->num_attributeSetIds = 0;
316     res->attributeSetIds = 0;
317     res->num_schemas = 0;
318     res->schemas = 0;
319     res->num_recordSyntaxes = 0;
320     res->recordSyntaxes = 0;
321     res->num_resourceChallenges = 0;
322     res->resourceChallenges = 0;
323     res->restrictedAccess = 0;
324     res->costInfo = 0;
325     res->num_variantSets = 0;
326     res->variantSets = 0;
327     res->num_elementSetNames = 0;
328     res->elementSetNames = 0;
329     res->num_unitSystems = 0;
330     res->unitSystems = 0;
331
332     for (c = n->child; c; c = c->next)
333     {
334         int i = 0;
335         switch (is_numeric_tag (eh, c))
336         {
337         case 501:
338             for (n = c->child; n; n = n->next)
339             {
340                 if (is_numeric_tag(eh, n) != 518)
341                     continue;
342                 (res->num_queryTypesSupported)++;
343             }
344             if (res->num_queryTypesSupported)
345                 res->queryTypesSupported =
346                     (Z_QueryTypeDetails **)
347                     odr_malloc (eh->o, res->num_queryTypesSupported
348                                 * sizeof(*res->queryTypesSupported));
349             for (n = c->child; n; n = n->next)
350             {
351                 if (is_numeric_tag(eh, n) != 518)
352                     continue;
353                 res->queryTypesSupported[i++] = f_queryTypeDetails (eh, n);
354             }
355             break;
356         case 503:
357             res->diagnosticsSets =
358                 f_oid_seq(eh, c, &res->num_diagnosticsSets, CLASS_DIAGSET);
359             break;
360         case 505:
361             res->attributeSetIds =
362                 f_oid_seq(eh, c, &res->num_attributeSetIds, CLASS_ATTSET);
363             break;
364         case 507:
365             res->schemas =
366                 f_oid_seq(eh, c, &res->num_schemas, CLASS_SCHEMA);
367             break;
368         case 509:
369             res->recordSyntaxes =
370                 f_oid_seq (eh, c, &res->num_recordSyntaxes, CLASS_RECSYN);
371             break;
372         case 511:
373             res->resourceChallenges =
374                 f_oid_seq (eh, c, &res->num_resourceChallenges, CLASS_RESFORM);
375             break;
376         case 513: res->restrictedAccess = NULL; break; /* fix */
377         case 514: res->costInfo = NULL; break; /* fix */
378         case 515:
379             res->variantSets =
380                 f_oid_seq (eh, c, &res->num_variantSets, CLASS_VARSET);
381             break;
382         case 516:
383             res->elementSetNames =
384                 f_string_seq (eh, c, &res->num_elementSetNames);
385             break;
386         case 517:
387             res->unitSystems = f_string_seq (eh, c, &res->num_unitSystems);
388             break;
389         }
390     }
391     return res;
392 }
393
394 static int *f_recordCount(ExpHandle *eh, data1_node *c, int *which)
395 {
396     int *r= (int *)odr_malloc(eh->o, sizeof(*r));
397     int *wp = which;
398     char intbuf[64];
399
400     c = c->child;
401     if (!is_numeric_tag (eh, c))
402         return 0;
403     if (c->u.tag.element->tag->value.numeric == 210)
404         *wp = Z_DatabaseInfo_actualNumber;
405     else if (c->u.tag.element->tag->value.numeric == 211)
406         *wp = Z_DatabaseInfo_approxNumber;
407     else
408         return 0;
409     if (!c->child || c->child->which != DATA1N_data)
410         return 0;
411     sprintf(intbuf, "%.*s", c->child->u.data.len, c->child->u.data.data);
412     *r = atoi(intbuf);
413     return r;
414 }
415
416 static Z_ContactInfo *f_contactInfo(ExpHandle *eh, data1_node *n)
417 {
418     Z_ContactInfo *res = (Z_ContactInfo *)
419         odr_malloc (eh->o, sizeof(*res));
420     data1_node *c;
421     
422     res->name = 0;
423     res->description = 0;
424     res->address = 0;
425     res->email = 0;
426     res->phone = 0;
427     
428     for (c = n->child; c; c = c->next)
429     {
430         switch (is_numeric_tag (eh, c))
431         {
432         case 102: res->name = f_string (eh, c); break;
433         case 113: res->description = f_humstring (eh, c); break;
434         case 127: res->address = f_humstring (eh, c); break;
435         case 128: res->email = f_string (eh, c); break;
436         case 129: res->phone = f_string (eh, c); break;
437         }
438     }
439     return res;
440 }
441
442 static Z_DatabaseList *f_databaseList(ExpHandle *eh, data1_node *n)
443 {
444     data1_node *c;
445     Z_DatabaseList *res;
446     int i = 0;
447     
448     for (c = n->child; c; c = c->next)
449     {
450         if (!is_numeric_tag (eh, c) != 102) 
451             continue;
452         ++i;
453     }
454     if (!i)
455         return NULL;
456
457     res = (Z_DatabaseList *)odr_malloc (eh->o, sizeof(*res));
458     
459     res->num_databases = i;
460     res->databases = (char **)odr_malloc (eh->o, sizeof(*res->databases) * i);
461     i = 0;
462     for (c = n->child; c; c = c->next)
463     {
464         if (!is_numeric_tag (eh, c) != 102)
465             continue;
466         res->databases[i++] = f_string (eh, c);
467     }
468     return res;
469 }
470
471 static Z_NetworkAddressIA *f_networkAddressIA(ExpHandle *eh, data1_node *n)
472 {
473     Z_NetworkAddressIA *res = (Z_NetworkAddressIA *)
474         odr_malloc (eh->o, sizeof(*res));
475     data1_node *c;
476     
477     res->hostAddress = 0;
478     res->port = 0;
479
480     for (c = n->child; c; c = c->next)
481     {
482         switch (is_numeric_tag (eh, c))
483         {
484         case 121: res->hostAddress = f_string (eh, c); break;
485         case 122: res->port = f_integer (eh, c); break;
486         }
487     }
488     return res;
489 }
490
491 static Z_NetworkAddressOther *f_networkAddressOther(ExpHandle *eh,
492                                                     data1_node *n)
493 {
494     Z_NetworkAddressOther *res = (Z_NetworkAddressOther *)
495         odr_malloc (eh->o, sizeof(*res));
496     data1_node *c;
497
498     res->type = 0;
499     res->address = 0;
500
501     for (c = n->child; c; c = c->next)
502     {
503         switch (is_numeric_tag (eh, c))
504         {
505         case 124: res->type = f_string (eh, c); break;
506         case 121: res->address = f_string (eh, c); break;
507         }
508     }
509     return res;
510 }
511
512 static Z_NetworkAddress **f_networkAddresses(ExpHandle *eh, data1_node *n, 
513                                              int *num)
514 {
515     Z_NetworkAddress **res = NULL;
516     data1_node *c;
517     int i = 0;
518     
519     *num = 0;
520     for (c = n->child; c; c = c->next)
521     {
522         switch (is_numeric_tag (eh, c))
523         {
524         case 120:
525         case 123:
526             (*num)++;
527             break;
528         }
529     }
530
531     if (*num)
532         res = (Z_NetworkAddress **) odr_malloc (eh->o, sizeof(*res) * (*num));
533                                                
534     for (c = n->child; c; c = c->next)
535     {
536         switch (is_numeric_tag (eh, c))
537         {
538         case 120:
539             res[i] = (Z_NetworkAddress *) odr_malloc (eh->o, sizeof(**res));
540             res[i]->which = Z_NetworkAddress_iA;
541             res[i]->u.internetAddress = f_networkAddressIA(eh, c);
542             i++;
543             break;
544         case 123:
545             res[i] = (Z_NetworkAddress *) odr_malloc (eh->o, sizeof(**res));
546             res[i]->which = Z_NetworkAddress_other;
547             res[i]->u.other = f_networkAddressOther(eh, c);
548             i++;
549             break;
550         }
551     }
552     return res;
553 }
554
555 static Z_CategoryInfo *f_categoryInfo(ExpHandle *eh, data1_node *n)
556 {
557     Z_CategoryInfo *res = (Z_CategoryInfo *)odr_malloc(eh->o, sizeof(*res));
558     data1_node *c;
559
560     res->category = 0;
561     res->originalCategory = 0;
562     res->description = 0;
563     res->asn1Module = 0;
564     for (c = n->child; c; c = c->next)
565     {
566         switch (is_numeric_tag (eh, c))
567         {
568         case 102: res->category = f_string(eh, c); break;
569         case 302: res->originalCategory = f_string(eh, c); break;
570         case 113: res->description = f_humstring(eh, c); break;
571         case 303: res->asn1Module = f_string (eh, c); break;
572         }
573     }
574     return res;
575 }
576
577 static Z_CategoryList *f_categoryList(ExpHandle *eh, data1_node *n)
578 {
579     Z_CategoryList *res = (Z_CategoryList *)odr_malloc(eh->o, sizeof(*res));
580     data1_node *c;
581
582     res->commonInfo = 0;
583     res->num_categories = 0;
584     res->categories = NULL;
585
586     for (c = n->child; c; c = c->next)
587     {
588         int i = 0;
589
590         switch (is_numeric_tag (eh, c))
591         {
592         case 600: res->commonInfo = f_commonInfo(eh, c); break;
593         case 300:
594             for (n = c->child; n; n = n->next)
595             {
596                 if (is_numeric_tag(eh, n) != 301)
597                     continue;
598                 (res->num_categories)++;
599             }
600             if (res->num_categories)
601                 res->categories =
602                     (Z_CategoryInfo **)odr_malloc (eh->o, res->num_categories 
603                                                    * sizeof(*res->categories));
604             for (n = c->child; n; n = n->next)
605             {
606                 if (is_numeric_tag(eh, n) != 301)
607                     continue;
608                 res->categories[i++] = f_categoryInfo (eh, n);
609             }
610             break;
611         }
612     }
613     assert (res->num_categories && res->categories);
614     return res;
615 }
616
617 static Z_TargetInfo *f_targetInfo(ExpHandle *eh, data1_node *n)
618 {
619     Z_TargetInfo *res = (Z_TargetInfo *)odr_malloc(eh->o, sizeof(*res));
620     data1_node *c;
621
622     res->commonInfo = 0;
623     res->name = 0;
624     res->recentNews = 0;
625     res->icon = 0;
626     res->namedResultSets = 0;
627     res->multipleDBsearch = 0;
628     res->maxResultSets = 0;
629     res->maxResultSize = 0;
630     res->maxTerms = 0;
631     res->timeoutInterval = 0;
632     res->welcomeMessage = 0;
633     res->contactInfo = 0;
634     res->description = 0;
635     res->num_nicknames = 0;
636     res->nicknames = 0;
637     res->usageRest = 0;
638     res->paymentAddr = 0;
639     res->hours = 0;
640     res->num_dbCombinations = 0;
641     res->dbCombinations = 0;
642     res->num_addresses = 0;
643     res->addresses = 0;
644     res->num_languages = 0;
645     res->languages = NULL;
646     res->commonAccessInfo = 0;
647     
648     for (c = n->child; c; c = c->next)
649     {
650         int i = 0;
651
652         switch (is_numeric_tag (eh, c))
653         {
654         case 600: res->commonInfo = f_commonInfo(eh, c); break;
655         case 102: res->name = f_string(eh, c); break;
656         case 103: res->recentNews = f_humstring(eh, c); break;
657         case 104: res->icon = NULL; break; /* fix */
658         case 105: res->namedResultSets = f_bool(eh, c); break;
659         case 106: res->multipleDBsearch = f_bool(eh, c); break;
660         case 107: res->maxResultSets = f_integer(eh, c); break;
661         case 108: res->maxResultSize = f_integer(eh, c); break;
662         case 109: res->maxTerms = f_integer(eh, c); break;
663         case 110: res->timeoutInterval = f_intunit(eh, c); break;
664         case 111: res->welcomeMessage = f_humstring(eh, c); break;
665         case 112: res->contactInfo = f_contactInfo(eh, c); break;
666         case 113: res->description = f_humstring(eh, c); break;
667         case 114: 
668             res->num_nicknames = 0;
669             for (n = c->child; n; n = n->next)
670             {
671                 if (is_numeric_tag(eh, n) != 102)
672                     continue;
673                 (res->num_nicknames)++;
674             }
675             if (res->num_nicknames)
676                 res->nicknames =
677                     (char **)odr_malloc (eh->o, res->num_nicknames 
678                                 * sizeof(*res->nicknames));
679             for (n = c->child; n; n = n->next)
680             {
681                 if (is_numeric_tag(eh, n) != 102)
682                     continue;
683                 res->nicknames[i++] = f_string (eh, n);
684             }
685             break;
686         case 115: res->usageRest = f_humstring(eh, c); break;
687         case 116: res->paymentAddr = f_humstring(eh, c); break;
688         case 117: res->hours = f_humstring(eh, c); break;
689         case 118:
690             res->num_dbCombinations = 0;
691             for (n = c->child; n; n = n->next)
692             {
693                 if (!is_numeric_tag(eh, n) != 605)
694                     continue;
695                 (res->num_dbCombinations)++;
696             }
697             if (res->num_dbCombinations)
698                 res->dbCombinations =
699                     (Z_DatabaseList **)odr_malloc (eh->o, res->num_dbCombinations
700                                 * sizeof(*res->dbCombinations));
701             for (n = c->child; n; n = n->next)
702             {
703                 if (!is_numeric_tag(eh, n) != 605)
704                     continue;
705                 res->dbCombinations[i++] = f_databaseList (eh, n);
706             }
707             break;
708         case 119: 
709             res->addresses =
710                 f_networkAddresses (eh, c, &res->num_addresses);
711             break;
712         case 125:
713             res->num_languages = 0;
714             for (n = c->child; n; n = n->next)
715             {
716                 if (!is_numeric_tag(eh, n) != 126)
717                     continue;
718                 (res->num_languages)++;
719             }
720             if (res->num_languages)
721                 res->languages = (char **)
722                     odr_malloc (eh->o, res->num_languages *
723                                 sizeof(*res->languages));
724             for (n = c->child; n; n = n->next)
725             {
726                 if (!is_numeric_tag(eh, n) != 126)
727                     continue;
728                 res->languages[i++] = f_string (eh, n);
729             }
730             break;
731         case 500: res->commonAccessInfo = f_accessInfo(eh, c); break;
732         }
733     }
734     if (!res->namedResultSets)
735         res->namedResultSets = eh->false_value;
736     if (!res->multipleDBsearch)
737         res->multipleDBsearch = eh->false_value;
738     return res;
739 }
740
741 static Z_DatabaseInfo *f_databaseInfo(ExpHandle *eh, data1_node *n)
742 {
743     Z_DatabaseInfo *res = (Z_DatabaseInfo *)odr_malloc(eh->o, sizeof(*res));
744     data1_node *c;
745
746     res->commonInfo = 0;
747     res->name = 0;
748     res->explainDatabase = 0;
749     res->num_nicknames = 0;
750     res->nicknames = 0;
751     res->icon = 0;
752     res->userFee = 0;
753     res->available = 0;
754     res->titleString = 0;
755     res->num_keywords = 0;
756     res->keywords = 0;
757     res->description = 0;
758     res->associatedDbs = 0;
759     res->subDbs = 0;
760     res->disclaimers = 0;
761     res->news = 0;
762     res->u.actualNumber = 0;
763     res->defaultOrder = 0;
764     res->avRecordSize = 0;
765     res->maxRecordSize = 0;
766     res->hours = 0;
767     res->bestTime = 0;
768     res->lastUpdate = 0;
769     res->updateInterval = 0;
770     res->coverage = 0;
771     res->proprietary = 0;
772     res->copyrightText = 0;
773     res->copyrightNotice = 0;
774     res->producerContactInfo = 0;
775     res->supplierContactInfo = 0;
776     res->submissionContactInfo = 0;
777     res->accessInfo = 0;
778     
779     for (c = n->child; c; c = c->next)
780     {
781         int i = 0;
782
783         switch (is_numeric_tag (eh, c))
784         {
785         case 600: res->commonInfo = f_commonInfo(eh, c); break;
786         case 102: res->name = f_string(eh, c); break;
787         case 226: res->explainDatabase = odr_nullval(); break;
788         case 114:
789             res->num_nicknames = 0;
790             for (n = c->child; n; n = n->next)
791             {
792                 if (!is_numeric_tag(eh, n) ||
793                     n->u.tag.element->tag->value.numeric != 102)
794                     continue;
795                 (res->num_nicknames)++;
796             }
797             if (res->num_nicknames)
798                 res->nicknames =
799                     (char **)odr_malloc (eh->o, res->num_nicknames 
800                                 * sizeof(*res->nicknames));
801             for (n = c->child; n; n = n->next)
802             {
803                 if (!is_numeric_tag(eh, n) ||
804                     n->u.tag.element->tag->value.numeric != 102)
805                     continue;
806                 res->nicknames[i++] = f_string (eh, n);
807             }
808             break;
809         case 104: res->icon = 0; break;      /* fix */
810         case 201: res->userFee = f_bool(eh, c); break;
811         case 202: res->available = f_bool(eh, c); break;
812         case 203: res->titleString = f_humstring(eh, c); break;
813         case 227:
814             res->num_keywords = 0;
815             for (n = c->child; n; n = n->next)
816             {
817                 if (!is_numeric_tag(eh, n) != 1000)
818                     continue;
819                 (res->num_keywords)++;
820             }
821             if (res->num_keywords)
822                 res->keywords =
823                     (Z_HumanString **)odr_malloc (eh->o, res->num_keywords 
824                                 * sizeof(*res->keywords));
825             for (n = c->child; n; n = n->next)
826             {
827                 if (!is_numeric_tag(eh, n) != 1000)
828                     continue;
829                 res->keywords[i++] = f_humstring (eh, n);
830             }
831             break;
832         case 113: res->description = f_humstring(eh, c); break;
833         case 205:
834             res->associatedDbs = f_databaseList (eh, c);
835             break;
836         case 206:
837             res->subDbs = f_databaseList (eh, c);
838             break;
839         case 207: res->disclaimers = f_humstring(eh, c); break;
840         case 103: res->news = f_humstring(eh, c); break;
841         case 209: res->u.actualNumber =
842                       f_recordCount(eh, c, &res->which); break;
843         case 212: res->defaultOrder = f_humstring(eh, c); break;
844         case 213: res->avRecordSize = f_integer(eh, c); break;
845         case 214: res->maxRecordSize = f_integer(eh, c); break;
846         case 215: res->hours = f_humstring(eh, c); break;
847         case 216: res->bestTime = f_humstring(eh, c); break;
848         case 217: res->lastUpdate = f_string(eh, c); break;
849         case 218: res->updateInterval = f_intunit(eh, c); break;
850         case 219: res->coverage = f_humstring(eh, c); break;
851         case 220: res->proprietary = f_bool(eh, c); break;
852         case 221: res->copyrightText = f_humstring(eh, c); break;
853         case 222: res->copyrightNotice = f_humstring(eh, c); break;
854         case 223: res->producerContactInfo = f_contactInfo(eh, c); break;
855         case 224: res->supplierContactInfo = f_contactInfo(eh, c); break;
856         case 225: res->submissionContactInfo = f_contactInfo(eh, c); break;
857         case 500: res->accessInfo = f_accessInfo(eh, c); break;
858         }
859     }
860     if (!res->userFee)
861         res->userFee = eh->false_value;
862     if (!res->available)
863         res->available = eh->true_value;
864     return res;
865 }
866
867 Z_StringOrNumeric *f_stringOrNumeric (ExpHandle *eh, data1_node *n)
868 {
869     Z_StringOrNumeric *res = (Z_StringOrNumeric *)
870         odr_malloc (eh->o, sizeof(*res));
871     data1_node *c;
872     for (c = n->child; c; c = c->next)
873     {
874         switch (is_numeric_tag (eh, c))
875         {
876         case 1001:
877             res->which = Z_StringOrNumeric_string;
878             res->u.string = f_string (eh, c);
879             break;
880         case 1002:
881             res->which = Z_StringOrNumeric_numeric;
882             res->u.numeric = f_integer (eh, c);
883             break;
884         }
885     }
886     return res;
887 }
888
889 Z_AttributeDescription *f_attributeDescription (
890     ExpHandle *eh, data1_node *n)
891 {
892     Z_AttributeDescription *res = (Z_AttributeDescription *)
893         odr_malloc(eh->o, sizeof(*res));
894     data1_node *c;
895     int i = 0;
896         
897     res->name = 0;
898     res->description = 0;
899     res->attributeValue = 0;
900     res->num_equivalentAttributes = 0;
901     res->equivalentAttributes = 0;
902
903     for (c = n->child; c; c = c->next)
904     {
905         switch (is_numeric_tag (eh, c))
906         {
907         case 102: res->name = f_string (eh, c); break;
908         case 113: res->description = f_humstring (eh, c); break;
909         case 710: res->attributeValue = f_stringOrNumeric (eh, c); break;
910         case 752: (res->num_equivalentAttributes++); break;
911         }
912     }
913     if (res->num_equivalentAttributes)
914         res->equivalentAttributes = (Z_StringOrNumeric **)
915             odr_malloc (eh->o, sizeof(*res->equivalentAttributes) *
916                         res->num_equivalentAttributes);
917     for (c = n->child; c; c = c->next)
918         if (is_numeric_tag (eh, c) == 752)
919             res->equivalentAttributes[i++] = f_stringOrNumeric (eh, c);
920     return res;
921 }
922
923 Z_AttributeType *f_attributeType (ExpHandle *eh, data1_node *n)
924 {
925     Z_AttributeType *res = (Z_AttributeType *)
926         odr_malloc(eh->o, sizeof(*res));
927     data1_node *c;
928
929     res->name = 0;
930     res->description = 0;
931     res->attributeType = 0;
932     res->num_attributeValues = 0;
933     res->attributeValues = 0;
934
935     for (c = n->child; c; c = c->next)
936     {
937         int i = 0;
938         switch (is_numeric_tag (eh, c))
939         {
940         case 102: res->name = f_string (eh, c); break;
941         case 113: res->description = f_humstring (eh, c); break;
942         case 704: res->attributeType = f_integer (eh, c); break;
943         case 708:
944             for (n = c->child; n; n = n->next)
945             {
946                 if (is_numeric_tag(eh, n) != 709)
947                     continue;
948                 (res->num_attributeValues)++;
949             }
950             if (res->num_attributeValues)
951                 res->attributeValues = (Z_AttributeDescription **)
952                     odr_malloc (eh->o, res->num_attributeValues
953                                 * sizeof(*res->attributeValues));
954             for (n = c->child; n; n = n->next)
955             {
956                 if (is_numeric_tag(eh, n) != 709)
957                     continue;
958                 res->attributeValues[i++] = f_attributeDescription (eh, n);
959             }
960             break;
961         }
962     }
963     return res;
964 }
965
966 Z_AttributeSetInfo *f_attributeSetInfo (ExpHandle *eh, data1_node *n)
967 {
968     Z_AttributeSetInfo *res = (Z_AttributeSetInfo *)
969         odr_malloc(eh->o, sizeof(*res));
970     data1_node *c;
971
972     res->commonInfo = 0;
973     res->attributeSet = 0;
974     res->name = 0;
975     res->num_attributes = 0;
976     res->attributes = 0;
977     res->description = 0;
978     for (c = n->child; c; c = c->next)
979     {
980         int i = 0;
981         switch (is_numeric_tag (eh, c))
982         {
983         case 600: res->commonInfo = f_commonInfo (eh, c); break;
984         case 1000: res->attributeSet = f_oid (eh, c, CLASS_ATTSET); break;
985         case 102: res->name = f_string (eh, c); break;
986         case 750:
987             for (n = c->child; n; n = n->next)
988             {
989                 if (is_numeric_tag(eh, n) != 751)
990                     continue;
991                 (res->num_attributes)++;
992             }
993             if (res->num_attributes)
994                 res->attributes = (Z_AttributeType **)
995                     odr_malloc (eh->o, res->num_attributes
996                                 * sizeof(*res->attributes));
997             for (n = c->child; n; n = n->next)
998             {
999                 if (is_numeric_tag(eh, n) != 751)
1000                     continue;
1001                 res->attributes[i++] = f_attributeType (eh, n);
1002             }
1003             break;
1004         case 113: res->description = f_humstring (eh, c); break;
1005         }
1006     }
1007     return res;
1008 }
1009
1010 Z_OmittedAttributeInterpretation *f_omittedAttributeInterpretation (
1011     ExpHandle *eh, data1_node *n)
1012 {
1013     Z_OmittedAttributeInterpretation *res = (Z_OmittedAttributeInterpretation*)
1014         odr_malloc (eh->o, sizeof(*res));
1015     data1_node *c;
1016
1017     res->defaultValue = 0;
1018     res->defaultDescription = 0;
1019     for (c = n->child; c; c = c->next)
1020     {
1021         switch (is_numeric_tag (eh, c))
1022         {
1023         case 706:
1024             res->defaultValue = f_stringOrNumeric (eh, c);
1025             break;
1026         case 113:
1027             res->defaultDescription = f_humstring(eh, c);
1028             break;
1029         }
1030     }
1031     return res;
1032 }
1033
1034 Z_AttributeValue *f_attributeValue (ExpHandle *eh, data1_node *n)
1035 {
1036     Z_AttributeValue *res = (Z_AttributeValue *)
1037         odr_malloc (eh->o, sizeof(*res));
1038     data1_node *c;
1039
1040     res->value = 0;
1041     res->description = 0;
1042     res->num_subAttributes = 0;
1043     res->subAttributes = 0;
1044     res->num_superAttributes = 0;
1045     res->superAttributes = 0;
1046     res->partialSupport = 0;
1047     for (c = n->child; c; c = c->next)
1048     {
1049         int i = 0;
1050         switch (is_numeric_tag (eh, c))
1051         {
1052         case 710:
1053             res->value = f_stringOrNumeric (eh, c);  break;
1054         case 113:
1055             res->description = f_humstring (eh, c); break;
1056         case 712:
1057             for (n = c->child; n; n = n->next)
1058             {
1059                 if (is_numeric_tag(eh, n) != 713)
1060                     continue;
1061                 (res->num_subAttributes)++;
1062             }
1063             if (res->num_subAttributes)
1064                 res->subAttributes =
1065                     (Z_StringOrNumeric **)
1066                     odr_malloc (eh->o, res->num_subAttributes
1067                                 * sizeof(*res->subAttributes));
1068             for (n = c->child; n; n = n->next)
1069             {
1070                 if (is_numeric_tag(eh, n) != 713)
1071                     continue;
1072                 res->subAttributes[i++] = f_stringOrNumeric (eh, n);
1073             }
1074             break;
1075         case 714:
1076             for (n = c->child; n; n = n->next)
1077             {
1078                 if (is_numeric_tag(eh, n) != 715)
1079                     continue;
1080                 (res->num_superAttributes)++;
1081             }
1082             if (res->num_superAttributes)
1083                 res->superAttributes =
1084                     (Z_StringOrNumeric **)
1085                     odr_malloc (eh->o, res->num_superAttributes
1086                                 * sizeof(*res->superAttributes));
1087             for (n = c->child; n; n = n->next)
1088             {
1089                 if (is_numeric_tag(eh, n) != 715)
1090                     continue;
1091                 res->superAttributes[i++] = f_stringOrNumeric (eh, n);
1092             }
1093             break;
1094         case 711:
1095             res->partialSupport = odr_nullval ();
1096             break;
1097         }
1098     }
1099     return res;
1100 }
1101
1102 Z_AttributeTypeDetails *f_attributeTypeDetails (ExpHandle *eh, data1_node *n)
1103 {
1104     Z_AttributeTypeDetails *res = (Z_AttributeTypeDetails *)
1105         odr_malloc(eh->o, sizeof(*res));
1106     data1_node *c;
1107     res->attributeType = 0;
1108     res->defaultIfOmitted = 0;
1109     res->num_attributeValues = 0;
1110     res->attributeValues = 0;
1111     for (c = n->child; c; c = c->next)
1112     {
1113         int i = 0;
1114         switch (is_numeric_tag (eh, c))
1115         {
1116         case 704: res->attributeType = f_integer (eh, c); break;
1117         case 705:
1118             res->defaultIfOmitted = f_omittedAttributeInterpretation (eh, c);
1119             break;
1120         case 708:
1121             for (n = c->child; n; n = n->next)
1122             {
1123                 if (is_numeric_tag(eh, n) != 709)
1124                     continue;
1125                 (res->num_attributeValues)++;
1126             }
1127             if (res->num_attributeValues)
1128                 res->attributeValues =
1129                     (Z_AttributeValue **)
1130                     odr_malloc (eh->o, res->num_attributeValues
1131                                 * sizeof(*res->attributeValues));
1132             for (n = c->child; n; n = n->next)
1133             {
1134                 if (is_numeric_tag(eh, n) != 709)
1135                     continue;
1136                 res->attributeValues[i++] = f_attributeValue (eh, n);
1137             }
1138             break;
1139         }
1140     }
1141     return res;
1142 }
1143
1144 Z_AttributeSetDetails *f_attributeSetDetails (ExpHandle *eh, data1_node *n)
1145 {
1146     Z_AttributeSetDetails *res = (Z_AttributeSetDetails *)
1147         odr_malloc(eh->o, sizeof(*res));
1148     data1_node *c;
1149     
1150     res->attributeSet = 0;
1151     res->num_attributesByType = 0;
1152     res->attributesByType = 0;
1153     for (c = n->child; c; c = c->next)
1154     {
1155         int i = 0;
1156         switch (is_numeric_tag (eh, c))
1157         {
1158         case 1000: res->attributeSet = f_oid(eh, c, CLASS_ATTSET); break;
1159         case 702:
1160             for (n = c->child; n; n = n->next)
1161             {
1162                 if (is_numeric_tag(eh, n) != 703)
1163                     continue;
1164                 (res->num_attributesByType)++;
1165             }
1166             if (res->num_attributesByType)
1167                 res->attributesByType =
1168                     (Z_AttributeTypeDetails **)
1169                     odr_malloc (eh->o, res->num_attributesByType
1170                                 * sizeof(*res->attributesByType));
1171             for (n = c->child; n; n = n->next)
1172             {
1173                 if (is_numeric_tag(eh, n) != 703)
1174                     continue;
1175                 res->attributesByType[i++] = f_attributeTypeDetails (eh, n);
1176             }
1177             break;
1178         }
1179     }
1180     return res;
1181 }
1182
1183 Z_AttributeValueList *f_attributeValueList (ExpHandle *eh, data1_node *n)
1184 {
1185     Z_AttributeValueList *res = (Z_AttributeValueList *)
1186         odr_malloc (eh->o, sizeof(*res));
1187     data1_node *c;
1188     int i = 0;
1189
1190     res->num_attributes = 0;
1191     res->attributes = 0;
1192     for (c = n->child; c; c = c->next)
1193         if (is_numeric_tag (eh, c) == 710)
1194             (res->num_attributes)++;
1195     if (res->num_attributes)
1196     {
1197         res->attributes = (Z_StringOrNumeric **)
1198             odr_malloc (eh->o, res->num_attributes * sizeof(*res->attributes));
1199     }
1200     for (c = n->child; c; c = c->next)
1201         if (is_numeric_tag(eh, c) == 710)
1202             res->attributes[i++] = f_stringOrNumeric (eh, c);
1203     return res;
1204 }
1205
1206 Z_AttributeOccurrence *f_attributeOccurrence (ExpHandle *eh, data1_node *n)
1207 {
1208     Z_AttributeOccurrence *res = (Z_AttributeOccurrence *)
1209         odr_malloc (eh->o, sizeof(*res));
1210     data1_node *c;
1211
1212     res->attributeSet = 0;
1213     res->attributeType = 0;
1214     res->mustBeSupplied = 0;
1215     res->which = Z_AttributeOcc_any_or_none;
1216     res->attributeValues.any_or_none = odr_nullval ();
1217
1218     for (c = n->child; c; c = c->next)
1219     {
1220         switch (is_numeric_tag (eh, c))
1221         {
1222         case 1000:
1223             res->attributeSet = f_oid (eh, c, CLASS_ATTSET); break;
1224         case 704:
1225             res->attributeType = f_integer (eh, c); break;
1226         case 720:
1227             res->mustBeSupplied = odr_nullval (); break;
1228         case 721:
1229             res->which = Z_AttributeOcc_any_or_none;
1230             res->attributeValues.any_or_none = odr_nullval ();
1231             break;
1232         case 722:
1233             res->which = Z_AttributeOcc_specific;
1234             res->attributeValues.specific = f_attributeValueList (eh, c);
1235             break;
1236         }
1237     }
1238     return res;
1239 }
1240
1241 Z_AttributeCombination *f_attributeCombination (ExpHandle *eh, data1_node *n)
1242 {
1243     Z_AttributeCombination *res = (Z_AttributeCombination *)
1244         odr_malloc (eh->o, sizeof(*res));
1245     data1_node *c;
1246     int i = 0;
1247
1248     res->num_occurrences = 0;
1249     res->occurrences = 0;
1250     for (c = n->child; c; c = c->next)
1251         if (is_numeric_tag (eh, c) == 719)
1252             (res->num_occurrences)++;
1253     if (res->num_occurrences)
1254     {
1255         res->occurrences = (Z_AttributeOccurrence **)
1256             odr_malloc (eh->o, res->num_occurrences * sizeof(*res->occurrences));
1257     }
1258     for (c = n->child; c; c = c->next)
1259         if (is_numeric_tag(eh, c) == 719)
1260             res->occurrences[i++] = f_attributeOccurrence (eh, c);
1261     assert (res->num_occurrences);
1262     return res;
1263 }
1264
1265 Z_AttributeCombinations *f_attributeCombinations (ExpHandle *eh, data1_node *n)
1266 {
1267     Z_AttributeCombinations *res = (Z_AttributeCombinations *)
1268         odr_malloc (eh->o, sizeof(*res));
1269     data1_node *c;
1270     res->defaultAttributeSet = 0;
1271     res->num_legalCombinations = 0;
1272     res->legalCombinations = 0;
1273
1274     for (c = n->child; c; c = c->next)
1275     {
1276         int i = 0;
1277         switch (is_numeric_tag (eh, c))
1278         {
1279         case 1000:
1280             res->defaultAttributeSet = f_oid (eh, c, CLASS_ATTSET);
1281             break;
1282         case 717:
1283             for (n = c->child; n; n = n->next)
1284             {
1285                 if (is_numeric_tag(eh, n) != 718)
1286                     continue;
1287                 (res->num_legalCombinations)++;
1288             }
1289             if (res->num_legalCombinations)
1290                 res->legalCombinations =
1291                     (Z_AttributeCombination **)
1292                     odr_malloc (eh->o, res->num_legalCombinations
1293                                 * sizeof(*res->legalCombinations));
1294             for (n = c->child; n; n = n->next)
1295             {
1296                 if (is_numeric_tag(eh, n) != 718)
1297                     continue;
1298                 res->legalCombinations[i++] = f_attributeCombination (eh, n);
1299             }
1300             break;
1301         }
1302     }
1303     assert (res->num_legalCombinations);
1304     return res;
1305 }
1306
1307 Z_AttributeDetails *f_attributeDetails (ExpHandle *eh, data1_node *n)
1308 {
1309     Z_AttributeDetails *res = (Z_AttributeDetails *)
1310         odr_malloc(eh->o, sizeof(*res));
1311     data1_node *c;
1312
1313     res->commonInfo = 0;
1314     res->databaseName = 0;
1315     res->num_attributesBySet = 0;
1316     res->attributesBySet = NULL;
1317     res->attributeCombinations = NULL;
1318
1319     for (c = n->child; c; c = c->next)
1320     {
1321         int i = 0;
1322         switch (is_numeric_tag (eh, c))
1323         {
1324         case 600: res->commonInfo = f_commonInfo(eh, c); break;
1325         case 102: res->databaseName = f_string (eh, c); break;
1326         case 700:
1327             for (n = c->child; n; n = n->next)
1328             {
1329                 if (is_numeric_tag(eh, n) != 701)
1330                     continue;
1331                 (res->num_attributesBySet)++;
1332             }
1333             if (res->num_attributesBySet)
1334                 res->attributesBySet =
1335                     (Z_AttributeSetDetails **)
1336                     odr_malloc (eh->o, res->num_attributesBySet
1337                                 * sizeof(*res->attributesBySet));
1338             for (n = c->child; n; n = n->next)
1339             {
1340                 if (is_numeric_tag(eh, n) != 701)
1341                     continue;
1342                 res->attributesBySet[i++] = f_attributeSetDetails (eh, n);
1343             }
1344             break;
1345         case 716:
1346             res->attributeCombinations = f_attributeCombinations (eh, c);
1347             break;
1348         }
1349     }
1350     return res;
1351 }
1352
1353 Z_ExplainRecord *data1_nodetoexplain (data1_handle dh, data1_node *n,
1354                                       int select, ODR o)
1355 {
1356     ExpHandle eh;
1357     Z_ExplainRecord *res = (Z_ExplainRecord *)odr_malloc(o, sizeof(*res));
1358
1359     eh.dh = dh;
1360     eh.select = select;
1361     eh.o = o;
1362     eh.false_value = (int *)odr_malloc(eh.o, sizeof(eh.false_value));
1363     *eh.false_value = 0;
1364     eh.true_value = (int *)odr_malloc(eh.o, sizeof(eh.true_value));
1365     *eh.true_value = 1;
1366
1367     assert(n->which == DATA1N_root);
1368     if (strcmp(n->u.root.type, "explain"))
1369     {
1370         yaz_log(YLOG_WARN, "Attempt to convert a non-Explain record");
1371         return 0;
1372     }
1373     for (n = n->child; n; n = n->next)
1374     {
1375         switch (is_numeric_tag (&eh, n))
1376         {
1377         case 1:
1378             res->which = Z_Explain_categoryList;
1379             if (!(res->u.categoryList = f_categoryList(&eh, n)))
1380                 return 0;
1381             return res;     
1382         case 2:
1383             res->which = Z_Explain_targetInfo;
1384             if (!(res->u.targetInfo = f_targetInfo(&eh, n)))
1385                 return 0;
1386             return res;
1387         case 3:
1388             res->which = Z_Explain_databaseInfo;
1389             if (!(res->u.databaseInfo = f_databaseInfo(&eh, n)))
1390                 return 0;
1391             return res;
1392         case 7:
1393             res->which = Z_Explain_attributeSetInfo;
1394             if (!(res->u.attributeSetInfo = f_attributeSetInfo(&eh, n)))
1395                 return 0;
1396             return res;     
1397         case 10:
1398             res->which = Z_Explain_attributeDetails;
1399             if (!(res->u.attributeDetails = f_attributeDetails(&eh, n)))
1400                 return 0;
1401             return res;
1402         }
1403     }
1404     yaz_log(YLOG_WARN, "No category in Explain record");
1405     return 0;
1406 }
1407 /*
1408  * Local variables:
1409  * c-basic-offset: 4
1410  * indent-tabs-mode: nil
1411  * End:
1412  * vim: shiftwidth=4 tabstop=8 expandtab
1413  */
1414