From 3174c590c327b0f6814b9d390085e6f15acd1946 Mon Sep 17 00:00:00 2001 From: Shu Chen Date: Wed, 5 Sep 2018 15:40:02 +0800 Subject: [PATCH] [utils] implement a common method for command line parsing (#3001) --- Android.mk | 1 + .../libopenthread-cli-windows.vcxproj | 2 + etc/visual-studio/libopenthread.vcxproj | 2 + .../libopenthread.vcxproj.filters | 6 ++ etc/visual-studio/libopenthread_k.vcxproj | 2 + .../libopenthread_k.vcxproj.filters | 6 ++ src/cli/cli.cpp | 31 +++---- src/core/Makefile.am | 3 + src/core/utils/parse_cmdline.cpp | 79 ++++++++++++++++++ src/core/utils/parse_cmdline.hpp | 81 +++++++++++++++++++ src/diag/openthread-diag.cpp | 41 ++-------- tests/unit/Makefile.am | 3 +- 12 files changed, 198 insertions(+), 59 deletions(-) create mode 100644 src/core/utils/parse_cmdline.cpp create mode 100644 src/core/utils/parse_cmdline.hpp diff --git a/Android.mk b/Android.mk index 79c5c98a5..7a925ac36 100644 --- a/Android.mk +++ b/Android.mk @@ -168,6 +168,7 @@ LOCAL_SRC_FILES := \ src/core/utils/missing_strlcpy.c \ src/core/utils/missing_strlcat.c \ src/core/utils/missing_strnlen.c \ + src/core/utils/parse_cmdline.cpp \ src/core/utils/slaac_address.cpp \ src/diag/diag_process.cpp \ src/diag/openthread-diag.cpp \ diff --git a/etc/visual-studio/libopenthread-cli-windows.vcxproj b/etc/visual-studio/libopenthread-cli-windows.vcxproj index f36e63ac7..db367ff02 100644 --- a/etc/visual-studio/libopenthread-cli-windows.vcxproj +++ b/etc/visual-studio/libopenthread-cli-windows.vcxproj @@ -61,6 +61,8 @@ + + diff --git a/etc/visual-studio/libopenthread.vcxproj b/etc/visual-studio/libopenthread.vcxproj index c845381a9..e4603df29 100644 --- a/etc/visual-studio/libopenthread.vcxproj +++ b/etc/visual-studio/libopenthread.vcxproj @@ -148,6 +148,7 @@ + @@ -238,6 +239,7 @@ + diff --git a/etc/visual-studio/libopenthread.vcxproj.filters b/etc/visual-studio/libopenthread.vcxproj.filters index d7efac078..fdbebd734 100644 --- a/etc/visual-studio/libopenthread.vcxproj.filters +++ b/etc/visual-studio/libopenthread.vcxproj.filters @@ -327,6 +327,9 @@ Source Files\utils + + Source Files\utils + Source Files\utils @@ -593,6 +596,9 @@ Header Files\utils + + Header Files\utils + Header Files\utils diff --git a/etc/visual-studio/libopenthread_k.vcxproj b/etc/visual-studio/libopenthread_k.vcxproj index 7ef558d67..0e54e2b07 100644 --- a/etc/visual-studio/libopenthread_k.vcxproj +++ b/etc/visual-studio/libopenthread_k.vcxproj @@ -157,6 +157,7 @@ + @@ -272,6 +273,7 @@ + diff --git a/etc/visual-studio/libopenthread_k.vcxproj.filters b/etc/visual-studio/libopenthread_k.vcxproj.filters index 85ca29a7a..8d1b8a615 100644 --- a/etc/visual-studio/libopenthread_k.vcxproj.filters +++ b/etc/visual-studio/libopenthread_k.vcxproj.filters @@ -327,6 +327,9 @@ Source Files\utils + + Source Files\utils + Source Files\utils @@ -590,6 +593,9 @@ Header Files\utils + + Header Files\utils + Header Files\utils diff --git a/src/cli/cli.cpp b/src/cli/cli.cpp index 74e20e40f..c6d6136c7 100644 --- a/src/cli/cli.cpp +++ b/src/cli/cli.cpp @@ -39,6 +39,7 @@ #include #include +#include "utils/parse_cmdline.hpp" #include "utils/wrap_string.h" #include @@ -3477,33 +3478,19 @@ void Interpreter::ProcessDiag(int argc, char *argv[]) void Interpreter::ProcessLine(char *aBuf, uint16_t aBufLength, Server &aServer) { - char * argv[kMaxArgs]; + char * argv[kMaxArgs] = {NULL}; char * cmd; uint8_t argc = 0, i = 0; mServer = &aServer; - VerifyOrExit(aBuf != NULL); + VerifyOrExit(aBuf != NULL && strnlen(aBuf, aBufLength + 1) <= aBufLength); - for (; *aBuf == ' '; aBuf++, aBufLength--) - ; + VerifyOrExit(Utils::CmdLineParser::ParseCmd(aBuf, argc, argv, kMaxArgs) == OT_ERROR_NONE, + mServer->OutputFormat("Error: too many args (max %d)\r\n", kMaxArgs)); + VerifyOrExit(argc >= 1, mServer->OutputFormat("Error: no given command.\r\n")); - for (cmd = aBuf + 1; (cmd < aBuf + aBufLength) && (cmd != NULL); ++cmd) - { - VerifyOrExit(argc < kMaxArgs, mServer->OutputFormat("Error: too many args (max %d)\r\n", kMaxArgs)); - - if (*cmd == ' ' || *cmd == '\r' || *cmd == '\n') - { - *cmd = '\0'; - } - - if (*(cmd - 1) == '\0' && *cmd != ' ') - { - argv[argc++] = cmd; - } - } - - cmd = aBuf; + cmd = argv[0]; #if OPENTHREAD_ENABLE_DIAG VerifyOrExit( @@ -3515,7 +3502,7 @@ void Interpreter::ProcessLine(char *aBuf, uint16_t aBufLength, Server &aServer) { if (strcmp(cmd, sCommands[i].mName) == 0) { - (this->*sCommands[i].mCommand)(argc, argv); + (this->*sCommands[i].mCommand)(argc - 1, &argv[1]); break; } } @@ -3528,7 +3515,7 @@ void Interpreter::ProcessLine(char *aBuf, uint16_t aBufLength, Server &aServer) { if (strcmp(cmd, mUserCommands[i].mName) == 0) { - mUserCommands[i].mCommand(argc, argv); + mUserCommands[i].mCommand(argc - 1, &argv[1]); break; } } diff --git a/src/core/Makefile.am b/src/core/Makefile.am index 785ed2d6e..51266cda0 100644 --- a/src/core/Makefile.am +++ b/src/core/Makefile.am @@ -213,6 +213,7 @@ SOURCES_COMMON = \ utils/missing_strlcat.c \ utils/missing_strlcpy.c \ utils/missing_strnlen.c \ + utils/parse_cmdline.cpp \ utils/slaac_address.cpp \ $(NULL) @@ -233,6 +234,7 @@ libopenthread_radio_a_SOURCES = \ utils/missing_strlcat.c \ utils/missing_strlcpy.c \ utils/missing_strnlen.c \ + utils/parse_cmdline.cpp \ $(NULL) libopenthread_mtd_a_SOURCES = \ @@ -346,6 +348,7 @@ HEADERS_COMMON = \ utils/child_supervision.hpp \ utils/heap.hpp \ utils/jam_detector.hpp \ + utils/parse_cmdline.hpp \ utils/slaac_address.hpp \ utils/wrap_stdbool.h \ utils/wrap_stdint.h \ diff --git a/src/core/utils/parse_cmdline.cpp b/src/core/utils/parse_cmdline.cpp new file mode 100644 index 000000000..9da77ee9f --- /dev/null +++ b/src/core/utils/parse_cmdline.cpp @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2018, 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. + */ + +/** + * @file + * This file implements the command line parser. + */ + +#include "parse_cmdline.hpp" + +#include "common/code_utils.hpp" + +namespace ot { +namespace Utils { + +static bool IsSpaceOrNewLine(char aChar) +{ + return (aChar == ' ') || (aChar == '\t') || (aChar == '\r') || (aChar == '\n'); +} + +otError CmdLineParser::ParseCmd(char *aString, uint8_t &aArgc, char *aArgv[], uint8_t aArgcMax) +{ + otError error = OT_ERROR_NONE; + char * cmd; + + aArgc = 0; + + for (cmd = aString; IsSpaceOrNewLine(*cmd) && *cmd; cmd++) + ; + + if (*cmd) + { + aArgv[aArgc++] = cmd++; // the first argument + } + + for (; *cmd; cmd++) + { + if (IsSpaceOrNewLine(*cmd)) + { + *cmd = '\0'; + } + else if (*(cmd - 1) == '\0') + { + VerifyOrExit(aArgc < aArgcMax, error = OT_ERROR_INVALID_ARGS); + aArgv[aArgc++] = cmd; + } + } + +exit: + return error; +} + +} // namespace Utils +} // namespace ot diff --git a/src/core/utils/parse_cmdline.hpp b/src/core/utils/parse_cmdline.hpp new file mode 100644 index 000000000..f2ec0025c --- /dev/null +++ b/src/core/utils/parse_cmdline.hpp @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2018, 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. + */ + +/** + * @file + * This file includes definitions for command line parser. + */ + +#ifndef PARSE_CMD_LINE_HPP_ +#define PARSE_CMD_LINE_HPP_ + +#include +#include + +namespace ot { +namespace Utils { + +/** + * @addtogroup utils-parse-cmd-line + * + * @brief + * This module includes definitions for command line parser. + * + * @{ + */ + +/** + * This class implements the command line parser. + * + */ +class CmdLineParser +{ +public: + /** + * This function parses the command line. + * + * Note: this method may change the input @p aString, it will put a '\0' by the end of each argument, + * and @p aArgv will point to the arguments in the input @p aString. + * + * @param[in] aString A NULL-terminated input string. + * @param[out] aArgc The argument counter of the command line. + * @param[out] aArgv The argument vector of the command line. + * @param[in] aArgcMax The maximum argument counter. + * + */ + static otError ParseCmd(char *aString, uint8_t &aArgc, char *aArgv[], uint8_t aArgcMax); +}; + +/** + * @} + */ + +} // namespace Utils +} // namespace ot + +#endif // PARSE_CMD_LINE_HPP_ diff --git a/src/diag/openthread-diag.cpp b/src/diag/openthread-diag.cpp index 13f4fdaf0..762054731 100644 --- a/src/diag/openthread-diag.cpp +++ b/src/diag/openthread-diag.cpp @@ -35,6 +35,7 @@ #include #include +#include "utils/parse_cmdline.hpp" #include "utils/wrap_string.h" #include @@ -54,16 +55,6 @@ void otDiagProcessCmd(int aArgCount, char *aArgVector[], char *aOutput, size_t a Diag::ProcessCmd(aArgCount, aArgVector, aOutput, aOutputMaxLen); } -static bool IsSpace(char aChar) -{ - return (aChar == ' ') || (aChar == '\t'); -} - -static bool IsNullOrNewline(char aChar) -{ - return (aChar == 0) || (aChar == '\n') || (aChar == '\r'); -} - void otDiagProcessCmdLine(const char *aInput, char *aOutput, size_t aOutputMaxLen) { enum @@ -75,34 +66,12 @@ void otDiagProcessCmdLine(const char *aInput, char *aOutput, size_t aOutputMaxLe otError error = OT_ERROR_NONE; char buffer[kMaxCommandBuffer]; char *argVector[kMaxArgs]; - int argCount = 0; - char *bufPtr = &buffer[0]; - uint16_t bufLen = sizeof(buffer); + uint8_t argCount = 0; - while (!IsNullOrNewline(*aInput)) - { - while (IsSpace(*aInput)) - { - aInput++; - } + VerifyOrExit(strnlen(aInput, kMaxCommandBuffer) < kMaxCommandBuffer, error = OT_ERROR_NO_BUFS); - argVector[argCount] = bufPtr; - - while (!IsSpace(*aInput) && !IsNullOrNewline(*aInput)) - { - *bufPtr++ = *aInput++; - VerifyOrExit(--bufLen > 0, error = OT_ERROR_NO_BUFS); - } - - if (argVector[argCount] != bufPtr) - { - *bufPtr++ = 0; - VerifyOrExit(--bufLen > 0, error = OT_ERROR_NO_BUFS); - - argCount++; - VerifyOrExit(argCount < kMaxArgs, error = OT_ERROR_INVALID_ARGS); - } - } + strcpy(buffer, aInput); + error = ot::Utils::CmdLineParser::ParseCmd(buffer, argCount, argVector, kMaxArgs); exit: diff --git a/tests/unit/Makefile.am b/tests/unit/Makefile.am index f388e5502..109b61caa 100644 --- a/tests/unit/Makefile.am +++ b/tests/unit/Makefile.am @@ -228,7 +228,8 @@ test_toolchain_LDADD = $(COMMON_LDADD) test_toolchain_SOURCES = test_platform.cpp test_toolchain.cpp test_toolchain_c.c if OPENTHREAD_ENABLE_DIAG -test_diag_LDADD = $(top_builddir)/src/diag/libopenthread-diag.a +test_diag_LDADD = $(top_builddir)/src/diag/libopenthread-diag.a \ + $(COMMON_LDADD) test_diag_SOURCES = test_diag.cpp if OPENTHREAD_ENABLE_POSIX_APP test_diag_LDADD += $(top_builddir)/src/posix/platform/libopenthread-posix.a \