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