summaryrefslogtreecommitdiffstats
path: root/tool.c
diff options
context:
space:
mode:
Diffstat (limited to 'tool.c')
-rw-r--r--tool.c3099
1 files changed, 3099 insertions, 0 deletions
diff --git a/tool.c b/tool.c
new file mode 100644
index 0000000..cc8579c
--- /dev/null
+++ b/tool.c
@@ -0,0 +1,3099 @@
+/* This file is part of "reprepro"
+ * Copyright (C) 2006,2007,2008,2009,2010 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 <getopt.h>
+#include <string.h>
+#include <strings.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <time.h>
+#include "error.h"
+#include "filecntl.h"
+#include "mprintf.h"
+#include "strlist.h"
+#include "names.h"
+#include "dirs.h"
+#include "checksums.h"
+#include "chunks.h"
+#include "chunkedit.h"
+#include "signature.h"
+#include "debfile.h"
+#include "sourceextraction.h"
+#include "uncompression.h"
+
+/* for compatibility with used code */
+int verbose=0;
+bool interrupted(void) {
+ return false;
+}
+
+static void about(bool help) NORETURN;
+static void about(bool help) {
+ fprintf(help?stdout:stderr,
+"changestool: Modify a Debian style .changes file\n"
+"Syntax: changestool [--create] <changesfile> <commands>\n"
+"Possible commands include:\n"
+" verify\n"
+" updatechecksums [<files to update>]\n"
+" includeallsources [<files to copy from .dsc to .changes>]\n"
+" adddeb <.deb filenames>\n"
+" adddsc <.dsc filenames>\n"
+" addrawfile <filenames>\n"
+" add <filenames processed by filename suffix>\n"
+" setdistribution <distributions to list>\n"
+" dumbremove <filenames>\n"
+);
+ if (help)
+ exit(EXIT_SUCCESS);
+ else
+ exit(EXIT_FAILURE);
+}
+
+struct binaryfile {
+ struct binaryfile *next; // in binaries.files list
+ struct binary *binary; // parent
+ struct fileentry *file;
+ char *controlchunk;
+ char *name, *version, *architecture;
+ char *sourcename, *sourceversion;
+ char *maintainer;
+ char *section, *priority;
+ char *shortdescription;
+ bool hasmd5sums;
+};
+
+static void binaryfile_free(struct binaryfile *p) {
+ if (p == NULL)
+ return;
+
+ free(p->controlchunk);
+ free(p->name);
+ free(p->version);
+ free(p->architecture);
+ free(p->sourcename);
+ free(p->sourceversion);
+ free(p->maintainer);
+ free(p->section);
+ free(p->priority);
+ free(p->shortdescription);
+ free(p);
+}
+
+enum filetype { ft_UNKNOWN,
+ ft_TAR, ft_ORIG_TAR, ft_DIFF,
+#define ft_MaxInSource ft_DSC-1
+ ft_DSC, ft_DEB, ft_UDEB, ft_DDEB, ft_Count};
+#define ft_Max ft_Count-1
+
+static const struct {
+ const char *suffix;
+ size_t len;
+ bool allowcompressed;
+} typesuffix[ft_Count] = {
+ { "?", -1, false},
+ { ".tar", 4, true},
+ { ".orig.tar", 9, true},
+ { ".diff", 5, true},
+ { ".dsc", 4, false},
+ { ".deb", 4, false},
+ { ".udeb", 5, false},
+ { ".ddeb", 5, false}
+};
+
+struct dscfile {
+ struct fileentry *file;
+ char *name;
+ char *version;
+ struct strlist binaries;
+ char *maintainer;
+ char *controlchunk;
+ // hard to get:
+ char *section, *priority;
+ // TODO: check Architectures?
+ struct checksumsarray expected;
+ struct fileentry **uplink;
+ bool parsed, modified;
+};
+
+static void dscfile_free(struct dscfile *p) {
+ if (p == NULL)
+ return;
+
+ free(p->name);
+ free(p->version);
+ free(p->maintainer);
+ free(p->controlchunk);
+ free(p->section);
+ free(p->priority);
+ checksumsarray_done(&p->expected);
+ free(p->uplink);
+ free(p);
+}
+
+struct fileentry {
+ struct fileentry *next;
+ char *basename; size_t namelen;
+ char *fullfilename;
+ /* NULL means was not listed there yet: */
+ struct checksums *checksumsfromchanges,
+ *realchecksums;
+ char *section, *priority;
+ enum filetype type;
+ enum compression compression;
+ /* only if type deb or udeb */
+ struct binaryfile *deb;
+ /* only if type dsc */
+ struct dscfile *dsc;
+ int refcount;
+};
+struct changes;
+static struct fileentry *add_fileentry(struct changes *c, const char *basefilename, size_t len, bool source, /*@null@*//*@out@*/size_t *ofs_p);
+
+struct changes {
+ /* the filename of the .changes file */
+ char *filename;
+ /* directory of filename */
+ char *basedir;
+ /* Contents of the .changes file: */
+ char *name;
+ char *version;
+ char *maintainer;
+ char *control;
+ struct strlist architectures;
+ struct strlist distributions;
+ size_t binarycount;
+ struct binary {
+ char *name;
+ char *description;
+ struct binaryfile *files;
+ bool missedinheader, uncheckable;
+ } *binaries;
+ struct fileentry *files;
+ bool modified;
+};
+
+static void fileentry_free(/*@only@*/struct fileentry *f) {
+ if (f == NULL)
+ return;
+ free(f->basename);
+ free(f->fullfilename);
+ checksums_free(f->checksumsfromchanges);
+ checksums_free(f->realchecksums);
+ free(f->section);
+ free(f->priority);
+ if (f->type == ft_DEB || f->type == ft_DDEB || f->type == ft_UDEB) {
+ binaryfile_free(f->deb);
+ } else if (f->type == ft_DSC) {
+ dscfile_free(f->dsc);
+ }
+ free(f);
+}
+
+static void changes_free(struct changes *c) {
+ unsigned int i;
+
+ if (c == NULL)
+ return;
+
+ free(c->filename);
+ free(c->basedir);
+ free(c->name);
+ free(c->version);
+ free(c->maintainer);
+ free(c->control);
+ strlist_done(&c->architectures);
+ strlist_done(&c->distributions);
+ for (i = 0 ; i < c->binarycount ; i++) {
+ free(c->binaries[i].name);
+ free(c->binaries[i].description);
+ // .files belongs elsewhere
+ }
+ free(c->binaries);
+ while (c->files) {
+ struct fileentry *f = c->files;
+ c->files = f->next;
+ fileentry_free(f);
+ }
+ free(c);
+}
+
+static struct fileentry **find_fileentry(struct changes *c, const char *basefilename, size_t basenamelen, size_t *ofs_p) {
+ struct fileentry **fp = &c->files;
+ struct fileentry *f;
+ size_t ofs = 0;
+
+ while ((f=*fp) != NULL) {
+ if (f->namelen == basenamelen &&
+ strncmp(basefilename, f->basename, basenamelen) == 0) {
+ break;
+ }
+ fp = &f->next;
+ ofs++;
+ }
+ if (ofs_p != NULL)
+ *ofs_p = ofs;
+ return fp;
+}
+
+static struct fileentry *add_fileentry(struct changes *c, const char *basefilename, size_t len, bool source, size_t *ofs_p) {
+ size_t ofs = 0;
+ struct fileentry **fp = find_fileentry(c, basefilename, len, &ofs);
+ struct fileentry *f = *fp;
+
+ if (f == NULL) {
+ enum compression;
+
+ f = zNEW(struct fileentry);
+ if (FAILEDTOALLOC(f))
+ return NULL;
+ f->basename = strndup(basefilename, len);
+ f->namelen = len;
+
+ if (FAILEDTOALLOC(f->basename)) {
+ free(f);
+ return NULL;
+ }
+ *fp = f;
+
+ /* guess compression */
+ f->compression = compression_by_suffix(f->basename, &len);
+
+ /* guess type */
+ for (f->type = source?ft_MaxInSource:ft_Max ;
+ f->type > ft_UNKNOWN ; f->type--) {
+ size_t l = typesuffix[f->type].len;
+
+ if (f->compression != c_none &&
+ !typesuffix[f->type].allowcompressed)
+ continue;
+ if (len <= l)
+ continue;
+ if (strncmp(f->basename + (len-l),
+ typesuffix[f->type].suffix,
+ l) == 0)
+ break;
+ }
+ }
+ if (ofs_p != NULL)
+ *ofs_p = ofs;
+ return f;
+}
+
+static retvalue searchforfile(const char *changesdir, const char *basefilename, /*@null@*/const struct strlist *searchpath, /*@null@*/const char *searchfirstin, char **result) {
+ int i; bool found;
+ char *fullname;
+
+ if (searchfirstin != NULL) {
+ fullname = calc_dirconcat(searchfirstin, basefilename);
+ if (FAILEDTOALLOC(fullname))
+ return RET_ERROR_OOM;
+ if (isregularfile(fullname)) {
+ *result = fullname;
+ return RET_OK;
+ }
+ free(fullname);
+ }
+
+ fullname = calc_dirconcat(changesdir, basefilename);
+ if (FAILEDTOALLOC(fullname))
+ return RET_ERROR_OOM;
+
+ found = isregularfile(fullname);
+ i = 0;
+ while (!found && searchpath != NULL && i < searchpath->count) {
+ free(fullname);
+ fullname = calc_dirconcat(searchpath->values[i],
+ basefilename);
+ if (FAILEDTOALLOC(fullname))
+ return RET_ERROR_OOM;
+ if (isregularfile(fullname)) {
+ found = true;
+ break;
+ }
+ i++;
+ }
+ if (found) {
+ *result = fullname;
+ return RET_OK;
+ } else {
+ free(fullname);
+ return RET_NOTHING;
+ }
+}
+
+static retvalue findfile(const char *filename, const struct changes *c, /*@null@*/const struct strlist *searchpath, /*@null@*/const char *searchfirstin, char **result) {
+ char *fullfilename;
+
+ if (rindex(filename, '/') == NULL) {
+ retvalue r;
+
+ r = searchforfile(c->basedir, filename,
+ searchpath, searchfirstin, &fullfilename);
+ if (!RET_IS_OK(r))
+ return r;
+ } else {
+ if (!isregularfile(filename))
+ return RET_NOTHING;
+ fullfilename = strdup(filename);
+ if (FAILEDTOALLOC(fullfilename))
+ return RET_ERROR_OOM;
+ }
+ *result = fullfilename;
+ return RET_OK;
+}
+
+static retvalue add_file(struct changes *c, /*@only@*/char *basefilename, /*@only@*/char *fullfilename, enum filetype type, struct fileentry **file) {
+ size_t basenamelen = strlen(basefilename);
+ struct fileentry **fp;
+ struct fileentry *f;
+
+ fp = find_fileentry(c, basefilename, basenamelen, NULL);
+ f = *fp;
+
+ if (f != NULL) {
+ *file = f;
+ free(basefilename);
+ free(fullfilename);
+ return RET_NOTHING;
+ }
+ assert (f == NULL);
+ f = zNEW(struct fileentry);
+ if (FAILEDTOALLOC(f)) {
+ free(basefilename);
+ free(fullfilename);
+ return RET_ERROR_OOM;
+ }
+ f->basename = basefilename;
+ f->namelen = basenamelen;
+ f->fullfilename = fullfilename;
+ f->type = type;
+ f->compression = c_none;
+
+ *fp = f;
+ *file = f;
+ return RET_OK;
+}
+
+
+static struct binary *get_binary(struct changes *c, const char *p, size_t len) {
+ unsigned int j;
+
+ for (j = 0 ; j < c->binarycount ; j++) {
+ if (strncmp(c->binaries[j].name, p, len) == 0 &&
+ c->binaries[j].name[len] == '\0')
+ break;
+ }
+ if (j == c->binarycount) {
+ char *name = strndup(p, len);
+ struct binary *n;
+
+ if (FAILEDTOALLOC(name))
+ return NULL;
+ n = realloc(c->binaries, (j+1)*sizeof(struct binary));
+ if (FAILEDTOALLOC(n)) {
+ free(name);
+ return NULL;
+ }
+ c->binaries = n;
+ c->binarycount = j+1;
+ c->binaries[j].name = name;
+ c->binaries[j].description = NULL;
+ c->binaries[j].files = NULL;
+ c->binaries[j].missedinheader = true;
+ c->binaries[j].uncheckable = false;
+ }
+ assert (j < c->binarycount);
+ return &c->binaries[j];
+}
+
+static retvalue parse_changes_description(struct changes *c, struct strlist *tmp) {
+ int i;
+
+ for (i = 0 ; i < tmp->count ; i++) {
+ struct binary *b;
+ const char *p = tmp->values[i];
+ const char *e = p;
+ const char *d;
+ while (*e != '\0' && *e != ' ' && *e != '\t')
+ e++;
+ d = e;
+ while (*d == ' ' || *d == '\t')
+ d++;
+ if (*d == '-')
+ d++;
+ while (*d == ' ' || *d == '\t')
+ d++;
+
+ b = get_binary(c, p, e-p);
+ if (FAILEDTOALLOC(b))
+ return RET_ERROR_OOM;
+
+ b->description = strdup(d);
+ if (FAILEDTOALLOC(b->description))
+ return RET_ERROR_OOM;
+ }
+ return RET_OK;
+}
+
+static retvalue parse_changes_files(struct changes *c, struct strlist filelines[cs_hashCOUNT]) {
+ int i;
+ struct fileentry *f;
+ retvalue r;
+ struct hashes *hashes;
+ struct strlist *tmp;
+ size_t ofs, count = 0;
+ enum checksumtype cs;
+
+ tmp = &filelines[cs_md5sum];
+ hashes = nzNEW(tmp->count, struct hashes);
+ if (FAILEDTOALLOC(hashes))
+ return RET_ERROR_OOM;
+
+ for (i = 0 ; i < tmp->count ; i++) {
+ char *p;
+ const char *md5start, *md5end, *sizestart, *sizeend,
+ *sectionstart, *sectionend, *priostart, *prioend,
+ *filestart, *fileend;
+ p = tmp->values[i];
+#undef xisspace
+#define xisspace(c) (c == ' ' || c == '\t')
+ while (*p !='\0' && xisspace(*p))
+ p++;
+ md5start = p;
+ while ((*p >= '0' && *p <= '9') ||
+ (*p >= 'A' && *p <= 'F') ||
+ (*p >= 'a' && *p <= 'f')) {
+ if (*p >= 'A' && *p <= 'F')
+ (*p) += 'a' - 'A';
+ p++;
+ }
+ md5end = p;
+ while (*p !='\0' && !xisspace(*p))
+ p++;
+ while (*p !='\0' && xisspace(*p))
+ p++;
+ while (*p == '0' && ('0' <= p[1] && p[1] <= '9'))
+ p++;
+ sizestart = p;
+ while ((*p >= '0' && *p <= '9'))
+ p++;
+ sizeend = p;
+ while (*p !='\0' && !xisspace(*p))
+ p++;
+ while (*p !='\0' && xisspace(*p))
+ p++;
+ sectionstart = p;
+ while (*p !='\0' && !xisspace(*p))
+ p++;
+ sectionend = p;
+ while (*p !='\0' && xisspace(*p))
+ p++;
+ priostart = p;
+ while (*p !='\0' && !xisspace(*p))
+ p++;
+ prioend = p;
+ while (*p !='\0' && xisspace(*p))
+ p++;
+ filestart = p;
+ while (*p !='\0' && !xisspace(*p))
+ p++;
+ fileend = p;
+ while (*p !='\0' && xisspace(*p))
+ p++;
+ if (*p != '\0') {
+ fprintf(stderr,
+"Unexpected sixth argument in '%s'!\n",
+ tmp->values[i]);
+ free(hashes);
+ return RET_ERROR;
+ }
+ if (fileend - filestart == 0)
+ continue;
+ f = add_fileentry(c, filestart, fileend-filestart, false, &ofs);
+ assert (ofs <= count);
+ if (ofs == count)
+ count++;
+ if (hashes[ofs].hashes[cs_md5sum].start != NULL) {
+ fprintf(stderr,
+"WARNING: Multiple occourance of '%s' in .changes file!\nIgnoring all but the first one.\n",
+ f->basename);
+ continue;
+ }
+ hashes[ofs].hashes[cs_md5sum].start = md5start;
+ hashes[ofs].hashes[cs_md5sum].len = md5end - md5start;
+ hashes[ofs].hashes[cs_length].start = sizestart;
+ hashes[ofs].hashes[cs_length].len = sizeend - sizestart;
+
+ if (sectionend - sectionstart == 1 && *sectionstart == '-') {
+ f->section = NULL;
+ } else {
+ f->section = strndup(sectionstart,
+ sectionend - sectionstart);
+ if (FAILEDTOALLOC(f->section))
+ return RET_ERROR_OOM;
+ }
+ if (prioend - priostart == 1 && *priostart == '-') {
+ f->priority = NULL;
+ } else {
+ f->priority = strndup(priostart, prioend - priostart);
+ if (FAILEDTOALLOC(f->priority))
+ return RET_ERROR_OOM;
+ }
+ }
+ const char * const hashname[cs_hashCOUNT] = {"Md5", "Sha1", "Sha256", "Sha512" };
+ for (cs = cs_firstEXTENDED ; cs < cs_hashCOUNT ; cs++) {
+ tmp = &filelines[cs];
+
+ for (i = 0 ; i < tmp->count ; i++) {
+ char *p;
+ const char *hashstart, *hashend, *sizestart, *sizeend,
+ *filestart, *fileend;
+ p = tmp->values[i];
+ while (*p !='\0' && xisspace(*p))
+ p++;
+ hashstart = p;
+ while ((*p >= '0' && *p <= '9') ||
+ (*p >= 'A' && *p <= 'F') ||
+ (*p >= 'a' && *p <= 'f') ) {
+ if (*p >= 'A' && *p <= 'F')
+ (*p) += 'a' - 'A';
+ p++;
+ }
+ hashend = p;
+ while (*p !='\0' && !xisspace(*p))
+ p++;
+ while (*p !='\0' && xisspace(*p))
+ p++;
+ while (*p == '0' && ('0' <= p[1] && p[1] <= '9'))
+ p++;
+ sizestart = p;
+ while ((*p >= '0' && *p <= '9'))
+ p++;
+ sizeend = p;
+ while (*p !='\0' && !xisspace(*p))
+ p++;
+ while (*p !='\0' && xisspace(*p))
+ p++;
+ filestart = p;
+ while (*p !='\0' && !xisspace(*p))
+ p++;
+ fileend = p;
+ while (*p !='\0' && xisspace(*p))
+ p++;
+ if (*p != '\0') {
+ fprintf(stderr,
+"Unexpected forth argument in '%s'!\n",
+ tmp->values[i]);
+ return RET_ERROR;
+ }
+ if (fileend - filestart == 0)
+ continue;
+ f = add_fileentry(c, filestart, fileend-filestart,
+ false, &ofs);
+ assert (ofs <= count);
+ // until md5sums are no longer obligatory:
+ if (ofs == count)
+ continue;
+ if (hashes[ofs].hashes[cs].start != NULL) {
+ fprintf(stderr,
+"WARNING: Multiple occourance of '%s' in Checksums-'%s' of .changes file!\n"
+"Ignoring all but the first one.\n",
+ f->basename, hashname[cs]);
+ continue;
+ }
+ hashes[ofs].hashes[cs].start = hashstart;
+ hashes[ofs].hashes[cs].len = hashend - hashstart;
+
+ size_t sizelen = sizeend - sizestart;
+
+ if (hashes[ofs].hashes[cs_length].start == NULL) {
+ hashes[ofs].hashes[cs_length].start = sizestart;
+ hashes[ofs].hashes[cs_length].len = sizelen;
+
+ } else if (hashes[ofs].hashes[cs_length].len != sizelen
+ || memcmp(sizestart,
+ hashes[ofs].hashes[cs_length].start,
+ sizelen) != 0) {
+ fprintf(stderr,
+"Error: Contradicting file size information for '%s' ('%.*s' vs '%.*s') in .changes file\n",
+ f->basename,
+ (int)sizelen, sizestart,
+ (int)hashes[ofs].hashes[cs_length].len,
+ hashes[ofs].hashes[cs_length].start);
+ return RET_ERROR;
+ }
+ }
+ }
+ ofs = 0;
+ for (f = c->files ; f != NULL ; f = f->next, ofs++) {
+ r = checksums_initialize(&f->checksumsfromchanges,
+ hashes[ofs].hashes);
+ if (RET_WAS_ERROR(r))
+ return r;
+ }
+ assert (count == ofs);
+ free(hashes);
+
+ return RET_OK;
+}
+
+static retvalue read_dscfile(const char *fullfilename, struct dscfile **dsc) {
+ struct dscfile *n;
+ struct strlist filelines[cs_hashCOUNT];
+ enum checksumtype cs;
+ retvalue r;
+
+ n = zNEW(struct dscfile);
+ if (FAILEDTOALLOC(n))
+ return RET_ERROR_OOM;
+ r = signature_readsignedchunk(fullfilename, fullfilename,
+ &n->controlchunk, NULL, NULL);
+ assert (r != RET_NOTHING);
+ // TODO: can this be ignored sometimes?
+ if (RET_WAS_ERROR(r)) {
+ free(n);
+ return r;
+ }
+ r = chunk_getname(n->controlchunk, "Source", &n->name, false);
+ if (RET_WAS_ERROR(r)) {
+ dscfile_free(n);
+ return r;
+ }
+ r = chunk_getvalue(n->controlchunk, "Maintainer", &n->maintainer);
+ if (RET_WAS_ERROR(r)) {
+ dscfile_free(n);
+ return r;
+ }
+ r = chunk_getvalue(n->controlchunk, "Version", &n->version);
+ if (RET_WAS_ERROR(r)) {
+ dscfile_free(n);
+ return r;
+ }
+
+ /* usually not here, but hidden in the contents */
+ r = chunk_getvalue(n->controlchunk, "Section", &n->section);
+ if (RET_WAS_ERROR(r)) {
+ dscfile_free(n);
+ return r;
+ }
+ /* dito */
+ r = chunk_getvalue(n->controlchunk, "Priority", &n->priority);
+ if (RET_WAS_ERROR(r)) {
+ dscfile_free(n);
+ return r;
+ }
+
+ for (cs = cs_md5sum ; cs < cs_hashCOUNT ; cs++) {
+ assert (source_checksum_names[cs] != NULL);
+ r = chunk_getextralinelist(n->controlchunk,
+ source_checksum_names[cs], &filelines[cs]);
+ if (r == RET_NOTHING) {
+ if (cs == cs_md5sum) {
+ fprintf(stderr,
+"Error: Missing 'Files' entry in '%s'!\n", fullfilename);
+ r = RET_ERROR;
+ }
+ strlist_init(&filelines[cs]);
+ }
+ if (RET_WAS_ERROR(r)) {
+ while (cs-- > cs_md5sum) {
+ strlist_done(&filelines[cs]);
+ }
+ dscfile_free(n);
+ return r;
+ }
+ }
+ r = checksumsarray_parse(&n->expected, filelines, fullfilename);
+ for (cs = cs_md5sum ; cs < cs_hashCOUNT ; cs++) {
+ strlist_done(&filelines[cs]);
+ }
+ if (RET_WAS_ERROR(r)) {
+ dscfile_free(n);
+ return r;
+ }
+ if (n->expected.names.count > 0) {
+ n->uplink = nzNEW(n->expected.names.count, struct fileentry *);
+ if (FAILEDTOALLOC(n->uplink)) {
+ dscfile_free(n);
+ return RET_ERROR_OOM;
+ }
+ }
+ *dsc = n;
+ return RET_OK;
+}
+
+static retvalue parse_dsc(struct fileentry *dscfile, struct changes *changes) {
+ struct dscfile *n;
+ retvalue r;
+ int i;
+
+ if (dscfile->fullfilename == NULL)
+ return RET_NOTHING;
+ r = read_dscfile(dscfile->fullfilename, &n);
+ assert (r != RET_NOTHING);
+ if (RET_WAS_ERROR(r))
+ return r;
+ for (i = 0 ; i < n->expected.names.count ; i++) {
+ const char *basefilename = n->expected.names.values[i];
+ n->uplink[i] = add_fileentry(changes,
+ basefilename, strlen(basefilename),
+ true, NULL);
+ if (FAILEDTOALLOC(n->uplink[i])) {
+ dscfile_free(n);
+ return RET_ERROR_OOM;
+ }
+ }
+ dscfile->dsc = n;
+ return RET_OK;
+}
+
+#define DSC_WRITE_FILES 1
+#define DSC_WRITE_ALL 0xFFFF
+#define flagset(a) (flags & a) != 0
+
+static retvalue write_dsc_file(struct fileentry *dscfile, unsigned int flags) {
+ struct dscfile *dsc = dscfile->dsc;
+ int i;
+ struct chunkeditfield *cef;
+ retvalue r;
+ char *control; size_t controllen;
+ struct checksums *checksums;
+ char *destfilename;
+ enum checksumtype cs;
+
+ if (flagset(DSC_WRITE_FILES)) {
+ cef = NULL;
+ for (cs = cs_hashCOUNT ; (cs--) > cs_md5sum ; ) {
+ cef = cef_newfield(source_checksum_names[cs],
+ CEF_ADD, CEF_LATE,
+ dsc->expected.names.count, cef);
+ if (FAILEDTOALLOC(cef))
+ return RET_ERROR_OOM;
+ for (i = 0 ; i < dsc->expected.names.count ; i++) {
+ const char *basefilename =
+ dsc->expected.names.values[i];
+ const char *hash, *size;
+ size_t hashlen, sizelen;
+
+ if (!checksums_gethashpart(dsc->expected.checksums[i],
+ cs, &hash, &hashlen,
+ &size, &sizelen)) {
+ assert (cs != cs_md5sum);
+ cef = cef_pop(cef);
+ break;
+ }
+ cef_setline2(cef, i, hash, hashlen,
+ size, sizelen,
+ 1, basefilename, NULL);
+ }
+ }
+ } else
+ cef = NULL;
+
+ r = chunk_edit(dsc->controlchunk, &control, &controllen, cef);
+ cef_free(cef);
+ if (RET_WAS_ERROR(r))
+ return r;
+ assert (RET_IS_OK(r));
+
+ // TODO: try to add the signatures to it again...
+
+ // TODO: add options to place changed files in different directory...
+ if (dscfile->fullfilename != NULL)
+ destfilename = strdup(dscfile->fullfilename);
+ else
+ destfilename = strdup(dscfile->basename);
+ if (FAILEDTOALLOC(destfilename)) {
+ free(control);
+ return RET_ERROR_OOM;
+ }
+
+ r = checksums_replace(destfilename, control, controllen, &checksums);
+ if (RET_WAS_ERROR(r)) {
+ free(destfilename);
+ free(control);
+ return r;
+ }
+ assert (RET_IS_OK(r));
+
+ free(dscfile->fullfilename);
+ dscfile->fullfilename = destfilename;
+ checksums_free(dscfile->realchecksums);
+ dscfile->realchecksums = checksums;
+ free(dsc->controlchunk);
+ dsc->controlchunk = control;
+ return RET_OK;
+}
+
+static retvalue read_binaryfile(const char *fullfilename, struct binaryfile **result) {
+ retvalue r;
+ struct binaryfile *n;
+
+ n = zNEW(struct binaryfile);
+ if (FAILEDTOALLOC(n))
+ return RET_ERROR_OOM;
+
+ r = extractcontrol(&n->controlchunk, fullfilename);
+ if (!RET_IS_OK(r)) {
+ free(n);
+ if (r == RET_ERROR_OOM)
+ return r;
+ else
+ return RET_NOTHING;
+ }
+
+ r = chunk_getname(n->controlchunk, "Package", &n->name, false);
+ if (RET_WAS_ERROR(r)) {
+ binaryfile_free(n);
+ return r;
+ }
+ r = chunk_getvalue(n->controlchunk, "Version", &n->version);
+ if (RET_WAS_ERROR(r)) {
+ binaryfile_free(n);
+ return r;
+ }
+ r = chunk_getnameandversion(n->controlchunk, "Source",
+ &n->sourcename, &n->sourceversion);
+ if (RET_WAS_ERROR(r)) {
+ binaryfile_free(n);
+ return r;
+ }
+ r = chunk_getvalue(n->controlchunk, "Maintainer", &n->maintainer);
+ if (RET_WAS_ERROR(r)) {
+ binaryfile_free(n);
+ return r;
+ }
+ r = chunk_getvalue(n->controlchunk, "Architecture", &n->architecture);
+ if (RET_WAS_ERROR(r)) {
+ binaryfile_free(n);
+ return r;
+ }
+ r = chunk_getvalue(n->controlchunk, "Section", &n->section);
+ if (RET_WAS_ERROR(r)) {
+ binaryfile_free(n);
+ return r;
+ }
+ r = chunk_getvalue(n->controlchunk, "Priority", &n->priority);
+ if (RET_WAS_ERROR(r)) {
+ binaryfile_free(n);
+ return r;
+ }
+ r = chunk_getvalue(n->controlchunk, "Description", &n->shortdescription);
+ if (RET_WAS_ERROR(r)) {
+ binaryfile_free(n);
+ return r;
+ }
+ *result = n;
+ return RET_OK;
+}
+
+static retvalue parse_deb(struct fileentry *debfile, struct changes *changes) {
+ retvalue r;
+ struct binaryfile *n;
+
+ if (debfile->fullfilename == NULL)
+ return RET_NOTHING;
+ r = read_binaryfile(debfile->fullfilename, &n);
+ if (!RET_IS_OK(r))
+ return r;
+ if (n->name != NULL) {
+ n->binary = get_binary(changes, n->name, strlen(n->name));
+ if (FAILEDTOALLOC(n->binary)) {
+ binaryfile_free(n);
+ return RET_ERROR_OOM;
+ }
+ n->next = n->binary->files;
+ n->binary->files = n;
+ }
+
+ debfile->deb = n;
+ return RET_OK;
+}
+
+static retvalue processfiles(const char *changesfilename, struct changes *changes,
+ const struct strlist *searchpath) {
+ char *dir;
+ struct fileentry *file;
+ retvalue r;
+
+ r = dirs_getdirectory(changesfilename, &dir);
+ if (RET_WAS_ERROR(r))
+ return r;
+
+ for (file = changes->files; file != NULL ; file = file->next) {
+ assert (file->fullfilename == NULL);
+
+ r = searchforfile(dir, file->basename, searchpath, NULL,
+ &file->fullfilename);
+
+ if (RET_IS_OK(r)) {
+ if (file->type == ft_DSC)
+ r = parse_dsc(file, changes);
+ else if (file->type == ft_DEB || file->type == ft_DDEB || file->type == ft_UDEB)
+ r = parse_deb(file, changes);
+ if (RET_WAS_ERROR(r)) {
+ free(dir);
+ return r;
+ }
+ }
+
+ if (r == RET_NOTHING) {
+ /* apply heuristics when not readable */
+ if (file->type == ft_DSC) {
+ } else if (file->type == ft_DEB || file->type == ft_DDEB || file->type == ft_UDEB) {
+ struct binary *b; size_t len;
+
+ len = 0;
+ while (file->basename[len] != '_' &&
+ file->basename[len] != '\0')
+ len++;
+ b = get_binary(changes, file->basename, len);
+ if (FAILEDTOALLOC(b)) {
+ free(dir);
+ return RET_ERROR_OOM;
+ }
+ b->uncheckable = true;
+ }
+ }
+ }
+ free(dir);
+ return RET_OK;
+}
+
+static retvalue parse_changes(const char *changesfile, const char *chunk, struct changes **changes, const struct strlist *searchpath) {
+ retvalue r;
+ struct strlist tmp;
+ struct strlist filelines[cs_hashCOUNT];
+ enum checksumtype cs;
+#define R if (RET_WAS_ERROR(r)) { changes_free(n); return r; }
+
+ struct changes *n = zNEW(struct changes);
+ if (FAILEDTOALLOC(n))
+ return RET_ERROR_OOM;
+ n->filename = strdup(changesfile);
+ if (FAILEDTOALLOC(n->filename)) {
+ changes_free(n);
+ return RET_ERROR_OOM;
+ }
+ r = dirs_getdirectory(changesfile, &n->basedir);
+ R;
+ // TODO: do getname here? trim spaces?
+ r = chunk_getvalue(chunk, "Source", &n->name);
+ R;
+ if (r == RET_NOTHING) {
+ fprintf(stderr, "Missing 'Source:' field in %s!\n",
+ changesfile);
+ n->name = NULL;
+ }
+ r = chunk_getvalue(chunk, "Version", &n->version);
+ R;
+ if (r == RET_NOTHING) {
+ fprintf(stderr, "Missing 'Version:' field in %s!\n",
+ changesfile);
+ n->version = NULL;
+ }
+ r = chunk_getwordlist(chunk, "Architecture", &n->architectures);
+ R;
+ if (r == RET_NOTHING)
+ strlist_init(&n->architectures);
+ r = chunk_getwordlist(chunk, "Distribution", &n->distributions);
+ R;
+ if (r == RET_NOTHING)
+ strlist_init(&n->distributions);
+ r = chunk_getvalue(chunk, "Maintainer", &n->maintainer);
+ R;
+ if (r == RET_NOTHING) {
+ fprintf(stderr, "Missing 'Maintainer:' field in %s!\n",
+ changesfile);
+ n->maintainer = NULL;
+ }
+ r = chunk_getuniqwordlist(chunk, "Binary", &tmp);
+ R;
+ if (r == RET_NOTHING) {
+ n->binaries = NULL;
+ } else {
+ int i;
+
+ assert (RET_IS_OK(r));
+ n->binaries = nzNEW(tmp.count, struct binary);
+ if (FAILEDTOALLOC(n->binaries)) {
+ changes_free(n);
+ return RET_ERROR_OOM;
+ }
+ for (i = 0 ; i < tmp.count ; i++) {
+ n->binaries[i].name = tmp.values[i];
+ }
+ n->binarycount = tmp.count;
+ free(tmp.values);
+ }
+ r = chunk_getextralinelist(chunk, "Description", &tmp);
+ R;
+ if (RET_IS_OK(r)) {
+ r = parse_changes_description(n, &tmp);
+ strlist_done(&tmp);
+ if (RET_WAS_ERROR(r)) {
+ changes_free(n);
+ return r;
+ }
+ }
+ for (cs = cs_md5sum ; cs < cs_hashCOUNT ; cs++) {
+ assert (changes_checksum_names[cs] != NULL);
+ r = chunk_getextralinelist(chunk,
+ changes_checksum_names[cs], &filelines[cs]);
+ if (r == RET_NOTHING) {
+ if (cs == cs_md5sum)
+ break;
+ strlist_init(&filelines[cs]);
+ }
+ if (RET_WAS_ERROR(r)) {
+ while (cs-- > cs_md5sum) {
+ strlist_done(&filelines[cs]);
+ }
+ changes_free(n);
+ return r;
+ }
+ }
+ if (cs == cs_hashCOUNT) {
+ r = parse_changes_files(n, filelines);
+ for (cs = cs_md5sum ; cs < cs_hashCOUNT ; cs++) {
+ strlist_done(&filelines[cs]);
+ }
+ if (RET_WAS_ERROR(r)) {
+ changes_free(n);
+ return r;
+ }
+ }
+ r = processfiles(changesfile, n, searchpath);
+ R;
+ *changes = n;
+ return RET_OK;
+}
+
+#define CHANGES_WRITE_FILES 0x01
+#define CHANGES_WRITE_BINARIES 0x02
+#define CHANGES_WRITE_SOURCE 0x04
+#define CHANGES_WRITE_VERSION 0x08
+#define CHANGES_WRITE_ARCHITECTURES 0x10
+#define CHANGES_WRITE_MAINTAINER 0x20
+#define CHANGES_WRITE_DISTRIBUTIONS 0x40
+#define CHANGES_WRITE_ALL 0xFFFF
+
+static retvalue write_changes_file(const char *changesfilename, struct changes *c, unsigned int flags, bool fakefields) {
+ struct chunkeditfield *cef;
+ char datebuffer[100];
+ retvalue r;
+ char *control; size_t controllen;
+ unsigned int filecount = 0;
+ struct fileentry *f;
+ struct tm *tm; time_t t;
+ unsigned int i;
+ struct strlist binaries;
+ enum checksumtype cs;
+
+ strlist_init(&binaries);
+
+ for (f = c->files; f != NULL ; f = f->next) {
+ if (f->checksumsfromchanges != NULL)
+ filecount++;
+ }
+
+ if (flagset(CHANGES_WRITE_FILES)) {
+ cef = NULL;
+ for (cs = cs_md5sum ; cs < cs_hashCOUNT ; cs++) {
+ cef = cef_newfield(changes_checksum_names[cs],
+ CEF_ADD, CEF_LATE, filecount, cef);
+ if (FAILEDTOALLOC(cef))
+ return RET_ERROR_OOM;
+ i = 0;
+ for (f = c->files; f != NULL ; f = f->next) {
+ const char *hash, *size;
+ size_t hashlen, sizelen;
+
+ if (f->checksumsfromchanges == NULL)
+ continue;
+ if (!checksums_gethashpart(f->checksumsfromchanges,
+ cs, &hash, &hashlen,
+ &size, &sizelen)) {
+ assert (cs != cs_md5sum);
+ cef = cef_pop(cef);
+ break;
+ }
+ if (cs == cs_md5sum)
+ cef_setline2(cef, i,
+ hash, hashlen, size, sizelen,
+ 3,
+ f->section?f->section:"-",
+ f->priority?f->priority:"-",
+ f->basename, NULL);
+ else
+ /* strange way, but as dpkg-genchanges
+ * does it this way... */
+ cef_setline2(cef, i,
+ hash, hashlen, size, sizelen,
+ 1,
+ f->basename, NULL);
+ i++;
+ }
+ assert (f != NULL || i == filecount);
+ }
+ } else {
+ cef = cef_newfield("Files", CEF_KEEP, CEF_LATE, 0, NULL);
+ if (FAILEDTOALLOC(cef))
+ return RET_ERROR_OOM;
+ }
+ if (fakefields) {
+ cef = cef_newfield("Changes", CEF_ADDMISSED, CEF_LATE, 0, cef);
+ if (FAILEDTOALLOC(cef))
+ return RET_ERROR_OOM;
+ cef_setdata(cef,
+"\n Changes information missing, as not an original .changes file");
+ } else {
+ cef = cef_newfield("Changes", CEF_KEEP, CEF_LATE, 0, cef);
+ if (FAILEDTOALLOC(cef))
+ return RET_ERROR_OOM;
+ }
+ cef = cef_newfield("Closes", CEF_KEEP, CEF_LATE, 0, cef);
+ if (FAILEDTOALLOC(cef))
+ return RET_ERROR_OOM;
+ if (flagset(CHANGES_WRITE_BINARIES)) {
+ unsigned int count = 0;
+ for (i = 0 ; i < c->binarycount ; i++) {
+ const struct binary *b = c->binaries + i;
+ if (b->description != NULL)
+ count++;
+ }
+ cef = cef_newfield("Description", CEF_ADD, CEF_LATE,
+ count, cef);
+ if (FAILEDTOALLOC(cef))
+ return RET_ERROR_OOM;
+ count = 0;
+ for (i = 0 ; i < c->binarycount ; i++) {
+ const struct binary *b = c->binaries + i;
+ if (b->description == NULL)
+ continue;
+ cef_setline(cef, count++, 3,
+ b->name,
+ "-",
+ b->description,
+ NULL);
+ }
+
+ }
+ // Changed-by: line
+ if (flagset(CHANGES_WRITE_MAINTAINER) && c->maintainer != NULL) {
+ cef = cef_newfield("Maintainer", CEF_ADD, CEF_EARLY, 0, cef);
+ if (FAILEDTOALLOC(cef))
+ return RET_ERROR_OOM;
+ cef_setdata(cef, c->maintainer);
+ } else {
+ cef = cef_newfield("Maintainer", CEF_KEEP, CEF_EARLY, 0, cef);
+ if (FAILEDTOALLOC(cef))
+ return RET_ERROR_OOM;
+ }
+ if (fakefields) {
+ cef = cef_newfield("Urgency", CEF_ADDMISSED, CEF_EARLY, 0, cef);
+ if (FAILEDTOALLOC(cef)) {
+ return RET_ERROR_OOM;
+ }
+ cef_setdata(cef, "low");
+ } else {
+ cef = cef_newfield("Urgency", CEF_KEEP, CEF_EARLY, 0, cef);
+ if (FAILEDTOALLOC(cef))
+ return RET_ERROR_OOM;
+ }
+ cef = cef_newfield("Distribution", CEF_KEEP, CEF_EARLY, 0, cef);
+ if (FAILEDTOALLOC(cef))
+ return RET_ERROR_OOM;
+ if (c->distributions.count > 0) {
+ if (flagset(CHANGES_WRITE_DISTRIBUTIONS))
+ cef = cef_newfield("Distribution", CEF_ADD,
+ CEF_EARLY, 0, cef);
+ else
+ cef = cef_newfield("Distribution", CEF_ADDMISSED,
+ CEF_EARLY, 0, cef);
+ if (FAILEDTOALLOC(cef))
+ return RET_ERROR_OOM;
+ cef_setwordlist(cef, &c->distributions);
+ } else if (flagset(CHANGES_WRITE_DISTRIBUTIONS)) {
+ cef = cef_newfield("Distribution", CEF_DELETE,
+ CEF_EARLY, 0, cef);
+ if (FAILEDTOALLOC(cef))
+ return RET_ERROR_OOM;
+ }
+ if (c->version != NULL) {
+ if (flagset(CHANGES_WRITE_VERSION))
+ cef = cef_newfield("Version", CEF_ADD,
+ CEF_EARLY, 0, cef);
+ else
+ cef = cef_newfield("Version", CEF_ADDMISSED,
+ CEF_EARLY, 0, cef);
+ if (FAILEDTOALLOC(cef))
+ return RET_ERROR_OOM;
+ cef_setdata(cef, c->version);
+ } else if (flagset(CHANGES_WRITE_VERSION)) {
+ cef = cef_newfield("Version", CEF_DELETE,
+ CEF_EARLY, 0, cef);
+ if (FAILEDTOALLOC(cef))
+ return RET_ERROR_OOM;
+ }
+ if (flagset(CHANGES_WRITE_ARCHITECTURES)) {
+ cef = cef_newfield("Architecture", CEF_ADD, CEF_EARLY, 0, cef);
+ if (FAILEDTOALLOC(cef))
+ return RET_ERROR_OOM;
+ cef_setwordlist(cef, &c->architectures);
+ } else {
+ cef = cef_newfield("Architecture", CEF_KEEP, CEF_EARLY, 0, cef);
+ if (FAILEDTOALLOC(cef))
+ return RET_ERROR_OOM;
+ }
+ if (flagset(CHANGES_WRITE_BINARIES)) {
+ r = strlist_init_n(c->binarycount, &binaries);
+ if (RET_WAS_ERROR(r)) {
+ cef_free(cef);
+ return r;
+ }
+ assert (RET_IS_OK(r));
+ for (i = 0 ; i < c->binarycount ; i++) {
+ const struct binary *b = c->binaries + i;
+ if (!b->missedinheader) {
+ r = strlist_add_dup(&binaries, b->name);
+ if (RET_WAS_ERROR(r)) {
+ strlist_done(&binaries);
+ cef_free(cef);
+ return r;
+ }
+ }
+ }
+ cef = cef_newfield("Binary", CEF_ADD, CEF_EARLY, 0, cef);
+ if (FAILEDTOALLOC(cef)) {
+ strlist_done(&binaries);
+ return RET_ERROR_OOM;
+ }
+ cef_setwordlist(cef, &binaries);
+ } else {
+ cef = cef_newfield("Binary", CEF_KEEP, CEF_EARLY, 0, cef);
+ if (FAILEDTOALLOC(cef))
+ return RET_ERROR_OOM;
+ }
+ if (c->name != NULL) {
+ if (flagset(CHANGES_WRITE_SOURCE))
+ cef = cef_newfield("Source", CEF_ADD,
+ CEF_EARLY, 0, cef);
+ else
+ cef = cef_newfield("Source", CEF_ADDMISSED,
+ CEF_EARLY, 0, cef);
+ if (FAILEDTOALLOC(cef)) {
+ strlist_done(&binaries);
+ return RET_ERROR_OOM;
+ }
+ cef_setdata(cef, c->name);
+ } else if (flagset(CHANGES_WRITE_SOURCE)) {
+ cef = cef_newfield("Source", CEF_DELETE,
+ CEF_EARLY, 0, cef);
+ if (FAILEDTOALLOC(cef)) {
+ strlist_done(&binaries);
+ return RET_ERROR_OOM;
+ }
+ }
+ // TODO: if localized make sure this uses C locale....
+ t = time(NULL);
+ if ((tm = localtime(&t)) != NULL &&
+ strftime(datebuffer, sizeof(datebuffer)-1,
+ "%a, %e %b %Y %H:%M:%S %Z", tm) > 0) {
+ cef = cef_newfield("Date", CEF_ADD, CEF_EARLY, 0, cef);
+ if (FAILEDTOALLOC(cef)) {
+ strlist_done(&binaries);
+ return RET_ERROR_OOM;
+ }
+ cef_setdata(cef, datebuffer);
+ } else {
+ cef = cef_newfield("Date", CEF_DELETE,
+ CEF_EARLY, 0, cef);
+ if (FAILEDTOALLOC(cef)) {
+ strlist_done(&binaries);
+ return RET_ERROR_OOM;
+ }
+ }
+ cef = cef_newfield("Format", CEF_ADDMISSED, CEF_EARLY, 0, cef);
+ if (FAILEDTOALLOC(cef)) {
+ strlist_done(&binaries);
+ return RET_ERROR_OOM;
+ }
+ cef_setdata(cef, "1.7");
+
+ r = chunk_edit((c->control==NULL)?"":c->control, &control, &controllen,
+ cef);
+ strlist_done(&binaries);
+ cef_free(cef);
+ if (RET_WAS_ERROR(r))
+ return r;
+ assert (RET_IS_OK(r));
+
+ // TODO: try to add the signatures to it again...
+
+ // TODO: add options to place changed files in different directory...
+
+ r = checksums_replace(changesfilename, control, controllen, NULL);
+ if (RET_WAS_ERROR(r)) {
+ free(control);
+ return r;
+ }
+ assert (RET_IS_OK(r));
+
+ free(c->control);
+ c->control = control;
+ return RET_OK;
+}
+
+static retvalue getchecksums(struct changes *changes) {
+ struct fileentry *file;
+ retvalue r;
+
+ for (file = changes->files; file != NULL ; file = file->next) {
+
+ if (file->fullfilename == NULL)
+ continue;
+ assert (file->realchecksums == NULL);
+
+ r = checksums_read(file->fullfilename, &file->realchecksums);
+ if (r == RET_ERROR_OOM)
+ return r;
+ else if (!RET_IS_OK(r)) {
+ // assume everything else is not fatal and means
+ // a file not readable...
+ file->realchecksums = NULL;
+ }
+ }
+ return RET_OK;
+}
+
+static bool may_be_type(const char *name, enum filetype ft) {
+ enum compression c;
+ size_t len = strlen(name);
+
+ c = compression_by_suffix(name, &len);
+ if (c != c_none && !typesuffix[ft].allowcompressed)
+ return false;
+ return strncmp(name + (len - typesuffix[ft].len),
+ typesuffix[ft].suffix,
+ typesuffix[ft].len) == 0;
+}
+
+static void verify_sourcefile_checksums(struct dscfile *dsc, int i, const char *dscfile) {
+ const struct fileentry * const file = dsc->uplink[i];
+ const struct checksums * const expectedchecksums
+ = dsc->expected.checksums[i];
+ const char * const basefilename = dsc->expected.names.values[i];
+ assert (file != NULL);
+
+ if (file->checksumsfromchanges == NULL) {
+ if (may_be_type(basefilename, ft_ORIG_TAR)) {
+ fprintf(stderr,
+"Not checking checksums of '%s', as not included in .changes file.\n",
+ basefilename);
+ return;
+ } else if (file->realchecksums == NULL) {
+ fprintf(stderr,
+"ERROR: File '%s' mentioned in '%s' was not found and is not mentioned in the .changes!\n",
+ basefilename, dscfile);
+ return;
+ }
+ }
+ if (file->realchecksums == NULL)
+ /* there will be an message later about that */
+ return;
+ if (checksums_check(expectedchecksums, file->realchecksums, NULL))
+ return;
+
+ if (file->checksumsfromchanges != NULL &&
+ checksums_check(expectedchecksums, file->checksumsfromchanges, NULL))
+ fprintf(stderr,
+"ERROR: checksums of '%s' differ from the ones listed in both '%s' and the .changes file!\n",
+ basefilename, dscfile);
+ else {
+ fprintf(stderr,
+"ERROR: checksums of '%s' differ from those listed in '%s':\n!\n",
+ basefilename, dscfile);
+ checksums_printdifferences(stderr,
+ expectedchecksums, file->realchecksums);
+ }
+}
+
+static void verify_binary_name(const char *basefilename, const char *name, const char *version, const char *architecture, enum filetype type, enum compression c) {
+ size_t nlen, vlen, alen, slen;
+ const char *versionwithoutepoch;
+
+ if (name == NULL)
+ return;
+ nlen = strlen(name);
+ if (strncmp(basefilename, name, nlen) != 0 || basefilename[nlen] != '_') {
+ fprintf(stderr,
+"ERROR: '%s' does not start with '%s_' as expected!\n",
+ basefilename, name);
+ return;
+ }
+ if (version == NULL)
+ return;
+ versionwithoutepoch = strchr(version, ':');
+ if (versionwithoutepoch == NULL)
+ versionwithoutepoch = version;
+ else
+ versionwithoutepoch++;
+ vlen = strlen(versionwithoutepoch);
+ if (strncmp(basefilename+nlen+1, versionwithoutepoch, vlen) != 0
+ || basefilename[nlen+1+vlen] != '_') {
+ fprintf(stderr,
+"ERROR: '%s' does not start with '%s_%s_' as expected!\n",
+ basefilename, name, version);
+ return;
+ }
+ if (architecture == NULL)
+ return;
+ alen = strlen(architecture);
+ slen = typesuffix[type].len;
+ if (strncmp(basefilename+nlen+1+vlen+1, architecture, alen) != 0
+ || strncmp(basefilename+nlen+1+vlen+1+alen,
+ typesuffix[type].suffix, slen) != 0
+ || strcmp(basefilename+nlen+1+vlen+1+alen+slen,
+ uncompression_suffix[c]) != 0)
+ fprintf(stderr,
+"ERROR: '%s' is not called '%s_%s_%s%s%s' as expected!\n",
+ basefilename, name, versionwithoutepoch,
+ architecture, typesuffix[type].suffix,
+ uncompression_suffix[c]);
+}
+
+static retvalue verify(const char *changesfilename, struct changes *changes) {
+ retvalue r;
+ struct fileentry *file;
+ size_t k;
+
+ printf("Checking Source packages...\n");
+ for (file = changes->files; file != NULL ; file = file->next) {
+ const char *name, *version, *p;
+ size_t namelen, versionlen, l;
+ bool has_tar, has_diff, has_orig, has_format_tar;
+ int i;
+
+ if (file->type != ft_DSC)
+ continue;
+ if (!strlist_in(&changes->architectures, "source")) {
+ fprintf(stderr,
+"ERROR: '%s' contains a .dsc, but does not list Architecture 'source'!\n",
+ changesfilename);
+ }
+ if (file->fullfilename == NULL) {
+ fprintf(stderr,
+"ERROR: Could not find '%s'!\n", file->basename);
+ continue;
+ }
+ if (file->dsc == NULL) {
+ fprintf(stderr,
+"WARNING: Could not read '%s', thus it cannot be checked!\n",
+ file->fullfilename);
+ continue;
+ }
+ if (file->dsc->name == NULL)
+ fprintf(stderr,
+"ERROR: '%s' does not contain a 'Source:' header!\n", file->fullfilename);
+ else if (changes->name != NULL &&
+ strcmp(changes->name, file->dsc->name) != 0)
+ fprintf(stderr,
+"ERROR: '%s' lists Source '%s' while .changes lists '%s'!\n",
+ file->fullfilename,
+ file->dsc->name, changes->name);
+ if (file->dsc->version == NULL)
+ fprintf(stderr,
+"ERROR: '%s' does not contain a 'Version:' header!\n", file->fullfilename);
+ else if (changes->version != NULL &&
+ strcmp(changes->version,
+ file->dsc->version) != 0)
+ fprintf(stderr,
+"ERROR: '%s' lists Version '%s' while .changes lists '%s'!\n",
+ file->fullfilename,
+ file->dsc->version, changes->version);
+ if (file->dsc->maintainer == NULL)
+ fprintf(stderr,
+"ERROR: No maintainer specified in '%s'!\n", file->fullfilename);
+ else if (changes->maintainer != NULL &&
+ strcmp(changes->maintainer,
+ file->dsc->maintainer) != 0)
+ fprintf(stderr,
+"Warning: '%s' lists Maintainer '%s' while .changes lists '%s'!\n",
+ file->fullfilename,
+ file->dsc->maintainer, changes->maintainer);
+ if (file->dsc->section != NULL && file->section != NULL &&
+ strcmp(file->section, file->dsc->section) != 0)
+ fprintf(stderr,
+"Warning: '%s' has Section '%s' while .changes says it is '%s'!\n",
+ file->fullfilename,
+ file->dsc->section, file->section);
+ if (file->dsc->priority != NULL && file->priority != NULL
+ && strcmp(file->priority,
+ file->dsc->priority) != 0)
+ fprintf(stderr,
+"Warning: '%s' has Priority '%s' while .changes says it is '%s'!\n",
+ file->fullfilename,
+ file->dsc->priority, file->priority);
+ // Todo: check types of files it contains...
+ // check names are sensible
+ p = file->basename;
+ while (*p != '\0' && *p != '_')
+ p++;
+ if (*p == '_') {
+ l = strlen(p+1);
+ assert (l >= 4); /* It ends in ".dsc" to come here */
+ } else
+ l = 0;
+
+ if (file->dsc->name != NULL) {
+ name = file->dsc->name;
+ namelen = strlen(name);
+ } else {
+ // TODO: more believe file name or changes name?
+ if (changes->name != NULL) {
+ name = changes->name;
+ namelen = strlen(name);
+ } else {
+ if (*p != '_') {
+ name = NULL;
+ namelen = 0;
+ fprintf(stderr,
+"Warning: '%s' does not contain a '_' separating name and version!\n",
+ file->basename);
+ }else {
+ name = file->basename;
+ namelen = p-name;
+ }
+ }
+ }
+ if (file->dsc->version != NULL) {
+ version = file->dsc->version;
+ versionlen = strlen(version);
+ } else {
+ // TODO: dito
+ if (changes->version != NULL) {
+ version = changes->version;
+ versionlen = strlen(version);
+ } else {
+ if (*p != '_') {
+ version = NULL;
+ SETBUTNOTUSED( versionlen = 0; )
+ if (name != NULL)
+ fprintf(stderr,
+"ERROR: '%s' does not contain a '_' separating name and version!\n",
+ file->basename);
+ } else {
+ version = p+1;
+ versionlen = l-4;
+ }
+ }
+ }
+ if (version != NULL) {
+ const char *colon = strchr(version, ':');
+ if (colon != NULL) {
+ colon++;
+ versionlen -= (colon-version);
+ version = colon;
+ }
+ }
+ if (name != NULL && version != NULL) {
+ if (*p != '_'
+ || (size_t)(p-file->basename) != namelen || l-4 != versionlen
+ || strncmp(p+1, version, versionlen) != 0
+ || strncmp(file->basename, name, namelen) != 0)
+ fprintf(stderr,
+"ERROR: '%s' is not called '%*s_%*s.dsc' as expected!\n",
+ file->basename,
+ (unsigned int)namelen, name,
+ (unsigned int)versionlen, version);
+ }
+ has_tar = false;
+ has_format_tar = false;
+ has_diff = false;
+ has_orig = false;
+ for (i = 0 ; i < file->dsc->expected.names.count ; i++) {
+ const char *basefilename
+ = file->dsc->expected.names.values[i];
+ const struct fileentry *sfile = file->dsc->uplink[i];
+ size_t expectedversionlen, expectedformatlen;
+ const char *expectedformat;
+ bool istar = false, versionok;
+
+ switch (sfile->type) {
+ case ft_UNKNOWN:
+ fprintf(stderr,
+"ERROR: '%s' lists a file '%s' with unrecognized suffix!\n",
+ file->fullfilename,
+ basefilename);
+ break;
+ case ft_TAR:
+ istar = true;
+ has_tar = true;
+ break;
+ case ft_ORIG_TAR:
+ if (has_orig)
+ fprintf(stderr,
+"ERROR: '%s' lists multiple .orig..tar files!\n",
+ file->fullfilename);
+ has_orig = true;
+ break;
+ case ft_DIFF:
+ if (has_diff)
+ fprintf(stderr,
+"ERROR: '%s' lists multiple .diff files!\n",
+ file->fullfilename);
+ has_diff = true;
+ break;
+ default:
+ assert (sfile->type == ft_UNKNOWN);
+ }
+
+ if (name == NULL) // TODO: try extracting it from this
+ continue;
+ if (strncmp(sfile->basename, name, namelen) != 0
+ || sfile->basename[namelen] != '_') {
+ fprintf(stderr,
+"ERROR: '%s' does not begin with '%*s_' as expected!\n",
+ sfile->basename,
+ (unsigned int)namelen, name);
+ /* cannot check further */
+ continue;
+ }
+
+ if (version == NULL)
+ continue;
+ /* versionlen is now always initialized */
+
+ if (sfile->type == ft_ORIG_TAR) {
+ const char *q, *revision;
+ revision = NULL;
+ for (q = version; *q != '\0'; q++) {
+ if (*q == '-')
+ revision = q;
+ }
+ if (revision == NULL)
+ expectedversionlen = versionlen;
+ else
+ expectedversionlen = revision - version;
+ } else
+ expectedversionlen = versionlen;
+
+ versionok = strncmp(sfile->basename+namelen+1,
+ version, expectedversionlen) == 0;
+ if (istar) {
+ if (!versionok) {
+ fprintf(stderr,
+"ERROR: '%s' does not start with '%*s_%*s' as expected!\n",
+ sfile->basename,
+ (unsigned int)namelen, name,
+ (unsigned int)expectedversionlen,
+ version);
+ continue;
+ }
+ expectedformat = sfile->basename + namelen + 1 +
+ expectedversionlen;
+ if (strncmp(expectedformat, ".tar.", 5) == 0)
+ expectedformatlen = 0;
+ else {
+ const char *dot;
+
+ dot = strchr(expectedformat + 1, '.');
+ if (dot == NULL)
+ expectedformatlen = 0;
+ else {
+ expectedformatlen =
+ dot - expectedformat;
+ has_format_tar = true;
+ }
+ }
+ } else {
+ expectedformat = "";
+ expectedformatlen = 0;
+ }
+
+ if (sfile->type == ft_UNKNOWN)
+ continue;
+ if (versionok
+ && strncmp(sfile->basename+namelen+1
+ +expectedversionlen
+ +expectedformatlen,
+ typesuffix[sfile->type].suffix,
+ typesuffix[sfile->type].len) == 0
+ && strcmp(sfile->basename+namelen+1
+ +expectedversionlen
+ +expectedformatlen
+ +typesuffix[sfile->type].len,
+ uncompression_suffix[sfile->compression])
+ == 0)
+ continue;
+ fprintf(stderr,
+"ERROR: '%s' is not called '%.*s_%.*s%.*s%s%s' as expected!\n",
+ sfile->basename,
+ (unsigned int)namelen, name,
+ (unsigned int)expectedversionlen,
+ version,
+ (unsigned int)expectedformatlen,
+ expectedformat,
+ typesuffix[sfile->type].suffix,
+ uncompression_suffix[sfile->compression]);
+ }
+ if (!has_tar && !has_orig)
+ if (has_diff)
+ fprintf(stderr,
+"ERROR: '%s' lists only a .diff, but no .orig.tar!\n",
+ file->fullfilename);
+ else
+ fprintf(stderr,
+"ERROR: '%s' lists no source files!\n",
+ file->fullfilename);
+ else if (has_diff && !has_orig)
+ fprintf(stderr,
+"ERROR: '%s' lists a .diff, but the .tar is not called .orig.tar!\n",
+ file->fullfilename);
+ else if (!has_format_tar && !has_diff && has_orig)
+ fprintf(stderr,
+"ERROR: '%s' lists a .orig.tar, but no .diff!\n",
+ file->fullfilename);
+ }
+ printf("Checking Binary consistency...\n");
+ for (k = 0 ; k < changes->binarycount ; k++) {
+ struct binary *b = &changes->binaries[k];
+
+ if (b->files == NULL && !b->uncheckable) {
+ /* no files - not even conjectured -,
+ * headers must be wrong */
+
+ if (b->description != NULL && !b->missedinheader) {
+ fprintf(stderr,
+"ERROR: '%s' has binary '%s' in 'Binary:' and 'Description:' header, but no files for it found!\n",
+ changesfilename, b->name);
+ } else if (b->description != NULL) {
+ fprintf(stderr,
+"ERROR: '%s' has unexpected description of '%s'\n",
+ changesfilename, b->name);
+ } else {
+ assert (!b->missedinheader);
+ fprintf(stderr,
+"ERROR: '%s' has unexpected Binary: '%s'\n",
+ changesfilename, b->name);
+ }
+ }
+ if (b->files == NULL)
+ continue;
+ /* files are there, make sure they are listed and
+ * have a description*/
+
+ if (b->description == NULL) {
+ fprintf(stderr,
+"ERROR: '%s' has no description for '%s'\n",
+ changesfilename, b->name);
+ }
+ if (b->missedinheader) {
+ fprintf(stderr,
+"ERROR: '%s' does not list '%s' in its Binary header!\n",
+ changesfilename, b->name);
+ }
+ // TODO: check if the files have the names they should
+ // have an architectures as they are listed...
+ }
+ for (file = changes->files; file != NULL ; file = file->next) {
+ const struct binary *b;
+ const struct binaryfile *deb;
+
+ if (file->type != ft_DEB && file->type != ft_DDEB && file->type != ft_UDEB)
+ continue;
+ if (file->fullfilename == NULL) {
+ fprintf(stderr,
+"ERROR: Could not find '%s'!\n", file->basename);
+ continue;
+ }
+ if (file->deb == NULL) {
+ fprintf(stderr,
+"WARNING: Could not read '%s', thus it cannot be checked!\n", file->fullfilename);
+ continue;
+ }
+ deb = file->deb;
+ b = deb->binary;
+
+ if (deb->shortdescription == NULL)
+ fprintf(stderr,
+"Warning: '%s' contains no description!\n",
+ file->fullfilename);
+ else if (b->description != NULL &&
+ strcmp(b->description, deb->shortdescription) != 0)
+ fprintf(stderr,
+"Warning: '%s' says '%s' has description '%s' while '%s' has '%s'!\n",
+ changesfilename, b->name,
+ b->description,
+ file->fullfilename,
+ deb->shortdescription);
+ if (deb->name == NULL)
+ fprintf(stderr,
+"ERROR: '%s' does not contain a 'Package:' header!\n", file->fullfilename);
+ if (deb->sourcename != NULL) {
+ if (strcmp(changes->name, deb->sourcename) != 0)
+ fprintf(stderr,
+"ERROR: '%s' lists Source '%s' while .changes lists '%s'!\n",
+ file->fullfilename,
+ deb->sourcename, changes->name);
+ } else if (deb->name != NULL &&
+ strcmp(changes->name, deb->name) != 0) {
+ fprintf(stderr,
+"ERROR: '%s' lists Source '%s' while .changes lists '%s'!\n",
+ file->fullfilename,
+ deb->name, changes->name);
+ }
+ if (deb->version == NULL)
+ fprintf(stderr,
+"ERROR: '%s' does not contain a 'Version:' header!\n", file->fullfilename);
+ if (deb->sourceversion != NULL) {
+ if (strcmp(changes->version, deb->sourceversion) != 0)
+ fprintf(stderr,
+"ERROR: '%s' lists Source version '%s' while .changes lists '%s'!\n",
+ file->fullfilename,
+ deb->sourceversion, changes->version);
+ } else if (deb->version != NULL &&
+ strcmp(changes->version, deb->version) != 0) {
+ fprintf(stderr,
+"ERROR: '%s' lists Source version '%s' while .changes lists '%s'!\n",
+ file->fullfilename,
+ deb->version, changes->name);
+ }
+
+ if (deb->maintainer == NULL)
+ fprintf(stderr,
+"ERROR: No maintainer specified in '%s'!\n", file->fullfilename);
+ else if (changes->maintainer != NULL &&
+ strcmp(changes->maintainer,
+ deb->maintainer) != 0)
+ fprintf(stderr,
+"Warning: '%s' lists Maintainer '%s' while .changes lists '%s'!\n",
+ file->fullfilename,
+ deb->maintainer, changes->maintainer);
+ if (deb->section == NULL)
+ fprintf(stderr,
+"ERROR: No section specified in '%s'!\n", file->fullfilename);
+ else if (file->section != NULL &&
+ strcmp(file->section, deb->section) != 0)
+ fprintf(stderr,
+"Warning: '%s' has Section '%s' while .changes says it is '%s'!\n",
+ file->fullfilename,
+ deb->section, file->section);
+ if (deb->priority == NULL)
+ fprintf(stderr,
+"ERROR: No priority specified in '%s'!\n", file->fullfilename);
+ else if (file->priority != NULL &&
+ strcmp(file->priority, deb->priority) != 0)
+ fprintf(stderr,
+"Warning: '%s' has Priority '%s' while .changes says it is '%s'!\n",
+ file->fullfilename,
+ deb->priority, file->priority);
+ verify_binary_name(file->basename, deb->name, deb->version,
+ deb->architecture, file->type, file->compression);
+ if (deb->architecture != NULL
+ && !strlist_in(&changes->architectures,
+ deb->architecture)) {
+ fprintf(stderr,
+"ERROR: '%s' does not list Architecture: '%s' needed for '%s'!\n",
+ changesfilename, deb->architecture,
+ file->fullfilename);
+ }
+ // todo: check for md5sums file, verify it...
+ }
+
+ printf("Checking checksums...\n");
+ r = getchecksums(changes);
+ if (RET_WAS_ERROR(r))
+ return r;
+ for (file = changes->files; file != NULL ; file = file->next) {
+
+ if (file->checksumsfromchanges == NULL)
+ /* nothing to check here */
+ continue;
+
+ if (file->fullfilename == NULL) {
+ fprintf(stderr,
+"WARNING: Could not check checksums of '%s' as file not found!\n",
+ file->basename);
+ if (file->type == ft_DSC) {
+ fprintf(stderr,
+"WARNING: This file most likely contains additional checksums which could also not be checked because it was not found!\n");
+ }
+ continue;
+ }
+ if (file->realchecksums == NULL) {
+ fprintf(stderr,
+"WARNING: Could not check checksums of '%s'! File vanished while checking or not readable?\n",
+ file->basename);
+ } else if (!checksums_check(file->realchecksums,
+ file->checksumsfromchanges, NULL)) {
+ fprintf(stderr,
+"ERROR: checksums of '%s' differ from those listed in .changes:\n",
+ file->fullfilename);
+ checksums_printdifferences(stderr,
+ file->checksumsfromchanges,
+ file->realchecksums);
+ }
+
+ if (file->type == ft_DSC) {
+ int i;
+
+ if (file->dsc == NULL) {
+ fprintf(stderr,
+"WARNING: Could not read '%s', thus the content cannot be checked\n"
+" and may be faulty and other things depending on it may be incorrect!\n", file->basename);
+ continue;
+ }
+
+ for (i = 0 ; i < file->dsc->expected.names.count ; i++) {
+ verify_sourcefile_checksums(file->dsc, i,
+ file->fullfilename);
+ }
+ }
+ // TODO: check .deb files
+ }
+ return RET_OK;
+}
+
+static bool isarg(int argc, char **argv, const char *name) {
+ while (argc > 0) {
+ if (strcmp(*argv, name) == 0)
+ return true;
+ argc--;
+ argv++;
+ }
+ return false;
+}
+
+static bool improvedchecksum_supported(const struct changes *c, bool improvedfilehashes[cs_hashCOUNT]) {
+ enum checksumtype cs;
+ struct fileentry *file;
+
+ for (cs = cs_md5sum ; cs < cs_hashCOUNT ; cs++) {
+ if (!improvedfilehashes[cs])
+ continue;
+ for (file = c->files; file != NULL ; file = file->next) {
+ const char *dummy1, *dummy3;
+ size_t dummy2, dummy4;
+
+ if (file->checksumsfromchanges == NULL)
+ continue;
+
+ if (!checksums_gethashpart(file->checksumsfromchanges,
+ cs,
+ &dummy1, &dummy2,
+ &dummy3, &dummy4))
+ break;
+ }
+ if (file == NULL)
+ return true;
+ }
+ return false;
+}
+
+static bool anyset(bool *list, size_t count) {
+ while (count > 0)
+ if (list[--count])
+ return true;
+ return false;
+}
+
+static retvalue updatechecksums(const char *changesfilename, struct changes *c, int argc, char **argv) {
+ retvalue r;
+ struct fileentry *file;
+ bool improvedfilehashes[cs_hashCOUNT];
+
+ r = getchecksums(c);
+ if (RET_WAS_ERROR(r))
+ return r;
+ /* first update all .dsc files and perhaps recalculate their checksums*/
+ for (file = c->files; file != NULL ; file = file->next) {
+ int i;
+ bool improvedhash[cs_hashCOUNT];
+
+ if (file->type != ft_DSC)
+ continue;
+
+ if (file->dsc == NULL) {
+ fprintf(stderr,
+"WARNING: Could not read '%s', hoping the content and its checksums are correct!\n",
+ file->basename);
+ continue;
+ }
+ memset(improvedhash, 0, sizeof(improvedhash));
+
+ assert (file->fullfilename != NULL);
+ for (i = 0 ; i < file->dsc->expected.names.count ; i++) {
+ const char *basefilename = file->dsc->expected.names.values[i];
+ const struct fileentry *sfile = file->dsc->uplink[i];
+ struct checksums **expected_p = &file->dsc->expected.checksums[i];
+ const struct checksums * const expected = *expected_p;
+ const char *hashes1, *hashes2;
+ size_t dummy;
+ bool doit;
+ bool improves;
+
+ assert (expected != NULL);
+ assert (basefilename != NULL);
+
+ doit = isarg(argc, argv, basefilename);
+ if (argc > 0 && !doit)
+ continue;
+
+ assert (sfile != NULL);
+ if (sfile->checksumsfromchanges == NULL) {
+ if (!doit) {
+ fprintf(stderr,
+"Not checking/updating '%s' as not in .changes and not specified on command line.\n",
+ basefilename);
+ continue;
+ }
+ if (sfile->realchecksums == NULL) {
+ fprintf(stderr,
+"WARNING: Could not check checksums of '%s'!\n", basefilename);
+ continue;
+ }
+ } else {
+ if (sfile->realchecksums == NULL) {
+ fprintf(stderr,
+"WARNING: Could not check checksums of '%s'!\n",
+ basefilename);
+ continue;
+ }
+ }
+
+ if (checksums_check(expected, sfile->realchecksums,
+ &improves)) {
+ if (!improves) {
+ /* already correct */
+ continue;
+ }
+ /* future versions might be able to store them
+ * in the dsc */
+ r = checksums_combine(expected_p,
+ sfile->realchecksums,
+ improvedhash);
+ if (RET_WAS_ERROR(r))
+ return r;
+ continue;
+ }
+ r = checksums_getcombined(expected, &hashes1, &dummy);
+ if (!RET_IS_OK(r))
+ hashes1 = "<unknown>";
+ r = checksums_getcombined(sfile->realchecksums,
+ &hashes2, &dummy);
+ if (!RET_IS_OK(r))
+ hashes2 = "<unknown>";
+ fprintf(stderr,
+"Going to update '%s' in '%s'\nfrom '%s'\nto '%s'.\n",
+ basefilename, file->fullfilename,
+ hashes1, hashes2);
+ checksums_free(*expected_p);
+ *expected_p = checksums_dup(sfile->realchecksums);
+ if (FAILEDTOALLOC(*expected_p))
+ return RET_ERROR_OOM;
+ file->dsc->modified = true;
+ }
+ checksumsarray_resetunsupported(&file->dsc->expected,
+ improvedhash);
+ if (file->dsc->modified | anyset(improvedhash, cs_hashCOUNT)) {
+ r = write_dsc_file(file, DSC_WRITE_FILES);
+ if (RET_WAS_ERROR(r))
+ return r;
+ }
+ }
+ memset(improvedfilehashes, 0, sizeof(improvedfilehashes));
+ for (file = c->files; file != NULL ; file = file->next) {
+ bool improves;
+ const char *hashes1, *hashes2;
+ size_t dummy;
+
+ if (file->checksumsfromchanges == NULL)
+ /* nothing to check here */
+ continue;
+ if (file->realchecksums == NULL) {
+ fprintf(stderr,
+"WARNING: Could not check checksums of '%s'! Leaving it as it is.\n",
+ file->basename);
+ continue;
+ }
+ if (checksums_check(file->checksumsfromchanges,
+ file->realchecksums, &improves)) {
+ if (!improves)
+ continue;
+ /* future versions might store sha sums in .changes: */
+ r = checksums_combine(&file->checksumsfromchanges,
+ file->realchecksums, improvedfilehashes);
+ if (RET_WAS_ERROR(r))
+ return r;
+ continue;
+ }
+ r = checksums_getcombined(file->checksumsfromchanges,
+ &hashes1, &dummy);
+ if (!RET_IS_OK(r))
+ hashes1 = "<unknown>";
+ r = checksums_getcombined(file->realchecksums,
+ &hashes2, &dummy);
+ if (!RET_IS_OK(r))
+ hashes2 = "<unknown>";
+ fprintf(stderr,
+"Going to update '%s' in '%s'\nfrom '%s'\nto '%s'.\n",
+ file->basename, changesfilename,
+ hashes1, hashes2);
+ checksums_free(file->checksumsfromchanges);
+ file->checksumsfromchanges = checksums_dup(file->realchecksums);
+ if (FAILEDTOALLOC(file->checksumsfromchanges))
+ return RET_ERROR_OOM;
+ c->modified = true;
+ }
+ if (c->modified) {
+ return write_changes_file(changesfilename, c,
+ CHANGES_WRITE_FILES, false);
+ } else if (improvedchecksum_supported(c, improvedfilehashes)) {
+ return write_changes_file(changesfilename, c,
+ CHANGES_WRITE_FILES, false);
+ } else
+ return RET_NOTHING;
+}
+
+static retvalue includeallsources(const char *changesfilename, struct changes *c, int argc, char **argv) {
+ struct fileentry *file;
+
+ for (file = c->files; file != NULL ; file = file->next) {
+ int i;
+
+ if (file->type != ft_DSC)
+ continue;
+
+ if (file->dsc == NULL) {
+ fprintf(stderr,
+"WARNING: Could not read '%s', thus cannot determine if it depends on unlisted files!\n",
+ file->basename);
+ continue;
+ }
+ assert (file->fullfilename != NULL);
+ for (i = 0 ; i < file->dsc->expected.names.count ; i++) {
+ const char *basefilename = file->dsc->expected.names.values[i];
+ struct fileentry * const sfile = file->dsc->uplink[i];
+ struct checksums **expected_p = &file->dsc->expected.checksums[i];
+ const struct checksums * const expected = *expected_p;
+
+ assert (expected != NULL);
+ assert (basefilename != NULL);
+ assert (sfile != NULL);
+
+ if (sfile->checksumsfromchanges != NULL)
+ continue;
+
+ if (argc > 0 && !isarg(argc, argv, basefilename))
+ continue;
+
+ sfile->checksumsfromchanges = checksums_dup(expected);
+ if (FAILEDTOALLOC(sfile->checksumsfromchanges))
+ return RET_ERROR_OOM;
+ /* copy section and priority information from the dsc */
+ if (sfile->section == NULL && file->section != NULL) {
+ sfile->section = strdup(file->section);
+ if (FAILEDTOALLOC(sfile->section))
+ return RET_ERROR_OOM;
+ }
+ if (sfile->priority == NULL && file->priority != NULL) {
+ sfile->priority = strdup(file->priority);
+ if (FAILEDTOALLOC(sfile->priority))
+ return RET_ERROR_OOM;
+ }
+
+ fprintf(stderr, "Going to add '%s' to '%s'.\n",
+ basefilename, changesfilename);
+ c->modified = true;
+ }
+ }
+ if (c->modified) {
+ return write_changes_file(changesfilename, c,
+ CHANGES_WRITE_FILES, false);
+ } else
+ return RET_NOTHING;
+}
+
+static retvalue adddsc(struct changes *c, const char *dscfilename, const struct strlist *searchpath) {
+ retvalue r;
+ struct fileentry *f;
+ struct dscfile *dsc;
+ char *fullfilename, *basefilename;
+ char *origdirectory;
+ const char *v;
+ int i;
+
+ r = findfile(dscfilename, c, searchpath, ".", &fullfilename);
+ if (RET_WAS_ERROR(r))
+ return r;
+ if (r == RET_NOTHING) {
+ fprintf(stderr, "Cannot find '%s'!\n", dscfilename);
+ return RET_ERROR_MISSING;
+ }
+ r = read_dscfile(fullfilename, &dsc);
+ if (r == RET_NOTHING) {
+ fprintf(stderr, "Error reading '%s'!\n", fullfilename);
+ r = RET_ERROR;
+ }
+ if (RET_WAS_ERROR(r)) {
+ free(fullfilename);
+ return r;
+ }
+ if (dsc->name == NULL || dsc->version == NULL) {
+ if (dsc->name == NULL)
+ fprintf(stderr, "Could not extract name of '%s'!\n",
+ fullfilename);
+ else
+ fprintf(stderr, "Could not extract version of '%s'!\n",
+ fullfilename);
+ dscfile_free(dsc);
+ free(fullfilename);
+ return RET_ERROR;
+ }
+ if (c->name != NULL) {
+ if (strcmp(c->name, dsc->name) != 0) {
+ fprintf(stderr,
+"ERROR: '%s' lists source '%s' while '%s' already is '%s'!\n",
+ fullfilename, dsc->name,
+ c->filename, c->name);
+ dscfile_free(dsc);
+ free(fullfilename);
+ return RET_ERROR;
+ }
+ } else {
+ c->name = strdup(dsc->name);
+ if (FAILEDTOALLOC(c->name)) {
+ dscfile_free(dsc);
+ free(fullfilename);
+ return RET_ERROR_OOM;
+ }
+ }
+ if (c->version != NULL) {
+ if (strcmp(c->version, dsc->version) != 0)
+ fprintf(stderr,
+"WARNING: '%s' lists version '%s' while '%s' already lists '%s'!\n",
+ fullfilename, dsc->version,
+ c->filename, c->version);
+ } else {
+ c->version = strdup(dsc->version);
+ if (FAILEDTOALLOC(c->version)) {
+ dscfile_free(dsc);
+ free(fullfilename);
+ return RET_ERROR_OOM;
+ }
+ }
+ // TODO: make sure if the .changes name/version are modified they will
+ // also be written...
+ v = strchr(dsc->version, ':');
+ if (v != NULL)
+ v++;
+ else
+ v = dsc->version;
+ basefilename = mprintf("%s_%s.dsc", dsc->name, v);
+ if (FAILEDTOALLOC(basefilename)) {
+ dscfile_free(dsc);
+ free(fullfilename);
+ return RET_ERROR_OOM;
+ }
+
+ r = dirs_getdirectory(fullfilename, &origdirectory);
+ if (RET_WAS_ERROR(r)) {
+ dscfile_free(dsc);
+ free(origdirectory);
+ free(fullfilename);
+ return r;
+ }
+
+ // TODO: add rename/copy option to be activated when old and new
+ // basefilename differ
+
+ r = add_file(c, basefilename, fullfilename, ft_DSC, &f);
+ if (RET_WAS_ERROR(r)) {
+ dscfile_free(dsc);
+ free(origdirectory);
+ return r;
+ }
+ if (r == RET_NOTHING) {
+ fprintf(stderr,
+"ERROR: '%s' already contains a file of the same name!\n",
+ c->filename);
+ dscfile_free(dsc);
+ free(origdirectory);
+ // TODO: check instead if it is already the same...
+ return RET_ERROR;
+ }
+ /* f owns dsc, fullfilename and basefilename now */
+ f->dsc = dsc;
+
+ /* now include the files needed by this */
+ for (i = 0 ; i < dsc->expected.names.count ; i++) {
+ struct fileentry *file;
+ const char *b = dsc->expected.names.values[i];
+ const struct checksums *checksums = dsc->expected.checksums[i];
+
+ file = add_fileentry(c, b, strlen(b), true, NULL);
+ if (FAILEDTOALLOC(file)) {
+ free(origdirectory);
+ return RET_ERROR_OOM;
+ }
+ dsc->uplink[i] = file;
+ /* make them appear in the .changes file if not there: */
+ // TODO: add missing checksums here from file
+ if (file->checksumsfromchanges == NULL) {
+ file->checksumsfromchanges = checksums_dup(checksums);
+ if (FAILEDTOALLOC(file->checksumsfromchanges)) {
+ free(origdirectory);
+ return RET_ERROR_OOM;
+ }
+ } // TODO: otherwise warn if not the same
+ }
+
+ c->modified = true;
+ r = checksums_read(f->fullfilename, &f->realchecksums);
+ if (RET_WAS_ERROR(r)) {
+ free(origdirectory);
+ return r;
+ }
+ f->checksumsfromchanges = checksums_dup(f->realchecksums);
+ if (FAILEDTOALLOC(f->checksumsfromchanges)) {
+ free(origdirectory);
+ return RET_ERROR_OOM;
+ }
+ /* for a "extended" dsc with section or priority */
+ if (dsc->section != NULL) {
+ free(f->section);
+ f->section = strdup(dsc->section);
+ if (FAILEDTOALLOC(f->section)) {
+ free(origdirectory);
+ return RET_ERROR_OOM;
+ }
+ }
+ if (dsc->priority != NULL) {
+ free(f->priority);
+ f->priority = strdup(dsc->priority);
+ if (FAILEDTOALLOC(f->priority)) {
+ free(origdirectory);
+ return RET_ERROR_OOM;
+ }
+ }
+ if (f->section == NULL || f->priority == NULL) {
+ struct sourceextraction *extraction;
+ int j;
+
+ extraction = sourceextraction_init(
+ (f->section == NULL)?&f->section:NULL,
+ (f->priority == NULL)?&f->priority:NULL);
+ if (FAILEDTOALLOC(extraction)) {
+ free(origdirectory);
+ return RET_ERROR_OOM;
+ }
+ for (j = 0 ; j < dsc->expected.names.count ; j++) {
+ sourceextraction_setpart(extraction, j,
+ dsc->expected.names.values[j]);
+ }
+ while (sourceextraction_needs(extraction, &j)) {
+ if (dsc->uplink[j]->fullfilename == NULL) {
+ /* look for file */
+ r = findfile(dsc->expected.names.values[j], c,
+ searchpath, origdirectory,
+ &dsc->uplink[j]->fullfilename);
+ if (RET_WAS_ERROR(r)) {
+ sourceextraction_abort(extraction);
+ free(origdirectory);
+ return r;
+ }
+ if (r == RET_NOTHING ||
+ dsc->uplink[j]->fullfilename == NULL)
+ break;
+ }
+ r = sourceextraction_analyse(extraction,
+ dsc->uplink[j]->fullfilename);
+ if (RET_WAS_ERROR(r)) {
+ sourceextraction_abort(extraction);
+ free(origdirectory);
+ return r;
+ }
+ }
+ r = sourceextraction_finish(extraction);
+ if (RET_WAS_ERROR(r)) {
+ free(origdirectory);
+ return r;
+ }
+ }
+ free(origdirectory);
+ /* update information in the main .changes file if not there already */
+ if (c->maintainer == NULL && dsc->maintainer != NULL) {
+ c->maintainer = strdup(dsc->maintainer);
+ if (FAILEDTOALLOC(c->maintainer))
+ return RET_ERROR_OOM;
+ }
+ if (!strlist_in(&c->architectures, "source")) {
+ r = strlist_add_dup(&c->architectures, "source");
+ if (RET_WAS_ERROR(r))
+ return r;
+ }
+ return RET_OK;
+}
+
+static retvalue adddscs(const char *changesfilename, struct changes *c, int argc, char **argv, const struct strlist *searchpath, bool fakefields) {
+ if (argc <= 0) {
+ fprintf(stderr,
+"Filenames of .dsc files to include expected!\n");
+ return RET_ERROR;
+ }
+ while (argc > 0) {
+ retvalue r = adddsc(c, argv[0], searchpath);
+ if (RET_WAS_ERROR(r))
+ return r;
+ argc--; argv++;
+ }
+ if (c->modified) {
+ return write_changes_file(changesfilename, c,
+ CHANGES_WRITE_ALL, fakefields);
+ } else
+ return RET_NOTHING;
+}
+
+static retvalue adddeb(struct changes *c, const char *debfilename, const struct strlist *searchpath) {
+ retvalue r;
+ struct fileentry *f;
+ struct binaryfile *deb;
+ const char *packagetype;
+ enum filetype type;
+ char *fullfilename, *basefilename;
+ const char *v;
+
+ r = findfile(debfilename, c, searchpath, ".", &fullfilename);
+ if (RET_WAS_ERROR(r))
+ return r;
+ if (r == RET_NOTHING) {
+ fprintf(stderr, "Cannot find '%s'!\n", debfilename);
+ return RET_ERROR_MISSING;
+ }
+ r = read_binaryfile(fullfilename, &deb);
+ if (r == RET_NOTHING) {
+ fprintf(stderr, "Error reading '%s'!\n", fullfilename);
+ r = RET_ERROR;
+ }
+ if (RET_WAS_ERROR(r)) {
+ free(fullfilename);
+ return r;
+ }
+ // TODO: check if there are other things but the name to distinguish them
+ if (strlen(fullfilename) > 5 &&
+ strcmp(fullfilename+strlen(fullfilename)-5, ".udeb") == 0) {
+ packagetype = "udeb";
+ type = ft_UDEB;
+ } else if (strlen(fullfilename) > 5 &&
+ strcmp(fullfilename+strlen(fullfilename)-5, ".ddeb") == 0) {
+ packagetype = "ddeb";
+ type = ft_DDEB;
+ } else {
+ packagetype = "deb";
+ type = ft_DEB;
+ }
+ if (deb->name == NULL || deb->version == NULL || deb->architecture == NULL) {
+ if (deb->name == NULL)
+ fprintf(stderr,
+"Could not extract packagename of '%s'!\n",
+ fullfilename);
+ else if (deb->version == NULL)
+ fprintf(stderr,
+"Could not extract version of '%s'!\n",
+ fullfilename);
+ else
+ fprintf(stderr,
+"Could not extract architecture of '%s'!\n",
+ fullfilename);
+ binaryfile_free(deb);
+ free(fullfilename);
+ return RET_ERROR;
+ }
+ if (c->name != NULL) {
+ const char *sourcename;
+ if (deb->sourcename != NULL)
+ sourcename = deb->sourcename;
+ else
+ sourcename = deb->name;
+ if (strcmp(c->name, sourcename) != 0) {
+ fprintf(stderr,
+"ERROR: '%s' lists source '%s' while '%s' already is '%s'!\n",
+ fullfilename, sourcename,
+ c->filename, c->name);
+ binaryfile_free(deb);
+ free(fullfilename);
+ return RET_ERROR;
+ }
+ } else {
+ if (deb->sourcename != NULL)
+ c->name = strdup(deb->sourcename);
+ else
+ c->name = strdup(deb->name);
+ if (FAILEDTOALLOC(c->name)) {
+ binaryfile_free(deb);
+ free(fullfilename);
+ return RET_ERROR_OOM;
+ }
+ }
+ if (c->version != NULL) {
+ const char *sourceversion;
+ if (deb->sourceversion != NULL)
+ sourceversion = deb->sourceversion;
+ else
+ sourceversion = deb->version;
+ if (strcmp(c->version, sourceversion) != 0)
+ fprintf(stderr,
+"WARNING: '%s' lists source version '%s' while '%s' already lists '%s'!\n",
+ fullfilename, sourceversion,
+ c->filename, c->version);
+ } else {
+ if (deb->sourceversion != NULL)
+ c->version = strdup(deb->sourceversion);
+ else
+ c->version = strdup(deb->version);
+ if (FAILEDTOALLOC(c->version)) {
+ binaryfile_free(deb);
+ free(fullfilename);
+ return RET_ERROR_OOM;
+ }
+ }
+ // TODO: make sure if the .changes name/version are modified they will
+ // also be written...
+ v = strchr(deb->version, ':');
+ if (v != NULL)
+ v++;
+ else
+ v = deb->version;
+ basefilename = mprintf("%s_%s_%s.%s", deb->name, v, deb->architecture,
+ packagetype);
+ if (FAILEDTOALLOC(basefilename)) {
+ binaryfile_free(deb);
+ free(fullfilename);
+ return RET_ERROR_OOM;
+ }
+
+ // TODO: add rename/copy option to be activated when old and new
+ // basefilename differ
+
+ r = add_file(c, basefilename, fullfilename, type, &f);
+ if (RET_WAS_ERROR(r)) {
+ binaryfile_free(deb);
+ return r;
+ }
+ if (r == RET_NOTHING) {
+ fprintf(stderr,
+"ERROR: '%s' already contains a file of the same name!\n",
+ c->filename);
+ binaryfile_free(deb);
+ // TODO: check instead if it is already the same...
+ return RET_ERROR;
+ }
+ /* f owns deb, fullfilename and basefilename now */
+ f->deb = deb;
+ deb->binary = get_binary(c, deb->name, strlen(deb->name));
+ if (FAILEDTOALLOC(deb->binary))
+ return RET_ERROR_OOM;
+ deb->next = deb->binary->files;
+ deb->binary->files = deb;
+ deb->binary->missedinheader = false;
+ c->modified = true;
+ r = checksums_read(f->fullfilename, &f->realchecksums);
+ if (RET_WAS_ERROR(r))
+ return r;
+ f->checksumsfromchanges = checksums_dup(f->realchecksums);
+ if (FAILEDTOALLOC(f->checksumsfromchanges))
+ return RET_ERROR_OOM;
+ if (deb->shortdescription != NULL) {
+ if (deb->binary->description == NULL) {
+ deb->binary->description = strdup(deb->shortdescription);
+ deb->binary->missedinheader = false;
+ } else if (strcmp(deb->binary->description,
+ deb->shortdescription) != 0) {
+ fprintf(stderr,
+"WARNING: '%s' already lists a different description for '%s' than contained in '%s'!\n",
+ c->filename, deb->name, fullfilename);
+ }
+ }
+ if (deb->section != NULL) {
+ free(f->section);
+ f->section = strdup(deb->section);
+ }
+ if (deb->priority != NULL) {
+ free(f->priority);
+ f->priority = strdup(deb->priority);
+ }
+ if (c->maintainer == NULL && deb->maintainer != NULL) {
+ c->maintainer = strdup(deb->maintainer);
+ }
+ if (deb->architecture != NULL &&
+ !strlist_in(&c->architectures, deb->architecture)) {
+ strlist_add_dup(&c->architectures, deb->architecture);
+ }
+ return RET_OK;
+}
+
+static retvalue adddebs(const char *changesfilename, struct changes *c, int argc, char **argv, const struct strlist *searchpath, bool fakefields) {
+ if (argc <= 0) {
+ fprintf(stderr,
+"Filenames of .deb files to include expected!\n");
+ return RET_ERROR;
+ }
+ while (argc > 0) {
+ retvalue r = adddeb(c, argv[0], searchpath);
+ if (RET_WAS_ERROR(r))
+ return r;
+ argc--; argv++;
+ }
+ if (c->modified) {
+ return write_changes_file(changesfilename, c,
+ CHANGES_WRITE_ALL, fakefields);
+ } else
+ return RET_NOTHING;
+}
+
+static retvalue addrawfile(struct changes *c, const char *filename, const struct strlist *searchpath) {
+ retvalue r;
+ struct fileentry *f;
+ char *fullfilename, *basefilename;
+ struct checksums *checksums;
+
+ r = findfile(filename, c, searchpath, ".", &fullfilename);
+ if (RET_WAS_ERROR(r))
+ return r;
+ if (r == RET_NOTHING) {
+ fprintf(stderr, "Cannot find '%s'!\n", filename);
+ return RET_ERROR_MISSING;
+ }
+ basefilename = strdup(dirs_basename(filename));
+ if (FAILEDTOALLOC(basefilename)) {
+ free(fullfilename);
+ return RET_ERROR_OOM;
+ }
+ r = checksums_read(fullfilename, &checksums);
+ if (RET_WAS_ERROR(r)) {
+ free(fullfilename);
+ free(basefilename);
+ return r;
+ }
+ r = add_file(c, basefilename, fullfilename, ft_UNKNOWN, &f);
+ // fullfilename and basefilename now belong to *f or are already free'd
+ basefilename = NULL;
+ fullfilename = NULL;
+ if (RET_WAS_ERROR(r)) {
+ checksums_free(checksums);
+ return r;
+ }
+ if (r == RET_NOTHING) {
+
+ assert (f != NULL);
+
+ if (f->checksumsfromchanges != NULL) {
+ /* already listed in .changes */
+
+ if (!checksums_check(f->checksumsfromchanges, checksums,
+ NULL)) {
+ fprintf(stderr,
+"ERROR: '%s' already contains a file with name '%s' but different checksums!\n",
+ c->filename, f->basename);
+ checksums_free(checksums);
+ return RET_ERROR;
+ }
+ printf(
+"'%s' already lists '%s' with same checksums. Doing nothing.\n",
+ c->filename, f->basename);
+ checksums_free(checksums);
+ return RET_NOTHING;
+ } else {
+ /* file already expected by some other part (e.g. a .dsc) */
+
+ // TODO: find out whom this files belong to and warn if different
+ }
+ }
+
+ c->modified = true;
+ assert (f->checksumsfromchanges == NULL);
+ f->checksumsfromchanges = checksums;
+ checksums = NULL;
+ if (f->realchecksums == NULL)
+ f->realchecksums = checksums_dup(f->checksumsfromchanges);
+ if (FAILEDTOALLOC(f->realchecksums))
+ return RET_ERROR_OOM;
+ return RET_OK;
+}
+
+static retvalue addrawfiles(const char *changesfilename, struct changes *c, int argc, char **argv, const struct strlist *searchpath, bool fakefields) {
+ if (argc <= 0) {
+ fprintf(stderr,
+"Filenames of files to add (without further parsing) expected!\n");
+ return RET_ERROR;
+ }
+ while (argc > 0) {
+ retvalue r = addrawfile(c, argv[0], searchpath);
+ if (RET_WAS_ERROR(r))
+ return r;
+ argc--; argv++;
+ }
+ if (c->modified) {
+ return write_changes_file(changesfilename, c,
+ CHANGES_WRITE_FILES, fakefields);
+ } else
+ return RET_NOTHING;
+}
+
+static retvalue addfiles(const char *changesfilename, struct changes *c, int argc, char **argv, const struct strlist *searchpath, bool fakefields) {
+ if (argc <= 0) {
+ fprintf(stderr, "Filenames of files to add expected!\n");
+ return RET_ERROR;
+ }
+ while (argc > 0) {
+ retvalue r;
+ const char *filename = argv[0];
+ size_t l = strlen(filename);
+
+ if ((l > 4 && strcmp(filename+l-4, ".deb") == 0) ||
+ (l > 5 && strcmp(filename+l-5, ".ddeb") == 0) ||
+ (l > 5 && strcmp(filename+l-5, ".udeb") == 0))
+ r = adddeb(c, filename, searchpath);
+ else if ((l > 4 && strcmp(filename+l-4, ".dsc") == 0))
+ r = adddsc(c, filename, searchpath);
+ else
+ r = addrawfile(c, argv[0], searchpath);
+ if (RET_WAS_ERROR(r))
+ return r;
+ argc--; argv++;
+ }
+ if (c->modified) {
+ return write_changes_file(changesfilename, c,
+ CHANGES_WRITE_ALL, fakefields);
+ } else
+ return RET_NOTHING;
+}
+
+static retvalue dumbremovefiles(const char *changesfilename, struct changes *c, int argc, char **argv) {
+ if (argc <= 0) {
+ fprintf(stderr,
+"Filenames of files to remove (without further parsing) expected!\n");
+ return RET_ERROR;
+ }
+ while (argc > 0) {
+ struct fileentry **fp;
+ /*@null@*/ struct fileentry *f;
+
+ fp = find_fileentry(c, argv[0], strlen(argv[0]), NULL);
+ f = *fp;
+ if (f == NULL) {
+ fprintf(stderr,
+"Not removing '%s' as not listed in '%s'!\n",
+ argv[0], c->filename);
+ } else if (f->checksumsfromchanges != NULL) {
+ /* removing its checksums makes it vanish from the
+ * .changes file generated, while still keeping pointers
+ * from other files intact */
+ checksums_free(f->checksumsfromchanges);
+ f->checksumsfromchanges = NULL;
+ c->modified = true;
+ }
+ argc--; argv++;
+ }
+ if (c->modified) {
+ return write_changes_file(changesfilename, c,
+ CHANGES_WRITE_FILES, false);
+ } else
+ return RET_NOTHING;
+}
+
+static retvalue setdistribution(const char *changesfilename, struct changes *c, int argc, char **argv) {
+ retvalue r;
+ struct strlist distributions;
+ int i;
+
+ if (argc <= 0) {
+ fprintf(stderr, "expected Distribution name to set!\n");
+ return RET_ERROR;
+ }
+ r = strlist_init_n(argc, &distributions);
+ if (RET_WAS_ERROR(r))
+ return r;
+ for (i = 0 ; i < argc ; i++) {
+ r = strlist_add_dup(&distributions, argv[i]);
+ if (RET_WAS_ERROR(r)) {
+ strlist_done(&distributions);
+ return r;
+ }
+ }
+ strlist_done(&c->distributions);
+ strlist_move(&c->distributions, &distributions);
+ return write_changes_file(changesfilename, c,
+ CHANGES_WRITE_DISTRIBUTIONS, false);
+}
+
+static int execute_command(int argc, char **argv, const char *changesfilename, const struct strlist *searchpath, bool file_exists, bool create_file, bool fakefields, struct changes *changesdata) {
+ const char *command = argv[0];
+ retvalue r;
+
+ assert (argc > 0);
+
+ if (strcasecmp(command, "verify") == 0) {
+ if (argc > 1) {
+ fprintf(stderr, "Too many arguments!\n");
+ r = RET_ERROR;
+ } else if (file_exists)
+ r = verify(changesfilename, changesdata);
+ else {
+ fprintf(stderr, "No such file '%s'!\n",
+ changesfilename);
+ r = RET_ERROR;
+ }
+ } else if (strcasecmp(command, "updatechecksums") == 0) {
+ if (file_exists)
+ r = updatechecksums(changesfilename, changesdata,
+ argc-1, argv+1);
+ else {
+ fprintf(stderr, "No such file '%s'!\n",
+ changesfilename);
+ r = RET_ERROR;
+ }
+ } else if (strcasecmp(command, "includeallsources") == 0) {
+ if (file_exists)
+ r = includeallsources(changesfilename, changesdata,
+ argc-1, argv+1);
+ else {
+ fprintf(stderr, "No such file '%s'!\n",
+ changesfilename);
+ r = RET_ERROR;
+ }
+ } else if (strcasecmp(command, "addrawfile") == 0) {
+ if (file_exists || create_file)
+ r = addrawfiles(changesfilename, changesdata,
+ argc-1, argv+1, searchpath, fakefields);
+ else {
+ fprintf(stderr, "No such file '%s'!\n",
+ changesfilename);
+ r = RET_ERROR;
+ }
+ } else if (strcasecmp(command, "adddsc") == 0) {
+ if (file_exists || create_file)
+ r = adddscs(changesfilename, changesdata,
+ argc-1, argv+1, searchpath, fakefields);
+ else {
+ fprintf(stderr, "No such file '%s'!\n",
+ changesfilename);
+ r = RET_ERROR;
+ }
+ } else if (strcasecmp(command, "adddeb") == 0) {
+ if (file_exists || create_file)
+ r = adddebs(changesfilename, changesdata,
+ argc-1, argv+1, searchpath, fakefields);
+ else {
+ fprintf(stderr, "No such file '%s'!\n",
+ changesfilename);
+ r = RET_ERROR;
+ }
+ } else if (strcasecmp(command, "add") == 0) {
+ if (file_exists || create_file)
+ r = addfiles(changesfilename, changesdata,
+ argc-1, argv+1, searchpath, fakefields);
+ else {
+ fprintf(stderr, "No such file '%s'!\n",
+ changesfilename);
+ r = RET_ERROR;
+ }
+ } else if (strcasecmp(command, "setdistribution") == 0) {
+ if (file_exists)
+ r = setdistribution(changesfilename, changesdata,
+ argc-1, argv+1);
+ else {
+ fprintf(stderr, "No such file '%s'!\n",
+ changesfilename);
+ r = RET_ERROR;
+ }
+ } else if (strcasecmp(command, "dumbremove") == 0) {
+ if (file_exists)
+ r = dumbremovefiles(changesfilename, changesdata,
+ argc-1, argv+1);
+ else {
+ fprintf(stderr, "No such file '%s'!\n",
+ changesfilename);
+ r = RET_ERROR;
+ }
+ } else {
+ fprintf(stderr, "Unknown command '%s'\n", command);
+ r = RET_ERROR;
+ }
+ return r;
+}
+
+static retvalue splitpath(struct strlist *list, const char *path) {
+ retvalue r;
+ const char *next;
+
+ while ((next = index(path, ':')) != NULL) {
+ if (next > path) {
+ char *dir = strndup(path, next-path);
+ if (FAILEDTOALLOC(dir)) {
+ return RET_ERROR_OOM;
+ }
+ r = strlist_add(list, dir);
+ if (RET_WAS_ERROR(r))
+ return r;
+ }
+ path = next+1;
+ }
+ return strlist_add_dup(list, path);
+}
+
+int main(int argc, char *argv[]) {
+ static int longoption = 0;
+ static const struct option longopts[] = {
+ {"help", no_argument, NULL, 'h'},
+ {"create", no_argument, NULL, 'C'},
+ {"create-with-all-fields", no_argument, &longoption, 6},
+ {"searchpath", required_argument, NULL, 's'},
+ {"gunzip", required_argument, &longoption, 1},
+ {"bunzip2", required_argument, &longoption, 2},
+ {"unlzma", required_argument, &longoption, 3},
+ {"unxz", required_argument, &longoption, 4},
+ {"lunzip", required_argument, &longoption, 5},
+ {"unzstd", required_argument, &longoption, 7},
+ {NULL, 0, NULL, 0},
+ };
+ int c;
+ const char *changesfilename;
+ bool file_exists;
+ bool create_file = false;
+ bool all_fields = false;
+ struct strlist searchpath;
+ struct changes *changesdata;
+ char *gunzip = NULL, *bunzip2 = NULL, *unlzma = NULL,
+ *unxz = NULL, *lunzip = NULL, *unzstd = NULL;
+ retvalue r;
+
+ strlist_init(&searchpath);
+
+ while ((c = getopt_long(argc, argv, "+hi:s:", longopts, NULL)) != -1) {
+ switch (c) {
+ case '\0':
+ switch (longoption) {
+ case 1:
+ gunzip = strdup(optarg);
+ break;
+ case 2:
+ bunzip2 = strdup(optarg);
+ break;
+ case 3:
+ unlzma = strdup(optarg);
+ break;
+ case 4:
+ unxz = strdup(optarg);
+ break;
+ case 5:
+ lunzip = strdup(optarg);
+ break;
+ case 7:
+ unzstd = strdup(optarg);
+ break;
+ case 6:
+ create_file = true;
+ all_fields = true;
+ break;
+ }
+ break;
+ case 'h':
+ about(true);
+ case 'C':
+ create_file = true;
+ break;
+ case 's':
+ r = splitpath(&searchpath, optarg);
+ if (RET_WAS_ERROR(r)) {
+ if (r == RET_ERROR_OOM)
+ fprintf(stderr,
+"Out of memory!\n");
+ exit(EXIT_FAILURE);
+ }
+ break;
+ }
+ }
+ if (argc - optind < 2) {
+ about(false);
+ }
+ signature_init(false);
+ uncompressions_check(gunzip, bunzip2, unlzma, unxz, lunzip, unzstd);
+
+ changesfilename = argv[optind];
+ if (strcmp(changesfilename, "-") != 0 &&
+ !endswith(changesfilename, ".changes")) {
+ fprintf(stderr, "first argument not ending with '.changes'\n");
+ exit(EXIT_FAILURE);
+ }
+ file_exists = isregularfile(changesfilename);
+ if (file_exists) {
+ char *changes;
+
+ r = signature_readsignedchunk(changesfilename, changesfilename,
+ &changes, NULL, NULL);
+ if (!RET_IS_OK(r)) {
+ signatures_done();
+ if (r == RET_ERROR_OOM)
+ fprintf(stderr, "Out of memory!\n");
+ exit(EXIT_FAILURE);
+ }
+ r = parse_changes(changesfilename, changes,
+ &changesdata, &searchpath);
+ if (RET_IS_OK(r))
+ changesdata->control = changes;
+ else {
+ free(changes);
+ changesdata = NULL;
+ }
+ } else {
+ changesdata = zNEW(struct changes);
+ if (FAILEDTOALLOC(changesdata))
+ r = RET_ERROR_OOM;
+ else {
+ changesdata->filename = strdup(changesfilename);
+ if (FAILEDTOALLOC(changesdata->filename))
+ r = RET_ERROR_OOM;
+ else
+ r = dirs_getdirectory(changesfilename,
+ &changesdata->basedir);
+ }
+ }
+
+ if (!RET_WAS_ERROR(r)) {
+ argc -= (optind+1);
+ argv += (optind+1);
+ r = execute_command(argc, argv, changesfilename, &searchpath,
+ file_exists, create_file, all_fields,
+ changesdata);
+ }
+ changes_free(changesdata);
+
+ signatures_done();
+ if (RET_IS_OK(r))
+ exit(EXIT_SUCCESS);
+ if (r == RET_ERROR_OOM)
+ fprintf(stderr, "Out of memory!\n");
+ exit(EXIT_FAILURE);
+}