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