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