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