Subcommand Trees
Subcommands allow organizing related operations under a single parent command, similar to how tools like git <branch> or aws ec2 describe-instances structure their CLI surface. Each subcommand is a full WshShellCmd_t with its own options, handler, groups, and (optionally) further subcommands.
Enabling Subcommands
Subcommands are gated by a config flag so projects that don't need them pay no RAM/flash cost.
WSH_SHELL_SUBCOMMANDS— master switch. When0, theSubCmds/SubCmdNumfields ofWshShellCmd_tare compiled out and the dispatcher treats commands as flat.WSH_SHELL_SUBCOMMANDS_MAX_DEPTH— hard cap on how deep the shell descends. Protects against accidental loops in user-supplied tables and bounds stack/iteration cost.
Declaring a Subcommand Tree
Every node — root, parent, leaf — is a full WshShellCmd_t. Each level can declare its own options, handler, access groups, and further children. Options are not inherited from the parent; each node parses only its own flag table. This keeps parsing predictable and handlers focused.
Each node uses the standard X-macro option table + handler pattern — see Writing Commands for the full boilerplate. The only subcommand-specific addition is wiring children into the parent descriptor:
Subcommand tables are fully static — no allocation, no registration calls. The built-in wsh user demo in src/wsh_shell_cmd_def.c is a complete working example with flags at every level (wsh user --count, wsh user list --short, wsh user whoami --name --groups).
Dispatch Flow
Given an input line like user whoami --verbose:
- The shell locates the top-level command (
user). - If the next non-flag token matches a subcommand name, the dispatcher descends into it and shifts
argvso the subcommand handler sees its own name asargv[0]. - Descent stops when the next token starts with
-(flag) or no subcommand match is found. - Group-based access control is re-checked at every level. A user without the required groups on a subcommand gets a clear access-denied error instead of silently falling through.
- The leaf command's handler is invoked.
If the user types user bogus, the shell prints Unknown subcommand: bogus and stops — the parent handler is not called with a garbage argument.
Help Output
WshShellCmd_PrintOptionsOverview (used by -h / --help) lists subcommands after the options table. The overview is produced automatically — each parent command advertises its children for free:
If a command has no options, the "Options overview" section is suppressed to keep output clean.
Autocomplete
Tab completion walks the subcommand tree end-to-end:
user <TAB>→ showslistandwhoami.user l<TAB>→ completes touser list.user list <TAB>→ completes the leaf command's flags.
Multi-candidate listings and longest-common-prefix expansion work identically to top-level commands.
Validation
At registration time (WshShellCmd_Attach), the shell recursively validates the tree:
- Duplicate subcommand names within the same parent trigger
WSH_SHELL_ASSERT. - Each subcommand's own options are validated (duplicate short/long flags).
- NULL entries in a
SubCmdsarray are reported with the parent name for easy debugging.
Design Notes
- Subcommand lookup is a simple linear scan — cheap for embedded-typical N (< 10 children). No hashing, no allocation.
- The parent handler is still called when the user runs the parent with no subcommand (e.g.,
useralone). This is a convenient hook for printing an overview. - Subcommands do not inherit their parent's options; each level has its own option table. This keeps parsing predictable and handlers focused.