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