• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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