Zebra uses own XML reader (was part of YAZ before)
[idzebra-moved-to-github.git] / index / extract.c
1 /* $Id: extract.c,v 1.121 2002-08-28 12:47:10 adam Exp $
2    Copyright (C) 1995,1996,1997,1998,1999,2000,2001,2002
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
24 #include <stdio.h>
25 #include <assert.h>
26 #ifdef WIN32
27 #include <io.h>
28 #else
29 #include <unistd.h>
30 #endif
31 #include <fcntl.h>
32
33 #include "index.h"
34 #include <direntz.h>
35 #include <charmap.h>
36
37 #if _FILE_OFFSET_BITS == 64
38 #define PRINTF_OFF_T "%Ld"
39 #else
40 #define PRINTF_OFF_T "%ld"
41 #endif
42
43 static void shellsort(void *ar, int r, size_t s,
44                       int (*cmp)(const void *a, const void *b))
45 {
46     char *a = ar;
47     char v[100];
48     int h, i, j, k;
49     static const int incs[16] = { 1391376, 463792, 198768, 86961, 33936,
50                                   13776, 4592, 1968, 861, 336, 
51                                   112, 48, 21, 7, 3, 1 };
52     for ( k = 0; k < 16; k++)
53         for (h = incs[k], i = h; i < r; i++)
54         { 
55             memcpy (v, a+s*i, s);
56             j = i;
57             while (j > h && (*cmp)(a + s*(j-h), v) > 0)
58             {
59                 memcpy (a + s*j, a + s*(j-h), s);
60                 j -= h;
61             }
62             memcpy (a+s*j, v, s);
63         } 
64 }
65
66
67 static void logRecord (ZebraHandle zh)
68 {
69     ++zh->records_processed;
70     if (!(zh->records_processed % 1000))
71     {
72         logf (LOG_LOG, "Records: %7d i/u/d %d/%d/%d", 
73               zh->records_processed, zh->records_inserted, zh->records_updated,
74               zh->records_deleted);
75     }
76 }
77
78 static void extract_init (struct recExtractCtrl *p, RecWord *w)
79 {
80     w->zebra_maps = p->zebra_maps;
81     w->seqno = 1;
82     w->attrSet = VAL_BIB1;
83     w->attrUse = 1016;
84     w->reg_type = 'w';
85     w->extractCtrl = p;
86 }
87
88 static const char **searchRecordKey (ZebraHandle zh,
89                                      struct recKeys *reckeys,
90                                      int attrSetS, int attrUseS)
91 {
92     static const char *ws[32];
93     int off = 0;
94     int startSeq = -1;
95     int i;
96     int seqno = 0;
97 #if SU_SCHEME
98     int chS, ch;
99 #else
100     short attrUse;
101     char attrSet;
102 #endif
103
104     for (i = 0; i<32; i++)
105         ws[i] = NULL;
106
107 #if SU_SCHEME
108     chS = zebraExplain_lookupSU (zh->reg->zei, attrSetS, attrUseS);
109     if (chS < 0)
110         return ws;
111 #endif
112     while (off < reckeys->buf_used)
113     {
114
115         const char *src = reckeys->buf + off;
116         const char *wstart;
117         int lead;
118     
119         lead = *src++;
120 #if SU_SCHEME
121         if ((lead & 3)<3)
122         {
123             memcpy (&ch, src, sizeof(ch));
124             src += sizeof(ch);
125         }
126 #else
127         if (!(lead & 1))
128         {
129             memcpy (&attrSet, src, sizeof(attrSet));
130             src += sizeof(attrSet);
131         }
132         if (!(lead & 2))
133         {
134             memcpy (&attrUse, src, sizeof(attrUse));
135             src += sizeof(attrUse);
136         }
137 #endif
138         wstart = src;
139         while (*src++)
140             ;
141         if (lead & 60)
142             seqno += ((lead>>2) & 15)-1;
143         else
144         {
145             memcpy (&seqno, src, sizeof(seqno));
146             src += sizeof(seqno);
147         }
148         if (
149 #if SU_SCHEME
150             ch == chS
151 #else
152             attrUseS == attrUse && attrSetS == attrSet
153 #endif
154             )
155         {
156             int woff;
157
158
159             if (startSeq == -1)
160                 startSeq = seqno;
161             woff = seqno - startSeq;
162             if (woff >= 0 && woff < 31)
163                 ws[woff] = wstart;
164         }
165
166         off = src - reckeys->buf;
167     }
168     assert (off == reckeys->buf_used);
169     return ws;
170 }
171
172 struct file_read_info {
173     off_t file_max;         /* maximum offset so far */
174     off_t file_offset;      /* current offset */
175     off_t file_moffset;     /* offset of rec/rec boundary */
176     int file_more;
177     int fd;
178     char *sdrbuf;
179     int sdrmax;
180 };
181
182 static struct file_read_info *file_read_start (int fd)
183 {
184     struct file_read_info *fi = (struct file_read_info *)
185         xmalloc (sizeof(*fi));
186
187     fi->fd = fd;
188     fi->file_max = 0;
189     fi->file_moffset = 0;
190     fi->sdrbuf = 0;
191     fi->sdrmax = 0;
192     return fi;
193 }
194
195 static void file_read_stop (struct file_read_info *fi)
196 {
197     xfree (fi);
198 }
199
200 static off_t file_seek (void *handle, off_t offset)
201 {
202     struct file_read_info *p = (struct file_read_info *) handle;
203     p->file_offset = offset;
204     if (p->sdrbuf)
205         return offset;
206     return lseek (p->fd, offset, SEEK_SET);
207 }
208
209 static off_t file_tell (void *handle)
210 {
211     struct file_read_info *p = (struct file_read_info *) handle;
212     return p->file_offset;
213 }
214
215 static int file_read (void *handle, char *buf, size_t count)
216 {
217     struct file_read_info *p = (struct file_read_info *) handle;
218     int fd = p->fd;
219     int r;
220     if (p->sdrbuf)
221     {
222         r = count;
223         if (r > p->sdrmax - p->file_offset)
224             r = p->sdrmax - p->file_offset;
225         if (r)
226             memcpy (buf, p->sdrbuf + p->file_offset, r);
227     }
228     else
229         r = read (fd, buf, count);
230     if (r > 0)
231     {
232         p->file_offset += r;
233         if (p->file_offset > p->file_max)
234             p->file_max = p->file_offset;
235     }
236     return r;
237 }
238
239 static void file_begin (void *handle)
240 {
241     struct file_read_info *p = (struct file_read_info *) handle;
242
243     p->file_offset = p->file_moffset;
244     if (!p->sdrbuf && p->file_moffset)
245         lseek (p->fd, p->file_moffset, SEEK_SET);
246     p->file_more = 0;
247 }
248
249 static void file_end (void *handle, off_t offset)
250 {
251     struct file_read_info *p = (struct file_read_info *) handle;
252
253     assert (p->file_more == 0);
254     p->file_more = 1;
255     p->file_moffset = offset;
256 }
257
258 static char *fileMatchStr (ZebraHandle zh,
259                            struct recKeys *reckeys, struct recordGroup *rGroup,
260                            const char *fname, const char *spec)
261 {
262     static char dstBuf[2048];      /* static here ??? */
263     char *dst = dstBuf;
264     const char *s = spec;
265     static const char **w;
266
267     while (1)
268     {
269         while (*s == ' ' || *s == '\t')
270             s++;
271         if (!*s)
272             break;
273         if (*s == '(')
274         {
275             char attset_str[64], attname_str[64];
276             data1_attset *attset;
277             int i;
278             char matchFlag[32];
279             int attSet = 1, attUse = 1;
280             int first = 1;
281
282             s++;
283             for (i = 0; *s && *s != ',' && *s != ')'; s++)
284                 if (i < 63)
285                     attset_str[i++] = *s;
286             attset_str[i] = '\0';
287
288             if (*s == ',')
289             {
290                 s++;
291                 for (i = 0; *s && *s != ')'; s++)
292                     if (i < 63)
293                         attname_str[i++] = *s;
294                 attname_str[i] = '\0';
295             }
296             
297             if ((attset = data1_get_attset (zh->reg->dh, attset_str)))
298             {
299                 data1_att *att;
300                 attSet = attset->reference;
301                 att = data1_getattbyname(zh->reg->dh, attset, attname_str);
302                 if (att)
303                     attUse = att->value;
304                 else
305                     attUse = atoi (attname_str);
306             }
307             w = searchRecordKey (zh, reckeys, attSet, attUse);
308             assert (w);
309
310             if (*s == ')')
311             {
312                 for (i = 0; i<32; i++)
313                     matchFlag[i] = 1;
314             }
315             else
316             {
317                 logf (LOG_WARN, "Missing ) in match criteria %s in group %s",
318                       spec, rGroup->groupName ? rGroup->groupName : "none");
319                 return NULL;
320             }
321             s++;
322
323             for (i = 0; i<32; i++)
324                 if (matchFlag[i] && w[i])
325                 {
326                     if (first)
327                     {
328                         *dst++ = ' ';
329                         first = 0;
330                     }
331                     strcpy (dst, w[i]);
332                     dst += strlen(w[i]);
333                 }
334             if (first)
335             {
336                 logf (LOG_WARN, "Record didn't contain match"
337                       " fields in (%s,%s)", attset_str, attname_str);
338                 return NULL;
339             }
340         }
341         else if (*s == '$')
342         {
343             int spec_len;
344             char special[64];
345             const char *spec_src = NULL;
346             const char *s1 = ++s;
347             while (*s1 && *s1 != ' ' && *s1 != '\t')
348                 s1++;
349
350             spec_len = s1 - s;
351             if (spec_len > 63)
352                 spec_len = 63;
353             memcpy (special, s, spec_len);
354             special[spec_len] = '\0';
355             s = s1;
356
357             if (!strcmp (special, "group"))
358                 spec_src = rGroup->groupName;
359             else if (!strcmp (special, "database"))
360                 spec_src = rGroup->databaseName;
361             else if (!strcmp (special, "filename"))
362                 spec_src = fname;
363             else if (!strcmp (special, "type"))
364                 spec_src = rGroup->recordType;
365             else 
366                 spec_src = NULL;
367             if (spec_src)
368             {
369                 strcpy (dst, spec_src);
370                 dst += strlen (spec_src);
371             }
372         }
373         else if (*s == '\"' || *s == '\'')
374         {
375             int stopMarker = *s++;
376             char tmpString[64];
377             int i = 0;
378
379             while (*s && *s != stopMarker)
380             {
381                 if (i < 63)
382                     tmpString[i++] = *s++;
383             }
384             if (*s)
385                 s++;
386             tmpString[i] = '\0';
387             strcpy (dst, tmpString);
388             dst += strlen (tmpString);
389         }
390         else
391         {
392             logf (LOG_WARN, "Syntax error in match criteria %s in group %s",
393                   spec, rGroup->groupName ? rGroup->groupName : "none");
394             return NULL;
395         }
396         *dst++ = 1;
397     }
398     if (dst == dstBuf)
399     {
400         logf (LOG_WARN, "No match criteria for record %s in group %s",
401               fname, rGroup->groupName ? rGroup->groupName : "none");
402         return NULL;
403     }
404     *dst = '\0';
405     return dstBuf;
406 }
407
408 struct recordLogInfo {
409     const char *fname;
410     int recordOffset;
411     struct recordGroup *rGroup;
412 };
413      
414 static void recordLogPreamble (int level, const char *msg, void *info)
415 {
416     struct recordLogInfo *p = (struct recordLogInfo *) info;
417     FILE *outf = yaz_log_file ();
418
419     if (level & LOG_LOG)
420         return ;
421     fprintf (outf, "File %s, offset %d, type %s\n",
422              p->rGroup->recordType, p->recordOffset, p->fname);
423     log_event_start (NULL, NULL);
424 }
425
426
427 static int recordExtract (ZebraHandle zh,
428                           SYSNO *sysno, const char *fname,
429                           struct recordGroup *rGroup, int deleteFlag,
430                           struct file_read_info *fi,
431                           RecType recType, char *subType, void *clientData)
432 {
433     RecordAttr *recordAttr;
434     int r;
435     char *matchStr;
436     SYSNO sysnotmp;
437     Record rec;
438     struct recordLogInfo logInfo;
439     off_t recordOffset = 0;
440
441     if (fi->fd != -1)
442     {
443         struct recExtractCtrl extractCtrl;
444
445         /* we are going to read from a file, so prepare the extraction */
446         int i;
447
448         zh->reg->keys.buf_used = 0;
449         zh->reg->keys.prevAttrUse = -1;
450         zh->reg->keys.prevAttrSet = -1;
451         zh->reg->keys.prevSeqNo = 0;
452         zh->reg->sortKeys = 0;
453         
454         recordOffset = fi->file_moffset;
455         extractCtrl.offset = fi->file_moffset;
456         extractCtrl.readf = file_read;
457         extractCtrl.seekf = file_seek;
458         extractCtrl.tellf = file_tell;
459         extractCtrl.endf = file_end;
460         extractCtrl.fh = fi;
461         extractCtrl.subType = subType;
462         extractCtrl.init = extract_init;
463         extractCtrl.tokenAdd = extract_token_add;
464         extractCtrl.schemaAdd = extract_schema_add;
465         extractCtrl.dh = zh->reg->dh;
466         extractCtrl.handle = zh;
467         for (i = 0; i<256; i++)
468         {
469             if (zebra_maps_is_positioned(zh->reg->zebra_maps, i))
470                 extractCtrl.seqno[i] = 1;
471             else
472                 extractCtrl.seqno[i] = 0;
473         }
474         extractCtrl.zebra_maps = zh->reg->zebra_maps;
475         extractCtrl.flagShowRecords = !rGroup->flagRw;
476
477         if (!rGroup->flagRw)
478             printf ("File: %s " PRINTF_OFF_T "\n", fname, recordOffset);
479
480         logInfo.fname = fname;
481         logInfo.recordOffset = recordOffset;
482         logInfo.rGroup = rGroup;
483         log_event_start (recordLogPreamble, &logInfo);
484
485         r = (*recType->extract)(clientData, &extractCtrl);
486
487         log_event_start (NULL, NULL);
488
489         if (r == RECCTRL_EXTRACT_EOF)
490             return 0;
491         else if (r == RECCTRL_EXTRACT_ERROR_GENERIC)
492         {
493             /* error occured during extraction ... */
494             if (rGroup->flagRw &&
495                 zh->records_processed < rGroup->fileVerboseLimit)
496             {
497                 logf (LOG_WARN, "fail %s %s " PRINTF_OFF_T, rGroup->recordType,
498                       fname, recordOffset);
499             }
500             return 0;
501         }
502         else if (r == RECCTRL_EXTRACT_ERROR_NO_SUCH_FILTER)
503         {
504             /* error occured during extraction ... */
505             if (rGroup->flagRw &&
506                 zh->records_processed < rGroup->fileVerboseLimit)
507             {
508                 logf (LOG_WARN, "no filter for %s %s " 
509                       PRINTF_OFF_T, rGroup->recordType,
510                       fname, recordOffset);
511             }
512             return 0;
513         }
514         if (zh->reg->keys.buf_used == 0)
515         {
516             /* the extraction process returned no information - the record
517                is probably empty - unless flagShowRecords is in use */
518             if (!rGroup->flagRw)
519                 return 1;
520             
521             logf (LOG_WARN, "empty %s %s " PRINTF_OFF_T, rGroup->recordType,
522                   fname, recordOffset);
523             return 1;
524         }
525     }
526
527     /* perform match if sysno not known and if match criteria is specified */
528        
529     matchStr = NULL;
530     if (!sysno) 
531     {
532         sysnotmp = 0;
533         sysno = &sysnotmp;
534         if (rGroup->recordId && *rGroup->recordId)
535         {
536             char *rinfo;
537         
538             matchStr = fileMatchStr (zh, &zh->reg->keys, rGroup, fname, 
539                                      rGroup->recordId);
540             if (matchStr)
541             {
542                 rinfo = dict_lookup (zh->reg->matchDict, matchStr);
543                 if (rinfo)
544                     memcpy (sysno, rinfo+1, sizeof(*sysno));
545             }
546             else
547             {
548                 logf (LOG_WARN, "Bad match criteria");
549                 return 0;
550             }
551         }
552     }
553
554     if (! *sysno)
555     {
556         /* new record */
557         if (deleteFlag)
558         {
559             logf (LOG_LOG, "delete %s %s " PRINTF_OFF_T, rGroup->recordType,
560                   fname, recordOffset);
561             logf (LOG_WARN, "cannot delete record above (seems new)");
562             return 1;
563         }
564         if (zh->records_processed < rGroup->fileVerboseLimit)
565             logf (LOG_LOG, "add %s %s " PRINTF_OFF_T, rGroup->recordType,
566                   fname, recordOffset);
567         rec = rec_new (zh->reg->records);
568
569         *sysno = rec->sysno;
570
571         recordAttr = rec_init_attr (zh->reg->zei, rec);
572
573         if (matchStr)
574         {
575             dict_insert (zh->reg->matchDict, matchStr, sizeof(*sysno), sysno);
576         }
577         extract_flushRecordKeys (zh, *sysno, 1, &zh->reg->keys);
578         extract_flushSortKeys (zh, *sysno, 1, &zh->reg->sortKeys);
579
580         zh->records_inserted++;
581     }
582     else
583     {
584         /* record already exists */
585         struct recKeys delkeys;
586
587         rec = rec_get (zh->reg->records, *sysno);
588         assert (rec);
589         
590         recordAttr = rec_init_attr (zh->reg->zei, rec);
591
592         if (recordAttr->runNumber ==
593             zebraExplain_runNumberIncrement (zh->reg->zei, 0))
594         {
595             yaz_log (LOG_LOG, "run number = %d", recordAttr->runNumber);
596             yaz_log (LOG_LOG, "skipped %s %s " PRINTF_OFF_T,
597                      rGroup->recordType, fname, recordOffset);
598             extract_flushSortKeys (zh, *sysno, -1, &zh->reg->sortKeys);
599             rec_rm (&rec);
600             logRecord (zh);
601             return 1;
602         }
603         delkeys.buf_used = rec->size[recInfo_delKeys];
604         delkeys.buf = rec->info[recInfo_delKeys];
605         extract_flushSortKeys (zh, *sysno, 0, &zh->reg->sortKeys);
606         extract_flushRecordKeys (zh, *sysno, 0, &delkeys);
607         if (deleteFlag)
608         {
609             /* record going to be deleted */
610             if (!delkeys.buf_used)
611             {
612                 logf (LOG_LOG, "delete %s %s " PRINTF_OFF_T,
613                       rGroup->recordType, fname, recordOffset);
614                 logf (LOG_WARN, "cannot delete file above, storeKeys false");
615             }
616             else
617             {
618                 if (zh->records_processed < rGroup->fileVerboseLimit)
619                     logf (LOG_LOG, "delete %s %s " PRINTF_OFF_T,
620                          rGroup->recordType, fname, recordOffset);
621                 zh->records_deleted++;
622                 if (matchStr)
623                     dict_delete (zh->reg->matchDict, matchStr);
624                 rec_del (zh->reg->records, &rec);
625             }
626             rec_rm (&rec);
627             logRecord (zh);
628             return 1;
629         }
630         else
631         {
632             /* record going to be updated */
633             if (!delkeys.buf_used)
634             {
635                 logf (LOG_LOG, "update %s %s " PRINTF_OFF_T,
636                       rGroup->recordType, fname, recordOffset);
637                 logf (LOG_WARN, "cannot update file above, storeKeys false");
638             }
639             else
640             {
641                 if (zh->records_processed < rGroup->fileVerboseLimit)
642                     logf (LOG_LOG, "update %s %s " PRINTF_OFF_T,
643                         rGroup->recordType, fname, recordOffset);
644                 extract_flushRecordKeys (zh, *sysno, 1, &zh->reg->keys);
645                 zh->records_updated++;
646             }
647         }
648     }
649     /* update file type */
650     xfree (rec->info[recInfo_fileType]);
651     rec->info[recInfo_fileType] =
652         rec_strdup (rGroup->recordType, &rec->size[recInfo_fileType]);
653
654     /* update filename */
655     xfree (rec->info[recInfo_filename]);
656     rec->info[recInfo_filename] =
657         rec_strdup (fname, &rec->size[recInfo_filename]);
658
659     /* update delete keys */
660     xfree (rec->info[recInfo_delKeys]);
661     if (zh->reg->keys.buf_used > 0 && rGroup->flagStoreKeys == 1)
662     {
663 #if 1
664         rec->size[recInfo_delKeys] = zh->reg->keys.buf_used;
665         rec->info[recInfo_delKeys] = zh->reg->keys.buf;
666         zh->reg->keys.buf = NULL;
667         zh->reg->keys.buf_max = 0;
668 #else
669         rec->info[recInfo_delKeys] = xmalloc (reckeys.buf_used);
670         rec->size[recInfo_delKeys] = reckeys.buf_used;
671         memcpy (rec->info[recInfo_delKeys], reckeys.buf,
672                 rec->size[recInfo_delKeys]);
673 #endif
674     }
675     else
676     {
677         rec->info[recInfo_delKeys] = NULL;
678         rec->size[recInfo_delKeys] = 0;
679     }
680
681     /* save file size of original record */
682     zebraExplain_recordBytesIncrement (zh->reg->zei,
683                                        - recordAttr->recordSize);
684     recordAttr->recordSize = fi->file_moffset - recordOffset;
685     if (!recordAttr->recordSize)
686         recordAttr->recordSize = fi->file_max - recordOffset;
687     zebraExplain_recordBytesIncrement (zh->reg->zei,
688                                        recordAttr->recordSize);
689
690     /* set run-number for this record */
691     recordAttr->runNumber = zebraExplain_runNumberIncrement (zh->reg->zei,
692                                                              0);
693
694     /* update store data */
695     xfree (rec->info[recInfo_storeData]);
696     if (rGroup->flagStoreData == 1)
697     {
698         rec->size[recInfo_storeData] = recordAttr->recordSize;
699         rec->info[recInfo_storeData] = (char *)
700             xmalloc (recordAttr->recordSize);
701         if (lseek (fi->fd, recordOffset, SEEK_SET) < 0)
702         {
703             logf (LOG_ERRNO|LOG_FATAL, "seek to " PRINTF_OFF_T " in %s",
704                   recordOffset, fname);
705             exit (1);
706         }
707         if (read (fi->fd, rec->info[recInfo_storeData], recordAttr->recordSize)
708             < recordAttr->recordSize)
709         {
710             logf (LOG_ERRNO|LOG_FATAL, "read %d bytes of %s",
711                   recordAttr->recordSize, fname);
712             exit (1);
713         }
714     }
715     else
716     {
717         rec->info[recInfo_storeData] = NULL;
718         rec->size[recInfo_storeData] = 0;
719     }
720     /* update database name */
721     xfree (rec->info[recInfo_databaseName]);
722     rec->info[recInfo_databaseName] =
723         rec_strdup (rGroup->databaseName, &rec->size[recInfo_databaseName]); 
724
725     /* update offset */
726     recordAttr->recordOffset = recordOffset;
727     
728     /* commit this record */
729     rec_put (zh->reg->records, &rec);
730     logRecord (zh);
731     return 1;
732 }
733
734 int fileExtract (ZebraHandle zh, SYSNO *sysno, const char *fname, 
735                  const struct recordGroup *rGroupP, int deleteFlag)
736 {
737     int r, i, fd;
738     char gprefix[128];
739     char ext[128];
740     char ext_res[128];
741     char subType[128];
742     RecType recType;
743     struct recordGroup rGroupM;
744     struct recordGroup *rGroup = &rGroupM;
745     struct file_read_info *fi;
746     void *clientData;
747
748     memcpy (rGroup, rGroupP, sizeof(*rGroupP));
749    
750     if (!rGroup->groupName || !*rGroup->groupName)
751         *gprefix = '\0';
752     else
753         sprintf (gprefix, "%s.", rGroup->groupName);
754
755     logf (LOG_DEBUG, "fileExtract %s", fname);
756
757     /* determine file extension */
758     *ext = '\0';
759     for (i = strlen(fname); --i >= 0; )
760         if (fname[i] == '/')
761             break;
762         else if (fname[i] == '.')
763         {
764             strcpy (ext, fname+i+1);
765             break;
766         }
767     /* determine file type - depending on extension */
768     if (!rGroup->recordType)
769     {
770         sprintf (ext_res, "%srecordType.%s", gprefix, ext);
771         if (!(rGroup->recordType = res_get (zh->res, ext_res)))
772         {
773             sprintf (ext_res, "%srecordType", gprefix);
774             rGroup->recordType = res_get (zh->res, ext_res);
775         }
776     }
777     if (!rGroup->recordType)
778     {
779         if (zh->records_processed < rGroup->fileVerboseLimit)
780             logf (LOG_LOG, "? %s", fname);
781         return 0;
782     }
783     if (!*rGroup->recordType)
784         return 0;
785     if (!(recType =
786           recType_byName (zh->reg->recTypes, rGroup->recordType, subType,
787                           &clientData)))
788     {
789         logf (LOG_WARN, "No such record type: %s", rGroup->recordType);
790         return 0;
791     }
792
793     /* determine match criteria */
794     if (!rGroup->recordId)
795     {
796         sprintf (ext_res, "%srecordId.%s", gprefix, ext);
797         rGroup->recordId = res_get (zh->res, ext_res);
798     }
799
800     /* determine database name */
801     if (!rGroup->databaseName)
802     {
803         sprintf (ext_res, "%sdatabase.%s", gprefix, ext);
804         if (!(rGroup->databaseName = res_get (zh->res, ext_res)))
805         {
806             sprintf (ext_res, "%sdatabase", gprefix);
807             rGroup->databaseName = res_get (zh->res, ext_res);
808         }
809     }
810     if (!rGroup->databaseName)
811         rGroup->databaseName = "Default";
812
813     /* determine if explain database */
814     
815     sprintf (ext_res, "%sexplainDatabase", gprefix);
816     rGroup->explainDatabase =
817         atoi (res_get_def (zh->res, ext_res, "0"));
818
819     /* announce database */
820     if (zebraExplain_curDatabase (zh->reg->zei, rGroup->databaseName))
821     {
822         if (zebraExplain_newDatabase (zh->reg->zei, rGroup->databaseName,
823                                       rGroup->explainDatabase))
824             return 0;
825     }
826
827     if (rGroup->flagStoreData == -1)
828     {
829         const char *sval;
830         sprintf (ext_res, "%sstoreData.%s", gprefix, ext);
831         if (!(sval = res_get (zh->res, ext_res)))
832         {
833             sprintf (ext_res, "%sstoreData", gprefix);
834             sval = res_get (zh->res, ext_res);
835         }
836         if (sval)
837             rGroup->flagStoreData = atoi (sval);
838     }
839     if (rGroup->flagStoreData == -1)
840         rGroup->flagStoreData = 0;
841
842     if (rGroup->flagStoreKeys == -1)
843     {
844         const char *sval;
845
846         sprintf (ext_res, "%sstoreKeys.%s", gprefix, ext);
847         sval = res_get (zh->res, ext_res);
848         if (!sval)
849         {
850             sprintf (ext_res, "%sstoreKeys", gprefix);
851             sval = res_get (zh->res, ext_res);
852         }
853         if (!sval)
854             sval = res_get (zh->res, "storeKeys");
855         if (sval)
856             rGroup->flagStoreKeys = atoi (sval);
857     }
858     if (rGroup->flagStoreKeys == -1)
859         rGroup->flagStoreKeys = 0;
860
861     if (sysno && deleteFlag)
862         fd = -1;
863     else
864     {
865         char full_rep[1024];
866
867         if (zh->path_reg && !yaz_is_abspath (fname))
868         {
869             strcpy (full_rep, zh->path_reg);
870             strcat (full_rep, "/");
871             strcat (full_rep, fname);
872         }
873         else
874             strcpy (full_rep, fname);
875         
876
877         if ((fd = open (full_rep, O_BINARY|O_RDONLY)) == -1)
878         {
879             logf (LOG_WARN|LOG_ERRNO, "open %s", full_rep);
880             return 0;
881         }
882     }
883     fi = file_read_start (fd);
884     do
885     {
886         file_begin (fi);
887         r = recordExtract (zh, sysno, fname, rGroup, deleteFlag, fi,
888                            recType, subType, clientData);
889     } while (r && !sysno && fi->file_more);
890     file_read_stop (fi);
891     if (fd != -1)
892         close (fd);
893     return r;
894 }
895
896
897 int extract_rec_in_mem (ZebraHandle zh, const char *recordType,
898                         const char *buf, size_t buf_size,
899                         const char *databaseName, int delete_flag,
900                         int test_mode, int *sysno,
901                         int store_keys, int store_data,
902                         const char *match_criteria)
903 {
904     RecordAttr *recordAttr;
905     struct recExtractCtrl extractCtrl;
906     int i, r;
907     char *matchStr = 0;
908     RecType recType;
909     char subType[1024];
910     void *clientData;
911     const char *fname = "<no file>";
912     Record rec;
913     long recordOffset = 0;
914     struct zebra_fetch_control fc;
915
916     fc.fd = -1;
917     fc.record_int_buf = buf;
918     fc.record_int_len = buf_size;
919     fc.record_int_pos = 0;
920     fc.offset_end = 0;
921     fc.record_offset = 0;
922
923     extractCtrl.offset = 0;
924     extractCtrl.readf = zebra_record_int_read;
925     extractCtrl.seekf = zebra_record_int_seek;
926     extractCtrl.tellf = zebra_record_int_tell;
927     extractCtrl.endf = zebra_record_int_end;
928     extractCtrl.fh = &fc;
929
930     /* announce database */
931     if (zebraExplain_curDatabase (zh->reg->zei, databaseName))
932     {
933         if (zebraExplain_newDatabase (zh->reg->zei, databaseName, 0))
934             return 0;
935     }
936     if (!(recType =
937           recType_byName (zh->reg->recTypes, recordType, subType,
938                           &clientData)))
939     {
940         logf (LOG_WARN, "No such record type: %s", recordType);
941         return 0;
942     }
943
944     zh->reg->keys.buf_used = 0;
945     zh->reg->keys.prevAttrUse = -1;
946     zh->reg->keys.prevAttrSet = -1;
947     zh->reg->keys.prevSeqNo = 0;
948     zh->reg->sortKeys = 0;
949
950     extractCtrl.subType = subType;
951     extractCtrl.init = extract_init;
952     extractCtrl.tokenAdd = extract_token_add;
953     extractCtrl.schemaAdd = extract_schema_add;
954     extractCtrl.dh = zh->reg->dh;
955     extractCtrl.handle = zh;
956     extractCtrl.zebra_maps = zh->reg->zebra_maps;
957     extractCtrl.flagShowRecords = 0;
958     for (i = 0; i<256; i++)
959     {
960         if (zebra_maps_is_positioned(zh->reg->zebra_maps, i))
961             extractCtrl.seqno[i] = 1;
962         else
963             extractCtrl.seqno[i] = 0;
964     }
965
966     r = (*recType->extract)(clientData, &extractCtrl);
967
968     if (r == RECCTRL_EXTRACT_EOF)
969         return 0;
970     else if (r == RECCTRL_EXTRACT_ERROR_GENERIC)
971     {
972         /* error occured during extraction ... */
973         yaz_log (LOG_WARN, "extract error: generic");
974         return 0;
975     }
976     else if (r == RECCTRL_EXTRACT_ERROR_NO_SUCH_FILTER)
977     {
978         /* error occured during extraction ... */
979         yaz_log (LOG_WARN, "extract error: no such filter");
980         return 0;
981     }
982     if (zh->reg->keys.buf_used == 0)
983     {
984         /* the extraction process returned no information - the record
985            is probably empty - unless flagShowRecords is in use */
986         if (test_mode)
987             return 1;
988         logf (LOG_WARN, "No keys generated for record");
989         logf (LOG_WARN, " The file is probably empty");
990         return 1;
991     }
992     /* match criteria */
993
994     if (! *sysno)
995     {
996         /* new record */
997         if (delete_flag)
998         {
999             logf (LOG_LOG, "delete %s %s %ld", recordType,
1000                   fname, (long) recordOffset);
1001             logf (LOG_WARN, "cannot delete record above (seems new)");
1002             return 1;
1003         }
1004         logf (LOG_LOG, "add %s %s %ld", recordType, fname,
1005               (long) recordOffset);
1006         rec = rec_new (zh->reg->records);
1007
1008         *sysno = rec->sysno;
1009
1010         recordAttr = rec_init_attr (zh->reg->zei, rec);
1011
1012         if (matchStr)
1013         {
1014             dict_insert (zh->reg->matchDict, matchStr,
1015                          sizeof(*sysno), sysno);
1016         }
1017         extract_flushRecordKeys (zh, *sysno, 1, &zh->reg->keys);
1018         extract_flushSortKeys (zh, *sysno, 1, &zh->reg->sortKeys);
1019     }
1020     else
1021     {
1022         /* record already exists */
1023         struct recKeys delkeys;
1024
1025         rec = rec_get (zh->reg->records, *sysno);
1026         assert (rec);
1027         
1028         recordAttr = rec_init_attr (zh->reg->zei, rec);
1029
1030         if (recordAttr->runNumber ==
1031             zebraExplain_runNumberIncrement (zh->reg->zei, 0))
1032         {
1033             logf (LOG_LOG, "skipped %s %s %ld", recordType,
1034                   fname, (long) recordOffset);
1035             rec_rm (&rec);
1036             return 1;
1037         }
1038         delkeys.buf_used = rec->size[recInfo_delKeys];
1039         delkeys.buf = rec->info[recInfo_delKeys];
1040         extract_flushSortKeys (zh, *sysno, 0, &zh->reg->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                 logf (LOG_LOG, "delete %s %s %ld", recordType,
1048                       fname, (long) recordOffset);
1049                 logf (LOG_WARN, "cannot delete file above, storeKeys false");
1050             }
1051             else
1052             {
1053                 logf (LOG_LOG, "delete %s %s %ld", recordType,
1054                       fname, (long) recordOffset);
1055 #if 0
1056                 if (matchStr)
1057                     dict_delete (matchDict, matchStr);
1058 #endif
1059                 rec_del (zh->reg->records, &rec);
1060             }
1061             rec_rm (&rec);
1062             return 1;
1063         }
1064         else
1065         {
1066             /* record going to be updated */
1067             if (!delkeys.buf_used)
1068             {
1069                 logf (LOG_LOG, "update %s %s %ld", recordType,
1070                       fname, (long) recordOffset);
1071                 logf (LOG_WARN, "cannot update file above, storeKeys false");
1072             }
1073             else
1074             {
1075                 logf (LOG_LOG, "update %s %s %ld", recordType,
1076                       fname, (long) recordOffset);
1077                 extract_flushRecordKeys (zh, *sysno, 1, &zh->reg->keys);
1078             }
1079         }
1080     }
1081     /* update file type */
1082     xfree (rec->info[recInfo_fileType]);
1083     rec->info[recInfo_fileType] =
1084         rec_strdup (recordType, &rec->size[recInfo_fileType]);
1085
1086     /* update filename */
1087     xfree (rec->info[recInfo_filename]);
1088     rec->info[recInfo_filename] =
1089         rec_strdup (fname, &rec->size[recInfo_filename]);
1090
1091     /* update delete keys */
1092     xfree (rec->info[recInfo_delKeys]);
1093     if (zh->reg->keys.buf_used > 0 && store_keys == 1)
1094     {
1095         rec->size[recInfo_delKeys] = zh->reg->keys.buf_used;
1096         rec->info[recInfo_delKeys] = zh->reg->keys.buf;
1097         zh->reg->keys.buf = NULL;
1098         zh->reg->keys.buf_max = 0;
1099     }
1100     else
1101     {
1102         rec->info[recInfo_delKeys] = NULL;
1103         rec->size[recInfo_delKeys] = 0;
1104     }
1105
1106     /* save file size of original record */
1107     zebraExplain_recordBytesIncrement (zh->reg->zei,
1108                                        - recordAttr->recordSize);
1109 #if 0
1110     recordAttr->recordSize = fi->file_moffset - recordOffset;
1111     if (!recordAttr->recordSize)
1112         recordAttr->recordSize = fi->file_max - recordOffset;
1113 #else
1114     recordAttr->recordSize = buf_size;
1115 #endif
1116     zebraExplain_recordBytesIncrement (zh->reg->zei,
1117                                        recordAttr->recordSize);
1118
1119     /* set run-number for this record */
1120     recordAttr->runNumber =
1121         zebraExplain_runNumberIncrement (zh->reg->zei, 0);
1122
1123     /* update store data */
1124     xfree (rec->info[recInfo_storeData]);
1125     if (store_data == 1)
1126     {
1127         rec->size[recInfo_storeData] = recordAttr->recordSize;
1128         rec->info[recInfo_storeData] = (char *)
1129             xmalloc (recordAttr->recordSize);
1130 #if 1
1131         memcpy (rec->info[recInfo_storeData], buf, recordAttr->recordSize);
1132 #else
1133         if (lseek (fi->fd, recordOffset, SEEK_SET) < 0)
1134         {
1135             logf (LOG_ERRNO|LOG_FATAL, "seek to %ld in %s",
1136                   (long) recordOffset, fname);
1137             exit (1);
1138         }
1139         if (read (fi->fd, rec->info[recInfo_storeData], recordAttr->recordSize)
1140             < recordAttr->recordSize)
1141         {
1142             logf (LOG_ERRNO|LOG_FATAL, "read %d bytes of %s",
1143                   recordAttr->recordSize, fname);
1144             exit (1);
1145         }
1146 #endif
1147     }
1148     else
1149     {
1150         rec->info[recInfo_storeData] = NULL;
1151         rec->size[recInfo_storeData] = 0;
1152     }
1153     /* update database name */
1154     xfree (rec->info[recInfo_databaseName]);
1155     rec->info[recInfo_databaseName] =
1156         rec_strdup (databaseName, &rec->size[recInfo_databaseName]); 
1157
1158     /* update offset */
1159     recordAttr->recordOffset = recordOffset;
1160     
1161     /* commit this record */
1162     rec_put (zh->reg->records, &rec);
1163
1164     return 0;
1165 }
1166
1167 int explain_extract (void *handle, Record rec, data1_node *n)
1168 {
1169     ZebraHandle zh = (ZebraHandle) handle;
1170     struct recExtractCtrl extractCtrl;
1171     int i;
1172
1173     if (zebraExplain_curDatabase (zh->reg->zei,
1174                                   rec->info[recInfo_databaseName]))
1175     {
1176         abort();
1177         if (zebraExplain_newDatabase (zh->reg->zei,
1178                                       rec->info[recInfo_databaseName], 0))
1179             abort ();
1180     }
1181
1182     zh->reg->keys.buf_used = 0;
1183     zh->reg->keys.prevAttrUse = -1;
1184     zh->reg->keys.prevAttrSet = -1;
1185     zh->reg->keys.prevSeqNo = 0;
1186     zh->reg->sortKeys = 0;
1187     
1188     extractCtrl.init = extract_init;
1189     extractCtrl.tokenAdd = extract_token_add;
1190     extractCtrl.schemaAdd = extract_schema_add;
1191     extractCtrl.dh = zh->reg->dh;
1192     for (i = 0; i<256; i++)
1193         extractCtrl.seqno[i] = 0;
1194     extractCtrl.zebra_maps = zh->reg->zebra_maps;
1195     extractCtrl.flagShowRecords = 0;
1196     extractCtrl.handle = handle;
1197     
1198     grs_extract_tree(&extractCtrl, n);
1199
1200     if (rec->size[recInfo_delKeys])
1201     {
1202         struct recKeys delkeys;
1203         struct sortKey *sortKeys = 0;
1204
1205         delkeys.buf_used = rec->size[recInfo_delKeys];
1206         delkeys.buf = rec->info[recInfo_delKeys];
1207         extract_flushSortKeys (zh, rec->sysno, 0, &sortKeys);
1208         extract_flushRecordKeys (zh, rec->sysno, 0, &delkeys);
1209     }
1210     extract_flushRecordKeys (zh, rec->sysno, 1, &zh->reg->keys);
1211     extract_flushSortKeys (zh, rec->sysno, 1, &zh->reg->sortKeys);
1212
1213     xfree (rec->info[recInfo_delKeys]);
1214     rec->size[recInfo_delKeys] = zh->reg->keys.buf_used;
1215     rec->info[recInfo_delKeys] = zh->reg->keys.buf;
1216     zh->reg->keys.buf = NULL;
1217     zh->reg->keys.buf_max = 0;
1218     return 0;
1219 }
1220
1221 void extract_flushRecordKeys (ZebraHandle zh, SYSNO sysno,
1222                               int cmd, struct recKeys *reckeys)
1223 {
1224 #if SU_SCHEME
1225 #else
1226     unsigned char attrSet = (unsigned char) -1;
1227     unsigned short attrUse = (unsigned short) -1;
1228 #endif
1229     int seqno = 0;
1230     int off = 0;
1231     int ch = 0;
1232     ZebraExplainInfo zei = zh->reg->zei;
1233
1234     if (!zh->reg->key_buf)
1235     {
1236         int mem = 8*1024*1024;
1237         zh->reg->key_buf = (char**) xmalloc (mem);
1238         zh->reg->ptr_top = mem/sizeof(char*);
1239         zh->reg->ptr_i = 0;
1240         zh->reg->key_buf_used = 0;
1241         zh->reg->key_file_no = 0;
1242     }
1243     zebraExplain_recordCountIncrement (zei, cmd ? 1 : -1);
1244     while (off < reckeys->buf_used)
1245     {
1246         const char *src = reckeys->buf + off;
1247         struct it_key key;
1248         int lead;
1249     
1250         lead = *src++;
1251
1252 #if SU_SCHEME
1253         if ((lead & 3) < 3)
1254         {
1255             memcpy (&ch, src, sizeof(ch));
1256             src += sizeof(ch);
1257         }
1258 #else
1259         if (!(lead & 1))
1260         {
1261             memcpy (&attrSet, src, sizeof(attrSet));
1262             src += sizeof(attrSet);
1263         }
1264         if (!(lead & 2))
1265         {
1266             memcpy (&attrUse, src, sizeof(attrUse));
1267             src += sizeof(attrUse);
1268         }
1269 #endif
1270         if (zh->reg->key_buf_used + 1024 > 
1271             (zh->reg->ptr_top -zh->reg->ptr_i)*sizeof(char*))
1272             extract_flushWriteKeys (zh);
1273         ++(zh->reg->ptr_i);
1274         (zh->reg->key_buf)[zh->reg->ptr_top - zh->reg->ptr_i] =
1275             (char*)zh->reg->key_buf + zh->reg->key_buf_used;
1276 #if SU_SCHEME
1277 #else
1278         ch = zebraExplain_lookupSU (zei, attrSet, attrUse);
1279         if (ch < 0)
1280             ch = zebraExplain_addSU (zei, attrSet, attrUse);
1281 #endif
1282         assert (ch > 0);
1283         zh->reg->key_buf_used +=
1284             key_SU_encode (ch,((char*)zh->reg->key_buf) +
1285                            zh->reg->key_buf_used);
1286
1287         while (*src)
1288             ((char*)zh->reg->key_buf) [(zh->reg->key_buf_used)++] = *src++;
1289         src++;
1290         ((char*)(zh->reg->key_buf))[(zh->reg->key_buf_used)++] = '\0';
1291         ((char*)(zh->reg->key_buf))[(zh->reg->key_buf_used)++] = cmd;
1292
1293         if (lead & 60)
1294             seqno += ((lead>>2) & 15)-1;
1295         else
1296         {
1297             memcpy (&seqno, src, sizeof(seqno));
1298             src += sizeof(seqno);
1299         }
1300         key.seqno = seqno;
1301         key.sysno = sysno;
1302         memcpy ((char*)zh->reg->key_buf + zh->reg->key_buf_used, &key, sizeof(key));
1303         (zh->reg->key_buf_used) += sizeof(key);
1304         off = src - reckeys->buf;
1305     }
1306     assert (off == reckeys->buf_used);
1307 }
1308
1309 void extract_flushWriteKeys (ZebraHandle zh)
1310 {
1311     FILE *outf;
1312     char out_fname[200];
1313     char *prevcp, *cp;
1314     struct encode_info encode_info;
1315     int ptr_i = zh->reg->ptr_i;
1316 #if SORT_EXTRA
1317     int i;
1318 #endif
1319     if (!zh->reg->key_buf || ptr_i <= 0)
1320         return;
1321
1322     (zh->reg->key_file_no)++;
1323     logf (LOG_LOG, "sorting section %d", (zh->reg->key_file_no));
1324 #if !SORT_EXTRA
1325     qsort (zh->reg->key_buf + zh->reg->ptr_top - ptr_i, ptr_i,
1326                sizeof(char*), key_qsort_compare);
1327     extract_get_fname_tmp (zh, out_fname, zh->reg->key_file_no);
1328
1329     if (!(outf = fopen (out_fname, "wb")))
1330     {
1331         logf (LOG_FATAL|LOG_ERRNO, "fopen %s", out_fname);
1332         exit (1);
1333     }
1334     logf (LOG_LOG, "writing section %d", zh->reg->key_file_no);
1335     prevcp = cp = (zh->reg->key_buf)[zh->reg->ptr_top - ptr_i];
1336     
1337     encode_key_init (&encode_info);
1338     encode_key_write (cp, &encode_info, outf);
1339     
1340     while (--ptr_i > 0)
1341     {
1342         cp = (zh->reg->key_buf)[zh->reg->ptr_top - ptr_i];
1343         if (strcmp (cp, prevcp))
1344         {
1345             encode_key_init (&encode_info);
1346             encode_key_write (cp, &encode_info, outf);
1347             prevcp = cp;
1348         }
1349         else
1350             encode_key_write (cp + strlen(cp), &encode_info, outf);
1351     }
1352 #else
1353     qsort (key_buf + ptr_top-ptr_i, ptr_i, sizeof(char*), key_x_compare);
1354     extract_get_fname_tmp (out_fname, key_file_no);
1355
1356     if (!(outf = fopen (out_fname, "wb")))
1357     {
1358         logf (LOG_FATAL|LOG_ERRNO, "fopen %s", out_fname);
1359         exit (1);
1360     }
1361     logf (LOG_LOG, "writing section %d", key_file_no);
1362     i = ptr_i;
1363     prevcp =  key_buf[ptr_top-i];
1364     while (1)
1365         if (!--i || strcmp (prevcp, key_buf[ptr_top-i]))
1366         {
1367             key_y_len = strlen(prevcp)+1;
1368 #if 0
1369             logf (LOG_LOG, "key_y_len: %2d %02x %02x %s",
1370                       key_y_len, prevcp[0], prevcp[1], 2+prevcp);
1371 #endif
1372             qsort (key_buf + ptr_top-ptr_i, ptr_i - i,
1373                                    sizeof(char*), key_y_compare);
1374             cp = key_buf[ptr_top-ptr_i];
1375             --key_y_len;
1376             encode_key_init (&encode_info);
1377             encode_key_write (cp, &encode_info, outf);
1378             while (--ptr_i > i)
1379             {
1380                 cp = key_buf[ptr_top-ptr_i];
1381                 encode_key_write (cp+key_y_len, &encode_info, outf);
1382             }
1383             if (!i)
1384                 break;
1385             prevcp = key_buf[ptr_top-ptr_i];
1386         }
1387 #endif
1388     if (fclose (outf))
1389     {
1390         logf (LOG_FATAL|LOG_ERRNO, "fclose %s", out_fname);
1391         exit (1);
1392     }
1393     logf (LOG_LOG, "finished section %d", zh->reg->key_file_no);
1394     zh->reg->ptr_i = 0;
1395     zh->reg->key_buf_used = 0;
1396 }
1397
1398 void extract_add_index_string (RecWord *p, const char *string,
1399                                int length)
1400 {
1401     char *dst;
1402     unsigned char attrSet;
1403     unsigned short attrUse;
1404     int lead = 0;
1405     int diff = 0;
1406     int *pseqno = &p->seqno;
1407     ZebraHandle zh = p->extractCtrl->handle;
1408     ZebraExplainInfo zei = zh->reg->zei;
1409     struct recKeys *keys = &zh->reg->keys;
1410     
1411     if (keys->buf_used+1024 > keys->buf_max)
1412     {
1413         char *b;
1414
1415         b = (char *) xmalloc (keys->buf_max += 128000);
1416         if (keys->buf_used > 0)
1417             memcpy (b, keys->buf, keys->buf_used);
1418         xfree (keys->buf);
1419         keys->buf = b;
1420     }
1421     dst = keys->buf + keys->buf_used;
1422
1423     attrSet = p->attrSet;
1424     if (keys->buf_used > 0 && keys->prevAttrSet == attrSet)
1425         lead |= 1;
1426     else
1427         keys->prevAttrSet = attrSet;
1428     attrUse = p->attrUse;
1429     if (keys->buf_used > 0 && keys->prevAttrUse == attrUse)
1430         lead |= 2;
1431     else
1432         keys->prevAttrUse = attrUse;
1433 #if 1
1434     diff = 1 + *pseqno - keys->prevSeqNo;
1435     if (diff >= 1 && diff <= 15)
1436         lead |= (diff << 2);
1437     else
1438         diff = 0;
1439 #endif
1440     keys->prevSeqNo = *pseqno;
1441     
1442     *dst++ = lead;
1443
1444 #if SU_SCHEME
1445     if ((lead & 3) < 3)
1446     {
1447         int ch = zebraExplain_lookupSU (zei, attrSet, attrUse);
1448         if (ch < 0)
1449         {
1450             ch = zebraExplain_addSU (zei, attrSet, attrUse);
1451             yaz_log (LOG_DEBUG, "addSU set=%d use=%d SU=%d",
1452                      attrSet, attrUse, ch);
1453         }
1454         assert (ch > 0);
1455         memcpy (dst, &ch, sizeof(ch));
1456         dst += sizeof(ch);
1457     }
1458 #else
1459     if (!(lead & 1))
1460     {
1461         memcpy (dst, &attrSet, sizeof(attrSet));
1462         dst += sizeof(attrSet);
1463     }
1464     if (!(lead & 2))
1465     {
1466         memcpy (dst, &attrUse, sizeof(attrUse));
1467         dst += sizeof(attrUse);
1468     }
1469 #endif
1470     *dst++ = p->reg_type;
1471     memcpy (dst, string, length);
1472     dst += length;
1473     *dst++ = '\0';
1474
1475     if (!diff)
1476     {
1477         memcpy (dst, pseqno, sizeof(*pseqno));
1478         dst += sizeof(*pseqno);
1479     }
1480     keys->buf_used = dst - keys->buf;
1481 }
1482
1483 static void extract_add_sort_string (RecWord *p, const char *string,
1484                                      int length)
1485 {
1486     struct sortKey *sk;
1487     ZebraHandle zh = p->extractCtrl->handle;
1488
1489     for (sk = zh->reg->sortKeys; sk; sk = sk->next)
1490         if (sk->attrSet == p->attrSet && sk->attrUse == p->attrUse)
1491             return;
1492
1493     sk = (struct sortKey *) xmalloc (sizeof(*sk));
1494     sk->next = zh->reg->sortKeys;
1495     zh->reg->sortKeys = sk;
1496
1497     sk->string = (char *) xmalloc (length);
1498     sk->length = length;
1499     memcpy (sk->string, string, length);
1500
1501     sk->attrSet = p->attrSet;
1502     sk->attrUse = p->attrUse;
1503 }
1504
1505 void extract_add_string (RecWord *p, const char *string, int length)
1506 {
1507     assert (length > 0);
1508     if (zebra_maps_is_sort (p->zebra_maps, p->reg_type))
1509         extract_add_sort_string (p, string, length);
1510     else
1511         extract_add_index_string (p, string, length);
1512 }
1513
1514 static void extract_add_incomplete_field (RecWord *p)
1515 {
1516     const char *b = p->string;
1517     int remain = p->length;
1518     const char **map = 0;
1519
1520     if (remain > 0)
1521         map = zebra_maps_input(p->zebra_maps, p->reg_type, &b, remain);
1522
1523     while (map)
1524     {
1525         char buf[IT_MAX_WORD+1];
1526         int i, remain;
1527
1528         /* Skip spaces */
1529         while (map && *map && **map == *CHR_SPACE)
1530         {
1531             remain = p->length - (b - p->string);
1532             if (remain > 0)
1533                 map = zebra_maps_input(p->zebra_maps, p->reg_type, &b, remain);
1534             else
1535                 map = 0;
1536         }
1537         if (!map)
1538             break;
1539         i = 0;
1540         while (map && *map && **map != *CHR_SPACE)
1541         {
1542             const char *cp = *map;
1543
1544             while (i < IT_MAX_WORD && *cp)
1545                 buf[i++] = *(cp++);
1546             remain = p->length - (b - p->string);
1547             if (remain > 0)
1548                 map = zebra_maps_input(p->zebra_maps, p->reg_type, &b, remain);
1549             else
1550                 map = 0;
1551         }
1552         if (!i)
1553             return;
1554         extract_add_string (p, buf, i);
1555         p->seqno++;
1556     }
1557 }
1558
1559 static void extract_add_complete_field (RecWord *p)
1560 {
1561     const char *b = p->string;
1562     char buf[IT_MAX_WORD+1];
1563     const char **map = 0;
1564     int i = 0, remain = p->length;
1565
1566     if (remain > 0)
1567         map = zebra_maps_input (p->zebra_maps, p->reg_type, &b, remain);
1568
1569     while (remain > 0 && i < IT_MAX_WORD)
1570     {
1571         while (map && *map && **map == *CHR_SPACE)
1572         {
1573             remain = p->length - (b - p->string);
1574             if (remain > 0)
1575                 map = zebra_maps_input(p->zebra_maps, p->reg_type, &b, remain);
1576             else
1577                 map = 0;
1578         }
1579         if (!map)
1580             break;
1581
1582         if (i && i < IT_MAX_WORD)
1583             buf[i++] = *CHR_SPACE;
1584         while (map && *map && **map != *CHR_SPACE)
1585         {
1586             const char *cp = *map;
1587
1588             if (i >= IT_MAX_WORD)
1589                 break;
1590             while (i < IT_MAX_WORD && *cp)
1591                 buf[i++] = *(cp++);
1592             remain = p->length  - (b - p->string);
1593             if (remain > 0)
1594                 map = zebra_maps_input (p->zebra_maps, p->reg_type, &b,
1595                                         remain);
1596             else
1597                 map = 0;
1598         }
1599     }
1600     if (!i)
1601         return;
1602     extract_add_string (p, buf, i);
1603 }
1604
1605 void extract_token_add (RecWord *p)
1606 {
1607     WRBUF wrbuf;
1608     if ((wrbuf = zebra_replace(p->zebra_maps, p->reg_type, 0,
1609                                p->string, p->length)))
1610     {
1611         p->string = wrbuf_buf(wrbuf);
1612         p->length = wrbuf_len(wrbuf);
1613     }
1614     if (zebra_maps_is_complete (p->zebra_maps, p->reg_type))
1615         extract_add_complete_field (p);
1616     else
1617         extract_add_incomplete_field(p);
1618 }
1619
1620 void extract_schema_add (struct recExtractCtrl *p, Odr_oid *oid)
1621 {
1622     ZebraHandle zh = (ZebraHandle) (p->handle);
1623     zebraExplain_addSchema (zh->reg->zei, oid);
1624 }
1625
1626 void extract_flushSortKeys (ZebraHandle zh, SYSNO sysno,
1627                             int cmd, struct sortKey **skp)
1628 {
1629     struct sortKey *sk = *skp;
1630     SortIdx sortIdx = zh->reg->sortIdx;
1631
1632     sortIdx_sysno (sortIdx, sysno);
1633     while (sk)
1634     {
1635         struct sortKey *sk_next = sk->next;
1636         sortIdx_type (sortIdx, sk->attrUse);
1637         sortIdx_add (sortIdx, sk->string, sk->length);
1638         xfree (sk->string);
1639         xfree (sk);
1640         sk = sk_next;
1641     }
1642     *skp = 0;
1643 }
1644
1645 void encode_key_init (struct encode_info *i)
1646 {
1647     i->sysno = 0;
1648     i->seqno = 0;
1649     i->cmd = -1;
1650 }
1651
1652 char *encode_key_int (int d, char *bp)
1653 {
1654     if (d <= 63)
1655         *bp++ = d;
1656     else if (d <= 16383)
1657     {
1658         *bp++ = 64 + (d>>8);
1659         *bp++ = d  & 255;
1660     }
1661     else if (d <= 4194303)
1662     {
1663         *bp++ = 128 + (d>>16);
1664         *bp++ = (d>>8) & 255;
1665         *bp++ = d & 255;
1666     }
1667     else
1668     {
1669         *bp++ = 192 + (d>>24);
1670         *bp++ = (d>>16) & 255;
1671         *bp++ = (d>>8) & 255;
1672         *bp++ = d & 255;
1673     }
1674     return bp;
1675 }
1676
1677 void encode_key_write (char *k, struct encode_info *i, FILE *outf)
1678 {
1679     struct it_key key;
1680     char *bp = i->buf;
1681
1682     while ((*bp++ = *k++))
1683         ;
1684     memcpy (&key, k+1, sizeof(struct it_key));
1685     bp = encode_key_int ( (key.sysno - i->sysno) * 2 + *k, bp);
1686     if (i->sysno != key.sysno)
1687     {
1688         i->sysno = key.sysno;
1689         i->seqno = 0;
1690     }
1691     else if (!i->seqno && !key.seqno && i->cmd == *k)
1692         return;
1693     bp = encode_key_int (key.seqno - i->seqno, bp);
1694     i->seqno = key.seqno;
1695     i->cmd = *k;
1696     if (fwrite (i->buf, bp - i->buf, 1, outf) != 1)
1697     {
1698         logf (LOG_FATAL|LOG_ERRNO, "fwrite");
1699         exit (1);
1700     }
1701 }
1702