kvmtool学习二——参数解析

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; // parse_opt_flags 组合
}; // ctx -> context

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;   /* initrd 文件 */
}; // kvm->cfg 成员结构体部分
// 当e=0时,-!!(e) 为-1 ,编译报错 negative width in bit-field
#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)
// __builtin_types_compatible_p gcc扩展,用来判断两个类型是否相同 相同返回1,不同返回-1


#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){
	// 传递结果 (ctx, opt, flags,(const char **)opt->value)
	if (p->opt) {
		*arg = p->opt;
		p->opt = NULL;
	} else if (p->argc > 1) {
		p->argc--;
		*arg = *++p->argv; // 指向 -k bzImage 的 bzImage,opt-value 改变,同时kvm-cfg也改变
	} 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:
        // (const char *str, char **endptr, int base)
        // endptr -- 对类型为 char* 的对象的引用,其值由函数设置为 str 中数值后的下一个字符
        // base 基数 0 默认检查 0x ...
		*(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; // 1&2=0
	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;
	}
} // str是否包含prefix前缀

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; // 对于-k 此时 为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); // !(3&16)=1

	/* we must reset ->opt, unknown short option leave it dangling 悬挂的;摇摆的 */
	ctx->opt = NULL;

	for (; ctx->argc; ctx->argc--, ctx->argv++) {
		const char *arg = ctx->argv[0]; // 取第一个参数 此时 arg = -k
        // 参数格式:-k bzImage 
		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; // opt = 'k'
            // 是否输出帮助
			if (internal_help && *ctx->opt == 'h')
                return 0;
				// return parse_options_usage(usagestr, options);
			switch (parse_short_opt(ctx, options)) {
			// 解析短选项 ,如-k
                case -1:
                    return 0;
                    // return parse_options_usage(usagestr, options);
                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;
					// return parse_options_usage(usagestr,options);
				switch (parse_short_opt(ctx, options)) {
				case -1:
                return 0;
					// return parse_options_usage(usagestr,options);
				case -2:

                    // strdup()会先用maolloc()配置与参数s 字符串相同的空间大小,然后将参数s 字符串的内容复制到该内存地址,然后把该地址返回
					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;
		}

		// if (internal_help && !strcmp(arg + 2, "help-all"))
		// 	// return usage_with_options_internal(usagestr, options,
		// 	// 		1);
		// if (internal_help && !strcmp(arg + 2, "help"))
		// 	return parse_options_usage(usagestr, options);
		// switch (parse_long_opt(ctx, arg + 2, options)) {
		// case -1:
		// 	return parse_options_usage(usagestr, options);
		// case -2:
		// 	goto unknown;
		// default:
		// 	break;
		// }
		continue;
unknown:
		if (!(ctx->flags & PARSE_OPT_KEEP_UNKNOWN))
			return -1;
		ctx->out[ctx->cpidx++] = ctx->argv[0];
		ctx->opt = NULL;
	}
	return 0; // 0 
}

static int parse_options_end(struct parse_opt_ctx_t *ctx){
	// memmove(void *str1, const void *str2, size_t n) 从 str2 复制 n 个字符到 str1,但是在重叠内存块这方面 在此输入 ctx-argc=0
	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: /* PARSE_OPT_UNKNOWN */
            if (ctx.argv[0][1] == '-') {
                printf("unknown option `%s'", ctx.argv[0] + 2);
            } else {
                printf("unknown switch `%c'", *ctx.opt);
            }
            // usage_with_options(usagestr, options);
	}
	// 是否还有参数没有解析,自定义选项处理
	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)); // 初始化为0
    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() \
    }; // 取BUILD_OPTIONS 宏展开 截取相关部分
    argc = parse_options(argc, argv, options,run_usage,
				PARSE_OPT_STOP_AT_NON_OPTION |
				PARSE_OPT_KEEP_DASHDASH);
    printf("a");
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值