Home | History | Annotate | Download | only in bdist_wininst
      1 /*
      2   IMPORTANT NOTE: IF THIS FILE IS CHANGED, WININST-6.EXE MUST BE RECOMPILED
      3   WITH THE MSVC6 WININST.DSW WORKSPACE FILE MANUALLY, AND WININST-7.1.EXE MUST
      4   BE RECOMPILED WITH THE MSVC 2003.NET WININST-7.1.VCPROJ FILE MANUALLY.
      5 
      6   IF CHANGES TO THIS FILE ARE CHECKED INTO PYTHON CVS, THE RECOMPILED BINARIES
      7   MUST BE CHECKED IN AS WELL!
      8 */
      9 
     10 /*
     11  * Written by Thomas Heller, May 2000
     12  *
     13  * $Id$
     14  */
     15 
     16 /*
     17  * Windows Installer program for distutils.
     18  *
     19  * (a kind of self-extracting zip-file)
     20  *
     21  * At runtime, the exefile has appended:
     22  * - compressed setup-data in ini-format, containing the following sections:
     23  *      [metadata]
     24  *      author=Greg Ward
     25  *      author_email=gward (at) python.net
     26  *      description=Python Distribution Utilities
     27  *      licence=Python
     28  *      name=Distutils
     29  *      url=http://www.python.org/sigs/distutils-sig/
     30  *      version=0.9pre
     31  *
     32  *      [Setup]
     33  *      info= text to be displayed in the edit-box
     34  *      title= to be displayed by this program
     35  *      target_version = if present, python version required
     36  *      pyc_compile = if 0, do not compile py to pyc
     37  *      pyo_compile = if 0, do not compile py to pyo
     38  *
     39  * - a struct meta_data_hdr, describing the above
     40  * - a zip-file, containing the modules to be installed.
     41  *   for the format see http://www.pkware.com/appnote.html
     42  *
     43  * What does this program do?
     44  * - the setup-data is uncompressed and written to a temporary file.
     45  * - setup-data is queried with GetPrivateProfile... calls
     46  * - [metadata] - info is displayed in the dialog box
     47  * - The registry is searched for installations of python
     48  * - The user can select the python version to use.
     49  * - The python-installation directory (sys.prefix) is displayed
     50  * - When the start-button is pressed, files from the zip-archive
     51  *   are extracted to the file system. All .py filenames are stored
     52  *   in a list.
     53  */
     54 /*
     55  * Includes now an uninstaller.
     56  */
     57 
     58 /*
     59  * To Do:
     60  *
     61  * display some explanation when no python version is found
     62  * instead showing the user an empty listbox to select something from.
     63  *
     64  * Finish the code so that we can use other python installations
     65  * additionally to those found in the registry,
     66  * and then #define USE_OTHER_PYTHON_VERSIONS
     67  *
     68  *  - install a help-button, which will display something meaningful
     69  *    to the poor user.
     70  *    text to the user
     71  *  - should there be a possibility to display a README file
     72  *    before starting the installation (if one is present in the archive)
     73  *  - more comments about what the code does(?)
     74  *
     75  *  - evolve this into a full blown installer (???)
     76  */
     77 
     78 #include <windows.h>
     79 #include <commctrl.h>
     80 #include <imagehlp.h>
     81 #include <objbase.h>
     82 #include <shlobj.h>
     83 #include <objidl.h>
     84 #include "resource.h"
     85 
     86 #include <stdio.h>
     87 #include <stdlib.h>
     88 #include <stdarg.h>
     89 #include <string.h>
     90 #include <time.h>
     91 #include <sys/types.h>
     92 #include <sys/stat.h>
     93 #include <malloc.h>
     94 #include <io.h>
     95 #include <fcntl.h>
     96 
     97 #include "archive.h"
     98 
     99 /* Only for debugging!
    100    static int dprintf(char *fmt, ...)
    101    {
    102    char Buffer[4096];
    103    va_list marker;
    104    int result;
    105 
    106    va_start(marker, fmt);
    107    result = wvsprintf(Buffer, fmt, marker);
    108    OutputDebugString(Buffer);
    109    return result;
    110    }
    111 */
    112 
    113 /* Bah: global variables */
    114 FILE *logfile;
    115 
    116 char modulename[MAX_PATH];
    117 
    118 HWND hwndMain;
    119 HWND hDialog;
    120 
    121 char *ini_file;                 /* Full pathname of ini-file */
    122 /* From ini-file */
    123 char info[4096];                /* [Setup] info= */
    124 char title[80];                 /* [Setup] title=, contains package name
    125                                    including version: "Distutils-1.0.1" */
    126 char target_version[10];        /* [Setup] target_version=, required python
    127                                    version or empty string */
    128 char build_info[80];            /* [Setup] build_info=, distutils version
    129                                    and build date */
    130 
    131 char meta_name[80];             /* package name without version like
    132                                    'Distutils' */
    133 char install_script[MAX_PATH];
    134 char *pre_install_script; /* run before we install a single file */
    135 
    136 char user_access_control[10]; // one of 'auto', 'force', otherwise none.
    137 
    138 int py_major, py_minor;         /* Python version selected for installation */
    139 
    140 char *arc_data;                 /* memory mapped archive */
    141 DWORD arc_size;                 /* number of bytes in archive */
    142 int exe_size;                   /* number of bytes for exe-file portion */
    143 char python_dir[MAX_PATH];
    144 char pythondll[MAX_PATH];
    145 BOOL pyc_compile, pyo_compile;
    146 /* Either HKLM or HKCU, depending on where Python itself is registered, and
    147    the permissions of the current user. */
    148 HKEY hkey_root = (HKEY)-1;
    149 
    150 BOOL success;                   /* Installation successful? */
    151 char *failure_reason = NULL;
    152 
    153 HANDLE hBitmap;
    154 char *bitmap_bytes;
    155 
    156 
    157 #define WM_NUMFILES WM_USER+1
    158 /* wParam: 0, lParam: total number of files */
    159 #define WM_NEXTFILE WM_USER+2
    160 /* wParam: number of this file */
    161 /* lParam: points to pathname */
    162 
    163 static BOOL notify(int code, char *fmt, ...);
    164 
    165 /* Note: If scheme.prefix is nonempty, it must end with a '\'! */
    166 /* Note: purelib must be the FIRST entry! */
    167 SCHEME old_scheme[] = {
    168     { "PURELIB", "" },
    169     { "PLATLIB", "" },
    170     { "HEADERS", "" }, /* 'Include/dist_name' part already in archive */
    171     { "SCRIPTS", "Scripts\\" },
    172     { "DATA", "" },
    173     { NULL, NULL },
    174 };
    175 
    176 SCHEME new_scheme[] = {
    177     { "PURELIB", "Lib\\site-packages\\" },
    178     { "PLATLIB", "Lib\\site-packages\\" },
    179     { "HEADERS", "" }, /* 'Include/dist_name' part already in archive */
    180     { "SCRIPTS", "Scripts\\" },
    181     { "DATA", "" },
    182     { NULL, NULL },
    183 };
    184 
    185 static void unescape(char *dst, char *src, unsigned size)
    186 {
    187     char *eon;
    188     char ch;
    189 
    190     while (src && *src && (size > 2)) {
    191         if (*src == '\\') {
    192             switch (*++src) {
    193             case 'n':
    194                 ++src;
    195                 *dst++ = '\r';
    196                 *dst++ = '\n';
    197                 size -= 2;
    198                 break;
    199             case 'r':
    200                 ++src;
    201                 *dst++ = '\r';
    202                 --size;
    203                 break;
    204             case '0': case '1': case '2': case '3':
    205                 ch = (char)strtol(src, &eon, 8);
    206                 if (ch == '\n') {
    207                     *dst++ = '\r';
    208                     --size;
    209                 }
    210                 *dst++ = ch;
    211                 --size;
    212                 src = eon;
    213             }
    214         } else {
    215             *dst++ = *src++;
    216             --size;
    217         }
    218     }
    219     *dst = '\0';
    220 }
    221 
    222 static struct tagFile {
    223     char *path;
    224     struct tagFile *next;
    225 } *file_list = NULL;
    226 
    227 static void set_failure_reason(char *reason)
    228 {
    229     if (failure_reason)
    230     free(failure_reason);
    231     failure_reason = strdup(reason);
    232     success = FALSE;
    233 }
    234 static char *get_failure_reason()
    235 {
    236     if (!failure_reason)
    237     return "Installation failed.";
    238     return failure_reason;
    239 }
    240 
    241 static void add_to_filelist(char *path)
    242 {
    243     struct tagFile *p;
    244     p = (struct tagFile *)malloc(sizeof(struct tagFile));
    245     p->path = strdup(path);
    246     p->next = file_list;
    247     file_list = p;
    248 }
    249 
    250 static int do_compile_files(int (__cdecl * PyRun_SimpleString)(char *),
    251                              int optimize)
    252 {
    253     struct tagFile *p;
    254     int total, n;
    255     char Buffer[MAX_PATH + 64];
    256     int errors = 0;
    257 
    258     total = 0;
    259     p = file_list;
    260     while (p) {
    261         ++total;
    262         p = p->next;
    263     }
    264     SendDlgItemMessage(hDialog, IDC_PROGRESS, PBM_SETRANGE, 0,
    265                         MAKELPARAM(0, total));
    266     SendDlgItemMessage(hDialog, IDC_PROGRESS, PBM_SETPOS, 0, 0);
    267 
    268     n = 0;
    269     p = file_list;
    270     while (p) {
    271         ++n;
    272         wsprintf(Buffer,
    273                   "import py_compile; py_compile.compile (r'%s')",
    274                   p->path);
    275         if (PyRun_SimpleString(Buffer)) {
    276             ++errors;
    277         }
    278         /* We send the notification even if the files could not
    279          * be created so that the uninstaller will remove them
    280          * in case they are created later.
    281          */
    282         wsprintf(Buffer, "%s%c", p->path, optimize ? 'o' : 'c');
    283         notify(FILE_CREATED, Buffer);
    284 
    285         SendDlgItemMessage(hDialog, IDC_PROGRESS, PBM_SETPOS, n, 0);
    286         SetDlgItemText(hDialog, IDC_INFO, p->path);
    287         p = p->next;
    288     }
    289     return errors;
    290 }
    291 
    292 #define DECLPROC(dll, result, name, args)\
    293     typedef result (*__PROC__##name) args;\
    294     result (*name)args = (__PROC__##name)GetProcAddress(dll, #name)
    295 
    296 
    297 #define DECLVAR(dll, type, name)\
    298     type *name = (type*)GetProcAddress(dll, #name)
    299 
    300 typedef void PyObject;
    301 
    302 
    303 /*
    304  * Returns number of files which failed to compile,
    305  * -1 if python could not be loaded at all
    306  */
    307 static int compile_filelist(HINSTANCE hPython, BOOL optimize_flag)
    308 {
    309     DECLPROC(hPython, void, Py_Initialize, (void));
    310     DECLPROC(hPython, void, Py_SetProgramName, (char *));
    311     DECLPROC(hPython, void, Py_Finalize, (void));
    312     DECLPROC(hPython, int, PyRun_SimpleString, (char *));
    313     DECLPROC(hPython, PyObject *, PySys_GetObject, (char *));
    314     DECLVAR(hPython, int, Py_OptimizeFlag);
    315 
    316     int errors = 0;
    317     struct tagFile *p = file_list;
    318 
    319     if (!p)
    320         return 0;
    321 
    322     if (!Py_Initialize || !Py_SetProgramName || !Py_Finalize)
    323         return -1;
    324 
    325     if (!PyRun_SimpleString || !PySys_GetObject || !Py_OptimizeFlag)
    326         return -1;
    327 
    328     *Py_OptimizeFlag = optimize_flag ? 1 : 0;
    329     Py_SetProgramName(modulename);
    330     Py_Initialize();
    331 
    332     errors += do_compile_files(PyRun_SimpleString, optimize_flag);
    333     Py_Finalize();
    334 
    335     return errors;
    336 }
    337 
    338 typedef PyObject *(*PyCFunction)(PyObject *, PyObject *);
    339 
    340 struct PyMethodDef {
    341     char        *ml_name;
    342     PyCFunction  ml_meth;
    343     int                  ml_flags;
    344     char        *ml_doc;
    345 };
    346 typedef struct PyMethodDef PyMethodDef;
    347 
    348 // XXX - all of these are potentially fragile!  We load and unload
    349 // the Python DLL multiple times - so storing functions pointers
    350 // is dangerous (although things *look* OK at present)
    351 // Better might be to roll prepare_script_environment() into
    352 // LoadPythonDll(), and create a new UnloadPythonDLL() which also
    353 // clears the global pointers.
    354 void *(*g_Py_BuildValue)(char *, ...);
    355 int (*g_PyArg_ParseTuple)(PyObject *, char *, ...);
    356 PyObject * (*g_PyLong_FromVoidPtr)(void *);
    357 
    358 PyObject *g_PyExc_ValueError;
    359 PyObject *g_PyExc_OSError;
    360 
    361 PyObject *(*g_PyErr_Format)(PyObject *, char *, ...);
    362 
    363 #define DEF_CSIDL(name) { name, #name }
    364 
    365 struct {
    366     int nFolder;
    367     char *name;
    368 } csidl_names[] = {
    369     /* Startup menu for all users.
    370        NT only */
    371     DEF_CSIDL(CSIDL_COMMON_STARTMENU),
    372     /* Startup menu. */
    373     DEF_CSIDL(CSIDL_STARTMENU),
    374 
    375 /*    DEF_CSIDL(CSIDL_COMMON_APPDATA), */
    376 /*    DEF_CSIDL(CSIDL_LOCAL_APPDATA), */
    377     /* Repository for application-specific data.
    378        Needs Internet Explorer 4.0 */
    379     DEF_CSIDL(CSIDL_APPDATA),
    380 
    381     /* The desktop for all users.
    382        NT only */
    383     DEF_CSIDL(CSIDL_COMMON_DESKTOPDIRECTORY),
    384     /* The desktop. */
    385     DEF_CSIDL(CSIDL_DESKTOPDIRECTORY),
    386 
    387     /* Startup folder for all users.
    388        NT only */
    389     DEF_CSIDL(CSIDL_COMMON_STARTUP),
    390     /* Startup folder. */
    391     DEF_CSIDL(CSIDL_STARTUP),
    392 
    393     /* Programs item in the start menu for all users.
    394        NT only */
    395     DEF_CSIDL(CSIDL_COMMON_PROGRAMS),
    396     /* Program item in the user's start menu. */
    397     DEF_CSIDL(CSIDL_PROGRAMS),
    398 
    399 /*    DEF_CSIDL(CSIDL_PROGRAM_FILES_COMMON), */
    400 /*    DEF_CSIDL(CSIDL_PROGRAM_FILES), */
    401 
    402     /* Virtual folder containing fonts. */
    403     DEF_CSIDL(CSIDL_FONTS),
    404 };
    405 
    406 #define DIM(a) (sizeof(a) / sizeof((a)[0]))
    407 
    408 static PyObject *FileCreated(PyObject *self, PyObject *args)
    409 {
    410     char *path;
    411     if (!g_PyArg_ParseTuple(args, "s", &path))
    412         return NULL;
    413     notify(FILE_CREATED, path);
    414     return g_Py_BuildValue("");
    415 }
    416 
    417 static PyObject *DirectoryCreated(PyObject *self, PyObject *args)
    418 {
    419     char *path;
    420     if (!g_PyArg_ParseTuple(args, "s", &path))
    421         return NULL;
    422     notify(DIR_CREATED, path);
    423     return g_Py_BuildValue("");
    424 }
    425 
    426 static PyObject *GetSpecialFolderPath(PyObject *self, PyObject *args)
    427 {
    428     char *name;
    429     char lpszPath[MAX_PATH];
    430     int i;
    431     static HRESULT (WINAPI *My_SHGetSpecialFolderPath)(HWND hwnd,
    432                                                        LPTSTR lpszPath,
    433                                                        int nFolder,
    434                                                        BOOL fCreate);
    435 
    436     if (!My_SHGetSpecialFolderPath) {
    437         HINSTANCE hLib = LoadLibrary("shell32.dll");
    438         if (!hLib) {
    439             g_PyErr_Format(g_PyExc_OSError,
    440                            "function not available");
    441             return NULL;
    442         }
    443         My_SHGetSpecialFolderPath = (BOOL (WINAPI *)(HWND, LPTSTR,
    444                                                      int, BOOL))
    445             GetProcAddress(hLib,
    446                            "SHGetSpecialFolderPathA");
    447     }
    448 
    449     if (!g_PyArg_ParseTuple(args, "s", &name))
    450         return NULL;
    451 
    452     if (!My_SHGetSpecialFolderPath) {
    453         g_PyErr_Format(g_PyExc_OSError, "function not available");
    454         return NULL;
    455     }
    456 
    457     for (i = 0; i < DIM(csidl_names); ++i) {
    458         if (0 == strcmpi(csidl_names[i].name, name)) {
    459             int nFolder;
    460             nFolder = csidl_names[i].nFolder;
    461             if (My_SHGetSpecialFolderPath(NULL, lpszPath,
    462                                           nFolder, 0))
    463                 return g_Py_BuildValue("s", lpszPath);
    464             else {
    465                 g_PyErr_Format(g_PyExc_OSError,
    466                                "no such folder (%s)", name);
    467                 return NULL;
    468             }
    469 
    470         }
    471     };
    472     g_PyErr_Format(g_PyExc_ValueError, "unknown CSIDL (%s)", name);
    473     return NULL;
    474 }
    475 
    476 static PyObject *CreateShortcut(PyObject *self, PyObject *args)
    477 {
    478     char *path; /* path and filename */
    479     char *description;
    480     char *filename;
    481 
    482     char *arguments = NULL;
    483     char *iconpath = NULL;
    484     int iconindex = 0;
    485     char *workdir = NULL;
    486 
    487     WCHAR wszFilename[MAX_PATH];
    488 
    489     IShellLink *ps1 = NULL;
    490     IPersistFile *pPf = NULL;
    491 
    492     HRESULT hr;
    493 
    494     hr = CoInitialize(NULL);
    495     if (FAILED(hr)) {
    496         g_PyErr_Format(g_PyExc_OSError,
    497                        "CoInitialize failed, error 0x%x", hr);
    498         goto error;
    499     }
    500 
    501     if (!g_PyArg_ParseTuple(args, "sss|sssi",
    502                             &path, &description, &filename,
    503                             &arguments, &workdir, &iconpath, &iconindex))
    504         return NULL;
    505 
    506     hr = CoCreateInstance(&CLSID_ShellLink,
    507                           NULL,
    508                           CLSCTX_INPROC_SERVER,
    509                           &IID_IShellLink,
    510                           &ps1);
    511     if (FAILED(hr)) {
    512         g_PyErr_Format(g_PyExc_OSError,
    513                        "CoCreateInstance failed, error 0x%x", hr);
    514         goto error;
    515     }
    516 
    517     hr = ps1->lpVtbl->QueryInterface(ps1, &IID_IPersistFile,
    518                                      (void **)&pPf);
    519     if (FAILED(hr)) {
    520         g_PyErr_Format(g_PyExc_OSError,
    521                        "QueryInterface(IPersistFile) error 0x%x", hr);
    522         goto error;
    523     }
    524 
    525 
    526     hr = ps1->lpVtbl->SetPath(ps1, path);
    527     if (FAILED(hr)) {
    528         g_PyErr_Format(g_PyExc_OSError,
    529                        "SetPath() failed, error 0x%x", hr);
    530         goto error;
    531     }
    532 
    533     hr = ps1->lpVtbl->SetDescription(ps1, description);
    534     if (FAILED(hr)) {
    535         g_PyErr_Format(g_PyExc_OSError,
    536                        "SetDescription() failed, error 0x%x", hr);
    537         goto error;
    538     }
    539 
    540     if (arguments) {
    541         hr = ps1->lpVtbl->SetArguments(ps1, arguments);
    542         if (FAILED(hr)) {
    543             g_PyErr_Format(g_PyExc_OSError,
    544                            "SetArguments() error 0x%x", hr);
    545             goto error;
    546         }
    547     }
    548 
    549     if (iconpath) {
    550         hr = ps1->lpVtbl->SetIconLocation(ps1, iconpath, iconindex);
    551         if (FAILED(hr)) {
    552             g_PyErr_Format(g_PyExc_OSError,
    553                            "SetIconLocation() error 0x%x", hr);
    554             goto error;
    555         }
    556     }
    557 
    558     if (workdir) {
    559         hr = ps1->lpVtbl->SetWorkingDirectory(ps1, workdir);
    560         if (FAILED(hr)) {
    561             g_PyErr_Format(g_PyExc_OSError,
    562                            "SetWorkingDirectory() error 0x%x", hr);
    563             goto error;
    564         }
    565     }
    566 
    567     MultiByteToWideChar(CP_ACP, 0,
    568                         filename, -1,
    569                         wszFilename, MAX_PATH);
    570 
    571     hr = pPf->lpVtbl->Save(pPf, wszFilename, TRUE);
    572     if (FAILED(hr)) {
    573         g_PyErr_Format(g_PyExc_OSError,
    574                        "Failed to create shortcut '%s' - error 0x%x", filename, hr);
    575         goto error;
    576     }
    577 
    578     pPf->lpVtbl->Release(pPf);
    579     ps1->lpVtbl->Release(ps1);
    580     CoUninitialize();
    581     return g_Py_BuildValue("");
    582 
    583   error:
    584     if (pPf)
    585         pPf->lpVtbl->Release(pPf);
    586 
    587     if (ps1)
    588         ps1->lpVtbl->Release(ps1);
    589 
    590     CoUninitialize();
    591 
    592     return NULL;
    593 }
    594 
    595 static PyObject *PyMessageBox(PyObject *self, PyObject *args)
    596 {
    597     int rc;
    598     char *text, *caption;
    599     int flags;
    600     if (!g_PyArg_ParseTuple(args, "ssi", &text, &caption, &flags))
    601         return NULL;
    602     rc = MessageBox(GetFocus(), text, caption, flags);
    603     return g_Py_BuildValue("i", rc);
    604 }
    605 
    606 static PyObject *GetRootHKey(PyObject *self)
    607 {
    608     return g_PyLong_FromVoidPtr(hkey_root);
    609 }
    610 
    611 #define METH_VARARGS 0x0001
    612 #define METH_NOARGS   0x0004
    613 typedef PyObject *(*PyCFunction)(PyObject *, PyObject *);
    614 
    615 PyMethodDef meth[] = {
    616     {"create_shortcut", CreateShortcut, METH_VARARGS, NULL},
    617     {"get_special_folder_path", GetSpecialFolderPath, METH_VARARGS, NULL},
    618     {"get_root_hkey", (PyCFunction)GetRootHKey, METH_NOARGS, NULL},
    619     {"file_created", FileCreated, METH_VARARGS, NULL},
    620     {"directory_created", DirectoryCreated, METH_VARARGS, NULL},
    621     {"message_box", PyMessageBox, METH_VARARGS, NULL},
    622 };
    623 
    624 static HINSTANCE LoadPythonDll(char *fname)
    625 {
    626     char fullpath[_MAX_PATH];
    627     LONG size = sizeof(fullpath);
    628     char subkey_name[80];
    629     char buffer[260 + 12];
    630     HINSTANCE h;
    631 
    632     /* make sure PYTHONHOME is set, to that sys.path is initialized correctly */
    633     wsprintf(buffer, "PYTHONHOME=%s", python_dir);
    634     _putenv(buffer);
    635     h = LoadLibrary(fname);
    636     if (h)
    637         return h;
    638     wsprintf(subkey_name,
    639              "SOFTWARE\\Python\\PythonCore\\%d.%d\\InstallPath",
    640              py_major, py_minor);
    641     if (ERROR_SUCCESS != RegQueryValue(HKEY_CURRENT_USER, subkey_name,
    642                                        fullpath, &size) &&
    643         ERROR_SUCCESS != RegQueryValue(HKEY_LOCAL_MACHINE, subkey_name,
    644                                        fullpath, &size))
    645         return NULL;
    646     strcat(fullpath, "\\");
    647     strcat(fullpath, fname);
    648     return LoadLibrary(fullpath);
    649 }
    650 
    651 static int prepare_script_environment(HINSTANCE hPython)
    652 {
    653     PyObject *mod;
    654     DECLPROC(hPython, PyObject *, PyImport_ImportModule, (char *));
    655     DECLPROC(hPython, int, PyObject_SetAttrString, (PyObject *, char *, PyObject *));
    656     DECLPROC(hPython, PyObject *, PyObject_GetAttrString, (PyObject *, char *));
    657     DECLPROC(hPython, PyObject *, PyCFunction_New, (PyMethodDef *, PyObject *));
    658     DECLPROC(hPython, PyObject *, Py_BuildValue, (char *, ...));
    659     DECLPROC(hPython, int, PyArg_ParseTuple, (PyObject *, char *, ...));
    660     DECLPROC(hPython, PyObject *, PyErr_Format, (PyObject *, char *));
    661     DECLPROC(hPython, PyObject *, PyLong_FromVoidPtr, (void *));
    662     if (!PyImport_ImportModule || !PyObject_GetAttrString ||
    663         !PyObject_SetAttrString || !PyCFunction_New)
    664         return 1;
    665     if (!Py_BuildValue || !PyArg_ParseTuple || !PyErr_Format)
    666         return 1;
    667 
    668     mod = PyImport_ImportModule("__builtin__");
    669     if (mod) {
    670         int i;
    671         g_PyExc_ValueError = PyObject_GetAttrString(mod, "ValueError");
    672         g_PyExc_OSError = PyObject_GetAttrString(mod, "OSError");
    673         for (i = 0; i < DIM(meth); ++i) {
    674             PyObject_SetAttrString(mod, meth[i].ml_name,
    675                                    PyCFunction_New(&meth[i], NULL));
    676         }
    677     }
    678     g_Py_BuildValue = Py_BuildValue;
    679     g_PyArg_ParseTuple = PyArg_ParseTuple;
    680     g_PyErr_Format = PyErr_Format;
    681     g_PyLong_FromVoidPtr = PyLong_FromVoidPtr;
    682 
    683     return 0;
    684 }
    685 
    686 /*
    687  * This function returns one of the following error codes:
    688  * 1 if the Python-dll does not export the functions we need
    689  * 2 if no install-script is specified in pathname
    690  * 3 if the install-script file could not be opened
    691  * the return value of PyRun_SimpleString() otherwise,
    692  * which is 0 if everything is ok, -1 if an exception had occurred
    693  * in the install-script.
    694  */
    695 
    696 static int
    697 do_run_installscript(HINSTANCE hPython, char *pathname, int argc, char **argv)
    698 {
    699     int fh, result;
    700     DECLPROC(hPython, void, Py_Initialize, (void));
    701     DECLPROC(hPython, int, PySys_SetArgv, (int, char **));
    702     DECLPROC(hPython, int, PyRun_SimpleString, (char *));
    703     DECLPROC(hPython, void, Py_Finalize, (void));
    704     DECLPROC(hPython, PyObject *, Py_BuildValue, (char *, ...));
    705     DECLPROC(hPython, PyObject *, PyCFunction_New,
    706              (PyMethodDef *, PyObject *));
    707     DECLPROC(hPython, int, PyArg_ParseTuple, (PyObject *, char *, ...));
    708     DECLPROC(hPython, PyObject *, PyErr_Format, (PyObject *, char *));
    709 
    710     if (!Py_Initialize || !PySys_SetArgv
    711         || !PyRun_SimpleString || !Py_Finalize)
    712         return 1;
    713 
    714     if (!Py_BuildValue || !PyArg_ParseTuple || !PyErr_Format)
    715         return 1;
    716 
    717     if (!PyCFunction_New || !PyArg_ParseTuple || !PyErr_Format)
    718         return 1;
    719 
    720     if (pathname == NULL || pathname[0] == '\0')
    721         return 2;
    722 
    723     fh = open(pathname, _O_RDONLY);
    724     if (-1 == fh) {
    725         fprintf(stderr, "Could not open postinstall-script %s\n",
    726             pathname);
    727         return 3;
    728     }
    729 
    730     SetDlgItemText(hDialog, IDC_INFO, "Running Script...");
    731 
    732     Py_Initialize();
    733 
    734     prepare_script_environment(hPython);
    735     PySys_SetArgv(argc, argv);
    736     result = 3;
    737     {
    738         struct _stat statbuf;
    739         if(0 == _fstat(fh, &statbuf)) {
    740             char *script = alloca(statbuf.st_size + 5);
    741             int n = read(fh, script, statbuf.st_size);
    742             if (n > 0) {
    743                 script[n] = '\n';
    744                 script[n+1] = 0;
    745                 result = PyRun_SimpleString(script);
    746             }
    747         }
    748     }
    749     Py_Finalize();
    750 
    751     close(fh);
    752     return result;
    753 }
    754 
    755 static int
    756 run_installscript(char *pathname, int argc, char **argv, char **pOutput)
    757 {
    758     HINSTANCE hPython;
    759     int result = 1;
    760     int out_buf_size;
    761     HANDLE redirected, old_stderr, old_stdout;
    762     char *tempname;
    763 
    764     *pOutput = NULL;
    765 
    766     tempname = tempnam(NULL, NULL);
    767     // We use a static CRT while the Python version we load uses
    768     // the CRT from one of various possible DLLs.  As a result we
    769     // need to redirect the standard handles using the API rather
    770     // than the CRT.
    771     redirected = CreateFile(
    772                                     tempname,
    773                                     GENERIC_WRITE | GENERIC_READ,
    774                                     FILE_SHARE_READ,
    775                                     NULL,
    776                                     CREATE_ALWAYS,
    777                                     FILE_ATTRIBUTE_NORMAL | FILE_FLAG_WRITE_THROUGH,
    778                                     NULL);
    779     old_stdout = GetStdHandle(STD_OUTPUT_HANDLE);
    780     old_stderr = GetStdHandle(STD_ERROR_HANDLE);
    781     SetStdHandle(STD_OUTPUT_HANDLE, redirected);
    782     SetStdHandle(STD_ERROR_HANDLE, redirected);
    783 
    784     hPython = LoadPythonDll(pythondll);
    785     if (hPython) {
    786         result = do_run_installscript(hPython, pathname, argc, argv);
    787         FreeLibrary(hPython);
    788     } else {
    789         fprintf(stderr, "*** Could not load Python ***");
    790     }
    791     SetStdHandle(STD_OUTPUT_HANDLE, old_stdout);
    792     SetStdHandle(STD_ERROR_HANDLE, old_stderr);
    793     out_buf_size = min(GetFileSize(redirected, NULL), 4096);
    794     *pOutput = malloc(out_buf_size+1);
    795     if (*pOutput) {
    796         DWORD nread = 0;
    797         SetFilePointer(redirected, 0, 0, FILE_BEGIN);
    798         ReadFile(redirected, *pOutput, out_buf_size, &nread, NULL);
    799         (*pOutput)[nread] = '\0';
    800     }
    801     CloseHandle(redirected);
    802     DeleteFile(tempname);
    803     return result;
    804 }
    805 
    806 static int do_run_simple_script(HINSTANCE hPython, char *script)
    807 {
    808     int rc;
    809     DECLPROC(hPython, void, Py_Initialize, (void));
    810     DECLPROC(hPython, void, Py_SetProgramName, (char *));
    811     DECLPROC(hPython, void, Py_Finalize, (void));
    812     DECLPROC(hPython, int, PyRun_SimpleString, (char *));
    813     DECLPROC(hPython, void, PyErr_Print, (void));
    814 
    815     if (!Py_Initialize || !Py_SetProgramName || !Py_Finalize ||
    816         !PyRun_SimpleString || !PyErr_Print)
    817         return -1;
    818 
    819     Py_SetProgramName(modulename);
    820     Py_Initialize();
    821     prepare_script_environment(hPython);
    822     rc = PyRun_SimpleString(script);
    823     if (rc)
    824         PyErr_Print();
    825     Py_Finalize();
    826     return rc;
    827 }
    828 
    829 static int run_simple_script(char *script)
    830 {
    831     int rc;
    832     HINSTANCE hPython;
    833     char *tempname = tempnam(NULL, NULL);
    834     // Redirect output using win32 API - see comments above...
    835     HANDLE redirected = CreateFile(
    836                                     tempname,
    837                                     GENERIC_WRITE | GENERIC_READ,
    838                                     FILE_SHARE_READ,
    839                                     NULL,
    840                                     CREATE_ALWAYS,
    841                                     FILE_ATTRIBUTE_NORMAL | FILE_FLAG_WRITE_THROUGH,
    842                                     NULL);
    843     HANDLE old_stdout = GetStdHandle(STD_OUTPUT_HANDLE);
    844     HANDLE old_stderr = GetStdHandle(STD_ERROR_HANDLE);
    845     SetStdHandle(STD_OUTPUT_HANDLE, redirected);
    846     SetStdHandle(STD_ERROR_HANDLE, redirected);
    847 
    848     hPython = LoadPythonDll(pythondll);
    849     if (!hPython) {
    850         char reason[128];
    851         wsprintf(reason, "Can't load Python for pre-install script (%d)", GetLastError());
    852         set_failure_reason(reason);
    853         return -1;
    854     }
    855     rc = do_run_simple_script(hPython, script);
    856     FreeLibrary(hPython);
    857     SetStdHandle(STD_OUTPUT_HANDLE, old_stdout);
    858     SetStdHandle(STD_ERROR_HANDLE, old_stderr);
    859     /* We only care about the output when we fail.  If the script works
    860        OK, then we discard it
    861     */
    862     if (rc) {
    863         int err_buf_size;
    864         char *err_buf;
    865         const char *prefix = "Running the pre-installation script failed\r\n";
    866         int prefix_len = strlen(prefix);
    867         err_buf_size = GetFileSize(redirected, NULL);
    868         if (err_buf_size==INVALID_FILE_SIZE) // an error - let's try anyway...
    869             err_buf_size = 4096;
    870         err_buf = malloc(prefix_len + err_buf_size + 1);
    871         if (err_buf) {
    872             DWORD n = 0;
    873             strcpy(err_buf, prefix);
    874             SetFilePointer(redirected, 0, 0, FILE_BEGIN);
    875             ReadFile(redirected, err_buf+prefix_len, err_buf_size, &n, NULL);
    876             err_buf[prefix_len+n] = '\0';
    877             set_failure_reason(err_buf);
    878             free(err_buf);
    879         } else {
    880             set_failure_reason("Out of memory!");
    881         }
    882     }
    883     CloseHandle(redirected);
    884     DeleteFile(tempname);
    885     return rc;
    886 }
    887 
    888 
    889 static BOOL SystemError(int error, char *msg)
    890 {
    891     char Buffer[1024];
    892     int n;
    893 
    894     if (error) {
    895         LPVOID lpMsgBuf;
    896         FormatMessage(
    897             FORMAT_MESSAGE_ALLOCATE_BUFFER |
    898             FORMAT_MESSAGE_FROM_SYSTEM,
    899             NULL,
    900             error,
    901             MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
    902             (LPSTR)&lpMsgBuf,
    903             0,
    904             NULL
    905             );
    906         strncpy(Buffer, lpMsgBuf, sizeof(Buffer));
    907         LocalFree(lpMsgBuf);
    908     } else
    909         Buffer[0] = '\0';
    910     n = lstrlen(Buffer);
    911     _snprintf(Buffer+n, sizeof(Buffer)-n, msg);
    912     MessageBox(hwndMain, Buffer, "Runtime Error", MB_OK | MB_ICONSTOP);
    913     return FALSE;
    914 }
    915 
    916 static BOOL notify (int code, char *fmt, ...)
    917 {
    918     char Buffer[1024];
    919     va_list marker;
    920     BOOL result = TRUE;
    921     int a, b;
    922     char *cp;
    923 
    924     va_start(marker, fmt);
    925     _vsnprintf(Buffer, sizeof(Buffer), fmt, marker);
    926 
    927     switch (code) {
    928 /* Questions */
    929     case CAN_OVERWRITE:
    930         break;
    931 
    932 /* Information notification */
    933     case DIR_CREATED:
    934         if (logfile)
    935             fprintf(logfile, "100 Made Dir: %s\n", fmt);
    936         break;
    937 
    938     case FILE_CREATED:
    939         if (logfile)
    940             fprintf(logfile, "200 File Copy: %s\n", fmt);
    941         goto add_to_filelist_label;
    942         break;
    943 
    944     case FILE_OVERWRITTEN:
    945         if (logfile)
    946             fprintf(logfile, "200 File Overwrite: %s\n", fmt);
    947       add_to_filelist_label:
    948         if ((cp = strrchr(fmt, '.')) && (0 == strcmp (cp, ".py")))
    949             add_to_filelist(fmt);
    950         break;
    951 
    952 /* Error Messages */
    953     case ZLIB_ERROR:
    954         MessageBox(GetFocus(), Buffer, "Error",
    955                     MB_OK | MB_ICONWARNING);
    956         break;
    957 
    958     case SYSTEM_ERROR:
    959         SystemError(GetLastError(), Buffer);
    960         break;
    961 
    962     case NUM_FILES:
    963         a = va_arg(marker, int);
    964         b = va_arg(marker, int);
    965         SendMessage(hDialog, WM_NUMFILES, 0, MAKELPARAM(0, a));
    966         SendMessage(hDialog, WM_NEXTFILE, b,(LPARAM)fmt);
    967     }
    968     va_end(marker);
    969 
    970     return result;
    971 }
    972 
    973 static char *MapExistingFile(char *pathname, DWORD *psize)
    974 {
    975     HANDLE hFile, hFileMapping;
    976     DWORD nSizeLow, nSizeHigh;
    977     char *data;
    978 
    979     hFile = CreateFile(pathname,
    980                         GENERIC_READ, FILE_SHARE_READ, NULL,
    981                         OPEN_EXISTING,
    982                         FILE_ATTRIBUTE_NORMAL, NULL);
    983     if (hFile == INVALID_HANDLE_VALUE)
    984         return NULL;
    985     nSizeLow = GetFileSize(hFile, &nSizeHigh);
    986     hFileMapping = CreateFileMapping(hFile,
    987                                       NULL, PAGE_READONLY, 0, 0, NULL);
    988     CloseHandle(hFile);
    989 
    990     if (hFileMapping == INVALID_HANDLE_VALUE)
    991         return NULL;
    992 
    993     data = MapViewOfFile(hFileMapping,
    994                           FILE_MAP_READ, 0, 0, 0);
    995 
    996     CloseHandle(hFileMapping);
    997     *psize = nSizeLow;
    998     return data;
    999 }
   1000 
   1001 
   1002 static void create_bitmap(HWND hwnd)
   1003 {
   1004     BITMAPFILEHEADER *bfh;
   1005     BITMAPINFO *bi;
   1006     HDC hdc;
   1007 
   1008     if (!bitmap_bytes)
   1009         return;
   1010 
   1011     if (hBitmap)
   1012         return;
   1013 
   1014     hdc = GetDC(hwnd);
   1015 
   1016     bfh = (BITMAPFILEHEADER *)bitmap_bytes;
   1017     bi = (BITMAPINFO *)(bitmap_bytes + sizeof(BITMAPFILEHEADER));
   1018 
   1019     hBitmap = CreateDIBitmap(hdc,
   1020                              &bi->bmiHeader,
   1021                              CBM_INIT,
   1022                              bitmap_bytes + bfh->bfOffBits,
   1023                              bi,
   1024                              DIB_RGB_COLORS);
   1025     ReleaseDC(hwnd, hdc);
   1026 }
   1027 
   1028 /* Extract everything we need to begin the installation.  Currently this
   1029    is the INI filename with install data, and the raw pre-install script
   1030 */
   1031 static BOOL ExtractInstallData(char *data, DWORD size, int *pexe_size,
   1032                                char **out_ini_file, char **out_preinstall_script)
   1033 {
   1034     /* read the end of central directory record */
   1035     struct eof_cdir *pe = (struct eof_cdir *)&data[size - sizeof
   1036                                                    (struct eof_cdir)];
   1037 
   1038     int arc_start = size - sizeof (struct eof_cdir) - pe->nBytesCDir -
   1039         pe->ofsCDir;
   1040 
   1041     int ofs = arc_start - sizeof (struct meta_data_hdr);
   1042 
   1043     /* read meta_data info */
   1044     struct meta_data_hdr *pmd = (struct meta_data_hdr *)&data[ofs];
   1045     char *src, *dst;
   1046     char *ini_file;
   1047     char tempdir[MAX_PATH];
   1048 
   1049     /* ensure that if we fail, we don't have garbage out pointers */
   1050     *out_ini_file = *out_preinstall_script = NULL;
   1051 
   1052     if (pe->tag != 0x06054b50) {
   1053         return FALSE;
   1054     }
   1055 
   1056     if (pmd->tag != 0x1234567B) {
   1057         return SystemError(0,
   1058                    "Invalid cfgdata magic number (see bdist_wininst.py)");
   1059     }
   1060     if (ofs < 0) {
   1061         return FALSE;
   1062     }
   1063 
   1064     if (pmd->bitmap_size) {
   1065         /* Store pointer to bitmap bytes */
   1066         bitmap_bytes = (char *)pmd - pmd->uncomp_size - pmd->bitmap_size;
   1067     }
   1068 
   1069     *pexe_size = ofs - pmd->uncomp_size - pmd->bitmap_size;
   1070 
   1071     src = ((char *)pmd) - pmd->uncomp_size;
   1072     ini_file = malloc(MAX_PATH); /* will be returned, so do not free it */
   1073     if (!ini_file)
   1074         return FALSE;
   1075     if (!GetTempPath(sizeof(tempdir), tempdir)
   1076         || !GetTempFileName(tempdir, "~du", 0, ini_file)) {
   1077         SystemError(GetLastError(),
   1078                      "Could not create temporary file");
   1079         return FALSE;
   1080     }
   1081 
   1082     dst = map_new_file(CREATE_ALWAYS, ini_file, NULL, pmd->uncomp_size,
   1083                         0, 0, NULL/*notify*/);
   1084     if (!dst)
   1085         return FALSE;
   1086     /* Up to the first \0 is the INI file data. */
   1087     strncpy(dst, src, pmd->uncomp_size);
   1088     src += strlen(dst) + 1;
   1089     /* Up to next \0 is the pre-install script */
   1090     *out_preinstall_script = strdup(src);
   1091     *out_ini_file = ini_file;
   1092     UnmapViewOfFile(dst);
   1093     return TRUE;
   1094 }
   1095 
   1096 static void PumpMessages(void)
   1097 {
   1098     MSG msg;
   1099     while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
   1100         TranslateMessage(&msg);
   1101         DispatchMessage(&msg);
   1102     }
   1103 }
   1104 
   1105 LRESULT CALLBACK
   1106 WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
   1107 {
   1108     HDC hdc;
   1109     HFONT hFont;
   1110     int h;
   1111     PAINTSTRUCT ps;
   1112     switch (msg) {
   1113     case WM_PAINT:
   1114         hdc = BeginPaint(hwnd, &ps);
   1115         h = GetSystemMetrics(SM_CYSCREEN) / 10;
   1116         hFont = CreateFont(h, 0, 0, 0, 700, TRUE,
   1117                             0, 0, 0, 0, 0, 0, 0, "Times Roman");
   1118         hFont = SelectObject(hdc, hFont);
   1119         SetBkMode(hdc, TRANSPARENT);
   1120         TextOut(hdc, 15, 15, title, strlen(title));
   1121         SetTextColor(hdc, RGB(255, 255, 255));
   1122         TextOut(hdc, 10, 10, title, strlen(title));
   1123         DeleteObject(SelectObject(hdc, hFont));
   1124         EndPaint(hwnd, &ps);
   1125         return 0;
   1126     }
   1127     return DefWindowProc(hwnd, msg, wParam, lParam);
   1128 }
   1129 
   1130 static HWND CreateBackground(char *title)
   1131 {
   1132     WNDCLASS wc;
   1133     HWND hwnd;
   1134     char buffer[4096];
   1135 
   1136     wc.style = CS_VREDRAW | CS_HREDRAW;
   1137     wc.lpfnWndProc = WindowProc;
   1138     wc.cbWndExtra = 0;
   1139     wc.cbClsExtra = 0;
   1140     wc.hInstance = GetModuleHandle(NULL);
   1141     wc.hIcon = NULL;
   1142     wc.hCursor = LoadCursor(NULL, IDC_ARROW);
   1143     wc.hbrBackground = CreateSolidBrush(RGB(0, 0, 128));
   1144     wc.lpszMenuName = NULL;
   1145     wc.lpszClassName = "SetupWindowClass";
   1146 
   1147     if (!RegisterClass(&wc))
   1148         MessageBox(hwndMain,
   1149                     "Could not register window class",
   1150                     "Setup.exe", MB_OK);
   1151 
   1152     wsprintf(buffer, "Setup %s", title);
   1153     hwnd = CreateWindow("SetupWindowClass",
   1154                          buffer,
   1155                          0,
   1156                          0, 0,
   1157                          GetSystemMetrics(SM_CXFULLSCREEN),
   1158                          GetSystemMetrics(SM_CYFULLSCREEN),
   1159                          NULL,
   1160                          NULL,
   1161                          GetModuleHandle(NULL),
   1162                          NULL);
   1163     ShowWindow(hwnd, SW_SHOWMAXIMIZED);
   1164     UpdateWindow(hwnd);
   1165     return hwnd;
   1166 }
   1167 
   1168 /*
   1169  * Center a window on the screen
   1170  */
   1171 static void CenterWindow(HWND hwnd)
   1172 {
   1173     RECT rc;
   1174     int w, h;
   1175 
   1176     GetWindowRect(hwnd, &rc);
   1177     w = GetSystemMetrics(SM_CXSCREEN);
   1178     h = GetSystemMetrics(SM_CYSCREEN);
   1179     MoveWindow(hwnd,
   1180                (w - (rc.right-rc.left))/2,
   1181                (h - (rc.bottom-rc.top))/2,
   1182                 rc.right-rc.left, rc.bottom-rc.top, FALSE);
   1183 }
   1184 
   1185 #include <prsht.h>
   1186 
   1187 BOOL CALLBACK
   1188 IntroDlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
   1189 {
   1190     LPNMHDR lpnm;
   1191     char Buffer[4096];
   1192 
   1193     switch (msg) {
   1194     case WM_INITDIALOG:
   1195         create_bitmap(hwnd);
   1196         if(hBitmap)
   1197             SendDlgItemMessage(hwnd, IDC_BITMAP, STM_SETIMAGE,
   1198                                IMAGE_BITMAP, (LPARAM)hBitmap);
   1199         CenterWindow(GetParent(hwnd));
   1200         wsprintf(Buffer,
   1201                   "This Wizard will install %s on your computer. "
   1202                   "Click Next to continue "
   1203                   "or Cancel to exit the Setup Wizard.",
   1204                   meta_name);
   1205         SetDlgItemText(hwnd, IDC_TITLE, Buffer);
   1206         SetDlgItemText(hwnd, IDC_INTRO_TEXT, info);
   1207         SetDlgItemText(hwnd, IDC_BUILD_INFO, build_info);
   1208         return FALSE;
   1209 
   1210     case WM_NOTIFY:
   1211         lpnm = (LPNMHDR) lParam;
   1212 
   1213         switch (lpnm->code) {
   1214         case PSN_SETACTIVE:
   1215             PropSheet_SetWizButtons(GetParent(hwnd), PSWIZB_NEXT);
   1216             break;
   1217 
   1218         case PSN_WIZNEXT:
   1219             break;
   1220 
   1221         case PSN_RESET:
   1222             break;
   1223 
   1224         default:
   1225             break;
   1226         }
   1227     }
   1228     return FALSE;
   1229 }
   1230 
   1231 #ifdef USE_OTHER_PYTHON_VERSIONS
   1232 /* These are really private variables used to communicate
   1233  * between StatusRoutine and CheckPythonExe
   1234  */
   1235 char bound_image_dll[_MAX_PATH];
   1236 int bound_image_major;
   1237 int bound_image_minor;
   1238 
   1239 static BOOL __stdcall StatusRoutine(IMAGEHLP_STATUS_REASON reason,
   1240                                     PSTR ImageName,
   1241                                     PSTR DllName,
   1242                                     ULONG Va,
   1243                                     ULONG Parameter)
   1244 {
   1245     char fname[_MAX_PATH];
   1246     int int_version;
   1247 
   1248     switch(reason) {
   1249     case BindOutOfMemory:
   1250     case BindRvaToVaFailed:
   1251     case BindNoRoomInImage:
   1252     case BindImportProcedureFailed:
   1253         break;
   1254 
   1255     case BindImportProcedure:
   1256     case BindForwarder:
   1257     case BindForwarderNOT:
   1258     case BindImageModified:
   1259     case BindExpandFileHeaders:
   1260     case BindImageComplete:
   1261     case BindSymbolsNotUpdated:
   1262     case BindMismatchedSymbols:
   1263     case BindImportModuleFailed:
   1264         break;
   1265 
   1266     case BindImportModule:
   1267         if (1 == sscanf(DllName, "python%d", &int_version)) {
   1268             SearchPath(NULL, DllName, NULL, sizeof(fname),
   1269                        fname, NULL);
   1270             strcpy(bound_image_dll, fname);
   1271             bound_image_major = int_version / 10;
   1272             bound_image_minor = int_version % 10;
   1273             OutputDebugString("BOUND ");
   1274             OutputDebugString(fname);
   1275             OutputDebugString("\n");
   1276         }
   1277         break;
   1278     }
   1279     return TRUE;
   1280 }
   1281 
   1282 /*
   1283  */
   1284 static LPSTR get_sys_prefix(LPSTR exe, LPSTR dll)
   1285 {
   1286     void (__cdecl * Py_Initialize)(void);
   1287     void (__cdecl * Py_SetProgramName)(char *);
   1288     void (__cdecl * Py_Finalize)(void);
   1289     void* (__cdecl * PySys_GetObject)(char *);
   1290     void (__cdecl * PySys_SetArgv)(int, char **);
   1291     char* (__cdecl * Py_GetPrefix)(void);
   1292     char* (__cdecl * Py_GetPath)(void);
   1293     HINSTANCE hPython;
   1294     LPSTR prefix = NULL;
   1295     int (__cdecl * PyRun_SimpleString)(char *);
   1296 
   1297     {
   1298         char Buffer[256];
   1299         wsprintf(Buffer, "PYTHONHOME=%s", exe);
   1300         *strrchr(Buffer, '\\') = '\0';
   1301 //      MessageBox(GetFocus(), Buffer, "PYTHONHOME", MB_OK);
   1302                 _putenv(Buffer);
   1303                 _putenv("PYTHONPATH=");
   1304     }
   1305 
   1306     hPython = LoadLibrary(dll);
   1307     if (!hPython)
   1308         return NULL;
   1309     Py_Initialize = (void (*)(void))GetProcAddress
   1310         (hPython,"Py_Initialize");
   1311 
   1312     PySys_SetArgv = (void (*)(int, char **))GetProcAddress
   1313         (hPython,"PySys_SetArgv");
   1314 
   1315     PyRun_SimpleString = (int (*)(char *))GetProcAddress
   1316         (hPython,"PyRun_SimpleString");
   1317 
   1318     Py_SetProgramName = (void (*)(char *))GetProcAddress
   1319         (hPython,"Py_SetProgramName");
   1320 
   1321     PySys_GetObject = (void* (*)(char *))GetProcAddress
   1322         (hPython,"PySys_GetObject");
   1323 
   1324     Py_GetPrefix = (char * (*)(void))GetProcAddress
   1325         (hPython,"Py_GetPrefix");
   1326 
   1327     Py_GetPath = (char * (*)(void))GetProcAddress
   1328         (hPython,"Py_GetPath");
   1329 
   1330     Py_Finalize = (void (*)(void))GetProcAddress(hPython,
   1331                                                   "Py_Finalize");
   1332     Py_SetProgramName(exe);
   1333     Py_Initialize();
   1334     PySys_SetArgv(1, &exe);
   1335 
   1336     MessageBox(GetFocus(), Py_GetPrefix(), "PREFIX", MB_OK);
   1337     MessageBox(GetFocus(), Py_GetPath(), "PATH", MB_OK);
   1338 
   1339     Py_Finalize();
   1340     FreeLibrary(hPython);
   1341 
   1342     return prefix;
   1343 }
   1344 
   1345 static BOOL
   1346 CheckPythonExe(LPSTR pathname, LPSTR version, int *pmajor, int *pminor)
   1347 {
   1348     bound_image_dll[0] = '\0';
   1349     if (!BindImageEx(BIND_NO_BOUND_IMPORTS | BIND_NO_UPDATE | BIND_ALL_IMAGES,
   1350                      pathname,
   1351                      NULL,
   1352                      NULL,
   1353                      StatusRoutine))
   1354         return SystemError(0, "Could not bind image");
   1355     if (bound_image_dll[0] == '\0')
   1356         return SystemError(0, "Does not seem to be a python executable");
   1357     *pmajor = bound_image_major;
   1358     *pminor = bound_image_minor;
   1359     if (version && *version) {
   1360         char core_version[12];
   1361         wsprintf(core_version, "%d.%d", bound_image_major, bound_image_minor);
   1362         if (strcmp(version, core_version))
   1363             return SystemError(0, "Wrong Python version");
   1364     }
   1365     get_sys_prefix(pathname, bound_image_dll);
   1366     return TRUE;
   1367 }
   1368 
   1369 /*
   1370  * Browse for other python versions. Insert it into the listbox specified
   1371  * by hwnd. version, if not NULL or empty, is the version required.
   1372  */
   1373 static BOOL GetOtherPythonVersion(HWND hwnd, LPSTR version)
   1374 {
   1375     char vers_name[_MAX_PATH + 80];
   1376     DWORD itemindex;
   1377     OPENFILENAME of;
   1378     char pathname[_MAX_PATH];
   1379     DWORD result;
   1380 
   1381     strcpy(pathname, "python.exe");
   1382 
   1383     memset(&of, 0, sizeof(of));
   1384     of.lStructSize = sizeof(OPENFILENAME);
   1385     of.hwndOwner = GetParent(hwnd);
   1386     of.hInstance = NULL;
   1387     of.lpstrFilter = "python.exe\0python.exe\0";
   1388     of.lpstrCustomFilter = NULL;
   1389     of.nMaxCustFilter = 0;
   1390     of.nFilterIndex = 1;
   1391     of.lpstrFile = pathname;
   1392     of.nMaxFile = sizeof(pathname);
   1393     of.lpstrFileTitle = NULL;
   1394     of.nMaxFileTitle = 0;
   1395     of.lpstrInitialDir = NULL;
   1396     of.lpstrTitle = "Python executable";
   1397     of.Flags = OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST;
   1398     of.lpstrDefExt = "exe";
   1399 
   1400     result = GetOpenFileName(&of);
   1401     if (result) {
   1402         int major, minor;
   1403         if (!CheckPythonExe(pathname, version, &major, &minor)) {
   1404             return FALSE;
   1405         }
   1406         *strrchr(pathname, '\\') = '\0';
   1407         wsprintf(vers_name, "Python Version %d.%d in %s",
   1408                   major, minor, pathname);
   1409         itemindex = SendMessage(hwnd, LB_INSERTSTRING, -1,
   1410                                 (LPARAM)(LPSTR)vers_name);
   1411         SendMessage(hwnd, LB_SETCURSEL, itemindex, 0);
   1412         SendMessage(hwnd, LB_SETITEMDATA, itemindex,
   1413                     (LPARAM)(LPSTR)strdup(pathname));
   1414         return TRUE;
   1415     }
   1416     return FALSE;
   1417 }
   1418 #endif /* USE_OTHER_PYTHON_VERSIONS */
   1419 
   1420 typedef struct _InstalledVersionInfo {
   1421     char prefix[MAX_PATH+1]; // sys.prefix directory.
   1422     HKEY hkey; // Is this Python in HKCU or HKLM?
   1423 } InstalledVersionInfo;
   1424 
   1425 
   1426 /*
   1427  * Fill the listbox specified by hwnd with all python versions found
   1428  * in the registry. version, if not NULL or empty, is the version
   1429  * required.
   1430  */
   1431 static BOOL GetPythonVersions(HWND hwnd, HKEY hkRoot, LPSTR version)
   1432 {
   1433     DWORD index = 0;
   1434     char core_version[80];
   1435     HKEY hKey;
   1436     BOOL result = TRUE;
   1437     DWORD bufsize;
   1438 
   1439     if (ERROR_SUCCESS != RegOpenKeyEx(hkRoot,
   1440                                        "Software\\Python\\PythonCore",
   1441                                        0,       KEY_READ, &hKey))
   1442         return FALSE;
   1443     bufsize = sizeof(core_version);
   1444     while (ERROR_SUCCESS == RegEnumKeyEx(hKey, index,
   1445                                           core_version, &bufsize, NULL,
   1446                                           NULL, NULL, NULL)) {
   1447         char subkey_name[80], vers_name[80];
   1448         int itemindex;
   1449         DWORD value_size;
   1450         HKEY hk;
   1451 
   1452         bufsize = sizeof(core_version);
   1453         ++index;
   1454         if (version && *version && strcmp(version, core_version))
   1455             continue;
   1456 
   1457         wsprintf(vers_name, "Python Version %s (found in registry)",
   1458                   core_version);
   1459         wsprintf(subkey_name,
   1460                   "Software\\Python\\PythonCore\\%s\\InstallPath",
   1461                   core_version);
   1462         if (ERROR_SUCCESS == RegOpenKeyEx(hkRoot, subkey_name, 0, KEY_READ, &hk)) {
   1463             InstalledVersionInfo *ivi =
   1464                   (InstalledVersionInfo *)malloc(sizeof(InstalledVersionInfo));
   1465             value_size = sizeof(ivi->prefix);
   1466             if (ivi &&
   1467                 ERROR_SUCCESS == RegQueryValueEx(hk, NULL, NULL, NULL,
   1468                                                  ivi->prefix, &value_size)) {
   1469                 itemindex = SendMessage(hwnd, LB_ADDSTRING, 0,
   1470                                         (LPARAM)(LPSTR)vers_name);
   1471                 ivi->hkey = hkRoot;
   1472                 SendMessage(hwnd, LB_SETITEMDATA, itemindex,
   1473                             (LPARAM)(LPSTR)ivi);
   1474             }
   1475             RegCloseKey(hk);
   1476         }
   1477     }
   1478     RegCloseKey(hKey);
   1479     return result;
   1480 }
   1481 
   1482 /* Determine if the current user can write to HKEY_LOCAL_MACHINE */
   1483 BOOL HasLocalMachinePrivs()
   1484 {
   1485                 HKEY hKey;
   1486                 DWORD result;
   1487                 static char KeyName[] =
   1488                         "Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall";
   1489 
   1490                 result = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
   1491                                           KeyName,
   1492                                           0,
   1493                                           KEY_CREATE_SUB_KEY,
   1494                                           &hKey);
   1495                 if (result==0)
   1496                         RegCloseKey(hKey);
   1497                 return result==0;
   1498 }
   1499 
   1500 // Check the root registry key to use - either HKLM or HKCU.
   1501 // If Python is installed in HKCU, then our extension also must be installed
   1502 // in HKCU - as Python won't be available for other users, we shouldn't either
   1503 // (and will fail if we are!)
   1504 // If Python is installed in HKLM, then we will also prefer to use HKLM, but
   1505 // this may not be possible - so we silently fall back to HKCU.
   1506 //
   1507 // We assume hkey_root is already set to where Python itself is installed.
   1508 void CheckRootKey(HWND hwnd)
   1509 {
   1510     if (hkey_root==HKEY_CURRENT_USER) {
   1511         ; // as above, always install ourself in HKCU too.
   1512     } else if (hkey_root==HKEY_LOCAL_MACHINE) {
   1513         // Python in HKLM, but we may or may not have permissions there.
   1514         // Open the uninstall key with 'create' permissions - if this fails,
   1515         // we don't have permission.
   1516         if (!HasLocalMachinePrivs())
   1517             hkey_root = HKEY_CURRENT_USER;
   1518     } else {
   1519         MessageBox(hwnd, "Don't know Python's installation type",
   1520                            "Strange", MB_OK | MB_ICONSTOP);
   1521         /* Default to wherever they can, but preferring HKLM */
   1522         hkey_root = HasLocalMachinePrivs() ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
   1523     }
   1524 }
   1525 
   1526 /* Return the installation scheme depending on Python version number */
   1527 SCHEME *GetScheme(int major, int minor)
   1528 {
   1529     if (major > 2)
   1530         return new_scheme;
   1531     else if((major == 2) && (minor >= 2))
   1532         return new_scheme;
   1533     return old_scheme;
   1534 }
   1535 
   1536 BOOL CALLBACK
   1537 SelectPythonDlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
   1538 {
   1539     LPNMHDR lpnm;
   1540 
   1541     switch (msg) {
   1542     case WM_INITDIALOG:
   1543         if (hBitmap)
   1544             SendDlgItemMessage(hwnd, IDC_BITMAP, STM_SETIMAGE,
   1545                                IMAGE_BITMAP, (LPARAM)hBitmap);
   1546         GetPythonVersions(GetDlgItem(hwnd, IDC_VERSIONS_LIST),
   1547                            HKEY_LOCAL_MACHINE, target_version);
   1548         GetPythonVersions(GetDlgItem(hwnd, IDC_VERSIONS_LIST),
   1549                            HKEY_CURRENT_USER, target_version);
   1550         {               /* select the last entry which is the highest python
   1551                    version found */
   1552             int count;
   1553             count = SendDlgItemMessage(hwnd, IDC_VERSIONS_LIST,
   1554                                         LB_GETCOUNT, 0, 0);
   1555             if (count && count != LB_ERR)
   1556                 SendDlgItemMessage(hwnd, IDC_VERSIONS_LIST, LB_SETCURSEL,
   1557                                     count-1, 0);
   1558 
   1559             /* If a specific Python version is required,
   1560              * display a prominent notice showing this fact.
   1561              */
   1562             if (target_version && target_version[0]) {
   1563                 char buffer[4096];
   1564                 wsprintf(buffer,
   1565                          "Python %s is required for this package. "
   1566                          "Select installation to use:",
   1567                          target_version);
   1568                 SetDlgItemText(hwnd, IDC_TITLE, buffer);
   1569             }
   1570 
   1571             if (count == 0) {
   1572                 char Buffer[4096];
   1573                 char *msg;
   1574                 if (target_version && target_version[0]) {
   1575                     wsprintf(Buffer,
   1576                              "Python version %s required, which was not found"
   1577                              " in the registry.", target_version);
   1578                     msg = Buffer;
   1579                 } else
   1580                     msg = "No Python installation found in the registry.";
   1581                 MessageBox(hwnd, msg, "Cannot install",
   1582                            MB_OK | MB_ICONSTOP);
   1583             }
   1584         }
   1585         goto UpdateInstallDir;
   1586         break;
   1587 
   1588     case WM_COMMAND:
   1589         switch (LOWORD(wParam)) {
   1590 /*
   1591   case IDC_OTHERPYTHON:
   1592   if (GetOtherPythonVersion(GetDlgItem(hwnd, IDC_VERSIONS_LIST),
   1593   target_version))
   1594   goto UpdateInstallDir;
   1595   break;
   1596 */
   1597         case IDC_VERSIONS_LIST:
   1598             switch (HIWORD(wParam)) {
   1599                 int id;
   1600             case LBN_SELCHANGE:
   1601               UpdateInstallDir:
   1602                 PropSheet_SetWizButtons(GetParent(hwnd),
   1603                                         PSWIZB_BACK | PSWIZB_NEXT);
   1604                 id = SendDlgItemMessage(hwnd, IDC_VERSIONS_LIST,
   1605                                          LB_GETCURSEL, 0, 0);
   1606                 if (id == LB_ERR) {
   1607                     PropSheet_SetWizButtons(GetParent(hwnd),
   1608                                             PSWIZB_BACK);
   1609                     SetDlgItemText(hwnd, IDC_PATH, "");
   1610                     SetDlgItemText(hwnd, IDC_INSTALL_PATH, "");
   1611                     strcpy(python_dir, "");
   1612                     strcpy(pythondll, "");
   1613                 } else {
   1614                     char *pbuf;
   1615                     int result;
   1616                     InstalledVersionInfo *ivi;
   1617                     PropSheet_SetWizButtons(GetParent(hwnd),
   1618                                             PSWIZB_BACK | PSWIZB_NEXT);
   1619                     /* Get the python directory */
   1620                     ivi = (InstalledVersionInfo *)
   1621                         SendDlgItemMessage(hwnd,
   1622                             IDC_VERSIONS_LIST,
   1623                             LB_GETITEMDATA,
   1624                             id,
   1625                             0);
   1626                     hkey_root = ivi->hkey;
   1627                     strcpy(python_dir, ivi->prefix);
   1628                     SetDlgItemText(hwnd, IDC_PATH, python_dir);
   1629                     /* retrieve the python version and pythondll to use */
   1630                     result = SendDlgItemMessage(hwnd, IDC_VERSIONS_LIST,
   1631                                                  LB_GETTEXTLEN, (WPARAM)id, 0);
   1632                     pbuf = (char *)malloc(result + 1);
   1633                     if (pbuf) {
   1634                         /* guess the name of the python-dll */
   1635                         SendDlgItemMessage(hwnd, IDC_VERSIONS_LIST,
   1636                                             LB_GETTEXT, (WPARAM)id,
   1637                                             (LPARAM)pbuf);
   1638                         result = sscanf(pbuf, "Python Version %d.%d",
   1639                                          &py_major, &py_minor);
   1640                         if (result == 2) {
   1641 #ifdef _DEBUG
   1642                             wsprintf(pythondll, "python%d%d_d.dll",
   1643                                      py_major, py_minor);
   1644 #else
   1645                             wsprintf(pythondll, "python%d%d.dll",
   1646                                      py_major, py_minor);
   1647 #endif
   1648                         }
   1649                         free(pbuf);
   1650                     } else
   1651                         strcpy(pythondll, "");
   1652                     /* retrieve the scheme for this version */
   1653                     {
   1654                         char install_path[_MAX_PATH];
   1655                         SCHEME *scheme = GetScheme(py_major, py_minor);
   1656                         strcpy(install_path, python_dir);
   1657                         if (install_path[strlen(install_path)-1] != '\\')
   1658                             strcat(install_path, "\\");
   1659                         strcat(install_path, scheme[0].prefix);
   1660                         SetDlgItemText(hwnd, IDC_INSTALL_PATH, install_path);
   1661                     }
   1662                 }
   1663             }
   1664             break;
   1665         }
   1666         return 0;
   1667 
   1668     case WM_NOTIFY:
   1669         lpnm = (LPNMHDR) lParam;
   1670 
   1671         switch (lpnm->code) {
   1672             int id;
   1673         case PSN_SETACTIVE:
   1674             id = SendDlgItemMessage(hwnd, IDC_VERSIONS_LIST,
   1675                                      LB_GETCURSEL, 0, 0);
   1676             if (id == LB_ERR)
   1677                 PropSheet_SetWizButtons(GetParent(hwnd),
   1678                                         PSWIZB_BACK);
   1679             else
   1680                 PropSheet_SetWizButtons(GetParent(hwnd),
   1681                                         PSWIZB_BACK | PSWIZB_NEXT);
   1682             break;
   1683 
   1684         case PSN_WIZNEXT:
   1685             break;
   1686 
   1687         case PSN_WIZFINISH:
   1688             break;
   1689 
   1690         case PSN_RESET:
   1691             break;
   1692 
   1693         default:
   1694             break;
   1695         }
   1696     }
   1697     return 0;
   1698 }
   1699 
   1700 static BOOL OpenLogfile(char *dir)
   1701 {
   1702     char buffer[_MAX_PATH+1];
   1703     time_t ltime;
   1704     struct tm *now;
   1705     long result;
   1706     HKEY hKey, hSubkey;
   1707     char subkey_name[256];
   1708     static char KeyName[] =
   1709         "Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall";
   1710     const char *root_name = (hkey_root==HKEY_LOCAL_MACHINE ?
   1711                             "HKEY_LOCAL_MACHINE" : "HKEY_CURRENT_USER");
   1712     DWORD disposition;
   1713 
   1714     /* Use Create, as the Uninstall subkey may not exist under HKCU.
   1715        Use CreateKeyEx, so we can specify a SAM specifying write access
   1716     */
   1717         result = RegCreateKeyEx(hkey_root,
   1718                       KeyName,
   1719                       0, /* reserved */
   1720                   NULL, /* class */
   1721                   0, /* options */
   1722                   KEY_CREATE_SUB_KEY, /* sam */
   1723                   NULL, /* security */
   1724                   &hKey, /* result key */
   1725                   NULL); /* disposition */
   1726     if (result != ERROR_SUCCESS) {
   1727         if (result == ERROR_ACCESS_DENIED) {
   1728             /* This should no longer be able to happen - we have already
   1729                checked if they have permissions in HKLM, and all users
   1730                should have write access to HKCU.
   1731             */
   1732             MessageBox(GetFocus(),
   1733                        "You do not seem to have sufficient access rights\n"
   1734                        "on this machine to install this software",
   1735                        NULL,
   1736                        MB_OK | MB_ICONSTOP);
   1737             return FALSE;
   1738         } else {
   1739             MessageBox(GetFocus(), KeyName, "Could not open key", MB_OK);
   1740         }
   1741     }
   1742 
   1743     sprintf(buffer, "%s\\%s-wininst.log", dir, meta_name);
   1744     logfile = fopen(buffer, "a");
   1745     if (!logfile) {
   1746         char error[1024];
   1747 
   1748         sprintf(error, "Can't create \"%s\" (%s).\n\n"
   1749                 "Try to execute the installer as administrator.",
   1750                 buffer, strerror(errno));
   1751         MessageBox(GetFocus(), error, NULL, MB_OK | MB_ICONSTOP);
   1752         return FALSE;
   1753     }
   1754 
   1755     time(&ltime);
   1756     now = localtime(&ltime);
   1757     strftime(buffer, sizeof(buffer),
   1758              "*** Installation started %Y/%m/%d %H:%M ***\n",
   1759              localtime(&ltime));
   1760     fprintf(logfile, buffer);
   1761     fprintf(logfile, "Source: %s\n", modulename);
   1762 
   1763     /* Root key must be first entry processed by uninstaller. */
   1764     fprintf(logfile, "999 Root Key: %s\n", root_name);
   1765 
   1766     sprintf(subkey_name, "%s-py%d.%d", meta_name, py_major, py_minor);
   1767 
   1768     result = RegCreateKeyEx(hKey, subkey_name,
   1769                             0, NULL, 0,
   1770                             KEY_WRITE,
   1771                             NULL,
   1772                             &hSubkey,
   1773                             &disposition);
   1774 
   1775     if (result != ERROR_SUCCESS)
   1776         MessageBox(GetFocus(), subkey_name, "Could not create key", MB_OK);
   1777 
   1778     RegCloseKey(hKey);
   1779 
   1780     if (disposition == REG_CREATED_NEW_KEY)
   1781         fprintf(logfile, "020 Reg DB Key: [%s]%s\n", KeyName, subkey_name);
   1782 
   1783     sprintf(buffer, "Python %d.%d %s", py_major, py_minor, title);
   1784 
   1785     result = RegSetValueEx(hSubkey, "DisplayName",
   1786                            0,
   1787                            REG_SZ,
   1788                            buffer,
   1789                            strlen(buffer)+1);
   1790 
   1791     if (result != ERROR_SUCCESS)
   1792         MessageBox(GetFocus(), buffer, "Could not set key value", MB_OK);
   1793 
   1794     fprintf(logfile, "040 Reg DB Value: [%s\\%s]%s=%s\n",
   1795         KeyName, subkey_name, "DisplayName", buffer);
   1796 
   1797     {
   1798         FILE *fp;
   1799         sprintf(buffer, "%s\\Remove%s.exe", dir, meta_name);
   1800         fp = fopen(buffer, "wb");
   1801         fwrite(arc_data, exe_size, 1, fp);
   1802         fclose(fp);
   1803 
   1804         sprintf(buffer, "\"%s\\Remove%s.exe\" -u \"%s\\%s-wininst.log\"",
   1805             dir, meta_name, dir, meta_name);
   1806 
   1807         result = RegSetValueEx(hSubkey, "UninstallString",
   1808                                0,
   1809                                REG_SZ,
   1810                                buffer,
   1811                                strlen(buffer)+1);
   1812 
   1813         if (result != ERROR_SUCCESS)
   1814             MessageBox(GetFocus(), buffer, "Could not set key value", MB_OK);
   1815 
   1816         fprintf(logfile, "040 Reg DB Value: [%s\\%s]%s=%s\n",
   1817             KeyName, subkey_name, "UninstallString", buffer);
   1818     }
   1819     return TRUE;
   1820 }
   1821 
   1822 static void CloseLogfile(void)
   1823 {
   1824     char buffer[_MAX_PATH+1];
   1825     time_t ltime;
   1826     struct tm *now;
   1827 
   1828     time(&ltime);
   1829     now = localtime(&ltime);
   1830     strftime(buffer, sizeof(buffer),
   1831              "*** Installation finished %Y/%m/%d %H:%M ***\n",
   1832              localtime(&ltime));
   1833     fprintf(logfile, buffer);
   1834     if (logfile)
   1835         fclose(logfile);
   1836 }
   1837 
   1838 BOOL CALLBACK
   1839 InstallFilesDlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
   1840 {
   1841     LPNMHDR lpnm;
   1842     char Buffer[4096];
   1843     SCHEME *scheme;
   1844 
   1845     switch (msg) {
   1846     case WM_INITDIALOG:
   1847         if (hBitmap)
   1848             SendDlgItemMessage(hwnd, IDC_BITMAP, STM_SETIMAGE,
   1849                                IMAGE_BITMAP, (LPARAM)hBitmap);
   1850         wsprintf(Buffer,
   1851                   "Click Next to begin the installation of %s. "
   1852                   "If you want to review or change any of your "
   1853                   " installation settings, click Back. "
   1854                   "Click Cancel to exit the wizard.",
   1855                   meta_name);
   1856         SetDlgItemText(hwnd, IDC_TITLE, Buffer);
   1857         SetDlgItemText(hwnd, IDC_INFO, "Ready to install");
   1858         break;
   1859 
   1860     case WM_NUMFILES:
   1861         SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0, lParam);
   1862         PumpMessages();
   1863         return TRUE;
   1864 
   1865     case WM_NEXTFILE:
   1866         SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, wParam,
   1867                             0);
   1868         SetDlgItemText(hwnd, IDC_INFO, (LPSTR)lParam);
   1869         PumpMessages();
   1870         return TRUE;
   1871 
   1872     case WM_NOTIFY:
   1873         lpnm = (LPNMHDR) lParam;
   1874 
   1875         switch (lpnm->code) {
   1876         case PSN_SETACTIVE:
   1877             PropSheet_SetWizButtons(GetParent(hwnd),
   1878                                     PSWIZB_BACK | PSWIZB_NEXT);
   1879             break;
   1880 
   1881         case PSN_WIZFINISH:
   1882             break;
   1883 
   1884         case PSN_WIZNEXT:
   1885             /* Handle a Next button click here */
   1886             hDialog = hwnd;
   1887             success = TRUE;
   1888 
   1889             /* Disable the buttons while we work.  Sending CANCELTOCLOSE has
   1890               the effect of disabling the cancel button, which is a) as we
   1891               do everything synchronously we can't cancel, and b) the next
   1892               step is 'finished', when it is too late to cancel anyway.
   1893               The next step being 'Finished' means we also don't need to
   1894               restore the button state back */
   1895             PropSheet_SetWizButtons(GetParent(hwnd), 0);
   1896             SendMessage(GetParent(hwnd), PSM_CANCELTOCLOSE, 0, 0);
   1897             /* Make sure the installation directory name ends in a */
   1898             /* backslash */
   1899             if (python_dir[strlen(python_dir)-1] != '\\')
   1900                 strcat(python_dir, "\\");
   1901             /* Strip the trailing backslash again */
   1902             python_dir[strlen(python_dir)-1] = '\0';
   1903 
   1904             CheckRootKey(hwnd);
   1905 
   1906             if (!OpenLogfile(python_dir))
   1907                 break;
   1908 
   1909 /*
   1910  * The scheme we have to use depends on the Python version...
   1911  if sys.version < "2.2":
   1912     WINDOWS_SCHEME = {
   1913     'purelib': '$base',
   1914     'platlib': '$base',
   1915     'headers': '$base/Include/$dist_name',
   1916     'scripts': '$base/Scripts',
   1917     'data'   : '$base',
   1918     }
   1919  else:
   1920     WINDOWS_SCHEME = {
   1921     'purelib': '$base/Lib/site-packages',
   1922     'platlib': '$base/Lib/site-packages',
   1923     'headers': '$base/Include/$dist_name',
   1924     'scripts': '$base/Scripts',
   1925     'data'   : '$base',
   1926     }
   1927 */
   1928             scheme = GetScheme(py_major, py_minor);
   1929             /* Run the pre-install script. */
   1930             if (pre_install_script && *pre_install_script) {
   1931                 SetDlgItemText (hwnd, IDC_TITLE,
   1932                                 "Running pre-installation script");
   1933                 run_simple_script(pre_install_script);
   1934             }
   1935             if (!success) {
   1936                 break;
   1937             }
   1938             /* Extract all files from the archive */
   1939             SetDlgItemText(hwnd, IDC_TITLE, "Installing files...");
   1940             if (!unzip_archive (scheme,
   1941                                 python_dir, arc_data,
   1942                                 arc_size, notify))
   1943                 set_failure_reason("Failed to unzip installation files");
   1944             /* Compile the py-files */
   1945             if (success && pyc_compile) {
   1946                 int errors;
   1947                 HINSTANCE hPython;
   1948                 SetDlgItemText(hwnd, IDC_TITLE,
   1949                                 "Compiling files to .pyc...");
   1950 
   1951                 SetDlgItemText(hDialog, IDC_INFO, "Loading python...");
   1952                 hPython = LoadPythonDll(pythondll);
   1953                 if (hPython) {
   1954                     errors = compile_filelist(hPython, FALSE);
   1955                     FreeLibrary(hPython);
   1956                 }
   1957                 /* Compilation errors are intentionally ignored:
   1958                  * Python2.0 contains a bug which will result
   1959                  * in sys.path containing garbage under certain
   1960                  * circumstances, and an error message will only
   1961                  * confuse the user.
   1962                  */
   1963             }
   1964             if (success && pyo_compile) {
   1965                 int errors;
   1966                 HINSTANCE hPython;
   1967                 SetDlgItemText(hwnd, IDC_TITLE,
   1968                                 "Compiling files to .pyo...");
   1969 
   1970                 SetDlgItemText(hDialog, IDC_INFO, "Loading python...");
   1971                 hPython = LoadPythonDll(pythondll);
   1972                 if (hPython) {
   1973                     errors = compile_filelist(hPython, TRUE);
   1974                     FreeLibrary(hPython);
   1975                 }
   1976                 /* Errors ignored: see above */
   1977             }
   1978 
   1979 
   1980             break;
   1981 
   1982         case PSN_RESET:
   1983             break;
   1984 
   1985         default:
   1986             break;
   1987         }
   1988     }
   1989     return 0;
   1990 }
   1991 
   1992 
   1993 BOOL CALLBACK
   1994 FinishedDlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
   1995 {
   1996     LPNMHDR lpnm;
   1997 
   1998     switch (msg) {
   1999     case WM_INITDIALOG:
   2000         if (hBitmap)
   2001             SendDlgItemMessage(hwnd, IDC_BITMAP, STM_SETIMAGE,
   2002                                IMAGE_BITMAP, (LPARAM)hBitmap);
   2003         if (!success)
   2004             SetDlgItemText(hwnd, IDC_INFO, get_failure_reason());
   2005 
   2006         /* async delay: will show the dialog box completely before
   2007            the install_script is started */
   2008         PostMessage(hwnd, WM_USER, 0, 0L);
   2009         return TRUE;
   2010 
   2011     case WM_USER:
   2012 
   2013         if (success && install_script && install_script[0]) {
   2014             char fname[MAX_PATH];
   2015             char *buffer;
   2016             HCURSOR hCursor;
   2017             int result;
   2018 
   2019             char *argv[3] = {NULL, "-install", NULL};
   2020 
   2021             SetDlgItemText(hwnd, IDC_TITLE,
   2022                             "Please wait while running postinstall script...");
   2023             strcpy(fname, python_dir);
   2024             strcat(fname, "\\Scripts\\");
   2025             strcat(fname, install_script);
   2026 
   2027             if (logfile)
   2028                 fprintf(logfile, "300 Run Script: [%s]%s\n", pythondll, fname);
   2029 
   2030             hCursor = SetCursor(LoadCursor(NULL, IDC_WAIT));
   2031 
   2032             argv[0] = fname;
   2033 
   2034             result = run_installscript(fname, 2, argv, &buffer);
   2035             if (0 != result) {
   2036                 fprintf(stderr, "*** run_installscript: internal error 0x%X ***\n", result);
   2037             }
   2038             if (buffer)
   2039                 SetDlgItemText(hwnd, IDC_INFO, buffer);
   2040             SetDlgItemText(hwnd, IDC_TITLE,
   2041                             "Postinstall script finished.\n"
   2042                             "Click the Finish button to exit the Setup wizard.");
   2043 
   2044             free(buffer);
   2045             SetCursor(hCursor);
   2046             CloseLogfile();
   2047         }
   2048 
   2049         return TRUE;
   2050 
   2051     case WM_NOTIFY:
   2052         lpnm = (LPNMHDR) lParam;
   2053 
   2054         switch (lpnm->code) {
   2055         case PSN_SETACTIVE: /* Enable the Finish button */
   2056             PropSheet_SetWizButtons(GetParent(hwnd), PSWIZB_FINISH);
   2057             break;
   2058 
   2059         case PSN_WIZNEXT:
   2060             break;
   2061 
   2062         case PSN_WIZFINISH:
   2063             break;
   2064 
   2065         case PSN_RESET:
   2066             break;
   2067 
   2068         default:
   2069             break;
   2070         }
   2071     }
   2072     return 0;
   2073 }
   2074 
   2075 void RunWizard(HWND hwnd)
   2076 {
   2077     PROPSHEETPAGE   psp =       {0};
   2078     HPROPSHEETPAGE  ahpsp[4] =  {0};
   2079     PROPSHEETHEADER psh =       {0};
   2080 
   2081     /* Display module information */
   2082     psp.dwSize =        sizeof(psp);
   2083     psp.dwFlags =       PSP_DEFAULT|PSP_HIDEHEADER;
   2084     psp.hInstance =     GetModuleHandle (NULL);
   2085     psp.lParam =        0;
   2086     psp.pfnDlgProc =    IntroDlgProc;
   2087     psp.pszTemplate =   MAKEINTRESOURCE(IDD_INTRO);
   2088 
   2089     ahpsp[0] =          CreatePropertySheetPage(&psp);
   2090 
   2091     /* Select python version to use */
   2092     psp.dwFlags =       PSP_DEFAULT|PSP_HIDEHEADER;
   2093     psp.pszTemplate =       MAKEINTRESOURCE(IDD_SELECTPYTHON);
   2094     psp.pfnDlgProc =        SelectPythonDlgProc;
   2095 
   2096     ahpsp[1] =              CreatePropertySheetPage(&psp);
   2097 
   2098     /* Install the files */
   2099     psp.dwFlags =           PSP_DEFAULT|PSP_HIDEHEADER;
   2100     psp.pszTemplate =       MAKEINTRESOURCE(IDD_INSTALLFILES);
   2101     psp.pfnDlgProc =        InstallFilesDlgProc;
   2102 
   2103     ahpsp[2] =              CreatePropertySheetPage(&psp);
   2104 
   2105     /* Show success or failure */
   2106     psp.dwFlags =           PSP_DEFAULT|PSP_HIDEHEADER;
   2107     psp.pszTemplate =       MAKEINTRESOURCE(IDD_FINISHED);
   2108     psp.pfnDlgProc =        FinishedDlgProc;
   2109 
   2110     ahpsp[3] =              CreatePropertySheetPage(&psp);
   2111 
   2112     /* Create the property sheet */
   2113     psh.dwSize =            sizeof(psh);
   2114     psh.hInstance =         GetModuleHandle(NULL);
   2115     psh.hwndParent =        hwnd;
   2116     psh.phpage =            ahpsp;
   2117     psh.dwFlags =           PSH_WIZARD/*97*//*|PSH_WATERMARK|PSH_HEADER*/;
   2118         psh.pszbmWatermark =    NULL;
   2119         psh.pszbmHeader =       NULL;
   2120         psh.nStartPage =        0;
   2121         psh.nPages =            4;
   2122 
   2123         PropertySheet(&psh);
   2124 }
   2125 
   2126 // subtly different from HasLocalMachinePrivs(), in that after executing
   2127 // an 'elevated' process, we expect this to return TRUE - but there is no
   2128 // such implication for HasLocalMachinePrivs
   2129 BOOL MyIsUserAnAdmin()
   2130 {
   2131     typedef BOOL (WINAPI *PFNIsUserAnAdmin)();
   2132     static PFNIsUserAnAdmin pfnIsUserAnAdmin = NULL;
   2133     HMODULE shell32;
   2134     // This function isn't guaranteed to be available (and it can't hurt
   2135     // to leave the library loaded)
   2136     if (0 == (shell32=LoadLibrary("shell32.dll")))
   2137         return FALSE;
   2138     if (0 == (pfnIsUserAnAdmin=(PFNIsUserAnAdmin)GetProcAddress(shell32, "IsUserAnAdmin")))
   2139         return FALSE;
   2140     return (*pfnIsUserAnAdmin)();
   2141 }
   2142 
   2143 // Some magic for Vista's UAC.  If there is a target_version, and
   2144 // if that target version is installed in the registry under
   2145 // HKLM, and we are not current administrator, then
   2146 // re-execute ourselves requesting elevation.
   2147 // Split into 2 functions - "should we elevate" and "spawn elevated"
   2148 
   2149 // Returns TRUE if we should spawn an elevated child
   2150 BOOL NeedAutoUAC()
   2151 {
   2152     HKEY hk;
   2153     char key_name[80];
   2154     // no Python version info == we can't know yet.
   2155     if (target_version[0] == '\0')
   2156         return FALSE;
   2157     // see how python is current installed
   2158     wsprintf(key_name,
   2159                      "Software\\Python\\PythonCore\\%s\\InstallPath",
   2160                      target_version);
   2161     if (ERROR_SUCCESS != RegOpenKeyEx(HKEY_LOCAL_MACHINE,
   2162                                       key_name, 0, KEY_READ, &hk))
   2163         return FALSE;
   2164     RegCloseKey(hk);
   2165     // Python is installed in HKLM - we must elevate.
   2166     return TRUE;
   2167 }
   2168 
   2169 // Returns TRUE if the platform supports UAC.
   2170 BOOL PlatformSupportsUAC()
   2171 {
   2172     // Note that win2k does seem to support ShellExecute with 'runas',
   2173     // but does *not* support IsUserAnAdmin - so we just pretend things
   2174     // only work on XP and later.
   2175     BOOL bIsWindowsXPorLater;
   2176     OSVERSIONINFO winverinfo;
   2177     winverinfo.dwOSVersionInfoSize = sizeof(winverinfo);
   2178     if (!GetVersionEx(&winverinfo))
   2179         return FALSE; // something bad has gone wrong
   2180     bIsWindowsXPorLater =
   2181        ( (winverinfo.dwMajorVersion > 5) ||
   2182        ( (winverinfo.dwMajorVersion == 5) && (winverinfo.dwMinorVersion >= 1) ));
   2183     return bIsWindowsXPorLater;
   2184 }
   2185 
   2186 // Spawn ourself as an elevated application.  On failure, a message is
   2187 // displayed to the user - but this app will always terminate, even
   2188 // on error.
   2189 void SpawnUAC()
   2190 {
   2191     // interesting failure scenario that has been seen: initial executable
   2192     // runs from a network drive - but once elevated, that network share
   2193     // isn't seen, and ShellExecute fails with SE_ERR_ACCESSDENIED.
   2194     int ret = (int)ShellExecute(0, "runas", modulename, "", NULL,
   2195                                 SW_SHOWNORMAL);
   2196     if (ret <= 32) {
   2197         char msg[128];
   2198         wsprintf(msg, "Failed to start elevated process (ShellExecute returned %d)", ret);
   2199         MessageBox(0, msg, "Setup", MB_OK | MB_ICONERROR);
   2200     }
   2201 }
   2202 
   2203 int DoInstall(void)
   2204 {
   2205     char ini_buffer[4096];
   2206 
   2207     /* Read installation information */
   2208     GetPrivateProfileString("Setup", "title", "", ini_buffer,
   2209                              sizeof(ini_buffer), ini_file);
   2210     unescape(title, ini_buffer, sizeof(title));
   2211 
   2212     GetPrivateProfileString("Setup", "info", "", ini_buffer,
   2213                              sizeof(ini_buffer), ini_file);
   2214     unescape(info, ini_buffer, sizeof(info));
   2215 
   2216     GetPrivateProfileString("Setup", "build_info", "", build_info,
   2217                              sizeof(build_info), ini_file);
   2218 
   2219     pyc_compile = GetPrivateProfileInt("Setup", "target_compile", 1,
   2220                                         ini_file);
   2221     pyo_compile = GetPrivateProfileInt("Setup", "target_optimize", 1,
   2222                                         ini_file);
   2223 
   2224     GetPrivateProfileString("Setup", "target_version", "",
   2225                              target_version, sizeof(target_version),
   2226                              ini_file);
   2227 
   2228     GetPrivateProfileString("metadata", "name", "",
   2229                              meta_name, sizeof(meta_name),
   2230                              ini_file);
   2231 
   2232     GetPrivateProfileString("Setup", "install_script", "",
   2233                              install_script, sizeof(install_script),
   2234                              ini_file);
   2235 
   2236     GetPrivateProfileString("Setup", "user_access_control", "",
   2237                              user_access_control, sizeof(user_access_control), ini_file);
   2238 
   2239     // See if we need to do the Vista UAC magic.
   2240     if (strcmp(user_access_control, "force")==0) {
   2241         if (PlatformSupportsUAC() && !MyIsUserAnAdmin()) {
   2242             SpawnUAC();
   2243             return 0;
   2244         }
   2245         // already admin - keep going
   2246     } else if (strcmp(user_access_control, "auto")==0) {
   2247         // Check if it looks like we need UAC control, based
   2248         // on how Python itself was installed.
   2249         if (PlatformSupportsUAC() && !MyIsUserAnAdmin() && NeedAutoUAC()) {
   2250             SpawnUAC();
   2251             return 0;
   2252         }
   2253     } else {
   2254         // display a warning about unknown values - only the developer
   2255         // of the extension will see it (until they fix it!)
   2256         if (user_access_control[0] && strcmp(user_access_control, "none") != 0) {
   2257             MessageBox(GetFocus(), "Bad user_access_control value", "oops", MB_OK);
   2258         // nothing to do.
   2259         }
   2260     }
   2261 
   2262     hwndMain = CreateBackground(title);
   2263 
   2264     RunWizard(hwndMain);
   2265 
   2266     /* Clean up */
   2267     UnmapViewOfFile(arc_data);
   2268     if (ini_file)
   2269         DeleteFile(ini_file);
   2270 
   2271     if (hBitmap)
   2272         DeleteObject(hBitmap);
   2273 
   2274     return 0;
   2275 }
   2276 
   2277 /*********************** uninstall section ******************************/
   2278 
   2279 static int compare(const void *p1, const void *p2)
   2280 {
   2281     return strcmp(*(char **)p2, *(char **)p1);
   2282 }
   2283 
   2284 /*
   2285  * Commit suicide (remove the uninstaller itself).
   2286  *
   2287  * Create a batch file to first remove the uninstaller
   2288  * (will succeed after it has finished), then the batch file itself.
   2289  *
   2290  * This technique has been demonstrated by Jeff Richter,
   2291  * MSJ 1/1996
   2292  */
   2293 void remove_exe(void)
   2294 {
   2295     char exename[_MAX_PATH];
   2296     char batname[_MAX_PATH];
   2297     FILE *fp;
   2298     STARTUPINFO si;
   2299     PROCESS_INFORMATION pi;
   2300 
   2301     GetModuleFileName(NULL, exename, sizeof(exename));
   2302     sprintf(batname, "%s.bat", exename);
   2303     fp = fopen(batname, "w");
   2304     fprintf(fp, ":Repeat\n");
   2305     fprintf(fp, "del \"%s\"\n", exename);
   2306     fprintf(fp, "if exist \"%s\" goto Repeat\n", exename);
   2307     fprintf(fp, "del \"%s\"\n", batname);
   2308     fclose(fp);
   2309 
   2310     ZeroMemory(&si, sizeof(si));
   2311     si.cb = sizeof(si);
   2312     si.dwFlags = STARTF_USESHOWWINDOW;
   2313     si.wShowWindow = SW_HIDE;
   2314     if (CreateProcess(NULL,
   2315                       batname,
   2316                       NULL,
   2317                       NULL,
   2318                       FALSE,
   2319                       CREATE_SUSPENDED | IDLE_PRIORITY_CLASS,
   2320                       NULL,
   2321                       "\\",
   2322                       &si,
   2323                       &pi)) {
   2324         SetThreadPriority(pi.hThread, THREAD_PRIORITY_IDLE);
   2325         SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
   2326         SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS);
   2327         CloseHandle(pi.hProcess);
   2328         ResumeThread(pi.hThread);
   2329         CloseHandle(pi.hThread);
   2330     }
   2331 }
   2332 
   2333 void DeleteRegistryKey(char *string)
   2334 {
   2335     char *keyname;
   2336     char *subkeyname;
   2337     char *delim;
   2338     HKEY hKey;
   2339     long result;
   2340     char *line;
   2341 
   2342     line = strdup(string); /* so we can change it */
   2343 
   2344     keyname = strchr(line, '[');
   2345     if (!keyname)
   2346         return;
   2347     ++keyname;
   2348 
   2349     subkeyname = strchr(keyname, ']');
   2350     if (!subkeyname)
   2351         return;
   2352     *subkeyname++='\0';
   2353     delim = strchr(subkeyname, '\n');
   2354     if (delim)
   2355         *delim = '\0';
   2356 
   2357     result = RegOpenKeyEx(hkey_root,
   2358                           keyname,
   2359                           0,
   2360                           KEY_WRITE,
   2361                           &hKey);
   2362 
   2363     if (result != ERROR_SUCCESS)
   2364         MessageBox(GetFocus(), string, "Could not open key", MB_OK);
   2365     else {
   2366         result = RegDeleteKey(hKey, subkeyname);
   2367         if (result != ERROR_SUCCESS && result != ERROR_FILE_NOT_FOUND)
   2368             MessageBox(GetFocus(), string, "Could not delete key", MB_OK);
   2369         RegCloseKey(hKey);
   2370     }
   2371     free(line);
   2372 }
   2373 
   2374 void DeleteRegistryValue(char *string)
   2375 {
   2376     char *keyname;
   2377     char *valuename;
   2378     char *value;
   2379     HKEY hKey;
   2380     long result;
   2381     char *line;
   2382 
   2383     line = strdup(string); /* so we can change it */
   2384 
   2385 /* Format is 'Reg DB Value: [key]name=value' */
   2386     keyname = strchr(line, '[');
   2387     if (!keyname)
   2388         return;
   2389     ++keyname;
   2390     valuename = strchr(keyname, ']');
   2391     if (!valuename)
   2392         return;
   2393     *valuename++ = '\0';
   2394     value = strchr(valuename, '=');
   2395     if (!value)
   2396         return;
   2397 
   2398     *value++ = '\0';
   2399 
   2400     result = RegOpenKeyEx(hkey_root,
   2401                           keyname,
   2402                           0,
   2403                           KEY_WRITE,
   2404                           &hKey);
   2405     if (result != ERROR_SUCCESS)
   2406         MessageBox(GetFocus(), string, "Could not open key", MB_OK);
   2407     else {
   2408         result = RegDeleteValue(hKey, valuename);
   2409         if (result != ERROR_SUCCESS && result != ERROR_FILE_NOT_FOUND)
   2410             MessageBox(GetFocus(), string, "Could not delete value", MB_OK);
   2411         RegCloseKey(hKey);
   2412     }
   2413     free(line);
   2414 }
   2415 
   2416 BOOL MyDeleteFile(char *line)
   2417 {
   2418     char *pathname = strchr(line, ':');
   2419     if (!pathname)
   2420         return FALSE;
   2421     ++pathname;
   2422     while (isspace(*pathname))
   2423         ++pathname;
   2424     return DeleteFile(pathname);
   2425 }
   2426 
   2427 BOOL MyRemoveDirectory(char *line)
   2428 {
   2429     char *pathname = strchr(line, ':');
   2430     if (!pathname)
   2431         return FALSE;
   2432     ++pathname;
   2433     while (isspace(*pathname))
   2434         ++pathname;
   2435     return RemoveDirectory(pathname);
   2436 }
   2437 
   2438 BOOL Run_RemoveScript(char *line)
   2439 {
   2440     char *dllname;
   2441     char *scriptname;
   2442     static char lastscript[MAX_PATH];
   2443 
   2444 /* Format is 'Run Scripts: [pythondll]scriptname' */
   2445 /* XXX Currently, pythondll carries no path!!! */
   2446     dllname = strchr(line, '[');
   2447     if (!dllname)
   2448         return FALSE;
   2449     ++dllname;
   2450     scriptname = strchr(dllname, ']');
   2451     if (!scriptname)
   2452         return FALSE;
   2453     *scriptname++ = '\0';
   2454     /* this function may be called more than one time with the same
   2455        script, only run it one time */
   2456     if (strcmp(lastscript, scriptname)) {
   2457         char *argv[3] = {NULL, "-remove", NULL};
   2458         char *buffer = NULL;
   2459 
   2460         argv[0] = scriptname;
   2461 
   2462         if (0 != run_installscript(scriptname, 2, argv, &buffer))
   2463             fprintf(stderr, "*** Could not run installation script ***");
   2464 
   2465         if (buffer && buffer[0])
   2466             MessageBox(GetFocus(), buffer, "uninstall-script", MB_OK);
   2467         free(buffer);
   2468 
   2469         strcpy(lastscript, scriptname);
   2470     }
   2471     return TRUE;
   2472 }
   2473 
   2474 int DoUninstall(int argc, char **argv)
   2475 {
   2476     FILE *logfile;
   2477     char buffer[4096];
   2478     int nLines = 0;
   2479     int i;
   2480     char *cp;
   2481     int nFiles = 0;
   2482     int nDirs = 0;
   2483     int nErrors = 0;
   2484     char **lines;
   2485     int lines_buffer_size = 10;
   2486 
   2487     if (argc != 3) {
   2488         MessageBox(NULL,
   2489                    "Wrong number of args",
   2490                    NULL,
   2491                    MB_OK);
   2492         return 1; /* Error */
   2493     }
   2494     if (strcmp(argv[1], "-u")) {
   2495         MessageBox(NULL,
   2496                    "2. arg is not -u",
   2497                    NULL,
   2498                    MB_OK);
   2499         return 1; /* Error */
   2500     }
   2501 
   2502     logfile = fopen(argv[2], "r");
   2503     if (!logfile) {
   2504         MessageBox(NULL,
   2505                    "could not open logfile",
   2506                    NULL,
   2507                    MB_OK);
   2508         return 1; /* Error */
   2509     }
   2510 
   2511     lines = (char **)malloc(sizeof(char *) * lines_buffer_size);
   2512     if (!lines)
   2513         return SystemError(0, "Out of memory");
   2514 
   2515     /* Read the whole logfile, realloacting the buffer */
   2516     while (fgets(buffer, sizeof(buffer), logfile)) {
   2517         int len = strlen(buffer);
   2518         /* remove trailing white space */
   2519         while (isspace(buffer[len-1]))
   2520             len -= 1;
   2521         buffer[len] = '\0';
   2522         lines[nLines++] = strdup(buffer);
   2523         if (nLines >= lines_buffer_size) {
   2524             lines_buffer_size += 10;
   2525             lines = (char **)realloc(lines,
   2526                                      sizeof(char *) * lines_buffer_size);
   2527             if (!lines)
   2528                 return SystemError(0, "Out of memory");
   2529         }
   2530     }
   2531     fclose(logfile);
   2532 
   2533     /* Sort all the lines, so that highest 3-digit codes are first */
   2534     qsort(&lines[0], nLines, sizeof(char *),
   2535           compare);
   2536 
   2537     if (IDYES != MessageBox(NULL,
   2538                             "Are you sure you want to remove\n"
   2539                             "this package from your computer?",
   2540                             "Please confirm",
   2541                             MB_YESNO | MB_ICONQUESTION))
   2542         return 0;
   2543 
   2544     hkey_root = HKEY_LOCAL_MACHINE;
   2545     cp = "";
   2546     for (i = 0; i < nLines; ++i) {
   2547         /* Ignore duplicate lines */
   2548         if (strcmp(cp, lines[i])) {
   2549             int ign;
   2550             cp = lines[i];
   2551             /* Parse the lines */
   2552             if (2 == sscanf(cp, "%d Root Key: %s", &ign, &buffer)) {
   2553                 if (strcmp(buffer, "HKEY_CURRENT_USER")==0)
   2554                     hkey_root = HKEY_CURRENT_USER;
   2555                 else {
   2556                     // HKLM - check they have permissions.
   2557                     if (!HasLocalMachinePrivs()) {
   2558                         MessageBox(GetFocus(),
   2559                                    "You do not seem to have sufficient access rights\n"
   2560                                    "on this machine to uninstall this software",
   2561                                    NULL,
   2562                                    MB_OK | MB_ICONSTOP);
   2563                         return 1; /* Error */
   2564                     }
   2565                 }
   2566             } else if (2 == sscanf(cp, "%d Made Dir: %s", &ign, &buffer)) {
   2567                 if (MyRemoveDirectory(cp))
   2568                     ++nDirs;
   2569                 else {
   2570                     int code = GetLastError();
   2571                     if (code != 2 && code != 3) { /* file or path not found */
   2572                         ++nErrors;
   2573                     }
   2574                 }
   2575             } else if (2 == sscanf(cp, "%d File Copy: %s", &ign, &buffer)) {
   2576                 if (MyDeleteFile(cp))
   2577                     ++nFiles;
   2578                 else {
   2579                     int code = GetLastError();
   2580                     if (code != 2 && code != 3) { /* file or path not found */
   2581                         ++nErrors;
   2582                     }
   2583                 }
   2584             } else if (2 == sscanf(cp, "%d File Overwrite: %s", &ign, &buffer)) {
   2585                 if (MyDeleteFile(cp))
   2586                     ++nFiles;
   2587                 else {
   2588                     int code = GetLastError();
   2589                     if (code != 2 && code != 3) { /* file or path not found */
   2590                         ++nErrors;
   2591                     }
   2592                 }
   2593             } else if (2 == sscanf(cp, "%d Reg DB Key: %s", &ign, &buffer)) {
   2594                 DeleteRegistryKey(cp);
   2595             } else if (2 == sscanf(cp, "%d Reg DB Value: %s", &ign, &buffer)) {
   2596                 DeleteRegistryValue(cp);
   2597             } else if (2 == sscanf(cp, "%d Run Script: %s", &ign, &buffer)) {
   2598                 Run_RemoveScript(cp);
   2599             }
   2600         }
   2601     }
   2602 
   2603     if (DeleteFile(argv[2])) {
   2604         ++nFiles;
   2605     } else {
   2606         ++nErrors;
   2607         SystemError(GetLastError(), argv[2]);
   2608     }
   2609     if (nErrors)
   2610         wsprintf(buffer,
   2611                  "%d files and %d directories removed\n"
   2612                  "%d files or directories could not be removed",
   2613                  nFiles, nDirs, nErrors);
   2614     else
   2615         wsprintf(buffer, "%d files and %d directories removed",
   2616                  nFiles, nDirs);
   2617     MessageBox(NULL, buffer, "Uninstall Finished!",
   2618                MB_OK | MB_ICONINFORMATION);
   2619     remove_exe();
   2620     return 0;
   2621 }
   2622 
   2623 int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst,
   2624                     LPSTR lpszCmdLine, INT nCmdShow)
   2625 {
   2626     extern int __argc;
   2627     extern char **__argv;
   2628     char *basename;
   2629 
   2630     GetModuleFileName(NULL, modulename, sizeof(modulename));
   2631 
   2632     /* Map the executable file to memory */
   2633     arc_data = MapExistingFile(modulename, &arc_size);
   2634     if (!arc_data) {
   2635         SystemError(GetLastError(), "Could not open archive");
   2636         return 1;
   2637     }
   2638 
   2639     /* OK. So this program can act as installer (self-extracting
   2640      * zip-file, or as uninstaller when started with '-u logfile'
   2641      * command line flags.
   2642      *
   2643      * The installer is usually started without command line flags,
   2644      * and the uninstaller is usually started with the '-u logfile'
   2645      * flag. What to do if some innocent user double-clicks the
   2646      * exe-file?
   2647      * The following implements a defensive strategy...
   2648      */
   2649 
   2650     /* Try to extract the configuration data into a temporary file */
   2651     if (ExtractInstallData(arc_data, arc_size, &exe_size,
   2652                            &ini_file, &pre_install_script))
   2653         return DoInstall();
   2654 
   2655     if (!ini_file && __argc > 1) {
   2656         return DoUninstall(__argc, __argv);
   2657     }
   2658 
   2659 
   2660     basename = strrchr(modulename, '\\');
   2661     if (basename)
   2662         ++basename;
   2663 
   2664     /* Last guess about the purpose of this program */
   2665     if (basename && (0 == strncmp(basename, "Remove", 6)))
   2666         SystemError(0, "This program is normally started by windows");
   2667     else
   2668         SystemError(0, "Setup program invalid or damaged");
   2669     return 1;
   2670 }
   2671