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