diff options
Diffstat (limited to 'PC/bdist_wininst/install.c')
-rw-r--r-- | PC/bdist_wininst/install.c | 2294 |
1 files changed, 2294 insertions, 0 deletions
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; +} |