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