From 69d47432d9d434196b45276f0aac5cabed66860d Mon Sep 17 00:00:00 2001
From: Guillem Jover <gjover@sipwise.com>
Date: Fri, 18 Nov 2016 14:11:58 +0100
Subject: [PATCH] Add preliminary .buildinfo support

---
 changes.c       |  5 +++-
 changes.h       |  3 ++-
 checkin.c       | 55 +++++++++++++++++++++++++++++++++-------
 distribution.h  |  1 +
 docs/reprepro.1 |  3 +++
 incoming.c      | 78 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 needbuild.c     |  2 +-
 tracking.c      | 10 ++++++--
 trackingt.h     |  1 +
 9 files changed, 144 insertions(+), 14 deletions(-)

diff --git a/changes.c b/changes.c
index 5d01754..2cafca3 100644
--- a/changes.c
+++ b/changes.c
@@ -237,6 +237,9 @@ retvalue changes_parsefileline(const char *fileline, /*@out@*/filetype *result_t
 		} else if (l > 6 && strncmp(p-6, ".build", 6) == 0) {
 			type = fe_LOG;
 			eoi = p - 6;
+		} else if (l > 10 && strncmp(p-10, ".buildinfo", 10) == 0) {
+			type = fe_BUILDINFO;
+			eoi = p - 10;
 		}
 		if (type != fe_UNKNOWN) {
 			/* check for a proper version */
@@ -255,7 +258,7 @@ retvalue changes_parsefileline(const char *fileline, /*@out@*/filetype *result_t
 					return RET_ERROR;
 				}
 				checkfilename = true;
-			} else if (type == fe_LOG) {
+			} else if (type == fe_LOG || type == fe_BUILDINFO) {
 				if (*p == '_') {
 					archstart = p + 1;
 					archend = eoi;
diff --git a/changes.h b/changes.h
index 30ad136..f2b72c3 100644
--- a/changes.h
+++ b/changes.h
@@ -10,7 +10,8 @@ typedef enum {
 	fe_DEB, fe_UDEB,
 	fe_DSC, fe_DIFF, fe_ORIG, fe_TAR,
 	fe_ALTSRC,
-	fe_BYHAND, fe_LOG, fe_CHANGES
+	fe_BYHAND, fe_LOG, fe_CHANGES,
+	fe_BUILDINFO
 } filetype;
 
 #define FE_PACKAGE(ft) ((ft) == fe_DEB || (ft) == fe_UDEB || (ft) == fe_DSC)
diff --git a/checkin.c b/checkin.c
index 3b1ec32..be46256 100644
--- a/checkin.c
+++ b/checkin.c
@@ -161,7 +161,7 @@ static void changes_free(/*@only@*/struct changes *changes) {
 }
 
 
-static retvalue newentry(struct fileentry **entry, const char *fileline, const struct atomlist *packagetypes, const struct atomlist *forcearchitectures, const char *sourcename, bool includebyhand, bool includelogs, bool *ignoredlines_p, bool skip_binaries) {
+static retvalue newentry(struct fileentry **entry, const char *fileline, const struct atomlist *packagetypes, const struct atomlist *forcearchitectures, const char *sourcename, bool includebyhand, bool includelogs, bool includebuildinfos, bool *ignoredlines_p, bool skip_binaries) {
 	struct fileentry *e;
 	retvalue r;
 
@@ -207,7 +207,7 @@ static retvalue newentry(struct fileentry **entry, const char *fileline, const s
 		*ignoredlines_p = true;
 		return RET_NOTHING;
 	}
-	if (e->type != fe_LOG &&
+	if (e->type != fe_LOG && e->type != fe_BUILDINFO &&
 			e->architecture_into == architecture_source &&
 			strcmp(e->name, sourcename) != 0) {
 		fprintf(stderr,
@@ -242,6 +242,34 @@ static retvalue newentry(struct fileentry **entry, const char *fileline, const s
 			*ignoredlines_p = true;
 			return RET_NOTHING;
 		}
+	} else if (e->type == fe_BUILDINFO) {
+		if (strcmp(e->name, sourcename) != 0) {
+			fprintf(stderr,
+"Warning: File '%s' looks like buildinfo but does not start with '%s_'!\n",
+					e->basename, sourcename);
+		}
+		if (!includebuildinfos) {
+			// TODO: at least check them and fail if wrong?
+			fprintf(stderr, "Ignoring buildinfo file: '%s'!\n",
+							e->basename);
+			freeentries(e);
+			*ignoredlines_p = true;
+			return RET_NOTHING;
+		}
+		if (atom_defined(e->architecture_into) &&
+				limitations_missed(forcearchitectures,
+					e->architecture_into)) {
+			if (verbose > 1)
+				fprintf(stderr,
+"Skipping '%s' as not for architecture ",
+					e->basename);
+			atomlist_fprint(stderr, at_architecture,
+					forcearchitectures);
+			fputs(".\n", stderr);
+			freeentries(e);
+			*ignoredlines_p = true;
+			return RET_NOTHING;
+		}
 	} else if (forcearchitectures != NULL) {
 		if (e->architecture_into == architecture_all &&
 				!skip_binaries) {
@@ -275,7 +303,7 @@ static retvalue newentry(struct fileentry **entry, const char *fileline, const s
 }
 
 /* Parse the Files-header to see what kind of files we carry around */
-static retvalue changes_parsefilelines(const char *filename, struct changes *changes, const struct strlist *filelines, const struct atomlist *packagetypes, const struct atomlist *forcearchitectures, bool includebyhand, bool includelogs, bool *ignoredlines_p, bool skip_binaries) {
+static retvalue changes_parsefilelines(const char *filename, struct changes *changes, const struct strlist *filelines, const struct atomlist *packagetypes, const struct atomlist *forcearchitectures, bool includebyhand, bool includelogs, bool includebuildinfos, bool *ignoredlines_p, bool skip_binaries) {
 	retvalue result, r;
 	int i;
 
@@ -287,7 +315,8 @@ static retvalue changes_parsefilelines(const char *filename, struct changes *cha
 
 		r = newentry(&changes->files, fileline,
 				packagetypes, forcearchitectures,
-				changes->source, includebyhand, includelogs,
+				changes->source,
+				includebyhand, includelogs, includebuildinfos,
 				ignoredlines_p, skip_binaries);
 		RET_UPDATE(result, r);
 		if (r == RET_ERROR)
@@ -374,7 +403,7 @@ static retvalue check(const char *filename, struct changes *changes, const char
 	return r;
 }
 
-static retvalue changes_read(const char *filename, /*@out@*/struct changes **changes, const struct atomlist *packagetypes, const struct atomlist *forcearchitectures, bool includebyhand, bool includelogs) {
+static retvalue changes_read(const char *filename, /*@out@*/struct changes **changes, const struct atomlist *packagetypes, const struct atomlist *forcearchitectures, bool includebyhand, bool includelogs, bool includebuildinfos) {
 	retvalue r;
 	struct changes *c;
 	struct strlist filelines[cs_hashCOUNT];
@@ -467,8 +496,8 @@ static retvalue changes_read(const char *filename, /*@out@*/struct changes **cha
 	}
 	r = changes_parsefilelines(filename, c, &filelines[cs_md5sum],
 			packagetypes, forcearchitectures,
-			includebyhand, includelogs, &ignoredlines,
-			skip_binaries);
+			includebyhand, includelogs, includebuildinfos,
+			&ignoredlines, skip_binaries);
 	if (RET_WAS_ERROR(r)) {
 		strlist_done(&filelines[cs_md5sum]);
 		changes_free(c);
@@ -523,7 +552,9 @@ static retvalue changes_fixfields(const struct distribution *distribution, const
 		const struct overridedata *oinfo = NULL;
 		const char *force = NULL;
 
-		if (e->type == fe_BYHAND || e->type == fe_LOG) {
+		if (e->type == fe_BYHAND ||
+		    e->type == fe_BUILDINFO ||
+		    e->type == fe_LOG) {
 			needsourcedir = true;
 			continue;
 		}
@@ -1242,6 +1273,11 @@ static retvalue changes_includepkgs(struct distribution *distribution, struct ch
 					distribution, trackingdata);
 			if (r == RET_NOTHING)
 				*missed_p = true;
+		} else if (e->type == fe_BUILDINFO && trackingdata != NULL) {
+			r = trackedpackage_addfilekey(trackingdata->tracks,
+					trackingdata->pkg,
+					ft_BUILDINFO, e->filekey, false);
+			e->filekey = NULL;
 		} else if (e->type == fe_LOG && trackingdata != NULL) {
 			r = trackedpackage_addfilekey(trackingdata->tracks,
 					trackingdata->pkg,
@@ -1359,7 +1395,8 @@ retvalue changes_add(trackingdb const tracks, const struct atomlist *packagetype
 	r = changes_read(changesfilename, &changes,
 			packagetypes, forcearchitectures,
 			distribution->trackingoptions.includebyhand,
-			distribution->trackingoptions.includelogs);
+			distribution->trackingoptions.includelogs,
+			distribution->trackingoptions.includebuildinfos);
 	if (RET_WAS_ERROR(r))
 		return r;
 
diff --git a/distribution.h b/distribution.h
index 2a2ba64..733a68e 100644
--- a/distribution.h
+++ b/distribution.h
@@ -70,6 +70,7 @@ struct distribution {
 	enum trackingtype { dt_NONE=0, dt_KEEP, dt_ALL, dt_MINIMAL } tracking;
 	struct trackingoptions { bool includechanges;
 		bool includebyhand;
+		bool includebuildinfos;
 		bool includelogs;
 		bool needsources;
 		bool keepsources;
diff --git a/docs/reprepro.1 b/docs/reprepro.1
index 485b449..6b37826 100644
--- a/docs/reprepro.1
+++ b/docs/reprepro.1
@@ -1514,6 +1514,9 @@ Thus it is also put into the pool.
 Add \fBbyhand\fP and \fBraw\-\fP\fI*\fP files to the tracked
 files and thus in the pool.
 .br
+.B includebuildinfos
+Add buildinfo files to the tracked files and thus in the pool.
+.br
 .B includelogs
 Add log files to the tracked files and thus in the pool.
 (Not that putting log files in changes files is a reprepro
diff --git a/incoming.c b/incoming.c
index 029931b..0b2c221 100644
--- a/incoming.c
+++ b/incoming.c
@@ -1116,6 +1116,73 @@ static retvalue candidate_read_files(struct incoming *i, struct candidate *c) {
 	return RET_OK;
 }
 
+static retvalue candidate_preparebuildinfos(const struct incoming *i, const struct candidate *c, struct candidate_perdistribution *per) {
+	retvalue r;
+	struct candidate_package *package;
+	struct candidate_file *firstbuildinfo = NULL, *file;
+	component_t component = component_strange;
+	int count = 0;
+
+	for (file = c->files ; file != NULL ; file = file->next) {
+		if (file->type == fe_BUILDINFO) {
+			count++;
+			if (firstbuildinfo == NULL)
+				firstbuildinfo = file;
+		}
+	}
+	if (count == 0)
+		return RET_NOTHING;
+
+	/* search for a component to use */
+	for (package = per->packages ; package != NULL ;
+	                               package = package->next) {
+		if (atom_defined(package->component)) {
+			component = package->component;
+			break;
+		}
+	}
+
+	/* pseudo package containing buildinfo files */
+	package = candidate_newpackage(per, firstbuildinfo);
+	if (FAILEDTOALLOC(package))
+		return RET_ERROR_OOM;
+	r = strlist_init_n(count, &package->filekeys);
+	if (RET_WAS_ERROR(r))
+		return r;
+	package->files = nzNEW(count, const struct candidate_file *);
+	if (FAILEDTOALLOC(package->files))
+		return RET_ERROR_OOM;
+
+	for (file = c->files ; file != NULL ; file = file->next) {
+		char *filekey;
+
+		if (file->type != fe_BUILDINFO)
+			continue;
+
+		r = candidate_usefile(i, c, file);
+		if (RET_WAS_ERROR(r))
+			return r;
+
+		// TODO: add same checks on the basename contents?
+
+		filekey = calc_filekey(component, c->source, BASENAME(i, file->ofs));
+		if (FAILEDTOALLOC(filekey))
+			return RET_ERROR_OOM;
+
+		r = files_canadd(filekey, file->checksums);
+		if (RET_WAS_ERROR(r))
+			return r;
+		if (RET_IS_OK(r))
+			package->files[package->filekeys.count] = file;
+		r = strlist_add(&package->filekeys, filekey);
+		assert (r == RET_OK);
+	}
+	assert (package->filekeys.count == count);
+	return RET_OK;
+}
+
+
+
 static retvalue candidate_preparechangesfile(const struct candidate *c, struct candidate_perdistribution *per) {
 	retvalue r;
 	char *basefilename, *filekey;
@@ -1614,6 +1681,11 @@ static retvalue prepare_for_distribution(const struct incoming *i, const struct
 			if (RET_WAS_ERROR(r))
 				return r;
 		}
+		if (d->into->trackingoptions.includebuildinfos) {
+			r = candidate_preparebuildinfos(i, c, d);
+			if (RET_WAS_ERROR(r))
+				return r;
+		}
 		if (d->into->trackingoptions.includechanges) {
 			r = candidate_preparechangesfile(c, d);
 			if (RET_WAS_ERROR(r))
@@ -1795,6 +1867,12 @@ static retvalue candidate_add_into(const struct incoming *i, const struct candid
 			r = trackedpackage_adddupfilekeys(trackingdata.tracks,
 					trackingdata.pkg,
 					ft_XTRA_DATA, &p->filekeys, false);
+		} else if (p->master->type == fe_BUILDINFO) {
+			assert (tracks != NULL);
+
+			r = trackedpackage_adddupfilekeys(trackingdata.tracks,
+					trackingdata.pkg,
+					ft_BUILDINFO, &p->filekeys, false);
 		} else if (p->master->type == fe_LOG) {
 			assert (tracks != NULL);
 
diff --git a/needbuild.c b/needbuild.c
index 8aaa52d..2001221 100644
--- a/needbuild.c
+++ b/needbuild.c
@@ -90,7 +90,7 @@ static retvalue tracked_source_needs_build(architecture_t architecture, const ch
 			   so nothing is to be done */
 			return RET_NOTHING;
 		}
-		if (ft == ft_LOG || ft == ft_CHANGES) {
+		if (ft == ft_LOG || ft == ft_BUILDINFO || ft == ft_CHANGES) {
 			const char *a = strrchr(fk, '_');
 			const char *e;
 
diff --git a/tracking.c b/tracking.c
index c231e90..ab1fcfa 100644
--- a/tracking.c
+++ b/tracking.c
@@ -85,6 +85,7 @@ retvalue tracking_initialize(/*@out@*/trackingdb *db, const struct distribution
 static inline enum filetype filetypechar(enum filetype filetype) {
 	switch (filetype) {
 		case ft_LOG:
+		case ft_BUILDINFO:
 		case ft_CHANGES:
 		case ft_ALL_BINARY:
 		case ft_ARCH_BINARY:
@@ -697,6 +698,7 @@ retvalue tracking_foreach_ro(struct distribution *d, tracking_foreach_ro_action
 retvalue tracking_parse(struct distribution *d, struct configiterator *iter) {
 	enum trackingflags { tf_keep, tf_all, tf_minimal,
 		tf_includechanges, tf_includebyhand, tf_includelogs,
+		tf_includebuildinfos,
 		tf_keepsources,
 		tf_needsources, tf_embargoalls,
 		tf_COUNT /* must be last */
@@ -706,6 +708,7 @@ retvalue tracking_parse(struct distribution *d, struct configiterator *iter) {
 		{"all",		tf_all},
 		{"minimal",	tf_minimal},
 		{"includechanges",	tf_includechanges},
+		{"includebuildinfos",	tf_includebuildinfos},
 		{"includelogs",		tf_includelogs},
 		{"includebyhand",	tf_includebyhand},
 		{"keepsources",		tf_keepsources},
@@ -749,6 +752,7 @@ retvalue tracking_parse(struct distribution *d, struct configiterator *iter) {
 
 	d->trackingoptions.includechanges = flags[tf_includechanges];
 	d->trackingoptions.includebyhand = flags[tf_includebyhand];
+	d->trackingoptions.includebuildinfos = flags[tf_includebuildinfos];
 	d->trackingoptions.includelogs = flags[tf_includelogs];
 	d->trackingoptions.keepsources = flags[tf_keepsources];
 	d->trackingoptions.needsources = flags[tf_needsources];
@@ -953,10 +957,12 @@ static inline retvalue trackedpackage_removeall(trackingdb tracks, struct tracke
 static inline bool tracking_needed(trackingdb tracks, struct trackedpackage *pkg, int ofs) {
 	if (pkg->refcounts[ofs] > 0)
 		return true;
-	// TODO: add checks so that only .changes and .log files belonging
-	// to still existing binaries are kept in minimal mode
+	// TODO: add checks so that only .changes, .buildinfo and .log files
+	// belonging to still existing binaries are kept in minimal mode
 	if (pkg->filetypes[ofs] == ft_LOG && tracks->options.includelogs)
 		return true;
+	if (pkg->filetypes[ofs] == ft_BUILDINFO && tracks->options.includebuildinfos)
+		return true;
 	if (pkg->filetypes[ofs] == ft_CHANGES && tracks->options.includechanges)
 		return true;
 	if (pkg->filetypes[ofs] == ft_XTRA_DATA)
diff --git a/trackingt.h b/trackingt.h
index ce7670c..6faf093 100644
--- a/trackingt.h
+++ b/trackingt.h
@@ -5,6 +5,7 @@ enum filetype { ft_ALL_BINARY='a',
 		ft_ARCH_BINARY='b',
 		ft_CHANGES = 'c',
 		ft_LOG='l',
+		ft_BUILDINFO='i',
 		ft_SOURCE='s',
 		ft_XTRA_DATA='x'};
 
-- 
2.11.0.rc1.160.g51e66c2

