Fix setting of recordAttr->recordSize in case of storeData method.
[idzebra-moved-to-github.git] / index / extract.c
1 /* $Id: extract.c,v 1.237 2006-11-16 11:11:36 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 SORT_EXTRA
1158     int i;
1159 #endif
1160     if (!zh->reg->key_buf || ptr_i <= 0)
1161     {
1162         yaz_log(log_level_extract, "  nothing to flush section=%d buf=%p i=%d",
1163                zh->reg->key_file_no, zh->reg->key_buf, ptr_i);
1164         return;
1165     }
1166
1167     (zh->reg->key_file_no)++;
1168     yaz_log (YLOG_LOG, "sorting section %d", (zh->reg->key_file_no));
1169     yaz_log(log_level_extract, "  sort_buff at %p n=%d",
1170                     zh->reg->key_buf + zh->reg->ptr_top - ptr_i,ptr_i);
1171 #if !SORT_EXTRA
1172     qsort (zh->reg->key_buf + zh->reg->ptr_top - ptr_i, ptr_i,
1173                sizeof(char*), key_qsort_compare);
1174
1175     /* zebra.cfg: tempfiles:  
1176        Y: always use temp files (old way) 
1177        A: use temp files, if more than one (auto) 
1178           = if this is both the last and the first 
1179        N: never bother with temp files (new) */
1180
1181     temp_policy=toupper(res_get_def(zh->res,"tempfiles","auto")[0]);
1182     if (temp_policy != 'Y' && temp_policy != 'N' && temp_policy != 'A') {
1183         yaz_log (YLOG_WARN, "Illegal tempfiles setting '%c'. using 'Auto' ", 
1184                         temp_policy);
1185         temp_policy='A';
1186     }
1187
1188     if (   ( temp_policy =='N' )   ||     /* always from memory */
1189          ( ( temp_policy =='A' ) &&       /* automatic */
1190              (zh->reg->key_file_no == 1) &&  /* this is first time */
1191              (final) ) )                     /* and last (=only) time */
1192     { /* go directly from memory */
1193         zh->reg->key_file_no =0; /* signal not to read files */
1194         zebra_index_merge(zh); 
1195         zh->reg->ptr_i = 0;
1196         zh->reg->key_buf_used = 0; 
1197         return; 
1198     }
1199
1200     /* Not doing directly from memory, write into a temp file */
1201     extract_get_fname_tmp (zh, out_fname, zh->reg->key_file_no);
1202
1203     if (!(outf = fopen (out_fname, "wb")))
1204     {
1205         yaz_log (YLOG_FATAL|YLOG_ERRNO, "fopen %s", out_fname);
1206         zebra_exit("extract_flushWriteKeys");
1207     }
1208     yaz_log (YLOG_LOG, "writing section %d", zh->reg->key_file_no);
1209     prevcp = cp = (zh->reg->key_buf)[zh->reg->ptr_top - ptr_i];
1210     
1211     encode_key_init (&encode_info);
1212     encode_key_write (cp, &encode_info, outf);
1213     
1214     while (--ptr_i > 0)
1215     {
1216         cp = (zh->reg->key_buf)[zh->reg->ptr_top - ptr_i];
1217         if (strcmp (cp, prevcp))
1218         {
1219             encode_key_flush ( &encode_info, outf);
1220             encode_key_init (&encode_info);
1221             encode_key_write (cp, &encode_info, outf);
1222             prevcp = cp;
1223         }
1224         else
1225             encode_key_write (cp + strlen(cp), &encode_info, outf);
1226     }
1227     encode_key_flush ( &encode_info, outf);
1228 #else
1229     qsort (key_buf + ptr_top-ptr_i, ptr_i, sizeof(char*), key_x_compare);
1230     extract_get_fname_tmp (out_fname, key_file_no);
1231
1232     if (!(outf = fopen (out_fname, "wb")))
1233     {
1234         yaz_log (YLOG_FATAL|YLOG_ERRNO, "fopen %s", out_fname);
1235         zebra_exit("extract_flushWriteKeys");
1236     }
1237     yaz_log (YLOG_LOG, "writing section %d", key_file_no);
1238     i = ptr_i;
1239     prevcp =  key_buf[ptr_top-i];
1240     while (1)
1241         if (!--i || strcmp (prevcp, key_buf[ptr_top-i]))
1242         {
1243             key_y_len = strlen(prevcp)+1;
1244 #if 0
1245             yaz_log (YLOG_LOG, "key_y_len: %2d %02x %02x %s",
1246                       key_y_len, prevcp[0], prevcp[1], 2+prevcp);
1247 #endif
1248             qsort (key_buf + ptr_top-ptr_i, ptr_i - i,
1249                                    sizeof(char*), key_y_compare);
1250             cp = key_buf[ptr_top-ptr_i];
1251             --key_y_len;
1252             encode_key_init (&encode_info);
1253             encode_key_write (cp, &encode_info, outf);
1254             while (--ptr_i > i)
1255             {
1256                 cp = key_buf[ptr_top-ptr_i];
1257                 encode_key_write (cp+key_y_len, &encode_info, outf);
1258             }
1259             encode_key_flush ( &encode_info, outf);
1260             if (!i)
1261                 break;
1262             prevcp = key_buf[ptr_top-ptr_i];
1263         }
1264 #endif
1265     if (fclose (outf))
1266     {
1267         yaz_log (YLOG_FATAL|YLOG_ERRNO, "fclose %s", out_fname);
1268         zebra_exit("extract_flushWriteKeys");
1269     }
1270     yaz_log (YLOG_LOG, "finished section %d", zh->reg->key_file_no);
1271     zh->reg->ptr_i = 0;
1272     zh->reg->key_buf_used = 0;
1273 }
1274
1275 ZEBRA_RES zebra_rec_keys_to_snippets(ZebraHandle zh,
1276                                      zebra_rec_keys_t reckeys,
1277                                      zebra_snippets *snippets)
1278 {
1279     NMEM nmem = nmem_create();
1280     if (zebra_rec_keys_rewind(reckeys)) 
1281     {
1282         const char *str;
1283         size_t slen;
1284         struct it_key key;
1285         while (zebra_rec_keys_read(reckeys, &str, &slen, &key))
1286         {
1287             char dst_buf[IT_MAX_WORD];
1288             char *dst_term = dst_buf;
1289             int ord;
1290             zint seqno;
1291             int index_type;
1292
1293             assert(key.len <= IT_KEY_LEVEL_MAX && key.len > 2);
1294             seqno = key.mem[key.len-1];
1295             ord = CAST_ZINT_TO_INT(key.mem[0]);
1296             
1297             zebraExplain_lookup_ord(zh->reg->zei, ord, &index_type,
1298                                     0/* db */, 0 /* string_index */);
1299             assert(index_type);
1300             zebra_term_untrans_iconv(zh, nmem, index_type,
1301                                      &dst_term, str);
1302             zebra_snippets_append(snippets, seqno, ord, dst_term);
1303             nmem_reset(nmem);
1304         }
1305     }
1306     nmem_destroy(nmem);
1307     return ZEBRA_OK;
1308 }
1309
1310 void print_rec_keys(ZebraHandle zh, zebra_rec_keys_t reckeys)
1311 {
1312     yaz_log(YLOG_LOG, "print_rec_keys");
1313     if (zebra_rec_keys_rewind(reckeys))
1314     {
1315         const char *str;
1316         size_t slen;
1317         struct it_key key;
1318         while (zebra_rec_keys_read(reckeys, &str, &slen, &key))
1319         {
1320             char dst_buf[IT_MAX_WORD];
1321             zint seqno;
1322             int index_type;
1323             int ord = CAST_ZINT_TO_INT(key.mem[0]);
1324             const char *db = 0;
1325             assert(key.len <= IT_KEY_LEVEL_MAX && key.len > 2);
1326
1327             zebraExplain_lookup_ord(zh->reg->zei, ord, &index_type, &db, 0);
1328             
1329             seqno = key.mem[key.len-1];
1330             
1331             zebra_term_untrans(zh, index_type, dst_buf, str);
1332             
1333             yaz_log(YLOG_LOG, "ord=%d seqno=" ZINT_FORMAT 
1334                     " term=%s", ord, seqno, dst_buf); 
1335         }
1336     }
1337 }
1338
1339 static void extract_add_index_string(RecWord *p, zinfo_index_category_t cat,
1340                                      const char *str, int length)
1341 {
1342     struct it_key key;
1343     ZebraHandle zh = p->extractCtrl->handle;
1344     ZebraExplainInfo zei = zh->reg->zei;
1345     int ch, i;
1346
1347     ch = zebraExplain_lookup_attr_str(zei, cat, p->index_type, p->index_name);
1348     if (ch < 0)
1349         ch = zebraExplain_add_attr_str(zei, cat, p->index_type, p->index_name);
1350
1351     i = 0;
1352     key.mem[i++] = ch;
1353     key.mem[i++] = p->record_id;
1354     key.mem[i++] = p->section_id;
1355
1356     if (zh->m_segment_indexing)
1357         key.mem[i++] = p->segment;
1358     key.mem[i++] = p->seqno;
1359     key.len = i;
1360
1361     zebra_rec_keys_write(zh->reg->keys, str, length, &key);
1362 }
1363
1364 static void extract_add_sort_string(RecWord *p, const char *str, int length)
1365 {
1366     struct it_key key;
1367     ZebraHandle zh = p->extractCtrl->handle;
1368     ZebraExplainInfo zei = zh->reg->zei;
1369     int ch;
1370     zinfo_index_category_t cat = zinfo_index_category_sort;
1371
1372     ch = zebraExplain_lookup_attr_str(zei, cat, p->index_type, p->index_name);
1373     if (ch < 0)
1374         ch = zebraExplain_add_attr_str(zei, cat, p->index_type, p->index_name);
1375     key.len = 2;
1376     key.mem[0] = ch;
1377     key.mem[1] = p->record_id;
1378
1379     zebra_rec_keys_write(zh->reg->sortKeys, str, length, &key);
1380 }
1381
1382 static void extract_add_string(RecWord *p, const char *string, int length)
1383 {
1384     ZebraHandle zh = p->extractCtrl->handle;
1385     assert (length > 0);
1386
1387     if (!p->index_name)
1388         return;
1389
1390     if (zebra_maps_is_sort(zh->reg->zebra_maps, p->index_type))
1391         extract_add_sort_string(p, string, length);
1392     else
1393     {
1394         extract_add_index_string(p, zinfo_index_category_index,
1395                                  string, length);
1396         if (zebra_maps_is_alwaysmatches(zh->reg->zebra_maps, p->index_type))
1397         {
1398             RecWord word;
1399             memcpy(&word, p, sizeof(word));
1400
1401             word.seqno = 1;
1402             extract_add_index_string(
1403                 &word, zinfo_index_category_alwaysmatches, "", 0);
1404         }
1405     }
1406 }
1407
1408 static void extract_add_incomplete_field(RecWord *p)
1409 {
1410     ZebraHandle zh = p->extractCtrl->handle;
1411     const char *b = p->term_buf;
1412     int remain = p->term_len;
1413     const char **map = 0;
1414     
1415     if (remain > 0)
1416         map = zebra_maps_input(zh->reg->zebra_maps, p->index_type, &b, remain, 0);
1417
1418     if (map)
1419     {   
1420         if (zebra_maps_is_first_in_field(zh->reg->zebra_maps, p->index_type))
1421         {
1422              /* first in field marker */
1423             extract_add_string(p, FIRST_IN_FIELD_STR, FIRST_IN_FIELD_LEN);
1424             p->seqno++;
1425         }
1426     }
1427     while (map)
1428     {
1429         char buf[IT_MAX_WORD+1];
1430         int i, remain;
1431
1432         /* Skip spaces */
1433         while (map && *map && **map == *CHR_SPACE)
1434         {
1435             remain = p->term_len - (b - p->term_buf);
1436             if (remain > 0)
1437                 map = zebra_maps_input(zh->reg->zebra_maps, p->index_type, &b,
1438                                        remain, 0);
1439             else
1440                 map = 0;
1441         }
1442         if (!map)
1443             break;
1444         i = 0;
1445         while (map && *map && **map != *CHR_SPACE)
1446         {
1447             const char *cp = *map;
1448
1449             while (i < IT_MAX_WORD && *cp)
1450                 buf[i++] = *(cp++);
1451             remain = p->term_len - (b - p->term_buf);
1452             if (remain > 0)
1453                 map = zebra_maps_input(zh->reg->zebra_maps, p->index_type, &b, remain, 0);
1454             else
1455                 map = 0;
1456         }
1457         if (!i)
1458             return;
1459         extract_add_string (p, buf, i);
1460         p->seqno++;
1461     }
1462 }
1463
1464 static void extract_add_complete_field (RecWord *p)
1465 {
1466     ZebraHandle zh = p->extractCtrl->handle;
1467     const char *b = p->term_buf;
1468     char buf[IT_MAX_WORD+1];
1469     const char **map = 0;
1470     int i = 0, remain = p->term_len;
1471
1472     if (remain > 0)
1473         map = zebra_maps_input (zh->reg->zebra_maps, p->index_type, &b, remain, 1);
1474
1475     while (remain > 0 && i < IT_MAX_WORD)
1476     {
1477         while (map && *map && **map == *CHR_SPACE)
1478         {
1479             remain = p->term_len - (b - p->term_buf);
1480
1481             if (remain > 0)
1482             {
1483                 int first = i ? 0 : 1;  /* first position */
1484                 map = zebra_maps_input(zh->reg->zebra_maps, p->index_type, &b, remain, first);
1485             }
1486             else
1487                 map = 0;
1488         }
1489         if (!map)
1490             break;
1491
1492         if (i && i < IT_MAX_WORD)
1493             buf[i++] = *CHR_SPACE;
1494         while (map && *map && **map != *CHR_SPACE)
1495         {
1496             const char *cp = *map;
1497
1498             if (**map == *CHR_CUT)
1499             {
1500                 i = 0;
1501             }
1502             else
1503             {
1504                 if (i >= IT_MAX_WORD)
1505                     break;
1506                 while (i < IT_MAX_WORD && *cp)
1507                     buf[i++] = *(cp++);
1508             }
1509             remain = p->term_len  - (b - p->term_buf);
1510             if (remain > 0)
1511             {
1512                 map = zebra_maps_input (zh->reg->zebra_maps, p->index_type, &b,
1513                                         remain, 0);
1514             }
1515             else
1516                 map = 0;
1517         }
1518     }
1519     if (!i)
1520         return;
1521     extract_add_string (p, buf, i);
1522 }
1523
1524 static void extract_token_add(RecWord *p)
1525 {
1526     ZebraHandle zh = p->extractCtrl->handle;
1527     WRBUF wrbuf;
1528
1529     if (log_level_extract)
1530     {
1531         yaz_log(log_level_extract, "extract_token_add "
1532                 "type=%c index=%s seqno=" ZINT_FORMAT " s=%.*s",
1533                 p->index_type, p->index_name, 
1534                 p->seqno, p->term_len, p->term_buf);
1535     }
1536     if ((wrbuf = zebra_replace(zh->reg->zebra_maps, p->index_type, 0,
1537                                p->term_buf, p->term_len)))
1538     {
1539         p->term_buf = wrbuf_buf(wrbuf);
1540         p->term_len = wrbuf_len(wrbuf);
1541     }
1542     if (zebra_maps_is_complete (zh->reg->zebra_maps, p->index_type))
1543         extract_add_complete_field (p);
1544     else
1545         extract_add_incomplete_field(p);
1546 }
1547
1548 static void extract_set_store_data_cb(struct recExtractCtrl *p,
1549                                       void *buf, size_t sz)
1550 {
1551     ZebraHandle zh = (ZebraHandle) p->handle;
1552
1553     xfree(zh->store_data_buf);
1554     zh->store_data_buf = 0;
1555     zh->store_data_size = 0;
1556     if (buf && sz)
1557     {
1558         zh->store_data_buf = xmalloc(sz);
1559         zh->store_data_size = sz;
1560         memcpy(zh->store_data_buf, buf, sz);
1561     }
1562 }
1563
1564 static void extract_set_store_data_prepare(struct recExtractCtrl *p)
1565 {
1566     ZebraHandle zh = (ZebraHandle) p->handle;
1567     xfree(zh->store_data_buf);
1568     zh->store_data_buf = 0;
1569     zh->store_data_size = 0;
1570     p->setStoreData = extract_set_store_data_cb;
1571 }
1572
1573 static void extract_schema_add (struct recExtractCtrl *p, Odr_oid *oid)
1574 {
1575     ZebraHandle zh = (ZebraHandle) p->handle;
1576     zebraExplain_addSchema (zh->reg->zei, oid);
1577 }
1578
1579 void extract_flushSortKeys (ZebraHandle zh, SYSNO sysno,
1580                             int cmd, zebra_rec_keys_t reckeys)
1581 {
1582     if (zebra_rec_keys_rewind(reckeys))
1583     {
1584         SortIdx sortIdx = zh->reg->sortIdx;
1585         size_t slen;
1586         const char *str;
1587         struct it_key key_in;
1588
1589         sortIdx_sysno (sortIdx, sysno);
1590
1591         while (zebra_rec_keys_read(reckeys, &str, &slen, &key_in))
1592         {
1593             int ord = CAST_ZINT_TO_INT(key_in.mem[0]);
1594             
1595             sortIdx_type(sortIdx, ord);
1596             if (cmd == 1)
1597                 sortIdx_add(sortIdx, str, slen);
1598             else
1599                 sortIdx_add(sortIdx, "", 1);
1600         }
1601     }
1602 }
1603
1604 static void encode_key_init(struct encode_info *i)
1605 {
1606     i->encode_handle = iscz1_start();
1607     i->decode_handle = iscz1_start();
1608 }
1609
1610 static void encode_key_write (char *k, struct encode_info *i, FILE *outf)
1611 {
1612     struct it_key key;
1613     char *bp = i->buf, *bp0;
1614     const char *src = (char *) &key;
1615
1616     /* copy term to output buf */
1617     while ((*bp++ = *k++))
1618         ;
1619     /* and copy & align key so we can mangle */
1620     memcpy (&key, k+1, sizeof(struct it_key));  /* *k is insert/delete */
1621
1622 #if 0
1623     /* debugging */
1624     key_logdump_txt(YLOG_LOG, &key, *k ? "i" : "d");
1625 #endif
1626     assert(key.mem[0] >= 0);
1627
1628     bp0 = bp++;
1629     iscz1_encode(i->encode_handle, &bp, &src);
1630
1631     *bp0 = (*k * 128) + bp - bp0 - 1; /* length and insert/delete combined */
1632     if (fwrite (i->buf, bp - i->buf, 1, outf) != 1)
1633     {
1634         yaz_log (YLOG_FATAL|YLOG_ERRNO, "fwrite");
1635         zebra_exit("encode_key_write");
1636     }
1637
1638 #if 0
1639     /* debugging */
1640     if (1)
1641     {
1642         struct it_key key2;
1643         const char *src = bp0+1;
1644         char *dst = (char*) &key2;
1645         iscz1_decode(i->decode_handle, &dst, &src);
1646
1647         key_logdump_txt(YLOG_LOG, &key2, *k ? "i" : "d");
1648
1649         assert(key2.mem[1]);
1650     }
1651 #endif
1652 }
1653
1654 static void encode_key_flush (struct encode_info *i, FILE *outf)
1655
1656     iscz1_stop(i->encode_handle);
1657     iscz1_stop(i->decode_handle);
1658 }
1659
1660 /*
1661  * Local variables:
1662  * c-basic-offset: 4
1663  * indent-tabs-mode: nil
1664  * End:
1665  * vim: shiftwidth=4 tabstop=8 expandtab
1666  */
1667