For record file stream indexing, stop looping if offset is unmodified
[idzebra-moved-to-github.git] / index / extract.c
1 /* $Id: extract.c,v 1.232 2006-10-29 20:35:58 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 = 0;
48
49 static void zebra_init_log_level(void)
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         off_t prev_off = streamp->tellf(streamp);
445
446         r = zebra_extract_record_stream(zh, streamp,
447                                         deleteFlag,
448                                         0, /* tst_mode */
449                                         zh->m_record_type,
450                                         sysno,
451                                         0, /*match_criteria */
452                                         fname,
453                                         1, /* force_update */
454                                         1, /* allow_update */
455                                         recType, recTypeClientData);
456         if (r != ZEBRA_OK)
457         {
458             break;
459         }
460         if (sysno)
461         {
462             break;
463         }
464         if (prev_off == streamp->tellf(streamp))
465             break;
466     }
467     if (streamp)
468         stream.destroy(streamp);
469     zh->m_record_type = original_record_type;
470     return r;
471 }
472
473 /*
474   If sysno is provided, then it's used to identify the reocord.
475   If not, and match_criteria is provided, then sysno is guessed
476   If not, and a record is provided, then sysno is got from there
477   
478  */
479
480 ZEBRA_RES zebra_buffer_extract_record(ZebraHandle zh, 
481                                       const char *buf, size_t buf_size,
482                                       int delete_flag,
483                                       int test_mode, 
484                                       const char *recordType,
485                                       SYSNO *sysno,
486                                       const char *match_criteria,
487                                       const char *fname,
488                                       int force_update,
489                                       int allow_update)
490 {
491     struct ZebraRecStream stream;
492     ZEBRA_RES res;
493     void *clientData;
494     RecType recType = 0;
495
496     if (recordType && *recordType)
497     {
498         yaz_log(log_level, "Record type explicitly specified: %s", recordType);
499         recType = recType_byName (zh->reg->recTypes, zh->res, recordType,
500                                   &clientData);
501     } 
502     else
503     {
504         if (!(zh->m_record_type))
505         {
506             yaz_log (YLOG_WARN, "No such record type defined");
507             return ZEBRA_FAIL;
508         }
509         yaz_log(log_level, "Get record type from rgroup: %s",
510                 zh->m_record_type);
511         recType = recType_byName (zh->reg->recTypes, zh->res,
512                                   zh->m_record_type, &clientData);
513         recordType = zh->m_record_type;
514     }
515     
516     if (!recType)
517     {
518         yaz_log (YLOG_WARN, "No such record type: %s", recordType);
519         return ZEBRA_FAIL;
520     }
521
522
523
524     zebra_create_stream_mem(&stream, buf, buf_size);
525
526     res = zebra_extract_record_stream(zh, &stream,
527                                       delete_flag,
528                                       test_mode, 
529                                       recordType,
530                                       sysno,
531                                       match_criteria,
532                                       fname,
533                                       force_update,
534                                       allow_update,
535                                       recType, clientData);
536     stream.destroy(&stream);
537     return res;
538 }
539
540
541 ZEBRA_RES zebra_extract_record_stream(ZebraHandle zh, 
542                                       struct ZebraRecStream *stream,
543                                       int delete_flag,
544                                       int test_mode, 
545                                       const char *recordType,
546                                       SYSNO *sysno,
547                                       const char *match_criteria,
548                                       const char *fname,
549                                       int force_update,
550                                       int allow_update,
551                                       RecType recType,
552                                       void *recTypeClientData)
553
554 {
555     SYSNO sysno0 = 0;
556     RecordAttr *recordAttr;
557     struct recExtractCtrl extractCtrl;
558     int r;
559     const char *matchStr = 0;
560     Record rec;
561     off_t start_offset = 0;
562     const char *pr_fname = fname;  /* filename to print .. */
563     int show_progress = zh->records_processed < zh->m_file_verbose_limit ? 1:0;
564
565     zebra_init_log_level();
566
567     if (!pr_fname)
568         pr_fname = "<no file>";  /* make it printable if file is omitted */
569
570     zebra_rec_keys_reset(zh->reg->keys);
571     zebra_rec_keys_reset(zh->reg->sortKeys);
572
573     if (zebraExplain_curDatabase (zh->reg->zei, zh->basenames[0]))
574     {
575         if (zebraExplain_newDatabase (zh->reg->zei, zh->basenames[0], 
576                                       zh->m_explain_database))
577             return ZEBRA_FAIL;
578     }
579
580     if (stream)
581     {
582         off_t null_offset = 0;
583         extractCtrl.stream = stream;
584
585         start_offset = stream->tellf(stream);
586
587         extractCtrl.first_record = start_offset ? 0 : 1;
588         
589         stream->endf(stream, &null_offset);;
590
591         extractCtrl.init = extract_init;
592         extractCtrl.tokenAdd = extract_token_add;
593         extractCtrl.schemaAdd = extract_schema_add;
594         extractCtrl.dh = zh->reg->dh;
595         extractCtrl.handle = zh;
596         extractCtrl.match_criteria[0] = '\0';
597         extractCtrl.staticrank = 0;
598
599     
600         init_extractCtrl(zh, &extractCtrl);
601         
602         extract_set_store_data_prepare(&extractCtrl);
603         
604         r = (*recType->extract)(recTypeClientData, &extractCtrl);
605         
606         if (r == RECCTRL_EXTRACT_EOF)
607             return ZEBRA_FAIL;
608         else if (r == RECCTRL_EXTRACT_ERROR_GENERIC)
609         {
610             /* error occured during extraction ... */
611             yaz_log (YLOG_WARN, "extract error: generic");
612             return ZEBRA_FAIL;
613         }
614         else if (r == RECCTRL_EXTRACT_ERROR_NO_SUCH_FILTER)
615         {
616             /* error occured during extraction ... */
617             yaz_log (YLOG_WARN, "extract error: no such filter");
618             return ZEBRA_FAIL;
619         }
620         
621         all_matches_add(&extractCtrl);
622         
623         if (extractCtrl.match_criteria[0])
624             match_criteria = extractCtrl.match_criteria;
625     }
626     if (!sysno) {
627
628         sysno = &sysno0;
629
630         if (match_criteria && *match_criteria) {
631             matchStr = match_criteria;
632         } else {
633             if (zh->m_record_id && *zh->m_record_id) {
634                 matchStr = fileMatchStr (zh, zh->reg->keys, pr_fname, 
635                                          zh->m_record_id);
636                 if (!matchStr)
637                 {
638                     yaz_log (YLOG_WARN, "Bad match criteria (recordID)");
639                     return ZEBRA_FAIL;
640                 }
641             }
642         }
643         if (matchStr) 
644         {
645             int db_ord = zebraExplain_get_database_ord(zh->reg->zei);
646             char *rinfo = dict_lookup_ord(zh->reg->matchDict, db_ord,
647                                           matchStr);
648             if (rinfo)
649             {
650                 assert(*rinfo == sizeof(*sysno));
651                 memcpy (sysno, rinfo+1, sizeof(*sysno));
652             }
653         }
654     }
655     if (zebra_rec_keys_empty(zh->reg->keys))
656     {
657         /* the extraction process returned no information - the record
658            is probably empty - unless flagShowRecords is in use */
659         if (test_mode)
660             return ZEBRA_OK;
661     }
662
663     if (! *sysno)
664     {
665         /* new record */
666         if (delete_flag)
667         {
668             yaz_log (YLOG_LOG, "delete %s %s " ZINT_FORMAT, recordType,
669                          pr_fname, (zint) start_offset);
670             yaz_log (YLOG_WARN, "cannot delete record above (seems new)");
671             return ZEBRA_FAIL;
672         }
673         if (show_progress)
674             yaz_log (YLOG_LOG, "add %s %s " ZINT_FORMAT, recordType, pr_fname,
675                      (zint) start_offset);
676         rec = rec_new (zh->reg->records);
677
678         *sysno = rec->sysno;
679
680         recordAttr = rec_init_attr (zh->reg->zei, rec);
681         recordAttr->staticrank = extractCtrl.staticrank;
682
683         if (matchStr)
684         {
685             int db_ord = zebraExplain_get_database_ord(zh->reg->zei);
686             dict_insert_ord(zh->reg->matchDict, db_ord, matchStr,
687                             sizeof(*sysno), sysno);
688         }
689
690
691         extract_flushSortKeys (zh, *sysno, 1, zh->reg->sortKeys);
692         extract_flushRecordKeys (zh, *sysno, 1, zh->reg->keys,
693                          recordAttr->staticrank);
694         zh->records_inserted++;
695     } 
696     else
697     {
698         /* record already exists */
699         zebra_rec_keys_t delkeys = zebra_rec_keys_open();
700         zebra_rec_keys_t sortKeys = zebra_rec_keys_open();
701         if (!allow_update)
702         {
703             yaz_log (YLOG_LOG, "skipped %s %s " ZINT_FORMAT, 
704                          recordType, pr_fname, (zint) start_offset);
705             logRecord(zh);
706             return ZEBRA_FAIL;
707         }
708
709         rec = rec_get (zh->reg->records, *sysno);
710         assert (rec);
711         
712         recordAttr = rec_init_attr (zh->reg->zei, rec);
713
714         zebra_rec_keys_set_buf(delkeys,
715                                rec->info[recInfo_delKeys],
716                                rec->size[recInfo_delKeys],
717                                0);
718         zebra_rec_keys_set_buf(sortKeys,
719                                rec->info[recInfo_sortKeys],
720                                rec->size[recInfo_sortKeys],
721                                0);
722
723         extract_flushSortKeys (zh, *sysno, 0, sortKeys);
724         extract_flushRecordKeys (zh, *sysno, 0, delkeys,
725                                  recordAttr->staticrank);
726         if (delete_flag)
727         {
728             /* record going to be deleted */
729             if (zebra_rec_keys_empty(delkeys))
730             {
731                 yaz_log(YLOG_LOG, "delete %s %s " ZINT_FORMAT, recordType,
732                         pr_fname, (zint) start_offset);
733                 yaz_log(YLOG_WARN, "cannot delete file above, "
734                         "storeKeys false (3)");
735             }
736             else
737             {
738                 if (show_progress)
739                     yaz_log(YLOG_LOG, "delete %s %s " ZINT_FORMAT, recordType,
740                             pr_fname, (zint) start_offset);
741                 zh->records_deleted++;
742                 if (matchStr)
743                 {
744                     int db_ord = zebraExplain_get_database_ord(zh->reg->zei);
745                     dict_delete_ord(zh->reg->matchDict, db_ord, matchStr);
746                 }
747                 rec_del (zh->reg->records, &rec);
748             }
749             rec_free(&rec);
750             logRecord(zh);
751             return ZEBRA_OK;
752         }
753         else
754         {
755             if (show_progress)
756                     yaz_log(YLOG_LOG, "update %s %s " ZINT_FORMAT, recordType,
757                             pr_fname, (zint) ZINT_FORMAT);
758             recordAttr->staticrank = extractCtrl.staticrank;
759             extract_flushSortKeys (zh, *sysno, 1, zh->reg->sortKeys);
760             extract_flushRecordKeys (zh, *sysno, 1, zh->reg->keys, 
761                                          recordAttr->staticrank);
762             zh->records_updated++;
763         }
764         zebra_rec_keys_close(delkeys);
765         zebra_rec_keys_close(sortKeys);
766     }
767     /* update file type */
768     xfree (rec->info[recInfo_fileType]);
769     rec->info[recInfo_fileType] =
770         rec_strdup (recordType, &rec->size[recInfo_fileType]);
771
772     /* update filename */
773     xfree (rec->info[recInfo_filename]);
774     rec->info[recInfo_filename] =
775         rec_strdup (fname, &rec->size[recInfo_filename]);
776
777     /* update delete keys */
778     xfree (rec->info[recInfo_delKeys]);
779     if (!zebra_rec_keys_empty(zh->reg->keys) && zh->m_store_keys == 1)
780     {
781         zebra_rec_keys_get_buf(zh->reg->keys,
782                                &rec->info[recInfo_delKeys],
783                                &rec->size[recInfo_delKeys]);
784     }
785     else
786     {
787         rec->info[recInfo_delKeys] = NULL;
788         rec->size[recInfo_delKeys] = 0;
789     }
790     /* update sort keys */
791     xfree (rec->info[recInfo_sortKeys]);
792
793     zebra_rec_keys_get_buf(zh->reg->sortKeys,
794                            &rec->info[recInfo_sortKeys],
795                            &rec->size[recInfo_sortKeys]);
796
797     /* save file size of original record */
798     zebraExplain_recordBytesIncrement (zh->reg->zei,
799                                        - recordAttr->recordSize);
800     if (stream)
801     {
802         off_t end_offset = stream->endf(stream, 0);
803
804         if (!end_offset)
805             end_offset = stream->tellf(stream);
806         else
807             stream->seekf(stream, end_offset);
808
809         recordAttr->recordSize = end_offset - start_offset;
810         zebraExplain_recordBytesIncrement(zh->reg->zei,
811                                           recordAttr->recordSize);
812     }
813
814     /* set run-number for this record */
815     recordAttr->runNumber =
816         zebraExplain_runNumberIncrement (zh->reg->zei, 0);
817
818     /* update store data */
819     xfree (rec->info[recInfo_storeData]);
820
821     /* update store data */
822     if (zh->store_data_buf)
823     {
824         rec->size[recInfo_storeData] = zh->store_data_size;
825         rec->info[recInfo_storeData] = zh->store_data_buf;
826         zh->store_data_buf = 0;
827     }
828     else if (zh->m_store_data)
829     {
830         off_t cur_offset = stream->tellf(stream);
831
832         rec->size[recInfo_storeData] = recordAttr->recordSize;
833         rec->info[recInfo_storeData] = (char *)
834             xmalloc (recordAttr->recordSize);
835         stream->seekf(stream, start_offset);
836         stream->readf(stream, rec->info[recInfo_storeData],
837                       recordAttr->recordSize);
838         stream->seekf(stream, cur_offset);
839     }
840     else
841     {
842         rec->info[recInfo_storeData] = NULL;
843         rec->size[recInfo_storeData] = 0;
844     }
845     /* update database name */
846     xfree (rec->info[recInfo_databaseName]);
847     rec->info[recInfo_databaseName] =
848         rec_strdup (zh->basenames[0], &rec->size[recInfo_databaseName]); 
849
850     /* update offset */
851     recordAttr->recordOffset = start_offset;
852     
853     /* commit this record */
854     rec_put (zh->reg->records, &rec);
855     logRecord(zh);
856     return ZEBRA_OK;
857 }
858
859 ZEBRA_RES zebra_extract_explain(void *handle, Record rec, data1_node *n)
860 {
861     ZebraHandle zh = (ZebraHandle) handle;
862     struct recExtractCtrl extractCtrl;
863
864     if (zebraExplain_curDatabase (zh->reg->zei,
865                                   rec->info[recInfo_databaseName]))
866     {
867         abort();
868         if (zebraExplain_newDatabase (zh->reg->zei,
869                                       rec->info[recInfo_databaseName], 0))
870             abort ();
871     }
872
873     zebra_rec_keys_reset(zh->reg->keys);
874     zebra_rec_keys_reset(zh->reg->sortKeys);
875
876     extractCtrl.init = extract_init;
877     extractCtrl.tokenAdd = extract_token_add;
878     extractCtrl.schemaAdd = extract_schema_add;
879     extractCtrl.dh = zh->reg->dh;
880
881     init_extractCtrl(zh, &extractCtrl);
882
883     extractCtrl.flagShowRecords = 0;
884     extractCtrl.match_criteria[0] = '\0';
885     extractCtrl.staticrank = 0;
886     extractCtrl.handle = handle;
887     extractCtrl.first_record = 1;
888     
889     extract_set_store_data_prepare(&extractCtrl);
890
891     if (n)
892         grs_extract_tree(&extractCtrl, n);
893
894     if (rec->size[recInfo_delKeys])
895     {
896         zebra_rec_keys_t delkeys = zebra_rec_keys_open();
897         
898         zebra_rec_keys_t sortkeys = zebra_rec_keys_open();
899
900         zebra_rec_keys_set_buf(delkeys, rec->info[recInfo_delKeys],
901                                rec->size[recInfo_delKeys],
902                                0);
903         extract_flushRecordKeys (zh, rec->sysno, 0, delkeys, 0);
904         zebra_rec_keys_close(delkeys);
905
906         zebra_rec_keys_set_buf(sortkeys, rec->info[recInfo_sortKeys],
907                                rec->size[recInfo_sortKeys],
908                                0);
909
910         extract_flushSortKeys (zh, rec->sysno, 0, sortkeys);
911         zebra_rec_keys_close(sortkeys);
912     }
913     extract_flushRecordKeys (zh, rec->sysno, 1, zh->reg->keys, 0);
914     extract_flushSortKeys (zh, rec->sysno, 1, zh->reg->sortKeys);
915
916     xfree (rec->info[recInfo_delKeys]);
917     zebra_rec_keys_get_buf(zh->reg->keys,
918                            &rec->info[recInfo_delKeys], 
919                            &rec->size[recInfo_delKeys]);
920
921     xfree (rec->info[recInfo_sortKeys]);
922     zebra_rec_keys_get_buf(zh->reg->sortKeys,
923                            &rec->info[recInfo_sortKeys],
924                            &rec->size[recInfo_sortKeys]);
925     return ZEBRA_OK;
926 }
927
928 void extract_rec_keys_adjust(ZebraHandle zh, int is_insert,
929                              zebra_rec_keys_t reckeys)
930 {
931     ZebraExplainInfo zei = zh->reg->zei;
932     struct ord_stat {
933         int no;
934         int ord;
935         struct ord_stat *next;
936     };
937
938     if (zebra_rec_keys_rewind(reckeys))
939     {
940         struct ord_stat *ord_list = 0;
941         struct ord_stat *p;
942         size_t slen;
943         const char *str;
944         struct it_key key_in;
945         while(zebra_rec_keys_read(reckeys, &str, &slen, &key_in))
946         {
947             int ord = CAST_ZINT_TO_INT(key_in.mem[0]);
948
949             for (p = ord_list; p ; p = p->next)
950                 if (p->ord == ord)
951                 {
952                     p->no++;
953                     break;
954                 }
955             if (!p)
956             {
957                 p = xmalloc(sizeof(*p));
958                 p->no = 1;
959                 p->ord = ord;
960                 p->next = ord_list;
961                 ord_list = p;
962             }
963         }
964
965         p = ord_list;
966         while (p)
967         {
968             struct ord_stat *p1 = p;
969
970             if (is_insert)
971                 zebraExplain_ord_adjust_occurrences(zei, p->ord, p->no, 1);
972             else
973                 zebraExplain_ord_adjust_occurrences(zei, p->ord, - p->no, -1);
974             p = p->next;
975             xfree(p1);
976         }
977     }
978 }
979
980 void extract_flushRecordKeys (ZebraHandle zh, SYSNO sysno,
981                               int cmd,
982                               zebra_rec_keys_t reckeys,
983                               zint staticrank)
984 {
985     ZebraExplainInfo zei = zh->reg->zei;
986
987     extract_rec_keys_adjust(zh, cmd, reckeys);
988
989     if (!zh->reg->key_buf)
990     {
991         int mem= 1024*1024* atoi( res_get_def( zh->res, "memmax", "8"));
992         if (mem <= 0)
993         {
994             yaz_log(YLOG_WARN, "Invalid memory setting, using default 8 MB");
995             mem= 1024*1024*8;
996         }
997         /* FIXME: That "8" should be in a default settings include */
998         /* not hard-coded here! -H */
999         zh->reg->key_buf = (char**) xmalloc (mem);
1000         zh->reg->ptr_top = mem/sizeof(char*);
1001         zh->reg->ptr_i = 0;
1002         zh->reg->key_buf_used = 0;
1003         zh->reg->key_file_no = 0;
1004     }
1005     zebraExplain_recordCountIncrement (zei, cmd ? 1 : -1);
1006
1007     if (zebra_rec_keys_rewind(reckeys))
1008     {
1009         size_t slen;
1010         const char *str;
1011         struct it_key key_in;
1012         while(zebra_rec_keys_read(reckeys, &str, &slen, &key_in))
1013         {
1014             int ch = 0;
1015             int i, j = 0;
1016             struct it_key key_out;
1017
1018             assert(key_in.len >= 2);
1019             assert(key_in.len <= IT_KEY_LEVEL_MAX);
1020             
1021             /* check for buffer overflow */
1022             if (zh->reg->key_buf_used + 1024 > 
1023                 (zh->reg->ptr_top -zh->reg->ptr_i)*sizeof(char*))
1024                 extract_flushWriteKeys (zh, 0);
1025             
1026             ++(zh->reg->ptr_i);
1027             assert(zh->reg->ptr_i > 0);
1028             (zh->reg->key_buf)[zh->reg->ptr_top - zh->reg->ptr_i] =
1029                 (char*)zh->reg->key_buf + zh->reg->key_buf_used;
1030
1031             /* key_in.mem[0] ord/ch */
1032             /* key_in.mem[1] filter specified record ID */
1033
1034             /* encode the ordinal value (field/use/attribute) .. */
1035             ch = CAST_ZINT_TO_INT(key_in.mem[0]);
1036             zh->reg->key_buf_used +=
1037                 key_SU_encode(ch, (char*)zh->reg->key_buf +
1038                               zh->reg->key_buf_used);
1039
1040             /* copy the 0-terminated stuff from str to output */
1041             memcpy((char*)zh->reg->key_buf + zh->reg->key_buf_used, str, slen);
1042             zh->reg->key_buf_used += slen;
1043             ((char*)zh->reg->key_buf)[(zh->reg->key_buf_used)++] = '\0';
1044
1045             /* the delete/insert indicator */
1046             ((char*)zh->reg->key_buf)[(zh->reg->key_buf_used)++] = cmd;
1047
1048             if (zh->m_staticrank) /* rank config enabled ? */
1049             {
1050                 if (staticrank < 0)
1051                 {
1052                     yaz_log(YLOG_WARN, "staticrank = %ld. Setting to 0",
1053                             (long) staticrank);
1054                     staticrank = 0;
1055                 }
1056                 key_out.mem[j++] = staticrank;
1057             }
1058             
1059             if (key_in.mem[1]) /* filter specified record ID */
1060                 key_out.mem[j++] = key_in.mem[1];
1061             else
1062                 key_out.mem[j++] = sysno;
1063             for (i = 2; i < key_in.len; i++)
1064                 key_out.mem[j++] = key_in.mem[i];
1065             key_out.len = j;
1066
1067             memcpy((char*)zh->reg->key_buf + zh->reg->key_buf_used,
1068                    &key_out, sizeof(key_out));
1069             (zh->reg->key_buf_used) += sizeof(key_out);
1070         }
1071     }
1072 }
1073
1074 void extract_flushWriteKeys (ZebraHandle zh, int final)
1075         /* optimizing: if final=1, and no files written yet */
1076         /* push the keys directly to merge, sidestepping the */
1077         /* temp file altogether. Speeds small updates */
1078 {
1079     FILE *outf;
1080     char out_fname[200];
1081     char *prevcp, *cp;
1082     struct encode_info encode_info;
1083     int ptr_i = zh->reg->ptr_i;
1084     int temp_policy;
1085 #if SORT_EXTRA
1086     int i;
1087 #endif
1088     if (!zh->reg->key_buf || ptr_i <= 0)
1089     {
1090         yaz_log(log_level, "  nothing to flush section=%d buf=%p i=%d",
1091                zh->reg->key_file_no, zh->reg->key_buf, ptr_i);
1092         return;
1093     }
1094
1095     (zh->reg->key_file_no)++;
1096     yaz_log (YLOG_LOG, "sorting section %d", (zh->reg->key_file_no));
1097     yaz_log(log_level, "  sort_buff at %p n=%d",
1098                     zh->reg->key_buf + zh->reg->ptr_top - ptr_i,ptr_i);
1099 #if !SORT_EXTRA
1100     qsort (zh->reg->key_buf + zh->reg->ptr_top - ptr_i, ptr_i,
1101                sizeof(char*), key_qsort_compare);
1102
1103     /* zebra.cfg: tempfiles:  
1104        Y: always use temp files (old way) 
1105        A: use temp files, if more than one (auto) 
1106           = if this is both the last and the first 
1107        N: never bother with temp files (new) */
1108
1109     temp_policy=toupper(res_get_def(zh->res,"tempfiles","auto")[0]);
1110     if (temp_policy != 'Y' && temp_policy != 'N' && temp_policy != 'A') {
1111         yaz_log (YLOG_WARN, "Illegal tempfiles setting '%c'. using 'Auto' ", 
1112                         temp_policy);
1113         temp_policy='A';
1114     }
1115
1116     if (   ( temp_policy =='N' )   ||     /* always from memory */
1117          ( ( temp_policy =='A' ) &&       /* automatic */
1118              (zh->reg->key_file_no == 1) &&  /* this is first time */
1119              (final) ) )                     /* and last (=only) time */
1120     { /* go directly from memory */
1121         zh->reg->key_file_no =0; /* signal not to read files */
1122         zebra_index_merge(zh); 
1123         zh->reg->ptr_i = 0;
1124         zh->reg->key_buf_used = 0; 
1125         return; 
1126     }
1127
1128     /* Not doing directly from memory, write into a temp file */
1129     extract_get_fname_tmp (zh, out_fname, zh->reg->key_file_no);
1130
1131     if (!(outf = fopen (out_fname, "wb")))
1132     {
1133         yaz_log (YLOG_FATAL|YLOG_ERRNO, "fopen %s", out_fname);
1134         exit (1);
1135     }
1136     yaz_log (YLOG_LOG, "writing section %d", zh->reg->key_file_no);
1137     prevcp = cp = (zh->reg->key_buf)[zh->reg->ptr_top - ptr_i];
1138     
1139     encode_key_init (&encode_info);
1140     encode_key_write (cp, &encode_info, outf);
1141     
1142     while (--ptr_i > 0)
1143     {
1144         cp = (zh->reg->key_buf)[zh->reg->ptr_top - ptr_i];
1145         if (strcmp (cp, prevcp))
1146         {
1147             encode_key_flush ( &encode_info, outf);
1148             encode_key_init (&encode_info);
1149             encode_key_write (cp, &encode_info, outf);
1150             prevcp = cp;
1151         }
1152         else
1153             encode_key_write (cp + strlen(cp), &encode_info, outf);
1154     }
1155     encode_key_flush ( &encode_info, outf);
1156 #else
1157     qsort (key_buf + ptr_top-ptr_i, ptr_i, sizeof(char*), key_x_compare);
1158     extract_get_fname_tmp (out_fname, key_file_no);
1159
1160     if (!(outf = fopen (out_fname, "wb")))
1161     {
1162         yaz_log (YLOG_FATAL|YLOG_ERRNO, "fopen %s", out_fname);
1163         exit (1);
1164     }
1165     yaz_log (YLOG_LOG, "writing section %d", key_file_no);
1166     i = ptr_i;
1167     prevcp =  key_buf[ptr_top-i];
1168     while (1)
1169         if (!--i || strcmp (prevcp, key_buf[ptr_top-i]))
1170         {
1171             key_y_len = strlen(prevcp)+1;
1172 #if 0
1173             yaz_log (YLOG_LOG, "key_y_len: %2d %02x %02x %s",
1174                       key_y_len, prevcp[0], prevcp[1], 2+prevcp);
1175 #endif
1176             qsort (key_buf + ptr_top-ptr_i, ptr_i - i,
1177                                    sizeof(char*), key_y_compare);
1178             cp = key_buf[ptr_top-ptr_i];
1179             --key_y_len;
1180             encode_key_init (&encode_info);
1181             encode_key_write (cp, &encode_info, outf);
1182             while (--ptr_i > i)
1183             {
1184                 cp = key_buf[ptr_top-ptr_i];
1185                 encode_key_write (cp+key_y_len, &encode_info, outf);
1186             }
1187             encode_key_flush ( &encode_info, outf);
1188             if (!i)
1189                 break;
1190             prevcp = key_buf[ptr_top-ptr_i];
1191         }
1192 #endif
1193     if (fclose (outf))
1194     {
1195         yaz_log (YLOG_FATAL|YLOG_ERRNO, "fclose %s", out_fname);
1196         exit (1);
1197     }
1198     yaz_log (YLOG_LOG, "finished section %d", zh->reg->key_file_no);
1199     zh->reg->ptr_i = 0;
1200     zh->reg->key_buf_used = 0;
1201 }
1202
1203 ZEBRA_RES zebra_snippets_rec_keys(ZebraHandle zh,
1204                                   zebra_rec_keys_t reckeys,
1205                                   zebra_snippets *snippets)
1206 {
1207     NMEM nmem = nmem_create();
1208     if (zebra_rec_keys_rewind(reckeys)) 
1209     {
1210         const char *str;
1211         size_t slen;
1212         struct it_key key;
1213         while (zebra_rec_keys_read(reckeys, &str, &slen, &key))
1214         {
1215             char dst_buf[IT_MAX_WORD];
1216             char *dst_term = dst_buf;
1217             int ord;
1218             zint seqno;
1219             int index_type;
1220
1221             assert(key.len <= IT_KEY_LEVEL_MAX && key.len > 2);
1222             seqno = key.mem[key.len-1];
1223             ord = CAST_ZINT_TO_INT(key.mem[0]);
1224             
1225             zebraExplain_lookup_ord(zh->reg->zei, ord, &index_type,
1226                                     0/* db */, 0 /* string_index */);
1227             assert(index_type);
1228             zebra_term_untrans_iconv(zh, nmem, index_type,
1229                                      &dst_term, str);
1230             zebra_snippets_append(snippets, seqno, ord, dst_term);
1231             nmem_reset(nmem);
1232         }
1233     }
1234     nmem_destroy(nmem);
1235     return ZEBRA_OK;
1236 }
1237
1238 void print_rec_keys(ZebraHandle zh, zebra_rec_keys_t reckeys)
1239 {
1240     yaz_log(YLOG_LOG, "print_rec_keys");
1241     if (zebra_rec_keys_rewind(reckeys))
1242     {
1243         const char *str;
1244         size_t slen;
1245         struct it_key key;
1246         while (zebra_rec_keys_read(reckeys, &str, &slen, &key))
1247         {
1248             char dst_buf[IT_MAX_WORD];
1249             zint seqno;
1250             int index_type;
1251             int ord = CAST_ZINT_TO_INT(key.mem[0]);
1252             const char *db = 0;
1253             assert(key.len <= IT_KEY_LEVEL_MAX && key.len > 2);
1254
1255             zebraExplain_lookup_ord(zh->reg->zei, ord, &index_type, &db, 0);
1256             
1257             seqno = key.mem[key.len-1];
1258             
1259             zebra_term_untrans(zh, index_type, dst_buf, str);
1260             
1261             yaz_log(YLOG_LOG, "ord=%d seqno=" ZINT_FORMAT 
1262                     " term=%s", ord, seqno, dst_buf); 
1263         }
1264     }
1265 }
1266
1267 static void extract_add_index_string(RecWord *p, zinfo_index_category_t cat,
1268                                      const char *str, int length)
1269 {
1270     struct it_key key;
1271     ZebraHandle zh = p->extractCtrl->handle;
1272     ZebraExplainInfo zei = zh->reg->zei;
1273     int ch, i;
1274
1275     ch = zebraExplain_lookup_attr_str(zei, cat, p->index_type, p->index_name);
1276     if (ch < 0)
1277         ch = zebraExplain_add_attr_str(zei, cat, p->index_type, p->index_name);
1278
1279     i = 0;
1280     key.mem[i++] = ch;
1281     key.mem[i++] = p->record_id;
1282     key.mem[i++] = p->section_id;
1283
1284     if (zh->m_segment_indexing)
1285         key.mem[i++] = p->segment;
1286     key.mem[i++] = p->seqno;
1287     key.len = i;
1288
1289     zebra_rec_keys_write(zh->reg->keys, str, length, &key);
1290 }
1291
1292 static void extract_add_sort_string(RecWord *p, const char *str, int length)
1293 {
1294     struct it_key key;
1295     ZebraHandle zh = p->extractCtrl->handle;
1296     ZebraExplainInfo zei = zh->reg->zei;
1297     int ch;
1298     zinfo_index_category_t cat = zinfo_index_category_sort;
1299
1300     ch = zebraExplain_lookup_attr_str(zei, cat, p->index_type, p->index_name);
1301     if (ch < 0)
1302         ch = zebraExplain_add_attr_str(zei, cat, p->index_type, p->index_name);
1303     key.len = 2;
1304     key.mem[0] = ch;
1305     key.mem[1] = p->record_id;
1306
1307     zebra_rec_keys_write(zh->reg->sortKeys, str, length, &key);
1308 }
1309
1310 static void extract_add_string(RecWord *p, const char *string, int length)
1311 {
1312     ZebraHandle zh = p->extractCtrl->handle;
1313     assert (length > 0);
1314
1315     if (!p->index_name)
1316         return;
1317
1318     if (zebra_maps_is_sort(zh->reg->zebra_maps, p->index_type))
1319         extract_add_sort_string(p, string, length);
1320     else
1321     {
1322         extract_add_index_string(p, zinfo_index_category_index,
1323                                  string, length);
1324         if (zebra_maps_is_alwaysmatches(zh->reg->zebra_maps, p->index_type))
1325         {
1326             RecWord word;
1327             memcpy(&word, p, sizeof(word));
1328
1329             word.seqno = 1;
1330             extract_add_index_string(
1331                 &word, zinfo_index_category_alwaysmatches, "", 0);
1332         }
1333     }
1334 }
1335
1336 static void extract_add_incomplete_field(RecWord *p)
1337 {
1338     ZebraHandle zh = p->extractCtrl->handle;
1339     const char *b = p->term_buf;
1340     int remain = p->term_len;
1341     const char **map = 0;
1342     
1343     if (remain > 0)
1344         map = zebra_maps_input(zh->reg->zebra_maps, p->index_type, &b, remain, 0);
1345
1346     if (map)
1347     {   
1348         if (zebra_maps_is_first_in_field(zh->reg->zebra_maps, p->index_type))
1349         {
1350              /* first in field marker */
1351             extract_add_string(p, FIRST_IN_FIELD_STR, FIRST_IN_FIELD_LEN);
1352             p->seqno++;
1353         }
1354     }
1355     while (map)
1356     {
1357         char buf[IT_MAX_WORD+1];
1358         int i, remain;
1359
1360         /* Skip spaces */
1361         while (map && *map && **map == *CHR_SPACE)
1362         {
1363             remain = p->term_len - (b - p->term_buf);
1364             if (remain > 0)
1365                 map = zebra_maps_input(zh->reg->zebra_maps, p->index_type, &b,
1366                                        remain, 0);
1367             else
1368                 map = 0;
1369         }
1370         if (!map)
1371             break;
1372         i = 0;
1373         while (map && *map && **map != *CHR_SPACE)
1374         {
1375             const char *cp = *map;
1376
1377             while (i < IT_MAX_WORD && *cp)
1378                 buf[i++] = *(cp++);
1379             remain = p->term_len - (b - p->term_buf);
1380             if (remain > 0)
1381                 map = zebra_maps_input(zh->reg->zebra_maps, p->index_type, &b, remain, 0);
1382             else
1383                 map = 0;
1384         }
1385         if (!i)
1386             return;
1387         extract_add_string (p, buf, i);
1388         p->seqno++;
1389     }
1390 }
1391
1392 static void extract_add_complete_field (RecWord *p)
1393 {
1394     ZebraHandle zh = p->extractCtrl->handle;
1395     const char *b = p->term_buf;
1396     char buf[IT_MAX_WORD+1];
1397     const char **map = 0;
1398     int i = 0, remain = p->term_len;
1399
1400     if (remain > 0)
1401         map = zebra_maps_input (zh->reg->zebra_maps, p->index_type, &b, remain, 1);
1402
1403     while (remain > 0 && i < IT_MAX_WORD)
1404     {
1405         while (map && *map && **map == *CHR_SPACE)
1406         {
1407             remain = p->term_len - (b - p->term_buf);
1408
1409             if (remain > 0)
1410             {
1411                 int first = i ? 0 : 1;  /* first position */
1412                 map = zebra_maps_input(zh->reg->zebra_maps, p->index_type, &b, remain, first);
1413             }
1414             else
1415                 map = 0;
1416         }
1417         if (!map)
1418             break;
1419
1420         if (i && i < IT_MAX_WORD)
1421             buf[i++] = *CHR_SPACE;
1422         while (map && *map && **map != *CHR_SPACE)
1423         {
1424             const char *cp = *map;
1425
1426             if (**map == *CHR_CUT)
1427             {
1428                 i = 0;
1429             }
1430             else
1431             {
1432                 if (i >= IT_MAX_WORD)
1433                     break;
1434                 while (i < IT_MAX_WORD && *cp)
1435                     buf[i++] = *(cp++);
1436             }
1437             remain = p->term_len  - (b - p->term_buf);
1438             if (remain > 0)
1439             {
1440                 map = zebra_maps_input (zh->reg->zebra_maps, p->index_type, &b,
1441                                         remain, 0);
1442             }
1443             else
1444                 map = 0;
1445         }
1446     }
1447     if (!i)
1448         return;
1449     extract_add_string (p, buf, i);
1450 }
1451
1452 static void extract_token_add(RecWord *p)
1453 {
1454     ZebraHandle zh = p->extractCtrl->handle;
1455     WRBUF wrbuf;
1456
1457     if (log_level)
1458     {
1459         yaz_log(log_level, "extract_token_add "
1460                 "type=%c index=%s seqno=" ZINT_FORMAT " s=%.*s",
1461                 p->index_type, p->index_name, 
1462                 p->seqno, p->term_len, p->term_buf);
1463     }
1464     if ((wrbuf = zebra_replace(zh->reg->zebra_maps, p->index_type, 0,
1465                                p->term_buf, p->term_len)))
1466     {
1467         p->term_buf = wrbuf_buf(wrbuf);
1468         p->term_len = wrbuf_len(wrbuf);
1469     }
1470     if (zebra_maps_is_complete (zh->reg->zebra_maps, p->index_type))
1471         extract_add_complete_field (p);
1472     else
1473         extract_add_incomplete_field(p);
1474 }
1475
1476 static void extract_set_store_data_cb(struct recExtractCtrl *p,
1477                                       void *buf, size_t sz)
1478 {
1479     ZebraHandle zh = (ZebraHandle) p->handle;
1480
1481     xfree(zh->store_data_buf);
1482     zh->store_data_buf = 0;
1483     zh->store_data_size = 0;
1484     if (buf && sz)
1485     {
1486         zh->store_data_buf = xmalloc(sz);
1487         zh->store_data_size = sz;
1488         memcpy(zh->store_data_buf, buf, sz);
1489     }
1490 }
1491
1492 static void extract_set_store_data_prepare(struct recExtractCtrl *p)
1493 {
1494     ZebraHandle zh = (ZebraHandle) p->handle;
1495     xfree(zh->store_data_buf);
1496     zh->store_data_buf = 0;
1497     zh->store_data_size = 0;
1498     p->setStoreData = extract_set_store_data_cb;
1499 }
1500
1501 static void extract_schema_add (struct recExtractCtrl *p, Odr_oid *oid)
1502 {
1503     ZebraHandle zh = (ZebraHandle) p->handle;
1504     zebraExplain_addSchema (zh->reg->zei, oid);
1505 }
1506
1507 void extract_flushSortKeys (ZebraHandle zh, SYSNO sysno,
1508                             int cmd, zebra_rec_keys_t reckeys)
1509 {
1510     if (zebra_rec_keys_rewind(reckeys))
1511     {
1512         SortIdx sortIdx = zh->reg->sortIdx;
1513         size_t slen;
1514         const char *str;
1515         struct it_key key_in;
1516
1517         sortIdx_sysno (sortIdx, sysno);
1518
1519         while (zebra_rec_keys_read(reckeys, &str, &slen, &key_in))
1520         {
1521             int ord = CAST_ZINT_TO_INT(key_in.mem[0]);
1522             
1523             sortIdx_type(sortIdx, ord);
1524             if (cmd == 1)
1525                 sortIdx_add(sortIdx, str, slen);
1526             else
1527                 sortIdx_add(sortIdx, "", 1);
1528         }
1529     }
1530 }
1531
1532 static void encode_key_init(struct encode_info *i)
1533 {
1534     i->encode_handle = iscz1_start();
1535     i->decode_handle = iscz1_start();
1536 }
1537
1538 static void encode_key_write (char *k, struct encode_info *i, FILE *outf)
1539 {
1540     struct it_key key;
1541     char *bp = i->buf, *bp0;
1542     const char *src = (char *) &key;
1543
1544     /* copy term to output buf */
1545     while ((*bp++ = *k++))
1546         ;
1547     /* and copy & align key so we can mangle */
1548     memcpy (&key, k+1, sizeof(struct it_key));  /* *k is insert/delete */
1549
1550 #if 0
1551     /* debugging */
1552     key_logdump_txt(YLOG_LOG, &key, *k ? "i" : "d");
1553 #endif
1554     assert(key.mem[0] >= 0);
1555
1556     bp0 = bp++;
1557     iscz1_encode(i->encode_handle, &bp, &src);
1558
1559     *bp0 = (*k * 128) + bp - bp0 - 1; /* length and insert/delete combined */
1560     if (fwrite (i->buf, bp - i->buf, 1, outf) != 1)
1561     {
1562         yaz_log (YLOG_FATAL|YLOG_ERRNO, "fwrite");
1563         exit (1);
1564     }
1565
1566 #if 0
1567     /* debugging */
1568     if (1)
1569     {
1570         struct it_key key2;
1571         const char *src = bp0+1;
1572         char *dst = (char*) &key2;
1573         iscz1_decode(i->decode_handle, &dst, &src);
1574
1575         key_logdump_txt(YLOG_LOG, &key2, *k ? "i" : "d");
1576
1577         assert(key2.mem[1]);
1578     }
1579 #endif
1580 }
1581
1582 static void encode_key_flush (struct encode_info *i, FILE *outf)
1583
1584     iscz1_stop(i->encode_handle);
1585     iscz1_stop(i->decode_handle);
1586 }
1587
1588 /*
1589  * Local variables:
1590  * c-basic-offset: 4
1591  * indent-tabs-mode: nil
1592  * End:
1593  * vim: shiftwidth=4 tabstop=8 expandtab
1594  */
1595