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