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