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