Moved file locking utilities from index/lockutil.c to util/flock.c
[idzebra-moved-to-github.git] / index / trav.c
1 /* $Id: trav.c,v 1.49 2005-06-14 20:28:54 adam Exp $
2    Copyright (C) 1995-2005
3    Index Data ApS
4
5 This file is part of the Zebra server.
6
7 Zebra is free software; you can redistribute it and/or modify it under
8 the terms of the GNU General Public License as published by the Free
9 Software Foundation; either version 2, or (at your option) any later
10 version.
11
12 Zebra is distributed in the hope that it will be useful, but WITHOUT ANY
13 WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
15 for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with Zebra; see the file LICENSE.zebra.  If not, write to the
19 Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
20 02111-1307, USA.
21 */
22
23 #include <stdio.h>
24 #include <assert.h>
25 #include <sys/types.h>
26 #ifdef WIN32
27 #include <io.h>
28 #define S_ISREG(x) (x & _S_IFREG)
29 #define S_ISDIR(x) (x & _S_IFDIR)
30 #endif
31 #if HAVE_UNISTD_H
32 #include <unistd.h>
33 #endif
34 #include <direntz.h>
35 #include <fcntl.h>
36 #include <time.h>
37
38 #include "index.h"
39
40 static int repComp (const char *a, const char *b, size_t len)
41 {
42     if (!len)
43         return 0;
44     return memcmp (a, b, len);
45 }
46
47 static void repositoryExtractR (ZebraHandle zh, int deleteFlag, char *rep,
48                                 int level)
49 {
50     struct dir_entry *e;
51     int i;
52     size_t rep_len = strlen (rep);
53
54     e = dir_open (rep, zh->path_reg, zh->m_follow_links);
55     if (!e)
56         return;
57     yaz_log (YLOG_LOG, "dir %s", rep);
58     if (rep[rep_len-1] != '/')
59         rep[rep_len] = '/';
60     else
61         --rep_len;
62     
63     for (i=0; e[i].name; i++)
64     {
65         char *ecp;
66         strcpy (rep +rep_len+1, e[i].name);
67         if ((ecp = strrchr (e[i].name, '/')))
68             *ecp = '\0';
69
70         switch (e[i].kind)
71         {
72         case dirs_file:
73             fileExtract (zh, NULL, rep, deleteFlag);
74             break;
75         case dirs_dir:
76             repositoryExtractR (zh, deleteFlag, rep, level+1);
77             break;
78         }
79     }
80     dir_free (&e);
81
82 }
83
84 static void fileDeleteR (ZebraHandle zh,
85                          struct dirs_info *di, struct dirs_entry *dst,
86                          const char *base, char *src)
87 {
88     char tmppath[1024];
89     size_t src_len = strlen (src);
90
91     while (dst && !repComp (dst->path, src, src_len+1))
92     {
93         switch (dst->kind)
94         {
95         case dirs_file:
96             sprintf (tmppath, "%s%s", base, dst->path);
97             fileExtract (zh, &dst->sysno, tmppath, 1);
98              
99             strcpy (tmppath, dst->path);
100             dst = dirs_read (di); 
101             dirs_del (di, tmppath);
102             break;
103         case dirs_dir:
104             strcpy (tmppath, dst->path);
105             dst = dirs_read (di);
106             dirs_rmdir (di, tmppath);
107             break;
108         default:
109             dst = dirs_read (di);
110         }
111     }
112 }
113
114 static void fileUpdateR (ZebraHandle zh,
115                          struct dirs_info *di, struct dirs_entry *dst,
116                          const char *base, char *src, 
117                          int level)
118 {
119     struct dir_entry *e_src;
120     int i_src = 0;
121     static char tmppath[1024];
122     size_t src_len = strlen (src);
123
124     sprintf (tmppath, "%s%s", base, src);
125     e_src = dir_open (tmppath, zh->path_reg, zh->m_follow_links);
126     yaz_log (YLOG_LOG, "dir %s", tmppath);
127
128 #if 0
129     if (!dst || repComp (dst->path, src, src_len))
130 #else
131     if (!dst || strcmp (dst->path, src))
132 #endif
133     {
134         if (!e_src)
135             return;
136
137         if (src_len && src[src_len-1] != '/')
138         {
139             src[src_len] = '/';
140             src[++src_len] = '\0';
141         }
142         dirs_mkdir (di, src, 0);
143         if (dst && repComp (dst->path, src, src_len))
144             dst = NULL;
145     }
146     else if (!e_src)
147     {
148         strcpy (src, dst->path);
149         fileDeleteR (zh, di, dst, base, src);
150         return;
151     }
152     else
153     {
154         if (src_len && src[src_len-1] != '/')
155         {
156             src[src_len] = '/';
157             src[++src_len] = '\0';
158         }
159         dst = dirs_read (di); 
160     }
161     dir_sort (e_src);
162
163     while (1)
164     {
165         int sd;
166
167         if (dst && !repComp (dst->path, src, src_len))
168         {
169             if (e_src[i_src].name)
170             {
171                 yaz_log (YLOG_DEBUG, "dst=%s src=%s", dst->path + src_len,
172                       e_src[i_src].name);
173                 sd = strcmp (dst->path + src_len, e_src[i_src].name);
174             }
175             else
176                 sd = -1;
177         }
178         else if (e_src[i_src].name)
179             sd = 1;
180         else
181             break;
182         yaz_log (YLOG_DEBUG, "trav sd=%d", sd);
183
184         if (sd == 0)
185         {
186             strcpy (src + src_len, e_src[i_src].name);
187             sprintf (tmppath, "%s%s", base, src);
188             
189             switch (e_src[i_src].kind)
190             {
191             case dirs_file:
192                 if (e_src[i_src].mtime > dst->mtime)
193                 {
194                     if (fileExtract (zh, &dst->sysno, tmppath, 0))
195                     {
196                         dirs_add (di, src, dst->sysno, e_src[i_src].mtime);
197                     }
198                     yaz_log (YLOG_DEBUG, "old: %s", ctime (&dst->mtime));
199                     yaz_log (YLOG_DEBUG, "new: %s", ctime (&e_src[i_src].mtime));
200                 }
201                 dst = dirs_read (di);
202                 break;
203             case dirs_dir:
204                 fileUpdateR (zh, di, dst, base, src, level+1);
205                 dst = dirs_last (di);
206                 yaz_log (YLOG_DEBUG, "last is %s", dst ? dst->path : "null");
207                 break;
208             default:
209                 dst = dirs_read (di); 
210             }
211             i_src++;
212         }
213         else if (sd > 0)
214         {
215             SYSNO sysno = 0;
216             strcpy (src + src_len, e_src[i_src].name);
217             sprintf (tmppath, "%s%s", base, src);
218
219             switch (e_src[i_src].kind)
220             {
221             case dirs_file:
222                 if (fileExtract (zh, &sysno, tmppath, 0))
223                     dirs_add (di, src, sysno, e_src[i_src].mtime);            
224                 break;
225             case dirs_dir:
226                 fileUpdateR (zh, di, dst, base, src, level+1);
227                 if (dst)
228                     dst = dirs_last (di);
229                 break;
230             }
231             i_src++;
232         }
233         else  /* sd < 0 */
234         {
235             strcpy (src, dst->path);
236             sprintf (tmppath, "%s%s", base, dst->path);
237
238             switch (dst->kind)
239             {
240             case dirs_file:
241                 fileExtract (zh, &dst->sysno, tmppath, 1);
242                 dirs_del (di, dst->path);
243                 dst = dirs_read (di);
244                 break;
245             case dirs_dir:
246                 fileDeleteR (zh, di, dst, base, src);
247                 dst = dirs_last (di);
248             }
249         }
250     }
251     dir_free (&e_src);
252 }
253
254 void repositoryShow (ZebraHandle zh, const char *path)
255 {
256     char src[1024];
257     int src_len;
258     struct dirs_entry *dst;
259     Dict dict;
260     struct dirs_info *di;
261
262     if (!(dict = dict_open_res (zh->reg->bfs, FMATCH_DICT, 50, 0, 0, zh->res)))
263     {
264         yaz_log (YLOG_FATAL, "dict_open fail of %s", FMATCH_DICT);
265         return;
266     }
267     
268     strncpy(src, path, sizeof(src)-1);
269     src[sizeof(src)-1]='\0';
270     src_len = strlen (src);
271     
272     if (src_len && src[src_len-1] != '/')
273     {
274         src[src_len] = '/';
275         src[++src_len] = '\0';
276     }
277     
278     di = dirs_open (dict, src, zh->m_flag_rw);
279     
280     while ( (dst = dirs_read (di)) )
281         yaz_log (YLOG_LOG, "%s", dst->path);
282     dirs_free (&di);
283     dict_close (dict);
284 }
285
286 static void fileUpdate (ZebraHandle zh, Dict dict, const char *path)
287 {
288     struct dirs_info *di;
289     struct stat sbuf;
290     char src[1024];
291     char dst[1024];
292     int src_len, ret;
293
294     assert (path);
295
296     if (zh->path_reg && !yaz_is_abspath(path))
297     {
298         strcpy (src, zh->path_reg);
299         strcat (src, "/");
300     }
301     else
302         *src = '\0';
303     strcat (src, path);
304     ret = zebra_file_stat (src, &sbuf, zh->m_follow_links);
305
306     strcpy (src, path);
307     src_len = strlen (src);
308
309     if (ret == -1)
310     {
311         yaz_log (YLOG_WARN|YLOG_ERRNO, "Cannot access path %s", src);
312     } 
313     else if (S_ISREG(sbuf.st_mode))
314     {
315         struct dirs_entry *e_dst;
316         di = dirs_fopen (dict, src, zh->m_flag_rw);
317
318         e_dst = dirs_read (di);
319         if (e_dst)
320         {
321             if (sbuf.st_mtime > e_dst->mtime)
322                 if (fileExtract (zh, &e_dst->sysno, src, 0))
323                     dirs_add (di, src, e_dst->sysno, sbuf.st_mtime);
324         }
325         else
326         {
327             SYSNO sysno = 0;
328             if (fileExtract (zh, &sysno, src, 0))
329                  dirs_add (di, src, sysno, sbuf.st_mtime);
330         }
331         dirs_free (&di);
332     }
333     else if (S_ISDIR(sbuf.st_mode))
334     {
335         if (src_len && src[src_len-1] != '/')
336         {
337             src[src_len] = '/';
338             src[++src_len] = '\0';
339         }
340         di = dirs_open (dict, src, zh->m_flag_rw);
341         *dst = '\0';
342         fileUpdateR (zh, di, dirs_read (di), src, dst, 0);
343         dirs_free (&di);
344     }
345     else
346     {
347         yaz_log (YLOG_WARN, "Skipping path %s", src);
348     }
349 }
350
351 static void repositoryExtract (ZebraHandle zh,
352                                int deleteFlag, const char *path)
353 {
354     struct stat sbuf;
355     char src[1024];
356     int ret;
357
358     assert (path);
359
360     if (zh->path_reg && !yaz_is_abspath(path))
361     {
362         strcpy (src, zh->path_reg);
363         strcat (src, "/");
364     }
365     else
366         *src = '\0';
367     strcat (src, path);
368     ret = zebra_file_stat (src, &sbuf, zh->m_follow_links);
369
370     strcpy (src, path);
371
372     if (ret == -1)
373         yaz_log (YLOG_WARN|YLOG_ERRNO, "Cannot access path %s", src);
374     else if (S_ISREG(sbuf.st_mode))
375         fileExtract (zh, NULL, src, deleteFlag);
376     else if (S_ISDIR(sbuf.st_mode))
377         repositoryExtractR (zh, deleteFlag, src, 0);
378     else
379         yaz_log (YLOG_WARN, "Skipping path %s", src);
380 }
381
382 static void repositoryExtractG (ZebraHandle zh, const char *path, 
383                                 int deleteFlag)
384 {
385     if (!strcmp(path, "") || !strcmp(path, "-"))
386     {
387         char src[1024];
388         
389         while (scanf ("%1020s", src) == 1)
390             repositoryExtract (zh, deleteFlag, src);
391     }
392     else
393         repositoryExtract (zh, deleteFlag, path);
394 }
395
396 #if 0
397 static int dump_file_dict_func(char *name, const char *info, int pos,
398                                 void *client)
399 {
400     yaz_log(YLOG_LOG, "%s", name);
401     return 0;
402 }
403 static void dump_file_dict(Dict dict)
404 {
405     int before = 10;
406     int after = 1000;
407     char term[1000];
408     
409     strcpy(term, "0");
410     dict_scan (dict, term, &before, &after, 0, dump_file_dict_func);
411 }
412 #endif
413
414 void repositoryUpdate (ZebraHandle zh, const char *path)
415 {
416     assert (path);
417     if (zh->m_record_id && !strcmp (zh->m_record_id, "file"))
418     {
419         Dict dict;
420         if (!(dict = dict_open_res (zh->reg->bfs, FMATCH_DICT, 50,
421                                     zh->m_flag_rw, 0, zh->res)))
422         {
423             yaz_log (YLOG_FATAL, "dict_open fail of %s", FMATCH_DICT);
424             return ;
425         }
426         if (!strcmp(path, "") || !strcmp(path, "-"))
427         {
428             char src[1024];
429             while (scanf ("%s", src) == 1)
430                 fileUpdate (zh, dict, src);
431         }
432         else
433             fileUpdate (zh, dict, path);
434 #if 0
435         dump_file_dict(dict);
436 #endif
437         dict_close (dict);
438         
439     }
440     else 
441         repositoryExtractG (zh, path, 0);
442 }
443
444 void repositoryDelete (ZebraHandle zh, const char *path)
445 {
446     assert (path);
447     repositoryExtractG (zh, path, 1);
448 }
449