]> www.fi.muni.cz Git - evince.git/blob - backend/impress/zip.c
Bump api and libtool versions and rename libview and libdocument libraries
[evince.git] / backend / impress / zip.c
1 /* imposter (OO.org Impress viewer)
2 ** Copyright (C) 2003-2005 Gurer Ozen
3 ** This code is free software; you can redistribute it and/or
4 ** modify it under the terms of GNU General Public License.
5 */
6
7 #include <config.h>
8 #include "common.h"
9 #include "zip.h"
10 #include <zlib.h>
11 #define _(x) x
12
13 typedef unsigned long ulong;
14
15 enum {
16         ZIP_OK = 0,
17         ZIP_NOMEM,
18         ZIP_NOSIG,
19         ZIP_BADZIP,
20         ZIP_NOMULTI,
21         ZIP_EOPEN,
22         ZIP_EREAD,
23         ZIP_NOFILE
24 };
25
26 struct zipfile {
27         struct zipfile *next;
28         char *name;
29         ulong crc;
30         ulong zip_size;
31         ulong real_size;
32         ulong pos;
33 };
34
35 struct zip_struct {
36         FILE *f;
37         struct zipfile *files;
38         ulong cd_pos;
39         ulong cd_size;
40         ulong cd_offset;
41         ulong head_size;
42         ulong rem_size;
43         ulong nr_files;
44 };
45
46 char *
47 zip_error (int err)
48 {
49         char *ret;
50
51         switch (err) {
52                 case ZIP_OK:
53                         ret = _("No error");
54                         break;
55                 case ZIP_NOMEM:
56                         ret = _("Not enough memory");
57                         break;
58                 case ZIP_NOSIG:
59                         ret = _("Cannot find ZIP signature");
60                         break;
61                 case ZIP_BADZIP:
62                         ret = _("Invalid ZIP file");
63                         break;
64                 case ZIP_NOMULTI:
65                         ret = _("Multi file ZIPs are not supported");
66                         break;
67                 case ZIP_EOPEN:
68                         ret = _("Cannot open the file");
69                         break;
70                 case ZIP_EREAD:
71                         ret = _("Cannot read data from file");
72                         break;
73                 case ZIP_NOFILE:
74                         ret = _("Cannot find file in the ZIP archive");
75                         break;
76                 default:
77                         ret = _("Unknown error");
78                         break;
79         }
80         return ret;
81 }
82
83 static int
84 find_cd (zip *z)
85 {
86         FILE *f;
87         char *buf;
88         ulong size, pos, i, flag;
89
90         f = z->f;
91         if (fseek (f, 0, SEEK_END) != 0) return 1;
92         size = ftell (f);
93         if (size < 0xffff) pos = 0; else pos = size - 0xffff;
94         buf = malloc (size - pos + 1);
95         if (!buf) return 1;
96         if (fseek (f, pos, SEEK_SET) != 0) {
97                 free (buf);
98                 return 1;
99         }
100         if (fread (buf, size - pos, 1, f) != 1) {
101                 free (buf);
102                 return 1;
103         }
104         flag = 0;
105         for (i = size - pos - 3; i > 0; i--) {
106                 if (buf[i] == 0x50 && buf[i+1] == 0x4b && buf[i+2] == 0x05 && buf[i+3] == 0x06) {
107                         z->cd_pos = i + pos;
108                         flag = 1;
109                         break;
110                 }
111         }
112         free (buf);
113         if (flag != 1) return 1;
114         return 0;
115 }
116
117 static unsigned long
118 get_long (unsigned char *buf)
119 {
120         return buf[0] + (buf[1] << 8) + (buf[2] << 16) + (buf[3] << 24);
121 }
122
123 static unsigned long
124 get_word (unsigned char *buf)
125 {
126         return buf[0] + (buf[1] << 8);
127 }
128
129 static int
130 list_files (zip *z)
131 {
132         unsigned char buf[46];
133         struct zipfile *zfile;
134         ulong pat, fn_size;
135         int nr = 0;
136
137         pat = z->cd_offset;
138         while (nr < z->nr_files) {
139                 fseek (z->f, pat + z->head_size, SEEK_SET);
140
141                 if (fread (buf, 46, 1, z->f) != 1) return ZIP_EREAD;
142                 if (get_long (buf) != 0x02014b50) return ZIP_BADZIP;
143
144                 zfile = malloc (sizeof (struct zipfile));
145                 if (!zfile) return ZIP_NOMEM;
146                 memset (zfile, 0, sizeof (struct zipfile));
147
148                 zfile->crc = get_long (buf + 16);
149                 zfile->zip_size = get_long (buf + 20);
150                 zfile->real_size = get_long (buf + 24);
151                 fn_size = get_word (buf + 28);
152                 zfile->pos = get_long (buf + 42);
153
154                 zfile->name = malloc (fn_size + 1);
155                 if (!zfile->name) {
156                         free (zfile);
157                         return ZIP_NOMEM;
158                 }
159                 fread (zfile->name, fn_size, 1, z->f);
160                 zfile->name[fn_size] = '\0';
161
162                 zfile->next = z->files;
163                 z->files = zfile;
164
165                 pat += 0x2e + fn_size + get_word (buf + 30) + get_word (buf + 32);
166                 nr++;
167         }
168         return ZIP_OK;
169 }
170
171 zip *
172 zip_open (const char *fname, int *err)
173 {
174         unsigned char buf[22];
175         zip *z;
176         FILE *f;
177
178         f = fopen (fname, "rb");
179         if (NULL == f) {
180                 *err = ZIP_EOPEN;
181                 return NULL;
182         }
183
184         z = malloc (sizeof (zip));
185         memset (z, 0, sizeof (zip));
186         z->f = f;
187
188         if (find_cd (z)) {
189                 zip_close (z);
190                 *err = ZIP_NOSIG;
191                 return NULL;
192         }
193
194         fseek (f, z->cd_pos, SEEK_SET);
195         if (fread (buf, 22, 1, f) != 1) {
196                 zip_close (z);
197                 *err = ZIP_EREAD;
198                 return NULL;
199         }
200         z->nr_files = get_word (buf + 10);
201         if (get_word (buf + 8) != z->nr_files) {
202                 zip_close (z);
203                 *err =  ZIP_NOMULTI;
204                 return NULL;
205         }
206         z->cd_size = get_long (buf + 12);
207         z->cd_offset = get_long (buf + 16);
208         z->rem_size = get_word (buf + 20);
209         z->head_size = z->cd_pos - (z->cd_offset + z->cd_size);
210
211         *err = list_files (z);
212         if (*err != ZIP_OK) {
213                 zip_close (z);
214                 return NULL;
215         }
216
217         *err = ZIP_OK;
218         return z;
219 }
220
221 void
222 zip_close (zip *z)
223 {
224         struct zipfile *zfile, *tmp;
225
226         zfile = z->files;
227         while (zfile) {
228                 tmp = zfile->next;
229                 if (zfile->name) free (zfile->name);
230                 free (zfile);
231                 zfile = tmp;
232         }
233         z->files = NULL;
234         if (z->f) fclose (z->f);
235         z->f = NULL;
236 }
237
238 static struct zipfile *
239 find_file (zip *z, const char *name)
240 {
241         struct zipfile *zfile;
242
243         zfile = z->files;
244         while (zfile) {
245                 if (strcmp (zfile->name, name) == 0) return zfile;
246                 zfile = zfile->next;
247         }
248         return NULL;
249 }
250
251 static int
252 seek_file (zip *z, struct zipfile *zfile)
253 {
254         unsigned char buf[30];
255
256         fseek (z->f, zfile->pos + z->head_size, SEEK_SET);
257         if (fread (buf, 30, 1, z->f) != 1) return ZIP_EREAD;
258         if (get_long (buf) != 0x04034b50) return ZIP_BADZIP;
259         fseek (z->f, get_word (buf + 26) + get_word (buf + 28), SEEK_CUR);
260         return ZIP_OK;
261 }
262
263 iks *
264 zip_load_xml (zip *z, const char *name, int *err)
265 {
266         iksparser *prs;
267         char *real_buf;
268         iks *x;
269         struct zipfile *zfile;
270
271         *err = ZIP_OK;
272
273         zfile = find_file (z, name);
274         if (!zfile) {
275                 *err = ZIP_NOFILE;
276                 return NULL;
277         }
278
279         seek_file (z, zfile);
280
281         real_buf = malloc (zfile->real_size + 1);
282         if (zfile->zip_size < zfile->real_size) {
283                 char *zip_buf;
284                 z_stream zs;
285                 zs.zalloc = NULL;
286                 zs.zfree = NULL;
287                 zs.opaque = NULL;
288                 zip_buf = malloc (zfile->zip_size);
289                 fread (zip_buf, zfile->zip_size, 1, z->f);
290                 zs.next_in = zip_buf;
291                 zs.avail_in = zfile->zip_size;
292                 zs.next_out = real_buf;
293                 zs.avail_out = zfile->real_size;
294                 inflateInit2 (&zs, -MAX_WBITS);
295                 inflate (&zs, Z_FINISH);
296                 inflateEnd (&zs);
297                 free (zip_buf);
298         } else {
299                 fread (real_buf, zfile->real_size, 1, z->f);
300         }
301
302         real_buf[zfile->real_size] = '\0';
303         prs = iks_dom_new (&x);
304         iks_parse (prs, real_buf, zfile->real_size, 1);
305         iks_parser_delete (prs);
306         free (real_buf);
307         return x;
308 }
309
310 unsigned long zip_get_size (zip *z, const char *name)
311 {
312         struct zipfile *zf;
313
314         zf = find_file (z, name);
315         if (!zf) return 0;
316         return zf->real_size;
317 }
318
319 int zip_load (zip *z, const char *name, char *buf)
320 {
321         struct zipfile *zfile;
322
323         zfile = find_file (z, name);
324         if (!zfile) return ZIP_NOFILE;
325
326         seek_file (z, zfile);
327
328         if (zfile->zip_size < zfile->real_size) {
329                 char *zip_buf;
330                 z_stream zs;
331                 zs.zalloc = NULL;
332                 zs.zfree = NULL;
333                 zs.opaque = NULL;
334                 zip_buf = malloc (zfile->zip_size);
335                 fread (zip_buf, zfile->zip_size, 1, z->f);
336                 zs.next_in = zip_buf;
337                 zs.avail_in = zfile->zip_size;
338                 zs.next_out = buf;
339                 zs.avail_out = zfile->real_size;
340                 inflateInit2 (&zs, -MAX_WBITS);
341                 inflate (&zs, Z_FINISH);
342                 inflateEnd (&zs);
343                 free (zip_buf);
344         } else {
345                 fread (buf, zfile->real_size, 1, z->f);
346         }
347
348         return ZIP_OK;
349 }