|
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. |
|
5 |
|
6 IF CHANGES TO THIS FILE ARE CHECKED INTO PYTHON CVS, THE RECOMPILED BINARIES |
|
7 MUST BE CHECKED IN AS WELL! |
|
8 */ |
|
9 |
|
10 #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 */ |
|
20 static void normpath(char *path) |
|
21 { |
|
22 while (path && *path) { |
|
23 if (*path == '/') |
|
24 *path = '\\'; |
|
25 ++path; |
|
26 } |
|
27 } |
|
28 |
|
29 BOOL 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 */ |
|
60 char *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 |
|
151 BOOL |
|
152 extract_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 */ |
|
202 BOOL |
|
203 unzip_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 } |