Fix LDC, LDC_W, and INSTANCEOF opcodes, more debugging
[jamvm-avr32-jem.git] / src / zip.c
blobebcb212909f576e2e2a1d71aa0553fc7ed382a9e
1 /*
2 * Copyright (C) 2003, 2004, 2005, 2006, 2007
3 * Robert Lougher <rob@lougher.org.uk>.
5 * This file is part of JamVM.
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2,
10 * or (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22 #include "jam.h"
23 #include "hash.h"
24 #include "zip.h"
26 #ifdef USE_ZIP
27 #include <stdlib.h>
28 #include <string.h>
29 #include <zlib.h>
30 #include <sys/mman.h>
32 #include <sys/types.h>
33 #include <sys/stat.h>
34 #include <fcntl.h>
36 /* Definitions needed for hashtable */
37 #define HASHTABSZE 1<<8
38 #define HASH(ptr) utf8Hash(ptr)
39 #define COMPARE(ptr1, ptr2, hash1, hash2) (hash1 == hash2) && utf8Comp(ptr1, ptr2)
40 #define PREPARE(ptr) ptr
41 #define SCAVENGE(ptr) FALSE
42 #define FOUND(ptr) ptr
44 /* zlib window size */
45 #define MAX_WINDOW_BITS 15
47 /* Macros for reading non-aligned little-endian values from zip file */
48 #define READ_LE_INT(p) ((p)[0]|((p)[1]<<8)|((p)[2]<<16)|((p)[3]<<24))
49 #define READ_LE_SHORT(p) ((p)[0]|((p)[1]<<8))
51 /* Offsets and lengths of fields within the zip file format */
53 #define SIG_LEN 4
55 /* End of central directory record */
56 #define END_CEN_SIG 0x06054b50
57 #define END_CEN_LEN 22
58 #define END_CEN_ENTRIES_OFFSET 8
59 #define END_CEN_DIR_START_OFFSET 16
61 /* Central directory file header */
62 #define CEN_FILE_HEADER_SIG 0x02014b50
63 #define CEN_FILE_HEADER_LEN 46
64 #define CEN_FILE_COMPMETH_OFFSET 10
65 #define CEN_FILE_COMPLEN_OFFSET 20
66 #define CEN_FILE_UNCOMPLEN_OFFSET 24
67 #define CEN_FILE_PATHLEN_OFFSET 28
68 #define CEN_FILE_EXTRALEN_OFFSET 30
69 #define CEN_FILE_COMMENTLEN_OFFSET 32
70 #define CEN_FILE_LOCALHDR_OFFSET 42
72 /* Local file header */
73 #define LOC_FILE_HEADER_SIG 0x04034b50
74 #define LOC_FILE_HEADER_LEN 30
75 #define LOC_FILE_EXTRA_OFFSET 28
77 /* Supported compression methods */
78 #define COMP_STORED 0
79 #define COMP_DEFLATED 8
81 ZipFile *processArchive(char *path) {
82 unsigned char magic[SIG_LEN];
83 unsigned char *data, *pntr;
84 int entries, fd, len;
85 int next_file_sig;
87 HashTable *hash_table;
88 ZipFile *zip;
90 if((fd = open(path, O_RDONLY)) == -1)
91 return NULL;
93 /* First 4 bytes must be the signature for the first local file header */
94 if(read(fd, &magic[0], SIG_LEN) != SIG_LEN
95 || READ_LE_INT(magic) != LOC_FILE_HEADER_SIG)
96 goto error;
98 /* Get the length */
99 len = lseek(fd, 0, SEEK_END);
101 /* Mmap the file into memory */
102 if((data = (unsigned char*)mmap(0, len, PROT_READ | PROT_WRITE, MAP_FILE |
103 MAP_PRIVATE, fd, 0)) == MAP_FAILED)
104 goto error;
106 /* Locate the end of central directory record by searching backwards for
107 the record signature. */
109 if(len < END_CEN_LEN)
110 goto error2;
112 for(pntr = data + len - END_CEN_LEN; pntr >= data; )
113 if(*pntr == (END_CEN_SIG & 0xff))
114 if(READ_LE_INT(pntr) == END_CEN_SIG)
115 break;
116 else
117 pntr -= SIG_LEN;
118 else
119 pntr--;
121 /* Check that we found it */
122 if(pntr < data)
123 goto error2;
125 /* Get the number of entries in the central directory */
126 entries = READ_LE_SHORT(pntr + END_CEN_ENTRIES_OFFSET);
128 /* Create and initialise hash table to hold the directory.
129 Do not create lock -- we're single threaded (bootstrapping)
130 and once entries are added, table is read-only */
132 hash_table = (HashTable*)sysMalloc(sizeof(HashTable));
133 initHashTable((*hash_table), HASHTABSZE, FALSE);
135 /* Get the offset from the start of the file of the first directory entry */
136 pntr = data + READ_LE_INT(pntr + END_CEN_DIR_START_OFFSET);
138 /* Scan the directory list and add the entries to the hash table */
140 /* We terminate each pathname "in place" to save memory -- this may
141 over-write the signature for the next entry. We therefore
142 speculatively load the next sig *before* we terminate the pathname */
144 if((pntr + SIG_LEN) > (data + len))
145 goto error2;
147 /* Speculatively read first sig */
148 next_file_sig = READ_LE_INT(pntr);
150 while(entries--) {
151 char *found, *pathname, *term;
152 int path_len, comment_len, extra_len;
154 if((pntr + CEN_FILE_HEADER_LEN) > (data + len))
155 goto error2;
157 /* Check directory entry signature is present */
158 if(next_file_sig != CEN_FILE_HEADER_SIG)
159 goto error2;
161 /* Get the length of the pathname */
162 path_len = READ_LE_SHORT(pntr + CEN_FILE_PATHLEN_OFFSET);
164 /* Not interested in these fields but need length to skip */
165 extra_len = READ_LE_SHORT(pntr + CEN_FILE_EXTRALEN_OFFSET);
166 comment_len = READ_LE_SHORT(pntr + CEN_FILE_COMMENTLEN_OFFSET);
168 /* The pathname starts after the fixed part of the dir entry */
169 pathname = (char*)(pntr += CEN_FILE_HEADER_LEN);
170 term = (char*)(pntr += path_len);
172 /* Skip rest of variable fields -- should then be pointing to next
173 sig. If no extra or comment fields, pntr == term */
174 pntr += extra_len + comment_len;
176 /* Even if this is the last entry, a well-formed zip file will *always*
177 have at least 4 extra bytes */
178 if((pntr + SIG_LEN) > (data + len))
179 goto error2;
181 /* Speculatively read next sig */
182 next_file_sig = READ_LE_INT(pntr);
184 /* Terminate the pathname */
185 *term = '\0';
187 /* Add if absent, no scavenge, not locked */
188 findHashEntry((*hash_table), pathname, found, TRUE, FALSE, FALSE);
191 zip = (ZipFile*) sysMalloc(sizeof(ZipFile));
193 zip->data = data;
194 zip->length = len;
195 zip->dir_hash = hash_table;
197 return zip;
199 error2:
200 munmap(data, len);
202 error:
203 close(fd);
204 return NULL;
207 char *findArchiveDirEntry(char *pathname, ZipFile *zip) {
208 char *found;
210 /* Do not add if absent, no scavenge, not locked */
211 findHashEntry((*zip->dir_hash), pathname, found, FALSE, FALSE, FALSE);
213 return found;
216 char *findArchiveEntry(char *pathname, ZipFile *zip, int *uncomp_len) {
217 int offset, path_len, comp_len, extra_len, comp_method;
218 unsigned char *decomp_buff, *comp_data;
219 unsigned char *dir_entry;
221 if((dir_entry = (unsigned char *)findArchiveDirEntry(pathname, zip)) == NULL)
222 return NULL;
224 /* Found the file -- the pathname points directly into the
225 directory entry. Read the values relative to it */
227 /* Offset of the file entry relative to the start of the file */
228 offset = READ_LE_INT(dir_entry + (CEN_FILE_LOCALHDR_OFFSET -
229 CEN_FILE_HEADER_LEN));
231 if((offset + LOC_FILE_HEADER_LEN) > zip->length)
232 return NULL;
234 /* Get the variable length part of the local file header */
235 extra_len = READ_LE_SHORT(zip->data + offset + LOC_FILE_EXTRA_OFFSET);
237 /* Get the path_len again -- faster than doing a strlen */
238 path_len = READ_LE_SHORT(dir_entry + (CEN_FILE_PATHLEN_OFFSET - CEN_FILE_HEADER_LEN));
240 /* The file's length when uncompressed -- this is passed out */
241 *uncomp_len = READ_LE_INT(dir_entry + (CEN_FILE_UNCOMPLEN_OFFSET - CEN_FILE_HEADER_LEN));
243 /* The compressed file's length, i.e. the data size in the file */
244 comp_len = READ_LE_INT(dir_entry + (CEN_FILE_COMPLEN_OFFSET - CEN_FILE_HEADER_LEN));
246 /* How the file is compressed */
247 comp_method = READ_LE_SHORT(dir_entry + (CEN_FILE_COMPMETH_OFFSET - CEN_FILE_HEADER_LEN));
249 /* Calculate the data start */
250 offset += LOC_FILE_HEADER_LEN + path_len + extra_len;
252 /* Make sure we're not reading outside the file */
253 if((offset + comp_len) > zip->length)
254 return NULL;
256 comp_data = zip->data + offset;
257 decomp_buff = sysMalloc(*uncomp_len);
259 switch(comp_method) {
260 case COMP_STORED:
261 /* Data isn't compressed, so just return it "as is" */
262 memcpy(decomp_buff, comp_data, comp_len);
263 return (char*)decomp_buff;
265 case COMP_DEFLATED: {
266 z_stream stream;
267 int err;
269 stream.next_in = comp_data;
270 stream.avail_in = comp_len;
271 stream.next_out = decomp_buff;
272 stream.avail_out = *uncomp_len;
274 stream.zalloc = Z_NULL;
275 stream.zfree = Z_NULL;
277 /* Use a negative windowBits value to stop the inflator looking
278 for a header */
279 if(inflateInit2(&stream, -MAX_WINDOW_BITS) != Z_OK)
280 goto error;
282 err = inflate(&stream, Z_SYNC_FLUSH);
283 inflateEnd(&stream);
285 if(err == Z_STREAM_END || (err == Z_OK && stream.avail_in == 0))
286 return (char*)decomp_buff;
287 break;
290 default:
291 break;
294 error:
295 sysFree(decomp_buff);
296 return NULL;
298 #else
299 ZipFile *processArchive(char *path) {
300 return NULL;
303 char *findArchiveDirEntry(char *pathname, ZipFile *zip) {
304 return NULL;
307 char *findArchiveEntry(char *pathname, ZipFile *zip, int *entry_len) {
308 return NULL;
310 #endif