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