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