Skip to content

Add positional options support #61

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 44 additions & 4 deletions argparse.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

#define OPT_UNSET 1
#define OPT_LONG (1 << 1)
#define OPT_POSI (1 << 2) // Positional argument

static const char *
prefix_skip(const char *str, const char *prefix)
Expand All @@ -38,7 +39,9 @@ argparse_error(struct argparse *self, const struct argparse_option *opt,
const char *reason, int flags)
{
(void)self;
if (flags & OPT_LONG) {
if (flags & OPT_POSI) {
fprintf(stderr, "error: option `%s` %s\n", opt->long_name, reason);
} else if (flags & OPT_LONG) {
fprintf(stderr, "error: option `--%s` %s\n", opt->long_name, reason);
} else {
fprintf(stderr, "error: option `-%c` %s\n", opt->short_name, reason);
Expand Down Expand Up @@ -145,10 +148,29 @@ argparse_options_check(const struct argparse_option *options)
}
}

static int
argparse_pos_opt(struct argparse *self, const struct argparse_option *options)
{
options += self->posidx;
for (; options->type != ARGPARSE_OPT_END; options++, self->posidx++) {
if (!(options->flags & OPT_POSITIONAL)) {
continue;
}

self->posidx++;
return argparse_getvalue(self, options, OPT_POSI);
}
return -2;
}

static int
argparse_short_opt(struct argparse *self, const struct argparse_option *options)
{
for (; options->type != ARGPARSE_OPT_END; options++) {
if (options->flags & OPT_POSITIONAL) {
continue;
}

if (options->short_name == *self->optvalue) {
self->optvalue = self->optvalue[1] ? self->optvalue + 1 : NULL;
return argparse_getvalue(self, options, 0);
Expand All @@ -163,6 +185,10 @@ argparse_long_opt(struct argparse *self, const struct argparse_option *options)
for (; options->type != ARGPARSE_OPT_END; options++) {
const char *rest;
int opt_flags = 0;
if (options->flags & OPT_POSITIONAL) {
continue;
}

if (!options->long_name)
continue;

Expand Down Expand Up @@ -229,6 +255,14 @@ argparse_parse(struct argparse *self, int argc, const char **argv)
for (; self->argc; self->argc--, self->argv++) {
const char *arg = self->argv[0];
if (arg[0] != '-' || !arg[1]) {
self->optvalue = arg;
switch (argparse_pos_opt(self, self->options)) {
case 0:
continue;
case -1:
break;
}

if (self->flags & ARGPARSE_STOP_AT_NON_OPTION) {
goto end;
}
Expand Down Expand Up @@ -318,7 +352,10 @@ argparse_usage(struct argparse *self)
len += 2; // separator ", "
}
if ((options)->long_name) {
len += strlen((options)->long_name) + 2;
len += strlen((options)->long_name);
if (!(options->flags & OPT_POSITIONAL)) {
len += 2;
}
}
if (options->type == ARGPARSE_OPT_INTEGER) {
len += strlen("=<int>");
Expand Down Expand Up @@ -353,7 +390,11 @@ argparse_usage(struct argparse *self)
pos += fprintf(stdout, ", ");
}
if (options->long_name) {
pos += fprintf(stdout, "--%s", options->long_name);
if (options->flags & OPT_POSITIONAL) {
pos += fprintf(stdout, "%s", options->long_name);
} else {
pos += fprintf(stdout, "--%s", options->long_name);
}
}
if (options->type == ARGPARSE_OPT_INTEGER) {
pos += fprintf(stdout, "=<int>");
Expand Down Expand Up @@ -391,4 +432,3 @@ argparse_help_cb(struct argparse *self, const struct argparse_option *option)
argparse_help_cb_no_exit(self, option);
exit(EXIT_SUCCESS);
}

2 changes: 2 additions & 0 deletions argparse.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ enum argparse_option_type {

enum argparse_option_flags {
OPT_NONEG = 1, /* disable negation */
OPT_POSITIONAL = 2, /* option is positional */
};

/**
Expand Down Expand Up @@ -98,6 +99,7 @@ struct argparse {
const char **argv;
const char **out;
int cpidx;
int posidx; // index of next option to check if positional
const char *optvalue; // current option value
};

Expand Down
11 changes: 10 additions & 1 deletion tests/basic.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
#include "argparse.h"

static const char *const usages[] = {
"basic [options] [[--] args]",
"basic [options] posi poss [[--] args]",
"basic [options]",
NULL,
};
Expand All @@ -21,6 +21,8 @@ main(int argc, const char **argv)
int int_num = 0;
float flt_num = 0.f;
const char *path = NULL;
int posi = 0;
const char *poss = NULL;
int perms = 0;
struct argparse_option options[] = {
OPT_HELP(),
Expand All @@ -34,6 +36,9 @@ main(int argc, const char **argv)
OPT_BIT(0, "read", &perms, "read perm", NULL, PERM_READ, OPT_NONEG),
OPT_BIT(0, "write", &perms, "write perm", NULL, PERM_WRITE, 0),
OPT_BIT(0, "exec", &perms, "exec perm", NULL, PERM_EXEC, 0),
OPT_GROUP("Positional options"),
OPT_INTEGER(0, "posi", &posi, "positional integer", NULL, 0, OPT_POSITIONAL),
OPT_STRING(0, "poss", &poss, "positional string", NULL, 0, OPT_POSITIONAL),
OPT_END(),
};

Expand All @@ -51,6 +56,10 @@ main(int argc, const char **argv)
printf("int_num: %d\n", int_num);
if (flt_num != 0)
printf("flt_num: %g\n", flt_num);
if (posi != 0)
printf("posi: %d\n", posi);
if (poss != NULL)
printf("poss: %s\n", poss);
if (argc != 0) {
printf("argc: %d\n", argc);
int i;
Expand Down
14 changes: 11 additions & 3 deletions tests/basic.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@
. $(dirname ${BASH_SOURCE[0]})/tap-functions
plan_no_plan

is "$(./basic -f --path=/path/to/file a 2>&1)" 'force: 1
is "$(./basic -f 42 --path=/path/to/file a b 2>&1)" 'force: 1
path: /path/to/file
posi: 42
poss: a
argc: 1
argv[0]: a'
argv[0]: b'

is "$(./basic -f -f --force --no-force 2>&1)" 'force: 2'

Expand All @@ -18,6 +20,8 @@ is "$(./basic -i2 2>&1)" 'int_num: 2'

is "$(./basic -ia 2>&1)" 'error: option `-i` expects an integer value'

is "$(./basic a 2>&1)" 'error: option `posi` expects an integer value'

is "$(./basic -i 0xFFFFFFFFFFFFFFFFF 2>&1)" \
'error: option `-i` numerical result out of range'

Expand All @@ -41,7 +45,7 @@ test: 1'

is "$(./basic --read --write 2>&1)" 'perms: 3'

help_usage='Usage: basic [options] [[--] args]
help_usage='Usage: basic [options] posi poss [[--] args]
or: basic [options]

A brief description of what the program does and how it works.
Expand All @@ -60,6 +64,10 @@ Bits options
--write write perm
--exec exec perm

Positional options
posi=<int> positional integer
poss=<str> positional string

Additional description of the program after the description of the arguments.'

is "$(./basic -h)" "$help_usage"
Expand Down