Files
ropkg/libropkg/version.c

484 lines
10 KiB
C
Raw Normal View History

#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;
}
}