1 /*
2 * Copyright (c) 2022 Samsung Electronics Co., Ltd.
3 * All Rights Reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * - Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 *
11 * - Redistributions in binary form must reproduce the above copyright notice,
12 * this list of conditions and the following disclaimer in the documentation
13 * and/or other materials provided with the distribution.
14 *
15 * - Neither the name of the copyright owner, nor the names of its contributors
16 * may be used to endorse or promote products derived from this software
17 * without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
23 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32 #ifndef _OAPV_APP_ARGS_H_
33 #define _OAPV_APP_ARGS_H_
34
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include "oapv.h"
39
40 #define ARGS_VAL_TYPE_MANDATORY (1 << 0) /* mandatory or not */
41 #define ARGS_VAL_TYPE_NONE (1 << 2) /* no value */
42 #define ARGS_VAL_TYPE_INTEGER (2 << 2) /* integer type value */
43 #define ARGS_VAL_TYPE_STRING (3 << 2) /* string type value */
44 #define ARGS_GET_CMD_OPT_VAL_TYPE(x) ((x) & 0x0C)
45 #define ARGS_GET_IS_OPT_TYPE_PPT(x) (((x) >> 1) & 0x01)
46
47 #define ARGS_END_KEY (0)
48 #define ARGS_NO_KEY (127)
49 #define ARGS_KEY_LONG_CONFIG "config"
50 #define ARGS_MAX_NUM_CONF_FILES (16)
51
52 #define ARGS_MAX_KEY_LONG (32)
53
54 typedef struct args_opt {
55 char key; /* option keyword. ex) -f */
56 char key_long[ARGS_MAX_KEY_LONG]; /* option long keyword, ex) --file */
57 int val_type; /* value type */
58 int flag; /* flag to setting or not */
59 void *val; /* actual value */
60 char desc[512]; /* description of option */
61 } args_opt_t;
62
63 typedef struct args_parser args_parser_t;
64 struct args_parser {
65 void (*release)(args_parser_t *args);
66 int (*parse)(args_parser_t *args, int argc, const char *argv[], char **errstr);
67 int (*get_help)(args_parser_t *args, int idx, char *help);
68 int (*get_str)(args_parser_t *args, char *keyl, char *str, int *flag);
69 int (*get_int)(args_parser_t *args, char *keyl, int *val, int *flag);
70 int (*set_str)(args_parser_t *args, char *keyl, char *str);
71 int (*set_int)(args_parser_t *args, char *keyl, int val);
72 int (*set_flag)(args_parser_t *args, char *keyl, int flag);
73 int (*check_mandatory)(args_parser_t *args, char **err_arg);
74
75 args_opt_t *opts;
76 int num_option;
77 };
78
args_search_long_key(args_opt_t * opts,const char * key)79 static int args_search_long_key(args_opt_t *opts, const char *key)
80 {
81 args_opt_t *o;
82 int oidx = 0;
83
84 o = opts;
85 while(o->key != ARGS_END_KEY) {
86 if(!strcmp(key, o->key_long)) {
87 return oidx;
88 }
89 oidx++;
90 o++;
91 }
92 return -1;
93 }
94
args_search_short_arg(args_opt_t * ops,const char key)95 static int args_search_short_arg(args_opt_t *ops, const char key)
96 {
97 args_opt_t *o;
98 int oidx = 0;
99
100 o = ops;
101
102 while(o->key != ARGS_END_KEY) {
103 if(o->key != ARGS_NO_KEY && o->key == key) {
104 return oidx;
105 }
106 oidx++;
107 o++;
108 }
109 return -1;
110 }
111
args_read_value(args_opt_t * ops,const char * argv)112 static int args_read_value(args_opt_t *ops, const char *argv)
113 {
114 if(argv == NULL || ops->val == NULL) {
115 return -1;
116 }
117 if(argv[0] == '-' && (argv[1] < '0' || argv[1] > '9'))
118 return -1;
119
120 switch(ARGS_GET_CMD_OPT_VAL_TYPE(ops->val_type)) {
121 case ARGS_VAL_TYPE_INTEGER:
122 *((int *)ops->val) = atoi(argv);
123 break;
124
125 case ARGS_VAL_TYPE_STRING:
126 strcpy((char *)ops->val, argv);
127 break;
128
129 default:
130 return -1;
131 }
132 return 0;
133 }
134
args_get_arg(args_opt_t * ops,int idx,char * result)135 static int args_get_arg(args_opt_t *ops, int idx, char *result)
136 {
137 char vtype[32];
138 char value[512];
139 args_opt_t *o = ops + idx;
140
141 switch(ARGS_GET_CMD_OPT_VAL_TYPE(o->val_type)) {
142 case ARGS_VAL_TYPE_INTEGER:
143 strncpy(vtype, "INTEGER", sizeof(vtype) - 1);
144 sprintf(value, "%d", *((int *)o->val));
145 break;
146
147 case ARGS_VAL_TYPE_STRING:
148 strncpy(vtype, "STRING", sizeof(vtype) - 1);
149 sprintf(value, "%s", (char *)o->val);
150 break;
151
152 case ARGS_VAL_TYPE_NONE:
153 default:
154 strncpy(vtype, "FLAG", sizeof(vtype) - 1);
155 sprintf(value, "%d", *((int *)o->val));
156 break;
157 }
158
159 if(o->flag) {
160 strcat(value, " (SET)");
161 }
162 else {
163 strcat(value, " (DEFAULT)");
164 }
165
166 sprintf(result, " -%c(--%s) = %s\n : %s", o->key, o->key_long,
167 value, o->desc);
168
169 return 0;
170 }
171
args_parse_int_x_int(char * str,int * num0,int * num1)172 static int args_parse_int_x_int(char *str, int *num0, int *num1)
173 {
174 char str0_t[64];
175 int i, cnt0 = 0, cnt1;
176 char *str0, *str1 = NULL;
177
178 str0 = str;
179 cnt1 = (int)strlen(str);
180
181 /* find 'x' */
182 for(i = 0; i < (int)strlen(str); i++) {
183 if(str[i] == 'x' || str[i] == 'X') {
184 str1 = str + i + 1;
185 cnt0 = i;
186 cnt1 = cnt1 - cnt0 - 1;
187 break;
188 }
189 }
190
191 /* check malformed data */
192 if(str1 == NULL || cnt0 == 0 || cnt1 == 0)
193 return -1;
194
195 for(i = 0; i < cnt0; i++) {
196 if(str0[i] < 0x30 || str0[i] > 0x39)
197 return -1; /* not a number */
198 }
199 for(i = 0; i < cnt1; i++) {
200 if(str1[i] < 0x30 || str1[i] > 0x39)
201 return -1; /* not a number */
202 }
203
204 strncpy(str0_t, str0, cnt0);
205 str0_t[cnt0] = '\0';
206
207 *num0 = atoi(str0_t);
208 *num1 = atoi(str1);
209
210 return 0;
211 }
212
args_parse_cfg(FILE * fp,args_opt_t * ops,int is_type_ppt)213 static int args_parse_cfg(FILE *fp, args_opt_t *ops, int is_type_ppt)
214 {
215 char *parser;
216 char line[256] = "", tag[50] = "", val[256] = "";
217 int oidx;
218
219 while(fgets(line, sizeof(line), fp)) {
220 parser = strchr(line, '#');
221 if(parser != NULL)
222 *parser = '\0';
223
224 parser = strtok(line, "= \t");
225 if(parser == NULL)
226 continue;
227 strncpy(tag, parser, sizeof(tag) - 1);
228
229 parser = strtok(NULL, "=\n");
230 if(parser == NULL)
231 continue;
232 strncpy(val, parser, sizeof(val) - 1);
233
234 oidx = args_search_long_key(ops, tag);
235 if(oidx < 0)
236 continue;
237
238 if(ops[oidx].val == NULL) {
239 return -1;
240 }
241
242 if(ARGS_GET_IS_OPT_TYPE_PPT(ops[oidx].val_type) == is_type_ppt) {
243 if(ARGS_GET_CMD_OPT_VAL_TYPE(ops[oidx].val_type) != ARGS_VAL_TYPE_NONE) {
244 if(args_read_value(ops + oidx, val))
245 continue;
246 }
247 else {
248 *((int *)ops[oidx].val) = 1;
249 }
250 ops[oidx].flag = 1;
251 }
252 }
253 return 0;
254 }
255
args_parse_cmd(int argc,const char * argv[],args_opt_t * ops,int * idx,char ** errstr)256 static int args_parse_cmd(int argc, const char *argv[], args_opt_t *ops,
257 int *idx, char **errstr)
258 {
259 int aidx; /* arg index */
260 int oidx; /* option index */
261
262 aidx = *idx + 1;
263
264 if(aidx >= argc || argv[aidx] == NULL)
265 goto NO_MORE;
266 if(argv[aidx][0] != '-')
267 goto ERR;
268
269 if(argv[aidx][1] == '-') {
270 /* long option */
271 oidx = args_search_long_key(ops, argv[aidx] + 2);
272 if(oidx < 0) {
273 *errstr = (char *)argv[aidx];
274 goto ERR;
275 }
276 }
277 else if(strlen(argv[aidx]) == 2) {
278 /* short option */
279 oidx = args_search_short_arg(ops, argv[aidx][1]);
280 if(oidx < 0) {
281 *errstr = (char *)argv[aidx];
282 goto ERR;
283 }
284 }
285 else {
286 goto ERR;
287 }
288
289 if(ARGS_GET_CMD_OPT_VAL_TYPE(ops[oidx].val_type) !=
290 ARGS_VAL_TYPE_NONE) {
291 if(aidx + 1 >= argc) {
292 *errstr = (char *)argv[aidx];
293 goto ERR;
294 }
295 if(args_read_value(ops + oidx, argv[aidx + 1])) {
296 *errstr = (char *)argv[aidx];
297 goto ERR;
298 }
299 *idx = *idx + 1;
300 }
301 else {
302 *((int *)ops[oidx].val) = 1;
303 }
304 ops[oidx].flag = 1;
305 *idx = *idx + 1;
306
307 return ops[oidx].key;
308
309 NO_MORE:
310 return 0;
311
312 ERR:
313 return -1;
314 }
315
args_set_variable_by_key_long(args_opt_t * opts,char * key_long,void * var)316 static int args_set_variable_by_key_long(args_opt_t *opts, char *key_long, void *var)
317 {
318 int idx;
319 char buf[ARGS_MAX_KEY_LONG];
320 char *ko = key_long;
321 char *kt = buf;
322
323 /* if long key has "_", convert to "-". */
324 while(*ko != '\0') {
325 if(*ko == '_')
326 *kt = '-';
327 else
328 *kt = *ko;
329
330 ko++;
331 kt++;
332 }
333 *kt = '\0';
334
335 idx = args_search_long_key(opts, buf);
336 if(idx < 0)
337 return -1;
338 opts[idx].val = var;
339 return 0;
340 }
341
args_set_variable_by_key(args_opt_t * opts,char * key,void * var)342 static int args_set_variable_by_key(args_opt_t *opts, char *key, void *var)
343 {
344 int idx;
345 idx = args_search_short_arg(opts, key[0]);
346 if(idx < 0)
347 return -1;
348 opts[idx].val = var;
349 return 0;
350 }
351
352 #define ARGS_SET_PARAM_VAR_KEY_LONG(opts, param, key_long) \
353 args_set_variable_by_key_long(opts, #key_long, (void *)&((param)->key_long))
354
355 #define ARGS_SET_PARAM_VAR_KEY(opts, param, key) \
356 args_set_variable_by_key(opts, #key, (void *)&((param)->key))
357
args_get(args_parser_t * args,char * keyl,void ** val,int * flag)358 static int args_get(args_parser_t *args, char *keyl, void **val, int *flag)
359 {
360 int idx;
361
362 idx = args_search_long_key(args->opts, keyl);
363 if(idx >= 0) {
364 if(val)
365 *val = args->opts[idx].val;
366 if(flag)
367 *flag = args->opts[idx].flag;
368 return 0;
369 }
370 else {
371 if(val)
372 *val = NULL; /* no value */
373 if(flag)
374 *flag = 0; /* no set */
375 return -1;
376 }
377 }
378
args_set_str(args_parser_t * args,char * keyl,char * str)379 static int args_set_str(args_parser_t *args, char *keyl, char *str)
380 {
381 int idx;
382
383 idx = args_search_long_key(args->opts, keyl);
384 if(idx >= 0) {
385 sprintf((char *)(args->opts[idx].val), "%s", str);
386 args->opts[idx].flag = 1;
387 return 0;
388 }
389 else {
390 return -1;
391 }
392 }
393
args_set_int(args_parser_t * args,char * keyl,int val)394 static int args_set_int(args_parser_t *args, char *keyl, int val)
395 {
396 int idx;
397
398 idx = args_search_long_key(args->opts, keyl);
399 if(idx >= 0) {
400 *((int *)(args->opts[idx].val)) = val;
401 args->opts[idx].flag = 1;
402 return 0;
403 }
404 else {
405 return -1;
406 }
407 }
408
args_set_flag(args_parser_t * args,char * keyl,int flag)409 static int args_set_flag(args_parser_t *args, char *keyl, int flag)
410 {
411 int idx;
412
413 idx = args_search_long_key(args->opts, keyl);
414 if(idx >= 0) {
415 args->opts[idx].flag = flag;
416 return 0;
417 }
418 return -1;
419 }
420
args_get_str(args_parser_t * args,char * keyl,char * str,int * flag)421 static int args_get_str(args_parser_t *args, char *keyl, char *str, int *flag)
422 {
423 char *p = NULL;
424 if(args_get(args, keyl, (void **)&p, flag))
425 return -1;
426 if(p) {
427 if(str)
428 strcpy(str, p);
429 }
430 return 0;
431 }
432
args_get_int(args_parser_t * args,char * keyl,int * val,int * flag)433 static int args_get_int(args_parser_t *args, char *keyl, int *val, int *flag)
434 {
435 int *p = NULL;
436 if(args_get(args, keyl, (void **)&p, flag))
437 return -1;
438 if(p) {
439 *val = *p;
440 }
441 return 0;
442 }
443
args_parse(args_parser_t * args,int argc,const char * argv[],char ** errstr)444 static int args_parse(args_parser_t *args, int argc, const char *argv[],
445 char **errstr)
446 {
447 int i, ret = 0, idx = 0;
448 const char *fname_cfg = NULL;
449 FILE *fp;
450
451 int num_configs = 0;
452 int pos_conf_files[ARGS_MAX_NUM_CONF_FILES];
453 memset(&pos_conf_files, -1, sizeof(int) * ARGS_MAX_NUM_CONF_FILES);
454
455 /* config file parsing */
456 for(i = 1; i < argc; i++) {
457 if(!strcmp(argv[i], "--" ARGS_KEY_LONG_CONFIG)) {
458 if(i + 1 < argc) {
459 num_configs++;
460 pos_conf_files[num_configs - 1] = i + 1;
461 }
462 }
463 }
464 for(int i = 0; i < num_configs; i++) {
465 fname_cfg = argv[pos_conf_files[i]];
466 if(fname_cfg) {
467 fp = fopen(fname_cfg, "r");
468 if(fp == NULL)
469 return -1; /* config file error */
470
471 if(args_parse_cfg(fp, args->opts, 1)) {
472 fclose(fp);
473 return -1; /* config file error */
474 }
475 fclose(fp);
476 }
477 }
478 /* command line parsing */
479 while(1) {
480 ret = args_parse_cmd(argc, argv, args->opts, &idx, errstr);
481 if(ret <= 0)
482 break;
483 }
484 return ret;
485 }
486
args_get_help(args_parser_t * args,int idx,char * help)487 static int args_get_help(args_parser_t *args, int idx, char *help)
488 {
489 int optional;
490 char vtype[32];
491 args_opt_t *o = args->opts + idx;
492 char default_value[256] = { 0 };
493
494 switch(ARGS_GET_CMD_OPT_VAL_TYPE(o->val_type)) {
495 case ARGS_VAL_TYPE_INTEGER:
496 strncpy(vtype, "INTEGER", sizeof(vtype) - 1);
497 if(o->val != NULL)
498 sprintf(default_value, " [%d]", *(int *)(o->val));
499 break;
500 case ARGS_VAL_TYPE_STRING:
501 strncpy(vtype, "STRING", sizeof(vtype) - 1);
502 if(o->val != NULL)
503 sprintf(default_value, " [%s]", strlen((char *)(o->val)) == 0 ? "None" : (char *)(o->val));
504 break;
505 case ARGS_VAL_TYPE_NONE:
506 default:
507 strncpy(vtype, "FLAG", sizeof(vtype) - 1);
508 if(o->val != NULL)
509 sprintf(default_value, " [%s]", *(int *)(o->val) ? "On" : "Off");
510 break;
511 }
512 optional = !(o->val_type & ARGS_VAL_TYPE_MANDATORY);
513
514 if(o->key != ARGS_NO_KEY) {
515 sprintf(help, " -%c, --%s [%s]%s%s\n : %s", o->key, o->key_long,
516 vtype, (optional) ? " (optional)" : "", (optional) ? default_value : "", o->desc);
517 }
518 else {
519 sprintf(help, " --%s [%s]%s%s\n : %s", o->key_long,
520 vtype, (optional) ? " (optional)" : "", (optional) ? default_value : "", o->desc);
521 }
522
523 return 0;
524 }
525
args_check_mandatory(args_parser_t * args,char ** err_arg)526 static int args_check_mandatory(args_parser_t *args, char **err_arg)
527 {
528 args_opt_t *o = args->opts;
529
530 while(o->key != 0) {
531 if(o->val_type & ARGS_VAL_TYPE_MANDATORY) {
532 if(o->flag == 0) {
533 /* not filled all mandatory argument */
534 *err_arg = o->key_long;
535 return -1;
536 }
537 }
538 o++;
539 }
540 return 0;
541 }
542
args_release(args_parser_t * args)543 static void args_release(args_parser_t *args)
544 {
545 if(args != NULL) {
546 if(args->opts != NULL)
547 free(args->opts);
548 free(args);
549 }
550 }
551
args_create(const args_opt_t * opt_table,int num_opt)552 static args_parser_t *args_create(const args_opt_t *opt_table, int num_opt)
553 {
554 args_parser_t *args = NULL;
555 args_opt_t *opts = NULL;
556
557 args = (args_parser_t *)malloc(sizeof(args_parser_t));
558 if(args == NULL)
559 goto ERR;
560 memset(args, 0, sizeof(args_parser_t));
561
562 opts = (args_opt_t *)malloc(num_opt * sizeof(args_opt_t));
563 if(opts == NULL)
564 goto ERR;
565 memcpy(opts, opt_table, num_opt * sizeof(args_opt_t));
566 args->opts = opts;
567
568 args->release = args_release;
569 args->parse = args_parse;
570 args->get_help = args_get_help;
571 args->get_str = args_get_str;
572 args->get_int = args_get_int;
573 args->set_str = args_set_str;
574 args->set_int = args_set_int;
575 args->set_flag = args_set_flag;
576 args->check_mandatory = args_check_mandatory;
577
578 /* find actual number of options */
579 args->num_option = 0;
580 while(opt_table[args->num_option].key != ARGS_END_KEY)
581 args->num_option++;
582
583 return args;
584
585 ERR:
586 free(opts);
587 free(args);
588 return NULL;
589 }
590
591 #endif /*_OAPV_APP_ARGS_H_ */
592