@@ -129,11 +129,11 @@ void go_through_commands(esp_cli_command_sets_t *cmd_sets, void *cmd_walker_ctx,
129129 }
130130
131131 esp_cli_command_set_t * dynamic_set = cmd_sets ? & cmd_sets -> dynamic_set : NULL ;
132- /* it is possible that the set is empty, in which case set dynamic_set to NULL
133- * to prevent the for loop to try to access a list of commands pointer set to NULL */
134- if ( dynamic_set && ! dynamic_set -> cmd_ptr_set ) {
135- dynamic_set = NULL ;
136- }
132+ /* Note: unlike FOR_EACH_STATIC_COMMAND which uses the comma operator,
133+ * FOR_EACH_DYNAMIC_COMMAND uses && short-circuit evaluation, so it is safe
134+ * to pass a set with cmd_ptr_set == NULL and cmd_set_size == 0.
135+ * We must NOT null out the set here, because FOR_EACH_DYNAMIC_COMMAND( NULL, ...)
136+ * means "walk ALL dynamic commands", bypassing the set filter. */
137137 esp_cli_dynamic_commands_lock ();
138138 FOR_EACH_DYNAMIC_COMMAND (dynamic_set , cmd ) {
139139 continue_walk = cmd_walker (cmd_walker_ctx , cmd );
@@ -325,6 +325,13 @@ esp_cli_command_t *esp_cli_commands_find_command(esp_cli_command_set_handle_t cm
325325 find_cmd_ctx_t ctx = { .cmd = NULL , .name = name };
326326 go_through_commands (cmd_set , & ctx , compare_command_name );
327327
328+ /* The "help" command is a built-in registered by esp_cli_commands itself.
329+ * It must always be found regardless of the command set passed by the user.
330+ * If not found in the filtered set, search all commands. */
331+ if (!ctx .cmd && cmd_set != NULL && strcmp (name , "help" ) == 0 ) {
332+ go_through_commands (NULL , & ctx , compare_command_name );
333+ }
334+
328335 /* if command was found during the walk, cmd field will be populated with
329336 * the command matching the name given in parameter, otherwise it will still
330337 * be NULL (value set as default value above) */
@@ -541,6 +548,12 @@ void esp_cli_commands_get_completion(esp_cli_command_set_handle_t cmd_set, const
541548 .completion_cb = completion_cb
542549 };
543550 go_through_commands (cmd_set , & ctx , call_completion_cb );
551+
552+ /* The "help" command is a built-in that must always be completable
553+ * regardless of the command set */
554+ if (cmd_set != NULL && len <= 4 && strncmp (buf , "help" , len ) == 0 ) {
555+ completion_cb (cb_ctx , "help" );
556+ }
544557}
545558
546559const char * esp_cli_commands_get_hint (esp_cli_command_set_handle_t cmd_set , const char * buf , int * color , bool * bold )
@@ -740,6 +753,22 @@ static int help_command(void *context, esp_cli_commands_exec_arg_t *cmd_args, in
740753 };
741754 go_through_commands (cmd_sets , & ctx , call_command_funcs );
742755
756+ /* The "help" command is a built-in that must always appear in listings
757+ * regardless of the command set */
758+ if (cmd_sets != NULL ) {
759+ esp_cli_command_t * help_cmd = esp_cli_commands_find_command (NULL , "help" );
760+ if (help_cmd ) {
761+ if (!command_name ) {
762+ /* Listing all commands: also print the help entry */
763+ print_verbose_level_arr [verbose_level ](cmd_args , help_cmd );
764+ } else if (strcmp (command_name , "help" ) == 0 ) {
765+ /* User asked specifically for "help help" */
766+ print_verbose_level_arr [verbose_level ](cmd_args , help_cmd );
767+ ctx .command_found = true;
768+ }
769+ }
770+ }
771+
743772 if (command_name && !ctx .command_found ) {
744773 ESP_CLI_COMMANDS_FD_PRINT (cmd_args -> out_fd , cmd_args -> write_func , "help: invalid command name %s\n" , command_name );
745774 return 1 ;
0 commit comments