484 lines
10 KiB
C
484 lines
10 KiB
C
#include "version.h"
|
|
|
|
#include <blue/core/stringstream.h>
|
|
#include <ctype.h>
|
|
#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);
|
|
|
|
version->v_release_phase = ROPKG_VERSION_RELEASE_PHASE_RELEASE;
|
|
version->v_upstream_version[0] = 1;
|
|
version->v_version_release_phase = ROPKG_VERSION_RELEASE_PHASE_RELEASE;
|
|
version->v_version_release_phase_revision = 1;
|
|
version->v_package_revision = 1;
|
|
|
|
*out = version;
|
|
return ROPKG_SUCCESS;
|
|
}
|
|
|
|
void ropkg_version_destroy(struct ropkg_version *version)
|
|
{
|
|
free(version);
|
|
}
|
|
|
|
static b_result parse_release_phase(
|
|
const char *s,
|
|
enum ropkg_version_release_phase *out)
|
|
{
|
|
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 {
|
|
b_result result = b_error_with_msg_template(
|
|
ROPKG_ERROR_VENDOR,
|
|
ROPKG_ERR_INVALID_VERSION_FORMAT,
|
|
ROPKG_EMSG_INVALID_RELEASE_PHASE,
|
|
B_ERROR_PARAM("phase", s));
|
|
b_error_add_submsg(
|
|
result,
|
|
B_ERROR_SUBMSG_INFO,
|
|
ROPKG_EMSG_AVAILABLE_RELEASE_PHASES);
|
|
return result;
|
|
}
|
|
|
|
return B_RESULT_SUCCESS;
|
|
}
|
|
|
|
static b_result parse_version_release_phase(
|
|
const char *s,
|
|
enum ropkg_version_release_phase *out_phase,
|
|
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;
|
|
return B_RESULT_SUCCESS;
|
|
}
|
|
|
|
if (*s == '~') {
|
|
i++;
|
|
s++;
|
|
} else {
|
|
return b_error_with_msg(
|
|
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 {
|
|
b_result result = b_error_with_msg_template(
|
|
ROPKG_ERROR_VENDOR,
|
|
ROPKG_ERR_INVALID_VERSION_FORMAT,
|
|
ROPKG_EMSG_INVALID_VERSION_RELEASE_PHASE,
|
|
B_ERROR_PARAM("phase", temp));
|
|
b_error_add_submsg(
|
|
result,
|
|
B_ERROR_SUBMSG_INFO,
|
|
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;
|
|
return B_RESULT_SUCCESS;
|
|
}
|
|
|
|
static b_result parse_package_revision(
|
|
const char *s,
|
|
unsigned int *out_revision,
|
|
size_t *nr_read)
|
|
{
|
|
size_t i = 0;
|
|
|
|
if (*s == '\0') {
|
|
*out_revision = 1;
|
|
*nr_read = i;
|
|
return B_RESULT_SUCCESS;
|
|
}
|
|
|
|
if (*s == '-') {
|
|
i++;
|
|
s++;
|
|
} else {
|
|
return b_error_with_msg(
|
|
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;
|
|
return B_RESULT_SUCCESS;
|
|
}
|
|
|
|
static b_result parse_upstream_version(
|
|
const char *s,
|
|
unsigned int out[ROPKG_VERSION_UPSTREAM_DIGIT_MAX],
|
|
size_t *nr_read)
|
|
{
|
|
if (!isdigit(*s)) {
|
|
return b_error_with_msg(
|
|
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) {
|
|
return b_error_with_msg(
|
|
ROPKG_ERROR_VENDOR,
|
|
ROPKG_ERR_INVALID_VERSION_FORMAT,
|
|
ROPKG_EMSG_INVALID_UPSTREAM_VERSION_TWO_DOTS);
|
|
}
|
|
|
|
if (nr_digits >= ROPKG_VERSION_UPSTREAM_DIGIT_MAX - 1) {
|
|
return b_error_with_msg(
|
|
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) {
|
|
return b_error_with_msg(
|
|
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;
|
|
return B_RESULT_SUCCESS;
|
|
}
|
|
|
|
b_result ropkg_version_parse(struct ropkg_version *version, const char *s)
|
|
{
|
|
enum ropkg_version_release_phase release_phase
|
|
= ROPKG_VERSION_RELEASE_PHASE_RELEASE;
|
|
unsigned int upstream_version[ROPKG_VERSION_UPSTREAM_DIGIT_MAX]
|
|
= {0, 0, 0, 0, 0};
|
|
enum ropkg_version_release_phase version_release_phase
|
|
= ROPKG_VERSION_RELEASE_PHASE_RELEASE;
|
|
unsigned int version_release_phase_revision = 1;
|
|
unsigned int package_revision = 1;
|
|
|
|
b_result result = B_RESULT_SUCCESS;
|
|
|
|
char temp[64] = {0};
|
|
size_t z = 0;
|
|
|
|
while (*s && !isdigit(*s) && z < sizeof temp - 1) {
|
|
temp[z] = tolower(*s);
|
|
z++;
|
|
s++;
|
|
}
|
|
|
|
result = parse_release_phase(temp, &release_phase);
|
|
if (b_result_is_error(result)) {
|
|
return b_result_propagate(result);
|
|
}
|
|
|
|
size_t nr_read = 0;
|
|
result = parse_upstream_version(s, upstream_version, &nr_read);
|
|
if (b_result_is_error(result)) {
|
|
return b_result_propagate(result);
|
|
}
|
|
|
|
s += nr_read;
|
|
if (*s == '~' || *s == 0) {
|
|
result = parse_version_release_phase(
|
|
s,
|
|
&version_release_phase,
|
|
&version_release_phase_revision,
|
|
&nr_read);
|
|
|
|
s += nr_read;
|
|
if (b_result_is_error(result)) {
|
|
return b_result_propagate(result);
|
|
}
|
|
}
|
|
|
|
if (*s == '-' || *s == 0) {
|
|
result = parse_package_revision(s, &package_revision, &nr_read);
|
|
|
|
s += nr_read;
|
|
if (b_result_is_error(result)) {
|
|
return b_result_propagate(result);
|
|
}
|
|
}
|
|
|
|
if (*s != 0) {
|
|
return b_error_with_msg(
|
|
ROPKG_ERROR_VENDOR,
|
|
ROPKG_ERR_INVALID_VERSION_FORMAT,
|
|
ROPKG_EMSG_INVALID_VERSION_UNKNOWN_END);
|
|
}
|
|
|
|
version->v_release_phase = release_phase;
|
|
memcpy(version->v_upstream_version,
|
|
upstream_version,
|
|
sizeof upstream_version);
|
|
version->v_version_release_phase = version_release_phase;
|
|
version->v_version_release_phase_revision
|
|
= version_release_phase_revision;
|
|
version->v_package_revision = package_revision;
|
|
|
|
return B_RESULT_SUCCESS;
|
|
}
|
|
|
|
void ropkg_version_set_release_phase(
|
|
struct ropkg_version *version,
|
|
enum ropkg_version_release_phase phase)
|
|
{
|
|
if (phase == ROPKG_VERSION_RELEASE_PHASE_RC) {
|
|
return;
|
|
}
|
|
|
|
version->v_release_phase = phase;
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
void ropkg_version_set_version_release_phase(
|
|
struct ropkg_version *version,
|
|
enum ropkg_version_release_phase phase,
|
|
unsigned int revision)
|
|
{
|
|
version->v_version_release_phase = phase;
|
|
|
|
if (phase == ROPKG_VERSION_RELEASE_PHASE_RELEASE) {
|
|
version->v_version_release_phase_revision = 1;
|
|
} else {
|
|
version->v_version_release_phase_revision = revision;
|
|
}
|
|
}
|
|
|
|
void ropkg_version_set_package_revision(
|
|
struct ropkg_version *version,
|
|
unsigned int revision)
|
|
{
|
|
version->v_package_revision = revision;
|
|
}
|
|
|
|
static const char *release_phase_to_string(
|
|
enum ropkg_version_release_phase phase)
|
|
{
|
|
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)
|
|
{
|
|
b_stringstream str;
|
|
b_stringstream_begin(&str, out, max);
|
|
|
|
b_stringstream_add(
|
|
&str,
|
|
release_phase_to_string(version->v_release_phase));
|
|
|
|
unsigned int version_length
|
|
= upstream_version_length(version->v_upstream_version);
|
|
|
|
for (unsigned int i = 0; i < version_length; i++) {
|
|
if (i > 0) {
|
|
b_stringstream_add(&str, ".");
|
|
}
|
|
|
|
b_stringstream_addf(&str, "%u", version->v_upstream_version[i]);
|
|
}
|
|
|
|
if (version->v_version_release_phase
|
|
!= ROPKG_VERSION_RELEASE_PHASE_RELEASE) {
|
|
b_stringstream_addf(
|
|
&str,
|
|
"~%s",
|
|
release_phase_to_string(
|
|
version->v_version_release_phase));
|
|
|
|
if (version->v_version_release_phase_revision > 1
|
|
|| version->v_version_release_phase
|
|
== ROPKG_VERSION_RELEASE_PHASE_RC) {
|
|
b_stringstream_addf(
|
|
&str,
|
|
"%u",
|
|
version->v_version_release_phase_revision);
|
|
}
|
|
}
|
|
|
|
if (version->v_package_revision > 1) {
|
|
b_stringstream_addf(&str, "-%u", version->v_package_revision);
|
|
}
|
|
|
|
b_stringstream_end(&str);
|
|
return b_stringstream_get_length(&str);
|
|
}
|
|
|
|
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)
|
|
|
|
COMPARE(left, right, v_release_phase);
|
|
|
|
for (int i = 0; i < ROPKG_VERSION_UPSTREAM_DIGIT_MAX; i++) {
|
|
COMPARE(left, right, v_upstream_version[i]);
|
|
}
|
|
|
|
COMPARE(left, right, v_version_release_phase);
|
|
COMPARE(left, right, v_version_release_phase_revision);
|
|
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;
|
|
}
|
|
}
|