feat(cli): add entrypoint with --help/--version and exit codes

This commit is contained in:
2026-08-28 19:48:33 -04:00
parent 238db8b484
commit b5718b7487
5 changed files with 262 additions and 10 deletions
+2 -1
View File
@@ -3,4 +3,5 @@
AM_CFLAGS = -std=c23 -Wall -Wextra -Wpedantic
bin_PROGRAMS = stupidtools
stupidtools_SOURCES = main.c
stupidtools_SOURCES = main.c cli.c
noinst_HEADERS = cli.h
+111
View File
@@ -0,0 +1,111 @@
/*
* 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 <string.h>
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] <buildfile.kdl>\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;
}
+36
View File
@@ -0,0 +1,36 @@
/*
* cli.h - argument parsing for the stupidtools entrypoint.
*
* Copyright (c) 2026 huntedbytheirs
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef STUPIDTOOLS_CLI_H
#define STUPIDTOOLS_CLI_H
#include <stdio.h>
/* Must match AC_INIT version in configure.ac (todo 1: 1.0.0). */
#define STUPIDTOOLS_VERSION "1.0.0"
#define CLI_EXIT_OK 0
#define CLI_EXIT_RUNTIME 1
#define CLI_EXIT_USAGE 2
/* What the caller should do after parsing argv. */
enum cli_action {
CLI_ACTION_RUN, /* proceed with the parsed buildfile (todo 16+) */
CLI_ACTION_HELP, /* usage already printed to stdout, exit 0 */
CLI_ACTION_VERSION, /* caller prints the version line, exit 0 */
CLI_ACTION_ERROR /* error + usage already printed to stderr, exit 2 */
};
struct cli_opts {
const char *program; /* argv[0] as given (or a default) */
const char *buildfile; /* single positional buildfile, NULL if none */
};
enum cli_action cli_parse(int argc, char **argv, struct cli_opts *opts);
void cli_print_usage(FILE *stream, const char *program);
#endif /* STUPIDTOOLS_CLI_H */
+31 -9
View File
@@ -4,21 +4,43 @@
* Copyright (c) 2026 huntedbytheirs
* SPDX-License-Identifier: BSD-3-Clause
*
* Minimal skeleton: reports the version. Real CLI parsing (todo 4)
* replaces this stub.
* Entrypoint: parse argv via cli.c, dispatch --help/--version/usage
* errors, and reserve the `stupidtools <buildfile>` shape. Buildfile
* processing arrives in later todos (16+).
*/
#include <stdio.h>
#include "cli.h"
/* Must match AC_INIT version in configure.ac (todo 1: 1.0.0). */
#define STUPIDTOOLS_VERSION "1.0.0"
#include <stdio.h>
int
main(int argc, char **argv)
{
(void)argc;
(void)argv;
struct cli_opts opts;
enum cli_action action;
printf("stupidtools %s\n", STUPIDTOOLS_VERSION);
return 0;
action = cli_parse(argc, argv, &opts);
switch (action) {
case CLI_ACTION_HELP:
return CLI_EXIT_OK;
case CLI_ACTION_VERSION:
printf("stupidtools %s\n", STUPIDTOOLS_VERSION);
return CLI_EXIT_OK;
case CLI_ACTION_ERROR:
return CLI_EXIT_USAGE;
case CLI_ACTION_RUN:
break;
}
if (opts.buildfile == NULL) {
fprintf(stderr, "%s: error: no build file given\n",
opts.program);
cli_print_usage(stderr, opts.program);
return CLI_EXIT_RUNTIME;
}
/* Accepted: the buildfile is parsed in todo 16+, not read here. */
(void)opts.buildfile;
return CLI_EXIT_OK;
}