#include "pkgbase.h"
#include "pkgdata.h"
#include "pkgkeys.h"
+#include "pkginfo.h"
#include "pkglist.h"
#include "pkgtask.h"
*/
for( int item = IDM_PACKAGE_UNMARK; item <= IDM_PACKAGE_REMOVE; item++ )
{
- /* ...evaluating an independent state flag for each...
+ /* ...evaluating an independent state flag for each,
+ * setting it as non-zero for menu items which may be
+ * made "selectable", or zero otherwise...
*/
int state_flag = state;
switch( item )
* the currently selected list view item...
*/
case IDM_PACKAGE_INSTALL:
+ /*
+ * "Mark for Installation" is available for packages
+ * which exist in the repository, (long-term or new),
+ * but which are not yet identified as "installed".
+ */
state_flag &= PKGSTATE_FLAG( AVAILABLE )
| PKGSTATE_FLAG( AVAILABLE_NEW );
break;
case IDM_PACKAGE_REMOVE:
case IDM_PACKAGE_REINSTALL:
+ /*
+ * "Mark for Removal" and "Mark for Reinstallation"
+ * are viable selections only for packages identified
+ * as "installed", (current or upgradeable).
+ */
state_flag &= PKGSTATE_FLAG( INSTALLED_CURRENT )
| PKGSTATE_FLAG( INSTALLED_OLD );
break;
case IDM_PACKAGE_UPGRADE:
+ /*
+ * "Mark for Upgrade" is viable only for packages
+ * identified as "installed", and then only when an
+ * upgrade has been published.
+ */
state_flag &= PKGSTATE_FLAG( INSTALLED_OLD );
break;
case IDM_PACKAGE_UNMARK:
+ /*
+ * The "Unmark" facility is available only for packages
+ * which have been marked, (perhaps inadvertently), for
+ * any of the preceding actions.
+ */
state_flag &= PKGSTATE_FLAG( AVAILABLE_INSTALL )
| PKGSTATE_FLAG( UPGRADE ) | PKGSTATE_FLAG( DOWNGRADE )
| PKGSTATE_FLAG( REMOVE ) | PKGSTATE_FLAG( PURGE )
break;
default:
+ /* When none of the preceding is applicable, the menu
+ * item should not be selectable.
+ */
state_flag = 0;
}
/* ...and set the menu item enabled state accordingly.
}
}
+EXTERN_C void pkgMarkSchedule( HWND pkglist, pkgActionItem *actions )
+{
+ /* Helper routine to update the status icons within the package
+ * list view, to reflect scheduled actions in respect of the package
+ * associated with each.
+ */
+ LVITEM lookup;
+ lookup.iItem = 0;
+ lookup.iSubItem = 0;
+ lookup.mask = LVIF_IMAGE | LVIF_PARAM;
+ while( ListView_GetItem( pkglist, &lookup ) )
+ {
+ /* Traverse the displayed list of packages from top to bottom...
+ */
+ unsigned long op;
+ pkgActionItem *ref = actions->GetReference( (pkgXmlNode *)(lookup.lParam));
+ if( ref != NULL )
+ {
+ if( (op = ref->HasAttribute( ACTION_MASK )) != 0UL )
+ {
+ /* ...identifying any action scheduled on each package,
+ * and selecting the appropriate list view icon...
+ */
+ switch( op )
+ {
+ case ACTION_INSTALL:
+ /*
+ * ...for packages scheduled for installation...
+ *
+ * FIXME: we should also consider that such packages
+ * may have been scheduled for reinstallation.
+ */
+ lookup.iImage = PKGSTATE( AVAILABLE_INSTALL );
+ break;
+
+ case ACTION_UPGRADE:
+ /*
+ * ...for packages scheduled for upgrade...
+ */
+ lookup.iImage = PKGSTATE( UPGRADE );
+ break;
+
+ case ACTION_REMOVE:
+ /*
+ * ...for packages scheduled for removal.
+ */
+ lookup.iImage = PKGSTATE( REMOVE );
+ }
+ }
+ else
+ { /* A previously scheduled action has been cancelled.
+ */
+ pkgActionItem avail;
+ pkgXmlNode *rel = ref->Selection();
+ if( rel == NULL ) rel = ref->Selection( to_remove );
+ rel = rel->GetParent()->FindFirstAssociate( release_key );
+ while( rel != NULL )
+ {
+ /* Examine each available release specification for the nominated
+ * package; select the one which represents the latest (most recent)
+ * available release.
+ */
+ avail.SelectIfMostRecentFit( rel );
+
+ /* Also check for the presence of an installation record for each
+ * release; if found, mark it as the currently installed release;
+ * (we assign the "to-remove" attribute, but we don't action it).
+ */
+ if( rel->GetInstallationRecord( rel->GetPropVal( tarname_key, NULL )) != NULL )
+ avail.SelectPackage( rel, to_remove );
+
+ /* Cycle, until all known releases have been examined.
+ */
+ rel = rel->FindNextAssociate( release_key );
+ }
+ avail.ConfirmInstallationStatus();
+ if( (rel = avail.Selection( to_remove )) == NULL )
+ lookup.iImage = PKGSTATE( AVAILABLE );
+ else
+ {
+ pkgSpecs current( rel );
+ pkgSpecs latest( avail.Selection() );
+ lookup.iImage = (latest > current) ? PKGSTATE( INSTALLED_OLD )
+ : PKGSTATE( INSTALLED_CURRENT );
+ }
+ }
+ /* Apply new icon selection...
+ */
+ ListView_SetItem( pkglist, &lookup );
+ }
+ /* ...and move on to the next list view entry.
+ */
+ lookup.iItem++;
+ }
+}
+
+void AppWindowMaker::Schedule
+( unsigned long action, const char *bounds, const char *pkgname )
+{
+ /* GUI menu driven interface to the pkgActionItem task scheduler;
+ * it constructs a pseudo-argument string, emulating the effect of
+ * parsing a CLI argument, then passes this to the CLI scheduler
+ * API class method.
+ */
+ if( pkgname == NULL )
+ {
+ /* Initial entry on menu item selection; package name has not
+ * yet been identified, so find the selected list view item...
+ */
+ LVITEM lookup;
+ lookup.iItem = (PackageListView != NULL)
+ ? ListView_GetNextItem( PackageListView, (WPARAM)(-1), LVIS_SELECTED )
+ : -1;
+
+ /* ...and look up the package name identified within it.
+ */
+ const char *pkg, *fmt = "%s-%s";
+ pkgXmlNode *ref = pkgListSelection( PackageListView, &lookup );
+ if( (pkg = ref->GetContainerAttribute( name_key, NULL )) != NULL )
+ {
+ /* We now have a valid package name; check for a
+ * component package association.
+ */
+ const char *cpt;
+ if( (cpt = ref->GetPropVal( class_key, NULL )) == NULL )
+ {
+ /* Current list view selection represents a
+ * non-component package; encode its name only
+ * as a string argument, using only the final
+ * string field of the format specification.
+ */
+ char pkgspec[ 1 + snprintf( NULL, 0, fmt + 3, pkg ) ];
+ snprintf( pkgspec, sizeof( pkgspec ), fmt + 3, pkg );
+
+ /* Recurse, to capture any supplied version bounds
+ * specification, and ultimately schedule the action.
+ */
+ Schedule( action, bounds, pkgspec );
+ }
+ else
+ { /* Current list view selection represents a
+ * package name qualified by a component name;
+ * use the full format specification to encode
+ * the fully qualified package name.
+ */
+ char pkgspec[ 1 + snprintf( NULL, 0, fmt, pkg, cpt ) ];
+ snprintf( pkgspec, sizeof( pkgspec ), fmt, pkg, cpt );
+
+ /* Again, recurse to capture any supplied version
+ * bounds specification, before ultimately scheduling
+ * the selected action.
+ */
+ Schedule( action, bounds, pkgspec );
+ }
+ }
+ }
+ else if( bounds != NULL )
+ {
+ /* Recursive entry, after package name identification,
+ * but with supplied version bounds specification yet
+ * to be resolved; append the bounds specification to
+ * the package name, as it would be in a CLI argument...
+ */
+ const char *fmt = "%s=%s";
+ char pkgspec[ 1 + snprintf( NULL, 0, fmt, pkgname, bounds ) ];
+ snprintf( pkgspec, sizeof( pkgspec ), fmt, pkgname, bounds );
+ /*
+ * ...then recurse a final time, to schedule the action.
+ */
+ Schedule( action, NULL, pkgspec );
+ }
+ else
+ { /* Final recursive entry, with pkgname argument in the
+ * same form as a CLI package name/bounds specification
+ * argument; hand it off to the CLI scheduler, capturing
+ * the resultant schedule of actions, and update the list
+ * view state icons to reflect the pending actions.
+ */
+ pkgMarkSchedule( PackageListView, pkgData->Schedule( action, pkgname ) );
+ UpdatePackageMenuBindings();
+ }
+}
+
+inline unsigned long pkgActionItem::CancelScheduledAction( void )
+{
+ /* Helper method to mark a scheduled action as "cancelled".
+ */
+ return (this != NULL) ? (flags &= ~ACTION_MASK) : 0UL;
+}
+
+void AppWindowMaker::UnmarkSelectedPackage( void )
+{
+ /* Method to clear any request for an action in respect of
+ * the currently selected package entry in the list view; we
+ * implement this as a cancellation of any pending scheduled
+ * action, in respect of the selected package.
+ *
+ * First, obtain a reference for the list view selection...
+ */
+ LVITEM lookup;
+ lookup.iItem = (PackageListView != NULL)
+ ? ListView_GetNextItem( PackageListView, (WPARAM)(-1), LVIS_SELECTED )
+ : -1;
+
+ /* ...and when it represents a valid selection...
+ */
+ if( lookup.iItem >= 0 )
+ {
+ /* ...retrieve its associated XML database package reference...
+ */
+ pkgXmlNode *pkg = pkgListSelection( PackageListView, &lookup );
+ /*
+ * ...search the action schedule, for an action associated with
+ * this package, if any, and cancel it.
+ */
+ pkgData->Schedule()->GetReference( pkg )->CancelScheduledAction();
+
+ /* The scheduling state for packages shown in the list view
+ * may have changed, so refresh the icon associations and the
+ * package menu bindings accordingly.
+ */
+ pkgMarkSchedule( PackageListView, pkgData->Schedule() );
+ UpdatePackageMenuBindings();
+ }
+}
+
long AppWindowMaker::OnNotify( WPARAM client_id, LPARAM data )
{
/* Handler for notifiable events to be processed in the context
return EXIT_SUCCESS;
}
+inline unsigned long pkgActionItem::Unapplied( void )
+{
+ /* Helper method to count the pending actions in a
+ * scheduled action list.
+ */
+ unsigned long count = 0;
+ if( this != NULL )
+ {
+ /* Assuming that the initial 'this' pointer is closer
+ * to the end of the list, than to the beginning...
+ */
+ pkgActionItem *item = this;
+ while( item->next != NULL )
+ /*
+ * ...advance, to ensure we have located the very
+ * last item in the schedule.
+ */
+ item = item->next;
+
+ /* Now, working back from end to beginning...
+ */
+ while( item != NULL )
+ {
+ /* ...note items with any scheduled action...
+ */
+ if( item->flags & ACTION_MASK )
+ /*
+ * ...and count them...
+ */
+ ++count;
+
+ /* ...then move on, to consider the preceding
+ * entry, if any.
+ */
+ item = item->prev;
+ }
+ }
+ /* Ultimately, return the count of pending actions,
+ * as noted while processing the above loop.
+ */
+ return count;
+}
+
+long AppWindowMaker::OnClose()
+{
+ /* Intercept application termination requests; check for
+ * outstanding pending actions, and offer a cancellation
+ * option for the termination request, so that the user
+ * has an opportunity to complete such actions.
+ */
+ if( (pkgData->Schedule()->Unapplied() > 0)
+ && (MessageBox( AppWindow,
+ "You have marked changes which have not been applied;\n"
+ "these will be lost, if you quit without applying them.\n\n"
+ "Are you sure you want to discard these marked changes?",
+ "Discard Marked Changes?", MB_YESNO | MB_ICONWARNING
+ ) == IDNO)
+ ) return 0;
+ return -1;
+}
+
/* $RCSfile$: end of file */
}
}
+pkgActionItem *pkgActionItem::GetReference( pkgXmlNode *package )
+{
+ /* Method to locate a scheduled action, if any, which relates
+ * to a specified package.
+ */
+ if( this != NULL )
+ {
+ /* The schedule of actions is not empty. Assume that the
+ * initial 'this' pointer is closer to the end, than to the
+ * beginning of the list of scheduled actions, and...
+ */
+ pkgActionItem *item = this;
+ while( item->next != NULL )
+ /*
+ * ...advance, to locate the very last entry in the list.
+ */
+ item = item->next;
+
+ /* Now, working backward toward the beginning of the list...
+ */
+ while( item != NULL )
+ {
+ /* ...identify a "release" specification associated with
+ * each action item in turn...
+ */
+ pkgXmlNode *ref = item->Selection();
+ if( (ref != NULL) || ((ref = item->Selection( to_remove )) != NULL) )
+ {
+ /* ...convert this to an actual component package, or
+ * full package, reference...
+ */
+ while( ref->IsElementOfType( release_key ) )
+ ref = ref->GetParent();
+
+ /* ...and, if it matches the search target, return it.
+ */
+ if( ref == package ) return item;
+ }
+ /* ...or, when we haven't yet found a matching package,
+ * try the preceding scheduled action item, if any.
+ */
+ item = item->prev;
+ }
+ }
+ /* If we fall through to here, then we found no action to be
+ * performed on the specified package; report accordingly.
+ */
+ return NULL;
+}
+
static void dmh_notify_no_match
( const char *name, pkgXmlNode *package, const char *bounds )
{
dmh_control( DMH_END_DIGEST );
}
-void pkgXmlDocument::Schedule( unsigned long action, const char* name )
+pkgActionItem* pkgXmlDocument::Schedule( unsigned long action, const char* name )
{
/* Task scheduler interface; schedules actions to process all
* dependencies for the package specified by "name", honouring
* any appended version bounds specified for the parent.
*/
- char scratch_pad[strlen( name )];
- const char *bounds_specification = get_version_bounds( name );
- if( bounds_specification != NULL )
- {
- /* Separate any version bounds specification from
- * the original package name specification.
+ if( this == NULL )
+ /*
+ * An unassigned XML database document cannot have any
+ * assigned action; bail out, with appropriate status.
*/
- size_t scratch_pad_len = bounds_specification - name;
- name = (const char *)(memcpy( scratch_pad, name, scratch_pad_len ));
- scratch_pad[scratch_pad_len] = '\0';
- }
+ return NULL;
- pkgXmlNode *release;
- if( (release = FindPackageByName( name )) != NULL )
+ /* We may call this method without any assigned package name,
+ * in which case, we interpret it as a request to return the
+ * list of previously scheduled actions...
+ */
+ if( name != NULL )
{
- /* We found the specification for the named package...
+ /* ...but when a package name is specified, then we
+ * proceed to schedule the specified action on it.
*/
- pkgXmlNode *component = release->FindFirstAssociate( component_key );
- if( component != NULL )
- /*
- * When it is subdivided into component-packages,
- * we need to consider each as a possible candidate
- * for task scheduling.
+ char scratch_pad[strlen( name )];
+ const char *bounds_specification = get_version_bounds( name );
+ if( bounds_specification != NULL )
+ {
+ /* Separate any version bounds specification from
+ * the original package name specification.
*/
- release = component;
+ size_t scratch_pad_len = bounds_specification - name;
+ name = (const char *)(memcpy( scratch_pad, name, scratch_pad_len ));
+ scratch_pad[scratch_pad_len] = '\0';
+ }
- while( release != NULL )
+ pkgXmlNode *release;
+ if( (release = FindPackageByName( name )) != NULL )
{
- /* Within each candidate package or component-package...
+ /* We found the specification for the named package...
*/
- pkgXmlNode *package = release;
- if( (release = release->FindFirstAssociate( release_key )) != NULL )
- {
- /* ...initially assume it is not installed, and that
- * no installable upgrade is available.
- */
- pkgActionItem latest;
- pkgXmlNode *installed = NULL, *upgrade = NULL;
-
- /* Establish the action for which dependency resolution is
- * to be performed; note that this may be promoted to a more
- * inclusive class, during resolution, so we need to reset
- * it for each new dependency which may be encountered.
- */
- request = action;
-
- /* Any action request processed here is, by definition,
- * a request for a primary action; mark it as such.
- */
- action |= ACTION_PRIMARY;
-
- /* When the user has given a version bounds specification,
- * then we must assign appropriate action item requirements.
+ pkgXmlNode *component = release->FindFirstAssociate( component_key );
+ if( component != NULL )
+ /*
+ * When it is subdivided into component-packages,
+ * we need to consider each as a possible candidate
+ * for task scheduling.
*/
- if( bounds_specification != NULL )
- latest.ApplyBounds( release, bounds_specification );
+ release = component;
- /* For each candidate release in turn...
+ while( release != NULL )
+ {
+ /* Within each candidate package or component-package...
*/
- while( release != NULL )
+ pkgXmlNode *package = release;
+ if( (release = release->FindFirstAssociate( release_key )) != NULL )
{
- /* ...inspect it to identify any which is already installed,
- * and also the latest available...
+ /* ...initially assume it is not installed, and that
+ * no installable upgrade is available.
*/
- if( is_installed( release ) )
- /*
- * ...i.e. here we have identified a release
- * which is currently installed...
- */
- latest.SelectPackage( installed = release, to_remove );
+ pkgActionItem latest;
+ pkgXmlNode *installed = NULL, *upgrade = NULL;
- if( latest.SelectIfMostRecentFit( release ) == release )
- /*
- * ...while this is the most recent we have
- * encountered so far.
- */
- upgrade = release;
+ /* Establish the action for which dependency resolution is
+ * to be performed; note that this may be promoted to a more
+ * inclusive class, during resolution, so we need to reset
+ * it for each new dependency which may be encountered.
+ */
+ request = action;
- /* Continue with the next specified release, if any.
+ /* Any action request processed here is, by definition,
+ * a request for a primary action; mark it as such.
*/
- release = release->FindNextAssociate( release_key );
- }
+ action |= ACTION_PRIMARY;
- if( (installed = assert_installed( upgrade, installed )) == NULL )
- {
- /* There is no installed version...
- * therefore, there is nothing to do for any action
- * other than ACTION_INSTALL...
+ /* When the user has given a version bounds specification,
+ * then we must assign appropriate action item requirements.
*/
- if( (action & ACTION_MASK) == ACTION_INSTALL )
+ if( bounds_specification != NULL )
+ latest.ApplyBounds( release, bounds_specification );
+
+ /* For each candidate release in turn...
+ */
+ while( release != NULL )
{
- /*
- * ...in which case, we must recursively resolve
- * any dependencies for the scheduled "upgrade".
- */
- if( latest.Selection() == NULL )
- dmh_notify_no_match( name, package, bounds_specification );
- else
- ResolveDependencies(
- upgrade, Schedule( with_download( action ), latest )
- );
- }
- else
- { /* attempting ACTION_UPGRADE or ACTION_REMOVE
- * is an error; diagnose it.
+ /* ...inspect it to identify any which is already installed,
+ * and also the latest available...
*/
- if( component == NULL )
+ if( is_installed( release ) )
/*
- * In this case, the user explicitly specified a single
- * package component, so it's a simple error...
+ * ...i.e. here we have identified a release
+ * which is currently installed...
*/
- dmh_notify( DMH_ERROR, "%s %s: package is not installed\n",
- action_name( action & ACTION_MASK ), name
- );
- else
- {
- /* ...but here, the user specified only the package name,
- * which implicitly applies to all associated components;
- * since some may be installed, prefer to issue a warning
- * in respect of any which aren't.
+ latest.SelectPackage( installed = release, to_remove );
+
+ if( latest.SelectIfMostRecentFit( release ) == release )
+ /*
+ * ...while this is the most recent we have
+ * encountered so far.
*/
- const char *extname = component->GetPropVal( class_key, "" );
- char full_package_name[2 + strlen( name ) + strlen( extname )];
- sprintf( full_package_name, *extname ? "%s-%s" : "%s", name, extname );
+ upgrade = release;
- dmh_control( DMH_BEGIN_DIGEST );
- dmh_notify( DMH_WARNING, "%s %s: request ignored...\n",
- extname = action_name( action & ACTION_MASK ), full_package_name
- );
- dmh_notify( DMH_WARNING, "%s: package was not previously installed\n",
- full_package_name
- );
- dmh_notify( DMH_WARNING, "%s: it will remain this way until you...\n",
- full_package_name
- );
- dmh_notify( DMH_WARNING, "use 'mingw-get install %s' to install it\n",
- full_package_name
- );
- dmh_control( DMH_END_DIGEST );
- }
- }
- }
- else if( upgrade && (upgrade != installed) )
- {
- /* There is an installed version, but an upgrade to a newer
- * version is available; when performing ACTION_UPGRADE...
- */
- if( (action & ACTION_MASK) == ACTION_UPGRADE )
- /*
- * ...we must recursively resolve any dependencies...
+ /* Continue with the next specified release, if any.
*/
- ResolveDependencies( upgrade,
- Schedule( with_download( action ), latest )
- );
+ release = release->FindNextAssociate( release_key );
+ }
- else if( (action & ACTION_MASK) == ACTION_REMOVE )
+ if( (installed = assert_installed( upgrade, installed )) == NULL )
{
- /* ...while for ACTION_REMOVE, we have little to do,
- * beyond scheduling the removal; (we don't extend the
- * scope of a remove request to prerequisite packages,
- * so there is no need to resolve dependencies)...
- */
- latest.SelectPackage( installed );
- Schedule( action, latest );
- }
- else
- { /* ...but, we decline to proceed with ACTION_INSTALL
- * unless the --reinstall option is enabled...
+ /* There is no installed version...
+ * therefore, there is nothing to do for any action
+ * other than ACTION_INSTALL...
*/
- if( pkgOptions()->Test( OPTION_REINSTALL ) )
+ if( (action & ACTION_MASK) == ACTION_INSTALL )
{
- /* ...in which case, we resolve dependencies for,
- * and reschedule a reinstallation of the currently
- * installed version...
+ /*
+ * ...in which case, we must recursively resolve
+ * any dependencies for the scheduled "upgrade".
*/
- latest.SelectPackage( installed );
- ResolveDependencies( installed,
- Schedule( with_download( action | ACTION_REMOVE ), latest )
- );
+ if( latest.Selection() == NULL )
+ dmh_notify_no_match( name, package, bounds_specification );
+ else
+ ResolveDependencies(
+ upgrade, Schedule( with_download( action ), latest )
+ );
}
else
- { /* ...otherwise, we reformulate the appropriate
- * fully qualified package name...
+ { /* attempting ACTION_UPGRADE or ACTION_REMOVE
+ * is an error; diagnose it.
*/
- const char *extname = ( component != NULL )
- ? component->GetPropVal( class_key, "" )
- : "";
- char full_package_name[2 + strlen( name ) + strlen( extname )];
- sprintf( full_package_name, *extname ? "%s-%s" : "%s", name, extname );
+ if( component == NULL )
+ /*
+ * In this case, the user explicitly specified a single
+ * package component, so it's a simple error...
+ */
+ dmh_notify( DMH_ERROR, "%s %s: package is not installed\n",
+ action_name( action & ACTION_MASK ), name
+ );
+ else
+ {
+ /* ...but here, the user specified only the package name,
+ * which implicitly applies to all associated components;
+ * since some may be installed, prefer to issue a warning
+ * in respect of any which aren't.
+ */
+ const char *extname = component->GetPropVal( class_key, "" );
+ char full_package_name[2 + strlen( name ) + strlen( extname )];
+ sprintf( full_package_name, *extname ? "%s-%s" : "%s", name, extname );
+
+ dmh_control( DMH_BEGIN_DIGEST );
+ dmh_notify( DMH_WARNING, "%s %s: request ignored...\n",
+ extname = action_name( action & ACTION_MASK ), full_package_name
+ );
+ dmh_notify( DMH_WARNING, "%s: package was not previously installed\n",
+ full_package_name
+ );
+ dmh_notify( DMH_WARNING, "%s: it will remain this way until you...\n",
+ full_package_name
+ );
+ dmh_notify( DMH_WARNING, "use 'mingw-get install %s' to install it\n",
+ full_package_name
+ );
+ dmh_control( DMH_END_DIGEST );
+ }
+ }
+ }
+ else if( upgrade && (upgrade != installed) )
+ {
+ /* There is an installed version, but an upgrade to a newer
+ * version is available; when performing ACTION_UPGRADE...
+ */
+ if( (action & ACTION_MASK) == ACTION_UPGRADE )
/*
- * ...which we then incorporate into an advisory
- * diagnostic message, which serves both to inform
- * the user of this error condition, and also to
- * suggest appropriate corrective action.
+ * ...we must recursively resolve any dependencies...
*/
- dmh_control( DMH_BEGIN_DIGEST );
- dmh_notify( DMH_ERROR, "%s: package is already installed\n",
- full_package_name
- );
- dmh_notify( DMH_ERROR, "use 'mingw-get upgrade %s' to upgrade it\n",
- full_package_name
- );
- dmh_notify( DMH_ERROR, "or 'mingw-get install --reinstall %s'\n",
- full_package_name
- );
- dmh_notify( DMH_ERROR, "to reinstall the currently installed version\n"
+ ResolveDependencies( upgrade,
+ Schedule( with_download( action ), latest )
);
- dmh_control( DMH_END_DIGEST );
+
+ else if( (action & ACTION_MASK) == ACTION_REMOVE )
+ {
+ /* ...while for ACTION_REMOVE, we have little to do,
+ * beyond scheduling the removal; (we don't extend the
+ * scope of a remove request to prerequisite packages,
+ * so there is no need to resolve dependencies)...
+ */
+ latest.SelectPackage( installed );
+ Schedule( action, latest );
+ }
+ else
+ { /* ...but, we decline to proceed with ACTION_INSTALL
+ * unless the --reinstall option is enabled...
+ */
+ if( pkgOptions()->Test( OPTION_REINSTALL ) )
+ {
+ /* ...in which case, we resolve dependencies for,
+ * and reschedule a reinstallation of the currently
+ * installed version...
+ */
+ latest.SelectPackage( installed );
+ ResolveDependencies( installed,
+ Schedule( with_download( action | ACTION_REMOVE ), latest )
+ );
+ }
+ else
+ { /* ...otherwise, we reformulate the appropriate
+ * fully qualified package name...
+ */
+ const char *extname = ( component != NULL )
+ ? component->GetPropVal( class_key, "" )
+ : "";
+ char full_package_name[2 + strlen( name ) + strlen( extname )];
+ sprintf( full_package_name, *extname ? "%s-%s" : "%s", name, extname );
+ /*
+ * ...which we then incorporate into an advisory
+ * diagnostic message, which serves both to inform
+ * the user of this error condition, and also to
+ * suggest appropriate corrective action.
+ */
+ dmh_control( DMH_BEGIN_DIGEST );
+ dmh_notify( DMH_ERROR, "%s: package is already installed\n",
+ full_package_name
+ );
+ dmh_notify( DMH_ERROR, "use 'mingw-get upgrade %s' to upgrade it\n",
+ full_package_name
+ );
+ dmh_notify( DMH_ERROR, "or 'mingw-get install --reinstall %s'\n",
+ full_package_name
+ );
+ dmh_notify( DMH_ERROR, "to reinstall the currently installed version\n"
+ );
+ dmh_control( DMH_END_DIGEST );
+ }
}
}
- }
- else
- { /* In this case, the package is already installed,
- * and no more recent release is available; we still
- * recursively resolve its dependencies, to capture
- * any potential upgrades for them.
- */
- if( latest.Selection() == NULL )
- dmh_notify_no_match( name, package, bounds_specification );
else
- ResolveDependencies( upgrade, Schedule( action, latest ));
+ { /* In this case, the package is already installed,
+ * and no more recent release is available; we still
+ * recursively resolve its dependencies, to capture
+ * any potential upgrades for them.
+ */
+ if( latest.Selection() == NULL )
+ dmh_notify_no_match( name, package, bounds_specification );
+ else
+ ResolveDependencies( upgrade, Schedule( action, latest ));
+ }
}
- }
- if( (component = component->FindNextAssociate( component_key )) != NULL )
- /*
- * When evaluating a component-package, we extend our
- * evaluation, to consider for any further components of
- * the current package.
- */
- release = component;
+ if( (component = component->FindNextAssociate( component_key )) != NULL )
+ /*
+ * When evaluating a component-package, we extend our
+ * evaluation, to consider for any further components of
+ * the current package.
+ */
+ release = component;
+ }
}
- }
- else
- /* We found no information on the requested package;
- * diagnose as a non-fatal error.
- */
- dmh_notify( DMH_ERROR, pkgMsgUnknownPackage(), name );
+ else
+ /* We found no information on the requested package;
+ * diagnose as a non-fatal error.
+ */
+ dmh_notify( DMH_ERROR, pkgMsgUnknownPackage(), name );
+ }
+ /* Finally, we return a pointer to the currently scheduled
+ * actions list, if any.
+ */
+ return actions;
}
void pkgXmlDocument::RescheduleInstalledPackages( unsigned long action )