1 /**
2 * \file confeval.c
3 * \ingroup Configuration
4 * \brief Configuration helper functions
5 * \author Jaroslav Kysela <perex@perex.cz>
6 * \date 2021
7 *
8 * Configuration string evaluation.
9 *
10 * See the \ref confarg_math page for more details.
11 */
12 /*
13 * Configuration string evaluation
14 * Copyright (c) 2000 by Abramo Bagnara <abramo@alsa-project.org>,
15 * Jaroslav Kysela <perex@perex.cz>
16 *
17 *
18 * This library is free software; you can redistribute it and/or modify
19 * it under the terms of the GNU Lesser General Public License as
20 * published by the Free Software Foundation; either version 2.1 of
21 * the License, or (at your option) any later version.
22 *
23 * This program is distributed in the hope that it will be useful,
24 * but WITHOUT ANY WARRANTY; without even the implied warranty of
25 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
26 * GNU Lesser General Public License for more details.
27 *
28 * You should have received a copy of the GNU Lesser General Public
29 * License along with this library; if not, write to the Free Software
30 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
31 *
32 */
33
34 #include "local.h"
35 #include <stdlib.h>
36 #include <stdio.h>
37 #include <string.h>
38 #include <ctype.h>
39 #include <limits.h>
40
41 #ifndef DOC_HIDDEN
42 typedef long long value_type_t;
43 #endif /* DOC_HIDDEN */
44
_find_end_of_expression(const char *s, char begin, char end)45 static const char *_find_end_of_expression(const char *s, char begin, char end)
46 {
47 int count = 1;
48 while (*s) {
49 if (*s == begin) {
50 count++;
51 } else if (*s == end) {
52 count--;
53 if (count == 0)
54 return s + 1;
55 }
56 s++;
57 }
58 return NULL;
59 }
60
_parse_integer(value_type_t *val, const char **s)61 static int _parse_integer(value_type_t *val, const char **s)
62 {
63 long long v;
64 char *end;
65
66 errno = 0;
67 v = strtoll(*s, &end, 0);
68 if (errno)
69 return -errno;
70 *val = v;
71 if (((long long)*val) != v)
72 return -ERANGE;
73 *s = end;
74 return 0;
75 }
76
_to_integer(value_type_t *val, snd_config_t *c)77 static int _to_integer(value_type_t *val, snd_config_t *c)
78 {
79 int err;
80
81 switch(snd_config_get_type(c)) {
82 case SND_CONFIG_TYPE_INTEGER:
83 {
84 long v;
85 err = snd_config_get_integer(c, &v);
86 if (err >= 0)
87 *val = v;
88 }
89 break;
90 case SND_CONFIG_TYPE_INTEGER64:
91 {
92 long long v;
93 err = snd_config_get_integer64(c, &v);
94 if (err >= 0) {
95 *val = v;
96 if (((long long)*val) != v)
97 return -ERANGE;
98 return 0;
99 }
100 }
101 break;
102 case SND_CONFIG_TYPE_STRING:
103 {
104 const char *s;
105 long long v;
106 err = snd_config_get_string(c, &s);
107 if (err >= 0) {
108 err = safe_strtoll(s, &v);
109 if (err >= 0) {
110 *val = v;
111 if (((long long)*val) != v)
112 return -ERANGE;
113 return 0;
114 }
115 }
116 }
117 break;
118 default:
119 return -EINVAL;
120 }
121 return err;
122 }
123
124 #ifndef DOC_HIDDEN
_snd_eval_string(snd_config_t **dst, const char *s, snd_config_expand_fcn_t fcn, void *private_data)125 int _snd_eval_string(snd_config_t **dst, const char *s,
126 snd_config_expand_fcn_t fcn, void *private_data)
127 {
128 snd_config_t *tmp;
129 const char *save, *e;
130 char *m;
131 value_type_t left, right;
132 int err, c, op, off;
133 enum {
134 LEFT,
135 OP,
136 RIGHT,
137 END
138 } pos;
139
140 while (*s && *s <= ' ') s++;
141 save = s;
142 pos = LEFT;
143 op = 0;
144 while (*s) {
145 while (*s && *s <= ' ') s++;
146 c = *s;
147 if (c == '\0')
148 break;
149 if (pos == END) {
150 SNDERR("unexpected expression tail '%s'", s);
151 return -EINVAL;
152 }
153 if (pos == OP) {
154 switch (c) {
155 case '+':
156 case '-':
157 case '*':
158 case '/':
159 case '%':
160 case '|':
161 case '&': op = c; break;
162 default:
163 SNDERR("unknown operation '%c'", c);
164 return -EINVAL;
165 }
166 pos = RIGHT;
167 s++;
168 continue;
169 }
170 if (c == '(') {
171 e = _find_end_of_expression(s + 1, '(', ')');
172 off = 1;
173 goto _expr;
174 } else if (c == '$') {
175 if (s[1] == '[') {
176 e = _find_end_of_expression(s + 2, '[', ']');
177 off = 2;
178 _expr:
179 if (e == NULL)
180 return -EINVAL;
181 m = malloc(e - s - (off - 1));
182 if (m == NULL)
183 return -ENOMEM;
184 memcpy(m, s + off, e - s - off);
185 m[e - s - (off + 1)] = '\0';
186 err = _snd_eval_string(&tmp, m, fcn, private_data);
187 free(m);
188 if (err < 0)
189 return err;
190 s = e;
191 if (*s)
192 s++;
193 } else {
194 e = s + 1;
195 while (*e) {
196 if (!isalnum(*e) && *e != '_')
197 break;
198 e++;
199 }
200 m = malloc(e - s);
201 if (m == NULL)
202 return -ENOMEM;
203 memcpy(m, s + 1, e - s - 1);
204 m[e - s - 1] = '\0';
205 err = fcn(&tmp, m, private_data);
206 free(m);
207 if (err < 0)
208 return err;
209 if (tmp == NULL) {
210 err = snd_config_imake_integer(&tmp, NULL, 0);
211 if (err < 0)
212 return err;
213 }
214 s = e;
215 }
216 err = _to_integer(op == LEFT ? &left : &right, tmp);
217 snd_config_delete(tmp);
218 } else if (c == '-' || (c >= '0' && c <= '9')) {
219 err = _parse_integer(op == LEFT ? &left : &right, &s);
220 } else {
221 return -EINVAL;
222 }
223 if (err < 0)
224 return err;
225 pos = op == LEFT ? OP : END;
226 }
227 if (pos != OP && pos != END) {
228 SNDERR("incomplete expression '%s'", save);
229 return -EINVAL;
230 }
231
232 if (pos == END) {
233 switch (op) {
234 case '+': left = left + right; break;
235 case '-': left = left - right; break;
236 case '*': left = left * right; break;
237 case '/': left = left / right; break;
238 case '%': left = left % right; break;
239 case '|': left = left | right; break;
240 case '&': left = left & right; break;
241 default: return -EINVAL;
242 }
243 }
244
245 if (left > INT_MAX || left < INT_MIN)
246 return snd_config_imake_integer64(dst, NULL, left);
247 else
248 return snd_config_imake_integer(dst, NULL, left);
249 }
250 #endif /* DOC_HIDDEN */
251
252 /**
253 * \brief Evaluate an math expression in the string
254 * \param[out] dst The function puts the handle to the new configuration
255 * node at the address specified by \a dst.
256 * \param[in] s A string to evaluate
257 * \param[in] fcn A function to get the variable contents
258 * \param[in] private_data A private value for the variable contents function
259 * \return 0 if successful, otherwise a negative error code.
260 */
snd_config_evaluate_string(snd_config_t **dst, const char *s, snd_config_expand_fcn_t fcn, void *private_data)261 int snd_config_evaluate_string(snd_config_t **dst, const char *s,
262 snd_config_expand_fcn_t fcn, void *private_data)
263 {
264 assert(dst && s);
265 int err;
266
267 if (*s != '$')
268 return -EINVAL;
269 if (s[1] == '[') {
270 err = _snd_eval_string(dst, s, fcn, private_data);
271 if (err < 0)
272 SNDERR("wrong expression '%s'", s);
273 } else {
274 err = fcn(dst, s + 1, private_data);
275 }
276 return err;
277 }
278