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