home *** CD-ROM | disk | FTP | other *** search
/ AmigActive 15 / AACD15.ISO / AACD / Programming / Python2 / Python20_source / Misc / distutils / install.c < prev    next >
Encoding:
C/C++ Source or Header  |  2000-10-25  |  21.9 KB  |  901 lines

  1. /*
  2.  * Written by Thomas Heller, May 2000
  3.  *
  4.  * $Id: install.c,v 1.8 2000/10/12 19:25:58 theller Exp $
  5.  */
  6.  
  7. /*
  8.  * Windows Installer program for distutils.
  9.  *
  10.  * (a kind of self-extracting zip-file)
  11.  *
  12.  * At runtime, the exefile has appended:
  13.  * - compressed setup-data in ini-format, containing the following sections:
  14.  *    [metadata]
  15.  *    author=Greg Ward
  16.  *    author_email=gward@python.net
  17.  *    description=Python Distribution Utilities
  18.  *    licence=Python
  19.  *    name=Distutils
  20.  *    url=http://www.python.org/sigs/distutils-sig/
  21.  *    version=0.9pre
  22.  *
  23.  *    [Setup]
  24.  *    info= text to be displayed in the edit-box
  25.  *    title= to be displayed by this program
  26.  *    target_version = if present, python version required
  27.  *    pyc_compile = if 0, do not compile py to pyc
  28.  *    pyo_compile = if 0, do not compile py to pyo
  29.  *
  30.  * - a struct meta_data_hdr, describing the above
  31.  * - a zip-file, containing the modules to be installed.
  32.  *   for the format see http://www.pkware.com/appnote.html
  33.  *
  34.  * What does this program do?
  35.  * - the setup-data is uncompressed and written to a temporary file.
  36.  * - setup-data is queried with GetPrivateProfile... calls
  37.  * - [metadata] - info is displayed in the dialog box
  38.  * - The registry is searched for installations of python
  39.  * - The user can select the python version to use.
  40.  * - The python-installation directory (sys.prefix) is displayed
  41.  * - When the start-button is pressed, files from the zip-archive
  42.  *   are extracted to the file system. All .py filenames are stored
  43.  *   in a list.
  44.  */
  45.  
  46. /*
  47.  * To Do:
  48.  *  - install a help-button, which will display the above
  49.  *    text to the user
  50.  *  - should there be a possibility to display a README file
  51.  *    before starting the installation (if one is present in the archive)
  52.  *  - think about uninstaller
  53.  *  - more comments about what the code does(?)
  54.  *
  55.  *  - think about an uninstaller (?)
  56.  *  - evolve this into a full blown installer (???)
  57.  */
  58.  
  59. #include <windows.h>
  60. #include <commctrl.h>
  61. #include "resource.h"
  62.  
  63. #include <stdio.h>
  64. #include <stdlib.h>
  65. #include <stdarg.h>
  66. #include <string.h>
  67.  
  68. #include "archive.h"
  69.  
  70. /* Bah: global variables */
  71. HWND hwndMain;
  72. HWND hDialog;
  73.  
  74. char *ini_file;            /* Full pathname of ini-file */
  75. /* From ini-file */
  76. char info[4096];        /* [Setup] info= */
  77. char title[80];            /* [Setup] title= */
  78. char target_version[10];    /* [Setup] target_version= */
  79. char build_info[80];        /* [Setup] build_info= */
  80.  
  81. char meta_name[80];
  82.  
  83. char *arc_data;            /* memory mapped archive */
  84. DWORD arc_size;            /* number of bytes in archive */
  85. char install_dir[MAX_PATH];
  86. char pythondll[MAX_PATH];
  87. BOOL pyc_compile, pyo_compile;
  88.  
  89. BOOL success;            /* Installation successfull? */
  90.  
  91. #define WM_NUMFILES WM_USER+1
  92.     /* wParam: 0, lParam: total number of files */
  93. #define WM_NEXTFILE WM_USER+2
  94.     /* wParam: number of this file */
  95.     /* lParam: points to pathname */
  96.  
  97. enum { UNSPECIFIED, ASK, ALWAYS, NEVER } allow_overwrite = UNSPECIFIED;
  98.  
  99. static void unescape (char *str)
  100. {
  101.     char *dst = str;
  102.     char *src = str;
  103.     char *eon;
  104.     char ch;
  105.  
  106.     while (src && *src) {
  107.     if (*src == '\\') {
  108.         switch (*++src) {
  109.         case 'n':
  110.         *dst++ = '\n';
  111.         *dst++ = '\r';
  112.         break;
  113.         case 'r':
  114.         *dst++ = '\r';
  115.         break;
  116.         case '0': case '1': case '2': case '3':
  117.         ch = (char)strtol (src, &eon, 8);
  118.         if (ch == '\n')
  119.             *dst++ = '\r';
  120.         *dst++ = ch;
  121.         src = eon;
  122.         }
  123.     } else
  124.         *dst++ = *src++;
  125.     }
  126.     *dst = '\0';
  127. }
  128.  
  129. static struct tagFile {
  130.     char *path;
  131.     struct tagFile *next;
  132. } *file_list = NULL;
  133.  
  134. static void add_to_filelist (char *path)
  135. {
  136.     struct tagFile *p;
  137.     p = (struct tagFile *)malloc (sizeof (struct tagFile));
  138.     p->path = strdup (path);
  139.     p->next = file_list;
  140.     file_list = p;
  141. }
  142.  
  143. static int do_compile_files (int (__cdecl * PyRun_SimpleString)(char *))
  144. {
  145.     struct tagFile *p;
  146.     int total, n;
  147.     char Buffer[MAX_PATH + 64];
  148.     int errors = 0;
  149.  
  150.     total = 0;
  151.     p = file_list;
  152.     while (p) {
  153.     ++total;
  154.     p = p->next;
  155.     }
  156.     SendDlgItemMessage (hDialog, IDC_PROGRESS, PBM_SETRANGE, 0,
  157.             MAKELPARAM (0, total));
  158.     SendDlgItemMessage (hDialog, IDC_PROGRESS, PBM_SETPOS, 0, 0);
  159.  
  160.     n = 0;
  161.     p = file_list;
  162.     while (p) {
  163.     ++n;
  164.         wsprintf (Buffer,
  165.           "import py_compile; py_compile.compile (r'%s')",
  166.           p->path);
  167.         if (PyRun_SimpleString (Buffer))
  168.         ++errors;
  169.     SendDlgItemMessage (hDialog, IDC_PROGRESS, PBM_SETPOS, n, 0);
  170.     SetDlgItemText (hDialog, IDC_INFO, p->path);
  171.     p = p->next;
  172.     }
  173.     return errors;
  174. }
  175.  
  176. static int compile_filelist (BOOL optimize_flag)
  177. {
  178.     void (__cdecl * Py_Initialize)(void);
  179.     void (__cdecl * Py_Finalize)(void);
  180.     int (__cdecl * PyRun_SimpleString)(char *);
  181.     int *Py_OptimizeFlag;
  182.     int errors = 0;
  183.     HINSTANCE hPython;
  184.     struct tagFile *p = file_list;
  185.  
  186.     if (!p)
  187.     return 0;
  188.     SetDlgItemText (hDialog, IDC_INFO, "Loading python...");
  189.         
  190.     hPython = LoadLibrary (pythondll);
  191.     if (!hPython)
  192.     return 1;
  193.     Py_Initialize = (void (*)(void))GetProcAddress
  194.     (hPython,"Py_Initialize");
  195.  
  196.     Py_Finalize = (void (*)(void))GetProcAddress (hPython,
  197.                           "Py_Finalize");
  198.     PyRun_SimpleString = (int (*)(char *))GetProcAddress (
  199.     hPython, "PyRun_SimpleString");
  200.  
  201.     Py_OptimizeFlag = (int *)GetProcAddress (hPython,
  202.                          "Py_OptimizeFlag");
  203.     
  204.     *Py_OptimizeFlag = optimize_flag;
  205.     Py_Initialize ();
  206.  
  207.     errors += do_compile_files (PyRun_SimpleString);
  208.     Py_Finalize ();
  209.     FreeLibrary (hPython);
  210.  
  211.     return errors;
  212. }
  213.  
  214. static BOOL SystemError (int error, char *msg)
  215. {
  216.     char Buffer[1024];
  217.     int n;
  218.  
  219.     if (error) {
  220.         LPVOID lpMsgBuf;
  221.     FormatMessage( 
  222.         FORMAT_MESSAGE_ALLOCATE_BUFFER | 
  223.         FORMAT_MESSAGE_FROM_SYSTEM,
  224.         NULL,
  225.         error,
  226.         MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
  227.         (LPSTR)&lpMsgBuf,
  228.         0,
  229.         NULL 
  230.         );
  231.         strncpy (Buffer, lpMsgBuf, sizeof (Buffer));
  232.     LocalFree (lpMsgBuf);
  233.     } else
  234.     Buffer[0] = '\0';
  235.     n = lstrlen (Buffer);
  236.     _snprintf (Buffer+n, sizeof (Buffer)-n, msg);
  237.     MessageBox (hwndMain, Buffer, "Runtime Error", MB_OK | MB_ICONSTOP);
  238.     return FALSE;
  239. }
  240.  
  241. static BOOL AskOverwrite (char *filename)
  242. {
  243.     int result;
  244.   again:
  245.     if (allow_overwrite == ALWAYS)
  246.     return TRUE;
  247.     if (allow_overwrite == NEVER)
  248.     return FALSE;
  249.     if (allow_overwrite == ASK)
  250.         return (IDYES == MessageBox (hwndMain,
  251.                 filename,
  252.                 "Overwrite existing file?",
  253.                 MB_YESNO | MB_ICONQUESTION));
  254.  
  255.     result = MessageBox (hwndMain,
  256. "Overwrite existing files?\n"
  257. "\n"
  258. "Press YES to ALWAYS overwrite existing files,\n"
  259. "press NO to NEVER overwrite existing files,\n"
  260. "press CANCEL to ASK individually.",
  261.                  "Overwrite options",
  262.              MB_YESNOCANCEL | MB_ICONQUESTION);
  263.     if (result == IDYES)
  264.     allow_overwrite = ALWAYS;
  265.     else if (result == IDNO)
  266.     allow_overwrite = NEVER;
  267.     else
  268.     allow_overwrite = ASK;
  269.     goto again;
  270. }
  271.  
  272. static BOOL notify (int code, char *fmt, ...)
  273. {
  274.     char Buffer[1024];
  275.     va_list marker;
  276.     BOOL result = TRUE;
  277.     int a, b;
  278.     char *cp;
  279.  
  280.     va_start (marker, fmt);
  281.     _vsnprintf (Buffer, sizeof (Buffer), fmt, marker);
  282.  
  283.     switch (code) {
  284. /* Questions */
  285.     case CAN_OVERWRITE:
  286.     result = AskOverwrite (Buffer);
  287.     break;
  288.  
  289. /* Information notification */
  290.     case DIR_CREATED:
  291.     break;
  292.  
  293.     case FILE_CREATED:
  294.     case FILE_OVERWRITTEN:
  295.     if ((cp = strrchr(fmt, '.')) && (0 == strcmp (cp, ".py")))
  296.          add_to_filelist (fmt);
  297.     break;
  298.  
  299. /* Error Messages */
  300.     case ZLIB_ERROR:
  301.     MessageBox (GetFocus(), Buffer, "Error", MB_OK | MB_ICONWARNING);
  302.     break;
  303.  
  304.     case SYSTEM_ERROR:
  305.     SystemError (GetLastError(), Buffer);
  306.     break;
  307.  
  308.     case NUM_FILES:
  309.     a = va_arg (marker, int);
  310.     b = va_arg (marker, int);
  311.     SendMessage (hDialog, WM_NUMFILES, 0, MAKELPARAM (0, a));
  312.     SendMessage (hDialog, WM_NEXTFILE, b, (LPARAM)fmt);
  313.     }
  314.     va_end (marker);
  315.     
  316.     return result;
  317. }
  318.  
  319. static char *MapExistingFile (char *pathname, DWORD *psize)
  320. {
  321.     HANDLE hFile, hFileMapping;
  322.     DWORD nSizeLow, nSizeHigh;
  323.     char *data;
  324.  
  325.     hFile = CreateFile (pathname,
  326.     GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,
  327.     FILE_ATTRIBUTE_NORMAL, NULL);
  328.     if (hFile == INVALID_HANDLE_VALUE)
  329.     return NULL;
  330.     nSizeLow = GetFileSize (hFile, &nSizeHigh);
  331.     hFileMapping = CreateFileMapping (hFile,
  332.     NULL, PAGE_READONLY, 0, 0, NULL);
  333.     CloseHandle (hFile);
  334.  
  335.     if (hFileMapping == INVALID_HANDLE_VALUE)
  336.     return NULL;
  337.     
  338.     data = MapViewOfFile (hFileMapping,
  339.     FILE_MAP_READ, 0, 0, 0);
  340.  
  341.     CloseHandle (hFileMapping);
  342.     *psize = nSizeLow;
  343.     return data;
  344. }
  345.  
  346. static char *ExtractIniFile (char *data, DWORD size)
  347. {
  348.     /* read the end of central directory record */
  349.     struct eof_cdir *pe = (struct eof_cdir *)&data[size - sizeof
  350.                           (struct eof_cdir)];
  351.     
  352.     int arc_start = size - sizeof (struct eof_cdir) - pe->nBytesCDir -
  353.     pe->ofsCDir;
  354.  
  355.     int ofs = arc_start - sizeof (struct meta_data_hdr);
  356.  
  357.     /* read meta_data info */
  358.     struct meta_data_hdr *pmd = (struct meta_data_hdr *)&data[ofs];
  359.     char *src, *dst;
  360.     char *ini_file;
  361.     char tempdir[MAX_PATH];
  362.  
  363.     if (pe->tag != 0x06054b50) {
  364.     SystemError (0, "Setup program invalid or damaged");
  365.     return NULL;
  366.     }
  367.  
  368.     if (pmd->tag != 0x12345679 || ofs < 0) {
  369.     SystemError (0, "Setup program invalid or damaged");
  370.     return NULL;
  371.     }
  372.  
  373.     src = ((char *)pmd) - pmd->uncomp_size;
  374.     ini_file = malloc (MAX_PATH); /* will be returned, so do not free it */
  375.     if (!ini_file)
  376.     return NULL;
  377.     if (!GetTempPath (sizeof (tempdir), tempdir)
  378.     || !GetTempFileName (tempdir, "~du", 0, ini_file)) {
  379.     SystemError (GetLastError(), "Could not create temporary file");
  380.     return NULL;
  381.     }
  382.     
  383.     dst = map_new_file (CREATE_ALWAYS, ini_file, NULL, pmd->uncomp_size,
  384.             0, 0, notify);
  385.     if (!dst)
  386.     return NULL;
  387.     memcpy (dst, src, pmd->uncomp_size);
  388.     UnmapViewOfFile(dst);
  389.     return ini_file;
  390. }
  391.  
  392. static void PumpMessages (void)
  393. {
  394.     MSG msg;
  395.     while (PeekMessage (&msg, NULL, 0, 0, PM_REMOVE)) {
  396.         TranslateMessage (&msg);
  397.         DispatchMessage (&msg);
  398.     }
  399. }
  400.  
  401. LRESULT CALLBACK
  402. WindowProc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
  403. {
  404.     HDC hdc;
  405.     HFONT hFont;
  406.     int h;
  407.     PAINTSTRUCT ps;
  408.     switch (msg) {
  409.     case WM_PAINT:
  410.     hdc = BeginPaint (hwnd, &ps);
  411.     h = GetSystemMetrics (SM_CYSCREEN) / 10;
  412.     hFont = CreateFont (h, 0, 0, 0, 700, TRUE,
  413.                 0, 0, 0, 0, 0, 0, 0, "Times Roman");
  414.     hFont = SelectObject (hdc, hFont);
  415.     SetBkMode (hdc, TRANSPARENT);
  416.     TextOut (hdc, 15, 15, title, strlen (title));
  417.     SetTextColor (hdc, RGB (255, 255, 255));
  418.     TextOut (hdc, 10, 10, title, strlen (title));
  419.     DeleteObject (SelectObject (hdc, hFont));
  420.     EndPaint (hwnd, &ps);
  421.     return 0;
  422.     }
  423.     return DefWindowProc (hwnd, msg, wParam, lParam);
  424. }
  425.  
  426. static HWND CreateBackground (char *title)
  427. {
  428.     WNDCLASS wc;
  429.     HWND hwnd;
  430.     char buffer[4096];
  431.  
  432.     wc.style = CS_VREDRAW | CS_HREDRAW;
  433.     wc.lpfnWndProc = WindowProc;
  434.     wc.cbWndExtra = 0;
  435.     wc.cbClsExtra = 0;
  436.     wc.hInstance = GetModuleHandle (NULL);
  437.     wc.hIcon = NULL;
  438.     wc.hCursor = LoadCursor (NULL, IDC_ARROW);
  439.     wc.hbrBackground = CreateSolidBrush (RGB (0, 0, 128));
  440.     wc.lpszMenuName = NULL;
  441.     wc.lpszClassName = "SetupWindowClass";
  442.  
  443.     if (!RegisterClass (&wc))
  444.     MessageBox (hwndMain,
  445.             "Could not register window class",
  446.             "Setup.exe", MB_OK);
  447.  
  448.     wsprintf (buffer, "Setup %s", title);
  449.     hwnd = CreateWindow ("SetupWindowClass",
  450.              buffer,
  451.              0,
  452.              0, 0,
  453.              GetSystemMetrics (SM_CXFULLSCREEN),
  454.              GetSystemMetrics (SM_CYFULLSCREEN),
  455.              NULL,
  456.              NULL,
  457.              GetModuleHandle (NULL),
  458.              NULL);
  459.     ShowWindow (hwnd, SW_SHOWMAXIMIZED);
  460.     UpdateWindow (hwnd);
  461.     return hwnd;
  462. }
  463.  
  464. /*
  465.  * Center a window on the screen
  466.  */
  467. static void CenterWindow (HWND hwnd)
  468. {
  469.     RECT rc;
  470.     int w, h;
  471.  
  472.     GetWindowRect (hwnd, &rc);
  473.     w = GetSystemMetrics (SM_CXSCREEN);
  474.     h = GetSystemMetrics (SM_CYSCREEN);
  475.     MoveWindow (hwnd, (w - (rc.right-rc.left))/2, (h - (rc.bottom-rc.top))/2,
  476.         rc.right-rc.left, rc.bottom-rc.top, FALSE);
  477. }
  478.  
  479. #include <prsht.h>
  480.  
  481. BOOL CALLBACK
  482. IntroDlgProc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
  483. {
  484.     LPNMHDR lpnm;
  485.     char Buffer[4096];
  486.     switch (msg) {
  487.     case WM_INITDIALOG:
  488.     CenterWindow (GetParent (hwnd));
  489.     wsprintf (Buffer,
  490.           "This Wizard will install %s on your computer. "
  491.           "Click Next to continue or Cancel to exit the Setup Wizard.",
  492.           meta_name);
  493.     SetDlgItemText (hwnd, IDC_TITLE, Buffer);
  494.     SetDlgItemText (hwnd, IDC_INTRO_TEXT, info);
  495.     SetDlgItemText (hwnd, IDC_BUILD_INFO, build_info);
  496.     return FALSE;
  497.  
  498.     case WM_NOTIFY:
  499.         lpnm = (LPNMHDR) lParam;
  500.  
  501.         switch (lpnm->code) {
  502.     case PSN_SETACTIVE:
  503.         PropSheet_SetWizButtons(GetParent(hwnd), PSWIZB_NEXT);
  504.         break;
  505.  
  506.     case PSN_WIZNEXT:
  507.         break;
  508.  
  509.     case PSN_RESET:
  510.         break;
  511.         
  512.     default:
  513.         break;
  514.     }
  515.     }
  516.     return FALSE;
  517. }
  518.  
  519. /*
  520.  * Fill the listbox specified by hwnd with all python versions found
  521.  * in the registry. version, if not NULL or empty, is the version
  522.  * required.
  523.  */
  524. static BOOL GetPythonVersions (HWND hwnd, HKEY hkRoot, LPSTR version)
  525. {
  526.     DWORD index = 0;
  527.     char core_version[80];
  528.     HKEY hKey;
  529.     BOOL result = TRUE;
  530.     DWORD bufsize;
  531.  
  532.     if (ERROR_SUCCESS != RegOpenKeyEx (hkRoot,
  533.                      "Software\\Python\\PythonCore",
  534.                      0,    KEY_READ, &hKey))
  535.     return FALSE;
  536.     bufsize = sizeof (core_version);
  537.     while (ERROR_SUCCESS == RegEnumKeyEx (hKey, index,
  538.                       core_version, &bufsize, NULL,
  539.                       NULL, NULL, NULL)) {
  540.     char subkey_name[80], vers_name[80], prefix_buf[MAX_PATH+1];
  541.     int itemindex;
  542.     DWORD value_size;
  543.     HKEY hk;
  544.  
  545.     bufsize = sizeof (core_version);
  546.     ++index;
  547.     if (version && *version && strcmp (version, core_version))
  548.         continue;
  549.  
  550.     wsprintf (vers_name, "Python Version %s (found in registry)",
  551.           core_version);
  552.     itemindex = SendMessage (hwnd, LB_ADDSTRING, 0,
  553.                  (LPARAM)(LPSTR)vers_name);
  554.     wsprintf (subkey_name,
  555.           "Software\\Python\\PythonCore\\%s\\InstallPath",
  556.           core_version);
  557.     value_size = sizeof (subkey_name);
  558.     RegOpenKeyEx (hkRoot, subkey_name, 0, KEY_READ, &hk);
  559.     RegQueryValueEx (hk, NULL, NULL, NULL, prefix_buf,
  560.              &value_size);
  561.     RegCloseKey (hk);
  562.     SendMessage (hwnd, LB_SETITEMDATA, itemindex,
  563.              (LPARAM)(LPSTR)strdup (prefix_buf));
  564.     }
  565.     RegCloseKey (hKey);
  566.     return result;
  567. }
  568.  
  569. BOOL CALLBACK
  570. SelectPythonDlgProc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
  571. {
  572.     LPNMHDR lpnm;
  573.  
  574.     switch (msg) {
  575.     case WM_INITDIALOG:
  576.     GetPythonVersions (GetDlgItem (hwnd, IDC_VERSIONS_LIST),
  577.                HKEY_LOCAL_MACHINE, target_version);
  578.     GetPythonVersions (GetDlgItem (hwnd, IDC_VERSIONS_LIST),
  579.                HKEY_CURRENT_USER, target_version);
  580.     {    /* select the last entry which is the highest python
  581.            version found */
  582.         int count;
  583.         count = SendDlgItemMessage (hwnd, IDC_VERSIONS_LIST,
  584.                     LB_GETCOUNT, 0, 0);
  585.         if (count && count != LB_ERR)
  586.         SendDlgItemMessage (hwnd, IDC_VERSIONS_LIST, LB_SETCURSEL,
  587.                     count-1, 0);
  588.     }
  589.     goto UpdateInstallDir;
  590.     break;
  591.  
  592.     case WM_COMMAND:
  593.     switch (LOWORD (wParam)) {
  594.     case IDC_VERSIONS_LIST:
  595.         switch (HIWORD (wParam)) {
  596.         int id;
  597.         char *cp;
  598.         case LBN_SELCHANGE:
  599.           UpdateInstallDir:
  600.         PropSheet_SetWizButtons(GetParent(hwnd),
  601.                     PSWIZB_BACK | PSWIZB_NEXT);
  602.         id = SendDlgItemMessage (hwnd, IDC_VERSIONS_LIST,
  603.                      LB_GETCURSEL, 0, 0);
  604.         if (id == LB_ERR) {
  605.             PropSheet_SetWizButtons(GetParent(hwnd),
  606.                         PSWIZB_BACK);
  607.             SetDlgItemText (hwnd, IDC_PATH, "");
  608.             strcpy (install_dir, "");
  609.             strcpy (pythondll, "");
  610.         } else {
  611.             char *pbuf;
  612.             int result;
  613.             PropSheet_SetWizButtons(GetParent(hwnd),
  614.                         PSWIZB_BACK | PSWIZB_NEXT);
  615.             cp = (LPSTR)SendDlgItemMessage (hwnd,
  616.                             IDC_VERSIONS_LIST,
  617.                             LB_GETITEMDATA,
  618.                             id,
  619.                             0);
  620.             strcpy (install_dir, cp);
  621.             SetDlgItemText (hwnd, IDC_PATH, install_dir);
  622.             result = SendDlgItemMessage (hwnd, IDC_VERSIONS_LIST,
  623.                     LB_GETTEXTLEN, (WPARAM)id, 0);
  624.             pbuf = (char *)malloc (result + 1);
  625.             if (pbuf) {
  626.             /* guess the name of the python-dll */
  627.             int major, minor;
  628.             SendDlgItemMessage (hwnd, IDC_VERSIONS_LIST,
  629.                         LB_GETTEXT, (WPARAM)id,
  630.                         (LPARAM)pbuf);
  631.             result = sscanf (pbuf, "Python Version %d.%d",
  632.                      &major, &minor);
  633.             if (result == 2)
  634.                 wsprintf (pythondll, "python%d%d.dll",
  635.                       major, minor);
  636.             free (pbuf);
  637.             } else
  638.             strcpy (pythondll, "");
  639.         }
  640.         }
  641.         break;
  642.     }
  643.     return 0;
  644.  
  645.     case WM_NOTIFY:
  646.         lpnm = (LPNMHDR) lParam;
  647.  
  648.         switch (lpnm->code) {
  649.         int id;
  650.     case PSN_SETACTIVE:
  651.         id = SendDlgItemMessage (hwnd, IDC_VERSIONS_LIST,
  652.                      LB_GETCURSEL, 0, 0);
  653.         if (id == LB_ERR)
  654.         PropSheet_SetWizButtons(GetParent(hwnd),
  655.                     PSWIZB_BACK);
  656.         else
  657.         PropSheet_SetWizButtons(GetParent(hwnd),
  658.                     PSWIZB_BACK | PSWIZB_NEXT);
  659.         break;
  660.  
  661.     case PSN_WIZNEXT:
  662.         break;
  663.  
  664.     case PSN_WIZFINISH:
  665.         break;
  666.  
  667.     case PSN_RESET:
  668.         break;
  669.         
  670.     default:
  671.         break;
  672.     }
  673.     }
  674.     return 0;
  675. }
  676.  
  677. BOOL CALLBACK
  678. InstallFilesDlgProc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
  679. {
  680.     LPNMHDR lpnm;
  681.     char Buffer[4096];
  682.  
  683.     switch (msg) {
  684.     case WM_INITDIALOG:
  685.     wsprintf (Buffer,
  686.           "Click Next to begin the installation of %s. "
  687.           "If you want to review or change any of your "
  688.           " installation settings, click Back. "
  689.           "Click Cancel to exit the wizard.",
  690.           meta_name);
  691.     SetDlgItemText (hwnd, IDC_TITLE, Buffer);
  692.     break;
  693.  
  694.     case WM_NUMFILES:
  695.     SendDlgItemMessage (hwnd, IDC_PROGRESS, PBM_SETRANGE, 0, lParam);
  696.     PumpMessages ();
  697.     return TRUE;
  698.  
  699.     case WM_NEXTFILE:
  700.     SendDlgItemMessage (hwnd, IDC_PROGRESS, PBM_SETPOS, wParam,
  701.                 0);
  702.     SetDlgItemText (hwnd, IDC_INFO, (LPSTR)lParam);
  703.     PumpMessages ();
  704.     return TRUE;
  705.  
  706.     case WM_NOTIFY:
  707.         lpnm = (LPNMHDR) lParam;
  708.  
  709.         switch (lpnm->code) {
  710.     case PSN_SETACTIVE:
  711.         PropSheet_SetWizButtons(GetParent(hwnd),
  712.                     PSWIZB_BACK | PSWIZB_NEXT);
  713.  
  714.         break;
  715.  
  716.     case PSN_WIZFINISH:
  717.         break;
  718.  
  719.     case PSN_WIZNEXT:
  720.         /* Handle a Next button click here */
  721.         hDialog = hwnd;
  722.  
  723.         /* Make sure the installation directory name ends in a */
  724.         /* backslash */
  725.         if (install_dir[strlen(install_dir)-1] != '\\')
  726.         strcat (install_dir, "\\");
  727.         /* Strip the trailing backslash again */
  728.         install_dir[strlen(install_dir)-1] = '\0';
  729.         
  730.  
  731.         /* Extract all files from the archive */
  732.         SetDlgItemText (hwnd, IDC_TITLE, "Installing files...");
  733.         success = unzip_archive (install_dir, arc_data,
  734.                     arc_size, notify);
  735.         /* Compile the py-files */
  736.         if (pyc_compile) {
  737.         SetDlgItemText (hwnd, IDC_TITLE,
  738.                 "Compiling files to .pyc...");
  739.         compile_filelist (FALSE);
  740.         }
  741.         if (pyo_compile) {
  742.         SetDlgItemText (hwnd, IDC_TITLE,
  743.                 "Compiling files to .pyo...");
  744.         compile_filelist (TRUE);
  745.         }
  746.         break;
  747.  
  748.     case PSN_RESET:
  749.         break;
  750.         
  751.     default:
  752.         break;
  753.     }
  754.     }
  755.     return 0;
  756. }
  757.  
  758.  
  759. BOOL CALLBACK
  760. FinishedDlgProc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
  761. {
  762.     LPNMHDR lpnm;
  763.  
  764.     switch (msg) {
  765.     case WM_INITDIALOG:
  766.     if (!success)
  767.         SetDlgItemText (hwnd, IDC_INFO, "Installation failed.");
  768.     break;
  769.  
  770.     case WM_NOTIFY:
  771.         lpnm = (LPNMHDR) lParam;
  772.  
  773.         switch (lpnm->code) {
  774.     case PSN_SETACTIVE: /* Enable the Finish button */
  775.         PropSheet_SetWizButtons(GetParent(hwnd), PSWIZB_FINISH);
  776.         break;
  777.  
  778.     case PSN_WIZNEXT:
  779.         break;
  780.  
  781.     case PSN_WIZFINISH:
  782.         break;
  783.  
  784.     case PSN_RESET:
  785.         break;
  786.         
  787.     default:
  788.         break;
  789.     }
  790.     }
  791.     return 0;
  792. }
  793.  
  794. void RunWizard (HWND hwnd)
  795. {
  796.     PROPSHEETPAGE   psp =       {0};
  797.     HPROPSHEETPAGE  ahpsp[4] =  {0};
  798.     PROPSHEETHEADER psh =       {0};
  799.  
  800.     /* Display module information */
  801.     psp.dwSize =        sizeof(psp);
  802.     psp.dwFlags =       PSP_DEFAULT|PSP_HIDEHEADER;
  803.     psp.hInstance =     GetModuleHandle (NULL);
  804.     psp.lParam =        0;
  805.     psp.pfnDlgProc =    IntroDlgProc;
  806.     psp.pszTemplate =   MAKEINTRESOURCE(IDD_INTRO);
  807.  
  808.     ahpsp[0] =          CreatePropertySheetPage(&psp);
  809.  
  810.     /* Select python version to use */
  811.     psp.dwFlags =       PSP_DEFAULT|PSP_HIDEHEADER;
  812.     psp.pszTemplate =       MAKEINTRESOURCE(IDD_SELECTPYTHON);
  813.     psp.pfnDlgProc =        SelectPythonDlgProc;
  814.  
  815.     ahpsp[1] =              CreatePropertySheetPage(&psp);
  816.  
  817.     /* Install the files */
  818.     psp.dwFlags =        PSP_DEFAULT|PSP_HIDEHEADER;
  819.     psp.pszTemplate =       MAKEINTRESOURCE(IDD_INSTALLFILES);
  820.     psp.pfnDlgProc =        InstallFilesDlgProc;
  821.  
  822.     ahpsp[2] =              CreatePropertySheetPage(&psp);
  823.  
  824.     /* Show success or failure */
  825.     psp.dwFlags =           PSP_DEFAULT|PSP_HIDEHEADER;
  826.     psp.pszTemplate =       MAKEINTRESOURCE(IDD_FINISHED);
  827.     psp.pfnDlgProc =        FinishedDlgProc;
  828.  
  829.     ahpsp[3] =              CreatePropertySheetPage(&psp);
  830.  
  831.     /* Create the property sheet */
  832.     psh.dwSize =            sizeof(psh);
  833.     psh.hInstance =         GetModuleHandle (NULL);
  834.     psh.hwndParent =        hwnd;
  835.     psh.phpage =            ahpsp;
  836.     psh.dwFlags =           PSH_WIZARD/*97*//*|PSH_WATERMARK|PSH_HEADER*/;
  837.     psh.pszbmWatermark =    NULL;
  838.     psh.pszbmHeader =       NULL;
  839.     psh.nStartPage =        0;
  840.     psh.nPages =            4;
  841.  
  842.     PropertySheet(&psh);
  843. }
  844.  
  845. int WINAPI WinMain (HINSTANCE hInst, HINSTANCE hPrevInst,
  846.             LPSTR lpszCmdLine, INT nCmdShow)
  847. {
  848.     char modulename[MAX_PATH];
  849.  
  850.     GetModuleFileName (NULL, modulename, sizeof (modulename));
  851.  
  852.     /* Map the executable file to memory */
  853.     arc_data = MapExistingFile (modulename, &arc_size);
  854.     if (!arc_data) {
  855.     SystemError (GetLastError(), "Could not open archive");
  856.     return 1;
  857.     }
  858.  
  859.     /* Extract the configuration data into a temporary file */
  860.     ini_file = ExtractIniFile (arc_data, arc_size);
  861.     if (!ini_file) {
  862.     return 1;
  863.     }
  864.  
  865.     /* Read installation information */
  866.     GetPrivateProfileString ("Setup", "title", "", title,
  867.                  sizeof (title), ini_file);
  868.     unescape (title);
  869.  
  870.     GetPrivateProfileString ("Setup", "info", "", info,
  871.                  sizeof (info), ini_file);
  872.     unescape (info);
  873.  
  874.     GetPrivateProfileString ("Setup", "build_info", "", build_info,
  875.                  sizeof (build_info), ini_file);
  876.  
  877.     pyc_compile = GetPrivateProfileInt ("Setup", "target_compile", 1,
  878.                     ini_file);
  879.     pyo_compile = GetPrivateProfileInt ("Setup", "target_optimize", 1,
  880.                     ini_file);
  881.  
  882.     GetPrivateProfileString ("Setup", "target_version", "",
  883.                  target_version, sizeof (target_version),
  884.                  ini_file);
  885.  
  886.     GetPrivateProfileString ("metadata", "name", "",
  887.                  meta_name, sizeof (meta_name),
  888.                  ini_file);
  889.  
  890.     hwndMain = CreateBackground (title);
  891.  
  892.     RunWizard (hwndMain);
  893.  
  894.     /* Clean up */
  895.     UnmapViewOfFile (arc_data);
  896.     if (ini_file)
  897.     DeleteFile (ini_file);
  898.  
  899.     return 0;
  900. }
  901.