| /* |
| * 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_ */ |