33ee2e5017f9b6142d21526522c43761d52ada91
[idzebra-moved-to-github.git] / index / extract.c
1 /* $Id: extract.c,v 1.238 2006-11-20 13:59:13 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         recordAttr->recordSize = zh->store_data_size;
828     }
829     else if (zh->m_store_data)
830     {
831         off_t cur_offset = stream->tellf(stream);
832
833         rec->size[recInfo_storeData] = recordAttr->recordSize;
834         rec->info[recInfo_storeData] = (char *)
835             xmalloc (recordAttr->recordSize);
836         stream->seekf(stream, start_offset);
837         stream->readf(stream, rec->info[recInfo_storeData],
838                       recordAttr->recordSize);
839         stream->seekf(stream, cur_offset);
840     }
841     else
842     {
843         rec->info[recInfo_storeData] = NULL;
844         rec->size[recInfo_storeData] = 0;
845     }
846     /* update database name */
847     xfree (rec->info[recInfo_databaseName]);
848     rec->info[recInfo_databaseName] =
849         rec_strdup (zh->basenames[0], &rec->size[recInfo_databaseName]); 
850
851     /* update offset */
852     recordAttr->recordOffset = start_offset;
853     
854     /* commit this record */
855     rec_put (zh->reg->records, &rec);
856     logRecord(zh);
857     return ZEBRA_OK;
858 }
859
860 ZEBRA_RES zebra_extract_explain(void *handle, Record rec, data1_node *n)
861 {
862     ZebraHandle zh = (ZebraHandle) handle;
863     struct recExtractCtrl extractCtrl;
864
865     if (zebraExplain_curDatabase (zh->reg->zei,
866                                   rec->info[recInfo_databaseName]))
867     {
868         abort();
869         if (zebraExplain_newDatabase (zh->reg->zei,
870                                       rec->info[recInfo_databaseName], 0))
871             abort ();
872     }
873
874     zebra_rec_keys_reset(zh->reg->keys);
875     zebra_rec_keys_reset(zh->reg->sortKeys);
876
877     extractCtrl.init = extract_init;
878     extractCtrl.tokenAdd = extract_token_add;
879     extractCtrl.schemaAdd = extract_schema_add;
880     extractCtrl.dh = zh->reg->dh;
881
882     init_extractCtrl(zh, &extractCtrl);
883
884     extractCtrl.flagShowRecords = 0;
885     extractCtrl.match_criteria[0] = '\0';
886     extractCtrl.staticrank = 0;
887     extractCtrl.handle = handle;
888     extractCtrl.first_record = 1;
889     
890     extract_set_store_data_prepare(&extractCtrl);
891
892     if (n)
893         grs_extract_tree(&extractCtrl, n);
894
895     if (rec->size[recInfo_delKeys])
896     {
897         zebra_rec_keys_t delkeys = zebra_rec_keys_open();
898         
899         zebra_rec_keys_t sortkeys = zebra_rec_keys_open();
900
901         zebra_rec_keys_set_buf(delkeys, rec->info[recInfo_delKeys],
902                                rec->size[recInfo_delKeys],
903                                0);
904         extract_flushRecordKeys (zh, rec->sysno, 0, delkeys, 0);
905         zebra_rec_keys_close(delkeys);
906
907         zebra_rec_keys_set_buf(sortkeys, rec->info[recInfo_sortKeys],
908                                rec->size[recInfo_sortKeys],
909                                0);
910
911         extract_flushSortKeys (zh, rec->sysno, 0, sortkeys);
912         zebra_rec_keys_close(sortkeys);
913     }
914     extract_flushRecordKeys (zh, rec->sysno, 1, zh->reg->keys, 0);
915     extract_flushSortKeys (zh, rec->sysno, 1, zh->reg->sortKeys);
916
917     xfree (rec->info[recInfo_delKeys]);
918     zebra_rec_keys_get_buf(zh->reg->keys,
919                            &rec->info[recInfo_delKeys], 
920                            &rec->size[recInfo_delKeys]);
921
922     xfree (rec->info[recInfo_sortKeys]);
923     zebra_rec_keys_get_buf(zh->reg->sortKeys,
924                            &rec->info[recInfo_sortKeys],
925                            &rec->size[recInfo_sortKeys]);
926     return ZEBRA_OK;
927 }
928
929 void extract_rec_keys_log(ZebraHandle zh, int is_insert,
930                           zebra_rec_keys_t reckeys,
931                           int level)
932 {
933     if (zebra_rec_keys_rewind(reckeys))
934     {
935         size_t slen;
936         const char *str;
937         struct it_key key;
938         NMEM nmem = nmem_create();
939
940         while(zebra_rec_keys_read(reckeys, &str, &slen, &key))
941         {
942             char keystr[200]; /* room for zints to print */
943             char *dst_term = 0;
944             int ord = CAST_ZINT_TO_INT(key.mem[0]);
945             int index_type, i;
946             const char *string_index;
947             
948             zebraExplain_lookup_ord(zh->reg->zei, ord, &index_type,
949                                     0/* db */, &string_index);
950             assert(index_type);
951             zebra_term_untrans_iconv(zh, nmem, index_type,
952                                      &dst_term, str);
953             *keystr = '\0';
954             for (i = 0; i<key.len; i++)
955             {
956                 sprintf(keystr + strlen(keystr), ZINT_FORMAT " ", key.mem[i]);
957             }
958
959             if (*str < CHR_BASE_CHAR)
960             {
961                 int i;
962                 char dst_buf[200]; /* room for special chars */
963
964                 strcpy(dst_buf , "?");
965
966                 if (!strcmp(str, ""))
967                     strcpy(dst_buf, "alwaysmatches");
968                 if (!strcmp(str, FIRST_IN_FIELD_STR))
969                     strcpy(dst_buf, "firstinfield");
970                 else if (!strcmp(str, CHR_UNKNOWN))
971                     strcpy(dst_buf, "unknown");
972                 else if (!strcmp(str, CHR_SPACE))
973                     strcpy(dst_buf, "space");
974                 
975                 for (i = 0; i<slen; i++)
976                 {
977                     sprintf(dst_buf + strlen(dst_buf), " %d", str[i] & 0xff);
978                 }
979                 yaz_log(level, "%s%c %s %s", keystr, index_type,
980                         string_index, dst_buf);
981                 
982             }
983             else
984                 yaz_log(level, "%s%c %s \"%s\"", keystr, index_type,
985                         string_index, dst_term);
986
987             nmem_reset(nmem);
988         }
989         nmem_destroy(nmem);
990     }
991 }
992
993 void extract_rec_keys_adjust(ZebraHandle zh, int is_insert,
994                              zebra_rec_keys_t reckeys)
995 {
996     ZebraExplainInfo zei = zh->reg->zei;
997     struct ord_stat {
998         int no;
999         int ord;
1000         struct ord_stat *next;
1001     };
1002
1003     if (zebra_rec_keys_rewind(reckeys))
1004     {
1005         struct ord_stat *ord_list = 0;
1006         struct ord_stat *p;
1007         size_t slen;
1008         const char *str;
1009         struct it_key key_in;
1010         while(zebra_rec_keys_read(reckeys, &str, &slen, &key_in))
1011         {
1012             int ord = CAST_ZINT_TO_INT(key_in.mem[0]);
1013
1014             for (p = ord_list; p ; p = p->next)
1015                 if (p->ord == ord)
1016                 {
1017                     p->no++;
1018                     break;
1019                 }
1020             if (!p)
1021             {
1022                 p = xmalloc(sizeof(*p));
1023                 p->no = 1;
1024                 p->ord = ord;
1025                 p->next = ord_list;
1026                 ord_list = p;
1027             }
1028         }
1029
1030         p = ord_list;
1031         while (p)
1032         {
1033             struct ord_stat *p1 = p;
1034
1035             if (is_insert)
1036                 zebraExplain_ord_adjust_occurrences(zei, p->ord, p->no, 1);
1037             else
1038                 zebraExplain_ord_adjust_occurrences(zei, p->ord, - p->no, -1);
1039             p = p->next;
1040             xfree(p1);
1041         }
1042     }
1043 }
1044
1045 void extract_flushRecordKeys (ZebraHandle zh, SYSNO sysno,
1046                               int cmd,
1047                               zebra_rec_keys_t reckeys,
1048                               zint staticrank)
1049 {
1050     ZebraExplainInfo zei = zh->reg->zei;
1051
1052     extract_rec_keys_adjust(zh, cmd, reckeys);
1053
1054     if (log_level_details)
1055     {
1056         yaz_log(log_level_details, "Keys for record " ZINT_FORMAT " %s",
1057                 sysno, cmd ? "insert" : "delete");
1058         extract_rec_keys_log(zh, cmd, reckeys, log_level_details);
1059     }
1060
1061     if (!zh->reg->key_buf)
1062     {
1063         int mem= 1024*1024* atoi( res_get_def( zh->res, "memmax", "8"));
1064         if (mem <= 0)
1065         {
1066             yaz_log(YLOG_WARN, "Invalid memory setting, using default 8 MB");
1067             mem= 1024*1024*8;
1068         }
1069         /* FIXME: That "8" should be in a default settings include */
1070         /* not hard-coded here! -H */
1071         zh->reg->key_buf = (char**) xmalloc (mem);
1072         zh->reg->ptr_top = mem/sizeof(char*);
1073         zh->reg->ptr_i = 0;
1074         zh->reg->key_buf_used = 0;
1075         zh->reg->key_file_no = 0;
1076     }
1077     zebraExplain_recordCountIncrement (zei, cmd ? 1 : -1);
1078
1079     if (zebra_rec_keys_rewind(reckeys))
1080     {
1081         size_t slen;
1082         const char *str;
1083         struct it_key key_in;
1084         while(zebra_rec_keys_read(reckeys, &str, &slen, &key_in))
1085         {
1086             int ch = 0;
1087             int i, j = 0;
1088             struct it_key key_out;
1089
1090             assert(key_in.len >= 2);
1091             assert(key_in.len <= IT_KEY_LEVEL_MAX);
1092             
1093             /* check for buffer overflow */
1094             if (zh->reg->key_buf_used + 1024 > 
1095                 (zh->reg->ptr_top -zh->reg->ptr_i)*sizeof(char*))
1096                 extract_flushWriteKeys (zh, 0);
1097             
1098             ++(zh->reg->ptr_i);
1099             assert(zh->reg->ptr_i > 0);
1100             (zh->reg->key_buf)[zh->reg->ptr_top - zh->reg->ptr_i] =
1101                 (char*)zh->reg->key_buf + zh->reg->key_buf_used;
1102
1103             /* key_in.mem[0] ord/ch */
1104             /* key_in.mem[1] filter specified record ID */
1105
1106             /* encode the ordinal value (field/use/attribute) .. */
1107             ch = CAST_ZINT_TO_INT(key_in.mem[0]);
1108             zh->reg->key_buf_used +=
1109                 key_SU_encode(ch, (char*)zh->reg->key_buf +
1110                               zh->reg->key_buf_used);
1111
1112             /* copy the 0-terminated stuff from str to output */
1113             memcpy((char*)zh->reg->key_buf + zh->reg->key_buf_used, str, slen);
1114             zh->reg->key_buf_used += slen;
1115             ((char*)zh->reg->key_buf)[(zh->reg->key_buf_used)++] = '\0';
1116
1117             /* the delete/insert indicator */
1118             ((char*)zh->reg->key_buf)[(zh->reg->key_buf_used)++] = cmd;
1119
1120             if (zh->m_staticrank) /* rank config enabled ? */
1121             {
1122                 if (staticrank < 0)
1123                 {
1124                     yaz_log(YLOG_WARN, "staticrank = %ld. Setting to 0",
1125                             (long) staticrank);
1126                     staticrank = 0;
1127                 }
1128                 key_out.mem[j++] = staticrank;
1129             }
1130             
1131             if (key_in.mem[1]) /* filter specified record ID */
1132                 key_out.mem[j++] = key_in.mem[1];
1133             else
1134                 key_out.mem[j++] = sysno;
1135             for (i = 2; i < key_in.len; i++)
1136                 key_out.mem[j++] = key_in.mem[i];
1137             key_out.len = j;
1138
1139             memcpy((char*)zh->reg->key_buf + zh->reg->key_buf_used,
1140                    &key_out, sizeof(key_out));
1141             (zh->reg->key_buf_used) += sizeof(key_out);
1142         }
1143     }
1144 }
1145
1146 void extract_flushWriteKeys (ZebraHandle zh, int final)
1147         /* optimizing: if final=1, and no files written yet */
1148         /* push the keys directly to merge, sidestepping the */
1149         /* temp file altogether. Speeds small updates */
1150 {
1151     FILE *outf;
1152     char out_fname[200];
1153     char *prevcp, *cp;
1154     struct encode_info encode_info;
1155     int ptr_i = zh->reg->ptr_i;
1156     int temp_policy;
1157     if (!zh->reg->key_buf || ptr_i <= 0)
1158     {
1159         yaz_log(log_level_extract, "  nothing to flush section=%d buf=%p i=%d",
1160                zh->reg->key_file_no, zh->reg->key_buf, ptr_i);
1161         return;
1162     }
1163
1164     (zh->reg->key_file_no)++;
1165     yaz_log (YLOG_LOG, "sorting section %d", (zh->reg->key_file_no));
1166     yaz_log(log_level_extract, "  sort_buff at %p n=%d",
1167                     zh->reg->key_buf + zh->reg->ptr_top - ptr_i,ptr_i);
1168
1169
1170 #if USE_SHELLSORT
1171     shellsort(zh->reg->key_buf + zh->reg->ptr_top - ptr_i, ptr_i,
1172               sizeof(char*), key_qsort_compare);
1173 #else
1174     qsort(zh->reg->key_buf + zh->reg->ptr_top - ptr_i, ptr_i,
1175           sizeof(char*), key_qsort_compare);
1176 #endif
1177     /* zebra.cfg: tempfiles:  
1178        Y: always use temp files (old way) 
1179        A: use temp files, if more than one (auto) 
1180           = if this is both the last and the first 
1181        N: never bother with temp files (new) */
1182
1183     temp_policy=toupper(res_get_def(zh->res,"tempfiles","auto")[0]);
1184     if (temp_policy != 'Y' && temp_policy != 'N' && temp_policy != 'A') {
1185         yaz_log (YLOG_WARN, "Illegal tempfiles setting '%c'. using 'Auto' ", 
1186                         temp_policy);
1187         temp_policy='A';
1188     }
1189
1190     if (   ( temp_policy =='N' )   ||     /* always from memory */
1191          ( ( temp_policy =='A' ) &&       /* automatic */
1192              (zh->reg->key_file_no == 1) &&  /* this is first time */
1193              (final) ) )                     /* and last (=only) time */
1194     { /* go directly from memory */
1195         zh->reg->key_file_no =0; /* signal not to read files */
1196         zebra_index_merge(zh); 
1197         zh->reg->ptr_i = 0;
1198         zh->reg->key_buf_used = 0; 
1199         return; 
1200     }
1201
1202     /* Not doing directly from memory, write into a temp file */
1203     extract_get_fname_tmp (zh, out_fname, zh->reg->key_file_no);
1204
1205     if (!(outf = fopen (out_fname, "wb")))
1206     {
1207         yaz_log (YLOG_FATAL|YLOG_ERRNO, "fopen %s", out_fname);
1208         zebra_exit("extract_flushWriteKeys");
1209     }
1210     yaz_log (YLOG_LOG, "writing section %d", zh->reg->key_file_no);
1211     prevcp = cp = (zh->reg->key_buf)[zh->reg->ptr_top - ptr_i];
1212     
1213     encode_key_init (&encode_info);
1214     encode_key_write (cp, &encode_info, outf);
1215     
1216     while (--ptr_i > 0)
1217     {
1218         cp = (zh->reg->key_buf)[zh->reg->ptr_top - ptr_i];
1219         if (strcmp (cp, prevcp))
1220         {
1221             encode_key_flush ( &encode_info, outf);
1222             encode_key_init (&encode_info);
1223             encode_key_write (cp, &encode_info, outf);
1224             prevcp = cp;
1225         }
1226         else
1227             encode_key_write (cp + strlen(cp), &encode_info, outf);
1228     }
1229     encode_key_flush ( &encode_info, outf);
1230     if (fclose (outf))
1231     {
1232         yaz_log (YLOG_FATAL|YLOG_ERRNO, "fclose %s", out_fname);
1233         zebra_exit("extract_flushWriteKeys");
1234     }
1235     yaz_log (YLOG_LOG, "finished section %d", zh->reg->key_file_no);
1236     zh->reg->ptr_i = 0;
1237     zh->reg->key_buf_used = 0;
1238 }
1239
1240 ZEBRA_RES zebra_rec_keys_to_snippets(ZebraHandle zh,
1241                                      zebra_rec_keys_t reckeys,
1242                                      zebra_snippets *snippets)
1243 {
1244     NMEM nmem = nmem_create();
1245     if (zebra_rec_keys_rewind(reckeys)) 
1246     {
1247         const char *str;
1248         size_t slen;
1249         struct it_key key;
1250         while (zebra_rec_keys_read(reckeys, &str, &slen, &key))
1251         {
1252             char dst_buf[IT_MAX_WORD];
1253             char *dst_term = dst_buf;
1254             int ord;
1255             zint seqno;
1256             int index_type;
1257
1258             assert(key.len <= IT_KEY_LEVEL_MAX && key.len > 2);
1259             seqno = key.mem[key.len-1];
1260             ord = CAST_ZINT_TO_INT(key.mem[0]);
1261             
1262             zebraExplain_lookup_ord(zh->reg->zei, ord, &index_type,
1263                                     0/* db */, 0 /* string_index */);
1264             assert(index_type);
1265             zebra_term_untrans_iconv(zh, nmem, index_type,
1266                                      &dst_term, str);
1267             zebra_snippets_append(snippets, seqno, ord, dst_term);
1268             nmem_reset(nmem);
1269         }
1270     }
1271     nmem_destroy(nmem);
1272     return ZEBRA_OK;
1273 }
1274
1275 void print_rec_keys(ZebraHandle zh, zebra_rec_keys_t reckeys)
1276 {
1277     yaz_log(YLOG_LOG, "print_rec_keys");
1278     if (zebra_rec_keys_rewind(reckeys))
1279     {
1280         const char *str;
1281         size_t slen;
1282         struct it_key key;
1283         while (zebra_rec_keys_read(reckeys, &str, &slen, &key))
1284         {
1285             char dst_buf[IT_MAX_WORD];
1286             zint seqno;
1287             int index_type;
1288             int ord = CAST_ZINT_TO_INT(key.mem[0]);
1289             const char *db = 0;
1290             assert(key.len <= IT_KEY_LEVEL_MAX && key.len > 2);
1291
1292             zebraExplain_lookup_ord(zh->reg->zei, ord, &index_type, &db, 0);
1293             
1294             seqno = key.mem[key.len-1];
1295             
1296             zebra_term_untrans(zh, index_type, dst_buf, str);
1297             
1298             yaz_log(YLOG_LOG, "ord=%d seqno=" ZINT_FORMAT 
1299                     " term=%s", ord, seqno, dst_buf); 
1300         }
1301     }
1302 }
1303
1304 static void extract_add_index_string(RecWord *p, zinfo_index_category_t cat,
1305                                      const char *str, int length)
1306 {
1307     struct it_key key;
1308     ZebraHandle zh = p->extractCtrl->handle;
1309     ZebraExplainInfo zei = zh->reg->zei;
1310     int ch, i;
1311
1312     ch = zebraExplain_lookup_attr_str(zei, cat, p->index_type, p->index_name);
1313     if (ch < 0)
1314         ch = zebraExplain_add_attr_str(zei, cat, p->index_type, p->index_name);
1315
1316     i = 0;
1317     key.mem[i++] = ch;
1318     key.mem[i++] = p->record_id;
1319     key.mem[i++] = p->section_id;
1320
1321     if (zh->m_segment_indexing)
1322         key.mem[i++] = p->segment;
1323     key.mem[i++] = p->seqno;
1324     key.len = i;
1325
1326     zebra_rec_keys_write(zh->reg->keys, str, length, &key);
1327 }
1328
1329 static void extract_add_sort_string(RecWord *p, const char *str, int length)
1330 {
1331     struct it_key key;
1332     ZebraHandle zh = p->extractCtrl->handle;
1333     ZebraExplainInfo zei = zh->reg->zei;
1334     int ch;
1335     zinfo_index_category_t cat = zinfo_index_category_sort;
1336
1337     ch = zebraExplain_lookup_attr_str(zei, cat, p->index_type, p->index_name);
1338     if (ch < 0)
1339         ch = zebraExplain_add_attr_str(zei, cat, p->index_type, p->index_name);
1340     key.len = 2;
1341     key.mem[0] = ch;
1342     key.mem[1] = p->record_id;
1343
1344     zebra_rec_keys_write(zh->reg->sortKeys, str, length, &key);
1345 }
1346
1347 static void extract_add_string(RecWord *p, const char *string, int length)
1348 {
1349     ZebraHandle zh = p->extractCtrl->handle;
1350     assert (length > 0);
1351
1352     if (!p->index_name)
1353         return;
1354
1355     if (zebra_maps_is_sort(zh->reg->zebra_maps, p->index_type))
1356         extract_add_sort_string(p, string, length);
1357     else
1358     {
1359         extract_add_index_string(p, zinfo_index_category_index,
1360                                  string, length);
1361         if (zebra_maps_is_alwaysmatches(zh->reg->zebra_maps, p->index_type))
1362         {
1363             RecWord word;
1364             memcpy(&word, p, sizeof(word));
1365
1366             word.seqno = 1;
1367             extract_add_index_string(
1368                 &word, zinfo_index_category_alwaysmatches, "", 0);
1369         }
1370     }
1371 }
1372
1373 static void extract_add_incomplete_field(RecWord *p)
1374 {
1375     ZebraHandle zh = p->extractCtrl->handle;
1376     const char *b = p->term_buf;
1377     int remain = p->term_len;
1378     const char **map = 0;
1379     
1380     if (remain > 0)
1381         map = zebra_maps_input(zh->reg->zebra_maps, p->index_type, &b, remain, 0);
1382
1383     if (map)
1384     {   
1385         if (zebra_maps_is_first_in_field(zh->reg->zebra_maps, p->index_type))
1386         {
1387              /* first in field marker */
1388             extract_add_string(p, FIRST_IN_FIELD_STR, FIRST_IN_FIELD_LEN);
1389             p->seqno++;
1390         }
1391     }
1392     while (map)
1393     {
1394         char buf[IT_MAX_WORD+1];
1395         int i, remain;
1396
1397         /* Skip spaces */
1398         while (map && *map && **map == *CHR_SPACE)
1399         {
1400             remain = p->term_len - (b - p->term_buf);
1401             if (remain > 0)
1402                 map = zebra_maps_input(zh->reg->zebra_maps, p->index_type, &b,
1403                                        remain, 0);
1404             else
1405                 map = 0;
1406         }
1407         if (!map)
1408             break;
1409         i = 0;
1410         while (map && *map && **map != *CHR_SPACE)
1411         {
1412             const char *cp = *map;
1413
1414             while (i < IT_MAX_WORD && *cp)
1415                 buf[i++] = *(cp++);
1416             remain = p->term_len - (b - p->term_buf);
1417             if (remain > 0)
1418                 map = zebra_maps_input(zh->reg->zebra_maps, p->index_type, &b, remain, 0);
1419             else
1420                 map = 0;
1421         }
1422         if (!i)
1423             return;
1424         extract_add_string (p, buf, i);
1425         p->seqno++;
1426     }
1427 }
1428
1429 static void extract_add_complete_field (RecWord *p)
1430 {
1431     ZebraHandle zh = p->extractCtrl->handle;
1432     const char *b = p->term_buf;
1433     char buf[IT_MAX_WORD+1];
1434     const char **map = 0;
1435     int i = 0, remain = p->term_len;
1436
1437     if (remain > 0)
1438         map = zebra_maps_input (zh->reg->zebra_maps, p->index_type, &b, remain, 1);
1439
1440     while (remain > 0 && i < IT_MAX_WORD)
1441     {
1442         while (map && *map && **map == *CHR_SPACE)
1443         {
1444             remain = p->term_len - (b - p->term_buf);
1445
1446             if (remain > 0)
1447             {
1448                 int first = i ? 0 : 1;  /* first position */
1449                 map = zebra_maps_input(zh->reg->zebra_maps, p->index_type, &b, remain, first);
1450             }
1451             else
1452                 map = 0;
1453         }
1454         if (!map)
1455             break;
1456
1457         if (i && i < IT_MAX_WORD)
1458             buf[i++] = *CHR_SPACE;
1459         while (map && *map && **map != *CHR_SPACE)
1460         {
1461             const char *cp = *map;
1462
1463             if (**map == *CHR_CUT)
1464             {
1465                 i = 0;
1466             }
1467             else
1468             {
1469                 if (i >= IT_MAX_WORD)
1470                     break;
1471                 while (i < IT_MAX_WORD && *cp)
1472                     buf[i++] = *(cp++);
1473             }
1474             remain = p->term_len  - (b - p->term_buf);
1475             if (remain > 0)
1476             {
1477                 map = zebra_maps_input (zh->reg->zebra_maps, p->index_type, &b,
1478                                         remain, 0);
1479             }
1480             else
1481                 map = 0;
1482         }
1483     }
1484     if (!i)
1485         return;
1486     extract_add_string (p, buf, i);
1487 }
1488
1489 static void extract_token_add(RecWord *p)
1490 {
1491     ZebraHandle zh = p->extractCtrl->handle;
1492     WRBUF wrbuf;
1493
1494     if (log_level_extract)
1495     {
1496         yaz_log(log_level_extract, "extract_token_add "
1497                 "type=%c index=%s seqno=" ZINT_FORMAT " s=%.*s",
1498                 p->index_type, p->index_name, 
1499                 p->seqno, p->term_len, p->term_buf);
1500     }
1501     if ((wrbuf = zebra_replace(zh->reg->zebra_maps, p->index_type, 0,
1502                                p->term_buf, p->term_len)))
1503     {
1504         p->term_buf = wrbuf_buf(wrbuf);
1505         p->term_len = wrbuf_len(wrbuf);
1506     }
1507     if (zebra_maps_is_complete (zh->reg->zebra_maps, p->index_type))
1508         extract_add_complete_field (p);
1509     else
1510         extract_add_incomplete_field(p);
1511 }
1512
1513 static void extract_set_store_data_cb(struct recExtractCtrl *p,
1514                                       void *buf, size_t sz)
1515 {
1516     ZebraHandle zh = (ZebraHandle) p->handle;
1517
1518     xfree(zh->store_data_buf);
1519     zh->store_data_buf = 0;
1520     zh->store_data_size = 0;
1521     if (buf && sz)
1522     {
1523         zh->store_data_buf = xmalloc(sz);
1524         zh->store_data_size = sz;
1525         memcpy(zh->store_data_buf, buf, sz);
1526     }
1527 }
1528
1529 static void extract_set_store_data_prepare(struct recExtractCtrl *p)
1530 {
1531     ZebraHandle zh = (ZebraHandle) p->handle;
1532     xfree(zh->store_data_buf);
1533     zh->store_data_buf = 0;
1534     zh->store_data_size = 0;
1535     p->setStoreData = extract_set_store_data_cb;
1536 }
1537
1538 static void extract_schema_add (struct recExtractCtrl *p, Odr_oid *oid)
1539 {
1540     ZebraHandle zh = (ZebraHandle) p->handle;
1541     zebraExplain_addSchema (zh->reg->zei, oid);
1542 }
1543
1544 void extract_flushSortKeys (ZebraHandle zh, SYSNO sysno,
1545                             int cmd, zebra_rec_keys_t reckeys)
1546 {
1547     if (zebra_rec_keys_rewind(reckeys))
1548     {
1549         SortIdx sortIdx = zh->reg->sortIdx;
1550         size_t slen;
1551         const char *str;
1552         struct it_key key_in;
1553
1554         sortIdx_sysno (sortIdx, sysno);
1555
1556         while (zebra_rec_keys_read(reckeys, &str, &slen, &key_in))
1557         {
1558             int ord = CAST_ZINT_TO_INT(key_in.mem[0]);
1559             
1560             sortIdx_type(sortIdx, ord);
1561             if (cmd == 1)
1562                 sortIdx_add(sortIdx, str, slen);
1563             else
1564                 sortIdx_add(sortIdx, "", 1);
1565         }
1566     }
1567 }
1568
1569 static void encode_key_init(struct encode_info *i)
1570 {
1571     i->encode_handle = iscz1_start();
1572     i->decode_handle = iscz1_start();
1573 }
1574
1575 static void encode_key_write (char *k, struct encode_info *i, FILE *outf)
1576 {
1577     struct it_key key;
1578     char *bp = i->buf, *bp0;
1579     const char *src = (char *) &key;
1580
1581     /* copy term to output buf */
1582     while ((*bp++ = *k++))
1583         ;
1584     /* and copy & align key so we can mangle */
1585     memcpy (&key, k+1, sizeof(struct it_key));  /* *k is insert/delete */
1586
1587 #if 0
1588     /* debugging */
1589     key_logdump_txt(YLOG_LOG, &key, *k ? "i" : "d");
1590 #endif
1591     assert(key.mem[0] >= 0);
1592
1593     bp0 = bp++;
1594     iscz1_encode(i->encode_handle, &bp, &src);
1595
1596     *bp0 = (*k * 128) + bp - bp0 - 1; /* length and insert/delete combined */
1597     if (fwrite (i->buf, bp - i->buf, 1, outf) != 1)
1598     {
1599         yaz_log (YLOG_FATAL|YLOG_ERRNO, "fwrite");
1600         zebra_exit("encode_key_write");
1601     }
1602
1603 #if 0
1604     /* debugging */
1605     if (1)
1606     {
1607         struct it_key key2;
1608         const char *src = bp0+1;
1609         char *dst = (char*) &key2;
1610         iscz1_decode(i->decode_handle, &dst, &src);
1611
1612         key_logdump_txt(YLOG_LOG, &key2, *k ? "i" : "d");
1613
1614         assert(key2.mem[1]);
1615     }
1616 #endif
1617 }
1618
1619 static void encode_key_flush (struct encode_info *i, FILE *outf)
1620
1621     iscz1_stop(i->encode_handle);
1622     iscz1_stop(i->decode_handle);
1623 }
1624
1625 /*
1626  * Local variables:
1627  * c-basic-offset: 4
1628  * indent-tabs-mode: nil
1629  * End:
1630  * vim: shiftwidth=4 tabstop=8 expandtab
1631  */
1632