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