Towards GPL
[idzebra-moved-to-github.git] / index / extract.c
1 /* $Id: extract.c,v 1.120 2002-08-02 19:26:55 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)
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         if (zh->reg->keys.buf_used == 0)
503         {
504             /* the extraction process returned no information - the record
505                is probably empty - unless flagShowRecords is in use */
506             if (!rGroup->flagRw)
507                 return 1;
508             
509             logf (LOG_WARN, "empty %s %s " PRINTF_OFF_T, rGroup->recordType,
510                   fname, recordOffset);
511             return 1;
512         }
513     }
514
515     /* perform match if sysno not known and if match criteria is specified */
516        
517     matchStr = NULL;
518     if (!sysno) 
519     {
520         sysnotmp = 0;
521         sysno = &sysnotmp;
522         if (rGroup->recordId && *rGroup->recordId)
523         {
524             char *rinfo;
525         
526             matchStr = fileMatchStr (zh, &zh->reg->keys, rGroup, fname, 
527                                      rGroup->recordId);
528             if (matchStr)
529             {
530                 rinfo = dict_lookup (zh->reg->matchDict, matchStr);
531                 if (rinfo)
532                     memcpy (sysno, rinfo+1, sizeof(*sysno));
533             }
534             else
535             {
536                 logf (LOG_WARN, "Bad match criteria");
537                 return 0;
538             }
539         }
540     }
541
542     if (! *sysno)
543     {
544         /* new record */
545         if (deleteFlag)
546         {
547             logf (LOG_LOG, "delete %s %s " PRINTF_OFF_T, rGroup->recordType,
548                   fname, recordOffset);
549             logf (LOG_WARN, "cannot delete record above (seems new)");
550             return 1;
551         }
552         if (zh->records_processed < rGroup->fileVerboseLimit)
553             logf (LOG_LOG, "add %s %s " PRINTF_OFF_T, rGroup->recordType,
554                   fname, recordOffset);
555         rec = rec_new (zh->reg->records);
556
557         *sysno = rec->sysno;
558
559         recordAttr = rec_init_attr (zh->reg->zei, rec);
560
561         if (matchStr)
562         {
563             dict_insert (zh->reg->matchDict, matchStr, sizeof(*sysno), sysno);
564         }
565         extract_flushRecordKeys (zh, *sysno, 1, &zh->reg->keys);
566         extract_flushSortKeys (zh, *sysno, 1, &zh->reg->sortKeys);
567
568         zh->records_inserted++;
569     }
570     else
571     {
572         /* record already exists */
573         struct recKeys delkeys;
574
575         rec = rec_get (zh->reg->records, *sysno);
576         assert (rec);
577         
578         recordAttr = rec_init_attr (zh->reg->zei, rec);
579
580         if (recordAttr->runNumber ==
581             zebraExplain_runNumberIncrement (zh->reg->zei, 0))
582         {
583             yaz_log (LOG_LOG, "run number = %d", recordAttr->runNumber);
584             yaz_log (LOG_LOG, "skipped %s %s " PRINTF_OFF_T,
585                      rGroup->recordType, fname, recordOffset);
586             extract_flushSortKeys (zh, *sysno, -1, &zh->reg->sortKeys);
587             rec_rm (&rec);
588             logRecord (zh);
589             return 1;
590         }
591         delkeys.buf_used = rec->size[recInfo_delKeys];
592         delkeys.buf = rec->info[recInfo_delKeys];
593         extract_flushSortKeys (zh, *sysno, 0, &zh->reg->sortKeys);
594         extract_flushRecordKeys (zh, *sysno, 0, &delkeys);
595         if (deleteFlag)
596         {
597             /* record going to be deleted */
598             if (!delkeys.buf_used)
599             {
600                 logf (LOG_LOG, "delete %s %s " PRINTF_OFF_T,
601                       rGroup->recordType, fname, recordOffset);
602                 logf (LOG_WARN, "cannot delete file above, storeKeys false");
603             }
604             else
605             {
606                 if (zh->records_processed < rGroup->fileVerboseLimit)
607                     logf (LOG_LOG, "delete %s %s " PRINTF_OFF_T,
608                          rGroup->recordType, fname, recordOffset);
609                 zh->records_deleted++;
610                 if (matchStr)
611                     dict_delete (zh->reg->matchDict, matchStr);
612                 rec_del (zh->reg->records, &rec);
613             }
614             rec_rm (&rec);
615             logRecord (zh);
616             return 1;
617         }
618         else
619         {
620             /* record going to be updated */
621             if (!delkeys.buf_used)
622             {
623                 logf (LOG_LOG, "update %s %s " PRINTF_OFF_T,
624                       rGroup->recordType, fname, recordOffset);
625                 logf (LOG_WARN, "cannot update file above, storeKeys false");
626             }
627             else
628             {
629                 if (zh->records_processed < rGroup->fileVerboseLimit)
630                     logf (LOG_LOG, "update %s %s " PRINTF_OFF_T,
631                         rGroup->recordType, fname, recordOffset);
632                 extract_flushRecordKeys (zh, *sysno, 1, &zh->reg->keys);
633                 zh->records_updated++;
634             }
635         }
636     }
637     /* update file type */
638     xfree (rec->info[recInfo_fileType]);
639     rec->info[recInfo_fileType] =
640         rec_strdup (rGroup->recordType, &rec->size[recInfo_fileType]);
641
642     /* update filename */
643     xfree (rec->info[recInfo_filename]);
644     rec->info[recInfo_filename] =
645         rec_strdup (fname, &rec->size[recInfo_filename]);
646
647     /* update delete keys */
648     xfree (rec->info[recInfo_delKeys]);
649     if (zh->reg->keys.buf_used > 0 && rGroup->flagStoreKeys == 1)
650     {
651 #if 1
652         rec->size[recInfo_delKeys] = zh->reg->keys.buf_used;
653         rec->info[recInfo_delKeys] = zh->reg->keys.buf;
654         zh->reg->keys.buf = NULL;
655         zh->reg->keys.buf_max = 0;
656 #else
657         rec->info[recInfo_delKeys] = xmalloc (reckeys.buf_used);
658         rec->size[recInfo_delKeys] = reckeys.buf_used;
659         memcpy (rec->info[recInfo_delKeys], reckeys.buf,
660                 rec->size[recInfo_delKeys]);
661 #endif
662     }
663     else
664     {
665         rec->info[recInfo_delKeys] = NULL;
666         rec->size[recInfo_delKeys] = 0;
667     }
668
669     /* save file size of original record */
670     zebraExplain_recordBytesIncrement (zh->reg->zei,
671                                        - recordAttr->recordSize);
672     recordAttr->recordSize = fi->file_moffset - recordOffset;
673     if (!recordAttr->recordSize)
674         recordAttr->recordSize = fi->file_max - recordOffset;
675     zebraExplain_recordBytesIncrement (zh->reg->zei,
676                                        recordAttr->recordSize);
677
678     /* set run-number for this record */
679     recordAttr->runNumber = zebraExplain_runNumberIncrement (zh->reg->zei,
680                                                              0);
681
682     /* update store data */
683     xfree (rec->info[recInfo_storeData]);
684     if (rGroup->flagStoreData == 1)
685     {
686         rec->size[recInfo_storeData] = recordAttr->recordSize;
687         rec->info[recInfo_storeData] = (char *)
688             xmalloc (recordAttr->recordSize);
689         if (lseek (fi->fd, recordOffset, SEEK_SET) < 0)
690         {
691             logf (LOG_ERRNO|LOG_FATAL, "seek to " PRINTF_OFF_T " in %s",
692                   recordOffset, fname);
693             exit (1);
694         }
695         if (read (fi->fd, rec->info[recInfo_storeData], recordAttr->recordSize)
696             < recordAttr->recordSize)
697         {
698             logf (LOG_ERRNO|LOG_FATAL, "read %d bytes of %s",
699                   recordAttr->recordSize, fname);
700             exit (1);
701         }
702     }
703     else
704     {
705         rec->info[recInfo_storeData] = NULL;
706         rec->size[recInfo_storeData] = 0;
707     }
708     /* update database name */
709     xfree (rec->info[recInfo_databaseName]);
710     rec->info[recInfo_databaseName] =
711         rec_strdup (rGroup->databaseName, &rec->size[recInfo_databaseName]); 
712
713     /* update offset */
714     recordAttr->recordOffset = recordOffset;
715     
716     /* commit this record */
717     rec_put (zh->reg->records, &rec);
718     logRecord (zh);
719     return 1;
720 }
721
722 int fileExtract (ZebraHandle zh, SYSNO *sysno, const char *fname, 
723                  const struct recordGroup *rGroupP, int deleteFlag)
724 {
725     int r, i, fd;
726     char gprefix[128];
727     char ext[128];
728     char ext_res[128];
729     char subType[128];
730     RecType recType;
731     struct recordGroup rGroupM;
732     struct recordGroup *rGroup = &rGroupM;
733     struct file_read_info *fi;
734     void *clientData;
735
736     memcpy (rGroup, rGroupP, sizeof(*rGroupP));
737    
738     if (!rGroup->groupName || !*rGroup->groupName)
739         *gprefix = '\0';
740     else
741         sprintf (gprefix, "%s.", rGroup->groupName);
742
743     logf (LOG_DEBUG, "fileExtract %s", fname);
744
745     /* determine file extension */
746     *ext = '\0';
747     for (i = strlen(fname); --i >= 0; )
748         if (fname[i] == '/')
749             break;
750         else if (fname[i] == '.')
751         {
752             strcpy (ext, fname+i+1);
753             break;
754         }
755     /* determine file type - depending on extension */
756     if (!rGroup->recordType)
757     {
758         sprintf (ext_res, "%srecordType.%s", gprefix, ext);
759         if (!(rGroup->recordType = res_get (zh->res, ext_res)))
760         {
761             sprintf (ext_res, "%srecordType", gprefix);
762             rGroup->recordType = res_get (zh->res, ext_res);
763         }
764     }
765     if (!rGroup->recordType)
766     {
767         if (zh->records_processed < rGroup->fileVerboseLimit)
768             logf (LOG_LOG, "? %s", fname);
769         return 0;
770     }
771     if (!*rGroup->recordType)
772         return 0;
773     if (!(recType =
774           recType_byName (zh->reg->recTypes, rGroup->recordType, subType,
775                           &clientData)))
776     {
777         logf (LOG_WARN, "No such record type: %s", rGroup->recordType);
778         return 0;
779     }
780
781     /* determine match criteria */
782     if (!rGroup->recordId)
783     {
784         sprintf (ext_res, "%srecordId.%s", gprefix, ext);
785         rGroup->recordId = res_get (zh->res, ext_res);
786     }
787
788     /* determine database name */
789     if (!rGroup->databaseName)
790     {
791         sprintf (ext_res, "%sdatabase.%s", gprefix, ext);
792         if (!(rGroup->databaseName = res_get (zh->res, ext_res)))
793         {
794             sprintf (ext_res, "%sdatabase", gprefix);
795             rGroup->databaseName = res_get (zh->res, ext_res);
796         }
797     }
798     if (!rGroup->databaseName)
799         rGroup->databaseName = "Default";
800
801     /* determine if explain database */
802     
803     sprintf (ext_res, "%sexplainDatabase", gprefix);
804     rGroup->explainDatabase =
805         atoi (res_get_def (zh->res, ext_res, "0"));
806
807     /* announce database */
808     if (zebraExplain_curDatabase (zh->reg->zei, rGroup->databaseName))
809     {
810         if (zebraExplain_newDatabase (zh->reg->zei, rGroup->databaseName,
811                                       rGroup->explainDatabase))
812             return 0;
813     }
814
815     if (rGroup->flagStoreData == -1)
816     {
817         const char *sval;
818         sprintf (ext_res, "%sstoreData.%s", gprefix, ext);
819         if (!(sval = res_get (zh->res, ext_res)))
820         {
821             sprintf (ext_res, "%sstoreData", gprefix);
822             sval = res_get (zh->res, ext_res);
823         }
824         if (sval)
825             rGroup->flagStoreData = atoi (sval);
826     }
827     if (rGroup->flagStoreData == -1)
828         rGroup->flagStoreData = 0;
829
830     if (rGroup->flagStoreKeys == -1)
831     {
832         const char *sval;
833
834         sprintf (ext_res, "%sstoreKeys.%s", gprefix, ext);
835         sval = res_get (zh->res, ext_res);
836         if (!sval)
837         {
838             sprintf (ext_res, "%sstoreKeys", gprefix);
839             sval = res_get (zh->res, ext_res);
840         }
841         if (!sval)
842             sval = res_get (zh->res, "storeKeys");
843         if (sval)
844             rGroup->flagStoreKeys = atoi (sval);
845     }
846     if (rGroup->flagStoreKeys == -1)
847         rGroup->flagStoreKeys = 0;
848
849     if (sysno && deleteFlag)
850         fd = -1;
851     else
852     {
853         char full_rep[1024];
854
855         if (zh->path_reg && !yaz_is_abspath (fname))
856         {
857             strcpy (full_rep, zh->path_reg);
858             strcat (full_rep, "/");
859             strcat (full_rep, fname);
860         }
861         else
862             strcpy (full_rep, fname);
863         
864
865         if ((fd = open (full_rep, O_BINARY|O_RDONLY)) == -1)
866         {
867             logf (LOG_WARN|LOG_ERRNO, "open %s", full_rep);
868             return 0;
869         }
870     }
871     fi = file_read_start (fd);
872     do
873     {
874         file_begin (fi);
875         r = recordExtract (zh, sysno, fname, rGroup, deleteFlag, fi,
876                            recType, subType, clientData);
877     } while (r && !sysno && fi->file_more);
878     file_read_stop (fi);
879     if (fd != -1)
880         close (fd);
881     return r;
882 }
883
884
885 int extract_rec_in_mem (ZebraHandle zh, const char *recordType,
886                         const char *buf, size_t buf_size,
887                         const char *databaseName, int delete_flag,
888                         int test_mode, int *sysno,
889                         int store_keys, int store_data,
890                         const char *match_criteria)
891 {
892     RecordAttr *recordAttr;
893     struct recExtractCtrl extractCtrl;
894     int i, r;
895     char *matchStr = 0;
896     RecType recType;
897     char subType[1024];
898     void *clientData;
899     const char *fname = "<no file>";
900     Record rec;
901     long recordOffset = 0;
902     struct zebra_fetch_control fc;
903
904     fc.fd = -1;
905     fc.record_int_buf = buf;
906     fc.record_int_len = buf_size;
907     fc.record_int_pos = 0;
908     fc.offset_end = 0;
909     fc.record_offset = 0;
910
911     extractCtrl.offset = 0;
912     extractCtrl.readf = zebra_record_int_read;
913     extractCtrl.seekf = zebra_record_int_seek;
914     extractCtrl.tellf = zebra_record_int_tell;
915     extractCtrl.endf = zebra_record_int_end;
916     extractCtrl.fh = &fc;
917
918     /* announce database */
919     if (zebraExplain_curDatabase (zh->reg->zei, databaseName))
920     {
921         if (zebraExplain_newDatabase (zh->reg->zei, databaseName, 0))
922             return 0;
923     }
924     if (!(recType =
925           recType_byName (zh->reg->recTypes, recordType, subType,
926                           &clientData)))
927     {
928         logf (LOG_WARN, "No such record type: %s", recordType);
929         return 0;
930     }
931
932     zh->reg->keys.buf_used = 0;
933     zh->reg->keys.prevAttrUse = -1;
934     zh->reg->keys.prevAttrSet = -1;
935     zh->reg->keys.prevSeqNo = 0;
936     zh->reg->sortKeys = 0;
937
938     extractCtrl.subType = subType;
939     extractCtrl.init = extract_init;
940     extractCtrl.tokenAdd = extract_token_add;
941     extractCtrl.schemaAdd = extract_schema_add;
942     extractCtrl.dh = zh->reg->dh;
943     extractCtrl.handle = zh;
944     extractCtrl.zebra_maps = zh->reg->zebra_maps;
945     extractCtrl.flagShowRecords = 0;
946     for (i = 0; i<256; i++)
947     {
948         if (zebra_maps_is_positioned(zh->reg->zebra_maps, i))
949             extractCtrl.seqno[i] = 1;
950         else
951             extractCtrl.seqno[i] = 0;
952     }
953
954     r = (*recType->extract)(clientData, &extractCtrl);
955
956     if (r == RECCTRL_EXTRACT_EOF)
957         return 0;
958     else if (r == RECCTRL_EXTRACT_ERROR)
959     {
960         /* error occured during extraction ... */
961 #if 1
962         yaz_log (LOG_WARN, "extract error");
963 #else
964         if (rGroup->flagRw &&
965             zh->records_processed < rGroup->fileVerboseLimit)
966         {
967             logf (LOG_WARN, "fail %s %s %ld", rGroup->recordType,
968                   fname, (long) recordOffset);
969         }
970 #endif
971         return 0;
972     }
973     if (zh->reg->keys.buf_used == 0)
974     {
975         /* the extraction process returned no information - the record
976            is probably empty - unless flagShowRecords is in use */
977         if (test_mode)
978             return 1;
979         logf (LOG_WARN, "No keys generated for record");
980         logf (LOG_WARN, " The file is probably empty");
981         return 1;
982     }
983     /* match criteria */
984
985     if (! *sysno)
986     {
987         /* new record */
988         if (delete_flag)
989         {
990             logf (LOG_LOG, "delete %s %s %ld", recordType,
991                   fname, (long) recordOffset);
992             logf (LOG_WARN, "cannot delete record above (seems new)");
993             return 1;
994         }
995         logf (LOG_LOG, "add %s %s %ld", recordType, fname,
996               (long) recordOffset);
997         rec = rec_new (zh->reg->records);
998
999         *sysno = rec->sysno;
1000
1001         recordAttr = rec_init_attr (zh->reg->zei, rec);
1002
1003         if (matchStr)
1004         {
1005             dict_insert (zh->reg->matchDict, matchStr,
1006                          sizeof(*sysno), sysno);
1007         }
1008         extract_flushRecordKeys (zh, *sysno, 1, &zh->reg->keys);
1009         extract_flushSortKeys (zh, *sysno, 1, &zh->reg->sortKeys);
1010     }
1011     else
1012     {
1013         /* record already exists */
1014         struct recKeys delkeys;
1015
1016         rec = rec_get (zh->reg->records, *sysno);
1017         assert (rec);
1018         
1019         recordAttr = rec_init_attr (zh->reg->zei, rec);
1020
1021         if (recordAttr->runNumber ==
1022             zebraExplain_runNumberIncrement (zh->reg->zei, 0))
1023         {
1024             logf (LOG_LOG, "skipped %s %s %ld", recordType,
1025                   fname, (long) recordOffset);
1026             rec_rm (&rec);
1027             return 1;
1028         }
1029         delkeys.buf_used = rec->size[recInfo_delKeys];
1030         delkeys.buf = rec->info[recInfo_delKeys];
1031         extract_flushSortKeys (zh, *sysno, 0, &zh->reg->sortKeys);
1032         extract_flushRecordKeys (zh, *sysno, 0, &delkeys);
1033         if (delete_flag)
1034         {
1035             /* record going to be deleted */
1036             if (!delkeys.buf_used)
1037             {
1038                 logf (LOG_LOG, "delete %s %s %ld", recordType,
1039                       fname, (long) recordOffset);
1040                 logf (LOG_WARN, "cannot delete file above, storeKeys false");
1041             }
1042             else
1043             {
1044                 logf (LOG_LOG, "delete %s %s %ld", recordType,
1045                       fname, (long) recordOffset);
1046 #if 0
1047                 if (matchStr)
1048                     dict_delete (matchDict, matchStr);
1049 #endif
1050                 rec_del (zh->reg->records, &rec);
1051             }
1052             rec_rm (&rec);
1053             return 1;
1054         }
1055         else
1056         {
1057             /* record going to be updated */
1058             if (!delkeys.buf_used)
1059             {
1060                 logf (LOG_LOG, "update %s %s %ld", recordType,
1061                       fname, (long) recordOffset);
1062                 logf (LOG_WARN, "cannot update file above, storeKeys false");
1063             }
1064             else
1065             {
1066                 logf (LOG_LOG, "update %s %s %ld", recordType,
1067                       fname, (long) recordOffset);
1068                 extract_flushRecordKeys (zh, *sysno, 1, &zh->reg->keys);
1069             }
1070         }
1071     }
1072     /* update file type */
1073     xfree (rec->info[recInfo_fileType]);
1074     rec->info[recInfo_fileType] =
1075         rec_strdup (recordType, &rec->size[recInfo_fileType]);
1076
1077     /* update filename */
1078     xfree (rec->info[recInfo_filename]);
1079     rec->info[recInfo_filename] =
1080         rec_strdup (fname, &rec->size[recInfo_filename]);
1081
1082     /* update delete keys */
1083     xfree (rec->info[recInfo_delKeys]);
1084     if (zh->reg->keys.buf_used > 0 && store_keys == 1)
1085     {
1086         rec->size[recInfo_delKeys] = zh->reg->keys.buf_used;
1087         rec->info[recInfo_delKeys] = zh->reg->keys.buf;
1088         zh->reg->keys.buf = NULL;
1089         zh->reg->keys.buf_max = 0;
1090     }
1091     else
1092     {
1093         rec->info[recInfo_delKeys] = NULL;
1094         rec->size[recInfo_delKeys] = 0;
1095     }
1096
1097     /* save file size of original record */
1098     zebraExplain_recordBytesIncrement (zh->reg->zei,
1099                                        - recordAttr->recordSize);
1100 #if 0
1101     recordAttr->recordSize = fi->file_moffset - recordOffset;
1102     if (!recordAttr->recordSize)
1103         recordAttr->recordSize = fi->file_max - recordOffset;
1104 #else
1105     recordAttr->recordSize = buf_size;
1106 #endif
1107     zebraExplain_recordBytesIncrement (zh->reg->zei,
1108                                        recordAttr->recordSize);
1109
1110     /* set run-number for this record */
1111     recordAttr->runNumber =
1112         zebraExplain_runNumberIncrement (zh->reg->zei, 0);
1113
1114     /* update store data */
1115     xfree (rec->info[recInfo_storeData]);
1116     if (store_data == 1)
1117     {
1118         rec->size[recInfo_storeData] = recordAttr->recordSize;
1119         rec->info[recInfo_storeData] = (char *)
1120             xmalloc (recordAttr->recordSize);
1121 #if 1
1122         memcpy (rec->info[recInfo_storeData], buf, recordAttr->recordSize);
1123 #else
1124         if (lseek (fi->fd, recordOffset, SEEK_SET) < 0)
1125         {
1126             logf (LOG_ERRNO|LOG_FATAL, "seek to %ld in %s",
1127                   (long) recordOffset, fname);
1128             exit (1);
1129         }
1130         if (read (fi->fd, rec->info[recInfo_storeData], recordAttr->recordSize)
1131             < recordAttr->recordSize)
1132         {
1133             logf (LOG_ERRNO|LOG_FATAL, "read %d bytes of %s",
1134                   recordAttr->recordSize, fname);
1135             exit (1);
1136         }
1137 #endif
1138     }
1139     else
1140     {
1141         rec->info[recInfo_storeData] = NULL;
1142         rec->size[recInfo_storeData] = 0;
1143     }
1144     /* update database name */
1145     xfree (rec->info[recInfo_databaseName]);
1146     rec->info[recInfo_databaseName] =
1147         rec_strdup (databaseName, &rec->size[recInfo_databaseName]); 
1148
1149     /* update offset */
1150     recordAttr->recordOffset = recordOffset;
1151     
1152     /* commit this record */
1153     rec_put (zh->reg->records, &rec);
1154
1155     return 0;
1156 }
1157
1158 int explain_extract (void *handle, Record rec, data1_node *n)
1159 {
1160     ZebraHandle zh = (ZebraHandle) handle;
1161     struct recExtractCtrl extractCtrl;
1162     int i;
1163
1164     if (zebraExplain_curDatabase (zh->reg->zei,
1165                                   rec->info[recInfo_databaseName]))
1166     {
1167         abort();
1168         if (zebraExplain_newDatabase (zh->reg->zei,
1169                                       rec->info[recInfo_databaseName], 0))
1170             abort ();
1171     }
1172
1173     zh->reg->keys.buf_used = 0;
1174     zh->reg->keys.prevAttrUse = -1;
1175     zh->reg->keys.prevAttrSet = -1;
1176     zh->reg->keys.prevSeqNo = 0;
1177     zh->reg->sortKeys = 0;
1178     
1179     extractCtrl.init = extract_init;
1180     extractCtrl.tokenAdd = extract_token_add;
1181     extractCtrl.schemaAdd = extract_schema_add;
1182     extractCtrl.dh = zh->reg->dh;
1183     for (i = 0; i<256; i++)
1184         extractCtrl.seqno[i] = 0;
1185     extractCtrl.zebra_maps = zh->reg->zebra_maps;
1186     extractCtrl.flagShowRecords = 0;
1187     extractCtrl.handle = handle;
1188     
1189     grs_extract_tree(&extractCtrl, n);
1190
1191     if (rec->size[recInfo_delKeys])
1192     {
1193         struct recKeys delkeys;
1194         struct sortKey *sortKeys = 0;
1195
1196         delkeys.buf_used = rec->size[recInfo_delKeys];
1197         delkeys.buf = rec->info[recInfo_delKeys];
1198         extract_flushSortKeys (zh, rec->sysno, 0, &sortKeys);
1199         extract_flushRecordKeys (zh, rec->sysno, 0, &delkeys);
1200     }
1201     extract_flushRecordKeys (zh, rec->sysno, 1, &zh->reg->keys);
1202     extract_flushSortKeys (zh, rec->sysno, 1, &zh->reg->sortKeys);
1203
1204     xfree (rec->info[recInfo_delKeys]);
1205     rec->size[recInfo_delKeys] = zh->reg->keys.buf_used;
1206     rec->info[recInfo_delKeys] = zh->reg->keys.buf;
1207     zh->reg->keys.buf = NULL;
1208     zh->reg->keys.buf_max = 0;
1209     return 0;
1210 }
1211
1212 void extract_flushRecordKeys (ZebraHandle zh, SYSNO sysno,
1213                               int cmd, struct recKeys *reckeys)
1214 {
1215 #if SU_SCHEME
1216 #else
1217     unsigned char attrSet = (unsigned char) -1;
1218     unsigned short attrUse = (unsigned short) -1;
1219 #endif
1220     int seqno = 0;
1221     int off = 0;
1222     int ch = 0;
1223     ZebraExplainInfo zei = zh->reg->zei;
1224
1225     if (!zh->reg->key_buf)
1226     {
1227         int mem = 8*1024*1024;
1228         zh->reg->key_buf = (char**) xmalloc (mem);
1229         zh->reg->ptr_top = mem/sizeof(char*);
1230         zh->reg->ptr_i = 0;
1231         zh->reg->key_buf_used = 0;
1232         zh->reg->key_file_no = 0;
1233     }
1234     zebraExplain_recordCountIncrement (zei, cmd ? 1 : -1);
1235     while (off < reckeys->buf_used)
1236     {
1237         const char *src = reckeys->buf + off;
1238         struct it_key key;
1239         int lead;
1240     
1241         lead = *src++;
1242
1243 #if SU_SCHEME
1244         if ((lead & 3) < 3)
1245         {
1246             memcpy (&ch, src, sizeof(ch));
1247             src += sizeof(ch);
1248         }
1249 #else
1250         if (!(lead & 1))
1251         {
1252             memcpy (&attrSet, src, sizeof(attrSet));
1253             src += sizeof(attrSet);
1254         }
1255         if (!(lead & 2))
1256         {
1257             memcpy (&attrUse, src, sizeof(attrUse));
1258             src += sizeof(attrUse);
1259         }
1260 #endif
1261         if (zh->reg->key_buf_used + 1024 > 
1262             (zh->reg->ptr_top -zh->reg->ptr_i)*sizeof(char*))
1263             extract_flushWriteKeys (zh);
1264         ++(zh->reg->ptr_i);
1265         (zh->reg->key_buf)[zh->reg->ptr_top - zh->reg->ptr_i] =
1266             (char*)zh->reg->key_buf + zh->reg->key_buf_used;
1267 #if SU_SCHEME
1268 #else
1269         ch = zebraExplain_lookupSU (zei, attrSet, attrUse);
1270         if (ch < 0)
1271             ch = zebraExplain_addSU (zei, attrSet, attrUse);
1272 #endif
1273         assert (ch > 0);
1274         zh->reg->key_buf_used +=
1275             key_SU_encode (ch,((char*)zh->reg->key_buf) +
1276                            zh->reg->key_buf_used);
1277
1278         while (*src)
1279             ((char*)zh->reg->key_buf) [(zh->reg->key_buf_used)++] = *src++;
1280         src++;
1281         ((char*)(zh->reg->key_buf))[(zh->reg->key_buf_used)++] = '\0';
1282         ((char*)(zh->reg->key_buf))[(zh->reg->key_buf_used)++] = cmd;
1283
1284         if (lead & 60)
1285             seqno += ((lead>>2) & 15)-1;
1286         else
1287         {
1288             memcpy (&seqno, src, sizeof(seqno));
1289             src += sizeof(seqno);
1290         }
1291         key.seqno = seqno;
1292         key.sysno = sysno;
1293         memcpy ((char*)zh->reg->key_buf + zh->reg->key_buf_used, &key, sizeof(key));
1294         (zh->reg->key_buf_used) += sizeof(key);
1295         off = src - reckeys->buf;
1296     }
1297     assert (off == reckeys->buf_used);
1298 }
1299
1300 void extract_flushWriteKeys (ZebraHandle zh)
1301 {
1302     FILE *outf;
1303     char out_fname[200];
1304     char *prevcp, *cp;
1305     struct encode_info encode_info;
1306     int ptr_i = zh->reg->ptr_i;
1307 #if SORT_EXTRA
1308     int i;
1309 #endif
1310     if (!zh->reg->key_buf || ptr_i <= 0)
1311         return;
1312
1313     (zh->reg->key_file_no)++;
1314     logf (LOG_LOG, "sorting section %d", (zh->reg->key_file_no));
1315 #if !SORT_EXTRA
1316     qsort (zh->reg->key_buf + zh->reg->ptr_top - ptr_i, ptr_i,
1317                sizeof(char*), key_qsort_compare);
1318     extract_get_fname_tmp (zh, out_fname, zh->reg->key_file_no);
1319
1320     if (!(outf = fopen (out_fname, "wb")))
1321     {
1322         logf (LOG_FATAL|LOG_ERRNO, "fopen %s", out_fname);
1323         exit (1);
1324     }
1325     logf (LOG_LOG, "writing section %d", zh->reg->key_file_no);
1326     prevcp = cp = (zh->reg->key_buf)[zh->reg->ptr_top - ptr_i];
1327     
1328     encode_key_init (&encode_info);
1329     encode_key_write (cp, &encode_info, outf);
1330     
1331     while (--ptr_i > 0)
1332     {
1333         cp = (zh->reg->key_buf)[zh->reg->ptr_top - ptr_i];
1334         if (strcmp (cp, prevcp))
1335         {
1336             encode_key_init (&encode_info);
1337             encode_key_write (cp, &encode_info, outf);
1338             prevcp = cp;
1339         }
1340         else
1341             encode_key_write (cp + strlen(cp), &encode_info, outf);
1342     }
1343 #else
1344     qsort (key_buf + ptr_top-ptr_i, ptr_i, sizeof(char*), key_x_compare);
1345     extract_get_fname_tmp (out_fname, key_file_no);
1346
1347     if (!(outf = fopen (out_fname, "wb")))
1348     {
1349         logf (LOG_FATAL|LOG_ERRNO, "fopen %s", out_fname);
1350         exit (1);
1351     }
1352     logf (LOG_LOG, "writing section %d", key_file_no);
1353     i = ptr_i;
1354     prevcp =  key_buf[ptr_top-i];
1355     while (1)
1356         if (!--i || strcmp (prevcp, key_buf[ptr_top-i]))
1357         {
1358             key_y_len = strlen(prevcp)+1;
1359 #if 0
1360             logf (LOG_LOG, "key_y_len: %2d %02x %02x %s",
1361                       key_y_len, prevcp[0], prevcp[1], 2+prevcp);
1362 #endif
1363             qsort (key_buf + ptr_top-ptr_i, ptr_i - i,
1364                                    sizeof(char*), key_y_compare);
1365             cp = key_buf[ptr_top-ptr_i];
1366             --key_y_len;
1367             encode_key_init (&encode_info);
1368             encode_key_write (cp, &encode_info, outf);
1369             while (--ptr_i > i)
1370             {
1371                 cp = key_buf[ptr_top-ptr_i];
1372                 encode_key_write (cp+key_y_len, &encode_info, outf);
1373             }
1374             if (!i)
1375                 break;
1376             prevcp = key_buf[ptr_top-ptr_i];
1377         }
1378 #endif
1379     if (fclose (outf))
1380     {
1381         logf (LOG_FATAL|LOG_ERRNO, "fclose %s", out_fname);
1382         exit (1);
1383     }
1384     logf (LOG_LOG, "finished section %d", zh->reg->key_file_no);
1385     zh->reg->ptr_i = 0;
1386     zh->reg->key_buf_used = 0;
1387 }
1388
1389 void extract_add_index_string (RecWord *p, const char *string,
1390                                int length)
1391 {
1392     char *dst;
1393     unsigned char attrSet;
1394     unsigned short attrUse;
1395     int lead = 0;
1396     int diff = 0;
1397     int *pseqno = &p->seqno;
1398     ZebraHandle zh = p->extractCtrl->handle;
1399     ZebraExplainInfo zei = zh->reg->zei;
1400     struct recKeys *keys = &zh->reg->keys;
1401     
1402     if (keys->buf_used+1024 > keys->buf_max)
1403     {
1404         char *b;
1405
1406         b = (char *) xmalloc (keys->buf_max += 128000);
1407         if (keys->buf_used > 0)
1408             memcpy (b, keys->buf, keys->buf_used);
1409         xfree (keys->buf);
1410         keys->buf = b;
1411     }
1412     dst = keys->buf + keys->buf_used;
1413
1414     attrSet = p->attrSet;
1415     if (keys->buf_used > 0 && keys->prevAttrSet == attrSet)
1416         lead |= 1;
1417     else
1418         keys->prevAttrSet = attrSet;
1419     attrUse = p->attrUse;
1420     if (keys->buf_used > 0 && keys->prevAttrUse == attrUse)
1421         lead |= 2;
1422     else
1423         keys->prevAttrUse = attrUse;
1424 #if 1
1425     diff = 1 + *pseqno - keys->prevSeqNo;
1426     if (diff >= 1 && diff <= 15)
1427         lead |= (diff << 2);
1428     else
1429         diff = 0;
1430 #endif
1431     keys->prevSeqNo = *pseqno;
1432     
1433     *dst++ = lead;
1434
1435 #if SU_SCHEME
1436     if ((lead & 3) < 3)
1437     {
1438         int ch = zebraExplain_lookupSU (zei, attrSet, attrUse);
1439         if (ch < 0)
1440         {
1441             ch = zebraExplain_addSU (zei, attrSet, attrUse);
1442             yaz_log (LOG_DEBUG, "addSU set=%d use=%d SU=%d",
1443                      attrSet, attrUse, ch);
1444         }
1445         assert (ch > 0);
1446         memcpy (dst, &ch, sizeof(ch));
1447         dst += sizeof(ch);
1448     }
1449 #else
1450     if (!(lead & 1))
1451     {
1452         memcpy (dst, &attrSet, sizeof(attrSet));
1453         dst += sizeof(attrSet);
1454     }
1455     if (!(lead & 2))
1456     {
1457         memcpy (dst, &attrUse, sizeof(attrUse));
1458         dst += sizeof(attrUse);
1459     }
1460 #endif
1461     *dst++ = p->reg_type;
1462     memcpy (dst, string, length);
1463     dst += length;
1464     *dst++ = '\0';
1465
1466     if (!diff)
1467     {
1468         memcpy (dst, pseqno, sizeof(*pseqno));
1469         dst += sizeof(*pseqno);
1470     }
1471     keys->buf_used = dst - keys->buf;
1472 }
1473
1474 static void extract_add_sort_string (RecWord *p, const char *string,
1475                                      int length)
1476 {
1477     struct sortKey *sk;
1478     ZebraHandle zh = p->extractCtrl->handle;
1479
1480     for (sk = zh->reg->sortKeys; sk; sk = sk->next)
1481         if (sk->attrSet == p->attrSet && sk->attrUse == p->attrUse)
1482             return;
1483
1484     sk = (struct sortKey *) xmalloc (sizeof(*sk));
1485     sk->next = zh->reg->sortKeys;
1486     zh->reg->sortKeys = sk;
1487
1488     sk->string = (char *) xmalloc (length);
1489     sk->length = length;
1490     memcpy (sk->string, string, length);
1491
1492     sk->attrSet = p->attrSet;
1493     sk->attrUse = p->attrUse;
1494 }
1495
1496 void extract_add_string (RecWord *p, const char *string, int length)
1497 {
1498     assert (length > 0);
1499     if (zebra_maps_is_sort (p->zebra_maps, p->reg_type))
1500         extract_add_sort_string (p, string, length);
1501     else
1502         extract_add_index_string (p, string, length);
1503 }
1504
1505 static void extract_add_incomplete_field (RecWord *p)
1506 {
1507     const char *b = p->string;
1508     int remain = p->length;
1509     const char **map = 0;
1510
1511     if (remain > 0)
1512         map = zebra_maps_input(p->zebra_maps, p->reg_type, &b, remain);
1513
1514     while (map)
1515     {
1516         char buf[IT_MAX_WORD+1];
1517         int i, remain;
1518
1519         /* Skip spaces */
1520         while (map && *map && **map == *CHR_SPACE)
1521         {
1522             remain = p->length - (b - p->string);
1523             if (remain > 0)
1524                 map = zebra_maps_input(p->zebra_maps, p->reg_type, &b, remain);
1525             else
1526                 map = 0;
1527         }
1528         if (!map)
1529             break;
1530         i = 0;
1531         while (map && *map && **map != *CHR_SPACE)
1532         {
1533             const char *cp = *map;
1534
1535             while (i < IT_MAX_WORD && *cp)
1536                 buf[i++] = *(cp++);
1537             remain = p->length - (b - p->string);
1538             if (remain > 0)
1539                 map = zebra_maps_input(p->zebra_maps, p->reg_type, &b, remain);
1540             else
1541                 map = 0;
1542         }
1543         if (!i)
1544             return;
1545         extract_add_string (p, buf, i);
1546         p->seqno++;
1547     }
1548 }
1549
1550 static void extract_add_complete_field (RecWord *p)
1551 {
1552     const char *b = p->string;
1553     char buf[IT_MAX_WORD+1];
1554     const char **map = 0;
1555     int i = 0, remain = p->length;
1556
1557     if (remain > 0)
1558         map = zebra_maps_input (p->zebra_maps, p->reg_type, &b, remain);
1559
1560     while (remain > 0 && i < IT_MAX_WORD)
1561     {
1562         while (map && *map && **map == *CHR_SPACE)
1563         {
1564             remain = p->length - (b - p->string);
1565             if (remain > 0)
1566                 map = zebra_maps_input(p->zebra_maps, p->reg_type, &b, remain);
1567             else
1568                 map = 0;
1569         }
1570         if (!map)
1571             break;
1572
1573         if (i && i < IT_MAX_WORD)
1574             buf[i++] = *CHR_SPACE;
1575         while (map && *map && **map != *CHR_SPACE)
1576         {
1577             const char *cp = *map;
1578
1579             if (i >= IT_MAX_WORD)
1580                 break;
1581             while (i < IT_MAX_WORD && *cp)
1582                 buf[i++] = *(cp++);
1583             remain = p->length  - (b - p->string);
1584             if (remain > 0)
1585                 map = zebra_maps_input (p->zebra_maps, p->reg_type, &b,
1586                                         remain);
1587             else
1588                 map = 0;
1589         }
1590     }
1591     if (!i)
1592         return;
1593     extract_add_string (p, buf, i);
1594 }
1595
1596 void extract_token_add (RecWord *p)
1597 {
1598     WRBUF wrbuf;
1599     if ((wrbuf = zebra_replace(p->zebra_maps, p->reg_type, 0,
1600                                p->string, p->length)))
1601     {
1602         p->string = wrbuf_buf(wrbuf);
1603         p->length = wrbuf_len(wrbuf);
1604     }
1605     if (zebra_maps_is_complete (p->zebra_maps, p->reg_type))
1606         extract_add_complete_field (p);
1607     else
1608         extract_add_incomplete_field(p);
1609 }
1610
1611 void extract_schema_add (struct recExtractCtrl *p, Odr_oid *oid)
1612 {
1613     ZebraHandle zh = (ZebraHandle) (p->handle);
1614     zebraExplain_addSchema (zh->reg->zei, oid);
1615 }
1616
1617 void extract_flushSortKeys (ZebraHandle zh, SYSNO sysno,
1618                             int cmd, struct sortKey **skp)
1619 {
1620     struct sortKey *sk = *skp;
1621     SortIdx sortIdx = zh->reg->sortIdx;
1622
1623     sortIdx_sysno (sortIdx, sysno);
1624     while (sk)
1625     {
1626         struct sortKey *sk_next = sk->next;
1627         sortIdx_type (sortIdx, sk->attrUse);
1628         sortIdx_add (sortIdx, sk->string, sk->length);
1629         xfree (sk->string);
1630         xfree (sk);
1631         sk = sk_next;
1632     }
1633     *skp = 0;
1634 }
1635
1636 void encode_key_init (struct encode_info *i)
1637 {
1638     i->sysno = 0;
1639     i->seqno = 0;
1640     i->cmd = -1;
1641 }
1642
1643 char *encode_key_int (int d, char *bp)
1644 {
1645     if (d <= 63)
1646         *bp++ = d;
1647     else if (d <= 16383)
1648     {
1649         *bp++ = 64 + (d>>8);
1650         *bp++ = d  & 255;
1651     }
1652     else if (d <= 4194303)
1653     {
1654         *bp++ = 128 + (d>>16);
1655         *bp++ = (d>>8) & 255;
1656         *bp++ = d & 255;
1657     }
1658     else
1659     {
1660         *bp++ = 192 + (d>>24);
1661         *bp++ = (d>>16) & 255;
1662         *bp++ = (d>>8) & 255;
1663         *bp++ = d & 255;
1664     }
1665     return bp;
1666 }
1667
1668 void encode_key_write (char *k, struct encode_info *i, FILE *outf)
1669 {
1670     struct it_key key;
1671     char *bp = i->buf;
1672
1673     while ((*bp++ = *k++))
1674         ;
1675     memcpy (&key, k+1, sizeof(struct it_key));
1676     bp = encode_key_int ( (key.sysno - i->sysno) * 2 + *k, bp);
1677     if (i->sysno != key.sysno)
1678     {
1679         i->sysno = key.sysno;
1680         i->seqno = 0;
1681     }
1682     else if (!i->seqno && !key.seqno && i->cmd == *k)
1683         return;
1684     bp = encode_key_int (key.seqno - i->seqno, bp);
1685     i->seqno = key.seqno;
1686     i->cmd = *k;
1687     if (fwrite (i->buf, bp - i->buf, 1, outf) != 1)
1688     {
1689         logf (LOG_FATAL|LOG_ERRNO, "fwrite");
1690         exit (1);
1691     }
1692 }
1693