blob: 55711eda2f37f67e44233fe5d22a7496859a7c6c [file] [log] [blame]
/*
* Copyright (c) 2022 Samsung Electronics Co., Ltd.
* All Rights Reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* - Neither the name of the copyright owner, nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef _OAPV_APP_ARGS_H_
#define _OAPV_APP_ARGS_H_
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "oapv.h"
#define ARGS_VAL_TYPE_MANDATORY (1 << 0) /* mandatory or not */
#define ARGS_VAL_TYPE_NONE (1 << 2) /* no value */
#define ARGS_VAL_TYPE_INTEGER (2 << 2) /* integer type value */
#define ARGS_VAL_TYPE_STRING (3 << 2) /* string type value */
#define ARGS_GET_CMD_OPT_VAL_TYPE(x) ((x) & 0x0C)
#define ARGS_GET_IS_OPT_TYPE_PPT(x) (((x) >> 1) & 0x01)
#define ARGS_END_KEY (0)
#define ARGS_NO_KEY (127)
#define ARGS_KEY_LONG_CONFIG "config"
#define ARGS_MAX_NUM_CONF_FILES (16)
#define ARGS_MAX_KEY_LONG (32)
typedef struct args_opt {
char key; /* option keyword. ex) -f */
char key_long[ARGS_MAX_KEY_LONG]; /* option long keyword, ex) --file */
int val_type; /* value type */
int flag; /* flag to setting or not */
void *val; /* actual value */
char desc[512]; /* description of option */
} args_opt_t;
typedef struct args_parser args_parser_t;
struct args_parser {
void (*release)(args_parser_t *args);
int (*parse)(args_parser_t *args, int argc, const char *argv[], char **errstr);
int (*get_help)(args_parser_t *args, int idx, char *help);
int (*get_str)(args_parser_t *args, char *keyl, char *str, int *flag);
int (*get_int)(args_parser_t *args, char *keyl, int *val, int *flag);
int (*set_str)(args_parser_t *args, char *keyl, char *str);
int (*set_int)(args_parser_t *args, char *keyl, int val);
int (*set_flag)(args_parser_t *args, char *keyl, int flag);
int (*check_mandatory)(args_parser_t *args, char **err_arg);
args_opt_t *opts;
int num_option;
};
static int args_search_long_key(args_opt_t *opts, const char *key)
{
args_opt_t *o;
int oidx = 0;
o = opts;
while(o->key != ARGS_END_KEY) {
if(!strcmp(key, o->key_long)) {
return oidx;
}
oidx++;
o++;
}
return -1;
}
static int args_search_short_arg(args_opt_t *ops, const char key)
{
args_opt_t *o;
int oidx = 0;
o = ops;
while(o->key != ARGS_END_KEY) {
if(o->key != ARGS_NO_KEY && o->key == key) {
return oidx;
}
oidx++;
o++;
}
return -1;
}
static int args_read_value(args_opt_t *ops, const char *argv)
{
if(argv == NULL || ops->val == NULL) {
return -1;
}
if(argv[0] == '-' && (argv[1] < '0' || argv[1] > '9'))
return -1;
switch(ARGS_GET_CMD_OPT_VAL_TYPE(ops->val_type)) {
case ARGS_VAL_TYPE_INTEGER:
*((int *)ops->val) = atoi(argv);
break;
case ARGS_VAL_TYPE_STRING:
strcpy((char *)ops->val, argv);
break;
default:
return -1;
}
return 0;
}
static int args_get_arg(args_opt_t *ops, int idx, char *result)
{
char vtype[32];
char value[512];
args_opt_t *o = ops + idx;
switch(ARGS_GET_CMD_OPT_VAL_TYPE(o->val_type)) {
case ARGS_VAL_TYPE_INTEGER:
strncpy(vtype, "INTEGER", sizeof(vtype) - 1);
sprintf(value, "%d", *((int *)o->val));
break;
case ARGS_VAL_TYPE_STRING:
strncpy(vtype, "STRING", sizeof(vtype) - 1);
sprintf(value, "%s", (char *)o->val);
break;
case ARGS_VAL_TYPE_NONE:
default:
strncpy(vtype, "FLAG", sizeof(vtype) - 1);
sprintf(value, "%d", *((int *)o->val));
break;
}
if(o->flag) {
strcat(value, " (SET)");
}
else {
strcat(value, " (DEFAULT)");
}
sprintf(result, " -%c(--%s) = %s\n : %s", o->key, o->key_long,
value, o->desc);
return 0;
}
static int args_parse_int_x_int(char *str, int *num0, int *num1)
{
char str0_t[64];
int i, cnt0 = 0, cnt1;
char *str0, *str1 = NULL;
str0 = str;
cnt1 = (int)strlen(str);
/* find 'x' */
for(i = 0; i < (int)strlen(str); i++) {
if(str[i] == 'x' || str[i] == 'X') {
str1 = str + i + 1;
cnt0 = i;
cnt1 = cnt1 - cnt0 - 1;
break;
}
}
/* check malformed data */
if(str1 == NULL || cnt0 == 0 || cnt1 == 0)
return -1;
for(i = 0; i < cnt0; i++) {
if(str0[i] < 0x30 || str0[i] > 0x39)
return -1; /* not a number */
}
for(i = 0; i < cnt1; i++) {
if(str1[i] < 0x30 || str1[i] > 0x39)
return -1; /* not a number */
}
strncpy(str0_t, str0, cnt0);
str0_t[cnt0] = '\0';
*num0 = atoi(str0_t);
*num1 = atoi(str1);
return 0;
}
static int args_parse_cfg(FILE *fp, args_opt_t *ops, int is_type_ppt)
{
char *parser;
char line[256] = "", tag[50] = "", val[256] = "";
int oidx;
while(fgets(line, sizeof(line), fp)) {
parser = strchr(line, '#');
if(parser != NULL)
*parser = '\0';
parser = strtok(line, "= \t");
if(parser == NULL)
continue;
strncpy(tag, parser, sizeof(tag) - 1);
parser = strtok(NULL, "=\n");
if(parser == NULL)
continue;
strncpy(val, parser, sizeof(val) - 1);
oidx = args_search_long_key(ops, tag);
if(oidx < 0)
continue;
if(ops[oidx].val == NULL) {
return -1;
}
if(ARGS_GET_IS_OPT_TYPE_PPT(ops[oidx].val_type) == is_type_ppt) {
if(ARGS_GET_CMD_OPT_VAL_TYPE(ops[oidx].val_type) != ARGS_VAL_TYPE_NONE) {
if(args_read_value(ops + oidx, val))
continue;
}
else {
*((int *)ops[oidx].val) = 1;
}
ops[oidx].flag = 1;
}
}
return 0;
}
static int args_parse_cmd(int argc, const char *argv[], args_opt_t *ops,
int *idx, char **errstr)
{
int aidx; /* arg index */
int oidx; /* option index */
aidx = *idx + 1;
if(aidx >= argc || argv[aidx] == NULL)
goto NO_MORE;
if(argv[aidx][0] != '-')
goto ERR;
if(argv[aidx][1] == '-') {
/* long option */
oidx = args_search_long_key(ops, argv[aidx] + 2);
if(oidx < 0) {
*errstr = (char *)argv[aidx];
goto ERR;
}
}
else if(strlen(argv[aidx]) == 2) {
/* short option */
oidx = args_search_short_arg(ops, argv[aidx][1]);
if(oidx < 0) {
*errstr = (char *)argv[aidx];
goto ERR;
}
}
else {
goto ERR;
}
if(ARGS_GET_CMD_OPT_VAL_TYPE(ops[oidx].val_type) !=
ARGS_VAL_TYPE_NONE) {
if(aidx + 1 >= argc) {
*errstr = (char *)argv[aidx];
goto ERR;
}
if(args_read_value(ops + oidx, argv[aidx + 1])) {
*errstr = (char *)argv[aidx];
goto ERR;
}
*idx = *idx + 1;
}
else {
*((int *)ops[oidx].val) = 1;
}
ops[oidx].flag = 1;
*idx = *idx + 1;
return ops[oidx].key;
NO_MORE:
return 0;
ERR:
return -1;
}
static int args_set_variable_by_key_long(args_opt_t *opts, char *key_long, void *var)
{
int idx;
char buf[ARGS_MAX_KEY_LONG];
char *ko = key_long;
char *kt = buf;
/* if long key has "_", convert to "-". */
while(*ko != '\0') {
if(*ko == '_')
*kt = '-';
else
*kt = *ko;
ko++;
kt++;
}
*kt = '\0';
idx = args_search_long_key(opts, buf);
if(idx < 0)
return -1;
opts[idx].val = var;
return 0;
}
static int args_set_variable_by_key(args_opt_t *opts, char *key, void *var)
{
int idx;
idx = args_search_short_arg(opts, key[0]);
if(idx < 0)
return -1;
opts[idx].val = var;
return 0;
}
#define ARGS_SET_PARAM_VAR_KEY_LONG(opts, param, key_long) \
args_set_variable_by_key_long(opts, #key_long, (void *)&((param)->key_long))
#define ARGS_SET_PARAM_VAR_KEY(opts, param, key) \
args_set_variable_by_key(opts, #key, (void *)&((param)->key))
static int args_get(args_parser_t *args, char *keyl, void **val, int *flag)
{
int idx;
idx = args_search_long_key(args->opts, keyl);
if(idx >= 0) {
if(val)
*val = args->opts[idx].val;
if(flag)
*flag = args->opts[idx].flag;
return 0;
}
else {
if(val)
*val = NULL; /* no value */
if(flag)
*flag = 0; /* no set */
return -1;
}
}
static int args_set_str(args_parser_t *args, char *keyl, char *str)
{
int idx;
idx = args_search_long_key(args->opts, keyl);
if(idx >= 0) {
sprintf((char *)(args->opts[idx].val), "%s", str);
args->opts[idx].flag = 1;
return 0;
}
else {
return -1;
}
}
static int args_set_int(args_parser_t *args, char *keyl, int val)
{
int idx;
idx = args_search_long_key(args->opts, keyl);
if(idx >= 0) {
*((int *)(args->opts[idx].val)) = val;
args->opts[idx].flag = 1;
return 0;
}
else {
return -1;
}
}
static int args_set_flag(args_parser_t *args, char *keyl, int flag)
{
int idx;
idx = args_search_long_key(args->opts, keyl);
if(idx >= 0) {
args->opts[idx].flag = flag;
return 0;
}
return -1;
}
static int args_get_str(args_parser_t *args, char *keyl, char *str, int *flag)
{
char *p = NULL;
if(args_get(args, keyl, (void **)&p, flag))
return -1;
if(p) {
if(str)
strcpy(str, p);
}
return 0;
}
static int args_get_int(args_parser_t *args, char *keyl, int *val, int *flag)
{
int *p = NULL;
if(args_get(args, keyl, (void **)&p, flag))
return -1;
if(p) {
*val = *p;
}
return 0;
}
static int args_parse(args_parser_t *args, int argc, const char *argv[],
char **errstr)
{
int i, ret = 0, idx = 0;
const char *fname_cfg = NULL;
FILE *fp;
int num_configs = 0;
int pos_conf_files[ARGS_MAX_NUM_CONF_FILES];
memset(&pos_conf_files, -1, sizeof(int) * ARGS_MAX_NUM_CONF_FILES);
/* config file parsing */
for(i = 1; i < argc; i++) {
if(!strcmp(argv[i], "--" ARGS_KEY_LONG_CONFIG)) {
if(i + 1 < argc) {
num_configs++;
pos_conf_files[num_configs - 1] = i + 1;
}
}
}
for(int i = 0; i < num_configs; i++) {
fname_cfg = argv[pos_conf_files[i]];
if(fname_cfg) {
fp = fopen(fname_cfg, "r");
if(fp == NULL)
return -1; /* config file error */
if(args_parse_cfg(fp, args->opts, 1)) {
fclose(fp);
return -1; /* config file error */
}
fclose(fp);
}
}
/* command line parsing */
while(1) {
ret = args_parse_cmd(argc, argv, args->opts, &idx, errstr);
if(ret <= 0)
break;
}
return ret;
}
static int args_get_help(args_parser_t *args, int idx, char *help)
{
int optional;
char vtype[32];
args_opt_t *o = args->opts + idx;
char default_value[256] = { 0 };
switch(ARGS_GET_CMD_OPT_VAL_TYPE(o->val_type)) {
case ARGS_VAL_TYPE_INTEGER:
strncpy(vtype, "INTEGER", sizeof(vtype) - 1);
if(o->val != NULL)
sprintf(default_value, " [%d]", *(int *)(o->val));
break;
case ARGS_VAL_TYPE_STRING:
strncpy(vtype, "STRING", sizeof(vtype) - 1);
if(o->val != NULL)
sprintf(default_value, " [%s]", strlen((char *)(o->val)) == 0 ? "None" : (char *)(o->val));
break;
case ARGS_VAL_TYPE_NONE:
default:
strncpy(vtype, "FLAG", sizeof(vtype) - 1);
if(o->val != NULL)
sprintf(default_value, " [%s]", *(int *)(o->val) ? "On" : "Off");
break;
}
optional = !(o->val_type & ARGS_VAL_TYPE_MANDATORY);
if(o->key != ARGS_NO_KEY) {
sprintf(help, " -%c, --%s [%s]%s%s\n : %s", o->key, o->key_long,
vtype, (optional) ? " (optional)" : "", (optional) ? default_value : "", o->desc);
}
else {
sprintf(help, " --%s [%s]%s%s\n : %s", o->key_long,
vtype, (optional) ? " (optional)" : "", (optional) ? default_value : "", o->desc);
}
return 0;
}
static int args_check_mandatory(args_parser_t *args, char **err_arg)
{
args_opt_t *o = args->opts;
while(o->key != 0) {
if(o->val_type & ARGS_VAL_TYPE_MANDATORY) {
if(o->flag == 0) {
/* not filled all mandatory argument */
*err_arg = o->key_long;
return -1;
}
}
o++;
}
return 0;
}
static void args_release(args_parser_t *args)
{
if(args != NULL) {
if(args->opts != NULL)
free(args->opts);
free(args);
}
}
static args_parser_t *args_create(const args_opt_t *opt_table, int num_opt)
{
args_parser_t *args = NULL;
args_opt_t *opts = NULL;
args = (args_parser_t *)malloc(sizeof(args_parser_t));
if(args == NULL)
goto ERR;
memset(args, 0, sizeof(args_parser_t));
opts = (args_opt_t *)malloc(num_opt * sizeof(args_opt_t));
if(opts == NULL)
goto ERR;
memcpy(opts, opt_table, num_opt * sizeof(args_opt_t));
args->opts = opts;
args->release = args_release;
args->parse = args_parse;
args->get_help = args_get_help;
args->get_str = args_get_str;
args->get_int = args_get_int;
args->set_str = args_set_str;
args->set_int = args_set_int;
args->set_flag = args_set_flag;
args->check_mandatory = args_check_mandatory;
/* find actual number of options */
args->num_option = 0;
while(opt_table[args->num_option].key != ARGS_END_KEY)
args->num_option++;
return args;
ERR:
free(opts);
free(args);
return NULL;
}
#endif /*_OAPV_APP_ARGS_H_ */