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