# Commands ## Three files, three concerns | File | Holds | |---|---| | `src/command/cmd_defs.c` (`cmd_defs.h`) | The big static array of `Command` records — every `/foo` is one entry. | | `src/command/cmd_funcs.c` (`cmd_funcs.h`) | Handler implementations: `gboolean cmd_(ProfWin*, const char* const, gchar**)`. | | `src/command/cmd_ac.c` (`cmd_ac.h`) | Per-command autocomplete logic and registration. | ## Anatomy of a command record Defined inline in `cmd_defs.c` using uppercase macros (declared in `cmd_defs.c`): ```c { CMD_PREAMBLE("/caps", parse_args, 0, 1, NULL) CMD_MAINFUNC(cmd_caps) CMD_TAGS( CMD_TAG_DISCOVERY, CMD_TAG_CHAT, CMD_TAG_GROUPCHAT) CMD_SYN( "/caps", "/caps |") CMD_DESC("...") CMD_ARGS( { "", "..." }, { "", "..." }) CMD_EXAMPLES( "/caps user@host/res") } ``` | Macro | Role | |---|---| | `CMD_PREAMBLE(name, parse_fn, min, max, pre_hook)` | Command name, parser, arg count bounds, optional pre-hook. | | `CMD_MAINFUNC(fn)` | Single handler for the whole command. | | `CMD_SUBFUNCS({"sub", fn}, ...)` | Dispatch by first argument. Use **instead of** `CMD_MAINFUNC` when the command has subcommands (e.g. `/status get`, `/status set`). | | `CMD_TAGS(...)` | One or more `CMD_TAG_*` (UI grouping, search). | | `CMD_SYN(...)` | Synopsis lines for `/help `. | | `CMD_DESC(...)` | Long description. | | `CMD_ARGS({"name", "desc"}, ...)` | Argument table for help. | | `CMD_EXAMPLES(...)` | Examples shown in help. | The full set of `CMD_*` macro definitions is at the top of `cmd_defs.c` — read them when authoring a new command. ## Argument parsing `parse_args()` lives in `src/tools/parser.c`: ```c gchar** parse_args(const char* const inp, int min, int max, gboolean* result); ``` - Splits on whitespace, honours quoted segments. - Returns `NULL` on parse failure (sets `*result = FALSE`). - The handler receives the result as `gchar** args` — free is owned by the caller in `cmd_defs.c`, the handler does **not** free. Parser variants used in `CMD_PREAMBLE`: - `parse_args` — standard quoted-aware split. - `parse_args_with_freetext` — last argument is the rest of the line, no splitting (used by `/msg`, `/me`, etc.). - `parse_args_as_one` — concatenates remaining tokens into a single string. ## Handler signature ```c gboolean cmd_foo(ProfWin* window, const char* const command, gchar** args); ``` Return `TRUE` on success, `FALSE` to print bad-usage. `command` is the literal `/foo` typed (useful when the same handler is registered under multiple names). ## Connection-state gates Most commands that touch XMPP must verify connection state first: ```c if (connection_get_status() != JABBER_CONNECTED) { cons_show("You are not currently connected."); return TRUE; } ``` `cons_bad_cmd_usage(cmd)` is the canonical "wrong arguments" path; pass the literal command name. ## Adding a new command See `playbooks/add-command.md` for the full walkthrough. Short version: 1. New entry in `cmd_defs.c` with `CMD_PREAMBLE`/`CMD_MAINFUNC`/... 2. Implement `cmd_()` in `cmd_funcs.c` (declare in `cmd_funcs.h`). 3. (If autocompletion needed) implement `__autocomplete()` in `cmd_ac.c` and register it via `g_hash_table_insert(ac_funcs, "/name", ...)`. 4. Add a unit test pair `tests/unittests/test_cmd_.{c,h}` and register tests in `tests/unittests/unittests.c`. ## Conventions - `cmd_` for handlers; `__autocomplete` (file-static) for the autocomplete callback. - Subcommand dispatch via `CMD_SUBFUNCS` is preferred over `if/else` chains inside `cmd_` when possible. - Keep handlers in `cmd_funcs.c` thin: parse & validate args, then delegate to a domain module (`xmpp/`, `config/`, `ui/`).