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