Add:Core:Added support for checking a files version
[navit-package] / navit / file.c
1 /**
2  * Navit, a modular navigation system.
3  * Copyright (C) 2005-2008 Navit Team
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License
7  * version 2 as published by the Free Software Foundation.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the
16  * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17  * Boston, MA  02110-1301, USA.
18  */
19
20 #define _FILE_OFFSET_BITS 64
21 #define _LARGEFILE_SOURCE
22 #define _LARGEFILE64_SOURCE
23 #include <unistd.h>
24 #include <string.h>
25 #include <fcntl.h>
26 #include <sys/stat.h>
27 #include <sys/mman.h>
28 #include <dirent.h>
29 #include <stdio.h>
30 #include <wordexp.h>
31 #include <glib.h>
32 #include <zlib.h>
33 #include "debug.h"
34 #include "cache.h"
35 #include "file.h"
36 #include "config.h"
37
38 #ifndef O_LARGEFILE
39 #define O_LARGEFILE 0
40 #endif
41
42 #ifndef O_BINARY
43 #define O_BINARY 0
44 #endif
45
46 static GHashTable *file_name_hash;
47 static int file_name_id;
48 static struct cache *file_cache;
49
50 struct file_cache_id {
51         long long offset;
52         int size;
53         int file_name_id;
54         int method;
55 };
56
57 struct file *
58 file_create(char *name)
59 {
60         struct stat stat;
61         struct file *file= g_new0(struct file,1);
62
63         if (! file)
64                 return file;
65         file->fd=open(name, O_RDONLY|O_LARGEFILE | O_BINARY);
66         if (file->fd < 0) {
67                 g_free(file);
68                 return NULL;
69         }
70         fstat(file->fd, &stat);
71         file->size=stat.st_size;
72         file->name = g_strdup(name);
73         dbg_assert(file != NULL);
74         return file;
75 }
76
77 int file_is_dir(char *name)
78 {
79         struct stat buf;
80         if (! stat(name, &buf)) {
81                 return S_ISDIR(buf.st_mode);
82         }
83         return 0;
84
85 }
86
87 int file_mkdir(char *name, int pflag)
88 {
89         char buffer[strlen(name)+1];
90         int ret;
91         char *next;
92         dbg(1,"enter %s %d\n",name,pflag);
93         if (!pflag) {
94                 if (file_is_dir(name))
95                         return 0;
96                 return mkdir(name, 0777);
97         }
98         strcpy(buffer, name);
99         next=buffer;
100         while ((next=strchr(next, '/'))) {
101                 *next='\0';
102                 if (*buffer) {
103                         ret=file_mkdir(buffer, 0);
104                         if (ret)
105                                 return ret;
106                 }
107                 *next++='/';
108         }
109         if (pflag == 2)
110                 return 0;
111         return file_mkdir(buffer, 0);
112 }
113
114 int
115 file_mmap(struct file *file)
116 {
117 #if defined(_WIN32) || defined(__CEGCC__)
118     file->begin = (char*)mmap_readonly_win32( file->name, &file->map_handle, &file->map_file );
119 #else
120         file->begin=mmap(NULL, file->size, PROT_READ|PROT_WRITE, MAP_PRIVATE, file->fd, 0);
121         dbg_assert(file->begin != NULL);
122         if (file->begin == (void *)0xffffffff) {
123                 perror("mmap");
124                 return 0;
125         }
126 #endif
127         dbg_assert(file->begin != (void *)0xffffffff);
128         file->end=file->begin+file->size;
129
130         return 1;
131 }
132
133 unsigned char *
134 file_data_read(struct file *file, long long offset, int size)
135 {
136         void *ret;
137         if (file->begin)
138                 return file->begin+offset;
139         if (file_cache) {
140                 struct file_cache_id id={offset,size,file->name_id,0};
141                 ret=cache_lookup(file_cache,&id); 
142                 if (ret)
143                         return ret;
144                 ret=cache_insert_new(file_cache,&id,size);
145         } else
146                 ret=g_malloc(size);
147         lseek(file->fd, offset, SEEK_SET);
148         if (read(file->fd, ret, size) != size) {
149                 file_data_free(file, ret);
150                 ret=NULL;
151         }
152         return ret;
153
154 }
155
156 static int
157 uncompress_int(Bytef *dest, uLongf *destLen, const Bytef *source, uLong sourceLen)
158 {
159         z_stream stream;
160         int err;
161
162         stream.next_in = (Bytef*)source;
163         stream.avail_in = (uInt)sourceLen;
164         stream.next_out = dest;
165         stream.avail_out = (uInt)*destLen;
166
167         stream.zalloc = (alloc_func)0;
168         stream.zfree = (free_func)0;
169
170         err = inflateInit2(&stream, -MAX_WBITS);
171         if (err != Z_OK) return err;
172
173         err = inflate(&stream, Z_FINISH);
174         if (err != Z_STREAM_END) {
175         inflateEnd(&stream);
176         if (err == Z_NEED_DICT || (err == Z_BUF_ERROR && stream.avail_in == 0))
177                 return Z_DATA_ERROR;
178                 return err;
179         }
180         *destLen = stream.total_out;
181
182         err = inflateEnd(&stream);
183         return err;
184 }
185
186 unsigned char *
187 file_data_read_compressed(struct file *file, long long offset, int size, int size_uncomp)
188 {
189         void *ret;
190         char buffer[size];
191         uLongf destLen=size_uncomp;
192
193         if (file_cache) {
194                 struct file_cache_id id={offset,size,file->name_id,1};
195                 ret=cache_lookup(file_cache,&id); 
196                 if (ret)
197                         return ret;
198                 ret=cache_insert_new(file_cache,&id,size_uncomp);
199         } else 
200                 ret=g_malloc(size_uncomp);
201         lseek(file->fd, offset, SEEK_SET);
202         if (read(file->fd, buffer, size) != size) {
203                 g_free(ret);
204                 ret=NULL;
205         } else {
206                 if (uncompress_int(ret, &destLen, (Bytef *)buffer, size) != Z_OK) {
207                         dbg(0,"uncompress failed\n");
208                         g_free(ret);
209                         ret=NULL;
210                 }
211         }
212         return ret;
213 }
214
215 void
216 file_data_free(struct file *file, unsigned char *data)
217 {
218         if (file->begin && data >= file->begin && data < file->end)
219                 return;
220         if (file_cache) {
221                 cache_entry_destroy(file_cache, data);
222         } else
223                 g_free(data);
224 }
225
226 int
227 file_exists(char *name)
228 {
229         struct stat buf;
230         if (! stat(name, &buf))
231                 return 1;
232         return 0;
233 }
234
235 void
236 file_remap_readonly(struct file *f)
237 {
238 #if defined(_WIN32) || defined(__CEGCC__)
239 #else
240         void *begin;
241         munmap(f->begin, f->size);
242         begin=mmap(f->begin, f->size, PROT_READ, MAP_PRIVATE, f->fd, 0);
243         if (f->begin != begin)
244                 printf("remap failed\n");
245 #endif
246 }
247
248 void
249 file_unmap(struct file *f)
250 {
251 #if defined(_WIN32) || defined(__CEGCC__)
252     mmap_unmap_win32( f->begin, f->map_handle , f->map_file );
253 #else
254         munmap(f->begin, f->size);
255 #endif
256 }
257
258 void *
259 file_opendir(char *dir)
260 {
261         return opendir(dir);
262 }
263
264 char *
265 file_readdir(void *hnd)
266 {
267         struct dirent *ent;
268
269         ent=readdir(hnd);
270         if (! ent)
271                 return NULL;
272         return ent->d_name;
273 }
274
275 void
276 file_closedir(void *hnd)
277 {
278         closedir(hnd);
279 }
280
281 struct file *
282 file_create_caseinsensitive(char *name)
283 {
284         char dirname[strlen(name)+1];
285         char *filename;
286         char *p;
287         void *d;
288         struct file *ret;
289
290         ret=file_create(name);
291         if (ret)
292                 return ret;
293
294         strcpy(dirname, name);
295         p=dirname+strlen(name);
296         while (p > dirname) {
297                 if (*p == '/')
298                         break;
299                 p--;
300         }
301         *p=0;
302         d=file_opendir(dirname);
303         if (d) {
304                 *p++='/';
305                 while ((filename=file_readdir(d))) {
306                         if (!strcasecmp(filename, p)) {
307                                 strcpy(p, filename);
308                                 ret=file_create(dirname);
309                                 if (ret)
310                                         break;
311                         }
312                 }
313                 file_closedir(d);
314         }
315         return ret;
316 }
317
318 void
319 file_destroy(struct file *f)
320 {
321         close(f->fd);
322
323     if ( f->begin != NULL )
324     {
325         file_unmap( f );
326     }
327
328         g_free(f->name);
329         g_free(f);
330 }
331
332 struct file_wordexp {
333         int err;
334         wordexp_t we;
335 };
336
337 struct file_wordexp *
338 file_wordexp_new(const char *pattern)
339 {
340         struct file_wordexp *ret=g_new0(struct file_wordexp, 1);
341
342         ret->err=wordexp(pattern, &ret->we, 0);
343         if (ret->err)
344                 dbg(0,"wordexp('%s') returned %d\n", pattern, ret->err);
345         return ret;
346 }
347
348 int
349 file_wordexp_get_count(struct file_wordexp *wexp)
350 {
351         return wexp->we.we_wordc;
352 }
353
354 char **
355 file_wordexp_get_array(struct file_wordexp *wexp)
356 {
357         return wexp->we.we_wordv;
358 }
359
360 void
361 file_wordexp_destroy(struct file_wordexp *wexp)
362 {
363         if (! wexp->err)
364                 wordfree(&wexp->we);
365         g_free(wexp);
366 }
367
368
369 int
370 file_get_param(struct file *file, struct param_list *param, int count)
371 {
372         int i=count;
373         param_add_string("Filename", file->name, &param, &count);
374         param_add_hex("Size", file->size, &param, &count);
375         return i-count;
376 }
377
378 int
379 file_version(struct file *file, int byname)
380 {
381 #ifndef __CEGCC__
382         struct stat st;
383         int error;
384         if (byname)
385                 error=stat(file->name, &st);
386         else
387                 error=fstat(file->fd, &st);
388         if (error || !file->version || file->mtime != st.st_mtime || file->ctime != st.st_ctime) {
389                 file->mtime=st.st_mtime;
390                 file->ctime=st.st_ctime;
391                 file->version++;
392                 dbg(0,"%s now version %d\n", file->name, file->version);
393         }
394         return file->version;
395 #else
396         return 0;
397 #endif
398 }
399
400 void
401 file_init(void)
402 {
403 #if 0
404         file_name_hash=g_hash_table_new(g_str_hash, g_str_equal);
405         file_cache=cache_new(sizeof(struct file_cache_id), 2*1024*1024);
406 #endif
407 }
408