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