summaryrefslogtreecommitdiffstats
path: root/pull.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 19:12:14 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 19:12:14 +0000
commit4b8a0f3f3dcf60dac2ce308ea08d413a535af29f (patch)
tree0f09c0ad2a4d0f535d89040a63dc3a866a6606e6 /pull.c
parentInitial commit. (diff)
downloadreprepro-4b8a0f3f3dcf60dac2ce308ea08d413a535af29f.tar.xz
reprepro-4b8a0f3f3dcf60dac2ce308ea08d413a535af29f.zip
Adding upstream version 5.4.4.upstream/5.4.4upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'pull.c')
-rw-r--r--pull.c1114
1 files changed, 1114 insertions, 0 deletions
diff --git a/pull.c b/pull.c
new file mode 100644
index 0000000..3c5673d
--- /dev/null
+++ b/pull.c
@@ -0,0 +1,1114 @@
+/* This file is part of "reprepro"
+ * Copyright (C) 2006,2007,2016 Bernhard R. Link
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02111-1301 USA
+ */
+#include <config.h>
+
+#include <errno.h>
+#include <assert.h>
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "error.h"
+#include "ignore.h"
+#include "mprintf.h"
+#include "strlist.h"
+#include "names.h"
+#include "pull.h"
+#include "upgradelist.h"
+#include "distribution.h"
+#include "tracking.h"
+#include "termdecide.h"
+#include "filterlist.h"
+#include "log.h"
+#include "configparser.h"
+#include "package.h"
+
+/***************************************************************************
+ * step one: *
+ * parse CONFDIR/pull to get pull information saved in *
+ * pull_rule structs *
+ **************************************************************************/
+
+/* the data for some upstream part to get pull from, some
+ * some fields can be NULL or empty */
+struct pull_rule {
+ struct pull_rule *next;
+ //e.g. "Name: woody"
+ char *name;
+ //e.g. "From: woody"
+ char *from;
+ //e.g. "Architectures: i386 sparc mips" (not set means all)
+ struct atomlist architectures_from;
+ struct atomlist architectures_into;
+ bool architectures_set;
+ //e.g. "Components: main contrib" (not set means all)
+ struct atomlist components;
+ bool components_set;
+ //e.g. "UDebComponents: main" // (not set means all)
+ struct atomlist udebcomponents;
+ bool udebcomponents_set;
+ // We don't have equivalents for ddebs yet since we don't know
+ // what the Debian archive layout is going to look like
+ // NULL means no condition
+ /*@null@*/term *includecondition;
+ struct filterlist filterlist;
+ struct filterlist filtersrclist;
+ /*----only set after _addsourcedistribution----*/
+ /*@NULL@*/ struct distribution *distribution;
+ bool used;
+};
+
+static void pull_rule_free(/*@only@*/struct pull_rule *pull) {
+ if (pull == NULL)
+ return;
+ free(pull->name);
+ free(pull->from);
+ atomlist_done(&pull->architectures_from);
+ atomlist_done(&pull->architectures_into);
+ atomlist_done(&pull->components);
+ atomlist_done(&pull->udebcomponents);
+ term_free(pull->includecondition);
+ filterlist_release(&pull->filterlist);
+ filterlist_release(&pull->filtersrclist);
+ free(pull);
+}
+
+void pull_freerules(struct pull_rule *p) {
+ while (p != NULL) {
+ struct pull_rule *rule;
+
+ rule = p;
+ p = rule->next;
+ pull_rule_free(rule);
+ }
+}
+
+CFlinkedlistinit(pull_rule)
+CFvalueSETPROC(pull_rule, name)
+CFvalueSETPROC(pull_rule, from)
+CFatomlistSETPROC(pull_rule, components, at_component)
+CFatomlistSETPROC(pull_rule, udebcomponents, at_component)
+CFfilterlistSETPROC(pull_rule, filterlist)
+CFfilterlistSETPROC(pull_rule, filtersrclist)
+CFtermSETPROC(pull_rule, includecondition)
+
+CFUSETPROC(pull_rule, architectures) {
+ CFSETPROCVAR(pull_rule, this);
+ retvalue r;
+
+ this->architectures_set = true;
+ r = config_getsplitatoms(iter, "Architectures",
+ at_architecture,
+ &this->architectures_from,
+ &this->architectures_into);
+ if (r == RET_NOTHING) {
+ fprintf(stderr,
+"Warning parsing %s, line %u: an empty Architectures field\n"
+"causes the whole rule to do nothing.\n",
+ config_filename(iter),
+ config_markerline(iter));
+ }
+ return r;
+}
+
+static const struct configfield pullconfigfields[] = {
+ CFr("Name", pull_rule, name),
+ CFr("From", pull_rule, from),
+ CF("Architectures", pull_rule, architectures),
+ CF("Components", pull_rule, components),
+ CF("UDebComponents", pull_rule, udebcomponents),
+ CF("FilterFormula", pull_rule, includecondition),
+ CF("FilterSrcList", pull_rule, filtersrclist),
+ CF("FilterList", pull_rule, filterlist)
+};
+
+retvalue pull_getrules(struct pull_rule **rules) {
+ struct pull_rule *pull = NULL;
+ retvalue r;
+
+ r = configfile_parse("pulls", IGNORABLE(unknownfield),
+ configparser_pull_rule_init, linkedlistfinish,
+ "pull rule",
+ pullconfigfields, ARRAYCOUNT(pullconfigfields), &pull);
+ if (RET_IS_OK(r))
+ *rules = pull;
+ else if (r == RET_NOTHING) {
+ assert (pull == NULL);
+ *rules = NULL;
+ r = RET_OK;
+ } else {
+ // TODO special handle unknownfield
+ pull_freerules(pull);
+ }
+ return r;
+}
+
+/***************************************************************************
+ * step two: *
+ * create pull_distribution structs to hold all additional information for *
+ * a distribution *
+ **************************************************************************/
+
+struct pull_target;
+static void pull_freetargets(struct pull_target *targets);
+
+struct pull_distribution {
+ struct pull_distribution *next;
+ /*@dependant@*/struct distribution *distribution;
+ struct pull_target *targets;
+ /*@dependant@*/struct pull_rule *rules[];
+};
+
+void pull_freedistributions(struct pull_distribution *d) {
+ while (d != NULL) {
+ struct pull_distribution *next;
+
+ next = d->next;
+ pull_freetargets(d->targets);
+ free(d);
+ d = next;
+ }
+}
+
+static retvalue pull_initdistribution(struct pull_distribution **pp,
+ struct distribution *distribution,
+ struct pull_rule *rules) {
+ struct pull_distribution *p;
+ int i;
+
+ assert(distribution != NULL);
+ if (distribution->pulls.count == 0)
+ return RET_NOTHING;
+
+ p = malloc(sizeof(struct pull_distribution)+
+ sizeof(struct pull_rules *)*distribution->pulls.count);
+ if (FAILEDTOALLOC(p))
+ return RET_ERROR_OOM;
+ p->next = NULL;
+ p->distribution = distribution;
+ p->targets = NULL;
+ for (i = 0 ; i < distribution->pulls.count ; i++) {
+ const char *name = distribution->pulls.values[i];
+ if (strcmp(name, "-") == 0) {
+ p->rules[i] = NULL;
+ } else {
+ struct pull_rule *rule = rules;
+ while (rule && strcmp(rule->name, name) != 0)
+ rule = rule->next;
+ if (rule == NULL) {
+ fprintf(stderr,
+"Error: Unknown pull rule '%s' in distribution '%s'!\n",
+ name, distribution->codename);
+ free(p);
+ return RET_ERROR_MISSING;
+ }
+ p->rules[i] = rule;
+ rule->used = true;
+ }
+ }
+ *pp = p;
+ return RET_OK;
+}
+
+static retvalue pull_init(struct pull_distribution **pulls,
+ struct pull_rule *rules,
+ struct distribution *distributions) {
+ struct pull_distribution *p = NULL, **pp = &p;
+ struct distribution *d;
+ retvalue r;
+
+ for (d = distributions ; d != NULL ; d = d->next) {
+ if (!d->selected)
+ continue;
+ r = pull_initdistribution(pp, d, rules);
+ if (RET_WAS_ERROR(r)) {
+ pull_freedistributions(p);
+ return r;
+ }
+ if (RET_IS_OK(r)) {
+ assert (*pp != NULL);
+ pp = &(*pp)->next;
+ }
+ }
+ *pulls = p;
+ return RET_OK;
+}
+
+/***************************************************************************
+ * step three: *
+ * load the config of the distributions mentioned in the rules *
+ **************************************************************************/
+
+static retvalue pull_loadsourcedistributions(struct distribution *alldistributions, struct pull_rule *rules) {
+ struct pull_rule *rule;
+ struct distribution *d;
+
+ for (rule = rules ; rule != NULL ; rule = rule->next) {
+ if (rule->used && rule->distribution == NULL) {
+ for (d = alldistributions ; d != NULL ; d = d->next) {
+ if (strcmp(d->codename, rule->from) == 0) {
+ rule->distribution = d;
+ break;
+ }
+ }
+ if (d == NULL) {
+ fprintf(stderr,
+"Error: Unknown distribution '%s' referenced in pull rule '%s'\n",
+ rule->from, rule->name);
+ return RET_ERROR_MISSING;
+ }
+ }
+ }
+ return RET_OK;
+}
+
+/***************************************************************************
+ * step four: *
+ * create pull_targets and pull_sources *
+ **************************************************************************/
+
+struct pull_source {
+ struct pull_source *next;
+ /* NULL, if this is a delete rule */
+ struct target *source;
+ struct pull_rule *rule;
+};
+struct pull_target {
+ /*@null@*/struct pull_target *next;
+ /*@null@*/struct pull_source *sources;
+ /*@dependent@*/struct target *target;
+ /*@null@*/struct upgradelist *upgradelist;
+};
+
+static void pull_freetargets(struct pull_target *targets) {
+ while (targets != NULL) {
+ struct pull_target *target = targets;
+ targets = target->next;
+ while (target->sources != NULL) {
+ struct pull_source *source = target->sources;
+ target->sources = source->next;
+ free(source);
+ }
+ free(target);
+ }
+}
+
+static retvalue pull_createsource(struct pull_rule *rule,
+ struct target *target,
+ struct pull_source ***s) {
+ const struct atomlist *c;
+ const struct atomlist *a_from, *a_into;
+ int ai;
+
+ assert (rule != NULL);
+ assert (rule->distribution != NULL);
+
+ if (rule->architectures_set) {
+ a_from = &rule->architectures_from;
+ a_into = &rule->architectures_into;
+ } else {
+ a_from = &rule->distribution->architectures;
+ a_into = &rule->distribution->architectures;
+ }
+ if (target->packagetype == pt_udeb) {
+ if (rule->udebcomponents_set)
+ c = &rule->udebcomponents;
+ else
+ c = &rule->distribution->udebcomponents;
+ } else {
+ if (rule->components_set)
+ c = &rule->components;
+ else
+ c = &rule->distribution->components;
+ }
+
+ if (!atomlist_in(c, target->component))
+ return RET_NOTHING;
+
+ for (ai = 0 ; ai < a_into->count ; ai++) {
+ struct pull_source *source;
+
+ if (a_into->atoms[ai] != target->architecture)
+ continue;
+
+ source = NEW(struct pull_source);
+ if (FAILEDTOALLOC(source))
+ return RET_ERROR_OOM;
+
+ source->next = NULL;
+ source->rule = rule;
+ source->source = distribution_getpart(rule->distribution,
+ target->component,
+ a_from->atoms[ai],
+ target->packagetype);
+ **s = source;
+ *s = &source->next;
+ }
+ return RET_OK;
+}
+
+static retvalue pull_createdelete(struct pull_source ***s) {
+ struct pull_source *source;
+
+ source = NEW(struct pull_source);
+ if (FAILEDTOALLOC(source))
+ return RET_ERROR_OOM;
+
+ source->next = NULL;
+ source->rule = NULL;
+ source->source = NULL;
+ **s = source;
+ *s = &source->next;
+ return RET_OK;
+}
+
+static retvalue generatepulltarget(struct pull_distribution *pd, struct target *target) {
+ struct pull_source **s;
+ struct pull_target *pt;
+ retvalue r;
+ int i;
+
+ pt = NEW(struct pull_target);
+ if (FAILEDTOALLOC(pt))
+ return RET_ERROR_OOM;
+ pt->target = target;
+ pt->next = pd->targets;
+ pt->upgradelist = NULL;
+ pt->sources = NULL;
+ s = &pt->sources;
+ pd->targets = pt;
+
+ for (i = 0 ; i < pd->distribution->pulls.count ; i++) {
+ struct pull_rule *rule = pd->rules[i];
+
+ if (rule == NULL)
+ r = pull_createdelete(&s);
+ else
+ r = pull_createsource(rule, target, &s);
+ if (RET_WAS_ERROR(r))
+ return r;
+ }
+
+ return RET_OK;
+}
+
+static retvalue pull_generatetargets(struct pull_distribution *pull_distributions, const struct atomlist *components, const struct atomlist *architectures, const struct atomlist *packagetypes) {
+ struct pull_distribution *pd;
+ struct target *target;
+ retvalue r;
+
+ for (pd = pull_distributions ; pd != NULL ; pd = pd->next) {
+ for (target = pd->distribution->targets ; target != NULL ;
+ target = target->next) {
+
+ if (!target_matches(target, components, architectures, packagetypes))
+ continue;
+
+ r = generatepulltarget(pd, target);
+ if (RET_WAS_ERROR(r))
+ return r;
+ }
+ }
+ return RET_OK;
+}
+
+/***************************************************************************
+ * Some checking to be able to warn against typos *
+ **************************************************************************/
+
+static bool *preparefoundlist(const struct atomlist *list) {
+ bool *found;
+ int i, j;
+
+ found = nzNEW(list->count, bool);
+ if (FAILEDTOALLOC(found))
+ return found;
+ for (i = 0 ; i < list->count ; i++) {
+ if (found[i])
+ continue;
+ for (j = i + 1 ; j < list->count ; j++)
+ if (list->atoms[i] == list->atoms[j])
+ found[j] = true;
+ }
+ return found;
+}
+
+
+static inline void markasused(const struct strlist *pulls, const char *rulename, const struct atomlist *needed, const struct atomlist *have, bool *found) {
+ int i, j, o;
+
+ for (i = 0 ; i < pulls->count ; i++) {
+ if (strcmp(pulls->values[i], rulename) != 0)
+ continue;
+ for (j = 0 ; j < have->count ; j++) {
+ o = atomlist_ofs(needed, have->atoms[j]);
+ if (o >= 0)
+ found[o] = true;
+ }
+ }
+}
+
+static void checkifarchitectureisused(const struct atomlist *architectures, const struct distribution *alldistributions, const struct pull_rule *rule, const char *action) {
+ bool *found;
+ const struct distribution *d;
+ int i;
+
+ assert (rule != NULL);
+ if (architectures->count == 0)
+ return;
+ found = preparefoundlist(architectures);
+ if (found == NULL)
+ return;
+ for (d = alldistributions ; d != NULL ; d = d->next) {
+ markasused(&d->pulls, rule->name,
+ architectures, &d->architectures,
+ found);
+ }
+ for (i = 0 ; i < architectures->count ; i++) {
+ if (found[i])
+ continue;
+ fprintf(stderr,
+"Warning: pull rule '%s' wants to %s architecture '%s',\n"
+"but no distribution using this has such an architecture.\n"
+"(This will simply be ignored and is not even checked when using --fast).\n",
+ rule->name, action,
+ atoms_architectures[architectures->atoms[i]]);
+ }
+ free(found);
+ return;
+}
+
+static void checkifcomponentisused(const struct atomlist *components, const struct distribution *alldistributions, const struct pull_rule *rule, const char *action) {
+ bool *found;
+ const struct distribution *d;
+ int i;
+
+ assert (rule != NULL);
+ if (components->count == 0)
+ return;
+ found = preparefoundlist(components);
+ if (found == NULL)
+ return;
+ for (d = alldistributions ; d != NULL ; d = d->next) {
+ markasused(&d->pulls, rule->name,
+ components, &d->components,
+ found);
+ }
+ for (i = 0 ; i < components->count ; i++) {
+ if (found[i])
+ continue;
+ fprintf(stderr,
+"Warning: pull rule '%s' wants to %s component '%s',\n"
+"but no distribution using this has such an component.\n"
+"(This will simply be ignored and is not even checked when using --fast).\n",
+ rule->name, action,
+ atoms_components[components->atoms[i]]);
+ }
+ free(found);
+ return;
+}
+
+static void checkifudebcomponentisused(const struct atomlist *udebcomponents, const struct distribution *alldistributions, const struct pull_rule *rule, const char *action) {
+ bool *found;
+ const struct distribution *d;
+ int i;
+
+ assert (rule != NULL);
+ if (udebcomponents->count == 0)
+ return;
+ found = preparefoundlist(udebcomponents);
+ if (found == NULL)
+ return;
+ for (d = alldistributions ; d != NULL ; d = d->next) {
+ markasused(&d->pulls, rule->name,
+ udebcomponents, &d->udebcomponents,
+ found);
+ }
+ for (i = 0 ; i < udebcomponents->count ; i++) {
+ if (found[i])
+ continue;
+ fprintf(stderr,
+"Warning: pull rule '%s' wants to %s udeb component '%s',\n"
+"but no distribution using this has such an udeb component.\n"
+"(This will simply be ignored and is not even checked when using --fast).\n",
+ rule->name, action,
+ atoms_components[udebcomponents->atoms[i]]);
+ }
+ free(found);
+ return;
+}
+
+static void checksubset(const struct atomlist *needed, const struct atomlist *have, const char *rulename, const char *from, const char *what, const char **atoms) {
+ int i, j;
+
+ for (i = 0 ; i < needed->count ; i++) {
+ atom_t value = needed->atoms[i];
+
+ for (j = 0 ; j < i ; j++) {
+ if (value == needed->atoms[j])
+ break;
+ }
+ if (j < i)
+ continue;
+
+ if (!atomlist_in(have, value)) {
+ fprintf(stderr,
+"Warning: pull rule '%s' wants to get something from %s '%s',\n"
+"but there is no such %s in distribution '%s'.\n"
+"(This will simply be ignored and is not even checked when using --fast).\n",
+ rulename, what,
+ atoms[value], what, from);
+ }
+ }
+}
+
+static void searchunused(const struct distribution *alldistributions, const struct pull_rule *rule) {
+ if (rule->distribution != NULL) {
+ // TODO: move this part of the checks into parsing?
+ checksubset(&rule->architectures_from,
+ &rule->distribution->architectures,
+ rule->name, rule->from, "architecture",
+ atoms_architectures);
+ checksubset(&rule->components,
+ &rule->distribution->components,
+ rule->name, rule->from, "component",
+ atoms_components);
+ checksubset(&rule->udebcomponents,
+ &rule->distribution->udebcomponents,
+ rule->name, rule->from, "udeb component",
+ atoms_components);
+ }
+
+ if (rule->distribution == NULL) {
+ assert (strcmp(rule->from, "*") == 0);
+ checkifarchitectureisused(&rule->architectures_from,
+ alldistributions, rule, "get something from");
+ /* no need to check component and udebcomponent, as those
+ * are the same with the others */
+ }
+ checkifarchitectureisused(&rule->architectures_into,
+ alldistributions, rule, "put something into");
+ checkifcomponentisused(&rule->components,
+ alldistributions, rule, "put something into");
+ checkifudebcomponentisused(&rule->udebcomponents,
+ alldistributions, rule, "put something into");
+}
+
+static void pull_searchunused(const struct distribution *alldistributions, struct pull_rule *pull_rules) {
+ struct pull_rule *rule;
+
+ for (rule = pull_rules ; rule != NULL ; rule = rule->next) {
+ if (!rule->used)
+ continue;
+
+ searchunused(alldistributions, rule);
+ }
+}
+
+/***************************************************************************
+ * combination of the steps two, three and four *
+ **************************************************************************/
+
+retvalue pull_prepare(struct distribution *alldistributions, struct pull_rule *rules, bool fast, const struct atomlist *components, const struct atomlist *architectures, const struct atomlist *types, struct pull_distribution **pd) {
+ struct pull_distribution *pulls;
+ retvalue r;
+
+ r = pull_init(&pulls, rules, alldistributions);
+ if (RET_WAS_ERROR(r))
+ return r;
+
+ r = pull_loadsourcedistributions(alldistributions, rules);
+ if (RET_WAS_ERROR(r)) {
+ pull_freedistributions(pulls);
+ return r;
+ }
+ if (!fast)
+ pull_searchunused(alldistributions, rules);
+
+ r = pull_generatetargets(pulls, components, architectures, types);
+ if (RET_WAS_ERROR(r)) {
+ pull_freedistributions(pulls);
+ return r;
+ }
+ *pd = pulls;
+ return RET_OK;
+}
+
+/***************************************************************************
+ * step five: *
+ * decide what gets pulled *
+ **************************************************************************/
+
+static upgrade_decision ud_decide_by_rule(void *privdata, struct target *target, struct package *new, /*@null@*/const char *old_version) {
+ struct pull_rule *rule = privdata;
+ upgrade_decision decision = UD_UPGRADE;
+ retvalue r;
+ struct filterlist *fl;
+ const char *n, *v;
+ bool cmdline_still_undecided;
+
+ if (target->packagetype == pt_dsc) {
+ assert (strcmp(new->name, new->source) == 0);
+ assert (strcmp(new->version, new->sourceversion) == 0);
+ if (rule->filtersrclist.set)
+ fl = &rule->filtersrclist;
+ else
+ fl = &rule->filterlist;
+ n = new->name;
+ v = new->version;
+ } else {
+ if (rule->filterlist.set) {
+ fl = &rule->filterlist;
+ n = new->name;
+ v = new->version;
+ } else {
+ fl = &rule->filtersrclist;
+ n = new->source;
+ v = new->sourceversion;
+ }
+ }
+
+ switch (filterlist_find(n, v, fl)) {
+ case flt_deinstall:
+ case flt_purge:
+ return UD_NO;
+ case flt_warning:
+ return UD_LOUDNO;
+ case flt_supersede:
+ decision = UD_SUPERSEDE;
+ break;
+ case flt_hold:
+ decision = UD_HOLD;
+ break;
+ case flt_error:
+ /* cannot yet be handled! */
+ fprintf(stderr,
+"Package name marked to be unexpected('error'): '%s'!\n", new->name);
+ return UD_ERROR;
+ case flt_upgradeonly:
+ if (old_version == NULL)
+ return UD_NO;
+ break;
+ case flt_install:
+ break;
+ case flt_unchanged:
+ case flt_auto_hold:
+ assert (false);
+ break;
+ }
+
+ cmdline_still_undecided = false;
+ switch (filterlist_find(new->source, new->sourceversion,
+ &cmdline_src_filter)) {
+ case flt_deinstall:
+ case flt_purge:
+ return UD_NO;
+ case flt_warning:
+ return UD_LOUDNO;
+ case flt_auto_hold:
+ cmdline_still_undecided = true;
+ decision = UD_HOLD;
+ break;
+ case flt_hold:
+ decision = UD_HOLD;
+ break;
+ case flt_supersede:
+ decision = UD_SUPERSEDE;
+ break;
+ case flt_error:
+ /* cannot yet be handled! */
+ fprintf(stderr,
+"Package name marked to be unexpected('error'): '%s'!\n", new->name);
+ return UD_ERROR;
+ case flt_upgradeonly:
+ if (old_version == NULL)
+ return UD_NO;
+ break;
+ case flt_install:
+ decision = UD_UPGRADE;
+ break;
+ case flt_unchanged:
+ cmdline_still_undecided = true;
+ break;
+ }
+
+
+ if (target->packagetype != pt_dsc) {
+ switch (filterlist_find(new->name, new->version,
+ &cmdline_bin_filter)) {
+ case flt_deinstall:
+ case flt_purge:
+ return UD_NO;
+ case flt_warning:
+ return UD_LOUDNO;
+ case flt_hold:
+ decision = UD_HOLD;
+ break;
+ case flt_supersede:
+ decision = UD_SUPERSEDE;
+ break;
+ case flt_error:
+ /* cannot yet be handled! */
+ fprintf(stderr,
+"Package name marked to be unexpected('error'): '%s'!\n", new->name);
+ return UD_ERROR;
+ case flt_upgradeonly:
+ if (old_version == NULL)
+ return UD_NO;
+ break;
+ case flt_install:
+ decision = UD_UPGRADE;
+ break;
+ case flt_unchanged:
+ break;
+ case flt_auto_hold:
+ /* hold only if it was not in the src-filter */
+ if (cmdline_still_undecided)
+ decision = UD_HOLD;
+ break;
+ }
+ } else if (cmdline_bin_filter.defaulttype == flt_auto_hold) {
+ if (cmdline_still_undecided)
+ decision = UD_HOLD;
+ }
+
+ /* formula tested last as it is the most expensive */
+ if (rule->includecondition != NULL) {
+ r = term_decidepackage(rule->includecondition, new, target);
+ if (RET_WAS_ERROR(r))
+ return UD_ERROR;
+ if (r == RET_NOTHING) {
+ return UD_NO;
+ }
+ }
+
+ return decision;
+}
+
+static inline retvalue pull_searchformissing(/*@null@*/FILE *out, struct pull_target *p) {
+ struct pull_source *source;
+ retvalue result, r;
+
+ if (verbose > 2 && out != NULL)
+ fprintf(out, " pulling into '%s'\n", p->target->identifier);
+ assert(p->upgradelist == NULL);
+ r = upgradelist_initialize(&p->upgradelist, p->target);
+ if (RET_WAS_ERROR(r))
+ return r;
+
+ result = RET_NOTHING;
+
+ for (source=p->sources ; source != NULL ; source=source->next) {
+
+ if (source->rule == NULL) {
+ if (verbose > 4 && out != NULL)
+ fprintf(out,
+" marking everything to be deleted\n");
+ r = upgradelist_deleteall(p->upgradelist);
+ RET_UPDATE(result, r);
+ if (RET_WAS_ERROR(r))
+ return result;
+ continue;
+ }
+
+ if (verbose > 4 && out != NULL)
+ fprintf(out, " looking what to get from '%s'\n",
+ source->source->identifier);
+ r = upgradelist_pull(p->upgradelist, source->source,
+ ud_decide_by_rule, source->rule, source);
+ RET_UPDATE(result, r);
+ if (RET_WAS_ERROR(r))
+ return result;
+ }
+
+ return result;
+}
+
+static retvalue pull_search(/*@null@*/FILE *out, struct pull_distribution *d) {
+ retvalue result, r;
+ struct pull_target *u;
+
+ result = RET_NOTHING;
+ for (u=d->targets ; u != NULL ; u=u->next) {
+ r = pull_searchformissing(out, u);
+ RET_UPDATE(result, r);
+ if (RET_WAS_ERROR(r))
+ break;
+ }
+ return result;
+}
+
+static bool pull_isbigdelete(struct pull_distribution *d) {
+ struct pull_target *u, *v;
+
+ for (u = d->targets ; u != NULL ; u=u->next) {
+ if (upgradelist_isbigdelete(u->upgradelist)) {
+ d->distribution->omitted = true;
+ for (v = d->targets ; v != NULL ; v = v->next) {
+ upgradelist_free(v->upgradelist);
+ v->upgradelist = NULL;
+ }
+ return true;
+ }
+ }
+ return false;
+}
+
+
+static void pull_from_callback(void *privdata, const char **rule_p, const char **from_p) {
+ struct pull_source *source = privdata;
+
+ *rule_p = source->rule->name;
+ *from_p = source->rule->from;
+}
+
+static retvalue pull_install(struct pull_distribution *distribution) {
+ retvalue result, r;
+ struct pull_target *u;
+ struct distribution *d = distribution->distribution;
+
+ assert (logger_isprepared(d->logger));
+
+ result = RET_NOTHING;
+ for (u=distribution->targets ; u != NULL ; u=u->next) {
+ r = upgradelist_install(u->upgradelist, d->logger,
+ false, pull_from_callback);
+ RET_UPDATE(d->status, r);
+ RET_UPDATE(result, r);
+ upgradelist_free(u->upgradelist);
+ u->upgradelist = NULL;
+ if (RET_WAS_ERROR(r))
+ break;
+ }
+ if (RET_IS_OK(result) && d->tracking != dt_NONE) {
+ r = tracking_retrack(d, false);
+ RET_ENDUPDATE(result, r);
+ }
+ return result;
+}
+
+static void pull_dumppackage(const char *packagename, /*@null@*/const char *oldversion, /*@null@*/const char *newversion, /*@null@*/const char *bestcandidate, /*@null@*/const struct strlist *newfilekeys, /*@null@*/const char *newcontrol, void *privdata) {
+ struct pull_source *source = privdata;
+
+ if (newversion == NULL) {
+ if (oldversion != NULL && bestcandidate != NULL) {
+ printf("'%s': '%s' will be deleted"
+ " (best new: '%s')\n",
+ packagename, oldversion, bestcandidate);
+ } else if (oldversion != NULL) {
+ printf("'%s': '%s' will be deleted"
+ " (no longer available or superseded)\n",
+ packagename, oldversion);
+ } else {
+ printf("'%s': will NOT be added as '%s'\n",
+ packagename, bestcandidate);
+ }
+ } else if (newversion == oldversion) {
+ if (bestcandidate != NULL) {
+ if (verbose > 1)
+ printf("'%s': '%s' will be kept"
+ " (best new: '%s')\n",
+ packagename, oldversion,
+ bestcandidate);
+ } else {
+ if (verbose > 0)
+ printf("'%s': '%s' will be kept"
+ " (unavailable for reload)\n",
+ packagename, oldversion);
+ }
+ } else {
+ const char *via = source->rule->name;
+
+ assert (newfilekeys != NULL);
+ assert (newcontrol != NULL);
+ if (oldversion != NULL)
+ (void)printf("'%s': '%s' will be upgraded"
+ " to '%s' (from '%s'):\n files needed: ",
+ packagename, oldversion,
+ newversion, via);
+ else
+ (void)printf("'%s': newly installed"
+ " as '%s' (from '%s'):\n files needed: ",
+ packagename, newversion, via);
+ (void)strlist_fprint(stdout, newfilekeys);
+ if (verbose > 2)
+ (void)printf("\n installing as: '%s'\n",
+ newcontrol);
+ else
+ (void)putchar('\n');
+ }
+}
+
+static void pull_dump(struct pull_distribution *distribution) {
+ struct pull_target *u;
+
+ for (u=distribution->targets ; u != NULL ; u=u->next) {
+ if (u->upgradelist == NULL)
+ continue;
+ printf("Updates needed for '%s':\n", u->target->identifier);
+ upgradelist_dump(u->upgradelist, pull_dumppackage);
+ upgradelist_free(u->upgradelist);
+ u->upgradelist = NULL;
+ }
+}
+
+static void pull_dumplistpackage(const char *packagename, /*@null@*/const char *oldversion, /*@null@*/const char *newversion, /*@null@*/const char *bestcandidate, /*@null@*/const struct strlist *newfilekeys, /*@null@*/const char *newcontrol, void *privdata) {
+ struct pull_source *source = privdata;
+
+ if (newversion == NULL) {
+ if (oldversion == NULL)
+ return;
+ printf("delete '%s' '%s'\n", packagename, oldversion);
+ } else if (newversion == oldversion) {
+ if (bestcandidate != NULL)
+ printf("keep '%s' '%s' '%s'\n", packagename,
+ oldversion, bestcandidate);
+ else
+ printf("keep '%s' '%s' unavailable\n", packagename,
+ oldversion);
+ } else {
+ const char *via = source->rule->name;
+
+ assert (newfilekeys != NULL);
+ assert (newcontrol != NULL);
+ if (oldversion != NULL)
+ (void)printf("update '%s' '%s' '%s' '%s'\n",
+ packagename, oldversion,
+ newversion, via);
+ else
+ (void)printf("add '%s' - '%s' '%s'\n",
+ packagename, newversion, via);
+ }
+}
+
+static void pull_dumplist(struct pull_distribution *distribution) {
+ struct pull_target *u;
+
+ for (u=distribution->targets ; u != NULL ; u=u->next) {
+ if (u->upgradelist == NULL)
+ continue;
+ printf("Updates needed for '%s':\n", u->target->identifier);
+ upgradelist_dump(u->upgradelist, pull_dumplistpackage);
+ upgradelist_free(u->upgradelist);
+ u->upgradelist = NULL;
+ }
+}
+
+retvalue pull_update(struct pull_distribution *distributions) {
+ retvalue result, r;
+ struct pull_distribution *d;
+
+ for (d=distributions ; d != NULL ; d=d->next) {
+ r = distribution_prepareforwriting(d->distribution);
+ if (RET_WAS_ERROR(r))
+ return r;
+ r = distribution_loadalloverrides(d->distribution);
+ if (RET_WAS_ERROR(r))
+ return r;
+ }
+
+ if (verbose >= 0)
+ printf("Calculating packages to pull...\n");
+
+ result = RET_NOTHING;
+
+ for (d=distributions ; d != NULL ; d=d->next) {
+ r = pull_search(stdout, d);
+ RET_UPDATE(result, r);
+ if (RET_WAS_ERROR(r))
+ break;
+ // TODO: make already here sure the files are ready?
+ }
+ if (RET_WAS_ERROR(result)) {
+ for (d=distributions ; d != NULL ; d=d->next) {
+ struct pull_target *u;
+ for (u=d->targets ; u != NULL ; u=u->next) {
+ upgradelist_free(u->upgradelist);
+ u->upgradelist = NULL;
+ }
+ }
+ return result;
+ }
+ if (verbose >= 0)
+ printf("Installing (and possibly deleting) packages...\n");
+
+ for (d=distributions ; d != NULL ; d=d->next) {
+ if (global.onlysmalldeletes) {
+ if (pull_isbigdelete(d)) {
+ fprintf(stderr,
+"Not processing '%s' because of --onlysmalldeletes\n",
+ d->distribution->codename);
+ continue;
+ }
+ }
+ r = pull_install(d);
+ RET_UPDATE(result, r);
+ if (RET_WAS_ERROR(r))
+ break;
+ }
+ logger_wait();
+
+ return result;
+}
+
+retvalue pull_checkupdate(struct pull_distribution *distributions) {
+ struct pull_distribution *d;
+ retvalue result, r;
+
+ for (d=distributions ; d != NULL ; d=d->next) {
+ r = distribution_loadalloverrides(d->distribution);
+ if (RET_WAS_ERROR(r))
+ return r;
+ }
+
+ if (verbose >= 0)
+ fprintf(stderr, "Calculating packages to get...\n");
+
+ result = RET_NOTHING;
+
+ for (d=distributions ; d != NULL ; d=d->next) {
+ r = pull_search(stderr, d);
+ RET_UPDATE(result, r);
+ if (RET_WAS_ERROR(r))
+ break;
+ pull_dump(d);
+ }
+
+ return result;
+}
+
+retvalue pull_dumpupdate(struct pull_distribution *distributions) {
+ struct pull_distribution *d;
+ retvalue result, r;
+
+ for (d=distributions ; d != NULL ; d=d->next) {
+ r = distribution_loadalloverrides(d->distribution);
+ if (RET_WAS_ERROR(r))
+ return r;
+ }
+
+ result = RET_NOTHING;
+
+ for (d=distributions ; d != NULL ; d=d->next) {
+ r = pull_search(NULL, d);
+ RET_UPDATE(result, r);
+ if (RET_WAS_ERROR(r))
+ break;
+ pull_dumplist(d);
+ }
+
+ return result;
+}