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