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