123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117 | // Command line argument parsing
#pragma once
#include <stdbool.h>
#include "datatypes/typeinfo.h"
typedef struct {
const char *name;
void *dest;
const TypeInfo_t *type;
char short_flag;
bool required, populated;
// Optional presentation info for autogenerated usage/help text:
const char *description; // one-line description of the flag
const char *metavar; // display name for the value (default: derived from the type)
const char *default_text; // the default value, as source text, for help output
bool positional; // display as a positional argument instead of a flag
} cli_arg_t;
// The help/version/usage text for one argument-parsing pass, along with which
// short flags (if any) the automatic --help and --version flags claim:
typedef struct {
Text_t usage, help;
char help_short; // short alias for --help ('h'), or 0 for none
bool strict_positionals; // only fill positional values into .positional spec entries;
// otherwise any unpopulated entry can be filled positionally
// Appended to parse errors: when the first word resembles one of this
// command's subcommand names, a failed parse is much more likely a
// mistyped command than a genuinely bad argument. Only shown on failure,
// so a word that resembles a command but really is an argument (a
// directory named `tests`, say) still runs normally.
Text_t command_hint;
} cli_help_info_t;
void tomo_parse_arg_list(List_t args, cli_help_info_t info, int spec_len, cli_arg_t spec[spec_len]);
// Fills in a command's `usage` when the program doesn't supply one of its own.
Text_t tomo_generate_usage(Text_t prefix, int spec_len, cli_arg_t spec[spec_len]);
// One command in a git-style CLI. Commands nest: `tomo build`, `git submodule
// init`. A command with children can still have a handler of its own (like
// `git stash` alongside `git stash pop`), and a command with no handler is
// purely a namespace for its children.
typedef struct cli_command_s cli_command_t;
struct cli_command_s {
const char *name; // "run", "build", ... (NULL for the root command)
// An alternate name that invokes the same command ("fmt" for "format"),
// or NULL. Accepted wherever the name is, but not listed as a command of
// its own:
const char *alias;
const char *summary; // one-liner shown in the parent's command listing
const char *description; // optional prose paragraph for this command's help
// Usage/help shown for `<prog> <cmd> --help` and parse errors; leave empty
// to have them autogenerated from the spec and children at startup:
Text_t usage, help;
int spec_len;
cli_arg_t *spec; // this command's own flags and positionals
int num_children;
cli_command_t **children; // nested subcommands, or NULL
// Runs after this command's spec is parsed. extra_args is the raw argv
// tail after the first bare "--", untouched. NULL means this command is
// only a namespace: naming it without a child prints its help.
int (*handler)(cli_command_t *self, List_t extra_args);
};
// A whole command-line program. Global flags are valid anywhere on the command
// line (they are popped before dispatch), so global and command-specific flag
// names/short flags must be disjoint.
typedef struct {
const char *name; // the program name, e.g. "tomo"
const char *summary; // one-liner shown at the top of the help
const char *description; // optional prose paragraph for the top-level help
const char *version; // NULL: no automatic --version flag
char version_short; // short alias for --version ('v'), or 0 for long-only
int global_len;
cli_arg_t *global_spec; // flags valid anywhere on the command line
// Runs after global flags are popped, before dispatch:
void (*after_globals)(void);
// Only fill positional values into args marked .positional. Programs whose
// args are generated (rather than hand-written) leave this false, so every
// argument can be given either positionally or by flag.
bool strict_positionals;
// Treat everything after the first bare "--" as opaque arguments to hand
// to the command's handler (`tomo run prog.tm -- prog args...`). Programs
// that don't relay arguments elsewhere leave this false, and then "--"
// keeps its usual meaning of "the rest are values, not flags", which is
// how a value that starts with a dash (or collides with a command name)
// gets passed.
bool passthrough_after_double_dash;
// The root command: its children are the program's top-level commands, and
// its own spec/handler (if any) run when the first word doesn't name one --
// e.g. `tomo file.tm` and a Tomo program's own `main()`.
cli_command_t root;
} cli_spec_t;
int tomo_dispatch_command(int argc, char *argv[], cli_spec_t *cli);
// The palette for help, usage, and error text, with every escape emptied out when
// output isn't colored (NO_COLOR, COLOR=0, or not a terminal). Same approach as
// report_style() in report.h: each site writes `style.bold` unconditionally
// rather than branching on USE_COLOR or filtering escapes back out afterwards.
typedef struct {
const char *bold, *dim, *italic, *reset;
const char *heading; // section headings: "Flags:", "Commands:"
const char *usage; // the "Usage:" label
const char *flag; // --flag names
const char *value; // value placeholders: text, N, path
const char *command; // subcommand names
const char *error; // error messages
} cli_style_t;
cli_style_t tomo_cli_style(void);
bool pop_cli_flag(List_t *args, char short_flag, const char *flag, void *dest, const TypeInfo_t *type);
bool pop_cli_positional(List_t *args, const char *flag, void *dest, const TypeInfo_t *type, bool allow_dashes);
|