Fix for x86_64 build fail
[platform/upstream/connectedhomeip.git] / src / lib / shell / shell.cpp
1 /*
2  *
3  *    Copyright (c) 2020 Project CHIP Authors
4  *    Copyright (c) 2017 Google LLC
5  *
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
9  *
10  *        http://www.apache.org/licenses/LICENSE-2.0
11  *
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.
17  */
18
19 /**
20  *    @file
21  *      Source implementation for a generic shell API for CHIP examples.
22  */
23
24 #include "shell.h"
25 #include "commands.h"
26
27 #include <core/CHIPError.h>
28 #include <support/CodeUtils.h>
29 #include <support/logging/CHIPLogging.h>
30
31 #include <assert.h>
32 #include <ctype.h>
33 #include <errno.h>
34 #include <stdbool.h>
35 #include <stdint.h>
36 #include <string.h>
37
38 using namespace chip::Logging;
39
40 namespace chip {
41 namespace Shell {
42
43 Shell Shell::theShellRoot;
44
45 intptr_t shell_line_read(char * buffer, size_t max)
46 {
47     ssize_t read = 0;
48     bool done    = false;
49     char * inptr = buffer;
50
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)
53     {
54         if (read == 0)
55         {
56             read = streamer_read(streamer_get(), inptr, 1);
57         }
58
59         // Process any characters we just read in.
60         while (read > 0)
61         {
62             switch (*inptr)
63             {
64             case '\r':
65             case '\n':
66                 streamer_printf(streamer_get(), "\r\n");
67                 *inptr = 0; // null terminate
68                 done   = true;
69                 break;
70             case 0x7F:
71                 // delete backspace character + 1 more
72                 inptr -= 2;
73                 if (inptr >= buffer - 1)
74                 {
75                     streamer_printf(streamer_get(), "\b \b");
76                 }
77                 else
78                 {
79                     inptr = buffer - 1;
80                 }
81                 break;
82             default:
83                 if (isprint(static_cast<int>(*inptr)) || *inptr == '\t')
84                 {
85                     streamer_printf(streamer_get(), "%c", *inptr);
86                 }
87                 else
88                 {
89                     inptr--;
90                 }
91                 break;
92             }
93
94             inptr++;
95             read--;
96         }
97     }
98
99     return inptr - buffer;
100 }
101
102 void Shell::ForEachCommand(shell_command_iterator_t * on_command, void * arg)
103 {
104     for (unsigned i = 0; i < _commandSetCount; i++)
105     {
106         for (unsigned j = 0; j < _commandSetSize[i]; j++)
107         {
108             if (on_command(&_commandSet[i][j], arg))
109             {
110                 return;
111             }
112         }
113     }
114 }
115
116 void Shell::RegisterCommands(shell_command_t * command_set, unsigned count)
117 {
118     if (_commandSetCount >= CHIP_SHELL_MAX_MODULES)
119     {
120         ChipLogError(Shell, "Max number of modules reached\n");
121         assert(0);
122     }
123
124     _commandSet[_commandSetCount]     = command_set;
125     _commandSetSize[_commandSetCount] = count;
126     ++_commandSetCount;
127 }
128
129 int Shell::ExecCommand(int argc, char * argv[])
130 {
131     int retval = CHIP_ERROR_INVALID_ARGUMENT;
132
133     // Find the command
134     for (unsigned i = 0; i < _commandSetCount; i++)
135     {
136         for (unsigned j = 0; j < _commandSetSize[i]; j++)
137         {
138             if (strcmp(argv[0], _commandSet[i][j].cmd_name) == 0)
139             {
140                 // Execute the command!
141                 retval = _commandSet[i][j].cmd_func(argc - 1, argv + 1);
142                 break;
143             }
144         }
145     }
146
147     return retval;
148 }
149
150 static bool IsSeparator(char aChar)
151 {
152     return (aChar == ' ') || (aChar == '\t') || (aChar == '\r') || (aChar == '\n');
153 }
154
155 static bool IsEscape(char aChar)
156 {
157     return (aChar == '\\');
158 }
159
160 static bool IsEscapable(char aChar)
161 {
162     return IsSeparator(aChar) || IsEscape(aChar);
163 }
164
165 int Shell::TokenizeLine(char * buffer, char ** tokens, int max_tokens)
166 {
167     size_t len = strlen(buffer);
168     int cursor = 0;
169     size_t i   = 0;
170
171     // Strip leading spaces
172     while (buffer[i] && buffer[i] == ' ')
173     {
174         i++;
175     }
176
177     VerifyOrExit((len - i) > 0, cursor = 0);
178
179     // The first token starts at the beginning.
180     tokens[cursor++] = &buffer[i];
181
182     for (; i < len && cursor < max_tokens; i++)
183     {
184         if (IsEscape(buffer[i]) && IsEscapable(buffer[i + 1]))
185         {
186             // include the null terminator: strlen(cmd) = strlen(cmd + 1) + 1
187             memmove(&buffer[i], &buffer[i + 1], strlen(&buffer[i]));
188         }
189         else if (IsSeparator(buffer[i]))
190         {
191             buffer[i] = 0;
192             if (!IsSeparator(buffer[i + 1]))
193             {
194                 tokens[cursor++] = &buffer[i + 1];
195             }
196         }
197     }
198
199     tokens[cursor] = nullptr;
200
201 exit:
202     return cursor;
203 }
204
205 void Shell::TaskLoop(void * arg)
206 {
207     int retval;
208     int argc;
209     char * argv[CHIP_SHELL_MAX_TOKENS];
210     char line[CHIP_SHELL_MAX_LINE_SIZE];
211
212     theShellRoot.RegisterDefaultCommands();
213
214     while (true)
215     {
216         streamer_printf(streamer_get(), CHIP_SHELL_PROMPT);
217
218         shell_line_read(line, sizeof(line));
219         argc = shell_line_tokenize(line, argv, CHIP_SHELL_MAX_TOKENS);
220
221         if (argc > 0)
222         {
223             retval = theShellRoot.ExecCommand(argc, argv);
224
225             if (retval)
226             {
227                 char errorStr[160];
228                 bool errorStrFound = FormatCHIPError(errorStr, sizeof(errorStr), retval);
229                 if (!errorStrFound)
230                 {
231                     errorStr[0] = 0;
232                 }
233                 streamer_printf(streamer_get(), "Error %s: %s\r\n", argv[0], errorStr);
234             }
235             else
236             {
237                 streamer_printf(streamer_get(), "Done\r\n", argv[0]);
238             }
239         }
240         else
241         {
242             // Empty input has no output -- just display prompt
243         }
244     }
245 }
246
247 } // namespace Shell
248 } // namespace chip