[PATCH] pacman: implement universal transactions
morganamilo
morganamilo at archlinux.org
Mon Feb 21 21:15:51 UTC 2022
This allows using -S, -U and -R in one command:
pacman -S foo -R bar
To make this work some breaking changes have made.
Targets have to come after the operation:
pacman -S foo //works
pacman -Syu //works
pacman -yuS //works
pacman foo -S //doesn't work
This could be supported with some code to copy all targets before the
first operation into the first operation's target list.
And -u as a short for --unneeded has been removed as it conflicts with
--sysupgrade. However as -u/--sysupgrade is bound to -S, accidently
doing `pacman -Ru` will not accidently cause a system upgrade.
Another quirk with the UI is that -S has many non transaction related
flags, -Sc -Sg -Sl -Si. These have been split off as "sync only" flags.
Meaning they show up with `pacman -Si foo` but will be invalid on
`pacman -R bar -Si foo`.
Also when -R'ing and -S'ing the same package in some command it's
treated as a full uninstall then reinstall. The backup files are
.pacsave'd and the install reason is set to explicit. I feel like this
behavious is good. This also allows you to wipe config files which what
--nokeep was intending to solve.
Other flags just have to have the op they belong to to be used for them
to be valid.
For example:
pacman -Rn foo //works
pacman -S -Rn //works
pacman -Sn //doesn't work
pacman -Sn -R //works
We could posibly drop these flags belonging to each operation and just
make them generic transaction flags.
Implements FS#9694
Signed-off-by: morganamilo <morganamilo at archlinux.org>
---
src/pacman/conf.c | 10 +-
src/pacman/conf.h | 29 +-
src/pacman/meson.build | 1 +
src/pacman/pacman.c | 467 +++++++++++++++++++-------------
src/pacman/pacman.h | 12 +-
src/pacman/remove.c | 99 +------
src/pacman/sync.c | 403 +--------------------------
src/pacman/trans.c | 478 +++++++++++++++++++++++++++++++++
src/pacman/upgrade.c | 26 +-
src/pacman/util.c | 15 +-
test/pacman/tests/remove049.py | 4 +-
11 files changed, 814 insertions(+), 730 deletions(-)
create mode 100644 src/pacman/trans.c
diff --git a/src/pacman/conf.c b/src/pacman/conf.c
index f9edf75b..9a98264c 100644
--- a/src/pacman/conf.c
+++ b/src/pacman/conf.c
@@ -105,7 +105,8 @@ config_t *config_new(void)
return NULL;
}
/* defaults which may get overridden later */
- newconfig->op = PM_OP_MAIN;
+ newconfig->op = 0;
+ newconfig->curr_op = 0;
newconfig->logmask = ALPM_LOG_ERROR | ALPM_LOG_WARNING;
newconfig->configfile = strdup(CONFFILE);
if(alpm_capabilities() & ALPM_CAPABILITY_SIGNATURES) {
@@ -131,6 +132,13 @@ config_t *config_new(void)
return newconfig;
}
+void targets_free(targets_t *targets) {
+ FREELIST(targets->targets);
+ FREELIST(targets->sync);
+ FREELIST(targets->upgrade);
+ FREELIST(targets->remove);
+}
+
int config_free(config_t *oldconfig)
{
if(oldconfig == NULL) {
diff --git a/src/pacman/conf.h b/src/pacman/conf.h
index f7916ca9..93813ef4 100644
--- a/src/pacman/conf.h
+++ b/src/pacman/conf.h
@@ -43,8 +43,17 @@ typedef struct __config_repo_t {
int siglevel_mask;
} config_repo_t;
+
+typedef struct __targets_t {
+ alpm_list_t *targets;
+ alpm_list_t *sync;
+ alpm_list_t *upgrade;
+ alpm_list_t *remove;
+} targets_t;
+
typedef struct __config_t {
unsigned short op;
+ unsigned short curr_op;
unsigned short quiet;
unsigned short verbose;
unsigned short version;
@@ -142,14 +151,15 @@ typedef struct __config_t {
/* Operations */
enum {
- PM_OP_MAIN = 1,
- PM_OP_REMOVE,
- PM_OP_UPGRADE,
- PM_OP_QUERY,
- PM_OP_SYNC,
- PM_OP_DEPTEST,
- PM_OP_DATABASE,
- PM_OP_FILES
+ PM_OP_INVALID = 1 << 0,
+ PM_OP_REMOVE = 1 << 1,
+ PM_OP_UPGRADE = 1 << 2,
+ PM_OP_QUERY = 1 << 3,
+ PM_OP_SYNC = 1 << 4,
+ PM_OP_DEPTEST = 1 << 5,
+ PM_OP_DATABASE = 1 << 6,
+ PM_OP_FILES = 1 << 7,
+ PM_OP_TRANS = PM_OP_SYNC | PM_OP_UPGRADE | PM_OP_REMOVE
};
/* Long Operations */
@@ -235,6 +245,9 @@ enum {
/* global config variable */
extern config_t *config;
+
+void targets_free(targets_t *targets);
+
void enable_colors(int colors);
config_t *config_new(void);
int config_free(config_t *oldconfig);
diff --git a/src/pacman/meson.build b/src/pacman/meson.build
index 6926f676..6bf31495 100644
--- a/src/pacman/meson.build
+++ b/src/pacman/meson.build
@@ -10,6 +10,7 @@ pacman_sources = files('''
remove.c
sighandler.h sighandler.c
sync.c
+ trans.c
callback.h callback.c
upgrade.c
util.h util.c
diff --git a/src/pacman/pacman.c b/src/pacman/pacman.c
index ea536a47..829747c2 100644
--- a/src/pacman/pacman.c
+++ b/src/pacman/pacman.c
@@ -47,7 +47,7 @@
#include "sighandler.h"
/* list of targets specified on command line */
-static alpm_list_t *pm_targets;
+static targets_t pm_targets;
/* Used to sort the options in --help */
static int options_cmp(const void *p1, const void *p2)
@@ -104,7 +104,7 @@ static void usage(int op, const char * const myname)
char const *const str_opr = _("operation");
/* please limit your strings to 80 characters in width */
- if(op == PM_OP_MAIN) {
+ if(!op) {
printf("%s: %s <%s> [...]\n", str_usg, myname, str_opr);
printf(_("operations:\n"));
printf(" %s {-h --help}\n", myname);
@@ -125,11 +125,10 @@ static void usage(int op, const char * const myname)
addlist(_(" -c, --cascade remove packages and all packages that depend on them\n"));
addlist(_(" -n, --nosave remove configuration files\n"));
addlist(_(" -s, --recursive remove unnecessary dependencies\n"
- " (-ss includes explicitly installed dependencies)\n"));
- addlist(_(" -u, --unneeded remove unneeded packages\n"));
+ " (-ss includes explicitly installed dependencies)\n"));
+ addlist(_(" --unneeded remove unneeded packages\n"));
} else if(op == PM_OP_UPGRADE) {
printf("%s: %s {-U --upgrade} [%s] <%s>\n", str_usg, myname, str_opt, str_file);
- addlist(_(" --needed do not reinstall up to date packages\n"));
printf("%s:\n", str_opt);
} else if(op == PM_OP_QUERY) {
printf("%s: %s {-Q --query} [%s] [%s]\n", str_usg, myname, str_opt, str_pkg);
@@ -163,7 +162,6 @@ static void usage(int op, const char * const myname)
addlist(_(" -u, --sysupgrade upgrade installed packages (-uu enables downgrades)\n"));
addlist(_(" -y, --refresh download fresh package databases from the server\n"
" (-yy to force a refresh even if up to date)\n"));
- addlist(_(" --needed do not reinstall up to date packages\n"));
} else if(op == PM_OP_DATABASE) {
printf("%s: %s {-D --database} <%s> <%s>\n", str_usg, myname, str_opt, str_pkg);
printf("%s:\n", str_opt);
@@ -185,29 +183,27 @@ static void usage(int op, const char * const myname)
addlist(_(" --machinereadable\n"
" produce machine-readable output\n"));
}
- switch(op) {
- case PM_OP_SYNC:
- case PM_OP_UPGRADE:
- addlist(_(" -w, --downloadonly download packages but do not install/upgrade anything\n"));
- addlist(_(" --overwrite <glob>\n"
- " overwrite conflicting files (can be used more than once)\n"));
- addlist(_(" --asdeps install packages as non-explicitly installed\n"));
- addlist(_(" --asexplicit install packages as explicitly installed\n"));
- addlist(_(" --ignore <pkg> ignore a package upgrade (can be used more than once)\n"));
- addlist(_(" --ignoregroup <grp>\n"
- " ignore a group upgrade (can be used more than once)\n"));
- __attribute__((fallthrough));
- case PM_OP_REMOVE:
- addlist(_(" -d, --nodeps skip dependency version checks (-dd to skip all checks)\n"));
- addlist(_(" --assume-installed <package=version>\n"
- " add a virtual package to satisfy dependencies\n"));
- addlist(_(" --dbonly only modify database entries, not package files\n"));
- addlist(_(" --noprogressbar do not show a progress bar when downloading files\n"));
- addlist(_(" --noscriptlet do not execute the install scriptlet if one exists\n"));
- addlist(_(" -p, --print print the targets instead of performing the operation\n"));
- addlist(_(" --print-format <string>\n"
- " specify how the targets should be printed\n"));
- break;
+ if(op & (PM_OP_UPGRADE | PM_OP_SYNC)) {
+ addlist(_(" --needed do not reinstall up to date packages\n"));
+ addlist(_(" -w, --downloadonly download packages but do not install/upgrade anything\n"));
+ addlist(_(" --overwrite <glob>\n"
+ " overwrite conflicting files (can be used more than once)\n"));
+ addlist(_(" --asdeps install packages as non-explicitly installed\n"));
+ addlist(_(" --asexplicit install packages as explicitly installed\n"));
+ addlist(_(" --ignore <pkg> ignore a package upgrade (can be used more than once)\n"));
+ addlist(_(" --ignoregroup <grp>\n"
+ " ignore a group upgrade (can be used more than once)\n"));
+ }
+ if(op & PM_OP_TRANS) {
+ addlist(_(" -d, --nodeps skip dependency version checks (-dd to skip all checks)\n"));
+ addlist(_(" --assume-installed <package=version>\n"
+ " add a virtual package to satisfy dependencies\n"));
+ addlist(_(" --dbonly only modify database entries, not package files\n"));
+ addlist(_(" --noprogressbar do not show a progress bar when downloading files\n"));
+ addlist(_(" --noscriptlet do not execute the install scriptlet if one exists\n"));
+ addlist(_(" -p, --print print the targets instead of performing the operation\n"));
+ addlist(_(" --print-format <string>\n"
+ " specify how the targets should be printed\n"));
}
addlist(_(" -b, --dbpath <path> set an alternate database location\n"));
@@ -301,7 +297,7 @@ static void cleanup(int ret)
}
/* free memory */
- FREELIST(pm_targets);
+ targets_free(&pm_targets);
console_cursor_show();
exit(ret);
}
@@ -327,6 +323,24 @@ static int parsearg_util_addlist(alpm_list_t **list)
return 0;
}
+static void set_op(int opt)
+{
+ int trans = (config->op & PM_OP_TRANS) != 0;
+ int setting_trans = (opt & PM_OP_TRANS) != 0;
+
+ /* we allow -SS so allow -QQ for sake of consistency */
+ if(config->op == opt) {
+ return;
+ }
+
+ if(!config->op || (trans && setting_trans)) {
+ config->op |= opt;
+ config->curr_op = opt;
+ } else {
+ config->op |= PM_OP_INVALID;
+ }
+}
+
/** Helper function for parsing operation from command-line arguments.
* @param opt Keycode returned by getopt_long
* @param dryrun If nonzero, application state is NOT changed
@@ -338,25 +352,25 @@ static int parsearg_op(int opt, int dryrun)
/* operations */
case 'D':
if(dryrun) break;
- config->op = (config->op != PM_OP_MAIN ? 0 : PM_OP_DATABASE); break;
+ set_op(PM_OP_DATABASE); break;
case 'F':
if(dryrun) break;
- config->op = (config->op != PM_OP_MAIN ? 0 : PM_OP_FILES); break;
+ set_op(PM_OP_FILES); break;
case 'Q':
if(dryrun) break;
- config->op = (config->op != PM_OP_MAIN ? 0 : PM_OP_QUERY); break;
+ set_op(PM_OP_QUERY); break;
case 'R':
if(dryrun) break;
- config->op = (config->op != PM_OP_MAIN ? 0 : PM_OP_REMOVE); break;
+ set_op(PM_OP_REMOVE); break;
case 'S':
if(dryrun) break;
- config->op = (config->op != PM_OP_MAIN ? 0 : PM_OP_SYNC); break;
+ set_op(PM_OP_SYNC); break;
case 'T':
if(dryrun) break;
- config->op = (config->op != PM_OP_MAIN ? 0 : PM_OP_DEPTEST); break;
+ set_op(PM_OP_DEPTEST); break;
case 'U':
if(dryrun) break;
- config->op = (config->op != PM_OP_MAIN ? 0 : PM_OP_UPGRADE); break;
+ set_op(PM_OP_UPGRADE); break;
case 'V':
if(dryrun) break;
config->version = 1; break;
@@ -369,6 +383,7 @@ static int parsearg_op(int opt, int dryrun)
return 0;
}
+
/** Helper functions for parsing command-line arguments.
* @param opt Keycode returned by getopt_long
* @return 0 on success, 1 on failure
@@ -619,62 +634,8 @@ static void checkargs_query(void)
"--native", "--foreign");
}
-/* options common to -S -R -U */
-static int parsearg_trans(int opt)
-{
- switch(opt) {
- case OP_NODEPS:
- case 'd':
- if(config->flags & ALPM_TRANS_FLAG_NODEPVERSION) {
- config->flags |= ALPM_TRANS_FLAG_NODEPS;
- } else {
- config->flags |= ALPM_TRANS_FLAG_NODEPVERSION;
- }
- break;
- case OP_DBONLY:
- config->flags |= ALPM_TRANS_FLAG_DBONLY;
- config->flags |= ALPM_TRANS_FLAG_NOSCRIPTLET;
- config->flags |= ALPM_TRANS_FLAG_NOHOOKS;
- break;
- case OP_NOPROGRESSBAR:
- config->noprogressbar = 1;
- break;
- case OP_NOSCRIPTLET:
- config->flags |= ALPM_TRANS_FLAG_NOSCRIPTLET;
- break;
- case OP_PRINT:
- case 'p':
- config->print = 1;
- break;
- case OP_PRINTFORMAT:
- config->print = 1;
- free(config->print_format);
- config->print_format = strdup(optarg);
- break;
- case OP_ASSUMEINSTALLED:
- parsearg_util_addlist(&(config->assumeinstalled));
- break;
- default:
- return 1;
- }
- return 0;
-}
-
-static void checkargs_trans(void)
-{
- if(config->print) {
- invalid_opt(config->flags & ALPM_TRANS_FLAG_DBONLY,
- "--print", "--dbonly");
- invalid_opt(config->flags & ALPM_TRANS_FLAG_NOSCRIPTLET,
- "--print", "--noscriptlet");
- }
-}
-
static int parsearg_remove(int opt)
{
- if(parsearg_trans(opt) == 0) {
- return 0;
- }
switch(opt) {
case OP_CASCADE:
case 'c':
@@ -693,7 +654,6 @@ static int parsearg_remove(int opt)
}
break;
case OP_UNNEEDED:
- case 'u':
config->flags |= ALPM_TRANS_FLAG_UNNEEDED;
break;
default:
@@ -704,7 +664,6 @@ static int parsearg_remove(int opt)
static void checkargs_remove(void)
{
- checkargs_trans();
if(config->flags & ALPM_TRANS_FLAG_NOSAVE) {
invalid_opt(config->print, "--nosave", "--print");
invalid_opt(config->flags & ALPM_TRANS_FLAG_DBONLY,
@@ -715,9 +674,6 @@ static void checkargs_remove(void)
/* options common to -S -U */
static int parsearg_upgrade(int opt)
{
- if(parsearg_trans(opt) == 0) {
- return 0;
- }
switch(opt) {
case OP_OVERWRITE_FILES:
parsearg_util_addlist(&(config->overwrite_files));
@@ -750,7 +706,6 @@ static int parsearg_upgrade(int opt)
static void checkargs_upgrade(void)
{
- checkargs_trans();
invalid_opt(config->flags & ALPM_TRANS_FLAG_ALLDEPS
&& config->flags & ALPM_TRANS_FLAG_ALLEXPLICIT,
"--asdeps", "--asexplicit");
@@ -758,9 +713,6 @@ static void checkargs_upgrade(void)
static int parsearg_files(int opt)
{
- if(parsearg_trans(opt) == 0) {
- return 0;
- }
switch(opt) {
case OP_LIST:
case 'l':
@@ -799,6 +751,26 @@ static int parsearg_sync(int opt)
if(parsearg_upgrade(opt) == 0) {
return 0;
}
+ switch(opt) {
+ case OP_SYSUPGRADE:
+ case 'u':
+ (config->op_s_upgrade)++;
+ break;
+ case OP_REFRESH:
+ case 'y':
+ (config->op_s_sync)++;
+ break;
+ default:
+ return 1;
+ }
+ return 0;
+}
+
+static int parsearg_sync_only(int opt)
+{
+ if(parsearg_sync(opt) == 0) {
+ return 0;
+ }
switch(opt) {
case OP_CLEAN:
case 'c':
@@ -840,7 +812,12 @@ static int parsearg_sync(int opt)
static void checkargs_sync(void)
{
- checkargs_upgrade();
+ /* no conflicts currently */
+}
+
+static void checkargs_sync_only(void)
+{
+ checkargs_sync();
if(config->op_s_clean) {
invalid_opt(config->group, "--clean", "--groups");
invalid_opt(config->op_s_info, "--clean", "--info");
@@ -870,6 +847,89 @@ static void checkargs_sync(void)
}
}
+/* options common to -S -R -U */
+static int parsearg_trans(int opt)
+{
+ if(config->op & (PM_OP_SYNC | PM_OP_UPGRADE) && parsearg_upgrade(opt) == 0) {
+ return 0;
+ }
+ if(config->op & PM_OP_SYNC && parsearg_sync(opt) == 0) {
+ return 0;
+ }
+ if(config->op & PM_OP_REMOVE && parsearg_remove(opt) == 0) {
+ return 0;
+ }
+ switch(opt) {
+ case OP_NODEPS:
+ case 'd':
+ if(config->flags & ALPM_TRANS_FLAG_NODEPVERSION) {
+ config->flags |= ALPM_TRANS_FLAG_NODEPS;
+ } else {
+ config->flags |= ALPM_TRANS_FLAG_NODEPVERSION;
+ }
+ break;
+ case OP_DBONLY:
+ config->flags |= ALPM_TRANS_FLAG_DBONLY;
+ config->flags |= ALPM_TRANS_FLAG_NOSCRIPTLET;
+ config->flags |= ALPM_TRANS_FLAG_NOHOOKS;
+ break;
+ case OP_NOPROGRESSBAR:
+ config->noprogressbar = 1;
+ break;
+ case OP_NOSCRIPTLET:
+ config->flags |= ALPM_TRANS_FLAG_NOSCRIPTLET;
+ break;
+ case OP_PRINT:
+ case 'p':
+ config->print = 1;
+ break;
+ case OP_PRINTFORMAT:
+ config->print = 1;
+ free(config->print_format);
+ config->print_format = strdup(optarg);
+ break;
+ case OP_ASSUMEINSTALLED:
+ parsearg_util_addlist(&(config->assumeinstalled));
+ break;
+ default:
+ return 1;
+ }
+ return 0;
+}
+
+static void checkargs_trans(void)
+{
+ if(config->op & (PM_OP_SYNC | PM_OP_UPGRADE)) {
+ checkargs_upgrade();
+ }
+ if(config->op & PM_OP_SYNC) {
+ checkargs_sync();
+ }
+ if(config->op & PM_OP_REMOVE) {
+ checkargs_remove();
+ }
+
+ if(config->print) {
+ invalid_opt(config->flags & ALPM_TRANS_FLAG_DBONLY,
+ "--print", "--dbonly");
+ invalid_opt(config->flags & ALPM_TRANS_FLAG_NOSCRIPTLET,
+ "--print", "--noscriptlet");
+ }
+}
+
+static void add_target(char *targ)
+{
+ if(config->curr_op & PM_OP_SYNC) {
+ pm_targets.sync = alpm_list_add(pm_targets.sync, targ);
+ } else if(config->curr_op & PM_OP_REMOVE) {
+ pm_targets.remove = alpm_list_add(pm_targets.remove, targ);
+ } else if(config->curr_op & PM_OP_UPGRADE) {
+ pm_targets.upgrade = alpm_list_add(pm_targets.upgrade, targ);
+ } else {
+ pm_targets.targets = alpm_list_add(pm_targets.targets, targ);
+ }
+}
+
/** Parse command-line arguments for each operation.
* @param argc argc
* @param argv argv
@@ -880,7 +940,7 @@ static int parseargs(int argc, char *argv[])
int opt;
int option_index = 0;
int result;
- const char *optstring = "DFQRSTUVb:cdefghiklmnopqr:stuvwxy";
+ const char *optstring = "-DFQRSTUVb:cdefghiklmnopqr:stuvwxy";
static const struct option opts[] =
{
{"database", no_argument, 0, 'D'},
@@ -955,6 +1015,11 @@ static int parseargs(int argc, char *argv[])
while((opt = getopt_long(argc, argv, optstring, opts, &option_index)) != -1) {
if(opt == 0) {
continue;
+ } else if(opt == 1) {
+ char *targ = strdup(optarg);
+ /* add the target to our target array */
+ add_target(targ);
+ continue;
} else if(opt == '?') {
/* unknown option, getopt printed an error */
return 1;
@@ -962,8 +1027,8 @@ static int parseargs(int argc, char *argv[])
parsearg_op(opt, 0);
}
- if(config->op == 0) {
- pm_printf(ALPM_LOG_ERROR, _("only one operation may be used at a time\n"));
+ if(config->op & PM_OP_INVALID) {
+ pm_printf(ALPM_LOG_ERROR, _("these operations can not be used together\n"));
return 1;
}
if(config->help) {
@@ -978,7 +1043,7 @@ static int parseargs(int argc, char *argv[])
/* parse all other options */
optind = 1;
while((opt = getopt_long(argc, argv, optstring, opts, &option_index)) != -1) {
- if(opt == 0) {
+ if(opt == 0 || opt == 1) {
continue;
} else if(opt == '?') {
/* this should have failed during first pass already */
@@ -995,14 +1060,9 @@ static int parseargs(int argc, char *argv[])
case PM_OP_QUERY:
result = parsearg_query(opt);
break;
- case PM_OP_REMOVE:
- result = parsearg_remove(opt);
- break;
case PM_OP_SYNC:
- result = parsearg_sync(opt);
- break;
- case PM_OP_UPGRADE:
- result = parsearg_upgrade(opt);
+ /* handle -Si -Sg etc if -S is the only trans op */
+ result = parsearg_sync_only(opt);
break;
case PM_OP_FILES:
result = parsearg_files(opt);
@@ -1016,6 +1076,13 @@ static int parseargs(int argc, char *argv[])
continue;
}
+ if(config->op & PM_OP_TRANS) {
+ result = parsearg_trans(opt);
+ }
+ if(result == 0) {
+ continue;
+ }
+
/* fall back to global options */
result = parsearg_global(opt);
if(result != 0) {
@@ -1032,7 +1099,7 @@ static int parseargs(int argc, char *argv[])
while(optind < argc) {
/* add the target to our target array */
- pm_targets = alpm_list_add(pm_targets, strdup(argv[optind]));
+ add_target(strdup(argv[optind]));
optind++;
}
@@ -1044,7 +1111,7 @@ static int parseargs(int argc, char *argv[])
/* no conflicting options */
break;
case PM_OP_SYNC:
- checkargs_sync();
+ checkargs_sync_only();
break;
case PM_OP_QUERY:
checkargs_query();
@@ -1061,10 +1128,83 @@ static int parseargs(int argc, char *argv[])
default:
break;
}
+ if(config->op & PM_OP_TRANS) {
+ checkargs_trans();
+ }
return 0;
}
+static void parse_stdin(alpm_list_t **targets)
+{
+ if(!isatty(fileno(stdin))) {
+ int target_found = 0;
+ char *vdata, *line = NULL;
+ size_t line_size = 0;
+ ssize_t nread;
+
+ /* remove the '-' from the list */
+ *targets = alpm_list_remove_str(*targets, "-", &vdata);
+ free(vdata);
+
+ while((nread = getline(&line, &line_size, stdin)) != -1) {
+ if(line[nread - 1] == '\n') {
+ /* remove trailing newline */
+ line[nread - 1] = '\0';
+ }
+ if(line[0] == '\0') {
+ /* skip empty lines */
+ continue;
+ }
+ if(!alpm_list_append_strdup(targets, line)) {
+ break;
+ }
+ target_found = 1;
+ }
+ free(line);
+
+ if(ferror(stdin)) {
+ pm_printf(ALPM_LOG_ERROR,
+ _("failed to read arguments from stdin: (%s)\n"), strerror(errno));
+ cleanup(EXIT_FAILURE);
+ }
+
+ if(!freopen(ctermid(NULL), "r", stdin)) {
+ pm_printf(ALPM_LOG_ERROR, _("failed to reopen stdin for reading: (%s)\n"),
+ strerror(errno));
+ }
+
+ if(!target_found) {
+ pm_printf(ALPM_LOG_ERROR, _("argument '-' specified with empty stdin\n"));
+ cleanup(1);
+ }
+ } else {
+ /* do not read stdin from terminal */
+ pm_printf(ALPM_LOG_ERROR, _("argument '-' specified without input on stdin\n"));
+ cleanup(1);
+ }
+}
+
+
+static void parse_stdins(void)
+{
+ alpm_list_t **lists[4] = {&pm_targets.targets, &pm_targets.sync, &pm_targets.upgrade, &pm_targets.remove};
+ int replaced = 0;
+
+ for(int i = 0; i < 4; i++) {
+ alpm_list_t **list = lists[i];
+ if(alpm_list_find_str(*list, "-")) {
+ if(replaced) {
+ pm_printf(ALPM_LOG_ERROR, _("argument '-' can not be specified multiple times\n"));
+ cleanup(1);
+ }
+ parse_stdin(list);
+ replaced++;
+ }
+ }
+}
+
+
/** Print command line to logfile.
* @param argc
* @param argv
@@ -1138,54 +1278,7 @@ int main(int argc, char *argv[])
}
/* we support reading targets from stdin if a cmdline parameter is '-' */
- if(alpm_list_find_str(pm_targets, "-")) {
- if(!isatty(fileno(stdin))) {
- int target_found = 0;
- char *vdata, *line = NULL;
- size_t line_size = 0;
- ssize_t nread;
-
- /* remove the '-' from the list */
- pm_targets = alpm_list_remove_str(pm_targets, "-", &vdata);
- free(vdata);
-
- while((nread = getline(&line, &line_size, stdin)) != -1) {
- if(line[nread - 1] == '\n') {
- /* remove trailing newline */
- line[nread - 1] = '\0';
- }
- if(line[0] == '\0') {
- /* skip empty lines */
- continue;
- }
- if(!alpm_list_append_strdup(&pm_targets, line)) {
- break;
- }
- target_found = 1;
- }
- free(line);
-
- if(ferror(stdin)) {
- pm_printf(ALPM_LOG_ERROR,
- _("failed to read arguments from stdin: (%s)\n"), strerror(errno));
- cleanup(EXIT_FAILURE);
- }
-
- if(!freopen(ctermid(NULL), "r", stdin)) {
- pm_printf(ALPM_LOG_ERROR, _("failed to reopen stdin for reading: (%s)\n"),
- strerror(errno));
- }
-
- if(!target_found) {
- pm_printf(ALPM_LOG_ERROR, _("argument '-' specified with empty stdin\n"));
- cleanup(1);
- }
- } else {
- /* do not read stdin from terminal */
- pm_printf(ALPM_LOG_ERROR, _("argument '-' specified without input on stdin\n"));
- cleanup(1);
- }
- }
+ parse_stdins();
if(config->sysroot && (chroot(config->sysroot) != 0 || chdir("/") != 0)) {
pm_printf(ALPM_LOG_ERROR,
@@ -1233,7 +1326,10 @@ int main(int argc, char *argv[])
printf("Lock File : %s\n", alpm_option_get_lockfile(config->handle));
printf("Log File : %s\n", alpm_option_get_logfile(config->handle));
printf("GPG Dir : %s\n", alpm_option_get_gpgdir(config->handle));
- list_display("Targets :", pm_targets, 0);
+ list_display("Targets :", pm_targets.targets, 0);
+ list_display("Sync :", pm_targets.sync, 0);
+ list_display("Upgrade :", pm_targets.upgrade, 0);
+ list_display("Remove :", pm_targets.remove, 0);
}
/* Log command line */
@@ -1242,31 +1338,26 @@ int main(int argc, char *argv[])
}
/* start the requested operation */
- switch(config->op) {
- case PM_OP_DATABASE:
- ret = pacman_database(pm_targets);
- break;
- case PM_OP_REMOVE:
- ret = pacman_remove(pm_targets);
- break;
- case PM_OP_UPGRADE:
- ret = pacman_upgrade(pm_targets);
- break;
- case PM_OP_QUERY:
- ret = pacman_query(pm_targets);
- break;
- case PM_OP_SYNC:
- ret = pacman_sync(pm_targets);
- break;
- case PM_OP_DEPTEST:
- ret = pacman_deptest(pm_targets);
- break;
- case PM_OP_FILES:
- ret = pacman_files(pm_targets);
- break;
- default:
- pm_printf(ALPM_LOG_ERROR, _("no operation specified (use -h for help)\n"));
- ret = EXIT_FAILURE;
+ if(config->op & PM_OP_TRANS) {
+ ret = do_transaction(&pm_targets);
+ } else {
+ switch(config->op) {
+ case PM_OP_DATABASE:
+ ret = pacman_database(pm_targets.targets);
+ break;
+ case PM_OP_QUERY:
+ ret = pacman_query(pm_targets.targets);
+ break;
+ case PM_OP_DEPTEST:
+ ret = pacman_deptest(pm_targets.targets);
+ break;
+ case PM_OP_FILES:
+ ret = pacman_files(pm_targets.targets);
+ break;
+ default:
+ pm_printf(ALPM_LOG_ERROR, _("no operation specified (use -h for help)\n"));
+ ret = EXIT_FAILURE;
+ }
}
cleanup(ret);
diff --git a/src/pacman/pacman.h b/src/pacman/pacman.h
index 2d5229ed..b5a1061a 100644
--- a/src/pacman/pacman.h
+++ b/src/pacman/pacman.h
@@ -22,6 +22,8 @@
#include <alpm_list.h>
+#include "conf.h"
+
#define PACMAN_CALLER_PREFIX "PACMAN"
/* database.c */
@@ -32,12 +34,14 @@ int pacman_deptest(alpm_list_t *targets);
int pacman_files(alpm_list_t *files);
/* query.c */
int pacman_query(alpm_list_t *targets);
-/* remove.c */
-int pacman_remove(alpm_list_t *targets);
/* sync.c */
int pacman_sync(alpm_list_t *targets);
-int sync_prepare_execute(void);
+/* remove.c */
+int load_remove(alpm_list_t *targets);
/* upgrade.c */
-int pacman_upgrade(alpm_list_t *targets);
+int load_upgrade(alpm_list_t *targets);
+/* trans.c */
+int do_transaction(targets_t *targets);
+
#endif /* PM_PACMAN_H */
diff --git a/src/pacman/remove.c b/src/pacman/remove.c
index 98f8bcda..ff11b6e6 100644
--- a/src/pacman/remove.c
+++ b/src/pacman/remove.c
@@ -18,7 +18,6 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#include <fnmatch.h>
#include <stdlib.h>
#include <stdio.h>
@@ -30,11 +29,6 @@
#include "util.h"
#include "conf.h"
-static int fnmatch_cmp(const void *pattern, const void *string)
-{
- return fnmatch(pattern, string, 0);
-}
-
static int remove_target(const char *target)
{
alpm_pkg_t *pkg;
@@ -69,28 +63,16 @@ static int remove_target(const char *target)
return 0;
}
-/**
- * @brief Remove a specified list of packages.
- *
- * @param targets a list of packages (as strings) to remove from the system
- *
- * @return 0 on success, 1 on failure
- */
-int pacman_remove(alpm_list_t *targets)
+int load_remove(alpm_list_t *targets)
{
int retval = 0;
- alpm_list_t *i, *data = NULL;
+ alpm_list_t *i;
if(targets == NULL) {
pm_printf(ALPM_LOG_ERROR, _("no targets specified (use -h for help)\n"));
return 1;
}
- /* Step 0: create a new transaction */
- if(trans_init(config->flags, 0) == -1) {
- return 1;
- }
-
/* Step 1: add targets to the created transaction */
for(i = targets; i; i = alpm_list_next(i)) {
char *target = i->data;
@@ -102,81 +84,6 @@ int pacman_remove(alpm_list_t *targets)
}
}
- if(retval == 1) {
- goto cleanup;
- }
-
- /* Step 2: prepare the transaction based on its type, targets and flags */
- if(alpm_trans_prepare(config->handle, &data) == -1) {
- alpm_errno_t err = alpm_errno(config->handle);
- pm_printf(ALPM_LOG_ERROR, _("failed to prepare transaction (%s)\n"),
- alpm_strerror(err));
- switch(err) {
- case ALPM_ERR_UNSATISFIED_DEPS:
- for(i = data; i; i = alpm_list_next(i)) {
- alpm_depmissing_t *miss = i->data;
- char *depstring = alpm_dep_compute_string(miss->depend);
- colon_printf(_("removing %s breaks dependency '%s' required by %s\n"),
- miss->causingpkg, depstring, miss->target);
- free(depstring);
- alpm_depmissing_free(miss);
- }
- break;
- default:
- break;
- }
- alpm_list_free(data);
- retval = 1;
- goto cleanup;
- }
-
- /* Search for holdpkg in target list */
- int holdpkg = 0;
- for(i = alpm_trans_get_remove(config->handle); i; i = alpm_list_next(i)) {
- alpm_pkg_t *pkg = i->data;
- if(alpm_list_find(config->holdpkg, alpm_pkg_get_name(pkg), fnmatch_cmp)) {
- pm_printf(ALPM_LOG_WARNING, _("%s is designated as a HoldPkg.\n"),
- alpm_pkg_get_name(pkg));
- holdpkg = 1;
- }
- }
- if(holdpkg && (noyes(_("HoldPkg was found in target list. Do you want to continue?")) == 0)) {
- retval = 1;
- goto cleanup;
- }
-
- /* Step 3: actually perform the removal */
- alpm_list_t *pkglist = alpm_trans_get_remove(config->handle);
- if(pkglist == NULL) {
- printf(_(" there is nothing to do\n"));
- goto cleanup; /* we are done */
- }
-
- if(config->print) {
- print_packages(pkglist);
- goto cleanup;
- }
-
- /* print targets and ask user confirmation */
- display_targets();
- printf("\n");
- if(yesno(_("Do you want to remove these packages?")) == 0) {
- retval = 1;
- goto cleanup;
- }
-
- if(alpm_trans_commit(config->handle, &data) == -1) {
- pm_printf(ALPM_LOG_ERROR, _("failed to commit transaction (%s)\n"),
- alpm_strerror(alpm_errno(config->handle)));
- retval = 1;
- }
-
- FREELIST(data);
-
- /* Step 4: release transaction resources */
-cleanup:
- if(trans_release() == -1) {
- retval = 1;
- }
return retval;
}
+
diff --git a/src/pacman/sync.c b/src/pacman/sync.c
index 1f2c8cba..b72fdec3 100644
--- a/src/pacman/sync.c
+++ b/src/pacman/sync.c
@@ -35,7 +35,6 @@
#include "pacman.h"
#include "util.h"
#include "package.h"
-#include "callback.h"
#include "conf.h"
static int unlink_verbose(const char *pathname, int ignore_missing)
@@ -510,376 +509,6 @@ static int sync_list(alpm_list_t *syncs, alpm_list_t *targets)
return ret;
}
-static alpm_db_t *get_db(const char *dbname)
-{
- alpm_list_t *i;
- for(i = alpm_get_syncdbs(config->handle); i; i = i->next) {
- alpm_db_t *db = i->data;
- if(strcmp(alpm_db_get_name(db), dbname) == 0) {
- return db;
- }
- }
- return NULL;
-}
-
-static int process_pkg(alpm_pkg_t *pkg)
-{
- int ret = alpm_add_pkg(config->handle, pkg);
-
- if(ret == -1) {
- alpm_errno_t err = alpm_errno(config->handle);
- pm_printf(ALPM_LOG_ERROR, "'%s': %s\n", alpm_pkg_get_name(pkg), alpm_strerror(err));
- return 1;
- }
- config->explicit_adds = alpm_list_add(config->explicit_adds, pkg);
- return 0;
-}
-
-static int group_exists(alpm_list_t *dbs, const char *name)
-{
- alpm_list_t *i;
- for(i = dbs; i; i = i->next) {
- alpm_db_t *db = i->data;
-
- if(alpm_db_get_group(db, name)) {
- return 1;
- }
- }
-
- return 0;
-}
-
-static int process_group(alpm_list_t *dbs, const char *group, int error)
-{
- int ret = 0;
- alpm_list_t *i;
- alpm_list_t *pkgs = alpm_find_group_pkgs(dbs, group);
- int count = alpm_list_count(pkgs);
-
- if(!count) {
- if(group_exists(dbs, group)) {
- return 0;
- }
-
- pm_printf(ALPM_LOG_ERROR, _("target not found: %s\n"), group);
- return 1;
- }
-
- if(error) {
- /* we already know another target errored. there is no reason to prompt the
- * user here; we already validated the group name so just move on since we
- * won't actually be installing anything anyway. */
- goto cleanup;
- }
-
- if(config->print == 0) {
- char *array = malloc(count);
- int n = 0;
- const colstr_t *colstr = &config->colstr;
- colon_printf(_n("There is %d member in group %s%s%s:\n",
- "There are %d members in group %s%s%s:\n", count),
- count, colstr->groups, group, colstr->title);
- select_display(pkgs);
- if(!array) {
- ret = 1;
- goto cleanup;
- }
- if(multiselect_question(array, count)) {
- ret = 1;
- free(array);
- goto cleanup;
- }
- for(i = pkgs, n = 0; i; i = alpm_list_next(i)) {
- alpm_pkg_t *pkg = i->data;
-
- if(array[n++] == 0) {
- continue;
- }
-
- if(process_pkg(pkg) == 1) {
- ret = 1;
- free(array);
- goto cleanup;
- }
- }
- free(array);
- } else {
- for(i = pkgs; i; i = alpm_list_next(i)) {
- alpm_pkg_t *pkg = i->data;
-
- if(process_pkg(pkg) == 1) {
- ret = 1;
- goto cleanup;
- }
- }
- }
-
-cleanup:
- alpm_list_free(pkgs);
- return ret;
-}
-
-static int process_targname(alpm_list_t *dblist, const char *targname,
- int error)
-{
- alpm_pkg_t *pkg = alpm_find_dbs_satisfier(config->handle, dblist, targname);
-
- /* skip ignored packages when user says no */
- if(alpm_errno(config->handle) == ALPM_ERR_PKG_IGNORED) {
- pm_printf(ALPM_LOG_WARNING, _("skipping target: %s\n"), targname);
- return 0;
- }
-
- if(pkg) {
- return process_pkg(pkg);
- }
- /* fallback on group */
- return process_group(dblist, targname, error);
-}
-
-static int process_target(const char *target, int error)
-{
- /* process targets */
- char *targstring = strdup(target);
- char *targname = strchr(targstring, '/');
- int ret = 0;
- alpm_list_t *dblist;
-
- if(targname && targname != targstring) {
- alpm_db_t *db;
- const char *dbname;
- int usage;
-
- *targname = '\0';
- targname++;
- dbname = targstring;
- db = get_db(dbname);
- if(!db) {
- pm_printf(ALPM_LOG_ERROR, _("database not found: %s\n"),
- dbname);
- ret = 1;
- goto cleanup;
- }
-
- /* explicitly mark this repo as valid for installs since
- * a repo name was given with the target */
- alpm_db_get_usage(db, &usage);
- alpm_db_set_usage(db, usage|ALPM_DB_USAGE_INSTALL);
-
- dblist = alpm_list_add(NULL, db);
- ret = process_targname(dblist, targname, error);
- alpm_list_free(dblist);
-
- /* restore old usage so we don't possibly disturb later
- * targets */
- alpm_db_set_usage(db, usage);
- } else {
- targname = targstring;
- dblist = alpm_get_syncdbs(config->handle);
- ret = process_targname(dblist, targname, error);
- }
-
-cleanup:
- free(targstring);
- if(ret && access(target, R_OK) == 0) {
- pm_printf(ALPM_LOG_WARNING,
- _("'%s' is a file, did you mean %s instead of %s?\n"),
- target, "-U/--upgrade", "-S/--sync");
- }
- return ret;
-}
-
-static int sync_trans(alpm_list_t *targets)
-{
- int retval = 0;
- alpm_list_t *i;
-
- /* Step 1: create a new transaction... */
- if(trans_init(config->flags, 1) == -1) {
- return 1;
- }
-
- /* process targets */
- for(i = targets; i; i = alpm_list_next(i)) {
- const char *targ = i->data;
- if(process_target(targ, retval) == 1) {
- retval = 1;
- }
- }
-
- if(retval) {
- trans_release();
- return retval;
- }
-
- if(config->op_s_upgrade) {
- if(!config->print) {
- colon_printf(_("Starting full system upgrade...\n"));
- alpm_logaction(config->handle, PACMAN_CALLER_PREFIX,
- "starting full system upgrade\n");
- }
- if(alpm_sync_sysupgrade(config->handle, config->op_s_upgrade >= 2) == -1) {
- pm_printf(ALPM_LOG_ERROR, "%s\n", alpm_strerror(alpm_errno(config->handle)));
- trans_release();
- return 1;
- }
- }
-
- return sync_prepare_execute();
-}
-
-static void print_broken_dep(alpm_depmissing_t *miss)
-{
- char *depstring = alpm_dep_compute_string(miss->depend);
- alpm_list_t *trans_add = alpm_trans_get_add(config->handle);
- alpm_pkg_t *pkg;
- if(miss->causingpkg == NULL) {
- /* package being installed/upgraded has unresolved dependency */
- colon_printf(_("unable to satisfy dependency '%s' required by %s\n"),
- depstring, miss->target);
- } else if((pkg = alpm_pkg_find(trans_add, miss->causingpkg))) {
- /* upgrading a package breaks a local dependency */
- colon_printf(_("installing %s (%s) breaks dependency '%s' required by %s\n"),
- miss->causingpkg, alpm_pkg_get_version(pkg), depstring, miss->target);
- } else {
- /* removing a package breaks a local dependency */
- colon_printf(_("removing %s breaks dependency '%s' required by %s\n"),
- miss->causingpkg, depstring, miss->target);
- }
- free(depstring);
-}
-
-int sync_prepare_execute(void)
-{
- alpm_list_t *i, *packages, *data = NULL;
- int retval = 0;
-
- /* Step 2: "compute" the transaction based on targets and flags */
- if(alpm_trans_prepare(config->handle, &data) == -1) {
- alpm_errno_t err = alpm_errno(config->handle);
- pm_printf(ALPM_LOG_ERROR, _("failed to prepare transaction (%s)\n"),
- alpm_strerror(err));
- switch(err) {
- case ALPM_ERR_PKG_INVALID_ARCH:
- for(i = data; i; i = alpm_list_next(i)) {
- char *pkg = i->data;
- colon_printf(_("package %s does not have a valid architecture\n"), pkg);
- free(pkg);
- }
- break;
- case ALPM_ERR_UNSATISFIED_DEPS:
- for(i = data; i; i = alpm_list_next(i)) {
- print_broken_dep(i->data);
- alpm_depmissing_free(i->data);
- }
- break;
- case ALPM_ERR_CONFLICTING_DEPS:
- for(i = data; i; i = alpm_list_next(i)) {
- alpm_conflict_t *conflict = i->data;
- /* only print reason if it contains new information */
- if(conflict->reason->mod == ALPM_DEP_MOD_ANY) {
- colon_printf(_("%s and %s are in conflict\n"),
- conflict->package1, conflict->package2);
- } else {
- char *reason = alpm_dep_compute_string(conflict->reason);
- colon_printf(_("%s and %s are in conflict (%s)\n"),
- conflict->package1, conflict->package2, reason);
- free(reason);
- }
- alpm_conflict_free(conflict);
- }
- break;
- default:
- break;
- }
- retval = 1;
- goto cleanup;
- }
-
- packages = alpm_trans_get_add(config->handle);
- if(packages == NULL) {
- /* nothing to do: just exit without complaining */
- if(!config->print) {
- printf(_(" there is nothing to do\n"));
- }
- goto cleanup;
- }
-
- /* Step 3: actually perform the operation */
- if(config->print) {
- print_packages(packages);
- goto cleanup;
- }
-
- display_targets();
- printf("\n");
-
- int confirm;
- if(config->op_s_downloadonly) {
- confirm = yesno(_("Proceed with download?"));
- } else {
- confirm = yesno(_("Proceed with installation?"));
- }
- if(!confirm) {
- retval = 1;
- goto cleanup;
- }
-
- multibar_move_completed_up(true);
- if(alpm_trans_commit(config->handle, &data) == -1) {
- alpm_errno_t err = alpm_errno(config->handle);
- pm_printf(ALPM_LOG_ERROR, _("failed to commit transaction (%s)\n"),
- alpm_strerror(err));
- switch(err) {
- case ALPM_ERR_FILE_CONFLICTS:
- for(i = data; i; i = alpm_list_next(i)) {
- alpm_fileconflict_t *conflict = i->data;
- switch(conflict->type) {
- case ALPM_FILECONFLICT_TARGET:
- fprintf(stderr, _("%s exists in both '%s' and '%s'\n"),
- conflict->file, conflict->target, conflict->ctarget);
- break;
- case ALPM_FILECONFLICT_FILESYSTEM:
- if(conflict->ctarget[0]) {
- fprintf(stderr, _("%s: %s exists in filesystem (owned by %s)\n"),
- conflict->target, conflict->file, conflict->ctarget);
- } else {
- fprintf(stderr, _("%s: %s exists in filesystem\n"),
- conflict->target, conflict->file);
- }
- break;
- }
- alpm_fileconflict_free(conflict);
- }
- break;
- case ALPM_ERR_PKG_INVALID:
- case ALPM_ERR_PKG_INVALID_CHECKSUM:
- case ALPM_ERR_PKG_INVALID_SIG:
- for(i = data; i; i = alpm_list_next(i)) {
- char *filename = i->data;
- fprintf(stderr, _("%s is invalid or corrupted\n"), filename);
- free(filename);
- }
- break;
- default:
- break;
- }
- /* TODO: stderr? */
- printf(_("Errors occurred, no packages were upgraded.\n"));
- retval = 1;
- goto cleanup;
- }
-
- /* Step 4: release transaction resources */
-cleanup:
- alpm_list_free(data);
- if(trans_release() == -1) {
- retval = 1;
- }
-
- return retval;
-}
-
int pacman_sync(alpm_list_t *targets)
{
alpm_list_t *sync_dbs = NULL;
@@ -902,26 +531,8 @@ int pacman_sync(alpm_list_t *targets)
return ret;
}
- if(check_syncdbs(1, 0)) {
- return 1;
- }
-
sync_dbs = alpm_get_syncdbs(config->handle);
- if(config->op_s_sync) {
- /* grab a fresh package list */
- colon_printf(_("Synchronizing package databases...\n"));
- alpm_logaction(config->handle, PACMAN_CALLER_PREFIX,
- "synchronizing package lists\n");
- if(!sync_syncdbs(config->op_s_sync, sync_dbs)) {
- return 1;
- }
- }
-
- if(check_syncdbs(1, 1)) {
- return 1;
- }
-
/* search for a package */
if(config->op_s_search) {
return sync_search(sync_dbs, targets);
@@ -943,17 +554,9 @@ int pacman_sync(alpm_list_t *targets)
}
if(targets == NULL) {
- if(config->op_s_upgrade) {
- /* proceed */
- } else if(config->op_s_sync) {
- return 0;
- } else {
- /* don't proceed here unless we have an operation that doesn't require a
- * target list */
- pm_printf(ALPM_LOG_ERROR, _("no targets specified (use -h for help)\n"));
- return 1;
- }
+ pm_printf(ALPM_LOG_ERROR, _("no targets specified (use -h for help)\n"));
+ return 1;
}
- return sync_trans(targets);
+ return 0;
}
diff --git a/src/pacman/trans.c b/src/pacman/trans.c
new file mode 100644
index 00000000..62c7fb4d
--- /dev/null
+++ b/src/pacman/trans.c
@@ -0,0 +1,478 @@
+/*
+ * trans.c
+ *
+ * Copyright (c) 2006-2021 Pacman Development Team <pacman-dev at archlinux.org>
+ * Copyright (c) 2002-2006 by Judd Vinet <jvinet at zeroflux.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <fnmatch.h>
+
+#include <alpm.h>
+#include <alpm_list.h>
+
+/* pacman */
+#include "pacman.h"
+#include "callback.h"
+#include "conf.h"
+#include "util.h"
+
+static int fnmatch_cmp(const void *pattern, const void *string)
+{
+ return fnmatch(pattern, string, 0);
+}
+
+static void print_broken_dep(alpm_depmissing_t *miss)
+{
+ char *depstring = alpm_dep_compute_string(miss->depend);
+ alpm_list_t *trans_add = alpm_trans_get_add(config->handle);
+ alpm_pkg_t *pkg;
+ if(miss->causingpkg == NULL) {
+ /* package being installed/upgraded has unresolved dependency */
+ colon_printf(_("unable to satisfy dependency '%s' required by %s\n"),
+ depstring, miss->target);
+ } else if((pkg = alpm_pkg_find(trans_add, miss->causingpkg))) {
+ /* upgrading a package breaks a local dependency */
+ colon_printf(_("installing %s (%s) breaks dependency '%s' required by %s\n"),
+ miss->causingpkg, alpm_pkg_get_version(pkg), depstring, miss->target);
+ } else {
+ /* removing a package breaks a local dependency */
+ colon_printf(_("removing %s breaks dependency '%s' required by %s\n"),
+ miss->causingpkg, depstring, miss->target);
+ }
+ free(depstring);
+}
+
+static int sync_prepare_execute(void)
+{
+ alpm_list_t *i, *packages, *remove_packages, *data = NULL;
+ int retval = 0;
+
+ /* Step 2: "compute" the transaction based on targets and flags */
+ if(alpm_trans_prepare(config->handle, &data) == -1) {
+ alpm_errno_t err = alpm_errno(config->handle);
+ pm_printf(ALPM_LOG_ERROR, _("failed to prepare transaction (%s)\n"),
+ alpm_strerror(err));
+ switch(err) {
+ case ALPM_ERR_PKG_INVALID_ARCH:
+ for(i = data; i; i = alpm_list_next(i)) {
+ char *pkg = i->data;
+ colon_printf(_("package %s does not have a valid architecture\n"), pkg);
+ free(pkg);
+ }
+ break;
+ case ALPM_ERR_UNSATISFIED_DEPS:
+ for(i = data; i; i = alpm_list_next(i)) {
+ print_broken_dep(i->data);
+ alpm_depmissing_free(i->data);
+ }
+ break;
+ case ALPM_ERR_CONFLICTING_DEPS:
+ for(i = data; i; i = alpm_list_next(i)) {
+ alpm_conflict_t *conflict = i->data;
+ /* only print reason if it contains new information */
+ if(conflict->reason->mod == ALPM_DEP_MOD_ANY) {
+ colon_printf(_("%s and %s are in conflict\n"),
+ conflict->package1, conflict->package2);
+ } else {
+ char *reason = alpm_dep_compute_string(conflict->reason);
+ colon_printf(_("%s and %s are in conflict (%s)\n"),
+ conflict->package1, conflict->package2, reason);
+ free(reason);
+ }
+ alpm_conflict_free(conflict);
+ }
+ break;
+ default:
+ break;
+ }
+ retval = 1;
+ goto cleanup;
+ }
+
+ packages = alpm_trans_get_add(config->handle);
+ remove_packages = alpm_trans_get_remove(config->handle);
+
+ if(packages == NULL && remove_packages == NULL) {
+ /* nothing to do: just exit without complaining */
+ if(!config->print) {
+ printf(_(" there is nothing to do\n"));
+ }
+ goto cleanup;
+ }
+
+
+ /* Search for holdpkg in target list */
+ int holdpkg = 0;
+ for(i = remove_packages; i; i = alpm_list_next(i)) {
+ alpm_pkg_t *pkg = i->data;
+ if(alpm_list_find(config->holdpkg, alpm_pkg_get_name(pkg), fnmatch_cmp)) {
+ pm_printf(ALPM_LOG_WARNING, _("%s is designated as a HoldPkg.\n"),
+ alpm_pkg_get_name(pkg));
+ holdpkg = 1;
+ }
+ }
+ if(holdpkg && (noyes(_("HoldPkg was found in target list. Do you want to continue?")) == 0)) {
+ retval = 1;
+ goto cleanup;
+ }
+
+ /* Step 3: actually perform the operation */
+ if(config->print) {
+ print_packages(packages);
+ print_packages(remove_packages);
+ goto cleanup;
+ }
+
+ display_targets();
+ printf("\n");
+
+ int confirm;
+ if(config->op_s_downloadonly) {
+ confirm = yesno(_("Proceed with download?"));
+ } else {
+ confirm = yesno(_("Proceed with installation?"));
+ }
+ if(!confirm) {
+ retval = 1;
+ goto cleanup;
+ }
+
+ multibar_move_completed_up(true);
+ if(alpm_trans_commit(config->handle, &data) == -1) {
+ alpm_errno_t err = alpm_errno(config->handle);
+ pm_printf(ALPM_LOG_ERROR, _("failed to commit transaction (%s)\n"),
+ alpm_strerror(err));
+ switch(err) {
+ case ALPM_ERR_FILE_CONFLICTS:
+ for(i = data; i; i = alpm_list_next(i)) {
+ alpm_fileconflict_t *conflict = i->data;
+ switch(conflict->type) {
+ case ALPM_FILECONFLICT_TARGET:
+ fprintf(stderr, _("%s exists in both '%s' and '%s'\n"),
+ conflict->file, conflict->target, conflict->ctarget);
+ break;
+ case ALPM_FILECONFLICT_FILESYSTEM:
+ if(conflict->ctarget[0]) {
+ fprintf(stderr, _("%s: %s exists in filesystem (owned by %s)\n"),
+ conflict->target, conflict->file, conflict->ctarget);
+ } else {
+ fprintf(stderr, _("%s: %s exists in filesystem\n"),
+ conflict->target, conflict->file);
+ }
+ break;
+ }
+ alpm_fileconflict_free(conflict);
+ }
+ break;
+ case ALPM_ERR_PKG_INVALID:
+ case ALPM_ERR_PKG_INVALID_CHECKSUM:
+ case ALPM_ERR_PKG_INVALID_SIG:
+ for(i = data; i; i = alpm_list_next(i)) {
+ char *filename = i->data;
+ fprintf(stderr, _("%s is invalid or corrupted\n"), filename);
+ free(filename);
+ }
+ break;
+ default:
+ break;
+ }
+ /* TODO: stderr? */
+ printf(_("Errors occurred, no packages were upgraded.\n"));
+ retval = 1;
+ goto cleanup;
+ }
+
+ /* Step 4: release transaction resources */
+cleanup:
+ alpm_list_free(data);
+ if(trans_release() == -1) {
+ retval = 1;
+ }
+
+ return retval;
+}
+
+static int group_exists(alpm_list_t *dbs, const char *name)
+{
+ alpm_list_t *i;
+ for(i = dbs; i; i = i->next) {
+ alpm_db_t *db = i->data;
+
+ if(alpm_db_get_group(db, name)) {
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+static alpm_db_t *get_db(const char *dbname)
+{
+ alpm_list_t *i;
+ for(i = alpm_get_syncdbs(config->handle); i; i = i->next) {
+ alpm_db_t *db = i->data;
+ if(strcmp(alpm_db_get_name(db), dbname) == 0) {
+ return db;
+ }
+ }
+ return NULL;
+}
+
+static int process_pkg(alpm_pkg_t *pkg)
+{
+ int ret = alpm_add_pkg(config->handle, pkg);
+
+ if(ret == -1) {
+ alpm_errno_t err = alpm_errno(config->handle);
+ pm_printf(ALPM_LOG_ERROR, "'%s': %s\n", alpm_pkg_get_name(pkg), alpm_strerror(err));
+ return 1;
+ }
+ config->explicit_adds = alpm_list_add(config->explicit_adds, pkg);
+ return 0;
+}
+
+static int process_group(alpm_list_t *dbs, const char *group, int error)
+{
+ int ret = 0;
+ alpm_list_t *i;
+ alpm_list_t *pkgs = alpm_find_group_pkgs(dbs, group);
+ int count = alpm_list_count(pkgs);
+
+ if(!count) {
+ if(group_exists(dbs, group)) {
+ return 0;
+ }
+
+ pm_printf(ALPM_LOG_ERROR, _("target not found: %s\n"), group);
+ return 1;
+ }
+
+ if(error) {
+ /* we already know another target errored. there is no reason to prompt the
+ * user here; we already validated the group name so just move on since we
+ * won't actually be installing anything anyway. */
+ goto cleanup;
+ }
+
+ if(config->print == 0) {
+ char *array = malloc(count);
+ int n = 0;
+ const colstr_t *colstr = &config->colstr;
+ colon_printf(_n("There is %d member in group %s%s%s:\n",
+ "There are %d members in group %s%s%s:\n", count),
+ count, colstr->groups, group, colstr->title);
+ select_display(pkgs);
+ if(!array) {
+ ret = 1;
+ goto cleanup;
+ }
+ if(multiselect_question(array, count)) {
+ ret = 1;
+ free(array);
+ goto cleanup;
+ }
+ for(i = pkgs, n = 0; i; i = alpm_list_next(i)) {
+ alpm_pkg_t *pkg = i->data;
+
+ if(array[n++] == 0) {
+ continue;
+ }
+
+ if(process_pkg(pkg) == 1) {
+ ret = 1;
+ free(array);
+ goto cleanup;
+ }
+ }
+ free(array);
+ } else {
+ for(i = pkgs; i; i = alpm_list_next(i)) {
+ alpm_pkg_t *pkg = i->data;
+
+ if(process_pkg(pkg) == 1) {
+ ret = 1;
+ goto cleanup;
+ }
+ }
+ }
+
+cleanup:
+ alpm_list_free(pkgs);
+ return ret;
+}
+
+static int process_targname(alpm_list_t *dblist, const char *targname,
+ int error)
+{
+ alpm_pkg_t *pkg = alpm_find_dbs_satisfier(config->handle, dblist, targname);
+
+ /* skip ignored packages when user says no */
+ if(alpm_errno(config->handle) == ALPM_ERR_PKG_IGNORED) {
+ pm_printf(ALPM_LOG_WARNING, _("skipping target: %s\n"), targname);
+ return 0;
+ }
+
+ if(pkg) {
+ return process_pkg(pkg);
+ }
+ /* fallback on group */
+ return process_group(dblist, targname, error);
+}
+
+static int process_target(const char *target, int error)
+{
+ /* process targets */
+ char *targstring = strdup(target);
+ char *targname = strchr(targstring, '/');
+ int ret = 0;
+ alpm_list_t *dblist;
+
+ if(targname && targname != targstring) {
+ alpm_db_t *db;
+ const char *dbname;
+ int usage;
+
+ *targname = '\0';
+ targname++;
+ dbname = targstring;
+ db = get_db(dbname);
+ if(!db) {
+ pm_printf(ALPM_LOG_ERROR, _("database not found: %s\n"),
+ dbname);
+ ret = 1;
+ goto cleanup;
+ }
+
+ /* explicitly mark this repo as valid for installs since
+ * a repo name was given with the target */
+ alpm_db_get_usage(db, &usage);
+ alpm_db_set_usage(db, usage|ALPM_DB_USAGE_INSTALL);
+
+ dblist = alpm_list_add(NULL, db);
+ ret = process_targname(dblist, targname, error);
+ alpm_list_free(dblist);
+
+ /* restore old usage so we don't possibly disturb later
+ * targets */
+ alpm_db_set_usage(db, usage);
+ } else {
+ targname = targstring;
+ dblist = alpm_get_syncdbs(config->handle);
+ ret = process_targname(dblist, targname, error);
+ }
+
+cleanup:
+ free(targstring);
+ if(ret && access(target, R_OK) == 0) {
+ pm_printf(ALPM_LOG_WARNING,
+ _("'%s' is a file, did you mean %s instead of %s?\n"),
+ target, "-U/--upgrade", "-S/--sync");
+ }
+ return ret;
+}
+
+static int load_sync(alpm_list_t *targets)
+{
+ int retval = 0;
+ alpm_list_t *i;
+
+ if(targets == NULL && !config->op_s_upgrade && !config->op_s_sync) {
+ pm_printf(ALPM_LOG_ERROR, _("no targets specified (use -h for help)\n"));
+ return 1;
+ }
+
+ /* process targets */
+ for(i = targets; i; i = alpm_list_next(i)) {
+ const char *targ = i->data;
+ if(process_target(targ, retval) == 1) {
+ retval = 1;
+ }
+ }
+
+ return retval;
+}
+
+int do_transaction(targets_t *targets) {
+ int need_repos = (config->op & PM_OP_SYNC);
+ alpm_list_t *sync_dbs;
+
+ if(targets->targets != NULL) {
+ pm_printf(ALPM_LOG_ERROR, _("targets must come after operation\n"));
+ return 1;
+ }
+
+ if(check_syncdbs(need_repos, 0)) {
+ return 1;
+ }
+
+ sync_dbs = alpm_get_syncdbs(config->handle);
+
+ if(config->op_s_sync) {
+ /* grab a fresh package list */
+ colon_printf(_("Synchronizing package databases...\n"));
+ alpm_logaction(config->handle, PACMAN_CALLER_PREFIX,
+ "synchronizing package lists\n");
+ if(!sync_syncdbs(config->op_s_sync, sync_dbs)) {
+ return 1;
+ }
+ }
+
+ if(check_syncdbs(need_repos, 1)) {
+ return 1;
+ }
+
+ if(config->op_s_clean || config->op_s_search || config->op_s_info
+ || config->op_q_list || config->group) {
+ return pacman_sync(targets->sync);
+ }
+
+ /* Step 1: create a new transaction... */
+ if(trans_init(config->flags, 1) == -1) {
+ return 1;
+ }
+
+ if(config->op & PM_OP_SYNC && load_sync(targets->sync)) {
+ goto cleanup;
+ }
+ if(config->op & PM_OP_REMOVE && load_remove(targets->remove)) {
+ goto cleanup;
+ }
+ if(config->op & PM_OP_UPGRADE && load_upgrade(targets->upgrade)) {
+ goto cleanup;
+ }
+
+ if(config->op_s_upgrade) {
+ if(!config->print) {
+ colon_printf(_("Starting full system upgrade...\n"));
+ alpm_logaction(config->handle, PACMAN_CALLER_PREFIX,
+ "starting full system upgrade\n");
+ }
+ if(alpm_sync_sysupgrade(config->handle, config->op_s_upgrade >= 2) == -1) {
+ pm_printf(ALPM_LOG_ERROR, "%s\n", alpm_strerror(alpm_errno(config->handle)));
+ trans_release();
+ return 1;
+ }
+ }
+
+ return sync_prepare_execute();
+
+cleanup:
+ trans_release();
+ return 1;
+}
diff --git a/src/pacman/upgrade.c b/src/pacman/upgrade.c
index 0691856f..b0fa7b1d 100644
--- a/src/pacman/upgrade.c
+++ b/src/pacman/upgrade.c
@@ -65,7 +65,7 @@ static int load_packages(alpm_list_t *targets, int siglevel)
*
* @return 0 on success, 1 on failure
*/
-int pacman_upgrade(alpm_list_t *targets)
+int load_upgrade(alpm_list_t *targets)
{
int retval = 0;
alpm_list_t *remote_targets = NULL, *fetched_files = NULL;
@@ -88,38 +88,16 @@ int pacman_upgrade(alpm_list_t *targets)
if(remote_targets) {
retval = alpm_fetch_pkgurl(config->handle, remote_targets, &fetched_files);
- if(retval) {
- goto fail_free;
- }
- }
-
- /* Step 1: create a new transaction */
- if(trans_init(config->flags, 1) == -1) {
- retval = 1;
- goto fail_free;
}
printf(_("loading packages...\n"));
retval |= load_packages(local_targets, alpm_option_get_local_file_siglevel(config->handle));
retval |= load_packages(fetched_files, alpm_option_get_remote_file_siglevel(config->handle));
- if(retval) {
- goto fail_release;
- }
-
- alpm_list_free(remote_targets);
- alpm_list_free(local_targets);
- FREELIST(fetched_files);
-
- /* now that targets are resolved, we can hand it all off to the sync code */
- return sync_prepare_execute();
-
-fail_release:
- trans_release();
-fail_free:
alpm_list_free(remote_targets);
alpm_list_free(local_targets);
FREELIST(fetched_files);
return retval;
}
+
diff --git a/src/pacman/util.c b/src/pacman/util.c
index 53833d6f..f1b8fc40 100644
--- a/src/pacman/util.c
+++ b/src/pacman/util.c
@@ -106,16 +106,17 @@ int needs_root(void)
if(config->sysroot) {
return 1;
}
+ if(config->op & PM_OP_SYNC) {
+ return (config->op_s_clean || config->op_s_sync ||
+ (!config->group && !config->op_s_info && !config->op_q_list &&
+ !config->op_s_search && !config->print));
+ }
+ if(config->op & PM_OP_TRANS) {
+ return !config->print;
+ }
switch(config->op) {
case PM_OP_DATABASE:
return !config->op_q_check;
- case PM_OP_UPGRADE:
- case PM_OP_REMOVE:
- return !config->print;
- case PM_OP_SYNC:
- return (config->op_s_clean || config->op_s_sync ||
- (!config->group && !config->op_s_info && !config->op_q_list &&
- !config->op_s_search && !config->print));
case PM_OP_FILES:
return config->op_s_sync;
default:
diff --git a/test/pacman/tests/remove049.py b/test/pacman/tests/remove049.py
index a0336295..a147847a 100644
--- a/test/pacman/tests/remove049.py
+++ b/test/pacman/tests/remove049.py
@@ -10,9 +10,9 @@
lp3.depends = [ "pkg1" ]
self.addpkg2db("local", lp3)
-self.args = "-Ru pkg1 pkg2"
+self.args = "-R --unneeded pkg1 pkg2"
self.addrule("PACMAN_RETCODE=0")
self.addrule("PKG_EXIST=pkg1")
self.addrule("!PKG_EXIST=pkg2")
-self.addrule("PKG_EXIST=pkg3")
\ No newline at end of file
+self.addrule("PKG_EXIST=pkg3")
--
2.35.1
More information about the pacman-dev
mailing list