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