Recreate the navit git/gerrit project that vanished
[profile/ivi/navit.git] / navit / file.c
1 /**
2  * Navit, a modular navigation system.
3  * Copyright (C) 2005-2011 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 "config.h"
24 #ifdef HAVE_UNISTD_H
25 #include <unistd.h>
26 #endif
27 #ifdef _MSC_VER
28 #include <windows.h>
29 #else
30 #include <dirent.h>
31 #endif /* _MSC_VER */
32 #include <string.h>
33 #include <fcntl.h>
34 #include <sys/stat.h>
35 #include <sys/mman.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <wordexp.h>
39 #include <glib.h>
40 #include <zlib.h>
41 #include "debug.h"
42 #include "cache.h"
43 #include "file.h"
44 #include "atom.h"
45 #include "item.h"
46 #include "util.h"
47 #include "types.h"
48 #include "zipfile.h"
49 #ifdef HAVE_SOCKET
50 #include <sys/socket.h>
51 #include <netdb.h>
52 #endif
53
54 extern char *version;
55
56 #ifdef HAVE_LIBCRYPTO
57 #include <openssl/sha.h>
58 #include <openssl/hmac.h>
59 #include <openssl/aes.h>
60 #include <openssl/evp.h>
61 #include <openssl/rand.h>
62 #endif
63
64 #ifdef HAVE_API_ANDROID
65 #define lseek lseek64
66 #endif
67
68 #ifndef O_LARGEFILE
69 #define O_LARGEFILE 0
70 #endif
71
72 #ifndef O_BINARY
73 #define O_BINARY 0
74 #endif
75
76 #ifdef CACHE_SIZE
77 static GHashTable *file_name_hash;
78 #endif
79
80 static struct cache *file_cache;
81
82 #ifdef HAVE_PRAGMA_PACK
83 #pragma pack(push)
84 #pragma pack(1)
85 #endif
86
87 struct file_cache_id {
88         long long offset;
89         int size;
90         int file_name_id;
91         int method;
92 } ATTRIBUTE_PACKED;
93
94 #ifdef HAVE_PRAGMA_PACK
95 #pragma pack(pop)
96 #endif
97
98 #ifdef HAVE_SOCKET
99 static int
100 file_socket_connect(char *host, char *service)
101 {
102         struct addrinfo hints;
103         struct addrinfo *result, *rp;
104         int fd=-1,s;
105
106         memset(&hints, 0, sizeof(struct addrinfo));
107         hints.ai_family = AF_UNSPEC;
108         hints.ai_socktype = SOCK_STREAM;
109         hints.ai_flags = 0;
110         hints.ai_protocol = 0;
111         s = getaddrinfo(host, service, &hints, &result);
112         if (s != 0) {
113                 dbg(0,"getaddrinfo error %s\n",gai_strerror(s));
114                 return -1;
115         }
116         for (rp = result; rp != NULL; rp = rp->ai_next) {
117                 fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
118                 if (fd != -1) {
119                         if (connect(fd, rp->ai_addr, rp->ai_addrlen) != -1)
120                                 break;
121                         close(fd);
122                         fd=-1;
123                 }
124         }
125         freeaddrinfo(result);
126         return fd;
127 }
128
129 static void
130 file_http_request(struct file *file, char *method, char *host, char *path, char *header, int persistent)
131 {
132         char *request=g_strdup_printf("%s %s HTTP/1.0\r\nUser-Agent: navit %s\r\nHost: %s\r\n%s%s%s\r\n",method,path,version,host,persistent?"Connection: Keep-Alive\r\n":"",header?header:"",header?"\r\n":"");
133         write(file->fd, request, strlen(request));
134         dbg(1,"%s\n",request);
135         file->requests++;
136 }
137
138 static int
139 file_request_do(struct file *file, struct attr **options, int connect)
140 {
141         struct attr *attr;
142         char *name;
143
144         if (!options)
145                 return 0;
146         attr=attr_search(options, NULL, attr_url);
147         if (!attr)
148                 return 0;
149         name=attr->u.str;
150         if (!name)
151                 return 0;
152         g_free(file->name);
153         file->name = g_strdup(name);
154         if (!strncmp(name,"http://",7)) {
155                 char *host=g_strdup(name+7);
156                 char *port=strchr(host,':');
157                 char *path=strchr(name+7,'/');
158                 char *method="GET";
159                 char *header=NULL;
160                 int persistent=0;
161                 if ((attr=attr_search(options, NULL, attr_http_method)) && attr->u.str)
162                         method=attr->u.str;
163                 if ((attr=attr_search(options, NULL, attr_http_header)) && attr->u.str)
164                         header=attr->u.str;
165                 if ((attr=attr_search(options, NULL, attr_persistent)))
166                         persistent=attr->u.num;
167                 if (path) 
168                         host[path-name-7]='\0';
169                 if (port)
170                         *port++='\0';
171                 dbg(1,"host=%s path=%s\n",host,path);
172                 if (connect) 
173                         file->fd=file_socket_connect(host,port?port:"80");
174                 file_http_request(file,method,host,path,header,persistent);
175                 file->special=1;
176                 g_free(host);
177         }
178         return 1;
179 }
180 #endif
181
182 static unsigned char *
183 file_http_header_end(unsigned char *str, int len)
184 {
185         int i;
186         for (i=0; i+1<len; i+=2) {
187                 if (str[i+1]=='\n') {
188                         if (str[i]=='\n')
189                                 return str+i+2;
190                         else if (str[i]=='\r' && i+3<len && str[i+2]=='\r' && str[i+3]=='\n')
191                                 return str+i+4;
192                         --i;
193                 } else if (str[i+1]=='\r') {
194                         if (i+4<len && str[i+2]=='\n' && str[i+3]=='\r' && str[i+4]=='\n')
195                                 return str+i+5;
196                         --i;
197                 }
198         }
199         return NULL;
200 }
201
202 int
203 file_request(struct file *f, struct attr **options)
204 {
205 #ifdef HAVE_SOCKET
206         return file_request_do(f, options, 0);
207 #else
208         return 0;
209 #endif
210 }
211
212 char *
213 file_http_header(struct file *f, char *header)
214 {
215         if (!f->headers)
216                 return NULL;
217         return g_hash_table_lookup(f->headers, header);
218 }
219
220 struct file *
221 file_create(char *name, struct attr **options)
222 {
223         struct stat stat;
224         struct file *file= g_new0(struct file,1);
225         struct attr *attr;
226         int open_flags=O_LARGEFILE|O_BINARY;
227
228         if (options && (attr=attr_search(options, NULL, attr_url))) {
229 #ifdef HAVE_SOCKET
230                 file_request_do(file, options, 1);
231 #endif
232         } else {
233                 if (options && (attr=attr_search(options, NULL, attr_readwrite)) && attr->u.num) {
234                         open_flags |= O_RDWR;
235                         if ((attr=attr_search(options, NULL, attr_create)) && attr->u.num)
236                                 open_flags |= O_CREAT;
237                 } else
238                         open_flags |= O_RDONLY;
239                 file->name = g_strdup(name);
240                 file->fd=open(name, open_flags, 0666);
241                 if (file->fd == -1) {
242                         g_free(file);
243                         return NULL;
244                 }
245                 dbg(1,"fd=%d\n", file->fd);
246                 file->size=lseek(file->fd, 0, SEEK_END);
247                 dbg(1,"size="LONGLONG_FMT"\n", file->size);
248                 file->name_id = (long)atom(name);
249         }
250 #ifdef CACHE_SIZE
251         if (!options || !(attr=attr_search(options, NULL, attr_cache)) || attr->u.num)
252                 file->cache=1;
253 #endif
254         dbg_assert(file != NULL);
255         return file;
256 }
257
258 #if 0
259 struct file *
260 file_create_url(char *url)
261 {
262 }
263 #endif
264
265 #ifndef S_ISDIR
266 #define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
267 #endif
268 #ifndef S_ISREG
269 #define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
270 #endif
271
272 int file_is_dir(char *name)
273 {
274         struct stat buf;
275         if (! stat(name, &buf)) {
276                 return S_ISDIR(buf.st_mode);
277         }
278         return 0;
279
280 }
281
282 int file_is_reg(char *name)
283 {
284         struct stat buf;
285         if (! stat(name, &buf)) {
286                 return S_ISREG(buf.st_mode);
287         }
288         return 0;
289 }
290
291 long long
292 file_size(struct file *file)
293 {
294         return file->size;
295 }
296
297 int file_mkdir(char *name, int pflag)
298 {
299         char *buffer=g_alloca(sizeof(char)*(strlen(name)+1));
300         int ret;
301         char *next;
302         dbg(1,"enter %s %d\n",name,pflag);
303         if (!pflag) {
304                 if (file_is_dir(name))
305                         return 0;
306 #if defined HAVE_API_WIN32_BASE || defined _MSC_VER
307                 return mkdir(name);
308 #else
309                 return mkdir(name, 0777);
310 #endif
311         }
312         strcpy(buffer, name);
313         next=buffer;
314         while ((next=strchr(next, '/'))) {
315                 *next='\0';
316                 if (*buffer) {
317                         ret=file_mkdir(buffer, 0);
318                         if (ret)
319                                 return ret;
320                 }
321                 *next++='/';
322         }
323         if (pflag == 2)
324                 return 0;
325         return file_mkdir(buffer, 0);
326 }
327
328 int
329 file_mmap(struct file *file)
330 {
331 #if 0
332         int mmap_size=file->size+1024*1024;
333 #else
334         int mmap_size=file->size;
335 #endif
336 #ifdef HAVE_API_WIN32_BASE
337         file->begin = (char*)mmap_readonly_win32( file->name, &file->map_handle, &file->map_file );
338 #else
339         file->begin=mmap(NULL, mmap_size, PROT_READ|PROT_WRITE, MAP_PRIVATE, file->fd, 0);
340         dbg_assert(file->begin != NULL);
341         if (file->begin == (void *)0xffffffff) {
342                 perror("mmap");
343                 return 0;
344         }
345 #endif
346         dbg_assert(file->begin != (void *)0xffffffff);
347         file->mmap_end=file->begin+mmap_size;
348         file->end=file->begin+file->size;
349
350         return 1;
351 }
352
353 unsigned char *
354 file_data_read(struct file *file, long long offset, int size)
355 {
356         void *ret;
357         if (file->special)
358                 return NULL;
359         if (file->begin)
360                 return file->begin+offset;
361         if (file->cache) {
362                 struct file_cache_id id={offset,size,file->name_id,0};
363                 ret=cache_lookup(file_cache,&id); 
364                 if (ret)
365                         return ret;
366                 ret=cache_insert_new(file_cache,&id,size);
367         } else
368                 ret=g_malloc(size);
369         lseek(file->fd, offset, SEEK_SET);
370         if (read(file->fd, ret, size) != size) {
371                 file_data_free(file, ret);
372                 ret=NULL;
373         }
374         return ret;
375
376 }
377
378 static void
379 file_process_headers(struct file *file, unsigned char *headers)
380 {
381         char *tok;
382         char *cl;
383         if (file->headers)
384                 g_hash_table_destroy(file->headers);
385         file->headers=g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
386         while ((tok=strtok((char*)headers, "\r\n"))) {
387                 char *sep;
388                 tok=g_strdup(tok);
389                 sep=strchr(tok,':');
390                 if (!sep)
391                         sep=strchr(tok,'/');
392                 if (!sep) {
393                         g_free(tok);
394                         continue;
395                 }
396                 *sep++='\0';
397                 if (*sep == ' ')
398                         sep++;
399                 strtolower(tok, tok);
400                 dbg(1,"header '%s'='%s'\n",tok,sep);
401                 g_hash_table_insert(file->headers, tok, sep);
402                 headers=NULL;
403         }
404         cl=g_hash_table_lookup(file->headers, "content-length");
405         if (cl) 
406 #ifdef HAVE__ATOI64
407                 file->size=_atoi64(cl);
408 #else
409                 file->size=atoll(cl);
410 #endif
411 }
412
413 static void
414 file_shift_buffer(struct file *file, int amount)
415 {
416         memmove(file->buffer, file->buffer+amount, file->buffer_len-amount);
417         file->buffer_len-=amount;
418 }
419
420 unsigned char *
421 file_data_read_special(struct file *file, int size, int *size_ret)
422 {
423         unsigned char *ret,*hdr;
424         int rets=0,rd;
425         int buffer_size=8192;
426         int eof=0;
427         if (!file->special)
428                 return NULL;
429         if (!file->buffer)
430                 file->buffer=g_malloc(buffer_size);
431         ret=g_malloc(size);
432         while ((size > 0 || file->requests) && (!eof || file->buffer_len)) {
433                 int toread=buffer_size-file->buffer_len;
434                 if (toread >= 4096 && !eof) {
435                         if (!file->requests && toread > size)
436                                 toread=size;
437                         rd=read(file->fd, file->buffer+file->buffer_len, toread);
438                         if (rd > 0) {
439                                 file->buffer_len+=rd;
440                         } else
441                                 eof=1;
442                 }
443                 if (file->requests) {
444                         dbg(1,"checking header\n");
445                         if ((hdr=file_http_header_end(file->buffer, file->buffer_len))) {
446                                 hdr[-1]='\0';
447                                 dbg(1,"found %s (%d bytes)\n",file->buffer,sizeof(file->buffer));
448                                 file_process_headers(file, file->buffer);
449                                 file_shift_buffer(file, hdr-file->buffer);
450                                 file->requests--;
451                                 if (file_http_header(file, "location"))
452                                         break;
453                         }
454                 }
455                 if (!file->requests) {
456                         rd=file->buffer_len;
457                         if (rd > size)
458                                 rd=size;
459                         memcpy(ret+rets, file->buffer, rd);
460                         file_shift_buffer(file, rd);
461                         rets+=rd;
462                         size-=rd;
463                 }
464         }
465         *size_ret=rets;
466         return ret;
467 }
468
469 unsigned char *
470 file_data_read_all(struct file *file)
471 {
472         return file_data_read(file, 0, file->size);
473 }
474
475 void
476 file_data_flush(struct file *file, long long offset, int size)
477 {
478         if (file->cache) {
479                 struct file_cache_id id={offset,size,file->name_id,0};
480                 cache_flush(file_cache,&id);
481                 dbg(1,"Flushing "LONGLONG_FMT" %d bytes\n",offset,size);
482         }
483 }
484
485 int
486 file_data_write(struct file *file, long long offset, int size, unsigned char *data)
487 {
488         file_data_flush(file, offset, size);
489         lseek(file->fd, offset, SEEK_SET);
490         if (write(file->fd, data, size) != size)
491                 return 0;
492         if (file->size < offset+size)
493                 file->size=offset+size;
494         return 1;
495 }
496
497 int
498 file_get_contents(char *name, unsigned char **buffer, int *size)
499 {
500         struct file *file;
501         file=file_create(name, 0);
502         if (!file)
503                 return 0;
504         file->cache=0;
505         *size=file_size(file);
506         *buffer=file_data_read_all(file);
507         file_destroy(file);
508         return 1;       
509 }
510
511
512 static int
513 uncompress_int(Bytef *dest, uLongf *destLen, const Bytef *source, uLong sourceLen)
514 {
515         z_stream stream;
516         int err;
517
518         stream.next_in = (Bytef*)source;
519         stream.avail_in = (uInt)sourceLen;
520         stream.next_out = dest;
521         stream.avail_out = (uInt)*destLen;
522
523         stream.zalloc = (alloc_func)0;
524         stream.zfree = (free_func)0;
525
526         err = inflateInit2(&stream, -MAX_WBITS);
527         if (err != Z_OK) return err;
528
529         err = inflate(&stream, Z_FINISH);
530         if (err != Z_STREAM_END) {
531         inflateEnd(&stream);
532         if (err == Z_NEED_DICT || (err == Z_BUF_ERROR && stream.avail_in == 0))
533                 return Z_DATA_ERROR;
534                 return err;
535         }
536         *destLen = stream.total_out;
537
538         err = inflateEnd(&stream);
539         return err;
540 }
541
542 unsigned char *
543 file_data_read_compressed(struct file *file, long long offset, int size, int size_uncomp)
544 {
545         void *ret;
546         char *buffer = 0;
547         uLongf destLen=size_uncomp;
548
549         if (file->cache) {
550                 struct file_cache_id id={offset,size,file->name_id,1};
551                 ret=cache_lookup(file_cache,&id); 
552                 if (ret)
553                         return ret;
554                 ret=cache_insert_new(file_cache,&id,size_uncomp);
555         } else 
556                 ret=g_malloc(size_uncomp);
557         lseek(file->fd, offset, SEEK_SET);
558
559         buffer = (char *)g_malloc(size);
560         if (read(file->fd, buffer, size) != size) {
561                 g_free(ret);
562                 ret=NULL;
563         } else {
564                 if (uncompress_int(ret, &destLen, (Bytef *)buffer, size) != Z_OK) {
565                         dbg(0,"uncompress failed\n");
566                         g_free(ret);
567                         ret=NULL;
568                 }
569         }
570         g_free(buffer);
571
572         return ret;
573 }
574
575 unsigned char *
576 file_data_read_encrypted(struct file *file, long long offset, int size, int size_uncomp, int compressed, char *passwd)
577 {
578 #ifdef HAVE_LIBCRYPTO
579         void *ret;
580         unsigned char *buffer = 0;
581         uLongf destLen=size_uncomp;
582
583         if (file->cache) {
584                 struct file_cache_id id={offset,size,file->name_id,1};
585                 ret=cache_lookup(file_cache,&id); 
586                 if (ret)
587                         return ret;
588                 ret=cache_insert_new(file_cache,&id,size_uncomp);
589         } else 
590                 ret=g_malloc(size_uncomp);
591         lseek(file->fd, offset, SEEK_SET);
592
593         buffer = (unsigned char *)g_malloc(size);
594         if (read(file->fd, buffer, size) != size) {
595                 g_free(ret);
596                 ret=NULL;
597         } else {
598                 unsigned char key[34], salt[8], verify[2], counter[16], xor[16], mac[10], *datap;
599                 int overhead=sizeof(salt)+sizeof(verify)+sizeof(mac);
600                 int esize=size-overhead;
601                 PKCS5_PBKDF2_HMAC_SHA1(passwd, strlen(passwd), (unsigned char *)buffer, 8, 1000, 34, key);
602                 if (key[32] == buffer[8] && key[33] == buffer[9] && esize >= 0) {
603                         AES_KEY aeskey;
604                         AES_set_encrypt_key(key, 128, &aeskey);
605                         datap=buffer+sizeof(salt)+sizeof(verify);
606                         memset(counter, 0, sizeof(counter));
607                         while (esize > 0) {
608                                 int i,curr_size,idx=0;
609                                 do {
610                                         counter[idx]++;
611                                 } while (!counter[idx++]);
612                                 AES_encrypt(counter, xor, &aeskey);
613                                 curr_size=esize;
614                                 if (curr_size > sizeof(xor))
615                                         curr_size=sizeof(xor);
616                                 for (i = 0 ; i < curr_size ; i++) 
617                                         *datap++^=xor[i];
618                                 esize-=curr_size;
619                         }
620                         size-=overhead;
621                         datap=buffer+sizeof(salt)+sizeof(verify);
622                         if (compressed) {
623                                 if (uncompress_int(ret, &destLen, (Bytef *)datap, size) != Z_OK) {
624                                         dbg(0,"uncompress failed\n");
625                                         g_free(ret);
626                                         ret=NULL;
627                                 }
628                         } else {
629                                 if (size == destLen) 
630                                         memcpy(ret, buffer, destLen);
631                                 else {
632                                         dbg(0,"memcpy failed\n");
633                                         g_free(ret);
634                                         ret=NULL;
635                                 }
636                         }
637                 } else {
638                         g_free(ret);
639                         ret=NULL;
640                 }
641         }
642         g_free(buffer);
643
644         return ret;
645 #else
646         return NULL;
647 #endif
648 }
649
650 void
651 file_data_free(struct file *file, unsigned char *data)
652 {
653         if (file->begin) {
654                 if (data == file->begin)
655                         return;
656                 if (data >= file->begin && data < file->end)
657                         return;
658         }
659         if (file->cache && data) {
660                 cache_entry_destroy(file_cache, data);
661         } else
662                 g_free(data);
663 }
664
665 void
666 file_data_remove(struct file *file, unsigned char *data)
667 {
668         if (file->begin) {
669                 if (data == file->begin)
670                         return;
671                 if (data >= file->begin && data < file->end)
672                         return;
673         }
674         if (file->cache && data) {
675                 cache_flush_data(file_cache, data);
676         } else
677                 g_free(data);
678 }
679
680 int
681 file_exists(char const *name)
682 {
683         struct stat buf;
684         if (! stat(name, &buf))
685                 return 1;
686         return 0;
687 }
688
689 void
690 file_remap_readonly(struct file *f)
691 {
692 #if defined(_WIN32) || defined(__CEGCC__)
693 #else
694         void *begin;
695         munmap(f->begin, f->size);
696         begin=mmap(f->begin, f->size, PROT_READ, MAP_PRIVATE, f->fd, 0);
697         if (f->begin != begin)
698                 printf("remap failed\n");
699 #endif
700 }
701
702 void
703 file_unmap(struct file *f)
704 {
705 #if defined(_WIN32) || defined(__CEGCC__)
706     mmap_unmap_win32( f->begin, f->map_handle , f->map_file );
707 #else
708         munmap(f->begin, f->size);
709 #endif
710 }
711
712 #ifndef _MSC_VER
713 void *
714 file_opendir(char *dir)
715 {
716         return opendir(dir);
717 }
718 #else 
719 void *
720 file_opendir(char *dir)
721 {
722         WIN32_FIND_DATAA FindFileData;
723         HANDLE hFind = INVALID_HANDLE_VALUE;
724 #undef UNICODE         // we need FindFirstFileA() which takes an 8-bit c-string
725         char* fname=g_alloca(sizeof(char)*(strlen(dir)+4));
726         sprintf(fname,"%s\\*",dir);
727         hFind = FindFirstFileA(fname, &FindFileData);
728         return hFind;
729 }
730 #endif
731
732 #ifndef _MSC_VER
733 char *
734 file_readdir(void *hnd)
735 {
736         struct dirent *ent;
737
738         ent=readdir(hnd);
739         if (! ent)
740                 return NULL;
741         return ent->d_name;
742 }
743 #else
744 char *
745 file_readdir(void *hnd)
746 {
747         WIN32_FIND_DATA FindFileData;
748
749         if (FindNextFile(hnd, &FindFileData) ) {
750                 return FindFileData.cFileName;
751         } else {
752                 return NULL;
753         }
754 }
755 #endif /* _MSC_VER */
756
757 #ifndef _MSC_VER
758 void
759 file_closedir(void *hnd)
760 {
761         closedir(hnd);
762 }
763 #else
764 void
765 file_closedir(void *hnd)
766 {
767         FindClose(hnd);
768 }
769 #endif /* _MSC_VER */
770
771 struct file *
772 file_create_caseinsensitive(char *name, struct attr **options)
773 {
774         char *dirname=g_alloca(sizeof(char)*(strlen(name)+1));
775         char *filename;
776         char *p;
777         void *d;
778         struct file *ret;
779
780         ret=file_create(name, options);
781         if (ret)
782                 return ret;
783
784         strcpy(dirname, name);
785         p=dirname+strlen(name);
786         while (p > dirname) {
787                 if (*p == '/')
788                         break;
789                 p--;
790         }
791         *p=0;
792         d=file_opendir(dirname);
793         if (d) {
794                 *p++='/';
795                 while ((filename=file_readdir(d))) {
796                         if (!g_strcasecmp(filename, p)) {
797                                 strcpy(p, filename);
798                                 ret=file_create(dirname, options);
799                                 if (ret)
800                                         break;
801                         }
802                 }
803                 file_closedir(d);
804         }
805         return ret;
806 }
807
808 void
809 file_destroy(struct file *f)
810 {
811         if (f->headers)
812                 g_hash_table_destroy(f->headers);
813         switch (f->special) {
814         case 0:
815         case 1:
816                 close(f->fd);
817                 break;
818         }
819
820     if ( f->begin != NULL )
821     {
822         file_unmap( f );
823     }
824
825         g_free(f->buffer);
826         g_free(f->name);
827         g_free(f);
828 }
829
830 struct file_wordexp {
831         int err;
832         char *pattern;
833         wordexp_t we;
834 };
835
836 struct file_wordexp *
837 file_wordexp_new(const char *pattern)
838 {
839         struct file_wordexp *ret=g_new0(struct file_wordexp, 1);
840
841         ret->pattern=g_strdup(pattern);
842         ret->err=wordexp(pattern, &ret->we, 0);
843         if (ret->err)
844                 dbg(0,"wordexp('%s') returned %d\n", pattern, ret->err);
845         return ret;
846 }
847
848 int
849 file_wordexp_get_count(struct file_wordexp *wexp)
850 {
851         if (wexp->err)
852                 return 1;
853         return wexp->we.we_wordc;
854 }
855
856 char **
857 file_wordexp_get_array(struct file_wordexp *wexp)
858 {
859         if (wexp->err)
860                 return &wexp->pattern;
861         return wexp->we.we_wordv;
862 }
863
864 void
865 file_wordexp_destroy(struct file_wordexp *wexp)
866 {
867         if (! wexp->err)
868                 wordfree(&wexp->we);
869         g_free(wexp->pattern);
870         g_free(wexp);
871 }
872
873
874 int
875 file_get_param(struct file *file, struct param_list *param, int count)
876 {
877         int i=count;
878         param_add_string("Filename", file->name, &param, &count);
879         param_add_hex("Size", file->size, &param, &count);
880         return i-count;
881 }
882
883 int
884 file_version(struct file *file, int mode)
885 {
886 #ifndef HAVE_API_WIN32_BASE
887         struct stat st;
888         int error;
889         if (mode == 3) {
890                 long long size=lseek(file->fd, 0, SEEK_END);
891                 if (file->begin && file->begin+size > file->mmap_end) {
892                         file->version++;
893                 } else {
894                         file->size=size;
895                         if (file->begin)
896                                 file->end=file->begin+file->size;
897                 }
898         } else {
899                 if (mode == 2)
900                         error=stat(file->name, &st);
901                 else
902                         error=fstat(file->fd, &st);
903                 if (error || !file->version || file->mtime != st.st_mtime || file->ctime != st.st_ctime) {
904                         file->mtime=st.st_mtime;
905                         file->ctime=st.st_ctime;
906                         file->version++;
907                         dbg(1,"%s now version %d\n", file->name, file->version);
908                 }
909         }
910         return file->version;
911 #else
912         return 0;
913 #endif
914 }
915
916 void *
917 file_get_os_handle(struct file *file)
918 {
919         return GINT_TO_POINTER(file->fd);
920 }
921
922 void
923 file_init(void)
924 {
925 #ifdef CACHE_SIZE
926         file_name_hash=g_hash_table_new(g_str_hash, g_str_equal);
927         file_cache=cache_new(sizeof(struct file_cache_id), CACHE_SIZE);
928 #endif
929 }
930