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