diff --git a/include/uv.h b/include/uv.h index a00743c6..4fabf7a4 100644 --- a/include/uv.h +++ b/include/uv.h @@ -371,6 +371,21 @@ UV_EXTERN void uv_close(uv_handle_t* handle, uv_close_cb close_cb); UV_EXTERN uv_buf_t uv_buf_init(char* base, size_t len); +/* + * Utility function. Copies up to `size` characters from `src` to `dst` + * and ensures that `dst` is properly NUL terminated unless `size` is zero. + */ +UV_EXTERN size_t uv_strlcpy(char* dst, const char* src, size_t size); + +/* + * Utility function. Appends `src` to `dst` and ensures that `dst` is + * properly NUL terminated unless `size` is zero or `dst` does not + * contain a NUL byte. `size` is the total length of `dst` so at most + * `size - strlen(dst) - 1` characters will be copied from `src`. + */ +UV_EXTERN size_t uv_strlcat(char* dst, const char* src, size_t size); + + #define UV_STREAM_FIELDS \ /* number of bytes queued for writing */ \ size_t write_queue_size; \ diff --git a/src/unix/core.c b/src/unix/core.c index ac005694..cf3138fb 100644 --- a/src/unix/core.c +++ b/src/unix/core.c @@ -827,21 +827,3 @@ int uv__cloexec(int fd, int set) { return 0; #endif } - - -/* TODO move to uv-common.c? */ -size_t uv__strlcpy(char* dst, const char* src, size_t size) { - const char *org; - - if (size == 0) { - return 0; - } - - org = src; - while (--size && *src) { - *dst++ = *src++; - } - *dst = '\0'; - - return src - org; -} diff --git a/src/unix/internal.h b/src/unix/internal.h index 4784bcf8..5cf9589b 100644 --- a/src/unix/internal.h +++ b/src/unix/internal.h @@ -138,8 +138,6 @@ enum { UV_TCP_KEEPALIVE = 0x100 /* Turn on keep-alive. */ }; -size_t uv__strlcpy(char* dst, const char* src, size_t size); - int uv__close(int fd); void uv__req_init(uv_req_t*); void uv__handle_init(uv_loop_t* loop, uv_handle_t* handle, uv_handle_type type); diff --git a/src/unix/pipe.c b/src/unix/pipe.c index 87959a6e..8d3789b9 100644 --- a/src/unix/pipe.c +++ b/src/unix/pipe.c @@ -74,7 +74,7 @@ int uv_pipe_bind(uv_pipe_t* handle, const char* name) { } memset(&saddr, 0, sizeof saddr); - uv__strlcpy(saddr.sun_path, pipe_fname, sizeof(saddr.sun_path)); + uv_strlcpy(saddr.sun_path, pipe_fname, sizeof(saddr.sun_path)); saddr.sun_family = AF_UNIX; if (bind(sockfd, (struct sockaddr*)&saddr, sizeof saddr) == -1) { @@ -197,7 +197,7 @@ void uv_pipe_connect(uv_connect_t* req, } memset(&saddr, 0, sizeof saddr); - uv__strlcpy(saddr.sun_path, name, sizeof(saddr.sun_path)); + uv_strlcpy(saddr.sun_path, name, sizeof(saddr.sun_path)); saddr.sun_family = AF_UNIX; /* We don't check for EINPROGRESS. Think about it: the socket diff --git a/src/uv-common.c b/src/uv-common.c index 84285fe8..d5e2c2a9 100644 --- a/src/uv-common.c +++ b/src/uv-common.c @@ -41,6 +41,41 @@ uv_counters_t* uv_counters() { } +size_t uv_strlcpy(char* dst, const char* src, size_t size) { + size_t n; + + if (size == 0) + return 0; + + for (n = 0; n < (size - 1) && *src != '\0'; n++) + *dst++ = *src++; + + *dst = '\0'; + + return n; +} + + +size_t uv_strlcat(char* dst, const char* src, size_t size) { + size_t n; + + if (size == 0) + return 0; + + for (n = 0; n < size && *dst != '\0'; n++, dst++); + + if (n == size) + return n; + + while (n < (size - 1) && *src != '\0') + n++, *dst++ = *src++; + + *dst = '\0'; + + return n; +} + + uv_buf_t uv_buf_init(char* base, size_t len) { uv_buf_t buf; buf.base = base; diff --git a/test/test-list.h b/test/test-list.h index d18d9481..557a2a7c 100644 --- a/test/test-list.h +++ b/test/test-list.h @@ -118,6 +118,9 @@ TEST_DECLARE (threadpool_queue_work_simple) TEST_DECLARE (thread_mutex) TEST_DECLARE (thread_rwlock) TEST_DECLARE (thread_create) +TEST_DECLARE (strlcpy) +TEST_DECLARE (strlcat) + #ifdef _WIN32 TEST_DECLARE (spawn_detect_pipe_name_collisions_on_windows) TEST_DECLARE (argument_escaping) @@ -273,6 +276,8 @@ TASK_LIST_START TEST_ENTRY (thread_mutex) TEST_ENTRY (thread_rwlock) TEST_ENTRY (thread_create) + TEST_ENTRY (strlcpy) + TEST_ENTRY (strlcat) #if 0 /* These are for testing the test runner. */ diff --git a/test/test-util.c b/test/test-util.c new file mode 100644 index 00000000..d61d3b12 --- /dev/null +++ b/test/test-util.c @@ -0,0 +1,97 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "uv.h" +#include "task.h" + +#include + +#define memeq(a, b, c) (memcmp((a), (b), (c)) == 0) + + +TEST_IMPL(strlcpy) { + size_t r; + + { + char dst[2] = "A"; + r = uv_strlcpy(dst, "", 0); + ASSERT(r == 0); + ASSERT(memeq(dst, "A", 1)); + } + + { + char dst[2] = "A"; + r = uv_strlcpy(dst, "B", 1); + ASSERT(r == 0); + ASSERT(memeq(dst, "", 1)); + } + + { + char dst[2] = "A"; + r = uv_strlcpy(dst, "B", 2); + ASSERT(r == 1); + ASSERT(memeq(dst, "B", 2)); + } + + { + char dst[3] = "AB"; + r = uv_strlcpy(dst, "CD", 3); + ASSERT(r == 2); + ASSERT(memeq(dst, "CD", 3)); + } + + return 0; +} + + +TEST_IMPL(strlcat) { + size_t r; + + { + char dst[2] = "A"; + r = uv_strlcat(dst, "B", 1); + ASSERT(r == 1); + ASSERT(memeq(dst, "A", 2)); + } + + { + char dst[2] = "A"; + r = uv_strlcat(dst, "B", 2); + ASSERT(r == 1); + ASSERT(memeq(dst, "A", 2)); + } + + { + char dst[3] = "A"; + r = uv_strlcat(dst, "B", 3); + ASSERT(r == 2); + ASSERT(memeq(dst, "AB", 3)); + } + + { + char dst[5] = "AB"; + r = uv_strlcat(dst, "CD", 5); + ASSERT(r == 4); + ASSERT(memeq(dst, "ABCD", 5)); + } + + return 0; +} diff --git a/uv.gyp b/uv.gyp index 9c4cd46a..c0c2c5b3 100644 --- a/uv.gyp +++ b/uv.gyp @@ -278,6 +278,7 @@ 'test/runner.h', 'test/test-get-loadavg.c', 'test/task.h', + 'test/test-util.c', 'test/test-async.c', 'test/test-error.c', 'test/test-callback-stack.c',