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