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 <[email protected]>
---
 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 <[email protected]>
+ *  Copyright (c) 2002-2006 by Judd Vinet <[email protected]>
+ *
+ *  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

Reply via email to