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