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