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