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