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