mirror of
				https://github.com/python/cpython.git
				synced 2025-10-31 13:41:24 +00:00 
			
		
		
		
	
		
			
	
	
		
			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; | ||
|  | } |