Renamed function zebra_snippets_rec_keys to zebra_rec_keys_to_snippets.
[idzebra-moved-to-github.git] / index / extract.c
1 /* $Id: extract.c,v 1.235 2006-11-09 14:39:24 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_extract = 0;
47 static int log_level_details = 0;
48 static int log_level_initialized = 0;
49
50 static void zebra_init_log_level(void)
51 {
52     if (!log_level_initialized)
53     {
54         log_level_initialized = 1;
55
56         log_level_extract = yaz_log_module_level("extract");
57         log_level_details = yaz_log_module_level("indexdetails");
58     }
59 }
60
61 static void extract_flushRecordKeys (ZebraHandle zh, SYSNO sysno,
62                                      int cmd, zebra_rec_keys_t reckeys,
63                                      zint staticrank);
64 static void extract_flushSortKeys (ZebraHandle zh, SYSNO sysno,
65                                    int cmd, zebra_rec_keys_t skp);
66 static void extract_schema_add (struct recExtractCtrl *p, Odr_oid *oid);
67 static void extract_token_add (RecWord *p);
68
69 static void encode_key_init (struct encode_info *i);
70 static void encode_key_write (char *k, struct encode_info *i, FILE *outf);
71 static void encode_key_flush (struct encode_info *i, FILE *outf);
72
73 #define USE_SHELLSORT 0
74
75 #if USE_SHELLSORT
76 static void shellsort(void *ar, int r, size_t s,
77                       int (*cmp)(const void *a, const void *b))
78 {
79     char *a = ar;
80     char v[100];
81     int h, i, j, k;
82     static const int incs[16] = { 1391376, 463792, 198768, 86961, 33936,
83                                   13776, 4592, 1968, 861, 336, 
84                                   112, 48, 21, 7, 3, 1 };
85     for ( k = 0; k < 16; k++)
86         for (h = incs[k], i = h; i < r; i++)
87         { 
88             memcpy (v, a+s*i, s);
89             j = i;
90             while (j > h && (*cmp)(a + s*(j-h), v) > 0)
91             {
92                 memcpy (a + s*j, a + s*(j-h), s);
93                 j -= h;
94             }
95             memcpy (a+s*j, v, s);
96         } 
97 }
98 #endif
99
100 static void logRecord (ZebraHandle zh)
101 {
102     ++zh->records_processed;
103     if (!(zh->records_processed % 1000))
104     {
105         yaz_log(YLOG_LOG, "Records: "ZINT_FORMAT" i/u/d "
106                 ZINT_FORMAT"/"ZINT_FORMAT"/"ZINT_FORMAT, 
107                 zh->records_processed, zh->records_inserted, 
108                 zh->records_updated, zh->records_deleted);
109     }
110 }
111
112 static void extract_add_index_string (RecWord *p, 
113                                       zinfo_index_category_t cat,
114                                       const char *str, int length);
115
116 static void extract_set_store_data_prepare(struct recExtractCtrl *p);
117
118 static void extract_init(struct recExtractCtrl *p, RecWord *w)
119 {
120     w->seqno = 1;
121     w->index_name = "any";
122     w->index_type = 'w';
123     w->extractCtrl = p;
124     w->record_id = 0;
125     w->section_id = 0;
126     w->segment = 0;
127 }
128
129 static void searchRecordKey(ZebraHandle zh,
130                             zebra_rec_keys_t reckeys,
131                             const char *index_name,
132                             const char **ws, int ws_length)
133 {
134     int i;
135     int ch = -1;
136     zinfo_index_category_t cat = zinfo_index_category_index;
137
138     for (i = 0; i<ws_length; i++)
139         ws[i] = NULL;
140
141     if (ch < 0)
142         ch = zebraExplain_lookup_attr_str(zh->reg->zei, cat, '0', index_name);
143     if (ch < 0)
144         ch = zebraExplain_lookup_attr_str(zh->reg->zei, cat, 'p', index_name);
145     if (ch < 0)
146         ch = zebraExplain_lookup_attr_str(zh->reg->zei, cat, 'w', index_name);
147
148     if (ch < 0)
149         return ;
150
151     if (zebra_rec_keys_rewind(reckeys))
152     {
153         zint startSeq = -1;
154         const char *str;
155         size_t slen;
156         struct it_key key;
157         zint seqno;
158         while (zebra_rec_keys_read(reckeys, &str, &slen, &key))
159         {
160             assert(key.len <= IT_KEY_LEVEL_MAX && key.len > 2);
161
162             seqno = key.mem[key.len-1];
163             
164             if (key.mem[0] == ch)
165             {
166                 zint woff;
167                 
168                 if (startSeq == -1)
169                     startSeq = seqno;
170                 woff = seqno - startSeq;
171                 if (woff >= 0 && woff < ws_length)
172                     ws[woff] = str;
173             }
174         }
175     }
176 }
177
178 #define FILE_MATCH_BLANK "\t "
179
180 static char *fileMatchStr (ZebraHandle zh,
181                            zebra_rec_keys_t reckeys,
182                            const char *fname, const char *spec)
183 {
184     static char dstBuf[2048];      /* static here ??? */
185     char *dst = dstBuf;
186     const char *s = spec;
187
188     while (1)
189     {
190         for (; *s && strchr(FILE_MATCH_BLANK, *s); s++)
191             ;
192         if (!*s)
193             break;
194         if (*s == '(')
195         {
196             const char *ws[32];
197             char attset_str[64], attname_str[64];
198             int i;
199             int first = 1;
200             
201             for (s++; strchr(FILE_MATCH_BLANK, *s); s++)
202                 ;
203             for (i = 0; *s && *s != ',' && *s != ')' && 
204                      !strchr(FILE_MATCH_BLANK, *s); s++)
205                 if (i+1 < sizeof(attset_str))
206                     attset_str[i++] = *s;
207             attset_str[i] = '\0';
208             
209             for (; strchr(FILE_MATCH_BLANK, *s); s++)
210                 ;
211             if (*s != ',')
212                 strcpy(attname_str, attset_str);
213             else
214             {
215                 for (s++; strchr(FILE_MATCH_BLANK, *s); s++)
216                     ;
217                 for (i = 0; *s && *s != ')' && 
218                          !strchr(FILE_MATCH_BLANK, *s); s++)
219                     if (i+1 < sizeof(attname_str))
220                         attname_str[i++] = *s;
221                 attname_str[i] = '\0';
222             }
223
224             searchRecordKey (zh, reckeys, attname_str, ws, 32);
225
226             if (*s != ')')
227             {
228                 yaz_log (YLOG_WARN, "Missing ) in match criteria %s in group %s",
229                       spec, zh->m_group ? zh->m_group : "none");
230                 return NULL;
231             }
232             s++;
233
234             for (i = 0; i<32; i++)
235                 if (ws[i])
236                 {
237                     if (first)
238                     {
239                         *dst++ = ' ';
240                         first = 0;
241                     }
242                     strcpy (dst, ws[i]);
243                     dst += strlen(ws[i]);
244                 }
245             if (first)
246             {
247                 yaz_log (YLOG_WARN, "Record didn't contain match"
248                       " fields in (%s,%s)", attset_str, attname_str);
249                 return NULL;
250             }
251         }
252         else if (*s == '$')
253         {
254             int spec_len;
255             char special[64];
256             const char *spec_src = NULL;
257             const char *s1 = ++s;
258             while (*s1 && !strchr(FILE_MATCH_BLANK, *s1))
259                 s1++;
260
261             spec_len = s1 - s;
262             if (spec_len > sizeof(special)-1)
263                 spec_len = sizeof(special)-1;
264             memcpy (special, s, spec_len);
265             special[spec_len] = '\0';
266             s = s1;
267
268             if (!strcmp (special, "group"))
269                 spec_src = zh->m_group;
270             else if (!strcmp (special, "database"))
271                 spec_src = zh->basenames[0];
272             else if (!strcmp (special, "filename")) {
273                 spec_src = fname;
274             }
275             else if (!strcmp (special, "type"))
276                 spec_src = zh->m_record_type;
277             else 
278                 spec_src = NULL;
279             if (spec_src)
280             {
281                 strcpy (dst, spec_src);
282                 dst += strlen (spec_src);
283             }
284         }
285         else if (*s == '\"' || *s == '\'')
286         {
287             int stopMarker = *s++;
288             char tmpString[64];
289             int i = 0;
290
291             while (*s && *s != stopMarker)
292             {
293                 if (i+1 < sizeof(tmpString))
294                     tmpString[i++] = *s++;
295             }
296             if (*s)
297                 s++;
298             tmpString[i] = '\0';
299             strcpy (dst, tmpString);
300             dst += strlen (tmpString);
301         }
302         else
303         {
304             yaz_log (YLOG_WARN, "Syntax error in match criteria %s in group %s",
305                   spec, zh->m_group ? zh->m_group : "none");
306             return NULL;
307         }
308         *dst++ = 1;
309     }
310     if (dst == dstBuf)
311     {
312         yaz_log (YLOG_WARN, "No match criteria for record %s in group %s",
313               fname, zh->m_group ? zh->m_group : "none");
314         return NULL;
315     }
316     *dst = '\0';
317     return dstBuf;
318 }
319
320 struct recordLogInfo {
321     const char *fname;
322     int recordOffset;
323     struct recordGroup *rGroup;
324 };
325
326 static void init_extractCtrl(ZebraHandle zh, struct recExtractCtrl *ctrl)
327 {
328     int i;
329     for (i = 0; i<256; i++)
330     {
331         if (zebra_maps_is_positioned(zh->reg->zebra_maps, i))
332             ctrl->seqno[i] = 1;
333         else
334             ctrl->seqno[i] = 0;
335     }
336     ctrl->flagShowRecords = !zh->m_flag_rw;
337 }
338
339 static void all_matches_add(struct recExtractCtrl *ctrl)
340 {
341     RecWord word;
342     extract_init(ctrl, &word);
343     word.index_name = "_ALLRECORDS";
344     word.index_type = 'w';
345     word.seqno = 1;
346     extract_add_index_string (&word, zinfo_index_category_alwaysmatches,
347                               "", 0);
348 }
349
350 ZEBRA_RES zebra_extract_file(ZebraHandle zh, SYSNO *sysno, const char *fname, 
351                              int deleteFlag)
352 {
353     ZEBRA_RES r = ZEBRA_OK;
354     int i, fd;
355     char gprefix[128];
356     char ext[128];
357     char ext_res[128];
358     struct file_read_info *fi = 0;
359     const char *original_record_type = 0;
360     RecType recType;
361     void *recTypeClientData;
362     struct ZebraRecStream stream, *streamp;
363
364     zebra_init_log_level();
365
366     if (!zh->m_group || !*zh->m_group)
367         *gprefix = '\0';
368     else
369         sprintf (gprefix, "%s.", zh->m_group);
370     
371     yaz_log(log_level_extract, "zebra_extract_file %s", fname);
372
373     /* determine file extension */
374     *ext = '\0';
375     for (i = strlen(fname); --i >= 0; )
376         if (fname[i] == '/')
377             break;
378         else if (fname[i] == '.')
379         {
380             strcpy (ext, fname+i+1);
381             break;
382         }
383     /* determine file type - depending on extension */
384     original_record_type = zh->m_record_type;
385     if (!zh->m_record_type)
386     {
387         sprintf (ext_res, "%srecordType.%s", gprefix, ext);
388         zh->m_record_type = res_get (zh->res, ext_res);
389     }
390     if (!zh->m_record_type)
391     {
392         if (zh->records_processed < zh->m_file_verbose_limit)
393             yaz_log (YLOG_LOG, "? %s", fname);
394         return 0;
395     }
396     /* determine match criteria */
397     if (!zh->m_record_id)
398     {
399         sprintf (ext_res, "%srecordId.%s", gprefix, ext);
400         zh->m_record_id = res_get (zh->res, ext_res);
401     }
402
403     if (!(recType =
404           recType_byName (zh->reg->recTypes, zh->res, zh->m_record_type,
405                           &recTypeClientData)))
406     {
407         yaz_log(YLOG_WARN, "No such record type: %s", zh->m_record_type);
408         return ZEBRA_FAIL;
409     }
410
411     switch(recType->version)
412     {
413     case 0:
414         break;
415     default:
416         yaz_log(YLOG_WARN, "Bad filter version: %s", zh->m_record_type);
417     }
418     if (sysno && deleteFlag)
419     {
420         streamp = 0;
421         fi = 0;
422     }
423     else
424     {
425         char full_rep[1024];
426
427         if (zh->path_reg && !yaz_is_abspath (fname))
428         {
429             strcpy (full_rep, zh->path_reg);
430             strcat (full_rep, "/");
431             strcat (full_rep, fname);
432         }
433         else
434             strcpy (full_rep, fname);
435         
436         if ((fd = open (full_rep, O_BINARY|O_RDONLY)) == -1)
437         {
438             yaz_log (YLOG_WARN|YLOG_ERRNO, "open %s", full_rep);
439             zh->m_record_type = original_record_type;
440             return ZEBRA_FAIL;
441         }
442         streamp = &stream;
443         zebra_create_stream_fd(streamp, fd, 0);
444     }
445     while(1)
446     {
447         r = zebra_extract_record_stream(zh, streamp,
448                                         deleteFlag,
449                                         0, /* tst_mode */
450                                         zh->m_record_type,
451                                         sysno,
452                                         0, /*match_criteria */
453                                         fname,
454                                         1, /* force_update */
455                                         1, /* allow_update */
456                                         recType, recTypeClientData);
457         if (r != ZEBRA_OK)
458         {
459             break;
460         }
461         if (sysno)
462         {
463             break;
464         }
465     }
466     if (streamp)
467         stream.destroy(streamp);
468     zh->m_record_type = original_record_type;
469     return r;
470 }
471
472 /*
473   If sysno is provided, then it's used to identify the reocord.
474   If not, and match_criteria is provided, then sysno is guessed
475   If not, and a record is provided, then sysno is got from there
476   
477  */
478
479 ZEBRA_RES zebra_buffer_extract_record(ZebraHandle zh, 
480                                       const char *buf, size_t buf_size,
481                                       int delete_flag,
482                                       int test_mode, 
483                                       const char *recordType,
484                                       SYSNO *sysno,
485                                       const char *match_criteria,
486                                       const char *fname,
487                                       int force_update,
488                                       int allow_update)
489 {
490     struct ZebraRecStream stream;
491     ZEBRA_RES res;
492     void *clientData;
493     RecType recType = 0;
494
495     if (recordType && *recordType)
496     {
497         yaz_log(log_level_extract,
498                 "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_extract, "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_log(ZebraHandle zh, int is_insert,
929                           zebra_rec_keys_t reckeys,
930                           int level)
931 {
932     if (zebra_rec_keys_rewind(reckeys))
933     {
934         size_t slen;
935         const char *str;
936         struct it_key key;
937         NMEM nmem = nmem_create();
938
939         while(zebra_rec_keys_read(reckeys, &str, &slen, &key))
940         {
941             char keystr[200]; /* room for zints to print */
942             char *dst_term = 0;
943             int ord = CAST_ZINT_TO_INT(key.mem[0]);
944             int index_type, i;
945             const char *string_index;
946             
947             zebraExplain_lookup_ord(zh->reg->zei, ord, &index_type,
948                                     0/* db */, &string_index);
949             assert(index_type);
950             zebra_term_untrans_iconv(zh, nmem, index_type,
951                                      &dst_term, str);
952             *keystr = '\0';
953             for (i = 0; i<key.len; i++)
954             {
955                 sprintf(keystr + strlen(keystr), ZINT_FORMAT " ", key.mem[i]);
956             }
957
958             if (*str < CHR_BASE_CHAR)
959             {
960                 int i;
961                 char dst_buf[200]; /* room for special chars */
962
963                 strcpy(dst_buf , "?");
964
965                 if (!strcmp(str, ""))
966                     strcpy(dst_buf, "alwaysmatches");
967                 if (!strcmp(str, FIRST_IN_FIELD_STR))
968                     strcpy(dst_buf, "firstinfield");
969                 else if (!strcmp(str, CHR_UNKNOWN))
970                     strcpy(dst_buf, "unknown");
971                 else if (!strcmp(str, CHR_SPACE))
972                     strcpy(dst_buf, "space");
973                 
974                 for (i = 0; i<slen; i++)
975                 {
976                     sprintf(dst_buf + strlen(dst_buf), " %d", str[i] & 0xff);
977                 }
978                 yaz_log(level, "%s%c %s %s", keystr, index_type,
979                         string_index, dst_buf);
980                 
981             }
982             else
983                 yaz_log(level, "%s%c %s \"%s\"", keystr, index_type,
984                         string_index, dst_term);
985
986             nmem_reset(nmem);
987         }
988         nmem_destroy(nmem);
989     }
990 }
991
992 void extract_rec_keys_adjust(ZebraHandle zh, int is_insert,
993                              zebra_rec_keys_t reckeys)
994 {
995     ZebraExplainInfo zei = zh->reg->zei;
996     struct ord_stat {
997         int no;
998         int ord;
999         struct ord_stat *next;
1000     };
1001
1002     if (zebra_rec_keys_rewind(reckeys))
1003     {
1004         struct ord_stat *ord_list = 0;
1005         struct ord_stat *p;
1006         size_t slen;
1007         const char *str;
1008         struct it_key key_in;
1009         while(zebra_rec_keys_read(reckeys, &str, &slen, &key_in))
1010         {
1011             int ord = CAST_ZINT_TO_INT(key_in.mem[0]);
1012
1013             for (p = ord_list; p ; p = p->next)
1014                 if (p->ord == ord)
1015                 {
1016                     p->no++;
1017                     break;
1018                 }
1019             if (!p)
1020             {
1021                 p = xmalloc(sizeof(*p));
1022                 p->no = 1;
1023                 p->ord = ord;
1024                 p->next = ord_list;
1025                 ord_list = p;
1026             }
1027         }
1028
1029         p = ord_list;
1030         while (p)
1031         {
1032             struct ord_stat *p1 = p;
1033
1034             if (is_insert)
1035                 zebraExplain_ord_adjust_occurrences(zei, p->ord, p->no, 1);
1036             else
1037                 zebraExplain_ord_adjust_occurrences(zei, p->ord, - p->no, -1);
1038             p = p->next;
1039             xfree(p1);
1040         }
1041     }
1042 }
1043
1044 void extract_flushRecordKeys (ZebraHandle zh, SYSNO sysno,
1045                               int cmd,
1046                               zebra_rec_keys_t reckeys,
1047                               zint staticrank)
1048 {
1049     ZebraExplainInfo zei = zh->reg->zei;
1050
1051     extract_rec_keys_adjust(zh, cmd, reckeys);
1052
1053     if (log_level_details)
1054     {
1055         yaz_log(log_level_details, "Keys for record " ZINT_FORMAT " %s",
1056                 sysno, cmd ? "insert" : "delete");
1057         extract_rec_keys_log(zh, cmd, reckeys, log_level_details);
1058     }
1059
1060     if (!zh->reg->key_buf)
1061     {
1062         int mem= 1024*1024* atoi( res_get_def( zh->res, "memmax", "8"));
1063         if (mem <= 0)
1064         {
1065             yaz_log(YLOG_WARN, "Invalid memory setting, using default 8 MB");
1066             mem= 1024*1024*8;
1067         }
1068         /* FIXME: That "8" should be in a default settings include */
1069         /* not hard-coded here! -H */
1070         zh->reg->key_buf = (char**) xmalloc (mem);
1071         zh->reg->ptr_top = mem/sizeof(char*);
1072         zh->reg->ptr_i = 0;
1073         zh->reg->key_buf_used = 0;
1074         zh->reg->key_file_no = 0;
1075     }
1076     zebraExplain_recordCountIncrement (zei, cmd ? 1 : -1);
1077
1078     if (zebra_rec_keys_rewind(reckeys))
1079     {
1080         size_t slen;
1081         const char *str;
1082         struct it_key key_in;
1083         while(zebra_rec_keys_read(reckeys, &str, &slen, &key_in))
1084         {
1085             int ch = 0;
1086             int i, j = 0;
1087             struct it_key key_out;
1088
1089             assert(key_in.len >= 2);
1090             assert(key_in.len <= IT_KEY_LEVEL_MAX);
1091             
1092             /* check for buffer overflow */
1093             if (zh->reg->key_buf_used + 1024 > 
1094                 (zh->reg->ptr_top -zh->reg->ptr_i)*sizeof(char*))
1095                 extract_flushWriteKeys (zh, 0);
1096             
1097             ++(zh->reg->ptr_i);
1098             assert(zh->reg->ptr_i > 0);
1099             (zh->reg->key_buf)[zh->reg->ptr_top - zh->reg->ptr_i] =
1100                 (char*)zh->reg->key_buf + zh->reg->key_buf_used;
1101
1102             /* key_in.mem[0] ord/ch */
1103             /* key_in.mem[1] filter specified record ID */
1104
1105             /* encode the ordinal value (field/use/attribute) .. */
1106             ch = CAST_ZINT_TO_INT(key_in.mem[0]);
1107             zh->reg->key_buf_used +=
1108                 key_SU_encode(ch, (char*)zh->reg->key_buf +
1109                               zh->reg->key_buf_used);
1110
1111             /* copy the 0-terminated stuff from str to output */
1112             memcpy((char*)zh->reg->key_buf + zh->reg->key_buf_used, str, slen);
1113             zh->reg->key_buf_used += slen;
1114             ((char*)zh->reg->key_buf)[(zh->reg->key_buf_used)++] = '\0';
1115
1116             /* the delete/insert indicator */
1117             ((char*)zh->reg->key_buf)[(zh->reg->key_buf_used)++] = cmd;
1118
1119             if (zh->m_staticrank) /* rank config enabled ? */
1120             {
1121                 if (staticrank < 0)
1122                 {
1123                     yaz_log(YLOG_WARN, "staticrank = %ld. Setting to 0",
1124                             (long) staticrank);
1125                     staticrank = 0;
1126                 }
1127                 key_out.mem[j++] = staticrank;
1128             }
1129             
1130             if (key_in.mem[1]) /* filter specified record ID */
1131                 key_out.mem[j++] = key_in.mem[1];
1132             else
1133                 key_out.mem[j++] = sysno;
1134             for (i = 2; i < key_in.len; i++)
1135                 key_out.mem[j++] = key_in.mem[i];
1136             key_out.len = j;
1137
1138             memcpy((char*)zh->reg->key_buf + zh->reg->key_buf_used,
1139                    &key_out, sizeof(key_out));
1140             (zh->reg->key_buf_used) += sizeof(key_out);
1141         }
1142     }
1143 }
1144
1145 void extract_flushWriteKeys (ZebraHandle zh, int final)
1146         /* optimizing: if final=1, and no files written yet */
1147         /* push the keys directly to merge, sidestepping the */
1148         /* temp file altogether. Speeds small updates */
1149 {
1150     FILE *outf;
1151     char out_fname[200];
1152     char *prevcp, *cp;
1153     struct encode_info encode_info;
1154     int ptr_i = zh->reg->ptr_i;
1155     int temp_policy;
1156 #if SORT_EXTRA
1157     int i;
1158 #endif
1159     if (!zh->reg->key_buf || ptr_i <= 0)
1160     {
1161         yaz_log(log_level_extract, "  nothing to flush section=%d buf=%p i=%d",
1162                zh->reg->key_file_no, zh->reg->key_buf, ptr_i);
1163         return;
1164     }
1165
1166     (zh->reg->key_file_no)++;
1167     yaz_log (YLOG_LOG, "sorting section %d", (zh->reg->key_file_no));
1168     yaz_log(log_level_extract, "  sort_buff at %p n=%d",
1169                     zh->reg->key_buf + zh->reg->ptr_top - ptr_i,ptr_i);
1170 #if !SORT_EXTRA
1171     qsort (zh->reg->key_buf + zh->reg->ptr_top - ptr_i, ptr_i,
1172                sizeof(char*), key_qsort_compare);
1173
1174     /* zebra.cfg: tempfiles:  
1175        Y: always use temp files (old way) 
1176        A: use temp files, if more than one (auto) 
1177           = if this is both the last and the first 
1178        N: never bother with temp files (new) */
1179
1180     temp_policy=toupper(res_get_def(zh->res,"tempfiles","auto")[0]);
1181     if (temp_policy != 'Y' && temp_policy != 'N' && temp_policy != 'A') {
1182         yaz_log (YLOG_WARN, "Illegal tempfiles setting '%c'. using 'Auto' ", 
1183                         temp_policy);
1184         temp_policy='A';
1185     }
1186
1187     if (   ( temp_policy =='N' )   ||     /* always from memory */
1188          ( ( temp_policy =='A' ) &&       /* automatic */
1189              (zh->reg->key_file_no == 1) &&  /* this is first time */
1190              (final) ) )                     /* and last (=only) time */
1191     { /* go directly from memory */
1192         zh->reg->key_file_no =0; /* signal not to read files */
1193         zebra_index_merge(zh); 
1194         zh->reg->ptr_i = 0;
1195         zh->reg->key_buf_used = 0; 
1196         return; 
1197     }
1198
1199     /* Not doing directly from memory, write into a temp file */
1200     extract_get_fname_tmp (zh, out_fname, zh->reg->key_file_no);
1201
1202     if (!(outf = fopen (out_fname, "wb")))
1203     {
1204         yaz_log (YLOG_FATAL|YLOG_ERRNO, "fopen %s", out_fname);
1205         exit (1);
1206     }
1207     yaz_log (YLOG_LOG, "writing section %d", zh->reg->key_file_no);
1208     prevcp = cp = (zh->reg->key_buf)[zh->reg->ptr_top - ptr_i];
1209     
1210     encode_key_init (&encode_info);
1211     encode_key_write (cp, &encode_info, outf);
1212     
1213     while (--ptr_i > 0)
1214     {
1215         cp = (zh->reg->key_buf)[zh->reg->ptr_top - ptr_i];
1216         if (strcmp (cp, prevcp))
1217         {
1218             encode_key_flush ( &encode_info, outf);
1219             encode_key_init (&encode_info);
1220             encode_key_write (cp, &encode_info, outf);
1221             prevcp = cp;
1222         }
1223         else
1224             encode_key_write (cp + strlen(cp), &encode_info, outf);
1225     }
1226     encode_key_flush ( &encode_info, outf);
1227 #else
1228     qsort (key_buf + ptr_top-ptr_i, ptr_i, sizeof(char*), key_x_compare);
1229     extract_get_fname_tmp (out_fname, key_file_no);
1230
1231     if (!(outf = fopen (out_fname, "wb")))
1232     {
1233         yaz_log (YLOG_FATAL|YLOG_ERRNO, "fopen %s", out_fname);
1234         exit (1);
1235     }
1236     yaz_log (YLOG_LOG, "writing section %d", key_file_no);
1237     i = ptr_i;
1238     prevcp =  key_buf[ptr_top-i];
1239     while (1)
1240         if (!--i || strcmp (prevcp, key_buf[ptr_top-i]))
1241         {
1242             key_y_len = strlen(prevcp)+1;
1243 #if 0
1244             yaz_log (YLOG_LOG, "key_y_len: %2d %02x %02x %s",
1245                       key_y_len, prevcp[0], prevcp[1], 2+prevcp);
1246 #endif
1247             qsort (key_buf + ptr_top-ptr_i, ptr_i - i,
1248                                    sizeof(char*), key_y_compare);
1249             cp = key_buf[ptr_top-ptr_i];
1250             --key_y_len;
1251             encode_key_init (&encode_info);
1252             encode_key_write (cp, &encode_info, outf);
1253             while (--ptr_i > i)
1254             {
1255                 cp = key_buf[ptr_top-ptr_i];
1256                 encode_key_write (cp+key_y_len, &encode_info, outf);
1257             }
1258             encode_key_flush ( &encode_info, outf);
1259             if (!i)
1260                 break;
1261             prevcp = key_buf[ptr_top-ptr_i];
1262         }
1263 #endif
1264     if (fclose (outf))
1265     {
1266         yaz_log (YLOG_FATAL|YLOG_ERRNO, "fclose %s", out_fname);
1267         exit (1);
1268     }
1269     yaz_log (YLOG_LOG, "finished section %d", zh->reg->key_file_no);
1270     zh->reg->ptr_i = 0;
1271     zh->reg->key_buf_used = 0;
1272 }
1273
1274 ZEBRA_RES zebra_rec_keys_to_snippets(ZebraHandle zh,
1275                                      zebra_rec_keys_t reckeys,
1276                                      zebra_snippets *snippets)
1277 {
1278     NMEM nmem = nmem_create();
1279     if (zebra_rec_keys_rewind(reckeys)) 
1280     {
1281         const char *str;
1282         size_t slen;
1283         struct it_key key;
1284         while (zebra_rec_keys_read(reckeys, &str, &slen, &key))
1285         {
1286             char dst_buf[IT_MAX_WORD];
1287             char *dst_term = dst_buf;
1288             int ord;
1289             zint seqno;
1290             int index_type;
1291
1292             assert(key.len <= IT_KEY_LEVEL_MAX && key.len > 2);
1293             seqno = key.mem[key.len-1];
1294             ord = CAST_ZINT_TO_INT(key.mem[0]);
1295             
1296             zebraExplain_lookup_ord(zh->reg->zei, ord, &index_type,
1297                                     0/* db */, 0 /* string_index */);
1298             assert(index_type);
1299             zebra_term_untrans_iconv(zh, nmem, index_type,
1300                                      &dst_term, str);
1301             zebra_snippets_append(snippets, seqno, ord, dst_term);
1302             nmem_reset(nmem);
1303         }
1304     }
1305     nmem_destroy(nmem);
1306     return ZEBRA_OK;
1307 }
1308
1309 void print_rec_keys(ZebraHandle zh, zebra_rec_keys_t reckeys)
1310 {
1311     yaz_log(YLOG_LOG, "print_rec_keys");
1312     if (zebra_rec_keys_rewind(reckeys))
1313     {
1314         const char *str;
1315         size_t slen;
1316         struct it_key key;
1317         while (zebra_rec_keys_read(reckeys, &str, &slen, &key))
1318         {
1319             char dst_buf[IT_MAX_WORD];
1320             zint seqno;
1321             int index_type;
1322             int ord = CAST_ZINT_TO_INT(key.mem[0]);
1323             const char *db = 0;
1324             assert(key.len <= IT_KEY_LEVEL_MAX && key.len > 2);
1325
1326             zebraExplain_lookup_ord(zh->reg->zei, ord, &index_type, &db, 0);
1327             
1328             seqno = key.mem[key.len-1];
1329             
1330             zebra_term_untrans(zh, index_type, dst_buf, str);
1331             
1332             yaz_log(YLOG_LOG, "ord=%d seqno=" ZINT_FORMAT 
1333                     " term=%s", ord, seqno, dst_buf); 
1334         }
1335     }
1336 }
1337
1338 static void extract_add_index_string(RecWord *p, zinfo_index_category_t cat,
1339                                      const char *str, int length)
1340 {
1341     struct it_key key;
1342     ZebraHandle zh = p->extractCtrl->handle;
1343     ZebraExplainInfo zei = zh->reg->zei;
1344     int ch, i;
1345
1346     ch = zebraExplain_lookup_attr_str(zei, cat, p->index_type, p->index_name);
1347     if (ch < 0)
1348         ch = zebraExplain_add_attr_str(zei, cat, p->index_type, p->index_name);
1349
1350     i = 0;
1351     key.mem[i++] = ch;
1352     key.mem[i++] = p->record_id;
1353     key.mem[i++] = p->section_id;
1354
1355     if (zh->m_segment_indexing)
1356         key.mem[i++] = p->segment;
1357     key.mem[i++] = p->seqno;
1358     key.len = i;
1359
1360     zebra_rec_keys_write(zh->reg->keys, str, length, &key);
1361 }
1362
1363 static void extract_add_sort_string(RecWord *p, const char *str, int length)
1364 {
1365     struct it_key key;
1366     ZebraHandle zh = p->extractCtrl->handle;
1367     ZebraExplainInfo zei = zh->reg->zei;
1368     int ch;
1369     zinfo_index_category_t cat = zinfo_index_category_sort;
1370
1371     ch = zebraExplain_lookup_attr_str(zei, cat, p->index_type, p->index_name);
1372     if (ch < 0)
1373         ch = zebraExplain_add_attr_str(zei, cat, p->index_type, p->index_name);
1374     key.len = 2;
1375     key.mem[0] = ch;
1376     key.mem[1] = p->record_id;
1377
1378     zebra_rec_keys_write(zh->reg->sortKeys, str, length, &key);
1379 }
1380
1381 static void extract_add_string(RecWord *p, const char *string, int length)
1382 {
1383     ZebraHandle zh = p->extractCtrl->handle;
1384     assert (length > 0);
1385
1386     if (!p->index_name)
1387         return;
1388
1389     if (zebra_maps_is_sort(zh->reg->zebra_maps, p->index_type))
1390         extract_add_sort_string(p, string, length);
1391     else
1392     {
1393         extract_add_index_string(p, zinfo_index_category_index,
1394                                  string, length);
1395         if (zebra_maps_is_alwaysmatches(zh->reg->zebra_maps, p->index_type))
1396         {
1397             RecWord word;
1398             memcpy(&word, p, sizeof(word));
1399
1400             word.seqno = 1;
1401             extract_add_index_string(
1402                 &word, zinfo_index_category_alwaysmatches, "", 0);
1403         }
1404     }
1405 }
1406
1407 static void extract_add_incomplete_field(RecWord *p)
1408 {
1409     ZebraHandle zh = p->extractCtrl->handle;
1410     const char *b = p->term_buf;
1411     int remain = p->term_len;
1412     const char **map = 0;
1413     
1414     if (remain > 0)
1415         map = zebra_maps_input(zh->reg->zebra_maps, p->index_type, &b, remain, 0);
1416
1417     if (map)
1418     {   
1419         if (zebra_maps_is_first_in_field(zh->reg->zebra_maps, p->index_type))
1420         {
1421              /* first in field marker */
1422             extract_add_string(p, FIRST_IN_FIELD_STR, FIRST_IN_FIELD_LEN);
1423             p->seqno++;
1424         }
1425     }
1426     while (map)
1427     {
1428         char buf[IT_MAX_WORD+1];
1429         int i, remain;
1430
1431         /* Skip spaces */
1432         while (map && *map && **map == *CHR_SPACE)
1433         {
1434             remain = p->term_len - (b - p->term_buf);
1435             if (remain > 0)
1436                 map = zebra_maps_input(zh->reg->zebra_maps, p->index_type, &b,
1437                                        remain, 0);
1438             else
1439                 map = 0;
1440         }
1441         if (!map)
1442             break;
1443         i = 0;
1444         while (map && *map && **map != *CHR_SPACE)
1445         {
1446             const char *cp = *map;
1447
1448             while (i < IT_MAX_WORD && *cp)
1449                 buf[i++] = *(cp++);
1450             remain = p->term_len - (b - p->term_buf);
1451             if (remain > 0)
1452                 map = zebra_maps_input(zh->reg->zebra_maps, p->index_type, &b, remain, 0);
1453             else
1454                 map = 0;
1455         }
1456         if (!i)
1457             return;
1458         extract_add_string (p, buf, i);
1459         p->seqno++;
1460     }
1461 }
1462
1463 static void extract_add_complete_field (RecWord *p)
1464 {
1465     ZebraHandle zh = p->extractCtrl->handle;
1466     const char *b = p->term_buf;
1467     char buf[IT_MAX_WORD+1];
1468     const char **map = 0;
1469     int i = 0, remain = p->term_len;
1470
1471     if (remain > 0)
1472         map = zebra_maps_input (zh->reg->zebra_maps, p->index_type, &b, remain, 1);
1473
1474     while (remain > 0 && i < IT_MAX_WORD)
1475     {
1476         while (map && *map && **map == *CHR_SPACE)
1477         {
1478             remain = p->term_len - (b - p->term_buf);
1479
1480             if (remain > 0)
1481             {
1482                 int first = i ? 0 : 1;  /* first position */
1483                 map = zebra_maps_input(zh->reg->zebra_maps, p->index_type, &b, remain, first);
1484             }
1485             else
1486                 map = 0;
1487         }
1488         if (!map)
1489             break;
1490
1491         if (i && i < IT_MAX_WORD)
1492             buf[i++] = *CHR_SPACE;
1493         while (map && *map && **map != *CHR_SPACE)
1494         {
1495             const char *cp = *map;
1496
1497             if (**map == *CHR_CUT)
1498             {
1499                 i = 0;
1500             }
1501             else
1502             {
1503                 if (i >= IT_MAX_WORD)
1504                     break;
1505                 while (i < IT_MAX_WORD && *cp)
1506                     buf[i++] = *(cp++);
1507             }
1508             remain = p->term_len  - (b - p->term_buf);
1509             if (remain > 0)
1510             {
1511                 map = zebra_maps_input (zh->reg->zebra_maps, p->index_type, &b,
1512                                         remain, 0);
1513             }
1514             else
1515                 map = 0;
1516         }
1517     }
1518     if (!i)
1519         return;
1520     extract_add_string (p, buf, i);
1521 }
1522
1523 static void extract_token_add(RecWord *p)
1524 {
1525     ZebraHandle zh = p->extractCtrl->handle;
1526     WRBUF wrbuf;
1527
1528     if (log_level_extract)
1529     {
1530         yaz_log(log_level_extract, "extract_token_add "
1531                 "type=%c index=%s seqno=" ZINT_FORMAT " s=%.*s",
1532                 p->index_type, p->index_name, 
1533                 p->seqno, p->term_len, p->term_buf);
1534     }
1535     if ((wrbuf = zebra_replace(zh->reg->zebra_maps, p->index_type, 0,
1536                                p->term_buf, p->term_len)))
1537     {
1538         p->term_buf = wrbuf_buf(wrbuf);
1539         p->term_len = wrbuf_len(wrbuf);
1540     }
1541     if (zebra_maps_is_complete (zh->reg->zebra_maps, p->index_type))
1542         extract_add_complete_field (p);
1543     else
1544         extract_add_incomplete_field(p);
1545 }
1546
1547 static void extract_set_store_data_cb(struct recExtractCtrl *p,
1548                                       void *buf, size_t sz)
1549 {
1550     ZebraHandle zh = (ZebraHandle) p->handle;
1551
1552     xfree(zh->store_data_buf);
1553     zh->store_data_buf = 0;
1554     zh->store_data_size = 0;
1555     if (buf && sz)
1556     {
1557         zh->store_data_buf = xmalloc(sz);
1558         zh->store_data_size = sz;
1559         memcpy(zh->store_data_buf, buf, sz);
1560     }
1561 }
1562
1563 static void extract_set_store_data_prepare(struct recExtractCtrl *p)
1564 {
1565     ZebraHandle zh = (ZebraHandle) p->handle;
1566     xfree(zh->store_data_buf);
1567     zh->store_data_buf = 0;
1568     zh->store_data_size = 0;
1569     p->setStoreData = extract_set_store_data_cb;
1570 }
1571
1572 static void extract_schema_add (struct recExtractCtrl *p, Odr_oid *oid)
1573 {
1574     ZebraHandle zh = (ZebraHandle) p->handle;
1575     zebraExplain_addSchema (zh->reg->zei, oid);
1576 }
1577
1578 void extract_flushSortKeys (ZebraHandle zh, SYSNO sysno,
1579                             int cmd, zebra_rec_keys_t reckeys)
1580 {
1581     if (zebra_rec_keys_rewind(reckeys))
1582     {
1583         SortIdx sortIdx = zh->reg->sortIdx;
1584         size_t slen;
1585         const char *str;
1586         struct it_key key_in;
1587
1588         sortIdx_sysno (sortIdx, sysno);
1589
1590         while (zebra_rec_keys_read(reckeys, &str, &slen, &key_in))
1591         {
1592             int ord = CAST_ZINT_TO_INT(key_in.mem[0]);
1593             
1594             sortIdx_type(sortIdx, ord);
1595             if (cmd == 1)
1596                 sortIdx_add(sortIdx, str, slen);
1597             else
1598                 sortIdx_add(sortIdx, "", 1);
1599         }
1600     }
1601 }
1602
1603 static void encode_key_init(struct encode_info *i)
1604 {
1605     i->encode_handle = iscz1_start();
1606     i->decode_handle = iscz1_start();
1607 }
1608
1609 static void encode_key_write (char *k, struct encode_info *i, FILE *outf)
1610 {
1611     struct it_key key;
1612     char *bp = i->buf, *bp0;
1613     const char *src = (char *) &key;
1614
1615     /* copy term to output buf */
1616     while ((*bp++ = *k++))
1617         ;
1618     /* and copy & align key so we can mangle */
1619     memcpy (&key, k+1, sizeof(struct it_key));  /* *k is insert/delete */
1620
1621 #if 0
1622     /* debugging */
1623     key_logdump_txt(YLOG_LOG, &key, *k ? "i" : "d");
1624 #endif
1625     assert(key.mem[0] >= 0);
1626
1627     bp0 = bp++;
1628     iscz1_encode(i->encode_handle, &bp, &src);
1629
1630     *bp0 = (*k * 128) + bp - bp0 - 1; /* length and insert/delete combined */
1631     if (fwrite (i->buf, bp - i->buf, 1, outf) != 1)
1632     {
1633         yaz_log (YLOG_FATAL|YLOG_ERRNO, "fwrite");
1634         exit (1);
1635     }
1636
1637 #if 0
1638     /* debugging */
1639     if (1)
1640     {
1641         struct it_key key2;
1642         const char *src = bp0+1;
1643         char *dst = (char*) &key2;
1644         iscz1_decode(i->decode_handle, &dst, &src);
1645
1646         key_logdump_txt(YLOG_LOG, &key2, *k ? "i" : "d");
1647
1648         assert(key2.mem[1]);
1649     }
1650 #endif
1651 }
1652
1653 static void encode_key_flush (struct encode_info *i, FILE *outf)
1654
1655     iscz1_stop(i->encode_handle);
1656     iscz1_stop(i->decode_handle);
1657 }
1658
1659 /*
1660  * Local variables:
1661  * c-basic-offset: 4
1662  * indent-tabs-mode: nil
1663  * End:
1664  * vim: shiftwidth=4 tabstop=8 expandtab
1665  */
1666