You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
312 lines
7.6 KiB
312 lines
7.6 KiB
#include <windows.h> |
|
|
|
#include "zlib.h" |
|
|
|
#include <stdio.h> |
|
#include <stdarg.h> |
|
|
|
#include "archive.h" |
|
|
|
/* Convert unix-path to dos-path */ |
|
static void normpath(char *path) |
|
{ |
|
while (path && *path) { |
|
if (*path == '/') |
|
*path = '\\'; |
|
++path; |
|
} |
|
} |
|
|
|
BOOL ensure_directory(char *pathname, char *new_part, NOTIFYPROC notify) |
|
{ |
|
while (new_part && *new_part && (new_part = strchr(new_part, '\\'))) { |
|
DWORD attr; |
|
*new_part = '\0'; |
|
attr = GetFileAttributes(pathname); |
|
if (attr == -1) { |
|
/* nothing found */ |
|
if (!CreateDirectory(pathname, NULL) && notify) |
|
notify(SYSTEM_ERROR, |
|
"CreateDirectory (%s)", pathname); |
|
else |
|
notify(DIR_CREATED, pathname); |
|
} |
|
if (attr & FILE_ATTRIBUTE_DIRECTORY) { |
|
; |
|
} else { |
|
SetLastError(183); |
|
if (notify) |
|
notify(SYSTEM_ERROR, |
|
"CreateDirectory (%s)", pathname); |
|
} |
|
*new_part = '\\'; |
|
++new_part; |
|
} |
|
return TRUE; |
|
} |
|
|
|
/* XXX Should better explicitely specify |
|
* uncomp_size and file_times instead of pfhdr! |
|
*/ |
|
char *map_new_file(DWORD flags, char *filename, |
|
char *pathname_part, int size, |
|
WORD wFatDate, WORD wFatTime, |
|
NOTIFYPROC notify) |
|
{ |
|
HANDLE hFile, hFileMapping; |
|
char *dst; |
|
FILETIME ft; |
|
|
|
try_again: |
|
if (!flags) |
|
flags = CREATE_NEW; |
|
hFile = CreateFile(filename, |
|
GENERIC_WRITE | GENERIC_READ, |
|
0, NULL, |
|
flags, |
|
FILE_ATTRIBUTE_NORMAL, NULL); |
|
if (hFile == INVALID_HANDLE_VALUE) { |
|
DWORD x = GetLastError(); |
|
switch (x) { |
|
case ERROR_FILE_EXISTS: |
|
if (notify && notify(CAN_OVERWRITE, filename)) |
|
hFile = CreateFile(filename, |
|
GENERIC_WRITE|GENERIC_READ, |
|
0, NULL, |
|
CREATE_ALWAYS, |
|
FILE_ATTRIBUTE_NORMAL, |
|
NULL); |
|
else { |
|
if (notify) |
|
notify(FILE_OVERWRITTEN, filename); |
|
return NULL; |
|
} |
|
break; |
|
case ERROR_PATH_NOT_FOUND: |
|
if (ensure_directory(filename, pathname_part, notify)) |
|
goto try_again; |
|
else |
|
return FALSE; |
|
break; |
|
default: |
|
SetLastError(x); |
|
break; |
|
} |
|
} |
|
if (hFile == INVALID_HANDLE_VALUE) { |
|
if (notify) |
|
notify (SYSTEM_ERROR, "CreateFile (%s)", filename); |
|
return NULL; |
|
} |
|
|
|
if (notify) |
|
notify(FILE_CREATED, filename); |
|
|
|
DosDateTimeToFileTime(wFatDate, wFatTime, &ft); |
|
SetFileTime(hFile, &ft, &ft, &ft); |
|
|
|
|
|
if (size == 0) { |
|
/* We cannot map a zero-length file (Also it makes |
|
no sense */ |
|
CloseHandle(hFile); |
|
return NULL; |
|
} |
|
|
|
hFileMapping = CreateFileMapping(hFile, |
|
NULL, PAGE_READWRITE, 0, size, NULL); |
|
|
|
CloseHandle(hFile); |
|
|
|
if (hFileMapping == INVALID_HANDLE_VALUE) { |
|
if (notify) |
|
notify(SYSTEM_ERROR, |
|
"CreateFileMapping (%s)", filename); |
|
return NULL; |
|
} |
|
|
|
dst = MapViewOfFile(hFileMapping, |
|
FILE_MAP_WRITE, 0, 0, 0); |
|
|
|
CloseHandle(hFileMapping); |
|
|
|
if (!dst) { |
|
if (notify) |
|
notify(SYSTEM_ERROR, "MapViewOfFile (%s)", filename); |
|
return NULL; |
|
} |
|
return dst; |
|
} |
|
|
|
|
|
BOOL |
|
extract_file(char *dst, char *src, int method, int comp_size, |
|
int uncomp_size, NOTIFYPROC notify) |
|
{ |
|
z_stream zstream; |
|
int result; |
|
|
|
if (method == Z_DEFLATED) { |
|
int x; |
|
memset(&zstream, 0, sizeof(zstream)); |
|
zstream.next_in = src; |
|
zstream.avail_in = comp_size+1; |
|
zstream.next_out = dst; |
|
zstream.avail_out = uncomp_size; |
|
|
|
/* Apparently an undocumented feature of zlib: Set windowsize |
|
to negative values to supress the gzip header and be compatible with |
|
zip! */ |
|
result = TRUE; |
|
if (Z_OK != (x = inflateInit2(&zstream, -15))) { |
|
if (notify) |
|
notify(ZLIB_ERROR, |
|
"inflateInit2 returns %d", x); |
|
result = FALSE; |
|
goto cleanup; |
|
} |
|
if (Z_STREAM_END != (x = inflate(&zstream, Z_FINISH))) { |
|
if (notify) |
|
notify(ZLIB_ERROR, |
|
"inflate returns %d", x); |
|
result = FALSE; |
|
} |
|
cleanup: |
|
if (Z_OK != (x = inflateEnd(&zstream))) { |
|
if (notify) |
|
notify (ZLIB_ERROR, |
|
"inflateEnd returns %d", x); |
|
result = FALSE; |
|
} |
|
} else if (method == 0) { |
|
memcpy(dst, src, uncomp_size); |
|
result = TRUE; |
|
} else |
|
result = FALSE; |
|
UnmapViewOfFile(dst); |
|
return result; |
|
} |
|
|
|
/* Open a zip-compatible archive and extract all files |
|
* into the specified directory (which is assumed to exist) |
|
*/ |
|
BOOL |
|
unzip_archive(SCHEME *scheme, char *dirname, char *data, DWORD size, |
|
NOTIFYPROC notify) |
|
{ |
|
int n; |
|
char pathname[MAX_PATH]; |
|
char *new_part; |
|
|
|
/* read the end of central directory record */ |
|
struct eof_cdir *pe = (struct eof_cdir *)&data[size - sizeof |
|
(struct eof_cdir)]; |
|
|
|
int arc_start = size - sizeof (struct eof_cdir) - pe->nBytesCDir - |
|
pe->ofsCDir; |
|
|
|
/* set position to start of central directory */ |
|
int pos = arc_start + pe->ofsCDir; |
|
|
|
/* make sure this is a zip file */ |
|
if (pe->tag != 0x06054b50) |
|
return FALSE; |
|
|
|
/* Loop through the central directory, reading all entries */ |
|
for (n = 0; n < pe->nTotalCDir; ++n) { |
|
int i; |
|
char *fname; |
|
char *pcomp; |
|
char *dst; |
|
struct cdir *pcdir; |
|
struct fhdr *pfhdr; |
|
|
|
pcdir = (struct cdir *)&data[pos]; |
|
pfhdr = (struct fhdr *)&data[pcdir->ofs_local_header + |
|
arc_start]; |
|
|
|
if (pcdir->tag != 0x02014b50) |
|
return FALSE; |
|
if (pfhdr->tag != 0x04034b50) |
|
return FALSE; |
|
pos += sizeof(struct cdir); |
|
fname = (char *)&data[pos]; /* This is not null terminated! */ |
|
pos += pcdir->fname_length + pcdir->extra_length + |
|
pcdir->comment_length; |
|
|
|
pcomp = &data[pcdir->ofs_local_header |
|
+ sizeof(struct fhdr) |
|
+ arc_start |
|
+ pfhdr->fname_length |
|
+ pfhdr->extra_length]; |
|
|
|
/* dirname is the Python home directory (prefix) */ |
|
strcpy(pathname, dirname); |
|
if (pathname[strlen(pathname)-1] != '\\') |
|
strcat(pathname, "\\"); |
|
new_part = &pathname[lstrlen(pathname)]; |
|
/* we must now match the first part of the pathname |
|
* in the archive to a component in the installation |
|
* scheme (PURELIB, PLATLIB, HEADERS, SCRIPTS, or DATA) |
|
* and replace this part by the one in the scheme to use |
|
*/ |
|
for (i = 0; scheme[i].name; ++i) { |
|
if (0 == strnicmp(scheme[i].name, fname, |
|
strlen(scheme[i].name))) { |
|
char *rest; |
|
int len; |
|
|
|
/* length of the replaced part */ |
|
int namelen = strlen(scheme[i].name); |
|
|
|
strcat(pathname, scheme[i].prefix); |
|
|
|
rest = fname + namelen; |
|
len = pfhdr->fname_length - namelen; |
|
|
|
if ((pathname[strlen(pathname)-1] != '\\') |
|
&& (pathname[strlen(pathname)-1] != '/')) |
|
strcat(pathname, "\\"); |
|
/* Now that pathname ends with a separator, |
|
* we must make sure rest does not start with |
|
* an additional one. |
|
*/ |
|
if ((rest[0] == '\\') || (rest[0] == '/')) { |
|
++rest; |
|
--len; |
|
} |
|
|
|
strncat(pathname, rest, len); |
|
goto Done; |
|
} |
|
} |
|
/* no prefix to replace found, go unchanged */ |
|
strncat(pathname, fname, pfhdr->fname_length); |
|
Done: |
|
normpath(pathname); |
|
if (pathname[strlen(pathname)-1] != '\\') { |
|
/* |
|
* The local file header (pfhdr) does not always |
|
* contain the compressed and uncompressed sizes of |
|
* the data depending on bit 3 of the flags field. So |
|
* it seems better to use the data from the central |
|
* directory (pcdir). |
|
*/ |
|
dst = map_new_file(0, pathname, new_part, |
|
pcdir->uncomp_size, |
|
pcdir->last_mod_file_date, |
|
pcdir->last_mod_file_time, notify); |
|
if (dst) { |
|
if (!extract_file(dst, pcomp, pfhdr->method, |
|
pcdir->comp_size, |
|
pcdir->uncomp_size, |
|
notify)) |
|
return FALSE; |
|
} /* else ??? */ |
|
} |
|
if (notify) |
|
notify(NUM_FILES, new_part, (int)pe->nTotalCDir, |
|
(int)n+1); |
|
} |
|
return TRUE; |
|
}
|
|
|