4f5509b8ed379e241764f3957f461585eb41faac
[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.24 2002-02-20 23:07:54 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 (!zei->data1_target || !zei->data1_target->u.root.absyn)
439         {
440             logf (LOG_FATAL, "Explain schema missing. Check profilePath");
441             nmem_destroy (zei->nmem);
442             return 0;
443         }
444 #if ZINFO_DEBUG
445         data1_pr_tree (zei->dh, zei->data1_target, stderr);
446 #endif
447         node_tgtinfo = data1_search_tag (zei->dh, zei->data1_target->child,
448                                          "targetInfo");
449         zebraExplain_mergeAccessInfo (zei, node_tgtinfo,
450                                       &zei->accessInfo);
451
452         node_zebra = data1_search_tag (zei->dh, node_tgtinfo->child,
453                                        "zebraInfo");
454         np = 0;
455         if (node_zebra)
456         {
457             node_list = data1_search_tag (zei->dh, node_zebra->child,
458                                           "databaseList");
459             if (node_list)
460                 np = node_list->child;
461         }
462         for (; np; np = np->next)
463         {
464             data1_node *node_name = NULL;
465             data1_node *node_id = NULL;
466             data1_node *node_aid = NULL;
467             data1_node *np2;
468             if (np->which != DATA1N_tag || strcmp (np->u.tag.tag, "database"))
469                 continue;
470             for (np2 = np->child; np2; np2 = np2->next)
471             {
472                 if (np2->which != DATA1N_tag)
473                     continue;
474                 if (!strcmp (np2->u.tag.tag, "name"))
475                     node_name = np2->child;
476                 else if (!strcmp (np2->u.tag.tag, "id"))
477                     node_id = np2->child;
478                 else if (!strcmp (np2->u.tag.tag, "attributeDetailsId"))
479                     node_aid = np2->child;
480             }
481             assert (node_id && node_name && node_aid);
482             
483             *zdip = (struct zebDatabaseInfoB *) 
484                 nmem_malloc (zei->nmem, sizeof(**zdip));
485             (*zdip)->readFlag = 1;
486             (*zdip)->dirty = 0;
487             (*zdip)->data1_database = NULL;
488             (*zdip)->recordCount = 0;
489             (*zdip)->recordBytes = 0;
490             zebraExplain_mergeAccessInfo (zei, 0, &(*zdip)->accessInfo);
491
492             (*zdip)->databaseName = (char *)
493                 nmem_malloc (zei->nmem, 1+node_name->u.data.len);
494             memcpy ((*zdip)->databaseName, node_name->u.data.data,
495                     node_name->u.data.len);
496             (*zdip)->databaseName[node_name->u.data.len] = '\0';
497             (*zdip)->sysno = atoi_n (node_id->u.data.data,
498                                      node_id->u.data.len);
499             (*zdip)->attributeDetails = (zebAttributeDetails)
500                 nmem_malloc (zei->nmem, sizeof(*(*zdip)->attributeDetails));
501             (*zdip)->attributeDetails->sysno = atoi_n (node_aid->u.data.data,
502                                                        node_aid->u.data.len);
503             (*zdip)->attributeDetails->readFlag = 1;
504             (*zdip)->attributeDetails->dirty = 0;
505             (*zdip)->attributeDetails->SUInfo = NULL;
506
507             zdip = &(*zdip)->next;
508         }
509         if (node_zebra)
510         {
511             np = data1_search_tag (zei->dh, node_zebra->child,
512                                    "ordinalSU");
513             np = np->child;
514             assert (np && np->which == DATA1N_data);
515             zei->ordinalSU = atoi_n (np->u.data.data, np->u.data.len);
516             
517             np = data1_search_tag (zei->dh, node_zebra->child,
518                                    "runNumber");
519             np = np->child;
520             assert (np && np->which == DATA1N_data);
521             zei->runNumber = atoi_n (np->u.data.data, np->u.data.len);
522             *zdip = NULL;
523         }
524         rec_rm (&trec);
525     }
526     else  /* create initial targetInfo */
527     {
528         data1_node *node_tgtinfo;
529
530         *zdip = NULL;
531         if (writeFlag)
532         {
533             char *sgml_buf;
534             int sgml_len;
535
536             zei->data1_target =
537                 data1_read_sgml (zei->dh, zei->nmem,
538                                  "<explain><targetInfo>TargetInfo\n"
539                                  "<name>Zebra</>\n"
540                                  "<namedResultSets>1</>\n"
541                                  "<multipleDBSearch>1</>\n"
542                                  "<nicknames><name>Zebra</></>\n"
543                                  "</></>\n" );
544             if (!zei->data1_target || !zei->data1_target->u.root.absyn)
545             {
546                 logf (LOG_FATAL, "Explain schema missing. Check profilePath");
547                 nmem_destroy (zei->nmem);
548                 return 0;
549             }
550             node_tgtinfo = data1_search_tag (zei->dh, zei->data1_target->child,
551                                             "targetInfo");
552             assert (node_tgtinfo);
553
554             zebraExplain_initCommonInfo (zei, node_tgtinfo);
555             zebraExplain_initAccessInfo (zei, node_tgtinfo);
556
557             /* write now because we want to be sure about the sysno */
558             trec = rec_new (records);
559             trec->info[recInfo_fileType] =
560                 rec_strdup ("grs.sgml", &trec->size[recInfo_fileType]);
561             trec->info[recInfo_databaseName] =
562                 rec_strdup ("IR-Explain-1", &trec->size[recInfo_databaseName]);
563             
564             sgml_buf = data1_nodetoidsgml(dh, zei->data1_target, 0, &sgml_len);
565             trec->info[recInfo_storeData] = (char *) xmalloc (sgml_len);
566             memcpy (trec->info[recInfo_storeData], sgml_buf, sgml_len);
567             trec->size[recInfo_storeData] = sgml_len;
568             
569             rec_put (records, &trec);
570             rec_rm (&trec);
571
572         }
573         zebraExplain_newDatabase (zei, "IR-Explain-1", 0);
574             
575         if (!zei->categoryList->dirty)
576         {
577             struct zebraCategoryListInfo *zcl = zei->categoryList;
578             data1_node *node_cl;
579             
580             zcl->dirty = 1;
581             zcl->data1_categoryList =
582                 data1_read_sgml (zei->dh, zei->nmem,
583                                  "<explain><categoryList>CategoryList\n"
584                                  "</></>\n");
585         
586             if (zcl->data1_categoryList)
587             {
588                 assert (zcl->data1_categoryList->child);
589                 node_cl = data1_search_tag (zei->dh,
590                                             zcl->data1_categoryList->child,
591                                             "categoryList");
592                 assert (node_cl);
593                 zebraExplain_initCommonInfo (zei, node_cl);
594             }
595         }
596     }
597     return zei;
598 }
599
600 static void zebraExplain_readAttributeDetails (ZebraExplainInfo zei,
601                                                zebAttributeDetails zad)
602 {
603     Record rec;
604     struct zebSUInfoB **zsuip = &zad->SUInfo;
605     data1_node *node_adinfo, *node_zebra, *node_list, *np;
606
607     assert (zad->sysno);
608     rec = rec_get (zei->records, zad->sysno);
609
610     zad->data1_tree = read_sgml_rec (zei->dh, zei->nmem, rec);
611
612     node_adinfo = data1_search_tag (zei->dh, zad->data1_tree->child,
613                                     "attributeDetails");
614     node_zebra = data1_search_tag (zei->dh, node_adinfo->child,
615                                  "zebraInfo");
616     node_list = data1_search_tag (zei->dh, node_zebra->child,
617                                   "attrlist");
618     for (np = node_list->child; np; np = np->next)
619     {
620         data1_node *node_set = NULL;
621         data1_node *node_use = NULL;
622         data1_node *node_ordinal = NULL;
623         data1_node *np2;
624         char oid_str[128];
625         int oid_str_len;
626
627         if (np->which != DATA1N_tag || strcmp (np->u.tag.tag, "attr"))
628             continue;
629         for (np2 = np->child; np2; np2 = np2->next)
630         {
631             if (np2->which != DATA1N_tag || !np2->child ||
632                 np2->child->which != DATA1N_data)
633                 continue;
634             if (!strcmp (np2->u.tag.tag, "set"))
635                 node_set = np2->child;
636             else if (!strcmp (np2->u.tag.tag, "use"))
637                 node_use = np2->child;
638             else if (!strcmp (np2->u.tag.tag, "ordinal"))
639                 node_ordinal = np2->child;
640         }
641         assert (node_set && node_use && node_ordinal);
642
643         oid_str_len = node_set->u.data.len;
644         if (oid_str_len >= (int) sizeof(oid_str))
645             oid_str_len = sizeof(oid_str)-1;
646         memcpy (oid_str, node_set->u.data.data, oid_str_len);
647         oid_str[oid_str_len] = '\0';
648
649         *zsuip = (struct zebSUInfoB *)
650             nmem_malloc (zei->nmem, sizeof(**zsuip));
651         (*zsuip)->info.set = oid_getvalbyname (oid_str);
652
653         (*zsuip)->info.use = atoi_n (node_use->u.data.data,
654                                      node_use->u.data.len);
655         (*zsuip)->info.ordinal = atoi_n (node_ordinal->u.data.data,
656                                          node_ordinal->u.data.len);
657         logf (LOG_DEBUG, "set=%d use=%d ordinal=%d",
658               (*zsuip)->info.set, (*zsuip)->info.use, (*zsuip)->info.ordinal);
659         zsuip = &(*zsuip)->next;
660     }
661     *zsuip = NULL;
662     zad->readFlag = 0;
663     rec_rm (&rec);
664 }
665
666 static void zebraExplain_readDatabase (ZebraExplainInfo zei,
667                                        struct zebDatabaseInfoB *zdi)
668 {
669     Record rec;
670     data1_node *node_dbinfo, *node_zebra, *np;
671
672     assert (zdi->sysno);
673     rec = rec_get (zei->records, zdi->sysno);
674
675     zdi->data1_database = read_sgml_rec (zei->dh, zei->nmem, rec);
676     
677     node_dbinfo = data1_search_tag (zei->dh, zdi->data1_database->child,
678                                    "databaseInfo");
679     zebraExplain_mergeAccessInfo (zei, node_dbinfo, &zdi->accessInfo);
680
681     node_zebra = data1_search_tag (zei->dh, node_dbinfo->child,
682                                  "zebraInfo");
683     if (node_zebra
684         && (np = data1_search_tag (zei->dh, node_zebra->child,
685                                    "recordBytes")) 
686         && np->child && np->child->which == DATA1N_data)
687         zdi->recordBytes = atoi_n (np->child->u.data.data,
688                                    np->child->u.data.len);
689     if ((np = data1_search_tag (zei->dh, node_dbinfo->child,
690                                 "recordCount")) &&
691         (np = data1_search_tag (zei->dh, np->child,
692                                 "recordCountActual")) &&
693         np->child->which == DATA1N_data)
694     {
695         zdi->recordCount = atoi_n (np->child->u.data.data,
696                                    np->child->u.data.len);
697     }
698     zdi->readFlag = 0;
699     rec_rm (&rec);
700 }
701
702 int zebraExplain_curDatabase (ZebraExplainInfo zei, const char *database)
703 {
704     struct zebDatabaseInfoB *zdi;
705     
706     assert (zei);
707     if (zei->curDatabaseInfo &&
708         !strcmp (zei->curDatabaseInfo->databaseName, database))
709         return 0;
710     for (zdi = zei->databaseInfo; zdi; zdi=zdi->next)
711     {
712         if (!strcmp (zdi->databaseName, database))
713             break;
714     }
715     if (!zdi)
716         return -1;
717 #if ZINFO_DEBUG
718     logf (LOG_LOG, "zebraExplain_curDatabase: %s", database);
719 #endif
720     if (zdi->readFlag)
721     {
722 #if ZINFO_DEBUG
723         logf (LOG_LOG, "zebraExplain_readDatabase: %s", database);
724 #endif
725         zebraExplain_readDatabase (zei, zdi);
726     }
727     if (zdi->attributeDetails->readFlag)
728     {
729 #if ZINFO_DEBUG
730         logf (LOG_LOG, "zebraExplain_readAttributeDetails: %s", database);
731 #endif
732         zebraExplain_readAttributeDetails (zei, zdi->attributeDetails);
733     }
734     zei->curDatabaseInfo = zdi;
735     return 0;
736 }
737
738 static void zebraExplain_initCommonInfo (ZebraExplainInfo zei, data1_node *n)
739 {
740     data1_node *c = data1_add_tag (zei->dh, n, "commonInfo", zei->nmem);
741
742     data1_add_tagdata_text (zei->dh, c, "dateAdded", zei->date, zei->nmem);
743     data1_add_tagdata_text (zei->dh, c, "dateChanged", zei->date, zei->nmem);
744     data1_add_tagdata_text (zei->dh, c, "languageCode", "EN", zei->nmem);
745 }
746
747 static void zebraExplain_updateCommonInfo (ZebraExplainInfo zei, data1_node *n)
748 {
749     data1_node *c = data1_search_tag (zei->dh, n->child, "commonInfo");
750     assert (c);
751     data1_make_tagdata_text (zei->dh, c, "dateChanged", zei->date, zei->nmem);
752 }
753
754 static void zebraExplain_initAccessInfo (ZebraExplainInfo zei, data1_node *n)
755 {
756     data1_node *c = data1_add_tag (zei->dh, n, "accessInfo", zei->nmem);
757     data1_node *d = data1_add_tag (zei->dh, c, "unitSystems", zei->nmem);
758     data1_add_tagdata_text (zei->dh, d, "string", "ISO", zei->nmem);
759 }
760
761 static void zebraExplain_updateAccessInfo (ZebraExplainInfo zei, data1_node *n,
762                                            zebAccessInfo accessInfo)
763 {
764     data1_node *c = data1_search_tag (zei->dh, n->child, "accessInfo");
765     data1_node *d;
766     zebAccessObject p;
767     
768     assert (c);
769
770     if ((p = accessInfo->attributeSetIds))
771     {
772         d = data1_make_tag (zei->dh, c, "attributeSetIds", zei->nmem);
773         for (; p; p = p->next)
774             data1_add_tagdata_oid (zei->dh, d, "oid", p->oid, zei->nmem);
775     }
776     if ((p = accessInfo->schemas))
777     {
778         d = data1_make_tag (zei->dh, c, "schemas", zei->nmem);
779         for (; p; p = p->next)
780             data1_add_tagdata_oid (zei->dh, d, "oid", p->oid, zei->nmem);
781     }
782 }
783
784 int zebraExplain_newDatabase (ZebraExplainInfo zei, const char *database,
785                               int explain_database)
786 {
787     struct zebDatabaseInfoB *zdi;
788     data1_node *node_dbinfo, *node_adinfo;
789
790 #if ZINFO_DEBUG
791     logf (LOG_LOG, "zebraExplain_newDatabase: %s", database);
792 #endif
793     assert (zei);
794     for (zdi = zei->databaseInfo; zdi; zdi=zdi->next)
795     {
796         if (!strcmp (zdi->databaseName, database))
797             break;
798     }
799     if (zdi)
800         return -1;
801     /* it's new really. make it */
802     zdi = (struct zebDatabaseInfoB *) nmem_malloc (zei->nmem, sizeof(*zdi));
803     zdi->next = zei->databaseInfo;
804     zei->databaseInfo = zdi;
805     zdi->sysno = 0;
806     zdi->recordCount = 0;
807     zdi->recordBytes = 0;
808     zdi->readFlag = 0;
809     zdi->databaseName = nmem_strdup (zei->nmem, database);
810
811     zebraExplain_mergeAccessInfo (zei, 0, &zdi->accessInfo);
812     
813     assert (zei->dh);
814     assert (zei->nmem);
815
816     zdi->data1_database =
817         data1_read_sgml (zei->dh, zei->nmem, 
818                          "<explain><databaseInfo>DatabaseInfo\n"
819                          "</></>\n");
820     if (!zdi->data1_database)
821         return -2;
822     
823     node_dbinfo = data1_search_tag (zei->dh, zdi->data1_database->child,
824                                    "databaseInfo");
825     assert (node_dbinfo);
826
827     zebraExplain_initCommonInfo (zei, node_dbinfo);
828     zebraExplain_initAccessInfo (zei, node_dbinfo);
829
830     data1_add_tagdata_text (zei->dh, node_dbinfo, "name",
831                                database, zei->nmem);
832     
833     if (explain_database)
834         data1_add_tagdata_text (zei->dh, node_dbinfo, "explainDatabase",
835                                 "", zei->nmem);
836     
837     data1_add_tagdata_text (zei->dh, node_dbinfo, "userFee",
838                             "0", zei->nmem);
839     
840     data1_add_tagdata_text (zei->dh, node_dbinfo, "available",
841                             "1", zei->nmem);
842     
843 #if ZINFO_DEBUG
844     data1_pr_tree (zei->dh, zdi->data1_database, stderr);
845 #endif
846     zdi->dirty = 1;
847     zei->dirty = 1;
848     zei->curDatabaseInfo = zdi;
849
850     zdi->attributeDetails = (zebAttributeDetails)
851         nmem_malloc (zei->nmem, sizeof(*zdi->attributeDetails));
852     zdi->attributeDetails->readFlag = 0;
853     zdi->attributeDetails->sysno = 0;
854     zdi->attributeDetails->dirty = 1;
855     zdi->attributeDetails->SUInfo = NULL;
856     zdi->attributeDetails->data1_tree =
857         data1_read_sgml (zei->dh, zei->nmem,
858                          "<explain><attributeDetails>AttributeDetails\n"
859                          "</></>\n");
860
861     node_adinfo =
862         data1_search_tag (zei->dh, zdi->attributeDetails->data1_tree->child,
863                           "attributeDetails");
864     assert (node_adinfo);
865
866     zebraExplain_initCommonInfo (zei, node_adinfo);
867
868     return 0;
869 }
870
871 static void writeAttributeValueDetails (ZebraExplainInfo zei,
872                                   zebAttributeDetails zad,
873                                   data1_node *node_atvs, data1_attset *attset)
874
875 {
876     struct zebSUInfoB *zsui;
877     int set_ordinal = attset->reference;
878     data1_attset_child *c;
879
880     for (c = attset->children; c; c = c->next)
881         writeAttributeValueDetails (zei, zad, node_atvs, c->child);
882     for (zsui = zad->SUInfo; zsui; zsui = zsui->next)
883     {
884         data1_node *node_attvalue, *node_value;
885         if (set_ordinal != zsui->info.set)
886             continue;
887         node_attvalue = data1_add_tag (zei->dh, node_atvs, "attributeValue",
888                                        zei->nmem);
889         node_value = data1_add_tag (zei->dh, node_attvalue, "value",
890                                     zei->nmem);
891         data1_add_tagdata_int (zei->dh, node_value, "numeric",
892                                zsui->info.use, zei->nmem);
893     }
894 }
895
896 static void zebraExplain_writeCategoryList (ZebraExplainInfo zei,
897                                             struct zebraCategoryListInfo *zcl,
898                                             int key_flush)
899 {
900     char *sgml_buf;
901     int sgml_len;
902     int i;
903     Record drec;
904     data1_node *node_ci, *node_categoryList;
905     int sysno = 0;
906     static char *category[] = {
907         "CategoryList",
908         "TargetInfo",
909         "DatabaseInfo",
910         "AttributeDetails",
911         NULL
912     };
913
914     assert (zcl);
915     if (!zcl->dirty)
916         return ;
917     zcl->dirty = 1;
918     node_categoryList = zcl->data1_categoryList;
919
920 #if ZINFO_DEBUG
921     logf (LOG_LOG, "zebraExplain_writeCategoryList");
922 #endif
923
924     drec = createRecord (zei->records, &sysno);
925
926     node_ci = data1_search_tag (zei->dh, node_categoryList->child,
927                                 "categoryList");
928     assert (node_ci);
929     node_ci = data1_add_tag (zei->dh, node_ci, "categories", zei->nmem);
930     assert (node_ci);
931     
932     for (i = 0; category[i]; i++)
933     {
934         data1_node *node_cat = data1_add_tag (zei->dh, node_ci, 
935                                               "category", zei->nmem);
936
937         data1_add_tagdata_text (zei->dh, node_cat, "name",
938                                 category[i], zei->nmem);
939     }
940     /* extract *searchable* keys from it. We do this here, because
941        record count, etc. is affected */
942     if (key_flush)
943         (*zei->updateFunc)(zei->updateHandle, drec, node_categoryList);
944
945     /* convert to "SGML" and write it */
946 #if ZINFO_DEBUG
947     data1_pr_tree (zei->dh, node_categoryList, stderr);
948 #endif
949     sgml_buf = data1_nodetoidsgml(zei->dh, node_categoryList, 0, &sgml_len);
950     drec->info[recInfo_storeData] = (char *) xmalloc (sgml_len);
951     memcpy (drec->info[recInfo_storeData], sgml_buf, sgml_len);
952     drec->size[recInfo_storeData] = sgml_len;
953     
954     rec_put (zei->records, &drec);
955 }
956
957 static void zebraExplain_writeAttributeDetails (ZebraExplainInfo zei,
958                                                 zebAttributeDetails zad,
959                                                 const char *databaseName,
960                                                 int key_flush)
961 {
962     char *sgml_buf;
963     int sgml_len;
964     Record drec;
965     data1_node *node_adinfo, *node_list, *node_zebra, *node_attributesBySet;
966     struct zebSUInfoB *zsui;
967     int set_min;
968     
969     if (!zad->dirty)
970         return;
971     
972     zad->dirty = 0;
973 #if ZINFO_DEBUG
974     logf (LOG_LOG, "zebraExplain_writeAttributeDetails");    
975 #endif
976
977     drec = createRecord (zei->records, &zad->sysno);
978     assert (zad->data1_tree);
979     node_adinfo = data1_search_tag (zei->dh, zad->data1_tree->child,
980                                    "attributeDetails");
981     zebraExplain_updateCommonInfo (zei, node_adinfo);
982
983     data1_add_tagdata_text (zei->dh, node_adinfo, "name",
984                             databaseName, zei->nmem);
985
986     /* extract *searchable* keys from it. We do this here, because
987        record count, etc. is affected */
988     if (key_flush)
989         (*zei->updateFunc)(zei->updateHandle, drec, zad->data1_tree);
990
991     node_attributesBySet = data1_make_tag (zei->dh, node_adinfo,
992                                            "attributesBySet", zei->nmem);
993     set_min = -1;
994     while (1)
995     {
996         data1_node *node_asd;
997         data1_attset *attset;
998         int set_ordinal = -1;
999         for (zsui = zad->SUInfo; zsui; zsui = zsui->next)
1000         {
1001             if ((set_ordinal < 0 || set_ordinal > zsui->info.set)
1002                 && zsui->info.set > set_min)
1003                 set_ordinal = zsui->info.set;
1004         }
1005         if (set_ordinal < 0)
1006             break;
1007         set_min = set_ordinal;
1008         node_asd = data1_add_tag (zei->dh, node_attributesBySet,
1009                                   "attributeSetDetails", zei->nmem);
1010
1011         attset = data1_attset_search_id (zei->dh, set_ordinal);
1012         if (!attset)
1013         {
1014             zebraExplain_loadAttsets (zei->dh, zei->res);
1015             attset = data1_attset_search_id (zei->dh, set_ordinal);
1016         }
1017         if (attset)
1018         {
1019             int oid[OID_SIZE];
1020             oident oe;
1021             
1022             oe.proto = PROTO_Z3950;
1023             oe.oclass = CLASS_ATTSET;
1024             oe.value = (enum oid_value) set_ordinal;
1025             
1026             if (oid_ent_to_oid (&oe, oid))
1027             {
1028                 data1_node *node_abt, *node_atd, *node_atvs;
1029                 data1_add_tagdata_oid (zei->dh, node_asd, "oid",
1030                                        oid, zei->nmem);
1031                 
1032                 node_abt = data1_add_tag (zei->dh, node_asd,
1033                                           "attributesByType", zei->nmem);
1034                 node_atd = data1_add_tag (zei->dh, node_abt,
1035                                           "attributeTypeDetails", zei->nmem);
1036                 data1_add_tagdata_int (zei->dh, node_atd,
1037                                        "type", 1, zei->nmem);
1038                 node_atvs = data1_add_tag (zei->dh, node_atd, 
1039                                            "attributeValues", zei->nmem);
1040                 writeAttributeValueDetails (zei, zad, node_atvs, attset);
1041             }
1042         }
1043     }
1044     /* zebra info (private) */
1045     node_zebra = data1_make_tag (zei->dh, node_adinfo,
1046                                  "zebraInfo", zei->nmem);
1047     node_list = data1_make_tag (zei->dh, node_zebra,
1048                                  "attrlist", zei->nmem);
1049     for (zsui = zad->SUInfo; zsui; zsui = zsui->next)
1050     {
1051         struct oident oident;
1052         int oid[OID_SIZE];
1053         data1_node *node_attr;
1054         
1055         node_attr = data1_add_tag (zei->dh, node_list, "attr", zei->nmem);
1056         
1057         oident.proto = PROTO_Z3950;
1058         oident.oclass = CLASS_ATTSET;
1059         oident.value = (enum oid_value) zsui->info.set;
1060         oid_ent_to_oid (&oident, oid);
1061         
1062         data1_add_tagdata_text (zei->dh, node_attr, "set",
1063                                 oident.desc, zei->nmem);
1064         data1_add_tagdata_int (zei->dh, node_attr, "use",
1065                                zsui->info.use, zei->nmem);
1066         data1_add_tagdata_int (zei->dh, node_attr, "ordinal",
1067                                zsui->info.ordinal, zei->nmem);
1068     }
1069     /* convert to "SGML" and write it */
1070 #if ZINFO_DEBUG
1071     data1_pr_tree (zei->dh, zad->data1_tree, stderr);
1072 #endif
1073     sgml_buf = data1_nodetoidsgml(zei->dh, zad->data1_tree,
1074                                   0, &sgml_len);
1075     drec->info[recInfo_storeData] = (char *) xmalloc (sgml_len);
1076     memcpy (drec->info[recInfo_storeData], sgml_buf, sgml_len);
1077     drec->size[recInfo_storeData] = sgml_len;
1078     
1079     rec_put (zei->records, &drec);
1080 }
1081
1082 static void zebraExplain_writeDatabase (ZebraExplainInfo zei,
1083                                         struct zebDatabaseInfoB *zdi,
1084                                         int key_flush)
1085 {
1086     char *sgml_buf;
1087     int sgml_len;
1088     Record drec;
1089     data1_node *node_dbinfo, *node_count, *node_zebra;
1090     
1091     if (!zdi->dirty)
1092         return;
1093
1094     zdi->dirty = 0;
1095 #if ZINFO_DEBUG
1096     logf (LOG_LOG, "zebraExplain_writeDatabase %s", zdi->databaseName);
1097 #endif
1098     drec = createRecord (zei->records, &zdi->sysno);
1099     assert (zdi->data1_database);
1100     node_dbinfo = data1_search_tag (zei->dh, zdi->data1_database->child,
1101                                    "databaseInfo");
1102
1103     zebraExplain_updateCommonInfo (zei, node_dbinfo);
1104     zebraExplain_updateAccessInfo (zei, node_dbinfo, zdi->accessInfo);
1105
1106     /* extract *searchable* keys from it. We do this here, because
1107        record count, etc. is affected */
1108     if (key_flush)
1109         (*zei->updateFunc)(zei->updateHandle, drec, zdi->data1_database);
1110     /* record count */
1111     node_count = data1_make_tag (zei->dh, node_dbinfo,
1112                                  "recordCount", zei->nmem);
1113     data1_add_tagdata_int (zei->dh, node_count, "recordCountActual",
1114                               zdi->recordCount, zei->nmem);
1115
1116     /* zebra info (private) */
1117     node_zebra = data1_make_tag (zei->dh, node_dbinfo,
1118                                  "zebraInfo", zei->nmem);
1119     data1_add_tagdata_int (zei->dh, node_zebra,
1120                            "recordBytes", zdi->recordBytes, zei->nmem);
1121     /* convert to "SGML" and write it */
1122 #if ZINFO_DEBUG
1123     data1_pr_tree (zei->dh, zdi->data1_database, stderr);
1124 #endif
1125     sgml_buf = data1_nodetoidsgml(zei->dh, zdi->data1_database,
1126                                   0, &sgml_len);
1127     drec->info[recInfo_storeData] = (char *) xmalloc (sgml_len);
1128     memcpy (drec->info[recInfo_storeData], sgml_buf, sgml_len);
1129     drec->size[recInfo_storeData] = sgml_len;
1130     
1131     rec_put (zei->records, &drec);
1132 }
1133
1134 static void writeAttributeValues (ZebraExplainInfo zei,
1135                                   data1_node *node_values,
1136                                   data1_attset *attset)
1137 {
1138     data1_att *atts;
1139     data1_attset_child *c;
1140
1141     if (!attset)
1142         return;
1143
1144     for (c = attset->children; c; c = c->next)
1145         writeAttributeValues (zei, node_values, c->child);
1146     for (atts = attset->atts; atts; atts = atts->next)
1147     {
1148         data1_node *node_value;
1149         
1150         node_value = data1_add_tag (zei->dh, node_values, "attributeValue",
1151                                     zei->nmem);
1152         data1_add_tagdata_text (zei->dh, node_value, "name",
1153                                 atts->name, zei->nmem);
1154         node_value = data1_add_tag (zei->dh, node_value, "value", zei->nmem);
1155         data1_add_tagdata_int (zei->dh, node_value, "numeric",
1156                                atts->value, zei->nmem);
1157     }
1158 }
1159
1160
1161 static void zebraExplain_writeAttributeSet (ZebraExplainInfo zei,
1162                                             zebAccessObject o,
1163                                             int key_flush)
1164 {
1165     char *sgml_buf;
1166     int sgml_len;
1167     Record drec;
1168     data1_node *node_root, *node_attinfo, *node_attributes, *node_atttype;
1169     data1_node *node_values;
1170     struct oident *entp;
1171     struct data1_attset *attset = NULL;
1172     
1173     if ((entp = oid_getentbyoid (o->oid)))
1174         attset = data1_attset_search_id (zei->dh, entp->value);
1175             
1176 #if ZINFO_DEBUG
1177     logf (LOG_LOG, "zebraExplain_writeAttributeSet %s",
1178           attset ? attset->name : "<unknown>");    
1179 #endif
1180
1181     drec = createRecord (zei->records, &o->sysno);
1182     node_root =
1183         data1_read_sgml (zei->dh, zei->nmem,
1184                          "<explain><attributeSetInfo>AttributeSetInfo\n"
1185                          "</></>\n" );
1186
1187     node_attinfo = data1_search_tag (zei->dh, node_root->child,
1188                                    "attributeSetInfo");
1189
1190     zebraExplain_initCommonInfo (zei, node_attinfo);
1191     zebraExplain_updateCommonInfo (zei, node_attinfo);
1192
1193     data1_add_tagdata_oid (zei->dh, node_attinfo,
1194                             "oid", o->oid, zei->nmem);
1195     if (attset && attset->name)
1196         data1_add_tagdata_text (zei->dh, node_attinfo,
1197                                 "name", attset->name, zei->nmem);
1198     
1199     node_attributes = data1_make_tag (zei->dh, node_attinfo,
1200                                       "attributes", zei->nmem);
1201     node_atttype = data1_make_tag (zei->dh, node_attributes,
1202                                    "attributeType", zei->nmem);
1203     data1_add_tagdata_text (zei->dh, node_atttype,
1204                             "name", "Use", zei->nmem);
1205     data1_add_tagdata_text (zei->dh, node_atttype,
1206                             "description", "Use Attribute", zei->nmem);
1207     data1_add_tagdata_int (zei->dh, node_atttype,
1208                            "type", 1, zei->nmem);
1209     node_values = data1_add_tag (zei->dh, node_atttype,
1210                                  "attributeValues", zei->nmem);
1211     if (attset)
1212         writeAttributeValues (zei, node_values, attset);
1213
1214     /* extract *searchable* keys from it. We do this here, because
1215        record count, etc. is affected */
1216     if (key_flush)
1217         (*zei->updateFunc)(zei->updateHandle, drec, node_root);
1218     /* convert to "SGML" and write it */
1219 #if ZINFO_DEBUG
1220     data1_pr_tree (zei->dh, node_root, stderr);
1221 #endif
1222     sgml_buf = data1_nodetoidsgml(zei->dh, node_root, 0, &sgml_len);
1223     drec->info[recInfo_storeData] = (char *) xmalloc (sgml_len);
1224     memcpy (drec->info[recInfo_storeData], sgml_buf, sgml_len);
1225     drec->size[recInfo_storeData] = sgml_len;
1226     
1227     rec_put (zei->records, &drec);
1228 }
1229
1230 static void zebraExplain_writeTarget (ZebraExplainInfo zei, int key_flush)
1231 {
1232     struct zebDatabaseInfoB *zdi;
1233     data1_node *node_tgtinfo, *node_list, *node_zebra;
1234     Record trec;
1235     int sgml_len;
1236     char *sgml_buf;
1237
1238     if (!zei->dirty)
1239         return;
1240     zei->dirty = 0;
1241
1242     trec = rec_get (zei->records, 1);
1243     xfree (trec->info[recInfo_storeData]);
1244
1245     node_tgtinfo = data1_search_tag (zei->dh, zei->data1_target->child,
1246                                    "targetInfo");
1247     assert (node_tgtinfo);
1248
1249     zebraExplain_updateCommonInfo (zei, node_tgtinfo);
1250     zebraExplain_updateAccessInfo (zei, node_tgtinfo, zei->accessInfo);
1251
1252     /* convert to "SGML" and write it */
1253     if (key_flush)
1254         (*zei->updateFunc)(zei->updateHandle, trec, zei->data1_target);
1255
1256     node_zebra = data1_make_tag (zei->dh, node_tgtinfo,
1257                                  "zebraInfo", zei->nmem);
1258     data1_add_tagdata_text (zei->dh, node_zebra, "version",
1259                                ZEBRAVER, zei->nmem);
1260     node_list = data1_add_tag (zei->dh, node_zebra,
1261                                   "databaseList", zei->nmem);
1262     for (zdi = zei->databaseInfo; zdi; zdi = zdi->next)
1263     {
1264         data1_node *node_db;
1265         node_db = data1_add_tag (zei->dh, node_list,
1266                                     "database", zei->nmem);
1267         data1_add_tagdata_text (zei->dh, node_db, "name",
1268                                    zdi->databaseName, zei->nmem);
1269         data1_add_tagdata_int (zei->dh, node_db, "id",
1270                                   zdi->sysno, zei->nmem);
1271         data1_add_tagdata_int (zei->dh, node_db, "attributeDetailsId",
1272                                   zdi->attributeDetails->sysno, zei->nmem);
1273     }
1274     data1_add_tagdata_int (zei->dh, node_zebra, "ordinalSU",
1275                               zei->ordinalSU, zei->nmem);
1276
1277     data1_add_tagdata_int (zei->dh, node_zebra, "runNumber",
1278                               zei->runNumber, zei->nmem);
1279
1280 #if ZINFO_DEBUG
1281     data1_pr_tree (zei->dh, zei->data1_target, stderr);
1282 #endif
1283     sgml_buf = data1_nodetoidsgml(zei->dh, zei->data1_target,
1284                                   0, &sgml_len);
1285     trec->info[recInfo_storeData] = (char *) xmalloc (sgml_len);
1286     memcpy (trec->info[recInfo_storeData], sgml_buf, sgml_len);
1287     trec->size[recInfo_storeData] = sgml_len;
1288     
1289     rec_put (zei->records, &trec);
1290 }
1291
1292 int zebraExplain_lookupSU (ZebraExplainInfo zei, int set, int use)
1293 {
1294     struct zebSUInfoB *zsui;
1295
1296     assert (zei->curDatabaseInfo);
1297     for (zsui = zei->curDatabaseInfo->attributeDetails->SUInfo;
1298          zsui; zsui=zsui->next)
1299         if (zsui->info.use == use && zsui->info.set == set)
1300             return zsui->info.ordinal;
1301     return -1;
1302 }
1303
1304 int zebraExplain_lookup_ord (ZebraExplainInfo zei, int ord,
1305                              const char **db, int *set, int *use)
1306 {
1307     struct zebDatabaseInfoB *zdb;
1308     for (zdb = zei->databaseInfo; zdb; zdb = zdb->next)
1309     {
1310         struct zebSUInfoB *zsui = zdb->attributeDetails->SUInfo;
1311         for ( ;zsui; zsui = zsui->next)
1312             if (zsui->info.ordinal == ord)
1313             {
1314                 *db = zdb->databaseName;
1315                 *set = zsui->info.set;
1316                 *use = zsui->info.use;
1317                 return 0;
1318             }
1319     }
1320     return -1;
1321 }
1322
1323 zebAccessObject zebraExplain_announceOid (ZebraExplainInfo zei,
1324                                           zebAccessObject *op,
1325                                           Odr_oid *oid)
1326 {
1327     zebAccessObject ao;
1328     
1329     for (ao = *op; ao; ao = ao->next)
1330         if (!oid_oidcmp (oid, ao->oid))
1331             break;
1332     if (!ao)
1333     {
1334         ao = (zebAccessObject) nmem_malloc (zei->nmem, sizeof(*ao));
1335         ao->handle = NULL;
1336         ao->sysno = 0;
1337         ao->oid = odr_oiddup_nmem (zei->nmem, oid);
1338         ao->next = *op;
1339         *op = ao;
1340     }
1341     return ao;
1342 }
1343
1344 void zebraExplain_addAttributeSet (ZebraExplainInfo zei, int set)
1345 {
1346     oident oe;
1347     int oid[OID_SIZE];
1348
1349     oe.proto = PROTO_Z3950;
1350     oe.oclass = CLASS_ATTSET;
1351     oe.value = (enum oid_value) set;
1352
1353     if (oid_ent_to_oid (&oe, oid))
1354     {
1355         zebraExplain_announceOid (zei, &zei->accessInfo->attributeSetIds, oid);
1356         zebraExplain_announceOid (zei, &zei->curDatabaseInfo->
1357                                   accessInfo->attributeSetIds, oid);
1358     }
1359 }
1360
1361 int zebraExplain_addSU (ZebraExplainInfo zei, int set, int use)
1362 {
1363     struct zebSUInfoB *zsui;
1364
1365     assert (zei->curDatabaseInfo);
1366     for (zsui = zei->curDatabaseInfo->attributeDetails->SUInfo;
1367          zsui; zsui=zsui->next)
1368         if (zsui->info.use == use && zsui->info.set == set)
1369             return -1;
1370     zebraExplain_addAttributeSet (zei, set);
1371     zsui = (struct zebSUInfoB *) nmem_malloc (zei->nmem, sizeof(*zsui));
1372     zsui->next = zei->curDatabaseInfo->attributeDetails->SUInfo;
1373     zei->curDatabaseInfo->attributeDetails->SUInfo = zsui;
1374     zei->curDatabaseInfo->attributeDetails->dirty = 1;
1375     zei->dirty = 1;
1376     zsui->info.set = set;
1377     zsui->info.use = use;
1378     zsui->info.ordinal = (zei->ordinalSU)++;
1379     return zsui->info.ordinal;
1380 }
1381
1382 void zebraExplain_addSchema (ZebraExplainInfo zei, Odr_oid *oid)
1383 {
1384     zebraExplain_announceOid (zei, &zei->accessInfo->schemas, oid);
1385     zebraExplain_announceOid (zei, &zei->curDatabaseInfo->
1386                               accessInfo->schemas, oid);
1387 }
1388
1389 void zebraExplain_recordBytesIncrement (ZebraExplainInfo zei, int adjust_num)
1390 {
1391     assert (zei->curDatabaseInfo);
1392
1393     if (adjust_num)
1394     {
1395         zei->curDatabaseInfo->recordBytes += adjust_num;
1396         zei->curDatabaseInfo->dirty = 1;
1397     }
1398 }
1399
1400 void zebraExplain_recordCountIncrement (ZebraExplainInfo zei, int adjust_num)
1401 {
1402     assert (zei->curDatabaseInfo);
1403
1404     if (adjust_num)
1405     {
1406         zei->curDatabaseInfo->recordCount += adjust_num;
1407         zei->curDatabaseInfo->dirty = 1;
1408     }
1409 }
1410
1411 int zebraExplain_runNumberIncrement (ZebraExplainInfo zei, int adjust_num)
1412 {
1413     if (adjust_num)
1414         zei->dirty = 1;
1415     return zei->runNumber += adjust_num;
1416 }
1417
1418 RecordAttr *rec_init_attr (ZebraExplainInfo zei, Record rec)
1419 {
1420     RecordAttr *recordAttr;
1421
1422     if (rec->info[recInfo_attr])
1423         return (RecordAttr *) rec->info[recInfo_attr];
1424     recordAttr = (RecordAttr *) xmalloc (sizeof(*recordAttr));
1425     rec->info[recInfo_attr] = (char *) recordAttr;
1426     rec->size[recInfo_attr] = sizeof(*recordAttr);
1427     
1428     recordAttr->recordSize = 0;
1429     recordAttr->recordOffset = 0;
1430     recordAttr->runNumber = zei->runNumber;
1431     return recordAttr;
1432 }
1433
1434 static void att_loadset(void *p, const char *n, const char *name)
1435 {
1436     data1_handle dh = (data1_handle) p;
1437     if (!data1_get_attset (dh, name))
1438         logf (LOG_WARN, "Couldn't load attribute set %s", name);
1439 }
1440
1441 void zebraExplain_loadAttsets (data1_handle dh, Res res)
1442 {
1443     res_trav(res, "attset", dh, att_loadset);
1444 }
1445
1446 /*
1447      zebraExplain_addSU adds to AttributeDetails for a database and
1448      adds attributeSet (in AccessInfo area) to DatabaseInfo if it doesn't
1449      exist for the database.
1450
1451      If the database doesn't exist globally (in TargetInfo) an 
1452      AttributeSetInfo must be added (globally).
1453  */