First work on multiple records in one file.
[idzebra-moved-to-github.git] / index / trav.c
1 /*
2  * Copyright (C) 1994-1995, Index Data I/S 
3  * All rights reserved.
4  * Sebastian Hammer, Adam Dickmeiss
5  *
6  * $Log: trav.c,v $
7  * Revision 1.25  1996-05-01 13:46:37  adam
8  * First work on multiple records in one file.
9  * New option, -offset, to the "unread" command in the filter module.
10  *
11  * Revision 1.24  1996/04/26  10:00:23  adam
12  * Added option -V to zebraidx to display version information.
13  * Removed stupid warnings from file update.
14  *
15  * Revision 1.23  1996/04/12  07:02:25  adam
16  * File update of single files.
17  *
18  * Revision 1.22  1996/04/09 06:50:50  adam
19  * Bug fix: bad reference in function fileUpdateR.
20  *
21  * Revision 1.21  1996/03/22 15:34:18  quinn
22  * Fixed bad reference
23  *
24  * Revision 1.20  1996/03/21  14:50:10  adam
25  * File update uses modify-time instead of change-time.
26  *
27  * Revision 1.19  1996/03/20  16:16:55  quinn
28  * Added diagnostic output
29  *
30  * Revision 1.18  1996/03/19  12:43:27  adam
31  * Bug fix: File update traversal didn't handle trailing slashes correctly.
32  * Bug fix: Update of sub directory groups wasn't handled correctly.
33  *
34  * Revision 1.17  1996/02/12  18:45:17  adam
35  * Changed naming of some functions.
36  *
37  * Revision 1.16  1996/02/05  12:30:02  adam
38  * Logging reduced a bit.
39  * The remaining running time is estimated during register merge.
40  *
41  * Revision 1.15  1995/12/07  17:38:48  adam
42  * Work locking mechanisms for concurrent updates/commit.
43  *
44  * Revision 1.14  1995/12/06  12:41:26  adam
45  * New command 'stat' for the index program.
46  * Filenames can be read from stdin by specifying '-'.
47  * Bug fix/enhancement of the transformation from terms to regular
48  * expressons in the search engine.
49  *
50  * Revision 1.13  1995/11/28  09:09:46  adam
51  * Zebra config renamed.
52  * Use setting 'recordId' to identify record now.
53  * Bug fix in recindex.c: rec_release_blocks was invokeded even
54  * though the blocks were already released.
55  * File traversal properly deletes records when needed.
56  *
57  * Revision 1.12  1995/11/24  11:31:37  adam
58  * Commands add & del read filenames from stdin if source directory is
59  * empty.
60  * Match criteria supports 'constant' strings.
61  *
62  * Revision 1.11  1995/11/22  17:19:19  adam
63  * Record management uses the bfile system.
64  *
65  * Revision 1.10  1995/11/21  15:01:16  adam
66  * New general match criteria implemented.
67  * New feature: document groups.
68  *
69  * Revision 1.9  1995/11/21  09:20:32  adam
70  * Yet more work on record match.
71  *
72  * Revision 1.8  1995/11/20  16:59:46  adam
73  * New update method: the 'old' keys are saved for each records.
74  *
75  * Revision 1.7  1995/11/20  11:56:28  adam
76  * Work on new traversal.
77  *
78  * Revision 1.6  1995/11/17  15:54:42  adam
79  * Started work on virtual directory structure.
80  *
81  * Revision 1.5  1995/10/17  18:02:09  adam
82  * New feature: databases. Implemented as prefix to words in dictionary.
83  *
84  * Revision 1.4  1995/09/28  09:19:46  adam
85  * xfree/xmalloc used everywhere.
86  * Extract/retrieve method seems to work for text records.
87  *
88  * Revision 1.3  1995/09/06  16:11:18  adam
89  * Option: only one word key per file.
90  *
91  * Revision 1.2  1995/09/04  12:33:43  adam
92  * Various cleanup. YAZ util used instead.
93  *
94  * Revision 1.1  1995/09/01  14:06:36  adam
95  * Split of work into more files.
96  *
97  */
98 #include <stdio.h>
99 #include <assert.h>
100 #include <unistd.h>
101 #include <dirent.h>
102 #include <sys/stat.h>
103 #include <sys/types.h>
104 #include <fcntl.h>
105 #include <ctype.h>
106 #include <time.h>
107
108 #include <alexutil.h>
109 #include "index.h"
110
111 static int repComp (const char *a, const char *b, size_t len)
112 {
113     if (!len)
114         return 0;
115     return memcmp (a, b, len);
116 }
117
118 static void repositoryExtractR (int deleteFlag, char *rep,
119                                 struct recordGroup *rGroup)
120 {
121     struct dir_entry *e;
122     int i;
123     size_t rep_len = strlen (rep);
124
125     e = dir_open (rep);
126     if (!e)
127         return;
128     logf (LOG_LOG, "Dir: %s", rep);
129     if (rep[rep_len-1] != '/')
130         rep[rep_len] = '/';
131     else
132         --rep_len;
133     for (i=0; e[i].name; i++)
134     {
135         strcpy (rep +rep_len+1, e[i].name);
136         switch (e[i].kind)
137         {
138         case dirs_file:
139             fileExtract (NULL, rep, rGroup, deleteFlag);
140             break;
141         case dirs_dir:
142             repositoryExtractR (deleteFlag, rep, rGroup);
143             break;
144         }
145     }
146     dir_free (&e);
147
148 }
149
150 static void fileDeleteR (struct dirs_info *di, struct dirs_entry *dst,
151                                const char *base, char *src,
152                                struct recordGroup *rGroup)
153 {
154     char tmppath[1024];
155     size_t src_len = strlen (src);
156
157     while (dst && !repComp (dst->path, src, src_len+1))
158     {
159         switch (dst->kind)
160         {
161         case dirs_file:
162             sprintf (tmppath, "%s%s", base, dst->path);
163             fileExtract (&dst->sysno, tmppath, rGroup, 1);
164              
165             strcpy (tmppath, dst->path);
166             dst = dirs_read (di); 
167             dirs_del (di, tmppath);
168             break;
169         case dirs_dir:
170             strcpy (tmppath, dst->path);
171             dst = dirs_read (di);
172             dirs_rmdir (di, tmppath);
173             break;
174         default:
175             dst = dirs_read (di);
176         }
177     }
178 }
179
180 static void fileUpdateR (struct dirs_info *di, struct dirs_entry *dst,
181                                const char *base, char *src, 
182                                struct recordGroup *rGroup)
183 {
184     struct dir_entry *e_src;
185     int i_src = 0;
186     static char tmppath[1024];
187     size_t src_len = strlen (src);
188
189     sprintf (tmppath, "%s%s", base, src);
190     e_src = dir_open (tmppath);
191     logf (LOG_LOG, "Dir: %s", tmppath);
192
193 #if 0
194     if (!dst || repComp (dst->path, src, src_len))
195 #else
196     if (!dst || strcmp (dst->path, src))
197 #endif
198     {
199         if (!e_src)
200             return;
201
202         if (src_len && src[src_len-1] != '/')
203         {
204             src[src_len] = '/';
205             src[++src_len] = '\0';
206         }
207         dirs_mkdir (di, src, 0);
208         if (dst && repComp (dst->path, src, src_len))
209             dst = NULL;
210     }
211     else if (!e_src)
212     {
213         strcpy (src, dst->path);
214         fileDeleteR (di, dst, base, src, rGroup);
215         return;
216     }
217     else
218     {
219         if (src_len && src[src_len-1] != '/')
220         {
221             src[src_len] = '/';
222             src[++src_len] = '\0';
223         }
224         dst = dirs_read (di); 
225     }
226     dir_sort (e_src);
227
228     while (1)
229     {
230         int sd;
231
232         if (dst && !repComp (dst->path, src, src_len))
233         {
234             if (e_src[i_src].name)
235             {
236                 logf (LOG_DEBUG, "dst=%s src=%s", dst->path + src_len,
237                       e_src[i_src].name);
238                 sd = strcmp (dst->path + src_len, e_src[i_src].name);
239             }
240             else
241                 sd = -1;
242         }
243         else if (e_src[i_src].name)
244             sd = 1;
245         else
246             break;
247         logf (LOG_DEBUG, "trav sd=%d", sd);
248         if (sd == 0)
249         {
250             strcpy (src + src_len, e_src[i_src].name);
251             sprintf (tmppath, "%s%s", base, src);
252             
253             switch (e_src[i_src].kind)
254             {
255             case dirs_file:
256                 if (e_src[i_src].mtime > dst->mtime)
257                 {
258                     if (fileExtract (&dst->sysno, tmppath, rGroup, 0))
259                     {
260                         dirs_add (di, src, dst->sysno, e_src[i_src].mtime);
261                     }
262                     logf (LOG_LOG, "old: %s", ctime (&dst->mtime));
263                     logf (LOG_LOG, "new: %s", ctime (&e_src[i_src].mtime));
264                 }
265                 dst = dirs_read (di);
266                 break;
267             case dirs_dir:
268                 fileUpdateR (di, dst, base, src, rGroup);
269                 dst = dirs_last (di);
270                 logf (LOG_DEBUG, "last is %s", dst ? dst->path : "null");
271                 break;
272             default:
273                 dst = dirs_read (di); 
274             }
275             i_src++;
276         }
277         else if (sd > 0)
278         {
279             SYSNO sysno = 0;
280             strcpy (src + src_len, e_src[i_src].name);
281             sprintf (tmppath, "%s%s", base, src);
282
283             switch (e_src[i_src].kind)
284             {
285             case dirs_file:
286                 if (fileExtract (&sysno, tmppath, rGroup, 0))
287                     dirs_add (di, src, sysno, e_src[i_src].mtime);            
288                 break;
289             case dirs_dir:
290                 fileUpdateR (di, dst, base, src, rGroup);
291                 if (dst)
292                     dst = dirs_last (di);
293                 break;
294             }
295             i_src++;
296         }
297         else  /* sd < 0 */
298         {
299             strcpy (src, dst->path);
300             sprintf (tmppath, "%s%s", base, dst->path);
301
302             switch (dst->kind)
303             {
304             case dirs_file:
305                 fileExtract (&dst->sysno, tmppath, rGroup, 1);
306                 dirs_del (di, dst->path);
307                 dst = dirs_read (di);
308                 break;
309             case dirs_dir:
310                 fileDeleteR (di, dst, base, src, rGroup);
311                 dst = dirs_last (di);
312             }
313         }
314     }
315     dir_free (&e_src);
316 }
317
318 static void groupRes (struct recordGroup *rGroup)
319 {
320     char resStr[256];
321     char gPrefix[256];
322
323     if (!rGroup->groupName || !*rGroup->groupName)
324         *gPrefix = '\0';
325     else
326         sprintf (gPrefix, "%s.", rGroup->groupName);
327
328     sprintf (resStr, "%srecordId", gPrefix);
329     rGroup->recordId = res_get (common_resource, resStr);
330 }
331
332 void repositoryShow (struct recordGroup *rGroup)
333 {
334     char src[1024];
335     int src_len;
336     struct dirs_entry *dst;
337     Dict dict;
338     struct dirs_info *di;
339     
340     if (!(dict = dict_open (FMATCH_DICT, 50, 1)))
341     {
342         logf (LOG_FATAL, "dict_open fail of %s", FMATCH_DICT);
343         exit (1);
344     }
345     
346     assert (rGroup->path);    
347     strcpy (src, rGroup->path);
348     src_len = strlen (src);
349     
350     if (src_len && src[src_len-1] != '/')
351     {
352         src[src_len] = '/';
353         src[++src_len] = '\0';
354     }
355     
356     di = dirs_open (dict, src);
357     
358     while ( (dst = dirs_read (di)) )
359         logf (LOG_LOG, "%s", dst->path);
360     dirs_free (&di);
361     dict_close (dict);
362 }
363
364 static void fileUpdate (Dict dict, struct recordGroup *rGroup,
365                         const char *path)
366 {
367     struct dirs_info *di;
368     struct stat sbuf;
369     char src[1024];
370     char dst[1024];
371     int src_len;
372
373     assert (path);
374     strcpy (src, path);
375     src_len = strlen (src);
376
377     stat (src, &sbuf);
378     if (S_ISREG(sbuf.st_mode))
379     {
380         struct dirs_entry *e_dst;
381         di = dirs_fopen (dict, src);
382
383         e_dst = dirs_read (di);
384         if (e_dst)
385         {
386             if (sbuf.st_mtime > e_dst->mtime)
387                 if (fileExtract (&e_dst->sysno, src, rGroup, 0))
388                     dirs_add (di, src, e_dst->sysno, sbuf.st_mtime);
389         }
390         else
391         {
392             SYSNO sysno = 0;
393             if (fileExtract (&sysno, src, rGroup, 0))
394                  dirs_add (di, src, sysno, sbuf.st_mtime);
395         }
396         dirs_free (&di);
397     }
398     else if (S_ISDIR(sbuf.st_mode))
399     {
400         if (src_len && src[src_len-1] != '/')
401         {
402             src[src_len] = '/';
403             src[++src_len] = '\0';
404         }
405         di = dirs_open (dict, src);
406         *dst = '\0';
407         fileUpdateR (di, dirs_read (di), src, dst, rGroup);
408         dirs_free (&di);
409     }
410     else
411     {
412         logf (LOG_WARN, "Cannot handle file %s", src);
413     }
414 }
415
416
417 static void repositoryExtract (int deleteFlag, struct recordGroup *rGroup,
418                                const char *path)
419 {
420     struct stat sbuf;
421     char src[1024];
422
423     assert (path);
424     strcpy (src, path);
425
426     stat (src, &sbuf);
427     if (S_ISREG(sbuf.st_mode))
428         fileExtract (NULL, src, rGroup, deleteFlag);
429     else if (S_ISDIR(sbuf.st_mode))
430         repositoryExtractR (deleteFlag, src, rGroup);
431     else
432         logf (LOG_WARN, "Cannot handle file %s", src);
433 }
434
435 static void repositoryExtractG (int deleteFlag, struct recordGroup *rGroup)
436 {
437     if (*rGroup->path == '\0' || !strcmp(rGroup->path, "-"))
438     {
439         char src[1024];
440
441         while (scanf ("%s", src) == 1)
442             repositoryExtract (deleteFlag, rGroup, src);
443     }
444     else
445         repositoryExtract (deleteFlag, rGroup, rGroup->path);
446 }
447
448 void repositoryUpdate (struct recordGroup *rGroup)
449 {
450     groupRes (rGroup);
451     assert (rGroup->path);
452     if (rGroup->recordId && !strcmp (rGroup->recordId, "file"))
453     {
454         Dict dict;
455         if (!(dict = dict_open (FMATCH_DICT, 50, 1)))
456         {
457             logf (LOG_FATAL, "dict_open fail of %s", FMATCH_DICT);
458             exit (1);
459         }
460         if (*rGroup->path == '\0' || !strcmp(rGroup->path, "-"))
461         {
462             char src[1024];
463             while (scanf ("%s", src) == 1)
464                 fileUpdate (dict, rGroup, src);
465         }
466         else
467             fileUpdate (dict, rGroup, rGroup->path);
468         dict_close (dict);
469     }
470     else 
471         repositoryExtractG (0, rGroup);
472 }
473
474 void repositoryDelete (struct recordGroup *rGroup)
475 {
476     repositoryExtractG (1, rGroup);
477 }
478