diff --git a/libmport/install_primative.c b/libmport/install_primative.c index f63e674..b248184 100644 --- a/libmport/install_primative.c +++ b/libmport/install_primative.c @@ -37,6 +37,9 @@ static char **get_dependencies(mportInstance *mport, mportPackageMeta *pack); static char *find_file_with_prefix(const char *dir, const char *prefix); +static /*@null@*/ /*@only@*/ mportPackageMeta **lookup_current_os_installed( + /*@notnull@*/ mportInstance *, /*@notnull@*/ mportPackageMeta *, + /*@out@*/ mportPackageMeta **); #define GOTO_CLEANUP_ON_MPORT_ERR(expr) \ do { \ @@ -144,12 +147,60 @@ find_file_with_prefix(const char *dir, const char *prefix) return found_file; } +/* + * Return the installed package(s) matching pkg->name, and set *match to the + * single entry whose os_release equals the running system's. The package name + * is not unique in the master DB (the same name can be installed under a + * different os_release), so scan every returned row rather than assuming the + * current-OS entry is first. Returns NULL (and sets *match to NULL) when the + * package is not installed under the current OS release; the caller owns the + * returned vector, and *match is an observer into it. + */ +static mportPackageMeta ** +lookup_current_os_installed(mportInstance *mport, mportPackageMeta *pkg, mportPackageMeta **match) +{ + mportPackageMeta **installed = NULL; + char *system_os_release; + int i; + + *match = NULL; + + if (mport_pkgmeta_search_master(mport, &installed, "pkg=%Q", pkg->name) != MPORT_OK) + return NULL; + if (installed == NULL) + return NULL; + + system_os_release = mport_get_osrelease(mport); + if (system_os_release == NULL) { + mport_pkgmeta_vec_free(installed); + return NULL; + } + + for (i = 0; installed[i] != NULL; i++) { + if (installed[i]->os_release != NULL && + strcmp(installed[i]->os_release, system_os_release) == 0) { + *match = installed[i]; + break; + } + } + + free(system_os_release); + + if (*match == NULL) { + mport_pkgmeta_vec_free(installed); + return NULL; + } + + return installed; +} + MPORT_PUBLIC_API int mport_install_primative( mportInstance *mport, const char *filename, const char *prefix, mportAutomatic automatic) { mportBundleRead *bundle = NULL; mportPackageMeta **already_installed = NULL; + mportPackageMeta *current_installed = NULL; mportPackageMeta **pkgs = NULL; mportPackageMeta *pkg = NULL; int i; @@ -184,19 +235,39 @@ mport_install_primative( goto cleanup; } - /* if we previously installed it and want to force, allow it. - In this case, automatic flag from previous install not honored - */ - if (mport_check_preconditions(mport, pkgs[0], MPORT_PRECHECK_INSTALLED) != - MPORT_OK) { - if (mport->force) { - mport_delete_primative(mport, pkgs[0], 1); + /* + * Only packages installed under the current OS release are treated + * as update/reinstall candidates here. A copy installed under a + * different os_release is left for the normal install path below, + * where MPORT_PRECHECK_INSTALLED considers a differing os_release to + * be a distinct package (matching pre-existing --force behavior). + */ + already_installed = lookup_current_os_installed(mport, pkgs[0], ¤t_installed); + if (current_installed != NULL) { + int version_cmp = + mport_version_cmp(current_installed->version, pkgs[0]->version); + + if (mport->force || version_cmp < 0) { + automatic = current_installed->automatic; + if (version_cmp < 0) { + mport_call_msg_cb(mport, "Updating %s from %s to %s.", + pkgs[0]->name, current_installed->version, + pkgs[0]->version); + } + if (mport_delete_primative(mport, current_installed, 1) != + MPORT_OK) { + ret = mport_err_code(); + goto cleanup; + } } else { mport_call_msg_cb(mport, "%s-%s: already installed.", pkgs[0]->name, pkgs[0]->version); ret = MPORT_OK; goto cleanup; } + + mport_pkgmeta_vec_free(already_installed); + already_installed = NULL; } if (mport_check_preconditions(mport, pkgs[0], MPORT_PRECHECK_CONFLICTS) != diff --git a/mport/mport.1 b/mport/mport.1 index e890845..4ce9413 100644 --- a/mport/mport.1 +++ b/mport/mport.1 @@ -247,6 +247,10 @@ package files. If .Fl A is specified, mark the installed package as automatic. +If the package is already installed and the local package file contains a newer +version, +.Nm +updates it by removing the installed package and installing the local file. For repository installs, use .Cm install . .It Cm annotate Op Fl a Op Fl q Fl S Ar package Ar tag