I got the relative magnitudes of the timeout increases reversed, so
[python.git] / PC / bdist_wininst / extract.c
blobb495e1cd8c54b8cbdeb4fba8f5841d5eff6cb99d
1 /*
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.
6 IF CHANGES TO THIS FILE ARE CHECKED INTO PYTHON CVS, THE RECOMPILED BINARIES
7 MUST BE CHECKED IN AS WELL!
8 */
10 #include <windows.h>
12 #include "zlib.h"
14 #include <stdio.h>
15 #include <stdarg.h>
17 #include "archive.h"
19 /* Convert unix-path to dos-path */
20 static void normpath(char *path)
22 while (path && *path) {
23 if (*path == '/')
24 *path = '\\';
25 ++path;
29 BOOL ensure_directory(char *pathname, char *new_part, NOTIFYPROC notify)
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);
43 if (attr & FILE_ATTRIBUTE_DIRECTORY) {
45 } else {
46 SetLastError(183);
47 if (notify)
48 notify(SYSTEM_ERROR,
49 "CreateDirectory (%s)", pathname);
51 *new_part = '\\';
52 ++new_part;
54 return TRUE;
57 /* XXX Should better explicitely specify
58 * uncomp_size and file_times instead of pfhdr!
60 char *map_new_file(DWORD flags, char *filename,
61 char *pathname_part, int size,
62 WORD wFatDate, WORD wFatTime,
63 NOTIFYPROC notify)
65 HANDLE hFile, hFileMapping;
66 char *dst;
67 FILETIME ft;
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;
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;
105 if (hFile == INVALID_HANDLE_VALUE) {
106 if (notify)
107 notify (SYSTEM_ERROR, "CreateFile (%s)", filename);
108 return NULL;
111 if (notify)
112 notify(FILE_CREATED, filename);
114 DosDateTimeToFileTime(wFatDate, wFatTime, &ft);
115 SetFileTime(hFile, &ft, &ft, &ft);
118 if (size == 0) {
119 /* We cannot map a zero-length file (Also it makes
120 no sense */
121 CloseHandle(hFile);
122 return NULL;
125 hFileMapping = CreateFileMapping(hFile,
126 NULL, PAGE_READWRITE, 0, size, NULL);
128 CloseHandle(hFile);
130 if (hFileMapping == INVALID_HANDLE_VALUE) {
131 if (notify)
132 notify(SYSTEM_ERROR,
133 "CreateFileMapping (%s)", filename);
134 return NULL;
137 dst = MapViewOfFile(hFileMapping,
138 FILE_MAP_WRITE, 0, 0, 0);
140 CloseHandle(hFileMapping);
142 if (!dst) {
143 if (notify)
144 notify(SYSTEM_ERROR, "MapViewOfFile (%s)", filename);
145 return NULL;
147 return dst;
151 BOOL
152 extract_file(char *dst, char *src, int method, int comp_size,
153 int uncomp_size, NOTIFYPROC notify)
155 z_stream zstream;
156 int result;
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;
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;
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;
183 cleanup:
184 if (Z_OK != (x = inflateEnd(&zstream))) {
185 if (notify)
186 notify (ZLIB_ERROR,
187 "inflateEnd returns %d", x);
188 result = FALSE;
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;
199 /* Open a zip-compatible archive and extract all files
200 * into the specified directory (which is assumed to exist)
202 BOOL
203 unzip_archive(SCHEME *scheme, char *dirname, char *data, DWORD size,
204 NOTIFYPROC notify)
206 int n;
207 char pathname[MAX_PATH];
208 char *new_part;
210 /* read the end of central directory record */
211 struct eof_cdir *pe = (struct eof_cdir *)&data[size - sizeof
212 (struct eof_cdir)];
214 int arc_start = size - sizeof (struct eof_cdir) - pe->nBytesCDir -
215 pe->ofsCDir;
217 /* set position to start of central directory */
218 int pos = arc_start + pe->ofsCDir;
220 /* make sure this is a zip file */
221 if (pe->tag != 0x06054b50)
222 return FALSE;
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;
233 pcdir = (struct cdir *)&data[pos];
234 pfhdr = (struct fhdr *)&data[pcdir->ofs_local_header +
235 arc_start];
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;
246 pcomp = &data[pcdir->ofs_local_header
247 + sizeof(struct fhdr)
248 + arc_start
249 + pfhdr->fname_length
250 + pfhdr->extra_length];
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
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;
268 /* length of the replaced part */
269 int namelen = strlen(scheme[i].name);
271 strcat(pathname, scheme[i].prefix);
273 rest = fname + namelen;
274 len = pfhdr->fname_length - namelen;
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.
283 if ((rest[0] == '\\') || (rest[0] == '/')) {
284 ++rest;
285 --len;
288 strncat(pathname, rest, len);
289 goto Done;
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] != '\\') {
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).
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 ??? */
316 if (notify)
317 notify(NUM_FILES, new_part, (int)pe->nTotalCDir,
318 (int)n+1);
320 return TRUE;