Improved applications versions handling.
This commit is contained in:
@@ -218,6 +218,7 @@ NXT_TEST_SRCS=" \
|
|||||||
test/nxt_utf8_test.c \
|
test/nxt_utf8_test.c \
|
||||||
test/nxt_rbtree1_test.c \
|
test/nxt_rbtree1_test.c \
|
||||||
test/nxt_http_parse_test.c \
|
test/nxt_http_parse_test.c \
|
||||||
|
test/nxt_strverscmp_test.c \
|
||||||
"
|
"
|
||||||
|
|
||||||
NXT_LIB_UTF8_FILE_NAME_TEST_SRCS=" \
|
NXT_LIB_UTF8_FILE_NAME_TEST_SRCS=" \
|
||||||
|
|||||||
@@ -291,8 +291,8 @@ nxt_app_start(nxt_task_t *task, void *data)
|
|||||||
nxt_app = lang->module;
|
nxt_app = lang->module;
|
||||||
|
|
||||||
if (nxt_app == NULL) {
|
if (nxt_app == NULL) {
|
||||||
nxt_debug(task, "application language module: %V \"%s\"",
|
nxt_debug(task, "application language module: %s \"%s\"",
|
||||||
&lang->version, lang->file);
|
lang->version, lang->file);
|
||||||
|
|
||||||
nxt_app = nxt_app_module_load(task, lang->file);
|
nxt_app = nxt_app_module_load(task, lang->file);
|
||||||
}
|
}
|
||||||
@@ -1020,8 +1020,14 @@ nxt_app_lang_module(nxt_runtime_t *rt, nxt_str_t *name)
|
|||||||
n = rt->languages->nelts;
|
n = rt->languages->nelts;
|
||||||
|
|
||||||
for (i = 0; i < n; i++) {
|
for (i = 0; i < n; i++) {
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Versions are sorted in descending order
|
||||||
|
* so first match chooses the highest version.
|
||||||
|
*/
|
||||||
|
|
||||||
if (nxt_str_eq(&lang[i].type, name->start, type_length)
|
if (nxt_str_eq(&lang[i].type, name->start, type_length)
|
||||||
&& nxt_str_start(&lang[i].version, version, version_length))
|
&& nxt_strvers_match(lang[i].version, version, version_length))
|
||||||
{
|
{
|
||||||
return &lang[i];
|
return &lang[i];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ typedef struct nxt_app_module_s nxt_app_module_t;
|
|||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
nxt_str_t type;
|
nxt_str_t type;
|
||||||
nxt_str_t version;
|
u_char *version;
|
||||||
char *file;
|
char *file;
|
||||||
nxt_application_module_t *module;
|
nxt_application_module_t *module;
|
||||||
} nxt_app_lang_module_t;
|
} nxt_app_lang_module_t;
|
||||||
|
|||||||
@@ -1010,7 +1010,7 @@ static nxt_conf_map_t nxt_app_lang_module_map[] = {
|
|||||||
|
|
||||||
{
|
{
|
||||||
nxt_string("version"),
|
nxt_string("version"),
|
||||||
NXT_CONF_MAP_STR_COPY,
|
NXT_CONF_MAP_CSTRZ,
|
||||||
offsetof(nxt_app_lang_module_t, version),
|
offsetof(nxt_app_lang_module_t, version),
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -1091,8 +1091,8 @@ nxt_main_port_modules_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg)
|
|||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
nxt_debug(task, "lang %V %V \"%s\"",
|
nxt_debug(task, "lang %V %s \"%s\"",
|
||||||
&lang->type, &lang->version, lang->file);
|
&lang->type, lang->version, lang->file);
|
||||||
}
|
}
|
||||||
|
|
||||||
qsort(rt->languages->elts, rt->languages->nelts,
|
qsort(rt->languages->elts, rt->languages->nelts,
|
||||||
@@ -1114,7 +1114,6 @@ static int nxt_cdecl
|
|||||||
nxt_app_lang_compare(const void *v1, const void *v2)
|
nxt_app_lang_compare(const void *v1, const void *v2)
|
||||||
{
|
{
|
||||||
int n;
|
int n;
|
||||||
size_t length;
|
|
||||||
const nxt_app_lang_module_t *lang1, *lang2;
|
const nxt_app_lang_module_t *lang1, *lang2;
|
||||||
|
|
||||||
lang1 = v1;
|
lang1 = v1;
|
||||||
@@ -1130,13 +1129,7 @@ nxt_app_lang_compare(const void *v1, const void *v2)
|
|||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
length = nxt_min(lang1->version.length, lang2->version.length);
|
n = nxt_strverscmp(lang1->version, lang2->version);
|
||||||
|
|
||||||
n = nxt_strncmp(lang1->version.start, lang2->version.start, length);
|
|
||||||
|
|
||||||
if (n == 0) {
|
|
||||||
n = lang1->version.length - lang2->version.length;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Negate result to move higher versions to the beginning. */
|
/* Negate result to move higher versions to the beginning. */
|
||||||
|
|
||||||
|
|||||||
@@ -91,7 +91,7 @@ nxt_runtime_create(nxt_task_t *task)
|
|||||||
/* Should not fail. */
|
/* Should not fail. */
|
||||||
lang = nxt_array_add(rt->languages);
|
lang = nxt_array_add(rt->languages);
|
||||||
lang->type = (nxt_str_t) nxt_string("go");
|
lang->type = (nxt_str_t) nxt_string("go");
|
||||||
lang->version = (nxt_str_t) nxt_null_string;
|
lang->version = (u_char *) "";
|
||||||
lang->file = NULL;
|
lang->file = NULL;
|
||||||
lang->module = &nxt_go_module;
|
lang->module = &nxt_go_module;
|
||||||
|
|
||||||
|
|||||||
120
src/nxt_string.c
120
src/nxt_string.c
@@ -315,3 +315,123 @@ nxt_str_strip(u_char *start, u_char *end)
|
|||||||
|
|
||||||
return (p + 1) - start;
|
return (p + 1) - start;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
nxt_int_t
|
||||||
|
nxt_strverscmp(const u_char *s1, const u_char *s2)
|
||||||
|
{
|
||||||
|
u_char c1, c2;
|
||||||
|
nxt_int_t diff;
|
||||||
|
|
||||||
|
enum {
|
||||||
|
st_str = 0,
|
||||||
|
st_num,
|
||||||
|
st_zero,
|
||||||
|
st_frac,
|
||||||
|
} state;
|
||||||
|
|
||||||
|
state = st_str;
|
||||||
|
|
||||||
|
for ( ;; ) {
|
||||||
|
c1 = *s1++;
|
||||||
|
c2 = *s2++;
|
||||||
|
|
||||||
|
diff = c1 - c2;
|
||||||
|
|
||||||
|
if (diff != 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (c1 == '\0') {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!nxt_isdigit(c1)) {
|
||||||
|
state = st_str;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state == st_str) {
|
||||||
|
state = (c1 != '0') ? st_num : st_zero;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state == st_zero && c1 != '0') {
|
||||||
|
state = st_frac;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (state) {
|
||||||
|
|
||||||
|
case st_str:
|
||||||
|
|
||||||
|
if ((u_char) (c1 - '1') > 8 || (u_char) (c2 - '1') > 8) {
|
||||||
|
return diff;
|
||||||
|
}
|
||||||
|
|
||||||
|
c1 = *s1++;
|
||||||
|
c2 = *s2++;
|
||||||
|
|
||||||
|
/* Fall through. */
|
||||||
|
|
||||||
|
case st_num:
|
||||||
|
|
||||||
|
while (nxt_isdigit(c1) && nxt_isdigit(c2)) {
|
||||||
|
c1 = *s1++;
|
||||||
|
c2 = *s2++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nxt_isdigit(c1)) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nxt_isdigit(c2)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return diff;
|
||||||
|
|
||||||
|
case st_zero:
|
||||||
|
|
||||||
|
if (c1 == '0' || c2 == '\0') {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (c2 == '0' || c1 == '\0') {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Fall through. */
|
||||||
|
|
||||||
|
case st_frac:
|
||||||
|
return diff;
|
||||||
|
}
|
||||||
|
|
||||||
|
nxt_unreachable();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
nxt_bool_t
|
||||||
|
nxt_strvers_match(u_char *version, u_char *prefix, size_t length)
|
||||||
|
{
|
||||||
|
u_char next, last;
|
||||||
|
|
||||||
|
if (nxt_strncmp(version, prefix, length) == 0) {
|
||||||
|
|
||||||
|
next = version[length];
|
||||||
|
|
||||||
|
if (next == '\0') {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
last = version[length - 1];
|
||||||
|
|
||||||
|
if (nxt_isdigit(last) != nxt_isdigit(next)) {
|
||||||
|
/* This is a version part boundary. */
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|||||||
@@ -16,6 +16,10 @@ nxt_lowcase(c) \
|
|||||||
nxt_upcase(c) \
|
nxt_upcase(c) \
|
||||||
(u_char) ((c >= 'a' && c <= 'z') ? c & ~0x20 : c)
|
(u_char) ((c >= 'a' && c <= 'z') ? c & ~0x20 : c)
|
||||||
|
|
||||||
|
#define \
|
||||||
|
nxt_isdigit(c) \
|
||||||
|
((u_char) ((c) - '0') <= 9)
|
||||||
|
|
||||||
|
|
||||||
#define NXT_CR (u_char) 13
|
#define NXT_CR (u_char) 13
|
||||||
#define NXT_LF (u_char) 10
|
#define NXT_LF (u_char) 10
|
||||||
@@ -171,4 +175,9 @@ nxt_strchr_start(s, c) \
|
|||||||
(((s)->length != 0) && ((s)->start[0] == c))
|
(((s)->length != 0) && ((s)->start[0] == c))
|
||||||
|
|
||||||
|
|
||||||
|
NXT_EXPORT nxt_int_t nxt_strverscmp(const u_char *s1, const u_char *s2);
|
||||||
|
NXT_EXPORT nxt_bool_t nxt_strvers_match(u_char *version, u_char *prefix,
|
||||||
|
size_t length);
|
||||||
|
|
||||||
|
|
||||||
#endif /* _NXT_STRING_H_INCLUDED_ */
|
#endif /* _NXT_STRING_H_INCLUDED_ */
|
||||||
|
|||||||
94
test/nxt_strverscmp_test.c
Normal file
94
test/nxt_strverscmp_test.c
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) NGINX, Inc.
|
||||||
|
* Copyright (C) Valentin V. Bartenev
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <nxt_main.h>
|
||||||
|
#include "nxt_tests.h"
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
const char *v1;
|
||||||
|
const char res;
|
||||||
|
const char *v2;
|
||||||
|
} nxt_strverscmp_test_t;
|
||||||
|
|
||||||
|
|
||||||
|
nxt_int_t
|
||||||
|
nxt_strverscmp_test(nxt_thread_t *thr)
|
||||||
|
{
|
||||||
|
nxt_int_t ret;
|
||||||
|
nxt_uint_t i;
|
||||||
|
|
||||||
|
static const nxt_strverscmp_test_t tests[] = {
|
||||||
|
{ "word", '=', "word" },
|
||||||
|
{ "42", '=', "42" },
|
||||||
|
{ "000", '=', "000" },
|
||||||
|
{ "2", '>', "1" },
|
||||||
|
{ "2", '<', "10" },
|
||||||
|
{ "rc2", '>', "rc" },
|
||||||
|
{ "rc2", '<', "rc3" },
|
||||||
|
{ "1.13.8", '>', "1.1.9" },
|
||||||
|
{ "1.9", '<', "1.13.8" },
|
||||||
|
{ "9.9", '<', "10.0" },
|
||||||
|
{ "1", '>', "007" },
|
||||||
|
{ "2b01", '<', "2b013" },
|
||||||
|
{ "011", '>', "01" },
|
||||||
|
{ "011", '>', "01.1" },
|
||||||
|
{ "011", '>', "01+1" },
|
||||||
|
{ "011", '<', "01:1" },
|
||||||
|
{ "011", '<', "01b" },
|
||||||
|
{ "020", '>', "01b" },
|
||||||
|
{ "a0", '>', "a01" },
|
||||||
|
{ "b00", '<', "b01" },
|
||||||
|
{ "c000", '<', "c01" },
|
||||||
|
{ "000", '<', "00" },
|
||||||
|
{ "000", '<', "00a" },
|
||||||
|
{ "00.", '>', "000" },
|
||||||
|
{ "a.0", '<', "a0" },
|
||||||
|
{ "b11", '>', "b0" },
|
||||||
|
};
|
||||||
|
|
||||||
|
nxt_thread_time_update(thr);
|
||||||
|
|
||||||
|
for (i = 0; i < nxt_nitems(tests); i++) {
|
||||||
|
|
||||||
|
ret = nxt_strverscmp((u_char *) tests[i].v1, (u_char *) tests[i].v2);
|
||||||
|
|
||||||
|
switch (tests[i].res) {
|
||||||
|
|
||||||
|
case '<':
|
||||||
|
if (ret < 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case '=':
|
||||||
|
if (ret == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case '>':
|
||||||
|
if (ret > 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
nxt_log_alert(thr->log,
|
||||||
|
"nxt_strverscmp() test \"%s\" %c \"%s\" failed: %i",
|
||||||
|
tests[i].v1, tests[i].res, tests[i].v2, ret);
|
||||||
|
|
||||||
|
return NXT_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
nxt_log_error(NXT_LOG_NOTICE, thr->log,
|
||||||
|
"nxt_strverscmp() test passed");
|
||||||
|
|
||||||
|
return NXT_OK;
|
||||||
|
}
|
||||||
@@ -158,5 +158,9 @@ main(int argc, char **argv)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (nxt_strverscmp_test(thr) != NXT_OK) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -63,6 +63,7 @@ nxt_int_t nxt_sprintf_test(nxt_thread_t *thr);
|
|||||||
nxt_int_t nxt_malloc_test(nxt_thread_t *thr);
|
nxt_int_t nxt_malloc_test(nxt_thread_t *thr);
|
||||||
nxt_int_t nxt_utf8_test(nxt_thread_t *thr);
|
nxt_int_t nxt_utf8_test(nxt_thread_t *thr);
|
||||||
nxt_int_t nxt_http_parse_test(nxt_thread_t *thr);
|
nxt_int_t nxt_http_parse_test(nxt_thread_t *thr);
|
||||||
|
nxt_int_t nxt_strverscmp_test(nxt_thread_t *thr);
|
||||||
|
|
||||||
|
|
||||||
#endif /* _NXT_TESTS_H_INCLUDED_ */
|
#endif /* _NXT_TESTS_H_INCLUDED_ */
|
||||||
|
|||||||
Reference in New Issue
Block a user