diff --git a/ALTERNATIVES b/ALTERNATIVES new file mode 100644 index 00000000..89d666f8 --- /dev/null +++ b/ALTERNATIVES @@ -0,0 +1,246 @@ +The XBPS alternatives framework +------------------------------- + +------------------------------------------------- +Package metadata stored in packages (props.plist) +------------------------------------------------- + +The 'nvi' pkg declares an alternative group named 'vi': + +... +alternatives + + vi + + /usr/bin/ex:/usr/bin/nvi + /usr/bin/vi:/usr/bin/nvi + /usr/share/man/man1/ex.1:/usr/share/man/man1/nvi.1 + /usr/share/man/man1/vi.1:/usr/share/man/man1/nvi.1 + + ... + +... + +The strings need to follow the : convention, delimited by +the ':' character. + +-------------------------------------------------------------------- +Package metadata stored in metadir (metadir/alternatives-0.48.plist) +-------------------------------------------------------------------- + +/var/db/xbps/alternatives-0.48.plist (dict): + +3 packages can provide the 'vi' alternative group: ex-vi, nvi or vim. +The entry order determines the priority (first entry always wins). + +After running `xbps-install -Sy nvi ex-vi vim' the order is the following: + +... + vi + + nvi + ex-vi + vim + + ... +... + +If the 'nvi' pkg is removed, and there's no alternative set for `vi', +the 'ex-vi' pkg now becomes the default alternative: + +... + vi + + ex-vi + vim + + ... +... + +The user now decides that 'vi' should be provided by the 'vim' pkg, +so that the matching entry is put into the head: + +... + vi + + vim + ex-vi + + ... +... + +When no packages provide an alternative group, the alternative group and its associated symlinks +will be completely removed. + + +-------------------- +xbps-alternatives(1) +-------------------- + +- Listing all available alternatives and current state: + + $ xbps-alternatives -l + libGL + - libGL (current) + - /usr/lib/libGL.so -> /usr/lib/libGL-mesa.so + - /usr/lib/libGL.so.1 -> /usr/lib/libGL-mesa.so.1 + - /usr/lib/xorg/modules/extensions/libglx.so -> /usr/lib/xorg/modules/extensions/libglx-xorg.so + - nvidia-libs + - /usr/lib/libGL.so -> /usr/lib/libGL-nvidia.so.1 + - /usr/lib/libGL.so.1 -> /usr/lib/libGL-nvidia.so.1 + - /usr/lib/xorg/modules/extensions/libglx.so -> /usr/lib/xorg/modules/extensions/libglx-nvidia.so + - catalyst-libs + - /usr/lib/libGL.so -> /usr/lib/libGL-fglrx.so.1 + - /usr/lib/libGL.so.1 -> /usr/lib/libGL-fglrx.so.1 + - /usr/lib/xorg/modules/extensions/libglx.so -> /usr/lib/xorg/modules/extensions/libglx-fglrx.so + ntpd + - openntpd (current) + - /etc/sv/ntpd -> /etc/sv/openntpd + - /usr/bin/ntpd -> /usr/bin/openntpd + - /usr/share/man/man1/ntpd.1 -> /usr/share/man/man1/openntpd.1 + - ntp + - /etc/sv/ntpd -> /etc/sv/isc-ntpd + - /usr/bin/ntpd -> /usr/bin/isc-ntpd + - /usr/share/man/man1/ntpd.1 -> /usr/share/man/man1/isc-ntpd.1 + - busybox + - /etc/sv/ntpd -> /etc/sv/busybox-ntpd + - /usr/bin/ntpd -> /usr/bin/busybox + sort + - coreutils (current) + - /usr/bin/sort -> /usr/bin/coreutils-sort + - /usr/share/man/man1/sort.1 -> /usr/share/man/man1/coreutils-sort.1 + - busybox + - /usr/bin/sort -> /usr/bin/busybox + vi + - nvi (current) + - /usr/bin/ex -> /usr/bin/nvi + - /usr/bin/vi -> /usr/bin/nvi + - /usr/share/man/man1/ex.1 -> /usr/share/man/man1/nvi.1 + - /usr/share/man/man1/vi.1 -> /usr/share/man/man1/nvi.1 + - vim + - /usr/bin/ex -> /usr/bin/vim-ex + - /usr/bin/vi -> /usr/bin/vim + - /usr/share/man/man1/ex.1 -> /usr/share/man/man1/vim-ex.1 + - /usr/share/man/man1/vi.1 -> /usr/share/man/man1/vim.1 + + ... + +- Listing available alternative groups of a package: + + $ xbps-alternatives -l busybox + ntpd + - /etc/sv/ntpd -> /etc/sv/busybox-ntpd + - /usr/bin/ntpd -> /usr/bin/busybox + sort + - /usr/bin/sort -> /usr/bin/busybox + ... + +- Apply all alternative groups specified by the 'nvi' package (declares one group: vi): + + $ xbps-alternatives -s nvi + Switched 'vi' alternatives group to 'nvi'. + Creating 'vi' alternatives group symlink: /usr/bin/ex -> /usr/bin/nvi + Creating 'vi' alternatives group symlink: /usr/bin/vi -> /usr/bin/nvi + Creating 'vi' alternatives group symlink: /usr/share/man/man1/ex.1 -> /usr/share/man/man1/nvi.1 + Creating 'vi' alternatives group symlink: /usr/share/man/man1/vi.1 -> /usr/share/man/man1/nvi.1 + $ + +- Creating a specific alternative group of a package containing multiple groups: + + $ xbps-alternatives -s busybox -g ntpd + Switched 'ntpd' alternatives group to 'busybox'. + Creating 'ntpd' alternatives group symlink: /etc/sv/ntpd -> /etc/sv/busybox-ntpd + Creating 'ntpd' alternatives group symlink: /usr/bin/ntpd -> /usr/bin/busybox + $ + +--------------- +xbps-install(1) +--------------- + +Installing a package with an alternatives group for the first time: + + $ xbps-install -Syv vim + Name Action Version New version Download size + vim install - 1.0_1 - + + Free space on disk: 49GB + + [*] Downloading binary packages + + [*] Verifying package integrity + vim-1.0_1: verifying SHA256 hash... + + [*] Running transaction tasks + vim-1.0_1: unpacking ... + vim-1.0_1: unpacked file `./usr/bin/vim' (0 bytes) + vim-1.0_1: unpacked file `./usr/share/man/man1/vim.1' (0 bytes) + vim-1.0_1: registered 'vi' alternatives group + Creating 'vi' alternatives group symlink: /usr/bin/vi -> /usr/bin/vim + Creating 'vi' alternatives group symlink: /usr/bin/ex -> /usr/bin/vim + Creating 'vi' alternatives group symlink: /usr/bin/view -> /usr/bin/vim + Creating 'vi' alternatives group symlink: /usr/share/man/man1/ex.1 -> /usr/share/man/man1/vim.1 + Creating 'vi' alternatives group symlink: /usr/share/man/man1/vi.1 -> /usr/share/man/man1/vim.1 + Creating 'vi' alternatives group symlink: /usr/share/man/man1/view.1 -> /usr/share/man/man1/vim.1 + + [*] Configuring unpacked packages + vim-1.0_1: configuring ... + vim-1.0_1: installed successfully. + + 0 downloaded, 1 installed, 0 updated, 1 configured, 0 removed. + $ + +Installing a package that provides a 'vi' alternatives group (nvi): + + $ xbps-install -Syv nvi + Name Action Version New version Download size + nvi install - 1.0_1 - + + Free space on disk: 49GB + + [*] Downloading binary packages + + [*] Verifying package integrity + nvi-1.0_1: verifying SHA256 hash... + + [*] Running transaction tasks + nvi-1.0_1: unpacking ... + nvi-1.0_1: unpacked file `./usr/bin/nvi' (0 bytes) + nvi-1.0_1: unpacked file `./usr/share/man/man1/nvi.1' (0 bytes) + nvi-1.0_1: registered 'vi' alternatives group + + [*] Configuring unpacked packages + nvi-1.0_1: configuring ... + nvi-1.0_1: installed successfully. + + 0 downloaded, 1 installed, 0 updated, 1 configured, 0 removed. + $ + +Removing a package that was the default provider of the 'vi' alternatives group (vim): + + $ xbps-remove -yv vim + Name Action Version New version Download size + vim remove 1.0_1 - - + + Free space on disk: 49GB + + Removing `vim-1.0_1' ... + Removing 'vi' alternatives group symlink: /usr/bin/vi + Removing 'vi' alternatives group symlink: /usr/bin/ex + Removing 'vi' alternatives group symlink: /usr/bin/view + Removing 'vi' alternatives group symlink: /usr/share/man/man1/ex.1 + Removing 'vi' alternatives group symlink: /usr/share/man/man1/vi.1 + Removing 'vi' alternatives group symlink: /usr/share/man/man1/view.1 + vim-1.0_1: unregistered 'vi' alternatives group + Switched 'vi' alternatives group to 'nvi' + Creating 'vi' alternatives group symlink: /usr/bin/vi -> /usr/bin/nvi + Creating 'vi' alternatives group symlink: /usr/bin/ex -> /usr/bin/nvi + Creating 'vi' alternatives group symlink: /usr/bin/view -> /usr/bin/nvi + Creating 'vi' alternatives group symlink: /usr/share/man/man1/ex.1 -> /usr/share/man/man1/nvi.1 + Creating 'vi' alternatives group symlink: /usr/share/man/man1/vi.1 -> /usr/share/man/man1/nvi.1 + Creating 'vi' alternatives group symlink: /usr/share/man/man1/view.1 -> /usr/share/man/man1/nvi.1 + Removed file `/usr/share/man/man1/vim.1' + Removed file `/usr/bin/vim' + Removed `vim-1.0_1' successfully. + + 0 downloaded, 0 installed, 0 updated, 0 configured, 1 removed. + $ diff --git a/bin/xbps-create/main.c b/bin/xbps-create/main.c index 5448b393..68b52566 100644 --- a/bin/xbps-create/main.c +++ b/bin/xbps-create/main.c @@ -102,6 +102,9 @@ usage(void) " -s --desc Short description (max 80 characters).\n" " -t --tags A list of tags/categories (blank separated list).\n" " -V --version Prints XBPS release version.\n" + " --alternatives List of available alternatives this pkg provides.\n" + " This expects a blank separated list of ::, e.g\n" + " 'vi:/usr/bin/vi:/usr/bin/vim foo:/usr/bin/foo:/usr/bin/blah'.\n" " --build-options A string with the used build options.\n" " --compression Compression format: none, gzip, bzip2, xz (default).\n" " --shlib-provides List of provided shared libraries (blank separated list,\n" @@ -162,6 +165,72 @@ out: xbps_object_release(array); } +static void +process_one_alternative(const char *altgrname, const char *val) +{ + xbps_dictionary_t d; + xbps_array_t a; + char *altfiles; + bool alloc = false; + + if ((d = xbps_dictionary_get(pkg_propsd, "alternatives")) == NULL) { + d = xbps_dictionary_create(); + assert(d); + alloc = true; + } + if ((a = xbps_dictionary_get(d, altgrname)) == NULL) { + a = xbps_array_create(); + assert(a); + } + altfiles = strchr(val, ':') + 1; + assert(altfiles); + + xbps_array_add_cstring(a, altfiles); + xbps_dictionary_set(d, altgrname, a); + xbps_dictionary_set(pkg_propsd, "alternatives", d); + + if (alloc) { + xbps_object_release(a); + xbps_object_release(d); + } +} + + +static void +process_dict_of_arrays(const char *key, const char *val) +{ + char *altgrname, *args, *p, *saveptr; + + assert(key); + + if (val == NULL) + return; + + args = strdup(val); + assert(args); + + if (strchr(args, ' ') == NULL) { + altgrname = strtok(args, ":"); + assert(altgrname); + process_one_alternative(altgrname, val); + goto out; + } + + for ((p = strtok_r(args, " ", &saveptr)); p; + (p = strtok_r(NULL, " ", &saveptr))) { + char *b; + + b = strdup(p); + assert(b); + altgrname = strtok(b, ":"); + assert(altgrname); + process_one_alternative(altgrname, p); + free(b); + } +out: + free(args); +} + static void process_file(const char *file, const char *key) { @@ -615,6 +684,7 @@ main(int argc, char **argv) { "shlib-requires", required_argument, NULL, '1' }, { "build-options", required_argument, NULL, '2' }, { "compression", required_argument, NULL, '3' }, + { "alternatives", required_argument, NULL, '4' }, { NULL, 0, NULL, 0 } }; struct archive *ar; @@ -624,7 +694,7 @@ main(int argc, char **argv) const char *conflicts, *deps, *homepage, *license, *maint, *bwith; const char *provides, *pkgver, *replaces, *reverts, *desc, *ldesc; const char *arch, *config_files, *mutable_files, *version; - const char *buildopts, *shlib_provides, *shlib_requires; + const char *buildopts, *shlib_provides, *shlib_requires, *alternatives; const char *compression, *tags = NULL, *srcrevs = NULL; char *pkgname, *binpkg, *tname, *p, cwd[PATH_MAX-1]; bool quiet = false, preserve = false; @@ -634,7 +704,7 @@ main(int argc, char **argv) arch = conflicts = deps = homepage = license = maint = compression = NULL; provides = pkgver = replaces = reverts = desc = ldesc = bwith = NULL; buildopts = config_files = mutable_files = shlib_provides = NULL; - shlib_requires = NULL; + alternatives = shlib_requires = NULL; while ((c = getopt_long(argc, argv, shortopts, longopts, NULL)) != -1) { if (optarg && strcmp(optarg, "") == 0) @@ -716,6 +786,9 @@ main(int argc, char **argv) case '3': compression = optarg; break; + case '4': + alternatives = optarg; + break; case '?': default: usage(); @@ -798,6 +871,7 @@ main(int argc, char **argv) process_array("reverts", reverts); process_array("shlib-provides", shlib_provides); process_array("shlib-requires", shlib_requires); + process_dict_of_arrays("alternatives", alternatives); /* save cwd */ memset(&cwd, 0, sizeof(cwd)); diff --git a/include/xbps.h.in b/include/xbps.h.in index 3cda9ddd..c632dfab 100644 --- a/include/xbps.h.in +++ b/include/xbps.h.in @@ -48,7 +48,7 @@ * * This header documents the full API for the XBPS Library. */ -#define XBPS_API_VERSION "20150603" +#define XBPS_API_VERSION "20151018" #ifndef XBPS_VERSION #define XBPS_VERSION "UNSET" @@ -96,6 +96,12 @@ */ #define XBPS_PKGDB "pkgdb-0.38.plist" +/** + * @def XBPS_ALTERNATIVES + * Filename for the package alternatives database. + */ +#define XBPS_ALTERNATIVES "alternatives-0.48.plist" + /** * @def XBPS_PKGPROPS * Filename for package metadata property list. @@ -280,6 +286,11 @@ extern "C" { * - XBPS_STATE_INVALID_DEP: package has an invalid dependency. * - XBPS_STATE_SHOW_INSTALL_MSG: package must show a post-install message. * - XBPS_STATE_SHOW_REMOVE_MSG: package must show a pre-remove message. + * - XBPS_STATE_ALTGROUP_ADDED: package has registered an alternative group. + * - XBPS_STATE_ALTGROUP_REMOVED: package has unregistered an alternative group. + * - XBPS_STATE_ALTGROUP_SWITCHED: alternative group has been switched. + * - XBPS_STATE_ALTGROUP_LINK_ADDED: link added by an alternative group. + * - XBPS_STATE_ALTGROUP_LINK_REMOVED: link removed by an alternative group. * - XBPS_STATE_UNPACK_FILE_PRESERVED: package unpack preserved a file. * - XBPS_STATE_PKGDB: pkgdb upgrade in progress. * - XBPS_STATE_PKGDB_DONE: pkgdb has been upgraded successfully. @@ -328,7 +339,12 @@ typedef enum xbps_state { XBPS_STATE_UNPACK_FILE_PRESERVED, XBPS_STATE_PKGDB, XBPS_STATE_PKGDB_DONE, - XBPS_STATE_TRANS_ADDPKG + XBPS_STATE_TRANS_ADDPKG, + XBPS_STATE_ALTGROUP_ADDED, + XBPS_STATE_ALTGROUP_REMOVED, + XBPS_STATE_ALTGROUP_SWITCHED, + XBPS_STATE_ALTGROUP_LINK_ADDED, + XBPS_STATE_ALTGROUP_LINK_REMOVED } xbps_state_t; /** @@ -514,6 +530,7 @@ struct xbps_handle { */ xbps_dictionary_t pkgdb_revdeps; xbps_dictionary_t vpkgd; + xbps_dictionary_t alternatives; /** * @var pkgdb * @@ -629,6 +646,10 @@ void xbps_dbg_printf_append(struct xbps_handle *, const char *, ...); void xbps_error_printf(const char *, ...); void xbps_warn_printf(const char *, ...); +int xbps_alternatives_flush(struct xbps_handle *xhp); +int xbps_alternatives_register(struct xbps_handle *, xbps_dictionary_t); +int xbps_alternatives_unregister(struct xbps_handle *, xbps_dictionary_t); + /** * Initialize the XBPS library with the following steps: * diff --git a/lib/Makefile b/lib/Makefile index 3ef2a237..571e621b 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -42,6 +42,7 @@ OBJS += plist.o plist_find.o plist_match.o archive.o OBJS += plist_remove.o plist_fetch.o util.o util_hash.o OBJS += repo.o repo_pkgdeps.o repo_sync.o OBJS += rpool.o cb_util.o proplib_wrapper.o +OBJS += package_alternatives.o OBJS += $(EXTOBJS) $(COMPAT_SRCS) .PHONY: all diff --git a/lib/package_alternatives.c b/lib/package_alternatives.c new file mode 100644 index 00000000..40858436 --- /dev/null +++ b/lib/package_alternatives.c @@ -0,0 +1,280 @@ +/*- + * Copyright (c) 2015 Juan Romero Pardines. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include + +#include "xbps_api_impl.h" + +/* + * Alternatives framework for xbps. + */ +static char * +left(const char *str) +{ + char *p; + size_t len; + + p = strdup(str); + len = strlen(p) - strlen(strchr(p, ':')); + p[len] = '\0'; + + return p; +} + +static const char * +right(const char *str) +{ + return strchr(str, ':') + 1; +} + +#if 0 +static int +make_symlink(const char *target, const char *link) +{ + char *t, *l, *tdir, *ldir; + + tdir = strdup(target); + assert(tdir); + ldir = strdup(link); + assert(ldir); + +} +#endif + +static void +xbps_alternatives_init(struct xbps_handle *xhp) +{ + char *plist; + + if (xbps_object_type(xhp->alternatives) == XBPS_TYPE_DICTIONARY) + return; + + plist = xbps_xasprintf("%s/%s", xhp->metadir, XBPS_ALTERNATIVES); + xhp->alternatives = xbps_dictionary_internalize_from_file(plist); + free(plist); + + if (xhp->alternatives == NULL) + xhp->alternatives = xbps_dictionary_create(); +} + +int +xbps_alternatives_flush(struct xbps_handle *xhp) +{ + char *plist; + + if (xbps_object_type(xhp->alternatives) != XBPS_TYPE_DICTIONARY) + return 0; + + /* ... and then write dictionary to disk */ + plist = xbps_xasprintf("%s/%s", xhp->metadir, XBPS_ALTERNATIVES); + if (!xbps_dictionary_externalize_to_file(xhp->alternatives, plist)) { + free(plist); + return EINVAL; + } + free(plist); + + return 0; +} + +static int +remove_symlinks(struct xbps_handle *xhp, xbps_array_t a, const char *grname) +{ + unsigned int i, cnt; + + cnt = xbps_array_count(a); + for (i = 0; i < cnt; i++) { + xbps_string_t str; + char *l, *lnk; + + str = xbps_array_get(a, i); + l = left(xbps_string_cstring_nocopy(str)); + assert(l); + lnk = xbps_xasprintf("%s%s", xhp->rootdir, l); + xbps_set_cb_state(xhp, XBPS_STATE_ALTGROUP_LINK_REMOVED, 0, NULL, + "Removing '%s' alternatives group symlink: %s", grname, l); + unlink(lnk); + free(lnk); + free(l); + } + + return 0; +} + +static int +create_symlinks(struct xbps_handle *xhp, xbps_array_t a, const char *grname) +{ + unsigned int i, cnt; + + cnt = xbps_array_count(a); + for (i = 0; i < cnt; i++) { + xbps_string_t str; + char *l, *lnk; + const char *tgt; + + str = xbps_array_get(a, i); + l = left(xbps_string_cstring_nocopy(str)); + assert(l); + tgt = right(xbps_string_cstring_nocopy(str)); + assert(tgt); + lnk = xbps_xasprintf("%s%s", xhp->rootdir, l); + xbps_set_cb_state(xhp, XBPS_STATE_ALTGROUP_LINK_ADDED, 0, NULL, + "Creating '%s' alternatives group symlink: %s -> %s", grname, l, tgt); + unlink(lnk); + symlink(tgt, lnk); + free(lnk); + free(l); + } + + return 0; +} + +int +xbps_alternatives_unregister(struct xbps_handle *xhp, xbps_dictionary_t pkgd) +{ + xbps_array_t allkeys; + xbps_dictionary_t alternatives; + const char *pkgver; + char *pkgname; + int rv = 0; + + alternatives = xbps_dictionary_get(pkgd, "alternatives"); + if (!xbps_dictionary_count(alternatives)) + return 0; + + xbps_alternatives_init(xhp); + + xbps_dictionary_get_cstring_nocopy(pkgd, "pkgver", &pkgver); + if ((pkgname = xbps_pkg_name(pkgver)) == NULL) + return EINVAL; + + allkeys = xbps_dictionary_all_keys(alternatives); + for (unsigned int i = 0; i < xbps_array_count(allkeys); i++) { + xbps_array_t array; + xbps_object_t keysym; + const char *first = NULL, *keyname; + + keysym = xbps_array_get(allkeys, i); + keyname = xbps_dictionary_keysym_cstring_nocopy(keysym); + + array = xbps_dictionary_get(xhp->alternatives, keyname); + if (array == NULL) + continue; + + xbps_array_get_cstring_nocopy(array, 0, &first); + if (strcmp(pkgname, first) == 0) { + /* this pkg is the current alternative for this group */ + rv = remove_symlinks(xhp, + xbps_dictionary_get(alternatives, keyname), + keyname); + if (rv != 0) + break; + } + xbps_set_cb_state(xhp, XBPS_STATE_ALTGROUP_REMOVED, 0, NULL, + "%s: unregistered '%s' alternatives group", pkgver, keyname); + xbps_remove_string_from_array(array, pkgname); + if (xbps_array_count(array) == 0) { + xbps_dictionary_remove(xhp->alternatives, keyname); + } else { + xbps_dictionary_t curpkgd; + + first = NULL; + xbps_array_get_cstring_nocopy(array, 0, &first); + curpkgd = xbps_pkgdb_get_pkg(xhp, first); + assert(curpkgd); + xbps_set_cb_state(xhp, XBPS_STATE_ALTGROUP_SWITCHED, 0, NULL, + "Switched '%s' alternatives group to '%s'", keyname, first); + alternatives = xbps_dictionary_get(curpkgd, "alternatives"); + rv = create_symlinks(xhp, + xbps_dictionary_get(alternatives, keyname), + keyname); + if (rv != 0) + break; + } + + } + xbps_object_release(allkeys); + free(pkgname); + + return rv; +} + +int +xbps_alternatives_register(struct xbps_handle *xhp, xbps_dictionary_t pkgd) +{ + xbps_array_t allkeys; + xbps_dictionary_t alternatives; + const char *pkgver; + char *pkgname; + int rv = 0; + + alternatives = xbps_dictionary_get(pkgd, "alternatives"); + if (!xbps_dictionary_count(alternatives)) + return 0; + + xbps_alternatives_init(xhp); + + xbps_dictionary_get_cstring_nocopy(pkgd, "pkgver", &pkgver); + pkgname = xbps_pkg_name(pkgver); + if (pkgname == NULL) + return EINVAL; + + allkeys = xbps_dictionary_all_keys(alternatives); + for (unsigned int i = 0; i < xbps_array_count(allkeys); i++) { + xbps_array_t array; + xbps_object_t keysym; + const char *keyname; + bool alloc = false; + + keysym = xbps_array_get(allkeys, i); + keyname = xbps_dictionary_keysym_cstring_nocopy(keysym); + + array = xbps_dictionary_get(xhp->alternatives, keyname); + if (array == NULL) { + alloc = true; + array = xbps_array_create(); + } + xbps_array_add_cstring(array, pkgname); + xbps_dictionary_set(xhp->alternatives, keyname, array); + xbps_set_cb_state(xhp, XBPS_STATE_ALTGROUP_ADDED, 0, NULL, + "%s: registered '%s' alternatives group", pkgver, keyname); + if (alloc) { + /* apply alternatives for this group */ + rv = create_symlinks(xhp, + xbps_dictionary_get(alternatives, keyname), + keyname); + xbps_object_release(array); + if (rv != 0) + break; + } + } + xbps_object_release(allkeys); + free(pkgname); + + return rv; +} diff --git a/lib/package_remove.c b/lib/package_remove.c index a3d1741c..288fd3f8 100644 --- a/lib/package_remove.c +++ b/lib/package_remove.c @@ -318,6 +318,10 @@ xbps_remove_pkg(struct xbps_handle *xhp, const char *pkgver, bool update) if ((rv = xbps_cb_message(xhp, pkgd, "remove-msg")) != 0) goto out; + /* unregister alternatives */ + if ((rv = xbps_alternatives_unregister(xhp, pkgd)) != 0) + goto out; + /* * If updating a package, we just need to execute the current * pre-remove action target and we are done. Its files will be diff --git a/lib/package_unpack.c b/lib/package_unpack.c index 94d7d97b..0fb96d7b 100644 --- a/lib/package_unpack.c +++ b/lib/package_unpack.c @@ -622,6 +622,14 @@ xbps_unpack_binary_pkg(struct xbps_handle *xhp, xbps_dictionary_t pkg_repod) "%s: [unpack] failed to set state to unpacked: %s", pkgver, strerror(rv)); } + /* register alternatives */ + if ((rv = xbps_alternatives_register(xhp, pkg_repod)) != 0) { + xbps_set_cb_state(xhp, XBPS_STATE_UNPACK_FAIL, + rv, pkgver, + "%s: [unpack] failed to register alternatives: %s", + pkgver, strerror(rv)); + } + out: if (pkg_fd != -1) close(pkg_fd); diff --git a/lib/transaction_commit.c b/lib/transaction_commit.c index b018e5d3..d50c7bd1 100644 --- a/lib/transaction_commit.c +++ b/lib/transaction_commit.c @@ -350,6 +350,10 @@ xbps_transaction_commit(struct xbps_handle *xhp) goto out; } } + /* flush changes to the alternatives framework */ + if ((rv = xbps_alternatives_flush(xhp)) != 0) + goto out; + /* if there are no packages to install or update we are done */ if (!xbps_dictionary_get(xhp->transd, "total-update-pkgs") && !xbps_dictionary_get(xhp->transd, "total-install-pkgs"))