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