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