diff options
author | Thomas Heller <theller@ctypes.org> | 2002-11-22 20:39:33 (GMT) |
---|---|---|
committer | Thomas Heller <theller@ctypes.org> | 2002-11-22 20:39:33 (GMT) |
commit | bb4b7d2d68b82803640f6ffbb79a7f694dcbf21c (patch) | |
tree | 0cb69aa43c7902de9adba851ae3955bf3885819b /PC | |
parent | 8bec48316b1ffb4ea153e78b9119d7e77b98f3eb (diff) | |
download | cpython-bb4b7d2d68b82803640f6ffbb79a7f694dcbf21c.zip cpython-bb4b7d2d68b82803640f6ffbb79a7f694dcbf21c.tar.gz cpython-bb4b7d2d68b82803640f6ffbb79a7f694dcbf21c.tar.bz2 |
Copied the sources from the distutils CVS repository.
Changed the MSVC project file to create the exe in the
lib/distutils/command directory, bdist_wininst.py must still be
changed to use it.
Also changed to use the same zlib as the zlib module - this has the nice
sideeffect that now the buggy 1.1.3 version is no longer used.
Most of the source files now conform to PEP 7, except for the maximum
line length. Windows api programming in 78 character lines =:(.
README.txt is a new file, but still empty except for placeholders.
Diffstat (limited to 'PC')
-rw-r--r-- | PC/bdist_wininst/.cvsignore | 4 | ||||
-rw-r--r-- | PC/bdist_wininst/PythonPowered.bmp | bin | 0 -> 2582 bytes | |||
-rw-r--r-- | PC/bdist_wininst/README.txt | 5 | ||||
-rw-r--r-- | PC/bdist_wininst/archive.h | 96 | ||||
-rw-r--r-- | PC/bdist_wininst/extract.c | 312 | ||||
-rw-r--r-- | PC/bdist_wininst/install.c | 2294 | ||||
-rw-r--r-- | PC/bdist_wininst/install.rc | 220 | ||||
-rw-r--r-- | PC/bdist_wininst/resource.h | 38 | ||||
-rw-r--r-- | PC/bdist_wininst/wininst.dsp | 127 | ||||
-rw-r--r-- | PC/bdist_wininst/wininst.dsw | 29 |
10 files changed, 3125 insertions, 0 deletions
diff --git a/PC/bdist_wininst/.cvsignore b/PC/bdist_wininst/.cvsignore new file mode 100644 index 0000000..cc3bc99 --- /dev/null +++ b/PC/bdist_wininst/.cvsignore @@ -0,0 +1,4 @@ +temp-debug +temp-release +wininst.ncb +wininst.plg diff --git a/PC/bdist_wininst/PythonPowered.bmp b/PC/bdist_wininst/PythonPowered.bmp Binary files differnew file mode 100644 index 0000000..dd70709 --- /dev/null +++ b/PC/bdist_wininst/PythonPowered.bmp diff --git a/PC/bdist_wininst/README.txt b/PC/bdist_wininst/README.txt new file mode 100644 index 0000000..7c29be7 --- /dev/null +++ b/PC/bdist_wininst/README.txt @@ -0,0 +1,5 @@ + +XXX Write description +XXX Dont't forget to mention upx + +XXX Add pointer to this file into PC/README.txt diff --git a/PC/bdist_wininst/archive.h b/PC/bdist_wininst/archive.h new file mode 100644 index 0000000..977898c --- /dev/null +++ b/PC/bdist_wininst/archive.h @@ -0,0 +1,96 @@ +#pragma pack(1) + +/* zip-archive headers + * See: http://www.pkware.com/appnote.html + */ + +struct eof_cdir { + long tag; /* must be 0x06054b50 */ + short disknum; + short firstdisk; + short nTotalCDirThis; + short nTotalCDir; + long nBytesCDir; + long ofsCDir; + short commentlen; +}; + +struct cdir { + long tag; /* must be 0x02014b50 */ + short version_made; + short version_extract; + short gp_bitflag; + short comp_method; + short last_mod_file_time; + short last_mod_file_date; + long crc32; + long comp_size; + long uncomp_size; + short fname_length; + short extra_length; + short comment_length; + short disknum_start; + short int_file_attr; + long ext_file_attr; + long ofs_local_header; +}; + +struct fhdr { + long tag; /* must be 0x04034b50 */ + short version_needed; + short flags; + short method; + short last_mod_file_time; + short last_mod_file_date; + long crc32; + long comp_size; + long uncomp_size; + short fname_length; + short extra_length; +}; + + +struct meta_data_hdr { + int tag; + int uncomp_size; + int bitmap_size; +}; + +#pragma pack() + +/* installation scheme */ + +typedef struct tagSCHEME { + char *name; + char *prefix; +} SCHEME; + +typedef int (*NOTIFYPROC)(int code, LPSTR text, ...); + +extern BOOL +extract_file(char *dst, char *src, int method, int comp_size, + int uncomp_size, NOTIFYPROC notify); + +extern BOOL +unzip_archive(SCHEME *scheme, char *dirname, char *data, + DWORD size, NOTIFYPROC notify); + +extern char * +map_new_file(DWORD flags, char *filename, char + *pathname_part, int size, + WORD wFatDate, WORD wFatTime, + NOTIFYPROC callback); + +extern BOOL +ensure_directory (char *pathname, char *new_part, + NOTIFYPROC callback); + +/* codes for NOITIFYPROC */ +#define DIR_CREATED 1 +#define CAN_OVERWRITE 2 +#define FILE_CREATED 3 +#define ZLIB_ERROR 4 +#define SYSTEM_ERROR 5 +#define NUM_FILES 6 +#define FILE_OVERWRITTEN 7 + 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; +} diff --git a/PC/bdist_wininst/install.c b/PC/bdist_wininst/install.c new file mode 100644 index 0000000..597bd9e --- /dev/null +++ b/PC/bdist_wininst/install.c @@ -0,0 +1,2294 @@ +/* + * Written by Thomas Heller, May 2000 + * + * $Id$ + */ + +/* + * Windows Installer program for distutils. + * + * (a kind of self-extracting zip-file) + * + * At runtime, the exefile has appended: + * - compressed setup-data in ini-format, containing the following sections: + * [metadata] + * author=Greg Ward + * author_email=gward@python.net + * description=Python Distribution Utilities + * licence=Python + * name=Distutils + * url=http://www.python.org/sigs/distutils-sig/ + * version=0.9pre + * + * [Setup] + * info= text to be displayed in the edit-box + * title= to be displayed by this program + * target_version = if present, python version required + * pyc_compile = if 0, do not compile py to pyc + * pyo_compile = if 0, do not compile py to pyo + * + * - a struct meta_data_hdr, describing the above + * - a zip-file, containing the modules to be installed. + * for the format see http://www.pkware.com/appnote.html + * + * What does this program do? + * - the setup-data is uncompressed and written to a temporary file. + * - setup-data is queried with GetPrivateProfile... calls + * - [metadata] - info is displayed in the dialog box + * - The registry is searched for installations of python + * - The user can select the python version to use. + * - The python-installation directory (sys.prefix) is displayed + * - When the start-button is pressed, files from the zip-archive + * are extracted to the file system. All .py filenames are stored + * in a list. + */ +/* + * Includes now an uninstaller. + */ + +/* + * To Do: + * + * display some explanation when no python version is found + * instead showing the user an empty listbox to select something from. + * + * Finish the code so that we can use other python installations + * additionaly to those found in the registry, + * and then #define USE_OTHER_PYTHON_VERSIONS + * + * - install a help-button, which will display something meaningful + * to the poor user. + * text to the user + * - should there be a possibility to display a README file + * before starting the installation (if one is present in the archive) + * - more comments about what the code does(?) + * + * - evolve this into a full blown installer (???) + */ + +#include <windows.h> +#include <commctrl.h> +#include <imagehlp.h> +#include <objbase.h> +#include <shlobj.h> +#include <objidl.h> +#include "resource.h" + +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <time.h> + +#include "archive.h" + +/* Only for debugging! + static int dprintf(char *fmt, ...) + { + char Buffer[4096]; + va_list marker; + int result; + + va_start(marker, fmt); + result = wvsprintf(Buffer, fmt, marker); + OutputDebugString(Buffer); + return result; + } +*/ + +/* Bah: global variables */ +FILE *logfile; + +char modulename[MAX_PATH]; + +HWND hwndMain; +HWND hDialog; + +char *ini_file; /* Full pathname of ini-file */ +/* From ini-file */ +char info[4096]; /* [Setup] info= */ +char title[80]; /* [Setup] title=, contains package name + including version: "Distutils-1.0.1" */ +char target_version[10]; /* [Setup] target_version=, required python + version or empty string */ +char build_info[80]; /* [Setup] build_info=, distutils version + and build date */ + +char meta_name[80]; /* package name without version like + 'Distutils' */ +char install_script[MAX_PATH]; + + +int py_major, py_minor; /* Python version selected for installation */ + +char *arc_data; /* memory mapped archive */ +DWORD arc_size; /* number of bytes in archive */ +int exe_size; /* number of bytes for exe-file portion */ +char python_dir[MAX_PATH]; +char pythondll[MAX_PATH]; +BOOL pyc_compile, pyo_compile; + +BOOL success; /* Installation successfull? */ + +HANDLE hBitmap; +char *bitmap_bytes; + + +#define WM_NUMFILES WM_USER+1 +/* wParam: 0, lParam: total number of files */ +#define WM_NEXTFILE WM_USER+2 +/* wParam: number of this file */ +/* lParam: points to pathname */ + +enum { UNSPECIFIED, ALWAYS, NEVER } allow_overwrite = UNSPECIFIED; + +static BOOL notify(int code, char *fmt, ...); + +/* Note: If scheme.prefix is nonempty, it must end with a '\'! */ +/* Note: purelib must be the FIRST entry! */ +SCHEME old_scheme[] = { + { "PURELIB", "" }, + { "PLATLIB", "" }, + { "HEADERS", "" }, /* 'Include/dist_name' part already in archive */ + { "SCRIPTS", "Scripts\\" }, + { "DATA", "" }, + { NULL, NULL }, +}; + +SCHEME new_scheme[] = { + { "PURELIB", "Lib\\site-packages\\" }, + { "PLATLIB", "Lib\\site-packages\\" }, + { "HEADERS", "" }, /* 'Include/dist_name' part already in archive */ + { "SCRIPTS", "Scripts\\" }, + { "DATA", "" }, + { NULL, NULL }, +}; + +static void unescape(char *dst, char *src, unsigned size) +{ + char *eon; + char ch; + + while (src && *src && (size > 2)) { + if (*src == '\\') { + switch (*++src) { + case 'n': + ++src; + *dst++ = '\r'; + *dst++ = '\n'; + size -= 2; + break; + case 'r': + ++src; + *dst++ = '\r'; + --size; + break; + case '0': case '1': case '2': case '3': + ch = (char)strtol(src, &eon, 8); + if (ch == '\n') { + *dst++ = '\r'; + --size; + } + *dst++ = ch; + --size; + src = eon; + } + } else { + *dst++ = *src++; + --size; + } + } + *dst = '\0'; +} + +static struct tagFile { + char *path; + struct tagFile *next; +} *file_list = NULL; + +static void add_to_filelist(char *path) +{ + struct tagFile *p; + p = (struct tagFile *)malloc(sizeof(struct tagFile)); + p->path = strdup(path); + p->next = file_list; + file_list = p; +} + +static int do_compile_files(int (__cdecl * PyRun_SimpleString)(char *), + int optimize) +{ + struct tagFile *p; + int total, n; + char Buffer[MAX_PATH + 64]; + int errors = 0; + + total = 0; + p = file_list; + while (p) { + ++total; + p = p->next; + } + SendDlgItemMessage(hDialog, IDC_PROGRESS, PBM_SETRANGE, 0, + MAKELPARAM(0, total)); + SendDlgItemMessage(hDialog, IDC_PROGRESS, PBM_SETPOS, 0, 0); + + n = 0; + p = file_list; + while (p) { + ++n; + wsprintf(Buffer, + "import py_compile; py_compile.compile (r'%s')", + p->path); + if (PyRun_SimpleString(Buffer)) { + ++errors; + } + /* We send the notification even if the files could not + * be created so that the uninstaller will remove them + * in case they are created later. + */ + wsprintf(Buffer, "%s%c", p->path, optimize ? 'o' : 'c'); + notify(FILE_CREATED, Buffer); + + SendDlgItemMessage(hDialog, IDC_PROGRESS, PBM_SETPOS, n, 0); + SetDlgItemText(hDialog, IDC_INFO, p->path); + p = p->next; + } + return errors; +} + +#define DECLPROC(dll, result, name, args)\ + typedef result (*__PROC__##name) args;\ + result (*name)args = (__PROC__##name)GetProcAddress(dll, #name) + + +#define DECLVAR(dll, type, name)\ + type *name = (type*)GetProcAddress(dll, #name) + +typedef void PyObject; + + +/* + * Returns number of files which failed to compile, + * -1 if python could not be loaded at all + */ +static int compile_filelist(HINSTANCE hPython, BOOL optimize_flag) +{ + DECLPROC(hPython, void, Py_Initialize, (void)); + DECLPROC(hPython, void, Py_SetProgramName, (char *)); + DECLPROC(hPython, void, Py_Finalize, (void)); + DECLPROC(hPython, int, PyRun_SimpleString, (char *)); + DECLPROC(hPython, PyObject *, PySys_GetObject, (char *)); + DECLVAR(hPython, int, Py_OptimizeFlag); + + int errors = 0; + struct tagFile *p = file_list; + + if (!p) + return 0; + + if (!Py_Initialize || !Py_SetProgramName || !Py_Finalize) + return -1; + + if (!PyRun_SimpleString || !PySys_GetObject || !Py_OptimizeFlag) + return -1; + + *Py_OptimizeFlag = optimize_flag ? 1 : 0; + Py_SetProgramName(modulename); + Py_Initialize(); + + errors += do_compile_files(PyRun_SimpleString, optimize_flag); + Py_Finalize(); + + return errors; +} + +typedef PyObject *(*PyCFunction)(PyObject *, PyObject *); + +struct PyMethodDef { + char *ml_name; + PyCFunction ml_meth; + int ml_flags; + char *ml_doc; +}; +typedef struct PyMethodDef PyMethodDef; + +void *(*g_Py_BuildValue)(char *, ...); +int (*g_PyArg_ParseTuple)(PyObject *, char *, ...); + +PyObject *g_PyExc_ValueError; +PyObject *g_PyExc_OSError; + +PyObject *(*g_PyErr_Format)(PyObject *, char *, ...); + +#define DEF_CSIDL(name) { name, #name } + +struct { + int nFolder; + char *name; +} csidl_names[] = { + /* Startup menu for all users. + NT only */ + DEF_CSIDL(CSIDL_COMMON_STARTMENU), + /* Startup menu. */ + DEF_CSIDL(CSIDL_STARTMENU), + +/* DEF_CSIDL(CSIDL_COMMON_APPDATA), */ +/* DEF_CSIDL(CSIDL_LOCAL_APPDATA), */ + /* Repository for application-specific data. + Needs Internet Explorer 4.0 */ + DEF_CSIDL(CSIDL_APPDATA), + + /* The desktop for all users. + NT only */ + DEF_CSIDL(CSIDL_COMMON_DESKTOPDIRECTORY), + /* The desktop. */ + DEF_CSIDL(CSIDL_DESKTOPDIRECTORY), + + /* Startup folder for all users. + NT only */ + DEF_CSIDL(CSIDL_COMMON_STARTUP), + /* Startup folder. */ + DEF_CSIDL(CSIDL_STARTUP), + + /* Programs item in the start menu for all users. + NT only */ + DEF_CSIDL(CSIDL_COMMON_PROGRAMS), + /* Program item in the user's start menu. */ + DEF_CSIDL(CSIDL_PROGRAMS), + +/* DEF_CSIDL(CSIDL_PROGRAM_FILES_COMMON), */ +/* DEF_CSIDL(CSIDL_PROGRAM_FILES), */ + + /* Virtual folder containing fonts. */ + DEF_CSIDL(CSIDL_FONTS), +}; + +#define DIM(a) (sizeof(a) / sizeof((a)[0])) + +static PyObject *FileCreated(PyObject *self, PyObject *args) +{ + char *path; + if (!g_PyArg_ParseTuple(args, "s", &path)) + return NULL; + notify(FILE_CREATED, path); + return g_Py_BuildValue(""); +} + +static PyObject *DirectoryCreated(PyObject *self, PyObject *args) +{ + char *path; + if (!g_PyArg_ParseTuple(args, "s", &path)) + return NULL; + notify(DIR_CREATED, path); + return g_Py_BuildValue(""); +} + +static PyObject *GetSpecialFolderPath(PyObject *self, PyObject *args) +{ + char *name; + char lpszPath[MAX_PATH]; + int i; + static HRESULT (WINAPI *My_SHGetSpecialFolderPath)(HWND hwnd, + LPTSTR lpszPath, + int nFolder, + BOOL fCreate); + + if (!My_SHGetSpecialFolderPath) { + HINSTANCE hLib = LoadLibrary("shell32.dll"); + if (!hLib) { + g_PyErr_Format(g_PyExc_OSError, + "function not available"); + return NULL; + } + My_SHGetSpecialFolderPath = (BOOL (WINAPI *)(HWND, LPTSTR, + int, BOOL)) + GetProcAddress(hLib, + "SHGetSpecialFolderPathA"); + } + + if (!g_PyArg_ParseTuple(args, "s", &name)) + return NULL; + + if (!My_SHGetSpecialFolderPath) { + g_PyErr_Format(g_PyExc_OSError, "function not available"); + return NULL; + } + + for (i = 0; i < DIM(csidl_names); ++i) { + if (0 == strcmpi(csidl_names[i].name, name)) { + int nFolder; + nFolder = csidl_names[i].nFolder; + if (My_SHGetSpecialFolderPath(NULL, lpszPath, + nFolder, 0)) + return g_Py_BuildValue("s", lpszPath); + else { + g_PyErr_Format(g_PyExc_OSError, + "no such folder (%s)", name); + return NULL; + } + + } + }; + g_PyErr_Format(g_PyExc_ValueError, "unknown CSIDL (%s)", name); + return NULL; +} + +static PyObject *CreateShortcut(PyObject *self, PyObject *args) +{ + char *path; /* path and filename */ + char *description; + char *filename; + + char *arguments = NULL; + char *iconpath = NULL; + int iconindex = 0; + char *workdir = NULL; + + WCHAR wszFilename[MAX_PATH]; + + IShellLink *ps1 = NULL; + IPersistFile *pPf = NULL; + + HRESULT hr; + + hr = CoInitialize(NULL); + if (FAILED(hr)) { + g_PyErr_Format(g_PyExc_OSError, + "CoInitialize failed, error 0x%x", hr); + goto error; + } + + if (!g_PyArg_ParseTuple(args, "sss|sssi", + &path, &description, &filename, + &arguments, &workdir, &iconpath, &iconindex)) + return NULL; + + hr = CoCreateInstance(&CLSID_ShellLink, + NULL, + CLSCTX_INPROC_SERVER, + &IID_IShellLink, + &ps1); + if (FAILED(hr)) { + g_PyErr_Format(g_PyExc_OSError, + "CoCreateInstance failed, error 0x%x", hr); + goto error; + } + + hr = ps1->lpVtbl->QueryInterface(ps1, &IID_IPersistFile, + (void **)&pPf); + if (FAILED(hr)) { + g_PyErr_Format(g_PyExc_OSError, + "QueryInterface(IPersistFile) error 0x%x", hr); + goto error; + } + + + hr = ps1->lpVtbl->SetPath(ps1, path); + if (FAILED(hr)) { + g_PyErr_Format(g_PyExc_OSError, + "SetPath() failed, error 0x%x", hr); + goto error; + } + + hr = ps1->lpVtbl->SetDescription(ps1, description); + if (FAILED(hr)) { + g_PyErr_Format(g_PyExc_OSError, + "SetDescription() failed, error 0x%x", hr); + goto error; + } + + if (arguments) { + hr = ps1->lpVtbl->SetArguments(ps1, arguments); + if (FAILED(hr)) { + g_PyErr_Format(g_PyExc_OSError, + "SetArguments() error 0x%x", hr); + goto error; + } + } + + if (iconpath) { + hr = ps1->lpVtbl->SetIconLocation(ps1, iconpath, iconindex); + if (FAILED(hr)) { + g_PyErr_Format(g_PyExc_OSError, + "SetIconLocation() error 0x%x", hr); + goto error; + } + } + + if (workdir) { + hr = ps1->lpVtbl->SetWorkingDirectory(ps1, workdir); + if (FAILED(hr)) { + g_PyErr_Format(g_PyExc_OSError, + "SetWorkingDirectory() error 0x%x", hr); + goto error; + } + } + + MultiByteToWideChar(CP_ACP, 0, + filename, -1, + wszFilename, MAX_PATH); + + hr = pPf->lpVtbl->Save(pPf, wszFilename, TRUE); + if (FAILED(hr)) { + g_PyErr_Format(g_PyExc_OSError, + "Save() failed, error 0x%x", hr); + goto error; + } + + pPf->lpVtbl->Release(pPf); + ps1->lpVtbl->Release(ps1); + CoUninitialize(); + return g_Py_BuildValue(""); + + error: + if (pPf) + pPf->lpVtbl->Release(pPf); + + if (ps1) + ps1->lpVtbl->Release(ps1); + + CoUninitialize(); + + return NULL; +} + +#define METH_VARARGS 0x0001 + +PyMethodDef meth[] = { + {"create_shortcut", CreateShortcut, METH_VARARGS, NULL}, + {"get_special_folder_path", GetSpecialFolderPath, METH_VARARGS, NULL}, + {"file_created", FileCreated, METH_VARARGS, NULL}, + {"directory_created", DirectoryCreated, METH_VARARGS, NULL}, +}; + +/* + * This function returns one of the following error codes: + * 1 if the Python-dll does not export the functions we need + * 2 if no install-script is specified in pathname + * 3 if the install-script file could not be opened + * the return value of PyRun_SimpleFile() otherwise, + * which is 0 if everything is ok, -1 if an exception had occurred + * in the install-script. + */ + +static int +run_installscript(HINSTANCE hPython, char *pathname, int argc, char **argv) +{ + DECLPROC(hPython, void, Py_Initialize, (void)); + DECLPROC(hPython, int, PySys_SetArgv, (int, char **)); + DECLPROC(hPython, int, PyRun_SimpleFile, (FILE *, char *)); + DECLPROC(hPython, void, Py_Finalize, (void)); + DECLPROC(hPython, PyObject *, PyImport_ImportModule, (char *)); + DECLPROC(hPython, int, PyObject_SetAttrString, + (PyObject *, char *, PyObject *)); + DECLPROC(hPython, PyObject *, PyObject_GetAttrString, + (PyObject *, char *)); + DECLPROC(hPython, PyObject *, Py_BuildValue, (char *, ...)); + DECLPROC(hPython, PyObject *, PyCFunction_New, + (PyMethodDef *, PyObject *)); + DECLPROC(hPython, int, PyArg_ParseTuple, (PyObject *, char *, ...)); + DECLPROC(hPython, PyObject *, PyErr_Format, (PyObject *, char *)); + + PyObject *mod; + + int result = 0; + FILE *fp; + + if (!Py_Initialize || !PySys_SetArgv + || !PyRun_SimpleFile || !Py_Finalize) + return 1; + + if (!PyImport_ImportModule || !PyObject_SetAttrString + || !Py_BuildValue) + return 1; + + if (!PyCFunction_New || !PyArg_ParseTuple || !PyErr_Format) + return 1; + + if (!PyObject_GetAttrString) + return 1; + + g_Py_BuildValue = Py_BuildValue; + g_PyArg_ParseTuple = PyArg_ParseTuple; + g_PyErr_Format = PyErr_Format; + + if (pathname == NULL || pathname[0] == '\0') + return 2; + + fp = fopen(pathname, "r"); + if (!fp) { + fprintf(stderr, "Could not open postinstall-script %s\n", + pathname); + return 3; + } + + SetDlgItemText(hDialog, IDC_INFO, "Running Script..."); + + Py_Initialize(); + + mod = PyImport_ImportModule("__builtin__"); + if (mod) { + int i; + + g_PyExc_ValueError = PyObject_GetAttrString(mod, + "ValueError"); + g_PyExc_OSError = PyObject_GetAttrString(mod, "OSError"); + for (i = 0; i < DIM(meth); ++i) { + PyObject_SetAttrString(mod, meth[i].ml_name, + PyCFunction_New(&meth[i], NULL)); + } + } + + PySys_SetArgv(argc, argv); + result = PyRun_SimpleFile(fp, pathname); + Py_Finalize(); + + fclose(fp); + + return result; +} + +static BOOL SystemError(int error, char *msg) +{ + char Buffer[1024]; + int n; + + if (error) { + LPVOID lpMsgBuf; + FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM, + NULL, + error, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPSTR)&lpMsgBuf, + 0, + NULL + ); + strncpy(Buffer, lpMsgBuf, sizeof(Buffer)); + LocalFree(lpMsgBuf); + } else + Buffer[0] = '\0'; + n = lstrlen(Buffer); + _snprintf(Buffer+n, sizeof(Buffer)-n, msg); + MessageBox(hwndMain, Buffer, "Runtime Error", MB_OK | MB_ICONSTOP); + return FALSE; +} + +static BOOL AskOverwrite(char *filename) +{ + int result; + again: + if (allow_overwrite == ALWAYS) + return TRUE; + if (allow_overwrite == NEVER) + return FALSE; + result = MessageBox(hDialog, + "Overwrite existing files?\n" + "\n" + "Press YES to ALWAYS overwrite existing files,\n" + "press NO to NEVER overwrite existing files.", + "Overwrite options", + MB_YESNO | MB_ICONQUESTION); + if (result == IDYES) + allow_overwrite = ALWAYS; + else if (result == IDNO) + allow_overwrite = NEVER; + goto again; +} + +static BOOL notify (int code, char *fmt, ...) +{ + char Buffer[1024]; + va_list marker; + BOOL result = TRUE; + int a, b; + char *cp; + + va_start(marker, fmt); + _vsnprintf(Buffer, sizeof(Buffer), fmt, marker); + + switch (code) { +/* Questions */ + case CAN_OVERWRITE: + result = AskOverwrite(Buffer); + break; + +/* Information notification */ + case DIR_CREATED: + if (logfile) + fprintf(logfile, "100 Made Dir: %s\n", fmt); + break; + + case FILE_CREATED: + if (logfile) + fprintf(logfile, "200 File Copy: %s\n", fmt); + goto add_to_filelist_label; + break; + + case FILE_OVERWRITTEN: + if (logfile) + fprintf(logfile, "200 File Overwrite: %s\n", fmt); + add_to_filelist_label: + if ((cp = strrchr(fmt, '.')) && (0 == strcmp (cp, ".py"))) + add_to_filelist(fmt); + break; + +/* Error Messages */ + case ZLIB_ERROR: + MessageBox(GetFocus(), Buffer, "Error", + MB_OK | MB_ICONWARNING); + break; + + case SYSTEM_ERROR: + SystemError(GetLastError(), Buffer); + break; + + case NUM_FILES: + a = va_arg(marker, int); + b = va_arg(marker, int); + SendMessage(hDialog, WM_NUMFILES, 0, MAKELPARAM(0, a)); + SendMessage(hDialog, WM_NEXTFILE, b,(LPARAM)fmt); + } + va_end(marker); + + return result; +} + +static char *MapExistingFile(char *pathname, DWORD *psize) +{ + HANDLE hFile, hFileMapping; + DWORD nSizeLow, nSizeHigh; + char *data; + + hFile = CreateFile(pathname, + GENERIC_READ, FILE_SHARE_READ, NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, NULL); + if (hFile == INVALID_HANDLE_VALUE) + return NULL; + nSizeLow = GetFileSize(hFile, &nSizeHigh); + hFileMapping = CreateFileMapping(hFile, + NULL, PAGE_READONLY, 0, 0, NULL); + CloseHandle(hFile); + + if (hFileMapping == INVALID_HANDLE_VALUE) + return NULL; + + data = MapViewOfFile(hFileMapping, + FILE_MAP_READ, 0, 0, 0); + + CloseHandle(hFileMapping); + *psize = nSizeLow; + return data; +} + + +static void create_bitmap(HWND hwnd) +{ + BITMAPFILEHEADER *bfh; + BITMAPINFO *bi; + HDC hdc; + + if (!bitmap_bytes) + return; + + if (hBitmap) + return; + + hdc = GetDC(hwnd); + + bfh = (BITMAPFILEHEADER *)bitmap_bytes; + bi = (BITMAPINFO *)(bitmap_bytes + sizeof(BITMAPFILEHEADER)); + + hBitmap = CreateDIBitmap(hdc, + &bi->bmiHeader, + CBM_INIT, + bitmap_bytes + bfh->bfOffBits, + bi, + DIB_RGB_COLORS); + ReleaseDC(hwnd, hdc); +} + +static char *ExtractIniFile(char *data, DWORD size, int *pexe_size) +{ + /* 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; + + int ofs = arc_start - sizeof (struct meta_data_hdr); + + /* read meta_data info */ + struct meta_data_hdr *pmd = (struct meta_data_hdr *)&data[ofs]; + char *src, *dst; + char *ini_file; + char tempdir[MAX_PATH]; + + if (pe->tag != 0x06054b50) { + return NULL; + } + + if (pmd->tag != 0x1234567A || ofs < 0) { + return NULL; + } + + if (pmd->bitmap_size) { + /* Store pointer to bitmap bytes */ + bitmap_bytes = (char *)pmd - pmd->uncomp_size - pmd->bitmap_size; + } + + *pexe_size = ofs - pmd->uncomp_size - pmd->bitmap_size; + + src = ((char *)pmd) - pmd->uncomp_size; + ini_file = malloc(MAX_PATH); /* will be returned, so do not free it */ + if (!ini_file) + return NULL; + if (!GetTempPath(sizeof(tempdir), tempdir) + || !GetTempFileName(tempdir, "~du", 0, ini_file)) { + SystemError(GetLastError(), + "Could not create temporary file"); + return NULL; + } + + dst = map_new_file(CREATE_ALWAYS, ini_file, NULL, pmd->uncomp_size, + 0, 0, NULL/*notify*/); + if (!dst) + return NULL; + memcpy(dst, src, pmd->uncomp_size); + UnmapViewOfFile(dst); + return ini_file; +} + +static void PumpMessages(void) +{ + MSG msg; + while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } +} + +LRESULT CALLBACK +WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + HDC hdc; + HFONT hFont; + int h; + PAINTSTRUCT ps; + switch (msg) { + case WM_PAINT: + hdc = BeginPaint(hwnd, &ps); + h = GetSystemMetrics(SM_CYSCREEN) / 10; + hFont = CreateFont(h, 0, 0, 0, 700, TRUE, + 0, 0, 0, 0, 0, 0, 0, "Times Roman"); + hFont = SelectObject(hdc, hFont); + SetBkMode(hdc, TRANSPARENT); + TextOut(hdc, 15, 15, title, strlen(title)); + SetTextColor(hdc, RGB(255, 255, 255)); + TextOut(hdc, 10, 10, title, strlen(title)); + DeleteObject(SelectObject(hdc, hFont)); + EndPaint(hwnd, &ps); + return 0; + } + return DefWindowProc(hwnd, msg, wParam, lParam); +} + +static HWND CreateBackground(char *title) +{ + WNDCLASS wc; + HWND hwnd; + char buffer[4096]; + + wc.style = CS_VREDRAW | CS_HREDRAW; + wc.lpfnWndProc = WindowProc; + wc.cbWndExtra = 0; + wc.cbClsExtra = 0; + wc.hInstance = GetModuleHandle(NULL); + wc.hIcon = NULL; + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + wc.hbrBackground = CreateSolidBrush(RGB(0, 0, 128)); + wc.lpszMenuName = NULL; + wc.lpszClassName = "SetupWindowClass"; + + if (!RegisterClass(&wc)) + MessageBox(hwndMain, + "Could not register window class", + "Setup.exe", MB_OK); + + wsprintf(buffer, "Setup %s", title); + hwnd = CreateWindow("SetupWindowClass", + buffer, + 0, + 0, 0, + GetSystemMetrics(SM_CXFULLSCREEN), + GetSystemMetrics(SM_CYFULLSCREEN), + NULL, + NULL, + GetModuleHandle(NULL), + NULL); + ShowWindow(hwnd, SW_SHOWMAXIMIZED); + UpdateWindow(hwnd); + return hwnd; +} + +/* + * Center a window on the screen + */ +static void CenterWindow(HWND hwnd) +{ + RECT rc; + int w, h; + + GetWindowRect(hwnd, &rc); + w = GetSystemMetrics(SM_CXSCREEN); + h = GetSystemMetrics(SM_CYSCREEN); + MoveWindow(hwnd, + (w - (rc.right-rc.left))/2, + (h - (rc.bottom-rc.top))/2, + rc.right-rc.left, rc.bottom-rc.top, FALSE); +} + +#include <prsht.h> + +BOOL CALLBACK +IntroDlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + LPNMHDR lpnm; + char Buffer[4096]; + + switch (msg) { + case WM_INITDIALOG: + create_bitmap(hwnd); + if(hBitmap) + SendDlgItemMessage(hwnd, IDC_BITMAP, STM_SETIMAGE, + IMAGE_BITMAP, (LPARAM)hBitmap); + CenterWindow(GetParent(hwnd)); + wsprintf(Buffer, + "This Wizard will install %s on your computer. " + "Click Next to continue " + "or Cancel to exit the Setup Wizard.", + meta_name); + SetDlgItemText(hwnd, IDC_TITLE, Buffer); + SetDlgItemText(hwnd, IDC_INTRO_TEXT, info); + SetDlgItemText(hwnd, IDC_BUILD_INFO, build_info); + return FALSE; + + case WM_NOTIFY: + lpnm = (LPNMHDR) lParam; + + switch (lpnm->code) { + case PSN_SETACTIVE: + PropSheet_SetWizButtons(GetParent(hwnd), PSWIZB_NEXT); + break; + + case PSN_WIZNEXT: + break; + + case PSN_RESET: + break; + + default: + break; + } + } + return FALSE; +} + +#ifdef USE_OTHER_PYTHON_VERSIONS +/* These are really private variables used to communicate + * between StatusRoutine and CheckPythonExe + */ +char bound_image_dll[_MAX_PATH]; +int bound_image_major; +int bound_image_minor; + +static BOOL __stdcall StatusRoutine(IMAGEHLP_STATUS_REASON reason, + PSTR ImageName, + PSTR DllName, + ULONG Va, + ULONG Parameter) +{ + char fname[_MAX_PATH]; + int int_version; + + switch(reason) { + case BindOutOfMemory: + case BindRvaToVaFailed: + case BindNoRoomInImage: + case BindImportProcedureFailed: + break; + + case BindImportProcedure: + case BindForwarder: + case BindForwarderNOT: + case BindImageModified: + case BindExpandFileHeaders: + case BindImageComplete: + case BindSymbolsNotUpdated: + case BindMismatchedSymbols: + case BindImportModuleFailed: + break; + + case BindImportModule: + if (1 == sscanf(DllName, "python%d", &int_version)) { + SearchPath(NULL, DllName, NULL, sizeof(fname), + fname, NULL); + strcpy(bound_image_dll, fname); + bound_image_major = int_version / 10; + bound_image_minor = int_version % 10; + OutputDebugString("BOUND "); + OutputDebugString(fname); + OutputDebugString("\n"); + } + break; + } + return TRUE; +} + +/* + */ +static LPSTR get_sys_prefix(LPSTR exe, LPSTR dll) +{ + void (__cdecl * Py_Initialize)(void); + void (__cdecl * Py_SetProgramName)(char *); + void (__cdecl * Py_Finalize)(void); + void* (__cdecl * PySys_GetObject)(char *); + void (__cdecl * PySys_SetArgv)(int, char **); + char* (__cdecl * Py_GetPrefix)(void); + char* (__cdecl * Py_GetPath)(void); + HINSTANCE hPython; + LPSTR prefix = NULL; + int (__cdecl * PyRun_SimpleString)(char *); + + { + char Buffer[256]; + wsprintf(Buffer, "PYTHONHOME=%s", exe); + *strrchr(Buffer, '\\') = '\0'; +// MessageBox(GetFocus(), Buffer, "PYTHONHOME", MB_OK); + _putenv(Buffer); + _putenv("PYTHONPATH="); + } + + hPython = LoadLibrary(dll); + if (!hPython) + return NULL; + Py_Initialize = (void (*)(void))GetProcAddress + (hPython,"Py_Initialize"); + + PySys_SetArgv = (void (*)(int, char **))GetProcAddress + (hPython,"PySys_SetArgv"); + + PyRun_SimpleString = (int (*)(char *))GetProcAddress + (hPython,"PyRun_SimpleString"); + + Py_SetProgramName = (void (*)(char *))GetProcAddress + (hPython,"Py_SetProgramName"); + + PySys_GetObject = (void* (*)(char *))GetProcAddress + (hPython,"PySys_GetObject"); + + Py_GetPrefix = (char * (*)(void))GetProcAddress + (hPython,"Py_GetPrefix"); + + Py_GetPath = (char * (*)(void))GetProcAddress + (hPython,"Py_GetPath"); + + Py_Finalize = (void (*)(void))GetProcAddress(hPython, + "Py_Finalize"); + Py_SetProgramName(exe); + Py_Initialize(); + PySys_SetArgv(1, &exe); + + MessageBox(GetFocus(), Py_GetPrefix(), "PREFIX", MB_OK); + MessageBox(GetFocus(), Py_GetPath(), "PATH", MB_OK); + + Py_Finalize(); + FreeLibrary(hPython); + + return prefix; +} + +static BOOL +CheckPythonExe(LPSTR pathname, LPSTR version, int *pmajor, int *pminor) +{ + bound_image_dll[0] = '\0'; + if (!BindImageEx(BIND_NO_BOUND_IMPORTS | BIND_NO_UPDATE | BIND_ALL_IMAGES, + pathname, + NULL, + NULL, + StatusRoutine)) + return SystemError(0, "Could not bind image"); + if (bound_image_dll[0] == '\0') + return SystemError(0, "Does not seem to be a python executable"); + *pmajor = bound_image_major; + *pminor = bound_image_minor; + if (version && *version) { + char core_version[12]; + wsprintf(core_version, "%d.%d", bound_image_major, bound_image_minor); + if (strcmp(version, core_version)) + return SystemError(0, "Wrong Python version"); + } + get_sys_prefix(pathname, bound_image_dll); + return TRUE; +} + +/* + * Browse for other python versions. Insert it into the listbox specified + * by hwnd. version, if not NULL or empty, is the version required. + */ +static BOOL GetOtherPythonVersion(HWND hwnd, LPSTR version) +{ + char vers_name[_MAX_PATH + 80]; + DWORD itemindex; + OPENFILENAME of; + char pathname[_MAX_PATH]; + DWORD result; + + strcpy(pathname, "python.exe"); + + memset(&of, 0, sizeof(of)); + of.lStructSize = sizeof(OPENFILENAME); + of.hwndOwner = GetParent(hwnd); + of.hInstance = NULL; + of.lpstrFilter = "python.exe\0python.exe\0"; + of.lpstrCustomFilter = NULL; + of.nMaxCustFilter = 0; + of.nFilterIndex = 1; + of.lpstrFile = pathname; + of.nMaxFile = sizeof(pathname); + of.lpstrFileTitle = NULL; + of.nMaxFileTitle = 0; + of.lpstrInitialDir = NULL; + of.lpstrTitle = "Python executable"; + of.Flags = OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST; + of.lpstrDefExt = "exe"; + + result = GetOpenFileName(&of); + if (result) { + int major, minor; + if (!CheckPythonExe(pathname, version, &major, &minor)) { + return FALSE; + } + *strrchr(pathname, '\\') = '\0'; + wsprintf(vers_name, "Python Version %d.%d in %s", + major, minor, pathname); + itemindex = SendMessage(hwnd, LB_INSERTSTRING, -1, + (LPARAM)(LPSTR)vers_name); + SendMessage(hwnd, LB_SETCURSEL, itemindex, 0); + SendMessage(hwnd, LB_SETITEMDATA, itemindex, + (LPARAM)(LPSTR)strdup(pathname)); + return TRUE; + } + return FALSE; +} +#endif /* USE_OTHER_PYTHON_VERSIONS */ + + +/* + * Fill the listbox specified by hwnd with all python versions found + * in the registry. version, if not NULL or empty, is the version + * required. + */ +static BOOL GetPythonVersions(HWND hwnd, HKEY hkRoot, LPSTR version) +{ + DWORD index = 0; + char core_version[80]; + HKEY hKey; + BOOL result = TRUE; + DWORD bufsize; + + if (ERROR_SUCCESS != RegOpenKeyEx(hkRoot, + "Software\\Python\\PythonCore", + 0, KEY_READ, &hKey)) + return FALSE; + bufsize = sizeof(core_version); + while (ERROR_SUCCESS == RegEnumKeyEx(hKey, index, + core_version, &bufsize, NULL, + NULL, NULL, NULL)) { + char subkey_name[80], vers_name[80], prefix_buf[MAX_PATH+1]; + int itemindex; + DWORD value_size; + HKEY hk; + + bufsize = sizeof(core_version); + ++index; + if (version && *version && strcmp(version, core_version)) + continue; + + wsprintf(vers_name, "Python Version %s (found in registry)", + core_version); + wsprintf(subkey_name, + "Software\\Python\\PythonCore\\%s\\InstallPath", + core_version); + value_size = sizeof(subkey_name); + if (ERROR_SUCCESS == RegOpenKeyEx(hkRoot, subkey_name, 0, KEY_READ, &hk)) { + if (ERROR_SUCCESS == RegQueryValueEx(hk, NULL, NULL, NULL, prefix_buf, + &value_size)) { + itemindex = SendMessage(hwnd, LB_ADDSTRING, 0, + (LPARAM)(LPSTR)vers_name); + SendMessage(hwnd, LB_SETITEMDATA, itemindex, + (LPARAM)(LPSTR)strdup(prefix_buf)); + } + RegCloseKey(hk); + } + } + RegCloseKey(hKey); + return result; +} + +/* Return the installation scheme depending on Python version number */ +SCHEME *GetScheme(int major, int minor) +{ + if (major > 2) + return new_scheme; + else if((major == 2) && (minor >= 2)) + return new_scheme; + return old_scheme; +} + +BOOL CALLBACK +SelectPythonDlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + LPNMHDR lpnm; + + switch (msg) { + case WM_INITDIALOG: + if (hBitmap) + SendDlgItemMessage(hwnd, IDC_BITMAP, STM_SETIMAGE, + IMAGE_BITMAP, (LPARAM)hBitmap); + GetPythonVersions(GetDlgItem(hwnd, IDC_VERSIONS_LIST), + HKEY_LOCAL_MACHINE, target_version); + GetPythonVersions(GetDlgItem(hwnd, IDC_VERSIONS_LIST), + HKEY_CURRENT_USER, target_version); + { /* select the last entry which is the highest python + version found */ + int count; + count = SendDlgItemMessage(hwnd, IDC_VERSIONS_LIST, + LB_GETCOUNT, 0, 0); + if (count && count != LB_ERR) + SendDlgItemMessage(hwnd, IDC_VERSIONS_LIST, LB_SETCURSEL, + count-1, 0); + + /* If a specific Python version is required, + * display a prominent notice showing this fact. + */ + if (target_version && target_version[0]) { + char buffer[4096]; + wsprintf(buffer, + "Python %s is required for this package. " + "Select installation to use:", + target_version); + SetDlgItemText(hwnd, IDC_TITLE, buffer); + } + + if (count == 0) { + char Buffer[4096]; + char *msg; + if (target_version && target_version[0]) { + wsprintf(Buffer, + "Python version %s required, which was not found" + " in the registry.", target_version); + msg = Buffer; + } else + msg = "No Python installation found in the registry."; + MessageBox(hwnd, msg, "Cannot install", + MB_OK | MB_ICONSTOP); + } + } + goto UpdateInstallDir; + break; + + case WM_COMMAND: + switch (LOWORD(wParam)) { +/* + case IDC_OTHERPYTHON: + if (GetOtherPythonVersion(GetDlgItem(hwnd, IDC_VERSIONS_LIST), + target_version)) + goto UpdateInstallDir; + break; +*/ + case IDC_VERSIONS_LIST: + switch (HIWORD(wParam)) { + int id; + char *cp; + case LBN_SELCHANGE: + UpdateInstallDir: + PropSheet_SetWizButtons(GetParent(hwnd), + PSWIZB_BACK | PSWIZB_NEXT); + id = SendDlgItemMessage(hwnd, IDC_VERSIONS_LIST, + LB_GETCURSEL, 0, 0); + if (id == LB_ERR) { + PropSheet_SetWizButtons(GetParent(hwnd), + PSWIZB_BACK); + SetDlgItemText(hwnd, IDC_PATH, ""); + SetDlgItemText(hwnd, IDC_INSTALL_PATH, ""); + strcpy(python_dir, ""); + strcpy(pythondll, ""); + } else { + char *pbuf; + int result; + PropSheet_SetWizButtons(GetParent(hwnd), + PSWIZB_BACK | PSWIZB_NEXT); + /* Get the python directory */ + cp = (LPSTR)SendDlgItemMessage(hwnd, + IDC_VERSIONS_LIST, + LB_GETITEMDATA, + id, + 0); + strcpy(python_dir, cp); + SetDlgItemText(hwnd, IDC_PATH, python_dir); + /* retrieve the python version and pythondll to use */ + result = SendDlgItemMessage(hwnd, IDC_VERSIONS_LIST, + LB_GETTEXTLEN, (WPARAM)id, 0); + pbuf = (char *)malloc(result + 1); + if (pbuf) { + /* guess the name of the python-dll */ + SendDlgItemMessage(hwnd, IDC_VERSIONS_LIST, + LB_GETTEXT, (WPARAM)id, + (LPARAM)pbuf); + result = sscanf(pbuf, "Python Version %d.%d", + &py_major, &py_minor); + if (result == 2) +#ifdef _DEBUG + wsprintf(pythondll, "c:\\python22\\PCBuild\\python%d%d_d.dll", + py_major, py_minor); +#else + wsprintf(pythondll, "python%d%d.dll", + py_major, py_minor); +#endif + free(pbuf); + } else + strcpy(pythondll, ""); + /* retrieve the scheme for this version */ + { + char install_path[_MAX_PATH]; + SCHEME *scheme = GetScheme(py_major, py_minor); + strcpy(install_path, python_dir); + if (install_path[strlen(install_path)-1] != '\\') + strcat(install_path, "\\"); + strcat(install_path, scheme[0].prefix); + SetDlgItemText(hwnd, IDC_INSTALL_PATH, install_path); + } + } + } + break; + } + return 0; + + case WM_NOTIFY: + lpnm = (LPNMHDR) lParam; + + switch (lpnm->code) { + int id; + case PSN_SETACTIVE: + id = SendDlgItemMessage(hwnd, IDC_VERSIONS_LIST, + LB_GETCURSEL, 0, 0); + if (id == LB_ERR) + PropSheet_SetWizButtons(GetParent(hwnd), + PSWIZB_BACK); + else + PropSheet_SetWizButtons(GetParent(hwnd), + PSWIZB_BACK | PSWIZB_NEXT); + break; + + case PSN_WIZNEXT: + break; + + case PSN_WIZFINISH: + break; + + case PSN_RESET: + break; + + default: + break; + } + } + return 0; +} + +static BOOL OpenLogfile(char *dir) +{ + char buffer[_MAX_PATH+1]; + time_t ltime; + struct tm *now; + long result; + HKEY hKey, hSubkey; + char subkey_name[256]; + static char KeyName[] = + "Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall"; + DWORD disposition; + + result = RegOpenKeyEx(HKEY_LOCAL_MACHINE, + KeyName, + 0, + KEY_CREATE_SUB_KEY, + &hKey); + if (result != ERROR_SUCCESS) { + if (result == ERROR_ACCESS_DENIED) { + MessageBox(GetFocus(), + "You do not seem to have sufficient access rights\n" + "on this machine to install this software", + NULL, + MB_OK | MB_ICONSTOP); + return FALSE; + } else { + MessageBox(GetFocus(), KeyName, "Could not open key", MB_OK); + } + } + + sprintf(buffer, "%s\\%s-wininst.log", dir, meta_name); + logfile = fopen(buffer, "a"); + time(<ime); + now = localtime(<ime); + strftime(buffer, sizeof(buffer), + "*** Installation started %Y/%m/%d %H:%M ***\n", + localtime(<ime)); + fprintf(logfile, buffer); + fprintf(logfile, "Source: %s\n", modulename); + + sprintf(subkey_name, "%s-py%d.%d", meta_name, py_major, py_minor); + + result = RegCreateKeyEx(hKey, subkey_name, + 0, NULL, 0, + KEY_WRITE, + NULL, + &hSubkey, + &disposition); + + if (result != ERROR_SUCCESS) + MessageBox(GetFocus(), subkey_name, "Could not create key", MB_OK); + + RegCloseKey(hKey); + + if (disposition == REG_CREATED_NEW_KEY) + fprintf(logfile, "020 Reg DB Key: [%s]%s\n", KeyName, subkey_name); + + sprintf(buffer, "Python %d.%d %s", py_major, py_minor, title); + + result = RegSetValueEx(hSubkey, "DisplayName", + 0, + REG_SZ, + buffer, + strlen(buffer)+1); + + if (result != ERROR_SUCCESS) + MessageBox(GetFocus(), buffer, "Could not set key value", MB_OK); + + fprintf(logfile, "040 Reg DB Value: [%s\\%s]%s=%s\n", + KeyName, subkey_name, "DisplayName", buffer); + + { + FILE *fp; + sprintf(buffer, "%s\\Remove%s.exe", dir, meta_name); + fp = fopen(buffer, "wb"); + fwrite(arc_data, exe_size, 1, fp); + fclose(fp); + + sprintf(buffer, "\"%s\\Remove%s.exe\" -u \"%s\\%s-wininst.log\"", + dir, meta_name, dir, meta_name); + + result = RegSetValueEx(hSubkey, "UninstallString", + 0, + REG_SZ, + buffer, + strlen(buffer)+1); + + if (result != ERROR_SUCCESS) + MessageBox(GetFocus(), buffer, "Could not set key value", MB_OK); + + fprintf(logfile, "040 Reg DB Value: [%s\\%s]%s=%s\n", + KeyName, subkey_name, "UninstallString", buffer); + } + return TRUE; +} + +static void CloseLogfile(void) +{ + char buffer[_MAX_PATH+1]; + time_t ltime; + struct tm *now; + + time(<ime); + now = localtime(<ime); + strftime(buffer, sizeof(buffer), + "*** Installation finished %Y/%m/%d %H:%M ***\n", + localtime(<ime)); + fprintf(logfile, buffer); + if (logfile) + fclose(logfile); +} + +BOOL CALLBACK +InstallFilesDlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + LPNMHDR lpnm; + char Buffer[4096]; + SCHEME *scheme; + + switch (msg) { + case WM_INITDIALOG: + if (hBitmap) + SendDlgItemMessage(hwnd, IDC_BITMAP, STM_SETIMAGE, + IMAGE_BITMAP, (LPARAM)hBitmap); + wsprintf(Buffer, + "Click Next to begin the installation of %s. " + "If you want to review or change any of your " + " installation settings, click Back. " + "Click Cancel to exit the wizard.", + meta_name); + SetDlgItemText(hwnd, IDC_TITLE, Buffer); + break; + + case WM_NUMFILES: + SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0, lParam); + PumpMessages(); + return TRUE; + + case WM_NEXTFILE: + SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, wParam, + 0); + SetDlgItemText(hwnd, IDC_INFO, (LPSTR)lParam); + PumpMessages(); + return TRUE; + + case WM_NOTIFY: + lpnm = (LPNMHDR) lParam; + + switch (lpnm->code) { + case PSN_SETACTIVE: + PropSheet_SetWizButtons(GetParent(hwnd), + PSWIZB_BACK | PSWIZB_NEXT); + break; + + case PSN_WIZFINISH: + break; + + case PSN_WIZNEXT: + /* Handle a Next button click here */ + hDialog = hwnd; + + /* Make sure the installation directory name ends in a */ + /* backslash */ + if (python_dir[strlen(python_dir)-1] != '\\') + strcat(python_dir, "\\"); + /* Strip the trailing backslash again */ + python_dir[strlen(python_dir)-1] = '\0'; + + if (!OpenLogfile(python_dir)) + break; + +/* + * The scheme we have to use depends on the Python version... + if sys.version < "2.2": + WINDOWS_SCHEME = { + 'purelib': '$base', + 'platlib': '$base', + 'headers': '$base/Include/$dist_name', + 'scripts': '$base/Scripts', + 'data' : '$base', + } + else: + WINDOWS_SCHEME = { + 'purelib': '$base/Lib/site-packages', + 'platlib': '$base/Lib/site-packages', + 'headers': '$base/Include/$dist_name', + 'scripts': '$base/Scripts', + 'data' : '$base', + } +*/ + scheme = GetScheme(py_major, py_minor); + + /* Extract all files from the archive */ + SetDlgItemText(hwnd, IDC_TITLE, "Installing files..."); + success = unzip_archive(scheme, + python_dir, arc_data, + arc_size, notify); + /* Compile the py-files */ + if (pyc_compile) { + int errors; + HINSTANCE hPython; + SetDlgItemText(hwnd, IDC_TITLE, + "Compiling files to .pyc..."); + + SetDlgItemText(hDialog, IDC_INFO, "Loading python..."); + hPython = LoadLibrary(pythondll); + if (hPython) { + errors = compile_filelist(hPython, FALSE); + FreeLibrary(hPython); + } + /* Compilation errors are intentionally ignored: + * Python2.0 contains a bug which will result + * in sys.path containing garbage under certain + * circumstances, and an error message will only + * confuse the user. + */ + } + if (pyo_compile) { + int errors; + HINSTANCE hPython; + SetDlgItemText(hwnd, IDC_TITLE, + "Compiling files to .pyo..."); + + SetDlgItemText(hDialog, IDC_INFO, "Loading python..."); + hPython = LoadLibrary(pythondll); + if (hPython) { + errors = compile_filelist(hPython, TRUE); + FreeLibrary(hPython); + } + /* Errors ignored: see above */ + } + + + break; + + case PSN_RESET: + break; + + default: + break; + } + } + return 0; +} + + +BOOL CALLBACK +FinishedDlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + LPNMHDR lpnm; + + switch (msg) { + case WM_INITDIALOG: + if (hBitmap) + SendDlgItemMessage(hwnd, IDC_BITMAP, STM_SETIMAGE, + IMAGE_BITMAP, (LPARAM)hBitmap); + if (!success) + SetDlgItemText(hwnd, IDC_INFO, "Installation failed."); + + /* async delay: will show the dialog box completely before + the install_script is started */ + PostMessage(hwnd, WM_USER, 0, 0L); + return TRUE; + + case WM_USER: + + if (install_script && install_script[0]) { + char fname[MAX_PATH]; + char *tempname; + FILE *fp; + char buffer[4096]; + int n; + HCURSOR hCursor; + HINSTANCE hPython; + + char *argv[3] = {NULL, "-install", NULL}; + + SetDlgItemText(hwnd, IDC_TITLE, + "Please wait while running postinstall script..."); + strcpy(fname, python_dir); + strcat(fname, "\\Scripts\\"); + strcat(fname, install_script); + + if (logfile) + fprintf(logfile, "300 Run Script: [%s]%s\n", pythondll, fname); + + tempname = tmpnam(NULL); + + if (!freopen(tempname, "a", stderr)) + MessageBox(GetFocus(), "freopen stderr", NULL, MB_OK); + if (!freopen(tempname, "a", stdout)) + MessageBox(GetFocus(), "freopen stdout", NULL, MB_OK); +/* + if (0 != setvbuf(stdout, NULL, _IONBF, 0)) + MessageBox(GetFocus(), "setvbuf stdout", NULL, MB_OK); +*/ + hCursor = SetCursor(LoadCursor(NULL, IDC_WAIT)); + + argv[0] = fname; + + hPython = LoadLibrary(pythondll); + if (hPython) { + int result; + result = run_installscript(hPython, fname, 2, argv); + if (-1 == result) { + fprintf(stderr, "*** run_installscript: internal error 0x%X ***\n", result); + } + FreeLibrary(hPython); + } else { + fprintf(stderr, "*** Could not load Python ***"); + } + fflush(stderr); + fflush(stdout); + + fp = fopen(tempname, "rb"); + n = fread(buffer, 1, sizeof(buffer), fp); + fclose(fp); + remove(tempname); + + buffer[n] = '\0'; + + SetDlgItemText(hwnd, IDC_INFO, buffer); + SetDlgItemText(hwnd, IDC_TITLE, + "Postinstall script finished.\n" + "Click the Finish button to exit the Setup wizard."); + + SetCursor(hCursor); + CloseLogfile(); + } + + return TRUE; + + case WM_NOTIFY: + lpnm = (LPNMHDR) lParam; + + switch (lpnm->code) { + case PSN_SETACTIVE: /* Enable the Finish button */ + PropSheet_SetWizButtons(GetParent(hwnd), PSWIZB_FINISH); + break; + + case PSN_WIZNEXT: + break; + + case PSN_WIZFINISH: + break; + + case PSN_RESET: + break; + + default: + break; + } + } + return 0; +} + +void RunWizard(HWND hwnd) +{ + PROPSHEETPAGE psp = {0}; + HPROPSHEETPAGE ahpsp[4] = {0}; + PROPSHEETHEADER psh = {0}; + + /* Display module information */ + psp.dwSize = sizeof(psp); + psp.dwFlags = PSP_DEFAULT|PSP_HIDEHEADER; + psp.hInstance = GetModuleHandle (NULL); + psp.lParam = 0; + psp.pfnDlgProc = IntroDlgProc; + psp.pszTemplate = MAKEINTRESOURCE(IDD_INTRO); + + ahpsp[0] = CreatePropertySheetPage(&psp); + + /* Select python version to use */ + psp.dwFlags = PSP_DEFAULT|PSP_HIDEHEADER; + psp.pszTemplate = MAKEINTRESOURCE(IDD_SELECTPYTHON); + psp.pfnDlgProc = SelectPythonDlgProc; + + ahpsp[1] = CreatePropertySheetPage(&psp); + + /* Install the files */ + psp.dwFlags = PSP_DEFAULT|PSP_HIDEHEADER; + psp.pszTemplate = MAKEINTRESOURCE(IDD_INSTALLFILES); + psp.pfnDlgProc = InstallFilesDlgProc; + + ahpsp[2] = CreatePropertySheetPage(&psp); + + /* Show success or failure */ + psp.dwFlags = PSP_DEFAULT|PSP_HIDEHEADER; + psp.pszTemplate = MAKEINTRESOURCE(IDD_FINISHED); + psp.pfnDlgProc = FinishedDlgProc; + + ahpsp[3] = CreatePropertySheetPage(&psp); + + /* Create the property sheet */ + psh.dwSize = sizeof(psh); + psh.hInstance = GetModuleHandle(NULL); + psh.hwndParent = hwnd; + psh.phpage = ahpsp; + psh.dwFlags = PSH_WIZARD/*97*//*|PSH_WATERMARK|PSH_HEADER*/; + psh.pszbmWatermark = NULL; + psh.pszbmHeader = NULL; + psh.nStartPage = 0; + psh.nPages = 4; + + PropertySheet(&psh); +} + +int DoInstall(void) +{ + char ini_buffer[4096]; + + /* Read installation information */ + GetPrivateProfileString("Setup", "title", "", ini_buffer, + sizeof(ini_buffer), ini_file); + unescape(title, ini_buffer, sizeof(title)); + + GetPrivateProfileString("Setup", "info", "", ini_buffer, + sizeof(ini_buffer), ini_file); + unescape(info, ini_buffer, sizeof(info)); + + GetPrivateProfileString("Setup", "build_info", "", build_info, + sizeof(build_info), ini_file); + + pyc_compile = GetPrivateProfileInt("Setup", "target_compile", 1, + ini_file); + pyo_compile = GetPrivateProfileInt("Setup", "target_optimize", 1, + ini_file); + + GetPrivateProfileString("Setup", "target_version", "", + target_version, sizeof(target_version), + ini_file); + + GetPrivateProfileString("metadata", "name", "", + meta_name, sizeof(meta_name), + ini_file); + + GetPrivateProfileString("Setup", "install_script", "", + install_script, sizeof(install_script), + ini_file); + + + hwndMain = CreateBackground(title); + + RunWizard(hwndMain); + + /* Clean up */ + UnmapViewOfFile(arc_data); + if (ini_file) + DeleteFile(ini_file); + + if (hBitmap) + DeleteObject(hBitmap); + + return 0; +} + +/*********************** uninstall section ******************************/ + +static int compare(const void *p1, const void *p2) +{ + return strcmp(*(char **)p2, *(char **)p1); +} + +/* + * Commit suicide (remove the uninstaller itself). + * + * Create a batch file to first remove the uninstaller + * (will succeed after it has finished), then the batch file itself. + * + * This technique has been demonstrated by Jeff Richter, + * MSJ 1/1996 + */ +void remove_exe(void) +{ + char exename[_MAX_PATH]; + char batname[_MAX_PATH]; + FILE *fp; + STARTUPINFO si; + PROCESS_INFORMATION pi; + + GetModuleFileName(NULL, exename, sizeof(exename)); + sprintf(batname, "%s.bat", exename); + fp = fopen(batname, "w"); + fprintf(fp, ":Repeat\n"); + fprintf(fp, "del \"%s\"\n", exename); + fprintf(fp, "if exist \"%s\" goto Repeat\n", exename); + fprintf(fp, "del \"%s\"\n", batname); + fclose(fp); + + ZeroMemory(&si, sizeof(si)); + si.cb = sizeof(si); + si.dwFlags = STARTF_USESHOWWINDOW; + si.wShowWindow = SW_HIDE; + if (CreateProcess(NULL, + batname, + NULL, + NULL, + FALSE, + CREATE_SUSPENDED | IDLE_PRIORITY_CLASS, + NULL, + "\\", + &si, + &pi)) { + SetThreadPriority(pi.hThread, THREAD_PRIORITY_IDLE); + SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL); + SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS); + CloseHandle(pi.hProcess); + ResumeThread(pi.hThread); + CloseHandle(pi.hThread); + } +} + +void DeleteRegistryKey(char *string) +{ + char *keyname; + char *subkeyname; + char *delim; + HKEY hKey; + long result; + char *line; + + line = strdup(string); /* so we can change it */ + + keyname = strchr(line, '['); + if (!keyname) + return; + ++keyname; + + subkeyname = strchr(keyname, ']'); + if (!subkeyname) + return; + *subkeyname++='\0'; + delim = strchr(subkeyname, '\n'); + if (delim) + *delim = '\0'; + + result = RegOpenKeyEx(HKEY_LOCAL_MACHINE, + keyname, + 0, + KEY_WRITE, + &hKey); + + if (result != ERROR_SUCCESS) + MessageBox(GetFocus(), string, "Could not open key", MB_OK); + else { + result = RegDeleteKey(hKey, subkeyname); + if (result != ERROR_SUCCESS) + MessageBox(GetFocus(), string, "Could not delete key", MB_OK); + RegCloseKey(hKey); + } + free(line); +} + +void DeleteRegistryValue(char *string) +{ + char *keyname; + char *valuename; + char *value; + HKEY hKey; + long result; + char *line; + + line = strdup(string); /* so we can change it */ + +/* Format is 'Reg DB Value: [key]name=value' */ + keyname = strchr(line, '['); + if (!keyname) + return; + ++keyname; + valuename = strchr(keyname, ']'); + if (!valuename) + return; + *valuename++ = '\0'; + value = strchr(valuename, '='); + if (!value) + return; + + *value++ = '\0'; + + result = RegOpenKeyEx(HKEY_LOCAL_MACHINE, + keyname, + 0, + KEY_WRITE, + &hKey); + if (result != ERROR_SUCCESS) + MessageBox(GetFocus(), string, "Could not open key", MB_OK); + else { + result = RegDeleteValue(hKey, valuename); + if (result != ERROR_SUCCESS) + MessageBox(GetFocus(), string, "Could not delete value", MB_OK); + RegCloseKey(hKey); + } + free(line); +} + +BOOL MyDeleteFile(char *line) +{ + char *pathname = strchr(line, ':'); + if (!pathname) + return FALSE; + ++pathname; + while (isspace(*pathname)) + ++pathname; + return DeleteFile(pathname); +} + +BOOL MyRemoveDirectory(char *line) +{ + char *pathname = strchr(line, ':'); + if (!pathname) + return FALSE; + ++pathname; + while (isspace(*pathname)) + ++pathname; + return RemoveDirectory(pathname); +} + +BOOL Run_RemoveScript(char *line) +{ + char *dllname; + char *scriptname; + static char lastscript[MAX_PATH]; + +/* Format is 'Run Scripts: [pythondll]scriptname' */ +/* XXX Currently, pythondll carries no path!!! */ + dllname = strchr(line, '['); + if (!dllname) + return FALSE; + ++dllname; + scriptname = strchr(dllname, ']'); + if (!scriptname) + return FALSE; + *scriptname++ = '\0'; + /* this function may be called more than one time with the same + script, only run it one time */ + if (strcmp(lastscript, scriptname)) { + HINSTANCE hPython; + char *argv[3] = {NULL, "-remove", NULL}; + char buffer[4096]; + FILE *fp; + char *tempname; + int n; + + argv[0] = scriptname; + + tempname = tmpnam(NULL); + + if (!freopen(tempname, "a", stderr)) + MessageBox(GetFocus(), "freopen stderr", NULL, MB_OK); + if (!freopen(tempname, "a", stdout)) + MessageBox(GetFocus(), "freopen stdout", NULL, MB_OK); + + hPython = LoadLibrary(dllname); + if (hPython) { + if (0x80000000 == run_installscript(hPython, scriptname, 2, argv)) + fprintf(stderr, "*** Could not load Python ***"); + FreeLibrary(hPython); + } + + fflush(stderr); + fflush(stdout); + + fp = fopen(tempname, "rb"); + n = fread(buffer, 1, sizeof(buffer), fp); + fclose(fp); + remove(tempname); + + buffer[n] = '\0'; + if (buffer[0]) + MessageBox(GetFocus(), buffer, "uninstall-script", MB_OK); + + strcpy(lastscript, scriptname); + } + return TRUE; +} + +int DoUninstall(int argc, char **argv) +{ + FILE *logfile; + char buffer[4096]; + int nLines = 0; + int i; + char *cp; + int nFiles = 0; + int nDirs = 0; + int nErrors = 0; + char **lines; + int lines_buffer_size = 10; + + if (argc != 3) { + MessageBox(NULL, + "Wrong number of args", + NULL, + MB_OK); + return 1; /* Error */ + } + if (strcmp(argv[1], "-u")) { + MessageBox(NULL, + "2. arg is not -u", + NULL, + MB_OK); + return 1; /* Error */ + } + + { + DWORD result; + HKEY hKey; + static char KeyName[] = + "Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall"; + + result = RegOpenKeyEx(HKEY_LOCAL_MACHINE, + KeyName, + 0, + KEY_CREATE_SUB_KEY, + &hKey); + if (result == ERROR_ACCESS_DENIED) { + MessageBox(GetFocus(), + "You do not seem to have sufficient access rights\n" + "on this machine to uninstall this software", + NULL, + MB_OK | MB_ICONSTOP); + return 1; /* Error */ + } + RegCloseKey(hKey); + } + + logfile = fopen(argv[2], "r"); + if (!logfile) { + MessageBox(NULL, + "could not open logfile", + NULL, + MB_OK); + return 1; /* Error */ + } + + lines = (char **)malloc(sizeof(char *) * lines_buffer_size); + if (!lines) + return SystemError(0, "Out of memory"); + + /* Read the whole logfile, realloacting the buffer */ + while (fgets(buffer, sizeof(buffer), logfile)) { + int len = strlen(buffer); + /* remove trailing white space */ + while (isspace(buffer[len-1])) + len -= 1; + buffer[len] = '\0'; + lines[nLines++] = strdup(buffer); + if (nLines >= lines_buffer_size) { + lines_buffer_size += 10; + lines = (char **)realloc(lines, + sizeof(char *) * lines_buffer_size); + if (!lines) + return SystemError(0, "Out of memory"); + } + } + fclose(logfile); + + /* Sort all the lines, so that highest 3-digit codes are first */ + qsort(&lines[0], nLines, sizeof(char *), + compare); + + if (IDYES != MessageBox(NULL, + "Are you sure you want to remove\n" + "this package from your computer?", + "Please confirm", + MB_YESNO | MB_ICONQUESTION)) + return 0; + + cp = ""; + for (i = 0; i < nLines; ++i) { + /* Ignore duplicate lines */ + if (strcmp(cp, lines[i])) { + int ign; + cp = lines[i]; + /* Parse the lines */ + if (2 == sscanf(cp, "%d Made Dir: %s", &ign, &buffer)) { + if (MyRemoveDirectory(cp)) + ++nDirs; + else { + int code = GetLastError(); + if (code != 2 && code != 3) { /* file or path not found */ + ++nErrors; + } + } + } else if (2 == sscanf(cp, "%d File Copy: %s", &ign, &buffer)) { + if (MyDeleteFile(cp)) + ++nFiles; + else { + int code = GetLastError(); + if (code != 2 && code != 3) { /* file or path not found */ + ++nErrors; + } + } + } else if (2 == sscanf(cp, "%d File Overwrite: %s", &ign, &buffer)) { + if (MyDeleteFile(cp)) + ++nFiles; + else { + int code = GetLastError(); + if (code != 2 && code != 3) { /* file or path not found */ + ++nErrors; + } + } + } else if (2 == sscanf(cp, "%d Reg DB Key: %s", &ign, &buffer)) { + DeleteRegistryKey(cp); + } else if (2 == sscanf(cp, "%d Reg DB Value: %s", &ign, &buffer)) { + DeleteRegistryValue(cp); + } else if (2 == sscanf(cp, "%d Run Script: %s", &ign, &buffer)) { + Run_RemoveScript(cp); + } + } + } + + if (DeleteFile(argv[2])) { + ++nFiles; + } else { + ++nErrors; + SystemError(GetLastError(), argv[2]); + } + if (nErrors) + wsprintf(buffer, + "%d files and %d directories removed\n" + "%d files or directories could not be removed", + nFiles, nDirs, nErrors); + else + wsprintf(buffer, "%d files and %d directories removed", + nFiles, nDirs); + MessageBox(NULL, buffer, "Uninstall Finished!", + MB_OK | MB_ICONINFORMATION); + remove_exe(); + return 0; +} + +int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, + LPSTR lpszCmdLine, INT nCmdShow) +{ + extern int __argc; + extern char **__argv; + char *basename; + + GetModuleFileName(NULL, modulename, sizeof(modulename)); + + /* Map the executable file to memory */ + arc_data = MapExistingFile(modulename, &arc_size); + if (!arc_data) { + SystemError(GetLastError(), "Could not open archive"); + return 1; + } + + /* OK. So this program can act as installer (self-extracting + * zip-file, or as uninstaller when started with '-u logfile' + * command line flags. + * + * The installer is usually started without command line flags, + * and the uninstaller is usually started with the '-u logfile' + * flag. What to do if some innocent user double-clicks the + * exe-file? + * The following implements a defensive strategy... + */ + + /* Try to extract the configuration data into a temporary file */ + ini_file = ExtractIniFile(arc_data, arc_size, &exe_size); + + if (ini_file) + return DoInstall(); + + if (!ini_file && __argc > 1) { + return DoUninstall(__argc, __argv); + } + + + basename = strrchr(modulename, '\\'); + if (basename) + ++basename; + + /* Last guess about the purpose of this program */ + if (basename && (0 == strncmp(basename, "Remove", 6))) + SystemError(0, "This program is normally started by windows"); + else + SystemError(0, "Setup program invalid or damaged"); + return 1; +} diff --git a/PC/bdist_wininst/install.rc b/PC/bdist_wininst/install.rc new file mode 100644 index 0000000..566c341 --- /dev/null +++ b/PC/bdist_wininst/install.rc @@ -0,0 +1,220 @@ +//Microsoft Developer Studio generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// Neutral resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_NEU) +#ifdef _WIN32 +LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL +#pragma code_page(1252) +#endif //_WIN32 + +///////////////////////////////////////////////////////////////////////////// +// +// Bitmap +// + +IDB_BITMAP BITMAP DISCARDABLE "PythonPowered.bmp" +#endif // Neutral resources +///////////////////////////////////////////////////////////////////////////// + + +///////////////////////////////////////////////////////////////////////////// +// German (Germany) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_DEU) +#ifdef _WIN32 +LANGUAGE LANG_GERMAN, SUBLANG_GERMAN +#pragma code_page(1252) +#endif //_WIN32 + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE DISCARDABLE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE DISCARDABLE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE DISCARDABLE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + +#endif // German (Germany) resources +///////////////////////////////////////////////////////////////////////////// + + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_INTRO DIALOGEX 0, 0, 379, 178 +STYLE WS_CHILD | WS_DISABLED | WS_CAPTION +CAPTION "Setup" +FONT 8, "MS Sans Serif", 0, 0, 0x1 +BEGIN + LTEXT "This Wizard will install %s on your computer. Click Next to continue or Cancel to exit the Setup Wizard.", + IDC_TITLE,125,10,247,20,NOT WS_GROUP + EDITTEXT IDC_INTRO_TEXT,125,31,247,131,ES_MULTILINE | ES_READONLY | + WS_VSCROLL | WS_HSCROLL | NOT WS_TABSTOP + CONTROL 110,IDC_BITMAP,"Static",SS_BITMAP | SS_CENTERIMAGE,6,8, + 104,163,WS_EX_CLIENTEDGE + LTEXT "",IDC_BUILD_INFO,125,163,247,8 +END + +IDD_SELECTPYTHON DIALOGEX 0, 0, 379, 178 +STYLE WS_CHILD | WS_DISABLED | WS_CAPTION +CAPTION "Setup" +FONT 8, "MS Sans Serif", 0, 0, 0x1 +BEGIN + LTEXT "Select python installation to use:",IDC_TITLE,125,10, + 247,12,NOT WS_GROUP + EDITTEXT IDC_PATH,191,136,181,14,ES_AUTOHSCROLL | ES_READONLY + LTEXT "Python Directory:",IDC_STATIC,125,137,55,8 + LISTBOX IDC_VERSIONS_LIST,125,24,247,106,LBS_SORT | + LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP + CONTROL 110,IDC_BITMAP,"Static",SS_BITMAP | SS_CENTERIMAGE,6,8, + 104,163,WS_EX_CLIENTEDGE + EDITTEXT IDC_INSTALL_PATH,191,157,181,14,ES_AUTOHSCROLL | + ES_READONLY + LTEXT "Installation Directory:",IDC_STATIC,125,158,66,8 + PUSHBUTTON "Find other ...",IDC_OTHERPYTHON,322,7,50,14,NOT + WS_VISIBLE +END + +IDD_INSTALLFILES DIALOGEX 0, 0, 379, 178 +STYLE WS_CHILD | WS_DISABLED | WS_CAPTION +CAPTION "Setup" +FONT 8, "MS Sans Serif", 0, 0, 0x1 +BEGIN + LTEXT "Click Next to begin the installation. If you want to review or change any of your installation settings, click Back. Click Cancel to exit the Wizard.", + IDC_TITLE,125,10,246,31,NOT WS_GROUP + CONTROL "Progress1",IDC_PROGRESS,"msctls_progress32",WS_BORDER, + 125,157,246,14 + CTEXT "Installation progress:",IDC_INFO,125,137,246,8 + CONTROL 110,IDC_BITMAP,"Static",SS_BITMAP | SS_CENTERIMAGE,6,8, + 104,163,WS_EX_CLIENTEDGE +END + +IDD_FINISHED DIALOGEX 0, 0, 379, 178 +STYLE WS_CHILD | WS_DISABLED | WS_CAPTION +CAPTION "Setup" +FONT 8, "MS Sans Serif" +BEGIN + LTEXT "Click the Finish button to exit the Setup wizard.", + IDC_TITLE,125,10,247,31,NOT WS_GROUP + CONTROL 110,IDC_BITMAP,"Static",SS_BITMAP | SS_CENTERIMAGE,6,8, + 104,163,WS_EX_CLIENTEDGE + EDITTEXT IDC_INFO,125,40,247,131,ES_MULTILINE | ES_READONLY | + WS_VSCROLL | WS_HSCROLL | NOT WS_TABSTOP +END + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO DISCARDABLE +BEGIN + IDD_INTRO, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 372 + VERTGUIDE, 125 + VERTGUIDE, 372 + TOPMARGIN, 7 + BOTTOMMARGIN, 171 + HORZGUIDE, 8 + HORZGUIDE, 31 + END + + IDD_SELECTPYTHON, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 372 + VERTGUIDE, 125 + VERTGUIDE, 372 + TOPMARGIN, 7 + BOTTOMMARGIN, 171 + HORZGUIDE, 8 + HORZGUIDE, 41 + END + + IDD_INSTALLFILES, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 372 + VERTGUIDE, 125 + VERTGUIDE, 371 + TOPMARGIN, 7 + BOTTOMMARGIN, 171 + HORZGUIDE, 8 + HORZGUIDE, 41 + END + + IDD_FINISHED, DIALOG + BEGIN + LEFTMARGIN, 6 + RIGHTMARGIN, 372 + VERTGUIDE, 125 + VERTGUIDE, 372 + TOPMARGIN, 7 + BOTTOMMARGIN, 171 + HORZGUIDE, 8 + HORZGUIDE, 41 + END +END +#endif // APSTUDIO_INVOKED + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/PC/bdist_wininst/resource.h b/PC/bdist_wininst/resource.h new file mode 100644 index 0000000..68f415f --- /dev/null +++ b/PC/bdist_wininst/resource.h @@ -0,0 +1,38 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Developer Studio generated include file. +// Used by install.rc +// +#define IDD_DIALOG1 101 +#define IDB_BITMAP1 103 +#define IDD_INTRO 107 +#define IDD_SELECTPYTHON 108 +#define IDD_INSTALLFILES 109 +#define IDD_FINISHED 110 +#define IDB_BITMAP 110 +#define IDC_EDIT1 1000 +#define IDC_TITLE 1000 +#define IDC_START 1001 +#define IDC_PROGRESS 1003 +#define IDC_INFO 1004 +#define IDC_PYTHON15 1006 +#define IDC_PATH 1007 +#define IDC_PYTHON16 1008 +#define IDC_INSTALL_PATH 1008 +#define IDC_PYTHON20 1009 +#define IDC_BROWSE 1010 +#define IDC_INTRO_TEXT 1021 +#define IDC_VERSIONS_LIST 1022 +#define IDC_BUILD_INFO 1024 +#define IDC_BITMAP 1025 +#define IDC_OTHERPYTHON 1026 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 112 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1028 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/PC/bdist_wininst/wininst.dsp b/PC/bdist_wininst/wininst.dsp new file mode 100644 index 0000000..4eb6a73 --- /dev/null +++ b/PC/bdist_wininst/wininst.dsp @@ -0,0 +1,127 @@ +# Microsoft Developer Studio Project File - Name="wininst" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Application" 0x0101 + +CFG=wininst - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "wininst.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "wininst.mak" CFG="wininst - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "wininst - Win32 Release" (based on "Win32 (x86) Application") +!MESSAGE "wininst - Win32 Debug" (based on "Win32 (x86) Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "wininst - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "." +# PROP Intermediate_Dir "temp-release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /MD /W3 /O1 /I "zlib" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x407 /d "NDEBUG" +# ADD RSC /l 0x407 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386 +# ADD LINK32 imagehlp.lib comdlg32.lib ole32.lib zlibstat.lib comctl32.lib kernel32.lib user32.lib gdi32.lib advapi32.lib shell32.lib /nologo /subsystem:windows /machine:I386 /nodefaultlib:"LIBC" /libpath:"zlib\static32" +# Begin Special Build Tool +SOURCE="$(InputPath)" +PostBuild_Cmds=c:\util\upx.exe --best wininst.exe +# End Special Build Tool + +!ELSEIF "$(CFG)" == "wininst - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "." +# PROP Intermediate_Dir "temp-debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /MD /W3 /Z7 /Od /I "zlib" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /FR /YX /FD /c +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x407 /d "_DEBUG" +# ADD RSC /l 0x407 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept +# ADD LINK32 imagehlp.lib comdlg32.lib ole32.lib zlibstat.lib comctl32.lib kernel32.lib user32.lib gdi32.lib advapi32.lib shell32.lib /nologo /subsystem:windows /pdb:none /debug /machine:I386 /nodefaultlib:"LIBC" /out:"./wininst_d.exe" /libpath:"zlib\static32" + +!ENDIF + +# Begin Target + +# Name "wininst - Win32 Release" +# Name "wininst - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\extract.c +# End Source File +# Begin Source File + +SOURCE=.\install.c +# End Source File +# Begin Source File + +SOURCE=.\install.rc +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=.\archive.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# Begin Source File + +SOURCE=.\PythonPowered.bmp +# End Source File +# End Group +# End Target +# End Project diff --git a/PC/bdist_wininst/wininst.dsw b/PC/bdist_wininst/wininst.dsw new file mode 100644 index 0000000..91881f5 --- /dev/null +++ b/PC/bdist_wininst/wininst.dsw @@ -0,0 +1,29 @@ +Microsoft Developer Studio Workspace File, Format Version 6.00 +# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! + +############################################################################### + +Project: "wininst"=.\wininst.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Global: + +Package=<5> +{{{ +}}} + +Package=<3> +{{{ +}}} + +############################################################################### + |