Common stream reader interface for record filters (struct ZebraRecStream).
[idzebra-moved-to-github.git] / index / extract.c
1 /* $Id: extract.c,v 1.228 2006-08-22 13:39:27 adam Exp $
2    Copyright (C) 1995-2006
3    Index Data ApS
4
5 This file is part of the Zebra server.
6
7 Zebra is free software; you can redistribute it and/or modify it under
8 the terms of the GNU General Public License as published by the Free
9 Software Foundation; either version 2, or (at your option) any later
10 version.
11
12 Zebra is distributed in the hope that it will be useful, but WITHOUT ANY
13 WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
15 for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20
21 */
22
23 #include <stdio.h>
24 #include <assert.h>
25 #include <ctype.h>
26 #ifdef WIN32
27 #include <io.h>
28 #endif
29 #if HAVE_UNISTD_H
30 #include <unistd.h>
31 #endif
32 #include <fcntl.h>
33
34 #include "index.h"
35 #include "orddict.h"
36 #include <direntz.h>
37 #include <charmap.h>
38
39 #define ENCODE_BUFLEN 768
40 struct encode_info {
41     void *encode_handle;
42     void *decode_handle;
43     char buf[ENCODE_BUFLEN];
44 };
45
46 static int log_level = 0;
47 static int log_level_initialized = 1;
48
49 static void zebra_init_log_level()
50 {
51     if (!log_level_initialized)
52     {
53         log_level = yaz_log_module_level("extract");
54         log_level_initialized = 1;
55     }
56 }
57
58 static void extract_flushRecordKeys (ZebraHandle zh, SYSNO sysno,
59                                      int cmd, zebra_rec_keys_t reckeys,
60                                      zint staticrank);
61 static void extract_flushSortKeys (ZebraHandle zh, SYSNO sysno,
62                                    int cmd, zebra_rec_keys_t skp);
63 static void extract_schema_add (struct recExtractCtrl *p, Odr_oid *oid);
64 static void extract_token_add (RecWord *p);
65
66 static void encode_key_init (struct encode_info *i);
67 static void encode_key_write (char *k, struct encode_info *i, FILE *outf);
68 static void encode_key_flush (struct encode_info *i, FILE *outf);
69
70 #define USE_SHELLSORT 0
71
72 #if USE_SHELLSORT
73 static void shellsort(void *ar, int r, size_t s,
74                       int (*cmp)(const void *a, const void *b))
75 {
76     char *a = ar;
77     char v[100];
78     int h, i, j, k;
79     static const int incs[16] = { 1391376, 463792, 198768, 86961, 33936,
80                                   13776, 4592, 1968, 861, 336, 
81                                   112, 48, 21, 7, 3, 1 };
82     for ( k = 0; k < 16; k++)
83         for (h = incs[k], i = h; i < r; i++)
84         { 
85             memcpy (v, a+s*i, s);
86             j = i;
87             while (j > h && (*cmp)(a + s*(j-h), v) > 0)
88             {
89                 memcpy (a + s*j, a + s*(j-h), s);
90                 j -= h;
91             }
92             memcpy (a+s*j, v, s);
93         } 
94 }
95 #endif
96
97 static void logRecord (ZebraHandle zh)
98 {
99     ++zh->records_processed;
100     if (!(zh->records_processed % 1000))
101     {
102         yaz_log(YLOG_LOG, "Records: "ZINT_FORMAT" i/u/d "
103                 ZINT_FORMAT"/"ZINT_FORMAT"/"ZINT_FORMAT, 
104                 zh->records_processed, zh->records_inserted, 
105                 zh->records_updated, zh->records_deleted);
106     }
107 }
108
109 static void extract_add_index_string (RecWord *p, 
110                                       zinfo_index_category_t cat,
111                                       const char *str, int length);
112
113 static void extract_set_store_data_prepare(struct recExtractCtrl *p);
114
115 static void extract_init(struct recExtractCtrl *p, RecWord *w)
116 {
117     w->seqno = 1;
118     w->index_name = "any";
119     w->index_type = 'w';
120     w->extractCtrl = p;
121     w->record_id = 0;
122     w->section_id = 0;
123     w->segment = 0;
124 }
125
126 static void searchRecordKey(ZebraHandle zh,
127                             zebra_rec_keys_t reckeys,
128                             const char *index_name,
129                             const char **ws, int ws_length)
130 {
131     int i;
132     int ch = -1;
133     zinfo_index_category_t cat = zinfo_index_category_index;
134
135     for (i = 0; i<ws_length; i++)
136         ws[i] = NULL;
137
138     if (ch < 0)
139         ch = zebraExplain_lookup_attr_str(zh->reg->zei, cat, '0', index_name);
140     if (ch < 0)
141         ch = zebraExplain_lookup_attr_str(zh->reg->zei, cat, 'p', index_name);
142     if (ch < 0)
143         ch = zebraExplain_lookup_attr_str(zh->reg->zei, cat, 'w', index_name);
144
145     if (ch < 0)
146         return ;
147
148     if (zebra_rec_keys_rewind(reckeys))
149     {
150         zint startSeq = -1;
151         const char *str;
152         size_t slen;
153         struct it_key key;
154         zint seqno;
155         while (zebra_rec_keys_read(reckeys, &str, &slen, &key))
156         {
157             assert(key.len <= IT_KEY_LEVEL_MAX && key.len > 2);
158
159             seqno = key.mem[key.len-1];
160             
161             if (key.mem[0] == ch)
162             {
163                 zint woff;
164                 
165                 if (startSeq == -1)
166                     startSeq = seqno;
167                 woff = seqno - startSeq;
168                 if (woff >= 0 && woff < ws_length)
169                     ws[woff] = str;
170             }
171         }
172     }
173 }
174
175 #define FILE_MATCH_BLANK "\t "
176
177 static char *fileMatchStr (ZebraHandle zh,
178                            zebra_rec_keys_t reckeys,
179                            const char *fname, const char *spec)
180 {
181     static char dstBuf[2048];      /* static here ??? */
182     char *dst = dstBuf;
183     const char *s = spec;
184
185     while (1)
186     {
187         for (; *s && strchr(FILE_MATCH_BLANK, *s); s++)
188             ;
189         if (!*s)
190             break;
191         if (*s == '(')
192         {
193             const char *ws[32];
194             char attset_str[64], attname_str[64];
195             int i;
196             int first = 1;
197             
198             for (s++; strchr(FILE_MATCH_BLANK, *s); s++)
199                 ;
200             for (i = 0; *s && *s != ',' && *s != ')' && 
201                      !strchr(FILE_MATCH_BLANK, *s); s++)
202                 if (i+1 < sizeof(attset_str))
203                     attset_str[i++] = *s;
204             attset_str[i] = '\0';
205             
206             for (; strchr(FILE_MATCH_BLANK, *s); s++)
207                 ;
208             if (*s != ',')
209                 strcpy(attname_str, attset_str);
210             else
211             {
212                 for (s++; strchr(FILE_MATCH_BLANK, *s); s++)
213                     ;
214                 for (i = 0; *s && *s != ')' && 
215                          !strchr(FILE_MATCH_BLANK, *s); s++)
216                     if (i+1 < sizeof(attname_str))
217                         attname_str[i++] = *s;
218                 attname_str[i] = '\0';
219             }
220
221             searchRecordKey (zh, reckeys, attname_str, ws, 32);
222
223             if (*s != ')')
224             {
225                 yaz_log (YLOG_WARN, "Missing ) in match criteria %s in group %s",
226                       spec, zh->m_group ? zh->m_group : "none");
227                 return NULL;
228             }
229             s++;
230
231             for (i = 0; i<32; i++)
232                 if (ws[i])
233                 {
234                     if (first)
235                     {
236                         *dst++ = ' ';
237                         first = 0;
238                     }
239                     strcpy (dst, ws[i]);
240                     dst += strlen(ws[i]);
241                 }
242             if (first)
243             {
244                 yaz_log (YLOG_WARN, "Record didn't contain match"
245                       " fields in (%s,%s)", attset_str, attname_str);
246                 return NULL;
247             }
248         }
249         else if (*s == '$')
250         {
251             int spec_len;
252             char special[64];
253             const char *spec_src = NULL;
254             const char *s1 = ++s;
255             while (*s1 && !strchr(FILE_MATCH_BLANK, *s1))
256                 s1++;
257
258             spec_len = s1 - s;
259             if (spec_len > sizeof(special)-1)
260                 spec_len = sizeof(special)-1;
261             memcpy (special, s, spec_len);
262             special[spec_len] = '\0';
263             s = s1;
264
265             if (!strcmp (special, "group"))
266                 spec_src = zh->m_group;
267             else if (!strcmp (special, "database"))
268                 spec_src = zh->basenames[0];
269             else if (!strcmp (special, "filename")) {
270                 spec_src = fname;
271             }
272             else if (!strcmp (special, "type"))
273                 spec_src = zh->m_record_type;
274             else 
275                 spec_src = NULL;
276             if (spec_src)
277             {
278                 strcpy (dst, spec_src);
279                 dst += strlen (spec_src);
280             }
281         }
282         else if (*s == '\"' || *s == '\'')
283         {
284             int stopMarker = *s++;
285             char tmpString[64];
286             int i = 0;
287
288             while (*s && *s != stopMarker)
289             {
290                 if (i+1 < sizeof(tmpString))
291                     tmpString[i++] = *s++;
292             }
293             if (*s)
294                 s++;
295             tmpString[i] = '\0';
296             strcpy (dst, tmpString);
297             dst += strlen (tmpString);
298         }
299         else
300         {
301             yaz_log (YLOG_WARN, "Syntax error in match criteria %s in group %s",
302                   spec, zh->m_group ? zh->m_group : "none");
303             return NULL;
304         }
305         *dst++ = 1;
306     }
307     if (dst == dstBuf)
308     {
309         yaz_log (YLOG_WARN, "No match criteria for record %s in group %s",
310               fname, zh->m_group ? zh->m_group : "none");
311         return NULL;
312     }
313     *dst = '\0';
314     return dstBuf;
315 }
316
317 struct recordLogInfo {
318     const char *fname;
319     int recordOffset;
320     struct recordGroup *rGroup;
321 };
322
323 static void init_extractCtrl(ZebraHandle zh, struct recExtractCtrl *ctrl)
324 {
325     int i;
326     for (i = 0; i<256; i++)
327     {
328         if (zebra_maps_is_positioned(zh->reg->zebra_maps, i))
329             ctrl->seqno[i] = 1;
330         else
331             ctrl->seqno[i] = 0;
332     }
333     ctrl->flagShowRecords = !zh->m_flag_rw;
334 }
335
336 static void all_matches_add(struct recExtractCtrl *ctrl)
337 {
338     RecWord word;
339     extract_init(ctrl, &word);
340     word.index_name = "_ALLRECORDS";
341     word.index_type = 'w';
342     word.seqno = 1;
343     extract_add_index_string (&word, zinfo_index_category_alwaysmatches,
344                               "", 0);
345 }
346
347 ZEBRA_RES zebra_extract_file(ZebraHandle zh, SYSNO *sysno, const char *fname, 
348                              int deleteFlag)
349 {
350     ZEBRA_RES r = ZEBRA_OK;
351     int i, fd;
352     char gprefix[128];
353     char ext[128];
354     char ext_res[128];
355     struct file_read_info *fi = 0;
356     const char *original_record_type = 0;
357     RecType recType;
358     void *recTypeClientData;
359     struct ZebraRecStream stream, *streamp;
360
361     zebra_init_log_level();
362
363     if (!zh->m_group || !*zh->m_group)
364         *gprefix = '\0';
365     else
366         sprintf (gprefix, "%s.", zh->m_group);
367     
368     yaz_log(log_level, "zebra_extract_file %s", fname);
369
370     /* determine file extension */
371     *ext = '\0';
372     for (i = strlen(fname); --i >= 0; )
373         if (fname[i] == '/')
374             break;
375         else if (fname[i] == '.')
376         {
377             strcpy (ext, fname+i+1);
378             break;
379         }
380     /* determine file type - depending on extension */
381     original_record_type = zh->m_record_type;
382     if (!zh->m_record_type)
383     {
384         sprintf (ext_res, "%srecordType.%s", gprefix, ext);
385         zh->m_record_type = res_get (zh->res, ext_res);
386     }
387     if (!zh->m_record_type)
388     {
389         if (zh->records_processed < zh->m_file_verbose_limit)
390             yaz_log (YLOG_LOG, "? %s", fname);
391         return 0;
392     }
393     /* determine match criteria */
394     if (!zh->m_record_id)
395     {
396         sprintf (ext_res, "%srecordId.%s", gprefix, ext);
397         zh->m_record_id = res_get (zh->res, ext_res);
398     }
399
400     if (!(recType =
401           recType_byName (zh->reg->recTypes, zh->res, zh->m_record_type,
402                           &recTypeClientData)))
403     {
404         yaz_log(YLOG_WARN, "No such record type: %s", zh->m_record_type);
405         return ZEBRA_FAIL;
406     }
407
408     switch(recType->version)
409     {
410     case 0:
411         break;
412     default:
413         yaz_log(YLOG_WARN, "Bad filter version: %s", zh->m_record_type);
414     }
415     if (sysno && deleteFlag)
416     {
417         streamp = 0;
418         fi = 0;
419     }
420     else
421     {
422         char full_rep[1024];
423
424         if (zh->path_reg && !yaz_is_abspath (fname))
425         {
426             strcpy (full_rep, zh->path_reg);
427             strcat (full_rep, "/");
428             strcat (full_rep, fname);
429         }
430         else
431             strcpy (full_rep, fname);
432         
433         if ((fd = open (full_rep, O_BINARY|O_RDONLY)) == -1)
434         {
435             yaz_log (YLOG_WARN|YLOG_ERRNO, "open %s", full_rep);
436             zh->m_record_type = original_record_type;
437             return ZEBRA_FAIL;
438         }
439         streamp = &stream;
440         zebra_create_stream_fd(streamp, fd, 0);
441     }
442     while(1)
443     {
444         r = zebra_extract_record_stream(zh, streamp,
445                                         deleteFlag,
446                                         0, /* tst_mode */
447                                         zh->m_record_type,
448                                         sysno,
449                                         0, /*match_criteria */
450                                         fname,
451                                         1, /* force_update */
452                                         1, /* allow_update */
453                                         recType, recTypeClientData);
454         if (r != ZEBRA_OK)
455         {
456             break;
457         }
458         if (sysno)
459         {
460             break;
461         }
462     }
463     if (streamp)
464         stream.destroy(streamp);
465     zh->m_record_type = original_record_type;
466     return r;
467 }
468
469 /*
470   If sysno is provided, then it's used to identify the reocord.
471   If not, and match_criteria is provided, then sysno is guessed
472   If not, and a record is provided, then sysno is got from there
473   
474  */
475
476 ZEBRA_RES zebra_buffer_extract_record(ZebraHandle zh, 
477                                       const char *buf, size_t buf_size,
478                                       int delete_flag,
479                                       int test_mode, 
480                                       const char *recordType,
481                                       SYSNO *sysno,
482                                       const char *match_criteria,
483                                       const char *fname,
484                                       int force_update,
485                                       int allow_update)
486 {
487     struct ZebraRecStream stream;
488     ZEBRA_RES res;
489     void *clientData;
490     RecType recType = 0;
491
492     if (recordType && *recordType)
493     {
494         yaz_log(log_level, "Record type explicitly specified: %s", recordType);
495         recType = recType_byName (zh->reg->recTypes, zh->res, recordType,
496                                   &clientData);
497     } 
498     else
499     {
500         if (!(zh->m_record_type))
501         {
502             yaz_log (YLOG_WARN, "No such record type defined");
503             return ZEBRA_FAIL;
504         }
505         yaz_log(log_level, "Get record type from rgroup: %s",
506                 zh->m_record_type);
507         recType = recType_byName (zh->reg->recTypes, zh->res,
508                                   zh->m_record_type, &clientData);
509         recordType = zh->m_record_type;
510     }
511     
512     if (!recType)
513     {
514         yaz_log (YLOG_WARN, "No such record type: %s", recordType);
515         return ZEBRA_FAIL;
516     }
517
518
519
520     zebra_create_stream_mem(&stream, buf, buf_size);
521
522     res = zebra_extract_record_stream(zh, &stream,
523                                       delete_flag,
524                                       test_mode, 
525                                       recordType,
526                                       sysno,
527                                       match_criteria,
528                                       fname,
529                                       force_update,
530                                       allow_update,
531                                       recType, clientData);
532     stream.destroy(&stream);
533     return res;
534 }
535
536
537 ZEBRA_RES zebra_extract_record_stream(ZebraHandle zh, 
538                                       struct ZebraRecStream *stream,
539                                       int delete_flag,
540                                       int test_mode, 
541                                       const char *recordType,
542                                       SYSNO *sysno,
543                                       const char *match_criteria,
544                                       const char *fname,
545                                       int force_update,
546                                       int allow_update,
547                                       RecType recType,
548                                       void *recTypeClientData)
549
550 {
551     SYSNO sysno0 = 0;
552     RecordAttr *recordAttr;
553     struct recExtractCtrl extractCtrl;
554     int r;
555     const char *matchStr = 0;
556     Record rec;
557     off_t start_offset = 0;
558     const char *pr_fname = fname;  /* filename to print .. */
559     int show_progress = zh->records_processed < zh->m_file_verbose_limit ? 1:0;
560
561     zebra_init_log_level();
562
563     if (!pr_fname)
564         pr_fname = "<no file>";  /* make it printable if file is omitted */
565
566     zebra_rec_keys_reset(zh->reg->keys);
567     zebra_rec_keys_reset(zh->reg->sortKeys);
568
569     if (zebraExplain_curDatabase (zh->reg->zei, zh->basenames[0]))
570     {
571         if (zebraExplain_newDatabase (zh->reg->zei, zh->basenames[0], 
572                                       zh->m_explain_database))
573             return ZEBRA_FAIL;
574     }
575
576     if (stream)
577     {
578         off_t null_offset = 0;
579         extractCtrl.stream = stream;
580
581         start_offset = stream->tellf(stream);
582
583         extractCtrl.first_record = start_offset ? 0 : 1;
584         
585         stream->endf(stream, &null_offset);;
586
587         extractCtrl.init = extract_init;
588         extractCtrl.tokenAdd = extract_token_add;
589         extractCtrl.schemaAdd = extract_schema_add;
590         extractCtrl.dh = zh->reg->dh;
591         extractCtrl.handle = zh;
592         extractCtrl.match_criteria[0] = '\0';
593         extractCtrl.staticrank = 0;
594
595     
596         init_extractCtrl(zh, &extractCtrl);
597         
598         extract_set_store_data_prepare(&extractCtrl);
599         
600         r = (*recType->extract)(recTypeClientData, &extractCtrl);
601         
602         if (r == RECCTRL_EXTRACT_EOF)
603             return ZEBRA_FAIL;
604         else if (r == RECCTRL_EXTRACT_ERROR_GENERIC)
605         {
606             /* error occured during extraction ... */
607             yaz_log (YLOG_WARN, "extract error: generic");
608             return ZEBRA_FAIL;
609         }
610         else if (r == RECCTRL_EXTRACT_ERROR_NO_SUCH_FILTER)
611         {
612             /* error occured during extraction ... */
613             yaz_log (YLOG_WARN, "extract error: no such filter");
614             return ZEBRA_FAIL;
615         }
616         
617         all_matches_add(&extractCtrl);
618         
619         if (extractCtrl.match_criteria[0])
620             match_criteria = extractCtrl.match_criteria;
621     }
622     if (!sysno) {
623
624         sysno = &sysno0;
625
626         if (match_criteria && *match_criteria) {
627             matchStr = match_criteria;
628         } else {
629             if (zh->m_record_id && *zh->m_record_id) {
630                 matchStr = fileMatchStr (zh, zh->reg->keys, pr_fname, 
631                                          zh->m_record_id);
632                 if (!matchStr)
633                 {
634                     yaz_log (YLOG_WARN, "Bad match criteria (recordID)");
635                     return ZEBRA_FAIL;
636                 }
637             }
638         }
639         if (matchStr) 
640         {
641             int db_ord = zebraExplain_get_database_ord(zh->reg->zei);
642             char *rinfo = dict_lookup_ord(zh->reg->matchDict, db_ord,
643                                           matchStr);
644             if (rinfo)
645             {
646                 assert(*rinfo == sizeof(*sysno));
647                 memcpy (sysno, rinfo+1, sizeof(*sysno));
648             }
649         }
650     }
651     if (zebra_rec_keys_empty(zh->reg->keys))
652     {
653         /* the extraction process returned no information - the record
654            is probably empty - unless flagShowRecords is in use */
655         if (test_mode)
656             return ZEBRA_OK;
657     }
658
659     if (! *sysno)
660     {
661         /* new record */
662         if (delete_flag)
663         {
664             yaz_log (YLOG_LOG, "delete %s %s " ZINT_FORMAT, recordType,
665                          pr_fname, (zint) start_offset);
666             yaz_log (YLOG_WARN, "cannot delete record above (seems new)");
667             return ZEBRA_FAIL;
668         }
669         if (show_progress)
670             yaz_log (YLOG_LOG, "add %s %s " ZINT_FORMAT, recordType, pr_fname,
671                      (zint) start_offset);
672         rec = rec_new (zh->reg->records);
673
674         *sysno = rec->sysno;
675
676         recordAttr = rec_init_attr (zh->reg->zei, rec);
677         recordAttr->staticrank = extractCtrl.staticrank;
678
679         if (matchStr)
680         {
681             int db_ord = zebraExplain_get_database_ord(zh->reg->zei);
682             dict_insert_ord(zh->reg->matchDict, db_ord, matchStr,
683                             sizeof(*sysno), sysno);
684         }
685
686
687         extract_flushSortKeys (zh, *sysno, 1, zh->reg->sortKeys);
688         extract_flushRecordKeys (zh, *sysno, 1, zh->reg->keys,
689                          recordAttr->staticrank);
690         zh->records_inserted++;
691     } 
692     else
693     {
694         /* record already exists */
695         zebra_rec_keys_t delkeys = zebra_rec_keys_open();
696         zebra_rec_keys_t sortKeys = zebra_rec_keys_open();
697         if (!allow_update)
698         {
699             yaz_log (YLOG_LOG, "skipped %s %s " ZINT_FORMAT, 
700                          recordType, pr_fname, (zint) start_offset);
701             logRecord(zh);
702             return ZEBRA_FAIL;
703         }
704
705         rec = rec_get (zh->reg->records, *sysno);
706         assert (rec);
707         
708         recordAttr = rec_init_attr (zh->reg->zei, rec);
709
710         zebra_rec_keys_set_buf(delkeys,
711                                rec->info[recInfo_delKeys],
712                                rec->size[recInfo_delKeys],
713                                0);
714         zebra_rec_keys_set_buf(sortKeys,
715                                rec->info[recInfo_sortKeys],
716                                rec->size[recInfo_sortKeys],
717                                0);
718
719         extract_flushSortKeys (zh, *sysno, 0, sortKeys);
720         extract_flushRecordKeys (zh, *sysno, 0, delkeys,
721                                  recordAttr->staticrank);
722         if (delete_flag)
723         {
724             /* record going to be deleted */
725             if (zebra_rec_keys_empty(delkeys))
726             {
727                 yaz_log(YLOG_LOG, "delete %s %s " ZINT_FORMAT, recordType,
728                         pr_fname, (zint) start_offset);
729                 yaz_log(YLOG_WARN, "cannot delete file above, "
730                         "storeKeys false (3)");
731             }
732             else
733             {
734                 if (show_progress)
735                     yaz_log(YLOG_LOG, "delete %s %s " ZINT_FORMAT, recordType,
736                             pr_fname, (zint) start_offset);
737                 zh->records_deleted++;
738                 if (matchStr)
739                 {
740                     int db_ord = zebraExplain_get_database_ord(zh->reg->zei);
741                     dict_delete_ord(zh->reg->matchDict, db_ord, matchStr);
742                 }
743                 rec_del (zh->reg->records, &rec);
744             }
745             rec_rm (&rec);
746             logRecord(zh);
747             return ZEBRA_OK;
748         }
749         else
750         {
751             if (show_progress)
752                     yaz_log(YLOG_LOG, "update %s %s " ZINT_FORMAT, recordType,
753                             pr_fname, (zint) ZINT_FORMAT);
754             recordAttr->staticrank = extractCtrl.staticrank;
755             extract_flushSortKeys (zh, *sysno, 1, zh->reg->sortKeys);
756             extract_flushRecordKeys (zh, *sysno, 1, zh->reg->keys, 
757                                          recordAttr->staticrank);
758             zh->records_updated++;
759         }
760         zebra_rec_keys_close(delkeys);
761         zebra_rec_keys_close(sortKeys);
762     }
763     /* update file type */
764     xfree (rec->info[recInfo_fileType]);
765     rec->info[recInfo_fileType] =
766         rec_strdup (recordType, &rec->size[recInfo_fileType]);
767
768     /* update filename */
769     xfree (rec->info[recInfo_filename]);
770     rec->info[recInfo_filename] =
771         rec_strdup (fname, &rec->size[recInfo_filename]);
772
773     /* update delete keys */
774     xfree (rec->info[recInfo_delKeys]);
775     if (!zebra_rec_keys_empty(zh->reg->keys) && zh->m_store_keys == 1)
776     {
777         zebra_rec_keys_get_buf(zh->reg->keys,
778                                &rec->info[recInfo_delKeys],
779                                &rec->size[recInfo_delKeys]);
780     }
781     else
782     {
783         rec->info[recInfo_delKeys] = NULL;
784         rec->size[recInfo_delKeys] = 0;
785     }
786     /* update sort keys */
787     xfree (rec->info[recInfo_sortKeys]);
788
789     zebra_rec_keys_get_buf(zh->reg->sortKeys,
790                            &rec->info[recInfo_sortKeys],
791                            &rec->size[recInfo_sortKeys]);
792
793     /* save file size of original record */
794     zebraExplain_recordBytesIncrement (zh->reg->zei,
795                                        - recordAttr->recordSize);
796     if (stream)
797     {
798         off_t end_offset = stream->endf(stream, 0);
799
800         if (!end_offset)
801             end_offset = stream->tellf(stream);
802         else
803             stream->seekf(stream, end_offset);
804
805         recordAttr->recordSize = end_offset - start_offset;
806         zebraExplain_recordBytesIncrement(zh->reg->zei,
807                                           recordAttr->recordSize);
808     }
809
810     /* set run-number for this record */
811     recordAttr->runNumber =
812         zebraExplain_runNumberIncrement (zh->reg->zei, 0);
813
814     /* update store data */
815     xfree (rec->info[recInfo_storeData]);
816
817     /* update store data */
818     if (zh->store_data_buf)
819     {
820         rec->size[recInfo_storeData] = zh->store_data_size;
821         rec->info[recInfo_storeData] = zh->store_data_buf;
822         zh->store_data_buf = 0;
823     }
824     else if (zh->m_store_data)
825     {
826         off_t cur_offset = stream->tellf(stream);
827
828         rec->size[recInfo_storeData] = recordAttr->recordSize;
829         rec->info[recInfo_storeData] = (char *)
830             xmalloc (recordAttr->recordSize);
831         stream->seekf(stream, start_offset);
832         stream->readf(stream, rec->info[recInfo_storeData],
833                       recordAttr->recordSize);
834         stream->seekf(stream, cur_offset);
835     }
836     else
837     {
838         rec->info[recInfo_storeData] = NULL;
839         rec->size[recInfo_storeData] = 0;
840     }
841     /* update database name */
842     xfree (rec->info[recInfo_databaseName]);
843     rec->info[recInfo_databaseName] =
844         rec_strdup (zh->basenames[0], &rec->size[recInfo_databaseName]); 
845
846     /* update offset */
847     recordAttr->recordOffset = start_offset;
848     
849     /* commit this record */
850     rec_put (zh->reg->records, &rec);
851     logRecord(zh);
852     return ZEBRA_OK;
853 }
854
855 ZEBRA_RES zebra_extract_explain(void *handle, Record rec, data1_node *n)
856 {
857     ZebraHandle zh = (ZebraHandle) handle;
858     struct recExtractCtrl extractCtrl;
859
860     if (zebraExplain_curDatabase (zh->reg->zei,
861                                   rec->info[recInfo_databaseName]))
862     {
863         abort();
864         if (zebraExplain_newDatabase (zh->reg->zei,
865                                       rec->info[recInfo_databaseName], 0))
866             abort ();
867     }
868
869     zebra_rec_keys_reset(zh->reg->keys);
870     zebra_rec_keys_reset(zh->reg->sortKeys);
871
872     extractCtrl.init = extract_init;
873     extractCtrl.tokenAdd = extract_token_add;
874     extractCtrl.schemaAdd = extract_schema_add;
875     extractCtrl.dh = zh->reg->dh;
876
877     init_extractCtrl(zh, &extractCtrl);
878
879     extractCtrl.flagShowRecords = 0;
880     extractCtrl.match_criteria[0] = '\0';
881     extractCtrl.staticrank = 0;
882     extractCtrl.handle = handle;
883     extractCtrl.first_record = 1;
884     
885     extract_set_store_data_prepare(&extractCtrl);
886
887     if (n)
888         grs_extract_tree(&extractCtrl, n);
889
890     if (rec->size[recInfo_delKeys])
891     {
892         zebra_rec_keys_t delkeys = zebra_rec_keys_open();
893         
894         zebra_rec_keys_t sortkeys = zebra_rec_keys_open();
895
896         zebra_rec_keys_set_buf(delkeys, rec->info[recInfo_delKeys],
897                                rec->size[recInfo_delKeys],
898                                0);
899         extract_flushRecordKeys (zh, rec->sysno, 0, delkeys, 0);
900         zebra_rec_keys_close(delkeys);
901
902         zebra_rec_keys_set_buf(sortkeys, rec->info[recInfo_sortKeys],
903                                rec->size[recInfo_sortKeys],
904                                0);
905
906         extract_flushSortKeys (zh, rec->sysno, 0, sortkeys);
907         zebra_rec_keys_close(sortkeys);
908     }
909     extract_flushRecordKeys (zh, rec->sysno, 1, zh->reg->keys, 0);
910     extract_flushSortKeys (zh, rec->sysno, 1, zh->reg->sortKeys);
911
912     xfree (rec->info[recInfo_delKeys]);
913     zebra_rec_keys_get_buf(zh->reg->keys,
914                            &rec->info[recInfo_delKeys], 
915                            &rec->size[recInfo_delKeys]);
916
917     xfree (rec->info[recInfo_sortKeys]);
918     zebra_rec_keys_get_buf(zh->reg->sortKeys,
919                            &rec->info[recInfo_sortKeys],
920                            &rec->size[recInfo_sortKeys]);
921     return ZEBRA_OK;
922 }
923
924 void extract_rec_keys_adjust(ZebraHandle zh, int is_insert,
925                              zebra_rec_keys_t reckeys)
926 {
927     ZebraExplainInfo zei = zh->reg->zei;
928     struct ord_stat {
929         int no;
930         int ord;
931         struct ord_stat *next;
932     };
933
934     if (zebra_rec_keys_rewind(reckeys))
935     {
936         struct ord_stat *ord_list = 0;
937         struct ord_stat *p;
938         size_t slen;
939         const char *str;
940         struct it_key key_in;
941         while(zebra_rec_keys_read(reckeys, &str, &slen, &key_in))
942         {
943             int ord = CAST_ZINT_TO_INT(key_in.mem[0]);
944
945             for (p = ord_list; p ; p = p->next)
946                 if (p->ord == ord)
947                 {
948                     p->no++;
949                     break;
950                 }
951             if (!p)
952             {
953                 p = xmalloc(sizeof(*p));
954                 p->no = 1;
955                 p->ord = ord;
956                 p->next = ord_list;
957                 ord_list = p;
958             }
959         }
960
961         p = ord_list;
962         while (p)
963         {
964             struct ord_stat *p1 = p;
965
966             if (is_insert)
967                 zebraExplain_ord_adjust_occurrences(zei, p->ord, p->no, 1);
968             else
969                 zebraExplain_ord_adjust_occurrences(zei, p->ord, - p->no, -1);
970             p = p->next;
971             xfree(p1);
972         }
973     }
974 }
975
976 void extract_flushRecordKeys (ZebraHandle zh, SYSNO sysno,
977                               int cmd,
978                               zebra_rec_keys_t reckeys,
979                               zint staticrank)
980 {
981     ZebraExplainInfo zei = zh->reg->zei;
982
983     extract_rec_keys_adjust(zh, cmd, reckeys);
984
985     if (!zh->reg->key_buf)
986     {
987         int mem= 1024*1024* atoi( res_get_def( zh->res, "memmax", "8"));
988         if (mem <= 0)
989         {
990             yaz_log(YLOG_WARN, "Invalid memory setting, using default 8 MB");
991             mem= 1024*1024*8;
992         }
993         /* FIXME: That "8" should be in a default settings include */
994         /* not hard-coded here! -H */
995         zh->reg->key_buf = (char**) xmalloc (mem);
996         zh->reg->ptr_top = mem/sizeof(char*);
997         zh->reg->ptr_i = 0;
998         zh->reg->key_buf_used = 0;
999         zh->reg->key_file_no = 0;
1000     }
1001     zebraExplain_recordCountIncrement (zei, cmd ? 1 : -1);
1002
1003     if (zebra_rec_keys_rewind(reckeys))
1004     {
1005         size_t slen;
1006         const char *str;
1007         struct it_key key_in;
1008         while(zebra_rec_keys_read(reckeys, &str, &slen, &key_in))
1009         {
1010             int ch = 0;
1011             int i, j = 0;
1012             struct it_key key_out;
1013
1014             assert(key_in.len >= 2);
1015             assert(key_in.len <= IT_KEY_LEVEL_MAX);
1016             
1017             /* check for buffer overflow */
1018             if (zh->reg->key_buf_used + 1024 > 
1019                 (zh->reg->ptr_top -zh->reg->ptr_i)*sizeof(char*))
1020                 extract_flushWriteKeys (zh, 0);
1021             
1022             ++(zh->reg->ptr_i);
1023             assert(zh->reg->ptr_i > 0);
1024             (zh->reg->key_buf)[zh->reg->ptr_top - zh->reg->ptr_i] =
1025                 (char*)zh->reg->key_buf + zh->reg->key_buf_used;
1026
1027             /* key_in.mem[0] ord/ch */
1028             /* key_in.mem[1] filter specified record ID */
1029
1030             /* encode the ordinal value (field/use/attribute) .. */
1031             ch = CAST_ZINT_TO_INT(key_in.mem[0]);
1032             zh->reg->key_buf_used +=
1033                 key_SU_encode(ch, (char*)zh->reg->key_buf +
1034                               zh->reg->key_buf_used);
1035
1036             /* copy the 0-terminated stuff from str to output */
1037             memcpy((char*)zh->reg->key_buf + zh->reg->key_buf_used, str, slen);
1038             zh->reg->key_buf_used += slen;
1039             ((char*)zh->reg->key_buf)[(zh->reg->key_buf_used)++] = '\0';
1040
1041             /* the delete/insert indicator */
1042             ((char*)zh->reg->key_buf)[(zh->reg->key_buf_used)++] = cmd;
1043
1044             if (zh->m_staticrank) /* rank config enabled ? */
1045             {
1046                 if (staticrank < 0)
1047                 {
1048                     yaz_log(YLOG_WARN, "staticrank = %ld. Setting to 0",
1049                             (long) staticrank);
1050                     staticrank = 0;
1051                 }
1052                 key_out.mem[j++] = staticrank;
1053             }
1054             
1055             if (key_in.mem[1]) /* filter specified record ID */
1056                 key_out.mem[j++] = key_in.mem[1];
1057             else
1058                 key_out.mem[j++] = sysno;
1059             for (i = 2; i < key_in.len; i++)
1060                 key_out.mem[j++] = key_in.mem[i];
1061             key_out.len = j;
1062
1063             memcpy((char*)zh->reg->key_buf + zh->reg->key_buf_used,
1064                    &key_out, sizeof(key_out));
1065             (zh->reg->key_buf_used) += sizeof(key_out);
1066         }
1067     }
1068 }
1069
1070 void extract_flushWriteKeys (ZebraHandle zh, int final)
1071         /* optimizing: if final=1, and no files written yet */
1072         /* push the keys directly to merge, sidestepping the */
1073         /* temp file altogether. Speeds small updates */
1074 {
1075     FILE *outf;
1076     char out_fname[200];
1077     char *prevcp, *cp;
1078     struct encode_info encode_info;
1079     int ptr_i = zh->reg->ptr_i;
1080     int temp_policy;
1081 #if SORT_EXTRA
1082     int i;
1083 #endif
1084     if (!zh->reg->key_buf || ptr_i <= 0)
1085     {
1086         yaz_log(log_level, "  nothing to flush section=%d buf=%p i=%d",
1087                zh->reg->key_file_no, zh->reg->key_buf, ptr_i);
1088         return;
1089     }
1090
1091     (zh->reg->key_file_no)++;
1092     yaz_log (YLOG_LOG, "sorting section %d", (zh->reg->key_file_no));
1093     yaz_log(log_level, "  sort_buff at %p n=%d",
1094                     zh->reg->key_buf + zh->reg->ptr_top - ptr_i,ptr_i);
1095 #if !SORT_EXTRA
1096     qsort (zh->reg->key_buf + zh->reg->ptr_top - ptr_i, ptr_i,
1097                sizeof(char*), key_qsort_compare);
1098
1099     /* zebra.cfg: tempfiles:  
1100        Y: always use temp files (old way) 
1101        A: use temp files, if more than one (auto) 
1102           = if this is both the last and the first 
1103        N: never bother with temp files (new) */
1104
1105     temp_policy=toupper(res_get_def(zh->res,"tempfiles","auto")[0]);
1106     if (temp_policy != 'Y' && temp_policy != 'N' && temp_policy != 'A') {
1107         yaz_log (YLOG_WARN, "Illegal tempfiles setting '%c'. using 'Auto' ", 
1108                         temp_policy);
1109         temp_policy='A';
1110     }
1111
1112     if (   ( temp_policy =='N' )   ||     /* always from memory */
1113          ( ( temp_policy =='A' ) &&       /* automatic */
1114              (zh->reg->key_file_no == 1) &&  /* this is first time */
1115              (final) ) )                     /* and last (=only) time */
1116     { /* go directly from memory */
1117         zh->reg->key_file_no =0; /* signal not to read files */
1118         zebra_index_merge(zh); 
1119         zh->reg->ptr_i = 0;
1120         zh->reg->key_buf_used = 0; 
1121         return; 
1122     }
1123
1124     /* Not doing directly from memory, write into a temp file */
1125     extract_get_fname_tmp (zh, out_fname, zh->reg->key_file_no);
1126
1127     if (!(outf = fopen (out_fname, "wb")))
1128     {
1129         yaz_log (YLOG_FATAL|YLOG_ERRNO, "fopen %s", out_fname);
1130         exit (1);
1131     }
1132     yaz_log (YLOG_LOG, "writing section %d", zh->reg->key_file_no);
1133     prevcp = cp = (zh->reg->key_buf)[zh->reg->ptr_top - ptr_i];
1134     
1135     encode_key_init (&encode_info);
1136     encode_key_write (cp, &encode_info, outf);
1137     
1138     while (--ptr_i > 0)
1139     {
1140         cp = (zh->reg->key_buf)[zh->reg->ptr_top - ptr_i];
1141         if (strcmp (cp, prevcp))
1142         {
1143             encode_key_flush ( &encode_info, outf);
1144             encode_key_init (&encode_info);
1145             encode_key_write (cp, &encode_info, outf);
1146             prevcp = cp;
1147         }
1148         else
1149             encode_key_write (cp + strlen(cp), &encode_info, outf);
1150     }
1151     encode_key_flush ( &encode_info, outf);
1152 #else
1153     qsort (key_buf + ptr_top-ptr_i, ptr_i, sizeof(char*), key_x_compare);
1154     extract_get_fname_tmp (out_fname, key_file_no);
1155
1156     if (!(outf = fopen (out_fname, "wb")))
1157     {
1158         yaz_log (YLOG_FATAL|YLOG_ERRNO, "fopen %s", out_fname);
1159         exit (1);
1160     }
1161     yaz_log (YLOG_LOG, "writing section %d", key_file_no);
1162     i = ptr_i;
1163     prevcp =  key_buf[ptr_top-i];
1164     while (1)
1165         if (!--i || strcmp (prevcp, key_buf[ptr_top-i]))
1166         {
1167             key_y_len = strlen(prevcp)+1;
1168 #if 0
1169             yaz_log (YLOG_LOG, "key_y_len: %2d %02x %02x %s",
1170                       key_y_len, prevcp[0], prevcp[1], 2+prevcp);
1171 #endif
1172             qsort (key_buf + ptr_top-ptr_i, ptr_i - i,
1173                                    sizeof(char*), key_y_compare);
1174             cp = key_buf[ptr_top-ptr_i];
1175             --key_y_len;
1176             encode_key_init (&encode_info);
1177             encode_key_write (cp, &encode_info, outf);
1178             while (--ptr_i > i)
1179             {
1180                 cp = key_buf[ptr_top-ptr_i];
1181                 encode_key_write (cp+key_y_len, &encode_info, outf);
1182             }
1183             encode_key_flush ( &encode_info, outf);
1184             if (!i)
1185                 break;
1186             prevcp = key_buf[ptr_top-ptr_i];
1187         }
1188 #endif
1189     if (fclose (outf))
1190     {
1191         yaz_log (YLOG_FATAL|YLOG_ERRNO, "fclose %s", out_fname);
1192         exit (1);
1193     }
1194     yaz_log (YLOG_LOG, "finished section %d", zh->reg->key_file_no);
1195     zh->reg->ptr_i = 0;
1196     zh->reg->key_buf_used = 0;
1197 }
1198
1199 ZEBRA_RES zebra_snippets_rec_keys(ZebraHandle zh,
1200                                   zebra_rec_keys_t reckeys,
1201                                   zebra_snippets *snippets)
1202 {
1203     NMEM nmem = nmem_create();
1204     if (zebra_rec_keys_rewind(reckeys)) 
1205     {
1206         const char *str;
1207         size_t slen;
1208         struct it_key key;
1209         while (zebra_rec_keys_read(reckeys, &str, &slen, &key))
1210         {
1211             char dst_buf[IT_MAX_WORD];
1212             char *dst_term = dst_buf;
1213             int ord;
1214             zint seqno;
1215             int index_type;
1216
1217             assert(key.len <= IT_KEY_LEVEL_MAX && key.len > 2);
1218             seqno = key.mem[key.len-1];
1219             ord = CAST_ZINT_TO_INT(key.mem[0]);
1220             
1221             zebraExplain_lookup_ord(zh->reg->zei, ord, &index_type,
1222                                     0/* db */, 0 /* string_index */);
1223             assert(index_type);
1224             zebra_term_untrans_iconv(zh, nmem, index_type,
1225                                      &dst_term, str);
1226             zebra_snippets_append(snippets, seqno, ord, dst_term);
1227             nmem_reset(nmem);
1228         }
1229     }
1230     nmem_destroy(nmem);
1231     return ZEBRA_OK;
1232 }
1233
1234 void print_rec_keys(ZebraHandle zh, zebra_rec_keys_t reckeys)
1235 {
1236     yaz_log(YLOG_LOG, "print_rec_keys");
1237     if (zebra_rec_keys_rewind(reckeys))
1238     {
1239         const char *str;
1240         size_t slen;
1241         struct it_key key;
1242         while (zebra_rec_keys_read(reckeys, &str, &slen, &key))
1243         {
1244             char dst_buf[IT_MAX_WORD];
1245             zint seqno;
1246             int index_type;
1247             int ord = CAST_ZINT_TO_INT(key.mem[0]);
1248             const char *db = 0;
1249             assert(key.len <= IT_KEY_LEVEL_MAX && key.len > 2);
1250
1251             zebraExplain_lookup_ord(zh->reg->zei, ord, &index_type, &db, 0);
1252             
1253             seqno = key.mem[key.len-1];
1254             
1255             zebra_term_untrans(zh, index_type, dst_buf, str);
1256             
1257             yaz_log(YLOG_LOG, "ord=%d seqno=" ZINT_FORMAT 
1258                     " term=%s", ord, seqno, dst_buf); 
1259         }
1260     }
1261 }
1262
1263 static void extract_add_index_string(RecWord *p, zinfo_index_category_t cat,
1264                                      const char *str, int length)
1265 {
1266     struct it_key key;
1267     ZebraHandle zh = p->extractCtrl->handle;
1268     ZebraExplainInfo zei = zh->reg->zei;
1269     int ch, i;
1270
1271     ch = zebraExplain_lookup_attr_str(zei, cat, p->index_type, p->index_name);
1272     if (ch < 0)
1273         ch = zebraExplain_add_attr_str(zei, cat, p->index_type, p->index_name);
1274
1275     i = 0;
1276     key.mem[i++] = ch;
1277     key.mem[i++] = p->record_id;
1278     key.mem[i++] = p->section_id;
1279
1280     if (zh->m_segment_indexing)
1281         key.mem[i++] = p->segment;
1282     key.mem[i++] = p->seqno;
1283     key.len = i;
1284
1285     zebra_rec_keys_write(zh->reg->keys, str, length, &key);
1286 }
1287
1288 static void extract_add_sort_string(RecWord *p, const char *str, int length)
1289 {
1290     struct it_key key;
1291     ZebraHandle zh = p->extractCtrl->handle;
1292     ZebraExplainInfo zei = zh->reg->zei;
1293     int ch;
1294     zinfo_index_category_t cat = zinfo_index_category_sort;
1295
1296     ch = zebraExplain_lookup_attr_str(zei, cat, p->index_type, p->index_name);
1297     if (ch < 0)
1298         ch = zebraExplain_add_attr_str(zei, cat, p->index_type, p->index_name);
1299     key.len = 2;
1300     key.mem[0] = ch;
1301     key.mem[1] = p->record_id;
1302
1303     zebra_rec_keys_write(zh->reg->sortKeys, str, length, &key);
1304 }
1305
1306 static void extract_add_string(RecWord *p, const char *string, int length)
1307 {
1308     ZebraHandle zh = p->extractCtrl->handle;
1309     assert (length > 0);
1310
1311     if (!p->index_name)
1312         return;
1313
1314     if (zebra_maps_is_sort(zh->reg->zebra_maps, p->index_type))
1315         extract_add_sort_string(p, string, length);
1316     else
1317     {
1318         extract_add_index_string(p, zinfo_index_category_index,
1319                                  string, length);
1320         if (zebra_maps_is_alwaysmatches(zh->reg->zebra_maps, p->index_type))
1321         {
1322             RecWord word;
1323             memcpy(&word, p, sizeof(word));
1324
1325             word.seqno = 1;
1326             extract_add_index_string(
1327                 &word, zinfo_index_category_alwaysmatches, "", 0);
1328         }
1329     }
1330 }
1331
1332 static void extract_add_incomplete_field (RecWord *p)
1333 {
1334     ZebraHandle zh = p->extractCtrl->handle;
1335     const char *b = p->term_buf;
1336     int remain = p->term_len;
1337     const char **map = 0;
1338     
1339     if (remain > 0)
1340         map = zebra_maps_input(zh->reg->zebra_maps, p->index_type, &b, remain, 0);
1341
1342     while (map)
1343     {
1344         char buf[IT_MAX_WORD+1];
1345         int i, remain;
1346
1347         /* Skip spaces */
1348         while (map && *map && **map == *CHR_SPACE)
1349         {
1350             remain = p->term_len - (b - p->term_buf);
1351             if (remain > 0)
1352                 map = zebra_maps_input(zh->reg->zebra_maps, p->index_type, &b,
1353                                        remain, 0);
1354             else
1355                 map = 0;
1356         }
1357         if (!map)
1358             break;
1359         i = 0;
1360         while (map && *map && **map != *CHR_SPACE)
1361         {
1362             const char *cp = *map;
1363
1364             while (i < IT_MAX_WORD && *cp)
1365                 buf[i++] = *(cp++);
1366             remain = p->term_len - (b - p->term_buf);
1367             if (remain > 0)
1368                 map = zebra_maps_input(zh->reg->zebra_maps, p->index_type, &b, remain, 0);
1369             else
1370                 map = 0;
1371         }
1372         if (!i)
1373             return;
1374         extract_add_string (p, buf, i);
1375         p->seqno++;
1376     }
1377 }
1378
1379 static void extract_add_complete_field (RecWord *p)
1380 {
1381     ZebraHandle zh = p->extractCtrl->handle;
1382     const char *b = p->term_buf;
1383     char buf[IT_MAX_WORD+1];
1384     const char **map = 0;
1385     int i = 0, remain = p->term_len;
1386
1387     if (remain > 0)
1388         map = zebra_maps_input (zh->reg->zebra_maps, p->index_type, &b, remain, 1);
1389
1390     while (remain > 0 && i < IT_MAX_WORD)
1391     {
1392         while (map && *map && **map == *CHR_SPACE)
1393         {
1394             remain = p->term_len - (b - p->term_buf);
1395
1396             if (remain > 0)
1397             {
1398                 int first = i ? 0 : 1;  /* first position */
1399                 map = zebra_maps_input(zh->reg->zebra_maps, p->index_type, &b, remain, first);
1400             }
1401             else
1402                 map = 0;
1403         }
1404         if (!map)
1405             break;
1406
1407         if (i && i < IT_MAX_WORD)
1408             buf[i++] = *CHR_SPACE;
1409         while (map && *map && **map != *CHR_SPACE)
1410         {
1411             const char *cp = *map;
1412
1413             if (**map == *CHR_CUT)
1414             {
1415                 i = 0;
1416             }
1417             else
1418             {
1419                 if (i >= IT_MAX_WORD)
1420                     break;
1421                 while (i < IT_MAX_WORD && *cp)
1422                     buf[i++] = *(cp++);
1423             }
1424             remain = p->term_len  - (b - p->term_buf);
1425             if (remain > 0)
1426             {
1427                 map = zebra_maps_input (zh->reg->zebra_maps, p->index_type, &b,
1428                                         remain, 0);
1429             }
1430             else
1431                 map = 0;
1432         }
1433     }
1434     if (!i)
1435         return;
1436     extract_add_string (p, buf, i);
1437 }
1438
1439 static void extract_token_add(RecWord *p)
1440 {
1441     ZebraHandle zh = p->extractCtrl->handle;
1442     WRBUF wrbuf;
1443     if (log_level)
1444         yaz_log(log_level, "extract_token_add "
1445                 "type=%c index=%s seqno=" ZINT_FORMAT " s=%.*s",
1446                 p->index_type, p->index_name, 
1447                 p->seqno, p->term_len, p->term_buf);
1448     if ((wrbuf = zebra_replace(zh->reg->zebra_maps, p->index_type, 0,
1449                                p->term_buf, p->term_len)))
1450     {
1451         p->term_buf = wrbuf_buf(wrbuf);
1452         p->term_len = wrbuf_len(wrbuf);
1453     }
1454     if (zebra_maps_is_complete (zh->reg->zebra_maps, p->index_type))
1455         extract_add_complete_field (p);
1456     else
1457         extract_add_incomplete_field(p);
1458 }
1459
1460 static void extract_set_store_data_cb(struct recExtractCtrl *p,
1461                                       void *buf, size_t sz)
1462 {
1463     ZebraHandle zh = (ZebraHandle) p->handle;
1464
1465     xfree(zh->store_data_buf);
1466     zh->store_data_buf = 0;
1467     zh->store_data_size = 0;
1468     if (buf && sz)
1469     {
1470         zh->store_data_buf = xmalloc(sz);
1471         zh->store_data_size = sz;
1472         memcpy(zh->store_data_buf, buf, sz);
1473     }
1474 }
1475
1476 static void extract_set_store_data_prepare(struct recExtractCtrl *p)
1477 {
1478     ZebraHandle zh = (ZebraHandle) p->handle;
1479     xfree(zh->store_data_buf);
1480     zh->store_data_buf = 0;
1481     zh->store_data_size = 0;
1482     p->setStoreData = extract_set_store_data_cb;
1483 }
1484
1485 static void extract_schema_add (struct recExtractCtrl *p, Odr_oid *oid)
1486 {
1487     ZebraHandle zh = (ZebraHandle) p->handle;
1488     zebraExplain_addSchema (zh->reg->zei, oid);
1489 }
1490
1491 void extract_flushSortKeys (ZebraHandle zh, SYSNO sysno,
1492                             int cmd, zebra_rec_keys_t reckeys)
1493 {
1494     if (zebra_rec_keys_rewind(reckeys))
1495     {
1496         SortIdx sortIdx = zh->reg->sortIdx;
1497         size_t slen;
1498         const char *str;
1499         struct it_key key_in;
1500
1501         sortIdx_sysno (sortIdx, sysno);
1502
1503         while (zebra_rec_keys_read(reckeys, &str, &slen, &key_in))
1504         {
1505             int ord = CAST_ZINT_TO_INT(key_in.mem[0]);
1506             
1507             sortIdx_type(sortIdx, ord);
1508             if (cmd == 1)
1509                 sortIdx_add(sortIdx, str, slen);
1510             else
1511                 sortIdx_add(sortIdx, "", 1);
1512         }
1513     }
1514 }
1515
1516 static void encode_key_init(struct encode_info *i)
1517 {
1518     i->encode_handle = iscz1_start();
1519     i->decode_handle = iscz1_start();
1520 }
1521
1522 static void encode_key_write (char *k, struct encode_info *i, FILE *outf)
1523 {
1524     struct it_key key;
1525     char *bp = i->buf, *bp0;
1526     const char *src = (char *) &key;
1527
1528     /* copy term to output buf */
1529     while ((*bp++ = *k++))
1530         ;
1531     /* and copy & align key so we can mangle */
1532     memcpy (&key, k+1, sizeof(struct it_key));  /* *k is insert/delete */
1533
1534 #if 0
1535     /* debugging */
1536     key_logdump_txt(YLOG_LOG, &key, *k ? "i" : "d");
1537 #endif
1538     assert(key.mem[0] >= 0);
1539
1540     bp0 = bp++;
1541     iscz1_encode(i->encode_handle, &bp, &src);
1542
1543     *bp0 = (*k * 128) + bp - bp0 - 1; /* length and insert/delete combined */
1544     if (fwrite (i->buf, bp - i->buf, 1, outf) != 1)
1545     {
1546         yaz_log (YLOG_FATAL|YLOG_ERRNO, "fwrite");
1547         exit (1);
1548     }
1549
1550 #if 0
1551     /* debugging */
1552     if (1)
1553     {
1554         struct it_key key2;
1555         const char *src = bp0+1;
1556         char *dst = (char*) &key2;
1557         iscz1_decode(i->decode_handle, &dst, &src);
1558
1559         key_logdump_txt(YLOG_LOG, &key2, *k ? "i" : "d");
1560
1561         assert(key2.mem[1]);
1562     }
1563 #endif
1564 }
1565
1566 static void encode_key_flush (struct encode_info *i, FILE *outf)
1567
1568     iscz1_stop(i->encode_handle);
1569     iscz1_stop(i->decode_handle);
1570 }
1571
1572 /*
1573  * Local variables:
1574  * c-basic-offset: 4
1575  * indent-tabs-mode: nil
1576  * End:
1577  * vim: shiftwidth=4 tabstop=8 expandtab
1578  */
1579