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