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