termdecide.c 8.73 KB
Newer Older
1
/*  This file is part of "reprepro"
2
 *  Copyright (C) 2004,2005,2007,2009,2016 Bernhard R. Link
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License version 2 as
 *  published by the Free Software Foundation.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02111-1301  USA
 */
#include <config.h>

#include <errno.h>
#include <assert.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include "error.h"
#include "mprintf.h"
#include "strlist.h"
#include "names.h"
#include "chunks.h"
#include "globmatch.h"
#include "dpkgversions.h"
#include "terms.h"
#include "termdecide.h"

static inline bool check_field(enum term_comparison c, const char *value, const char *with) {
36
	if (c == tc_none) {
37
		return true;
38
	} else if (c == tc_globmatch) {
39
		return globmatch(value, with);
40
	} else if (c == tc_notglobmatch) {
41 42 43 44
		return !globmatch(value, with);
	} else {
		int i;
		i = strcmp(value, with);
45
		if (i < 0)
46 47 48
			return c == tc_strictless
				|| c == tc_lessorequal
				|| c == tc_notequal;
49
		else if (i > 0)
50 51 52 53 54 55 56 57 58 59
			return  c == tc_strictmore
				|| c == tc_moreorequal
				|| c == tc_notequal;
		else
			return c == tc_lessorequal
				|| c == tc_moreorequal
				|| c == tc_equal;
	}
}

60 61 62 63
/* this has a target argument instead of using package->target
 * as the package might come from one distribution/architecture/...
 * and the decision being about adding it somewhere else */
retvalue term_decidepackage(const term *condition, struct package *package, struct target *target) {
64 65
	const struct term_atom *atom = condition;

66 67
	while (atom != NULL) {
		bool correct; char *value;
68 69 70
		enum term_comparison c = atom->comparison;
		retvalue r;

71
		if (atom->isspecial) {
72 73
			correct = atom->special.type->compare(c,
					&atom->special.comparewith,
74
					package, target);
75
		} else {
76
			r = chunk_getvalue(package->control,
77
					atom->generic.key, &value);
78
			if (RET_WAS_ERROR(r))
79
				return r;
80 81
			if (r == RET_NOTHING) {
				correct = (c == tc_notequal
82 83 84 85 86 87 88
						|| c == tc_notglobmatch);
			} else {
				correct = check_field(c, value,
						atom->generic.comparewith);
				free(value);
			}
		}
89
		if (atom->negated)
90
			correct = !correct;
91
		if (correct) {
92 93 94
			atom = atom->nextiftrue;
		} else {
			atom = atom->nextiffalse;
95
			if (atom == NULL) {
96 97 98 99 100 101 102 103 104 105 106
				/* do not include */
				return RET_NOTHING;
			}
		}

	}
	/* do include */
	return RET_OK;
}

static retvalue parsestring(enum term_comparison c, const char *value, size_t len, struct compare_with *v) {
107
	if (c == tc_none) {
108 109 110 111 112 113 114
		fprintf(stderr,
"Error: Special formula predicates (those starting with '$') are always\n"
"defined, thus specifying them without parameter to compare against\n"
"makes not sense!\n");
		return RET_ERROR;
	}
	v->pointer = strndup(value, len);
115
	if (FAILEDTOALLOC(v->pointer))
116 117 118 119 120 121
		return RET_ERROR_OOM;
	return RET_OK;
}
// TODO: check for well-formed versions
#define parseversion parsestring

122 123
static bool comparesource(enum term_comparison c, const struct compare_with *v, void *d1, UNUSED(void *d2)) {
	struct package *package = d1;
124 125
	retvalue r;

126
	r = package_getsource(package);
127
	if (!RET_IS_OK(r))
128
		return false;
129
	return check_field(c, package->source, v->pointer);
130 131 132
}

static inline bool compare_dpkgversions(enum term_comparison c, const char *version, const char *param) {
133
	if (c != tc_globmatch && c != tc_notglobmatch) {
134 135 136 137
		int cmp;
		retvalue r;

		r = dpkgversions_cmp(version, param, &cmp);
138 139
		if (RET_IS_OK(r)) {
			if (cmp < 0)
140 141 142
				return c == tc_strictless
					|| c == tc_lessorequal
					|| c == tc_notequal;
143
			else if (cmp > 0)
144 145 146 147 148 149 150 151 152 153 154 155 156
				return c == tc_strictmore
					|| c == tc_moreorequal
					|| c == tc_notequal;
			else
				return c == tc_lessorequal
					|| c == tc_moreorequal
					|| c == tc_equal;
		} else
			return false;
	} else
		return check_field(c, version, param);
}

157 158
static bool compareversion(enum term_comparison c, const struct compare_with *v, void *d1, UNUSED(void *d2)) {
	struct package *package = d1;
159 160
	retvalue r;

161
	r = package_getversion(package);
162
	if (!RET_IS_OK(r))
163
		return false;
164
	return compare_dpkgversions(c, package->version, v->pointer);
165
}
166 167
static bool comparesourceversion(enum term_comparison c, const struct compare_with *v, void *d1, UNUSED(void *d2)) {
	struct package *package = d1;
168 169
	retvalue r;

170
	r = package_getsource(package);
171
	if (!RET_IS_OK(r))
172
		return false;
173
	return compare_dpkgversions(c, package->sourceversion, v->pointer);
174 175 176 177 178 179
}

static void freestring(UNUSED(enum term_comparison c), struct compare_with *d) {
	free(d->pointer);
}
static void freeatom(enum term_comparison c, struct compare_with *d) {
180
	if (c != tc_equal && c != tc_notequal)
181 182 183 184
		free(d->pointer);
}

static retvalue parsetype(enum term_comparison c, const char *value, size_t len, struct compare_with *v) {
185
	if (c == tc_none) {
186 187 188 189 190
		fprintf(stderr,
"Error: $Type is always defined, it does not make sense without parameter\n"
"to compare against!\n");
		return RET_ERROR;
	}
191
	if (c != tc_equal && c != tc_notequal) {
192
		v->pointer = strndup(value, len);
193
		if (FAILEDTOALLOC(v->pointer))
194 195 196 197
			return RET_ERROR_OOM;
		return RET_OK;
	}
	v->number = packagetype_find_l(value, len);
198
	if (atom_defined(v->number))
199 200 201 202 203 204 205
		return RET_OK;
	fprintf(stderr, "Unknown package type '%.*s' in formula!\n",
			(int)len, value);
	return RET_ERROR;
}

static retvalue parsearchitecture(enum term_comparison c, const char *value, size_t len, struct compare_with *v) {
206
	if (c == tc_none) {
207 208 209 210 211
		fprintf(stderr,
"Error: $Architecture is always defined, it does not make sense without parameter\n"
"to compare against!\n");
		return RET_ERROR;
	}
212
	if (c != tc_equal && c != tc_notequal) {
213
		v->pointer = strndup(value, len);
214
		if (FAILEDTOALLOC(v->pointer))
215 216 217 218
			return RET_ERROR_OOM;
		return RET_OK;
	}
	v->number = architecture_find_l(value, len);
219
	if (atom_defined(v->number))
220
		return RET_OK;
221 222
	fprintf(stderr,
"Unknown architecture '%.*s' in formula (must be listed in conf/distributions to be known)!\n",
223 224 225 226 227
			(int)len, value);
	return RET_ERROR;
}

static retvalue parsecomponent(enum term_comparison c, const char *value, size_t len, struct compare_with *v) {
228
	if (c == tc_none) {
229 230 231 232 233
		fprintf(stderr,
"Error: $Component is always defined, it does not make sense without parameter\n"
"to compare against!\n");
		return RET_ERROR;
	}
234
	if (c != tc_equal && c != tc_notequal) {
235
		v->pointer = strndup(value, len);
236
		if (FAILEDTOALLOC(v->pointer))
237 238 239 240
			return RET_ERROR_OOM;
		return RET_OK;
	}
	v->number = component_find_l(value, len);
241
	if (atom_defined(v->number))
242
		return RET_OK;
243 244 245
	fprintf(stderr,
"Unknown component '%.*s' in formula (must be listed in conf/distributions to be known)!\n",
			(int)len, value);
246 247 248
	return RET_ERROR;
}

249
static bool comparetype(enum term_comparison c, const struct compare_with *v, UNUSED( void *d1), void *d2) {
250 251
	const struct target *target = d2;

252 253 254 255
	if (c == tc_equal)
		return v->number == target->packagetype;
	else if (c == tc_notequal)
		return v->number != target->packagetype;
256 257
	else
		return check_field(c,
258
				atoms_packagetypes[target->packagetype],
259 260 261
				v->pointer);

}
262
static bool comparearchitecture(enum term_comparison c, const struct compare_with *v, UNUSED(void *d1), void *d2) {
263 264
	const struct target *target = d2;

265 266 267 268
	if (c == tc_equal)
		return v->number == target->architecture;
	else if (c == tc_notequal)
		return v->number != target->architecture;
269 270
	else
		return check_field(c,
271
				atoms_architectures[target->architecture],
272 273
				v->pointer);
}
274
static bool comparecomponent(enum term_comparison c, const struct compare_with *v, UNUSED(void *d1), void *d2) {
275 276
	const struct target *target = d2;

277 278 279 280
	if (c == tc_equal)
		return v->number == target->component;
	else if (c == tc_notequal)
		return v->number != target->component;
281 282
	else
		return check_field(c,
283
				atoms_components[target->component],
284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302
				v->pointer);
}

static struct term_special targetdecisionspecial[] = {
	{"$Source", parsestring, comparesource, freestring},
	{"$SourceVersion", parseversion, comparesourceversion, freestring},
	{"$Version", parseversion, compareversion, freestring},
	{"$Architecture", parsearchitecture, comparearchitecture, freeatom},
	{"$Component", parsecomponent, comparecomponent, freeatom},
	{"$Type", parsetype, comparetype, freeatom},
	{"$PackageType", parsetype, comparetype, freeatom},
	{NULL, NULL, NULL, NULL}
};

retvalue term_compilefortargetdecision(term **term_p, const char *formula) {
	return term_compile(term_p, formula,
		T_GLOBMATCH|T_OR|T_BRACKETS|T_NEGATION|T_VERSION|T_NOTEQUAL,
		targetdecisionspecial);
}