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