Added remote record import using Z39.50 extended services and Segment
[idzebra-moved-to-github.git] / index / zebraapi.c
1 /*
2  * Copyright (C) 1995-2000, Index Data
3  * All rights reserved.
4  *
5  * $Log: zebraapi.c,v $
6  * Revision 1.29  2000-03-20 19:08:36  adam
7  * Added remote record import using Z39.50 extended services and Segment
8  * Requests.
9  *
10  * Revision 1.28  2000/03/15 15:00:30  adam
11  * First work on threaded version.
12  *
13  * Revision 1.27  2000/02/24 12:31:17  adam
14  * Added zebra_string_norm.
15  *
16  * Revision 1.26  1999/11/30 13:48:03  adam
17  * Improved installation. Updated for inclusion of YAZ header files.
18  *
19  * Revision 1.25  1999/11/04 15:00:45  adam
20  * Implemented delete result set(s).
21  *
22  * Revision 1.24  1999/10/14 14:33:50  adam
23  * Added truncation 5=106.
24  *
25  * Revision 1.23  1999/09/07 11:36:32  adam
26  * Minor changes.
27  *
28  * Revision 1.22  1999/08/02 10:13:47  adam
29  * Fixed bug regarding zebra_hits.
30  *
31  * Revision 1.21  1999/07/14 10:59:26  adam
32  * Changed functions isc_getmethod, isams_getmethod.
33  * Improved fatal error handling (such as missing EXPLAIN schema).
34  *
35  * Revision 1.20  1999/07/06 12:28:04  adam
36  * Updated record index structure. Format includes version ID. Compression
37  * algorithm ID is stored for each record block.
38  *
39  * Revision 1.19  1999/05/26 07:49:13  adam
40  * C++ compilation.
41  *
42  * Revision 1.18  1999/05/15 14:36:38  adam
43  * Updated dictionary. Implemented "compression" of dictionary.
44  *
45  * Revision 1.17  1999/05/12 13:08:06  adam
46  * First version of ISAMS.
47  *
48  * Revision 1.16  1999/02/19 10:38:30  adam
49  * Implemented chdir-setting.
50  *
51  * Revision 1.15  1999/02/17 12:18:12  adam
52  * Fixed zebra_close so that a NULL pointer is ignored.
53  *
54  * Revision 1.14  1999/02/02 14:51:11  adam
55  * Updated WIN32 code specific sections. Changed header.
56  *
57  * Revision 1.13  1998/12/16 12:23:30  adam
58  * Added facility for database name mapping using resource mapdb.
59  *
60  * Revision 1.12  1998/11/16 10:18:10  adam
61  * Better error reporting for result sets.
62  *
63  * Revision 1.11  1998/10/16 08:14:34  adam
64  * Updated record control system.
65  *
66  * Revision 1.10  1998/09/22 10:03:42  adam
67  * Changed result sets to be persistent in the sense that they can
68  * be re-searched if needed.
69  * Fixed memory leak in rsm_or.
70  *
71  * Revision 1.9  1998/09/02 13:53:17  adam
72  * Extra parameter decode added to search routines to implement
73  * persistent queries.
74  *
75  * Revision 1.8  1998/08/24 17:29:23  adam
76  * Minor changes.
77  *
78  * Revision 1.7  1998/06/24 12:16:13  adam
79  * Support for relations on text operands. Open range support in
80  * DFA module (i.e. [-j], [g-]).
81  *
82  * Revision 1.6  1998/06/22 11:36:47  adam
83  * Added authentication check facility to zebra.
84  *
85  * Revision 1.5  1998/06/13 00:14:08  adam
86  * Minor changes.
87  *
88  * Revision 1.4  1998/06/12 12:22:12  adam
89  * Work on Zebra API.
90  *
91  * Revision 1.3  1998/05/27 16:57:44  adam
92  * Zebra returns surrogate diagnostic for single records when
93  * appropriate.
94  *
95  * Revision 1.2  1998/05/20 10:12:19  adam
96  * Implemented automatic EXPLAIN database maintenance.
97  * Modified Zebra to work with ASN.1 compiled version of YAZ.
98  *
99  * Revision 1.1  1998/03/05 08:45:13  adam
100  * New result set model and modular ranking system. Moved towards
101  * descent server API. System information stored as "SGML" records.
102  *
103  */
104
105 #include <assert.h>
106 #include <stdio.h>
107 #ifdef WIN32
108 #include <io.h>
109 #include <process.h>
110 #include <direct.h>
111 #else
112 #include <unistd.h>
113 #endif
114
115 #include <yaz/diagbib1.h>
116 #include "zserver.h"
117 #include <charmap.h>
118
119 static void zebra_chdir (ZebraService zh)
120 {
121     const char *dir = res_get (zh->res, "chdir");
122     if (!dir)
123         return;
124     logf (LOG_DEBUG, "chdir %s", dir);
125 #ifdef WIN32
126     _chdir(dir);
127 #else
128     chdir (dir);
129 #endif
130 }
131
132 static int extract_rec_in_mem (ZebraHandle zh, const char *recordType,
133                                const char *buf, size_t buf_size,
134                                const char *databaseName, int delete_flag,
135                                int test_mode, int *sysno,
136                                int store_keys, int store_data,
137                                const char *match_criteria);
138
139 static int explain_extract (void *handle, Record rec, data1_node *n);
140 static void extract_index (ZebraHandle zh);
141
142 static void zebra_register_unlock (ZebraHandle zh);
143
144 static int zebra_register_lock (ZebraHandle zh)
145 {
146     if (!zh->service->active)
147     {
148         zh->errCode = 1019;
149         return 1;
150     }
151     return 0;
152 }
153
154 static void zebra_register_unlock (ZebraHandle zh)
155 {
156 }
157
158 ZebraHandle zebra_open (ZebraService zs)
159 {
160     ZebraHandle zh;
161
162     assert (zs);
163     if (zs->stop_flag)
164     {
165         zh->errCode = 1019;
166         return 0;
167     }
168
169     zh = (ZebraHandle) xmalloc (sizeof(*zh));
170
171     zh->service = zs;
172     zh->sets = 0;
173     zh->destroyed = 0;
174     zh->errCode = 0;
175     zh->errString = 0;
176
177     zh->key_buf = 0;
178     
179     zebra_mutex_cond_lock (&zs->session_lock);
180
181     zh->next = zs->sessions;
182     zs->sessions = zh;
183
184     zebra_mutex_cond_unlock (&zs->session_lock);
185     return zh;
186 }
187
188 static int zebra_register_activate (ZebraService zh);
189 static int zebra_register_deactivate (ZebraService zh);
190
191 ZebraService zebra_start (const char *configName)
192 {
193     ZebraService zh = xmalloc (sizeof(*zh));
194
195     yaz_log (LOG_LOG, "zebra_start %s", configName);
196
197     zh->configName = xstrdup(configName);
198     zh->sessions = 0;
199     zh->stop_flag = 0;
200     zh->active = 0;
201     zebra_mutex_cond_init (&zh->session_lock);
202     zebra_register_activate (zh);
203     return zh;
204 }
205
206 static int zebra_register_activate (ZebraService zh)
207 {
208     if (zh->active)
209         return 0;
210     yaz_log (LOG_LOG, "zebra_register_activate");
211     if (!(zh->res = res_open (zh->configName)))
212     {
213         logf (LOG_WARN, "Failed to read resources `%s'", zh->configName);
214         return -1;
215     }
216     zebra_chdir (zh);
217     zh->dh = data1_create ();
218     if (!zh->dh)
219         return -1;
220     zh->bfs = bfs_create (res_get (zh->res, "register"));
221     if (!zh->bfs)
222     {
223         data1_destroy(zh->dh);
224         return -1;
225     }
226     bf_lockDir (zh->bfs, res_get (zh->res, "lockDir"));
227     data1_set_tabpath (zh->dh, res_get(zh->res, "profilePath"));
228     zh->registerState = -1;  /* trigger open of registers! */
229     zh->registerChange = 0;
230     zh->recTypes = recTypes_init (zh->dh);
231     recTypes_default_handlers (zh->recTypes);
232
233     zh->records = NULL;
234     zh->zebra_maps = zebra_maps_open (zh->res);
235     zh->rank_classes = NULL;
236
237     zh->records = 0;
238     zh->dict = 0;
239     zh->sortIdx = 0;
240     zh->isams = 0;
241     zh->isam = 0;
242     zh->isamc = 0;
243     zh->isamd = 0;
244     zh->zei = 0;
245     
246     zebraRankInstall (zh, rank1_class);
247
248     if (!res_get (zh->res, "passwd"))
249         zh->passwd_db = NULL;
250     else
251     {
252         zh->passwd_db = passwd_db_open ();
253         if (!zh->passwd_db)
254             logf (LOG_WARN|LOG_ERRNO, "passwd_db_open failed");
255         else
256             passwd_db_file (zh->passwd_db, res_get (zh->res, "passwd"));
257     }
258
259     if (!(zh->records = rec_open (zh->bfs, 1, 0)))
260     {
261         logf (LOG_WARN, "rec_open");
262         return -1;
263     }
264     if (!(zh->dict = dict_open (zh->bfs, FNAME_DICT, 40, 1, 0)))
265     {
266         logf (LOG_WARN, "dict_open");
267         return -1;
268     }
269     if (!(zh->sortIdx = sortIdx_open (zh->bfs, 0)))
270     {
271         logf (LOG_WARN, "sortIdx_open");
272         return -1;
273     }
274     if (res_get_match (zh->res, "isam", "s", ISAM_DEFAULT))
275     {
276         struct ISAMS_M_s isams_m;
277         if (!(zh->isams = isams_open (zh->bfs, FNAME_ISAMS, 1,
278                                       key_isams_m(zh->res, &isams_m))))
279         {
280             logf (LOG_WARN, "isams_open");
281             return -1;
282         }
283     }
284 #if ZMBOL
285     else if (res_get_match (zh->res, "isam", "i", ISAM_DEFAULT))
286     {
287         if (!(zh->isam = is_open (zh->bfs, FNAME_ISAM, key_compare, 1,
288                                   sizeof (struct it_key), zh->res)))
289         {
290             logf (LOG_WARN, "is_open");
291             return -1;
292         }
293     }
294     else if (res_get_match (zh->res, "isam", "c", ISAM_DEFAULT))
295     {
296         struct ISAMC_M_s isamc_m;
297         if (!(zh->isamc = isc_open (zh->bfs, FNAME_ISAMC,
298                                     1, key_isamc_m(zh->res, &isamc_m))))
299         {
300             logf (LOG_WARN, "isc_open");
301             return -1;
302         }
303     }
304     else if (res_get_match (zh->res, "isam", "d", ISAM_DEFAULT))
305     {
306         struct ISAMD_M_s isamd_m;
307         
308         if (!(zh->isamd = isamd_open (zh->bfs, FNAME_ISAMD,
309                                       1, key_isamd_m(zh->res, &isamd_m))))
310         {
311             logf (LOG_WARN, "isamd_open");
312             return -1;
313         }
314     }
315 #endif
316     zh->zei = zebraExplain_open (zh->records, zh->dh,
317                                  zh->res, 1, 0 /* rGroup */,
318                                  explain_extract);
319     if (!zh->zei)
320     {
321         logf (LOG_WARN, "Cannot obtain EXPLAIN information");
322         return -1;
323     }
324     zh->active = 1;
325     yaz_log (LOG_LOG, "zebra_register_activate ok");
326     return 0;
327 }
328
329 void zebra_admin_shutdown (ZebraHandle zh)
330 {
331     zebraExplain_flush (zh->service->zei, 1, zh);
332     extract_index (zh);
333
334     zebra_mutex_cond_lock (&zh->service->session_lock);
335     zh->service->stop_flag = 1;
336     if (!zh->service->sessions)
337         zebra_register_deactivate(zh->service);
338     zebra_mutex_cond_unlock (&zh->service->session_lock);
339 }
340
341 void zebra_admin_start (ZebraHandle zh)
342 {
343     ZebraService zs = zh->service;
344     zh->errCode = 0;
345     zebra_mutex_cond_lock (&zs->session_lock);
346     if (!zs->stop_flag)
347         zebra_register_activate(zs);
348     zebra_mutex_cond_unlock (&zs->session_lock);
349 }
350
351 static int zebra_register_deactivate (ZebraService zh)
352 {
353     zh->stop_flag = 0;
354     if (!zh->active)
355         return 0;
356     yaz_log(LOG_LOG, "zebra_register_deactivate");
357     zebra_chdir (zh);
358     if (zh->records)
359     {
360         zebraExplain_close (zh->zei, 1);
361         dict_close (zh->dict);
362         sortIdx_close (zh->sortIdx);
363         if (zh->isams)
364             isams_close (zh->isams);
365 #if ZMBOL
366         if (zh->isam)
367             is_close (zh->isam);
368         if (zh->isamc)
369             isc_close (zh->isamc);
370         if (zh->isamd)
371             isamd_close (zh->isamd);
372 #endif
373         rec_close (&zh->records);
374     }
375     recTypes_destroy (zh->recTypes);
376     zebra_maps_close (zh->zebra_maps);
377     zebraRankDestroy (zh);
378     bfs_destroy (zh->bfs);
379     data1_destroy (zh->dh);
380
381     if (zh->passwd_db)
382         passwd_db_close (zh->passwd_db);
383     res_close (zh->res);
384     zh->active = 0;
385     return 0;
386 }
387
388 void zebra_stop(ZebraService zh)
389 {
390     if (!zh)
391         return ;
392     yaz_log (LOG_LOG, "zebra_stop");
393
394     assert (!zh->sessions);
395
396     zebra_mutex_cond_destroy (&zh->session_lock);
397
398     zebra_register_deactivate(zh);
399     xfree (zh->configName);
400     xfree (zh);
401 }
402
403 void zebra_close (ZebraHandle zh)
404 {
405     ZebraService zs = zh->service;
406     struct zebra_session **sp;
407     if (!zh)
408         return ;
409     resultSetDestroy (zh, -1, 0, 0);
410
411     if (zh->key_buf)
412     {
413         xfree (zh->key_buf);
414         zh->key_buf = 0;
415     }
416     zebra_mutex_cond_lock (&zs->session_lock);
417     sp = &zs->sessions;
418     while (1)
419     {
420         assert (*sp);
421         if (*sp == zh)
422         {
423             *sp = (*sp)->next;
424             break;
425         }
426         sp = &(*sp)->next;
427     }
428     if (!zs->sessions && zs->stop_flag)
429         zebra_register_deactivate(zs);
430     zebra_mutex_cond_unlock (&zs->session_lock);
431     xfree (zh);
432 }
433
434 struct map_baseinfo {
435     ZebraHandle zh;
436     NMEM mem;
437     int num_bases;
438     char **basenames;
439     int new_num_bases;
440     char **new_basenames;
441     int new_num_max;
442 };
443         
444 void map_basenames_func (void *vp, const char *name, const char *value)
445 {
446     struct map_baseinfo *p = (struct map_baseinfo *) vp;
447     int i, no;
448     char fromdb[128], todb[8][128];
449     
450     no =
451         sscanf (value, "%127s %127s %127s %127s %127s %127s %127s %127s %127s",
452                 fromdb, todb[0], todb[1], todb[2], todb[3], todb[4],
453                 todb[5], todb[6], todb[7]);
454     if (no < 2)
455         return ;
456     no--;
457     for (i = 0; i<p->num_bases; i++)
458         if (p->basenames[i] && !strcmp (p->basenames[i], fromdb))
459         {
460             p->basenames[i] = 0;
461             for (i = 0; i < no; i++)
462             {
463                 if (p->new_num_bases == p->new_num_max)
464                     return;
465                 p->new_basenames[(p->new_num_bases)++] = 
466                     nmem_strdup (p->mem, todb[i]);
467             }
468             return;
469         }
470 }
471
472 void map_basenames (ZebraHandle zh, ODR stream,
473                     int *num_bases, char ***basenames)
474 {
475     struct map_baseinfo info;
476     struct map_baseinfo *p = &info;
477     int i;
478
479     info.zh = zh;
480     info.num_bases = *num_bases;
481     info.basenames = *basenames;
482     info.new_num_max = 128;
483     info.new_num_bases = 0;
484     info.new_basenames = (char **)
485         odr_malloc (stream, sizeof(*info.new_basenames) * info.new_num_max);
486     info.mem = stream->mem;
487
488     res_trav (zh->service->res, "mapdb", &info, map_basenames_func);
489     
490     for (i = 0; i<p->num_bases; i++)
491         if (p->basenames[i] && p->new_num_bases < p->new_num_max)
492         {
493             p->new_basenames[(p->new_num_bases)++] = 
494                 nmem_strdup (p->mem, p->basenames[i]);
495         }
496     *num_bases = info.new_num_bases;
497     *basenames = info.new_basenames;
498     for (i = 0; i<*num_bases; i++)
499         logf (LOG_LOG, "base %s", (*basenames)[i]);
500 }
501
502 void zebra_search_rpn (ZebraHandle zh, ODR stream, ODR decode,
503                        Z_RPNQuery *query, int num_bases, char **basenames, 
504                        const char *setname)
505 {
506     zh->hits = 0;
507     if (zebra_register_lock (zh))
508         return;
509     map_basenames (zh, stream, &num_bases, &basenames);
510     resultSetAddRPN (zh, stream, decode, query, num_bases, basenames, setname);
511
512     zebra_register_unlock (zh);
513 }
514
515 void zebra_records_retrieve (ZebraHandle zh, ODR stream,
516                              const char *setname, Z_RecordComposition *comp,
517                              oid_value input_format, int num_recs,
518                              ZebraRetrievalRecord *recs)
519 {
520     ZebraPosSet poset;
521     int i, *pos_array;
522
523     if (zebra_register_lock (zh))
524         return;
525     zh->errCode = 0;
526     pos_array = (int *) xmalloc (num_recs * sizeof(*pos_array));
527     for (i = 0; i<num_recs; i++)
528         pos_array[i] = recs[i].position;
529     poset = zebraPosSetCreate (zh, setname, num_recs, pos_array);
530     if (!poset)
531     {
532         logf (LOG_DEBUG, "zebraPosSetCreate error");
533         zh->errCode = 30;
534         zh->errString = nmem_strdup (stream->mem, setname);
535     }
536     else
537     {
538         for (i = 0; i<num_recs; i++)
539         {
540             if (!poset[i].sysno)
541             {
542                 char num_str[20];
543
544                 sprintf (num_str, "%d", pos_array[i]);  
545                 zh->errCode = 13;
546                 zh->errString = nmem_strdup (stream->mem, num_str);
547                 break;
548             }
549             else
550             {
551                 recs[i].errCode =
552                     zebra_record_fetch (zh, poset[i].sysno, poset[i].score,
553                                         stream, input_format, comp,
554                                         &recs[i].format, &recs[i].buf,
555                                         &recs[i].len,
556                                         &recs[i].base);
557                 recs[i].errString = NULL;
558             }
559         }
560         zebraPosSetDestroy (zh, poset, num_recs);
561     }
562     zebra_register_unlock (zh);
563     xfree (pos_array);
564 }
565
566 void zebra_scan (ZebraHandle zh, ODR stream, Z_AttributesPlusTerm *zapt,
567                  oid_value attributeset,
568                  int num_bases, char **basenames,
569                  int *position, int *num_entries, ZebraScanEntry **entries,
570                  int *is_partial)
571 {
572     if (zebra_register_lock (zh))
573     {
574         *entries = 0;
575         *num_entries = 0;
576         return;
577     }
578     map_basenames (zh, stream, &num_bases, &basenames);
579     rpn_scan (zh, stream, zapt, attributeset,
580               num_bases, basenames, position,
581               num_entries, entries, is_partial);
582     zebra_register_unlock (zh);
583 }
584
585 void zebra_sort (ZebraHandle zh, ODR stream,
586                  int num_input_setnames, const char **input_setnames,
587                  const char *output_setname, Z_SortKeySpecList *sort_sequence,
588                  int *sort_status)
589 {
590     if (zebra_register_lock (zh))
591         return;
592     resultSetSort (zh, stream->mem, num_input_setnames, input_setnames,
593                    output_setname, sort_sequence, sort_status);
594     zebra_register_unlock (zh);
595 }
596
597 int zebra_deleleResultSet(ZebraHandle zh, int function,
598                           int num_setnames, char **setnames,
599                           int *statuses)
600 {
601     int i, status;
602     if (zebra_register_lock (zh))
603         return Z_DeleteStatus_systemProblemAtTarget;
604     switch (function)
605     {
606     case Z_DeleteRequest_list:
607         resultSetDestroy (zh, num_setnames, setnames, statuses);
608         break;
609     case Z_DeleteRequest_all:
610         resultSetDestroy (zh, -1, 0, statuses);
611         break;
612     }
613     zebra_register_unlock (zh);
614     status = Z_DeleteStatus_success;
615     for (i = 0; i<num_setnames; i++)
616         if (statuses[i] == Z_DeleteStatus_resultSetDidNotExist)
617             status = statuses[i];
618     return status;
619 }
620
621 int zebra_errCode (ZebraHandle zh)
622 {
623     return zh->errCode;
624 }
625
626 const char *zebra_errString (ZebraHandle zh)
627 {
628     return diagbib1_str (zh->errCode);
629 }
630
631 char *zebra_errAdd (ZebraHandle zh)
632 {
633     return zh->errString;
634 }
635
636 int zebra_hits (ZebraHandle zh)
637 {
638     return zh->hits;
639 }
640
641 int zebra_auth (ZebraService zh, const char *user, const char *pass)
642 {
643     if (!zh->passwd_db || !passwd_db_auth (zh->passwd_db, user, pass))
644         return 0;
645     return 1;
646 }
647
648 void zebra_admin_import_begin (ZebraHandle zh, const char *database)
649 {
650     if (zebra_register_lock (zh))
651         return;
652     zebra_register_unlock(zh);
653 }
654
655 void zebra_admin_import_segment (ZebraHandle zh, Z_Segment *segment)
656 {
657     if (zebra_register_lock (zh))
658         return;
659     if (segment->num_segmentRecords == 0)
660     {
661         zebraExplain_flush (zh->service->zei, 1, zh);
662         extract_index (zh);
663     }
664     else
665     {
666         int sysno;
667         int i;
668         for (i = 0; i<segment->num_segmentRecords; i++)
669         {
670             Z_NamePlusRecord *npr = segment->segmentRecords[i];
671             printf ("--------------%d--------------------\n", i);
672             if (npr->which == Z_NamePlusRecord_intermediateFragment)
673             {
674                 Z_FragmentSyntax *fragment = npr->u.intermediateFragment;
675                 if (fragment->which == Z_FragmentSyntax_notExternallyTagged)
676                 {
677                     Odr_oct *oct = fragment->u.notExternallyTagged;
678                     printf ("%.*s", (oct->len > 100 ? 100 : oct->len) ,
679                             oct->buf);
680                     
681                     sysno = 0;
682                     extract_rec_in_mem (zh, "grs.sgml",
683                                         oct->buf, oct->len,
684                                         "Default", 0 /* delete_flag */,
685                                         0 /* test_mode */,
686                                         &sysno /* sysno */,
687                                         1 /* store_keys */,
688                                         1 /* store_data */,
689                                         0 /* match criteria */);
690                 }
691             }
692         }
693     }
694     zebra_register_unlock(zh);
695 }
696
697 void zebra_admin_create (ZebraHandle zh, const char *database)
698 {
699     ZebraService zs = zh->service;
700     if (zebra_register_lock(zh))
701     {
702         zh->errCode = 1019;
703         return;
704     }
705     /* announce database */
706     if (zebraExplain_newDatabase (zs->zei, database, 0 /* explainDatabase */))
707     {
708         zh->errCode = 224;
709         zh->errString = "Database already exist";
710     }
711     zebra_register_unlock(zh);
712 }
713
714 int zebra_string_norm (ZebraHandle zh, unsigned reg_id,
715                        const char *input_str, int input_len,
716                        char *output_str, int output_len)
717 {
718     WRBUF wrbuf;
719     if (!zh->service->zebra_maps)
720         return -1;
721     wrbuf = zebra_replace(zh->service->zebra_maps, reg_id, "",
722                           input_str, input_len);
723     if (!wrbuf)
724         return -2;
725     if (wrbuf_len(wrbuf) >= output_len)
726         return -3;
727     if (wrbuf_len(wrbuf))
728         memcpy (output_str, wrbuf_buf(wrbuf), wrbuf_len(wrbuf));
729     output_str[wrbuf_len(wrbuf)] = '\0';
730     return wrbuf_len(wrbuf);
731 }
732
733 static void extract_init (struct recExtractCtrl *p, RecWord *w)
734 {
735     w->zebra_maps = p->zebra_maps;
736     w->seqnos = p->seqno;
737     w->attrSet = VAL_BIB1;
738     w->attrUse = 1016;
739     w->reg_type = 'w';
740     w->extractCtrl = p;
741 }
742
743 static void extract_add_index_string (RecWord *p, const char *string,
744                                       int length)
745 {
746     char *dst;
747     unsigned char attrSet;
748     unsigned short attrUse;
749     int lead = 0;
750     int diff = 0;
751     int *pseqno = &p->seqnos[p->reg_type];
752     ZebraHandle zh = p->extractCtrl->handle;
753     struct recKeys *keys = &zh->keys;
754
755     if (keys->buf_used+1024 > keys->buf_max)
756     {
757         char *b;
758
759         b = (char *) xmalloc (keys->buf_max += 128000);
760         if (keys->buf_used > 0)
761             memcpy (b, keys->buf, keys->buf_used);
762         xfree (keys->buf);
763         keys->buf = b;
764     }
765     dst = keys->buf + keys->buf_used;
766
767     attrSet = p->attrSet;
768     if (keys->buf_used > 0 && keys->prevAttrSet == attrSet)
769         lead |= 1;
770     else
771         keys->prevAttrSet = attrSet;
772     attrUse = p->attrUse;
773     if (keys->buf_used > 0 && keys->prevAttrUse == attrUse)
774         lead |= 2;
775     else
776         keys->prevAttrUse = attrUse;
777 #if 1
778     diff = 1 + *pseqno - keys->prevSeqNo;
779     if (diff >= 1 && diff <= 15)
780         lead |= (diff << 2);
781     else
782         diff = 0;
783 #endif
784     keys->prevSeqNo = *pseqno;
785     
786     *dst++ = lead;
787
788     if (!(lead & 1))
789     {
790         memcpy (dst, &attrSet, sizeof(attrSet));
791         dst += sizeof(attrSet);
792     }
793     if (!(lead & 2))
794     {
795         memcpy (dst, &attrUse, sizeof(attrUse));
796         dst += sizeof(attrUse);
797     }
798     *dst++ = p->reg_type;
799     memcpy (dst, string, length);
800     dst += length;
801     *dst++ = '\0';
802
803     if (!diff)
804     {
805         memcpy (dst, pseqno, sizeof(*pseqno));
806         dst += sizeof(*pseqno);
807     }
808     keys->buf_used = dst - keys->buf;
809     if (*pseqno)
810         (*pseqno)++;
811 }
812
813 static void extract_add_sort_string (RecWord *p, const char *string,
814                                      int length)
815 {
816     struct sortKey *sk;
817     ZebraHandle zh = p->extractCtrl->handle;
818     struct sortKey *sortKeys = zh->sortKeys;
819
820     for (sk = sortKeys; sk; sk = sk->next)
821         if (sk->attrSet == p->attrSet && sk->attrUse == p->attrUse)
822             return;
823
824     sk = (struct sortKey *) xmalloc (sizeof(*sk));
825     sk->next = sortKeys;
826     sortKeys = sk;
827
828     sk->string = (char *) xmalloc (length);
829     sk->length = length;
830     memcpy (sk->string, string, length);
831
832     sk->attrSet = p->attrSet;
833     sk->attrUse = p->attrUse;
834 }
835
836 static void extract_add_string (RecWord *p, const char *string, int length)
837 {
838     assert (length > 0);
839     if (zebra_maps_is_sort (p->zebra_maps, p->reg_type))
840         extract_add_sort_string (p, string, length);
841     else
842         extract_add_index_string (p, string, length);
843 }
844
845 static void extract_add_incomplete_field (RecWord *p)
846 {
847     const char *b = p->string;
848     int remain = p->length;
849     const char **map = 0;
850
851     if (remain > 0)
852         map = zebra_maps_input(p->zebra_maps, p->reg_type, &b, remain);
853
854     while (map)
855     {
856         char buf[IT_MAX_WORD+1];
857         int i, remain;
858
859         /* Skip spaces */
860         while (map && *map && **map == *CHR_SPACE)
861         {
862             remain = p->length - (b - p->string);
863             if (remain > 0)
864                 map = zebra_maps_input(p->zebra_maps, p->reg_type, &b, remain);
865             else
866                 map = 0;
867         }
868         if (!map)
869             break;
870         i = 0;
871         while (map && *map && **map != *CHR_SPACE)
872         {
873             const char *cp = *map;
874
875             while (i < IT_MAX_WORD && *cp)
876                 buf[i++] = *(cp++);
877             remain = p->length - (b - p->string);
878             if (remain > 0)
879                 map = zebra_maps_input(p->zebra_maps, p->reg_type, &b, remain);
880             else
881                 map = 0;
882         }
883         if (!i)
884             return;
885         extract_add_string (p, buf, i);
886     }
887     (p->seqnos[p->reg_type])++; /* to separate this from next one  */
888 }
889
890 static void extract_add_complete_field (RecWord *p)
891 {
892     const char *b = p->string;
893     char buf[IT_MAX_WORD+1];
894     const char **map = 0;
895     int i = 0, remain = p->length;
896
897     if (remain > 0)
898         map = zebra_maps_input (p->zebra_maps, p->reg_type, &b, remain);
899
900     while (remain > 0 && i < IT_MAX_WORD)
901     {
902         while (map && *map && **map == *CHR_SPACE)
903         {
904             remain = p->length - (b - p->string);
905             if (remain > 0)
906                 map = zebra_maps_input(p->zebra_maps, p->reg_type, &b, remain);
907             else
908                 map = 0;
909         }
910         if (!map)
911             break;
912
913         if (i && i < IT_MAX_WORD)
914             buf[i++] = *CHR_SPACE;
915         while (map && *map && **map != *CHR_SPACE)
916         {
917             const char *cp = *map;
918
919             if (i >= IT_MAX_WORD)
920                 break;
921             while (i < IT_MAX_WORD && *cp)
922                 buf[i++] = *(cp++);
923             remain = p->length  - (b - p->string);
924             if (remain > 0)
925                 map = zebra_maps_input (p->zebra_maps, p->reg_type, &b,
926                                         remain);
927             else
928                 map = 0;
929         }
930     }
931     if (!i)
932         return;
933     extract_add_string (p, buf, i);
934 }
935
936 static void extract_token_add (RecWord *p)
937 {
938     WRBUF wrbuf;
939     if ((wrbuf = zebra_replace(p->zebra_maps, p->reg_type, 0,
940                                p->string, p->length)))
941     {
942         p->string = wrbuf_buf(wrbuf);
943         p->length = wrbuf_len(wrbuf);
944     }
945     if (zebra_maps_is_complete (p->zebra_maps, p->reg_type))
946         extract_add_complete_field (p);
947     else
948         extract_add_incomplete_field(p);
949 }
950
951 static void extract_schema_add (struct recExtractCtrl *p, Odr_oid *oid)
952 {
953     ZebraHandle zh = (ZebraHandle) (p->handle);
954     zebraExplain_addSchema (zh->service->zei, oid);
955 }
956
957 static void extract_flushSortKeys (ZebraHandle zh, SYSNO sysno,
958                                    int cmd, struct sortKey **skp)
959 {
960     struct sortKey *sk = *skp;
961     SortIdx sortIdx = zh->service->sortIdx;
962
963     sortIdx_sysno (sortIdx, sysno);
964     while (sk)
965     {
966         struct sortKey *sk_next = sk->next;
967         sortIdx_type (sortIdx, sk->attrUse);
968         sortIdx_add (sortIdx, sk->string, sk->length);
969         xfree (sk->string);
970         xfree (sk);
971         sk = sk_next;
972     }
973     *skp = 0;
974 }
975
976 struct encode_info {
977     int  sysno;
978     int  seqno;
979     int  cmd;
980     char buf[768];
981 };
982
983 void encode_key_init (struct encode_info *i)
984 {
985     i->sysno = 0;
986     i->seqno = 0;
987     i->cmd = -1;
988 }
989
990 char *encode_key_int (int d, char *bp)
991 {
992     if (d <= 63)
993         *bp++ = d;
994     else if (d <= 16383)
995     {
996         *bp++ = 64 + (d>>8);
997         *bp++ = d  & 255;
998     }
999     else if (d <= 4194303)
1000     {
1001         *bp++ = 128 + (d>>16);
1002         *bp++ = (d>>8) & 255;
1003         *bp++ = d & 255;
1004     }
1005     else
1006     {
1007         *bp++ = 192 + (d>>24);
1008         *bp++ = (d>>16) & 255;
1009         *bp++ = (d>>8) & 255;
1010         *bp++ = d & 255;
1011     }
1012     return bp;
1013 }
1014
1015 void encode_key_write (char *k, struct encode_info *i, FILE *outf)
1016 {
1017     struct it_key key;
1018     char *bp = i->buf;
1019
1020     while ((*bp++ = *k++))
1021         ;
1022     memcpy (&key, k+1, sizeof(struct it_key));
1023     bp = encode_key_int ( (key.sysno - i->sysno) * 2 + *k, bp);
1024     if (i->sysno != key.sysno)
1025     {
1026         i->sysno = key.sysno;
1027         i->seqno = 0;
1028     }
1029     else if (!i->seqno && !key.seqno && i->cmd == *k)
1030         return;
1031     bp = encode_key_int (key.seqno - i->seqno, bp);
1032     i->seqno = key.seqno;
1033     i->cmd = *k;
1034     if (fwrite (i->buf, bp - i->buf, 1, outf) != 1)
1035     {
1036         logf (LOG_FATAL|LOG_ERRNO, "fwrite");
1037         exit (1);
1038     }
1039 }
1040
1041 static void extract_flushWriteKeys (ZebraHandle zh)
1042 {
1043     FILE *outf;
1044     char out_fname[200];
1045     char *prevcp, *cp;
1046     struct encode_info encode_info;
1047     int ptr_i = zh->ptr_i;
1048 #if SORT_EXTRA
1049     int i;
1050 #endif
1051     if (!zh->key_buf || ptr_i <= 0)
1052         return;
1053
1054     (zh->key_file_no)++;
1055     logf (LOG_LOG, "sorting section %d", (zh->key_file_no));
1056 #if !SORT_EXTRA
1057     qsort (zh->key_buf + zh->ptr_top - ptr_i, ptr_i, sizeof(char*),
1058             key_qsort_compare);
1059     extract_get_fname_tmp (zh, out_fname, zh->key_file_no);
1060
1061     if (!(outf = fopen (out_fname, "wb")))
1062     {
1063         logf (LOG_FATAL|LOG_ERRNO, "fopen %s", out_fname);
1064         exit (1);
1065     }
1066     logf (LOG_LOG, "writing section %d", zh->key_file_no);
1067     prevcp = cp = (zh->key_buf)[zh->ptr_top - ptr_i];
1068     
1069     encode_key_init (&encode_info);
1070     encode_key_write (cp, &encode_info, outf);
1071     
1072     while (--ptr_i > 0)
1073     {
1074         cp = (zh->key_buf)[zh->ptr_top - ptr_i];
1075         if (strcmp (cp, prevcp))
1076         {
1077             encode_key_init (&encode_info);
1078             encode_key_write (cp, &encode_info, outf);
1079             prevcp = cp;
1080         }
1081         else
1082             encode_key_write (cp + strlen(cp), &encode_info, outf);
1083     }
1084 #else
1085     qsort (key_buf + ptr_top-ptr_i, ptr_i, sizeof(char*), key_x_compare);
1086     extract_get_fname_tmp (out_fname, key_file_no);
1087
1088     if (!(outf = fopen (out_fname, "wb")))
1089     {
1090         logf (LOG_FATAL|LOG_ERRNO, "fopen %s", out_fname);
1091         exit (1);
1092     }
1093     logf (LOG_LOG, "writing section %d", key_file_no);
1094     i = ptr_i;
1095     prevcp =  key_buf[ptr_top-i];
1096     while (1)
1097         if (!--i || strcmp (prevcp, key_buf[ptr_top-i]))
1098         {
1099             key_y_len = strlen(prevcp)+1;
1100 #if 0
1101             logf (LOG_LOG, "key_y_len: %2d %02x %02x %s",
1102                       key_y_len, prevcp[0], prevcp[1], 2+prevcp);
1103 #endif
1104             qsort (key_buf + ptr_top-ptr_i, ptr_i - i,
1105                                    sizeof(char*), key_y_compare);
1106             cp = key_buf[ptr_top-ptr_i];
1107             --key_y_len;
1108             encode_key_init (&encode_info);
1109             encode_key_write (cp, &encode_info, outf);
1110             while (--ptr_i > i)
1111             {
1112                 cp = key_buf[ptr_top-ptr_i];
1113                 encode_key_write (cp+key_y_len, &encode_info, outf);
1114             }
1115             if (!i)
1116                 break;
1117             prevcp = key_buf[ptr_top-ptr_i];
1118         }
1119 #endif
1120     if (fclose (outf))
1121     {
1122         logf (LOG_FATAL|LOG_ERRNO, "fclose %s", out_fname);
1123         exit (1);
1124     }
1125     logf (LOG_LOG, "finished section %d", zh->key_file_no);
1126     zh->ptr_i = 0;
1127     zh->key_buf_used = 0;
1128 }
1129
1130 static void extract_flushRecordKeys (ZebraHandle zh, SYSNO sysno,
1131                                      int cmd, struct recKeys *reckeys)
1132 {
1133     unsigned char attrSet = (unsigned char) -1;
1134     unsigned short attrUse = (unsigned short) -1;
1135     int seqno = 0;
1136     int off = 0;
1137     ZebraExplainInfo zei = zh->service->zei;
1138
1139     if (!zh->key_buf)
1140     {
1141         int mem = 8*1024*1024;
1142         zh->key_buf = (char**) xmalloc (mem);
1143         zh->ptr_top = mem/sizeof(char*);
1144         zh->ptr_i = 0;
1145         zh->key_buf_used = 0;
1146         zh->key_file_no = 0;
1147     }
1148     zebraExplain_recordCountIncrement (zei, cmd ? 1 : -1);
1149     while (off < reckeys->buf_used)
1150     {
1151         const char *src = reckeys->buf + off;
1152         struct it_key key;
1153         int lead, ch;
1154     
1155         lead = *src++;
1156
1157         if (!(lead & 1))
1158         {
1159             memcpy (&attrSet, src, sizeof(attrSet));
1160             src += sizeof(attrSet);
1161         }
1162         if (!(lead & 2))
1163         {
1164             memcpy (&attrUse, src, sizeof(attrUse));
1165             src += sizeof(attrUse);
1166         }
1167         if (zh->key_buf_used + 1024 > (zh->ptr_top-zh->ptr_i)*sizeof(char*))
1168             extract_flushWriteKeys (zh);
1169         ++(zh->ptr_i);
1170         (zh->key_buf)[zh->ptr_top - zh->ptr_i] =
1171             (char*)zh->key_buf + zh->key_buf_used;
1172
1173         ch = zebraExplain_lookupSU (zei, attrSet, attrUse);
1174         if (ch < 0)
1175             ch = zebraExplain_addSU (zei, attrSet, attrUse);
1176         assert (ch > 0);
1177         zh->key_buf_used +=
1178             key_SU_code (ch,((char*)zh->key_buf) + zh->key_buf_used);
1179
1180         while (*src)
1181             ((char*)zh->key_buf) [(zh->key_buf_used)++] = *src++;
1182         src++;
1183         ((char*)(zh->key_buf))[(zh->key_buf_used)++] = '\0';
1184         ((char*)(zh->key_buf))[(zh->key_buf_used)++] = cmd;
1185
1186         if (lead & 60)
1187             seqno += ((lead>>2) & 15)-1;
1188         else
1189         {
1190             memcpy (&seqno, src, sizeof(seqno));
1191             src += sizeof(seqno);
1192         }
1193         key.seqno = seqno;
1194         key.sysno = sysno;
1195         memcpy ((char*)zh->key_buf + zh->key_buf_used, &key, sizeof(key));
1196         (zh->key_buf_used) += sizeof(key);
1197         off = src - reckeys->buf;
1198     }
1199     assert (off == reckeys->buf_used);
1200 }
1201
1202 static void extract_index (ZebraHandle zh)
1203 {
1204     extract_flushWriteKeys (zh);
1205     zebra_index_merge (zh);
1206 }
1207
1208 static int explain_extract (void *handle, Record rec, data1_node *n)
1209 {
1210     ZebraHandle zh = (ZebraHandle) handle;
1211     struct recExtractCtrl extractCtrl;
1212     int i;
1213
1214     if (zebraExplain_curDatabase (zh->service->zei,
1215                                   rec->info[recInfo_databaseName]))
1216     {
1217         abort();
1218         if (zebraExplain_newDatabase (zh->service->zei,
1219                                       rec->info[recInfo_databaseName], 0))
1220             abort ();
1221     }
1222
1223     zh->keys.buf_used = 0;
1224     zh->keys.prevAttrUse = -1;
1225     zh->keys.prevAttrSet = -1;
1226     zh->keys.prevSeqNo = 0;
1227     zh->sortKeys = 0;
1228     
1229     extractCtrl.init = extract_init;
1230     extractCtrl.tokenAdd = extract_token_add;
1231     extractCtrl.schemaAdd = extract_schema_add;
1232     extractCtrl.dh = zh->service->dh;
1233     for (i = 0; i<256; i++)
1234         extractCtrl.seqno[i] = 0;
1235     extractCtrl.zebra_maps = zh->service->zebra_maps;
1236     extractCtrl.flagShowRecords = 0;
1237     extractCtrl.handle = handle;
1238     
1239     grs_extract_tree(&extractCtrl, n);
1240
1241     logf (LOG_LOG, "flush explain record, sysno=%d", rec->sysno);
1242
1243     if (rec->size[recInfo_delKeys])
1244     {
1245         struct recKeys delkeys;
1246         struct sortKey *sortKeys = 0;
1247
1248         delkeys.buf_used = rec->size[recInfo_delKeys];
1249         delkeys.buf = rec->info[recInfo_delKeys];
1250         extract_flushSortKeys (zh, rec->sysno, 0, &sortKeys);
1251         extract_flushRecordKeys (zh, rec->sysno, 0, &delkeys);
1252     }
1253     extract_flushRecordKeys (zh, rec->sysno, 1, &zh->keys);
1254     extract_flushSortKeys (zh, rec->sysno, 1, &zh->sortKeys);
1255
1256     xfree (rec->info[recInfo_delKeys]);
1257     rec->size[recInfo_delKeys] = zh->keys.buf_used;
1258     rec->info[recInfo_delKeys] = zh->keys.buf;
1259     zh->keys.buf = NULL;
1260     zh->keys.buf_max = 0;
1261     return 0;
1262 }
1263
1264 static int extract_rec_in_mem (ZebraHandle zh, const char *recordType,
1265                                const char *buf, size_t buf_size,
1266                                const char *databaseName, int delete_flag,
1267                                int test_mode, int *sysno,
1268                                int store_keys, int store_data,
1269                                const char *match_criteria)
1270 {
1271     RecordAttr *recordAttr;
1272     struct recExtractCtrl extractCtrl;
1273     int i, r;
1274     RecType recType;
1275     char subType[1024];
1276     void *clientData;
1277     const char *fname = "<no file>";
1278     Record rec;
1279     long recordOffset = 0;
1280     struct zebra_fetch_control fc;
1281
1282     fc.fd = -1;
1283     fc.record_int_buf = buf;
1284     fc.record_int_len = buf_size;
1285     fc.record_int_pos = 0;
1286     fc.offset_end = 0;
1287     fc.record_offset = 0;
1288
1289     extractCtrl.offset = 0;
1290     extractCtrl.readf = zebra_record_int_read;
1291     extractCtrl.seekf = zebra_record_int_seek;
1292     extractCtrl.tellf = zebra_record_int_tell;
1293     extractCtrl.endf = zebra_record_int_end;
1294     extractCtrl.fh = &fc;
1295
1296     /* announce database */
1297     if (zebraExplain_curDatabase (zh->service->zei, databaseName))
1298     {
1299         if (zebraExplain_newDatabase (zh->service->zei, databaseName, 0))
1300             return 0;
1301     }
1302     if (!(recType =
1303           recType_byName (zh->service->recTypes, recordType, subType,
1304                           &clientData)))
1305     {
1306         logf (LOG_WARN, "No such record type: %s", recordType);
1307         return 0;
1308     }
1309
1310     zh->keys.buf_used = 0;
1311     zh->keys.prevAttrUse = -1;
1312     zh->keys.prevAttrSet = -1;
1313     zh->keys.prevSeqNo = 0;
1314     zh->sortKeys = 0;
1315
1316
1317     extractCtrl.subType = subType;
1318     extractCtrl.init = extract_init;
1319     extractCtrl.tokenAdd = extract_token_add;
1320     extractCtrl.schemaAdd = extract_schema_add;
1321     extractCtrl.dh = zh->service->dh;
1322     extractCtrl.handle = zh;
1323     extractCtrl.zebra_maps = zh->service->zebra_maps;
1324     extractCtrl.flagShowRecords = 0;
1325     for (i = 0; i<256; i++)
1326     {
1327         if (zebra_maps_is_positioned(zh->service->zebra_maps, i))
1328             extractCtrl.seqno[i] = 1;
1329         else
1330             extractCtrl.seqno[i] = 0;
1331     }
1332
1333     r = (*recType->extract)(clientData, &extractCtrl);
1334
1335     if (r == RECCTRL_EXTRACT_EOF)
1336         return 0;
1337     else if (r == RECCTRL_EXTRACT_ERROR)
1338     {
1339         /* error occured during extraction ... */
1340 #if 1
1341         yaz_log (LOG_WARN, "extract error");
1342 #else
1343         if (rGroup->flagRw &&
1344             records_processed < rGroup->fileVerboseLimit)
1345         {
1346             logf (LOG_WARN, "fail %s %s %ld", rGroup->recordType,
1347                   fname, (long) recordOffset);
1348         }
1349 #endif
1350         return 0;
1351     }
1352     if (zh->keys.buf_used == 0)
1353     {
1354         /* the extraction process returned no information - the record
1355            is probably empty - unless flagShowRecords is in use */
1356         if (test_mode)
1357             return 1;
1358         logf (LOG_WARN, "No keys generated for record");
1359         logf (LOG_WARN, " The file is probably empty");
1360         return 1;
1361     }
1362     /* match criteria */
1363
1364     if (! *sysno)
1365     {
1366         /* new record */
1367         if (delete_flag)
1368         {
1369             logf (LOG_LOG, "delete %s %s %ld", recordType,
1370                   fname, (long) recordOffset);
1371             logf (LOG_WARN, "cannot delete record above (seems new)");
1372             return 1;
1373         }
1374         logf (LOG_LOG, "add %s %s %ld", recordType, fname,
1375               (long) recordOffset);
1376         rec = rec_new (zh->service->records);
1377
1378         *sysno = rec->sysno;
1379
1380         recordAttr = rec_init_attr (zh->service->zei, rec);
1381
1382 #if 0
1383         if (matchStr)
1384         {
1385             dict_insert (matchDict, matchStr, sizeof(*sysno), sysno);
1386         }
1387 #endif
1388         extract_flushRecordKeys (zh, *sysno, 1, &zh->keys);
1389         extract_flushSortKeys (zh, *sysno, 1, &zh->sortKeys);
1390     }
1391     else
1392     {
1393         /* record already exists */
1394         struct recKeys delkeys;
1395
1396         rec = rec_get (zh->service->records, *sysno);
1397         assert (rec);
1398         
1399         recordAttr = rec_init_attr (zh->service->zei, rec);
1400
1401         if (recordAttr->runNumber ==
1402             zebraExplain_runNumberIncrement (zh->service->zei, 0))
1403         {
1404             logf (LOG_LOG, "skipped %s %s %ld", recordType,
1405                   fname, (long) recordOffset);
1406             rec_rm (&rec);
1407             return 1;
1408         }
1409         delkeys.buf_used = rec->size[recInfo_delKeys];
1410         delkeys.buf = rec->info[recInfo_delKeys];
1411         extract_flushSortKeys (zh, *sysno, 0, &zh->sortKeys);
1412         extract_flushRecordKeys (zh, *sysno, 0, &delkeys);
1413         if (delete_flag)
1414         {
1415             /* record going to be deleted */
1416             if (!delkeys.buf_used)
1417             {
1418                 logf (LOG_LOG, "delete %s %s %ld", recordType,
1419                       fname, (long) recordOffset);
1420                 logf (LOG_WARN, "cannot delete file above, storeKeys false");
1421             }
1422             else
1423             {
1424                 logf (LOG_LOG, "delete %s %s %ld", recordType,
1425                       fname, (long) recordOffset);
1426 #if 0
1427                 if (matchStr)
1428                     dict_delete (matchDict, matchStr);
1429 #endif
1430                 rec_del (zh->service->records, &rec);
1431             }
1432             rec_rm (&rec);
1433             return 1;
1434         }
1435         else
1436         {
1437             /* record going to be updated */
1438             if (!delkeys.buf_used)
1439             {
1440                 logf (LOG_LOG, "update %s %s %ld", recordType,
1441                       fname, (long) recordOffset);
1442                 logf (LOG_WARN, "cannot update file above, storeKeys false");
1443             }
1444             else
1445             {
1446                 logf (LOG_LOG, "update %s %s %ld", recordType,
1447                       fname, (long) recordOffset);
1448                 extract_flushRecordKeys (zh, *sysno, 1, &zh->keys);
1449             }
1450         }
1451     }
1452     /* update file type */
1453     xfree (rec->info[recInfo_fileType]);
1454     rec->info[recInfo_fileType] =
1455         rec_strdup (recordType, &rec->size[recInfo_fileType]);
1456
1457     /* update filename */
1458     xfree (rec->info[recInfo_filename]);
1459     rec->info[recInfo_filename] =
1460         rec_strdup (fname, &rec->size[recInfo_filename]);
1461
1462     /* update delete keys */
1463     xfree (rec->info[recInfo_delKeys]);
1464     if (zh->keys.buf_used > 0 && store_keys == 1)
1465     {
1466         rec->size[recInfo_delKeys] = zh->keys.buf_used;
1467         rec->info[recInfo_delKeys] = zh->keys.buf;
1468         zh->keys.buf = NULL;
1469         zh->keys.buf_max = 0;
1470     }
1471     else
1472     {
1473         rec->info[recInfo_delKeys] = NULL;
1474         rec->size[recInfo_delKeys] = 0;
1475     }
1476
1477     /* save file size of original record */
1478     zebraExplain_recordBytesIncrement (zh->service->zei,
1479                                        - recordAttr->recordSize);
1480 #if 0
1481     recordAttr->recordSize = fi->file_moffset - recordOffset;
1482     if (!recordAttr->recordSize)
1483         recordAttr->recordSize = fi->file_max - recordOffset;
1484 #else
1485     recordAttr->recordSize = buf_size;
1486 #endif
1487     zebraExplain_recordBytesIncrement (zh->service->zei,
1488                                        recordAttr->recordSize);
1489
1490     /* set run-number for this record */
1491     recordAttr->runNumber =
1492         zebraExplain_runNumberIncrement (zh->service->zei, 0);
1493
1494     /* update store data */
1495     xfree (rec->info[recInfo_storeData]);
1496     if (store_data == 1)
1497     {
1498         rec->size[recInfo_storeData] = recordAttr->recordSize;
1499         rec->info[recInfo_storeData] = (char *)
1500             xmalloc (recordAttr->recordSize);
1501 #if 1
1502         memcpy (rec->info[recInfo_storeData], buf, recordAttr->recordSize);
1503 #else
1504         if (lseek (fi->fd, recordOffset, SEEK_SET) < 0)
1505         {
1506             logf (LOG_ERRNO|LOG_FATAL, "seek to %ld in %s",
1507                   (long) recordOffset, fname);
1508             exit (1);
1509         }
1510         if (read (fi->fd, rec->info[recInfo_storeData], recordAttr->recordSize)
1511             < recordAttr->recordSize)
1512         {
1513             logf (LOG_ERRNO|LOG_FATAL, "read %d bytes of %s",
1514                   recordAttr->recordSize, fname);
1515             exit (1);
1516         }
1517 #endif
1518     }
1519     else
1520     {
1521         rec->info[recInfo_storeData] = NULL;
1522         rec->size[recInfo_storeData] = 0;
1523     }
1524     /* update database name */
1525     xfree (rec->info[recInfo_databaseName]);
1526     rec->info[recInfo_databaseName] =
1527         rec_strdup (databaseName, &rec->size[recInfo_databaseName]); 
1528
1529     /* update offset */
1530     recordAttr->recordOffset = recordOffset;
1531     
1532     /* commit this record */
1533     rec_put (zh->service->records, &rec);
1534
1535     return 0;
1536 }