kvmtool学习二——参数解析
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
typedef unsigned long long u64;
struct parse_opt_ctx_t {
const char **argv;
const char **out;
int argc, cpidx;
const char *opt;
int flags;
};
enum parse_opt_flags {
PARSE_OPT_KEEP_DASHDASH = 1,
PARSE_OPT_STOP_AT_NON_OPTION = 2,
PARSE_OPT_KEEP_ARGV0 = 4,
PARSE_OPT_KEEP_UNKNOWN = 8,
PARSE_OPT_NO_INTERNAL_HELP = 16,
};
enum parse_opt_type {
OPTION_END,
OPTION_STRING,
OPTION_CALLBACK,
OPTION_U64,
};
struct option {
enum parse_opt_type type;
int short_name;
const char *long_name;
void *value;
const char *argh;
const char *help;
void *ptr;
int flags;
};
struct kvm_config {
u64 ram_size;
const char *kernel_filename;
const char *initrd_filename;
};
#define BUILD_BUG_ON_ZERO(e) (sizeof(struct { int:-!!(e); }))
#define check_vtype(v, type) \
(BUILD_BUG_ON_ZERO(!__builtin_types_compatible_p(typeof(v), type)) + v)
#define OPT_U64(s, l, v, h) \
{ \
.type = OPTION_U64, \
.short_name = (s), \
.long_name = (l), \
.value = check_vtype(v, int *), \
.help = (h) \
}
#define OPT_STRING(s, l, v, a, h) \
{ \
.type = OPTION_STRING, \
.short_name = (s), \
.long_name = (l), \
.value = check_vtype(v, const char **), (a), \
.help = (h) \
}
#define OPT_END() { .type = OPTION_END }
static int get_arg(struct parse_opt_ctx_t *p, const struct option *opt,
int flags, const char **arg){
if (p->opt) {
*arg = p->opt;
p->opt = NULL;
} else if (p->argc > 1) {
p->argc--;
*arg = *++p->argv;
} else {
printf("requires a value\n");
return -1;
}
return 0;
}
static int readnum(const struct option *opt, int flags,
const char *str, char **end){
switch (opt->type) {
case OPTION_U64:
*(u64 *)opt->value = strtoull(str, end, 0);
break;
default:
printf("invalid numeric conversion\n");
return -1;
}
return 0;
}
static int get_value(struct parse_opt_ctx_t *p,
const struct option *opt, int flags){
const char *s, *arg = NULL;
const int unset = flags & 2;
if (unset && p->opt) {
printf("takes no value\n");
return -1;
}
if (unset && (opt->flags & 4 )){
printf("isn't available\n");
return -1;
}
if (!(flags & 1) && p->opt) {
switch (opt->type) {
case OPTION_CALLBACK:
if (!(opt->flags & 2))
break;
case OPTION_END:
case OPTION_STRING:
case OPTION_U64:
default:
break;
}
}
switch (opt->type) {
case OPTION_STRING:
if (unset)
*(const char **)opt->value = NULL;
else
return get_arg(p, opt, flags,
(const char **)opt->value);
return 0;
case OPTION_U64:
if (unset) {
*(long long *)opt->value = 0;
return 0;
}
if (get_arg(p, opt, flags, &arg))
return -1;
return readnum(opt, flags, arg, (char **)&s);
case OPTION_END:
default:
printf("should not happen, someone must be hit on the forehead");
}
}
int prefixcmp(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 check_typos(const char *arg, const struct option *options)
{
if (strlen(arg) < 3)
return;
if (!prefixcmp(arg, "no-")) {
printf("did you mean `--%s` (with two dashes ?)", arg);
exit(129);
}
for (; options->type != OPTION_END; options++) {
if (!options->long_name)
continue;
if (!prefixcmp(options->long_name, arg)) {
printf("did you mean `--%s` (with two dashes ?)", arg);
exit(129);
}
}
}
static void parse_options_start(struct parse_opt_ctx_t *ctx, int argc,
const char **argv, int flags)
{
memset(ctx, 0, sizeof(*ctx));
ctx->argc = argc;
ctx->argv = argv;
ctx->out = argv;
ctx->cpidx = ((flags & PARSE_OPT_KEEP_ARGV0) != 0);
ctx->flags = flags;
if ((flags & PARSE_OPT_KEEP_UNKNOWN) &&
(flags & PARSE_OPT_STOP_AT_NON_OPTION))
printf("STOP_AT_NON_OPTION and KEEP_UNKNOWN don't go together");
}
static int parse_short_opt(struct parse_opt_ctx_t *p,
const struct option *options)
{
for (; options->type != OPTION_END; options++) {
if (options->short_name == *p->opt) {
p->opt = p->opt[1] ? p->opt + 1 : NULL;
return get_value(p, options, 1);
}
}
return -2;
}
static int parse_options_step(struct parse_opt_ctx_t *ctx,
const struct option *options, const char * const usagestr[])
{
int internal_help = !(ctx->flags & PARSE_OPT_NO_INTERNAL_HELP);
ctx->opt = NULL;
for (; ctx->argc; ctx->argc--, ctx->argv++) {
const char *arg = ctx->argv[0];
if (*arg != '-' || !arg[1]) {
if (ctx->flags & PARSE_OPT_STOP_AT_NON_OPTION)
break;
ctx->out[ctx->cpidx++] = ctx->argv[0];
continue;
}
if (arg[1] != '-') {
ctx->opt = arg + 1;
if (internal_help && *ctx->opt == 'h')
return 0;
switch (parse_short_opt(ctx, options)) {
case -1:
return 0;
case -2:
goto unknown;
default:
break;
}
if (ctx->opt)
check_typos(arg + 1, options);
while (ctx->opt) {
if (internal_help && *ctx->opt == 'h')
return 0;
switch (parse_short_opt(ctx, options)) {
case -1:
return 0;
case -2:
ctx->argv[0] = strdup(ctx->opt - 1);
*(char *)ctx->argv[0] = '-';
goto unknown;
default:
break;
}
}
continue;
}
if (!arg[2]) {
if (!(ctx->flags & PARSE_OPT_KEEP_DASHDASH)) {
ctx->argc--;
ctx->argv++;
}
break;
}
continue;
unknown:
if (!(ctx->flags & PARSE_OPT_KEEP_UNKNOWN))
return -1;
ctx->out[ctx->cpidx++] = ctx->argv[0];
ctx->opt = NULL;
}
return 0;
}
static int parse_options_end(struct parse_opt_ctx_t *ctx){
memmove(ctx->out + ctx->cpidx, ctx->argv, ctx->argc * sizeof(*ctx->out));
ctx->out[ctx->cpidx + ctx->argc] = NULL;
return ctx->cpidx + ctx->argc;
}
int parse_options(int argc, const char **argv, const struct option *options,
const char * const usagestr[], int flags){
struct parse_opt_ctx_t ctx;
parse_options_start(&ctx, argc, argv, flags);
switch (parse_options_step(&ctx, options, usagestr)) {
case -1:
exit(129);
case 0:
break;
default:
if (ctx.argv[0][1] == '-') {
printf("unknown option `%s'", ctx.argv[0] + 2);
} else {
printf("unknown switch `%c'", *ctx.opt);
}
}
return parse_options_end(&ctx);
}
static const char * const run_usage[] = {
"lkvm run [<options>] [<kernel image>]",
NULL
};
int main () {
int argc = 6;
char *argv[] = {"-k", "bzImage" ,"-i", "root_fs.cpio", "-m", "2048"};
struct kvm_config * cfg = (struct kvm_config *) calloc(1, sizeof(*cfg));
struct option options [] = {
OPT_U64('m', "mem", &(cfg)->ram_size, "Virtual machine memory size in MiB." ), \
OPT_STRING('k', "kernel", &(cfg)->kernel_filename, "kernel","Kernel to boot in virtual machine"), \
OPT_STRING('i', "initrd", &(cfg)->initrd_filename, "initrd", "Initial RAM disk image"),\
OPT_END() \
};
argc = parse_options(argc, argv, options,run_usage,
PARSE_OPT_STOP_AT_NON_OPTION |
PARSE_OPT_KEEP_DASHDASH);
printf("a");
}