mirror of https://github.com/python/cpython
313 lines
7.3 KiB
C
313 lines
7.3 KiB
C
|
#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;
|
||
|
}
|