blob: 7e70afb22816081315b13f1236b59fec895cdd89 [file] [log] [blame]
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001#include <windows.h>
2
3#include "zlib.h"
4
5#include <stdio.h>
6#include <stdarg.h>
7
8#include "archive.h"
9
10/* Convert unix-path to dos-path */
11static void normpath(char *path)
12{
13 while (path && *path) {
14 if (*path == '/')
15 *path = '\\';
16 ++path;
17 }
18}
19
20BOOL ensure_directory(char *pathname, char *new_part, NOTIFYPROC notify)
21{
22 while (new_part && *new_part && (new_part = strchr(new_part, '\\'))) {
23 DWORD attr;
24 *new_part = '\0';
25 attr = GetFileAttributes(pathname);
26 if (attr == -1) {
27 /* nothing found */
28 if (!CreateDirectory(pathname, NULL) && notify)
29 notify(SYSTEM_ERROR,
30 "CreateDirectory (%s)", pathname);
31 else
32 notify(DIR_CREATED, pathname);
33 }
34 if (attr & FILE_ATTRIBUTE_DIRECTORY) {
35 ;
36 } else {
37 SetLastError(183);
38 if (notify)
39 notify(SYSTEM_ERROR,
40 "CreateDirectory (%s)", pathname);
41 }
42 *new_part = '\\';
43 ++new_part;
44 }
45 return TRUE;
46}
47
48/* XXX Should better explicitely specify
49 * uncomp_size and file_times instead of pfhdr!
50 */
51char *map_new_file(DWORD flags, char *filename,
52 char *pathname_part, int size,
53 WORD wFatDate, WORD wFatTime,
54 NOTIFYPROC notify)
55{
56 HANDLE hFile, hFileMapping;
57 char *dst;
58 FILETIME ft;
59
60 try_again:
61 if (!flags)
62 flags = CREATE_NEW;
63 hFile = CreateFile(filename,
64 GENERIC_WRITE | GENERIC_READ,
65 0, NULL,
66 flags,
67 FILE_ATTRIBUTE_NORMAL, NULL);
68 if (hFile == INVALID_HANDLE_VALUE) {
69 DWORD x = GetLastError();
70 switch (x) {
71 case ERROR_FILE_EXISTS:
72 if (notify && notify(CAN_OVERWRITE, filename))
73 hFile = CreateFile(filename,
74 GENERIC_WRITE|GENERIC_READ,
75 0, NULL,
76 CREATE_ALWAYS,
77 FILE_ATTRIBUTE_NORMAL,
78 NULL);
79 else {
80 if (notify)
81 notify(FILE_OVERWRITTEN, filename);
82 return NULL;
83 }
84 break;
85 case ERROR_PATH_NOT_FOUND:
86 if (ensure_directory(filename, pathname_part, notify))
87 goto try_again;
88 else
89 return FALSE;
90 break;
91 default:
92 SetLastError(x);
93 break;
94 }
95 }
96 if (hFile == INVALID_HANDLE_VALUE) {
97 if (notify)
98 notify (SYSTEM_ERROR, "CreateFile (%s)", filename);
99 return NULL;
100 }
101
102 if (notify)
103 notify(FILE_CREATED, filename);
104
105 DosDateTimeToFileTime(wFatDate, wFatTime, &ft);
106 SetFileTime(hFile, &ft, &ft, &ft);
107
108
109 if (size == 0) {
110 /* We cannot map a zero-length file (Also it makes
111 no sense */
112 CloseHandle(hFile);
113 return NULL;
114 }
115
116 hFileMapping = CreateFileMapping(hFile,
117 NULL, PAGE_READWRITE, 0, size, NULL);
118
119 CloseHandle(hFile);
120
121 if (hFileMapping == INVALID_HANDLE_VALUE) {
122 if (notify)
123 notify(SYSTEM_ERROR,
124 "CreateFileMapping (%s)", filename);
125 return NULL;
126 }
127
128 dst = MapViewOfFile(hFileMapping,
129 FILE_MAP_WRITE, 0, 0, 0);
130
131 CloseHandle(hFileMapping);
132
133 if (!dst) {
134 if (notify)
135 notify(SYSTEM_ERROR, "MapViewOfFile (%s)", filename);
136 return NULL;
137 }
138 return dst;
139}
140
141
142BOOL
143extract_file(char *dst, char *src, int method, int comp_size,
144 int uncomp_size, NOTIFYPROC notify)
145{
146 z_stream zstream;
147 int result;
148
149 if (method == Z_DEFLATED) {
150 int x;
151 memset(&zstream, 0, sizeof(zstream));
152 zstream.next_in = src;
153 zstream.avail_in = comp_size+1;
154 zstream.next_out = dst;
155 zstream.avail_out = uncomp_size;
156
157/* Apparently an undocumented feature of zlib: Set windowsize
158 to negative values to supress the gzip header and be compatible with
159 zip! */
160 result = TRUE;
161 if (Z_OK != (x = inflateInit2(&zstream, -15))) {
162 if (notify)
163 notify(ZLIB_ERROR,
164 "inflateInit2 returns %d", x);
165 result = FALSE;
166 goto cleanup;
167 }
168 if (Z_STREAM_END != (x = inflate(&zstream, Z_FINISH))) {
169 if (notify)
170 notify(ZLIB_ERROR,
171 "inflate returns %d", x);
172 result = FALSE;
173 }
174 cleanup:
175 if (Z_OK != (x = inflateEnd(&zstream))) {
176 if (notify)
177 notify (ZLIB_ERROR,
178 "inflateEnd returns %d", x);
179 result = FALSE;
180 }
181 } else if (method == 0) {
182 memcpy(dst, src, uncomp_size);
183 result = TRUE;
184 } else
185 result = FALSE;
186 UnmapViewOfFile(dst);
187 return result;
188}
189
190/* Open a zip-compatible archive and extract all files
191 * into the specified directory (which is assumed to exist)
192 */
193BOOL
194unzip_archive(SCHEME *scheme, char *dirname, char *data, DWORD size,
195 NOTIFYPROC notify)
196{
197 int n;
198 char pathname[MAX_PATH];
199 char *new_part;
200
201 /* read the end of central directory record */
202 struct eof_cdir *pe = (struct eof_cdir *)&data[size - sizeof
203 (struct eof_cdir)];
204
205 int arc_start = size - sizeof (struct eof_cdir) - pe->nBytesCDir -
206 pe->ofsCDir;
207
208 /* set position to start of central directory */
209 int pos = arc_start + pe->ofsCDir;
210
211 /* make sure this is a zip file */
212 if (pe->tag != 0x06054b50)
213 return FALSE;
214
215 /* Loop through the central directory, reading all entries */
216 for (n = 0; n < pe->nTotalCDir; ++n) {
217 int i;
218 char *fname;
219 char *pcomp;
220 char *dst;
221 struct cdir *pcdir;
222 struct fhdr *pfhdr;
223
224 pcdir = (struct cdir *)&data[pos];
225 pfhdr = (struct fhdr *)&data[pcdir->ofs_local_header +
226 arc_start];
227
228 if (pcdir->tag != 0x02014b50)
229 return FALSE;
230 if (pfhdr->tag != 0x04034b50)
231 return FALSE;
232 pos += sizeof(struct cdir);
233 fname = (char *)&data[pos]; /* This is not null terminated! */
234 pos += pcdir->fname_length + pcdir->extra_length +
235 pcdir->comment_length;
236
237 pcomp = &data[pcdir->ofs_local_header
238 + sizeof(struct fhdr)
239 + arc_start
240 + pfhdr->fname_length
241 + pfhdr->extra_length];
242
243 /* dirname is the Python home directory (prefix) */
244 strcpy(pathname, dirname);
245 if (pathname[strlen(pathname)-1] != '\\')
246 strcat(pathname, "\\");
247 new_part = &pathname[lstrlen(pathname)];
248 /* we must now match the first part of the pathname
249 * in the archive to a component in the installation
250 * scheme (PURELIB, PLATLIB, HEADERS, SCRIPTS, or DATA)
251 * and replace this part by the one in the scheme to use
252 */
253 for (i = 0; scheme[i].name; ++i) {
254 if (0 == strnicmp(scheme[i].name, fname,
255 strlen(scheme[i].name))) {
256 char *rest;
257 int len;
258
259 /* length of the replaced part */
260 int namelen = strlen(scheme[i].name);
261
262 strcat(pathname, scheme[i].prefix);
263
264 rest = fname + namelen;
265 len = pfhdr->fname_length - namelen;
266
267 if ((pathname[strlen(pathname)-1] != '\\')
268 && (pathname[strlen(pathname)-1] != '/'))
269 strcat(pathname, "\\");
270 /* Now that pathname ends with a separator,
271 * we must make sure rest does not start with
272 * an additional one.
273 */
274 if ((rest[0] == '\\') || (rest[0] == '/')) {
275 ++rest;
276 --len;
277 }
278
279 strncat(pathname, rest, len);
280 goto Done;
281 }
282 }
283 /* no prefix to replace found, go unchanged */
284 strncat(pathname, fname, pfhdr->fname_length);
285 Done:
286 normpath(pathname);
287 if (pathname[strlen(pathname)-1] != '\\') {
288 /*
289 * The local file header (pfhdr) does not always
290 * contain the compressed and uncompressed sizes of
291 * the data depending on bit 3 of the flags field. So
292 * it seems better to use the data from the central
293 * directory (pcdir).
294 */
295 dst = map_new_file(0, pathname, new_part,
296 pcdir->uncomp_size,
297 pcdir->last_mod_file_date,
298 pcdir->last_mod_file_time, notify);
299 if (dst) {
300 if (!extract_file(dst, pcomp, pfhdr->method,
301 pcdir->comp_size,
302 pcdir->uncomp_size,
303 notify))
304 return FALSE;
305 } /* else ??? */
306 }
307 if (notify)
308 notify(NUM_FILES, new_part, (int)pe->nTotalCDir,
309 (int)n+1);
310 }
311 return TRUE;
312}