1*77c1e3ccSAndroid Build Coastguard Worker /*
2*77c1e3ccSAndroid Build Coastguard Worker * Copyright (c) 2020, Alliance for Open Media. All rights reserved.
3*77c1e3ccSAndroid Build Coastguard Worker *
4*77c1e3ccSAndroid Build Coastguard Worker * This source code is subject to the terms of the BSD 2 Clause License and
5*77c1e3ccSAndroid Build Coastguard Worker * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
6*77c1e3ccSAndroid Build Coastguard Worker * was not distributed with this source code in the LICENSE file, you can
7*77c1e3ccSAndroid Build Coastguard Worker * obtain it at www.aomedia.org/license/software. If the Alliance for Open
8*77c1e3ccSAndroid Build Coastguard Worker * Media Patent License 1.0 was not distributed with this source code in the
9*77c1e3ccSAndroid Build Coastguard Worker * PATENTS file, you can obtain it at www.aomedia.org/license/patent.
10*77c1e3ccSAndroid Build Coastguard Worker */
11*77c1e3ccSAndroid Build Coastguard Worker #include "common/args_helper.h"
12*77c1e3ccSAndroid Build Coastguard Worker
13*77c1e3ccSAndroid Build Coastguard Worker #include <assert.h>
14*77c1e3ccSAndroid Build Coastguard Worker #include <stdio.h>
15*77c1e3ccSAndroid Build Coastguard Worker #include <stdlib.h>
16*77c1e3ccSAndroid Build Coastguard Worker #include <string.h>
17*77c1e3ccSAndroid Build Coastguard Worker #include <limits.h>
18*77c1e3ccSAndroid Build Coastguard Worker
19*77c1e3ccSAndroid Build Coastguard Worker #define SET_ERR_STRING(...) \
20*77c1e3ccSAndroid Build Coastguard Worker if (err_msg) snprintf(err_msg, ARG_ERR_MSG_MAX_LEN, __VA_ARGS__)
21*77c1e3ccSAndroid Build Coastguard Worker
arg_init(char ** argv)22*77c1e3ccSAndroid Build Coastguard Worker struct arg arg_init(char **argv) {
23*77c1e3ccSAndroid Build Coastguard Worker struct arg a;
24*77c1e3ccSAndroid Build Coastguard Worker
25*77c1e3ccSAndroid Build Coastguard Worker a.argv = argv;
26*77c1e3ccSAndroid Build Coastguard Worker a.argv_step = 1;
27*77c1e3ccSAndroid Build Coastguard Worker a.name = NULL;
28*77c1e3ccSAndroid Build Coastguard Worker a.val = NULL;
29*77c1e3ccSAndroid Build Coastguard Worker a.def = NULL;
30*77c1e3ccSAndroid Build Coastguard Worker return a;
31*77c1e3ccSAndroid Build Coastguard Worker }
32*77c1e3ccSAndroid Build Coastguard Worker
arg_match_helper(struct arg * arg_,const struct arg_def * def,char ** argv,char * err_msg)33*77c1e3ccSAndroid Build Coastguard Worker int arg_match_helper(struct arg *arg_, const struct arg_def *def, char **argv,
34*77c1e3ccSAndroid Build Coastguard Worker char *err_msg) {
35*77c1e3ccSAndroid Build Coastguard Worker struct arg arg;
36*77c1e3ccSAndroid Build Coastguard Worker
37*77c1e3ccSAndroid Build Coastguard Worker if (err_msg) err_msg[0] = '\0';
38*77c1e3ccSAndroid Build Coastguard Worker
39*77c1e3ccSAndroid Build Coastguard Worker assert(def->has_val == 0 || def->has_val == 1 || def->has_val == -1);
40*77c1e3ccSAndroid Build Coastguard Worker
41*77c1e3ccSAndroid Build Coastguard Worker if (!argv[0] || argv[0][0] != '-') return 0;
42*77c1e3ccSAndroid Build Coastguard Worker
43*77c1e3ccSAndroid Build Coastguard Worker arg = arg_init(argv);
44*77c1e3ccSAndroid Build Coastguard Worker
45*77c1e3ccSAndroid Build Coastguard Worker if (def->short_name && !strcmp(arg.argv[0] + 1, def->short_name)) {
46*77c1e3ccSAndroid Build Coastguard Worker arg.name = arg.argv[0] + 1;
47*77c1e3ccSAndroid Build Coastguard Worker arg.val = def->has_val ? arg.argv[1] : NULL;
48*77c1e3ccSAndroid Build Coastguard Worker arg.argv_step = def->has_val ? 2 : 1;
49*77c1e3ccSAndroid Build Coastguard Worker } else if (def->long_name) {
50*77c1e3ccSAndroid Build Coastguard Worker const size_t name_len = strlen(def->long_name);
51*77c1e3ccSAndroid Build Coastguard Worker
52*77c1e3ccSAndroid Build Coastguard Worker if (arg.argv[0][1] == '-' &&
53*77c1e3ccSAndroid Build Coastguard Worker !strncmp(arg.argv[0] + 2, def->long_name, name_len) &&
54*77c1e3ccSAndroid Build Coastguard Worker (arg.argv[0][name_len + 2] == '=' ||
55*77c1e3ccSAndroid Build Coastguard Worker arg.argv[0][name_len + 2] == '\0')) {
56*77c1e3ccSAndroid Build Coastguard Worker arg.name = arg.argv[0] + 2;
57*77c1e3ccSAndroid Build Coastguard Worker arg.val = arg.name[name_len] == '=' ? arg.name + name_len + 1 : NULL;
58*77c1e3ccSAndroid Build Coastguard Worker arg.argv_step = 1;
59*77c1e3ccSAndroid Build Coastguard Worker }
60*77c1e3ccSAndroid Build Coastguard Worker }
61*77c1e3ccSAndroid Build Coastguard Worker
62*77c1e3ccSAndroid Build Coastguard Worker if (arg.name) {
63*77c1e3ccSAndroid Build Coastguard Worker if (def->has_val == -1) {
64*77c1e3ccSAndroid Build Coastguard Worker arg.def = def;
65*77c1e3ccSAndroid Build Coastguard Worker *arg_ = arg;
66*77c1e3ccSAndroid Build Coastguard Worker return 1;
67*77c1e3ccSAndroid Build Coastguard Worker }
68*77c1e3ccSAndroid Build Coastguard Worker
69*77c1e3ccSAndroid Build Coastguard Worker if (!arg.val && def->has_val) {
70*77c1e3ccSAndroid Build Coastguard Worker SET_ERR_STRING("Error: option %s requires argument.\n", arg.name);
71*77c1e3ccSAndroid Build Coastguard Worker return 0;
72*77c1e3ccSAndroid Build Coastguard Worker }
73*77c1e3ccSAndroid Build Coastguard Worker
74*77c1e3ccSAndroid Build Coastguard Worker if (arg.val && !def->has_val) {
75*77c1e3ccSAndroid Build Coastguard Worker SET_ERR_STRING("Error: option %s requires no argument.\n", arg.name);
76*77c1e3ccSAndroid Build Coastguard Worker return 0;
77*77c1e3ccSAndroid Build Coastguard Worker }
78*77c1e3ccSAndroid Build Coastguard Worker
79*77c1e3ccSAndroid Build Coastguard Worker arg.def = def;
80*77c1e3ccSAndroid Build Coastguard Worker *arg_ = arg;
81*77c1e3ccSAndroid Build Coastguard Worker return 1;
82*77c1e3ccSAndroid Build Coastguard Worker }
83*77c1e3ccSAndroid Build Coastguard Worker
84*77c1e3ccSAndroid Build Coastguard Worker return 0;
85*77c1e3ccSAndroid Build Coastguard Worker }
86*77c1e3ccSAndroid Build Coastguard Worker
arg_parse_uint_helper(const struct arg * arg,char * err_msg)87*77c1e3ccSAndroid Build Coastguard Worker unsigned int arg_parse_uint_helper(const struct arg *arg, char *err_msg) {
88*77c1e3ccSAndroid Build Coastguard Worker char *endptr;
89*77c1e3ccSAndroid Build Coastguard Worker const unsigned long rawval = strtoul(arg->val, &endptr, 10); // NOLINT
90*77c1e3ccSAndroid Build Coastguard Worker
91*77c1e3ccSAndroid Build Coastguard Worker if (err_msg) err_msg[0] = '\0';
92*77c1e3ccSAndroid Build Coastguard Worker
93*77c1e3ccSAndroid Build Coastguard Worker if (arg->val[0] != '\0' && endptr[0] == '\0') {
94*77c1e3ccSAndroid Build Coastguard Worker if (rawval <= UINT_MAX) return (unsigned int)rawval;
95*77c1e3ccSAndroid Build Coastguard Worker SET_ERR_STRING("Option %s: Value %lu out of range for unsigned int\n",
96*77c1e3ccSAndroid Build Coastguard Worker arg->name, rawval);
97*77c1e3ccSAndroid Build Coastguard Worker return 0;
98*77c1e3ccSAndroid Build Coastguard Worker }
99*77c1e3ccSAndroid Build Coastguard Worker SET_ERR_STRING("Option %s: Invalid character '%c'\n", arg->name, *endptr);
100*77c1e3ccSAndroid Build Coastguard Worker return 0;
101*77c1e3ccSAndroid Build Coastguard Worker }
102*77c1e3ccSAndroid Build Coastguard Worker
arg_parse_int_helper(const struct arg * arg,char * err_msg)103*77c1e3ccSAndroid Build Coastguard Worker int arg_parse_int_helper(const struct arg *arg, char *err_msg) {
104*77c1e3ccSAndroid Build Coastguard Worker char *endptr;
105*77c1e3ccSAndroid Build Coastguard Worker const long rawval = strtol(arg->val, &endptr, 10); // NOLINT
106*77c1e3ccSAndroid Build Coastguard Worker
107*77c1e3ccSAndroid Build Coastguard Worker if (err_msg) err_msg[0] = '\0';
108*77c1e3ccSAndroid Build Coastguard Worker
109*77c1e3ccSAndroid Build Coastguard Worker if (arg->val[0] != '\0' && endptr[0] == '\0') {
110*77c1e3ccSAndroid Build Coastguard Worker if (rawval >= INT_MIN && rawval <= INT_MAX) return (int)rawval;
111*77c1e3ccSAndroid Build Coastguard Worker SET_ERR_STRING("Option %s: Value %ld out of range for signed int\n",
112*77c1e3ccSAndroid Build Coastguard Worker arg->name, rawval);
113*77c1e3ccSAndroid Build Coastguard Worker return 0;
114*77c1e3ccSAndroid Build Coastguard Worker }
115*77c1e3ccSAndroid Build Coastguard Worker SET_ERR_STRING("Option %s: Invalid character '%c'\n", arg->name, *endptr);
116*77c1e3ccSAndroid Build Coastguard Worker return 0;
117*77c1e3ccSAndroid Build Coastguard Worker }
118*77c1e3ccSAndroid Build Coastguard Worker
arg_parse_rational_helper(const struct arg * arg,char * err_msg)119*77c1e3ccSAndroid Build Coastguard Worker struct aom_rational arg_parse_rational_helper(const struct arg *arg,
120*77c1e3ccSAndroid Build Coastguard Worker char *err_msg) {
121*77c1e3ccSAndroid Build Coastguard Worker long rawval; // NOLINT
122*77c1e3ccSAndroid Build Coastguard Worker char *endptr;
123*77c1e3ccSAndroid Build Coastguard Worker struct aom_rational rat = { 0, 1 };
124*77c1e3ccSAndroid Build Coastguard Worker
125*77c1e3ccSAndroid Build Coastguard Worker if (err_msg) err_msg[0] = '\0';
126*77c1e3ccSAndroid Build Coastguard Worker
127*77c1e3ccSAndroid Build Coastguard Worker /* parse numerator */
128*77c1e3ccSAndroid Build Coastguard Worker rawval = strtol(arg->val, &endptr, 10);
129*77c1e3ccSAndroid Build Coastguard Worker
130*77c1e3ccSAndroid Build Coastguard Worker if (arg->val[0] != '\0' && endptr[0] == '/') {
131*77c1e3ccSAndroid Build Coastguard Worker if (rawval >= INT_MIN && rawval <= INT_MAX) {
132*77c1e3ccSAndroid Build Coastguard Worker rat.num = (int)rawval;
133*77c1e3ccSAndroid Build Coastguard Worker } else {
134*77c1e3ccSAndroid Build Coastguard Worker SET_ERR_STRING("Option %s: Value %ld out of range for signed int\n",
135*77c1e3ccSAndroid Build Coastguard Worker arg->name, rawval);
136*77c1e3ccSAndroid Build Coastguard Worker return rat;
137*77c1e3ccSAndroid Build Coastguard Worker }
138*77c1e3ccSAndroid Build Coastguard Worker } else {
139*77c1e3ccSAndroid Build Coastguard Worker SET_ERR_STRING("Option %s: Expected / at '%c'\n", arg->name, *endptr);
140*77c1e3ccSAndroid Build Coastguard Worker return rat;
141*77c1e3ccSAndroid Build Coastguard Worker }
142*77c1e3ccSAndroid Build Coastguard Worker
143*77c1e3ccSAndroid Build Coastguard Worker /* parse denominator */
144*77c1e3ccSAndroid Build Coastguard Worker rawval = strtol(endptr + 1, &endptr, 10);
145*77c1e3ccSAndroid Build Coastguard Worker
146*77c1e3ccSAndroid Build Coastguard Worker if (arg->val[0] != '\0' && endptr[0] == '\0') {
147*77c1e3ccSAndroid Build Coastguard Worker if (rawval >= INT_MIN && rawval <= INT_MAX) {
148*77c1e3ccSAndroid Build Coastguard Worker rat.den = (int)rawval;
149*77c1e3ccSAndroid Build Coastguard Worker } else {
150*77c1e3ccSAndroid Build Coastguard Worker SET_ERR_STRING("Option %s: Value %ld out of range for signed int\n",
151*77c1e3ccSAndroid Build Coastguard Worker arg->name, rawval);
152*77c1e3ccSAndroid Build Coastguard Worker return rat;
153*77c1e3ccSAndroid Build Coastguard Worker }
154*77c1e3ccSAndroid Build Coastguard Worker } else {
155*77c1e3ccSAndroid Build Coastguard Worker SET_ERR_STRING("Option %s: Invalid character '%c'\n", arg->name, *endptr);
156*77c1e3ccSAndroid Build Coastguard Worker return rat;
157*77c1e3ccSAndroid Build Coastguard Worker }
158*77c1e3ccSAndroid Build Coastguard Worker
159*77c1e3ccSAndroid Build Coastguard Worker return rat;
160*77c1e3ccSAndroid Build Coastguard Worker }
161*77c1e3ccSAndroid Build Coastguard Worker
arg_parse_enum_helper(const struct arg * arg,char * err_msg)162*77c1e3ccSAndroid Build Coastguard Worker int arg_parse_enum_helper(const struct arg *arg, char *err_msg) {
163*77c1e3ccSAndroid Build Coastguard Worker const struct arg_enum_list *listptr;
164*77c1e3ccSAndroid Build Coastguard Worker long rawval; // NOLINT
165*77c1e3ccSAndroid Build Coastguard Worker char *endptr;
166*77c1e3ccSAndroid Build Coastguard Worker
167*77c1e3ccSAndroid Build Coastguard Worker if (err_msg) err_msg[0] = '\0';
168*77c1e3ccSAndroid Build Coastguard Worker
169*77c1e3ccSAndroid Build Coastguard Worker /* First see if the value can be parsed as a raw value */
170*77c1e3ccSAndroid Build Coastguard Worker rawval = strtol(arg->val, &endptr, 10);
171*77c1e3ccSAndroid Build Coastguard Worker if (arg->val[0] != '\0' && endptr[0] == '\0') {
172*77c1e3ccSAndroid Build Coastguard Worker /* Got a raw value, make sure it's valid */
173*77c1e3ccSAndroid Build Coastguard Worker for (listptr = arg->def->enums; listptr->name; listptr++)
174*77c1e3ccSAndroid Build Coastguard Worker if (listptr->val == rawval) return (int)rawval;
175*77c1e3ccSAndroid Build Coastguard Worker }
176*77c1e3ccSAndroid Build Coastguard Worker
177*77c1e3ccSAndroid Build Coastguard Worker /* Next see if it can be parsed as a string */
178*77c1e3ccSAndroid Build Coastguard Worker for (listptr = arg->def->enums; listptr->name; listptr++)
179*77c1e3ccSAndroid Build Coastguard Worker if (!strcmp(arg->val, listptr->name)) return listptr->val;
180*77c1e3ccSAndroid Build Coastguard Worker
181*77c1e3ccSAndroid Build Coastguard Worker SET_ERR_STRING("Option %s: Invalid value '%s'\n", arg->name, arg->val);
182*77c1e3ccSAndroid Build Coastguard Worker return 0;
183*77c1e3ccSAndroid Build Coastguard Worker }
184*77c1e3ccSAndroid Build Coastguard Worker
arg_parse_enum_or_int_helper(const struct arg * arg,char * err_msg)185*77c1e3ccSAndroid Build Coastguard Worker int arg_parse_enum_or_int_helper(const struct arg *arg, char *err_msg) {
186*77c1e3ccSAndroid Build Coastguard Worker if (arg->def->enums) return arg_parse_enum_helper(arg, err_msg);
187*77c1e3ccSAndroid Build Coastguard Worker return arg_parse_int_helper(arg, err_msg);
188*77c1e3ccSAndroid Build Coastguard Worker }
189*77c1e3ccSAndroid Build Coastguard Worker
190*77c1e3ccSAndroid Build Coastguard Worker // parse a comma separated list of at most n integers
191*77c1e3ccSAndroid Build Coastguard Worker // return the number of elements in the list
arg_parse_list_helper(const struct arg * arg,int * list,int n,char * err_msg)192*77c1e3ccSAndroid Build Coastguard Worker int arg_parse_list_helper(const struct arg *arg, int *list, int n,
193*77c1e3ccSAndroid Build Coastguard Worker char *err_msg) {
194*77c1e3ccSAndroid Build Coastguard Worker const char *ptr = arg->val;
195*77c1e3ccSAndroid Build Coastguard Worker char *endptr;
196*77c1e3ccSAndroid Build Coastguard Worker int i = 0;
197*77c1e3ccSAndroid Build Coastguard Worker
198*77c1e3ccSAndroid Build Coastguard Worker if (err_msg) err_msg[0] = '\0';
199*77c1e3ccSAndroid Build Coastguard Worker
200*77c1e3ccSAndroid Build Coastguard Worker while (ptr[0] != '\0') {
201*77c1e3ccSAndroid Build Coastguard Worker long rawval = strtol(ptr, &endptr, 10); // NOLINT
202*77c1e3ccSAndroid Build Coastguard Worker if (rawval < INT_MIN || rawval > INT_MAX) {
203*77c1e3ccSAndroid Build Coastguard Worker SET_ERR_STRING("Option %s: Value %ld out of range for signed int\n",
204*77c1e3ccSAndroid Build Coastguard Worker arg->name, rawval);
205*77c1e3ccSAndroid Build Coastguard Worker return 0;
206*77c1e3ccSAndroid Build Coastguard Worker } else if (i >= n) {
207*77c1e3ccSAndroid Build Coastguard Worker SET_ERR_STRING("Option %s: List has more than %d entries\n", arg->name,
208*77c1e3ccSAndroid Build Coastguard Worker n);
209*77c1e3ccSAndroid Build Coastguard Worker return 0;
210*77c1e3ccSAndroid Build Coastguard Worker } else if (*endptr == ',') {
211*77c1e3ccSAndroid Build Coastguard Worker endptr++;
212*77c1e3ccSAndroid Build Coastguard Worker } else if (*endptr != '\0') {
213*77c1e3ccSAndroid Build Coastguard Worker SET_ERR_STRING("Option %s: Bad list separator '%c'\n", arg->name,
214*77c1e3ccSAndroid Build Coastguard Worker *endptr);
215*77c1e3ccSAndroid Build Coastguard Worker return 0;
216*77c1e3ccSAndroid Build Coastguard Worker }
217*77c1e3ccSAndroid Build Coastguard Worker list[i++] = (int)rawval;
218*77c1e3ccSAndroid Build Coastguard Worker ptr = endptr;
219*77c1e3ccSAndroid Build Coastguard Worker }
220*77c1e3ccSAndroid Build Coastguard Worker return i;
221*77c1e3ccSAndroid Build Coastguard Worker }
222