| 
									
										
										
										
											2004-07-14 15:17:04 +00:00
										 |  |  | /*
 | 
					
						
							|  |  |  |   IMPORTANT NOTE: IF THIS FILE IS CHANGED, WININST-6.EXE MUST BE RECOMPILED | 
					
						
							|  |  |  |   WITH THE MSVC6 WININST.DSW WORKSPACE FILE MANUALLY, AND WININST-7.1.EXE MUST | 
					
						
							|  |  |  |   BE RECOMPILED WITH THE MSVC 2003.NET WININST-7.1.VCPROJ FILE MANUALLY. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   IF CHANGES TO THIS FILE ARE CHECKED INTO PYTHON CVS, THE RECOMPILED BINARIES | 
					
						
							|  |  |  |   MUST BE CHECKED IN AS WELL! | 
					
						
							|  |  |  | */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2002-11-22 20:39:33 +00:00
										 |  |  | #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; | 
					
						
							|  |  |  | } |