Fixed bug regarding creation of some data1 nodes for Explain records.
[idzebra-moved-to-github.git] / index / zinfo.c
1 /*
2  * Copyright (C) 1994-1998, Index Data
3  * All rights reserved.
4  * Sebastian Hammer, Adam Dickmeiss
5  *
6  * $Log: zinfo.c,v $
7  * Revision 1.13  1998-11-03 10:17:09  adam
8  * Fixed bug regarding creation of some data1 nodes for Explain records.
9  *
10  * Revision 1.12  1998/10/13 20:37:11  adam
11  * Changed the way attribute sets are saved in Explain database to
12  * reflect "dynamic" OIDs.
13  *
14  * Revision 1.11  1998/06/09 12:16:48  adam
15  * Implemented auto-generation of CategoryList records.
16  *
17  * Revision 1.10  1998/06/08 14:43:15  adam
18  * Added suport for EXPLAIN Proxy servers - added settings databasePath
19  * and explainDatabase to facilitate this. Increased maximum number
20  * of databases and attributes in one register.
21  *
22  * Revision 1.9  1998/06/02 12:10:27  adam
23  * Fixed bug related to attributeDetails.
24  *
25  * Revision 1.8  1998/05/20 10:12:20  adam
26  * Implemented automatic EXPLAIN database maintenance.
27  * Modified Zebra to work with ASN.1 compiled version of YAZ.
28  *
29  * Revision 1.7  1998/03/05 08:45:13  adam
30  * New result set model and modular ranking system. Moved towards
31  * descent server API. System information stored as "SGML" records.
32  *
33  * Revision 1.6  1998/02/17 10:29:27  adam
34  * Moved towards 'automatic' EXPLAIN database.
35  *
36  * Revision 1.5  1997/10/27 14:33:05  adam
37  * Moved towards generic character mapping depending on "structure"
38  * field in abstract syntax file. Fixed a few memory leaks. Fixed
39  * bug with negative integers when doing searches with relational
40  * operators.
41  *
42  * Revision 1.4  1997/09/25 14:57:08  adam
43  * Added string.h.
44  *
45  * Revision 1.3  1996/05/22 08:21:59  adam
46  * Added public ZebDatabaseInfo structure.
47  *
48  * Revision 1.2  1996/05/14 06:16:41  adam
49  * Compact use/set bytes used in search service.
50  *
51  * Revision 1.1  1996/05/13 14:23:07  adam
52  * Work on compaction of set/use bytes in dictionary.
53  *
54  */
55
56 #include <stdlib.h>
57 #include <assert.h>
58 #include <string.h>
59 #include <time.h>
60
61 #include <zebraver.h>
62 #include "zinfo.h"
63
64 #define ZINFO_DEBUG 0
65
66 struct zebSUInfo {
67     int set;
68     int use;
69     int ordinal;
70 };
71
72 struct zebSUInfoB {
73     struct zebSUInfo info;
74     struct zebSUInfoB *next;
75 };
76
77 typedef struct zebAccessObjectB *zebAccessObject;
78 struct zebAccessObjectB {
79     void *handle;
80     int sysno;
81     Odr_oid *oid;
82     zebAccessObject next;
83 };
84
85 typedef struct zebAccessInfoB *zebAccessInfo;
86 struct zebAccessInfoB {
87     zebAccessObject attributeSetIds;
88     zebAccessObject schemas;
89 };
90
91 typedef struct {
92     struct zebSUInfoB *SUInfo;
93     int sysno;
94     int dirty;
95     int readFlag;
96     data1_node *data1_tree;
97 } *zebAttributeDetails;
98
99 struct zebDatabaseInfoB {
100     zebAttributeDetails attributeDetails;
101     char *databaseName;
102     data1_node *data1_database;
103     int recordCount;     /* records in db */
104     int recordBytes;     /* size of records */
105     int sysno;           /* sysno of database info */
106     int readFlag;        /* 1: read is needed when referenced; 0 if not */
107     int dirty;           /* 1: database is dirty: write is needed */
108     struct zebDatabaseInfoB *next;
109     zebAccessInfo accessInfo;
110 };
111
112 struct zebraExplainAttset {
113     char *name;
114     int ordinal;
115     struct zebraExplainAttset *next;
116 };
117
118 struct zebraCategoryListInfo {
119     int dirty;
120     int sysno;
121     data1_node *data1_categoryList;
122 };
123
124 struct zebraExplainInfo {
125     int  ordinalSU;
126     int  runNumber;
127     int  dirty;
128     Records records;
129     data1_handle dh;
130     Res res;
131     struct zebraExplainAttset *attsets;
132     NMEM nmem;
133     data1_node *data1_target;
134     struct zebraCategoryListInfo *categoryList;
135     struct zebDatabaseInfoB *databaseInfo;
136     struct zebDatabaseInfoB *curDatabaseInfo;
137     zebAccessInfo accessInfo;
138     char date[15]; /* YYYY MMDD HH MM SS */
139     int (*updateFunc)(void *handle, Record drec, data1_node *n);
140     void *updateHandle;
141 };
142
143 static void zebraExplain_initCommonInfo (ZebraExplainInfo zei, data1_node *n);
144 static void zebraExplain_initAccessInfo (ZebraExplainInfo zei, data1_node *n);
145
146 static data1_node *read_sgml_rec (data1_handle dh, NMEM nmem, Record rec)
147 {
148     return data1_read_sgml (dh, nmem, rec->info[recInfo_storeData]);
149 }
150
151 static data1_node *data1_search_tag (data1_handle dh, data1_node *n,
152                                     const char *tag)
153 {
154     logf (LOG_DEBUG, "data1_search_tag %s", tag);
155     for (; n; n = n->next)
156         if (n->which == DATA1N_tag && n->u.tag.tag &&
157             !yaz_matchstr (tag, n->u.tag.tag))
158         {
159             logf (LOG_DEBUG, " found");
160             return n;
161         }
162     logf (LOG_DEBUG, " not found");
163     return 0;
164 }
165
166 static data1_node *data1_add_tag (data1_handle dh, data1_node *at,
167                                   const char *tag, NMEM nmem)
168 {
169     data1_node *partag = get_parent_tag(dh, at);
170     data1_node *res = data1_mk_node (dh, nmem);
171     data1_element *e = NULL;
172
173     res->parent = at;
174     res->which = DATA1N_tag;
175     res->u.tag.tag = data1_insert_string (dh, res, nmem, tag);
176     res->u.tag.node_selected = 0;
177     res->u.tag.make_variantlist = 0;
178     res->u.tag.no_data_requested = 0;
179     res->u.tag.get_bytes = -1;
180    
181     if (partag)
182         e = partag->u.tag.element;
183     res->u.tag.element =
184         data1_getelementbytagname (dh, at->root->u.root.absyn,
185                                    e, res->u.tag.tag);
186     res->root = at->root;
187     if (!at->child)
188         at->child = res;
189     else
190     {
191         assert (at->last_child);
192         at->last_child->next = res;
193     }
194     at->last_child = res;
195     return res;
196 }
197
198 static data1_node *data1_make_tag (data1_handle dh, data1_node *at,
199                                    const char *tag, NMEM nmem)
200 {
201     data1_node *node = data1_search_tag (dh, at->child, tag);
202     if (!node)
203         node = data1_add_tag (dh, at, tag, nmem);
204     else
205         node->child = node->last_child = NULL;
206     return node;
207 }
208
209 static data1_node *data1_add_tagdata_int (data1_handle dh, data1_node *at,
210                                           const char *tag, int num,
211                                           NMEM nmem)
212 {
213     data1_node *node_data;
214     
215     node_data = data1_add_taggeddata (dh, at->root, at, tag, nmem);
216     if (!node_data)
217         return 0;
218     node_data->u.data.what = DATA1I_num;
219     node_data->u.data.data = node_data->lbuf;
220     sprintf (node_data->u.data.data, "%d", num);
221     node_data->u.data.len = strlen (node_data->u.data.data);
222     return node_data;
223 }
224
225 static data1_node *data1_add_tagdata_oid (data1_handle dh, data1_node *at,
226                                            const char *tag, Odr_oid *oid,
227                                            NMEM nmem)
228 {
229     data1_node *node_data;
230     char str[128], *p = str;
231     Odr_oid *ii;
232     
233     node_data = data1_add_taggeddata (dh, at->root, at, tag, nmem);
234     if (!node_data)
235         return 0;
236     
237     for (ii = oid; *ii >= 0; ii++)
238     {
239         if (ii != oid)
240             *p++ = '.';
241         sprintf (p, "%d", *ii);
242         p += strlen (p);
243     }
244     node_data->u.data.what = DATA1I_oid;
245     node_data->u.data.len = strlen (str);
246     node_data->u.data.data = data1_insert_string (dh, node_data, nmem, str);
247     return node_data;
248 }
249
250
251 static data1_node *data1_add_tagdata_text (data1_handle dh, data1_node *at,
252                                            const char *tag, const char *str,
253                                            NMEM nmem)
254 {
255     data1_node *node_data;
256     
257     node_data = data1_add_taggeddata (dh, at->root, at, tag, nmem);
258     if (!node_data)
259         return 0;
260     node_data->u.data.what = DATA1I_text;
261     node_data->u.data.len = strlen (str);
262     node_data->u.data.data = data1_insert_string (dh, node_data, nmem, str);
263     return node_data;
264 }
265
266 static data1_node *data1_make_tagdata_text (data1_handle dh, data1_node *at,
267                                             const char *tag, const char *str,
268                                             NMEM nmem)
269 {
270     data1_node *node = data1_search_tag (dh, at->child, tag);
271     if (!node)
272         return data1_add_tagdata_text (dh, at, tag, str, nmem);
273     else
274     {
275         data1_node *node_data = node->child;
276         node_data->u.data.what = DATA1I_text;
277         node_data->u.data.len = strlen (node_data->u.data.data);
278         node_data->u.data.data = data1_insert_string (dh, node_data,
279                                                       nmem, str);
280         return node_data;
281     }
282 }
283
284 static void zebraExplain_writeDatabase (ZebraExplainInfo zei,
285                                         struct zebDatabaseInfoB *zdi,
286                                         int key_flush);
287 static void zebraExplain_writeAttributeDetails (ZebraExplainInfo zei,
288                                                 zebAttributeDetails zad,
289                                                 const char *databaseName,
290                                                 int key_flush);
291 static void zebraExplain_writeTarget (ZebraExplainInfo zei, int key_flush);
292 static void zebraExplain_writeAttributeSet (ZebraExplainInfo zei,
293                                             zebAccessObject o,
294                                             int key_flush);
295 static void zebraExplain_writeCategoryList (ZebraExplainInfo zei,
296                                             struct zebraCategoryListInfo *zcl,
297                                             int key_flush);
298
299
300 static Record createRecord (Records records, int *sysno)
301 {
302     Record rec;
303     if (*sysno)
304     {
305         rec = rec_get (records, *sysno);
306         xfree (rec->info[recInfo_storeData]);
307     }
308     else
309     {
310         rec = rec_new (records);
311         *sysno = rec->sysno;
312         
313         rec->info[recInfo_fileType] =
314             rec_strdup ("grs.sgml", &rec->size[recInfo_fileType]);
315         rec->info[recInfo_databaseName] =
316             rec_strdup ("IR-Explain-1",
317                         &rec->size[recInfo_databaseName]); 
318     }
319     return rec;
320 }
321
322 void zebraExplain_close (ZebraExplainInfo zei, int writeFlag,
323                          int (*updateH)(Record drec, data1_node *n))
324 {
325     struct zebDatabaseInfoB *zdi;
326     
327     logf (LOG_DEBUG, "zebraExplain_close wr=%d", writeFlag);
328     if (writeFlag)
329     {
330         zebAccessObject o;
331         /* write each database info record */
332         for (zdi = zei->databaseInfo; zdi; zdi = zdi->next)
333         {
334             zebraExplain_writeDatabase (zei, zdi, 1);
335             zebraExplain_writeAttributeDetails (zei, zdi->attributeDetails,
336                                                 zdi->databaseName, 1);
337         }
338         zebraExplain_writeTarget (zei, 1);
339         zebraExplain_writeCategoryList (zei,
340                                         zei->categoryList,
341                                         1);
342         assert (zei->accessInfo);
343         for (o = zei->accessInfo->attributeSetIds; o; o = o->next)
344             if (!o->sysno)
345                 zebraExplain_writeAttributeSet (zei, o, 1);
346         for (o = zei->accessInfo->schemas; o; o = o->next)
347             if (!o->sysno)
348             {
349 /*              zebraExplain_writeSchema (zei, o, 1); */
350             }
351
352         for (zdi = zei->databaseInfo; zdi; zdi = zdi->next)
353         {
354             zebraExplain_writeDatabase (zei, zdi, 0);
355             zebraExplain_writeAttributeDetails (zei, zdi->attributeDetails,
356                                                 zdi->databaseName, 0);
357         }
358         zebraExplain_writeTarget (zei, 0);
359         
360     }
361     nmem_destroy (zei->nmem);
362     xfree (zei);
363 }
364
365 void zebraExplain_mergeOids (ZebraExplainInfo zei, data1_node *n,
366                              zebAccessObject *op)
367 {
368     data1_node *np;
369
370     for (np = n->child; np; np = np->next)
371     {
372         char str[64];
373         int len;
374         Odr_oid *oid;
375         zebAccessObject ao;
376
377         if (np->which != DATA1N_tag || strcmp (np->u.tag.tag, "oid"))
378             continue;
379         len = np->child->u.data.len;
380         if (len > 63)
381             len = 63;
382         memcpy (str, np->child->u.data.data, len);
383         str[len] = '\0';
384         
385         oid = odr_getoidbystr_nmem (zei->nmem, str);
386
387         for (ao = *op; ao; ao = ao->next)
388             if (!oid_oidcmp (oid, ao->oid))
389             {
390                 ao->sysno = 1;
391                 break;
392             }
393         if (!ao)
394         {
395             ao = nmem_malloc (zei->nmem, sizeof(*ao));
396             ao->handle = NULL;
397             ao->sysno = 1;
398             ao->oid = oid;
399             ao->next = *op;
400             *op = ao;
401         }
402     }
403 }
404
405 void zebraExplain_mergeAccessInfo (ZebraExplainInfo zei, data1_node *n,
406                                    zebAccessInfo *accessInfo)
407 {
408     data1_node *np;
409     
410     if (!n)
411     {
412         *accessInfo = nmem_malloc (zei->nmem, sizeof(**accessInfo));
413         (*accessInfo)->attributeSetIds = NULL;
414         (*accessInfo)->schemas = NULL;
415     }
416     else
417     {
418         if (!(n = data1_search_tag (zei->dh, n->child, "accessInfo")))
419             return;
420         if ((np = data1_search_tag (zei->dh, n->child, "attributeSetIds")))
421             zebraExplain_mergeOids (zei, np,
422                                     &(*accessInfo)->attributeSetIds);
423         if ((np = data1_search_tag (zei->dh, n->child, "schemas")))
424             zebraExplain_mergeOids (zei, np,
425                                     &(*accessInfo)->schemas);
426     }
427 }
428
429 ZebraExplainInfo zebraExplain_open (
430     Records records, data1_handle dh,
431     Res res,
432     int writeFlag,
433     void *updateHandle,
434     int (*updateFunc)(void *handle, Record drec, data1_node *n))
435 {
436     Record trec;
437     ZebraExplainInfo zei;
438     struct zebDatabaseInfoB **zdip;
439     time_t our_time;
440     struct tm *tm;
441
442     logf (LOG_DEBUG, "zebraExplain_open wr=%d", writeFlag);
443     zei = xmalloc (sizeof(*zei));
444     zei->updateHandle = updateHandle;
445     zei->updateFunc = updateFunc;
446     zei->dirty = 0;
447     zei->curDatabaseInfo = NULL;
448     zei->records = records;
449     zei->nmem = nmem_create ();
450     zei->dh = dh;
451     zei->attsets = NULL;
452     zei->res = res;
453     zei->categoryList = nmem_malloc (zei->nmem,
454                                      sizeof(*zei->categoryList));
455     zei->categoryList->sysno = 0;
456     zei->categoryList->dirty = 0;
457     zei->categoryList->data1_categoryList = NULL;
458
459     time (&our_time);
460     tm = localtime (&our_time);
461     sprintf (zei->date, "%04d%02d%02d%02d%02d%02d",
462              tm->tm_year+1900, tm->tm_mon+1,  tm->tm_mday,
463              tm->tm_hour, tm->tm_min, tm->tm_sec);
464
465     zdip = &zei->databaseInfo;
466     trec = rec_get (records, 1);      /* get "root" record */
467
468     zebraExplain_mergeAccessInfo (zei, 0, &zei->accessInfo);
469     if (trec)    /* targetInfo already exists ... */
470     {
471         data1_node *node_tgtinfo, *node_zebra, *node_list, *np;
472
473         zei->data1_target = read_sgml_rec (zei->dh, zei->nmem, trec);
474
475 #if ZINFO_DEBUG
476         data1_pr_tree (zei->dh, zei->data1_target, stderr);
477 #endif
478         node_tgtinfo = data1_search_tag (zei->dh, zei->data1_target->child,
479                                          "targetInfo");
480         zebraExplain_mergeAccessInfo (zei, node_tgtinfo,
481                                       &zei->accessInfo);
482
483         node_zebra = data1_search_tag (zei->dh, node_tgtinfo->child,
484                                        "zebraInfo");
485         node_list = data1_search_tag (zei->dh, node_zebra->child,
486                                       "databaseList");
487         for (np = node_list->child; np; np = np->next)
488         {
489             data1_node *node_name = NULL;
490             data1_node *node_id = NULL;
491             data1_node *node_aid = NULL;
492             data1_node *np2;
493             if (np->which != DATA1N_tag || strcmp (np->u.tag.tag, "database"))
494                 continue;
495             for (np2 = np->child; np2; np2 = np2->next)
496             {
497                 if (np2->which != DATA1N_tag)
498                     continue;
499                 if (!strcmp (np2->u.tag.tag, "name"))
500                     node_name = np2->child;
501                 else if (!strcmp (np2->u.tag.tag, "id"))
502                     node_id = np2->child;
503                 else if (!strcmp (np2->u.tag.tag, "attributeDetailsId"))
504                     node_aid = np2->child;
505             }
506             assert (node_id && node_name && node_aid);
507             
508             *zdip = nmem_malloc (zei->nmem, sizeof(**zdip));
509
510             (*zdip)->readFlag = 1;
511             (*zdip)->dirty = 0;
512             (*zdip)->data1_database = NULL;
513             (*zdip)->recordCount = 0;
514             (*zdip)->recordBytes = 0;
515             zebraExplain_mergeAccessInfo (zei, 0, &(*zdip)->accessInfo);
516
517             (*zdip)->databaseName = nmem_malloc (zei->nmem,
518                                                  1+node_name->u.data.len);
519             memcpy ((*zdip)->databaseName, node_name->u.data.data,
520                     node_name->u.data.len);
521             (*zdip)->databaseName[node_name->u.data.len] = '\0';
522             (*zdip)->sysno = atoi_n (node_id->u.data.data,
523                                      node_id->u.data.len);
524             (*zdip)->attributeDetails =
525                 nmem_malloc (zei->nmem, sizeof(*(*zdip)->attributeDetails));
526             (*zdip)->attributeDetails->sysno = atoi_n (node_aid->u.data.data,
527                                                        node_aid->u.data.len);
528             (*zdip)->attributeDetails->readFlag = 1;
529             (*zdip)->attributeDetails->dirty = 0;
530             (*zdip)->attributeDetails->SUInfo = NULL;
531
532             zdip = &(*zdip)->next;
533         }
534         np = data1_search_tag (zei->dh, node_zebra->child,
535                                "ordinalSU");
536         np = np->child;
537         assert (np && np->which == DATA1N_data);
538         zei->ordinalSU = atoi_n (np->u.data.data, np->u.data.len);
539
540         np = data1_search_tag (zei->dh, node_zebra->child,
541                                "runNumber");
542         np = np->child;
543         assert (np && np->which == DATA1N_data);
544         zei->runNumber = atoi_n (np->u.data.data, np->u.data.len);
545         *zdip = NULL;
546         rec_rm (&trec);
547     }
548     else  /* create initial targetInfo */
549     {
550         data1_node *node_tgtinfo;
551         zei->ordinalSU = 1;
552         zei->runNumber = 0;
553
554         if (writeFlag)
555         {
556             char *sgml_buf;
557             int sgml_len;
558
559             zei->data1_target =
560                 data1_read_sgml (zei->dh, zei->nmem,
561                                  "<explain><targetInfo>TargetInfo\n"
562                                  "<name>Zebra</>\n"
563                                  "<namedResultSets>1</>\n"
564                                  "<multipleDBSearch>1</>\n"
565                                  "<nicknames><name>Zebra</></>\n"
566                                  "</></>\n" );
567
568             node_tgtinfo = data1_search_tag (zei->dh, zei->data1_target->child,
569                                             "targetInfo");
570             assert (node_tgtinfo);
571
572             zebraExplain_initCommonInfo (zei, node_tgtinfo);
573             zebraExplain_initAccessInfo (zei, node_tgtinfo);
574
575             /* write now because we want to be sure about the sysno */
576             trec = rec_new (records);
577             trec->info[recInfo_fileType] =
578                 rec_strdup ("grs.sgml", &trec->size[recInfo_fileType]);
579             trec->info[recInfo_databaseName] =
580                 rec_strdup ("IR-Explain-1", &trec->size[recInfo_databaseName]);
581             
582             sgml_buf = data1_nodetoidsgml(dh, zei->data1_target, 0, &sgml_len);
583             trec->info[recInfo_storeData] = xmalloc (sgml_len);
584             memcpy (trec->info[recInfo_storeData], sgml_buf, sgml_len);
585             trec->size[recInfo_storeData] = sgml_len;
586             
587             rec_put (records, &trec);
588         }
589         *zdip = NULL;
590         rec_rm (&trec);
591         zebraExplain_newDatabase (zei, "IR-Explain-1", 0);
592
593         if (!zei->categoryList->dirty)
594         {
595             struct zebraCategoryListInfo *zcl = zei->categoryList;
596             data1_node *node_cl;
597
598             zcl->dirty = 1;
599             zcl->data1_categoryList =
600                 data1_read_sgml (zei->dh, zei->nmem,
601                                  "<explain><categoryList>CategoryList\n"
602                                  "/></>\n");
603             node_cl = data1_search_tag (zei->dh,
604                                         zcl->data1_categoryList->child,
605                                         "categoryList");
606             assert (node_cl);
607             zebraExplain_initCommonInfo (zei, node_cl);
608         }
609     }
610     return zei;
611 }
612
613 static void zebraExplain_readAttributeDetails (ZebraExplainInfo zei,
614                                                zebAttributeDetails zad)
615 {
616     Record rec;
617     struct zebSUInfoB **zsuip = &zad->SUInfo;
618     data1_node *node_adinfo, *node_zebra, *node_list, *np;
619
620     assert (zad->sysno);
621     rec = rec_get (zei->records, zad->sysno);
622
623     zad->data1_tree = read_sgml_rec (zei->dh, zei->nmem, rec);
624
625     node_adinfo = data1_search_tag (zei->dh, zad->data1_tree->child,
626                                     "attributeDetails");
627     node_zebra = data1_search_tag (zei->dh, node_adinfo->child,
628                                  "zebraInfo");
629     node_list = data1_search_tag (zei->dh, node_zebra->child,
630                                   "attrlist");
631     for (np = node_list->child; np; np = np->next)
632     {
633         data1_node *node_set = NULL;
634         data1_node *node_use = NULL;
635         data1_node *node_ordinal = NULL;
636         data1_node *np2;
637         char oid_str[128];
638         int oid_str_len;
639
640         if (np->which != DATA1N_tag || strcmp (np->u.tag.tag, "attr"))
641             continue;
642         for (np2 = np->child; np2; np2 = np2->next)
643         {
644             if (np2->which != DATA1N_tag || !np2->child ||
645                 np2->child->which != DATA1N_data)
646                 continue;
647             if (!strcmp (np2->u.tag.tag, "set"))
648                 node_set = np2->child;
649             else if (!strcmp (np2->u.tag.tag, "use"))
650                 node_use = np2->child;
651             else if (!strcmp (np2->u.tag.tag, "ordinal"))
652                 node_ordinal = np2->child;
653         }
654         assert (node_set && node_use && node_ordinal);
655
656         oid_str_len = node_set->u.data.len;
657         if (oid_str_len >= sizeof(oid_str))
658             oid_str_len = sizeof(oid_str)-1;
659         memcpy (oid_str, node_set->u.data.data, oid_str_len);
660         oid_str[oid_str_len] = '\0';
661         
662         *zsuip = nmem_malloc (zei->nmem, sizeof(**zsuip));
663         (*zsuip)->info.set = oid_getvalbyname (oid_str);
664
665         (*zsuip)->info.use = atoi_n (node_use->u.data.data,
666                                      node_use->u.data.len);
667         (*zsuip)->info.ordinal = atoi_n (node_ordinal->u.data.data,
668                                          node_ordinal->u.data.len);
669         logf (LOG_DEBUG, "set=%d use=%d ordinal=%d",
670               (*zsuip)->info.set, (*zsuip)->info.use, (*zsuip)->info.ordinal);
671         zsuip = &(*zsuip)->next;
672     }
673     *zsuip = NULL;
674     zad->readFlag = 0;
675     rec_rm (&rec);
676 }
677
678 static void zebraExplain_readDatabase (ZebraExplainInfo zei,
679                                        struct zebDatabaseInfoB *zdi)
680 {
681     Record rec;
682     data1_node *node_dbinfo, *node_zebra, *np;
683
684     assert (zdi->sysno);
685     rec = rec_get (zei->records, zdi->sysno);
686
687     zdi->data1_database = read_sgml_rec (zei->dh, zei->nmem, rec);
688     
689     node_dbinfo = data1_search_tag (zei->dh, zdi->data1_database->child,
690                                    "databaseInfo");
691     zebraExplain_mergeAccessInfo (zei, node_dbinfo, &zdi->accessInfo);
692
693     node_zebra = data1_search_tag (zei->dh, node_dbinfo->child,
694                                  "zebraInfo");
695     np  = data1_search_tag (zei->dh, node_dbinfo->child,
696                             "recordBytes");
697     if (np && np->child && np->child->which == DATA1N_data)
698         zdi->recordBytes = atoi_n (np->child->u.data.data,
699                                    np->child->u.data.len);
700     if ((np = data1_search_tag (zei->dh, node_dbinfo->child,
701                                 "recordCount")) &&
702         (np = data1_search_tag (zei->dh, np->child,
703                                 "recordCountActual")) &&
704         np->child->which == DATA1N_data)
705     {
706         zdi->recordCount = atoi_n (np->child->u.data.data,
707                                    np->child->u.data.len);
708     }    
709     zdi->readFlag = 0;
710     rec_rm (&rec);
711 }
712
713 int zebraExplain_curDatabase (ZebraExplainInfo zei, const char *database)
714 {
715     struct zebDatabaseInfoB *zdi;
716     
717     assert (zei);
718     if (zei->curDatabaseInfo &&
719         !strcmp (zei->curDatabaseInfo->databaseName, database))
720         return 0;
721     for (zdi = zei->databaseInfo; zdi; zdi=zdi->next)
722     {
723         if (!strcmp (zdi->databaseName, database))
724             break;
725     }
726     if (!zdi)
727         return -1;
728 #if ZINFO_DEBUG
729     logf (LOG_LOG, "zebraExplain_curDatabase: %s", database);
730 #endif
731     if (zdi->readFlag)
732     {
733 #if ZINFO_DEBUG
734         logf (LOG_LOG, "zebraExplain_readDatabase: %s", database);
735 #endif
736         zebraExplain_readDatabase (zei, zdi);
737     }
738     if (zdi->attributeDetails->readFlag)
739     {
740 #if ZINFO_DEBUG
741         logf (LOG_LOG, "zebraExplain_readAttributeDetails: %s", database);
742 #endif
743         zebraExplain_readAttributeDetails (zei, zdi->attributeDetails);
744     }
745     zei->curDatabaseInfo = zdi;
746     return 0;
747 }
748
749 static void zebraExplain_initCommonInfo (ZebraExplainInfo zei, data1_node *n)
750 {
751     data1_node *c = data1_add_tag (zei->dh, n, "commonInfo", zei->nmem);
752
753     data1_add_tagdata_text (zei->dh, c, "dateAdded", zei->date, zei->nmem);
754     data1_add_tagdata_text (zei->dh, c, "dateChanged", zei->date, zei->nmem);
755     data1_add_tagdata_text (zei->dh, c, "languageCode", "EN", zei->nmem);
756 }
757
758 static void zebraExplain_updateCommonInfo (ZebraExplainInfo zei, data1_node *n)
759 {
760     data1_node *c = data1_search_tag (zei->dh, n->child, "commonInfo");
761     assert (c);
762     data1_make_tagdata_text (zei->dh, c, "dateChanged", zei->date, zei->nmem);
763 }
764
765 static void zebraExplain_initAccessInfo (ZebraExplainInfo zei, data1_node *n)
766 {
767     data1_node *c = data1_add_tag (zei->dh, n, "accessInfo", zei->nmem);
768     data1_node *d = data1_add_tag (zei->dh, c, "unitSystems", zei->nmem);
769     data1_add_tagdata_text (zei->dh, d, "string", "ISO", zei->nmem);
770 }
771
772 static void zebraExplain_updateAccessInfo (ZebraExplainInfo zei, data1_node *n,
773                                            zebAccessInfo accessInfo)
774 {
775     data1_node *c = data1_search_tag (zei->dh, n->child, "accessInfo");
776     data1_node *d;
777     zebAccessObject p;
778     
779     assert (c);
780
781     if ((p = accessInfo->attributeSetIds))
782     {
783         d = data1_make_tag (zei->dh, c, "attributeSetIds", zei->nmem);
784         for (; p; p = p->next)
785             data1_add_tagdata_oid (zei->dh, d, "oid", p->oid, zei->nmem);
786     }
787     if ((p = accessInfo->schemas))
788     {
789         d = data1_make_tag (zei->dh, c, "schemas", zei->nmem);
790         for (; p; p = p->next)
791             data1_add_tagdata_oid (zei->dh, d, "oid", p->oid, zei->nmem);
792     }
793 }
794
795 int zebraExplain_newDatabase (ZebraExplainInfo zei, const char *database,
796                               int explain_database)
797 {
798     struct zebDatabaseInfoB *zdi;
799     data1_node *node_dbinfo, *node_adinfo;
800
801 #if ZINFO_DEBUG
802     logf (LOG_LOG, "zebraExplain_newDatabase: %s", database);
803 #endif
804     assert (zei);
805     for (zdi = zei->databaseInfo; zdi; zdi=zdi->next)
806     {
807         if (!strcmp (zdi->databaseName, database))
808             break;
809     }
810     if (zdi)
811         return -1;
812     /* it's new really. make it */
813     zdi = nmem_malloc (zei->nmem, sizeof(*zdi));
814     zdi->next = zei->databaseInfo;
815     zei->databaseInfo = zdi;
816     zdi->sysno = 0;
817     zdi->recordCount = 0;
818     zdi->recordBytes = 0;
819     zdi->readFlag = 0;
820     zdi->databaseName = nmem_strdup (zei->nmem, database);
821
822     zebraExplain_mergeAccessInfo (zei, 0, &zdi->accessInfo);
823     
824     assert (zei->dh);
825     assert (zei->nmem);
826
827     zdi->data1_database =
828         data1_read_sgml (zei->dh, zei->nmem, 
829                          "<explain><databaseInfo>DatabaseInfo\n"
830                          "</></>\n");
831     
832     node_dbinfo = data1_search_tag (zei->dh, zdi->data1_database->child,
833                                    "databaseInfo");
834     assert (node_dbinfo);
835
836     zebraExplain_initCommonInfo (zei, node_dbinfo);
837     zebraExplain_initAccessInfo (zei, node_dbinfo);
838
839     data1_add_tagdata_text (zei->dh, node_dbinfo, "name",
840                                database, zei->nmem);
841     
842     if (explain_database)
843         data1_add_tagdata_text (zei->dh, node_dbinfo, "explainDatabase",
844                                 "", zei->nmem);
845     
846     data1_add_tagdata_text (zei->dh, node_dbinfo, "userFee",
847                             "0", zei->nmem);
848     
849     data1_add_tagdata_text (zei->dh, node_dbinfo, "available",
850                             "1", zei->nmem);
851     
852 #if ZINFO_DEBUG
853     data1_pr_tree (zei->dh, zdi->data1_database, stderr);
854 #endif
855     zdi->dirty = 1;
856     zei->dirty = 1;
857     zei->curDatabaseInfo = zdi;
858
859     zdi->attributeDetails =
860         nmem_malloc (zei->nmem, sizeof(*zdi->attributeDetails));
861     zdi->attributeDetails->readFlag = 0;
862     zdi->attributeDetails->sysno = 0;
863     zdi->attributeDetails->dirty = 1;
864     zdi->attributeDetails->SUInfo = NULL;
865     zdi->attributeDetails->data1_tree =
866         data1_read_sgml (zei->dh, zei->nmem,
867                          "<explain><attributeDetails>AttributeDetails\n"
868                          "</></>\n");
869
870     node_adinfo =
871         data1_search_tag (zei->dh, zdi->attributeDetails->data1_tree->child,
872                           "attributeDetails");
873     assert (node_adinfo);
874
875     zebraExplain_initCommonInfo (zei, node_adinfo);
876
877     return 0;
878 }
879
880 static void writeAttributeValueDetails (ZebraExplainInfo zei,
881                                   zebAttributeDetails zad,
882                                   data1_node *node_atvs, data1_attset *attset)
883
884 {
885     struct zebSUInfoB *zsui;
886     int set_ordinal = attset->reference;
887     data1_attset_child *c;
888
889     for (c = attset->children; c; c = c->next)
890         writeAttributeValueDetails (zei, zad, node_atvs, c->child);
891     for (zsui = zad->SUInfo; zsui; zsui = zsui->next)
892     {
893         data1_node *node_attvalue, *node_value;
894         if (set_ordinal != zsui->info.set)
895             continue;
896         node_attvalue = data1_add_tag (zei->dh, node_atvs, "attributeValue",
897                                        zei->nmem);
898         node_value = data1_add_tag (zei->dh, node_attvalue, "value",
899                                     zei->nmem);
900         data1_add_tagdata_int (zei->dh, node_value, "numeric",
901                                zsui->info.use, zei->nmem);
902     }
903 }
904
905 static void zebraExplain_writeCategoryList (ZebraExplainInfo zei,
906                                             struct zebraCategoryListInfo *zcl,
907                                             int key_flush)
908 {
909     char *sgml_buf;
910     int sgml_len;
911     int i;
912     Record drec;
913     data1_node *node_ci, *node_categoryList;
914     int sysno = 0;
915     static char *category[] = {
916         "CategoryList",
917         "TargetInfo",
918         "DatabaseInfo",
919         "AttributeDetails",
920         NULL
921     };
922
923     assert (zcl);
924     if (!zcl->dirty)
925         return ;
926     zcl->dirty = 1;
927     node_categoryList = zcl->data1_categoryList;
928
929 #if ZINFO_DEBUG
930     logf (LOG_LOG, "zebraExplain_writeCategoryList");
931 #endif
932
933     drec = createRecord (zei->records, &sysno);
934
935     node_ci = data1_search_tag (zei->dh, node_categoryList->child,
936                                 "categoryList");
937     assert (node_ci);
938     node_ci = data1_add_tag (zei->dh, node_ci, "categories", zei->nmem);
939     assert (node_ci);
940     
941     for (i = 0; category[i]; i++)
942     {
943         data1_node *node_cat = data1_add_tag (zei->dh, node_ci, 
944                                               "category", zei->nmem);
945
946         data1_add_tagdata_text (zei->dh, node_cat, "name",
947                                 category[i], zei->nmem);
948     }
949     /* extract *searchable* keys from it. We do this here, because
950        record count, etc. is affected */
951     if (key_flush)
952         (*zei->updateFunc)(zei->updateHandle, drec, node_categoryList);
953
954     /* convert to "SGML" and write it */
955 #if ZINFO_DEBUG
956     data1_pr_tree (zei->dh, zad->data1_tree, stderr);
957 #endif
958     sgml_buf = data1_nodetoidsgml(zei->dh, node_categoryList,
959                                   0, &sgml_len);
960     drec->info[recInfo_storeData] = xmalloc (sgml_len);
961     memcpy (drec->info[recInfo_storeData], sgml_buf, sgml_len);
962     drec->size[recInfo_storeData] = sgml_len;
963     
964     rec_put (zei->records, &drec);
965 }
966
967 static void zebraExplain_writeAttributeDetails (ZebraExplainInfo zei,
968                                                 zebAttributeDetails zad,
969                                                 const char *databaseName,
970                                                 int key_flush)
971 {
972     char *sgml_buf;
973     int sgml_len;
974     Record drec;
975     data1_node *node_adinfo, *node_list, *node_zebra, *node_attributesBySet;
976     struct zebSUInfoB *zsui;
977     int set_min;
978     
979     if (!zad->dirty)
980         return;
981     
982     zad->dirty = 0;
983 #if ZINFO_DEBUG
984     logf (LOG_LOG, "zebraExplain_writeAttributeDetails");    
985 #endif
986
987     drec = createRecord (zei->records, &zad->sysno);
988     assert (zad->data1_tree);
989     node_adinfo = data1_search_tag (zei->dh, zad->data1_tree->child,
990                                    "attributeDetails");
991     zebraExplain_updateCommonInfo (zei, node_adinfo);
992
993     data1_add_tagdata_text (zei->dh, node_adinfo, "name",
994                             databaseName, zei->nmem);
995
996     /* extract *searchable* keys from it. We do this here, because
997        record count, etc. is affected */
998     if (key_flush)
999         (*zei->updateFunc)(zei->updateHandle, drec, zad->data1_tree);
1000
1001     node_attributesBySet = data1_make_tag (zei->dh, node_adinfo,
1002                                            "attributesBySet", zei->nmem);
1003     set_min = -1;
1004     while (1)
1005     {
1006         data1_node *node_asd;
1007         data1_attset *attset;
1008         int set_ordinal = -1;
1009         for (zsui = zad->SUInfo; zsui; zsui = zsui->next)
1010         {
1011             if ((set_ordinal < 0 || set_ordinal > zsui->info.set)
1012                 && zsui->info.set > set_min)
1013                 set_ordinal = zsui->info.set;
1014         }
1015         if (set_ordinal < 0)
1016             break;
1017         set_min = set_ordinal;
1018         node_asd = data1_add_tag (zei->dh, node_attributesBySet,
1019                                   "attributeSetDetails", zei->nmem);
1020
1021         attset = data1_attset_search_id (zei->dh, set_ordinal);
1022         if (!attset)
1023         {
1024             zebraExplain_loadAttsets (zei->dh, zei->res);
1025             attset = data1_attset_search_id (zei->dh, set_ordinal);
1026         }
1027         if (attset)
1028         {
1029             int oid[OID_SIZE];
1030             oident oe;
1031             
1032             oe.proto = PROTO_Z3950;
1033             oe.oclass = CLASS_ATTSET;
1034             oe.value = set_ordinal;
1035             
1036             if (oid_ent_to_oid (&oe, oid))
1037             {
1038                 data1_node *node_abt, *node_atd, *node_atvs;
1039                 data1_add_tagdata_oid (zei->dh, node_asd, "oid",
1040                                        oid, zei->nmem);
1041                 
1042                 node_abt = data1_add_tag (zei->dh, node_asd,
1043                                           "attributesByType", zei->nmem);
1044                 node_atd = data1_add_tag (zei->dh, node_abt,
1045                                           "attributeTypeDetails", zei->nmem);
1046                 data1_add_tagdata_int (zei->dh, node_atd,
1047                                        "type", 1, zei->nmem);
1048                 node_atvs = data1_add_tag (zei->dh, node_atd, 
1049                                            "attributeValues", zei->nmem);
1050                 writeAttributeValueDetails (zei, zad, node_atvs, attset);
1051             }
1052         }
1053     }
1054     /* zebra info (private) */
1055     node_zebra = data1_make_tag (zei->dh, node_adinfo,
1056                                  "zebraInfo", zei->nmem);
1057     node_list = data1_make_tag (zei->dh, node_zebra,
1058                                  "attrlist", zei->nmem);
1059     for (zsui = zad->SUInfo; zsui; zsui = zsui->next)
1060     {
1061         struct oident oident;
1062         int oid[OID_SIZE];
1063         data1_node *node_attr;
1064         
1065         node_attr = data1_add_tag (zei->dh, node_list, "attr", zei->nmem);
1066         
1067         oident.proto = PROTO_Z3950;
1068         oident.oclass = CLASS_ATTSET;
1069         oident.value = zsui->info.set;
1070         oid_ent_to_oid (&oident, oid);
1071         
1072         data1_add_tagdata_text (zei->dh, node_attr, "set",
1073                                 oident.desc, zei->nmem);
1074         data1_add_tagdata_int (zei->dh, node_attr, "use",
1075                                zsui->info.use, zei->nmem);
1076         data1_add_tagdata_int (zei->dh, node_attr, "ordinal",
1077                                zsui->info.ordinal, zei->nmem);
1078     }
1079     /* convert to "SGML" and write it */
1080 #if ZINFO_DEBUG
1081     data1_pr_tree (zei->dh, zad->data1_tree, stderr);
1082 #endif
1083     sgml_buf = data1_nodetoidsgml(zei->dh, zad->data1_tree,
1084                                   0, &sgml_len);
1085     drec->info[recInfo_storeData] = xmalloc (sgml_len);
1086     memcpy (drec->info[recInfo_storeData], sgml_buf, sgml_len);
1087     drec->size[recInfo_storeData] = sgml_len;
1088     
1089     rec_put (zei->records, &drec);
1090 }
1091
1092 static void zebraExplain_writeDatabase (ZebraExplainInfo zei,
1093                                         struct zebDatabaseInfoB *zdi,
1094                                         int key_flush)
1095 {
1096     char *sgml_buf;
1097     int sgml_len;
1098     Record drec;
1099     data1_node *node_dbinfo, *node_count, *node_zebra;
1100     
1101     if (!zdi->dirty)
1102         return;
1103
1104     zdi->dirty = 0;
1105 #if ZINFO_DEBUG
1106     logf (LOG_LOG, "zebraExplain_writeDatabase %s", zdi->databaseName);
1107 #endif
1108     drec = createRecord (zei->records, &zdi->sysno);
1109     assert (zdi->data1_database);
1110     node_dbinfo = data1_search_tag (zei->dh, zdi->data1_database->child,
1111                                    "databaseInfo");
1112
1113     zebraExplain_updateCommonInfo (zei, node_dbinfo);
1114     zebraExplain_updateAccessInfo (zei, node_dbinfo, zdi->accessInfo);
1115
1116     /* extract *searchable* keys from it. We do this here, because
1117        record count, etc. is affected */
1118     if (key_flush)
1119         (*zei->updateFunc)(zei->updateHandle, drec, zdi->data1_database);
1120     /* record count */
1121     node_count = data1_make_tag (zei->dh, node_dbinfo,
1122                                  "recordCount", zei->nmem);
1123     data1_add_tagdata_int (zei->dh, node_count, "recordCountActual",
1124                               zdi->recordCount, zei->nmem);
1125
1126     /* zebra info (private) */
1127     node_zebra = data1_make_tag (zei->dh, node_dbinfo,
1128                                  "zebraInfo", zei->nmem);
1129     data1_add_tagdata_int (zei->dh, node_zebra,
1130                            "recordBytes", zdi->recordBytes, zei->nmem);
1131     /* convert to "SGML" and write it */
1132 #if ZINFO_DEBUG
1133     data1_pr_tree (zei->dh, zdi->data1_database, stderr);
1134 #endif
1135     sgml_buf = data1_nodetoidsgml(zei->dh, zdi->data1_database,
1136                                   0, &sgml_len);
1137     drec->info[recInfo_storeData] = xmalloc (sgml_len);
1138     memcpy (drec->info[recInfo_storeData], sgml_buf, sgml_len);
1139     drec->size[recInfo_storeData] = sgml_len;
1140     
1141     rec_put (zei->records, &drec);
1142 }
1143
1144 static void writeAttributeValues (ZebraExplainInfo zei,
1145                                   data1_node *node_values,
1146                                   data1_attset *attset)
1147 {
1148     data1_att *atts;
1149     data1_attset_child *c;
1150
1151     if (!attset)
1152         return;
1153
1154     for (c = attset->children; c; c = c->next)
1155         writeAttributeValues (zei, node_values, c->child);
1156     for (atts = attset->atts; atts; atts = atts->next)
1157     {
1158         data1_node *node_value;
1159         
1160         node_value = data1_add_tag (zei->dh, node_values, "attributeValue",
1161                                     zei->nmem);
1162         data1_add_tagdata_text (zei->dh, node_value, "name",
1163                                 atts->name, zei->nmem);
1164         node_value = data1_add_tag (zei->dh, node_value, "value", zei->nmem);
1165         data1_add_tagdata_int (zei->dh, node_value, "numeric",
1166                                atts->value, zei->nmem);
1167     }
1168 }
1169
1170
1171 static void zebraExplain_writeAttributeSet (ZebraExplainInfo zei,
1172                                             zebAccessObject o,
1173                                             int key_flush)
1174 {
1175     char *sgml_buf;
1176     int sgml_len;
1177     Record drec;
1178     data1_node *node_root, *node_attinfo, *node_attributes, *node_atttype;
1179     data1_node *node_values;
1180     struct oident *entp;
1181     struct data1_attset *attset = NULL;
1182     
1183     if ((entp = oid_getentbyoid (o->oid)))
1184         attset = data1_attset_search_id (zei->dh, entp->value);
1185             
1186 #if ZINFO_DEBUG
1187     logf (LOG_LOG, "zebraExplain_writeAttributeSet %s",
1188           attset ? attset->name : "<unknown>");    
1189 #endif
1190
1191     drec = createRecord (zei->records, &o->sysno);
1192     node_root =
1193         data1_read_sgml (zei->dh, zei->nmem,
1194                          "<explain><attributeSetInfo>AttributeSetInfo\n"
1195                          "</></>\n" );
1196
1197     node_attinfo = data1_search_tag (zei->dh, node_root->child,
1198                                    "attributeSetInfo");
1199
1200     zebraExplain_initCommonInfo (zei, node_attinfo);
1201     zebraExplain_updateCommonInfo (zei, node_attinfo);
1202
1203     data1_add_tagdata_oid (zei->dh, node_attinfo,
1204                             "oid", o->oid, zei->nmem);
1205     if (attset && attset->name)
1206         data1_add_tagdata_text (zei->dh, node_attinfo,
1207                                 "name", attset->name, zei->nmem);
1208     
1209     node_attributes = data1_make_tag (zei->dh, node_attinfo,
1210                                       "attributes", zei->nmem);
1211     node_atttype = data1_make_tag (zei->dh, node_attributes,
1212                                    "attributeType", zei->nmem);
1213     data1_add_tagdata_text (zei->dh, node_atttype,
1214                             "name", "Use", zei->nmem);
1215     data1_add_tagdata_text (zei->dh, node_atttype,
1216                             "description", "Use Attribute", zei->nmem);
1217     data1_add_tagdata_int (zei->dh, node_atttype,
1218                            "type", 1, zei->nmem);
1219     node_values = data1_add_tag (zei->dh, node_atttype,
1220                                  "attributeValues", zei->nmem);
1221     if (attset)
1222         writeAttributeValues (zei, node_values, attset);
1223
1224     /* extract *searchable* keys from it. We do this here, because
1225        record count, etc. is affected */
1226     if (key_flush)
1227         (*zei->updateFunc)(zei->updateHandle, drec, node_root);
1228     /* convert to "SGML" and write it */
1229 #if ZINFO_DEBUG
1230     data1_pr_tree (zei->dh, node_root, stderr);
1231 #endif
1232     sgml_buf = data1_nodetoidsgml(zei->dh, node_root, 0, &sgml_len);
1233     drec->info[recInfo_storeData] = xmalloc (sgml_len);
1234     memcpy (drec->info[recInfo_storeData], sgml_buf, sgml_len);
1235     drec->size[recInfo_storeData] = sgml_len;
1236     
1237     rec_put (zei->records, &drec);
1238 }
1239
1240 static void zebraExplain_writeTarget (ZebraExplainInfo zei, int key_flush)
1241 {
1242     struct zebDatabaseInfoB *zdi;
1243     data1_node *node_tgtinfo, *node_list, *node_zebra;
1244     Record trec;
1245     int sgml_len;
1246     char *sgml_buf;
1247
1248     if (!zei->dirty)
1249         return;
1250     zei->dirty = 0;
1251
1252     trec = rec_get (zei->records, 1);
1253     xfree (trec->info[recInfo_storeData]);
1254
1255     node_tgtinfo = data1_search_tag (zei->dh, zei->data1_target->child,
1256                                    "targetInfo");
1257     assert (node_tgtinfo);
1258
1259     zebraExplain_updateCommonInfo (zei, node_tgtinfo);
1260     zebraExplain_updateAccessInfo (zei, node_tgtinfo, zei->accessInfo);
1261
1262     /* convert to "SGML" and write it */
1263     if (key_flush)
1264         (*zei->updateFunc)(zei->updateHandle, trec, zei->data1_target);
1265
1266     node_zebra = data1_make_tag (zei->dh, node_tgtinfo,
1267                                  "zebraInfo", zei->nmem);
1268     data1_add_tagdata_text (zei->dh, node_zebra, "version",
1269                                ZEBRAVER, zei->nmem);
1270     node_list = data1_add_tag (zei->dh, node_zebra,
1271                                   "databaseList", zei->nmem);
1272     for (zdi = zei->databaseInfo; zdi; zdi = zdi->next)
1273     {
1274         data1_node *node_db;
1275         node_db = data1_add_tag (zei->dh, node_list,
1276                                     "database", zei->nmem);
1277         data1_add_tagdata_text (zei->dh, node_db, "name",
1278                                    zdi->databaseName, zei->nmem);
1279         data1_add_tagdata_int (zei->dh, node_db, "id",
1280                                   zdi->sysno, zei->nmem);
1281         data1_add_tagdata_int (zei->dh, node_db, "attributeDetailsId",
1282                                   zdi->attributeDetails->sysno, zei->nmem);
1283     }
1284     data1_add_tagdata_int (zei->dh, node_zebra, "ordinalSU",
1285                               zei->ordinalSU, zei->nmem);
1286
1287     data1_add_tagdata_int (zei->dh, node_zebra, "runNumber",
1288                               zei->runNumber, zei->nmem);
1289
1290 #if ZINFO_DEBUG
1291     data1_pr_tree (zei->dh, zei->data1_target, stderr);
1292 #endif
1293     sgml_buf = data1_nodetoidsgml(zei->dh, zei->data1_target,
1294                                   0, &sgml_len);
1295     trec->info[recInfo_storeData] = xmalloc (sgml_len);
1296     memcpy (trec->info[recInfo_storeData], sgml_buf, sgml_len);
1297     trec->size[recInfo_storeData] = sgml_len;
1298     
1299     rec_put (zei->records, &trec);
1300 }
1301
1302 int zebraExplain_lookupSU (ZebraExplainInfo zei, int set, int use)
1303 {
1304     struct zebSUInfoB *zsui;
1305
1306     assert (zei->curDatabaseInfo);
1307     for (zsui = zei->curDatabaseInfo->attributeDetails->SUInfo;
1308          zsui; zsui=zsui->next)
1309         if (zsui->info.use == use && zsui->info.set == set)
1310             return zsui->info.ordinal;
1311     return -1;
1312 }
1313
1314 zebAccessObject zebraExplain_announceOid (ZebraExplainInfo zei,
1315                                           zebAccessObject *op,
1316                                           Odr_oid *oid)
1317 {
1318     zebAccessObject ao;
1319     
1320     for (ao = *op; ao; ao = ao->next)
1321         if (!oid_oidcmp (oid, ao->oid))
1322             break;
1323     if (!ao)
1324     {
1325         ao = nmem_malloc (zei->nmem, sizeof(*ao));
1326         ao->handle = NULL;
1327         ao->sysno = 0;
1328         ao->oid = odr_oiddup_nmem (zei->nmem, oid);
1329         ao->next = *op;
1330         *op = ao;
1331     }
1332     return ao;
1333 }
1334
1335 void zebraExplain_addAttributeSet (ZebraExplainInfo zei, int set)
1336 {
1337     oident oe;
1338     int oid[OID_SIZE];
1339
1340     oe.proto = PROTO_Z3950;
1341     oe.oclass = CLASS_ATTSET;
1342     oe.value = set;
1343
1344     if (oid_ent_to_oid (&oe, oid))
1345     {
1346         zebraExplain_announceOid (zei, &zei->accessInfo->attributeSetIds, oid);
1347         zebraExplain_announceOid (zei, &zei->curDatabaseInfo->
1348                                   accessInfo->attributeSetIds, oid);
1349     }
1350 }
1351
1352 int zebraExplain_addSU (ZebraExplainInfo zei, int set, int use)
1353 {
1354     struct zebSUInfoB *zsui;
1355
1356     assert (zei->curDatabaseInfo);
1357     for (zsui = zei->curDatabaseInfo->attributeDetails->SUInfo;
1358          zsui; zsui=zsui->next)
1359         if (zsui->info.use == use && zsui->info.set == set)
1360             return -1;
1361     zebraExplain_addAttributeSet (zei, set);
1362     zsui = nmem_malloc (zei->nmem, sizeof(*zsui));
1363     zsui->next = zei->curDatabaseInfo->attributeDetails->SUInfo;
1364     zei->curDatabaseInfo->attributeDetails->SUInfo = zsui;
1365     zei->curDatabaseInfo->attributeDetails->dirty = 1;
1366     zei->dirty = 1;
1367     zsui->info.set = set;
1368     zsui->info.use = use;
1369     zsui->info.ordinal = (zei->ordinalSU)++;
1370     return zsui->info.ordinal;
1371 }
1372
1373 void zebraExplain_addSchema (ZebraExplainInfo zei, Odr_oid *oid)
1374 {
1375     zebraExplain_announceOid (zei, &zei->accessInfo->schemas, oid);
1376     zebraExplain_announceOid (zei, &zei->curDatabaseInfo->
1377                               accessInfo->schemas, oid);
1378 }
1379
1380 void zebraExplain_recordBytesIncrement (ZebraExplainInfo zei, int adjust_num)
1381 {
1382     assert (zei->curDatabaseInfo);
1383
1384     if (adjust_num)
1385     {
1386         zei->curDatabaseInfo->recordBytes += adjust_num;
1387         zei->curDatabaseInfo->dirty = 1;
1388     }
1389 }
1390
1391 void zebraExplain_recordCountIncrement (ZebraExplainInfo zei, int adjust_num)
1392 {
1393     assert (zei->curDatabaseInfo);
1394
1395     if (adjust_num)
1396     {
1397         zei->curDatabaseInfo->recordCount += adjust_num;
1398         zei->curDatabaseInfo->dirty = 1;
1399     }
1400 }
1401
1402 int zebraExplain_runNumberIncrement (ZebraExplainInfo zei, int adjust_num)
1403 {
1404     if (adjust_num)
1405         zei->dirty = 1;
1406     return zei->runNumber += adjust_num;
1407 }
1408
1409 RecordAttr *rec_init_attr (ZebraExplainInfo zei, Record rec)
1410 {
1411     RecordAttr *recordAttr;
1412
1413     if (rec->info[recInfo_attr])
1414         return (RecordAttr *) rec->info[recInfo_attr];
1415     recordAttr = xmalloc (sizeof(*recordAttr));
1416     rec->info[recInfo_attr] = (char *) recordAttr;
1417     rec->size[recInfo_attr] = sizeof(*recordAttr);
1418     
1419     recordAttr->recordSize = 0;
1420     recordAttr->recordOffset = 0;
1421     recordAttr->runNumber = zei->runNumber;
1422     return recordAttr;
1423 }
1424
1425 static void att_loadset(void *p, const char *n, const char *name)
1426 {
1427     data1_handle dh = p;
1428     if (!data1_get_attset (dh, name))
1429         logf (LOG_WARN, "Couldn't load attribute set %s", name);
1430 }
1431
1432 void zebraExplain_loadAttsets (data1_handle dh, Res res)
1433 {
1434     res_trav(res, "attset", dh, att_loadset);
1435 }
1436
1437 /*
1438      zebraExplain_addSU adds to AttributeDetails for a database and
1439      adds attributeSet (in AccessInfo area) to DatabaseInfo if it doesn't
1440      exist for the database.
1441
1442      If the database doesn't exist globally (in TargetInfo) an 
1443      AttributeSetInfo must be added (globally).
1444  */