Added support for alwaysmatches on regular indexes. Is enabled
[idzebra-moved-to-github.git] / index / extract.c
1 /* $Id: extract.c,v 1.222 2006-06-22 09:48:08 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 Zebra; see the file LICENSE.zebra.  If not, write to the
19 Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
20 02111-1307, USA.
21 */
22
23 #include <stdio.h>
24 #include <assert.h>
25 #include <ctype.h>
26 #ifdef WIN32
27 #include <io.h>
28 #endif
29 #if HAVE_UNISTD_H
30 #include <unistd.h>
31 #endif
32 #include <fcntl.h>
33
34 #include "index.h"
35 #include "orddict.h"
36 #include <direntz.h>
37 #include <charmap.h>
38
39 #define ENCODE_BUFLEN 768
40 struct encode_info {
41     void *encode_handle;
42     void *decode_handle;
43     char buf[ENCODE_BUFLEN];
44 };
45
46 static int log_level = 0;
47 static int log_level_initialized = 1;
48
49 static void zebra_init_log_level()
50 {
51     if (!log_level_initialized)
52     {
53         log_level = yaz_log_module_level("extract");
54         log_level_initialized = 1;
55     }
56 }
57
58 static void extract_flushRecordKeys (ZebraHandle zh, SYSNO sysno,
59                                      int cmd, zebra_rec_keys_t reckeys,
60                                      zint staticrank);
61 static void extract_flushSortKeys (ZebraHandle zh, SYSNO sysno,
62                                    int cmd, zebra_rec_keys_t skp);
63 static void extract_schema_add (struct recExtractCtrl *p, Odr_oid *oid);
64 static void extract_token_add (RecWord *p);
65
66 static void encode_key_init (struct encode_info *i);
67 static void encode_key_write (char *k, struct encode_info *i, FILE *outf);
68 static void encode_key_flush (struct encode_info *i, FILE *outf);
69
70 #define USE_SHELLSORT 0
71
72 #if USE_SHELLSORT
73 static void shellsort(void *ar, int r, size_t s,
74                       int (*cmp)(const void *a, const void *b))
75 {
76     char *a = ar;
77     char v[100];
78     int h, i, j, k;
79     static const int incs[16] = { 1391376, 463792, 198768, 86961, 33936,
80                                   13776, 4592, 1968, 861, 336, 
81                                   112, 48, 21, 7, 3, 1 };
82     for ( k = 0; k < 16; k++)
83         for (h = incs[k], i = h; i < r; i++)
84         { 
85             memcpy (v, a+s*i, s);
86             j = i;
87             while (j > h && (*cmp)(a + s*(j-h), v) > 0)
88             {
89                 memcpy (a + s*j, a + s*(j-h), s);
90                 j -= h;
91             }
92             memcpy (a+s*j, v, s);
93         } 
94 }
95 #endif
96
97 static void logRecord (ZebraHandle zh)
98 {
99     ++zh->records_processed;
100     if (!(zh->records_processed % 1000))
101     {
102         yaz_log(YLOG_LOG, "Records: "ZINT_FORMAT" i/u/d "
103                 ZINT_FORMAT"/"ZINT_FORMAT"/"ZINT_FORMAT, 
104                 zh->records_processed, zh->records_inserted, 
105                 zh->records_updated, zh->records_deleted);
106     }
107 }
108
109 static void extract_add_index_string (RecWord *p, const char *str, int length);
110
111 static void extract_set_store_data_prepare(struct recExtractCtrl *p);
112
113 static void extract_init (struct recExtractCtrl *p, RecWord *w)
114 {
115     w->zebra_maps = p->zebra_maps;
116     w->seqno = 1;
117     w->index_name = "any";
118     w->index_type = 'w';
119     w->extractCtrl = p;
120     w->record_id = 0;
121     w->section_id = 0;
122 }
123
124 static void searchRecordKey(ZebraHandle zh,
125                             zebra_rec_keys_t reckeys,
126                             const char *index_name,
127                             const char **ws, int ws_length)
128 {
129     int i;
130     int ch = -1;
131
132     for (i = 0; i<ws_length; i++)
133         ws[i] = NULL;
134
135     if (ch < 0)
136         ch = zebraExplain_lookup_attr_str(zh->reg->zei, '0', index_name);
137     if (ch < 0)
138         ch = zebraExplain_lookup_attr_str(zh->reg->zei, 'p', index_name);
139     if (ch < 0)
140         ch = zebraExplain_lookup_attr_str(zh->reg->zei, 'w', index_name);
141
142     if (ch < 0)
143         return ;
144
145     if (zebra_rec_keys_rewind(reckeys))
146     {
147         zint startSeq = -1;
148         const char *str;
149         size_t slen;
150         struct it_key key;
151         zint seqno;
152         while (zebra_rec_keys_read(reckeys, &str, &slen, &key))
153         {
154             assert(key.len <= 4 && key.len > 2);
155
156             seqno = key.mem[key.len-1];
157             
158             if (key.mem[0] == ch)
159             {
160                 zint woff;
161                 
162                 if (startSeq == -1)
163                     startSeq = seqno;
164                 woff = seqno - startSeq;
165                 if (woff >= 0 && woff < ws_length)
166                     ws[woff] = str;
167             }
168         }
169     }
170 }
171
172 struct file_read_info {
173     off_t file_max;         /* maximum offset so far */
174     off_t file_offset;      /* current offset */
175     off_t file_moffset;     /* offset of rec/rec boundary */
176     int file_more;
177     int fd;
178 };
179
180 static struct file_read_info *file_read_start (int fd)
181 {
182     struct file_read_info *fi = (struct file_read_info *)
183         xmalloc (sizeof(*fi));
184
185     fi->fd = fd;
186     fi->file_max = 0;
187     fi->file_moffset = 0;
188     fi->file_offset = 0;
189     fi->file_more = 0;
190     return fi;
191 }
192
193 static void file_read_stop (struct file_read_info *fi)
194 {
195     xfree (fi);
196 }
197
198 static off_t file_seek (void *handle, off_t offset)
199 {
200     struct file_read_info *p = (struct file_read_info *) handle;
201     p->file_offset = offset;
202     return lseek (p->fd, offset, SEEK_SET);
203 }
204
205 static off_t file_tell (void *handle)
206 {
207     struct file_read_info *p = (struct file_read_info *) handle;
208     return p->file_offset;
209 }
210
211 static int file_read (void *handle, char *buf, size_t count)
212 {
213     struct file_read_info *p = (struct file_read_info *) handle;
214     int fd = p->fd;
215     int r;
216     r = read (fd, buf, count);
217     if (r > 0)
218     {
219         p->file_offset += r;
220         if (p->file_offset > p->file_max)
221             p->file_max = p->file_offset;
222     }
223     return r;
224 }
225
226 static void file_end (void *handle, off_t offset)
227 {
228     struct file_read_info *p = (struct file_read_info *) handle;
229
230     if (offset != p->file_moffset)
231     {
232         p->file_moffset = offset;
233         p->file_more = 1;
234     }
235 }
236
237 #define FILE_MATCH_BLANK "\t "
238
239 static char *fileMatchStr (ZebraHandle zh,
240                            zebra_rec_keys_t reckeys,
241                            const char *fname, const char *spec)
242 {
243     static char dstBuf[2048];      /* static here ??? */
244     char *dst = dstBuf;
245     const char *s = spec;
246
247     while (1)
248     {
249         for (; *s && strchr(FILE_MATCH_BLANK, *s); s++)
250             ;
251         if (!*s)
252             break;
253         if (*s == '(')
254         {
255             const char *ws[32];
256             char attset_str[64], attname_str[64];
257             int i;
258             int first = 1;
259             
260             for (s++; strchr(FILE_MATCH_BLANK, *s); s++)
261                 ;
262             for (i = 0; *s && *s != ',' && *s != ')' && 
263                      !strchr(FILE_MATCH_BLANK, *s); s++)
264                 if (i+1 < sizeof(attset_str))
265                     attset_str[i++] = *s;
266             attset_str[i] = '\0';
267             
268             for (; strchr(FILE_MATCH_BLANK, *s); s++)
269                 ;
270             if (*s != ',')
271                 strcpy(attname_str, attset_str);
272             else
273             {
274                 for (s++; strchr(FILE_MATCH_BLANK, *s); s++)
275                     ;
276                 for (i = 0; *s && *s != ')' && 
277                          !strchr(FILE_MATCH_BLANK, *s); s++)
278                     if (i+1 < sizeof(attname_str))
279                         attname_str[i++] = *s;
280                 attname_str[i] = '\0';
281             }
282
283             searchRecordKey (zh, reckeys, attname_str, ws, 32);
284
285             if (*s != ')')
286             {
287                 yaz_log (YLOG_WARN, "Missing ) in match criteria %s in group %s",
288                       spec, zh->m_group ? zh->m_group : "none");
289                 return NULL;
290             }
291             s++;
292
293             for (i = 0; i<32; i++)
294                 if (ws[i])
295                 {
296                     if (first)
297                     {
298                         *dst++ = ' ';
299                         first = 0;
300                     }
301                     strcpy (dst, ws[i]);
302                     dst += strlen(ws[i]);
303                 }
304             if (first)
305             {
306                 yaz_log (YLOG_WARN, "Record didn't contain match"
307                       " fields in (%s,%s)", attset_str, attname_str);
308                 return NULL;
309             }
310         }
311         else if (*s == '$')
312         {
313             int spec_len;
314             char special[64];
315             const char *spec_src = NULL;
316             const char *s1 = ++s;
317             while (*s1 && !strchr(FILE_MATCH_BLANK, *s1))
318                 s1++;
319
320             spec_len = s1 - s;
321             if (spec_len > sizeof(special)-1)
322                 spec_len = sizeof(special)-1;
323             memcpy (special, s, spec_len);
324             special[spec_len] = '\0';
325             s = s1;
326
327             if (!strcmp (special, "group"))
328                 spec_src = zh->m_group;
329             else if (!strcmp (special, "database"))
330                 spec_src = zh->basenames[0];
331             else if (!strcmp (special, "filename")) {
332                 spec_src = fname;
333             }
334             else if (!strcmp (special, "type"))
335                 spec_src = zh->m_record_type;
336             else 
337                 spec_src = NULL;
338             if (spec_src)
339             {
340                 strcpy (dst, spec_src);
341                 dst += strlen (spec_src);
342             }
343         }
344         else if (*s == '\"' || *s == '\'')
345         {
346             int stopMarker = *s++;
347             char tmpString[64];
348             int i = 0;
349
350             while (*s && *s != stopMarker)
351             {
352                 if (i+1 < sizeof(tmpString))
353                     tmpString[i++] = *s++;
354             }
355             if (*s)
356                 s++;
357             tmpString[i] = '\0';
358             strcpy (dst, tmpString);
359             dst += strlen (tmpString);
360         }
361         else
362         {
363             yaz_log (YLOG_WARN, "Syntax error in match criteria %s in group %s",
364                   spec, zh->m_group ? zh->m_group : "none");
365             return NULL;
366         }
367         *dst++ = 1;
368     }
369     if (dst == dstBuf)
370     {
371         yaz_log (YLOG_WARN, "No match criteria for record %s in group %s",
372               fname, zh->m_group ? zh->m_group : "none");
373         return NULL;
374     }
375     *dst = '\0';
376     return dstBuf;
377 }
378
379 struct recordLogInfo {
380     const char *fname;
381     int recordOffset;
382     struct recordGroup *rGroup;
383 };
384
385 static void init_extractCtrl(ZebraHandle zh, struct recExtractCtrl *ctrl)
386 {
387     int i;
388     for (i = 0; i<256; i++)
389     {
390         if (zebra_maps_is_positioned(zh->reg->zebra_maps, i))
391             ctrl->seqno[i] = 1;
392         else
393             ctrl->seqno[i] = 0;
394     }
395     ctrl->zebra_maps = zh->reg->zebra_maps;
396     ctrl->flagShowRecords = !zh->m_flag_rw;
397 }
398
399 static void all_matches_add(struct recExtractCtrl *ctrl)
400 {
401     RecWord word;
402     extract_init(ctrl, &word);
403     word.index_name = "allrecords";
404     word.index_type = 'w';
405     word.seqno = 1;
406     extract_add_index_string (&word, "", 0);
407 }
408
409 static ZEBRA_RES file_extract_record(ZebraHandle zh,
410                                      SYSNO *sysno, const char *fname,
411                                      int deleteFlag,
412                                      struct file_read_info *fi,
413                                      int force_update,
414                                      RecType recType,
415                                      void *recTypeClientData)
416 {
417     const char *match_str_to_print = "";
418     RecordAttr *recordAttr;
419     int r;
420     const char *matchStr = 0;
421     SYSNO sysnotmp;
422     Record rec;
423     off_t recordOffset = 0;
424     struct recExtractCtrl extractCtrl;
425     
426     /* announce database */
427     if (zebraExplain_curDatabase (zh->reg->zei, zh->basenames[0]))
428     {
429         if (zebraExplain_newDatabase (zh->reg->zei, zh->basenames[0],
430                                       zh->m_explain_database))
431             return ZEBRA_FAIL;
432     }
433
434     if (fi->fd != -1)
435     {
436         /* we are going to read from a file, so prepare the extraction */
437         zebra_rec_keys_reset(zh->reg->keys);
438
439         zebra_rec_keys_reset(zh->reg->sortKeys);
440         recordOffset = fi->file_moffset;
441         extractCtrl.handle = zh;
442         extractCtrl.offset = fi->file_moffset;
443         extractCtrl.readf = file_read;
444         extractCtrl.seekf = file_seek;
445         extractCtrl.tellf = file_tell;
446         extractCtrl.endf = file_end;
447         extractCtrl.fh = fi;
448         extractCtrl.init = extract_init;
449         extractCtrl.tokenAdd = extract_token_add;
450         extractCtrl.schemaAdd = extract_schema_add;
451         extractCtrl.dh = zh->reg->dh;
452         extractCtrl.match_criteria[0] = '\0';
453         extractCtrl.staticrank = 0;
454         
455         extractCtrl.first_record = fi->file_offset ? 0 : 1;
456
457         extract_set_store_data_prepare(&extractCtrl);
458
459         init_extractCtrl(zh, &extractCtrl);
460
461         if (!zh->m_flag_rw)
462             printf ("File: %s " ZINT_FORMAT "\n", fname, (zint)recordOffset);
463         if (zh->m_flag_rw)
464         {
465             char msg[512];
466             sprintf (msg, "%s:" ZINT_FORMAT , fname, (zint)recordOffset);
467             yaz_log_init_prefix2 (msg);
468         }
469
470         r = (*recType->extract)(recTypeClientData, &extractCtrl);
471
472         yaz_log_init_prefix2 (0);
473         if (r == RECCTRL_EXTRACT_EOF)
474             return ZEBRA_FAIL;
475         else if (r == RECCTRL_EXTRACT_ERROR_GENERIC)
476         {
477             /* error occured during extraction ... */
478             if (zh->m_flag_rw &&
479                 zh->records_processed < zh->m_file_verbose_limit)
480             {
481                 yaz_log (YLOG_WARN, "fail %s %s " ZINT_FORMAT, 
482                          zh->m_record_type,
483                          fname, (zint) recordOffset);
484             }
485             return ZEBRA_FAIL;
486         }
487         else if (r == RECCTRL_EXTRACT_ERROR_NO_SUCH_FILTER)
488         {
489             /* error occured during extraction ... */
490             if (zh->m_flag_rw &&
491                 zh->records_processed < zh->m_file_verbose_limit)
492             {
493                 yaz_log (YLOG_WARN, "no filter for %s %s " 
494                       ZINT_FORMAT, zh->m_record_type,
495                       fname, (zint) recordOffset);
496             }
497             return ZEBRA_FAIL;
498         }
499         all_matches_add(&extractCtrl);
500         if (extractCtrl.match_criteria[0])
501             matchStr = extractCtrl.match_criteria;
502     }
503
504     /* if matchStr is set now - we assume it's printable .
505        For internal matchStr (see below) we don't print */
506     if (matchStr)
507         match_str_to_print = matchStr;
508
509     /* perform internal match if sysno not known and if match criteria is
510        specified already */
511     if (!sysno) 
512     {
513         sysnotmp = 0;
514         sysno = &sysnotmp;
515
516         if (matchStr == 0 && zh->m_record_id && *zh->m_record_id)
517         {
518             matchStr = fileMatchStr (zh, zh->reg->keys, fname, 
519                                      zh->m_record_id);
520             if (!matchStr)
521             {
522                 yaz_log(YLOG_WARN, "Bad match criteria");
523
524                 if (zebra_rec_keys_empty(zh->reg->keys))
525                 {
526                     yaz_log(YLOG_WARN, "And no index keys");
527                 }
528                 return ZEBRA_FAIL;
529             }
530         }
531         if (matchStr)
532         {
533             int db_ord = zebraExplain_get_database_ord(zh->reg->zei);
534             char *rinfo = dict_lookup_ord(zh->reg->matchDict, db_ord,
535                                           matchStr);
536             if (rinfo)
537             {
538                 assert(*rinfo == sizeof(*sysno));
539                 memcpy (sysno, rinfo+1, sizeof(*sysno));
540             }
541         }
542     }
543     if (! *sysno && zebra_rec_keys_empty(zh->reg->keys) )
544     {
545          /* the extraction process returned no information - the record
546             is probably empty - unless flagShowRecords is in use */
547          if (!zh->m_flag_rw)
548              return ZEBRA_OK;
549   
550          if (zh->records_processed < zh->m_file_verbose_limit)
551              yaz_log(YLOG_WARN, "empty %s %s " ZINT_FORMAT, zh->m_record_type,
552                      fname, (zint)recordOffset);
553          return ZEBRA_OK;
554     }
555
556     if (! *sysno)
557     {
558         /* new record */
559         if (deleteFlag)
560         {
561             yaz_log(YLOG_LOG, "delete %s %s " ZINT_FORMAT, zh->m_record_type,
562                     fname, (zint)recordOffset);
563             yaz_log(YLOG_WARN, "cannot delete record above (seems new)");
564             return ZEBRA_OK;
565         }
566
567         rec = rec_new (zh->reg->records);
568         
569         *sysno = rec->sysno;
570         
571         if (zh->records_processed < zh->m_file_verbose_limit)
572         {
573             yaz_log(YLOG_LOG, "add %s %s " ZINT_FORMAT 
574                     " " ZINT_FORMAT " %s" ,
575                     zh->m_record_type,
576                     fname, (zint) recordOffset, *sysno, match_str_to_print);
577         }
578         recordAttr = rec_init_attr (zh->reg->zei, rec);
579         recordAttr->staticrank = extractCtrl.staticrank;
580
581         if (matchStr)
582         {
583             int db_ord = zebraExplain_get_database_ord(zh->reg->zei);
584             dict_insert_ord(zh->reg->matchDict, db_ord, matchStr,
585                             sizeof(*sysno), sysno);
586         }
587
588
589         extract_flushSortKeys (zh, *sysno, 1, zh->reg->sortKeys);
590         extract_flushRecordKeys (zh, *sysno, 1, zh->reg->keys,
591                                  recordAttr->staticrank);
592         zh->records_inserted++;
593     }
594     else
595     {
596         /* record already exists */
597         zebra_rec_keys_t delkeys = zebra_rec_keys_open();
598
599         zebra_rec_keys_t sortKeys = zebra_rec_keys_open();
600
601         rec = rec_get (zh->reg->records, *sysno);
602         assert (rec);
603         
604         recordAttr = rec_init_attr (zh->reg->zei, rec);
605
606         zebra_rec_keys_set_buf(delkeys,
607                                rec->info[recInfo_delKeys],
608                                rec->size[recInfo_delKeys],
609                                0);
610
611         zebra_rec_keys_set_buf(sortKeys,
612                                rec->info[recInfo_sortKeys],
613                                rec->size[recInfo_sortKeys],
614                                0);
615         extract_flushSortKeys (zh, *sysno, 0, sortKeys);
616         extract_flushRecordKeys (zh, *sysno, 0, delkeys,
617                                  recordAttr->staticrank); /* old values */  
618         if (deleteFlag)
619         {
620             /* record going to be deleted */
621             if (zebra_rec_keys_empty(delkeys))
622             {
623                 yaz_log(YLOG_LOG, "delete %s %s " ZINT_FORMAT 
624                         " " ZINT_FORMAT,
625                         zh->m_record_type, fname, (zint)recordOffset, *sysno);
626                 yaz_log(YLOG_WARN, "cannot delete file above, storeKeys false (1)");
627             }
628             else
629             {
630                 if (zh->records_processed < zh->m_file_verbose_limit)
631                 {
632                     yaz_log(YLOG_LOG, "delete %s %s " ZINT_FORMAT 
633                             " " ZINT_FORMAT " %s" ,
634                             zh->m_record_type, fname, (zint) recordOffset,
635                             *sysno, match_str_to_print);
636                 }
637                 zh->records_deleted++;
638                 if (matchStr)
639                 {
640                     int db_ord = zebraExplain_get_database_ord(zh->reg->zei);
641                     dict_delete_ord(zh->reg->matchDict, db_ord, matchStr);
642                 }
643                 rec_del (zh->reg->records, &rec);
644             }
645             rec_rm (&rec);
646             logRecord (zh);
647             return ZEBRA_OK;
648         }
649         else
650         {
651             /* flush new keys for sort&search etc */
652             if (zh->records_processed < zh->m_file_verbose_limit)
653             {
654                 yaz_log(YLOG_LOG, "update %s %s " ZINT_FORMAT 
655                         " " ZINT_FORMAT " %s" ,
656                         zh->m_record_type, fname, (zint) recordOffset, 
657                         *sysno, match_str_to_print);
658             }
659             recordAttr->staticrank = extractCtrl.staticrank;
660             extract_flushSortKeys (zh, *sysno, 1, zh->reg->sortKeys);
661             extract_flushRecordKeys (zh, *sysno, 1, zh->reg->keys,
662                                          recordAttr->staticrank);
663             zh->records_updated++;
664         }
665         zebra_rec_keys_close(delkeys);
666         zebra_rec_keys_close(sortKeys);
667     }
668     /* update file type */
669     xfree (rec->info[recInfo_fileType]);
670     rec->info[recInfo_fileType] =
671         rec_strdup (zh->m_record_type, &rec->size[recInfo_fileType]);
672
673     /* update filename */
674     xfree (rec->info[recInfo_filename]);
675     rec->info[recInfo_filename] =
676         rec_strdup (fname, &rec->size[recInfo_filename]);
677
678     /* update delete keys */
679     xfree (rec->info[recInfo_delKeys]);
680     if (!zebra_rec_keys_empty(zh->reg->keys) && zh->m_store_keys == 1)
681     {
682         zebra_rec_keys_get_buf(zh->reg->keys,
683                                &rec->info[recInfo_delKeys],
684                                &rec->size[recInfo_delKeys]);
685     }
686     else
687     {
688         rec->info[recInfo_delKeys] = NULL;
689         rec->size[recInfo_delKeys] = 0;
690     }
691
692     /* update sort keys */
693     xfree (rec->info[recInfo_sortKeys]);
694
695     zebra_rec_keys_get_buf(zh->reg->sortKeys,
696                            &rec->info[recInfo_sortKeys],
697                            &rec->size[recInfo_sortKeys]);
698
699     /* save file size of original record */
700     zebraExplain_recordBytesIncrement (zh->reg->zei,
701                                        - recordAttr->recordSize);
702     recordAttr->recordSize = fi->file_moffset - recordOffset;
703     if (!recordAttr->recordSize)
704         recordAttr->recordSize = fi->file_max - recordOffset;
705     zebraExplain_recordBytesIncrement (zh->reg->zei,
706                                        recordAttr->recordSize);
707
708     /* set run-number for this record */
709     recordAttr->runNumber = zebraExplain_runNumberIncrement (zh->reg->zei,
710                                                              0);
711
712     /* update store data */
713     xfree (rec->info[recInfo_storeData]);
714     if (zh->store_data_buf)
715     {
716         rec->size[recInfo_storeData] = zh->store_data_size;
717         rec->info[recInfo_storeData] = zh->store_data_buf;
718         zh->store_data_buf = 0;
719     }
720     else if (zh->m_store_data)
721     {
722         rec->size[recInfo_storeData] = recordAttr->recordSize;
723         rec->info[recInfo_storeData] = (char *)
724             xmalloc (recordAttr->recordSize);
725         if (lseek (fi->fd, recordOffset, SEEK_SET) < 0)
726         {
727             yaz_log(YLOG_ERRNO|YLOG_FATAL, "seek to " ZINT_FORMAT " in %s",
728                     (zint)recordOffset, fname);
729             exit (1);
730         }
731         if (read (fi->fd, rec->info[recInfo_storeData], recordAttr->recordSize)
732             < recordAttr->recordSize)
733         {
734             yaz_log (YLOG_ERRNO|YLOG_FATAL, "read %d bytes of %s",
735                   recordAttr->recordSize, fname);
736             exit (1);
737         }
738     }
739     else
740     {
741         rec->info[recInfo_storeData] = NULL;
742         rec->size[recInfo_storeData] = 0;
743     }
744     /* update database name */
745     xfree (rec->info[recInfo_databaseName]);
746     rec->info[recInfo_databaseName] =
747         rec_strdup (zh->basenames[0], &rec->size[recInfo_databaseName]); 
748
749     /* update offset */
750     recordAttr->recordOffset = recordOffset;
751     
752     /* commit this record */
753     rec_put (zh->reg->records, &rec);
754     logRecord (zh);
755     return ZEBRA_OK;
756 }
757
758 ZEBRA_RES zebra_extract_file(ZebraHandle zh, SYSNO *sysno, const char *fname, 
759                              int deleteFlag)
760 {
761     ZEBRA_RES r = ZEBRA_OK;
762     int i, fd;
763     char gprefix[128];
764     char ext[128];
765     char ext_res[128];
766     struct file_read_info *fi;
767     const char *original_record_type = 0;
768     RecType recType;
769     void *recTypeClientData;
770
771     zebra_init_log_level();
772
773     if (!zh->m_group || !*zh->m_group)
774         *gprefix = '\0';
775     else
776         sprintf (gprefix, "%s.", zh->m_group);
777     
778     yaz_log(log_level, "zebra_extract_file %s", fname);
779
780     /* determine file extension */
781     *ext = '\0';
782     for (i = strlen(fname); --i >= 0; )
783         if (fname[i] == '/')
784             break;
785         else if (fname[i] == '.')
786         {
787             strcpy (ext, fname+i+1);
788             break;
789         }
790     /* determine file type - depending on extension */
791     original_record_type = zh->m_record_type;
792     if (!zh->m_record_type)
793     {
794         sprintf (ext_res, "%srecordType.%s", gprefix, ext);
795         zh->m_record_type = res_get (zh->res, ext_res);
796     }
797     if (!zh->m_record_type)
798     {
799         if (zh->records_processed < zh->m_file_verbose_limit)
800             yaz_log (YLOG_LOG, "? %s", fname);
801         return 0;
802     }
803     /* determine match criteria */
804     if (!zh->m_record_id)
805     {
806         sprintf (ext_res, "%srecordId.%s", gprefix, ext);
807         zh->m_record_id = res_get (zh->res, ext_res);
808     }
809
810     if (!(recType =
811           recType_byName (zh->reg->recTypes, zh->res, zh->m_record_type,
812                           &recTypeClientData)))
813     {
814         yaz_log(YLOG_WARN, "No such record type: %s", zh->m_record_type);
815         return ZEBRA_FAIL;
816     }
817
818     switch(recType->version)
819     {
820     case 0:
821         break;
822     default:
823         yaz_log(YLOG_WARN, "Bad filter version: %s", zh->m_record_type);
824     }
825     if (sysno && deleteFlag)
826         fd = -1;
827     else
828     {
829         char full_rep[1024];
830
831         if (zh->path_reg && !yaz_is_abspath (fname))
832         {
833             strcpy (full_rep, zh->path_reg);
834             strcat (full_rep, "/");
835             strcat (full_rep, fname);
836         }
837         else
838             strcpy (full_rep, fname);
839         
840         if ((fd = open (full_rep, O_BINARY|O_RDONLY)) == -1)
841         {
842             yaz_log (YLOG_WARN|YLOG_ERRNO, "open %s", full_rep);
843             zh->m_record_type = original_record_type;
844             return ZEBRA_FAIL;
845         }
846     }
847     fi = file_read_start (fd);
848     while(1)
849     {
850         fi->file_moffset = fi->file_offset;
851         fi->file_more = 0;  /* file_end not called (yet) */
852         r = file_extract_record (zh, sysno, fname, deleteFlag, fi, 1,
853                                  recType, recTypeClientData);
854         if (fi->file_more)
855         {   /* file_end has been called so reset offset .. */
856             fi->file_offset = fi->file_moffset;
857             lseek(fi->fd, fi->file_moffset, SEEK_SET);
858         }
859         if (r != ZEBRA_OK)
860         {
861             break;
862         }
863         if (sysno)
864         {
865             break;
866         }
867     }
868     file_read_stop (fi);
869     if (fd != -1)
870         close (fd);
871     zh->m_record_type = original_record_type;
872     return r;
873 }
874
875 /*
876   If sysno is provided, then it's used to identify the reocord.
877   If not, and match_criteria is provided, then sysno is guessed
878   If not, and a record is provided, then sysno is got from there
879   
880  */
881 ZEBRA_RES zebra_buffer_extract_record(ZebraHandle zh, 
882                                       const char *buf, size_t buf_size,
883                                       int delete_flag,
884                                       int test_mode, 
885                                       const char *recordType,
886                                       SYSNO *sysno,
887                                       const char *match_criteria,
888                                       const char *fname,
889                                       int force_update,
890                                       int allow_update)
891 {
892     SYSNO sysno0 = 0;
893     RecordAttr *recordAttr;
894     struct recExtractCtrl extractCtrl;
895     int r;
896     const char *matchStr = 0;
897     RecType recType = NULL;
898     void *clientData;
899     Record rec;
900     long recordOffset = 0;
901     struct zebra_fetch_control fc;
902     const char *pr_fname = fname;  /* filename to print .. */
903     int show_progress = zh->records_processed < zh->m_file_verbose_limit ? 1:0;
904
905     zebra_init_log_level();
906
907     if (!pr_fname)
908         pr_fname = "<no file>";  /* make it printable if file is omitted */
909
910     fc.fd = -1;
911     fc.record_int_buf = buf;
912     fc.record_int_len = buf_size;
913     fc.record_int_pos = 0;
914     fc.offset_end = 0;
915     fc.record_offset = 0;
916
917     extractCtrl.offset = 0;
918     extractCtrl.readf = zebra_record_int_read;
919     extractCtrl.seekf = zebra_record_int_seek;
920     extractCtrl.tellf = zebra_record_int_tell;
921     extractCtrl.endf = zebra_record_int_end;
922     extractCtrl.first_record = 1;
923     extractCtrl.fh = &fc;
924
925     zebra_rec_keys_reset(zh->reg->keys);
926     zebra_rec_keys_reset(zh->reg->sortKeys);
927
928     if (zebraExplain_curDatabase (zh->reg->zei, zh->basenames[0]))
929     {
930         if (zebraExplain_newDatabase (zh->reg->zei, zh->basenames[0], 
931                                       zh->m_explain_database))
932             return ZEBRA_FAIL;
933     }
934     
935     if (recordType && *recordType)
936     {
937         yaz_log(log_level, "Record type explicitly specified: %s", recordType);
938         recType = recType_byName (zh->reg->recTypes, zh->res, recordType,
939                                   &clientData);
940     } 
941     else
942     {
943         if (!(zh->m_record_type))
944         {
945             yaz_log (YLOG_WARN, "No such record type defined");
946             return ZEBRA_FAIL;
947         }
948         yaz_log(log_level, "Get record type from rgroup: %s",
949                 zh->m_record_type);
950         recType = recType_byName (zh->reg->recTypes, zh->res,
951                                   zh->m_record_type, &clientData);
952         recordType = zh->m_record_type;
953     }
954     
955     if (!recType)
956     {
957         yaz_log (YLOG_WARN, "No such record type: %s", recordType);
958         return ZEBRA_FAIL;
959     }
960     
961     extractCtrl.init = extract_init;
962     extractCtrl.tokenAdd = extract_token_add;
963     extractCtrl.schemaAdd = extract_schema_add;
964     extractCtrl.dh = zh->reg->dh;
965     extractCtrl.handle = zh;
966     extractCtrl.match_criteria[0] = '\0';
967     extractCtrl.staticrank = 0;
968     
969     init_extractCtrl(zh, &extractCtrl);
970
971     extract_set_store_data_prepare(&extractCtrl);
972
973     r = (*recType->extract)(clientData, &extractCtrl);
974
975     if (r == RECCTRL_EXTRACT_EOF)
976         return ZEBRA_FAIL;
977     else if (r == RECCTRL_EXTRACT_ERROR_GENERIC)
978     {
979         /* error occured during extraction ... */
980         yaz_log (YLOG_WARN, "extract error: generic");
981         return ZEBRA_FAIL;
982     }
983     else if (r == RECCTRL_EXTRACT_ERROR_NO_SUCH_FILTER)
984     {
985         /* error occured during extraction ... */
986         yaz_log (YLOG_WARN, "extract error: no such filter");
987         return ZEBRA_FAIL;
988     }
989
990     all_matches_add(&extractCtrl);
991         
992     if (extractCtrl.match_criteria[0])
993         match_criteria = extractCtrl.match_criteria;
994
995     if (!sysno) {
996
997         sysno = &sysno0;
998
999         if (match_criteria && *match_criteria) {
1000             matchStr = match_criteria;
1001         } else {
1002             if (zh->m_record_id && *zh->m_record_id) {
1003                 matchStr = fileMatchStr (zh, zh->reg->keys, pr_fname, 
1004                                          zh->m_record_id);
1005                 if (!matchStr)
1006                 {
1007                     yaz_log (YLOG_WARN, "Bad match criteria (recordID)");
1008                     return ZEBRA_FAIL;
1009                 }
1010             }
1011         }
1012         if (matchStr) 
1013         {
1014             int db_ord = zebraExplain_get_database_ord(zh->reg->zei);
1015             char *rinfo = dict_lookup_ord(zh->reg->matchDict, db_ord,
1016                                           matchStr);
1017             if (rinfo)
1018             {
1019                 assert(*rinfo == sizeof(*sysno));
1020                 memcpy (sysno, rinfo+1, sizeof(*sysno));
1021             }
1022         }
1023     }
1024     if (zebra_rec_keys_empty(zh->reg->keys))
1025     {
1026         /* the extraction process returned no information - the record
1027            is probably empty - unless flagShowRecords is in use */
1028         if (test_mode)
1029             return ZEBRA_OK;
1030     }
1031
1032     if (! *sysno)
1033     {
1034         /* new record */
1035         if (delete_flag)
1036         {
1037             yaz_log (YLOG_LOG, "delete %s %s %ld", recordType,
1038                          pr_fname, (long) recordOffset);
1039             yaz_log (YLOG_WARN, "cannot delete record above (seems new)");
1040             return ZEBRA_FAIL;
1041         }
1042         if (show_progress)
1043             yaz_log (YLOG_LOG, "add %s %s %ld", recordType, pr_fname,
1044                      (long) recordOffset);
1045         rec = rec_new (zh->reg->records);
1046
1047         *sysno = rec->sysno;
1048
1049         recordAttr = rec_init_attr (zh->reg->zei, rec);
1050         recordAttr->staticrank = extractCtrl.staticrank;
1051
1052         if (matchStr)
1053         {
1054             int db_ord = zebraExplain_get_database_ord(zh->reg->zei);
1055             dict_insert_ord(zh->reg->matchDict, db_ord, matchStr,
1056                             sizeof(*sysno), sysno);
1057         }
1058
1059
1060         extract_flushSortKeys (zh, *sysno, 1, zh->reg->sortKeys);
1061         extract_flushRecordKeys (zh, *sysno, 1, zh->reg->keys,
1062                          recordAttr->staticrank);
1063         zh->records_inserted++;
1064     } 
1065     else
1066     {
1067         /* record already exists */
1068         zebra_rec_keys_t delkeys = zebra_rec_keys_open();
1069         zebra_rec_keys_t sortKeys = zebra_rec_keys_open();
1070         if (!allow_update)
1071         {
1072             yaz_log (YLOG_LOG, "skipped %s %s %ld", 
1073                          recordType, pr_fname, (long) recordOffset);
1074             logRecord(zh);
1075             return ZEBRA_FAIL;
1076         }
1077
1078         rec = rec_get (zh->reg->records, *sysno);
1079         assert (rec);
1080         
1081         recordAttr = rec_init_attr (zh->reg->zei, rec);
1082
1083         zebra_rec_keys_set_buf(delkeys,
1084                                rec->info[recInfo_delKeys],
1085                                rec->size[recInfo_delKeys],
1086                                0);
1087         zebra_rec_keys_set_buf(sortKeys,
1088                                rec->info[recInfo_sortKeys],
1089                                rec->size[recInfo_sortKeys],
1090                                0);
1091
1092         extract_flushSortKeys (zh, *sysno, 0, sortKeys);
1093         extract_flushRecordKeys (zh, *sysno, 0, delkeys,
1094                                  recordAttr->staticrank);
1095         if (delete_flag)
1096         {
1097             /* record going to be deleted */
1098             if (zebra_rec_keys_empty(delkeys))
1099             {
1100                 yaz_log(YLOG_LOG, "delete %s %s %ld", recordType,
1101                         pr_fname, (long) recordOffset);
1102                 yaz_log(YLOG_WARN, "cannot delete file above, "
1103                         "storeKeys false (3)");
1104             }
1105             else
1106             {
1107                 if (show_progress)
1108                     yaz_log(YLOG_LOG, "delete %s %s %ld", recordType,
1109                             pr_fname, (long) recordOffset);
1110                 zh->records_deleted++;
1111                 if (matchStr)
1112                 {
1113                     int db_ord = zebraExplain_get_database_ord(zh->reg->zei);
1114                     dict_delete_ord(zh->reg->matchDict, db_ord, matchStr);
1115                 }
1116                 rec_del (zh->reg->records, &rec);
1117             }
1118             rec_rm (&rec);
1119             logRecord(zh);
1120             return ZEBRA_OK;
1121         }
1122         else
1123         {
1124             if (show_progress)
1125                     yaz_log(YLOG_LOG, "update %s %s %ld", recordType,
1126                             pr_fname, (long) recordOffset);
1127             recordAttr->staticrank = extractCtrl.staticrank;
1128             extract_flushSortKeys (zh, *sysno, 1, zh->reg->sortKeys);
1129             extract_flushRecordKeys (zh, *sysno, 1, zh->reg->keys, 
1130                                          recordAttr->staticrank);
1131             zh->records_updated++;
1132         }
1133         zebra_rec_keys_close(delkeys);
1134         zebra_rec_keys_close(sortKeys);
1135     }
1136     /* update file type */
1137     xfree (rec->info[recInfo_fileType]);
1138     rec->info[recInfo_fileType] =
1139         rec_strdup (recordType, &rec->size[recInfo_fileType]);
1140
1141     /* update filename */
1142     xfree (rec->info[recInfo_filename]);
1143     rec->info[recInfo_filename] =
1144         rec_strdup (fname, &rec->size[recInfo_filename]);
1145
1146     /* update delete keys */
1147     xfree (rec->info[recInfo_delKeys]);
1148     if (!zebra_rec_keys_empty(zh->reg->keys) && zh->m_store_keys == 1)
1149     {
1150         zebra_rec_keys_get_buf(zh->reg->keys,
1151                                &rec->info[recInfo_delKeys],
1152                                &rec->size[recInfo_delKeys]);
1153     }
1154     else
1155     {
1156         rec->info[recInfo_delKeys] = NULL;
1157         rec->size[recInfo_delKeys] = 0;
1158     }
1159     /* update sort keys */
1160     xfree (rec->info[recInfo_sortKeys]);
1161
1162     zebra_rec_keys_get_buf(zh->reg->sortKeys,
1163                            &rec->info[recInfo_sortKeys],
1164                            &rec->size[recInfo_sortKeys]);
1165
1166     /* save file size of original record */
1167     zebraExplain_recordBytesIncrement (zh->reg->zei,
1168                                        - recordAttr->recordSize);
1169 #if 0
1170     recordAttr->recordSize = fi->file_moffset - recordOffset;
1171     if (!recordAttr->recordSize)
1172         recordAttr->recordSize = fi->file_max - recordOffset;
1173 #else
1174     recordAttr->recordSize = buf_size;
1175 #endif
1176     zebraExplain_recordBytesIncrement (zh->reg->zei,
1177                                        recordAttr->recordSize);
1178
1179     /* set run-number for this record */
1180     recordAttr->runNumber =
1181         zebraExplain_runNumberIncrement (zh->reg->zei, 0);
1182
1183     /* update store data */
1184     xfree (rec->info[recInfo_storeData]);
1185
1186     /* update store data */
1187     if (zh->store_data_buf)
1188     {
1189         rec->size[recInfo_storeData] = zh->store_data_size;
1190         rec->info[recInfo_storeData] = zh->store_data_buf;
1191         zh->store_data_buf = 0;
1192     }
1193     else if (zh->m_store_data)
1194     {
1195         rec->size[recInfo_storeData] = recordAttr->recordSize;
1196         rec->info[recInfo_storeData] = (char *)
1197             xmalloc (recordAttr->recordSize);
1198         memcpy (rec->info[recInfo_storeData], buf, recordAttr->recordSize);
1199     }
1200     else
1201     {
1202         rec->info[recInfo_storeData] = NULL;
1203         rec->size[recInfo_storeData] = 0;
1204     }
1205     /* update database name */
1206     xfree (rec->info[recInfo_databaseName]);
1207     rec->info[recInfo_databaseName] =
1208         rec_strdup (zh->basenames[0], &rec->size[recInfo_databaseName]); 
1209
1210     /* update offset */
1211     recordAttr->recordOffset = recordOffset;
1212     
1213     /* commit this record */
1214     rec_put (zh->reg->records, &rec);
1215     logRecord(zh);
1216     return ZEBRA_OK;
1217 }
1218
1219 ZEBRA_RES zebra_extract_explain(void *handle, Record rec, data1_node *n)
1220 {
1221     ZebraHandle zh = (ZebraHandle) handle;
1222     struct recExtractCtrl extractCtrl;
1223
1224     if (zebraExplain_curDatabase (zh->reg->zei,
1225                                   rec->info[recInfo_databaseName]))
1226     {
1227         abort();
1228         if (zebraExplain_newDatabase (zh->reg->zei,
1229                                       rec->info[recInfo_databaseName], 0))
1230             abort ();
1231     }
1232
1233     zebra_rec_keys_reset(zh->reg->keys);
1234     zebra_rec_keys_reset(zh->reg->sortKeys);
1235
1236     extractCtrl.init = extract_init;
1237     extractCtrl.tokenAdd = extract_token_add;
1238     extractCtrl.schemaAdd = extract_schema_add;
1239     extractCtrl.dh = zh->reg->dh;
1240
1241     init_extractCtrl(zh, &extractCtrl);
1242
1243     extractCtrl.flagShowRecords = 0;
1244     extractCtrl.match_criteria[0] = '\0';
1245     extractCtrl.staticrank = 0;
1246     extractCtrl.handle = handle;
1247     extractCtrl.first_record = 1;
1248     
1249     extract_set_store_data_prepare(&extractCtrl);
1250
1251     if (n)
1252         grs_extract_tree(&extractCtrl, n);
1253
1254     if (rec->size[recInfo_delKeys])
1255     {
1256         zebra_rec_keys_t delkeys = zebra_rec_keys_open();
1257         
1258         zebra_rec_keys_t sortkeys = zebra_rec_keys_open();
1259
1260         zebra_rec_keys_set_buf(delkeys, rec->info[recInfo_delKeys],
1261                                rec->size[recInfo_delKeys],
1262                                0);
1263         extract_flushRecordKeys (zh, rec->sysno, 0, delkeys, 0);
1264         zebra_rec_keys_close(delkeys);
1265
1266         zebra_rec_keys_set_buf(sortkeys, rec->info[recInfo_sortKeys],
1267                                rec->size[recInfo_sortKeys],
1268                                0);
1269
1270         extract_flushSortKeys (zh, rec->sysno, 0, sortkeys);
1271         zebra_rec_keys_close(sortkeys);
1272     }
1273     extract_flushRecordKeys (zh, rec->sysno, 1, zh->reg->keys, 0);
1274     extract_flushSortKeys (zh, rec->sysno, 1, zh->reg->sortKeys);
1275
1276     xfree (rec->info[recInfo_delKeys]);
1277     zebra_rec_keys_get_buf(zh->reg->keys,
1278                            &rec->info[recInfo_delKeys], 
1279                            &rec->size[recInfo_delKeys]);
1280
1281     xfree (rec->info[recInfo_sortKeys]);
1282     zebra_rec_keys_get_buf(zh->reg->sortKeys,
1283                            &rec->info[recInfo_sortKeys],
1284                            &rec->size[recInfo_sortKeys]);
1285     return ZEBRA_OK;
1286 }
1287
1288 void extract_rec_keys_adjust(ZebraHandle zh, int is_insert,
1289                              zebra_rec_keys_t reckeys)
1290 {
1291     ZebraExplainInfo zei = zh->reg->zei;
1292     struct ord_stat {
1293         int no;
1294         int ord;
1295         struct ord_stat *next;
1296     };
1297
1298     if (zebra_rec_keys_rewind(reckeys))
1299     {
1300         struct ord_stat *ord_list = 0;
1301         struct ord_stat *p;
1302         size_t slen;
1303         const char *str;
1304         struct it_key key_in;
1305         while(zebra_rec_keys_read(reckeys, &str, &slen, &key_in))
1306         {
1307             int ord = CAST_ZINT_TO_INT(key_in.mem[0]);
1308
1309             for (p = ord_list; p ; p = p->next)
1310                 if (p->ord == ord)
1311                 {
1312                     p->no++;
1313                     break;
1314                 }
1315             if (!p)
1316             {
1317                 p = xmalloc(sizeof(*p));
1318                 p->no = 1;
1319                 p->ord = ord;
1320                 p->next = ord_list;
1321                 ord_list = p;
1322             }
1323         }
1324
1325         p = ord_list;
1326         while (p)
1327         {
1328             struct ord_stat *p1 = p;
1329
1330             if (is_insert)
1331                 zebraExplain_ord_adjust_occurrences(zei, p->ord, p->no, 1);
1332             else
1333                 zebraExplain_ord_adjust_occurrences(zei, p->ord, - p->no, -1);
1334             p = p->next;
1335             xfree(p1);
1336         }
1337     }
1338 }
1339
1340 void extract_flushRecordKeys (ZebraHandle zh, SYSNO sysno,
1341                               int cmd,
1342                               zebra_rec_keys_t reckeys,
1343                               zint staticrank)
1344 {
1345     ZebraExplainInfo zei = zh->reg->zei;
1346
1347     extract_rec_keys_adjust(zh, cmd, reckeys);
1348
1349     if (!zh->reg->key_buf)
1350     {
1351         int mem= 1024*1024* atoi( res_get_def( zh->res, "memmax", "8"));
1352         if (mem <= 0)
1353         {
1354             yaz_log(YLOG_WARN, "Invalid memory setting, using default 8 MB");
1355             mem= 1024*1024*8;
1356         }
1357         /* FIXME: That "8" should be in a default settings include */
1358         /* not hard-coded here! -H */
1359         zh->reg->key_buf = (char**) xmalloc (mem);
1360         zh->reg->ptr_top = mem/sizeof(char*);
1361         zh->reg->ptr_i = 0;
1362         zh->reg->key_buf_used = 0;
1363         zh->reg->key_file_no = 0;
1364     }
1365     zebraExplain_recordCountIncrement (zei, cmd ? 1 : -1);
1366
1367     if (zebra_rec_keys_rewind(reckeys))
1368     {
1369         size_t slen;
1370         const char *str;
1371         struct it_key key_in;
1372         while(zebra_rec_keys_read(reckeys, &str, &slen, &key_in))
1373         {
1374             int ch = 0;
1375             struct it_key key_out;
1376             zint *keyp = key_out.mem;
1377
1378             assert(key_in.len == 4);
1379             
1380             /* check for buffer overflow */
1381             if (zh->reg->key_buf_used + 1024 > 
1382                 (zh->reg->ptr_top -zh->reg->ptr_i)*sizeof(char*))
1383                 extract_flushWriteKeys (zh, 0);
1384             
1385             ++(zh->reg->ptr_i);
1386             assert(zh->reg->ptr_i > 0);
1387             (zh->reg->key_buf)[zh->reg->ptr_top - zh->reg->ptr_i] =
1388                 (char*)zh->reg->key_buf + zh->reg->key_buf_used;
1389
1390             /* encode the ordinal value (field/use/attribute) .. */
1391             ch = CAST_ZINT_TO_INT(key_in.mem[0]);
1392             zh->reg->key_buf_used +=
1393                 key_SU_encode(ch, (char*)zh->reg->key_buf +
1394                               zh->reg->key_buf_used);
1395
1396             /* copy the 0-terminated stuff from str to output */
1397             memcpy((char*)zh->reg->key_buf + zh->reg->key_buf_used, str, slen);
1398             zh->reg->key_buf_used += slen;
1399             ((char*)zh->reg->key_buf)[(zh->reg->key_buf_used)++] = '\0';
1400
1401             /* the delete/insert indicator */
1402             ((char*)zh->reg->key_buf)[(zh->reg->key_buf_used)++] = cmd;
1403
1404             if (zh->m_staticrank) /* rank config enabled ? */
1405             {
1406                 if (staticrank < 0)
1407                 {
1408                     yaz_log(YLOG_WARN, "staticrank = %ld. Setting to 0",
1409                             (long) staticrank);
1410                     staticrank = 0;
1411                 }
1412                 *keyp++ = staticrank;
1413                 key_out.len = 4;
1414             }
1415             else
1416                 key_out.len = 3;
1417             
1418             if (key_in.mem[1]) /* filter specified record ID */
1419                 *keyp++ = key_in.mem[1];
1420             else
1421                 *keyp++ = sysno;
1422             *keyp++ = key_in.mem[2];  /* section_id */
1423             *keyp++ = key_in.mem[3];  /* sequence .. */
1424             
1425             memcpy((char*)zh->reg->key_buf + zh->reg->key_buf_used,
1426                    &key_out, sizeof(key_out));
1427             (zh->reg->key_buf_used) += sizeof(key_out);
1428         }
1429     }
1430 }
1431
1432 void extract_flushWriteKeys (ZebraHandle zh, int final)
1433         /* optimizing: if final=1, and no files written yet */
1434         /* push the keys directly to merge, sidestepping the */
1435         /* temp file altogether. Speeds small updates */
1436 {
1437     FILE *outf;
1438     char out_fname[200];
1439     char *prevcp, *cp;
1440     struct encode_info encode_info;
1441     int ptr_i = zh->reg->ptr_i;
1442     int temp_policy;
1443 #if SORT_EXTRA
1444     int i;
1445 #endif
1446     if (!zh->reg->key_buf || ptr_i <= 0)
1447     {
1448         yaz_log(log_level, "  nothing to flush section=%d buf=%p i=%d",
1449                zh->reg->key_file_no, zh->reg->key_buf, ptr_i);
1450         return;
1451     }
1452
1453     (zh->reg->key_file_no)++;
1454     yaz_log (YLOG_LOG, "sorting section %d", (zh->reg->key_file_no));
1455     yaz_log(log_level, "  sort_buff at %p n=%d",
1456                     zh->reg->key_buf + zh->reg->ptr_top - ptr_i,ptr_i);
1457 #if !SORT_EXTRA
1458     qsort (zh->reg->key_buf + zh->reg->ptr_top - ptr_i, ptr_i,
1459                sizeof(char*), key_qsort_compare);
1460
1461     /* zebra.cfg: tempfiles:  
1462        Y: always use temp files (old way) 
1463        A: use temp files, if more than one (auto) 
1464           = if this is both the last and the first 
1465        N: never bother with temp files (new) */
1466
1467     temp_policy=toupper(res_get_def(zh->res,"tempfiles","auto")[0]);
1468     if (temp_policy != 'Y' && temp_policy != 'N' && temp_policy != 'A') {
1469         yaz_log (YLOG_WARN, "Illegal tempfiles setting '%c'. using 'Auto' ", 
1470                         temp_policy);
1471         temp_policy='A';
1472     }
1473
1474     if (   ( temp_policy =='N' )   ||     /* always from memory */
1475          ( ( temp_policy =='A' ) &&       /* automatic */
1476              (zh->reg->key_file_no == 1) &&  /* this is first time */
1477              (final) ) )                     /* and last (=only) time */
1478     { /* go directly from memory */
1479         zh->reg->key_file_no =0; /* signal not to read files */
1480         zebra_index_merge(zh); 
1481         zh->reg->ptr_i = 0;
1482         zh->reg->key_buf_used = 0; 
1483         return; 
1484     }
1485
1486     /* Not doing directly from memory, write into a temp file */
1487     extract_get_fname_tmp (zh, out_fname, zh->reg->key_file_no);
1488
1489     if (!(outf = fopen (out_fname, "wb")))
1490     {
1491         yaz_log (YLOG_FATAL|YLOG_ERRNO, "fopen %s", out_fname);
1492         exit (1);
1493     }
1494     yaz_log (YLOG_LOG, "writing section %d", zh->reg->key_file_no);
1495     prevcp = cp = (zh->reg->key_buf)[zh->reg->ptr_top - ptr_i];
1496     
1497     encode_key_init (&encode_info);
1498     encode_key_write (cp, &encode_info, outf);
1499     
1500     while (--ptr_i > 0)
1501     {
1502         cp = (zh->reg->key_buf)[zh->reg->ptr_top - ptr_i];
1503         if (strcmp (cp, prevcp))
1504         {
1505             encode_key_flush ( &encode_info, outf);
1506             encode_key_init (&encode_info);
1507             encode_key_write (cp, &encode_info, outf);
1508             prevcp = cp;
1509         }
1510         else
1511             encode_key_write (cp + strlen(cp), &encode_info, outf);
1512     }
1513     encode_key_flush ( &encode_info, outf);
1514 #else
1515     qsort (key_buf + ptr_top-ptr_i, ptr_i, sizeof(char*), key_x_compare);
1516     extract_get_fname_tmp (out_fname, key_file_no);
1517
1518     if (!(outf = fopen (out_fname, "wb")))
1519     {
1520         yaz_log (YLOG_FATAL|YLOG_ERRNO, "fopen %s", out_fname);
1521         exit (1);
1522     }
1523     yaz_log (YLOG_LOG, "writing section %d", key_file_no);
1524     i = ptr_i;
1525     prevcp =  key_buf[ptr_top-i];
1526     while (1)
1527         if (!--i || strcmp (prevcp, key_buf[ptr_top-i]))
1528         {
1529             key_y_len = strlen(prevcp)+1;
1530 #if 0
1531             yaz_log (YLOG_LOG, "key_y_len: %2d %02x %02x %s",
1532                       key_y_len, prevcp[0], prevcp[1], 2+prevcp);
1533 #endif
1534             qsort (key_buf + ptr_top-ptr_i, ptr_i - i,
1535                                    sizeof(char*), key_y_compare);
1536             cp = key_buf[ptr_top-ptr_i];
1537             --key_y_len;
1538             encode_key_init (&encode_info);
1539             encode_key_write (cp, &encode_info, outf);
1540             while (--ptr_i > i)
1541             {
1542                 cp = key_buf[ptr_top-ptr_i];
1543                 encode_key_write (cp+key_y_len, &encode_info, outf);
1544             }
1545             encode_key_flush ( &encode_info, outf);
1546             if (!i)
1547                 break;
1548             prevcp = key_buf[ptr_top-ptr_i];
1549         }
1550 #endif
1551     if (fclose (outf))
1552     {
1553         yaz_log (YLOG_FATAL|YLOG_ERRNO, "fclose %s", out_fname);
1554         exit (1);
1555     }
1556     yaz_log (YLOG_LOG, "finished section %d", zh->reg->key_file_no);
1557     zh->reg->ptr_i = 0;
1558     zh->reg->key_buf_used = 0;
1559 }
1560
1561 ZEBRA_RES zebra_snippets_rec_keys(ZebraHandle zh,
1562                                   zebra_rec_keys_t reckeys,
1563                                   zebra_snippets *snippets)
1564 {
1565     NMEM nmem = nmem_create();
1566     if (zebra_rec_keys_rewind(reckeys)) 
1567     {
1568         const char *str;
1569         size_t slen;
1570         struct it_key key;
1571         while (zebra_rec_keys_read(reckeys, &str, &slen, &key))
1572         {
1573             char dst_buf[IT_MAX_WORD];
1574             char *dst_term = dst_buf;
1575             int ord;
1576             zint seqno;
1577             int index_type;
1578
1579             assert(key.len <= 4 && key.len > 2);
1580             seqno = key.mem[key.len-1];
1581             ord = CAST_ZINT_TO_INT(key.mem[0]);
1582             
1583             zebraExplain_lookup_ord(zh->reg->zei, ord, &index_type,
1584                                     0/* db */, 0 /* string_index */);
1585             assert(index_type);
1586             zebra_term_untrans_iconv(zh, nmem, index_type,
1587                                      &dst_term, str);
1588             zebra_snippets_append(snippets, seqno, ord, dst_term);
1589             nmem_reset(nmem);
1590         }
1591     }
1592     nmem_destroy(nmem);
1593     return ZEBRA_OK;
1594 }
1595
1596 void print_rec_keys(ZebraHandle zh, zebra_rec_keys_t reckeys)
1597 {
1598     yaz_log(YLOG_LOG, "print_rec_keys");
1599     if (zebra_rec_keys_rewind(reckeys))
1600     {
1601         const char *str;
1602         size_t slen;
1603         struct it_key key;
1604         while (zebra_rec_keys_read(reckeys, &str, &slen, &key))
1605         {
1606             char dst_buf[IT_MAX_WORD];
1607             zint seqno;
1608             int index_type;
1609             int ord = CAST_ZINT_TO_INT(key.mem[0]);
1610             const char *db = 0;
1611             assert(key.len <= 4 && key.len > 2);
1612
1613             zebraExplain_lookup_ord(zh->reg->zei, ord, &index_type, &db, 0);
1614             
1615             seqno = key.mem[key.len-1];
1616             
1617             zebra_term_untrans(zh, index_type, dst_buf, str);
1618             
1619             yaz_log(YLOG_LOG, "ord=%d seqno=" ZINT_FORMAT 
1620                     " term=%s", ord, seqno, dst_buf); 
1621         }
1622     }
1623 }
1624
1625 static void extract_add_index_string(RecWord *p, const char *str, int length)
1626 {
1627     struct it_key key;
1628
1629     ZebraHandle zh = p->extractCtrl->handle;
1630     ZebraExplainInfo zei = zh->reg->zei;
1631     int ch;
1632
1633     if (!p->index_name)
1634         return;
1635
1636     ch = zebraExplain_lookup_attr_str(zei, p->index_type, p->index_name);
1637     if (ch < 0)
1638         ch = zebraExplain_add_attr_str(zei, p->index_type, p->index_name);
1639
1640     key.len = 4;
1641     key.mem[0] = ch;
1642     key.mem[1] = p->record_id;
1643     key.mem[2] = p->section_id;
1644     key.mem[3] = p->seqno;
1645
1646 #if 0
1647     if (1)
1648     {
1649         char strz[80];
1650         int i;
1651
1652         strz[0] = 0;
1653         for (i = 0; i<length && i < 20; i++)
1654             sprintf(strz+strlen(strz), "%02X", str[i] & 0xff);
1655         /* just for debugging .. */
1656         yaz_log(YLOG_LOG, "add: set=%d use=%d "
1657                 "record_id=%lld section_id=%lld seqno=%lld %s",
1658                 p->attrSet, p->attrUse, p->record_id, p->section_id, p->seqno,
1659                 strz);
1660     }
1661 #endif
1662
1663     zebra_rec_keys_write(zh->reg->keys, str, length, &key);
1664 }
1665
1666 static void extract_add_sort_string(RecWord *p, const char *str, int length)
1667 {
1668     struct it_key key;
1669
1670     ZebraHandle zh = p->extractCtrl->handle;
1671     ZebraExplainInfo zei = zh->reg->zei;
1672     int ch;
1673
1674     if (!p->index_name)
1675         return;
1676
1677     ch = zebraExplain_lookup_attr_str(zei, p->index_type, p->index_name);
1678     if (ch < 0)
1679         ch = zebraExplain_add_attr_str(zei, p->index_type, p->index_name);
1680     key.len = 4;
1681     key.mem[0] = ch;
1682     key.mem[1] = p->record_id;
1683     key.mem[2] = p->section_id;
1684     key.mem[3] = p->seqno;
1685
1686 #if 0
1687     if (1)
1688     {
1689         char strz[80];
1690         int i;
1691
1692         strz[0] = 0;
1693         for (i = 0; i<length && i < 20; i++)
1694             sprintf(strz+strlen(strz), "%02X", str[i] & 0xff);
1695         /* just for debugging .. */
1696         yaz_log(YLOG_LOG, "add: set=%d use=%d "
1697                 "record_id=%lld section_id=%lld seqno=%lld %s",
1698                 p->attrSet, p->attrUse, p->record_id, p->section_id, p->seqno,
1699                 strz);
1700     }
1701 #endif
1702     zebra_rec_keys_write(zh->reg->sortKeys, str, length, &key);
1703 }
1704
1705 static void extract_add_string (RecWord *p, const char *string, int length)
1706 {
1707     assert (length > 0);
1708     if (zebra_maps_is_sort (p->zebra_maps, p->index_type))
1709         extract_add_sort_string (p, string, length);
1710     else
1711     {
1712         extract_add_index_string(p, string, length);
1713         if (zebra_maps_is_alwaysmatches(p->zebra_maps, p->index_type))
1714         {
1715             RecWord word;
1716             memcpy(&word, p, sizeof(word));
1717
1718             word.seqno = 1;
1719             extract_add_index_string (&word, "", 0);
1720         }
1721     }
1722 }
1723
1724 static void extract_add_incomplete_field (RecWord *p)
1725 {
1726     const char *b = p->term_buf;
1727     int remain = p->term_len;
1728     const char **map = 0;
1729     
1730     if (remain > 0)
1731         map = zebra_maps_input(p->zebra_maps, p->index_type, &b, remain, 0);
1732
1733     while (map)
1734     {
1735         char buf[IT_MAX_WORD+1];
1736         int i, remain;
1737
1738         /* Skip spaces */
1739         while (map && *map && **map == *CHR_SPACE)
1740         {
1741             remain = p->term_len - (b - p->term_buf);
1742             if (remain > 0)
1743                 map = zebra_maps_input(p->zebra_maps, p->index_type, &b,
1744                                        remain, 0);
1745             else
1746                 map = 0;
1747         }
1748         if (!map)
1749             break;
1750         i = 0;
1751         while (map && *map && **map != *CHR_SPACE)
1752         {
1753             const char *cp = *map;
1754
1755             while (i < IT_MAX_WORD && *cp)
1756                 buf[i++] = *(cp++);
1757             remain = p->term_len - (b - p->term_buf);
1758             if (remain > 0)
1759                 map = zebra_maps_input(p->zebra_maps, p->index_type, &b, remain, 0);
1760             else
1761                 map = 0;
1762         }
1763         if (!i)
1764             return;
1765         extract_add_string (p, buf, i);
1766         p->seqno++;
1767     }
1768 }
1769
1770 static void extract_add_complete_field (RecWord *p)
1771 {
1772     const char *b = p->term_buf;
1773     char buf[IT_MAX_WORD+1];
1774     const char **map = 0;
1775     int i = 0, remain = p->term_len;
1776
1777     if (remain > 0)
1778         map = zebra_maps_input (p->zebra_maps, p->index_type, &b, remain, 1);
1779
1780     while (remain > 0 && i < IT_MAX_WORD)
1781     {
1782         while (map && *map && **map == *CHR_SPACE)
1783         {
1784             remain = p->term_len - (b - p->term_buf);
1785
1786             if (remain > 0)
1787             {
1788                 int first = i ? 0 : 1;  /* first position */
1789                 map = zebra_maps_input(p->zebra_maps, p->index_type, &b, remain, first);
1790             }
1791             else
1792                 map = 0;
1793         }
1794         if (!map)
1795             break;
1796
1797         if (i && i < IT_MAX_WORD)
1798             buf[i++] = *CHR_SPACE;
1799         while (map && *map && **map != *CHR_SPACE)
1800         {
1801             const char *cp = *map;
1802
1803             if (**map == *CHR_CUT)
1804             {
1805                 i = 0;
1806             }
1807             else
1808             {
1809                 if (i >= IT_MAX_WORD)
1810                     break;
1811                 while (i < IT_MAX_WORD && *cp)
1812                     buf[i++] = *(cp++);
1813             }
1814             remain = p->term_len  - (b - p->term_buf);
1815             if (remain > 0)
1816             {
1817                 map = zebra_maps_input (p->zebra_maps, p->index_type, &b,
1818                                         remain, 0);
1819             }
1820             else
1821                 map = 0;
1822         }
1823     }
1824     if (!i)
1825         return;
1826     extract_add_string (p, buf, i);
1827 }
1828
1829 static void extract_token_add(RecWord *p)
1830 {
1831     WRBUF wrbuf;
1832     if (log_level)
1833         yaz_log(log_level, "extract_token_add "
1834                 "type=%c index=%s seqno=" ZINT_FORMAT " s=%.*s",
1835                 p->index_type, p->index_name, 
1836                 p->seqno, p->term_len, p->term_buf);
1837     if ((wrbuf = zebra_replace(p->zebra_maps, p->index_type, 0,
1838                                p->term_buf, p->term_len)))
1839     {
1840         p->term_buf = wrbuf_buf(wrbuf);
1841         p->term_len = wrbuf_len(wrbuf);
1842     }
1843     if (zebra_maps_is_complete (p->zebra_maps, p->index_type))
1844         extract_add_complete_field (p);
1845     else
1846         extract_add_incomplete_field(p);
1847 }
1848
1849 static void extract_set_store_data_cb(struct recExtractCtrl *p,
1850                                       void *buf, size_t sz)
1851 {
1852     ZebraHandle zh = (ZebraHandle) p->handle;
1853
1854     xfree(zh->store_data_buf);
1855     zh->store_data_buf = 0;
1856     zh->store_data_size = 0;
1857     if (buf && sz)
1858     {
1859         zh->store_data_buf = xmalloc(sz);
1860         zh->store_data_size = sz;
1861         memcpy(zh->store_data_buf, buf, sz);
1862     }
1863 }
1864
1865 static void extract_set_store_data_prepare(struct recExtractCtrl *p)
1866 {
1867     ZebraHandle zh = (ZebraHandle) p->handle;
1868     xfree(zh->store_data_buf);
1869     zh->store_data_buf = 0;
1870     zh->store_data_size = 0;
1871     p->setStoreData = extract_set_store_data_cb;
1872 }
1873
1874 static void extract_schema_add (struct recExtractCtrl *p, Odr_oid *oid)
1875 {
1876     ZebraHandle zh = (ZebraHandle) p->handle;
1877     zebraExplain_addSchema (zh->reg->zei, oid);
1878 }
1879
1880 void extract_flushSortKeys (ZebraHandle zh, SYSNO sysno,
1881                             int cmd, zebra_rec_keys_t reckeys)
1882 {
1883     if (zebra_rec_keys_rewind(reckeys))
1884     {
1885         SortIdx sortIdx = zh->reg->sortIdx;
1886         size_t slen;
1887         const char *str;
1888         struct it_key key_in;
1889
1890         sortIdx_sysno (sortIdx, sysno);
1891
1892         while (zebra_rec_keys_read(reckeys, &str, &slen, &key_in))
1893         {
1894             int ord = CAST_ZINT_TO_INT(key_in.mem[0]);
1895             
1896             sortIdx_type(sortIdx, ord);
1897             if (cmd == 1)
1898                 sortIdx_add(sortIdx, str, slen);
1899             else
1900                 sortIdx_add(sortIdx, "", 1);
1901         }
1902     }
1903 }
1904
1905 static void encode_key_init(struct encode_info *i)
1906 {
1907     i->encode_handle = iscz1_start();
1908     i->decode_handle = iscz1_start();
1909 }
1910
1911 static void encode_key_write (char *k, struct encode_info *i, FILE *outf)
1912 {
1913     struct it_key key;
1914     char *bp = i->buf, *bp0;
1915     const char *src = (char *) &key;
1916
1917     /* copy term to output buf */
1918     while ((*bp++ = *k++))
1919         ;
1920     /* and copy & align key so we can mangle */
1921     memcpy (&key, k+1, sizeof(struct it_key));  /* *k is insert/delete */
1922
1923 #if 0
1924     /* debugging */
1925     key_logdump_txt(YLOG_LOG, &key, *k ? "i" : "d");
1926 #endif
1927     assert(key.mem[0] >= 0);
1928
1929     bp0 = bp++;
1930     iscz1_encode(i->encode_handle, &bp, &src);
1931
1932     *bp0 = (*k * 128) + bp - bp0 - 1; /* length and insert/delete combined */
1933     if (fwrite (i->buf, bp - i->buf, 1, outf) != 1)
1934     {
1935         yaz_log (YLOG_FATAL|YLOG_ERRNO, "fwrite");
1936         exit (1);
1937     }
1938
1939 #if 0
1940     /* debugging */
1941     if (1)
1942     {
1943         struct it_key key2;
1944         const char *src = bp0+1;
1945         char *dst = (char*) &key2;
1946         iscz1_decode(i->decode_handle, &dst, &src);
1947
1948         key_logdump_txt(YLOG_LOG, &key2, *k ? "i" : "d");
1949
1950         assert(key2.mem[1]);
1951     }
1952 #endif
1953 }
1954
1955 static void encode_key_flush (struct encode_info *i, FILE *outf)
1956
1957     iscz1_stop(i->encode_handle);
1958     iscz1_stop(i->decode_handle);
1959 }
1960
1961 /*
1962  * Local variables:
1963  * c-basic-offset: 4
1964  * indent-tabs-mode: nil
1965  * End:
1966  * vim: shiftwidth=4 tabstop=8 expandtab
1967  */
1968