/* This file is part of "reprepro" * Copyright (C) 2010,2011,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 #include #include #include #include #include #include "error.h" #include "distribution.h" #include "trackingt.h" #include "package.h" #include "sourcecheck.h" /* This is / will be the implementation of the * unusedsources * withoutsource * reportcruft * removecruft (to be implemented) * commands. * * Currently those only work with tracking enabled, but * are in this file as the implementation without tracking * will need similar infrastructure */ /* TODO: some tree might be more efficient, check how bad the comparisons are here */ struct info_source { struct info_source *next; char *name; struct info_source_version { struct info_source_version *next; char *version; bool used; } version; }; static void free_source_info(struct info_source *s) { while (s != NULL) { struct info_source *h = s; s = s->next; while (h->version.next != NULL) { struct info_source_version *v = h->version.next; h->version.next = v->next; free(v->version); free(v); } free(h->version.version); free(h->name); free(h); } } static retvalue collect_source_versions(struct distribution *d, struct info_source **out) { struct info_source *root = NULL, *last = NULL; struct target *t; struct package_cursor cursor; retvalue result = RET_NOTHING, r; for (t = d->targets ; t != NULL ; t = t->next) { if (t->architecture != architecture_source) continue; r = package_openiterator(t, true, true, &cursor); if (RET_WAS_ERROR(r)) { RET_UPDATE(result, r); break; } while (package_next(&cursor)) { struct info_source **into = NULL; struct info_source_version *v; r = package_getversion(&cursor.current); if (!RET_IS_OK(r)) { RET_UPDATE(result, r); continue; } if (last != NULL) { int c; c = strcmp(cursor.current.name, last->name); if (c < 0) { /* start at the beginning */ last = NULL; } else while (c > 0) { into = &last->next; if (last->next == NULL) break; last = last->next; c = strcmp(cursor.current.name, last->name); if (c == 0) { into = NULL; break; } } } /* if into != NULL, place there, * if last != NULL, already found */ if (last == NULL) { into = &root; while ((last = *into) != NULL) { int c; c = strcmp(cursor.current.name, last->name); if (c == 0) { into = NULL; break; } if (c < 0) break; into = &last->next; } } if (into != NULL) { last = zNEW(struct info_source); if (FAILEDTOALLOC(last)) { result = RET_ERROR_OOM; break; } last->name = strdup(cursor.current.name); if (FAILEDTOALLOC(last->name)) { free(last); result = RET_ERROR_OOM; break; } last->version.version = package_dupversion( &cursor.current); if (FAILEDTOALLOC(last->version.version)) { result = RET_ERROR_OOM; free(last->name); free(last); break; } last->next = *into; *into = last; RET_UPDATE(result, RET_OK); continue; } assert (last != NULL); assert (strcmp(cursor.current.name, last->name)==0); v = &last->version; while (strcmp(v->version, cursor.current.version) != 0) { if (v->next == NULL) { v->next = zNEW(struct info_source_version); if (FAILEDTOALLOC(v->next)) { result = RET_ERROR_OOM; break; } v = v->next; v->version = package_dupversion( &cursor.current); if (FAILEDTOALLOC(v->version)) { result = RET_ERROR_OOM; break; } RET_UPDATE(result, RET_OK); break; } v = v->next; } } r = package_closeiterator(&cursor); if (RET_WAS_ERROR(r)) { RET_UPDATE(result, r); break; } } if (RET_IS_OK(result)) *out = root; else { assert (result != RET_NOTHING || root == NULL); free_source_info(root); } return result; } static retvalue process_binaries(struct distribution *d, struct info_source *sources, retvalue (*action)(struct package *, void *), void *privdata) { struct target *t; struct package_cursor cursor; retvalue result = RET_NOTHING, r; for (t = d->targets ; t != NULL ; t = t->next) { if (t->architecture == architecture_source) continue; r = package_openiterator(t, true, true, &cursor); if (RET_WAS_ERROR(r)) { RET_UPDATE(result, r); break; } while (package_next(&cursor)) { struct info_source *s; struct info_source_version *v; r = package_getsource(&cursor.current); if (!RET_IS_OK(r)) { RET_UPDATE(result, r); continue; } const char *source = cursor.current.source; const char *version = cursor.current.sourceversion; s = sources; while (s != NULL && strcmp(s->name, source) < 0) { s = s->next; } if (s != NULL && strcmp(source, s->name) == 0) { v = &s->version; while (v != NULL && strcmp(version, v->version) != 0) v = v->next; } else v = NULL; if (v != NULL) { v->used = true; } else if (action != NULL) { r = action(&cursor.current, privdata); RET_UPDATE(result, r); } } r = package_closeiterator(&cursor); if (RET_WAS_ERROR(r)) { RET_UPDATE(result, r); break; } } return result; } static retvalue listunusedsources(struct distribution *d, const struct trackedpackage *pkg) { bool hasbinary = false, hassource = false; int i; for (i = 0 ; i < pkg->filekeys.count ; i++) { if (pkg->refcounts[i] == 0) continue; if (pkg->filetypes[i] == 's') hassource = true; if (pkg->filetypes[i] == 'b') hasbinary = true; if (pkg->filetypes[i] == 'a') hasbinary = true; } if (hassource && ! hasbinary) { printf("%s %s %s\n", d->codename, pkg->sourcename, pkg->sourceversion); return RET_OK; } return RET_NOTHING; } retvalue unusedsources(struct distribution *alldistributions) { struct distribution *d; retvalue result = RET_NOTHING, r; for (d = alldistributions ; d != NULL ; d = d->next) { if (!d->selected) continue; if (!atomlist_in(&d->architectures, architecture_source)) continue; if (d->tracking != dt_NONE) { r = tracking_foreach_ro(d, listunusedsources); RET_UPDATE(result, r); if (RET_WAS_ERROR(r)) return r; continue; } struct info_source *sources = NULL; const struct info_source *s; const struct info_source_version *v; r = collect_source_versions(d, &sources); if (!RET_IS_OK(r)) continue; r = process_binaries(d, sources, NULL, NULL); RET_UPDATE(result, r); for (s = sources ; s != NULL ; s = s->next) { for (v = &s->version ; v != NULL ; v = v->next) { if (v->used) continue; printf("%s %s %s\n", d->codename, s->name, v->version); } } free_source_info(sources); } return result; } static retvalue listsourcemissing(struct distribution *d, const struct trackedpackage *pkg) { bool hasbinary = false, hassource = false; int i; for (i = 0 ; i < pkg->filekeys.count ; i++) { if (pkg->refcounts[i] == 0) continue; if (pkg->filetypes[i] == 's') hassource = true; if (pkg->filetypes[i] == 'b') hasbinary = true; if (pkg->filetypes[i] == 'a') hasbinary = true; } if (hasbinary && ! hassource) { for (i = 0 ; i < pkg->filekeys.count ; i++) { if (pkg->refcounts[i] == 0) continue; if (pkg->filetypes[i] != 'b' && pkg->filetypes[i] != 'a') continue; printf("%s %s %s %s\n", d->codename, pkg->sourcename, pkg->sourceversion, pkg->filekeys.values[i]); } return RET_OK; } return RET_NOTHING; } static retvalue listmissing(struct package *package, UNUSED(void*data)) { retvalue r; struct strlist list; r = package->target->getfilekeys(package->control, &list); if (!RET_IS_OK(r)) return r; assert (list.count == 1); printf("%s %s %s %s\n", package->target->distribution->codename, package->source, package->sourceversion, list.values[0]); strlist_done(&list); return RET_OK; } retvalue sourcemissing(struct distribution *alldistributions) { struct distribution *d; retvalue result = RET_NOTHING, r; for (d = alldistributions ; d != NULL ; d = d->next) { if (!d->selected) continue; if (!atomlist_in(&d->architectures, architecture_source)) { if (verbose >= 0) fprintf(stderr, "Not processing distribution '%s', as it has no source packages.\n", d->codename); continue; } if (d->tracking != dt_NONE) { r = tracking_foreach_ro(d, listsourcemissing); RET_UPDATE(result, r); if (RET_WAS_ERROR(r)) return r; } else { struct info_source *sources = NULL; r = collect_source_versions(d, &sources); if (!RET_IS_OK(r)) continue; r = process_binaries(d, sources, listmissing, NULL); RET_UPDATE(result, r); free_source_info(sources); } } return result; } static retvalue listcruft(struct distribution *d, const struct trackedpackage *pkg) { bool hasbinary = false, hassource = false; int i; for (i = 0 ; i < pkg->filekeys.count ; i++) { if (pkg->refcounts[i] == 0) continue; if (pkg->filetypes[i] == 's') hassource = true; if (pkg->filetypes[i] == 'b') hasbinary = true; if (pkg->filetypes[i] == 'a') hasbinary = true; } if (hasbinary && ! hassource) { printf("binaries-without-source %s %s %s\n", d->codename, pkg->sourcename, pkg->sourceversion); return RET_OK; } else if (hassource && ! hasbinary) { printf("source-without-binaries %s %s %s\n", d->codename, pkg->sourcename, pkg->sourceversion); return RET_OK; } return RET_NOTHING; } static retvalue listmissingonce(struct package *package, void *data) { struct info_source **already = data; struct info_source *s; for (s = *already ; s != NULL ; s = s->next) { if (strcmp(s->name, package->source) != 0) continue; if (strcmp(s->version.version, package->sourceversion) != 0) continue; return RET_NOTHING; } s = zNEW(struct info_source); if (FAILEDTOALLOC(s)) return RET_ERROR_OOM; s->name = strdup(package->source); s->version.version = strdup(package->sourceversion); if (FAILEDTOALLOC(s->name) || FAILEDTOALLOC(s->version.version)) { free(s->name); free(s->version.version); free(s); return RET_ERROR_OOM; } s->next = *already; *already = s; printf("binaries-without-source %s %s %s\n", package->target->distribution->codename, package->source, package->sourceversion); return RET_OK; } retvalue reportcruft(struct distribution *alldistributions) { struct distribution *d; retvalue result = RET_NOTHING, r; for (d = alldistributions ; d != NULL ; d = d->next) { if (!d->selected) continue; if (!atomlist_in(&d->architectures, architecture_source)) { if (verbose >= 0) fprintf(stderr, "Not processing distribution '%s', as it has no source packages.\n", d->codename); continue; } if (d->tracking != dt_NONE) { r = tracking_foreach_ro(d, listcruft); RET_UPDATE(result, r); if (RET_WAS_ERROR(r)) return r; continue; } struct info_source *sources = NULL; struct info_source *list = NULL; const struct info_source *s; const struct info_source_version *v; r = collect_source_versions( d, &sources); if (!RET_IS_OK(r)) continue; r = process_binaries( d, sources, listmissingonce, &list); RET_UPDATE(result, r); for (s = sources ; s != NULL ; s = s->next) { for (v = &s->version ; v != NULL ; v = v->next) { if (v->used) continue; printf("source-without-binaries %s %s %s\n", d->codename, s->name, v->version); } } free_source_info(list); free_source_info(sources); } return result; }