version.c 5.51 KB
Newer Older
1 2 3
/* version.c - Alpine Package Keeper (APK)
 *
 * Copyright (C) 2005-2008 Natanael Copa <n@tanael.org>
4
 * Copyright (C) 2008-2011 Timo Teräs <timo.teras@iki.fi>
5 6
 * All rights reserved.
 *
7
 * This program is free software; you can redistribute it and/or modify it
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
 * under the terms of the GNU General Public License version 2 as published
 * by the Free Software Foundation. See http://www.gnu.org/ for details.
 */
#include <stdio.h>

#include <ctype.h>
#include "apk_defines.h"
#include "apk_version.h"

/* Gentoo version: {digit}{.digit}...{letter}{_suf{#}}...{-r#} */

enum PARTS {
	TOKEN_INVALID = -1,
	TOKEN_DIGIT_OR_ZERO,
	TOKEN_DIGIT,
	TOKEN_LETTER,
	TOKEN_SUFFIX,
	TOKEN_SUFFIX_NO,
	TOKEN_REVISION_NO,
	TOKEN_END,
};

static void next_token(int *type, apk_blob_t *blob)
{
	int n = TOKEN_INVALID;

	if (blob->len == 0 || blob->ptr[0] == 0) {
		n = TOKEN_END;
36 37
	} else if ((*type == TOKEN_DIGIT || *type == TOKEN_DIGIT_OR_ZERO) &&
	           islower(blob->ptr[0])) {
38
		n = TOKEN_LETTER;
39 40
	} else if (*type == TOKEN_LETTER && isdigit(blob->ptr[0])) {
		n = TOKEN_DIGIT;
41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
	} else if (*type == TOKEN_SUFFIX && isdigit(blob->ptr[0])) {
		n = TOKEN_SUFFIX_NO;
	} else {
		switch (blob->ptr[0]) {
		case '.':
			n = TOKEN_DIGIT_OR_ZERO;
			break;
		case '_':
			n = TOKEN_SUFFIX;
			break;
		case '-':
			if (blob->len > 1 && blob->ptr[1] == 'r') {
				n = TOKEN_REVISION_NO;
				blob->ptr++;
				blob->len--;
			} else
				n = TOKEN_INVALID;
			break;
		}
		blob->ptr++;
		blob->len--;
	}

	if (n < *type) {
		if (! ((n == TOKEN_DIGIT_OR_ZERO && *type == TOKEN_DIGIT) ||
66 67
		       (n == TOKEN_SUFFIX && *type == TOKEN_SUFFIX_NO) ||
		       (n == TOKEN_DIGIT && *type == TOKEN_LETTER)))
68 69 70 71 72 73 74 75
			n = TOKEN_INVALID;
	}
	*type = n;
}

static int get_token(int *type, apk_blob_t *blob)
{
	static const char *pre_suffixes[] = { "alpha", "beta", "pre", "rc" };
76
	static const char *post_suffixes[] = { "cvs", "svn", "git", "hg", "p" };
77 78
	int v = 0, i = 0, nt = TOKEN_INVALID;

79 80 81 82 83
	if (blob->len <= 0) {
		*type = TOKEN_END;
		return 0;
	}

84 85 86 87
	switch (*type) {
	case TOKEN_DIGIT_OR_ZERO:
		/* Leading zero digits get a special treatment */
		if (blob->ptr[i] == '0') {
88
			while (i < blob->len && blob->ptr[i] == '0')
89 90 91 92 93 94 95 96
				i++;
			nt = TOKEN_DIGIT;
			v = -i;
			break;
		}
	case TOKEN_DIGIT:
	case TOKEN_SUFFIX_NO:
	case TOKEN_REVISION_NO:
97
		while (i < blob->len && isdigit(blob->ptr[i])) {
98 99 100 101 102 103 104 105 106 107
			v *= 10;
			v += blob->ptr[i++] - '0';
		}
		break;
	case TOKEN_LETTER:
		v = blob->ptr[i++];
		break;
	case TOKEN_SUFFIX:
		for (v = 0; v < ARRAY_SIZE(pre_suffixes); v++) {
			i = strlen(pre_suffixes[v]);
108
			if (i <= blob->len &&
109 110 111 112 113 114 115
			    strncmp(pre_suffixes[v], blob->ptr, i) == 0)
				break;
		}
		if (v < ARRAY_SIZE(pre_suffixes)) {
			v = v - ARRAY_SIZE(pre_suffixes);
			break;
		}
116 117 118 119 120
		for (v = 0; v < ARRAY_SIZE(post_suffixes); v++) {
			i = strlen(post_suffixes[v]);
			if (i <= blob->len &&
			    strncmp(post_suffixes[v], blob->ptr, i) == 0)
				break;
121
		}
122 123
		if (v < ARRAY_SIZE(post_suffixes))
			break;
124 125 126 127 128 129 130
		/* fallthrough: invalid suffix */
	default:
		*type = TOKEN_INVALID;
		return -1;
	}
	blob->ptr += i;
	blob->len -= i;
131 132 133
	if (blob->len == 0)
		*type = TOKEN_END;
	else if (nt != TOKEN_INVALID)
134 135 136 137 138 139 140
		*type = nt;
        else
		next_token(type, blob);

	return v;
}

141 142 143 144 145 146 147
const char *apk_version_op_string(int mask)
{
	switch (mask) {
	case APK_VERSION_LESS:
		return "<";
	case APK_VERSION_LESS|APK_VERSION_EQUAL:
		return "<=";
148 149 150
	case APK_VERSION_EQUAL|APK_VERSION_FUZZY:
	case APK_VERSION_FUZZY:
		return "~";
151 152 153 154 155 156
	case APK_VERSION_EQUAL:
		return "=";
	case APK_VERSION_GREATER|APK_VERSION_EQUAL:
		return ">=";
	case APK_VERSION_GREATER:
		return ">";
157 158
	case APK_DEPMASK_CHECKSUM:
		return "><";
159 160 161 162 163
	default:
		return "?";
	}
}

164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179
int apk_version_result_mask(const char *str)
{
	int r = 0;
	switch (*str) {
	case '<':
		r = APK_VERSION_LESS;
		str++;
		break;
	case '>':
		r = APK_VERSION_GREATER;
		str++;
		break;
	}
	if (*str ==  '=')
		r |= APK_VERSION_EQUAL;
	return r;
180
}
181

182 183 184 185 186 187 188 189 190 191
int apk_version_validate(apk_blob_t ver)
{
	int t = TOKEN_DIGIT;

	while (t != TOKEN_END && t != TOKEN_INVALID)
		get_token(&t, &ver);

	return t == TOKEN_END;
}

192
int apk_version_compare_blob_fuzzy(apk_blob_t a, apk_blob_t b, int fuzzy)
193
{
194
	int at = TOKEN_DIGIT, bt = TOKEN_DIGIT, tt;
195 196
	int av = 0, bv = 0;

197 198
	if (APK_BLOB_IS_NULL(a) || APK_BLOB_IS_NULL(b)) {
		if (APK_BLOB_IS_NULL(a) && APK_BLOB_IS_NULL(b))
199 200 201 202
			return APK_VERSION_EQUAL;
		return APK_VERSION_EQUAL | APK_VERSION_GREATER | APK_VERSION_LESS;
	}

203
	while (at == bt && at != TOKEN_END && at != TOKEN_INVALID && av == bv) {
204 205 206 207 208 209 210 211 212 213 214 215 216 217 218
		av = get_token(&at, &a);
		bv = get_token(&bt, &b);
#if 0
		fprintf(stderr,
			"av=%d, at=%d, a.len=%d\n"
			"bv=%d, bt=%d, b.len=%d\n",
			av, at, a.len, bv, bt, b.len);
#endif
	}

	/* value of this token differs? */
	if (av < bv)
		return APK_VERSION_LESS;
	if (av > bv)
		return APK_VERSION_GREATER;
219

220
	/* both have TOKEN_END or TOKEN_INVALID next? */
221
	if (at == bt || fuzzy)
222
		return APK_VERSION_EQUAL;
223 224 225 226

	/* leading version components and their values are equal,
	 * now the non-terminating version is greater unless it's a suffix
	 * indicating pre-release */
227 228
	tt = at;
	if (at == TOKEN_SUFFIX && get_token(&tt, &a) < 0)
229
		return APK_VERSION_LESS;
230 231
	tt = bt;
	if (bt == TOKEN_SUFFIX && get_token(&tt, &b) < 0)
232
		return APK_VERSION_GREATER;
233
	if (at > bt)
234
		return APK_VERSION_LESS;
235
	if (bt > at)
236 237 238
		return APK_VERSION_GREATER;

	return APK_VERSION_EQUAL;
239
}
240

241 242 243 244 245
int apk_version_compare_blob(apk_blob_t a, apk_blob_t b)
{
	return apk_version_compare_blob_fuzzy(a, b, FALSE);
}

246 247 248 249
int apk_version_compare(const char *str1, const char *str2)
{
	return apk_version_compare_blob(APK_BLOB_STR(str1), APK_BLOB_STR(str2));
}