Development towards compiled ASN.1.
[yaz-moved-to-github.git] / retrieval / d1_expout.c
1 /*
2  * Copyright (c) 1995-1998, Index Data.
3  * See the file LICENSE for details.
4  * Sebastian Hammer, Adam Dickmeiss
5  *
6  * $Log: d1_expout.c,v $
7  * Revision 1.10  1998-03-31 15:13:20  adam
8  * Development towards compiled ASN.1.
9  *
10  * Revision 1.9  1998/03/05 08:07:58  adam
11  * Make data1 to EXPLAIN ignore local tags in root.
12  *
13  * Revision 1.8  1998/02/11 11:53:35  adam
14  * Changed code so that it compiles as C++.
15  *
16  * Revision 1.7  1997/12/09 16:18:16  adam
17  * Work on EXPLAIN schema. First implementation of sub-schema facility
18  * in the *.abs files.
19  *
20  * Revision 1.6  1997/11/24 11:33:56  adam
21  * Using function odr_nullval() instead of global ODR_NULLVAL when
22  * appropriate.
23  *
24  * Revision 1.5  1997/11/19 10:30:06  adam
25  * More explain work.
26  *
27  * Revision 1.4  1997/11/18 09:51:08  adam
28  * Removed element num_children from data1_node. Minor changes in
29  * data1 to Explain.
30  *
31  * Revision 1.3  1997/09/17 12:10:36  adam
32  * YAZ version 1.4.
33  *
34  * Revision 1.2  1995/12/14 16:28:30  quinn
35  * More explain stuff.
36  *
37  * Revision 1.1  1995/12/14  11:09:51  quinn
38  * Work on Explain
39  *
40  *
41  */
42
43 #include <assert.h>
44 #include <string.h>
45 #include <stdlib.h>
46
47 #include <log.h>
48 #include <proto.h>
49 #include <data1.h>
50
51 typedef struct {
52     data1_handle dh;
53     ODR o;
54     int select;
55
56     bool_t *false_value;
57     bool_t *true_value;
58 } ExpHandle;
59
60 static int is_numeric_tag (ExpHandle *eh, data1_node *c)
61 {
62     if (!c || c->which != DATA1N_tag)
63         return 0;
64     if (!c->u.tag.element)
65     {
66         logf(LOG_WARN, "Tag %s is local", c->u.tag.tag);
67         return 0;
68     }
69     if (c->u.tag.element->tag->which != DATA1T_numeric)
70     {
71         logf(LOG_WARN, "Tag %s is not numeric", c->u.tag.tag);
72         return 0;
73     }
74     if (eh->select && !c->u.tag.node_selected)
75         return 0;
76     return c->u.tag.element->tag->value.numeric;
77 }
78
79 static int is_data_tag (ExpHandle *eh, data1_node *c)
80 {
81     if (!c || c->which != DATA1N_data)
82         return 0;
83     if (eh->select && !c->u.tag.node_selected)
84         return 0;
85     return 1;
86 }
87
88 static int *f_integer(ExpHandle *eh, data1_node *c)
89 {
90     int *r;
91     char intbuf[64];
92
93     c = c->child;
94     if (!is_data_tag (eh, c) || c->u.data.len > 63)
95         return 0;
96     r = (int *)odr_malloc(eh->o, sizeof(*r));
97     sprintf(intbuf, "%.*s", 63, c->u.data.data);
98     *r = atoi(intbuf);
99     return r;
100 }
101
102 static char *f_string(ExpHandle *eh, data1_node *c)
103 {
104     char *r;
105
106     c = c->child;
107     if (!is_data_tag (eh, c))
108         return 0;
109     r = (char *)odr_malloc(eh->o, c->u.data.len+1);
110     memcpy(r, c->u.data.data, c->u.data.len);
111     r[c->u.data.len] = '\0';
112     return r;
113 }
114
115 static bool_t *f_bool(ExpHandle *eh, data1_node *c)
116 {
117     bool_t *tf;
118     char intbuf[64];
119
120     c = c->child;
121     if (!is_data_tag (eh, c) || c->u.data.len > 63)
122         return 0;
123     tf = (int *)odr_malloc (eh->o, sizeof(*tf));
124     sprintf(intbuf, "%.*s", c->u.data.len, c->u.data.data);
125     *tf = atoi(intbuf);
126     return tf;
127 }
128
129 static Odr_oid *f_oid(ExpHandle *eh, data1_node *c, oid_class oclass)
130 {
131     char oidstr[64];
132     int oid_this[20];
133     oid_value value_for_this;
134
135     c = c->child;
136     if (!is_data_tag (eh, c) || c->u.data.len > 63)
137         return 0;
138     sprintf(oidstr, "%.*s", c->u.data.len, c->u.data.data);
139     value_for_this = oid_getvalbyname(oidstr);
140     if (value_for_this == VAL_NONE)
141         return NULL; /* fix */
142     else
143     {
144         struct oident ident;
145
146         ident.oclass = oclass;
147         ident.proto = PROTO_Z3950;
148         ident.value = value_for_this;
149         
150         oid_ent_to_oid (&ident, oid_this);
151     }
152     return odr_oiddup (eh->o, oid_this);
153 }
154
155 static Z_IntUnit *f_intunit(ExpHandle *eh, data1_node *c)
156 {
157     /* fix */
158     return 0;
159 }
160
161 static Z_HumanString *f_humstring(ExpHandle *eh, data1_node *c)
162 {
163     Z_HumanString *r;
164     Z_HumanStringUnit *u;
165
166     c = c->child;
167     if (!is_data_tag (eh, c))
168         return 0;
169     r = (Z_HumanString *)odr_malloc(eh->o, sizeof(*r));
170     r->num_strings = 1;
171     r->strings = (Z_HumanStringUnit **)odr_malloc(eh->o, sizeof(Z_HumanStringUnit*));
172     r->strings[0] = u = (Z_HumanStringUnit *)odr_malloc(eh->o, sizeof(*u));
173     u->language = 0;
174     u->text = (char *)odr_malloc(eh->o, c->u.data.len+1);
175     memcpy(u->text, c->u.data.data, c->u.data.len);
176     u->text[c->u.data.len] = '\0';
177     return r;
178 }
179
180 static Z_CommonInfo *f_commonInfo(ExpHandle *eh, data1_node *n)
181 {
182     Z_CommonInfo *res = (Z_CommonInfo *)odr_malloc(eh->o, sizeof(*res));
183     data1_node *c;
184
185     res->dateAdded = 0;
186     res->dateChanged = 0;
187     res->expiry = 0;
188     res->humanStringLanguage = 0;
189     res->otherInfo = 0;
190
191     for (c = n->child; c; c = c->next)
192     {
193         switch (is_numeric_tag (eh, c))
194         {
195             case 601: res->dateAdded = f_string(eh, c); break;
196             case 602: res->dateChanged = f_string(eh, c); break;
197             case 603: res->expiry = f_string(eh, c); break;
198             case 604: res->humanStringLanguage = f_string(eh, c); break;
199         }
200     }
201     return res;
202 }
203
204 Z_QueryTypeDetails *f_queryTypeDetails (ExpHandle *eh, data1_node *n)
205 {
206     /* fix */
207     return NULL;
208 }
209
210 Odr_oid **f_oid_seq (ExpHandle *eh, data1_node *n, int *num, oid_class oclass)
211 {
212     Odr_oid **res;
213     data1_node *c;
214     int i;
215
216     *num = 0;
217     for (c = n->child ; c; c = c->next)
218     {
219         if (is_numeric_tag (eh, c) != 1000)
220             continue;
221         ++(*num);
222     }
223     if (!*num)
224         return NULL;
225     res = (int **)odr_malloc (eh->o, sizeof(*res) * (*num));
226     for (c = n->child, i = 0 ; c; c = c->next)
227     {
228         if (is_numeric_tag (eh, c) != 1000)
229             continue;
230         res[i++] = f_oid (eh, c, oclass);
231     }
232     return res;
233 }
234     
235 char **f_string_seq (ExpHandle *eh, data1_node *n, int *num)
236 {
237     char **res;
238     data1_node *c;
239     int i;
240
241     *num = 0;
242     for (c = n->child ; c; c = c->next)
243     {
244         if (!is_numeric_tag (eh, c) != 1001)
245             continue;
246         ++(*num);
247     }
248     if (!*num)
249         return NULL;
250     res = (char **)odr_malloc (eh->o, sizeof(*res) * (*num));
251     for (c = n->child, i = 0 ; c; c = c->next)
252     {
253         if (!is_numeric_tag (eh, c) != 1001)
254             continue;
255         res[i++] = f_string (eh, c);
256     }
257     return res;
258 }
259
260 char **f_humstring_seq (ExpHandle *eh, data1_node *n, int *num)
261 {
262     /* fix */
263     return NULL;
264 }
265
266
267 Z_RpnCapabilities *f_rpnCapabilities (ExpHandle *eh, data1_node *c)
268 {
269     Z_RpnCapabilities *res = (Z_RpnCapabilities *)odr_malloc (eh->o, sizeof(*res));
270
271     res->num_operators = 0;
272     res->operators = NULL;
273     res->resultSetAsOperandSupported = eh->false_value;
274     res->restrictionOperandSupported = eh->false_value;
275     res->proximity = NULL;
276     /* fix */ /* 550 - 560 */
277     return res;
278 }
279
280 Z_QueryTypeDetails **f_queryTypesSupported (ExpHandle *eh, data1_node *c,
281                                             int *num)
282 {
283     data1_node *n;
284     Z_QueryTypeDetails **res;
285     int i;
286
287     *num = 0;
288     for (n = c->child; n; n = n->next)
289     {
290         if (is_numeric_tag(eh, n) != 519)
291             continue;
292         /* fix */ /* 518 and 520 */
293         (*num)++;
294     }
295     if (!*num)
296         return NULL;
297     res = (Z_QueryTypeDetails **)odr_malloc (eh->o, *num * sizeof(*res));
298     i = 0;
299     for (n = c->child; n; n = n->next)
300     {
301         if (is_numeric_tag(eh, n) == 519)
302         {
303             res[i] = (Z_QueryTypeDetails *)odr_malloc (eh->o, sizeof(**res));
304             res[i]->which = Z_QueryTypeDetails_rpn;
305             res[i]->u.rpn = f_rpnCapabilities (eh, n);
306             i++;
307         }
308         else
309             continue;
310         /* fix */ /* 518 and 520 */
311     }
312     return res;
313 }
314
315 static Z_AccessInfo *f_accessInfo(ExpHandle *eh, data1_node *n)
316 {
317     Z_AccessInfo *res = (Z_AccessInfo *)odr_malloc(eh->o, sizeof(*res));
318     data1_node *c;
319
320     res->num_queryTypesSupported = 0;
321     res->queryTypesSupported = 0;
322     res->num_diagnosticsSets = 0;
323     res->diagnosticsSets = 0;
324     res->num_attributeSetIds = 0;
325     res->attributeSetIds = 0;
326     res->num_schemas = 0;
327     res->schemas = 0;
328     res->num_recordSyntaxes = 0;
329     res->recordSyntaxes = 0;
330     res->num_resourceChallenges = 0;
331     res->resourceChallenges = 0;
332     res->restrictedAccess = 0;
333     res->costInfo = 0;
334     res->num_variantSets = 0;
335     res->variantSets = 0;
336     res->num_elementSetNames = 0;
337     res->elementSetNames = 0;
338     res->num_unitSystems = 0;
339     res->unitSystems = 0;
340
341     for (c = n->child; c; c = c->next)
342     {
343         switch (is_numeric_tag (eh, c))
344         {
345         case 501:
346             res->queryTypesSupported =
347                 f_queryTypesSupported (eh, c, &res->num_queryTypesSupported);
348             break;
349         case 503:
350             res->diagnosticsSets =
351                 f_oid_seq(eh, c, &res->num_diagnosticsSets, CLASS_DIAGSET);
352             break;
353         case 505:
354             res->attributeSetIds =
355                 f_oid_seq(eh, c, &res->num_attributeSetIds, CLASS_ATTSET);
356             break;
357         case 507:
358             res->schemas =
359                 f_oid_seq(eh, c, &res->num_schemas, CLASS_SCHEMA);
360             break;
361         case 509:
362             res->recordSyntaxes =
363                 f_oid_seq (eh, c, &res->num_recordSyntaxes, CLASS_RECSYN);
364             break;
365         case 511:
366             res->resourceChallenges =
367                 f_oid_seq (eh, c, &res->num_resourceChallenges, CLASS_RESFORM);
368             break;
369         case 513: res->restrictedAccess = NULL; break; /* fix */
370         case 514: res->costInfo = NULL; break; /* fix */
371         case 515:
372             res->variantSets =
373                 f_oid_seq (eh, c, &res->num_variantSets, CLASS_VARSET);
374             break;
375         case 516:
376             res->elementSetNames =
377                 f_string_seq (eh, c, &res->num_elementSetNames);
378             break;
379         case 517:
380             res->unitSystems = f_string_seq (eh, c, &res->num_unitSystems);
381             break;
382         }
383     }
384     return res;
385 }
386
387 static int *f_recordCount(ExpHandle *eh, data1_node *c, int *which)
388 {
389     int *r= (int *)odr_malloc(eh->o, sizeof(*r));
390     int *wp = which;
391     char intbuf[64];
392
393     c = c->child;
394     if (!is_numeric_tag (eh, c))
395         return 0;
396 #ifdef ASN_COMPILED
397     if (c->u.tag.element->tag->value.numeric == 210)
398         *wp = Z_DatabaseInfo_actualNumber;
399     else if (c->u.tag.element->tag->value.numeric == 211)
400         *wp = Z_DatabaseInfo_approxNumber;
401     else
402         return 0;
403 #else
404     if (c->u.tag.element->tag->value.numeric == 210)
405         *wp = Z_Exp_RecordCount_actualNumber;
406     else if (c->u.tag.element->tag->value.numeric == 211)
407         *wp = Z_Exp_RecordCount_approxNumber;
408     else
409         return 0;
410 #endif
411     if (!c->child || c->child->which != DATA1N_data)
412         return 0;
413     sprintf(intbuf, "%.*s", 63, c->child->u.data.data);
414     *r = atoi(intbuf);
415     return r;
416 }
417
418 static Z_ContactInfo *f_contactInfo(ExpHandle *eh, data1_node *n)
419 {
420     /* fix */
421     return 0;
422 }
423
424 static Z_DatabaseList *f_databaseList(ExpHandle *eh, data1_node *n)
425 {
426     data1_node *c;
427     Z_DatabaseList *res;
428     int i = 0;
429     
430     for (c = n->child; c; c = c->next)
431     {
432         if (!is_numeric_tag (eh, c) != 102) 
433             continue;
434         ++i;
435     }
436     if (!i)
437         return NULL;
438
439     res = (Z_DatabaseList *)odr_malloc (eh->o, sizeof(*res));
440     
441     res->num_databases = i;
442     res->databases = (char **)odr_malloc (eh->o, sizeof(*res->databases) * i);
443     i = 0;
444     for (c = n->child; c; c = c->next)
445     {
446         if (!is_numeric_tag (eh, c) != 102)
447             continue;
448         res->databases[i++] = f_string (eh, c);
449     }
450     return res;
451 }
452
453 static Z_TargetInfo *f_targetInfo(ExpHandle *eh, data1_node *n)
454 {
455     Z_TargetInfo *res = (Z_TargetInfo *)odr_malloc(eh->o, sizeof(*res));
456     data1_node *c;
457
458     res->commonInfo = 0;
459     res->name = 0;
460     res->recentNews = 0;
461     res->icon = 0;
462     res->namedResultSets = 0;
463     res->multipleDbSearch = 0;
464     res->maxResultSets = 0;
465     res->maxResultSize = 0;
466     res->maxTerms = 0;
467     res->timeoutInterval = 0;
468     res->welcomeMessage = 0;
469     res->contactInfo = 0;
470     res->description = 0;
471     res->num_nicknames = 0;
472     res->nicknames = 0;
473     res->usageRest = 0;
474     res->paymentAddr = 0;
475     res->hours = 0;
476     res->num_dbCombinations = 0;
477     res->dbCombinations = 0;
478     res->num_addresses = 0;
479     res->addresses = 0;
480     res->commonAccessInfo = 0;
481     
482     for (c = n->child; c; c = c->next)
483     {
484         int i = 0;
485
486         if (!is_numeric_tag (eh, c))
487             continue;
488         switch (c->u.tag.element->tag->value.numeric)
489         {
490         case 600: res->commonInfo = f_commonInfo(eh, c); break;
491         case 102: res->name = f_string(eh, c); break;
492         case 103: res->recentNews = f_humstring(eh, c); break;
493         case 104: res->icon = NULL; break; /* fix */
494         case 105: res->namedResultSets = f_bool(eh, c); break;
495         case 106: res->multipleDbSearch = f_bool(eh, c); break;
496         case 107: res->maxResultSets = f_integer(eh, c); break;
497         case 108: res->maxResultSize = f_integer(eh, c); break;
498         case 109: res->maxTerms = f_integer(eh, c); break;
499         case 110: res->timeoutInterval = f_intunit(eh, c); break;
500         case 111: res->welcomeMessage = f_humstring(eh, c); break;
501         case 112: res->contactInfo = f_contactInfo(eh, c); break;
502         case 113: res->description = f_humstring(eh, c); break;
503         case 114: 
504             res->num_nicknames = 0;
505             for (n = c->child; n; n = n->next)
506             {
507                 if (is_numeric_tag(eh, n) != 102)
508                     continue;
509                 (res->num_nicknames)++;
510             }
511             if (res->num_nicknames)
512                 res->nicknames =
513                     (char **)odr_malloc (eh->o, res->num_nicknames 
514                                 * sizeof(*res->nicknames));
515             for (n = c->child; n; n = n->next)
516             {
517                 if (is_numeric_tag(eh, n) != 102)
518                     continue;
519                 res->nicknames[i++] = f_string (eh, n);
520             }
521             break;
522         case 115: res->usageRest = f_humstring(eh, c); break;
523         case 116: res->paymentAddr = f_humstring(eh, c); break;
524         case 117: res->hours = f_humstring(eh, c); break;
525         case 118:
526             res->num_dbCombinations = 0;
527             for (n = c->child; n; n = n->next)
528             {
529                 if (!is_numeric_tag(eh, n) != 605)
530                     continue;
531                 (res->num_dbCombinations)++;
532             }
533             if (res->num_dbCombinations)
534                 res->dbCombinations =
535                     (Z_DatabaseList **)odr_malloc (eh->o, res->num_dbCombinations
536                                 * sizeof(*res->dbCombinations));
537             for (n = c->child; n; n = n->next)
538             {
539                 if (!is_numeric_tag(eh, n) != 605)
540                     continue;
541                 res->dbCombinations[i++] = f_databaseList (eh, n);
542             }
543             break;
544         case 119: res->addresses = 0; break; /* fix */
545         case 500: res->commonAccessInfo = f_accessInfo(eh, c); break;
546         }
547     }
548     if (!res->namedResultSets)
549         res->namedResultSets = eh->false_value;
550     if (!res->multipleDbSearch)
551         res->multipleDbSearch = eh->false_value;
552     return res;
553 }
554
555 static Z_DatabaseInfo *f_databaseInfo(ExpHandle *eh, data1_node *n)
556 {
557     Z_DatabaseInfo *res = (Z_DatabaseInfo *)odr_malloc(eh->o, sizeof(*res));
558     data1_node *c;
559
560     res->commonInfo = 0;
561     res->name = 0;
562     res->explainDatabase = 0;
563     res->num_nicknames = 0;
564     res->nicknames = 0;
565     res->icon = 0;
566     res->userFee = 0;
567     res->available = 0;
568     res->titleString = 0;
569     res->num_keywords = 0;
570     res->keywords = 0;
571     res->description = 0;
572     res->associatedDbs = 0;
573     res->subDbs = 0;
574     res->disclaimers = 0;
575     res->news = 0;
576 #ifdef ASN_COMPILED
577     res->u.actualNumber = 0;
578 #else
579     res->recordCount = 0;
580 #endif
581     res->defaultOrder = 0;
582     res->avRecordSize = 0;
583     res->maxRecordSize = 0;
584     res->hours = 0;
585     res->bestTime = 0;
586     res->lastUpdate = 0;
587     res->updateInterval = 0;
588     res->coverage = 0;
589     res->proprietary = 0;
590     res->copyrightText = 0;
591     res->copyrightNotice = 0;
592     res->producerContactInfo = 0;
593     res->supplierContactInfo = 0;
594     res->submissionContactInfo = 0;
595     res->accessInfo = 0;
596     
597     for (c = n->child; c; c = c->next)
598     {
599         int i = 0;
600
601         switch (is_numeric_tag (eh, c))
602         {
603         case 600: res->commonInfo = f_commonInfo(eh, c); break;
604         case 102: res->name = f_string(eh, c); break;
605         case 226: res->explainDatabase = odr_nullval(); break;
606         case 114:
607             res->num_nicknames = 0;
608             for (n = c->child; n; n = n->next)
609             {
610                 if (!is_numeric_tag(eh, n) ||
611                     n->u.tag.element->tag->value.numeric != 102)
612                     continue;
613                 (res->num_nicknames)++;
614             }
615             if (res->num_nicknames)
616                 res->nicknames =
617                     (char **)odr_malloc (eh->o, res->num_nicknames 
618                                 * sizeof(*res->nicknames));
619             for (n = c->child; n; n = n->next)
620             {
621                 if (!is_numeric_tag(eh, n) ||
622                     n->u.tag.element->tag->value.numeric != 102)
623                     continue;
624                 res->nicknames[i++] = f_string (eh, n);
625             }
626             break;
627         case 104: res->icon = 0; break;      /* fix */
628         case 201: res->userFee = f_bool(eh, c); break;
629         case 202: res->available = f_bool(eh, c); break;
630         case 203: res->titleString = f_humstring(eh, c); break;
631         case 227:
632             res->num_keywords = 0;
633             for (n = c->child; n; n = n->next)
634             {
635                 if (!is_numeric_tag(eh, n) != 1000)
636                     continue;
637                 (res->num_keywords)++;
638             }
639             if (res->num_keywords)
640                 res->keywords =
641                     (Z_HumanString **)odr_malloc (eh->o, res->num_keywords 
642                                 * sizeof(*res->keywords));
643             for (n = c->child; n; n = n->next)
644             {
645                 if (!is_numeric_tag(eh, n) != 1000)
646                     continue;
647                 res->keywords[i++] = f_humstring (eh, n);
648             }
649             break;
650         case 113: res->description = f_humstring(eh, c); break;
651         case 205:
652             res->associatedDbs = f_databaseList (eh, c);
653             break;
654         case 206:
655             res->subDbs = f_databaseList (eh, c);
656             break;
657         case 207: res->disclaimers = f_humstring(eh, c); break;
658         case 103: res->news = f_humstring(eh, c); break;
659 #ifdef ASN_COMPILED
660         case 209: res->u.actualNumber =
661                       f_recordCount(eh, c, &res->which); break;
662 #else
663         case 209: res->recordCount =
664                       f_recordCount(eh, c, &res->recordCount_which); break;
665 #endif
666         case 212: res->defaultOrder = f_humstring(eh, c); break;
667         case 213: res->avRecordSize = f_integer(eh, c); break;
668         case 214: res->maxRecordSize = f_integer(eh, c); break;
669         case 215: res->hours = f_humstring(eh, c); break;
670         case 216: res->bestTime = f_humstring(eh, c); break;
671         case 217: res->lastUpdate = f_string(eh, c); break;
672         case 218: res->updateInterval = f_intunit(eh, c); break;
673         case 219: res->coverage = f_humstring(eh, c); break;
674         case 220: res->proprietary = f_bool(eh, c); break;
675         case 221: res->copyrightText = f_humstring(eh, c); break;
676         case 222: res->copyrightNotice = f_humstring(eh, c); break;
677         case 223: res->producerContactInfo = f_contactInfo(eh, c); break;
678         case 224: res->supplierContactInfo = f_contactInfo(eh, c); break;
679         case 225: res->submissionContactInfo = f_contactInfo(eh, c); break;
680         case 500: res->accessInfo = f_accessInfo(eh, c); break;
681         }
682     }
683     if (!res->userFee)
684         res->userFee = eh->false_value;
685     if (!res->available)
686         res->available = eh->true_value;
687     return res;
688 }
689
690 Z_ExplainRecord *data1_nodetoexplain (data1_handle dh, data1_node *n,
691                                       int select, ODR o)
692 {
693     ExpHandle eh;
694     Z_ExplainRecord *res = (Z_ExplainRecord *)odr_malloc(o, sizeof(*res));
695
696     eh.dh = dh;
697     eh.select = select;
698     eh.o = o;
699     eh.false_value = (int *)odr_malloc(eh.o, sizeof(eh.false_value));
700     *eh.false_value = 0;
701     eh.true_value = (int *)odr_malloc(eh.o, sizeof(eh.true_value));
702     *eh.true_value = 1;
703
704     assert(n->which == DATA1N_root);
705     if (strcmp(n->u.root.type, "explain"))
706     {
707         logf(LOG_WARN, "Attempt to convert a non-Explain record");
708         return 0;
709     }
710     for (n = n->child; n; n = n->next)
711     {
712         switch (is_numeric_tag (&eh, n))
713         {
714         case 1:
715             res->which = Z_Explain_targetInfo;
716             if (!(res->u.targetInfo = f_targetInfo(&eh, n)))
717                 return 0;
718             return res;
719         case 2:
720             res->which = Z_Explain_databaseInfo;
721             if (!(res->u.databaseInfo = f_databaseInfo(&eh, n)))
722                 return 0;
723             return res;
724         }
725     }
726     logf(LOG_WARN, "No category in Explain record");
727     return 0;
728 }