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