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