More work on regular patterns.
[idzebra-moved-to-github.git] / index / extract.c
1 /*
2  * Copyright (C) 1994-1995, Index Data I/S 
3  * All rights reserved.
4  * Sebastian Hammer, Adam Dickmeiss
5  *
6  * $Log: extract.c,v $
7  * Revision 1.40  1995-12-05 16:57:40  adam
8  * More work on regular patterns.
9  *
10  * Revision 1.39  1995/12/05  13:20:18  adam
11  * Bug fix: file_read sometimes returned early EOF.
12  *
13  * Revision 1.38  1995/12/04  17:59:21  adam
14  * More work on regular expression conversion.
15  *
16  * Revision 1.37  1995/12/04  14:22:27  adam
17  * Extra arg to recType_byName.
18  * Started work on new regular expression parsed input to
19  * structured records.
20  *
21  * Revision 1.36  1995/11/30  08:34:29  adam
22  * Started work on commit facility.
23  * Changed a few malloc/free to xmalloc/xfree.
24  *
25  * Revision 1.35  1995/11/28  14:26:21  adam
26  * Bug fix: recordId with constant wasn't right.
27  * Bug fix: recordId dictionary entry wasn't deleted when needed.
28  *
29  * Revision 1.34  1995/11/28  09:09:38  adam
30  * Zebra config renamed.
31  * Use setting 'recordId' to identify record now.
32  * Bug fix in recindex.c: rec_release_blocks was invokeded even
33  * though the blocks were already released.
34  * File traversal properly deletes records when needed.
35  *
36  * Revision 1.33  1995/11/27  09:56:20  adam
37  * Record info elements better enumerated. Internal store of records.
38  *
39  * Revision 1.32  1995/11/25  10:24:05  adam
40  * More record fields - they are enumerated now.
41  * New options: flagStoreData flagStoreKey.
42  *
43  * Revision 1.31  1995/11/24  11:31:35  adam
44  * Commands add & del read filenames from stdin if source directory is
45  * empty.
46  * Match criteria supports 'constant' strings.
47  *
48  * Revision 1.30  1995/11/22  17:19:16  adam
49  * Record management uses the bfile system.
50  *
51  * Revision 1.29  1995/11/21  15:01:14  adam
52  * New general match criteria implemented.
53  * New feature: document groups.
54  *
55  * Revision 1.28  1995/11/21  09:20:30  adam
56  * Yet more work on record match.
57  *
58  * Revision 1.27  1995/11/20  16:59:45  adam
59  * New update method: the 'old' keys are saved for each records.
60  *
61  * Revision 1.26  1995/11/20  11:56:24  adam
62  * Work on new traversal.
63  *
64  * Revision 1.25  1995/11/16  15:34:54  adam
65  * Uses new record management system in both indexer and server.
66  *
67  * Revision 1.24  1995/11/15  19:13:08  adam
68  * Work on record management.
69  *
70  * Revision 1.23  1995/10/27  14:00:10  adam
71  * Implemented detection of database availability.
72  *
73  * Revision 1.22  1995/10/17  18:02:07  adam
74  * New feature: databases. Implemented as prefix to words in dictionary.
75  *
76  * Revision 1.21  1995/10/10  12:24:38  adam
77  * Temporary sort files are compressed.
78  *
79  * Revision 1.20  1995/10/06  13:52:05  adam
80  * Bug fixes. Handler may abort further scanning.
81  *
82  * Revision 1.19  1995/10/04  12:55:16  adam
83  * Bug fix in ranked search. Use=Any keys inserted.
84  *
85  * Revision 1.18  1995/10/04  09:37:08  quinn
86  * Fixed bug.
87  *
88  * Revision 1.17  1995/10/03  14:28:57  adam
89  * Buffered read in extract works.
90  *
91  * Revision 1.16  1995/10/03  14:28:45  adam
92  * Work on more effecient read handler in extract.
93  *
94  * Revision 1.15  1995/10/02  15:42:53  adam
95  * Extract uses file descriptors instead of FILE pointers.
96  *
97  * Revision 1.14  1995/10/02  15:29:13  adam
98  * More logging in file_extract.
99  *
100  * Revision 1.13  1995/09/29  14:01:39  adam
101  * Bug fixes.
102  *
103  * Revision 1.12  1995/09/28  14:22:56  adam
104  * Sort uses smaller temporary files.
105  *
106  * Revision 1.11  1995/09/28  12:10:31  adam
107  * Bug fixes. Field prefix used in queries.
108  *
109  * Revision 1.10  1995/09/28  09:19:41  adam
110  * xfree/xmalloc used everywhere.
111  * Extract/retrieve method seems to work for text records.
112  *
113  * Revision 1.9  1995/09/27  12:22:28  adam
114  * More work on extract in record control.
115  * Field name is not in isam keys but in prefix in dictionary words.
116  *
117  * Revision 1.8  1995/09/14  07:48:22  adam
118  * Record control management.
119  *
120  * Revision 1.7  1995/09/11  13:09:32  adam
121  * More work on relevance feedback.
122  *
123  * Revision 1.6  1995/09/08  14:52:27  adam
124  * Minor changes. Dictionary is lower case now.
125  *
126  * Revision 1.5  1995/09/06  16:11:16  adam
127  * Option: only one word key per file.
128  *
129  * Revision 1.4  1995/09/05  15:28:39  adam
130  * More work on search engine.
131  *
132  * Revision 1.3  1995/09/04  12:33:41  adam
133  * Various cleanup. YAZ util used instead.
134  *
135  * Revision 1.2  1995/09/04  09:10:34  adam
136  * More work on index add/del/update.
137  * Merge sort implemented.
138  * Initial work on z39 server.
139  *
140  * Revision 1.1  1995/09/01  14:06:35  adam
141  * Split of work into more files.
142  *
143  */
144 #include <stdio.h>
145 #include <assert.h>
146 #include <unistd.h>
147 #include <fcntl.h>
148 #include <ctype.h>
149
150 #include <alexutil.h>
151 #include <recctrl.h>
152 #include "index.h"
153
154 #include "recindex.h"
155
156 static Dict matchDict;
157
158 static Records records = NULL;
159
160 static char **key_buf;
161 static size_t ptr_top;
162 static size_t ptr_i;
163 static size_t key_buf_used;
164 static int key_file_no;
165
166 static int records_inserted = 0;
167 static int records_updated = 0;
168 static int records_deleted = 0;
169
170 #define MATCH_DICT "match"
171
172 void key_open (int mem)
173 {
174     if (mem < 50000)
175         mem = 50000;
176     key_buf = xmalloc (mem);
177     ptr_top = mem/sizeof(char*);
178     ptr_i = 0;
179
180     key_buf_used = 0;
181     key_file_no = 0;
182
183     if (!(matchDict = dict_open (MATCH_DICT, 20, 1)))
184     {
185         logf (LOG_FATAL, "dict_open fail of %s", MATCH_DICT);
186         exit (1);
187     }
188     assert (!records);
189     records = rec_open (1);
190 }
191
192 struct encode_info {
193     int  sysno;
194     int  seqno;
195     char buf[512];
196 };
197
198 void encode_key_init (struct encode_info *i)
199 {
200     i->sysno = 0;
201     i->seqno = 0;
202 }
203
204 char *encode_key_int (int d, char *bp)
205 {
206     if (d <= 63)
207         *bp++ = d;
208     else if (d <= 16383)
209     {
210         *bp++ = 64 + (d>>8);
211         *bp++ = d  & 255;
212     }
213     else if (d <= 4194303)
214     {
215         *bp++ = 128 + (d>>16);
216         *bp++ = (d>>8) & 255;
217         *bp++ = d & 255;
218     }
219     else
220     {
221         *bp++ = 192 + (d>>24);
222         *bp++ = (d>>16) & 255;
223         *bp++ = (d>>8) & 255;
224         *bp++ = d & 255;
225     }
226     return bp;
227 }
228
229 void encode_key_write (char *k, struct encode_info *i, FILE *outf)
230 {
231     struct it_key key;
232     char *bp = i->buf;
233
234     while ((*bp++ = *k++))
235         ;
236     memcpy (&key, k+1, sizeof(struct it_key));
237     bp = encode_key_int ( (key.sysno - i->sysno) * 2 + *k, bp);
238     if (i->sysno != key.sysno)
239     {
240         i->sysno = key.sysno;
241         i->seqno = 0;
242     }
243     bp = encode_key_int (key.seqno - i->seqno, bp);
244     i->seqno = key.seqno;
245     if (fwrite (i->buf, bp - i->buf, 1, outf) != 1)
246     {
247         logf (LOG_FATAL|LOG_ERRNO, "fwrite");
248         exit (1);
249     }
250 }
251
252 void key_flush (void)
253 {
254     FILE *outf;
255     char out_fname[200];
256     char *prevcp, *cp;
257     struct encode_info encode_info;
258     
259     if (ptr_i <= 0)
260         return;
261
262     key_file_no++;
263     logf (LOG_LOG, "sorting section %d", key_file_no);
264     qsort (key_buf + ptr_top-ptr_i, ptr_i, sizeof(char*), key_qsort_compare);
265     sprintf (out_fname, TEMP_FNAME, key_file_no);
266
267     if (!(outf = fopen (out_fname, "w")))
268     {
269         logf (LOG_FATAL|LOG_ERRNO, "fopen (4) %s", out_fname);
270         exit (1);
271     }
272     logf (LOG_LOG, "writing section %d", key_file_no);
273     prevcp = cp = key_buf[ptr_top-ptr_i];
274     
275     encode_key_init (&encode_info);
276     encode_key_write (cp, &encode_info, outf);
277     while (--ptr_i > 0)
278     {
279         cp = key_buf[ptr_top-ptr_i];
280         if (strcmp (cp, prevcp))
281         {
282             encode_key_init (&encode_info);
283             encode_key_write (cp, &encode_info, outf);
284             prevcp = cp;
285         }
286         else
287             encode_key_write (cp + strlen(cp), &encode_info, outf);
288     }
289     if (fclose (outf))
290     {
291         logf (LOG_FATAL|LOG_ERRNO, "fclose %s", out_fname);
292         exit (1);
293     }
294     logf (LOG_LOG, "finished section %d", key_file_no);
295     ptr_i = 0;
296     key_buf_used = 0;
297 }
298
299 int key_close (void)
300 {
301     key_flush ();
302     xfree (key_buf);
303     rec_close (&records);
304     dict_close (matchDict);
305
306     logf (LOG_LOG, "Records inserted %6d", records_inserted);
307     logf (LOG_LOG, "Records updated  %6d", records_updated);
308     logf (LOG_LOG, "Records deleted  %6d", records_deleted);
309     return key_file_no;
310 }
311
312 static void wordInit (RecWord *p)
313 {
314     p->attrSet = 1;
315     p->attrUse = 1016;
316     p->which = Word_String;
317 }
318
319 struct recKeys {
320     int buf_used;
321     int buf_max;
322     char *buf;
323 } reckeys;
324
325 static void addRecordKey (const RecWord *p)
326 {
327     char *dst;
328     char attrSet;
329     short attrUse;
330     size_t i;
331
332     if (reckeys.buf_used+1024 > reckeys.buf_max)
333     {
334         char *b;
335
336         b = xmalloc (reckeys.buf_max += 65000);
337         if (reckeys.buf_used > 0)
338             memcpy (b, reckeys.buf, reckeys.buf_used);
339         xfree (reckeys.buf);
340         reckeys.buf = b;
341     }
342     dst = reckeys.buf + reckeys.buf_used;
343     switch (p->which)
344     {
345     case Word_String:
346         attrSet = p->attrSet;
347         memcpy (dst, &attrSet, sizeof(attrSet));
348         dst += sizeof(attrSet);
349
350         attrUse = p->attrUse;
351         memcpy (dst, &attrUse, sizeof(attrUse));
352         dst += sizeof(attrUse);
353         
354         for (i = 0; p->u.string[i]; i++)
355             *dst++ = p->u.string[i];
356         *dst++ = '\0';
357
358         memcpy (dst, &p->seqno, sizeof(p->seqno));
359         dst += sizeof(p->seqno);
360
361         break;
362     default:
363         return;
364     }
365     reckeys.buf_used = dst - reckeys.buf;
366 }
367
368 static void flushRecordKeys (SYSNO sysno, int cmd, struct recKeys *reckeys, 
369                              const char *databaseName)
370 {
371     int off = 0;
372     while (off < reckeys->buf_used)
373     {
374         const char *src = reckeys->buf + off;
375         char attrSet;
376         short attrUse;
377         struct it_key key;
378         
379         memcpy (&attrSet, src, sizeof(attrSet));
380         src += sizeof(attrSet);
381
382         memcpy (&attrUse, src, sizeof(attrUse));
383         src += sizeof(attrUse);
384
385         if (key_buf_used + 1024 > (ptr_top-ptr_i)*sizeof(char*))
386             key_flush ();
387         ++ptr_i;
388         key_buf[ptr_top-ptr_i] = (char*)key_buf + key_buf_used;
389         key_buf_used += index_word_prefix ((char*)key_buf + key_buf_used,
390                                            attrSet, attrUse, databaseName);
391         while (*src)
392             ((char*)key_buf) [key_buf_used++] = index_char_cvt (*src++);
393         src++;
394         ((char*)key_buf) [key_buf_used++] = '\0';
395         
396         ((char*) key_buf)[key_buf_used++] = cmd;
397
398         memcpy (&key.seqno, src, sizeof(key.seqno));
399         src += sizeof(key.seqno);
400         key.sysno = sysno;
401         memcpy ((char*)key_buf + key_buf_used, &key, sizeof(key));
402         key_buf_used += sizeof(key);
403         off = src - reckeys->buf;
404     }
405     assert (off == reckeys->buf_used);
406 }
407
408 static const char **searchRecordKey (struct recKeys *reckeys,
409                                int attrSetS, int attrUseS)
410 {
411     static const char *ws[32];
412     int off = 0;
413     int startSeq = -1;
414     int i;
415
416     for (i = 0; i<32; i++)
417         ws[i] = NULL;
418     
419     while (off < reckeys->buf_used)
420     {
421         const char *src = reckeys->buf + off;
422         char attrSet;
423         short attrUse;
424         int seqno;
425         const char *wstart;
426         
427         memcpy (&attrSet, src, sizeof(attrSet));
428         src += sizeof(attrSet);
429
430         memcpy (&attrUse, src, sizeof(attrUse));
431         src += sizeof(attrUse);
432
433         wstart = src;
434         while (*src++)
435             ;
436
437         memcpy (&seqno, src, sizeof(seqno));
438         src += sizeof(seqno);
439
440 #if 0
441         logf (LOG_LOG, "(%d,%d) %d %s", attrSet, attrUse, seqno, wstart);
442 #endif
443         if (attrUseS == attrUse && attrSetS == attrSet)
444         {
445             int woff;
446
447
448             if (startSeq == -1)
449                 startSeq = seqno;
450             woff = seqno - startSeq;
451             if (woff >= 0 && woff < 31)
452                 ws[woff] = wstart;
453         }
454
455         off = src - reckeys->buf;
456     }
457     assert (off == reckeys->buf_used);
458     return ws;
459 }
460
461 static void addRecordKeyAny (const RecWord *p)
462 {
463     if (p->attrSet != 1 || p->attrUse != 1016)
464     {
465         RecWord w;
466
467         memcpy (&w, p, sizeof(w));
468         w.attrSet = 1;
469         w.attrUse = 1016;
470         addRecordKey (&w);
471     }
472     addRecordKey (p);
473 }
474
475
476 #define FILE_READ_BUFSIZE 4096
477
478 static int file_noread;
479 #if FILE_READ_BUFSIZE
480 static char *file_buf;
481 static int file_offset;
482 static int file_bufsize;
483 #endif
484
485 static void file_read_start (int fd)
486 {
487     file_noread = 0;
488 #if FILE_READ_BUFSIZE
489     file_offset = 0;
490     file_buf = xmalloc (FILE_READ_BUFSIZE);
491     file_bufsize = read (fd, file_buf, FILE_READ_BUFSIZE);
492 #endif
493 }
494
495 static void file_read_stop (int fd)
496 {
497 #if FILE_READ_BUFSIZE
498     xfree (file_buf);
499     file_buf = NULL;
500 #endif
501 }
502
503 static int file_read (int fd, char *buf, size_t count)
504 {
505 #if FILE_READ_BUFSIZE
506     int l = file_bufsize - file_offset;
507
508     if (count > l)
509     {
510         int r;
511         if (l > 0)
512             memcpy (buf, file_buf + file_offset, l);
513         count = count-l;
514         if (count > FILE_READ_BUFSIZE)
515         {
516             if ((r = read (fd, buf + l, count)) == -1)
517             {
518                 logf (LOG_FATAL|LOG_ERRNO, "read");
519                 exit (1);
520             }
521             file_bufsize = 0;
522             file_offset = 0;
523             file_noread += l+r;
524             return l+r;
525         }
526         file_bufsize = r = read (fd, file_buf, FILE_READ_BUFSIZE);
527         if (r == -1)
528         {
529             logf (LOG_FATAL|LOG_ERRNO, "read");
530             exit (1);
531         }
532         else if (r <= count)
533         {
534             file_offset = r;
535             memcpy (buf + l, file_buf, r);
536             file_noread += l+r;
537             return l+r;
538         }
539         else
540         {
541             file_offset = count;
542             memcpy (buf + l, file_buf, count - l);
543             file_noread += count;
544             return count;
545         }
546     }
547     memcpy (buf, file_buf + file_offset, count);
548     file_offset += count;
549     file_noread += count;
550     return count;
551 #else
552     int r;
553     r = read (fd, buf, count);
554     if (r > 0)
555         file_noread += r;
556     return r;
557 #endif
558 }
559
560 static int atois (const char **s)
561 {
562     int val = 0, c;
563     while ( (c=**s) >= '0' && c <= '9')
564     {
565         val = val*10 + c - '0';
566         ++(*s);
567     }
568     return val;
569 }
570
571 static char *fileMatchStr (struct recKeys *reckeys, struct recordGroup *rGroup,
572                            const char *fname,
573                            const char *spec)
574 {
575     static char dstBuf[2048];
576     char *dst = dstBuf;
577     const char *s = spec;
578     static const char **w;
579     int i;
580
581     while (1)
582     {
583         while (*s == ' ' || *s == '\t')
584             s++;
585         if (!*s)
586             break;
587         if (*s == '(')
588         {
589             char matchFlag[32];
590             int attrSet, attrUse;
591             int first = 1;
592
593             s++;
594             attrSet = atois (&s);
595             if (*s != ',')
596             {
597                 logf (LOG_WARN, "Missing , in match criteria %s in group %s",
598                       spec, rGroup->groupName ? rGroup->groupName : "none");
599                 return NULL;
600             }
601             s++;
602             attrUse = atois (&s);
603             w = searchRecordKey (reckeys, attrSet, attrUse);
604             assert (w);
605
606             if (*s == ')')
607             {
608                 for (i = 0; i<32; i++)
609                     matchFlag[i] = 1;
610             }
611             else
612             {
613                 logf (LOG_WARN, "Missing ) in match criteria %s in group %s",
614                       spec, rGroup->groupName ? rGroup->groupName : "none");
615                 return NULL;
616             }
617             s++;
618
619             for (i = 0; i<32; i++)
620                 if (matchFlag[i] && w[i])
621                 {
622                     if (first)
623                     {
624                         *dst++ = ' ';
625                         first = 0;
626                     }
627                     strcpy (dst, w[i]);
628                     dst += strlen(w[i]);
629                 }
630             if (first)
631             {
632                 logf (LOG_WARN, "Record in file %s didn't contain match"
633                       " fields in (%d,%d)", fname, attrSet, attrUse);
634                 return NULL;
635             }
636         }
637         else if (*s == '$')
638         {
639             int spec_len;
640             char special[64];
641             const char *spec_src = NULL;
642             const char *s1 = ++s;
643             while (*s1 && *s1 != ' ' && *s1 != '\t')
644                 s1++;
645
646             spec_len = s1 - s;
647             if (spec_len > 63)
648                 spec_len = 63;
649             memcpy (special, s, spec_len);
650             special[spec_len] = '\0';
651             s = s1;
652
653             if (!strcmp (special, "group"))
654                 spec_src = rGroup->groupName;
655             else if (!strcmp (special, "database"))
656                 spec_src = rGroup->databaseName;
657             else if (!strcmp (special, "filename"))
658                 spec_src = fname;
659             else if (!strcmp (special, "type"))
660                 spec_src = rGroup->recordType;
661             else 
662                 spec_src = NULL;
663             if (spec_src)
664             {
665                 strcpy (dst, spec_src);
666                 dst += strlen (spec_src);
667             }
668         }
669         else if (*s == '\"' || *s == '\'')
670         {
671             int stopMarker = *s++;
672             char tmpString[64];
673             int i = 0;
674
675             while (*s && *s != stopMarker)
676             {
677                 if (i < 63)
678                     tmpString[i++] = *s++;
679             }
680             if (*s)
681                 s++;
682             tmpString[i] = '\0';
683             strcpy (dst, tmpString);
684             dst += strlen (tmpString);
685         }
686         else
687         {
688             logf (LOG_WARN, "Syntax error in match criteria %s in group %s",
689                   spec, rGroup->groupName ? rGroup->groupName : "none");
690             return NULL;
691         }
692         *dst++ = 1;
693     }
694     if (dst == dstBuf)
695     {
696         logf (LOG_WARN, "No match criteria for record %s in group %s",
697               fname, rGroup->groupName ? rGroup->groupName : "none");
698         return NULL;
699     }
700     return dstBuf;
701 }
702
703 static int recordExtract (SYSNO *sysno, const char *fname,
704                           struct recordGroup *rGroup, int deleteFlag,
705                           int fd, RecType recType, char *subType)
706 {
707     struct recExtractCtrl extractCtrl;
708     int r;
709     char *matchStr;
710     SYSNO sysnotmp;
711     Record rec;
712
713     if (fd != -1)
714     {
715         extractCtrl.fd = fd;
716         /* extract keys */
717         extractCtrl.subType = subType;
718         extractCtrl.init = wordInit;
719         extractCtrl.add = addRecordKeyAny;
720
721         reckeys.buf_used = 0;
722         extractCtrl.readf = file_read;
723         r = (*recType->extract)(&extractCtrl);
724   
725         if (r)      
726         {
727             logf (LOG_WARN, "Couldn't extract file %s, code %d", fname, r);
728             return 0;
729         }
730     }
731
732     /* perform match if sysno not known and if match criteria is specified */
733        
734     matchStr = NULL;
735     if (!sysno) 
736     {
737         sysnotmp = 0;
738         sysno = &sysnotmp;
739         if (rGroup->recordId && *rGroup->recordId)
740         {
741             char *rinfo;
742         
743             matchStr = fileMatchStr (&reckeys, rGroup, fname, 
744                                      rGroup->recordId);
745             if (matchStr)
746             {
747                 rinfo = dict_lookup (matchDict, matchStr);
748                 if (rinfo)
749                     memcpy (sysno, rinfo+1, sizeof(*sysno));
750             }
751             else
752             {
753                 logf (LOG_WARN, "Record not inserted");
754                 return 0;
755             }
756         }
757     }
758
759     /* new record ? */
760     if (! *sysno)
761     {
762         if (deleteFlag)
763         {
764             logf (LOG_LOG, "? %s", fname);
765             return 1;
766         }
767         logf (LOG_LOG, "add %s %s", rGroup->recordType, fname);
768         rec = rec_new (records);
769         *sysno = rec->sysno;
770
771         if (matchStr)
772         {
773             dict_insert (matchDict, matchStr, sizeof(*sysno), sysno);
774         }
775         flushRecordKeys (*sysno, 1, &reckeys, rGroup->databaseName);
776
777         records_inserted++;
778     }
779     else
780     {
781         struct recKeys delkeys;
782
783         rec = rec_get (records, *sysno);
784
785         delkeys.buf_used = rec->size[recInfo_delKeys];
786         delkeys.buf = rec->info[recInfo_delKeys];
787         flushRecordKeys (*sysno, 0, &delkeys, rec->info[recInfo_databaseName]);
788         if (deleteFlag)
789         {
790             if (!delkeys.buf_used)
791             {
792                 logf (LOG_WARN, "cannot delete %s - no delete keys", fname);
793             }
794             else
795             {
796                 SYSNO sysnoz = 0;
797                 logf (LOG_LOG, "delete %s %s", rGroup->recordType, fname);
798                 records_deleted++;
799                 if (matchStr)
800                     dict_insert (matchDict, matchStr, sizeof(sysnoz), &sysnoz);
801                 rec_del (records, &rec);
802             }
803             return 1;
804         }
805         else
806         {
807             if (!delkeys.buf_used)
808             {
809                 logf (LOG_WARN, "cannot update %s - no delete keys",
810                       fname);
811             }
812             else
813             {
814                 logf (LOG_LOG, "update %s %s", rGroup->recordType,
815                       fname);
816                 flushRecordKeys (*sysno, 1, &reckeys, rGroup->databaseName); 
817                 records_updated++;
818             }
819         }
820     }
821     xfree (rec->info[recInfo_fileType]);
822     rec->info[recInfo_fileType] =
823         rec_strdup (rGroup->recordType, &rec->size[recInfo_fileType]);
824
825     xfree (rec->info[recInfo_filename]);
826     rec->info[recInfo_filename] =
827         rec_strdup (fname, &rec->size[recInfo_filename]);
828
829     xfree (rec->info[recInfo_delKeys]);
830     if (reckeys.buf_used > 0 && rGroup->flagStoreKeys == 1)
831     {
832         rec->info[recInfo_delKeys] = xmalloc (reckeys.buf_used);
833         rec->size[recInfo_delKeys] = reckeys.buf_used;
834         memcpy (rec->info[recInfo_delKeys], reckeys.buf,
835                 rec->size[recInfo_delKeys]);
836     }
837     else
838     {
839         rec->info[recInfo_delKeys] = NULL;
840         rec->size[recInfo_delKeys] = 0;
841     }
842
843     xfree (rec->info[recInfo_storeData]);
844     if (rGroup->flagStoreData == 1)
845     {
846         rec->size[recInfo_storeData] = file_noread;
847         rec->info[recInfo_storeData] = xmalloc (file_noread);
848 #if FILE_READ_BUFSIZE
849         if (file_noread < FILE_READ_BUFSIZE)
850             memcpy (rec->info[recInfo_storeData], file_buf, file_noread);
851         else
852 #endif
853         {
854             if (lseek (fd, 0L, SEEK_SET) < 0)
855             {
856                 logf (LOG_ERRNO|LOG_FATAL, "seek to 0 in %s", fname);
857                 exit (1);
858             }
859             if (read (fd, rec->info[recInfo_storeData], file_noread) 
860                 < file_noread)
861             {
862                 logf (LOG_ERRNO|LOG_FATAL, "read %d bytes of %s",
863                       file_noread, fname);
864                 exit (1);
865             }
866         }
867     }
868     else
869     {
870         rec->info[recInfo_storeData] = NULL;
871         rec->size[recInfo_storeData] = 0;
872     }
873     xfree (rec->info[recInfo_databaseName]);
874     rec->info[recInfo_databaseName] =
875         rec_strdup (rGroup->databaseName, &rec->size[recInfo_databaseName]); 
876
877     rec_put (records, &rec);
878     return 1;
879 }
880
881 int fileExtract (SYSNO *sysno, const char *fname, 
882                  const struct recordGroup *rGroupP, int deleteFlag)
883 {
884     int i, fd;
885     char gprefix[128];
886     char ext[128];
887     char ext_res[128];
888     char subType[128];
889     RecType recType;
890     struct recordGroup rGroupM;
891     struct recordGroup *rGroup = &rGroupM;
892
893     memcpy (rGroup, rGroupP, sizeof(*rGroupP));
894    
895     if (!rGroup->groupName || !*rGroup->groupName)
896         *gprefix = '\0';
897     else
898         sprintf (gprefix, "%s.", rGroup->groupName);
899
900     logf (LOG_DEBUG, "fileExtract %s", fname);
901
902     /* determine file extension */
903     for (i = strlen(fname); --i >= 0; )
904         if (fname[i] == '/')
905         {
906             strcpy (ext, "");
907             break;
908         }
909         else if (fname[i] == '.')
910         {
911             strcpy (ext, fname+i+1);
912             break;
913         }
914     /* determine file type - depending on extension */
915     if (!rGroup->recordType)
916     {
917         sprintf (ext_res, "%srecordType.%s", gprefix, ext);
918         if (!(rGroup->recordType = res_get (common_resource, ext_res)))
919         {
920             sprintf (ext_res, "%srecordType", gprefix);
921             if (!(rGroup->recordType = res_get (common_resource, ext_res)))
922             {
923                 logf (LOG_LOG, "? %s", fname);
924                 return 0;
925             }
926         }
927     }
928     if (!rGroup->recordType)
929     {
930         logf (LOG_LOG, "? record %s", fname);
931         return 0;
932     }
933     if (!(recType = recType_byName (rGroup->recordType, subType)))
934     {
935         logf (LOG_WARN, "No such record type: %s", rGroup->recordType);
936         return 0;
937     }
938
939     /* determine match criteria */
940     if (!rGroup->recordId)
941     {
942         sprintf (ext_res, "%srecordId.%s", gprefix, ext);
943         rGroup->recordId = res_get (common_resource, ext_res);
944     }
945
946     /* determine database name */
947     if (!rGroup->databaseName)
948     {
949         sprintf (ext_res, "%sdatabase.%s", gprefix, ext);
950         if (!(rGroup->databaseName = res_get (common_resource, ext_res)))
951         {
952             sprintf (ext_res, "%sdatabase", gprefix);
953             rGroup->databaseName = res_get (common_resource, ext_res);
954         }
955     }
956     if (!rGroup->databaseName)
957         rGroup->databaseName = "Default";
958
959     if (rGroup->flagStoreData == -1)
960     {
961         const char *sval;
962         sprintf (ext_res, "%sstoreData.%s", gprefix, ext);
963         if (!(sval = res_get (common_resource, ext_res)))
964         {
965             sprintf (ext_res, "%sstoreData", gprefix);
966             sval = res_get (common_resource, ext_res);
967         }
968         if (sval)
969             rGroup->flagStoreData = atoi (sval);
970     }
971     if (rGroup->flagStoreData == -1)
972         rGroup->flagStoreData = 0;
973
974     if (rGroup->flagStoreKeys == -1)
975     {
976         const char *sval;
977
978         sprintf (ext_res, "%sstoreKeys.%s", gprefix, ext);
979         if (!(sval = res_get (common_resource, ext_res)))
980         {
981             sprintf (ext_res, "%sstoreKeys", gprefix);
982             sval = res_get (common_resource, ext_res);
983         }
984         if (sval)
985             rGroup->flagStoreKeys = atoi (sval);
986     }
987     if (rGroup->flagStoreKeys == -1)
988         rGroup->flagStoreKeys = 0;
989
990     if (sysno && deleteFlag)
991         fd = -1;
992     else
993     {
994         if ((fd = open (fname, O_RDONLY)) == -1)
995         {
996             logf (LOG_WARN|LOG_ERRNO, "open %s", fname);
997             return 0;
998         }
999     }
1000     file_read_start (fd);
1001     recordExtract (sysno, fname, rGroup, deleteFlag, fd, recType, subType);
1002     file_read_stop (fd);
1003     if (fd != -1)
1004         close (fd);
1005     return 1;
1006 }
1007