summaryrefslogtreecommitdiffstats
path: root/filterlist.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 /filterlist.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 'filterlist.c')
-rw-r--r--filterlist.c599
1 files changed, 599 insertions, 0 deletions
diff --git a/filterlist.c b/filterlist.c
new file mode 100644
index 0000000..d2f39eb
--- /dev/null
+++ b/filterlist.c
@@ -0,0 +1,599 @@
+/* This file is part of "reprepro"
+ * Copyright (C) 2005 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 <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include "error.h"
+#include "mprintf.h"
+#include "strlist.h"
+#include "names.h"
+#include "configparser.h"
+#include "filterlist.h"
+
+static struct filterlistfile {
+ size_t reference_count;
+
+ char *filename;
+ size_t filename_len;
+
+ /*@owned@*//*@null@*/
+ struct filterlistitem *root;
+ /*@dependent@*//*@null@*/
+ const struct filterlistitem *last;
+
+ /*@owned@*//*@null@*/
+ struct filterlistfile *next;
+} *listfiles = NULL;
+
+struct filterlistitem {
+ /*@owned@*//*@null@*/
+ struct filterlistitem *next;
+ char *packagename;
+ char *version;
+ enum filterlisttype what;
+};
+
+static void filterlistitems_free(/*@null@*//*@only@*/struct filterlistitem *list) {
+ while (list != NULL) {
+ struct filterlistitem *next = list->next;
+ free(list->version);
+ free(list->packagename);
+ free(list);
+ list = next;
+ }
+}
+
+static void filterlistfile_unlock(struct filterlistfile *list) {
+ assert (list != NULL);
+
+ if (list->reference_count <= 1) {
+ struct filterlistfile **p = &listfiles;
+
+ assert (list->reference_count == 1);
+ if (list->reference_count == 0)
+ return;
+
+ while (*p != NULL && *p != list)
+ p = &(*p)->next;
+ assert (p != NULL);
+ if (*p == list) {
+ *p = list->next;
+ filterlistitems_free(list->root);
+ free(list->filename);
+ free(list);
+ }
+ } else
+ list->reference_count--;
+}
+
+static inline retvalue filterlistfile_parse(struct filterlistfile *n, const char *filename, FILE *f) {
+ char *lineend, *namestart, *nameend, *what, *version;
+ int cmp;
+ enum filterlisttype type;
+ struct filterlistitem *h;
+ char line[1001];
+ int lineno = 0;
+ struct filterlistitem **last = &n->root;
+
+ while (fgets(line, 1000, f) != NULL) {
+ lineno++;
+ lineend = strchr(line, '\n');
+ if (lineend == NULL) {
+ fprintf(stderr, "Overlong or unterminated line in '%s'!\n", filename);
+ return RET_ERROR;
+ }
+ while (lineend >= line && xisspace(*lineend))
+ *(lineend--) = '\0';
+ /* Ignore line only containing whitespace */
+ if (line[0] == '\0')
+ continue;
+ /* Ignore lines starting with a comment sign */
+ if (line[0] == '#')
+ continue;
+ namestart = line;
+ while (*namestart != '\0' && xisspace(*namestart))
+ namestart++;
+ nameend=namestart;
+ while (*nameend != '\0' && !xisspace(*nameend))
+ nameend++;
+ what = nameend;
+ while (*what != '\0' && xisspace(*what))
+ *(what++)='\0';
+ if (*what == '\0') {
+ fprintf(stderr,
+"Malformed line in '%s': %d!\n", filename, lineno);
+ return RET_ERROR;
+ }
+ version = NULL;
+ if (strcmp(what, "install") == 0) {
+ type = flt_install;
+ } else if (strcmp(what, "deinstall") == 0) {
+ type = flt_deinstall;
+ } else if (strcmp(what, "purge") == 0) {
+ type = flt_purge;
+ } else if (strcmp(what, "hold") == 0) {
+ type = flt_hold;
+ } else if (strcmp(what, "supersede") == 0) {
+ type = flt_supersede;
+ } else if (strcmp(what, "upgradeonly") == 0) {
+ type = flt_upgradeonly;
+ } else if (strcmp(what, "warning") == 0) {
+ type = flt_warning;
+ } else if (strcmp(what, "error") == 0) {
+ type = flt_error;
+ } else if (what[0] == '=') {
+ what++;
+ while (*what != '\0' && xisspace(*what))
+ what++;
+ version = what;
+ if (*version == '\0') {
+ fprintf(stderr,
+"Malformed line %d in '%s': missing version after '='!\n",
+ lineno, filename);
+ return RET_ERROR;
+ }
+ while (*what != '\0' && !xisspace(*what))
+ what++;
+ while (*what != '\0' && xisspace(*what))
+ *(what++) = '\0';
+ if (*what != '\0') {
+ fprintf(stderr,
+"Malformed line %d in '%s': space in version!\n",
+ lineno, filename);
+ return RET_ERROR;
+ }
+ type = flt_install;
+ } else {
+ fprintf(stderr,
+"Unknown status in '%s':%d: '%s'!\n", filename, lineno, what);
+ return RET_ERROR;
+ }
+ if (*last == NULL || strcmp(namestart, (*last)->packagename) < 0)
+ last = &n->root;
+ cmp = -1;
+ while (*last != NULL &&
+ (cmp=strcmp(namestart, (*last)->packagename)) > 0)
+ last = &((*last)->next);
+ if (cmp == 0) {
+ fprintf(stderr,
+"Two lines describing '%s' in '%s'!\n", namestart, filename);
+ return RET_ERROR;
+ }
+ h = zNEW(struct filterlistitem);
+ if (FAILEDTOALLOC(h)) {
+ return RET_ERROR_OOM;
+ }
+ h->next = *last;
+ *last = h;
+ h->what = type;
+ h->packagename = strdup(namestart);
+ if (FAILEDTOALLOC(h->packagename)) {
+ return RET_ERROR_OOM;
+ }
+ if (version == NULL)
+ h->version = NULL;
+ else {
+ h->version = strdup(version);
+ if (FAILEDTOALLOC(h->version))
+ return RET_ERROR_OOM;
+ }
+ }
+ n->last = *last;
+ return RET_OK;
+
+}
+
+static inline retvalue filterlistfile_read(struct filterlistfile *n, const char *filename) {
+ FILE *f;
+ retvalue r;
+
+ f = fopen(filename, "r");
+ if (f == NULL) {
+ fprintf(stderr, "Cannot open %s for reading: %s!\n",
+ filename, strerror(errno));
+ return RET_ERROR;
+ }
+ r = filterlistfile_parse(n, filename, f);
+
+ // Can this return an yet unseen error? was read-only..
+ (void)fclose(f);
+ return r;
+}
+
+static inline retvalue filterlistfile_getl(const char *filename, size_t len, struct filterlistfile **list) {
+ struct filterlistfile *p;
+ retvalue r;
+
+ for (p = listfiles ; p != NULL ; p = p->next) {
+ if (p->filename_len == len &&
+ strncmp(p->filename, filename, len) == 0) {
+ p->reference_count++;
+ *list = p;
+ return RET_OK;
+ }
+ }
+ p = zNEW(struct filterlistfile);
+ if (FAILEDTOALLOC(p))
+ return RET_ERROR_OOM;
+ p->reference_count = 1;
+ p->filename = strndup(filename, len);
+ p->filename_len = len;
+ if (FAILEDTOALLOC(p->filename)) {
+ free(p);
+ return RET_ERROR_OOM;
+ }
+ char *fullfilename = configfile_expandname(p->filename, NULL);
+ if (FAILEDTOALLOC(fullfilename))
+ r = RET_ERROR_OOM;
+ else {
+ r = filterlistfile_read(p, fullfilename);
+ free(fullfilename);
+ }
+
+ if (RET_IS_OK(r)) {
+ p->next = listfiles;
+ listfiles = p;
+ *list = p;
+ } else {
+ filterlistitems_free(p->root);
+ free(p->filename);
+ free(p);
+ }
+ return r;
+}
+
+static inline retvalue filterlistfile_get(/*@only@*/char *filename, /*@out@*/struct filterlistfile **list) {
+ struct filterlistfile *p;
+ retvalue r;
+ size_t len = strlen(filename);
+
+ for (p = listfiles ; p != NULL ; p = p->next) {
+ if (p->filename_len == len &&
+ strncmp(p->filename, filename, len) == 0) {
+ p->reference_count++;
+ *list = p;
+ free(filename);
+ return RET_OK;
+ }
+ }
+ p = zNEW(struct filterlistfile);
+ if (FAILEDTOALLOC(p)) {
+ free(filename);
+ return RET_ERROR_OOM;
+ }
+ p->reference_count = 1;
+ p->filename = filename;
+ p->filename_len = len;
+ if (FAILEDTOALLOC(p->filename)) {
+ free(p);
+ return RET_ERROR_OOM;
+ }
+ char *fullfilename = configfile_expandname(p->filename, NULL);
+ if (FAILEDTOALLOC(fullfilename))
+ r = RET_ERROR_OOM;
+ else {
+ r = filterlistfile_read(p, fullfilename);
+ free(fullfilename);
+ }
+
+ if (RET_IS_OK(r)) {
+ p->next = listfiles;
+ listfiles = p;
+ *list = p;
+ } else {
+ filterlistitems_free(p->root);
+ free(p->filename);
+ free(p);
+ }
+ return r;
+}
+
+void filterlist_release(struct filterlist *list) {
+ size_t i;
+
+ assert(list != NULL);
+
+ if (list->files != NULL) {
+ for (i = 0 ; i < list->count ; i++)
+ filterlistfile_unlock(list->files[i]);
+ free(list->files);
+ list->files = NULL;
+ } else {
+ assert (list->count == 0);
+ }
+}
+
+static const struct constant filterlisttype_listtypes[] = {
+ {"install", (int)flt_install},
+ {"hold", (int)flt_hold},
+ {"supersede", (int)flt_supersede},
+ {"deinstall", (int)flt_deinstall},
+ {"purge", (int)flt_purge},
+ {"upgradeonly", (int)flt_upgradeonly},
+ {"warning", (int)flt_warning},
+ {"error", (int)flt_error},
+ {NULL, 0}
+};
+
+retvalue filterlist_load(struct filterlist *list, struct configiterator *iter) {
+ enum filterlisttype defaulttype;
+ size_t count;
+ struct filterlistfile **files;
+ retvalue r;
+ char *filename;
+
+ r = config_getenum(iter, filterlisttype, listtypes, &defaulttype);
+ if (r == RET_NOTHING || r == RET_ERROR_UNKNOWNFIELD) {
+ fprintf(stderr,
+"Error parsing %s, line %u, column %u: Expected default action as first argument to FilterList: (one of install, purge, hold, ...)\n",
+ config_filename(iter),
+ config_markerline(iter),
+ config_markercolumn(iter));
+ return RET_ERROR;
+ }
+ if (RET_WAS_ERROR(r))
+ return r;
+
+ count = 0;
+ files = NULL;
+ while ((r = config_getword(iter, &filename)) != RET_NOTHING) {
+ struct filterlistfile **n;
+
+ n = realloc(files, (count+1)*
+ sizeof(struct filterlistfile *));
+ if (FAILEDTOALLOC(n)) {
+ free(filename);
+ r = RET_ERROR_OOM;
+ } else {
+ n[count] = NULL;
+ files = n;
+ // TODO: make filename only
+ r = filterlistfile_get(filename, &files[count]);
+ if (RET_IS_OK(r))
+ count++;
+ }
+ if (RET_WAS_ERROR(r)) {
+ while (count > 0) {
+ count--;
+ filterlistfile_unlock(files[count]);
+ }
+ free(files);
+ return r;
+ }
+ }
+ list->count = count;
+ list->files = files;
+ list->defaulttype = defaulttype;
+ list->set = true;
+ return RET_OK;
+}
+
+static inline bool find(const char *name, /*@null@*/struct filterlistfile *list) {
+ int cmp;
+ /*@dependent@*/const struct filterlistitem *last = list->last;
+
+ assert (last != NULL);
+
+ if (last->next != NULL) {
+ cmp = strcmp(name, last->next->packagename);
+ if (cmp == 0) {
+ list->last = last->next;
+ return true;
+ }
+ }
+ if (last->next == NULL || cmp < 0) {
+ cmp = strcmp(name, last->packagename);
+ if (cmp == 0) {
+ return true;
+ } else if (cmp > 0)
+ return false;
+ last = list->root;
+ cmp = strcmp(name, last->packagename);
+ if (cmp == 0) {
+ list->last = list->root;
+ return true;
+ } else if (cmp < 0)
+ return false;
+ }
+ /* now we are after last */
+ while (last->next != NULL) {
+ cmp = strcmp(name, last->next->packagename);
+ if (cmp == 0) {
+ list->last = last->next;
+ return true;
+ }
+ if (cmp < 0) {
+ list->last = last;
+ return false;
+ }
+ last = last->next;
+ }
+ list->last = last;
+ return false;
+}
+
+enum filterlisttype filterlist_find(const char *name, const char *version, const struct filterlist *list) {
+ size_t i;
+ for (i = 0 ; i < list->count ; i++) {
+ if (list->files[i]->root == NULL)
+ continue;
+ if (!find(name, list->files[i]))
+ continue;
+ if (list->files[i]->last->version == NULL)
+ return list->files[i]->last->what;
+ if (strcmp(list->files[i]->last->version, version) == 0)
+ return list->files[i]->last->what;
+ }
+ return list->defaulttype;
+}
+
+struct filterlist cmdline_bin_filter = {
+ .count = 0,
+ .files = NULL,
+ /* as long as nothing added, this does not change anything.
+ * Once something is added, that will be auto_hold */
+ .defaulttype = flt_unchanged,
+ .set = false,
+};
+struct filterlist cmdline_src_filter = {
+ .count = 0,
+ .files = NULL,
+ /* as long as nothing added, this does not change anything.
+ * Once something is added, that will be auto_hold */
+ .defaulttype = flt_unchanged,
+ .set = false,
+};
+
+static retvalue filterlist_cmdline_init(struct filterlist *l) {
+ if (l->count == 0) {
+ l->files = nzNEW(2, struct filterlistfile *);
+ if (FAILEDTOALLOC(l->files))
+ return RET_ERROR_OOM;
+ l->files[0] = zNEW(struct filterlistfile);
+ if (FAILEDTOALLOC(l->files[0]))
+ return RET_ERROR_OOM;
+ l->files[0]->reference_count = 1;
+ l->count = 1;
+ }
+ return RET_OK;
+}
+
+retvalue filterlist_cmdline_add_file(bool src, const char *filename) {
+ retvalue r;
+ struct filterlist *l = src ? &cmdline_src_filter : &cmdline_bin_filter;
+ char *name;
+
+ r = filterlist_cmdline_init(l);
+ if (RET_WAS_ERROR(r))
+ return r;
+ l->set = true;
+ l->defaulttype = flt_auto_hold;
+
+ if (strcmp(filename, "-") == 0)
+ filename = "/dev/stdin";
+ name = strdup(filename);
+ if (FAILEDTOALLOC(name))
+ return RET_ERROR_OOM;
+ if (l->count > 1) {
+ struct filterlistfile **n;
+
+ n = realloc(l->files, (l->count + 1) *
+ sizeof(struct filterlistfile *));
+ if (FAILEDTOALLOC(n)) {
+ free(name);
+ return RET_ERROR_OOM;
+ }
+ n[l->count++] = NULL;
+ l->files = n;
+ } else {
+ /* already allocated in _init */
+ assert (l->count == 1);
+ l->count++;
+ }
+
+ return filterlistfile_get(name, &l->files[l->count - 1]);
+}
+
+retvalue filterlist_cmdline_add_pkg(bool src, const char *package) {
+ retvalue r;
+ enum filterlisttype what;
+ struct filterlist *l = src ? &cmdline_src_filter : &cmdline_bin_filter;
+ struct filterlistfile *f;
+ struct filterlistitem **p, *h;
+ char *name, *version;
+ const char *c;
+ int cmp;
+
+ r = filterlist_cmdline_init(l);
+ if (RET_WAS_ERROR(r))
+ return r;
+ l->set = true;
+ l->defaulttype = flt_auto_hold;
+
+ c = strchr(package, '=');
+ if (c != NULL) {
+ what = flt_install;
+ name = strndup(package, c - package);
+ if (FAILEDTOALLOC(name))
+ return RET_ERROR_OOM;
+ version = strdup(c + 1);
+ if (FAILEDTOALLOC(version)) {
+ free(name);
+ return RET_ERROR_OOM;
+ }
+ } else {
+ version = NULL;
+ c = strchr(package, ':');
+ if (c == NULL) {
+ what = flt_install;
+ name = strndup(package, c - package);
+ if (FAILEDTOALLOC(name))
+ return RET_ERROR_OOM;
+ } else {
+ const struct constant *t = filterlisttype_listtypes;
+ while (t->name != NULL) {
+ if (strcmp(c + 1, t->name) == 0) {
+ what = t->value;
+ break;
+ }
+ t++;
+ }
+ if (t->name == NULL) {
+ fprintf(stderr,
+"Error: unknown filter-outcome '%s' (expected 'install' or ...)\n",
+ c + 1);
+ return RET_ERROR;
+ }
+
+ }
+ name = strndup(package, c - package);
+ if (FAILEDTOALLOC(name))
+ return RET_ERROR_OOM;
+ }
+ f = l->files[0];
+ assert (f != NULL);
+ p = &f->root;
+ cmp = -1;
+ while (*p != NULL && (cmp = strcmp(name, (*p)->packagename)) > 0)
+ p = &((*p)->next);
+ if (cmp == 0) {
+ fprintf(stderr,
+"Package in command line filter two times: '%s'\n",
+ name);
+ free(name);
+ free(version);
+ return RET_ERROR;
+ }
+ h = zNEW(struct filterlistitem);
+ if (FAILEDTOALLOC(h)) {
+ free(name);
+ free(version);
+ return RET_ERROR_OOM;
+ }
+ h->next = *p;
+ *p = h;
+ h->what = what;
+ h->packagename = name;
+ h->version = version;
+ f->last = h;
+ return RET_OK;
+}