/* * cli.c - argument parsing for the stupidtools entrypoint. * * Manual parsing on purpose: the flag surface is tiny and fully under * our control, so there is no need for getopt's permutation rules or * its platform quirks. Unknown options are usage errors (exit 2) with * the usage text on stderr; a missing buildfile is a runtime error * (exit 1). -- terminates option parsing so a buildfile whose name * starts with '-' stays addressable. * * Copyright (c) 2026 huntedbytheirs * SPDX-License-Identifier: BSD-3-Clause */ #include "cli.h" #include static const char * program_name(const char *argv0) { if (argv0 == NULL || argv0[0] == '\0') { return "stupidtools"; } return argv0; } static int is_option(const char *arg) { return arg[0] == '-' && arg[1] != '\0' && strcmp(arg, "-") != 0; } void cli_print_usage(FILE *stream, const char *program) { fprintf(stream, "Usage: %s [OPTIONS] \n" "\n" "Reads a KDL build file and emits a POSIX-sh ./configure script.\n" "\n" "Options:\n" " -h, --help show this help and exit\n" " -V, --version print version and exit\n" "\n" "Note: buildfile processing is not yet implemented.\n", program); } /* Take the next positional argument, reporting usage errors to stderr. * Returns CLI_ACTION_ERROR (with usage printed) or CLI_ACTION_RUN. */ static enum cli_action take_positional(const char *arg, struct cli_opts *opts) { if (opts->buildfile != NULL) { fprintf(stderr, "%s: unexpected extra argument '%s'\n", opts->program, arg); cli_print_usage(stderr, opts->program); return CLI_ACTION_ERROR; } opts->buildfile = arg; return CLI_ACTION_RUN; } enum cli_action cli_parse(int argc, char **argv, struct cli_opts *opts) { enum cli_action action; int i; opts->program = program_name(argc > 0 ? argv[0] : NULL); opts->buildfile = NULL; for (i = 1; i < argc; i++) { const char *arg = argv[i]; if (!is_option(arg)) { action = take_positional(arg, opts); if (action != CLI_ACTION_RUN) { return action; } continue; } if (strcmp(arg, "--") == 0) { for (i++; i < argc; i++) { action = take_positional(argv[i], opts); if (action != CLI_ACTION_RUN) { return action; } } break; } if (strcmp(arg, "-h") == 0 || strcmp(arg, "--help") == 0) { cli_print_usage(stdout, opts->program); return CLI_ACTION_HELP; } if (strcmp(arg, "-V") == 0 || strcmp(arg, "--version") == 0) { return CLI_ACTION_VERSION; } fprintf(stderr, "%s: unrecognized option '%s'\n", opts->program, arg); cli_print_usage(stderr, opts->program); return CLI_ACTION_ERROR; } return CLI_ACTION_RUN; }