1 /*
2  * FreeRTOS V202212.00
3  * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a copy of
6  * this software and associated documentation files (the "Software"), to deal in
7  * the Software without restriction, including without limitation the rights to
8  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9  * the Software, and to permit persons to whom the Software is furnished to do so,
10  * subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be included in all
13  * copies or substantial portions of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17  * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18  * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19  * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21  *
22  * https://www.FreeRTOS.org
23  * https://github.com/FreeRTOS
24  *
25  */
26 
27 /* Standard includes. */
28 #include <string.h>
29 #include <stdint.h>
30 
31 /* FreeRTOS includes. */
32 #include "FreeRTOS.h"
33 #include "task.h"
34 
35 /* Utils includes. */
36 #include "FreeRTOS_CLI.h"
37 
38 /* If the application writer needs to place the buffer used by the CLI at a
39  * fixed address then set configAPPLICATION_PROVIDES_cOutputBuffer to 1 in
40  * FreeRTOSConfig.h, then declare an array with the following name and size in
41  * one of the application files:
42  *  char cOutputBuffer[ configCOMMAND_INT_MAX_OUTPUT_SIZE ];
43  */
44 #ifndef configAPPLICATION_PROVIDES_cOutputBuffer
45     #define configAPPLICATION_PROVIDES_cOutputBuffer    0
46 #endif
47 
48 /*
49  * Register the command passed in using the pxCommandToRegister parameter
50  * and using pxCliDefinitionListItemBuffer as the memory for command line
51  * list items. Registering a command adds the command to the list of
52  * commands that are handled by the command interpreter.  Once a command
53  * has been registered it can be executed from the command line.
54  */
55 static void prvRegisterCommand( const CLI_Command_Definition_t * const pxCommandToRegister,
56                                 CLI_Definition_List_Item_t * pxCliDefinitionListItemBuffer );
57 
58 /*
59  * The callback function that is executed when "help" is entered.  This is the
60  * only default command that is always present.
61  */
62 static BaseType_t prvHelpCommand( char * pcWriteBuffer,
63                                   size_t xWriteBufferLen,
64                                   const char * pcCommandString );
65 
66 /*
67  * Return the number of parameters that follow the command name.
68  */
69 static int8_t prvGetNumberOfParameters( const char * pcCommandString );
70 
71 /* The definition of the "help" command.  This command is always at the front
72  * of the list of registered commands. */
73 static const CLI_Command_Definition_t xHelpCommand =
74 {
75     "help",
76     "\r\nhelp:\r\n Lists all the registered commands\r\n\r\n",
77     prvHelpCommand,
78     0
79 };
80 
81 /* The definition of the list of commands.  Commands that are registered are
82  * added to this list. */
83 static CLI_Definition_List_Item_t xRegisteredCommands =
84 {
85     &xHelpCommand, /* The first command in the list is always the help command, defined in this file. */
86     NULL           /* The next pointer is initialised to NULL, as there are no other registered commands yet. */
87 };
88 
89 /* A buffer into which command outputs can be written is declared here, rather
90 * than in the command console implementation, to allow multiple command consoles
91 * to share the same buffer.  For example, an application may allow access to the
92 * command interpreter by UART and by Ethernet.  Sharing a buffer is done purely
93 * to save RAM.  Note, however, that the command console itself is not re-entrant,
94 * so only one command interpreter interface can be used at any one time.  For that
95 * reason, no attempt at providing mutual exclusion to the cOutputBuffer array is
96 * attempted.
97 *
98 * configAPPLICATION_PROVIDES_cOutputBuffer is provided to allow the application
99 * writer to provide their own cOutputBuffer declaration in cases where the
100 * buffer needs to be placed at a fixed address (rather than by the linker). */
101 #if ( configAPPLICATION_PROVIDES_cOutputBuffer == 0 )
102     static char cOutputBuffer[ configCOMMAND_INT_MAX_OUTPUT_SIZE ];
103 #else
104     extern char cOutputBuffer[ configCOMMAND_INT_MAX_OUTPUT_SIZE ];
105 #endif
106 
107 
108 /*-----------------------------------------------------------*/
109 
110 #if ( configSUPPORT_DYNAMIC_ALLOCATION == 1 )
111 
FreeRTOS_CLIRegisterCommand(const CLI_Command_Definition_t * const pxCommandToRegister)112     BaseType_t FreeRTOS_CLIRegisterCommand( const CLI_Command_Definition_t * const pxCommandToRegister )
113     {
114         BaseType_t xReturn = pdFAIL;
115         CLI_Definition_List_Item_t * pxNewListItem;
116 
117         /* Check the parameter is not NULL. */
118         configASSERT( pxCommandToRegister != NULL );
119 
120         /* Create a new list item that will reference the command being registered. */
121         pxNewListItem = ( CLI_Definition_List_Item_t * ) pvPortMalloc( sizeof( CLI_Definition_List_Item_t ) );
122         configASSERT( pxNewListItem != NULL );
123 
124         if( pxNewListItem != NULL )
125         {
126             prvRegisterCommand( pxCommandToRegister, pxNewListItem );
127             xReturn = pdPASS;
128         }
129 
130         return xReturn;
131     }
132 
133 #endif /* #if ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) */
134 /*-----------------------------------------------------------*/
135 
136 #if ( configSUPPORT_STATIC_ALLOCATION == 1 )
137 
FreeRTOS_CLIRegisterCommandStatic(const CLI_Command_Definition_t * const pxCommandToRegister,CLI_Definition_List_Item_t * pxCliDefinitionListItemBuffer)138     BaseType_t FreeRTOS_CLIRegisterCommandStatic( const CLI_Command_Definition_t * const pxCommandToRegister,
139                                                   CLI_Definition_List_Item_t * pxCliDefinitionListItemBuffer )
140     {
141         /* Check the parameters are not NULL. */
142         configASSERT( pxCommandToRegister != NULL );
143         configASSERT( pxCliDefinitionListItemBuffer != NULL );
144 
145         prvRegisterCommand( pxCommandToRegister, pxCliDefinitionListItemBuffer );
146 
147         return pdPASS;
148     }
149 
150 #endif /* #if ( configSUPPORT_STATIC_ALLOCATION == 1 ) */
151 /*-----------------------------------------------------------*/
152 
FreeRTOS_CLIProcessCommand(const char * const pcCommandInput,char * pcWriteBuffer,size_t xWriteBufferLen)153 BaseType_t FreeRTOS_CLIProcessCommand( const char * const pcCommandInput,
154                                        char * pcWriteBuffer,
155                                        size_t xWriteBufferLen )
156 {
157     static const CLI_Definition_List_Item_t * pxCommand = NULL;
158     BaseType_t xReturn = pdTRUE;
159     const char * pcRegisteredCommandString;
160     size_t xCommandStringLength;
161 
162     /* Note:  This function is not re-entrant.  It must not be called from more
163      * thank one task. */
164 
165     if( pxCommand == NULL )
166     {
167         /* Search for the command string in the list of registered commands. */
168         for( pxCommand = &xRegisteredCommands; pxCommand != NULL; pxCommand = pxCommand->pxNext )
169         {
170             pcRegisteredCommandString = pxCommand->pxCommandLineDefinition->pcCommand;
171             xCommandStringLength = strlen( pcRegisteredCommandString );
172 
173             /* To ensure the string lengths match exactly, so as not to pick up
174              * a sub-string of a longer command, check the byte after the expected
175              * end of the string is either the end of the string or a space before
176              * a parameter. */
177             if( strncmp( pcCommandInput, pcRegisteredCommandString, xCommandStringLength ) == 0 )
178             {
179                 if( ( pcCommandInput[ xCommandStringLength ] == ' ' ) || ( pcCommandInput[ xCommandStringLength ] == 0x00 ) )
180                 {
181                     /* The command has been found.  Check it has the expected
182                      * number of parameters.  If cExpectedNumberOfParameters is -1,
183                      * then there could be a variable number of parameters and no
184                      * check is made. */
185                     if( pxCommand->pxCommandLineDefinition->cExpectedNumberOfParameters >= 0 )
186                     {
187                         if( prvGetNumberOfParameters( pcCommandInput ) != pxCommand->pxCommandLineDefinition->cExpectedNumberOfParameters )
188                         {
189                             xReturn = pdFALSE;
190                         }
191                     }
192 
193                     break;
194                 }
195             }
196         }
197     }
198 
199     if( ( pxCommand != NULL ) && ( xReturn == pdFALSE ) )
200     {
201         /* The command was found, but the number of parameters with the command
202          * was incorrect. */
203         strncpy( pcWriteBuffer, "Incorrect command parameter(s).  Enter \"help\" to view a list of available commands.\r\n\r\n", xWriteBufferLen );
204         pxCommand = NULL;
205     }
206     else if( pxCommand != NULL )
207     {
208         /* Call the callback function that is registered to this command. */
209         xReturn = pxCommand->pxCommandLineDefinition->pxCommandInterpreter( pcWriteBuffer, xWriteBufferLen, pcCommandInput );
210 
211         /* If xReturn is pdFALSE, then no further strings will be returned
212          * after this one, and	pxCommand can be reset to NULL ready to search
213          * for the next entered command. */
214         if( xReturn == pdFALSE )
215         {
216             pxCommand = NULL;
217         }
218     }
219     else
220     {
221         /* pxCommand was NULL, the command was not found. */
222         strncpy( pcWriteBuffer, "Command not recognised.  Enter 'help' to view a list of available commands.\r\n\r\n", xWriteBufferLen );
223         xReturn = pdFALSE;
224     }
225 
226     return xReturn;
227 }
228 /*-----------------------------------------------------------*/
229 
FreeRTOS_CLIGetOutputBuffer(void)230 char * FreeRTOS_CLIGetOutputBuffer( void )
231 {
232     return cOutputBuffer;
233 }
234 /*-----------------------------------------------------------*/
235 
FreeRTOS_CLIGetParameter(const char * pcCommandString,UBaseType_t uxWantedParameter,BaseType_t * pxParameterStringLength)236 const char * FreeRTOS_CLIGetParameter( const char * pcCommandString,
237                                        UBaseType_t uxWantedParameter,
238                                        BaseType_t * pxParameterStringLength )
239 {
240     UBaseType_t uxParametersFound = 0;
241     const char * pcReturn = NULL;
242 
243     *pxParameterStringLength = 0;
244 
245     while( uxParametersFound < uxWantedParameter )
246     {
247         /* Index the character pointer past the current word.  If this is the start
248          * of the command string then the first word is the command itself. */
249         while( ( ( *pcCommandString ) != 0x00 ) && ( ( *pcCommandString ) != ' ' ) )
250         {
251             pcCommandString++;
252         }
253 
254         /* Find the start of the next string. */
255         while( ( ( *pcCommandString ) != 0x00 ) && ( ( *pcCommandString ) == ' ' ) )
256         {
257             pcCommandString++;
258         }
259 
260         /* Was a string found? */
261         if( *pcCommandString != 0x00 )
262         {
263             /* Is this the start of the required parameter? */
264             uxParametersFound++;
265 
266             if( uxParametersFound == uxWantedParameter )
267             {
268                 /* How long is the parameter? */
269                 pcReturn = pcCommandString;
270 
271                 while( ( ( *pcCommandString ) != 0x00 ) && ( ( *pcCommandString ) != ' ' ) )
272                 {
273                     ( *pxParameterStringLength )++;
274                     pcCommandString++;
275                 }
276 
277                 if( *pxParameterStringLength == 0 )
278                 {
279                     pcReturn = NULL;
280                 }
281 
282                 break;
283             }
284         }
285         else
286         {
287             break;
288         }
289     }
290 
291     return pcReturn;
292 }
293 /*-----------------------------------------------------------*/
294 
prvRegisterCommand(const CLI_Command_Definition_t * const pxCommandToRegister,CLI_Definition_List_Item_t * pxCliDefinitionListItemBuffer)295 static void prvRegisterCommand( const CLI_Command_Definition_t * const pxCommandToRegister,
296                                 CLI_Definition_List_Item_t * pxCliDefinitionListItemBuffer )
297 {
298     static CLI_Definition_List_Item_t * pxLastCommandInList = &xRegisteredCommands;
299 
300     /* Check the parameters are not NULL. */
301     configASSERT( pxCommandToRegister != NULL );
302     configASSERT( pxCliDefinitionListItemBuffer != NULL );
303 
304     taskENTER_CRITICAL();
305     {
306         /* Reference the command being registered from the newly created
307          * list item. */
308         pxCliDefinitionListItemBuffer->pxCommandLineDefinition = pxCommandToRegister;
309 
310         /* The new list item will get added to the end of the list, so
311          * pxNext has nowhere to point. */
312         pxCliDefinitionListItemBuffer->pxNext = NULL;
313 
314         /* Add the newly created list item to the end of the already existing
315          * list. */
316         pxLastCommandInList->pxNext = pxCliDefinitionListItemBuffer;
317 
318         /* Set the end of list marker to the new list item. */
319         pxLastCommandInList = pxCliDefinitionListItemBuffer;
320     }
321     taskEXIT_CRITICAL();
322 }
323 /*-----------------------------------------------------------*/
324 
prvHelpCommand(char * pcWriteBuffer,size_t xWriteBufferLen,const char * pcCommandString)325 static BaseType_t prvHelpCommand( char * pcWriteBuffer,
326                                   size_t xWriteBufferLen,
327                                   const char * pcCommandString )
328 {
329     static const CLI_Definition_List_Item_t * pxCommand = NULL;
330     BaseType_t xReturn;
331 
332     ( void ) pcCommandString;
333 
334     if( pxCommand == NULL )
335     {
336         /* Reset the pxCommand pointer back to the start of the list. */
337         pxCommand = &xRegisteredCommands;
338     }
339 
340     /* Return the next command help string, before moving the pointer on to
341      * the next command in the list. */
342     strncpy( pcWriteBuffer, pxCommand->pxCommandLineDefinition->pcHelpString, xWriteBufferLen );
343     pxCommand = pxCommand->pxNext;
344 
345     if( pxCommand == NULL )
346     {
347         /* There are no more commands in the list, so there will be no more
348          *  strings to return after this one and pdFALSE should be returned. */
349         xReturn = pdFALSE;
350     }
351     else
352     {
353         xReturn = pdTRUE;
354     }
355 
356     return xReturn;
357 }
358 /*-----------------------------------------------------------*/
359 
prvGetNumberOfParameters(const char * pcCommandString)360 static int8_t prvGetNumberOfParameters( const char * pcCommandString )
361 {
362     int8_t cParameters = 0;
363     BaseType_t xLastCharacterWasSpace = pdFALSE;
364 
365     /* Count the number of space delimited words in pcCommandString. */
366     while( *pcCommandString != 0x00 )
367     {
368         if( ( *pcCommandString ) == ' ' )
369         {
370             if( xLastCharacterWasSpace != pdTRUE )
371             {
372                 cParameters++;
373                 xLastCharacterWasSpace = pdTRUE;
374             }
375         }
376         else
377         {
378             xLastCharacterWasSpace = pdFALSE;
379         }
380 
381         pcCommandString++;
382     }
383 
384     /* If the command string ended with spaces, then there will have been too
385      * many parameters counted. */
386     if( xLastCharacterWasSpace == pdTRUE )
387     {
388         cParameters--;
389     }
390 
391     /* The value returned is one less than the number of space delimited words,
392      * as the first word should be the command itself. */
393     return cParameters;
394 }
395 /*-----------------------------------------------------------*/
396