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