return 0;
}
-/* This is a direct translation of str_verscmp from boot.c */
-static bool is_digit(int c) {
- return c >= '0' && c <= '9';
-}
-
-static int c_order(int c) {
- if (c == '\0')
- return 0;
- if (is_digit(c))
- return 0;
- else if ((c >= 'a') && (c <= 'z'))
- return c;
- else
- return c + 0x10000;
-}
-
-static int str_verscmp(const char *s1, const char *s2) {
- const char *os1 = s1;
- const char *os2 = s2;
-
- while (*s1 || *s2) {
- int first;
-
- while ((*s1 && !is_digit(*s1)) || (*s2 && !is_digit(*s2))) {
- int order;
-
- order = c_order(*s1) - c_order(*s2);
- if (order)
- return order;
- s1++;
- s2++;
- }
-
- while (*s1 == '0')
- s1++;
- while (*s2 == '0')
- s2++;
-
- first = 0;
- while (is_digit(*s1) && is_digit(*s2)) {
- if (first == 0)
- first = *s1 - *s2;
- s1++;
- s2++;
- }
-
- if (is_digit(*s1))
- return 1;
- if (is_digit(*s2))
- return -1;
-
- if (first != 0)
- return first;
- }
-
- return strcmp(os1, os2);
-}
-
static int boot_entry_compare(const void *a, const void *b) {
- const BootEntry *aa = a;
- const BootEntry *bb = b;
+ const BootEntry *aa = a, *bb = b;
return str_verscmp(aa->filename, bb->filename);
}
}
static int condition_test_kernel_version(Condition *c) {
+ enum {
+ /* Listed in order of checking. Note that some comparators are prefixes of others, hence the longest
+ * should be listed first. */
+ LOWER_OR_EQUAL,
+ GREATER_OR_EQUAL,
+ LOWER,
+ GREATER,
+ EQUAL,
+ _ORDER_MAX,
+ };
+
+ static const char *const prefix[_ORDER_MAX] = {
+ [LOWER_OR_EQUAL] = "<=",
+ [GREATER_OR_EQUAL] = ">=",
+ [LOWER] = "<",
+ [GREATER] = ">",
+ [EQUAL] = "=",
+ };
+ const char *p = NULL;
struct utsname u;
+ size_t i;
+ int k;
assert(c);
assert(c->parameter);
assert_se(uname(&u) >= 0);
- return fnmatch(c->parameter, u.release, 0) == 0;
+ for (i = 0; i < _ORDER_MAX; i++) {
+ p = startswith(c->parameter, prefix[i]);
+ if (p)
+ break;
+ }
+
+ /* No prefix? Then treat as glob string */
+ if (!p)
+ return fnmatch(skip_leading_chars(c->parameter, NULL), u.release, 0) == 0;
+
+ k = str_verscmp(u.release, skip_leading_chars(p, NULL));
+
+ switch (i) {
+
+ case LOWER:
+ return k < 0;
+
+ case LOWER_OR_EQUAL:
+ return k <= 0;
+
+ case EQUAL:
+ return k == 0;
+
+ case GREATER_OR_EQUAL:
+ return k >= 0;
+
+ case GREATER:
+ return k > 0;
+
+ default:
+ assert_not_reached("Can't compare");
+ }
}
static int condition_test_user(Condition *c) {
static void test_condition_test_kernel_version(void) {
Condition *condition;
struct utsname u;
+ const char *v;
condition = condition_new(CONDITION_KERNEL_VERSION, "*thisreallyshouldntbeinthekernelversion*", false, false);
assert_se(condition);
assert_se(condition);
assert_se(condition_test(condition));
condition_free(condition);
+
+ /* 0.1.2 would be a very very very old kernel */
+ condition = condition_new(CONDITION_KERNEL_VERSION, "> 0.1.2", false, false);
+ assert_se(condition);
+ assert_se(condition_test(condition));
+ condition_free(condition);
+
+ condition = condition_new(CONDITION_KERNEL_VERSION, ">= 0.1.2", false, false);
+ assert_se(condition);
+ assert_se(condition_test(condition));
+ condition_free(condition);
+
+ condition = condition_new(CONDITION_KERNEL_VERSION, "< 0.1.2", false, false);
+ assert_se(condition);
+ assert_se(!condition_test(condition));
+ condition_free(condition);
+
+ condition = condition_new(CONDITION_KERNEL_VERSION, "<= 0.1.2", false, false);
+ assert_se(condition);
+ assert_se(!condition_test(condition));
+ condition_free(condition);
+
+ condition = condition_new(CONDITION_KERNEL_VERSION, "= 0.1.2", false, false);
+ assert_se(condition);
+ assert_se(!condition_test(condition));
+ condition_free(condition);
+
+ /* 4711.8.15 is a very very very future kernel */
+ condition = condition_new(CONDITION_KERNEL_VERSION, "< 4711.8.15", false, false);
+ assert_se(condition);
+ assert_se(condition_test(condition));
+ condition_free(condition);
+
+ condition = condition_new(CONDITION_KERNEL_VERSION, "<= 4711.8.15", false, false);
+ assert_se(condition);
+ assert_se(condition_test(condition));
+ condition_free(condition);
+
+ condition = condition_new(CONDITION_KERNEL_VERSION, "= 4711.8.15", false, false);
+ assert_se(condition);
+ assert_se(!condition_test(condition));
+ condition_free(condition);
+
+ condition = condition_new(CONDITION_KERNEL_VERSION, "> 4711.8.15", false, false);
+ assert_se(condition);
+ assert_se(!condition_test(condition));
+ condition_free(condition);
+
+ condition = condition_new(CONDITION_KERNEL_VERSION, ">= 4711.8.15", false, false);
+ assert_se(condition);
+ assert_se(!condition_test(condition));
+ condition_free(condition);
+
+ assert_se(uname(&u) >= 0);
+
+ v = strjoina(">=", u.release);
+ condition = condition_new(CONDITION_KERNEL_VERSION, v, false, false);
+ assert_se(condition);
+ assert_se(condition_test(condition));
+ condition_free(condition);
+
+ v = strjoina("= ", u.release);
+ condition = condition_new(CONDITION_KERNEL_VERSION, v, false, false);
+ assert_se(condition);
+ assert_se(condition_test(condition));
+ condition_free(condition);
+
+ v = strjoina("<=", u.release);
+ condition = condition_new(CONDITION_KERNEL_VERSION, v, false, false);
+ assert_se(condition);
+ assert_se(condition_test(condition));
+ condition_free(condition);
+
+ v = strjoina("> ", u.release);
+ condition = condition_new(CONDITION_KERNEL_VERSION, v, false, false);
+ assert_se(condition);
+ assert_se(!condition_test(condition));
+ condition_free(condition);
+
+ v = strjoina("< ", u.release);
+ condition = condition_new(CONDITION_KERNEL_VERSION, v, false, false);
+ assert_se(condition);
+ assert_se(!condition_test(condition));
+ condition_free(condition);
}
static void test_condition_test_null(void) {