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