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