New update method: the 'old' keys are saved for each records.
[idzebra-moved-to-github.git] / index / trav.c
1 /*
2  * Copyright (C) 1994-1995, Index Data I/S 
3  * All rights reserved.
4  * Sebastian Hammer, Adam Dickmeiss
5  *
6  * $Log: trav.c,v $
7  * Revision 1.8  1995-11-20 16:59:46  adam
8  * New update method: the 'old' keys are saved for each records.
9  *
10  * Revision 1.7  1995/11/20  11:56:28  adam
11  * Work on new traversal.
12  *
13  * Revision 1.6  1995/11/17  15:54:42  adam
14  * Started work on virtual directory structure.
15  *
16  * Revision 1.5  1995/10/17  18:02:09  adam
17  * New feature: databases. Implemented as prefix to words in dictionary.
18  *
19  * Revision 1.4  1995/09/28  09:19:46  adam
20  * xfree/xmalloc used everywhere.
21  * Extract/retrieve method seems to work for text records.
22  *
23  * Revision 1.3  1995/09/06  16:11:18  adam
24  * Option: only one word key per file.
25  *
26  * Revision 1.2  1995/09/04  12:33:43  adam
27  * Various cleanup. YAZ util used instead.
28  *
29  * Revision 1.1  1995/09/01  14:06:36  adam
30  * Split of work into more files.
31  *
32  */
33 #include <stdio.h>
34 #include <assert.h>
35 #include <unistd.h>
36 #include <dirent.h>
37 #include <sys/stat.h>
38 #include <sys/types.h>
39 #include <fcntl.h>
40 #include <ctype.h>
41
42 #include <alexutil.h>
43 #include "index.h"
44
45 static void repository_extract_r (int cmd, char *rep, char *databaseName)
46 {
47     struct dir_entry *e;
48     int i;
49     struct stat fs;
50     size_t rep_len = strlen (rep);
51
52     e = dir_open (rep);
53     if (!e)
54         return;
55     if (rep[rep_len-1] != '/')
56         rep[rep_len] = '/';
57     else
58         --rep_len;
59     for (i=0; e[i].name; i++)
60     {
61         strcpy (rep +rep_len+1, e[i].name);
62         stat (rep, &fs);
63         switch (fs.st_mode & S_IFMT)
64         {
65         case S_IFREG:
66             file_extract (cmd, rep, rep, databaseName);
67             break;
68         case S_IFDIR:
69             repository_extract_r (cmd, rep, databaseName);
70             break;
71         }
72     }
73     dir_free (&e);
74 }
75
76 void copy_file (const char *dst, const char *src)
77 {
78     int d_fd = open (dst, O_WRONLY|O_CREAT, 0666);
79     int s_fd = open (src, O_RDONLY);
80     char *buf;
81     size_t i, r, w;
82
83     if (d_fd == -1)
84     {
85         logf (LOG_FATAL|LOG_ERRNO, "Cannot create %s", dst);
86         exit (1);
87     }
88     if (s_fd == -1)
89     {
90         logf (LOG_FATAL|LOG_ERRNO, "Cannot open %s", src);
91         exit (1);
92     }
93     buf = xmalloc (4096);
94     while ((r=read (s_fd, buf, 4096))>0)
95         for (w = 0; w < r; w += i)
96         {
97             i = write (d_fd, buf + w, r - w);
98             if (i == -1)
99             {
100                 logf (LOG_FATAL|LOG_ERRNO, "write");
101                 exit (1);
102             }
103         }
104     if (r)
105     {
106         logf (LOG_FATAL|LOG_ERRNO, "read");
107         exit (1);
108     }
109     xfree (buf);
110     close (d_fd);
111     close (s_fd);
112 }
113
114 void del_file (const char *dst)
115 {
116     unlink (dst);
117 }
118
119 void del_dir (const char *dst)
120 {
121     logf (LOG_DEBUG, "rmdir of %s", dst);
122     if (rmdir (dst) == -1)
123         logf (LOG_ERRNO|LOG_WARN, "rmdir");
124 }
125
126 void repository_update_r (int cmd, char *dst, char *src, char *databaseName);
127
128 void repository_add_tree (int cmd, char *dst, char *src, char *databaseName)
129 {
130     mkdir (dst, 0755);
131     repository_update_r (cmd, dst, src, databaseName);
132 }
133
134 void repository_del_tree (int cmd, char *dst, char *src, char *databaseName)
135 {
136     size_t dst_len = strlen (dst);
137     size_t src_len = strlen (src);
138     struct dir_entry *e_dst;
139     int i_dst = 0;
140     struct stat fs_dst;
141
142     e_dst = dir_open (dst);
143
144     dir_sort (e_dst);
145
146     if (src[src_len-1] != '/')
147         src[src_len] = '/';
148     else
149         --src_len;
150     if (dst[dst_len-1] != '/')
151         dst[dst_len] = '/';
152     else
153         --dst_len;
154     while (e_dst[i_dst].name)
155     {
156         strcpy (dst +dst_len+1, e_dst[i_dst].name);
157         strcpy (src +src_len+1, e_dst[i_dst].name);
158         
159         stat (dst, &fs_dst);
160         switch (fs_dst.st_mode & S_IFMT)
161         {
162         case S_IFREG:
163             file_extract ('d', dst, dst, databaseName);
164             del_file (dst);
165             break;
166         case S_IFDIR:
167             repository_del_tree (cmd, dst, src, databaseName);
168             break;
169         }
170         i_dst++;
171     }
172     dir_free (&e_dst);
173     if (dst_len > 0)
174     {
175         dst[dst_len] = '\0';
176         del_dir (dst);
177     }
178 }
179
180 void repository_update_r (int cmd, char *dst, char *src, char *databaseName)
181 {
182     struct dir_entry *e_dst, *e_src;
183     int i_dst = 0, i_src = 0;
184     struct stat fs_dst, fs_src;
185     size_t dst_len = strlen (dst);
186     size_t src_len = strlen (src);
187
188     e_dst = dir_open (dst);
189     e_src = dir_open (src);
190
191     if (!e_dst && !e_src)
192         return;
193     if (!e_dst)
194     {
195         dir_free (&e_src);
196         repository_add_tree (cmd, dst, src, databaseName);
197         return;
198     }
199     else if (!e_src)
200     {
201         dir_free (&e_dst);
202         repository_del_tree (cmd, dst, src, databaseName);
203         return;
204     }
205
206     dir_sort (e_src);
207     dir_sort (e_dst);
208
209     if (src[src_len-1] != '/')
210         src[src_len] = '/';
211     else
212         --src_len;
213     if (dst[dst_len-1] != '/')
214         dst[dst_len] = '/';
215     else
216         --dst_len;
217     while (e_dst[i_dst].name || e_src[i_src].name)
218     {
219         int sd;
220
221         if (e_dst[i_dst].name && e_src[i_src].name)
222             sd = strcmp (e_dst[i_dst].name, e_src[i_src].name);
223         else if (e_src[i_src].name)
224             sd = 1;
225         else
226             sd = -1;
227                 
228         if (sd == 0)
229         {
230             strcpy (dst +dst_len+1, e_dst[i_dst].name);
231             strcpy (src +src_len+1, e_src[i_src].name);
232             
233             /* check type, date, length */
234
235             stat (dst, &fs_dst);
236             stat (src, &fs_src);
237                 
238             switch (fs_dst.st_mode & S_IFMT)
239             {
240             case S_IFREG:
241                 if (fs_src.st_ctime > fs_dst.st_ctime)
242                 {
243                     file_extract ('d', dst, dst, databaseName);
244                     file_extract ('a', src, dst, databaseName);
245                     copy_file (dst, src);
246                 }
247                 break;
248             case S_IFDIR:
249                 repository_update_r (cmd, dst, src, databaseName);
250                 break;
251             }
252             i_src++;
253             i_dst++;
254         }
255         else if (sd > 0)
256         {
257             strcpy (dst +dst_len+1, e_src[i_src].name);
258             strcpy (src +src_len+1, e_src[i_src].name);
259             
260             stat (src, &fs_src);
261             switch (fs_src.st_mode & S_IFMT)
262             {
263             case S_IFREG:
264                 file_extract ('a', src, dst, databaseName);
265                 copy_file (dst, src);
266                 break;
267             case S_IFDIR:
268                 repository_add_tree (cmd, dst, src, databaseName);
269                 break;
270             }
271             i_src++;
272         }
273         else 
274         {
275             strcpy (dst +dst_len+1, e_dst[i_dst].name);
276             strcpy (src +src_len+1, e_dst[i_dst].name);
277             
278             stat (dst, &fs_dst);
279             switch (fs_dst.st_mode & S_IFMT)
280             {
281             case S_IFREG:
282                 file_extract ('d', dst, dst, databaseName);
283                 del_file (dst);
284                 break;
285             case S_IFDIR:
286                 repository_del_tree (cmd, dst, src, databaseName);
287                 break;
288             }
289             i_dst++;
290         }
291     }
292     dir_free (&e_dst);
293     dir_free (&e_src);
294 }
295
296 static int repComp (const char *a, const char *b, size_t len)
297 {
298     if (!len)
299         return 0;
300     return memcmp (a, b, len);
301 }
302
303 static void repositoryUpdateR (struct dirs_info *di, struct dirs_entry *dst,
304                                const char *base, char *src, char *databaseName)
305 {
306     struct dir_entry *e_src;
307     int i_src = 0;
308     static char tmppath[256];
309     size_t src_len = strlen (src);
310
311     sprintf (tmppath, "%s%s", base, src);
312     e_src = dir_open (tmppath);
313
314 #if 1
315     if (!dst || repComp (dst->path, src, src_len))
316 #else
317     if (!dst || strcmp (dst->path, src))
318 #endif
319     {
320         if (!e_src)
321             return;
322 #if 1
323         if (src_len && src[src_len-1] == '/')
324             --src_len;
325         else
326             src[src_len] = '/';
327         src[src_len+1] = '\0';
328 #endif
329         dirs_mkdir (di, src, 0);
330         dst = NULL;
331     }
332     else if (!e_src)
333     {
334         /* delete tree dst */
335         return;
336     }
337     else
338     {
339 #if 1
340         if (src_len && src[src_len-1] == '/')
341             --src_len;
342         else
343             src[src_len] = '/';
344         src[src_len+1] = '\0';
345 #endif
346         dst = dirs_read (di); 
347     }
348     dir_sort (e_src);
349
350     while (1)
351     {
352         int sd;
353
354         if (dst && !repComp (dst->path, src, src_len+1))
355         {
356             if (e_src[i_src].name)
357             {
358                 logf (LOG_DEBUG, "dst=%s src=%s", dst->path + src_len+1, 
359                       e_src[i_src].name);
360                 sd = strcmp (dst->path + src_len+1, e_src[i_src].name);
361             }
362             else
363                 sd = -1;
364         }
365         else if (e_src[i_src].name)
366             sd = 1;
367         else
368             break;
369         logf (LOG_DEBUG, "trav sd=%d", sd);
370         if (sd == 0)
371         {
372             strcpy (src + src_len+1, e_src[i_src].name);
373             sprintf (tmppath, "%s%s", base, src);
374             
375             switch (e_src[i_src].kind)
376             {
377             case dirs_file:
378                 if (e_src[i_src].ctime > dst->ctime)
379                 {
380                     if (fileExtract (&dst->sysno, tmppath, databaseName, 0))
381                         dirs_add (di, src, dst->sysno, e_src[i_src].ctime);
382                 }
383                 dst = dirs_read (di);
384                 break;
385             case dirs_dir:
386                 repositoryUpdateR (di, dst, base, src, databaseName);
387                 dst = dirs_last (di);
388                 logf (LOG_DEBUG, "last is %s", dst ? dst->path : "null");
389                 break;
390             default:
391                 dst = dirs_read (di); 
392             }
393             i_src++;
394         }
395         else if (sd > 0)
396         {
397             SYSNO sysno = 0;
398             strcpy (src + src_len+1, e_src[i_src].name);
399             sprintf (tmppath, "%s%s", base, src);
400
401             switch (e_src[i_src].kind)
402             {
403             case dirs_file:
404                 if (fileExtract (&sysno, tmppath, databaseName, 0))
405                     dirs_add (di, src, sysno, e_src[i_src].ctime);            
406                 break;
407             case dirs_dir:
408                 repositoryUpdateR (di, dst, base, src, databaseName);
409                 if (dst)
410                     dst = dirs_last (di);
411                 break;
412             }
413             i_src++;
414         }
415         else  /* sd < 0 */
416         {
417             assert (0);
418         }
419     }
420     dir_free (&e_src);
421 }
422
423 void repositoryUpdate (const char *path, char *databaseName)
424 {
425     struct dirs_info *di;
426     char src[256];
427     Dict dict;
428
429     dict = dict_open ("repdict", 40, 1);
430     
431     di = dirs_open (dict, path);
432     strcpy (src, "");
433     repositoryUpdateR (di, dirs_read (di), path, src, databaseName);
434     dirs_free (&di);
435
436     dict_close (dict);
437 }
438
439 void repository (int cmd, const char *rep, const char *base_path,
440                  char *databaseName)
441 {
442     char rep_tmp1[2048];
443     char rep_tmp2[2048];
444
445     strcpy (rep_tmp1, rep);
446     if (base_path)
447     {
448         strcpy (rep_tmp2, base_path);
449         repository_update_r (cmd, rep_tmp2, rep_tmp1, databaseName);
450     }
451     else
452         repository_extract_r (cmd, rep_tmp1, databaseName);
453 }
454