6 * Written by Keith Marshall <keithmarshall@users.sourceforge.net>
7 * Copyright (C) 2013, 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 static const char *uri_key = "uri";
143 static const char *mirror_key = "mirror";
144 static const char *value_none = "none";
148 /* Top level class, implementing the controlling features
149 * of the mingw-get setup tool.
154 inline SetupTool( HINSTANCE );
155 static SetupTool *Invoke( void );
156 static unsigned int IsPref( unsigned int test ){ return test & prefs; }
158 bool InitialiseSetupHookAPI( void );
159 inline int DispatchSetupHookRequest( unsigned int, ... );
162 const wchar_t *dll_name;
163 HMODULE base_dll, hook_dll;
164 typedef int (*dll_request_hook)( unsigned int, va_list );
165 dll_request_hook dll_request;
167 inline void DoFirstTimeSetup( HWND );
168 inline int SetInstallationPreferences( HWND );
169 inline HMODULE HaveWorkingInstallation( void );
170 inline wchar_t *UseDefaultInstallationDirectory( void );
171 inline void ShowInstallationDirectory( HWND );
172 inline int InstallationRequest( HWND );
174 static void EnablePrefs( HWND, int );
175 static inline void EnablePrefs( HWND, HWND );
176 static inline void StorePref( HWND, int, int );
177 static inline void ShowPref( HWND, int, int );
179 static SetupTool *setup_hook;
180 static const wchar_t *default_dirpath;
181 wchar_t *approot_path( const wchar_t * = NULL );
182 void ChangeInstallationDirectory( HWND );
183 static wchar_t *approot_tail;
185 static unsigned int prefs;
186 static int CALLBACK OpeningDialogue( HWND, unsigned, WPARAM, LPARAM );
187 static int CALLBACK ConfigureInstallationPreferences
188 ( HWND, unsigned, WPARAM, LPARAM );
190 static const wchar_t *gui_program;
191 inline int RunInstalledProgram( const wchar_t * );
193 inline wchar_t *setup_dll( void )
194 { /* Helper function to ensure that the static "approot_path" buffer
195 * specifies a reference path name for mingw-get-setup-0.dll
197 return approot_path( L"libexec\\mingw-get\\mingw-get-setup-0.dll" );
200 inline void CreateApplicationLauncher
201 ( int, const wchar_t *, const char *, const char * );
204 /* We will never instantiate more than one object of the SetupTool
205 * class at any one time. Thus, we may record its status in a static
206 * member variable, which we must initialise; we assume the all setup
207 * operations will be completed successfully.
209 int SetupTool::Status = EXIT_SUCCESS;
211 /* We need an alternative status code, to indicate that the user has
212 * elected to run the standard installer immediately, on termination
213 * of the initial setup; the value is arbitrary, subject only to the
214 * requirement that it differs from EXIT_SUCCESS and EXIT_FAILURE.
216 #define EXIT_CONTINUE 101
218 /* Installation options are also recorded in a static member varaible;
219 * we initialise these to match a set of default preferences, which we
220 * believe will suit a majority of users.
222 unsigned int SetupTool::prefs = SETUP_OPTION_DEFAULTS;
224 /* We also provide a static hook, through which objects of any other
225 * class may invoke public methods of the SetupTool object, without
226 * requiring them to maintain their own reference to this object.
228 SetupTool *SetupTool::setup_hook = NULL;
229 SetupTool *SetupTool::Invoke( void ){ return setup_hook; }
231 static void RefreshDialogueWindow( HWND AppWindow )
233 /* A trivial helper routine, to force an immediate update
234 * of a dialogue window display.
236 InvalidateRect( AppWindow, NULL, FALSE );
237 UpdateWindow( AppWindow );
240 /* Embed an implementation of the diagnostic message handler,
241 * customised for deployment by the setup tool...
243 #include "dmhcore.cpp"
244 #include "dmhguix.cpp"
246 /* ...and supported by this custom initialisation routine.
248 EXTERN_C void dmh_init( const dmh_class subsystem, const char *progname )
250 /* Public entry point for message handler initialisation...
252 * We only do it once, silently ignoring any attempt to
253 * establish a second handler.
255 if( (dmh == NULL) && (subsystem == DMH_SUBSYSTEM_GUI) )
256 dmh = new dmhTypeGUI( strdup( progname ) );
259 inline unsigned long pkgSetupAction::HasAttribute( unsigned long mask )
261 /* Helper function to examine the flags within a setup action
262 * item, checking for the presence of a specified attribute.
264 return (this != NULL) ? mask & flags : 0UL;
267 void pkgSetupAction::ClearAllActions( void )
269 /* Routine to clear an entire list of setup action items,
270 * releasing the memory allocated to each.
272 pkgSetupAction *current = this;
273 while( current != NULL )
275 /* Walk the list, deleting each item in turn.
277 pkgSetupAction *defunct = current;
278 current = current->next;
283 /* Worker thread API for use with modal dialogue boxes; the worker thread
284 * is invoked by passing a function pointer, conforming to this prototype,
285 * to the _beginthread() API.
287 typedef void SetupThreadProcedure( void * );
289 class SetupDialogueThread
291 /* Locally defined class to manage the binding of an arbitrary worker
292 * thread procedure to a modal dialogue, with a generic message loop.
295 inline long ExitCode(){ return status; }
296 SetupDialogueThread( HWND, int, SetupThreadProcedure * );
299 static int CALLBACK SetupDialogue( HWND, unsigned int, WPARAM, LPARAM );
300 static SetupThreadProcedure *WorkerThread;
304 /* We need to instantiate this static class member.
306 SetupThreadProcedure *SetupDialogueThread::WorkerThread = NULL;
308 int CALLBACK SetupDialogueThread::SetupDialogue
309 ( HWND window, unsigned int msg, WPARAM request, LPARAM )
311 /* Generic handler for dialogue boxes which delegate an associated
312 * processing activity to a background thread.
316 /* We need to handle only two classes of windows messages
317 * on behalf of such dialogue boxes...
320 /* ...viz. on initial dialogue box creation, we delegate the
321 * designated activity to the background thread...
323 _beginthread( WorkerThread, 0, (void *)(window) );
327 switch( msg = LOWORD( request ) )
331 /* ...then we wait for a notification that the dialogue may be
332 * closed, (which isn't permitted until the thread completes).
334 EndDialog( window, msg );
337 case ID_SETUP_SHOW_LICENCE:
338 /* Any request to view the licence document is handled in situ,
339 * without closing the active dialogue box.
341 BrowseLicenceDocument( window, -4, -50 );
345 /* Any other messages, which are directed to this dialogue box,
346 * may be safely ignored.
351 SetupDialogueThread::SetupDialogueThread
352 ( HWND owner, int id, SetupThreadProcedure *setup_actions )
354 /* Class constructor assigns the worker procedure for the thread,
355 * then opens the dialogue box which starts it, ultimately capturing
356 * the exit code when this dialogue is closed.
358 WorkerThread = setup_actions;
359 status = DialogBox( NULL, MAKEINTRESOURCE( id ), owner, SetupDialogue );
362 class SetupDownloadAgent
364 /* A locally defined class to manage package download activities
365 * on behalf of the setup tool.
368 static void Run( void * );
369 SetupDownloadAgent( pkgSetupAction *packages ){ PackageList = packages; }
372 static pkgSetupAction *PackageList;
375 /* Initialise the list of packages for download, as an empty list;
376 * we will subsequently populate it, as required.
378 pkgSetupAction *SetupDownloadAgent::PackageList = NULL;
380 /* Embed the download agent implementation as a filtered subset of
381 * mingw-get's own download service implementation.
383 #include "pkginet.cpp"
384 #include "pkgnget.cpp"
386 static inline int dll_load_failed( const wchar_t *dll_name )
388 /* Helper to diagnose failure to load any supporting DLL.
390 return dmh_notify( DMH_ERROR,
391 "%S: DLL load failed; cannot run setup hooks\n", dll_name
395 void SetupDownloadAgent::Run( void *owner )
397 /* Method to run the download agent; it fetches all packages
398 * which are specified in the setup tool's initial installation
399 * list, (using a metered download procedure)...
401 pkgDownloadMeterGUI metered( (HWND)(owner) );
402 PackageList->DownloadArchiveFiles();
404 /* ...then unpacks each into its ultimate installed location,
405 * before delegating finalisation of the installation to the
406 * installer DLLs, which should thus have been installed.
408 if( (PackageList->UnpackScheduledArchives() == IDOK)
409 && SetupTool::Invoke()->InitialiseSetupHookAPI() )
411 /* Only on successful initialisation of the DLL hooks, do
412 * we actually continue with the delegated installation.
414 SetupTool::Invoke()->DispatchSetupHookRequest(
415 SETUP_HOOK_POST_INSTALL, PackageList, owner
418 /* Successful DLL initialisation is also a prerequisite for
419 * allowing the user to run the main installer, to continue
420 * the installation with a more comprehensive package set;
421 * however, progression to such advanced installation...
423 if( SetupTool::IsPref( SETUP_OPTION_WITH_GUI ) )
425 * ...is feasible, only if the user has elected to install
426 * the mingw-get GUI client.
428 EnableWindow( GetDlgItem( (HWND)(owner), IDOK ), TRUE );
431 /* DLL initialisation was unsuccessful; tell the user that
432 * we are unable to continue this installation.
434 dmh_notify( DMH_ERROR, "setup: unable to continue\n" );
436 /* In any event, always offer the option to quit, without
437 * proceeding to advanced installation.
439 EnableWindow( GetDlgItem( (HWND)(owner), IDCANCEL ), TRUE );
442 bool SetupTool::InitialiseSetupHookAPI( void )
444 /* Helper method, invoked by SetupDownloadAgent::Run(), to confirm
445 * that the requisite installer DLLs have been successfully unpacked,
446 * and if so, to initialise them for continuation of installation.
448 * First, we must confirm that mingw-get's main DLL is available...
450 if( (base_dll = HaveWorkingInstallation()) == NULL )
452 * ...immediately abandoning the installation, if not.
454 return (dll_load_failed( dll_name ) == 0);
456 /* Having successfully loaded the main DLL, we perform a similar
457 * check for the setup tool's bridging DLL...
459 if( (hook_dll = LoadLibraryW( dll_name = setup_dll() )) == NULL )
461 /* ...once again, abandoning the installation, if this is not
462 * available; in this case, since we've already successfully
463 * loaded mingw-get's main DLL, we also unload it.
465 FreeLibrary( base_dll ); base_dll = NULL;
466 return (dll_load_failed( dll_name ) == 0);
468 /* With both DLLs successfully loaded, we may establish the
469 * conduit, through which the setup tool invokes procedures in
470 * either of these DLLs...
472 dll_request = (dll_request_hook)(GetProcAddress( hook_dll, "setup_hook" ));
473 if( dll_request != NULL )
475 /* ...and, on success, complete the DLL initialisation by
476 * granting the DLLs shared access to the diagnostic message
477 * handler provided by the setup tool's main program.
479 DispatchSetupHookRequest( SETUP_HOOK_DMH_BIND, dmh );
482 /* If we get to here, DLL initialisation was unsuccessful;
483 * diagnose, and bail out.
485 dmh_notify( DMH_ERROR, "setup_hook: DLL entry point not found\n" );
489 inline int SetupTool::DispatchSetupHookRequest( unsigned int request, ... )
491 /* Helper method, providing a variant argument API to the
492 * request hook within the setup tool's bridging DLL; it
493 * collects the argument vector, to pass into the DLL.
496 va_start( argv, request );
497 int retval = dll_request( request, argv );
503 long InitiatePackageInstallation( HWND owner, pkgSetupAction *pkglist )
505 /* Helper routine, invoked by SetupTool::DoFirstTimeSetup(), to
506 * install mingw-get, and proceed to further package selection.
508 SetupDownloadAgent agent( pkglist );
509 SetupDialogueThread run( owner, IDD_SETUP_DOWNLOAD, SetupDownloadAgent::Run );
510 RefreshDialogueWindow( owner );
511 return run.ExitCode();
514 /* Embed a selected subset of the package stream extractor methods,
515 * sufficient to support unpacking of the .tar.xz archives which are
516 * used as the delivery medium for mingw-get.
518 #include "pkgstrm.cpp"
520 /* The standard archive extractor expects to use the pkgOpenArchiveStream()
521 * function to gain access to archive files; however, the generalised version
522 * of this is excluded from the subset embedded from pkgstrm.cpp, so we must
523 * provide a replacement...
525 pkgArchiveStream *pkgOpenArchiveStream( const char *archive_path_name )
527 /* ...function to prepare an archive file for extraction; (this specialised
528 * version is specific to extraction of xz compressed archives).
530 return new pkgXzArchiveStream( archive_path_name );
533 /* The archive extractor classes incorporate reference pointers to pkgXmlNode
534 * and pkgManifest class objects, but these are not actually used within the
535 * context required here; to placate the compiler, declare these as opaque
541 /* Embed the minimal subset of required extractor classes and methods...
543 #include "tarproc.cpp"
545 /* ...noting that the formal implementation of this required constructor
546 * has been excluded; (it implements a level of complexity, not required
547 * here); hence, we provide a minimal, do nothing, substitute.
549 pkgTarArchiveProcessor::pkgTarArchiveProcessor( pkgXmlNode * ){}
551 /* Similarly, we need a simplified implementation of the destructor
552 * for this same class...
554 pkgTarArchiveProcessor::~pkgTarArchiveProcessor()
556 /* ...but it this case, we still use it to clean up the memory
557 * allocated by the pkgTarArchiveExtractor class constructor.
559 free( (void *)(sysroot_path) );
564 int CALLBACK unwise( HWND owner, unsigned msg, WPARAM request, LPARAM data )
566 /* Handler for the dialogue box which is presented when the user
567 * makes an unwise choice of installation directory (specifically
568 * a choice with white space in the path name).
573 /* There are no particular initialisation requirements, for
574 * this dialogue box; just accept the defaults.
579 /* User has selected one of the continuation options...
581 switch( msg = LOWORD( request ) )
586 /* Ignore advice to choose a more appropriate location:
587 * this requires additional confirmation that the user has
588 * understood the implications of this choice; do no more
589 * than enable the button which provides such confirmation.
591 if( HIWORD( request ) == BN_CLICKED )
592 EnableWindow( GetDlgItem( owner, IDYES ),
593 SendMessage( (HWND)(data), BM_GETCHECK, 0, 0 ) == BST_CHECKED
598 /* Accept advice; go back to make a new choice of location
599 * for this installation, or...
602 /* ...confirm understanding of, and intent to ignore, advice;
603 * in either case close the dialogue, returning appropriate
606 EndDialog( owner, msg );
610 /* No other conditions are handled explicitly, within this dialogue.
615 static bool UnconfirmedDirectorySelection( HWND owner, const wchar_t *choice )
617 /* Helper function to ratify user's selection of installation directory;
618 * given an absolute path name retrieved from SHBrowsForFolder(), it checks
619 * for the presence of embedded white space, and reiterates the appropriate
620 * warning when any is found, before allowing the user to choose again, or
621 * to ignore the advice, and shoot himself/herself in the foot.
624 RefreshDialogueWindow( owner );
625 if( choice && *(path = choice) )
627 /* We have a non-empty path name selection to ratify...
630 if( iswspace( *path++ ) )
632 /* ...and we DID find embedded white space; castigate the user
633 * for his/her folly in ignoring advice...
635 return DialogBox( NULL, MAKEINTRESOURCE( IDD_SETUP_UNWISE ),
636 owner, unwise ) != IDYES;
638 /* If we get to here, then the user appears to have made a sane choice;
639 * caller need not repeat the request for a directory selection...
643 /* ...but when we were asked to ratify no selection at all, then caller
644 * must repeat this request.
650 int CALLBACK BrowserInit( HWND owner, unsigned msg, LPARAM, LPARAM dirname )
652 /* Helper function to set the default installation directory path name,
653 * and to assign a more appropriate dialogue box description...
655 if( msg == BFFM_INITIALIZED )
657 /* ...when initialising the SHBrowseForFolder() dialogue to elicit
658 * the user's choice of installation directory.
660 SendMessage( owner, WM_SETTEXT, 0, (LPARAM)("Select Installation Directory") );
661 SendMessage( owner, BFFM_SETSELECTIONW, TRUE, dirname );
667 bool mkdir_default( wchar_t *dirpath )
669 /* Helper function to create the default installation directory;
670 * this must exist before we call SHBrowseForFolder(), so that we
671 * may offer as the default choice, even if the user subsequently
672 * declines to accept it.
674 bool retval = false; struct _stat chkdir;
675 if( ! ((_wstat( dirpath, &chkdir ) == 0) && S_ISDIR( chkdir.st_mode )) )
677 /* The recommended default directory doesn't yet exist; try to
680 if( ! (retval = (_wmkdir( dirpath ) == 0)) )
682 * ...but if unsuccessful, fall back to offering the root
683 * directory of the default device, as a starting point for
684 * the user's selection.
688 /* In any event, this will return "true" if we created the
689 * directory, or "false" if it either already existed, or if
690 * our attempt to create it failed.
696 void rmdir_default( const wchar_t *offer, const wchar_t *selected )
698 /* Helper function, called if we created the default installation
699 * directory; it will remove it if the user rejected this default
700 * selection, and any descendant thereof.
702 const wchar_t *chk = offer;
703 while( *chk && *selected && (*chk++ == *selected++) ) ;
704 if( (*chk != L'\0') || ((*selected != L'\0') && (*selected != L'\\')) )
709 int setup_putenv( const char *varname, const wchar_t *value )
711 /* Helper function to assign a specified value to a named
712 * environment variable.
714 const char *fmt = "%s=%S";
715 char assignment[1 + snprintf( NULL, 0, fmt, varname, value )];
716 snprintf( assignment, sizeof( assignment ), fmt, varname, value );
717 return putenv( assignment );
720 wchar_t *SetupTool::approot_tail = NULL;
721 wchar_t *SetupTool::approot_path( const wchar_t *relative_path )
723 /* Helper method to resolve a relative path name to its absolute
724 * equivalent, within the directory hierarchy of the user's chosen
725 * installation directory.
727 * The resolved absolute path name is returned in this static
728 * buffer, initially defined to be empty.
730 static wchar_t dirpath[PATH_MAX] = L"\0";
732 /* When a previous call has left a "tail" buffer,
733 * (which may or may not be populated)...
735 if( approot_tail != NULL )
737 * ...then clear it...
739 *approot_tail = L'\0';
741 else if( *dirpath != L'\0' )
743 * ...otherwise, establish the "tail" buffer, following
744 * the "approot" string within the "dirpath" buffer.
746 for( approot_tail = dirpath; *approot_tail != L'\0'; approot_tail++ )
749 /* When a relative path argument is specified...
751 if( relative_path != NULL )
753 /* ...then we must resolve it as an absolute path, interpreting
754 * it as relative to "approot", by copying it into the "tail"
755 * buffer within "dirpath".
757 * We perform the copy character by character, storing at the
758 * advancing "caret" position, while ensuring we do not overrun
759 * the "endstop" location, which we set initially to mark the
760 * end of the "dirpath" buffer.
762 wchar_t *caret = approot_tail - 1;
763 wchar_t *endstop = dirpath + PATH_MAX - 1;
765 /* We initially set "caret" to point to the final character
766 * in "approot", so that we may check for the presence of a
767 * directory separator; if there ISN'T one there already...
769 if( (*caret != L'\\') && (*caret != L'/') )
771 * ...then we advance it to the start of the "tail"
772 * buffer, where we will insert one.
776 /* When the first character of the specified relative path
777 * is NOT itself a directory separator, (which is expected
778 * in the normal case)...
780 if( (*relative_path != L'\\') && (*relative_path != L'/') )
782 * ...then we explicitly insert one; (we may note that this
783 * will either overwrite an existing separator at the end of
784 * "approot", or it will occupy the first "caret" position
785 * within the "tail" buffer).
789 /* Now, we copy the relative path to the "caret" location...
791 do { /* ...ensuring that we do NOT overrun the "endstop"...
793 if( caret < endstop )
795 /* ...and normalising directory separators, favouring
796 * Microsoft's '\\' preference, as we go...
798 if( (*caret = *relative_path++) == L'/' )
802 /* ...but, if we hit the "endstop", we immediately
803 * truncate and terminate the copy...
807 /* ...continuing until we've copied the terminating NUL
808 * from the relative path argument itself, or we hit the
809 * "endstop", forcing early termination.
811 } while( *caret++ != L'\0' );
813 /* However we completed the operation, we return a pointer to the
814 * entire content of the "dirpath" static buffer.
819 inline int pkgSetupAction::UnpackScheduledArchives( void )
821 /* Helper to unpack each of the package archives, specified in the
822 * setup actions list, extracting content to its ultimate installed
823 * location within the user specified installation directory tree.
825 int status = IDCANCEL;
826 pkgSetupAction *install;
828 /* When the action list is not empty...
830 if( (install = this) != NULL )
832 /* ...ensure that we process it from its first entry.
835 while( install->prev != NULL ) install = install->prev;
836 while( install != NULL )
838 /* For each list entry, locate the downloaded copy of the
839 * archive, in the installer's package cache directory.
841 const char *cache = "%R" "var\\cache\\mingw-get\\packages\\%F";
842 char archive_name[mkpath( NULL, cache, install->ArchiveName(), NULL )];
843 mkpath( archive_name, cache, install->ArchiveName(), NULL );
845 /* Report activity, and provided download has made the
846 * cached copy of the archive available...
848 dmh_notify( DMH_INFO, "setup: unpacking %s\n", install->ArchiveName() );
849 if( install->HasAttribute( ACTION_DOWNLOAD ) == 0 )
851 * ...extract its content.
853 pkgTarArchiveExtractor( archive_name, "%R" );
856 { /* The package is not available; assume that download was
857 * unsuccessful, and diagnose accordingly...
859 dmh_notify( DMH_ERROR, "unpack: required archive file is not available\n" );
860 dmh_notify( DMH_ERROR, "unpack: aborted due to previous download failure\n" );
862 /* ...then cancel further installation activities.
867 /* Repeat for any other scheduled action items.
869 install = install->next;
872 /* Ultimately return the status code, indicating either complete
873 * success, or requesting cancellation of further installation.
879 char *arg_append( char *caret, const char *argtext, const char *endstop )
881 /* Command line construction helper function; append specified "argtext"
882 * into the constructed command line, with appropriate quoting to keep it
883 * as a single argument, and preceded by a space, at the position marked
884 * by "caret", and ensuring that the text does not overrun "endstop".
886 if( (endstop > caret) && ((endstop - caret) > 1)
887 && (argwrap( caret + 1, argtext, endstop - caret - 1) != NULL) )
889 /* The specified argument can be accommodated; insert the separating
890 * space, and update the caret to mark the position at which the next
891 * argument, if any, should be appended.
894 return caret + strlen( caret );
896 /* If we get to here, the argument could not be accommodated, return the
897 * caret position, unchanged.
903 char *arg_append( char *caret, const wchar_t *argtext, const char *endstop )
905 /* Overloaded variant of the preceding command line construction helper;
906 * this variant appends an argument, specified as a wide character string,
907 * to the constructed command line.
909 if( (endstop > caret) && ((endstop - caret) > 1) )
911 /* There is at least some remaining space in the command line buffer;
912 * convert the specified argument to a regular string...
914 const char *conv_fmt = "%S";
915 char conv_buf[1 + snprintf( NULL, 0, conv_fmt, argtext )];
916 snprintf( conv_buf, sizeof( conv_buf ), conv_fmt, argtext );
918 * ...then delegate the actual append operation to the regular string
919 * handling variant of the helper function.
921 return arg_append( caret, conv_buf, endstop );
923 /* If we get to here, the command line buffer space has been completely
924 * exhausted; return the caret position, unchanged.
930 /* POSIX specifies this as the maximum number of characters allowed in the
931 * aggregate of all arguments passed to a child process in an exec() function
932 * call. It isn't typically defined on MS-Windows, although limits do apply
933 * in the case of exec(), spawn(), or system() function calls. MSDN tells
934 * us that the maximum length of a command line is 8191 characters on WinXP
935 * and later, but only 2047 characters on earlier versions; allowing one
936 * additional character for a terminating NUL, we will adopt the lower
937 * of these, as a conservative choice.
939 # define ARG_MAX 2048
942 inline void SetupTool::CreateApplicationLauncher
943 ( int opt, const wchar_t *appname, const char *desc, const char *linkname )
945 /* Construct a command line to invoke the shlink.js script, (which is
946 * provided by the mingw-get package), via the system provided wscript
947 * interpreter, to install a desktop or start-menu application link.
949 static const char *cmd = "wscript";
950 static const char *location[] = { "--start-menu", "--desktop" };
952 /* Initialise the argument list, setting the wscript option to suppress
953 * its normal verbose behaviour, and set a buffer endstop reference, so
954 * that we may detect, and avoid overrun.
956 char cmdline[ARG_MAX] = "-nologo";
957 const char *endstop = cmdline + sizeof( cmdline );
959 /* Add the full path name reference for the shlink.js script which is to
960 * be executed; (note that we resolve this relative to the user's choice
961 * of installation directory, where the script will have been installed,
962 * during prior unpacking of the mingw-get-bin package tarball).
964 char *caret = arg_append( cmdline + strlen( cmdline ),
965 approot_path( L"libexec\\mingw-get\\shlink.js" ), endstop
968 /* Add options to select placement of the application links, and to specify
969 * whether they should be available to current user ot to all users.
971 caret = arg_append( ((opt & SETUP_OPTION_ALL_USERS) != 0)
972 ? arg_append( caret, "--all-users", endstop )
973 : caret, location[opt >> 2], endstop
976 /* Add a description for the application link.
978 caret = arg_append( arg_append( caret, "--description", endstop ),
982 /* Specify the path name for the application to be invoked, (again noting
983 * that this must be resolved relative to the installation directory), and
984 * the name to be given to the link file itself.
986 arg_append( arg_append( caret, approot_path( appname ), endstop ),
990 /* Finally, invoke the script interpreter to process the specified command.
991 * (We use a spawn() call here, in preference to system(), to avoid opening
992 * any unwanted console windows).
994 spawnlp( P_WAIT, cmd, cmd, cmdline, NULL );
997 inline SetupTool::SetupTool( HINSTANCE Instance ):
998 base_dll( NULL ), hook_dll( NULL )
1000 /* Constructor for the SetupTool object. Note that there should be only
1001 * one such object instantiated; this is intended as a one time only call,
1002 * and any attempted subsequent call will be ignored, as a consequence of
1003 * the first time initialisation of the setup_hook static member.
1005 if( setup_hook == NULL )
1008 UseDefaultInstallationDirectory();
1010 /* Get a security token for the process, so that we may selectively
1011 * disable those options which require administrative privilege, when
1012 * running as a normal user.
1014 * FIXME: this is specific to WinNT platforms; we should add a DLL
1015 * lookup, so we can avoid calling the security hooks which are not
1016 * supported on Win9x.
1019 SID_IDENTIFIER_AUTHORITY sid = SECURITY_NT_AUTHORITY;
1020 int authority = AllocateAndInitializeSid( &sid, 2,
1021 SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0,
1024 if( authority != 0 )
1026 if( CheckTokenMembership( NULL, group, &authority ) && (authority != 0) )
1027 prefs |= SETUP_OPTION_PRIVILEGED | SETUP_OPTION_ALL_USERS;
1031 /* To offer the user a choice of installation directory, we will
1032 * use the SHBrowswForFolder() API; this is a COM component API,
1033 * so we must initialise the COM subsystem.
1035 CoInitialize( NULL );
1037 /* Initialise our own Diagnostic Message Handler subsystem.
1039 dmh_init( DMH_SUBSYSTEM_GUI,
1040 WTK::StringResource( Instance, ID_MAIN_WINDOW_CLASS )
1043 /* Make ALL of Microsoft's common controls available.
1045 InitCommonControls();
1047 /* The setup tool runs as a sequence of dialogue boxes, without
1048 * any main window; display the opening dialogue.
1050 DialogBox( Instance,
1051 MAKEINTRESOURCE( IDD_SETUP_BEGIN ), NULL, OpeningDialogue
1054 /* When the user has requested progression to package installation...
1056 if( Status == EXIT_CONTINUE )
1058 /* ...then delegate that to the embedded mingw-get plugin, before
1059 * reasserting successful completion status for the setup tool.
1061 DispatchSetupHookRequest( SETUP_HOOK_RUN_INSTALLER, setup_dll() );
1062 Status = EXIT_SUCCESS;
1065 /* If the mingw-get-0.dll and mingw-get-setup-0.dll libraries
1066 * were successfully loaded, to complete the installation process,
1067 * then we must now unload them; we also have no further use for
1068 * mingw-get-setup-0.dll, so we may delete it.
1070 if( hook_dll != NULL ){ FreeLibrary( hook_dll ); _wunlink( setup_dll() ); }
1071 if( base_dll != NULL ){ FreeLibrary( base_dll ); }
1073 /* We're done with the COM subsystem; release it.
1079 const wchar_t *SetupTool::default_dirpath = L"C:\\MinGW";
1080 inline wchar_t *SetupTool::UseDefaultInstallationDirectory( void )
1082 /* Set up the specification for the preferred default installation
1083 * directory; (this is per MinGW.org Project recommendation).
1085 approot_tail = NULL;
1086 return wcscpy( approot_path(), default_dirpath );
1089 void SetupTool::ChangeInstallationDirectory( HWND AppWindow )
1091 /* Set up the specification for the preferred default installation
1092 * directory; (this is per MinGW.org Project recommendation).
1094 wchar_t *dirpath = approot_path();
1096 /* Exert our best effort to make the default installation directory
1097 * available for immediate selection via SHBrowseForFolder()
1099 bool default_dir_created = mkdir_default( dirpath );
1101 /* The browse for folder dialogue isn't modal; to ensure the user
1102 * doesn't inadvertently dismiss the invoking dialogue prematurely,
1103 * disable its active control buttons.
1105 int buttons[] = { IDOK, ID_SETUP_SHOW_LICENCE, ID_SETUP_CHDIR, IDCANCEL };
1106 for( int index = 0; index < 4; index++ )
1108 /* Note that we could simply have disabled the invoking dialogue
1109 * box itself, but this loop to disable individual buttons looks
1110 * better, giving the user better visual feedback indicating the
1111 * disabled state of the owner window's control buttons.
1113 buttons[index] = (int)(GetDlgItem( AppWindow, buttons[index] ));
1114 EnableWindow( (HWND)(buttons[index]), FALSE );
1117 /* Set up a standard "Browse for Folder" dialogue, to allow the
1118 * user to choose an installation directory...
1120 BROWSEINFOW explorer = { 0 };
1121 explorer.ulFlags = BIF_RETURNONLYFSDIRS | BIF_USENEWUI;
1122 explorer.lpszTitle = L"Please select a directory, in which to install MinGW; "
1123 L"it is strongly recommended that your choice should avoid any directory "
1124 L"which includes white space within its absolute path name.";
1125 explorer.lParam = (LPARAM)(dirpath);
1126 explorer.lpfn = BrowserInit;
1128 /* ...and invoke it, possibly repeatedly, until the user has
1129 * confirmed an appropriate, (or maybe inappropriate), choice.
1131 do { LPITEMIDLIST dir;
1132 if( (dir = SHBrowseForFolderW( &explorer )) != NULL )
1133 SHGetPathFromIDListW( dir, dirpath );
1135 /* Fow each proposed directory choice returned from the
1136 * browser dialogue, force a rescan to locate the end of
1137 * its name, in the "approot" return buffer...
1139 approot_tail = NULL;
1141 * ...then update the preferences display, to reflect the
1144 ShowInstallationDirectory( AppWindow );
1146 /* Finally, check for any possibly ill advised directory
1147 * choice, (specifically warning of embedded white space,
1148 * and give the user an opportunity for remedial action.
1150 } while( UnconfirmedDirectorySelection( AppWindow, dirpath ) );
1152 /* When we created the default installation directory, in preparation
1153 * for a default (recommended) install...
1155 if( default_dir_created )
1157 * ...we check if the user accepted this choice, and remove it when
1158 * some alternative (suitable or otherwise) was adopted.
1160 rmdir_default( default_dirpath, dirpath );
1162 /* The non-modal directory browser window has now been closed, but our
1163 * dialogue still exhibits disabled controls. Although we disabled each
1164 * control individually, the reverse process of reactivating each of them
1165 * individually seems to not, and subsequently restoring the focus to the
1166 * default "Continue", doesn't seem to properly restore the visual cue to
1167 * apprise the user of this default; thus we send a WM_COMMAND message to
1168 * close this dialogue, while requesting that it be reinitialised from
1169 * scratch, (so reactivating it in its entirety).
1171 SendMessage( AppWindow, WM_COMMAND, (WPARAM)(IDRETRY), 0 );
1174 inline void SetupTool::DoFirstTimeSetup( HWND AppWindow )
1175 #define archive_class(TAG) WTK::StringResource( NULL, ID_##TAG##_DISTNAME )
1177 /* When we didn't defer to any prior installation, proceed with a new
1178 * one; set up the APPROOT environment variable, to reflect the choice
1179 * of installation directory, (whether user's choice or default).
1181 setup_putenv( "APPROOT", approot_path( L"" ) );
1183 /* Identify the minimal set of packages, which we require to establish
1184 * the mingw-get installer set-up; we specify this a a linked action list,
1185 * beginning with a setup action request for the base package...
1187 pkgSetupAction *linked, *list;
1188 linked = list = new pkgSetupAction( NULL, archive_class( PACKAGE_BASE ), "bin" );
1189 if( IsPref( SETUP_OPTION_WITH_GUI ) )
1191 * ...optionally adding the GUI extension, at the user's behest...
1193 linked = new pkgSetupAction( linked, archive_class( PACKAGE_BASE ), "gui" );
1195 /* ...always installing the licence pack...
1197 linked = new pkgSetupAction( linked, archive_class( PACKAGE_BASE ), "lic" );
1199 /* ...and finishing up with the setup DLL and XML data packages.
1201 linked = new pkgSetupAction( linked, archive_class( PACKAGE_DATA ), "dll" );
1202 linked = new pkgSetupAction( linked, archive_class( PACKAGE_DATA ), "xml" );
1204 /* Download packages to the mingw-get package cache, (which we will
1205 * create, as a side effect of downloading, if necessary), and unpack
1206 * them in place, to establish the basic mingw-get installation.
1208 if( InitiatePackageInstallation( AppWindow, list ) == IDOK )
1209 Status = EXIT_CONTINUE;
1211 /* Finally, clean up and we are done.
1213 list->ClearAllActions();
1216 #define SETUP_OPTION(M) SetupTool::IsPref(SETUP_OPTION_##M)
1217 #define SETUP_ASSERT(M) SETUP_OPTION(M) ? BST_CHECKED : BST_UNCHECKED
1218 #define SETUP_REVERT(M) SETUP_OPTION(M) ? BST_UNCHECKED : BST_CHECKED
1220 inline void SetupTool::ShowPref( HWND window, int id, int option )
1222 /* Helper method to update the display of any check box control,
1223 * within the installation preferences dialogue box, to match the
1224 * state of its associated setting flag within the setup object.
1226 SendMessage( GetDlgItem( window, id ), BM_SETCHECK, option, 0 );
1229 inline void SetupTool::StorePref( HWND ctrl, int id, int option )
1231 /* Helper method to record any user specified change of state
1232 * in any check box control, within the installation preferences
1233 * dialogue box, to its associated flag within the setup object.
1235 if( SendMessage( GetDlgItem( ctrl, id ), BM_GETCHECK, 0, 0 ) == BST_CHECKED )
1237 * The user selected the option associated with the check box;
1238 * set the associated flag to its "on" state.
1243 /* The user cleared the check box; set the associated flag to
1249 void SetupTool::EnablePrefs( HWND dialogue, int state )
1251 /* Helper used by the ConfigureInstallationPreferences() method,
1252 * to set the enabled state for those selection controls which are
1253 * only meaningful when a particular controlling selection, (which
1254 * is nominally the choice to install the GUI), is in effect.
1256 if( IsPref( SETUP_OPTION_PRIVILEGED ) )
1258 /* The "all users" installation option is supported only
1259 * if the setup tool is run with administrator privilege.
1261 EnableWindow( GetDlgItem( dialogue, ID_SETUP_ALL_USERS ), state );
1263 /* All other options are available to everyone.
1265 EnableWindow( GetDlgItem( dialogue, ID_SETUP_ME_ONLY ), state );
1266 EnableWindow( GetDlgItem( dialogue, ID_SETUP_START_MENU ), state );
1267 EnableWindow( GetDlgItem( dialogue, ID_SETUP_DESKTOP ), state );
1270 inline void SetupTool::EnablePrefs( HWND dialogue, HWND ref )
1272 /* Overload the preceding EnablePrefs() method, so we can deduce
1273 * the state parameter from a reference check-box control.
1275 EnablePrefs( dialogue, SendMessage( ref, BM_GETCHECK, 0, 0 ) );
1278 inline HMODULE SetupTool::HaveWorkingInstallation( void )
1280 /* Helper method to check for a prior installation, by attempting
1281 * to load its installed DLL component into our process context.
1283 dll_name = approot_path( L"libexec\\mingw-get\\mingw-get-0.dll" );
1284 return LoadLibraryW( dll_name );
1287 /* Identify the path to the mingw-get GUI client program, relative
1288 * to the root of the mingw-get installation tree.
1290 const wchar_t *SetupTool::gui_program = L"libexec\\mingw-get\\guimain.exe";
1292 inline int SetupTool::RunInstalledProgram( const wchar_t *program )
1294 /* Helper method to spawn an external process, into which a
1295 * specified program image is loaded; (typically this will be
1296 * the mingw-get program, either inherited from a previous run
1297 * of this setup tool, or a copy which we have just installed).
1301 /* We assume that the program to run lives within the directory
1302 * heirarchy specified for mingw-get installation.
1304 program = approot_path( program );
1305 if( (status = _wspawnl( _P_NOWAIT, program, program, NULL )) != -1 )
1307 /* When the specified image has been successfully loaded, we
1308 * give it around 1500 ms to get running, then we promote it
1309 * to the foreground.
1311 Sleep( 1500 ); WTK::RaiseAppWindow( NULL, ID_MAIN_WINDOW_CLASS );
1313 /* The return value is that returned by the spawn request.
1318 static inline int MessageDialogue( HINSTANCE app, int id, HWND owner )
1320 /* Helper function to emulate MessageBox behaviour using an
1321 * application specified dialogue box resource.
1323 return DialogBox( app, MAKEINTRESOURCE( id ), owner, confirm );
1326 int CALLBACK SetupTool::ConfigureInstallationPreferences
1327 ( HWND window, unsigned int msg, WPARAM request, LPARAM data )
1329 /* Handler for user interaction with the installation preferences
1330 * dialogue; configures options for the ensuing installation.
1335 /* We need to handle only two classes of message...
1339 * On initialisation of the dialogue, we ensure that the
1340 * displayed state of the controls is consistent with the
1341 * default, or previous settings, as specified within the
1344 EnablePrefs( window, IsPref( SETUP_OPTION_WITH_GUI ) );
1345 ShowPref( window, ID_SETUP_WITH_GUI, SETUP_ASSERT( WITH_GUI ) );
1346 ShowPref( window, ID_SETUP_ME_ONLY, SETUP_REVERT( ALL_USERS ) );
1347 ShowPref( window, ID_SETUP_ALL_USERS, SETUP_ASSERT( ALL_USERS ) );
1348 ShowPref( window, ID_SETUP_START_MENU, SETUP_ASSERT( START_MENU ) );
1349 ShowPref( window, ID_SETUP_DESKTOP, SETUP_ASSERT( DESKTOP ) );
1350 Invoke()->ShowInstallationDirectory( window );
1352 /* Explicitly assign focus to the "Continue" button, and
1353 * return FALSE so the default message handler will not
1354 * override this with any default choice.
1356 SetFocus( GetDlgItem( window, IDOK ) );
1360 switch( msg = LOWORD( request ) )
1362 /* A small selection of WM_COMMAND class messages
1363 * requires our attention.
1365 case ID_SETUP_WITH_GUI:
1366 if( HIWORD( request ) == BN_CLICKED )
1368 * The user has toggled the state of the GUI installation
1369 * option; we intercept this, so we may assign appropriate
1370 * availablity of other preferences...
1372 EnablePrefs( window, (HWND)(data) );
1374 /* ...but otherwise, we leave the default message handler to
1375 * process this in its default manner.
1379 case ID_SETUP_CHDIR:
1380 /* The user has selected the option to change the directory
1381 * into which mingw-get is to be installed; we handle this
1382 * request explicitly...
1384 Invoke()->ChangeInstallationDirectory( window );
1386 * ...with no further demand on the default message handler.
1391 /* The user has clicked the "Continue" button, (presumably
1392 * after making an appropriate selection of his installation
1393 * preferences; before blindly proceeding to installation,
1394 * check for any previously existing DLL component...
1396 if( (dll_hook = Invoke()->HaveWorkingInstallation()) != NULL )
1398 /* ...and, when one exists and has been loaded into our
1399 * runtime context, unload it before offering a choice of
1400 * how to continue...
1402 FreeLibrary( dll_hook );
1403 switch( msg = MessageDialogue( NULL, IDD_SETUP_EXISTS, window ) )
1406 /* The user has indicated a preference to continue
1407 * installation, but with an alternative installation
1408 * directory, so that the existing installation may
1409 * be preserved; go back to change directory.
1411 Invoke()->ChangeInstallationDirectory( window );
1415 /* The user has indicated a preference to switch to
1416 * advanced installation mode, by running the existing
1417 * copy of mingw-get; attempt to do so, before falling
1418 * through to cancel this setup session.
1420 Invoke()->RunInstalledProgram( gui_program );
1423 /* The current setup session is to be cancelled; pass
1424 * the cancellation request down the dialogue stack.
1426 EndDialog( window, IDCANCEL );
1431 case ID_SETUP_SHOW_LICENCE:
1432 /* we propagate his chosen options back to
1433 * the SetupTool object...
1435 StorePref( window, ID_SETUP_DESKTOP, SETUP_OPTION_DESKTOP );
1436 StorePref( window, ID_SETUP_START_MENU, SETUP_OPTION_START_MENU );
1437 StorePref( window, ID_SETUP_ALL_USERS, SETUP_OPTION_ALL_USERS );
1438 StorePref( window, ID_SETUP_WITH_GUI, SETUP_OPTION_WITH_GUI );
1440 * ...before simply falling through...
1443 /* ...to accept the chosen preferences when the user clicked
1444 * "Continue", or to discard them on "Cancel"; in either case
1445 * we close the dialogue box, passing the closure method ID
1446 * back to the caller...
1448 EndDialog( window, msg );
1450 * ...while informing the default message handler that we do
1451 * not require it to process this message.
1456 /* Any other messages are simply ignored, leaving the default
1457 * message handler to process them.
1462 inline void SetupTool::ShowInstallationDirectory( HWND dialogue )
1464 /* Helper method to display the active choice of installation directory
1465 * within the preferences configuration dialogue.
1468 if( (viewport = GetDlgItem( dialogue, ID_SETUP_CURDIR )) != NULL )
1469 SendMessageW( viewport, WM_SETTEXT, 0, (LPARAM)(approot_path()) );
1472 inline int SetupTool::SetInstallationPreferences( HWND AppWindow )
1474 /* Helper method to display a dialogue box, offering the user
1475 * a choice of installation preferences.
1477 return DialogBox( NULL, MAKEINTRESOURCE( IDD_SETUP_OPTIONS ), AppWindow,
1478 ConfigureInstallationPreferences
1482 inline int SetupTool::InstallationRequest( HWND AppWindow )
1484 /* Method to process requests to proceed with installation, on user
1485 * demand from the opening dialogue box.
1487 * First step is to garner installation preferences from the user.
1490 do { if( (request = SetInstallationPreferences( AppWindow )) == IDOK )
1492 /* When the user is satisfied that all options have been
1493 * appropriately specified, apply them.
1495 DoFirstTimeSetup( AppWindow );
1496 const WTK::StringResource description( NULL, ID_MAIN_WINDOW_CAPTION );
1497 if( IsPref( SETUP_OPTION_WITH_GUI ) )
1498 for( int i = 2; i < 6; i++ )
1500 /* When the user has requested the creation of program
1501 * "shortcuts", create them as appropriate.
1503 if( (IsPref( i ) | IsPref( SETUP_OPTION_ALL_USERS )) == i )
1504 CreateApplicationLauncher( i, gui_program, description,
1505 (i & SETUP_OPTION_DESKTOP) ? "MinGW Installer" : description
1509 /* Continue collecting preference settings, until the user elects to
1510 * invoke the "continue" action.
1512 } while( request == IDRETRY );
1516 int CALLBACK SetupTool::OpeningDialogue
1517 ( HWND dlg, unsigned int msg, WPARAM request, LPARAM )
1519 /* Procedure to manage the initial dialogue box, which serves as
1520 * the main window for the setup application.
1523 { case WM_INITDIALOG:
1524 /* Invoked when the dialogue box is first displayed; centre it
1525 * on-screen, and assign an appropriate caption.
1527 WTK::AlignWindow( dlg, WTK_ALIGN_CENTRED | WTK_ALIGN_ONSCREEN );
1528 ChangeCaption( dlg, WTK::StringResource( NULL, ID_MAIN_DIALOGUE_CAPTION ) );
1532 /* Handle requests from user interaction with the controls which
1533 * are provided within the dialogue box.
1535 switch( msg = LOWORD( request ) )
1538 /* User has elected to proceed with installation; inhibit
1539 * further control interaction, and process the request.
1541 EnableWindow( GetDlgItem( dlg, IDOK ), FALSE );
1542 EnableWindow( GetDlgItem( dlg, IDCANCEL ), FALSE );
1543 while( Invoke()->InstallationRequest( dlg ) == ID_SETUP_SHOW_LICENCE )
1545 * During installation, the user may request to view
1546 * the product licence; honour such requests.
1548 BrowseLicenceDocument( dlg );
1551 /* Invoked immediately, when the user elects to cancel the
1552 * installation, otherwise by fall through on completion of
1553 * a request to proceed; this closes the top-level dialogue,
1554 * so terminating the application/
1556 EndDialog( dlg, msg );
1559 case ID_SETUP_SHOW_LICENCE:
1560 /* This represents a confirmation that the user would like
1561 * to view the licence document for the package.
1563 BrowseLicenceDocument( dlg );
1567 /* Other messages to the opening dialogue box may be safely ignored.
1572 pkgSetupAction::pkgSetupAction
1573 ( pkgSetupAction *queue, const char *pkg, const char *ext ):
1574 flags( ACTION_DOWNLOAD + ACTION_INSTALL ), prev( queue ), next( NULL )
1576 /* Constructor for a setup action item; having initialised the
1577 * flags, and the link reference to the first entry, if any, in
1578 * the actions list...
1582 /* ...it constructs the name of the package with which the action
1583 * is to be associated, qualified by the specified extension, and
1584 * copies it to the heap...
1586 char ref[1 + snprintf( NULL, 0, pkg, ext )]; sprintf( ref, pkg, ext );
1587 package_name = strdup( ref );
1590 /* ...or, when no qualifying extension is specified, it simply
1591 * stores the unqualified package name on the heap...
1593 package_name = strdup( pkg );
1597 /* ...before ultimately advancing the reference pointer, to
1598 * link this item to the end of the actions list.
1600 while( prev->next != NULL )
1606 pkgSetupAction::~pkgSetupAction()
1608 /* Destructor for a single setup action item; it frees the heap
1609 * memory which was originally allocated to store the name of the
1610 * package with which the action was associated, and detaches the
1611 * item from the actions list, before it is deleted.
1613 free( (void *)(package_name) );
1614 if( prev != NULL ) prev->next = next;
1615 if( next != NULL ) next->prev = prev;
1618 int APIENTRY WinMain
1619 ( HINSTANCE Instance, HINSTANCE PrevInstance, char *CmdLine, int ShowMode )
1621 /* The program entry point for the mingw-get setup tool.
1624 { /* We allow only one instance of mingw-get to run at any time,
1625 * so first, we check to ensure that there is no other instance
1628 if( ! WTK::RaiseAppWindow( Instance, ID_MAIN_WINDOW_CLASS ) )
1630 * There is no running mingw-get instance; we may tentatively
1631 * continue with the setup procedure.
1633 SetupTool Install( Instance );
1635 /* In any event, we return the setup tool status, as
1636 * the program exit code.
1638 return SetupTool::Status;
1640 catch( dmh_exception &e )
1642 /* Here, we handle any fatal exception which has been raised
1643 * and identified by the diagnostic message handler...
1645 MessageBox( NULL, e.what(), "WinMain", MB_ICONERROR );
1646 return EXIT_FAILURE;
1648 catch( WTK::runtime_error &e )
1650 /* ...while here, we diagnose any other error which was captured
1651 * during the creation of the application's window hierarchy, or
1652 * processing of its message loop...
1654 MessageBox( NULL, e.what(), "WinMain", MB_ICONERROR );
1655 return EXIT_FAILURE;
1658 { /* ...and here, we diagnose any other error which we weren't
1659 * able to explicitly identify.
1661 MessageBox( NULL, "Unknown exception", "WinMain", MB_ICONERROR );
1662 return EXIT_FAILURE;
1666 /* $RCSfile$: end of file */