tizen 2.3.1 release
[external/alsa-lib.git] / src / control / ctlparse.c
1 /**
2  * \file control/control.c
3  * \brief CTL interface - parse ASCII identifiers and values
4  * \author Jaroslav Kysela <perex@perex.cz>
5  * \date 2010
6  */
7 /*
8  *  Control Interface - ASCII parser
9  *  Copyright (c) 2010 by Jaroslav Kysela <perex@perex.cz>
10  *
11  *
12  *   This library is free software; you can redistribute it and/or modify
13  *   it under the terms of the GNU Lesser General Public License as
14  *   published by the Free Software Foundation; either version 2.1 of
15  *   the License, or (at your option) any later version.
16  *
17  *   This program is distributed in the hope that it will be useful,
18  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
19  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  *   GNU Lesser General Public License for more details.
21  *
22  *   You should have received a copy of the GNU Lesser General Public
23  *   License along with this library; if not, write to the Free Software
24  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
25  *
26  */
27
28 #include <unistd.h>
29 #include <string.h>
30 #include <ctype.h>
31 #include <math.h>
32 #include "control_local.h"
33
34 /* Function to convert from percentage to volume. val = percentage */
35
36 #ifdef HAVE_SOFT_FLOAT
37 static inline long int convert_prange1(long val, long min, long max)
38 {
39         long temp = val * (max - min);
40         return temp / 100 + min + ((temp % 100) == 0 ? 0 : 1);
41 }
42 #else
43
44 #define convert_prange1(val, min, max) \
45         ceil((val) * ((max) - (min)) * 0.01 + (min))
46 #endif
47
48 #define check_range(val, min, max) \
49         ((val < min) ? (min) : ((val > max) ? (max) : (val)))
50
51 static long get_integer(const char **ptr, long min, long max)
52 {
53         long val = min;
54         char *p = (char *)*ptr, *s;
55
56         if (*p == ':')
57                 p++;
58         if (*p == '\0' || (!isdigit(*p) && *p != '-'))
59                 goto out;
60
61         s = p;
62         val = strtol(s, &p, 10);
63         if (*p == '.') {
64                 p++;
65                 strtol(p, &p, 10);
66         }
67         if (*p == '%') {
68                 val = (long)convert_prange1(strtod(s, NULL), min, max);
69                 p++;
70         }
71         val = check_range(val, min, max);
72         if (*p == ',')
73                 p++;
74  out:
75         *ptr = p;
76         return val;
77 }
78
79 static long long get_integer64(const char **ptr, long long min, long long max)
80 {
81         long long val = min;
82         char *p = (char *)*ptr, *s;
83
84         if (*p == ':')
85                 p++;
86         if (*p == '\0' || (!isdigit(*p) && *p != '-'))
87                 goto out;
88
89         s = p;
90         val = strtol(s, &p, 10);
91         if (*p == '.') {
92                 p++;
93                 strtol(p, &p, 10);
94         }
95         if (*p == '%') {
96                 val = (long long)convert_prange1(strtod(s, NULL), min, max);
97                 p++;
98         }
99         val = check_range(val, min, max);
100         if (*p == ',')
101                 p++;
102  out:
103         *ptr = p;
104         return val;
105 }
106
107 /**
108  * \brief return ASCII CTL element identifier name
109  * \param id CTL identifier
110  * \return ascii identifier of CTL element
111  *
112  * The string is allocated using strdup().
113  */
114 char *snd_ctl_ascii_elem_id_get(snd_ctl_elem_id_t *id)
115 {
116         unsigned int index, device, subdevice;
117         char buf[256], buf1[32];
118
119         snprintf(buf, sizeof(buf), "numid=%u,iface=%s,name='%s'",
120                                 snd_ctl_elem_id_get_numid(id),
121                                 snd_ctl_elem_iface_name(
122                                         snd_ctl_elem_id_get_interface(id)),
123                                 snd_ctl_elem_id_get_name(id));
124         buf[sizeof(buf)-1] = '\0';
125         index = snd_ctl_elem_id_get_index(id);
126         device = snd_ctl_elem_id_get_device(id);
127         subdevice = snd_ctl_elem_id_get_subdevice(id);
128         if (index) {
129                 snprintf(buf1, sizeof(buf1), ",index=%i", index);
130                 if (strlen(buf) + strlen(buf1) < sizeof(buf))
131                         strcat(buf, buf1);
132         }
133         if (device) {
134                 snprintf(buf1, sizeof(buf1), ",device=%i", device);
135                 if (strlen(buf) + strlen(buf1) < sizeof(buf))
136                         strcat(buf, buf1);
137         }
138         if (subdevice) {
139                 snprintf(buf1, sizeof(buf1), ",subdevice=%i", subdevice);
140                 if (strlen(buf) + strlen(buf1) < sizeof(buf))
141                         strcat(buf, buf1);
142         }
143         return strdup(buf);
144 }
145
146 /**
147  * \brief parse ASCII string as CTL element identifier
148  * \param dst destination CTL identifier
149  * \param str source ASCII string
150  * \return zero on success, otherwise a negative error code
151  */
152 int snd_ctl_ascii_elem_id_parse(snd_ctl_elem_id_t *dst, const char *str)
153 {
154         int c, size, numid;
155         char *ptr;
156
157         while (*str == ' ' || *str == '\t')
158                 str++;
159         if (!(*str))
160                 return -EINVAL;
161         snd_ctl_elem_id_set_interface(dst, SND_CTL_ELEM_IFACE_MIXER);   /* default */
162         while (*str) {
163                 if (!strncasecmp(str, "numid=", 6)) {
164                         str += 6;
165                         numid = atoi(str);
166                         if (numid <= 0) {
167                                 fprintf(stderr, "amixer: Invalid numid %d\n", numid);
168                                 return -EINVAL;
169                         }
170                         snd_ctl_elem_id_set_numid(dst, atoi(str));
171                         while (isdigit(*str))
172                                 str++;
173                 } else if (!strncasecmp(str, "iface=", 6)) {
174                         str += 6;
175                         if (!strncasecmp(str, "card", 4)) {
176                                 snd_ctl_elem_id_set_interface(dst, SND_CTL_ELEM_IFACE_CARD);
177                                 str += 4;
178                         } else if (!strncasecmp(str, "mixer", 5)) {
179                                 snd_ctl_elem_id_set_interface(dst, SND_CTL_ELEM_IFACE_MIXER);
180                                 str += 5;
181                         } else if (!strncasecmp(str, "pcm", 3)) {
182                                 snd_ctl_elem_id_set_interface(dst, SND_CTL_ELEM_IFACE_PCM);
183                                 str += 3;
184                         } else if (!strncasecmp(str, "rawmidi", 7)) {
185                                 snd_ctl_elem_id_set_interface(dst, SND_CTL_ELEM_IFACE_RAWMIDI);
186                                 str += 7;
187                         } else if (!strncasecmp(str, "timer", 5)) {
188                                 snd_ctl_elem_id_set_interface(dst, SND_CTL_ELEM_IFACE_TIMER);
189                                 str += 5;
190                         } else if (!strncasecmp(str, "sequencer", 9)) {
191                                 snd_ctl_elem_id_set_interface(dst, SND_CTL_ELEM_IFACE_SEQUENCER);
192                                 str += 9;
193                         } else {
194                                 return -EINVAL;
195                         }
196                 } else if (!strncasecmp(str, "name=", 5)) {
197                         char buf[64];
198                         str += 5;
199                         ptr = buf;
200                         size = 0;
201                         if (*str == '\'' || *str == '\"') {
202                                 c = *str++;
203                                 while (*str && *str != c) {
204                                         if (size < (int)sizeof(buf)) {
205                                                 *ptr++ = *str;
206                                                 size++;
207                                         }
208                                         str++;
209                                 }
210                                 if (*str == c)
211                                         str++;
212                         } else {
213                                 while (*str && *str != ',') {
214                                         if (size < (int)sizeof(buf)) {
215                                                 *ptr++ = *str;
216                                                 size++;
217                                         }
218                                         str++;
219                                 }
220                         }
221                         *ptr = '\0';
222                         snd_ctl_elem_id_set_name(dst, buf);
223                 } else if (!strncasecmp(str, "index=", 6)) {
224                         str += 6;
225                         snd_ctl_elem_id_set_index(dst, atoi(str));
226                         while (isdigit(*str))
227                                 str++;
228                 } else if (!strncasecmp(str, "device=", 7)) {
229                         str += 7;
230                         snd_ctl_elem_id_set_device(dst, atoi(str));
231                         while (isdigit(*str))
232                                 str++;
233                 } else if (!strncasecmp(str, "subdevice=", 10)) {
234                         str += 10;
235                         snd_ctl_elem_id_set_subdevice(dst, atoi(str));
236                         while (isdigit(*str))
237                                 str++;
238                 }
239                 if (*str == ',') {
240                         str++;
241                 } else {
242                         if (*str)
243                                 return -EINVAL;
244                 }
245         }                       
246         return 0;
247 }
248
249 static int get_ctl_enum_item_index(snd_ctl_t *handle,
250                                    snd_ctl_elem_info_t *info,
251                                    const char **ptrp)
252
253         char *ptr = (char *)*ptrp;
254         int items, i, len;
255         const char *name;
256   
257         items = snd_ctl_elem_info_get_items(info);
258         if (items <= 0)
259                 return -1;
260
261         for (i = 0; i < items; i++) {
262                 snd_ctl_elem_info_set_item(info, i);
263                 if (snd_ctl_elem_info(handle, info) < 0)
264                         return -1;
265                 name = snd_ctl_elem_info_get_item_name(info);
266                 len = strlen(name);
267                 if (! strncmp(name, ptr, len)) {
268                         if (! ptr[len] || ptr[len] == ',' || ptr[len] == '\n') {
269                                 ptr += len;
270                                 *ptrp = ptr;
271                                 return i;
272                         }
273                 }
274         }
275         return -1;
276 }
277
278 /**
279  * \brief parse ASCII string as CTL element value
280  * \param dst destination CTL element value
281  * \param info CTL element info structure
282  * \param value source ASCII string
283  * \return zero on success, otherwise a negative error code
284  */
285 int snd_ctl_ascii_value_parse(snd_ctl_t *handle,
286                               snd_ctl_elem_value_t *dst,
287                               snd_ctl_elem_info_t *info,
288                               const char *value)
289 {
290         const char *ptr = value;
291         snd_ctl_elem_id_t *myid;
292         snd_ctl_elem_type_t type;
293         unsigned int idx, count;
294         long tmp;
295         long long tmp64;
296
297         snd_ctl_elem_id_alloca(&myid);
298         snd_ctl_elem_info_get_id(info, myid);
299         type = snd_ctl_elem_info_get_type(info);
300         count = snd_ctl_elem_info_get_count(info);
301         snd_ctl_elem_value_set_id(dst, myid);
302         
303         for (idx = 0; idx < count && idx < 128 && ptr && *ptr; idx++) {
304                 switch (type) {
305                 case SND_CTL_ELEM_TYPE_BOOLEAN:
306                         tmp = 0;
307                         if (!strncasecmp(ptr, "on", 2) ||
308                             !strncasecmp(ptr, "up", 2)) {
309                                 tmp = 1;
310                                 ptr += 2;
311                         } else if (!strncasecmp(ptr, "yes", 3)) {
312                                 tmp = 1;
313                                 ptr += 3;
314                         } else if (!strncasecmp(ptr, "toggle", 6)) {
315                                 tmp = snd_ctl_elem_value_get_boolean(dst, idx);
316                                 tmp = tmp > 0 ? 0 : 1;
317                                 ptr += 6;
318                         } else if (isdigit(*ptr)) {
319                                 tmp = atoi(ptr) > 0 ? 1 : 0;
320                                 while (isdigit(*ptr))
321                                         ptr++;
322                         } else {
323                                 while (*ptr && *ptr != ',')
324                                         ptr++;
325                         }
326                         snd_ctl_elem_value_set_boolean(dst, idx, tmp);
327                         break;
328                 case SND_CTL_ELEM_TYPE_INTEGER:
329                         tmp = get_integer(&ptr,
330                                           snd_ctl_elem_info_get_min(info),
331                                           snd_ctl_elem_info_get_max(info));
332                         snd_ctl_elem_value_set_integer(dst, idx, tmp);
333                         break;
334                 case SND_CTL_ELEM_TYPE_INTEGER64:
335                         tmp64 = get_integer64(&ptr,
336                                           snd_ctl_elem_info_get_min64(info),
337                                           snd_ctl_elem_info_get_max64(info));
338                         snd_ctl_elem_value_set_integer64(dst, idx, tmp64);
339                         break;
340                 case SND_CTL_ELEM_TYPE_ENUMERATED:
341                         tmp = get_ctl_enum_item_index(handle, info, &ptr);
342                         if (tmp < 0)
343                                 tmp = get_integer(&ptr, 0,
344                                         snd_ctl_elem_info_get_items(info) - 1);
345                         snd_ctl_elem_value_set_enumerated(dst, idx, tmp);
346                         break;
347                 case SND_CTL_ELEM_TYPE_BYTES:
348                         tmp = get_integer(&ptr, 0, 255);
349                         snd_ctl_elem_value_set_byte(dst, idx, tmp);
350                         break;
351                 default:
352                         break;
353                 }
354                 if (!strchr(value, ','))
355                         ptr = value;
356                 else if (*ptr == ',')
357                         ptr++;
358         }
359         return 0;
360 }