diff --git a/ALTERNATIVES b/ALTERNATIVES
deleted file mode 100644
index 89d666f8..00000000
--- a/ALTERNATIVES
+++ /dev/null
@@ -1,246 +0,0 @@
-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/NEWS b/NEWS
index 36f160f3..df9f4446 100644
--- a/NEWS
+++ b/NEWS
@@ -1,5 +1,9 @@
xbps-0.48 (???):
+ * xbps-alternatives(1): new utility to list or set alternatives provided by
+ packages. This effectively implements a functional alternatives framework
+ to switch easily a default provider via symbolic links.
+
* xbps-{install,reconfigure,remove}(1): do not log to console when the
syslog option (xbps.d(5)) is enabled. Messages are send to stderr/stdout,
so that logging to the console duplicates them. Close #123
diff --git a/bin/Makefile b/bin/Makefile
index 1815c56e..5a71d10b 100644
--- a/bin/Makefile
+++ b/bin/Makefile
@@ -1,6 +1,7 @@
-include ../config.mk
-SUBDIRS = xbps-create
+SUBDIRS += xbps-alternatives
+SUBDIRS += xbps-create
SUBDIRS += xbps-dgraph
SUBDIRS += xbps-install
SUBDIRS += xbps-pkgdb
diff --git a/bin/xbps-alternatives/Makefile b/bin/xbps-alternatives/Makefile
new file mode 100644
index 00000000..c80f4d5b
--- /dev/null
+++ b/bin/xbps-alternatives/Makefile
@@ -0,0 +1,6 @@
+TOPDIR = ../..
+-include $(TOPDIR)/config.mk
+
+BIN = xbps-alternatives
+
+include $(TOPDIR)/mk/prog.mk
diff --git a/bin/xbps-alternatives/main.c b/bin/xbps-alternatives/main.c
new file mode 100644
index 00000000..d73acfc1
--- /dev/null
+++ b/bin/xbps-alternatives/main.c
@@ -0,0 +1,252 @@
+/*-
+ * 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
+#include
+#include
+#include
+
+#include
+
+static void __attribute__((noreturn))
+usage(bool fail)
+{
+ fprintf(stdout,
+ "Usage: xbps-alternatives [OPTIONS] MODE\n\n"
+ "OPTIONS\n"
+ " -C --config Path to confdir (xbps.d)\n"
+ " -d --debug Debug mode shown to stderr\n"
+ " -g --group Group of alternatives to match\n"
+ " -h --help Print usage help\n"
+ " -r --rootdir Full path to rootdir\n"
+ " -v --verbose Verbose messages\n"
+ " -V --version Show XBPS version\n"
+ "MODE\n"
+ " -l --list [PKG] List all alternatives or from PKG\n"
+ " -s --set PKG Set alternatives for PKG\n\n");
+ exit(fail ? EXIT_FAILURE : EXIT_SUCCESS);
+}
+
+static int
+state_cb(const struct xbps_state_cb_data *xscd, void *cbd _unused)
+{
+ bool slog = false;
+
+ if ((xscd->xhp->flags & XBPS_FLAG_DISABLE_SYSLOG) == 0) {
+ slog = true;
+ openlog("xbps-alternatives", 0, LOG_USER);
+ }
+ if (xscd->desc) {
+ printf("%s\n", xscd->desc);
+ if (slog)
+ syslog(LOG_NOTICE, "%s", xscd->desc);
+ }
+ return 0;
+}
+
+static void
+list_pkg_alternatives(xbps_dictionary_t pkgd, bool print_key)
+{
+ xbps_dictionary_t pkg_alternatives;
+ xbps_array_t allkeys;
+
+ pkg_alternatives = xbps_dictionary_get(pkgd, "alternatives");
+ if (pkg_alternatives == NULL)
+ return;
+
+ allkeys = xbps_dictionary_all_keys(pkg_alternatives);
+ for (unsigned int i = 0; i < xbps_array_count(allkeys); i++) {
+ xbps_object_t keysym;
+ xbps_array_t array;
+ const char *keyname;
+
+ keysym = xbps_array_get(allkeys, i);
+ keyname = xbps_dictionary_keysym_cstring_nocopy(keysym);
+ array = xbps_dictionary_get_keysym(pkg_alternatives, keysym);
+
+ if (print_key)
+ printf("%s\n", keyname);
+
+ for (unsigned int x = 0; x < xbps_array_count(array); x++) {
+ const char *str;
+
+ xbps_array_get_cstring_nocopy(array, x, &str);
+ printf(" - %s\n", str);
+ }
+ }
+ xbps_object_release(allkeys);
+}
+
+static int
+list_alternatives(struct xbps_handle *xhp, const char *pkgname)
+{
+ xbps_dictionary_t alternatives, pkgd;
+ xbps_array_t allkeys;
+
+ (void)xbps_pkgdb_get_pkg(xhp, "foo");
+
+ if (pkgname) {
+ /* list alternatives for pkgname */
+ if ((pkgd = xbps_pkgdb_get_pkg(xhp, pkgname)) == NULL)
+ return ENOENT;
+
+ list_pkg_alternatives(pkgd, true);
+ return 0;
+ }
+ assert(xhp->pkgdb);
+
+ alternatives = xbps_dictionary_get(xhp->pkgdb, "_XBPS_ALTERNATIVES_");
+ if (alternatives == NULL)
+ return ENOENT;
+
+ 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;
+
+ keysym = xbps_array_get(allkeys, i);
+ keyname = xbps_dictionary_keysym_cstring_nocopy(keysym);
+ array = xbps_dictionary_get_keysym(alternatives, keysym);
+
+ printf("%s\n", keyname);
+ for (unsigned int x = 0; x < xbps_array_count(array); x++) {
+ const char *str;
+
+ xbps_array_get_cstring_nocopy(array, x, &str);
+ printf(" - %s%s\n", str, x == 0 ? " (current)" : "");
+ pkgd = xbps_pkgdb_get_pkg(xhp, str);
+ assert(pkgd);
+ list_pkg_alternatives(pkgd, false);
+ }
+ }
+ xbps_object_release(allkeys);
+
+ return 0;
+}
+
+int
+main(int argc, char **argv)
+{
+ const char *shortopts = "C:dg:hls:r:Vv";
+ const struct option longopts[] = {
+ { "config", required_argument, NULL, 'C' },
+ { "debug", no_argument, NULL, 'd' },
+ { "group", required_argument, NULL, 'g' },
+ { "help", no_argument, NULL, 'h' },
+ { "list", no_argument, NULL, 'l' },
+ { "set", required_argument, NULL, 's' },
+ { "rootdir", required_argument, NULL, 'r' },
+ { "verbose", no_argument, NULL, 'v' },
+ { "version", no_argument, NULL, 'V' },
+ { NULL, 0, NULL, 0 }
+ };
+ struct xbps_handle xh;
+ const char *confdir, *rootdir, *group, *pkg;
+ int c, rv, flags = 0;
+ bool list_mode = false, set_mode = false;
+
+ confdir = rootdir = group = pkg = NULL;
+
+ while ((c = getopt_long(argc, argv, shortopts, longopts, NULL)) != -1) {
+ switch (c) {
+ case 'C':
+ confdir = optarg;
+ break;
+ case 'd':
+ flags |= XBPS_FLAG_DEBUG;
+ break;
+ case 'g':
+ group = optarg;
+ break;
+ case 'h':
+ usage(false);
+ /* NOTREACHED */
+ case 'l':
+ list_mode = true;
+ break;
+ case 's':
+ set_mode = true;
+ pkg = optarg;
+ break;
+ case 'r':
+ rootdir = optarg;
+ break;
+ case 'v':
+ flags |= XBPS_FLAG_VERBOSE;
+ break;
+ case 'V':
+ printf("%s\n", XBPS_RELVER);
+ exit(EXIT_SUCCESS);
+ case '?':
+ default:
+ usage(true);
+ /* NOTREACHED */
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (!list_mode && !set_mode)
+ usage(true);
+ else if (argc && list_mode)
+ pkg = *argv;
+
+ memset(&xh, 0, sizeof(xh));
+ xh.state_cb = state_cb;
+ if (rootdir)
+ xbps_strlcpy(xh.rootdir, rootdir, sizeof(xh.rootdir));
+ if (confdir)
+ xbps_strlcpy(xh.confdir, confdir, sizeof(xh.confdir));
+
+ xh.flags = flags;
+
+ /* initialize xbps */
+ if ((rv = xbps_init(&xh)) != 0) {
+ xbps_error_printf("Failed to initialize libxbps: %s\n",
+ strerror(rv));
+ exit(EXIT_FAILURE);
+ }
+ if (set_mode) {
+ /* in set mode pkgdb must be locked and flushed on success */
+ if ((rv = xbps_pkgdb_lock(&xh)) != 0) {
+ fprintf(stderr, "failed to lock pkgdb: %s\n", strerror(rv));
+ exit(EXIT_FAILURE);
+ }
+ if ((rv = xbps_alternatives_set(&xh, pkg, group)) == 0)
+ rv = xbps_pkgdb_update(&xh, true, true);
+ } else if (list_mode) {
+ /* list alternative groups */
+ rv = list_alternatives(&xh, pkg);
+ }
+
+ xbps_end(&xh);
+ exit(rv ? EXIT_FAILURE : EXIT_SUCCESS);
+}
diff --git a/bin/xbps-alternatives/xbps-alternatives.1 b/bin/xbps-alternatives/xbps-alternatives.1
new file mode 100644
index 00000000..784c44f4
--- /dev/null
+++ b/bin/xbps-alternatives/xbps-alternatives.1
@@ -0,0 +1,89 @@
+.Dd October 30, 2015
+.Dt XBPS-ALTERNATIVES 1
+.Sh NAME
+.Nm xbps-alternatives
+.Nd XBPS utility to handle alternatives
+.Sh SYNOPSIS
+.Nm xbps-alternatives
+.Op OPTIONS
+.Ar MODE
+.Sh DESCRIPTION
+The
+.Nm
+utility lists or sets the alternatives provided by installed packages.
+Alternatives are classified by groups, and a group contains a number
+of symbolic links which are applied when the group is set.
+.Pp
+When a package is installed its alternative groups are registered in the package database (pkgdb).
+.Pp
+When a package is removed its alternative groups are unregistered from the package database (pkgdb).
+If there are no more alternative groups, the package database removes the references. If there were
+alternative groups registered previously, the previous package is set as default provider.
+.Sh OPTIONS
+.Bl -tag -width -x
+.It Fl C, Fl -config Ar dir
+Specifies a path to the XBPS configuration directory.
+If the first character is not '\/' then it's a relative path of
+.Ar rootdir .
+.It Fl d, Fl -debug
+Enables extra debugging shown to stderr.
+.It Fl g, Fl -group
+Alternative group name to match. To be used with the
+.Ar set
+mode.
+.It Fl h, Fl -help
+Show the help message.
+.It Fl r, Fl -rootdir Ar dir
+Specifies a full path for the target root directory.
+.It Fl v, Fl -verbose
+Enables verbose messages.
+.It Fl V, Fl -version
+Show the version information.
+.El
+.Sh MODE
+Only one of the following modes can be used at a time.
+.Bl -tag -width -x
+.It Fl l, Fl -list Op PKG
+Lists all current alternatives or only from
+.Ar PKG .
+.It Fl s, Fl -set Ar PKG Op -g Ar group
+Set alternative groups specified by
+.Ar PKG
+or just a specific group, if the
+.Fl g Fl -group
+option is set.
+.El
+.Sh FILES
+.Bl -tag -width /var/db/xbps/.-files.plist
+.It Ar /etc/xbps.d
+Default configuration directory.
+.It Ar /usr/share/xbps.d
+Default system configuration directory.
+.It Ar /var/db/xbps/.-files.plist
+Package files metadata.
+.It Ar /var/db/xbps/pkgdb-0.38.plist
+Default package database (0.38 format). Keeps track of installed packages and properties.
+.It Ar /var/cache/xbps
+Default cache directory to store downloaded binary packages.
+.El
+.Sh SEE ALSO
+.Xr xbps-checkvers 1 ,
+.Xr xbps-create 1 ,
+.Xr xbps-dgraph 1 ,
+.Xr xbps-fbulk 1 ,
+.Xr xbps-install 1 ,
+.Xr xbps-pkgdb 1 ,
+.Xr xbps-query 1 ,
+.Xr xbps-reconfigure 1 ,
+.Xr xbps-remove 1 ,
+.Xr xbps-rindex 1 ,
+.Xr xbps-uchroot 1 ,
+.Xr xbps-uunshare 1 ,
+.Xr xbps.d 5
+.Sh AUTHORS
+.An Juan Romero Pardines
+.Sh BUGS
+Probably, but I try to make this not happen. Use it under your own
+responsibility and enjoy your life.
+.Pp
+Report bugs at https://github.com/voidlinux/xbps/issues
diff --git a/include/xbps.h.in b/include/xbps.h.in
index c632dfab..20398d02 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 "20151018"
+#define XBPS_API_VERSION "20151030"
#ifndef XBPS_VERSION
#define XBPS_VERSION "UNSET"
@@ -96,12 +96,6 @@
*/
#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.
@@ -530,7 +524,6 @@ struct xbps_handle {
*/
xbps_dictionary_t pkgdb_revdeps;
xbps_dictionary_t vpkgd;
- xbps_dictionary_t alternatives;
/**
* @var pkgdb
*
@@ -646,7 +639,7 @@ 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_set(struct xbps_handle *, const char *, const char *);
int xbps_alternatives_register(struct xbps_handle *, xbps_dictionary_t);
int xbps_alternatives_unregister(struct xbps_handle *, xbps_dictionary_t);
diff --git a/lib/package_alternatives.c b/lib/package_alternatives.c
index 96a00e13..02b42e37 100644
--- a/lib/package_alternatives.c
+++ b/lib/package_alternatives.c
@@ -32,7 +32,7 @@
#include "xbps_api_impl.h"
/*
- * Alternatives framework for xbps.
+ * XXX TODO: relative symlinks.
*/
static char *
left(const char *str)
@@ -53,55 +53,6 @@ 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)
{
@@ -162,26 +113,92 @@ create_symlinks(struct xbps_handle *xhp, xbps_array_t a, const char *grname)
return 0;
}
+int
+xbps_alternatives_set(struct xbps_handle *xhp, const char *pkgname,
+ const char *group)
+{
+ xbps_array_t allkeys;
+ xbps_dictionary_t alternatives, pkg_alternatives, pkgd;
+ const char *pkgver;
+ int rv = 0;
+
+ assert(xhp);
+ assert(pkgname);
+
+ alternatives = xbps_dictionary_get(xhp->pkgdb, "_XBPS_ALTERNATIVES_");
+ if (alternatives == NULL)
+ return ENOENT;
+
+ pkgd = xbps_pkgdb_get_pkg(xhp, pkgname);
+ if (pkgd == NULL)
+ return ENOENT;
+
+ pkg_alternatives = xbps_dictionary_get(pkgd, "alternatives");
+ if (!xbps_dictionary_count(pkg_alternatives))
+ return ENOENT;
+
+ xbps_dictionary_get_cstring_nocopy(pkgd, "pkgver", &pkgver);
+
+ allkeys = xbps_dictionary_all_keys(pkg_alternatives);
+ for (unsigned int i = 0; i < xbps_array_count(allkeys); i++) {
+ xbps_array_t array;
+ xbps_object_t keysym;
+ xbps_string_t kstr;
+ const char *keyname;
+
+ keysym = xbps_array_get(allkeys, i);
+ keyname = xbps_dictionary_keysym_cstring_nocopy(keysym);
+
+ if (group && strcmp(keyname, group)) {
+ rv = ENOENT;
+ continue;
+ }
+
+ array = xbps_dictionary_get(alternatives, keyname);
+ if (array == NULL)
+ continue;
+
+ /* put this alternative group at the head */
+ xbps_remove_string_from_array(array, pkgname);
+ kstr = xbps_string_create_cstring(pkgname);
+ xbps_array_add_first(array, kstr);
+ xbps_object_release(kstr);
+
+ /* apply the alternatives group */
+ xbps_set_cb_state(xhp, XBPS_STATE_ALTGROUP_ADDED, 0, NULL,
+ "%s: applying '%s' alternatives group", pkgver, keyname);
+ rv = create_symlinks(xhp, xbps_dictionary_get(pkg_alternatives, keyname), keyname);
+ if (rv != 0)
+ break;
+ }
+ xbps_object_release(allkeys);
+ return rv;
+}
+
int
xbps_alternatives_unregister(struct xbps_handle *xhp, xbps_dictionary_t pkgd)
{
xbps_array_t allkeys;
- xbps_dictionary_t alternatives;
+ xbps_dictionary_t alternatives, pkg_alternatives;
const char *pkgver;
char *pkgname;
int rv = 0;
- alternatives = xbps_dictionary_get(pkgd, "alternatives");
- if (!xbps_dictionary_count(alternatives))
+ assert(xhp);
+
+ alternatives = xbps_dictionary_get(xhp->pkgdb, "_XBPS_ALTERNATIVES_");
+ if (alternatives == NULL)
return 0;
- xbps_alternatives_init(xhp);
+ pkg_alternatives = xbps_dictionary_get(pkgd, "alternatives");
+ if (!xbps_dictionary_count(pkg_alternatives))
+ return 0;
xbps_dictionary_get_cstring_nocopy(pkgd, "pkgver", &pkgver);
if ((pkgname = xbps_pkg_name(pkgver)) == NULL)
return EINVAL;
- allkeys = xbps_dictionary_all_keys(alternatives);
+ allkeys = xbps_dictionary_all_keys(pkg_alternatives);
for (unsigned int i = 0; i < xbps_array_count(allkeys); i++) {
xbps_array_t array;
xbps_object_t keysym;
@@ -190,7 +207,7 @@ xbps_alternatives_unregister(struct xbps_handle *xhp, xbps_dictionary_t pkgd)
keysym = xbps_array_get(allkeys, i);
keyname = xbps_dictionary_keysym_cstring_nocopy(keysym);
- array = xbps_dictionary_get(xhp->alternatives, keyname);
+ array = xbps_dictionary_get(alternatives, keyname);
if (array == NULL)
continue;
@@ -198,7 +215,7 @@ xbps_alternatives_unregister(struct xbps_handle *xhp, xbps_dictionary_t pkgd)
if (strcmp(pkgname, first) == 0) {
/* this pkg is the current alternative for this group */
rv = remove_symlinks(xhp,
- xbps_dictionary_get(alternatives, keyname),
+ xbps_dictionary_get(pkg_alternatives, keyname),
keyname);
if (rv != 0)
break;
@@ -207,7 +224,7 @@ xbps_alternatives_unregister(struct xbps_handle *xhp, xbps_dictionary_t pkgd)
"%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);
+ xbps_dictionary_remove(alternatives, keyname);
} else {
xbps_dictionary_t curpkgd;
@@ -217,9 +234,9 @@ xbps_alternatives_unregister(struct xbps_handle *xhp, xbps_dictionary_t pkgd)
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");
+ pkg_alternatives = xbps_dictionary_get(curpkgd, "alternatives");
rv = create_symlinks(xhp,
- xbps_dictionary_get(alternatives, keyname),
+ xbps_dictionary_get(pkg_alternatives, keyname),
keyname);
if (rv != 0)
break;
@@ -236,23 +253,35 @@ int
xbps_alternatives_register(struct xbps_handle *xhp, xbps_dictionary_t pkgd)
{
xbps_array_t allkeys;
- xbps_dictionary_t alternatives;
+ xbps_dictionary_t alternatives, pkg_alternatives;
const char *pkgver;
char *pkgname;
int rv = 0;
- alternatives = xbps_dictionary_get(pkgd, "alternatives");
- if (!xbps_dictionary_count(alternatives))
+ assert(xhp);
+
+ if (xhp->pkgdb == NULL)
+ return EINVAL;
+
+ pkg_alternatives = xbps_dictionary_get(pkgd, "alternatives");
+ if (!xbps_dictionary_count(pkg_alternatives))
return 0;
- xbps_alternatives_init(xhp);
+ alternatives = xbps_dictionary_get(xhp->pkgdb, "_XBPS_ALTERNATIVES_");
+ if (alternatives == NULL) {
+ alternatives = xbps_dictionary_create();
+ xbps_dictionary_set(xhp->pkgdb, "_XBPS_ALTERNATIVES_", alternatives);
+ xbps_object_release(alternatives);
+ }
+ alternatives = xbps_dictionary_get(xhp->pkgdb, "_XBPS_ALTERNATIVES_");
+ assert(alternatives);
xbps_dictionary_get_cstring_nocopy(pkgd, "pkgver", &pkgver);
pkgname = xbps_pkg_name(pkgver);
if (pkgname == NULL)
return EINVAL;
- allkeys = xbps_dictionary_all_keys(alternatives);
+ allkeys = xbps_dictionary_all_keys(pkg_alternatives);
for (unsigned int i = 0; i < xbps_array_count(allkeys); i++) {
xbps_array_t array;
xbps_object_t keysym;
@@ -262,19 +291,19 @@ xbps_alternatives_register(struct xbps_handle *xhp, xbps_dictionary_t pkgd)
keysym = xbps_array_get(allkeys, i);
keyname = xbps_dictionary_keysym_cstring_nocopy(keysym);
- array = xbps_dictionary_get(xhp->alternatives, keyname);
+ array = xbps_dictionary_get(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_dictionary_set(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),
+ xbps_dictionary_get(pkg_alternatives, keyname),
keyname);
xbps_object_release(array);
if (rv != 0)
diff --git a/lib/pkgdb.c b/lib/pkgdb.c
index 011bc7a6..ded8ff66 100644
--- a/lib/pkgdb.c
+++ b/lib/pkgdb.c
@@ -208,6 +208,7 @@ xbps_pkgdb_init(struct xbps_handle *xhp)
xbps_dbg_printf(xhp, "[pkgdb] pkgdb_map_vpkgs %s\n", strerror(rv));
return rv;
}
+ assert(xhp->pkgdb);
xbps_dbg_printf(xhp, "[pkgdb] initialized ok.\n");
return 0;
diff --git a/lib/plist.c b/lib/plist.c
index 9eb6ab64..ef18b06c 100644
--- a/lib/plist.c
+++ b/lib/plist.c
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2008-2013 Juan Romero Pardines.
+ * Copyright (c) 2008-2015 Juan Romero Pardines.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -66,6 +66,9 @@ array_foreach_thread(void *arg)
if (xbps_object_type(thd->dict) == XBPS_TYPE_DICTIONARY) {
pkgd = xbps_dictionary_get_keysym(thd->dict, obj);
key = xbps_dictionary_keysym_cstring_nocopy(obj);
+ /* ignore internal objs */
+ if (strncmp(key, "_XBPS_", 6) == 0)
+ continue;
} else {
pkgd = obj;
key = NULL;
@@ -148,6 +151,9 @@ xbps_array_foreach_cb(struct xbps_handle *xhp,
if (xbps_object_type(dict) == XBPS_TYPE_DICTIONARY) {
pkgd = xbps_dictionary_get_keysym(dict, obj);
key = xbps_dictionary_keysym_cstring_nocopy(obj);
+ /* ignore internal objs */
+ if (strncmp(key, "_XBPS_", 6) == 0)
+ continue;
} else {
pkgd = obj;
key = NULL;
diff --git a/lib/transaction_commit.c b/lib/transaction_commit.c
index d50c7bd1..b018e5d3 100644
--- a/lib/transaction_commit.c
+++ b/lib/transaction_commit.c
@@ -350,10 +350,6 @@ 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"))
diff --git a/lib/transaction_ops.c b/lib/transaction_ops.c
index cf3b882c..a2dbd2ce 100644
--- a/lib/transaction_ops.c
+++ b/lib/transaction_ops.c
@@ -260,7 +260,8 @@ xbps_transaction_update_packages(struct xbps_handle *xhp)
while ((obj = xbps_object_iterator_next(iter))) {
hold = false;
pkgd = xbps_dictionary_get_keysym(xhp->pkgdb, obj);
- xbps_dictionary_get_cstring_nocopy(pkgd, "pkgver", &pkgver);
+ if (!xbps_dictionary_get_cstring_nocopy(pkgd, "pkgver", &pkgver))
+ continue;
xbps_dictionary_get_bool(pkgd, "hold", &hold);
if (hold) {
xbps_dbg_printf(xhp, "[rpool] package `%s' "
diff --git a/lib/transaction_shlibs.c b/lib/transaction_shlibs.c
index 592dde11..a979f1a1 100644
--- a/lib/transaction_shlibs.c
+++ b/lib/transaction_shlibs.c
@@ -84,7 +84,9 @@ collect_shlibs(struct xbps_handle *xhp, xbps_array_t pkgs, bool req)
while ((obj = xbps_object_iterator_next(iter))) {
char *pkgname;
- xbps_dictionary_get_cstring_nocopy(obj, "pkgver", &pkgver);
+ if (!xbps_dictionary_get_cstring_nocopy(obj, "pkgver", &pkgver))
+ continue;
+
pkgname = xbps_pkg_name(pkgver);
assert(pkgname);
xbps_dictionary_set(pd, pkgname, obj);
diff --git a/tests/xbps/Kyuafile b/tests/xbps/Kyuafile
index 1572eba4..326f6960 100644
--- a/tests/xbps/Kyuafile
+++ b/tests/xbps/Kyuafile
@@ -3,6 +3,7 @@ syntax("kyuafile", 1)
test_suite("xbps")
include('libxbps/Kyuafile')
+include('xbps-alternatives/Kyuafile')
include('xbps-checkvers/Kyuafile')
include('xbps-create/Kyuafile')
include('xbps-install/Kyuafile')
diff --git a/tests/xbps/Makefile b/tests/xbps/Makefile
index af8ae620..8171e1bb 100644
--- a/tests/xbps/Makefile
+++ b/tests/xbps/Makefile
@@ -1,5 +1,5 @@
-include ../../config.mk
-SUBDIRS = common libxbps xbps-checkvers xbps-create xbps-install xbps-query xbps-rindex xbps-uhelper
+SUBDIRS = common libxbps xbps-alternatives xbps-checkvers xbps-create xbps-install xbps-query xbps-rindex xbps-uhelper
include ../../mk/subdir.mk
diff --git a/tests/xbps/xbps-alternatives/Kyuafile b/tests/xbps/xbps-alternatives/Kyuafile
new file mode 100644
index 00000000..1a1d609e
--- /dev/null
+++ b/tests/xbps/xbps-alternatives/Kyuafile
@@ -0,0 +1,4 @@
+syntax("kyuafile", 1)
+
+test_suite("xbps-alternatives")
+atf_test_program{name="main"}
diff --git a/tests/xbps/xbps-alternatives/Makefile b/tests/xbps/xbps-alternatives/Makefile
new file mode 100644
index 00000000..e6da7521
--- /dev/null
+++ b/tests/xbps/xbps-alternatives/Makefile
@@ -0,0 +1,8 @@
+TOPDIR = ../../..
+-include $(TOPDIR)/config.mk
+
+TESTSHELL = main
+TESTSSUBDIR = xbps/xbps-alternatives
+EXTRA_FILES = Kyuafile
+
+include $(TOPDIR)/mk/test.mk
diff --git a/tests/xbps/xbps-alternatives/main.sh b/tests/xbps/xbps-alternatives/main.sh
new file mode 100644
index 00000000..4e8e4c41
--- /dev/null
+++ b/tests/xbps/xbps-alternatives/main.sh
@@ -0,0 +1,270 @@
+#! /usr/bin/env atf-sh
+
+atf_test_case register_one
+
+register_one_head() {
+ atf_set "descr" "xbps-alternatives: register one pkg wth an alternatives group"
+}
+register_one_body() {
+ mkdir -p repo pkg_A/usr/bin
+ touch pkg_A/usr/bin/fileA
+ cd repo
+ xbps-create -A noarch -n A-1.1_1 -s "A pkg" --alternatives "file:/usr/bin/file:/usr/bin/fileA" ../pkg_A
+ atf_check_equal $? 0
+ xbps-rindex -d -a $PWD/*.xbps
+ atf_check_equal $? 0
+ cd ..
+
+ xbps-install -r root --repository=repo -ydv A
+ atf_check_equal $? 0
+ rv=1
+ if [ -e root/usr/bin/fileA ]; then
+ lnk=$(readlink root/usr/bin/file)
+ if [ "$lnk" = "/usr/bin/fileA" ]; then
+ rv=0
+ fi
+ echo "A lnk: $lnk"
+ fi
+ atf_check_equal $rv 0
+}
+
+atf_test_case register_multi
+
+register_multi_head() {
+ atf_set "descr" "xbps-alternatives: register multiple pkgs with an alternatives group"
+}
+register_multi_body() {
+ mkdir -p repo pkg_A/usr/bin pkg_B/usr/bin
+ touch pkg_A/usr/bin/fileA pkg_B/usr/bin/fileB
+ cd repo
+ xbps-create -A noarch -n A-1.1_1 -s "A pkg" --alternatives "file:/usr/bin/file:/usr/bin/fileA" ../pkg_A
+ atf_check_equal $? 0
+ xbps-create -A noarch -n B-1.1_1 -s "B pkg" --alternatives "file:/usr/bin/file:/usr/bin/fileB" ../pkg_B
+ atf_check_equal $? 0
+ xbps-rindex -d -a $PWD/*.xbps
+ atf_check_equal $? 0
+ cd ..
+
+ xbps-install -r root --repository=repo -ydv A
+ atf_check_equal $? 0
+ rv=1
+ if [ -e root/usr/bin/fileA ]; then
+ lnk=$(readlink root/usr/bin/file)
+ if [ "$lnk" = "/usr/bin/fileA" ]; then
+ rv=0
+ fi
+ echo "A lnk: $lnk"
+ fi
+ atf_check_equal $rv 0
+
+ xbps-install -r root --repository=repo -ydv B
+ atf_check_equal $? 0
+ rv=1
+ if [ -e root/usr/bin/fileA -a -e root/usr/bin/fileB ]; then
+ lnk=$(readlink root/usr/bin/file)
+ if [ "$lnk" = "/usr/bin/fileA" ]; then
+ rv=0
+ fi
+ echo "B lnk: $lnk"
+ fi
+ atf_check_equal $rv 0
+}
+
+atf_test_case unregister_one
+
+unregister_one_head() {
+ atf_set "descr" "xbps-alternatives: unregister one pkg with an alternatives group"
+}
+unregister_one_body() {
+ mkdir -p repo pkg_A/usr/bin
+ touch pkg_A/usr/bin/fileA
+ cd repo
+ xbps-create -A noarch -n A-1.1_1 -s "A pkg" --alternatives "file:/usr/bin/file:/usr/bin/fileA" ../pkg_A
+ atf_check_equal $? 0
+ xbps-rindex -d -a $PWD/*.xbps
+ atf_check_equal $? 0
+ cd ..
+
+ xbps-install -r root --repository=repo -ydv A
+ atf_check_equal $? 0
+ xbps-remove -r root -yvd A
+ rv=1
+ if [ ! -L root/usr/bin/file -a ! -e root/usr/bin/fileA ]; then
+ rv=0
+ fi
+ atf_check_equal $rv 0
+}
+
+atf_test_case unregister_multi
+
+unregister_multi_head() {
+ atf_set "descr" "xbps-alternatives: unregister multiple pkgs with an alternatives group"
+}
+unregister_multi_body() {
+ mkdir -p repo pkg_A/usr/bin pkg_B/usr/bin
+ touch pkg_A/usr/bin/fileA pkg_B/usr/bin/fileB
+ cd repo
+ xbps-create -A noarch -n A-1.1_1 -s "A pkg" --alternatives "file:/usr/bin/file:/usr/bin/fileA" ../pkg_A
+ atf_check_equal $? 0
+ xbps-create -A noarch -n B-1.1_1 -s "B pkg" --alternatives "file:/usr/bin/file:/usr/bin/fileB" ../pkg_B
+ atf_check_equal $? 0
+ xbps-rindex -d -a $PWD/*.xbps
+ atf_check_equal $? 0
+ cd ..
+
+ xbps-install -r root --repository=repo -ydv A
+ atf_check_equal $? 0
+
+ if [ -e root/usr/bin/fileA ]; then
+ lnk=$(readlink root/usr/bin/file)
+ if [ "$lnk" = "/usr/bin/fileA" ]; then
+ rv=0
+ fi
+ echo "A lnk: $lnk"
+ fi
+ atf_check_equal $rv 0
+ xbps-remove -r root -yvd A
+ rv=1
+ if [ ! -L root/usr/bin/file -a ! -e root/usr/bin/fileA ]; then
+ rv=0
+ fi
+ atf_check_equal $rv 0
+
+ xbps-install -r root --repository=repo -ydv B
+ atf_check_equal $? 0
+
+ if [ -e root/usr/bin/fileB ]; then
+ lnk=$(readlink root/usr/bin/file)
+ if [ "$lnk" = "/usr/bin/fileB" ]; then
+ rv=0
+ fi
+ echo "A lnk: $lnk"
+ fi
+ atf_check_equal $rv 0
+
+ xbps-remove -r root -yvd B
+ rv=1
+ if [ ! -L root/usr/bin/file -a ! -e root/usr/bin/fileB ]; then
+ rv=0
+ fi
+ atf_check_equal $rv 0
+}
+
+atf_test_case set_pkg
+
+set_pkg_head() {
+ atf_set "descr" "xbps-alternatives: set all alternative groups from pkg"
+}
+set_pkg_body() {
+ mkdir -p repo pkg_A/usr/bin pkg_B/usr/bin
+ touch pkg_A/usr/bin/A1 pkg_A/usr/bin/A2 pkg_B/usr/bin/B1 pkg_B/usr/bin/B2
+ cd repo
+ xbps-create -A noarch -n A-1.1_1 -s "A pkg" --alternatives "1:/usr/bin/1:/usr/bin/A1 2:/usr/bin/2:/usr/bin/A2" ../pkg_A
+ atf_check_equal $? 0
+ xbps-create -A noarch -n B-1.1_1 -s "B pkg" --alternatives "1:/usr/bin/1:/usr/bin/B1 2:/usr/bin/2:/usr/bin/B2" ../pkg_B
+ atf_check_equal $? 0
+ xbps-rindex -d -a $PWD/*.xbps
+ atf_check_equal $? 0
+ cd ..
+
+ xbps-install -r root --repository=repo -ydv A B
+ atf_check_equal $? 0
+ xbps-alternatives -r root -s B
+ atf_check_equal $? 0
+
+ rv=1
+ if [ -e root/usr/bin/B1 ]; then
+ lnk=$(readlink root/usr/bin/1)
+ if [ "$lnk" = "/usr/bin/B1" ]; then
+ rv=0
+ fi
+ echo "A lnk: $lnk"
+ fi
+ atf_check_equal $rv 0
+
+ rv=1
+ if [ -e root/usr/bin/B2 ]; then
+ lnk=$(readlink root/usr/bin/2)
+ if [ "$lnk" = "/usr/bin/B2" ]; then
+ rv=0
+ fi
+ echo "A lnk: $lnk"
+ fi
+ atf_check_equal $rv 0
+
+ xbps-alternatives -r root -s A
+ atf_check_equal $? 0
+
+ rv=1
+ if [ -e root/usr/bin/A1 ]; then
+ lnk=$(readlink root/usr/bin/1)
+ if [ "$lnk" = "/usr/bin/A1" ]; then
+ rv=0
+ fi
+ echo "A lnk: $lnk"
+ fi
+ atf_check_equal $rv 0
+
+ rv=1
+ if [ -e root/usr/bin/A2 ]; then
+ lnk=$(readlink root/usr/bin/2)
+ if [ "$lnk" = "/usr/bin/A2" ]; then
+ rv=0
+ fi
+ echo "A lnk: $lnk"
+ fi
+ atf_check_equal $rv 0
+}
+
+atf_test_case set_pkg_group
+
+set_pkg_group_head() {
+ atf_set "descr" "xbps-alternatives: set one alternative group from pkg"
+}
+set_pkg_group_body() {
+ mkdir -p repo pkg_A/usr/bin pkg_B/usr/bin
+ touch pkg_A/usr/bin/A1 pkg_A/usr/bin/A2 pkg_B/usr/bin/B1 pkg_B/usr/bin/B2
+ cd repo
+ xbps-create -A noarch -n A-1.1_1 -s "A pkg" --alternatives "1:/usr/bin/1:/usr/bin/A1 2:/usr/bin/2:/usr/bin/A2" ../pkg_A
+ atf_check_equal $? 0
+ xbps-create -A noarch -n B-1.1_1 -s "B pkg" --alternatives "1:/usr/bin/1:/usr/bin/B1 2:/usr/bin/2:/usr/bin/B2" ../pkg_B
+ atf_check_equal $? 0
+ xbps-rindex -d -a $PWD/*.xbps
+ atf_check_equal $? 0
+ cd ..
+
+ xbps-install -r root --repository=repo -ydv A B
+ atf_check_equal $? 0
+ xbps-alternatives -r root -s B -g 2
+ atf_check_equal $? 0
+
+ rv=1
+ if [ -e root/usr/bin/B1 ]; then
+ lnk=$(readlink root/usr/bin/1)
+ if [ "$lnk" = "/usr/bin/A1" ]; then
+ rv=0
+ fi
+ echo "A lnk: $lnk"
+ fi
+ atf_check_equal $rv 0
+
+ rv=1
+ if [ -e root/usr/bin/B2 ]; then
+ lnk=$(readlink root/usr/bin/2)
+ if [ "$lnk" = "/usr/bin/B2" ]; then
+ rv=0
+ fi
+ echo "A lnk: $lnk"
+ fi
+ atf_check_equal $rv 0
+
+}
+
+atf_init_test_cases() {
+ atf_add_test_case register_one
+ atf_add_test_case register_multi
+ atf_add_test_case unregister_one
+ atf_add_test_case unregister_multi
+ atf_add_test_case set_pkg
+ atf_add_test_case set_pkg_group
+}