6 * Written by Keith Marshall <keith@users.osdn.me>
7 * Copyright (C) 2013, 2020, MinGW.org Project
10 * Implementation of the mingw-get setup tool's dialogue controller.
13 * This is free software. Permission is granted to copy, modify and
14 * redistribute this software, under the provisions of the GNU General
15 * Public License, Version 3, (or, at your option, any later version),
16 * as published by the Free Software Foundation; see the file COPYING
17 * for licensing details.
19 * Note, in particular, that this software is provided "as is", in the
20 * hope that it may prove useful, but WITHOUT WARRANTY OF ANY KIND; not
21 * even an implied WARRANTY OF MERCHANTABILITY, nor of FITNESS FOR ANY
22 * PARTICULAR PURPOSE. Under no circumstances will the author, or the
23 * MinGW Project, accept liability for any damages, however caused,
24 * arising from the use of this software.
27 #define WIN32_LEAN_AND_MEAN
28 #define _WIN32_WINNT 0x0500
30 /* This component application does not support the DEBUGLEVEL feature
31 * of the main mingw-get application.
48 #include <sys/types.h>
54 static void ViewLicence( void * )
56 /* Helper routine, dispatching requests via the MS-Windows shell,
57 * so that the user's choice of web browser is invoked to display
58 * the GNU General Public Licence, Version 3.
60 * Note that this function runs in its own thread, and requires
61 * COM services to be enabled...
65 * ...so that the shell process may be invoked.
67 ShellExecute( NULL, NULL,
68 "http://www.gnu.org/licenses/gpl-3.0-standalone.html",
69 NULL, NULL, SW_SHOWNORMAL
72 /* The active thread terminates on return from this function;
73 * it no longer requires COM services.
78 /* Embed procedures for creation of file system objects, and for
79 * constructing argument vectors for passing to child processes.
84 static POINT offset = { 0, 0 };
85 static int CALLBACK confirm( HWND dlg, unsigned msg, WPARAM request, LPARAM )
87 /* A generic dialogue box procedure which positions the dialogue
88 * box at a specified offset, then waits for any supported button
89 * click, returning the associated button identifier; this is used
90 * by BrowseLicenceDocument() to confirm requests to invoke the
91 * web browser to display the product licence.
96 * When first displaying the confirmation dialogue...
98 if( (offset.x | offset.y) != 0 )
100 /* ...adjust its position as specified, and ensure that
101 * it is presented as foremost in the z-order...
104 GetWindowRect( dlg, &whence );
105 OffsetRect( &whence, offset.x, offset.y );
106 SetWindowPos( dlg, HWND_TOP,
107 whence.left, whence.top, 0, 0, SWP_NOSIZE
113 /* ...then close the dialogue, returning the identification
114 * of the button, as soon as any is clicked; (note that this
115 * assumes that the only active controls within the dialogue
116 * are presented as buttons).
118 EndDialog( dlg, LOWORD( request ) );
124 static inline void BrowseLicenceDocument( HWND dlg, long x = 0, long y = 0 )
126 /* Routine to dispatch requests to display the product licence;
127 * it is invoked when the user clicks the "View Licence" button,
128 * and responds by requesting confirmation before launching the
129 * internet browser in a separate thread, to display an online
130 * copy of the licence document.
132 offset.x = x; offset.y = y;
134 MAKEINTRESOURCE( IDD_SETUP_LICENCE ), dlg, confirm ) == IDOK
135 ) _beginthread( ViewLicence, 0, NULL );
138 /* The following string constants are abstracted from pkgkeys.c;
139 * we redefine this minimal subset here, to avoid the overhead of
140 * importing the entire content of the pkgkeys module.
142 * Note: All of these must be defined with "C" linkage, to comply
143 * with their original declarations in "pkgkeys.h".
147 static const char *uri_key = "uri";
148 static const char *mirror_key = "mirror";
149 static const char *value_none = "none";
155 /* Top level class, implementing the controlling features
156 * of the mingw-get setup tool.
161 inline SetupTool( HINSTANCE );
162 static SetupTool *Invoke( void );
163 static unsigned int IsPref( unsigned int test ){ return test & prefs; }
165 bool InitialiseSetupHookAPI( void );
166 inline int DispatchSetupHookRequest( unsigned int, ... );
169 const wchar_t *dll_name;
170 HMODULE base_dll, hook_dll;
171 typedef int (*dll_request_hook)( unsigned int, va_list );
172 dll_request_hook dll_request;
174 inline void DoFirstTimeSetup( HWND );
175 inline int SetInstallationPreferences( HWND );
176 inline HMODULE HaveWorkingInstallation( void );
177 inline wchar_t *UseDefaultInstallationDirectory( void );
178 inline void ShowInstallationDirectory( HWND );
179 inline int InstallationRequest( HWND );
181 static void EnablePrefs( HWND, int );
182 static inline void EnablePrefs( HWND, HWND );
183 static inline void StorePref( HWND, int, int );
184 static inline void ShowPref( HWND, int, int );
186 static SetupTool *setup_hook;
187 static const wchar_t *default_dirpath;
188 wchar_t *approot_path( const wchar_t * = NULL );
189 void ChangeInstallationDirectory( HWND );
190 static wchar_t *approot_tail;
192 static unsigned int prefs;
193 static int CALLBACK OpeningDialogue( HWND, unsigned, WPARAM, LPARAM );
194 static int CALLBACK ConfigureInstallationPreferences
195 ( HWND, unsigned, WPARAM, LPARAM );
197 static const wchar_t *gui_program;
198 inline int RunInstalledProgram( const wchar_t * );
200 inline wchar_t *setup_dll( void )
201 { /* Helper function to ensure that the static "approot_path" buffer
202 * specifies a reference path name for mingw-get-setup-0.dll
204 return approot_path( L"libexec\\mingw-get\\mingw-get-setup-0.dll" );
207 inline void CreateApplicationLauncher
208 ( int, const wchar_t *, const char *, const char * );
211 /* We will never instantiate more than one object of the SetupTool
212 * class at any one time. Thus, we may record its status in a static
213 * member variable, which we must initialise; we assume the all setup
214 * operations will be completed successfully.
216 int SetupTool::Status = EXIT_SUCCESS;
218 /* We need an alternative status code, to indicate that the user has
219 * elected to run the standard installer immediately, on termination
220 * of the initial setup; the value is arbitrary, subject only to the
221 * requirement that it differs from EXIT_SUCCESS and EXIT_FAILURE.
223 #define EXIT_CONTINUE 101
225 /* Installation options are also recorded in a static member varaible;
226 * we initialise these to match a set of default preferences, which we
227 * believe will suit a majority of users.
229 unsigned int SetupTool::prefs = SETUP_OPTION_DEFAULTS;
231 /* We also provide a static hook, through which objects of any other
232 * class may invoke public methods of the SetupTool object, without
233 * requiring them to maintain their own reference to this object.
235 SetupTool *SetupTool::setup_hook = NULL;
236 SetupTool *SetupTool::Invoke( void ){ return setup_hook; }
238 static void RefreshDialogueWindow( HWND AppWindow )
240 /* A trivial helper routine, to force an immediate update
241 * of a dialogue window display.
243 InvalidateRect( AppWindow, NULL, FALSE );
244 UpdateWindow( AppWindow );
247 /* Embed an implementation of the diagnostic message handler,
248 * customised for deployment by the setup tool...
250 #include "dmhcore.cpp"
251 #include "dmhguix.cpp"
253 /* ...and supported by this custom initialisation routine.
255 EXTERN_C void dmh_init( const dmh_class subsystem, const char *progname )
257 /* Public entry point for message handler initialisation...
259 * We only do it once, silently ignoring any attempt to
260 * establish a second handler.
262 if( (dmh == NULL) && (subsystem == DMH_SUBSYSTEM_GUI) )
263 dmh = new dmhTypeGUI( strdup( progname ) );
266 inline unsigned long pkgSetupAction::HasAttribute( unsigned long mask )
268 /* Helper function to examine the flags within a setup action
269 * item, checking for the presence of a specified attribute.
271 return (this != NULL) ? mask & flags : 0UL;
274 void pkgSetupAction::ClearAllActions( void )
276 /* Routine to clear an entire list of setup action items,
277 * releasing the memory allocated to each.
279 pkgSetupAction *current = this;
280 while( current != NULL )
282 /* Walk the list, deleting each item in turn.
284 pkgSetupAction *defunct = current;
285 current = current->next;
290 /* Worker thread API for use with modal dialogue boxes; the worker thread
291 * is invoked by passing a function pointer, conforming to this prototype,
292 * to the _beginthread() API.
294 typedef void SetupThreadProcedure( void * );
296 class SetupDialogueThread
298 /* Locally defined class to manage the binding of an arbitrary worker
299 * thread procedure to a modal dialogue, with a generic message loop.
302 inline long ExitCode(){ return status; }
303 SetupDialogueThread( HWND, int, SetupThreadProcedure * );
306 static int CALLBACK SetupDialogue( HWND, unsigned int, WPARAM, LPARAM );
307 static SetupThreadProcedure *WorkerThread;
311 /* We need to instantiate this static class member.
313 SetupThreadProcedure *SetupDialogueThread::WorkerThread = NULL;
315 int CALLBACK SetupDialogueThread::SetupDialogue
316 ( HWND window, unsigned int msg, WPARAM request, LPARAM )
318 /* Generic handler for dialogue boxes which delegate an associated
319 * processing activity to a background thread.
323 /* We need to handle only two classes of windows messages
324 * on behalf of such dialogue boxes...
327 /* ...viz. on initial dialogue box creation, we delegate the
328 * designated activity to the background thread...
330 _beginthread( WorkerThread, 0, (void *)(window) );
334 switch( msg = LOWORD( request ) )
338 /* ...then we wait for a notification that the dialogue may be
339 * closed, (which isn't permitted until the thread completes).
341 EndDialog( window, msg );
344 case ID_SETUP_SHOW_LICENCE:
345 /* Any request to view the licence document is handled in situ,
346 * without closing the active dialogue box.
348 BrowseLicenceDocument( window, -4, -50 );
352 /* Any other messages, which are directed to this dialogue box,
353 * may be safely ignored.
358 SetupDialogueThread::SetupDialogueThread
359 ( HWND owner, int id, SetupThreadProcedure *setup_actions )
361 /* Class constructor assigns the worker procedure for the thread,
362 * then opens the dialogue box which starts it, ultimately capturing
363 * the exit code when this dialogue is closed.
365 WorkerThread = setup_actions;
366 status = DialogBox( NULL, MAKEINTRESOURCE( id ), owner, SetupDialogue );
369 class SetupDownloadAgent
371 /* A locally defined class to manage package download activities
372 * on behalf of the setup tool.
375 static void Run( void * );
376 SetupDownloadAgent( pkgSetupAction *packages ){ PackageList = packages; }
379 static pkgSetupAction *PackageList;
382 /* Initialise the list of packages for download, as an empty list;
383 * we will subsequently populate it, as required.
385 pkgSetupAction *SetupDownloadAgent::PackageList = NULL;
387 /* Embed the download agent implementation as a filtered subset of
388 * mingw-get's own download service implementation.
390 #include "pkginet.cpp"
391 #include "pkgnget.cpp"
393 static inline int dll_load_failed( const wchar_t *dll_name )
395 /* Helper to diagnose failure to load any supporting DLL.
397 return dmh_notify( DMH_ERROR,
398 "%S: DLL load failed; cannot run setup hooks\n", dll_name
402 void SetupDownloadAgent::Run( void *owner )
404 /* Method to run the download agent; it fetches all packages
405 * which are specified in the setup tool's initial installation
406 * list, (using a metered download procedure)...
408 pkgDownloadMeterGUI metered( (HWND)(owner) );
409 PackageList->DownloadArchiveFiles();
411 /* ...then unpacks each into its ultimate installed location,
412 * before delegating finalisation of the installation to the
413 * installer DLLs, which should thus have been installed.
415 if( (PackageList->UnpackScheduledArchives() == IDOK)
416 && SetupTool::Invoke()->InitialiseSetupHookAPI() )
418 /* Only on successful initialisation of the DLL hooks, do
419 * we actually continue with the delegated installation.
421 SetupTool::Invoke()->DispatchSetupHookRequest(
422 SETUP_HOOK_POST_INSTALL, PackageList, owner
425 /* Successful DLL initialisation is also a prerequisite for
426 * allowing the user to run the main installer, to continue
427 * the installation with a more comprehensive package set;
428 * however, progression to such advanced installation...
430 if( SetupTool::IsPref( SETUP_OPTION_WITH_GUI ) )
432 * ...is feasible, only if the user has elected to install
433 * the mingw-get GUI client.
435 EnableWindow( GetDlgItem( (HWND)(owner), IDOK ), TRUE );
438 /* DLL initialisation was unsuccessful; tell the user that
439 * we are unable to continue this installation.
441 dmh_notify( DMH_ERROR, "setup: unable to continue\n" );
443 /* In any event, always offer the option to quit, without
444 * proceeding to advanced installation.
446 EnableWindow( GetDlgItem( (HWND)(owner), IDCANCEL ), TRUE );
449 bool SetupTool::InitialiseSetupHookAPI( void )
451 /* Helper method, invoked by SetupDownloadAgent::Run(), to confirm
452 * that the requisite installer DLLs have been successfully unpacked,
453 * and if so, to initialise them for continuation of installation.
455 * First, we must confirm that mingw-get's main DLL is available...
457 if( (base_dll = HaveWorkingInstallation()) == NULL )
459 * ...immediately abandoning the installation, if not.
461 return (dll_load_failed( dll_name ) == 0);
463 /* Having successfully loaded the main DLL, we perform a similar
464 * check for the setup tool's bridging DLL...
466 if( (hook_dll = LoadLibraryW( dll_name = setup_dll() )) == NULL )
468 /* ...once again, abandoning the installation, if this is not
469 * available; in this case, since we've already successfully
470 * loaded mingw-get's main DLL, we also unload it.
472 FreeLibrary( base_dll ); base_dll = NULL;
473 return (dll_load_failed( dll_name ) == 0);
475 /* With both DLLs successfully loaded, we may establish the
476 * conduit, through which the setup tool invokes procedures in
477 * either of these DLLs...
479 dll_request = (dll_request_hook)(GetProcAddress( hook_dll, "setup_hook" ));
480 if( dll_request != NULL )
482 /* ...and, on success, complete the DLL initialisation by
483 * granting the DLLs shared access to the diagnostic message
484 * handler provided by the setup tool's main program.
486 DispatchSetupHookRequest( SETUP_HOOK_DMH_BIND, dmh );
489 /* If we get to here, DLL initialisation was unsuccessful;
490 * diagnose, and bail out.
492 dmh_notify( DMH_ERROR, "setup_hook: DLL entry point not found\n" );
496 inline int SetupTool::DispatchSetupHookRequest( unsigned int request, ... )
498 /* Helper method, providing a variant argument API to the
499 * request hook within the setup tool's bridging DLL; it
500 * collects the argument vector, to pass into the DLL.
503 va_start( argv, request );
504 int retval = dll_request( request, argv );
510 long InitiatePackageInstallation( HWND owner, pkgSetupAction *pkglist )
512 /* Helper routine, invoked by SetupTool::DoFirstTimeSetup(), to
513 * install mingw-get, and proceed to further package selection.
515 SetupDownloadAgent agent( pkglist );
516 SetupDialogueThread run( owner, IDD_SETUP_DOWNLOAD, SetupDownloadAgent::Run );
517 RefreshDialogueWindow( owner );
518 return run.ExitCode();
521 /* Embed a selected subset of the package stream extractor methods,
522 * sufficient to support unpacking of the .tar.xz archives which are
523 * used as the delivery medium for mingw-get.
525 #include "pkgstrm.cpp"
527 /* The standard archive extractor expects to use the pkgOpenArchiveStream()
528 * function to gain access to archive files; however, the generalised version
529 * of this is excluded from the subset embedded from pkgstrm.cpp, so we must
530 * provide a replacement...
532 pkgArchiveStream *pkgOpenArchiveStream( const char *archive_path_name )
534 /* ...function to prepare an archive file for extraction; (this specialised
535 * version is specific to extraction of xz compressed archives).
537 return new pkgXzArchiveStream( archive_path_name );
540 /* The archive extractor classes incorporate reference pointers to pkgXmlNode
541 * and pkgManifest class objects, but these are not actually used within the
542 * context required here; to placate the compiler, declare these as opaque
548 /* Embed the minimal subset of required extractor classes and methods...
550 #include "tarproc.cpp"
552 /* ...noting that the formal implementation of this required constructor
553 * has been excluded; (it implements a level of complexity, not required
554 * here); hence, we provide a minimal, do nothing, substitute.
556 pkgTarArchiveProcessor::pkgTarArchiveProcessor( pkgXmlNode * ){}
558 /* Similarly, we need a simplified implementation of the destructor
559 * for this same class...
561 pkgTarArchiveProcessor::~pkgTarArchiveProcessor()
563 /* ...but it this case, we still use it to clean up the memory
564 * allocated by the pkgTarArchiveExtractor class constructor.
566 free( (void *)(sysroot_path) );
571 int CALLBACK unwise( HWND owner, unsigned msg, WPARAM request, LPARAM data )
573 /* Handler for the dialogue box which is presented when the user
574 * makes an unwise choice of installation directory (specifically
575 * a choice with white space in the path name).
580 /* There are no particular initialisation requirements, for
581 * this dialogue box; just accept the defaults.
586 /* User has selected one of the continuation options...
588 switch( msg = LOWORD( request ) )
593 /* Ignore advice to choose a more appropriate location:
594 * this requires additional confirmation that the user has
595 * understood the implications of this choice; do no more
596 * than enable the button which provides such confirmation.
598 if( HIWORD( request ) == BN_CLICKED )
599 EnableWindow( GetDlgItem( owner, IDYES ),
600 SendMessage( (HWND)(data), BM_GETCHECK, 0, 0 ) == BST_CHECKED
605 /* Accept advice; go back to make a new choice of location
606 * for this installation, or...
609 /* ...confirm understanding of, and intent to ignore, advice;
610 * in either case close the dialogue, returning appropriate
613 EndDialog( owner, msg );
617 /* No other conditions are handled explicitly, within this dialogue.
622 static bool UnconfirmedDirectorySelection( HWND owner, const wchar_t *choice )
624 /* Helper function to ratify user's selection of installation directory;
625 * given an absolute path name retrieved from SHBrowsForFolder(), it checks
626 * for the presence of embedded white space, and reiterates the appropriate
627 * warning when any is found, before allowing the user to choose again, or
628 * to ignore the advice, and shoot himself/herself in the foot.
631 RefreshDialogueWindow( owner );
632 if( choice && *(path = choice) )
634 /* We have a non-empty path name selection to ratify...
637 if( iswspace( *path++ ) )
639 /* ...and we DID find embedded white space; castigate the user
640 * for his/her folly in ignoring advice...
642 return DialogBox( NULL, MAKEINTRESOURCE( IDD_SETUP_UNWISE ),
643 owner, unwise ) != IDYES;
645 /* If we get to here, then the user appears to have made a sane choice;
646 * caller need not repeat the request for a directory selection...
650 /* ...but when we were asked to ratify no selection at all, then caller
651 * must repeat this request.
657 int CALLBACK BrowserInit( HWND owner, unsigned msg, LPARAM, LPARAM dirname )
659 /* Helper function to set the default installation directory path name,
660 * and to assign a more appropriate dialogue box description...
662 if( msg == BFFM_INITIALIZED )
664 /* ...when initialising the SHBrowseForFolder() dialogue to elicit
665 * the user's choice of installation directory.
667 SendMessage( owner, WM_SETTEXT, 0, (LPARAM)("Select Installation Directory") );
668 SendMessage( owner, BFFM_SETSELECTIONW, TRUE, dirname );
674 bool mkdir_default( wchar_t *dirpath )
676 /* Helper function to create the default installation directory;
677 * this must exist before we call SHBrowseForFolder(), so that we
678 * may offer as the default choice, even if the user subsequently
679 * declines to accept it.
681 bool retval = false; struct _stat chkdir;
682 if( ! ((_wstat( dirpath, &chkdir ) == 0) && S_ISDIR( chkdir.st_mode )) )
684 /* The recommended default directory doesn't yet exist; try to
687 if( ! (retval = (_wmkdir( dirpath ) == 0)) )
689 * ...but if unsuccessful, fall back to offering the root
690 * directory of the default device, as a starting point for
691 * the user's selection.
695 /* In any event, this will return "true" if we created the
696 * directory, or "false" if it either already existed, or if
697 * our attempt to create it failed.
703 void rmdir_default( const wchar_t *offer, const wchar_t *selected )
705 /* Helper function, called if we created the default installation
706 * directory; it will remove it if the user rejected this default
707 * selection, and any descendant thereof.
709 const wchar_t *chk = offer;
710 while( *chk && *selected && (*chk++ == *selected++) ) ;
711 if( (*chk != L'\0') || ((*selected != L'\0') && (*selected != L'\\')) )
716 int setup_putenv( const char *varname, const wchar_t *value )
718 /* Helper function to assign a specified value to a named
719 * environment variable.
721 const char *fmt = "%s=%S";
722 char assignment[1 + snprintf( NULL, 0, fmt, varname, value )];
723 snprintf( assignment, sizeof( assignment ), fmt, varname, value );
724 return putenv( assignment );
727 wchar_t *SetupTool::approot_tail = NULL;
728 wchar_t *SetupTool::approot_path( const wchar_t *relative_path )
730 /* Helper method to resolve a relative path name to its absolute
731 * equivalent, within the directory hierarchy of the user's chosen
732 * installation directory.
734 * The resolved absolute path name is returned in this static
735 * buffer, initially defined to be empty.
737 static wchar_t dirpath[PATH_MAX] = L"\0";
739 /* When a previous call has left a "tail" buffer,
740 * (which may or may not be populated)...
742 if( approot_tail != NULL )
744 * ...then clear it...
746 *approot_tail = L'\0';
748 else if( *dirpath != L'\0' )
750 * ...otherwise, establish the "tail" buffer, following
751 * the "approot" string within the "dirpath" buffer.
753 for( approot_tail = dirpath; *approot_tail != L'\0'; approot_tail++ )
756 /* When a relative path argument is specified...
758 if( relative_path != NULL )
760 /* ...then we must resolve it as an absolute path, interpreting
761 * it as relative to "approot", by copying it into the "tail"
762 * buffer within "dirpath".
764 * We perform the copy character by character, storing at the
765 * advancing "caret" position, while ensuring we do not overrun
766 * the "endstop" location, which we set initially to mark the
767 * end of the "dirpath" buffer.
769 wchar_t *caret = approot_tail - 1;
770 wchar_t *endstop = dirpath + PATH_MAX - 1;
772 /* We initially set "caret" to point to the final character
773 * in "approot", so that we may check for the presence of a
774 * directory separator; if there ISN'T one there already...
776 if( (*caret != L'\\') && (*caret != L'/') )
778 * ...then we advance it to the start of the "tail"
779 * buffer, where we will insert one.
783 /* When the first character of the specified relative path
784 * is NOT itself a directory separator, (which is expected
785 * in the normal case)...
787 if( (*relative_path != L'\\') && (*relative_path != L'/') )
789 * ...then we explicitly insert one; (we may note that this
790 * will either overwrite an existing separator at the end of
791 * "approot", or it will occupy the first "caret" position
792 * within the "tail" buffer).
796 /* Now, we copy the relative path to the "caret" location...
798 do { /* ...ensuring that we do NOT overrun the "endstop"...
800 if( caret < endstop )
802 /* ...and normalising directory separators, favouring
803 * Microsoft's '\\' preference, as we go...
805 if( (*caret = *relative_path++) == L'/' )
809 /* ...but, if we hit the "endstop", we immediately
810 * truncate and terminate the copy...
814 /* ...continuing until we've copied the terminating NUL
815 * from the relative path argument itself, or we hit the
816 * "endstop", forcing early termination.
818 } while( *caret++ != L'\0' );
820 /* However we completed the operation, we return a pointer to the
821 * entire content of the "dirpath" static buffer.
826 inline int pkgSetupAction::UnpackScheduledArchives( void )
828 /* Helper to unpack each of the package archives, specified in the
829 * setup actions list, extracting content to its ultimate installed
830 * location within the user specified installation directory tree.
832 int status = IDCANCEL;
833 pkgSetupAction *install;
835 /* When the action list is not empty...
837 if( (install = this) != NULL )
839 /* ...ensure that we process it from its first entry.
842 while( install->prev != NULL ) install = install->prev;
843 while( install != NULL )
845 /* For each list entry, locate the downloaded copy of the
846 * archive, in the installer's package cache directory.
848 const char *cache = "%R" "var\\cache\\mingw-get\\packages\\%F";
849 char archive_name[mkpath( NULL, cache, install->ArchiveName(), NULL )];
850 mkpath( archive_name, cache, install->ArchiveName(), NULL );
852 /* Report activity, and provided download has made the
853 * cached copy of the archive available...
855 dmh_notify( DMH_INFO, "setup: unpacking %s\n", install->ArchiveName() );
856 if( install->HasAttribute( ACTION_DOWNLOAD ) == 0 )
858 * ...extract its content.
860 pkgTarArchiveExtractor( archive_name, "%R" );
863 { /* The package is not available; assume that download was
864 * unsuccessful, and diagnose accordingly...
866 dmh_notify( DMH_ERROR, "unpack: required archive file is not available\n" );
867 dmh_notify( DMH_ERROR, "unpack: aborted due to previous download failure\n" );
869 /* ...then cancel further installation activities.
874 /* Repeat for any other scheduled action items.
876 install = install->next;
879 /* Ultimately return the status code, indicating either complete
880 * success, or requesting cancellation of further installation.
886 char *arg_append( char *caret, const char *argtext, const char *endstop )
888 /* Command line construction helper function; append specified "argtext"
889 * into the constructed command line, with appropriate quoting to keep it
890 * as a single argument, and preceded by a space, at the position marked
891 * by "caret", and ensuring that the text does not overrun "endstop".
893 if( (endstop > caret) && ((endstop - caret) > 1)
894 && (argwrap( caret + 1, argtext, endstop - caret - 1) != NULL) )
896 /* The specified argument can be accommodated; insert the separating
897 * space, and update the caret to mark the position at which the next
898 * argument, if any, should be appended.
901 return caret + strlen( caret );
903 /* If we get to here, the argument could not be accommodated, return the
904 * caret position, unchanged.
910 char *arg_append( char *caret, const wchar_t *argtext, const char *endstop )
912 /* Overloaded variant of the preceding command line construction helper;
913 * this variant appends an argument, specified as a wide character string,
914 * to the constructed command line.
916 if( (endstop > caret) && ((endstop - caret) > 1) )
918 /* There is at least some remaining space in the command line buffer;
919 * convert the specified argument to a regular string...
921 const char *conv_fmt = "%S";
922 char conv_buf[1 + snprintf( NULL, 0, conv_fmt, argtext )];
923 snprintf( conv_buf, sizeof( conv_buf ), conv_fmt, argtext );
925 * ...then delegate the actual append operation to the regular string
926 * handling variant of the helper function.
928 return arg_append( caret, conv_buf, endstop );
930 /* If we get to here, the command line buffer space has been completely
931 * exhausted; return the caret position, unchanged.
937 /* POSIX specifies this as the maximum number of characters allowed in the
938 * aggregate of all arguments passed to a child process in an exec() function
939 * call. It isn't typically defined on MS-Windows, although limits do apply
940 * in the case of exec(), spawn(), or system() function calls. MSDN tells
941 * us that the maximum length of a command line is 8191 characters on WinXP
942 * and later, but only 2047 characters on earlier versions; allowing one
943 * additional character for a terminating NUL, we will adopt the lower
944 * of these, as a conservative choice.
946 # define ARG_MAX 2048
949 inline void SetupTool::CreateApplicationLauncher
950 ( int opt, const wchar_t *appname, const char *desc, const char *linkname )
952 /* Construct a command line to invoke the shlink.js script, (which is
953 * provided by the mingw-get package), via the system provided wscript
954 * interpreter, to install a desktop or start-menu application link.
956 static const char *cmd = "wscript";
957 static const char *location[] = { "--start-menu", "--desktop" };
959 /* Initialise the argument list, setting the wscript option to suppress
960 * its normal verbose behaviour, and set a buffer endstop reference, so
961 * that we may detect, and avoid overrun.
963 char cmdline[ARG_MAX] = "-nologo";
964 const char *endstop = cmdline + sizeof( cmdline );
966 /* Add the full path name reference for the shlink.js script which is to
967 * be executed; (note that we resolve this relative to the user's choice
968 * of installation directory, where the script will have been installed,
969 * during prior unpacking of the mingw-get-bin package tarball).
971 char *caret = arg_append( cmdline + strlen( cmdline ),
972 approot_path( L"libexec\\mingw-get\\shlink.js" ), endstop
975 /* Add options to select placement of the application links, and to specify
976 * whether they should be available to current user ot to all users.
978 caret = arg_append( ((opt & SETUP_OPTION_ALL_USERS) != 0)
979 ? arg_append( caret, "--all-users", endstop )
980 : caret, location[opt >> 2], endstop
983 /* Add a description for the application link.
985 caret = arg_append( arg_append( caret, "--description", endstop ),
989 /* Specify the path name for the application to be invoked, (again noting
990 * that this must be resolved relative to the installation directory), and
991 * the name to be given to the link file itself.
993 arg_append( arg_append( caret, approot_path( appname ), endstop ),
997 /* Finally, invoke the script interpreter to process the specified command.
998 * (We use a spawn() call here, in preference to system(), to avoid opening
999 * any unwanted console windows).
1001 spawnlp( P_WAIT, cmd, cmd, cmdline, NULL );
1004 inline SetupTool::SetupTool( HINSTANCE Instance ):
1005 base_dll( NULL ), hook_dll( NULL )
1007 /* Constructor for the SetupTool object. Note that there should be only
1008 * one such object instantiated; this is intended as a one time only call,
1009 * and any attempted subsequent call will be ignored, as a consequence of
1010 * the first time initialisation of the setup_hook static member.
1012 if( setup_hook == NULL )
1015 UseDefaultInstallationDirectory();
1017 /* Get a security token for the process, so that we may selectively
1018 * disable those options which require administrative privilege, when
1019 * running as a normal user.
1021 * FIXME: this is specific to WinNT platforms; we should add a DLL
1022 * lookup, so we can avoid calling the security hooks which are not
1023 * supported on Win9x.
1026 SID_IDENTIFIER_AUTHORITY sid = SECURITY_NT_AUTHORITY;
1027 int authority = AllocateAndInitializeSid( &sid, 2,
1028 SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0,
1031 if( authority != 0 )
1033 if( CheckTokenMembership( NULL, group, &authority ) && (authority != 0) )
1034 prefs |= SETUP_OPTION_PRIVILEGED | SETUP_OPTION_ALL_USERS;
1038 /* To offer the user a choice of installation directory, we will
1039 * use the SHBrowswForFolder() API; this is a COM component API,
1040 * so we must initialise the COM subsystem.
1042 CoInitialize( NULL );
1044 /* Initialise our own Diagnostic Message Handler subsystem.
1046 dmh_init( DMH_SUBSYSTEM_GUI,
1047 WTK::StringResource( Instance, ID_MAIN_WINDOW_CLASS )
1050 /* Make ALL of Microsoft's common controls available.
1052 InitCommonControls();
1054 /* The setup tool runs as a sequence of dialogue boxes, without
1055 * any main window; display the opening dialogue.
1057 DialogBox( Instance,
1058 MAKEINTRESOURCE( IDD_SETUP_BEGIN ), NULL, OpeningDialogue
1061 /* When the user has requested progression to package installation...
1063 if( Status == EXIT_CONTINUE )
1065 /* ...then delegate that to the embedded mingw-get plugin, before
1066 * reasserting successful completion status for the setup tool.
1068 DispatchSetupHookRequest( SETUP_HOOK_RUN_INSTALLER, setup_dll() );
1069 Status = EXIT_SUCCESS;
1072 /* If the mingw-get-0.dll and mingw-get-setup-0.dll libraries
1073 * were successfully loaded, to complete the installation process,
1074 * then we must now unload them; we also have no further use for
1075 * mingw-get-setup-0.dll, so we may delete it.
1077 if( hook_dll != NULL ){ FreeLibrary( hook_dll ); _wunlink( setup_dll() ); }
1078 if( base_dll != NULL ){ FreeLibrary( base_dll ); }
1080 /* We're done with the COM subsystem; release it.
1086 const wchar_t *SetupTool::default_dirpath = L"C:\\MinGW";
1087 inline wchar_t *SetupTool::UseDefaultInstallationDirectory( void )
1089 /* Set up the specification for the preferred default installation
1090 * directory; (this is per MinGW.org Project recommendation).
1092 approot_tail = NULL;
1093 return wcscpy( approot_path(), default_dirpath );
1096 void SetupTool::ChangeInstallationDirectory( HWND AppWindow )
1098 /* Set up the specification for the preferred default installation
1099 * directory; (this is per MinGW.org Project recommendation).
1101 wchar_t *dirpath = approot_path();
1103 /* Exert our best effort to make the default installation directory
1104 * available for immediate selection via SHBrowseForFolder()
1106 bool default_dir_created = mkdir_default( dirpath );
1108 /* The browse for folder dialogue isn't modal; to ensure the user
1109 * doesn't inadvertently dismiss the invoking dialogue prematurely,
1110 * disable its active control buttons.
1112 int buttons[] = { IDOK, ID_SETUP_SHOW_LICENCE, ID_SETUP_CHDIR, IDCANCEL };
1113 for( int index = 0; index < 4; index++ )
1115 /* Note that we could simply have disabled the invoking dialogue
1116 * box itself, but this loop to disable individual buttons looks
1117 * better, giving the user better visual feedback indicating the
1118 * disabled state of the owner window's control buttons.
1120 buttons[index] = (int)(GetDlgItem( AppWindow, buttons[index] ));
1121 EnableWindow( (HWND)(buttons[index]), FALSE );
1124 /* Set up a standard "Browse for Folder" dialogue, to allow the
1125 * user to choose an installation directory...
1127 BROWSEINFOW explorer = { 0 };
1128 explorer.ulFlags = BIF_RETURNONLYFSDIRS | BIF_USENEWUI;
1129 explorer.lpszTitle = L"Please select a directory, in which to install MinGW; "
1130 L"it is strongly recommended that your choice should avoid any directory "
1131 L"which includes white space within its absolute path name.";
1132 explorer.lParam = (LPARAM)(dirpath);
1133 explorer.lpfn = BrowserInit;
1135 /* ...and invoke it, possibly repeatedly, until the user has
1136 * confirmed an appropriate, (or maybe inappropriate), choice.
1138 do { LPITEMIDLIST dir;
1139 if( (dir = SHBrowseForFolderW( &explorer )) != NULL )
1140 SHGetPathFromIDListW( dir, dirpath );
1142 /* Fow each proposed directory choice returned from the
1143 * browser dialogue, force a rescan to locate the end of
1144 * its name, in the "approot" return buffer...
1146 approot_tail = NULL;
1148 * ...then update the preferences display, to reflect the
1151 ShowInstallationDirectory( AppWindow );
1153 /* Finally, check for any possibly ill advised directory
1154 * choice, (specifically warning of embedded white space,
1155 * and give the user an opportunity for remedial action.
1157 } while( UnconfirmedDirectorySelection( AppWindow, dirpath ) );
1159 /* When we created the default installation directory, in preparation
1160 * for a default (recommended) install...
1162 if( default_dir_created )
1164 * ...we check if the user accepted this choice, and remove it when
1165 * some alternative (suitable or otherwise) was adopted.
1167 rmdir_default( default_dirpath, dirpath );
1169 /* The non-modal directory browser window has now been closed, but our
1170 * dialogue still exhibits disabled controls. Although we disabled each
1171 * control individually, the reverse process of reactivating each of them
1172 * individually seems to not, and subsequently restoring the focus to the
1173 * default "Continue", doesn't seem to properly restore the visual cue to
1174 * apprise the user of this default; thus we send a WM_COMMAND message to
1175 * close this dialogue, while requesting that it be reinitialised from
1176 * scratch, (so reactivating it in its entirety).
1178 SendMessage( AppWindow, WM_COMMAND, (WPARAM)(IDRETRY), 0 );
1181 inline void SetupTool::DoFirstTimeSetup( HWND AppWindow )
1182 #define archive_class(TAG) WTK::StringResource( NULL, ID_##TAG##_DISTNAME )
1184 /* When we didn't defer to any prior installation, proceed with a new
1185 * one; set up the APPROOT environment variable, to reflect the choice
1186 * of installation directory, (whether user's choice or default).
1188 setup_putenv( "APPROOT", approot_path( L"" ) );
1190 /* Identify the minimal set of packages, which we require to establish
1191 * the mingw-get installer set-up; we specify this a a linked action list,
1192 * beginning with a setup action request for the base package...
1194 pkgSetupAction *linked, *list;
1195 linked = list = new pkgSetupAction( NULL, archive_class( PACKAGE_BASE ), "bin" );
1196 if( IsPref( SETUP_OPTION_WITH_GUI ) )
1198 * ...optionally adding the GUI extension, at the user's behest...
1200 linked = new pkgSetupAction( linked, archive_class( PACKAGE_BASE ), "gui" );
1202 /* ...always installing the licence pack...
1204 linked = new pkgSetupAction( linked, archive_class( PACKAGE_BASE ), "lic" );
1206 /* ...and finishing up with the setup DLL and XML data packages.
1208 linked = new pkgSetupAction( linked, archive_class( PACKAGE_DATA ), "dll" );
1209 linked = new pkgSetupAction( linked, archive_class( PACKAGE_DATA ), "xml" );
1211 /* Download packages to the mingw-get package cache, (which we will
1212 * create, as a side effect of downloading, if necessary), and unpack
1213 * them in place, to establish the basic mingw-get installation.
1215 if( InitiatePackageInstallation( AppWindow, list ) == IDOK )
1216 Status = EXIT_CONTINUE;
1218 /* Finally, clean up and we are done.
1220 list->ClearAllActions();
1223 #define SETUP_OPTION(M) SetupTool::IsPref(SETUP_OPTION_##M)
1224 #define SETUP_ASSERT(M) SETUP_OPTION(M) ? BST_CHECKED : BST_UNCHECKED
1225 #define SETUP_REVERT(M) SETUP_OPTION(M) ? BST_UNCHECKED : BST_CHECKED
1227 inline void SetupTool::ShowPref( HWND window, int id, int option )
1229 /* Helper method to update the display of any check box control,
1230 * within the installation preferences dialogue box, to match the
1231 * state of its associated setting flag within the setup object.
1233 SendMessage( GetDlgItem( window, id ), BM_SETCHECK, option, 0 );
1236 inline void SetupTool::StorePref( HWND ctrl, int id, int option )
1238 /* Helper method to record any user specified change of state
1239 * in any check box control, within the installation preferences
1240 * dialogue box, to its associated flag within the setup object.
1242 if( SendMessage( GetDlgItem( ctrl, id ), BM_GETCHECK, 0, 0 ) == BST_CHECKED )
1244 * The user selected the option associated with the check box;
1245 * set the associated flag to its "on" state.
1250 /* The user cleared the check box; set the associated flag to
1256 void SetupTool::EnablePrefs( HWND dialogue, int state )
1258 /* Helper used by the ConfigureInstallationPreferences() method,
1259 * to set the enabled state for those selection controls which are
1260 * only meaningful when a particular controlling selection, (which
1261 * is nominally the choice to install the GUI), is in effect.
1263 if( IsPref( SETUP_OPTION_PRIVILEGED ) )
1265 /* The "all users" installation option is supported only
1266 * if the setup tool is run with administrator privilege.
1268 EnableWindow( GetDlgItem( dialogue, ID_SETUP_ALL_USERS ), state );
1270 /* All other options are available to everyone.
1272 EnableWindow( GetDlgItem( dialogue, ID_SETUP_ME_ONLY ), state );
1273 EnableWindow( GetDlgItem( dialogue, ID_SETUP_START_MENU ), state );
1274 EnableWindow( GetDlgItem( dialogue, ID_SETUP_DESKTOP ), state );
1277 inline void SetupTool::EnablePrefs( HWND dialogue, HWND ref )
1279 /* Overload the preceding EnablePrefs() method, so we can deduce
1280 * the state parameter from a reference check-box control.
1282 EnablePrefs( dialogue, SendMessage( ref, BM_GETCHECK, 0, 0 ) );
1285 inline HMODULE SetupTool::HaveWorkingInstallation( void )
1287 /* Helper method to check for a prior installation, by attempting
1288 * to load its installed DLL component into our process context.
1290 dll_name = approot_path( L"libexec\\mingw-get\\mingw-get-0.dll" );
1291 return LoadLibraryW( dll_name );
1294 /* Identify the path to the mingw-get GUI client program, relative
1295 * to the root of the mingw-get installation tree.
1297 const wchar_t *SetupTool::gui_program = L"libexec\\mingw-get\\guimain.exe";
1299 inline int SetupTool::RunInstalledProgram( const wchar_t *program )
1301 /* Helper method to spawn an external process, into which a
1302 * specified program image is loaded; (typically this will be
1303 * the mingw-get program, either inherited from a previous run
1304 * of this setup tool, or a copy which we have just installed).
1308 /* We assume that the program to run lives within the directory
1309 * heirarchy specified for mingw-get installation.
1311 program = approot_path( program );
1312 if( (status = _wspawnl( _P_NOWAIT, program, program, NULL )) != -1 )
1314 /* When the specified image has been successfully loaded, we
1315 * give it around 1500 ms to get running, then we promote it
1316 * to the foreground.
1318 Sleep( 1500 ); WTK::RaiseAppWindow( NULL, ID_MAIN_WINDOW_CLASS );
1320 /* The return value is that returned by the spawn request.
1325 static inline int MessageDialogue( HINSTANCE app, int id, HWND owner )
1327 /* Helper function to emulate MessageBox behaviour using an
1328 * application specified dialogue box resource.
1330 return DialogBox( app, MAKEINTRESOURCE( id ), owner, confirm );
1333 int CALLBACK SetupTool::ConfigureInstallationPreferences
1334 ( HWND window, unsigned int msg, WPARAM request, LPARAM data )
1336 /* Handler for user interaction with the installation preferences
1337 * dialogue; configures options for the ensuing installation.
1342 /* We need to handle only two classes of message...
1346 * On initialisation of the dialogue, we ensure that the
1347 * displayed state of the controls is consistent with the
1348 * default, or previous settings, as specified within the
1351 EnablePrefs( window, IsPref( SETUP_OPTION_WITH_GUI ) );
1352 ShowPref( window, ID_SETUP_WITH_GUI, SETUP_ASSERT( WITH_GUI ) );
1353 ShowPref( window, ID_SETUP_ME_ONLY, SETUP_REVERT( ALL_USERS ) );
1354 ShowPref( window, ID_SETUP_ALL_USERS, SETUP_ASSERT( ALL_USERS ) );
1355 ShowPref( window, ID_SETUP_START_MENU, SETUP_ASSERT( START_MENU ) );
1356 ShowPref( window, ID_SETUP_DESKTOP, SETUP_ASSERT( DESKTOP ) );
1357 Invoke()->ShowInstallationDirectory( window );
1359 /* Explicitly assign focus to the "Continue" button, and
1360 * return FALSE so the default message handler will not
1361 * override this with any default choice.
1363 SetFocus( GetDlgItem( window, IDOK ) );
1367 switch( msg = LOWORD( request ) )
1369 /* A small selection of WM_COMMAND class messages
1370 * requires our attention.
1372 case ID_SETUP_WITH_GUI:
1373 if( HIWORD( request ) == BN_CLICKED )
1375 * The user has toggled the state of the GUI installation
1376 * option; we intercept this, so we may assign appropriate
1377 * availablity of other preferences...
1379 EnablePrefs( window, (HWND)(data) );
1381 /* ...but otherwise, we leave the default message handler to
1382 * process this in its default manner.
1386 case ID_SETUP_CHDIR:
1387 /* The user has selected the option to change the directory
1388 * into which mingw-get is to be installed; we handle this
1389 * request explicitly...
1391 Invoke()->ChangeInstallationDirectory( window );
1393 * ...with no further demand on the default message handler.
1398 /* The user has clicked the "Continue" button, (presumably
1399 * after making an appropriate selection of his installation
1400 * preferences; before blindly proceeding to installation,
1401 * check for any previously existing DLL component...
1403 if( (dll_hook = Invoke()->HaveWorkingInstallation()) != NULL )
1405 /* ...and, when one exists and has been loaded into our
1406 * runtime context, unload it before offering a choice of
1407 * how to continue...
1409 FreeLibrary( dll_hook );
1410 switch( msg = MessageDialogue( NULL, IDD_SETUP_EXISTS, window ) )
1413 /* The user has indicated a preference to continue
1414 * installation, but with an alternative installation
1415 * directory, so that the existing installation may
1416 * be preserved; go back to change directory.
1418 Invoke()->ChangeInstallationDirectory( window );
1422 /* The user has indicated a preference to switch to
1423 * advanced installation mode, by running the existing
1424 * copy of mingw-get; attempt to do so, before falling
1425 * through to cancel this setup session.
1427 Invoke()->RunInstalledProgram( gui_program );
1430 /* The current setup session is to be cancelled; pass
1431 * the cancellation request down the dialogue stack.
1433 EndDialog( window, IDCANCEL );
1438 case ID_SETUP_SHOW_LICENCE:
1439 /* we propagate his chosen options back to
1440 * the SetupTool object...
1442 StorePref( window, ID_SETUP_DESKTOP, SETUP_OPTION_DESKTOP );
1443 StorePref( window, ID_SETUP_START_MENU, SETUP_OPTION_START_MENU );
1444 StorePref( window, ID_SETUP_ALL_USERS, SETUP_OPTION_ALL_USERS );
1445 StorePref( window, ID_SETUP_WITH_GUI, SETUP_OPTION_WITH_GUI );
1447 * ...before simply falling through...
1450 /* ...to accept the chosen preferences when the user clicked
1451 * "Continue", or to discard them on "Cancel"; in either case
1452 * we close the dialogue box, passing the closure method ID
1453 * back to the caller...
1455 EndDialog( window, msg );
1457 * ...while informing the default message handler that we do
1458 * not require it to process this message.
1463 /* Any other messages are simply ignored, leaving the default
1464 * message handler to process them.
1469 inline void SetupTool::ShowInstallationDirectory( HWND dialogue )
1471 /* Helper method to display the active choice of installation directory
1472 * within the preferences configuration dialogue.
1475 if( (viewport = GetDlgItem( dialogue, ID_SETUP_CURDIR )) != NULL )
1476 SendMessageW( viewport, WM_SETTEXT, 0, (LPARAM)(approot_path()) );
1479 inline int SetupTool::SetInstallationPreferences( HWND AppWindow )
1481 /* Helper method to display a dialogue box, offering the user
1482 * a choice of installation preferences.
1484 return DialogBox( NULL, MAKEINTRESOURCE( IDD_SETUP_OPTIONS ), AppWindow,
1485 ConfigureInstallationPreferences
1489 inline int SetupTool::InstallationRequest( HWND AppWindow )
1491 /* Method to process requests to proceed with installation, on user
1492 * demand from the opening dialogue box.
1494 * First step is to garner installation preferences from the user.
1497 do { if( (request = SetInstallationPreferences( AppWindow )) == IDOK )
1499 /* When the user is satisfied that all options have been
1500 * appropriately specified, apply them.
1502 DoFirstTimeSetup( AppWindow );
1503 const WTK::StringResource description( NULL, ID_MAIN_WINDOW_CAPTION );
1504 if( IsPref( SETUP_OPTION_WITH_GUI ) )
1505 for( int i = 2; i < 6; i++ )
1507 /* When the user has requested the creation of program
1508 * "shortcuts", create them as appropriate.
1510 if( (IsPref( i ) | IsPref( SETUP_OPTION_ALL_USERS )) == i )
1511 CreateApplicationLauncher( i, gui_program, description,
1512 (i & SETUP_OPTION_DESKTOP) ? "MinGW Installer" : description
1516 /* Continue collecting preference settings, until the user elects to
1517 * invoke the "continue" action.
1519 } while( request == IDRETRY );
1523 int CALLBACK SetupTool::OpeningDialogue
1524 ( HWND dlg, unsigned int msg, WPARAM request, LPARAM )
1526 /* Procedure to manage the initial dialogue box, which serves as
1527 * the main window for the setup application.
1530 { case WM_INITDIALOG:
1531 /* Invoked when the dialogue box is first displayed; centre it
1532 * on-screen, and assign an appropriate caption.
1534 WTK::AlignWindow( dlg, WTK_ALIGN_CENTRED | WTK_ALIGN_ONSCREEN );
1535 ChangeCaption( dlg, WTK::StringResource( NULL, ID_MAIN_DIALOGUE_CAPTION ) );
1539 /* Handle requests from user interaction with the controls which
1540 * are provided within the dialogue box.
1542 switch( msg = LOWORD( request ) )
1545 /* User has elected to proceed with installation; inhibit
1546 * further control interaction, and process the request.
1548 EnableWindow( GetDlgItem( dlg, IDOK ), FALSE );
1549 EnableWindow( GetDlgItem( dlg, IDCANCEL ), FALSE );
1550 while( Invoke()->InstallationRequest( dlg ) == ID_SETUP_SHOW_LICENCE )
1552 * During installation, the user may request to view
1553 * the product licence; honour such requests.
1555 BrowseLicenceDocument( dlg );
1558 /* Invoked immediately, when the user elects to cancel the
1559 * installation, otherwise by fall through on completion of
1560 * a request to proceed; this closes the top-level dialogue,
1561 * so terminating the application/
1563 EndDialog( dlg, msg );
1566 case ID_SETUP_SHOW_LICENCE:
1567 /* This represents a confirmation that the user would like
1568 * to view the licence document for the package.
1570 BrowseLicenceDocument( dlg );
1574 /* Other messages to the opening dialogue box may be safely ignored.
1579 pkgSetupAction::pkgSetupAction
1580 ( pkgSetupAction *queue, const char *pkg, const char *ext ):
1581 flags( ACTION_DOWNLOAD + ACTION_INSTALL ), prev( queue ), next( NULL )
1583 /* Constructor for a setup action item; having initialised the
1584 * flags, and the link reference to the first entry, if any, in
1585 * the actions list...
1589 /* ...it constructs the name of the package with which the action
1590 * is to be associated, qualified by the specified extension, and
1591 * copies it to the heap...
1593 char ref[1 + snprintf( NULL, 0, pkg, ext )]; sprintf( ref, pkg, ext );
1594 package_name = strdup( ref );
1597 /* ...or, when no qualifying extension is specified, it simply
1598 * stores the unqualified package name on the heap...
1600 package_name = strdup( pkg );
1604 /* ...before ultimately advancing the reference pointer, to
1605 * link this item to the end of the actions list.
1607 while( prev->next != NULL )
1613 pkgSetupAction::~pkgSetupAction()
1615 /* Destructor for a single setup action item; it frees the heap
1616 * memory which was originally allocated to store the name of the
1617 * package with which the action was associated, and detaches the
1618 * item from the actions list, before it is deleted.
1620 free( (void *)(package_name) );
1621 if( prev != NULL ) prev->next = next;
1622 if( next != NULL ) next->prev = prev;
1625 int APIENTRY WinMain
1626 ( HINSTANCE Instance, HINSTANCE PrevInstance, char *CmdLine, int ShowMode )
1628 /* The program entry point for the mingw-get setup tool.
1631 { /* We allow only one instance of mingw-get to run at any time,
1632 * so first, we check to ensure that there is no other instance
1635 if( ! WTK::RaiseAppWindow( Instance, ID_MAIN_WINDOW_CLASS ) )
1637 * There is no running mingw-get instance; we may tentatively
1638 * continue with the setup procedure.
1640 SetupTool Install( Instance );
1642 /* In any event, we return the setup tool status, as
1643 * the program exit code.
1645 return SetupTool::Status;
1647 catch( dmh_exception &e )
1649 /* Here, we handle any fatal exception which has been raised
1650 * and identified by the diagnostic message handler...
1652 MessageBox( NULL, e.what(), "WinMain", MB_ICONERROR );
1653 return EXIT_FAILURE;
1655 catch( WTK::runtime_error &e )
1657 /* ...while here, we diagnose any other error which was captured
1658 * during the creation of the application's window hierarchy, or
1659 * processing of its message loop...
1661 MessageBox( NULL, e.what(), "WinMain", MB_ICONERROR );
1662 return EXIT_FAILURE;
1665 { /* ...and here, we diagnose any other error which we weren't
1666 * able to explicitly identify.
1668 MessageBox( NULL, "Unknown exception", "WinMain", MB_ICONERROR );
1669 return EXIT_FAILURE;
1673 /* $RCSfile$: end of file */