summaryrefslogtreecommitdiffstats
path: root/PC/bdist_wininst/extract.c
diff options
context:
space:
mode:
Diffstat (limited to 'PC/bdist_wininst/extract.c')
-rw-r--r--PC/bdist_wininst/extract.c312
1 files changed, 312 insertions, 0 deletions
diff --git a/PC/bdist_wininst/extract.c b/PC/bdist_wininst/extract.c
new file mode 100644
index 0000000..7e70afb
--- /dev/null
+++ b/PC/bdist_wininst/extract.c
@@ -0,0 +1,312 @@
+#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;
+}