git mirror - github.com/owenewans/holy - branch master
clone: https://src.holypkg.eu/holy/

file src/version.c

#include "version.h"

#include <stddef.h>
#include <string.h>

struct part { const char *start, *end; };
struct version { struct part core, pre; int prerelease; };

static int digit(unsigned char c) { return c >= '0' && c <= '9'; }
static int letter(unsigned char c) { return c >= 'a' && c <= 'z'; }

static int parse(const char *text, struct version *out)
{
    const char *p, *start, *end;
    if (!text || !*text) return 0;
    for (end = text; *end; ++end) if (end - text >= 65536) return 0;
    p = text;
    do {
        start = p;
        while (digit((unsigned char)*p)) ++p;
        if (p == start) return 0;
        if (*p != '.') break;
        ++p;
    } while (1);
    out->core = (struct part){text, p};
    out->prerelease = *p == '-' || *p == '~';
    out->pre = (struct part){end, end};
    if (out->prerelease) {
        ++p;
        out->pre.start = p;
        do {
            start = p;
            while (digit((unsigned char)*p) || letter((unsigned char)*p)) ++p;
            if (p == start) return 0;
            if (*p != '.' && *p != '-') break;
            ++p;
        } while (1);
        out->pre.end = p;
    }
    return !*p;
}

static int number(struct part a, struct part b)
{
    size_t x, y;
    while (a.start < a.end && *a.start == '0') ++a.start;
    while (b.start < b.end && *b.start == '0') ++b.start;
    x = (size_t)(a.end - a.start);
    y = (size_t)(b.end - b.start);
    if (x != y) return x < y ? -1 : 1;
    if (x) {
        int compared = memcmp(a.start, b.start, x);
        if (compared) return compared < 0 ? -1 : 1;
    }
    return 0;
}

static int core(struct part a, struct part b)
{
    const char *x = a.start, *y = b.start;
    while (x < a.end || y < b.end) {
        static const char zero[] = "0";
        struct part left = {zero, zero + 1}, right = {zero, zero + 1};
        int order;
        if (x < a.end) {
            left.start = x;
            while (x < a.end && *x != '.') ++x;
            left.end = x;
            if (x < a.end) ++x;
        }
        if (y < b.end) {
            right.start = y;
            while (y < b.end && *y != '.') ++y;
            right.end = y;
            if (y < b.end) ++y;
        }
        order = number(left, right);
        if (order) return order;
    }
    return 0;
}

static int identifier(struct part a, struct part b)
{
    const char *x = a.start, *y = b.start;
    while (x < a.end && y < b.end) {
        struct part left, right;
        int numeric = digit((unsigned char)*x), order;
        if (numeric != digit((unsigned char)*y)) return numeric ? -1 : 1;
        left.start = x; right.start = y;
        while (x < a.end && (numeric ? digit((unsigned char)*x) : letter((unsigned char)*x))) ++x;
        while (y < b.end && (numeric ? digit((unsigned char)*y) : letter((unsigned char)*y))) ++y;
        left.end = x; right.end = y;
        if (numeric) order = number(left, right);
        else {
            size_t lx = (size_t)(x - left.start), ly = (size_t)(y - right.start);
            int compared = memcmp(left.start, right.start, lx < ly ? lx : ly);
            order = compared < 0 ? -1 : compared > 0 ? 1 :
                    lx < ly ? -1 : lx > ly ? 1 : 0;
        }
        if (order) return order;
    }
    return x == a.end && y == b.end ? 0 : x == a.end ? -1 : 1;
}

static int prerelease(struct part a, struct part b)
{
    const char *x = a.start, *y = b.start;
    while (x < a.end && y < b.end) {
        struct part left, right;
        int order;
        left.start = x; right.start = y;
        while (x < a.end && *x != '.' && *x != '-') ++x;
        while (y < b.end && *y != '.' && *y != '-') ++y;
        left.end = x; right.end = y;
        order = identifier(left, right);
        if (order) return order;
        if (x < a.end) ++x;
        if (y < b.end) ++y;
    }
    return x == a.end && y == b.end ? 0 : x == a.end ? -1 : 1;
}

int holy_version_compare(const char *left, const char *right, int *order)
{
    struct version a, b;
    if (!order || !parse(left, &a) || !parse(right, &b)) return 0;
    *order = core(a.core, b.core);
    if (!*order && a.prerelease != b.prerelease)
        *order = a.prerelease ? -1 : 1;
    if (!*order && a.prerelease)
        *order = prerelease(a.pre, b.pre);
    return 1;
}