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