#include "version.h" #include #include #include #include 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; } }