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 <stdlib.h>
35 #include <stdio.h>
36 #include <string.h>
37 #include <ctype.h>
38 #include <limits.h>
39 #include "local.h"
40
41 typedef long long value_type_t;
42
_find_end_of_expression(const char * s,char begin,char end)43 static const char *_find_end_of_expression(const char *s, char begin, char end)
44 {
45 int count = 1;
46 while (*s) {
47 if (*s == begin) {
48 count++;
49 } else if (*s == end) {
50 count--;
51 if (count == 0)
52 return s + 1;
53 }
54 s++;
55 }
56 return NULL;
57 }
58
_parse_integer(value_type_t * val,const char ** s)59 static int _parse_integer(value_type_t *val, const char **s)
60 {
61 long long v;
62 char *end;
63
64 errno = 0;
65 v = strtoll(*s, &end, 0);
66 if (errno)
67 return -errno;
68 *val = v;
69 if (((long long)*val) != v)
70 return -ERANGE;
71 *s = end;
72 return 0;
73 }
74
_to_integer(value_type_t * val,snd_config_t * c)75 static int _to_integer(value_type_t *val, snd_config_t *c)
76 {
77 int err;
78
79 switch(snd_config_get_type(c)) {
80 case SND_CONFIG_TYPE_INTEGER:
81 {
82 long v;
83 err = snd_config_get_integer(c, &v);
84 if (err >= 0)
85 *val = v;
86 }
87 break;
88 case SND_CONFIG_TYPE_INTEGER64:
89 {
90 long long v;
91 err = snd_config_get_integer64(c, &v);
92 if (err >= 0) {
93 *val = v;
94 if (((long long)*val) != v)
95 return -ERANGE;
96 return 0;
97 }
98 }
99 break;
100 case SND_CONFIG_TYPE_STRING:
101 {
102 const char *s;
103 long long v;
104 err = snd_config_get_string(c, &s);
105 if (err >= 0) {
106 err = safe_strtoll(s, &v);
107 if (err >= 0) {
108 *val = v;
109 if (((long long)*val) != v)
110 return -ERANGE;
111 return 0;
112 }
113 }
114 }
115 break;
116 default:
117 return -EINVAL;
118 }
119 return err;
120 }
121
_snd_eval_string(snd_config_t ** dst,const char * s,snd_config_expand_fcn_t fcn,void * private_data)122 int _snd_eval_string(snd_config_t **dst, const char *s,
123 snd_config_expand_fcn_t fcn, void *private_data)
124 {
125 snd_config_t *tmp;
126 const char *save, *e;
127 char *m;
128 value_type_t left, right;
129 int err, c, op, off;
130 enum {
131 LEFT,
132 OP,
133 RIGHT,
134 END
135 } pos;
136
137 while (*s && *s <= ' ') s++;
138 save = s;
139 pos = LEFT;
140 op = 0;
141 while (*s) {
142 while (*s && *s <= ' ') s++;
143 c = *s;
144 if (c == '\0')
145 break;
146 if (pos == END) {
147 SNDERR("unexpected expression tail '%s'", s);
148 return -EINVAL;
149 }
150 if (pos == OP) {
151 switch (c) {
152 case '+':
153 case '-':
154 case '*':
155 case '/':
156 case '%':
157 case '|':
158 case '&': op = c; break;
159 default:
160 SNDERR("unknown operation '%c'", c);
161 return -EINVAL;
162 }
163 pos = RIGHT;
164 s++;
165 continue;
166 }
167 if (c == '(') {
168 e = _find_end_of_expression(s + 1, '(', ')');
169 off = 1;
170 goto _expr;
171 } else if (c == '$') {
172 if (s[1] == '[') {
173 e = _find_end_of_expression(s + 2, '[', ']');
174 off = 2;
175 _expr:
176 if (e == NULL)
177 return -EINVAL;
178 m = malloc(e - s - (off - 1));
179 if (m == NULL)
180 return -ENOMEM;
181 memcpy(m, s + off, e - s - off);
182 m[e - s - (off + 1)] = '\0';
183 err = _snd_eval_string(&tmp, m, fcn, private_data);
184 free(m);
185 if (err < 0)
186 return err;
187 s = e;
188 if (*s)
189 s++;
190 } else {
191 e = s + 1;
192 while (*e) {
193 if (!isalnum(*e))
194 break;
195 e++;
196 }
197 m = malloc(e - s);
198 if (m == NULL)
199 return -ENOMEM;
200 memcpy(m, s + 1, e - s - 1);
201 m[e - s - 1] = '\0';
202 err = fcn(&tmp, m, private_data);
203 free(m);
204 if (err < 0)
205 return err;
206 s = e;
207 }
208 err = _to_integer(op == LEFT ? &left : &right, tmp);
209 snd_config_delete(tmp);
210 } else if (c == '-' || (c >= '0' && c <= '9')) {
211 err = _parse_integer(op == LEFT ? &left : &right, &s);
212 } else {
213 return -EINVAL;
214 }
215 if (err < 0)
216 return err;
217 pos = op == LEFT ? OP : END;
218 }
219 if (pos != OP && pos != END) {
220 SNDERR("incomplete expression '%s'", save);
221 return -EINVAL;
222 }
223
224 if (pos == END) {
225 switch (op) {
226 case '+': left = left + right; break;
227 case '-': left = left - right; break;
228 case '*': left = left * right; break;
229 case '/': left = left / right; break;
230 case '%': left = left % right; break;
231 case '|': left = left | right; break;
232 case '&': left = left & right; break;
233 default: return -EINVAL;
234 }
235 }
236
237 if (left > INT_MAX || left < INT_MIN)
238 return snd_config_imake_integer64(dst, NULL, left);
239 else
240 return snd_config_imake_integer(dst, NULL, left);
241 }
242
243 /**
244 * \brief Evaluate an math expression in the string
245 * \param[out] dst The function puts the handle to the new configuration
246 * node at the address specified by \a dst.
247 * \param[in] s A string to evaluate
248 * \param[in] fcn A function to get the variable contents
249 * \param[in] private_value A private value for the variable contents function
250 * \return 0 if successful, otherwise a negative error code.
251 */
snd_config_evaluate_string(snd_config_t ** dst,const char * s,snd_config_expand_fcn_t fcn,void * private_data)252 int snd_config_evaluate_string(snd_config_t **dst, const char *s,
253 snd_config_expand_fcn_t fcn, void *private_data)
254 {
255 assert(dst && s);
256 int err;
257
258 if (*s != '$')
259 return -EINVAL;
260 if (s[1] == '[') {
261 err = _snd_eval_string(dst, s, fcn, private_data);
262 if (err < 0)
263 SNDERR("wrong expression '%s'", s);
264 } else {
265 err = fcn(dst, s + 1, private_data);
266 }
267 return err;
268 }
269