summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorValentin Bartenev <vbart@nginx.com>2017-10-05 16:46:18 +0300
committerValentin Bartenev <vbart@nginx.com>2017-10-05 16:46:18 +0300
commit096562c0b14656354fcb0eb7b7c6d524f5fb1f1d (patch)
treeefd6dd7b94951778e132d84591d77ae8d7ecf469
parent653e9854637e129cd6a92adaff96784220165eea (diff)
downloadunit-096562c0b14656354fcb0eb7b7c6d524f5fb1f1d.tar.gz
unit-096562c0b14656354fcb0eb7b7c6d524f5fb1f1d.tar.bz2
Improved applications versions handling.
-rw-r--r--auto/sources1
-rw-r--r--src/nxt_application.c12
-rw-r--r--src/nxt_application.h2
-rw-r--r--src/nxt_main_process.c15
-rw-r--r--src/nxt_runtime.c2
-rw-r--r--src/nxt_string.c120
-rw-r--r--src/nxt_string.h9
-rw-r--r--test/nxt_strverscmp_test.c94
-rw-r--r--test/nxt_tests.c4
-rw-r--r--test/nxt_tests.h1
10 files changed, 244 insertions, 16 deletions
diff --git a/auto/sources b/auto/sources
index f2760ad6..473fe2f3 100644
--- a/auto/sources
+++ b/auto/sources
@@ -218,6 +218,7 @@ NXT_TEST_SRCS=" \
test/nxt_utf8_test.c \
test/nxt_rbtree1_test.c \
test/nxt_http_parse_test.c \
+ test/nxt_strverscmp_test.c \
"
NXT_LIB_UTF8_FILE_NAME_TEST_SRCS=" \
diff --git a/src/nxt_application.c b/src/nxt_application.c
index 36e855b8..2ecec636 100644
--- a/src/nxt_application.c
+++ b/src/nxt_application.c
@@ -291,8 +291,8 @@ nxt_app_start(nxt_task_t *task, void *data)
nxt_app = lang->module;
if (nxt_app == NULL) {
- nxt_debug(task, "application language module: %V \"%s\"",
- &lang->version, lang->file);
+ nxt_debug(task, "application language module: %s \"%s\"",
+ lang->version, 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;
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)
- && nxt_str_start(&lang[i].version, version, version_length))
+ && nxt_strvers_match(lang[i].version, version, version_length))
{
return &lang[i];
}
diff --git a/src/nxt_application.h b/src/nxt_application.h
index 02e7edeb..5561d854 100644
--- a/src/nxt_application.h
+++ b/src/nxt_application.h
@@ -24,7 +24,7 @@ typedef struct nxt_app_module_s nxt_app_module_t;
typedef struct {
nxt_str_t type;
- nxt_str_t version;
+ u_char *version;
char *file;
nxt_application_module_t *module;
} nxt_app_lang_module_t;
diff --git a/src/nxt_main_process.c b/src/nxt_main_process.c
index ba916abf..4a185a54 100644
--- a/src/nxt_main_process.c
+++ b/src/nxt_main_process.c
@@ -1010,7 +1010,7 @@ static nxt_conf_map_t nxt_app_lang_module_map[] = {
{
nxt_string("version"),
- NXT_CONF_MAP_STR_COPY,
+ NXT_CONF_MAP_CSTRZ,
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;
}
- nxt_debug(task, "lang %V %V \"%s\"",
- &lang->type, &lang->version, lang->file);
+ nxt_debug(task, "lang %V %s \"%s\"",
+ &lang->type, lang->version, lang->file);
}
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)
{
int n;
- size_t length;
const nxt_app_lang_module_t *lang1, *lang2;
lang1 = v1;
@@ -1130,13 +1129,7 @@ nxt_app_lang_compare(const void *v1, const void *v2)
return n;
}
- length = nxt_min(lang1->version.length, lang2->version.length);
-
- n = nxt_strncmp(lang1->version.start, lang2->version.start, length);
-
- if (n == 0) {
- n = lang1->version.length - lang2->version.length;
- }
+ n = nxt_strverscmp(lang1->version, lang2->version);
/* Negate result to move higher versions to the beginning. */
diff --git a/src/nxt_runtime.c b/src/nxt_runtime.c
index b1b80865..168f4f19 100644
--- a/src/nxt_runtime.c
+++ b/src/nxt_runtime.c
@@ -91,7 +91,7 @@ nxt_runtime_create(nxt_task_t *task)
/* Should not fail. */
lang = nxt_array_add(rt->languages);
lang->type = (nxt_str_t) nxt_string("go");
- lang->version = (nxt_str_t) nxt_null_string;
+ lang->version = (u_char *) "";
lang->file = NULL;
lang->module = &nxt_go_module;
diff --git a/src/nxt_string.c b/src/nxt_string.c
index 4690851c..28b9f887 100644
--- a/src/nxt_string.c
+++ b/src/nxt_string.c
@@ -315,3 +315,123 @@ nxt_str_strip(u_char *start, u_char *end)
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;
+}
diff --git a/src/nxt_string.h b/src/nxt_string.h
index 56dc18d7..2a3d55ea 100644
--- a/src/nxt_string.h
+++ b/src/nxt_string.h
@@ -16,6 +16,10 @@ nxt_lowcase(c) \
nxt_upcase(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_LF (u_char) 10
@@ -171,4 +175,9 @@ nxt_strchr_start(s, 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_ */
diff --git a/test/nxt_strverscmp_test.c b/test/nxt_strverscmp_test.c
new file mode 100644
index 00000000..40adbfb2
--- /dev/null
+++ b/test/nxt_strverscmp_test.c
@@ -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;
+}
diff --git a/test/nxt_tests.c b/test/nxt_tests.c
index 655192ab..7cba0f69 100644
--- a/test/nxt_tests.c
+++ b/test/nxt_tests.c
@@ -158,5 +158,9 @@ main(int argc, char **argv)
return 1;
}
+ if (nxt_strverscmp_test(thr) != NXT_OK) {
+ return 1;
+ }
+
return 0;
}
diff --git a/test/nxt_tests.h b/test/nxt_tests.h
index 508ddbe4..be4168cf 100644
--- a/test/nxt_tests.h
+++ b/test/nxt_tests.h
@@ -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_utf8_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_ */