Add:Core:Made cache size configurable
[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 #if 0
47 static GHashTable *file_name_hash;
48 #endif
49 static struct cache *file_cache;
50
51 struct file_cache_id {
52         int size;
53         int file_name_id;
54         int method;
55         long long offset;
56 };
57
58 struct file *
59 file_create(char *name)
60 {
61         struct stat stat;
62         struct file *file= g_new0(struct file,1);
63
64         if (! file)
65                 return file;
66         file->fd=open(name, O_RDONLY|O_LARGEFILE | O_BINARY);
67         if (file->fd == -1) {
68                 g_free(file);
69                 return NULL;
70         }
71         dbg(1,"fd=%d\n", file->fd);
72         fstat(file->fd, &stat);
73         file->size=stat.st_size;
74         dbg(1,"size=%Ld\n", file->size);
75         file->name = g_strdup(name);
76         dbg_assert(file != NULL);
77         return file;
78 }
79
80 int file_is_dir(char *name)
81 {
82         struct stat buf;
83         if (! stat(name, &buf)) {
84                 return S_ISDIR(buf.st_mode);
85         }
86         return 0;
87
88 }
89
90 int file_mkdir(char *name, int pflag)
91 {
92         char buffer[strlen(name)+1];
93         int ret;
94         char *next;
95         dbg(1,"enter %s %d\n",name,pflag);
96         if (!pflag) {
97                 if (file_is_dir(name))
98                         return 0;
99 #ifdef HAVE_API_WIN32_BASE
100                 return mkdir(name);
101 #else
102                 return mkdir(name, 0777);
103 #endif
104         }
105         strcpy(buffer, name);
106         next=buffer;
107         while ((next=strchr(next, '/'))) {
108                 *next='\0';
109                 if (*buffer) {
110                         ret=file_mkdir(buffer, 0);
111                         if (ret)
112                                 return ret;
113                 }
114                 *next++='/';
115         }
116         if (pflag == 2)
117                 return 0;
118         return file_mkdir(buffer, 0);
119 }
120
121 int
122 file_mmap(struct file *file)
123 {
124 #if defined(_WIN32) || defined(__CEGCC__)
125     file->begin = (char*)mmap_readonly_win32( file->name, &file->map_handle, &file->map_file );
126 #else
127         file->begin=mmap(NULL, file->size, PROT_READ|PROT_WRITE, MAP_PRIVATE, file->fd, 0);
128         dbg_assert(file->begin != NULL);
129         if (file->begin == (void *)0xffffffff) {
130                 perror("mmap");
131                 return 0;
132         }
133 #endif
134         dbg_assert(file->begin != (void *)0xffffffff);
135         file->end=file->begin+file->size;
136
137         return 1;
138 }
139
140 unsigned char *
141 file_data_read(struct file *file, long long offset, int size)
142 {
143         void *ret;
144         if (file->begin)
145                 return file->begin+offset;
146         if (file_cache) {
147                 struct file_cache_id id={offset,size,file->name_id,0};
148                 ret=cache_lookup(file_cache,&id); 
149                 if (ret)
150                         return ret;
151                 ret=cache_insert_new(file_cache,&id,size);
152         } else
153                 ret=g_malloc(size);
154         lseek(file->fd, offset, SEEK_SET);
155         if (read(file->fd, ret, size) != size) {
156                 file_data_free(file, ret);
157                 ret=NULL;
158         }
159         return ret;
160
161 }
162
163 static int
164 uncompress_int(Bytef *dest, uLongf *destLen, const Bytef *source, uLong sourceLen)
165 {
166         z_stream stream;
167         int err;
168
169         stream.next_in = (Bytef*)source;
170         stream.avail_in = (uInt)sourceLen;
171         stream.next_out = dest;
172         stream.avail_out = (uInt)*destLen;
173
174         stream.zalloc = (alloc_func)0;
175         stream.zfree = (free_func)0;
176
177         err = inflateInit2(&stream, -MAX_WBITS);
178         if (err != Z_OK) return err;
179
180         err = inflate(&stream, Z_FINISH);
181         if (err != Z_STREAM_END) {
182         inflateEnd(&stream);
183         if (err == Z_NEED_DICT || (err == Z_BUF_ERROR && stream.avail_in == 0))
184                 return Z_DATA_ERROR;
185                 return err;
186         }
187         *destLen = stream.total_out;
188
189         err = inflateEnd(&stream);
190         return err;
191 }
192
193 unsigned char *
194 file_data_read_compressed(struct file *file, long long offset, int size, int size_uncomp)
195 {
196         void *ret;
197         char buffer[size];
198         uLongf destLen=size_uncomp;
199
200         if (file_cache) {
201                 struct file_cache_id id={offset,size,file->name_id,1};
202                 ret=cache_lookup(file_cache,&id); 
203                 if (ret)
204                         return ret;
205                 ret=cache_insert_new(file_cache,&id,size_uncomp);
206         } else 
207                 ret=g_malloc(size_uncomp);
208         lseek(file->fd, offset, SEEK_SET);
209         if (read(file->fd, buffer, size) != size) {
210                 g_free(ret);
211                 ret=NULL;
212         } else {
213                 if (uncompress_int(ret, &destLen, (Bytef *)buffer, size) != Z_OK) {
214                         dbg(0,"uncompress failed\n");
215                         g_free(ret);
216                         ret=NULL;
217                 }
218         }
219         return ret;
220 }
221
222 void
223 file_data_free(struct file *file, unsigned char *data)
224 {
225         if (file->begin && data >= file->begin && data < file->end)
226                 return;
227         if (file_cache) {
228                 cache_entry_destroy(file_cache, data);
229         } else
230                 g_free(data);
231 }
232
233 int
234 file_exists(char *name)
235 {
236         struct stat buf;
237         if (! stat(name, &buf))
238                 return 1;
239         return 0;
240 }
241
242 void
243 file_remap_readonly(struct file *f)
244 {
245 #if defined(_WIN32) || defined(__CEGCC__)
246 #else
247         void *begin;
248         munmap(f->begin, f->size);
249         begin=mmap(f->begin, f->size, PROT_READ, MAP_PRIVATE, f->fd, 0);
250         if (f->begin != begin)
251                 printf("remap failed\n");
252 #endif
253 }
254
255 void
256 file_unmap(struct file *f)
257 {
258 #if defined(_WIN32) || defined(__CEGCC__)
259     mmap_unmap_win32( f->begin, f->map_handle , f->map_file );
260 #else
261         munmap(f->begin, f->size);
262 #endif
263 }
264
265 void *
266 file_opendir(char *dir)
267 {
268         return opendir(dir);
269 }
270
271 char *
272 file_readdir(void *hnd)
273 {
274         struct dirent *ent;
275
276         ent=readdir(hnd);
277         if (! ent)
278                 return NULL;
279         return ent->d_name;
280 }
281
282 void
283 file_closedir(void *hnd)
284 {
285         closedir(hnd);
286 }
287
288 struct file *
289 file_create_caseinsensitive(char *name)
290 {
291         char dirname[strlen(name)+1];
292         char *filename;
293         char *p;
294         void *d;
295         struct file *ret;
296
297         ret=file_create(name);
298         if (ret)
299                 return ret;
300
301         strcpy(dirname, name);
302         p=dirname+strlen(name);
303         while (p > dirname) {
304                 if (*p == '/')
305                         break;
306                 p--;
307         }
308         *p=0;
309         d=file_opendir(dirname);
310         if (d) {
311                 *p++='/';
312                 while ((filename=file_readdir(d))) {
313                         if (!strcasecmp(filename, p)) {
314                                 strcpy(p, filename);
315                                 ret=file_create(dirname);
316                                 if (ret)
317                                         break;
318                         }
319                 }
320                 file_closedir(d);
321         }
322         return ret;
323 }
324
325 void
326 file_destroy(struct file *f)
327 {
328         close(f->fd);
329
330     if ( f->begin != NULL )
331     {
332         file_unmap( f );
333     }
334
335         g_free(f->name);
336         g_free(f);
337 }
338
339 struct file_wordexp {
340         int err;
341         wordexp_t we;
342 };
343
344 struct file_wordexp *
345 file_wordexp_new(const char *pattern)
346 {
347         struct file_wordexp *ret=g_new0(struct file_wordexp, 1);
348
349         ret->err=wordexp(pattern, &ret->we, 0);
350         if (ret->err)
351                 dbg(0,"wordexp('%s') returned %d\n", pattern, ret->err);
352         return ret;
353 }
354
355 int
356 file_wordexp_get_count(struct file_wordexp *wexp)
357 {
358         return wexp->we.we_wordc;
359 }
360
361 char **
362 file_wordexp_get_array(struct file_wordexp *wexp)
363 {
364         return wexp->we.we_wordv;
365 }
366
367 void
368 file_wordexp_destroy(struct file_wordexp *wexp)
369 {
370         if (! wexp->err)
371                 wordfree(&wexp->we);
372         g_free(wexp);
373 }
374
375
376 int
377 file_get_param(struct file *file, struct param_list *param, int count)
378 {
379         int i=count;
380         param_add_string("Filename", file->name, &param, &count);
381         param_add_hex("Size", file->size, &param, &count);
382         return i-count;
383 }
384
385 int
386 file_version(struct file *file, int byname)
387 {
388 #ifndef __CEGCC__
389         struct stat st;
390         int error;
391         if (byname)
392                 error=stat(file->name, &st);
393         else
394                 error=fstat(file->fd, &st);
395         if (error || !file->version || file->mtime != st.st_mtime || file->ctime != st.st_ctime) {
396                 file->mtime=st.st_mtime;
397                 file->ctime=st.st_ctime;
398                 file->version++;
399                 dbg(0,"%s now version %d\n", file->name, file->version);
400         }
401         return file->version;
402 #else
403         return 0;
404 #endif
405 }
406
407 void *
408 file_get_os_handle(struct file *file)
409 {
410         return (void *)(file->fd);
411 }
412
413 void
414 file_init(void)
415 {
416 #ifdef CACHE_SIZE
417         file_name_hash=g_hash_table_new(g_str_hash, g_str_equal);
418         file_cache=cache_new(sizeof(struct file_cache_id), CACHE_SIZE);
419 #endif
420 }
421