3 * Copyright (c) 2020 Project CHIP Authors
4 * Copyright (c) 2017 Google LLC
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
21 * Source implementation for a generic shell API for CHIP examples.
27 #include <core/CHIPError.h>
28 #include <support/CodeUtils.h>
29 #include <support/logging/CHIPLogging.h>
38 using namespace chip::Logging;
43 Shell Shell::theShellRoot;
45 intptr_t shell_line_read(char * buffer, size_t max)
49 char * inptr = buffer;
51 // Read in characters until we get a new line or we hit our max size.
52 while (((inptr - buffer) < static_cast<int>(max)) && !done)
56 read = streamer_read(streamer_get(), inptr, 1);
59 // Process any characters we just read in.
66 streamer_printf(streamer_get(), "\r\n");
67 *inptr = 0; // null terminate
71 // delete backspace character + 1 more
73 if (inptr >= buffer - 1)
75 streamer_printf(streamer_get(), "\b \b");
83 if (isprint(static_cast<int>(*inptr)) || *inptr == '\t')
85 streamer_printf(streamer_get(), "%c", *inptr);
99 return inptr - buffer;
102 void Shell::ForEachCommand(shell_command_iterator_t * on_command, void * arg)
104 for (unsigned i = 0; i < _commandSetCount; i++)
106 for (unsigned j = 0; j < _commandSetSize[i]; j++)
108 if (on_command(&_commandSet[i][j], arg))
116 void Shell::RegisterCommands(shell_command_t * command_set, unsigned count)
118 if (_commandSetCount >= CHIP_SHELL_MAX_MODULES)
120 ChipLogError(Shell, "Max number of modules reached\n");
124 _commandSet[_commandSetCount] = command_set;
125 _commandSetSize[_commandSetCount] = count;
129 int Shell::ExecCommand(int argc, char * argv[])
131 int retval = CHIP_ERROR_INVALID_ARGUMENT;
134 for (unsigned i = 0; i < _commandSetCount; i++)
136 for (unsigned j = 0; j < _commandSetSize[i]; j++)
138 if (strcmp(argv[0], _commandSet[i][j].cmd_name) == 0)
140 // Execute the command!
141 retval = _commandSet[i][j].cmd_func(argc - 1, argv + 1);
150 static bool IsSeparator(char aChar)
152 return (aChar == ' ') || (aChar == '\t') || (aChar == '\r') || (aChar == '\n');
155 static bool IsEscape(char aChar)
157 return (aChar == '\\');
160 static bool IsEscapable(char aChar)
162 return IsSeparator(aChar) || IsEscape(aChar);
165 int Shell::TokenizeLine(char * buffer, char ** tokens, int max_tokens)
167 size_t len = strlen(buffer);
171 // Strip leading spaces
172 while (buffer[i] && buffer[i] == ' ')
177 VerifyOrExit((len - i) > 0, cursor = 0);
179 // The first token starts at the beginning.
180 tokens[cursor++] = &buffer[i];
182 for (; i < len && cursor < max_tokens; i++)
184 if (IsEscape(buffer[i]) && IsEscapable(buffer[i + 1]))
186 // include the null terminator: strlen(cmd) = strlen(cmd + 1) + 1
187 memmove(&buffer[i], &buffer[i + 1], strlen(&buffer[i]));
189 else if (IsSeparator(buffer[i]))
192 if (!IsSeparator(buffer[i + 1]))
194 tokens[cursor++] = &buffer[i + 1];
199 tokens[cursor] = nullptr;
205 void Shell::TaskLoop(void * arg)
209 char * argv[CHIP_SHELL_MAX_TOKENS];
210 char line[CHIP_SHELL_MAX_LINE_SIZE];
212 theShellRoot.RegisterDefaultCommands();
216 streamer_printf(streamer_get(), CHIP_SHELL_PROMPT);
218 shell_line_read(line, sizeof(line));
219 argc = shell_line_tokenize(line, argv, CHIP_SHELL_MAX_TOKENS);
223 retval = theShellRoot.ExecCommand(argc, argv);
228 bool errorStrFound = FormatCHIPError(errorStr, sizeof(errorStr), retval);
233 streamer_printf(streamer_get(), "Error %s: %s\r\n", argv[0], errorStr);
237 streamer_printf(streamer_get(), "Done\r\n", argv[0]);
242 // Empty input has no output -- just display prompt