switched to zlib
[swftools.git] / installer / archive.c
1 /* archive.c
2
3    Part of the swftools installer.
4    
5    Copyright (c) 2004 Matthias Kramm <kramm@quiss.org> 
6  
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11
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.
16
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, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
20
21 #include <stdlib.h>
22 #include <stdio.h>
23 #include <stdarg.h>
24 #ifdef WIN32
25 #include <windows.h>
26 #include <shlwapi.h>
27 #else
28 #include <sys/stat.h>
29 #endif
30 #include "archive.h"
31 #include "../z/zlib.h"
32 #define ZLIB_BUFFER_SIZE 16384
33
34 static int verbose = 0;
35 static void msg(char*format, ...)
36 {
37     char buf[1024];
38     int l;
39     va_list arglist;
40     if(!verbose)
41         return;
42     va_start(arglist, format);
43     vsprintf(buf, format, arglist);
44     va_end(arglist);
45     l = strlen(buf);
46     while(l && buf[l-1]=='\n') {
47         buf[l-1] = 0;
48         l--;
49     }
50     printf("(archive) %s\n", buf);
51     fflush(stdout);
52 }
53
54
55 typedef struct _reader
56 {
57     int (*read)(struct _reader*, void*data, int len);
58     void (*dealloc)(struct _reader*);
59     void *internal;
60     int pos;
61 } reader_t;
62
63 /* ---------------------------- mem reader ------------------------------- */
64
65 struct memread_t
66 {
67     unsigned char*data;
68     int length;
69 };
70 static int reader_memread(reader_t*reader, void* data, int _len) 
71 {
72     struct memread_t*mr = (struct memread_t*)reader->internal;
73
74     int len = _len;
75     if(mr->length - reader->pos < len) {
76         len = mr->length - reader->pos;
77     }
78     memcpy(data, &mr->data[reader->pos], len);
79     msg("at pos %d, asked to read %d bytes, did read %d bytes\n", reader->pos, _len, len);
80     reader->pos += len;
81     return len;
82 }
83 static void reader_memread_dealloc(reader_t*reader)
84 {
85     if(reader->internal)
86         free(reader->internal);
87     memset(reader, 0, sizeof(reader_t));
88 }
89 reader_t*reader_init_memreader(void*newdata, int newlength)
90 {
91     reader_t*r = malloc(sizeof(reader_t));
92     struct memread_t*mr = (struct memread_t*)malloc(sizeof(struct memread_t));
93     mr->data = (unsigned char*)newdata;
94     mr->length = newlength;
95     r->read = reader_memread;
96     r->dealloc = reader_memread_dealloc;
97     r->internal = (void*)mr;
98     r->pos = 0;
99     return r;
100
101
102
103 /* ---------------------------- zlibinflate reader -------------------------- */
104 struct zlibinflate_t
105 {
106     z_stream zs;
107     reader_t*input;
108     unsigned char readbuffer[ZLIB_BUFFER_SIZE];
109 };
110
111 static void zlib_error(int ret, char* msg, z_stream*zs)
112 {
113     fprintf(stderr, "%s: zlib error (%d): last zlib error: %s\n", msg, ret, zs->msg?zs->msg:"unknown");
114     perror("errno:");
115     exit(1);
116 }
117
118 static int reader_zlibinflate(reader_t*reader, void* data, int len) 
119 {
120     struct zlibinflate_t*z = (struct zlibinflate_t*)reader->internal;
121     int ret;
122     if(!z) {
123         return 0;
124     }
125     if(!len)
126         return 0;
127     
128     z->zs.next_out = (Bytef *)data;
129     z->zs.avail_out = len;
130
131     while(1) {
132         if(!z->zs.avail_in) {
133             z->zs.avail_in = z->input->read(z->input, z->readbuffer, ZLIB_BUFFER_SIZE);
134             z->zs.next_in = z->readbuffer;
135         }
136         if(z->zs.avail_in)
137             ret = inflate(&z->zs, Z_NO_FLUSH);
138         else
139             ret = inflate(&z->zs, Z_FINISH);
140     
141         if (ret != Z_OK &&
142             ret != Z_STREAM_END) zlib_error(ret, "bitio:inflate_inflate", &z->zs);
143
144         if (ret == Z_STREAM_END) {
145                 int pos = z->zs.next_out - (Bytef*)data;
146                 ret = inflateEnd(&z->zs);
147                 if (ret != Z_OK) zlib_error(ret, "bitio:inflate_end", &z->zs);
148                 free(reader->internal);
149                 reader->internal = 0;
150                 reader->pos += pos;
151                 return pos;
152         }
153         if(!z->zs.avail_out) {
154             break;
155         }
156     }
157     reader->pos += len;
158     return len;
159 }
160 static void reader_zlibinflate_dealloc(reader_t*reader)
161 {
162     struct zlibinflate_t*z = (struct zlibinflate_t*)reader->internal;
163     if(z) {
164         if(z->input) {
165             z->input->dealloc(z->input);z->input = 0;
166         }
167         inflateEnd(&z->zs);
168         free(reader->internal);
169     }
170     memset(reader, 0, sizeof(reader_t));
171 }
172 reader_t* reader_init_zlibinflate(reader_t*input)
173 {
174     reader_t*r = malloc(sizeof(reader_t));
175     struct zlibinflate_t*z;
176     int ret;
177     memset(r, 0, sizeof(reader_t));
178     z = (struct zlibinflate_t*)malloc(sizeof(struct zlibinflate_t));
179     memset(z, 0, sizeof(struct zlibinflate_t));
180     r->internal = z;
181     r->read = reader_zlibinflate;
182     r->dealloc = reader_zlibinflate_dealloc;
183     r->pos = 0;
184     z->input = input;
185     memset(&z->zs,0,sizeof(z_stream));
186     z->zs.zalloc = Z_NULL;
187     z->zs.zfree  = Z_NULL;
188     z->zs.opaque = Z_NULL;
189     ret = inflateInit(&z->zs);
190     if (ret != Z_OK) zlib_error(ret, "bitio:inflate_init", &z->zs);
191     return r;
192 }
193
194 /* -------------------------------------------------------------------------- */
195
196
197 static int create_directory(char*path,statusfunc_t f)
198 {
199     if(!path || !*path || (*path=='.' && (!path[1] || path[1]=='.')))
200         return 1; //nothing to do
201     while(path[0]=='.' && (path[1]=='/' || path[1]=='\\'))
202         path+=2;
203
204 #ifdef WIN32
205     if(PathIsDirectoryA(path))
206         return 1;
207 #else
208     struct stat st;
209     if(stat(path, &st)>=0) {
210         if(S_ISDIR(st.st_mode)) {
211             return 1; /* already exists */
212         }
213     }
214 #endif
215
216     if(mkdir(path,0755)<0) {
217         perror("mkdir");
218         char buf[1024];
219         sprintf(buf, "create directory \"%s\" FAILED", path);
220         f(-1, buf);
221         return 0;
222     }
223     return 1;
224 }
225 static int goto_directory(char*path,statusfunc_t f)
226 {
227     if(chdir(path)<0) {
228         char buf[1024];
229         sprintf(buf, "changing to directory \"%s\" FAILED", path);
230         f(-1, buf);
231         return 0;
232     }
233     return 1;
234 }
235 static char basenamebuf[256];
236 static char*get_directory(char*filename)
237 {
238     char*r1 = strrchr(filename, '\\');
239     char*r2 = strrchr(filename, '/');
240     char*r = r1>r2?r1:r2;
241     if(!r)
242         return "";
243     memcpy(basenamebuf, filename, r-filename);
244     basenamebuf[r-filename] = 0;
245     //msg("directory name of \"%s\" is \"%s\"", filename, basenamebuf);
246     return basenamebuf;
247 }
248 static int write_file(char*filename, reader_t*r, int len,statusfunc_t f)
249 {
250     while(filename[0]=='.' && (filename[1]=='/' || filename[1]=='\\'))
251         filename+=2;
252
253     filename=strdup(filename);
254
255     char*p = filename;
256     while(*p) {
257         if(*p=='/') *p='\\';
258         p++;
259     }
260
261     msg("create file \"%s\" (%d bytes)", filename, len);
262     FILE*fo = fopen(filename, "wb");
263
264     if(!fo) {
265         char buf[1024];
266         sprintf(buf, "Couldn't create file %s", filename);
267         f(-1, buf);
268         free(filename);
269         return 0;
270     }
271     int pos=0;
272     char buf[4096];
273     while(pos<len) {
274         int l = 4096;
275         if(pos+l>len)
276             l = len-pos;
277         int n = r->read(r, buf, l);
278         if(n < l) {
279             char buf[1024];
280             sprintf(buf, "Couldn't read byte %d (pos+%d) from input buffer for file %s", pos+n, n, filename);
281             f(-1, buf);
282             return 0;
283         }
284         fwrite(buf, l, 1, fo);
285         pos+=l;
286     }
287     fclose(fo);
288     free(filename);
289     return 1;
290 }
291
292 int unpack_archive(void*data, int len, char*destdir, statusfunc_t f)
293 {
294     reader_t*m = reader_init_memreader(data, len);
295     reader_t*z = reader_init_zlibinflate(m);
296
297     f(0, "Creating installation directory");
298     if(!create_directory(destdir,f)) return 0;
299     f(0, "Changing to installation directory");
300     if(!goto_directory(destdir,f)) return 0;
301     
302     f(0, "Uncompressing files...");
303     while(1) {
304         /* read id */
305         unsigned char id[4];
306         id[3] = 0;
307         if(z->read(z, id, 3)<3) {
308             f(-1, "Unexpected end of archive");
309             return 0;
310         }
311         if(!strcmp(id, "END"))
312             break;
313
314         unsigned b1=0,b2=0,b3=0,b4=0;
315         int l = 0;
316         l+=z->read(z, &b1, 1);
317         l+=z->read(z, &b2, 1);
318         l+=z->read(z, &b3, 1);
319         l+=z->read(z, &b4, 1);
320         if(l<4)
321             return 0;
322
323         /* read size */
324         int len = b1|b2<<8|b3<<16|b4<<24;
325         
326         /* read filename */
327         unsigned char filename_len;
328         z->read(z, &filename_len, 1);
329         char*filename = malloc(filename_len+1);
330         z->read(z, filename, filename_len);
331         filename[(int)filename_len] = 0;
332
333         if(verbose) printf("[%s] %s %d\n", id, filename, len);
334         char buf[2048];
335         sprintf(buf, "[%s] %s (%d bytes)", id, filename, len);
336         f(0, buf);
337         if(!strcmp(id, "DIR")) {
338             if(!create_directory(filename,f)) return 0;
339         } else {
340             if(!create_directory(get_directory(filename),f)) return 0;
341
342             if(!write_file(filename,z,len,f)) return 0;
343         }
344     }
345     f(0, "Finishing Installation");
346     return 1;
347 }
348