Files
ropkg/libropkg/version.c
T

494 lines
10 KiB
C
Raw Normal View History

2025-07-21 22:45:31 +01:00
#include "version.h"
#include <ctype.h>
2026-06-22 17:45:54 +01:00
#include <fx/stringstream.h>
2025-07-21 22:45:31 +01:00
#include <stdlib.h>
#include <string.h>
enum ropkg_status ropkg_version_create(struct ropkg_version **out)
{
struct ropkg_version *version = malloc(sizeof *version);
if (!version) {
return ROPKG_ERR_NO_MEMORY;
}
memset(version, 0x0, sizeof *version);
2026-06-22 17:45:54 +01:00
version->v_unref_phase = ROPKG_VERSION_RELEASE_PHASE_RELEASE;
2025-07-21 22:45:31 +01:00
version->v_upstream_version[0] = 1;
2026-06-22 17:45:54 +01:00
version->v_version_unref_phase = ROPKG_VERSION_RELEASE_PHASE_RELEASE;
version->v_version_unref_phase_revision = 1;
2025-07-21 22:45:31 +01:00
version->v_package_revision = 1;
*out = version;
return ROPKG_SUCCESS;
}
void ropkg_version_destroy(struct ropkg_version *version)
{
free(version);
}
2026-06-22 17:45:54 +01:00
static fx_result parse_unref_phase(
2025-07-21 22:45:31 +01:00
const char *s,
2026-06-22 17:45:54 +01:00
enum ropkg_version_unref_phase *out)
2025-07-21 22:45:31 +01:00
{
if (s[0] == 0) {
*out = ROPKG_VERSION_RELEASE_PHASE_RELEASE;
} else if (!strcmp(s, "alpha")) {
*out = ROPKG_VERSION_RELEASE_PHASE_ALPHA;
} else if (!strcmp(s, "beta")) {
*out = ROPKG_VERSION_RELEASE_PHASE_BETA;
} else {
2026-06-22 17:45:54 +01:00
fx_result result = fx_error_with_msg_template(
2025-07-21 22:45:31 +01:00
ROPKG_ERROR_VENDOR,
ROPKG_ERR_INVALID_VERSION_FORMAT,
ROPKG_EMSG_INVALID_RELEASE_PHASE,
2026-06-22 17:45:54 +01:00
FX_ERROR_PARAM("phase", s));
fx_error_add_submsg(
2025-07-21 22:45:31 +01:00
result,
2026-06-22 17:45:54 +01:00
FX_ERROR_SUBMSG_INFO,
2025-07-21 22:45:31 +01:00
ROPKG_EMSG_AVAILABLE_RELEASE_PHASES);
return result;
}
2026-06-22 17:45:54 +01:00
return FX_RESULT_SUCCESS;
2025-07-21 22:45:31 +01:00
}
2026-06-22 17:45:54 +01:00
static fx_result parse_version_unref_phase(
2025-07-21 22:45:31 +01:00
const char *s,
2026-06-22 17:45:54 +01:00
enum ropkg_version_unref_phase *out_phase,
2025-07-21 22:45:31 +01:00
unsigned int *out_revision,
size_t *nr_read)
{
size_t i = 0;
if (*s == '\0') {
*out_phase = ROPKG_VERSION_RELEASE_PHASE_RELEASE;
*out_revision = 1;
*nr_read = i;
2026-06-22 17:45:54 +01:00
return FX_RESULT_SUCCESS;
2025-07-21 22:45:31 +01:00
}
if (*s == '~') {
i++;
s++;
} else {
2026-06-22 17:45:54 +01:00
return fx_error_with_msg(
2025-07-21 22:45:31 +01:00
ROPKG_ERROR_VENDOR,
ROPKG_ERR_INVALID_VERSION_FORMAT,
ROPKG_EMSG_INVALID_VERSION_RELEASE_PHASE_UNKNOWN_START);
}
char temp[64];
size_t z = 0;
while (*s && isalpha(*s) && z < sizeof temp - 1) {
temp[z++] = tolower(*s);
temp[z] = 0;
i++;
s++;
}
if (!strcmp(temp, "alpha")) {
*out_phase = ROPKG_VERSION_RELEASE_PHASE_ALPHA;
} else if (!strcmp(temp, "beta")) {
*out_phase = ROPKG_VERSION_RELEASE_PHASE_BETA;
} else if (!strcmp(temp, "rc")) {
*out_phase = ROPKG_VERSION_RELEASE_PHASE_RC;
} else {
2026-06-22 17:45:54 +01:00
fx_result result = fx_error_with_msg_template(
2025-07-21 22:45:31 +01:00
ROPKG_ERROR_VENDOR,
ROPKG_ERR_INVALID_VERSION_FORMAT,
ROPKG_EMSG_INVALID_VERSION_RELEASE_PHASE,
2026-06-22 17:45:54 +01:00
FX_ERROR_PARAM("phase", temp));
fx_error_add_submsg(
2025-07-21 22:45:31 +01:00
result,
2026-06-22 17:45:54 +01:00
FX_ERROR_SUBMSG_INFO,
2025-07-21 22:45:31 +01:00
ROPKG_EMSG_AVAILABLE_VERSION_RELEASE_PHASES);
return result;
}
z = 0;
while (*s && isdigit(*s)) {
temp[z++] = *s;
temp[z] = 0;
i++;
s++;
}
if (z > 0) {
char *ep;
*out_revision = strtoul(temp, &ep, 10);
}
*nr_read = i;
2026-06-22 17:45:54 +01:00
return FX_RESULT_SUCCESS;
2025-07-21 22:45:31 +01:00
}
2026-06-22 17:45:54 +01:00
static fx_result parse_package_revision(
2025-07-21 22:45:31 +01:00
const char *s,
unsigned int *out_revision,
size_t *nr_read)
{
size_t i = 0;
if (*s == '\0') {
*out_revision = 1;
*nr_read = i;
2026-06-22 17:45:54 +01:00
return FX_RESULT_SUCCESS;
2025-07-21 22:45:31 +01:00
}
if (*s == '-') {
i++;
s++;
} else {
2026-06-22 17:45:54 +01:00
return fx_error_with_msg(
2025-07-21 22:45:31 +01:00
ROPKG_ERROR_VENDOR,
ROPKG_ERR_INVALID_VERSION_FORMAT,
ROPKG_EMSG_INVALID_PACKAGE_REVISION_UNKNOWN_START);
}
char temp[64];
size_t z = 0;
while (*s && isdigit(*s) && z < sizeof temp - 1) {
temp[z++] = *s;
temp[z] = 0;
i++;
s++;
}
char *ep;
*out_revision = strtoul(temp, &ep, 10);
*nr_read = i;
2026-06-22 17:45:54 +01:00
return FX_RESULT_SUCCESS;
2025-07-21 22:45:31 +01:00
}
2026-06-22 17:45:54 +01:00
static fx_result parse_upstream_version(
2025-07-21 22:45:31 +01:00
const char *s,
unsigned int out[ROPKG_VERSION_UPSTREAM_DIGIT_MAX],
size_t *nr_read)
{
if (!isdigit(*s)) {
2026-06-22 17:45:54 +01:00
return fx_error_with_msg(
2025-07-21 22:45:31 +01:00
ROPKG_ERROR_VENDOR,
ROPKG_ERR_INVALID_VERSION_FORMAT,
ROPKG_EMSG_INVALID_UPSTREAM_VERSION_UNKNOWN_START);
}
char temp[64];
size_t i = 0;
unsigned int nr_temp = 0;
unsigned int nr_digits = 0;
char last = 0;
char *ep;
while (1) {
last = s[i];
if (isdigit(s[i])) {
temp[nr_temp++] = s[i];
temp[nr_temp] = 0;
i++;
continue;
}
if (s[i] == '.') {
if (nr_temp == 0) {
2026-06-22 17:45:54 +01:00
return fx_error_with_msg(
2025-07-21 22:45:31 +01:00
ROPKG_ERROR_VENDOR,
ROPKG_ERR_INVALID_VERSION_FORMAT,
ROPKG_EMSG_INVALID_UPSTREAM_VERSION_TWO_DOTS);
}
if (nr_digits >= ROPKG_VERSION_UPSTREAM_DIGIT_MAX - 1) {
2026-06-22 17:45:54 +01:00
return fx_error_with_msg(
2025-07-21 22:45:31 +01:00
ROPKG_ERROR_VENDOR,
ROPKG_ERR_INVALID_VERSION_FORMAT,
ROPKG_EMSG_INVALID_UPSTREAM_VERSION_TOO_MANY_NUMBERS);
}
out[nr_digits] = strtoul(temp, &ep, 10);
nr_digits++;
nr_temp = 0;
i++;
continue;
}
if (nr_temp == 0) {
2026-06-22 17:45:54 +01:00
return fx_error_with_msg(
2025-07-21 22:45:31 +01:00
ROPKG_ERROR_VENDOR,
ROPKG_ERR_INVALID_VERSION_FORMAT,
ROPKG_EMSG_INVALID_UPSTREAM_VERSION_END_DOT);
}
out[nr_digits] = strtoul(temp, &ep, 10);
nr_digits++;
nr_temp = 0;
break;
}
*nr_read = i;
2026-06-22 17:45:54 +01:00
return FX_RESULT_SUCCESS;
2025-07-21 22:45:31 +01:00
}
2026-06-22 17:45:54 +01:00
fx_result ropkg_version_parse(struct ropkg_version *version, const char *s)
2025-07-21 22:45:31 +01:00
{
2026-06-22 17:45:54 +01:00
enum ropkg_version_unref_phase release_phase
2025-07-21 22:45:31 +01:00
= ROPKG_VERSION_RELEASE_PHASE_RELEASE;
unsigned int upstream_version[ROPKG_VERSION_UPSTREAM_DIGIT_MAX]
= {0, 0, 0, 0, 0};
2026-06-22 17:45:54 +01:00
enum ropkg_version_unref_phase version_unref_phase
2025-07-21 22:45:31 +01:00
= ROPKG_VERSION_RELEASE_PHASE_RELEASE;
2026-06-22 17:45:54 +01:00
unsigned int version_unref_phase_revision = 1;
2025-07-21 22:45:31 +01:00
unsigned int package_revision = 1;
2026-06-22 17:45:54 +01:00
fx_result result = FX_RESULT_SUCCESS;
2025-07-21 22:45:31 +01:00
char temp[64] = {0};
size_t z = 0;
while (*s && !isdigit(*s) && z < sizeof temp - 1) {
temp[z] = tolower(*s);
z++;
s++;
}
2026-06-22 17:45:54 +01:00
result = parse_unref_phase(temp, &release_phase);
if (fx_result_is_error(result)) {
return fx_result_propagate(result);
2025-07-21 22:45:31 +01:00
}
size_t nr_read = 0;
result = parse_upstream_version(s, upstream_version, &nr_read);
2026-06-22 17:45:54 +01:00
if (fx_result_is_error(result)) {
return fx_result_propagate(result);
2025-07-21 22:45:31 +01:00
}
s += nr_read;
if (*s == '~' || *s == 0) {
2026-06-22 17:45:54 +01:00
result = parse_version_unref_phase(
2025-07-21 22:45:31 +01:00
s,
2026-06-22 17:45:54 +01:00
&version_unref_phase,
&version_unref_phase_revision,
2025-07-21 22:45:31 +01:00
&nr_read);
s += nr_read;
2026-06-22 17:45:54 +01:00
if (fx_result_is_error(result)) {
return fx_result_propagate(result);
2025-07-21 22:45:31 +01:00
}
}
if (*s == '-' || *s == 0) {
result = parse_package_revision(s, &package_revision, &nr_read);
s += nr_read;
2026-06-22 17:45:54 +01:00
if (fx_result_is_error(result)) {
return fx_result_propagate(result);
2025-07-21 22:45:31 +01:00
}
}
if (*s != 0) {
2026-06-22 17:45:54 +01:00
return fx_error_with_msg(
2025-07-21 22:45:31 +01:00
ROPKG_ERROR_VENDOR,
ROPKG_ERR_INVALID_VERSION_FORMAT,
ROPKG_EMSG_INVALID_VERSION_UNKNOWN_END);
}
2026-06-22 17:45:54 +01:00
version->v_unref_phase = release_phase;
2025-07-21 22:45:31 +01:00
memcpy(version->v_upstream_version,
upstream_version,
sizeof upstream_version);
2026-06-22 17:45:54 +01:00
version->v_version_unref_phase = version_unref_phase;
version->v_version_unref_phase_revision = version_unref_phase_revision;
2025-07-21 22:45:31 +01:00
version->v_package_revision = package_revision;
2026-06-22 17:45:54 +01:00
return FX_RESULT_SUCCESS;
2025-07-21 22:45:31 +01:00
}
2026-06-22 17:45:54 +01:00
void ropkg_version_set_unref_phase(
2025-07-21 22:45:31 +01:00
struct ropkg_version *version,
2026-06-22 17:45:54 +01:00
enum ropkg_version_unref_phase phase)
2025-07-21 22:45:31 +01:00
{
if (phase == ROPKG_VERSION_RELEASE_PHASE_RC) {
return;
}
2026-06-22 17:45:54 +01:00
version->v_unref_phase = phase;
2025-07-21 22:45:31 +01:00
}
void ropkg_version_set_upstream_version(
struct ropkg_version *version,
const unsigned int upstream_version[ROPKG_VERSION_UPSTREAM_DIGIT_MAX])
{
memcpy(version->v_upstream_version,
upstream_version,
sizeof version->v_upstream_version);
}
2026-06-22 17:45:54 +01:00
void ropkg_version_set_version_unref_phase(
2025-07-21 22:45:31 +01:00
struct ropkg_version *version,
2026-06-22 17:45:54 +01:00
enum ropkg_version_unref_phase phase,
2025-07-21 22:45:31 +01:00
unsigned int revision)
{
2026-06-22 17:45:54 +01:00
version->v_version_unref_phase = phase;
2025-07-21 22:45:31 +01:00
if (phase == ROPKG_VERSION_RELEASE_PHASE_RELEASE) {
2026-06-22 17:45:54 +01:00
version->v_version_unref_phase_revision = 1;
2025-07-21 22:45:31 +01:00
} else {
2026-06-22 17:45:54 +01:00
version->v_version_unref_phase_revision = revision;
2025-07-21 22:45:31 +01:00
}
}
void ropkg_version_set_package_revision(
struct ropkg_version *version,
unsigned int revision)
{
version->v_package_revision = revision;
}
2026-06-22 17:45:54 +01:00
static const char *release_phase_to_string(enum ropkg_version_unref_phase phase)
2025-07-21 22:45:31 +01:00
{
switch (phase) {
case ROPKG_VERSION_RELEASE_PHASE_ALPHA:
return "alpha";
case ROPKG_VERSION_RELEASE_PHASE_BETA:
return "beta";
case ROPKG_VERSION_RELEASE_PHASE_RC:
return "rc";
default:
return "";
}
}
static unsigned int upstream_version_length(
const unsigned int version[ROPKG_VERSION_UPSTREAM_DIGIT_MAX])
{
unsigned int length = 0;
for (unsigned int i = 0; i < ROPKG_VERSION_UPSTREAM_DIGIT_MAX; i++) {
if (version[i] != 0) {
length = i + 1;
}
}
if (length < 2) {
length = 2;
}
return length;
}
size_t ropkg_version_to_string(
const struct ropkg_version *version,
char *out,
size_t max)
{
2026-06-22 17:45:54 +01:00
fx_stringstream *str = fx_stringstream_create_with_buffer(out, max);
2025-07-21 22:45:31 +01:00
2026-06-22 17:45:54 +01:00
fx_stream_write_cstr(
str,
release_phase_to_string(version->v_unref_phase),
NULL);
2025-07-21 22:45:31 +01:00
unsigned int version_length
= upstream_version_length(version->v_upstream_version);
for (unsigned int i = 0; i < version_length; i++) {
if (i > 0) {
2026-06-22 17:45:54 +01:00
fx_stream_write_char(str, '.');
2025-07-21 22:45:31 +01:00
}
2026-06-22 17:45:54 +01:00
fx_stream_write_fmt(
str,
NULL,
"%u",
version->v_upstream_version[i]);
2025-07-21 22:45:31 +01:00
}
2026-06-22 17:45:54 +01:00
if (version->v_version_unref_phase
2025-07-21 22:45:31 +01:00
!= ROPKG_VERSION_RELEASE_PHASE_RELEASE) {
2026-06-22 17:45:54 +01:00
fx_stream_write_fmt(
str,
NULL,
2025-07-21 22:45:31 +01:00
"~%s",
release_phase_to_string(
2026-06-22 17:45:54 +01:00
version->v_version_unref_phase));
2025-07-21 22:45:31 +01:00
2026-06-22 17:45:54 +01:00
if (version->v_version_unref_phase_revision > 1
|| version->v_version_unref_phase
2025-07-21 22:45:31 +01:00
== ROPKG_VERSION_RELEASE_PHASE_RC) {
2026-06-22 17:45:54 +01:00
fx_stream_write_fmt(
str,
NULL,
2025-07-21 22:45:31 +01:00
"%u",
2026-06-22 17:45:54 +01:00
version->v_version_unref_phase_revision);
2025-07-21 22:45:31 +01:00
}
}
if (version->v_package_revision > 1) {
2026-06-22 17:45:54 +01:00
fx_stream_write_fmt(
str,
NULL,
"-%u",
version->v_package_revision);
2025-07-21 22:45:31 +01:00
}
2026-06-22 17:45:54 +01:00
size_t written = fx_stringstream_get_length(str);
fx_stringstream_unref(str);
return written;
2025-07-21 22:45:31 +01:00
}
static int version_compare(
const struct ropkg_version *left,
const struct ropkg_version *right)
{
#define COMPARE(left, right, item) \
do { \
if ((left)->item < (right)->item) \
return -1; \
else if ((left)->item > (right)->item) \
return 1; \
} while (0)
2026-06-22 17:45:54 +01:00
COMPARE(left, right, v_unref_phase);
2025-07-21 22:45:31 +01:00
for (int i = 0; i < ROPKG_VERSION_UPSTREAM_DIGIT_MAX; i++) {
COMPARE(left, right, v_upstream_version[i]);
}
2026-06-22 17:45:54 +01:00
COMPARE(left, right, v_version_unref_phase);
COMPARE(left, right, v_version_unref_phase_revision);
2025-07-21 22:45:31 +01:00
COMPARE(left, right, v_package_revision);
#undef COMPARE
return 0;
}
bool ropkg_version_compare(
const struct ropkg_version *left,
const struct ropkg_version *right,
enum ropkg_comparison_op op)
{
int result = version_compare(left, right);
switch (op) {
case ROPKG_OP_EQUAL:
return result == 0;
case ROPKG_OP_NOT_EQUAL:
return result != 0;
case ROPKG_OP_LESS_THAN:
return result < 0;
case ROPKG_OP_LESS_EQUAL:
return result <= 0;
case ROPKG_OP_GREATER_THAN:
return result > 0;
case ROPKG_OP_GREATER_EQUAL:
return result >= 0;
default:
return false;
}
}