From 89cd4b59f8f6c8543c8f0d33efe384f4b1ab6fc3 Mon Sep 17 00:00:00 2001 From: Yakun Xu Date: Thu, 16 Jul 2020 14:39:02 +0800 Subject: [PATCH] [url] move url to library (#5233) This commit moves the URL parser to src/lib. Also includes the following changes: - Split URL on initialization. - Make a GetValue() const method. - Check aLastValue. - Add some negative tests. --- Android.mk | 1 + CMakeLists.txt | 6 +- configure.ac | 1 + src/core/common/code_utils.hpp | 2 - src/lib/CMakeLists.txt | 1 + src/lib/Makefile.am | 1 + src/lib/url/CMakeLists.txt | 40 ++++ src/lib/url/Makefile.am | 59 +++++ src/lib/url/url.cpp | 205 ++++++++++++++++++ src/lib/url/url.hpp | 100 +++++++++ src/posix/Makefile.am | 1 - src/posix/platform/CMakeLists.txt | 1 + src/posix/platform/Makefile.am | 22 +- src/posix/platform/hdlc_interface.cpp | 28 +-- src/posix/platform/hdlc_interface.hpp | 8 +- .../include/openthread/openthread-system.h | 6 - src/posix/platform/platform-posix.h | 2 +- src/posix/platform/radio.cpp | 12 +- src/posix/platform/radio_url.cpp | 137 +----------- src/posix/platform/radio_url.hpp | 33 +-- src/posix/platform/spi_interface.cpp | 24 +- src/posix/platform/spi_interface.hpp | 4 +- src/posix/platform/system.cpp | 10 +- 23 files changed, 477 insertions(+), 227 deletions(-) create mode 100644 src/lib/url/CMakeLists.txt create mode 100644 src/lib/url/Makefile.am create mode 100644 src/lib/url/url.cpp create mode 100644 src/lib/url/url.hpp diff --git a/Android.mk b/Android.mk index acb148677..234dd5abf 100644 --- a/Android.mk +++ b/Android.mk @@ -277,6 +277,7 @@ LOCAL_SRC_FILES := \ src/lib/spinel/spinel.c \ src/lib/spinel/spinel_decoder.cpp \ src/lib/spinel/spinel_encoder.cpp \ + src/lib/url/url.cpp \ src/posix/platform/alarm.cpp \ src/posix/platform/entropy.cpp \ src/posix/platform/hdlc_interface.cpp \ diff --git a/CMakeLists.txt b/CMakeLists.txt index 4d5f726f1..8e89e1f59 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -40,7 +40,11 @@ set(OT_EXTERNAL_MBEDTLS "" CACHE STRING "Specify external mbedtls library") add_library(ot-config INTERFACE) -target_include_directories(ot-config INTERFACE ${PROJECT_SOURCE_DIR}/src/core) +target_include_directories(ot-config INTERFACE + ${PROJECT_SOURCE_DIR}/include + ${PROJECT_SOURCE_DIR}/src + ${PROJECT_SOURCE_DIR}/src/core +) include(TestBigEndian) TEST_BIG_ENDIAN(OT_BIG_ENDIAN) diff --git a/configure.ac b/configure.ac index 7ae375ee6..31829bb7d 100644 --- a/configure.ac +++ b/configure.ac @@ -1026,6 +1026,7 @@ src/lib/Makefile src/lib/hdlc/Makefile src/lib/platform/Makefile src/lib/spinel/Makefile +src/lib/url/Makefile third_party/Makefile third_party/jlink/Makefile third_party/mbedtls/Makefile diff --git a/src/core/common/code_utils.hpp b/src/core/common/code_utils.hpp index 72e7045a7..61cbfadcc 100644 --- a/src/core/common/code_utils.hpp +++ b/src/core/common/code_utils.hpp @@ -34,8 +34,6 @@ #ifndef CODE_UTILS_HPP_ #define CODE_UTILS_HPP_ -#include "openthread-core-config.h" - #include #include diff --git a/src/lib/CMakeLists.txt b/src/lib/CMakeLists.txt index e3b522ba8..31e6c01ef 100644 --- a/src/lib/CMakeLists.txt +++ b/src/lib/CMakeLists.txt @@ -29,3 +29,4 @@ add_subdirectory(hdlc) add_subdirectory(platform) add_subdirectory(spinel) +add_subdirectory(url) diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am index 8cef70448..1bb226c11 100644 --- a/src/lib/Makefile.am +++ b/src/lib/Makefile.am @@ -32,6 +32,7 @@ SUBDIRS = \ hdlc \ platform \ spinel \ + url \ $(NULL) include $(abs_top_nlbuild_autotools_dir)/automake/post.am diff --git a/src/lib/url/CMakeLists.txt b/src/lib/url/CMakeLists.txt new file mode 100644 index 000000000..79c862935 --- /dev/null +++ b/src/lib/url/CMakeLists.txt @@ -0,0 +1,40 @@ +# +# Copyright (c) 2020, The OpenThread Authors. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. Neither the name of the copyright holder nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# + +add_library(openthread-url + url.cpp +) + +set_target_properties( + openthread-url + PROPERTIES + C_STANDARD 99 + CXX_STANDARD 11 +) + +target_link_libraries(openthread-url PRIVATE ot-config) diff --git a/src/lib/url/Makefile.am b/src/lib/url/Makefile.am new file mode 100644 index 000000000..099921b13 --- /dev/null +++ b/src/lib/url/Makefile.am @@ -0,0 +1,59 @@ +# +# Copyright (c) 2020, The OpenThread Authors. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. Neither the name of the copyright holder nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# + +include $(abs_top_nlbuild_autotools_dir)/automake/pre.am + +noinst_LIBRARIES = libopenthread-url.a + +libopenthread_url_a_CPPFLAGS = \ + -I$(top_srcdir)/include \ + -I$(top_srcdir)/src \ + $(NULL) + +libopenthread_url_a_SOURCES = \ + url.cpp \ + url.hpp \ + $(NULL) + +check_PROGRAMS = test-url + +test_url_CPPFLAGS = \ + -I$(top_srcdir)/include \ + -I$(top_srcdir)/src \ + -DSELF_TEST \ + $(NULL) + +test_url_SOURCES = \ + url.cpp \ + $(NULL) + +TESTS = \ + test-url \ + $(NULL) + +include $(abs_top_nlbuild_autotools_dir)/automake/post.am diff --git a/src/lib/url/url.cpp b/src/lib/url/url.cpp new file mode 100644 index 000000000..a07e145e9 --- /dev/null +++ b/src/lib/url/url.cpp @@ -0,0 +1,205 @@ +/* + * Copyright (c) 2020, The OpenThread Authors. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "lib/url/url.hpp" + +#include +#include +#include + +#include "core/common/code_utils.hpp" + +namespace ot { +namespace Url { + +otError Url::Init(char *aUrl) +{ + otError error = OT_ERROR_NONE; + char * url = aUrl; + + mEnd = aUrl + strlen(aUrl); + mProtocol = aUrl; + + url = strstr(aUrl, "://"); + VerifyOrExit(url != nullptr, error = OT_ERROR_PARSE); + url += sizeof("://") - 1; + mPath = url; + + url = strstr(url, "?"); + + if (url != nullptr) + { + mQuery = ++url; + + for (char *cur = strtok(url, "&"); cur != nullptr; cur = strtok(nullptr, "&")) + { + cur[-1] = '\0'; + } + } + else + { + mQuery = mEnd; + } + +exit: + return error; +} + +const char *Url::GetValue(const char *aName, const char *aLastValue) const +{ + const char * rval = nullptr; + const size_t len = strlen(aName); + const char * start; + + if (aLastValue == nullptr) + { + start = mQuery; + } + else + { + VerifyOrExit(aLastValue > mQuery && aLastValue < mEnd, OT_NOOP); + start = aLastValue + strlen(aLastValue) + 1; + } + + while (start < mEnd) + { + const char *last = nullptr; + + if (!strncmp(aName, start, len)) + { + if (start[len] == '=') + { + ExitNow(rval = &start[len + 1]); + } + else if (start[len] == '\0') + { + ExitNow(rval = ""); + } + } + last = start; + start = last + strlen(last) + 1; + } + +exit: + return rval; +} + +} // namespace Url +} // namespace ot + +#ifndef SELF_TEST +#define SELF_TEST 0 +#endif + +#if SELF_TEST +#include + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Warray-bounds" + +void TestSimple(void) +{ + char url[] = "spinel:///dev/ttyUSB0?baudrate=115200"; + ot::Url::Url args; + + assert(!args.Init(url)); + + assert(!strcmp(args.GetPath(), "/dev/ttyUSB0")); + assert(!strcmp(args.GetValue("baudrate"), "115200")); + assert(args.GetValue("not-exists") == nullptr); + assert(args.GetValue("last-value-wrong-position", url) == nullptr); + assert(args.GetValue("last-value-before-url", url - 1) == nullptr); + assert(args.GetValue("last-value-after-url", url + sizeof(url)) == nullptr); + + printf("PASS %s\r\n", __func__); +} + +void TestSimpleNoQueryString(void) +{ + char url[] = "spinel:///dev/ttyUSB0"; + ot::Url::Url args; + + assert(!args.Init(url)); + assert(!strcmp(args.GetPath(), "/dev/ttyUSB0")); + assert(args.GetValue("last-value-wrong-position", url) == nullptr); + assert(args.GetValue("last-value-before-url", url - 1) == nullptr); + assert(args.GetValue("last-value-after-url", url + sizeof(url)) == nullptr); + + printf("PASS %s\r\n", __func__); +} + +void TestMultipleProtocols(void) +{ + char url[] = "spinel+spi:///dev/ttyUSB0?baudrate=115200"; + ot::Url::Url args; + + assert(!args.Init(url)); + assert(!strcmp(args.GetPath(), "/dev/ttyUSB0")); + assert(!strcmp(args.GetValue("baudrate"), "115200")); + + printf("PASS %s\r\n", __func__); +} + +void TestMultipleProtocolsAndDuplicateParameters(void) +{ + char url[] = "spinel+exec:///path/to/ot-rcp?arg=1&arg=arg2&arg=3"; + ot::Url::Url args; + const char * arg = nullptr; + + assert(!args.Init(url)); + assert(!strcmp(args.GetPath(), "/path/to/ot-rcp")); + + arg = args.GetValue("arg"); + assert(!strcmp(arg, "1")); + + arg = args.GetValue("arg", arg); + assert(!strcmp(arg, "arg2")); + + arg = args.GetValue("arg", arg); + assert(!strcmp(arg, "3")); + + assert(args.GetValue("arg", url) == nullptr); + assert(args.GetValue("arg", url - 1) == nullptr); + assert(args.GetValue("arg", url + sizeof(url)) == nullptr); + + printf("PASS %s\r\n", __func__); +} + +#pragma GCC diagnostic pop + +int main(void) +{ + TestSimple(); + TestSimpleNoQueryString(); + TestMultipleProtocols(); + TestMultipleProtocolsAndDuplicateParameters(); + + return 0; +} + +#endif // SELF_TEST diff --git a/src/lib/url/url.hpp b/src/lib/url/url.hpp new file mode 100644 index 000000000..64dce12b4 --- /dev/null +++ b/src/lib/url/url.hpp @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2020, The OpenThread Authors. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef OT_LIB_URL_URL_HPP_ +#define OT_LIB_URL_URL_HPP_ + +#include + +/** + * @struct otUrl + * + * This structure represents a URL. + * + */ +struct otUrl +{ + const char *mProtocol; ///< The URL protocol. + const char *mPath; ///< The path. + const char *mQuery; ///< The start of the URL query string. + const char *mEnd; ///< The end of the URL buffer. +}; + +namespace ot { +namespace Url { + +/** + * This class implements the URL processing. + * + */ +class Url : public otUrl +{ +public: + /** + * This method initializes the URL. + * + * @param[in] aUrl A buffer stores the null-terminated URL string. + * + * @retval OT_ERROR_NONE Successfully parsed the URL. + * @retval OT_ERROR_PARSE Failed to parse the string as a URL. + * + */ + otError Init(char *aUrl); + + /** + * This method gets the path in URL. + * + * @returns The path in url. + * + */ + const char *GetPath(void) const { return mPath; } + + /** + * This method gets the value of parameter @p aName. + * + * @param[in] aName The parameter name. + * @param[in] aLastValue The last iterated parameter value, nullptr for the first value. + * + * @returns The parameter value. + * + */ + const char *GetValue(const char *aName, const char *aLastValue = nullptr) const; + + /** + * This method returns the URL protocol. + * + * @returns The URL protocol. + * + */ + const char *GetProtocol(void) const { return mProtocol; } +}; + +} // namespace Url +} // namespace ot + +#endif // OT_LIB_URL_URL_HPP_ diff --git a/src/posix/Makefile.am b/src/posix/Makefile.am index 18b09dfbe..d9e17d4fd 100644 --- a/src/posix/Makefile.am +++ b/src/posix/Makefile.am @@ -55,7 +55,6 @@ CPPFLAGS_COMMON = \ LIBTOOLFLAGS_COMMON = --preserve-dup-deps LDADD_COMMON = \ - $(top_builddir)/src/lib/platform/libopenthread-platform.a \ $(top_builddir)/src/posix/platform/libopenthread-posix.a \ -lutil \ $(NULL) diff --git a/src/posix/platform/CMakeLists.txt b/src/posix/platform/CMakeLists.txt index 4a36913aa..9d9c14e36 100644 --- a/src/posix/platform/CMakeLists.txt +++ b/src/posix/platform/CMakeLists.txt @@ -87,6 +87,7 @@ target_link_libraries(openthread-posix PUBLIC openthread-platform PRIVATE + openthread-url ot-config util $<$:rt> diff --git a/src/posix/platform/Makefile.am b/src/posix/platform/Makefile.am index 0dee2ad91..c93bc216b 100644 --- a/src/posix/platform/Makefile.am +++ b/src/posix/platform/Makefile.am @@ -27,6 +27,7 @@ # include $(abs_top_nlbuild_autotools_dir)/automake/pre.am +include $(top_srcdir)/src/lib/common.am lib_LIBRARIES = libopenthread-posix.a @@ -59,6 +60,11 @@ libopenthread_posix_a_SOURCES = \ virtual_time.cpp \ $(NULL) +libopenthread_posix_a_LIBADD = \ + $(call ot_list_objects,$(top_builddir)/src/lib/url/libopenthread-url.a) \ + $(call ot_list_objects,$(top_builddir)/src/lib/platform/libopenthread-platform.a) \ + $(NULL) + noinst_HEADERS = \ hdlc_interface.hpp \ openthread-posix-config.h \ @@ -77,7 +83,7 @@ if OPENTHREAD_BUILD_COVERAGE CLEANFILES = $(wildcard *.gcda *.gcno) endif # OPENTHREAD_BUILD_COVERAGE -check_PROGRAMS = test-settings test-radio-url +check_PROGRAMS = test-settings test_settings_CPPFLAGS = \ -I$(top_srcdir)/include \ @@ -92,22 +98,8 @@ test_settings_SOURCES = \ settings.cpp \ $(NULL) -test_radio_url_CPPFLAGS = \ - -I$(top_srcdir)/include \ - -I$(top_srcdir)/src \ - -I$(top_srcdir)/src/core \ - -I$(top_srcdir)/src/posix/platform/include \ - -DOPENTHREAD_CONFIG_LOG_PLATFORM=0 \ - -DSELF_TEST \ - $(NULL) - -test_radio_url_SOURCES = \ - radio_url.cpp \ - $(NULL) - TESTS = \ test-settings \ - test-radio-url \ $(NULL) include $(abs_top_nlbuild_autotools_dir)/automake/post.am diff --git a/src/posix/platform/hdlc_interface.cpp b/src/posix/platform/hdlc_interface.cpp index 27c59b6e3..9ae3573b4 100644 --- a/src/posix/platform/hdlc_interface.cpp +++ b/src/posix/platform/hdlc_interface.cpp @@ -138,30 +138,30 @@ HdlcInterface::HdlcInterface(SpinelInterface::ReceiveFrameCallback aCallback, { } -otError HdlcInterface::Init(Arguments &aArguments) +otError HdlcInterface::Init(const RadioUrl &aRadioUrl) { otError error = OT_ERROR_NONE; struct stat st; VerifyOrExit(mSockFd == -1, error = OT_ERROR_ALREADY); - VerifyOrDie(stat(aArguments.GetPath(), &st) == 0, OT_EXIT_INVALID_ARGUMENTS); + VerifyOrDie(stat(aRadioUrl.GetPath(), &st) == 0, OT_EXIT_INVALID_ARGUMENTS); if (S_ISCHR(st.st_mode)) { - mSockFd = OpenFile(aArguments.GetPath(), aArguments); + mSockFd = OpenFile(aRadioUrl); VerifyOrExit(mSockFd != -1, error = OT_ERROR_INVALID_ARGS); } #if OPENTHREAD_POSIX_CONFIG_RCP_PTY_ENABLE else if (S_ISREG(st.st_mode)) { - mSockFd = ForkPty(aArguments.GetPath(), aArguments.GetValue("forkpty-arg")); + mSockFd = ForkPty(aRadioUrl.GetPath(), aRadioUrl.GetValue("forkpty-arg")); VerifyOrExit(mSockFd != -1, error = OT_ERROR_INVALID_ARGS); } #endif // OPENTHREAD_POSIX_CONFIG_RCP_PTY_ENABLE else { - otLogCritPlat("Radio file '%s' not supported", aArguments.GetPath()); + otLogCritPlat("Radio file '%s' not supported", aRadioUrl.GetPath()); ExitNow(error = OT_ERROR_INVALID_ARGS); } @@ -409,12 +409,12 @@ exit: return error; } -int HdlcInterface::OpenFile(const char *aFile, Arguments &aArguments) +int HdlcInterface::OpenFile(const RadioUrl &aRadioUrl) { int fd = -1; int rval = 0; - fd = open(aFile, O_RDWR | O_NOCTTY | O_NONBLOCK | O_CLOEXEC); + fd = open(aRadioUrl.GetPath(), O_RDWR | O_NOCTTY | O_NONBLOCK | O_CLOEXEC); if (fd == -1) { perror("open uart failed"); @@ -436,7 +436,7 @@ int HdlcInterface::OpenFile(const char *aFile, Arguments &aArguments) tios.c_cflag = CS8 | HUPCL | CREAD | CLOCAL; - if ((value = aArguments.GetValue("uart-parity")) != nullptr) + if ((value = aRadioUrl.GetValue("uart-parity")) != nullptr) { if (strncmp(value, "odd", 3) == 0) { @@ -453,7 +453,7 @@ int HdlcInterface::OpenFile(const char *aFile, Arguments &aArguments) } } - if ((value = aArguments.GetValue("uart-stop")) != nullptr) + if ((value = aRadioUrl.GetValue("uart-stop")) != nullptr) { stopBit = atoi(value); } @@ -471,7 +471,7 @@ int HdlcInterface::OpenFile(const char *aFile, Arguments &aArguments) break; } - if ((value = aArguments.GetValue("uart-baudrate"))) + if ((value = aRadioUrl.GetValue("uart-baudrate"))) { baudrate = static_cast(atoi(value)); } @@ -563,7 +563,7 @@ int HdlcInterface::OpenFile(const char *aFile, Arguments &aArguments) break; } - if (aArguments.GetValue("uart-flow-control") != nullptr) + if (aRadioUrl.GetValue("uart-flow-control") != nullptr) { tios.c_cflag |= CRTSCTS; } @@ -583,7 +583,7 @@ exit: } #if OPENTHREAD_POSIX_CONFIG_RCP_PTY_ENABLE -int HdlcInterface::ForkPty(const char *aCommand, const char *aArguments) +int HdlcInterface::ForkPty(const char *aCommand, const char *aRadioUrl) { int fd = -1; int pid = -1; @@ -604,13 +604,13 @@ int HdlcInterface::ForkPty(const char *aCommand, const char *aArguments) const int kMaxCommand = 255; char cmd[kMaxCommand]; - if (aArguments == nullptr) + if (aRadioUrl == nullptr) { rval = snprintf(cmd, sizeof(cmd), "exec %s", aCommand); } else { - rval = snprintf(cmd, sizeof(cmd), "exec %s %s", aCommand, aArguments); + rval = snprintf(cmd, sizeof(cmd), "exec %s %s", aCommand, aRadioUrl); } VerifyOrExit(rval > 0 && static_cast(rval) < sizeof(cmd), fprintf(stderr, "NCP file and configuration is too long!"); diff --git a/src/posix/platform/hdlc_interface.hpp b/src/posix/platform/hdlc_interface.hpp index 5b9c7965a..2db2400e2 100644 --- a/src/posix/platform/hdlc_interface.hpp +++ b/src/posix/platform/hdlc_interface.hpp @@ -74,14 +74,14 @@ public: * * @note This method should be called before reading and sending spinel frames to the interface. * - * @param[in] aArguments Arguments parsed from radio url. + * @param[in] aRadioUrl RadioUrl parsed from radio url. * * @retval OT_ERROR_NONE The interface is initialized successfully * @retval OT_ERROR_ALREADY The interface is already initialized. * @retval OT_ERROR_INVALID_ARGS The UART device or executable cannot be found or failed to open/run. * */ - otError Init(Arguments &aArguments); + otError Init(const RadioUrl &aRadioUrl); /** * This method deinitializes the interface to the RCP. @@ -198,9 +198,9 @@ private: static void HandleHdlcFrame(void *aContext, otError aError); void HandleHdlcFrame(otError aError); - static int OpenFile(const char *aFile, Arguments &aArguments); + static int OpenFile(const RadioUrl &aRadioUrl); #if OPENTHREAD_POSIX_CONFIG_RCP_PTY_ENABLE - static int ForkPty(const char *aCommand, const char *aArguments); + static int ForkPty(const char *aCommand, const char *aRadioUrl); #endif enum diff --git a/src/posix/platform/include/openthread/openthread-system.h b/src/posix/platform/include/openthread/openthread-system.h index 3b2dc2728..6c63301c6 100644 --- a/src/posix/platform/include/openthread/openthread-system.h +++ b/src/posix/platform/include/openthread/openthread-system.h @@ -64,12 +64,6 @@ enum 32, ///< Default smallest SPI packet size we can receive in a single transaction. }; -/** - * This structure represents the radio URL. - * - */ -typedef struct otPosixRadioArguments otPosixRadioArguments; - /** * This structure represents platform specific configurations. * diff --git a/src/posix/platform/platform-posix.h b/src/posix/platform/platform-posix.h index 9b78b8283..fbebdb6fa 100644 --- a/src/posix/platform/platform-posix.h +++ b/src/posix/platform/platform-posix.h @@ -166,7 +166,7 @@ void platformAlarmAdvanceNow(uint64_t aDelta); * @param[in] aPlatformConfig Platform configuration structure. * */ -void platformRadioInit(otPosixRadioArguments *aArguments); +void platformRadioInit(otUrl *aRadioUrl); /** * This function shuts down the radio service used by OpenThread. diff --git a/src/posix/platform/radio.cpp b/src/posix/platform/radio.cpp index 14ca3a22a..0f21d64e2 100644 --- a/src/posix/platform/radio.cpp +++ b/src/posix/platform/radio.cpp @@ -94,15 +94,15 @@ void otPlatRadioSetPromiscuous(otInstance *aInstance, bool aEnable) SuccessOrDie(sRadioSpinel.SetPromiscuous(aEnable)); } -void platformRadioInit(otPosixRadioArguments *aArguments) +void platformRadioInit(otUrl *aRadioUrl) { - ot::Posix::Arguments *args = reinterpret_cast(aArguments); - bool resetRadio = (args->GetValue("no-reset") == nullptr); - bool restoreDataset = (args->GetValue("ncp-dataset") != nullptr); + ot::Posix::RadioUrl &radioUrl = *static_cast(aRadioUrl); + bool resetRadio = (radioUrl.GetValue("no-reset") == nullptr); + bool restoreDataset = (radioUrl.GetValue("ncp-dataset") != nullptr); #if OPENTHREAD_POSIX_CONFIG_MAX_POWER_TABLE_ENABLE uint8_t channel = ot::Radio::kChannelMin; int8_t power = ot::Posix::MaxPowerTable::kPowerDefault; - const char *maxPowerTable = args->GetValue("max-power-table"); + const char *maxPowerTable = radioUrl.GetValue("max-power-table"); if (maxPowerTable != nullptr) { @@ -126,7 +126,7 @@ void platformRadioInit(otPosixRadioArguments *aArguments) } #endif - SuccessOrDie(sRadioSpinel.GetSpinelInterface().Init(*args)); + SuccessOrDie(sRadioSpinel.GetSpinelInterface().Init(radioUrl)); sRadioSpinel.Init(resetRadio, restoreDataset); } diff --git a/src/posix/platform/radio_url.cpp b/src/posix/platform/radio_url.cpp index f73ed7697..aa21258a9 100644 --- a/src/posix/platform/radio_url.cpp +++ b/src/posix/platform/radio_url.cpp @@ -29,7 +29,6 @@ #include "posix/platform/radio_url.hpp" #include -#include #include @@ -96,142 +95,12 @@ const char *otSysGetRadioUrlHelpString(void) namespace ot { namespace Posix { -Arguments::Arguments(const char *aUrl) +RadioUrl::RadioUrl(const char *aUrl) { - char *url = &mUrl[0]; - - mPath = nullptr; - mStart = nullptr; - mEnd = nullptr; - - VerifyOrExit(aUrl != nullptr, OT_NOOP); - VerifyOrExit(strnlen(aUrl, sizeof(mUrl)) < sizeof(mUrl), OT_NOOP); + VerifyOrDie(strnlen(aUrl, sizeof(mUrl)) < sizeof(mUrl), OT_EXIT_INVALID_ARGUMENTS); strncpy(mUrl, aUrl, sizeof(mUrl) - 1); - url = strstr(url, "://"); - VerifyOrExit(url != nullptr, OT_NOOP); - url += sizeof("://") - 1; - mPath = url; - - mStart = strstr(url, "?"); - - if (mStart != nullptr) - { - mStart[0] = '\0'; - mStart++; - - mEnd = mStart + strlen(mStart); - - for (char *cur = strtok(mStart, "&"); cur != nullptr; cur = strtok(nullptr, "&")) - ; - } - else - { - mEnd = url + strlen(url); - mStart = mEnd; - } -exit: - return; -} - -const char *Arguments::GetValue(const char *aName, const char *aLastValue) -{ - const char * rval = nullptr; - const size_t len = strlen(aName); - char * start = (aLastValue == nullptr ? mStart : (const_cast(aLastValue) + strlen(aLastValue) + 1)); - - while (start < mEnd) - { - char *last = nullptr; - - if (!strncmp(aName, start, len)) - { - if (start[len] == '=') - { - ExitNow(rval = &start[len + 1]); - } - else if (start[len] == '&' || start[len] == '\0') - { - ExitNow(rval = ""); - } - } - last = start; - start = last + strlen(last) + 1; - } - -exit: - return rval; + SuccessOrDie(Url::Url::Init(mUrl)); } } // namespace Posix } // namespace ot - -#ifndef SELF_TEST -#define SELF_TEST 0 -#endif - -#if SELF_TEST -#include - -void TestSimple() -{ - char url[] = "spinel:///dev/ttyUSB0?baudrate=115200"; - ot::Posix::Arguments args(url); - - assert(!strcmp(args.GetPath(), "/dev/ttyUSB0")); - assert(!strcmp(args.GetValue("baudrate"), "115200")); - - printf("PASS %s\r\n", __func__); -} - -void TestSimpleNoArguments() -{ - char url[] = "spinel:///dev/ttyUSB0"; - ot::Posix::Arguments args(url); - - assert(!strcmp(args.GetPath(), "/dev/ttyUSB0")); - - printf("PASS %s\r\n", __func__); -} - -void TestMultipleProtocols() -{ - char url[] = "spinel+spi:///dev/ttyUSB0?baudrate=115200"; - ot::Posix::Arguments args(url); - - assert(!strcmp(args.GetPath(), "/dev/ttyUSB0")); - assert(!strcmp(args.GetValue("baudrate"), "115200")); - - printf("PASS %s\r\n", __func__); -} - -void TestMultipleProtocolsAndDuplicateParameters() -{ - char url[] = "spinel+exec:///path/to/ot-rcp?arg=1&arg=arg2&arg=3"; - ot::Posix::Arguments args(url); - const char * arg = nullptr; - - assert(!strcmp(args.GetPath(), "/path/to/ot-rcp")); - - arg = args.GetValue("arg"); - assert(!strcmp(arg, "1")); - - arg = args.GetValue("arg", arg); - assert(!strcmp(arg, "arg2")); - - arg = args.GetValue("arg", arg); - assert(!strcmp(arg, "3")); - - printf("PASS %s\r\n", __func__); -} - -int main() -{ - TestSimple(); - TestSimpleNoArguments(); - TestMultipleProtocols(); - TestMultipleProtocolsAndDuplicateParameters(); - - return 0; -} - -#endif // SELF_TEST diff --git a/src/posix/platform/radio_url.hpp b/src/posix/platform/radio_url.hpp index 10f07768d..2445989a9 100644 --- a/src/posix/platform/radio_url.hpp +++ b/src/posix/platform/radio_url.hpp @@ -34,37 +34,25 @@ #include -struct otPosixRadioArguments -{ - const char *mPath; ///< The path to the executable or device -}; +#include "lib/url/url.hpp" namespace ot { namespace Posix { -class Arguments : public otPosixRadioArguments +/** + * This class implements the radio URL processing. + * + */ +class RadioUrl : public ot::Url::Url { public: - Arguments(const char *aUrl); - /** - * This method gets the path in url + * This constructor initializes the object. * - * @returns The path in url. + * @param[in] aUrl The null-terminated URL string. * */ - const char *GetPath(void) const { return mPath; } - - /** - * This method returns the url agument value. - * - * @param[in] aName Argument name. - * @param[in] aLastValue The last iterated argument value, nullptr for first value. - * - * @returns The argument value. - * - */ - const char *GetValue(const char *aName, const char *aLastValue = nullptr); + RadioUrl(const char *aUrl); private: enum @@ -72,9 +60,6 @@ private: kRadioUrlMaxSize = 512, }; char mUrl[kRadioUrlMaxSize]; - - char *mStart; - char *mEnd; }; } // namespace Posix diff --git a/src/posix/platform/spi_interface.cpp b/src/posix/platform/spi_interface.cpp index 395db018a..ffff0dce4 100644 --- a/src/posix/platform/spi_interface.cpp +++ b/src/posix/platform/spi_interface.cpp @@ -91,7 +91,7 @@ SpiInterface::SpiInterface(SpinelInterface::ReceiveFrameCallback aCallback, { } -otError SpiInterface::Init(Arguments &aArguments) +otError SpiInterface::Init(const RadioUrl &aRadioUrl) { const char *spiGpioIntDevice; const char *spiGpioResetDevice; @@ -105,14 +105,14 @@ otError SpiInterface::Init(Arguments &aArguments) uint8_t spiSmallPacketSize = OT_PLATFORM_CONFIG_SPI_DEFAULT_SMALL_PACKET_SIZE; const char *value; - spiGpioIntDevice = aArguments.GetValue("gpio-int-device"); - spiGpioResetDevice = aArguments.GetValue("gpio-reset-device"); + spiGpioIntDevice = aRadioUrl.GetValue("gpio-int-device"); + spiGpioResetDevice = aRadioUrl.GetValue("gpio-reset-device"); if (!spiGpioIntDevice || !spiGpioResetDevice) { DieNow(OT_EXIT_INVALID_ARGUMENTS); } - if ((value = aArguments.GetValue("gpio-int-line"))) + if ((value = aRadioUrl.GetValue("gpio-int-line"))) { spiGpioIntLine = static_cast(atoi(value)); } @@ -120,7 +120,7 @@ otError SpiInterface::Init(Arguments &aArguments) { DieNow(OT_EXIT_INVALID_ARGUMENTS); } - if ((value = aArguments.GetValue("gpio-reset-line"))) + if ((value = aRadioUrl.GetValue("gpio-reset-line"))) { spiGpioResetLine = static_cast(atoi(value)); } @@ -128,27 +128,27 @@ otError SpiInterface::Init(Arguments &aArguments) { DieNow(OT_EXIT_INVALID_ARGUMENTS); } - if ((value = aArguments.GetValue("spi-mode"))) + if ((value = aRadioUrl.GetValue("spi-mode"))) { spiMode = static_cast(atoi(value)); } - if ((value = aArguments.GetValue("spi-speed"))) + if ((value = aRadioUrl.GetValue("spi-speed"))) { spiSpeed = static_cast(atoi(value)); } - if ((value = aArguments.GetValue("spi-reset-delay"))) + if ((value = aRadioUrl.GetValue("spi-reset-delay"))) { spiResetDelay = static_cast(atoi(value)); } - if ((value = aArguments.GetValue("spi-cs-delay"))) + if ((value = aRadioUrl.GetValue("spi-cs-delay"))) { spiCsDelay = static_cast(atoi(value)); } - if ((value = aArguments.GetValue("spi-align-allowance"))) + if ((value = aRadioUrl.GetValue("spi-align-allowance"))) { spiAlignAllowance = static_cast(atoi(value)); } - if ((value = aArguments.GetValue("spi-small-packet"))) + if ((value = aRadioUrl.GetValue("spi-small-packet"))) { spiSmallPacketSize = static_cast(atoi(value)); } @@ -170,7 +170,7 @@ otError SpiInterface::Init(Arguments &aArguments) } InitResetPin(spiGpioResetDevice, spiGpioResetLine); - InitSpiDev(aArguments.GetPath(), spiMode, spiSpeed); + InitSpiDev(aRadioUrl.GetPath(), spiMode, spiSpeed); // Reset RCP chip. TrigerReset(); diff --git a/src/posix/platform/spi_interface.hpp b/src/posix/platform/spi_interface.hpp index b28319792..de3414530 100644 --- a/src/posix/platform/spi_interface.hpp +++ b/src/posix/platform/spi_interface.hpp @@ -79,14 +79,14 @@ public: * * @note This method should be called before reading and sending spinel frames to the interface. * - * @param[in] aArguments Arguments parsed from radio url. + * @param[in] aRadioUrl Arguments parsed from radio url. * * @retval OT_ERROR_NONE The interface is initialized successfully. * @retval OT_ERROR_ALREADY The interface is already initialized. * @retval OT_ERROR_INVALID_ARGS The UART device or executable cannot be found or failed to open/run. * */ - otError Init(Arguments &aArguments); + otError Init(const RadioUrl &aRadioUrl); /** * This method deinitializes the interface to the RCP. diff --git a/src/posix/platform/system.cpp b/src/posix/platform/system.cpp index bde619158..8c80819a0 100644 --- a/src/posix/platform/system.cpp +++ b/src/posix/platform/system.cpp @@ -49,16 +49,16 @@ uint64_t gNodeId = 0; otInstance *otSysInit(otPlatformConfig *aPlatformConfig) { - otInstance * instance = nullptr; - ot::Posix::Arguments args(aPlatformConfig->mRadioUrl); + otInstance * instance = nullptr; + ot::Posix::RadioUrl radioUrl(aPlatformConfig->mRadioUrl); #if OPENTHREAD_POSIX_VIRTUAL_TIME - virtualTimeInit(static_cast(atoi(args.GetValue("forkpty-arg")))); + virtualTimeInit(static_cast(atoi(radioUrl.GetValue("forkpty-arg")))); #endif - VerifyOrDie(args.GetPath() != nullptr, OT_EXIT_INVALID_ARGUMENTS); + VerifyOrDie(radioUrl.GetPath() != nullptr, OT_EXIT_INVALID_ARGUMENTS); platformAlarmInit(aPlatformConfig->mSpeedUpFactor, aPlatformConfig->mRealTimeSignal); - platformRadioInit(&args); + platformRadioInit(&radioUrl); platformRandomInit(); instance = otInstanceInitSingle();