forked from xuos/xiuos
Merge branch 'prepare_for_master' of https://gitlink.org.cn/xuos/xiuos into mqtt
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
SRC_DIR := letter-shell
|
||||
SRC_FILES += argparse.c
|
||||
|
||||
include $(KERNEL_ROOT)/compiler.mk
|
||||
|
||||
@@ -0,0 +1,389 @@
|
||||
/**
|
||||
* Copyright (C) 2012-2015 Yecheng Fu <cofyc.jackson at gmail dot com>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a MIT-style license that can be found
|
||||
* in the LICENSE file.
|
||||
*/
|
||||
#include "argparse.h"
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#define OPT_UNSET 1
|
||||
#define OPT_LONG (1 << 1)
|
||||
|
||||
static const char*
|
||||
prefix_skip(const char* str, const char* prefix)
|
||||
{
|
||||
size_t len = strlen(prefix);
|
||||
return strncmp(str, prefix, len) ? NULL : str + len;
|
||||
}
|
||||
|
||||
static int
|
||||
prefix_cmp(const char* str, const char* prefix)
|
||||
{
|
||||
for (;; str++, prefix++)
|
||||
if (!*prefix) {
|
||||
return 0;
|
||||
} else if (*str != *prefix) {
|
||||
return (unsigned char)*prefix - (unsigned char)*str;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
argparse_error(struct argparse* self, const struct argparse_option* opt,
|
||||
const char* reason, int flags)
|
||||
{
|
||||
(void)self;
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
#include <xizi.h>
|
||||
|
||||
static int
|
||||
argparse_getvalue(struct argparse* self, const struct argparse_option* opt,
|
||||
int flags)
|
||||
{
|
||||
const char* s = NULL;
|
||||
if (!opt->value)
|
||||
goto skipped;
|
||||
switch (opt->type) {
|
||||
case ARGPARSE_OPT_BOOLEAN:
|
||||
if (flags & OPT_UNSET) {
|
||||
*(int*)opt->value = *(int*)opt->value - 1;
|
||||
} else {
|
||||
*(int*)opt->value = *(int*)opt->value + 1;
|
||||
}
|
||||
if (*(int*)opt->value < 0) {
|
||||
*(int*)opt->value = 0;
|
||||
}
|
||||
break;
|
||||
case ARGPARSE_OPT_BIT:
|
||||
if (flags & OPT_UNSET) {
|
||||
*(int*)opt->value &= ~opt->data;
|
||||
} else {
|
||||
*(int*)opt->value |= opt->data;
|
||||
}
|
||||
break;
|
||||
case ARGPARSE_OPT_STRING:
|
||||
if (self->optvalue) {
|
||||
*(const char**)opt->value = self->optvalue;
|
||||
self->optvalue = NULL;
|
||||
} else if (self->argc > 1) {
|
||||
self->argc--;
|
||||
*(const char**)opt->value = *++self->argv;
|
||||
} else {
|
||||
argparse_error(self, opt, "requires a value", flags);
|
||||
}
|
||||
break;
|
||||
case ARGPARSE_OPT_INTEGER:
|
||||
errno = 0;
|
||||
if (self->optvalue) {
|
||||
*(int*)opt->value = strtol(self->optvalue, (char**)&s, 0);
|
||||
self->optvalue = NULL;
|
||||
} else if (self->argc > 1) {
|
||||
self->argc--;
|
||||
*(int*)opt->value = strtol(*++self->argv, (char**)&s, 0);
|
||||
} else {
|
||||
argparse_error(self, opt, "requires a value", flags);
|
||||
}
|
||||
if (errno == ERANGE)
|
||||
argparse_error(self, opt, "numerical result out of range", flags);
|
||||
if (s[0] != '\0') // no digits or contains invalid characters
|
||||
argparse_error(self, opt, "expects an integer value", flags);
|
||||
break;
|
||||
case ARGPARSE_OPT_FLOAT:
|
||||
errno = 0;
|
||||
if (self->optvalue) {
|
||||
*(float*)opt->value = strtof(self->optvalue, (char**)&s);
|
||||
self->optvalue = NULL;
|
||||
} else if (self->argc > 1) {
|
||||
self->argc--;
|
||||
*(float*)opt->value = strtof(*++self->argv, (char**)&s);
|
||||
} else {
|
||||
argparse_error(self, opt, "requires a value", flags);
|
||||
}
|
||||
if (errno == ERANGE)
|
||||
argparse_error(self, opt, "numerical result out of range", flags);
|
||||
if (s[0] != '\0') // no digits or contains invalid characters
|
||||
argparse_error(self, opt, "expects a numerical value", flags);
|
||||
break;
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
|
||||
skipped:
|
||||
if (opt->callback) {
|
||||
return opt->callback(self, opt);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
argparse_options_check(const struct argparse_option* options)
|
||||
{
|
||||
for (; options->type != ARGPARSE_OPT_END; options++) {
|
||||
switch (options->type) {
|
||||
case ARGPARSE_OPT_END:
|
||||
case ARGPARSE_OPT_BOOLEAN:
|
||||
case ARGPARSE_OPT_BIT:
|
||||
case ARGPARSE_OPT_INTEGER:
|
||||
case ARGPARSE_OPT_FLOAT:
|
||||
case ARGPARSE_OPT_STRING:
|
||||
case ARGPARSE_OPT_GROUP:
|
||||
continue;
|
||||
default:
|
||||
fprintf(stderr, "wrong option type: %d", options->type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
argparse_short_opt(struct argparse* self, const struct argparse_option* options)
|
||||
{
|
||||
for (; options->type != ARGPARSE_OPT_END; options++) {
|
||||
if (options->short_name == *self->optvalue) {
|
||||
self->optvalue = self->optvalue[1] ? self->optvalue + 1 : NULL;
|
||||
return argparse_getvalue(self, options, 0);
|
||||
}
|
||||
}
|
||||
return -2;
|
||||
}
|
||||
|
||||
static int
|
||||
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->long_name)
|
||||
continue;
|
||||
|
||||
rest = prefix_skip(self->argv[0] + 2, options->long_name);
|
||||
if (!rest) {
|
||||
// negation disabled?
|
||||
if (options->flags & OPT_NONEG) {
|
||||
continue;
|
||||
}
|
||||
// only OPT_BOOLEAN/OPT_BIT supports negation
|
||||
if (options->type != ARGPARSE_OPT_BOOLEAN && options->type != ARGPARSE_OPT_BIT) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (prefix_cmp(self->argv[0] + 2, "no-")) {
|
||||
continue;
|
||||
}
|
||||
rest = prefix_skip(self->argv[0] + 2 + 3, options->long_name);
|
||||
if (!rest)
|
||||
continue;
|
||||
opt_flags |= OPT_UNSET;
|
||||
}
|
||||
if (*rest) {
|
||||
if (*rest != '=')
|
||||
continue;
|
||||
self->optvalue = rest + 1;
|
||||
}
|
||||
return argparse_getvalue(self, options, opt_flags | OPT_LONG);
|
||||
}
|
||||
return -2;
|
||||
}
|
||||
|
||||
int argparse_init(struct argparse* self, struct argparse_option* options,
|
||||
const char* const* usages, int flags)
|
||||
{
|
||||
memset(self, 0, sizeof(*self));
|
||||
self->options = options;
|
||||
self->usages = usages;
|
||||
self->flags = flags;
|
||||
self->description = NULL;
|
||||
self->epilog = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void argparse_describe(struct argparse* self, const char* description,
|
||||
const char* epilog)
|
||||
{
|
||||
self->description = description;
|
||||
self->epilog = epilog;
|
||||
}
|
||||
|
||||
int argparse_parse(struct argparse* self, int argc, const char** argv)
|
||||
{
|
||||
self->argc = argc - 1;
|
||||
self->argv = argv + 1;
|
||||
self->out = argv;
|
||||
|
||||
argparse_options_check(self->options);
|
||||
|
||||
for (; self->argc; self->argc--, self->argv++) {
|
||||
const char* arg = self->argv[0];
|
||||
if (arg[0] != '-' || !arg[1]) {
|
||||
if (self->flags & ARGPARSE_STOP_AT_NON_OPTION) {
|
||||
goto end;
|
||||
}
|
||||
// if it's not option or is a single char '-', copy verbatim
|
||||
self->out[self->cpidx++] = self->argv[0];
|
||||
continue;
|
||||
}
|
||||
// short option
|
||||
if (arg[1] != '-') {
|
||||
self->optvalue = arg + 1;
|
||||
switch (argparse_short_opt(self, self->options)) {
|
||||
case -1:
|
||||
break;
|
||||
case -2:
|
||||
goto unknown;
|
||||
}
|
||||
while (self->optvalue) {
|
||||
switch (argparse_short_opt(self, self->options)) {
|
||||
case -1:
|
||||
break;
|
||||
case -2:
|
||||
goto unknown;
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
// if '--' presents
|
||||
if (!arg[2]) {
|
||||
self->argc--;
|
||||
self->argv++;
|
||||
break;
|
||||
}
|
||||
// long option
|
||||
switch (argparse_long_opt(self, self->options)) {
|
||||
case -1:
|
||||
break;
|
||||
case -2:
|
||||
goto unknown;
|
||||
}
|
||||
continue;
|
||||
|
||||
unknown:
|
||||
fprintf(stderr, "error: unknown option `%s`\n", self->argv[0]);
|
||||
argparse_usage(self);
|
||||
if (!(self->flags & ARGPARSE_IGNORE_UNKNOWN_ARGS)) {
|
||||
return ARGPARSE_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
end:
|
||||
memmove(self->out + self->cpidx, self->argv,
|
||||
self->argc * sizeof(*self->out));
|
||||
self->out[self->cpidx + self->argc] = NULL;
|
||||
|
||||
return self->cpidx + self->argc;
|
||||
}
|
||||
|
||||
void argparse_usage(struct argparse* self)
|
||||
{
|
||||
if (self->usages) {
|
||||
fprintf(stdout, "Usage: %s\n", *self->usages++);
|
||||
while (*self->usages && **self->usages)
|
||||
fprintf(stdout, " or: %s\n", *self->usages++);
|
||||
} else {
|
||||
fprintf(stdout, "Usage:\n");
|
||||
}
|
||||
|
||||
// print description
|
||||
if (self->description)
|
||||
fprintf(stdout, "%s\n", self->description);
|
||||
|
||||
fputc('\n', stdout);
|
||||
|
||||
const struct argparse_option* options;
|
||||
|
||||
// figure out best width
|
||||
size_t usage_opts_width = 0;
|
||||
size_t len;
|
||||
options = self->options;
|
||||
for (; options->type != ARGPARSE_OPT_END; options++) {
|
||||
len = 0;
|
||||
if ((options)->short_name) {
|
||||
len += 2;
|
||||
}
|
||||
if ((options)->short_name && (options)->long_name) {
|
||||
len += 2; // separator ", "
|
||||
}
|
||||
if ((options)->long_name) {
|
||||
len += strlen((options)->long_name) + 2;
|
||||
}
|
||||
if (options->type == ARGPARSE_OPT_INTEGER) {
|
||||
len += strlen("=<int>");
|
||||
}
|
||||
if (options->type == ARGPARSE_OPT_FLOAT) {
|
||||
len += strlen("=<flt>");
|
||||
} else if (options->type == ARGPARSE_OPT_STRING) {
|
||||
len += strlen("=<str>");
|
||||
}
|
||||
len = (len + 3) - ((len + 3) & 3);
|
||||
if (usage_opts_width < len) {
|
||||
usage_opts_width = len;
|
||||
}
|
||||
}
|
||||
usage_opts_width += 4; // 4 spaces prefix
|
||||
|
||||
options = self->options;
|
||||
for (; options->type != ARGPARSE_OPT_END; options++) {
|
||||
size_t pos = 0;
|
||||
size_t pad = 0;
|
||||
if (options->type == ARGPARSE_OPT_GROUP) {
|
||||
fputc('\n', stdout);
|
||||
fprintf(stdout, "%s", options->help);
|
||||
fputc('\n', stdout);
|
||||
continue;
|
||||
}
|
||||
pos = fprintf(stdout, " ");
|
||||
if (options->short_name) {
|
||||
pos += fprintf(stdout, "-%c", options->short_name);
|
||||
}
|
||||
if (options->long_name && options->short_name) {
|
||||
pos += fprintf(stdout, ", ");
|
||||
}
|
||||
if (options->long_name) {
|
||||
pos += fprintf(stdout, "--%s", options->long_name);
|
||||
}
|
||||
if (options->type == ARGPARSE_OPT_INTEGER) {
|
||||
pos += fprintf(stdout, "=<int>");
|
||||
} else if (options->type == ARGPARSE_OPT_FLOAT) {
|
||||
pos += fprintf(stdout, "=<flt>");
|
||||
} else if (options->type == ARGPARSE_OPT_STRING) {
|
||||
pos += fprintf(stdout, "=<str>");
|
||||
}
|
||||
if (pos <= usage_opts_width) {
|
||||
pad = usage_opts_width - pos;
|
||||
} else {
|
||||
fputc('\n', stdout);
|
||||
pad = usage_opts_width;
|
||||
}
|
||||
fprintf(stdout, "%*s%s\n", (int)pad + 2, "", options->help);
|
||||
}
|
||||
|
||||
// print epilog
|
||||
if (self->epilog)
|
||||
fprintf(stdout, "%s\n", self->epilog);
|
||||
}
|
||||
|
||||
int argparse_help_cb_no_exit(struct argparse* self,
|
||||
const struct argparse_option* option)
|
||||
{
|
||||
(void)option;
|
||||
argparse_usage(self);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int argparse_help_cb(struct argparse* self, const struct argparse_option* option)
|
||||
{
|
||||
argparse_help_cb_no_exit(self, option);
|
||||
*(bool*)option->value = true;
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,157 @@
|
||||
/**
|
||||
* Copyright (C) 2012-2015 Yecheng Fu <cofyc.jackson at gmail dot com>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a MIT-style license that can be found
|
||||
* in the LICENSE file.
|
||||
*/
|
||||
#ifndef ARGPARSE_H
|
||||
#define ARGPARSE_H
|
||||
|
||||
/* For c++ compatibility */
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define ARGPARSE_HELP_DONE -1
|
||||
#define ARGPARSE_ERROR -2
|
||||
|
||||
struct argparse;
|
||||
struct argparse_option;
|
||||
|
||||
typedef int argparse_callback(struct argparse* self,
|
||||
const struct argparse_option* option);
|
||||
|
||||
enum argparse_flag {
|
||||
ARGPARSE_STOP_AT_NON_OPTION = 1 << 0,
|
||||
ARGPARSE_IGNORE_UNKNOWN_ARGS = 1 << 1,
|
||||
};
|
||||
|
||||
enum argparse_option_type {
|
||||
/* special */
|
||||
ARGPARSE_OPT_END,
|
||||
ARGPARSE_OPT_GROUP,
|
||||
/* options with no arguments */
|
||||
ARGPARSE_OPT_BOOLEAN,
|
||||
ARGPARSE_OPT_BIT,
|
||||
/* options with arguments (optional or required) */
|
||||
ARGPARSE_OPT_INTEGER,
|
||||
ARGPARSE_OPT_FLOAT,
|
||||
ARGPARSE_OPT_STRING,
|
||||
};
|
||||
|
||||
enum argparse_option_flags {
|
||||
OPT_NONEG = 1, /* disable negation */
|
||||
};
|
||||
|
||||
/**
|
||||
* argparse option
|
||||
*
|
||||
* `type`:
|
||||
* holds the type of the option, you must have an ARGPARSE_OPT_END last in your
|
||||
* array.
|
||||
*
|
||||
* `short_name`:
|
||||
* the character to use as a short option name, '\0' if none.
|
||||
*
|
||||
* `long_name`:
|
||||
* the long option name, without the leading dash, NULL if none.
|
||||
*
|
||||
* `value`:
|
||||
* stores pointer to the value to be filled.
|
||||
*
|
||||
* `help`:
|
||||
* the short help message associated to what the option does.
|
||||
* Must never be NULL (except for ARGPARSE_OPT_END).
|
||||
*
|
||||
* `callback`:
|
||||
* function is called when corresponding argument is parsed.
|
||||
*
|
||||
* `data`:
|
||||
* associated data. Callbacks can use it like they want.
|
||||
*
|
||||
* `flags`:
|
||||
* option flags.
|
||||
*/
|
||||
struct argparse_option {
|
||||
enum argparse_option_type type;
|
||||
const char short_name;
|
||||
const char* long_name;
|
||||
void* value;
|
||||
const char* help;
|
||||
argparse_callback* callback;
|
||||
intptr_t data;
|
||||
int flags;
|
||||
};
|
||||
|
||||
/**
|
||||
* argpparse
|
||||
*/
|
||||
struct argparse {
|
||||
// user supplied
|
||||
const struct argparse_option* options;
|
||||
const char* const* usages;
|
||||
int flags;
|
||||
const char* description; // a description after usage
|
||||
const char* epilog; // a description at the end
|
||||
// internal context
|
||||
int argc;
|
||||
const char** argv;
|
||||
const char** out;
|
||||
int cpidx;
|
||||
const char* optvalue; // current option value
|
||||
};
|
||||
|
||||
// built-in callbacks
|
||||
int argparse_help_cb(struct argparse* self,
|
||||
const struct argparse_option* option);
|
||||
int argparse_help_cb_no_exit(struct argparse* self,
|
||||
const struct argparse_option* option);
|
||||
|
||||
// built-in option macros
|
||||
#define OPT_END() \
|
||||
{ \
|
||||
ARGPARSE_OPT_END, 0, NULL, NULL, 0, NULL, 0, 0 \
|
||||
}
|
||||
#define OPT_BOOLEAN(...) \
|
||||
{ \
|
||||
ARGPARSE_OPT_BOOLEAN, __VA_ARGS__ \
|
||||
}
|
||||
#define OPT_BIT(...) \
|
||||
{ \
|
||||
ARGPARSE_OPT_BIT, __VA_ARGS__ \
|
||||
}
|
||||
#define OPT_INTEGER(...) \
|
||||
{ \
|
||||
ARGPARSE_OPT_INTEGER, __VA_ARGS__ \
|
||||
}
|
||||
#define OPT_FLOAT(...) \
|
||||
{ \
|
||||
ARGPARSE_OPT_FLOAT, __VA_ARGS__ \
|
||||
}
|
||||
#define OPT_STRING(...) \
|
||||
{ \
|
||||
ARGPARSE_OPT_STRING, __VA_ARGS__ \
|
||||
}
|
||||
#define OPT_GROUP(h) \
|
||||
{ \
|
||||
ARGPARSE_OPT_GROUP, 0, NULL, NULL, h, NULL, 0, 0 \
|
||||
}
|
||||
#define OPT_HELP(flag) OPT_BOOLEAN('h', "help", flag, \
|
||||
"show this help message and exit", \
|
||||
argparse_help_cb, 0, OPT_NONEG)
|
||||
|
||||
int argparse_init(struct argparse* self, struct argparse_option* options,
|
||||
const char* const* usages, int flags);
|
||||
void argparse_describe(struct argparse* self, const char* description,
|
||||
const char* epilog);
|
||||
int argparse_parse(struct argparse* self, int argc, const char** argv);
|
||||
void argparse_usage(struct argparse* self);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user