1c72fcc34Sopenharmony_ci/*
2c72fcc34Sopenharmony_ci *  Advanced Linux Sound Architecture Control Program - Parse initialization files
3c72fcc34Sopenharmony_ci *  Copyright (c) by Jaroslav Kysela <perex@perex.cz>,
4c72fcc34Sopenharmony_ci *		     Greg Kroah-Hartman <greg@kroah.com>,
5c72fcc34Sopenharmony_ci *		     Kay Sievers <kay.sievers@vrfy.org>
6c72fcc34Sopenharmony_ci *
7c72fcc34Sopenharmony_ci *
8c72fcc34Sopenharmony_ci *   This program is free software; you can redistribute it and/or modify
9c72fcc34Sopenharmony_ci *   it under the terms of the GNU General Public License as published by
10c72fcc34Sopenharmony_ci *   the Free Software Foundation; either version 2 of the License, or
11c72fcc34Sopenharmony_ci *   (at your option) any later version.
12c72fcc34Sopenharmony_ci *
13c72fcc34Sopenharmony_ci *   This program is distributed in the hope that it will be useful,
14c72fcc34Sopenharmony_ci *   but WITHOUT ANY WARRANTY; without even the implied warranty of
15c72fcc34Sopenharmony_ci *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16c72fcc34Sopenharmony_ci *   GNU General Public License for more details.
17c72fcc34Sopenharmony_ci *
18c72fcc34Sopenharmony_ci *   You should have received a copy of the GNU General Public License
19c72fcc34Sopenharmony_ci *   along with this program; if not, write to the Free Software
20c72fcc34Sopenharmony_ci *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
21c72fcc34Sopenharmony_ci *
22c72fcc34Sopenharmony_ci */
23c72fcc34Sopenharmony_ci
24c72fcc34Sopenharmony_ci#include <stdlib.h>
25c72fcc34Sopenharmony_ci#include <stdio.h>
26c72fcc34Sopenharmony_ci#include <stddef.h>
27c72fcc34Sopenharmony_ci#include <unistd.h>
28c72fcc34Sopenharmony_ci#include <string.h>
29c72fcc34Sopenharmony_ci#include <fcntl.h>
30c72fcc34Sopenharmony_ci#include <ctype.h>
31c72fcc34Sopenharmony_ci#include <errno.h>
32c72fcc34Sopenharmony_ci#include <fnmatch.h>
33c72fcc34Sopenharmony_ci#include <sys/stat.h>
34c72fcc34Sopenharmony_ci#include <sys/un.h>
35c72fcc34Sopenharmony_ci#include <sys/wait.h>
36c72fcc34Sopenharmony_ci#include <sys/select.h>
37c72fcc34Sopenharmony_ci#include <sys/types.h>
38c72fcc34Sopenharmony_ci#include <dirent.h>
39c72fcc34Sopenharmony_ci#include <math.h>
40c72fcc34Sopenharmony_ci#include "aconfig.h"
41c72fcc34Sopenharmony_ci#include "alsactl.h"
42c72fcc34Sopenharmony_ci#include "list.h"
43c72fcc34Sopenharmony_ci
44c72fcc34Sopenharmony_ci#define PATH_SIZE	512
45c72fcc34Sopenharmony_ci#define NAME_SIZE	128
46c72fcc34Sopenharmony_ci#define EJUSTRETURN	0x7fffffff
47c72fcc34Sopenharmony_ci
48c72fcc34Sopenharmony_cienum key_op {
49c72fcc34Sopenharmony_ci	KEY_OP_UNSET,
50c72fcc34Sopenharmony_ci	KEY_OP_MATCH,
51c72fcc34Sopenharmony_ci	KEY_OP_NOMATCH,
52c72fcc34Sopenharmony_ci	KEY_OP_ADD,
53c72fcc34Sopenharmony_ci	KEY_OP_ASSIGN,
54c72fcc34Sopenharmony_ci	KEY_OP_ASSIGN_FINAL
55c72fcc34Sopenharmony_ci};
56c72fcc34Sopenharmony_ci
57c72fcc34Sopenharmony_cistruct pair {
58c72fcc34Sopenharmony_ci	char *key;
59c72fcc34Sopenharmony_ci	char *value;
60c72fcc34Sopenharmony_ci	struct pair *next;
61c72fcc34Sopenharmony_ci};
62c72fcc34Sopenharmony_ci
63c72fcc34Sopenharmony_cistruct space {
64c72fcc34Sopenharmony_ci	struct pair *pairs;
65c72fcc34Sopenharmony_ci	char *rootdir;
66c72fcc34Sopenharmony_ci	char *go_to;
67c72fcc34Sopenharmony_ci	char *program_result;
68c72fcc34Sopenharmony_ci	const char *filename;
69c72fcc34Sopenharmony_ci	int linenum;
70c72fcc34Sopenharmony_ci	int log_run;
71c72fcc34Sopenharmony_ci	int exit_code;
72c72fcc34Sopenharmony_ci	int quit;
73c72fcc34Sopenharmony_ci	unsigned int ctl_id_changed;
74c72fcc34Sopenharmony_ci	snd_hctl_t *ctl_handle;
75c72fcc34Sopenharmony_ci	snd_ctl_card_info_t *ctl_card_info;
76c72fcc34Sopenharmony_ci	snd_ctl_elem_id_t *ctl_id;
77c72fcc34Sopenharmony_ci	snd_ctl_elem_info_t *ctl_info;
78c72fcc34Sopenharmony_ci	snd_ctl_elem_value_t *ctl_value;
79c72fcc34Sopenharmony_ci};
80c72fcc34Sopenharmony_ci
81c72fcc34Sopenharmony_cistatic void Perror(struct space *space, const char *fmt, ...)
82c72fcc34Sopenharmony_ci{
83c72fcc34Sopenharmony_ci	va_list arg;
84c72fcc34Sopenharmony_ci	va_start(arg, fmt);
85c72fcc34Sopenharmony_ci	fprintf(stderr, "%s:%i: ", space->filename, space->linenum);
86c72fcc34Sopenharmony_ci	vfprintf(stderr, fmt, arg);
87c72fcc34Sopenharmony_ci	putc('\n', stderr);
88c72fcc34Sopenharmony_ci	va_end(arg);
89c72fcc34Sopenharmony_ci}
90c72fcc34Sopenharmony_ci
91c72fcc34Sopenharmony_ci#include "init_sysdeps.c"
92c72fcc34Sopenharmony_ci#include "init_utils_string.c"
93c72fcc34Sopenharmony_ci#include "init_utils_run.c"
94c72fcc34Sopenharmony_ci#include "init_sysfs.c"
95c72fcc34Sopenharmony_ci
96c72fcc34Sopenharmony_cistatic void free_space(struct space *space)
97c72fcc34Sopenharmony_ci{
98c72fcc34Sopenharmony_ci	struct pair *pair = space->pairs;
99c72fcc34Sopenharmony_ci	struct pair *next = pair;
100c72fcc34Sopenharmony_ci
101c72fcc34Sopenharmony_ci	while (next) {
102c72fcc34Sopenharmony_ci		pair = next;
103c72fcc34Sopenharmony_ci		next = pair->next;
104c72fcc34Sopenharmony_ci		free(pair->value);
105c72fcc34Sopenharmony_ci		free(pair->key);
106c72fcc34Sopenharmony_ci		free(pair);
107c72fcc34Sopenharmony_ci	}
108c72fcc34Sopenharmony_ci	space->pairs = NULL;
109c72fcc34Sopenharmony_ci	if (space->ctl_value) {
110c72fcc34Sopenharmony_ci		snd_ctl_elem_value_free(space->ctl_value);
111c72fcc34Sopenharmony_ci		space->ctl_value = NULL;
112c72fcc34Sopenharmony_ci	}
113c72fcc34Sopenharmony_ci	if (space->ctl_info) {
114c72fcc34Sopenharmony_ci		snd_ctl_elem_info_free(space->ctl_info);
115c72fcc34Sopenharmony_ci		space->ctl_info = NULL;
116c72fcc34Sopenharmony_ci	}
117c72fcc34Sopenharmony_ci	if (space->ctl_id) {
118c72fcc34Sopenharmony_ci		snd_ctl_elem_id_free(space->ctl_id);
119c72fcc34Sopenharmony_ci		space->ctl_id = NULL;
120c72fcc34Sopenharmony_ci	}
121c72fcc34Sopenharmony_ci	if (space->ctl_card_info) {
122c72fcc34Sopenharmony_ci		snd_ctl_card_info_free(space->ctl_card_info);
123c72fcc34Sopenharmony_ci		space->ctl_card_info = NULL;
124c72fcc34Sopenharmony_ci	}
125c72fcc34Sopenharmony_ci	if (space->ctl_handle) {
126c72fcc34Sopenharmony_ci		snd_hctl_close(space->ctl_handle);
127c72fcc34Sopenharmony_ci		space->ctl_handle = NULL;
128c72fcc34Sopenharmony_ci	}
129c72fcc34Sopenharmony_ci	if (space->rootdir)
130c72fcc34Sopenharmony_ci		free(space->rootdir);
131c72fcc34Sopenharmony_ci	if (space->program_result)
132c72fcc34Sopenharmony_ci		free(space->program_result);
133c72fcc34Sopenharmony_ci	if (space->go_to)
134c72fcc34Sopenharmony_ci		free(space->go_to);
135c72fcc34Sopenharmony_ci	free(space);
136c72fcc34Sopenharmony_ci}
137c72fcc34Sopenharmony_ci
138c72fcc34Sopenharmony_cistatic struct pair *value_find(struct space *space, const char *key)
139c72fcc34Sopenharmony_ci{
140c72fcc34Sopenharmony_ci	struct pair *pair = space->pairs;
141c72fcc34Sopenharmony_ci
142c72fcc34Sopenharmony_ci	while (pair && strcmp(pair->key, key) != 0)
143c72fcc34Sopenharmony_ci		pair = pair->next;
144c72fcc34Sopenharmony_ci	return pair;
145c72fcc34Sopenharmony_ci}
146c72fcc34Sopenharmony_ci
147c72fcc34Sopenharmony_cistatic int value_set(struct space *space, const char *key, const char *value)
148c72fcc34Sopenharmony_ci{
149c72fcc34Sopenharmony_ci	struct pair *pair;
150c72fcc34Sopenharmony_ci
151c72fcc34Sopenharmony_ci	pair = value_find(space, key);
152c72fcc34Sopenharmony_ci	if (pair) {
153c72fcc34Sopenharmony_ci		free(pair->value);
154c72fcc34Sopenharmony_ci		pair->value = strdup(value);
155c72fcc34Sopenharmony_ci		if (pair->value == NULL)
156c72fcc34Sopenharmony_ci			return -ENOMEM;
157c72fcc34Sopenharmony_ci	} else {
158c72fcc34Sopenharmony_ci		pair = malloc(sizeof(struct pair));
159c72fcc34Sopenharmony_ci		if (pair == NULL)
160c72fcc34Sopenharmony_ci			return -ENOMEM;
161c72fcc34Sopenharmony_ci		pair->key = strdup(key);
162c72fcc34Sopenharmony_ci		if (pair->key == NULL) {
163c72fcc34Sopenharmony_ci			free(pair);
164c72fcc34Sopenharmony_ci			return -ENOMEM;
165c72fcc34Sopenharmony_ci		}
166c72fcc34Sopenharmony_ci		pair->value = strdup(value);
167c72fcc34Sopenharmony_ci		if (pair->value == NULL) {
168c72fcc34Sopenharmony_ci			free(pair->key);
169c72fcc34Sopenharmony_ci			free(pair);
170c72fcc34Sopenharmony_ci			return -ENOMEM;
171c72fcc34Sopenharmony_ci		}
172c72fcc34Sopenharmony_ci		pair->next = space->pairs;
173c72fcc34Sopenharmony_ci		space->pairs = pair;
174c72fcc34Sopenharmony_ci	}
175c72fcc34Sopenharmony_ci	return 0;
176c72fcc34Sopenharmony_ci}
177c72fcc34Sopenharmony_ci
178c72fcc34Sopenharmony_cistatic int init_space(struct space **space, int card)
179c72fcc34Sopenharmony_ci{
180c72fcc34Sopenharmony_ci	struct space *res;
181c72fcc34Sopenharmony_ci	char device[16];
182c72fcc34Sopenharmony_ci	int err;
183c72fcc34Sopenharmony_ci
184c72fcc34Sopenharmony_ci	res = calloc(1, sizeof(struct space));
185c72fcc34Sopenharmony_ci	if (res == NULL)
186c72fcc34Sopenharmony_ci		return -ENOMEM;
187c72fcc34Sopenharmony_ci	res->ctl_id_changed = ~0;
188c72fcc34Sopenharmony_ci	res->linenum = -1;
189c72fcc34Sopenharmony_ci	sprintf(device, "hw:%d", card);
190c72fcc34Sopenharmony_ci	err = snd_hctl_open(&res->ctl_handle, device, 0);
191c72fcc34Sopenharmony_ci	if (err < 0)
192c72fcc34Sopenharmony_ci		goto error;
193c72fcc34Sopenharmony_ci	err = snd_hctl_load(res->ctl_handle);
194c72fcc34Sopenharmony_ci	if (err < 0)
195c72fcc34Sopenharmony_ci		goto error;
196c72fcc34Sopenharmony_ci	err = snd_ctl_card_info_malloc(&res->ctl_card_info);
197c72fcc34Sopenharmony_ci	if (err < 0)
198c72fcc34Sopenharmony_ci		goto error;
199c72fcc34Sopenharmony_ci	err = snd_ctl_card_info(snd_hctl_ctl(res->ctl_handle), res->ctl_card_info);
200c72fcc34Sopenharmony_ci	if (err < 0)
201c72fcc34Sopenharmony_ci		goto error;
202c72fcc34Sopenharmony_ci	err = snd_ctl_elem_id_malloc(&res->ctl_id);
203c72fcc34Sopenharmony_ci	if (err < 0)
204c72fcc34Sopenharmony_ci		goto error;
205c72fcc34Sopenharmony_ci	err = snd_ctl_elem_info_malloc(&res->ctl_info);
206c72fcc34Sopenharmony_ci	if (err < 0)
207c72fcc34Sopenharmony_ci		goto error;
208c72fcc34Sopenharmony_ci	err = snd_ctl_elem_value_malloc(&res->ctl_value);
209c72fcc34Sopenharmony_ci	if (err < 0)
210c72fcc34Sopenharmony_ci		goto error;
211c72fcc34Sopenharmony_ci	*space = res;
212c72fcc34Sopenharmony_ci	return 0;
213c72fcc34Sopenharmony_ci error:
214c72fcc34Sopenharmony_ci 	free_space(res);
215c72fcc34Sopenharmony_ci 	return err;
216c72fcc34Sopenharmony_ci}
217c72fcc34Sopenharmony_ci
218c72fcc34Sopenharmony_cistatic const char *cardinfo_get(struct space *space, const char *attr)
219c72fcc34Sopenharmony_ci{
220c72fcc34Sopenharmony_ci	if (strncasecmp(attr, "CARD", 4) == 0) {
221c72fcc34Sopenharmony_ci		static char res[16];
222c72fcc34Sopenharmony_ci		sprintf(res, "%u", snd_ctl_card_info_get_card(space->ctl_card_info));
223c72fcc34Sopenharmony_ci		return res;
224c72fcc34Sopenharmony_ci	}
225c72fcc34Sopenharmony_ci	if (strncasecmp(attr, "ID", 2) == 0)
226c72fcc34Sopenharmony_ci		return snd_ctl_card_info_get_id(space->ctl_card_info);
227c72fcc34Sopenharmony_ci	if (strncasecmp(attr, "DRIVER", 6) == 0)
228c72fcc34Sopenharmony_ci		return snd_ctl_card_info_get_driver(space->ctl_card_info);
229c72fcc34Sopenharmony_ci	if (strncasecmp(attr, "NAME", 4) == 0)
230c72fcc34Sopenharmony_ci		return snd_ctl_card_info_get_name(space->ctl_card_info);
231c72fcc34Sopenharmony_ci	if (strncasecmp(attr, "LONGNAME", 8) == 0)
232c72fcc34Sopenharmony_ci		return snd_ctl_card_info_get_longname(space->ctl_card_info);
233c72fcc34Sopenharmony_ci	if (strncasecmp(attr, "MIXERNAME", 9) == 0)
234c72fcc34Sopenharmony_ci		return snd_ctl_card_info_get_mixername(space->ctl_card_info);
235c72fcc34Sopenharmony_ci	if (strncasecmp(attr, "COMPONENTS", 10) == 0)
236c72fcc34Sopenharmony_ci		return snd_ctl_card_info_get_components(space->ctl_card_info);
237c72fcc34Sopenharmony_ci	Perror(space, "unknown cardinfo{} attribute '%s'", attr);
238c72fcc34Sopenharmony_ci	return NULL;
239c72fcc34Sopenharmony_ci}
240c72fcc34Sopenharmony_ci
241c72fcc34Sopenharmony_cistatic int check_id_changed(struct space *space, unsigned int what)
242c72fcc34Sopenharmony_ci{
243c72fcc34Sopenharmony_ci	snd_hctl_elem_t *elem;
244c72fcc34Sopenharmony_ci	int err;
245c72fcc34Sopenharmony_ci
246c72fcc34Sopenharmony_ci	if ((space->ctl_id_changed & what & 1) != 0) {
247c72fcc34Sopenharmony_ci		snd_ctl_elem_id_set_numid(space->ctl_id, 0);
248c72fcc34Sopenharmony_ci		elem = snd_hctl_find_elem(space->ctl_handle, space->ctl_id);
249c72fcc34Sopenharmony_ci		if (!elem)
250c72fcc34Sopenharmony_ci			return -ENOENT;
251c72fcc34Sopenharmony_ci		err = snd_hctl_elem_info(elem, space->ctl_info);
252c72fcc34Sopenharmony_ci		if (err == 0)
253c72fcc34Sopenharmony_ci			space->ctl_id_changed &= ~1;
254c72fcc34Sopenharmony_ci		return err;
255c72fcc34Sopenharmony_ci	}
256c72fcc34Sopenharmony_ci	if ((space->ctl_id_changed & what & 2) != 0) {
257c72fcc34Sopenharmony_ci		snd_ctl_elem_id_set_numid(space->ctl_id, 0);
258c72fcc34Sopenharmony_ci		elem = snd_hctl_find_elem(space->ctl_handle, space->ctl_id);
259c72fcc34Sopenharmony_ci		if (!elem)
260c72fcc34Sopenharmony_ci			return -ENOENT;
261c72fcc34Sopenharmony_ci		err = snd_hctl_elem_read(elem, space->ctl_value);
262c72fcc34Sopenharmony_ci		if (err == 0)
263c72fcc34Sopenharmony_ci			space->ctl_id_changed &= ~2;
264c72fcc34Sopenharmony_ci		return err;
265c72fcc34Sopenharmony_ci	}
266c72fcc34Sopenharmony_ci	return 0;
267c72fcc34Sopenharmony_ci}
268c72fcc34Sopenharmony_ci
269c72fcc34Sopenharmony_cistatic const char *get_ctl_value(struct space *space)
270c72fcc34Sopenharmony_ci{
271c72fcc34Sopenharmony_ci	snd_ctl_elem_type_t type;
272c72fcc34Sopenharmony_ci	unsigned int idx, count;
273c72fcc34Sopenharmony_ci	static char res[1024], tmp[16];
274c72fcc34Sopenharmony_ci	static const char hex[] = "0123456789abcdef";
275c72fcc34Sopenharmony_ci	char *pos;
276c72fcc34Sopenharmony_ci	const char *pos1;
277c72fcc34Sopenharmony_ci
278c72fcc34Sopenharmony_ci	type = snd_ctl_elem_info_get_type(space->ctl_info);
279c72fcc34Sopenharmony_ci	count = snd_ctl_elem_info_get_count(space->ctl_info);
280c72fcc34Sopenharmony_ci	res[0] = '\0';
281c72fcc34Sopenharmony_ci	switch (type) {
282c72fcc34Sopenharmony_ci	case SND_CTL_ELEM_TYPE_BOOLEAN:
283c72fcc34Sopenharmony_ci		for (idx = 0; idx < count; idx++) {
284c72fcc34Sopenharmony_ci			if (idx > 0)
285c72fcc34Sopenharmony_ci				strlcat(res, ",", sizeof(res));
286c72fcc34Sopenharmony_ci			strlcat(res, snd_ctl_elem_value_get_boolean(space->ctl_value, idx) ? "on" : "off", sizeof(res));
287c72fcc34Sopenharmony_ci		}
288c72fcc34Sopenharmony_ci		break;
289c72fcc34Sopenharmony_ci	case SND_CTL_ELEM_TYPE_INTEGER:
290c72fcc34Sopenharmony_ci		for (idx = 0; idx < count; idx++) {
291c72fcc34Sopenharmony_ci			if (idx > 0)
292c72fcc34Sopenharmony_ci				strlcat(res, ",", sizeof(res));
293c72fcc34Sopenharmony_ci			snprintf(tmp, sizeof(tmp), "%li", snd_ctl_elem_value_get_integer(space->ctl_value, idx));
294c72fcc34Sopenharmony_ci			strlcat(res, tmp, sizeof(res));
295c72fcc34Sopenharmony_ci		}
296c72fcc34Sopenharmony_ci		break;
297c72fcc34Sopenharmony_ci	case SND_CTL_ELEM_TYPE_INTEGER64:
298c72fcc34Sopenharmony_ci		for (idx = 0; idx < count; idx++) {
299c72fcc34Sopenharmony_ci			if (idx > 0)
300c72fcc34Sopenharmony_ci				strlcat(res, ",", sizeof(res));
301c72fcc34Sopenharmony_ci			snprintf(tmp, sizeof(tmp), "%lli", snd_ctl_elem_value_get_integer64(space->ctl_value, idx));
302c72fcc34Sopenharmony_ci			strlcat(res, tmp, sizeof(res));
303c72fcc34Sopenharmony_ci		}
304c72fcc34Sopenharmony_ci		break;
305c72fcc34Sopenharmony_ci	case SND_CTL_ELEM_TYPE_ENUMERATED:
306c72fcc34Sopenharmony_ci		for (idx = 0; idx < count; idx++) {
307c72fcc34Sopenharmony_ci			if (idx > 0)
308c72fcc34Sopenharmony_ci				strlcat(res, ",", sizeof(res));
309c72fcc34Sopenharmony_ci			snprintf(tmp, sizeof(tmp), "%u", snd_ctl_elem_value_get_enumerated(space->ctl_value, idx));
310c72fcc34Sopenharmony_ci			strlcat(res, tmp, sizeof(res));
311c72fcc34Sopenharmony_ci		}
312c72fcc34Sopenharmony_ci		break;
313c72fcc34Sopenharmony_ci	case SND_CTL_ELEM_TYPE_BYTES:
314c72fcc34Sopenharmony_ci	case SND_CTL_ELEM_TYPE_IEC958:
315c72fcc34Sopenharmony_ci		if (type == SND_CTL_ELEM_TYPE_IEC958)
316c72fcc34Sopenharmony_ci			count = sizeof(snd_aes_iec958_t);
317c72fcc34Sopenharmony_ci		if (count > (sizeof(res)-1)/2)
318c72fcc34Sopenharmony_ci			count = (sizeof(res)-1/2);
319c72fcc34Sopenharmony_ci		pos = res;
320c72fcc34Sopenharmony_ci		pos1 = snd_ctl_elem_value_get_bytes(space->ctl_value);
321c72fcc34Sopenharmony_ci		while (count > 0) {
322c72fcc34Sopenharmony_ci			idx = *pos1++;
323c72fcc34Sopenharmony_ci			*pos++ = hex[idx >> 4];
324c72fcc34Sopenharmony_ci			*pos++ = hex[idx & 0x0f];
325c72fcc34Sopenharmony_ci			count++;
326c72fcc34Sopenharmony_ci		}
327c72fcc34Sopenharmony_ci		*pos++ = '\0';
328c72fcc34Sopenharmony_ci		break;
329c72fcc34Sopenharmony_ci	default:
330c72fcc34Sopenharmony_ci		Perror(space, "unknown element type '%i'", type);
331c72fcc34Sopenharmony_ci		return NULL;
332c72fcc34Sopenharmony_ci	}
333c72fcc34Sopenharmony_ci	return res;
334c72fcc34Sopenharmony_ci}
335c72fcc34Sopenharmony_ci
336c72fcc34Sopenharmony_ci/* Function to convert from percentage to volume. val = percentage */
337c72fcc34Sopenharmony_ci#define convert_prange1(val, min, max) \
338c72fcc34Sopenharmony_ci        ceil((val) * ((max) - (min)) * 0.01 + (min))
339c72fcc34Sopenharmony_ci
340c72fcc34Sopenharmony_cistatic int set_ctl_value(struct space *space, const char *value, int all)
341c72fcc34Sopenharmony_ci{
342c72fcc34Sopenharmony_ci	snd_ctl_elem_type_t type;
343c72fcc34Sopenharmony_ci	unsigned int idx, idx2, count, items;
344c72fcc34Sopenharmony_ci	const char *pos, *pos2;
345c72fcc34Sopenharmony_ci	snd_hctl_elem_t *elem;
346c72fcc34Sopenharmony_ci	int val;
347c72fcc34Sopenharmony_ci	long lval;
348c72fcc34Sopenharmony_ci
349c72fcc34Sopenharmony_ci	type = snd_ctl_elem_info_get_type(space->ctl_info);
350c72fcc34Sopenharmony_ci	count = snd_ctl_elem_info_get_count(space->ctl_info);
351c72fcc34Sopenharmony_ci	switch (type) {
352c72fcc34Sopenharmony_ci	case SND_CTL_ELEM_TYPE_BOOLEAN:
353c72fcc34Sopenharmony_ci		for (idx = 0; idx < count; idx++) {
354c72fcc34Sopenharmony_ci			while (*value == ' ')
355c72fcc34Sopenharmony_ci				value++;
356c72fcc34Sopenharmony_ci			if (*value == '\0')
357c72fcc34Sopenharmony_ci				goto missing;
358c72fcc34Sopenharmony_ci			val = strncasecmp(value, "true", 4) == 0 ||
359c72fcc34Sopenharmony_ci				strncasecmp(value, "yes", 3) == 0 ||
360c72fcc34Sopenharmony_ci				strncasecmp(value, "on", 2) == 0 ||
361c72fcc34Sopenharmony_ci				strncasecmp(value, "1", 1) == 0;
362c72fcc34Sopenharmony_ci			snd_ctl_elem_value_set_boolean(space->ctl_value, idx, val);
363c72fcc34Sopenharmony_ci			if (all)
364c72fcc34Sopenharmony_ci				continue;
365c72fcc34Sopenharmony_ci			pos = strchr(value, ',');
366c72fcc34Sopenharmony_ci			value = pos ? pos + 1 : value + strlen(value) - 1;
367c72fcc34Sopenharmony_ci		}
368c72fcc34Sopenharmony_ci		break;
369c72fcc34Sopenharmony_ci	case SND_CTL_ELEM_TYPE_INTEGER:
370c72fcc34Sopenharmony_ci		for (idx = 0; idx < count; idx++) {
371c72fcc34Sopenharmony_ci			while (*value == ' ')
372c72fcc34Sopenharmony_ci				value++;
373c72fcc34Sopenharmony_ci			pos = strchr(value, ',');
374c72fcc34Sopenharmony_ci			if (pos)
375c72fcc34Sopenharmony_ci				*(char *)pos = '\0';
376c72fcc34Sopenharmony_ci			remove_trailing_chars((char *)value, ' ');
377c72fcc34Sopenharmony_ci			items = pos ? (unsigned)(pos - value) : (unsigned)strlen(value);
378c72fcc34Sopenharmony_ci			if (items > 1 && value[items-1] == '%') {
379c72fcc34Sopenharmony_ci				val = convert_prange1(strtol(value, NULL, 0), snd_ctl_elem_info_get_min(space->ctl_info), snd_ctl_elem_info_get_max(space->ctl_info));
380c72fcc34Sopenharmony_ci				snd_ctl_elem_value_set_integer(space->ctl_value, idx, val);
381c72fcc34Sopenharmony_ci			} else if (items > 2 && value[items-2] == 'd' && value[items-1] == 'B') {
382c72fcc34Sopenharmony_ci				val = strtol(value, NULL, 0) * 100;
383c72fcc34Sopenharmony_ci				if ((pos2 = strchr(value, '.')) != NULL) {
384c72fcc34Sopenharmony_ci					if (isdigit(*(pos2-1)) && isdigit(*(pos2-2))) {
385c72fcc34Sopenharmony_ci						if (val < 0)
386c72fcc34Sopenharmony_ci							val -= strtol(pos2 + 1, NULL, 0);
387c72fcc34Sopenharmony_ci						else
388c72fcc34Sopenharmony_ci							val += strtol(pos2 + 1, NULL, 0);
389c72fcc34Sopenharmony_ci					} else if (isdigit(*(pos2-1))) {
390c72fcc34Sopenharmony_ci						if (val < 0)
391c72fcc34Sopenharmony_ci							val -= strtol(pos2 + 1, NULL, 0) * 10;
392c72fcc34Sopenharmony_ci						else
393c72fcc34Sopenharmony_ci							val += strtol(pos2 + 1, NULL, 0) * 10;
394c72fcc34Sopenharmony_ci					}
395c72fcc34Sopenharmony_ci				}
396c72fcc34Sopenharmony_ci				val = snd_ctl_convert_from_dB(snd_hctl_ctl(space->ctl_handle), space->ctl_id, val, &lval, -1);
397c72fcc34Sopenharmony_ci				if (val < 0) {
398c72fcc34Sopenharmony_ci					dbg("unable to convert dB value '%s' to internal integer range", value);
399c72fcc34Sopenharmony_ci					return val;
400c72fcc34Sopenharmony_ci				}
401c72fcc34Sopenharmony_ci				snd_ctl_elem_value_set_integer(space->ctl_value, idx, lval);
402c72fcc34Sopenharmony_ci			} else {
403c72fcc34Sopenharmony_ci				snd_ctl_elem_value_set_integer(space->ctl_value, idx, strtol(value, NULL, 0));
404c72fcc34Sopenharmony_ci			}
405c72fcc34Sopenharmony_ci			if (all)
406c72fcc34Sopenharmony_ci				continue;
407c72fcc34Sopenharmony_ci			value = pos ? pos + 1 : value + strlen(value) - 1;
408c72fcc34Sopenharmony_ci		}
409c72fcc34Sopenharmony_ci		break;
410c72fcc34Sopenharmony_ci	case SND_CTL_ELEM_TYPE_INTEGER64:
411c72fcc34Sopenharmony_ci		for (idx = 0; idx < count; idx++) {
412c72fcc34Sopenharmony_ci			while (*value == ' ')
413c72fcc34Sopenharmony_ci				value++;
414c72fcc34Sopenharmony_ci			snd_ctl_elem_value_set_integer64(space->ctl_value, idx, strtoll(value, NULL, 0));
415c72fcc34Sopenharmony_ci			if (all)
416c72fcc34Sopenharmony_ci				continue;
417c72fcc34Sopenharmony_ci			pos = strchr(value, ',');
418c72fcc34Sopenharmony_ci			value = pos ? pos + 1 : value + strlen(value) - 1;
419c72fcc34Sopenharmony_ci		}
420c72fcc34Sopenharmony_ci		break;
421c72fcc34Sopenharmony_ci	case SND_CTL_ELEM_TYPE_ENUMERATED:
422c72fcc34Sopenharmony_ci		for (idx = 0; idx < count; idx++) {
423c72fcc34Sopenharmony_ci			while (*value == ' ')
424c72fcc34Sopenharmony_ci				value++;
425c72fcc34Sopenharmony_ci			pos = strchr(value, ',');
426c72fcc34Sopenharmony_ci			if (isdigit(value[0]) || value[0] == '-') {
427c72fcc34Sopenharmony_ci				snd_ctl_elem_value_set_enumerated(space->ctl_value, idx, strtol(value, NULL, 0));
428c72fcc34Sopenharmony_ci			} else {
429c72fcc34Sopenharmony_ci				if (pos)
430c72fcc34Sopenharmony_ci					*(char *)pos = '\0';
431c72fcc34Sopenharmony_ci				remove_trailing_chars((char *)value, ' ');
432c72fcc34Sopenharmony_ci				items = snd_ctl_elem_info_get_items(space->ctl_info);
433c72fcc34Sopenharmony_ci				for (idx2 = 0; idx2 < items; idx2++) {
434c72fcc34Sopenharmony_ci					snd_ctl_elem_info_set_item(space->ctl_info, idx2);
435c72fcc34Sopenharmony_ci					elem = snd_hctl_find_elem(space->ctl_handle, space->ctl_id);
436c72fcc34Sopenharmony_ci					if (elem == NULL)
437c72fcc34Sopenharmony_ci						return -ENOENT;
438c72fcc34Sopenharmony_ci					val = snd_hctl_elem_info(elem, space->ctl_info);
439c72fcc34Sopenharmony_ci					if (val < 0)
440c72fcc34Sopenharmony_ci						return val;
441c72fcc34Sopenharmony_ci					if (strcasecmp(snd_ctl_elem_info_get_item_name(space->ctl_info), value) == 0) {
442c72fcc34Sopenharmony_ci						snd_ctl_elem_value_set_enumerated(space->ctl_value, idx, idx2);
443c72fcc34Sopenharmony_ci						break;
444c72fcc34Sopenharmony_ci					}
445c72fcc34Sopenharmony_ci				}
446c72fcc34Sopenharmony_ci				if (idx2 >= items) {
447c72fcc34Sopenharmony_ci					Perror(space, "wrong enum identifier '%s'", value);
448c72fcc34Sopenharmony_ci					return -EINVAL;
449c72fcc34Sopenharmony_ci				}
450c72fcc34Sopenharmony_ci			}
451c72fcc34Sopenharmony_ci			if (all)
452c72fcc34Sopenharmony_ci				continue;
453c72fcc34Sopenharmony_ci			value = pos ? pos + 1 : value + strlen(value) - 1;
454c72fcc34Sopenharmony_ci		}
455c72fcc34Sopenharmony_ci		break;
456c72fcc34Sopenharmony_ci	case SND_CTL_ELEM_TYPE_BYTES:
457c72fcc34Sopenharmony_ci	case SND_CTL_ELEM_TYPE_IEC958:
458c72fcc34Sopenharmony_ci		if (type == SND_CTL_ELEM_TYPE_IEC958)
459c72fcc34Sopenharmony_ci			count = sizeof(snd_aes_iec958_t);
460c72fcc34Sopenharmony_ci		while (*value == ' ')
461c72fcc34Sopenharmony_ci			value++;
462c72fcc34Sopenharmony_ci		if (strlen(value) != count * 2) {
463c72fcc34Sopenharmony_ci			Perror(space, "bad ctl value hexa length (should be %u bytes)", count);
464c72fcc34Sopenharmony_ci			return -EINVAL;
465c72fcc34Sopenharmony_ci		}
466c72fcc34Sopenharmony_ci		for (idx = 0; idx < count; idx += 2) {
467c72fcc34Sopenharmony_ci			int nibble1 = hextodigit(*(value++));
468c72fcc34Sopenharmony_ci			int nibble2 = hextodigit(*(value++));
469c72fcc34Sopenharmony_ci			if (nibble1 < 0 || nibble2 < 0) {
470c72fcc34Sopenharmony_ci				Perror(space, "bad ctl hexa value");
471c72fcc34Sopenharmony_ci				return -EINVAL;
472c72fcc34Sopenharmony_ci			}
473c72fcc34Sopenharmony_ci			val = (nibble1 << 4) | nibble2;
474c72fcc34Sopenharmony_ci			snd_ctl_elem_value_set_byte(space->ctl_value, idx, val);
475c72fcc34Sopenharmony_ci		}
476c72fcc34Sopenharmony_ci		break;
477c72fcc34Sopenharmony_ci	default:
478c72fcc34Sopenharmony_ci		Perror(space, "unknown element type '%i'", type);
479c72fcc34Sopenharmony_ci		return -EINVAL;
480c72fcc34Sopenharmony_ci	}
481c72fcc34Sopenharmony_ci	return 0;
482c72fcc34Sopenharmony_ci  missing:
483c72fcc34Sopenharmony_ci  	Perror(space, "missing some ctl values (line %i)", space->linenum);
484c72fcc34Sopenharmony_ci  	return -EINVAL;
485c72fcc34Sopenharmony_ci}
486c72fcc34Sopenharmony_ci
487c72fcc34Sopenharmony_cistatic int do_match(const char *key, enum key_op op,
488c72fcc34Sopenharmony_ci		    const char *key_value, const char *value)
489c72fcc34Sopenharmony_ci{
490c72fcc34Sopenharmony_ci	int match;
491c72fcc34Sopenharmony_ci
492c72fcc34Sopenharmony_ci	if (value == NULL)
493c72fcc34Sopenharmony_ci		return 0;
494c72fcc34Sopenharmony_ci	dbg("match %s '%s' <-> '%s'", key, key_value, value);
495c72fcc34Sopenharmony_ci	match = fnmatch(key_value, value, 0) == 0;
496c72fcc34Sopenharmony_ci	if (match && op == KEY_OP_MATCH) {
497c72fcc34Sopenharmony_ci		dbg("%s is true (matching value)", key);
498c72fcc34Sopenharmony_ci		return 1;
499c72fcc34Sopenharmony_ci	}
500c72fcc34Sopenharmony_ci	if (!match && op == KEY_OP_NOMATCH) {
501c72fcc34Sopenharmony_ci		dbg("%s is true (non-matching value)", key);
502c72fcc34Sopenharmony_ci		return 1;
503c72fcc34Sopenharmony_ci	}
504c72fcc34Sopenharmony_ci	dbg("%s is false", key);
505c72fcc34Sopenharmony_ci	return 0;
506c72fcc34Sopenharmony_ci}
507c72fcc34Sopenharmony_ci
508c72fcc34Sopenharmony_cistatic int ctl_match(snd_ctl_elem_id_t *pattern, snd_ctl_elem_id_t *id)
509c72fcc34Sopenharmony_ci{
510c72fcc34Sopenharmony_ci	if ((int)snd_ctl_elem_id_get_interface(pattern) != -1 &&
511c72fcc34Sopenharmony_ci	    snd_ctl_elem_id_get_interface(pattern) != snd_ctl_elem_id_get_interface(id))
512c72fcc34Sopenharmony_ci	    	return 0;
513c72fcc34Sopenharmony_ci	if ((int)snd_ctl_elem_id_get_device(pattern) != -1 &&
514c72fcc34Sopenharmony_ci	    snd_ctl_elem_id_get_device(pattern) != snd_ctl_elem_id_get_device(id))
515c72fcc34Sopenharmony_ci		return 0;
516c72fcc34Sopenharmony_ci	if ((int)snd_ctl_elem_id_get_subdevice(pattern) != -1 &&
517c72fcc34Sopenharmony_ci	    snd_ctl_elem_id_get_subdevice(pattern) != snd_ctl_elem_id_get_subdevice(id))
518c72fcc34Sopenharmony_ci	    	return 0;
519c72fcc34Sopenharmony_ci	if ((int)snd_ctl_elem_id_get_index(pattern) != -1 &&
520c72fcc34Sopenharmony_ci	    snd_ctl_elem_id_get_index(pattern) != snd_ctl_elem_id_get_index(id))
521c72fcc34Sopenharmony_ci	    	return 0;
522c72fcc34Sopenharmony_ci	if (fnmatch(snd_ctl_elem_id_get_name(pattern), snd_ctl_elem_id_get_name(id), 0) != 0)
523c72fcc34Sopenharmony_ci		return 0;
524c72fcc34Sopenharmony_ci	return 1;
525c72fcc34Sopenharmony_ci}
526c72fcc34Sopenharmony_ci
527c72fcc34Sopenharmony_cistatic const char *elemid_get(struct space *space, const char *attr)
528c72fcc34Sopenharmony_ci{
529c72fcc34Sopenharmony_ci	long long val;
530c72fcc34Sopenharmony_ci	snd_ctl_elem_type_t type;
531c72fcc34Sopenharmony_ci	static char res[256];
532c72fcc34Sopenharmony_ci
533c72fcc34Sopenharmony_ci	if (strncasecmp(attr, "numid", 5) == 0) {
534c72fcc34Sopenharmony_ci		val = snd_ctl_elem_id_get_numid(space->ctl_id);
535c72fcc34Sopenharmony_ci	    	goto value;
536c72fcc34Sopenharmony_ci	}
537c72fcc34Sopenharmony_ci	if (strncasecmp(attr, "iface", 5) == 0 ||
538c72fcc34Sopenharmony_ci	    strncasecmp(attr, "interface", 9) == 0)
539c72fcc34Sopenharmony_ci	    	return snd_ctl_elem_iface_name(snd_ctl_elem_id_get_interface(space->ctl_id));
540c72fcc34Sopenharmony_ci	if (strncasecmp(attr, "device", 6) == 0) {
541c72fcc34Sopenharmony_ci		val = snd_ctl_elem_id_get_device(space->ctl_id);
542c72fcc34Sopenharmony_ci	    	goto value;
543c72fcc34Sopenharmony_ci	}
544c72fcc34Sopenharmony_ci	if (strncasecmp(attr, "subdev", 6) == 0) {
545c72fcc34Sopenharmony_ci		val = snd_ctl_elem_id_get_subdevice(space->ctl_id);
546c72fcc34Sopenharmony_ci	    	goto value;
547c72fcc34Sopenharmony_ci	}
548c72fcc34Sopenharmony_ci	if (strncasecmp(attr, "name", 4) == 0)
549c72fcc34Sopenharmony_ci		return snd_ctl_elem_id_get_name(space->ctl_id);
550c72fcc34Sopenharmony_ci	if (strncasecmp(attr, "index", 5) == 0) {
551c72fcc34Sopenharmony_ci		val = snd_ctl_elem_id_get_index(space->ctl_id);
552c72fcc34Sopenharmony_ci	    	goto value;
553c72fcc34Sopenharmony_ci	}
554c72fcc34Sopenharmony_ci	if (strncasecmp(attr, "type", 4) == 0) {
555c72fcc34Sopenharmony_ci		if (check_id_changed(space, 1))
556c72fcc34Sopenharmony_ci			return NULL;
557c72fcc34Sopenharmony_ci		return snd_ctl_elem_type_name(snd_ctl_elem_info_get_type(space->ctl_info));
558c72fcc34Sopenharmony_ci	}
559c72fcc34Sopenharmony_ci	if (strncasecmp(attr, "attr", 4) == 0) {
560c72fcc34Sopenharmony_ci		if (check_id_changed(space, 1))
561c72fcc34Sopenharmony_ci			return NULL;
562c72fcc34Sopenharmony_ci		res[0] = '\0';
563c72fcc34Sopenharmony_ci		if (snd_ctl_elem_info_is_readable(space->ctl_info))
564c72fcc34Sopenharmony_ci			strcat(res, "r");
565c72fcc34Sopenharmony_ci		if (snd_ctl_elem_info_is_writable(space->ctl_info))
566c72fcc34Sopenharmony_ci			strcat(res, "w");
567c72fcc34Sopenharmony_ci		if (snd_ctl_elem_info_is_volatile(space->ctl_info))
568c72fcc34Sopenharmony_ci			strcat(res, "v");
569c72fcc34Sopenharmony_ci		if (snd_ctl_elem_info_is_inactive(space->ctl_info))
570c72fcc34Sopenharmony_ci			strcat(res, "i");
571c72fcc34Sopenharmony_ci		if (snd_ctl_elem_info_is_locked(space->ctl_info))
572c72fcc34Sopenharmony_ci			strcat(res, "l");
573c72fcc34Sopenharmony_ci		if (snd_ctl_elem_info_is_tlv_readable(space->ctl_info))
574c72fcc34Sopenharmony_ci			strcat(res, "R");
575c72fcc34Sopenharmony_ci		if (snd_ctl_elem_info_is_tlv_writable(space->ctl_info))
576c72fcc34Sopenharmony_ci			strcat(res, "W");
577c72fcc34Sopenharmony_ci		if (snd_ctl_elem_info_is_tlv_commandable(space->ctl_info))
578c72fcc34Sopenharmony_ci			strcat(res, "C");
579c72fcc34Sopenharmony_ci		if (snd_ctl_elem_info_is_owner(space->ctl_info))
580c72fcc34Sopenharmony_ci			strcat(res, "o");
581c72fcc34Sopenharmony_ci		if (snd_ctl_elem_info_is_user(space->ctl_info))
582c72fcc34Sopenharmony_ci			strcat(res, "u");
583c72fcc34Sopenharmony_ci		return res;
584c72fcc34Sopenharmony_ci	}
585c72fcc34Sopenharmony_ci	if (strncasecmp(attr, "owner", 5) == 0) {
586c72fcc34Sopenharmony_ci		if (check_id_changed(space, 1))
587c72fcc34Sopenharmony_ci			return NULL;
588c72fcc34Sopenharmony_ci		val = snd_ctl_elem_info_get_owner(space->ctl_info);
589c72fcc34Sopenharmony_ci		goto value;
590c72fcc34Sopenharmony_ci	}
591c72fcc34Sopenharmony_ci	if (strncasecmp(attr, "count", 5) == 0) {
592c72fcc34Sopenharmony_ci		if (check_id_changed(space, 1))
593c72fcc34Sopenharmony_ci			return NULL;
594c72fcc34Sopenharmony_ci		val = snd_ctl_elem_info_get_count(space->ctl_info);
595c72fcc34Sopenharmony_ci		goto value;
596c72fcc34Sopenharmony_ci	}
597c72fcc34Sopenharmony_ci	if (strncasecmp(attr, "min", 3) == 0) {
598c72fcc34Sopenharmony_ci		if (check_id_changed(space, 1))
599c72fcc34Sopenharmony_ci			return NULL;
600c72fcc34Sopenharmony_ci		type = snd_ctl_elem_info_get_type(space->ctl_info);
601c72fcc34Sopenharmony_ci		if (type == SND_CTL_ELEM_TYPE_INTEGER64)
602c72fcc34Sopenharmony_ci			val = snd_ctl_elem_info_get_min64(space->ctl_info);
603c72fcc34Sopenharmony_ci		else if (type == SND_CTL_ELEM_TYPE_INTEGER)
604c72fcc34Sopenharmony_ci			val = snd_ctl_elem_info_get_min(space->ctl_info);
605c72fcc34Sopenharmony_ci		else
606c72fcc34Sopenharmony_ci			goto empty;
607c72fcc34Sopenharmony_ci		goto value;
608c72fcc34Sopenharmony_ci	}
609c72fcc34Sopenharmony_ci	if (strncasecmp(attr, "max", 3) == 0) {
610c72fcc34Sopenharmony_ci		if (check_id_changed(space, 1))
611c72fcc34Sopenharmony_ci			return NULL;
612c72fcc34Sopenharmony_ci		type = snd_ctl_elem_info_get_type(space->ctl_info);
613c72fcc34Sopenharmony_ci		if (type == SND_CTL_ELEM_TYPE_INTEGER64)
614c72fcc34Sopenharmony_ci			val = snd_ctl_elem_info_get_max64(space->ctl_info);
615c72fcc34Sopenharmony_ci		else if (type == SND_CTL_ELEM_TYPE_INTEGER)
616c72fcc34Sopenharmony_ci			val = snd_ctl_elem_info_get_max(space->ctl_info);
617c72fcc34Sopenharmony_ci		else
618c72fcc34Sopenharmony_ci			goto empty;
619c72fcc34Sopenharmony_ci		goto value;
620c72fcc34Sopenharmony_ci	}
621c72fcc34Sopenharmony_ci	if (strncasecmp(attr, "step", 3) == 0) {
622c72fcc34Sopenharmony_ci		if (check_id_changed(space, 1))
623c72fcc34Sopenharmony_ci			return NULL;
624c72fcc34Sopenharmony_ci		type = snd_ctl_elem_info_get_type(space->ctl_info);
625c72fcc34Sopenharmony_ci		if (type == SND_CTL_ELEM_TYPE_INTEGER64)
626c72fcc34Sopenharmony_ci			val = snd_ctl_elem_info_get_step64(space->ctl_info);
627c72fcc34Sopenharmony_ci		else if (type == SND_CTL_ELEM_TYPE_INTEGER)
628c72fcc34Sopenharmony_ci			val = snd_ctl_elem_info_get_step(space->ctl_info);
629c72fcc34Sopenharmony_ci		else
630c72fcc34Sopenharmony_ci			goto empty;
631c72fcc34Sopenharmony_ci		goto value;
632c72fcc34Sopenharmony_ci	}
633c72fcc34Sopenharmony_ci	if (strncasecmp(attr, "items", 5) == 0) {
634c72fcc34Sopenharmony_ci		if (check_id_changed(space, 1))
635c72fcc34Sopenharmony_ci			return NULL;
636c72fcc34Sopenharmony_ci		if (snd_ctl_elem_info_get_type(space->ctl_info) == SND_CTL_ELEM_TYPE_ENUMERATED)
637c72fcc34Sopenharmony_ci			val = snd_ctl_elem_info_get_items(space->ctl_info);
638c72fcc34Sopenharmony_ci		else {
639c72fcc34Sopenharmony_ci		  empty:
640c72fcc34Sopenharmony_ci			res[0] = '\0';
641c72fcc34Sopenharmony_ci			return res;
642c72fcc34Sopenharmony_ci		}
643c72fcc34Sopenharmony_ci		goto value;
644c72fcc34Sopenharmony_ci	}
645c72fcc34Sopenharmony_ci	if (strncasecmp(attr, "value", 5) == 0) {
646c72fcc34Sopenharmony_ci		if (check_id_changed(space, 3))
647c72fcc34Sopenharmony_ci			return NULL;
648c72fcc34Sopenharmony_ci		return get_ctl_value(space);
649c72fcc34Sopenharmony_ci	}
650c72fcc34Sopenharmony_ci	if (strncasecmp(attr, "dBmin", 5) == 0) {
651c72fcc34Sopenharmony_ci		long min, max;
652c72fcc34Sopenharmony_ci		if (check_id_changed(space, 1))
653c72fcc34Sopenharmony_ci			return NULL;
654c72fcc34Sopenharmony_ci		if (snd_ctl_get_dB_range(snd_hctl_ctl(space->ctl_handle), space->ctl_id, &min, &max) < 0)
655c72fcc34Sopenharmony_ci			goto empty;
656c72fcc34Sopenharmony_ci		val = min;
657c72fcc34Sopenharmony_cidbvalue:
658c72fcc34Sopenharmony_ci		sprintf(res, "%li.%02lidB", (long)(val / 100), labs(val % 100));
659c72fcc34Sopenharmony_ci		return res;
660c72fcc34Sopenharmony_ci	}
661c72fcc34Sopenharmony_ci	if (strncasecmp(attr, "dBmax", 5) == 0) {
662c72fcc34Sopenharmony_ci		long min, max;
663c72fcc34Sopenharmony_ci		if (check_id_changed(space, 1))
664c72fcc34Sopenharmony_ci			return NULL;
665c72fcc34Sopenharmony_ci		if (snd_ctl_get_dB_range(snd_hctl_ctl(space->ctl_handle), space->ctl_id, &min, &max) < 0)
666c72fcc34Sopenharmony_ci			goto empty;
667c72fcc34Sopenharmony_ci		val = max;
668c72fcc34Sopenharmony_ci		goto dbvalue;
669c72fcc34Sopenharmony_ci	}
670c72fcc34Sopenharmony_ci	if (strncasecmp(attr, "enums", 5) == 0) {
671c72fcc34Sopenharmony_ci		unsigned int idx, items;
672c72fcc34Sopenharmony_ci		snd_hctl_elem_t *elem;
673c72fcc34Sopenharmony_ci		if (check_id_changed(space, 1))
674c72fcc34Sopenharmony_ci			return NULL;
675c72fcc34Sopenharmony_ci		if (snd_ctl_elem_info_get_type(space->ctl_info) != SND_CTL_ELEM_TYPE_ENUMERATED)
676c72fcc34Sopenharmony_ci			goto empty;
677c72fcc34Sopenharmony_ci		items = snd_ctl_elem_info_get_items(space->ctl_info);
678c72fcc34Sopenharmony_ci		strcpy(res, "|");
679c72fcc34Sopenharmony_ci		for (idx = 0; idx < items; idx++) {
680c72fcc34Sopenharmony_ci			snd_ctl_elem_info_set_item(space->ctl_info, idx);
681c72fcc34Sopenharmony_ci			elem = snd_hctl_find_elem(space->ctl_handle, space->ctl_id);
682c72fcc34Sopenharmony_ci			if (elem == NULL)
683c72fcc34Sopenharmony_ci				break;
684c72fcc34Sopenharmony_ci			if (snd_hctl_elem_info(elem, space->ctl_info) < 0)
685c72fcc34Sopenharmony_ci				break;
686c72fcc34Sopenharmony_ci			strlcat(res, snd_ctl_elem_info_get_item_name(space->ctl_info), sizeof(res));
687c72fcc34Sopenharmony_ci			strlcat(res, "|", sizeof(res));
688c72fcc34Sopenharmony_ci		}
689c72fcc34Sopenharmony_ci		return res;
690c72fcc34Sopenharmony_ci	}
691c72fcc34Sopenharmony_ci	if (strncasecmp(attr, "do_search", 9) == 0) {
692c72fcc34Sopenharmony_ci		int err, index = 0;
693c72fcc34Sopenharmony_ci		snd_hctl_elem_t *elem;
694c72fcc34Sopenharmony_ci		snd_ctl_elem_id_t *id;
695c72fcc34Sopenharmony_ci		char *pos = strchr(attr, ' ');
696c72fcc34Sopenharmony_ci		if (pos)
697c72fcc34Sopenharmony_ci			index = strtol(pos, NULL, 0);
698c72fcc34Sopenharmony_ci		err = snd_ctl_elem_id_malloc(&id);
699c72fcc34Sopenharmony_ci		if (err < 0)
700c72fcc34Sopenharmony_ci			return NULL;
701c72fcc34Sopenharmony_ci		elem = snd_hctl_first_elem(space->ctl_handle);
702c72fcc34Sopenharmony_ci		while (elem) {
703c72fcc34Sopenharmony_ci			snd_hctl_elem_get_id(elem, id);
704c72fcc34Sopenharmony_ci			if (!ctl_match(space->ctl_id, id))
705c72fcc34Sopenharmony_ci				goto next_search;
706c72fcc34Sopenharmony_ci			if (index > 0) {
707c72fcc34Sopenharmony_ci				index--;
708c72fcc34Sopenharmony_ci				goto next_search;
709c72fcc34Sopenharmony_ci			}
710c72fcc34Sopenharmony_ci			strcpy(res, "1");
711c72fcc34Sopenharmony_ci			snd_ctl_elem_id_copy(space->ctl_id, id);
712c72fcc34Sopenharmony_ci			snd_ctl_elem_id_free(id);
713c72fcc34Sopenharmony_ci			dbg("do_ctl_search found a control");
714c72fcc34Sopenharmony_ci			return res;
715c72fcc34Sopenharmony_ci		      next_search:
716c72fcc34Sopenharmony_ci			elem = snd_hctl_elem_next(elem);
717c72fcc34Sopenharmony_ci		}
718c72fcc34Sopenharmony_ci		snd_ctl_elem_id_free(id);
719c72fcc34Sopenharmony_ci		strcpy(res, "0");
720c72fcc34Sopenharmony_ci		return res;
721c72fcc34Sopenharmony_ci	}
722c72fcc34Sopenharmony_ci	if (strncasecmp(attr, "do_count", 8) == 0) {
723c72fcc34Sopenharmony_ci		int err, index = 0;
724c72fcc34Sopenharmony_ci		snd_hctl_elem_t *elem;
725c72fcc34Sopenharmony_ci		snd_ctl_elem_id_t *id;
726c72fcc34Sopenharmony_ci		err = snd_ctl_elem_id_malloc(&id);
727c72fcc34Sopenharmony_ci		if (err < 0)
728c72fcc34Sopenharmony_ci			return NULL;
729c72fcc34Sopenharmony_ci		elem = snd_hctl_first_elem(space->ctl_handle);
730c72fcc34Sopenharmony_ci		while (elem) {
731c72fcc34Sopenharmony_ci			snd_hctl_elem_get_id(elem, id);
732c72fcc34Sopenharmony_ci			if (ctl_match(space->ctl_id, id))
733c72fcc34Sopenharmony_ci				index++;
734c72fcc34Sopenharmony_ci			elem = snd_hctl_elem_next(elem);
735c72fcc34Sopenharmony_ci		}
736c72fcc34Sopenharmony_ci		snd_ctl_elem_id_free(id);
737c72fcc34Sopenharmony_ci		sprintf(res, "%d", index);
738c72fcc34Sopenharmony_ci		dbg("do_ctl_count found %s controls", res);
739c72fcc34Sopenharmony_ci		return res;
740c72fcc34Sopenharmony_ci	}
741c72fcc34Sopenharmony_ci	Perror(space, "unknown ctl{} attribute '%s'", attr);
742c72fcc34Sopenharmony_ci	return NULL;
743c72fcc34Sopenharmony_ci  value:
744c72fcc34Sopenharmony_ci  	sprintf(res, "%lli", val);
745c72fcc34Sopenharmony_ci  	return res;
746c72fcc34Sopenharmony_ci}
747c72fcc34Sopenharmony_ci
748c72fcc34Sopenharmony_cistatic int elemid_set(struct space *space, const char *attr, const char *value)
749c72fcc34Sopenharmony_ci{
750c72fcc34Sopenharmony_ci	unsigned int val;
751c72fcc34Sopenharmony_ci	void (*fcn)(snd_ctl_elem_id_t *, unsigned int);
752c72fcc34Sopenharmony_ci	snd_ctl_elem_iface_t iface;
753c72fcc34Sopenharmony_ci	int err;
754c72fcc34Sopenharmony_ci
755c72fcc34Sopenharmony_ci	if (strncasecmp(attr, "numid", 5) == 0) {
756c72fcc34Sopenharmony_ci		fcn = snd_ctl_elem_id_set_numid;
757c72fcc34Sopenharmony_ci	    	goto value;
758c72fcc34Sopenharmony_ci	}
759c72fcc34Sopenharmony_ci	if (strncasecmp(attr, "iface", 5) == 0 ||
760c72fcc34Sopenharmony_ci	    strncasecmp(attr, "interface", 9) == 0 ||
761c72fcc34Sopenharmony_ci	    strncasecmp(attr, "reset", 5) == 0 ||
762c72fcc34Sopenharmony_ci	    strncasecmp(attr, "search", 6) == 0) {
763c72fcc34Sopenharmony_ci	    	if (strlen(value) == 0 && strncasecmp(attr, "search", 6) == 0) {
764c72fcc34Sopenharmony_ci	    		iface = 0;
765c72fcc34Sopenharmony_ci	    		goto search;
766c72fcc34Sopenharmony_ci		}
767c72fcc34Sopenharmony_ci	    	for (iface = 0; iface <= SND_CTL_ELEM_IFACE_LAST; iface++) {
768c72fcc34Sopenharmony_ci	    		if (strcasecmp(value, snd_ctl_elem_iface_name(iface)) == 0) {
769c72fcc34Sopenharmony_ci			    	if (strncasecmp(attr, "reset", 5) == 0)
770c72fcc34Sopenharmony_ci			    		snd_ctl_elem_id_clear(space->ctl_id);
771c72fcc34Sopenharmony_ci			    	if (strncasecmp(attr, "search", 5) == 0) {
772c72fcc34Sopenharmony_ci			    	  search:
773c72fcc34Sopenharmony_ci			    		snd_ctl_elem_id_clear(space->ctl_id);
774c72fcc34Sopenharmony_ci			    		/* -1 means all */
775c72fcc34Sopenharmony_ci			    		snd_ctl_elem_id_set_interface(space->ctl_id, -1);
776c72fcc34Sopenharmony_ci			    		snd_ctl_elem_id_set_device(space->ctl_id, -1);
777c72fcc34Sopenharmony_ci			    		snd_ctl_elem_id_set_subdevice(space->ctl_id, -1);
778c72fcc34Sopenharmony_ci			    		snd_ctl_elem_id_set_name(space->ctl_id, "*");
779c72fcc34Sopenharmony_ci			    		snd_ctl_elem_id_set_index(space->ctl_id, -1);
780c72fcc34Sopenharmony_ci			    		if (strlen(value) == 0)
781c72fcc34Sopenharmony_ci			    			return 0;
782c72fcc34Sopenharmony_ci				}
783c72fcc34Sopenharmony_ci				snd_ctl_elem_id_set_interface(space->ctl_id, iface);
784c72fcc34Sopenharmony_ci				space->ctl_id_changed = ~0;
785c72fcc34Sopenharmony_ci			    	return 0;
786c72fcc34Sopenharmony_ci			}
787c72fcc34Sopenharmony_ci		}
788c72fcc34Sopenharmony_ci		Perror(space, "unknown control interface name '%s'", value);
789c72fcc34Sopenharmony_ci		return -EINVAL;
790c72fcc34Sopenharmony_ci	}
791c72fcc34Sopenharmony_ci	if (strncasecmp(attr, "device", 6) == 0) {
792c72fcc34Sopenharmony_ci		fcn = snd_ctl_elem_id_set_device;
793c72fcc34Sopenharmony_ci	    	goto value;
794c72fcc34Sopenharmony_ci	}
795c72fcc34Sopenharmony_ci	if (strncasecmp(attr, "subdev", 6) == 0) {
796c72fcc34Sopenharmony_ci		fcn = snd_ctl_elem_id_set_subdevice;
797c72fcc34Sopenharmony_ci	    	goto value;
798c72fcc34Sopenharmony_ci	}
799c72fcc34Sopenharmony_ci	if (strncasecmp(attr, "name", 4) == 0) {
800c72fcc34Sopenharmony_ci		snd_ctl_elem_id_set_name(space->ctl_id, value);
801c72fcc34Sopenharmony_ci	  	space->ctl_id_changed = ~0;
802c72fcc34Sopenharmony_ci		return 0;
803c72fcc34Sopenharmony_ci	}
804c72fcc34Sopenharmony_ci	if (strncasecmp(attr, "index", 5) == 0) {
805c72fcc34Sopenharmony_ci		fcn = snd_ctl_elem_id_set_index;
806c72fcc34Sopenharmony_ci	    	goto value;
807c72fcc34Sopenharmony_ci	}
808c72fcc34Sopenharmony_ci	if (strncasecmp(attr, "values", 6) == 0 ||
809c72fcc34Sopenharmony_ci	    strncasecmp(attr, "value", 5) == 0) {
810c72fcc34Sopenharmony_ci		err = check_id_changed(space, 1);
811c72fcc34Sopenharmony_ci		if (err < 0) {
812c72fcc34Sopenharmony_ci			Perror(space, "control element not found");
813c72fcc34Sopenharmony_ci			return err;
814c72fcc34Sopenharmony_ci		}
815c72fcc34Sopenharmony_ci		err = set_ctl_value(space, value, strncasecmp(attr, "values", 6) == 0);
816c72fcc34Sopenharmony_ci		if (err < 0) {
817c72fcc34Sopenharmony_ci			space->ctl_id_changed |= 2;
818c72fcc34Sopenharmony_ci		} else {
819c72fcc34Sopenharmony_ci			space->ctl_id_changed &= ~2;
820c72fcc34Sopenharmony_ci			snd_ctl_elem_value_set_id(space->ctl_value, space->ctl_id);
821c72fcc34Sopenharmony_ci			err = snd_ctl_elem_write(snd_hctl_ctl(space->ctl_handle), space->ctl_value);
822c72fcc34Sopenharmony_ci			if (err < 0) {
823c72fcc34Sopenharmony_ci				Perror(space, "value write error: %s", snd_strerror(err));
824c72fcc34Sopenharmony_ci				return err;
825c72fcc34Sopenharmony_ci			}
826c72fcc34Sopenharmony_ci		}
827c72fcc34Sopenharmony_ci	    	return err;
828c72fcc34Sopenharmony_ci	}
829c72fcc34Sopenharmony_ci	Perror(space, "unknown CTL{} attribute '%s'", attr);
830c72fcc34Sopenharmony_ci	return -EINVAL;
831c72fcc34Sopenharmony_ci  value:
832c72fcc34Sopenharmony_ci  	val = (unsigned int)strtol(value, NULL, 0);
833c72fcc34Sopenharmony_ci  	fcn(space->ctl_id, val);
834c72fcc34Sopenharmony_ci  	space->ctl_id_changed = ~0;
835c72fcc34Sopenharmony_ci  	return 0;
836c72fcc34Sopenharmony_ci}
837c72fcc34Sopenharmony_ci
838c72fcc34Sopenharmony_cistatic int get_key(char **line, char **key, enum key_op *op, char **value)
839c72fcc34Sopenharmony_ci{
840c72fcc34Sopenharmony_ci	char *linepos;
841c72fcc34Sopenharmony_ci	char *temp;
842c72fcc34Sopenharmony_ci
843c72fcc34Sopenharmony_ci	linepos = *line;
844c72fcc34Sopenharmony_ci	if (linepos == NULL && linepos[0] == '\0')
845c72fcc34Sopenharmony_ci		return -EINVAL;
846c72fcc34Sopenharmony_ci
847c72fcc34Sopenharmony_ci	/* skip whitespace */
848c72fcc34Sopenharmony_ci	while (isspace(linepos[0]) || linepos[0] == ',')
849c72fcc34Sopenharmony_ci		linepos++;
850c72fcc34Sopenharmony_ci
851c72fcc34Sopenharmony_ci	/* get the key */
852c72fcc34Sopenharmony_ci	if (linepos[0] == '\0')
853c72fcc34Sopenharmony_ci		return -EINVAL;
854c72fcc34Sopenharmony_ci	*key = linepos;
855c72fcc34Sopenharmony_ci
856c72fcc34Sopenharmony_ci	while (1) {
857c72fcc34Sopenharmony_ci		linepos++;
858c72fcc34Sopenharmony_ci		if (linepos[0] == '\0')
859c72fcc34Sopenharmony_ci			return -1;
860c72fcc34Sopenharmony_ci		if (isspace(linepos[0]))
861c72fcc34Sopenharmony_ci			break;
862c72fcc34Sopenharmony_ci		if (linepos[0] == '=')
863c72fcc34Sopenharmony_ci			break;
864c72fcc34Sopenharmony_ci		if (linepos[0] == '+')
865c72fcc34Sopenharmony_ci			break;
866c72fcc34Sopenharmony_ci		if (linepos[0] == '!')
867c72fcc34Sopenharmony_ci			break;
868c72fcc34Sopenharmony_ci		if (linepos[0] == ':')
869c72fcc34Sopenharmony_ci			break;
870c72fcc34Sopenharmony_ci	}
871c72fcc34Sopenharmony_ci
872c72fcc34Sopenharmony_ci	/* remember end of key */
873c72fcc34Sopenharmony_ci	temp = linepos;
874c72fcc34Sopenharmony_ci
875c72fcc34Sopenharmony_ci	/* skip whitespace after key */
876c72fcc34Sopenharmony_ci	while (isspace(linepos[0]))
877c72fcc34Sopenharmony_ci		linepos++;
878c72fcc34Sopenharmony_ci	if (linepos[0] == '\0')
879c72fcc34Sopenharmony_ci		return -EINVAL;
880c72fcc34Sopenharmony_ci
881c72fcc34Sopenharmony_ci	/* get operation type */
882c72fcc34Sopenharmony_ci	if (linepos[0] == '=' && linepos[1] == '=') {
883c72fcc34Sopenharmony_ci		*op = KEY_OP_MATCH;
884c72fcc34Sopenharmony_ci		linepos += 2;
885c72fcc34Sopenharmony_ci		dbg("operator=match");
886c72fcc34Sopenharmony_ci	} else if (linepos[0] == '!' && linepos[1] == '=') {
887c72fcc34Sopenharmony_ci		*op = KEY_OP_NOMATCH;
888c72fcc34Sopenharmony_ci		linepos += 2;
889c72fcc34Sopenharmony_ci		dbg("operator=nomatch");
890c72fcc34Sopenharmony_ci	} else if (linepos[0] == '+' && linepos[1] == '=') {
891c72fcc34Sopenharmony_ci		*op = KEY_OP_ADD;
892c72fcc34Sopenharmony_ci		linepos += 2;
893c72fcc34Sopenharmony_ci		dbg("operator=add");
894c72fcc34Sopenharmony_ci	} else if (linepos[0] == '=') {
895c72fcc34Sopenharmony_ci		*op = KEY_OP_ASSIGN;
896c72fcc34Sopenharmony_ci		linepos++;
897c72fcc34Sopenharmony_ci		dbg("operator=assign");
898c72fcc34Sopenharmony_ci	} else if (linepos[0] == ':' && linepos[1] == '=') {
899c72fcc34Sopenharmony_ci		*op = KEY_OP_ASSIGN_FINAL;
900c72fcc34Sopenharmony_ci		linepos += 2;
901c72fcc34Sopenharmony_ci		dbg("operator=assign_final");
902c72fcc34Sopenharmony_ci	} else
903c72fcc34Sopenharmony_ci		return -EINVAL;
904c72fcc34Sopenharmony_ci
905c72fcc34Sopenharmony_ci	/* terminate key */
906c72fcc34Sopenharmony_ci	temp[0] = '\0';
907c72fcc34Sopenharmony_ci	dbg("key='%s'", *key);
908c72fcc34Sopenharmony_ci
909c72fcc34Sopenharmony_ci	/* skip whitespace after operator */
910c72fcc34Sopenharmony_ci	while (isspace(linepos[0]))
911c72fcc34Sopenharmony_ci		linepos++;
912c72fcc34Sopenharmony_ci	if (linepos[0] == '\0')
913c72fcc34Sopenharmony_ci		return -EINVAL;
914c72fcc34Sopenharmony_ci
915c72fcc34Sopenharmony_ci	/* get the value*/
916c72fcc34Sopenharmony_ci	if (linepos[0] != '"')
917c72fcc34Sopenharmony_ci		return -EINVAL;
918c72fcc34Sopenharmony_ci	linepos++;
919c72fcc34Sopenharmony_ci	*value = linepos;
920c72fcc34Sopenharmony_ci
921c72fcc34Sopenharmony_ci	while (1) {
922c72fcc34Sopenharmony_ci		temp = strchr(linepos, '"');
923c72fcc34Sopenharmony_ci		if (temp && temp[-1] == '\\') {
924c72fcc34Sopenharmony_ci			linepos = temp + 1;
925c72fcc34Sopenharmony_ci			continue;
926c72fcc34Sopenharmony_ci		}
927c72fcc34Sopenharmony_ci		break;
928c72fcc34Sopenharmony_ci	}
929c72fcc34Sopenharmony_ci	if (!temp)
930c72fcc34Sopenharmony_ci		return -EINVAL;
931c72fcc34Sopenharmony_ci	temp[0] = '\0';
932c72fcc34Sopenharmony_ci	temp++;
933c72fcc34Sopenharmony_ci	dbg("value='%s'", *value);
934c72fcc34Sopenharmony_ci
935c72fcc34Sopenharmony_ci	/* move line to next key */
936c72fcc34Sopenharmony_ci	*line = temp;
937c72fcc34Sopenharmony_ci
938c72fcc34Sopenharmony_ci	return 0;
939c72fcc34Sopenharmony_ci}
940c72fcc34Sopenharmony_ci
941c72fcc34Sopenharmony_ci/* extract possible KEY{attr} */
942c72fcc34Sopenharmony_cistatic char *get_key_attribute(struct space *space, char *str, char *res, size_t ressize)
943c72fcc34Sopenharmony_ci{
944c72fcc34Sopenharmony_ci	char *pos;
945c72fcc34Sopenharmony_ci	char *attr;
946c72fcc34Sopenharmony_ci
947c72fcc34Sopenharmony_ci	attr = strchr(str, '{');
948c72fcc34Sopenharmony_ci	if (attr != NULL) {
949c72fcc34Sopenharmony_ci		attr++;
950c72fcc34Sopenharmony_ci		pos = strchr(attr, '}');
951c72fcc34Sopenharmony_ci		if (pos == NULL) {
952c72fcc34Sopenharmony_ci			Perror(space, "missing closing brace for format");
953c72fcc34Sopenharmony_ci			return NULL;
954c72fcc34Sopenharmony_ci		}
955c72fcc34Sopenharmony_ci		pos[0] = '\0';
956c72fcc34Sopenharmony_ci		strlcpy(res, attr, ressize);
957c72fcc34Sopenharmony_ci		pos[0] = '}';
958c72fcc34Sopenharmony_ci		dbg("attribute='%s'", res);
959c72fcc34Sopenharmony_ci		return res;
960c72fcc34Sopenharmony_ci	}
961c72fcc34Sopenharmony_ci
962c72fcc34Sopenharmony_ci	return NULL;
963c72fcc34Sopenharmony_ci}
964c72fcc34Sopenharmony_ci
965c72fcc34Sopenharmony_ci/* extract possible {attr} and move str behind it */
966c72fcc34Sopenharmony_cistatic char *get_format_attribute(struct space *space, char **str)
967c72fcc34Sopenharmony_ci{
968c72fcc34Sopenharmony_ci	char *pos;
969c72fcc34Sopenharmony_ci	char *attr = NULL;
970c72fcc34Sopenharmony_ci
971c72fcc34Sopenharmony_ci	if (*str[0] == '{') {
972c72fcc34Sopenharmony_ci		pos = strchr(*str, '}');
973c72fcc34Sopenharmony_ci		if (pos == NULL) {
974c72fcc34Sopenharmony_ci			Perror(space, "missing closing brace for format");
975c72fcc34Sopenharmony_ci			return NULL;
976c72fcc34Sopenharmony_ci		}
977c72fcc34Sopenharmony_ci		pos[0] = '\0';
978c72fcc34Sopenharmony_ci		attr = *str+1;
979c72fcc34Sopenharmony_ci		*str = pos+1;
980c72fcc34Sopenharmony_ci		dbg("attribute='%s', str='%s'", attr, *str);
981c72fcc34Sopenharmony_ci	}
982c72fcc34Sopenharmony_ci	return attr;
983c72fcc34Sopenharmony_ci}
984c72fcc34Sopenharmony_ci
985c72fcc34Sopenharmony_ci/* extract possible format length and move str behind it*/
986c72fcc34Sopenharmony_cistatic int get_format_len(struct space *space, char **str)
987c72fcc34Sopenharmony_ci{
988c72fcc34Sopenharmony_ci	int num;
989c72fcc34Sopenharmony_ci	char *tail;
990c72fcc34Sopenharmony_ci
991c72fcc34Sopenharmony_ci	if (isdigit(*str[0])) {
992c72fcc34Sopenharmony_ci		num = (int) strtoul(*str, &tail, 10);
993c72fcc34Sopenharmony_ci		if (num > 0) {
994c72fcc34Sopenharmony_ci			*str = tail;
995c72fcc34Sopenharmony_ci			dbg("format length=%i", num);
996c72fcc34Sopenharmony_ci			return num;
997c72fcc34Sopenharmony_ci		} else {
998c72fcc34Sopenharmony_ci			Perror(space, "format parsing error '%s'", *str);
999c72fcc34Sopenharmony_ci		}
1000c72fcc34Sopenharmony_ci	}
1001c72fcc34Sopenharmony_ci	return -1;
1002c72fcc34Sopenharmony_ci}
1003c72fcc34Sopenharmony_ci
1004c72fcc34Sopenharmony_cistatic void apply_format(struct space *space, char *string, size_t maxsize)
1005c72fcc34Sopenharmony_ci{
1006c72fcc34Sopenharmony_ci	char temp[PATH_SIZE];
1007c72fcc34Sopenharmony_ci	char temp2[PATH_SIZE];
1008c72fcc34Sopenharmony_ci	char *head, *tail, *pos, *cpos, *attr, *rest;
1009c72fcc34Sopenharmony_ci	struct pair *pair;
1010c72fcc34Sopenharmony_ci	int len;
1011c72fcc34Sopenharmony_ci	int i;
1012c72fcc34Sopenharmony_ci	int count;
1013c72fcc34Sopenharmony_ci	enum subst_type {
1014c72fcc34Sopenharmony_ci		SUBST_UNKNOWN,
1015c72fcc34Sopenharmony_ci		SUBST_CARDINFO,
1016c72fcc34Sopenharmony_ci		SUBST_CTL,
1017c72fcc34Sopenharmony_ci		SUBST_RESULT,
1018c72fcc34Sopenharmony_ci		SUBST_ATTR,
1019c72fcc34Sopenharmony_ci		SUBST_SYSFSROOT,
1020c72fcc34Sopenharmony_ci		SUBST_ENV,
1021c72fcc34Sopenharmony_ci		SUBST_CONFIG,
1022c72fcc34Sopenharmony_ci	};
1023c72fcc34Sopenharmony_ci	static const struct subst_map {
1024c72fcc34Sopenharmony_ci		char *name;
1025c72fcc34Sopenharmony_ci		char fmt;
1026c72fcc34Sopenharmony_ci		enum subst_type type;
1027c72fcc34Sopenharmony_ci	} map[] = {
1028c72fcc34Sopenharmony_ci		{ .name = "cardinfo",	.fmt = 'i',	.type = SUBST_CARDINFO },
1029c72fcc34Sopenharmony_ci		{ .name = "ctl",	.fmt = 'C',	.type = SUBST_CTL },
1030c72fcc34Sopenharmony_ci		{ .name = "result",	.fmt = 'c',	.type = SUBST_RESULT },
1031c72fcc34Sopenharmony_ci		{ .name = "attr",	.fmt = 's',	.type = SUBST_ATTR },
1032c72fcc34Sopenharmony_ci		{ .name = "sysfsroot",	.fmt = 'r',	.type = SUBST_SYSFSROOT },
1033c72fcc34Sopenharmony_ci		{ .name = "env",	.fmt = 'E',	.type = SUBST_ENV },
1034c72fcc34Sopenharmony_ci		{ .name = "config",	.fmt = 'g',	.type = SUBST_CONFIG },
1035c72fcc34Sopenharmony_ci		{ NULL, '\0', 0 }
1036c72fcc34Sopenharmony_ci	};
1037c72fcc34Sopenharmony_ci	enum subst_type type;
1038c72fcc34Sopenharmony_ci	const struct subst_map *subst;
1039c72fcc34Sopenharmony_ci
1040c72fcc34Sopenharmony_ci	head = string;
1041c72fcc34Sopenharmony_ci	while (1) {
1042c72fcc34Sopenharmony_ci		len = -1;
1043c72fcc34Sopenharmony_ci		while (head[0] != '\0') {
1044c72fcc34Sopenharmony_ci			if (head[0] == '$') {
1045c72fcc34Sopenharmony_ci				/* substitute named variable */
1046c72fcc34Sopenharmony_ci				if (head[1] == '\0')
1047c72fcc34Sopenharmony_ci					break;
1048c72fcc34Sopenharmony_ci				if (head[1] == '$') {
1049c72fcc34Sopenharmony_ci					strlcpy(temp, head+2, sizeof(temp));
1050c72fcc34Sopenharmony_ci					strlcpy(head+1, temp, maxsize);
1051c72fcc34Sopenharmony_ci					head++;
1052c72fcc34Sopenharmony_ci					continue;
1053c72fcc34Sopenharmony_ci				}
1054c72fcc34Sopenharmony_ci				head[0] = '\0';
1055c72fcc34Sopenharmony_ci				for (subst = map; subst->name; subst++) {
1056c72fcc34Sopenharmony_ci					if (strncasecmp(&head[1], subst->name, strlen(subst->name)) == 0) {
1057c72fcc34Sopenharmony_ci						type = subst->type;
1058c72fcc34Sopenharmony_ci						tail = head + strlen(subst->name)+1;
1059c72fcc34Sopenharmony_ci						dbg("will substitute format name '%s'", subst->name);
1060c72fcc34Sopenharmony_ci						goto found;
1061c72fcc34Sopenharmony_ci					}
1062c72fcc34Sopenharmony_ci				}
1063c72fcc34Sopenharmony_ci			} else if (head[0] == '%') {
1064c72fcc34Sopenharmony_ci				/* substitute format char */
1065c72fcc34Sopenharmony_ci				if (head[1] == '\0')
1066c72fcc34Sopenharmony_ci					break;
1067c72fcc34Sopenharmony_ci				if (head[1] == '%') {
1068c72fcc34Sopenharmony_ci					strlcpy(temp, head+2, sizeof(temp));
1069c72fcc34Sopenharmony_ci					strlcpy(head+1, temp, maxsize);
1070c72fcc34Sopenharmony_ci					head++;
1071c72fcc34Sopenharmony_ci					continue;
1072c72fcc34Sopenharmony_ci				}
1073c72fcc34Sopenharmony_ci				head[0] = '\0';
1074c72fcc34Sopenharmony_ci				tail = head+1;
1075c72fcc34Sopenharmony_ci				len = get_format_len(space, &tail);
1076c72fcc34Sopenharmony_ci				for (subst = map; subst->name; subst++) {
1077c72fcc34Sopenharmony_ci					if (tail[0] == subst->fmt) {
1078c72fcc34Sopenharmony_ci						type = subst->type;
1079c72fcc34Sopenharmony_ci						tail++;
1080c72fcc34Sopenharmony_ci						dbg("will substitute format char '%c'", subst->fmt);
1081c72fcc34Sopenharmony_ci						goto found;
1082c72fcc34Sopenharmony_ci					}
1083c72fcc34Sopenharmony_ci				}
1084c72fcc34Sopenharmony_ci			}
1085c72fcc34Sopenharmony_ci			head++;
1086c72fcc34Sopenharmony_ci		}
1087c72fcc34Sopenharmony_ci		break;
1088c72fcc34Sopenharmony_cifound:
1089c72fcc34Sopenharmony_ci		attr = get_format_attribute(space, &tail);
1090c72fcc34Sopenharmony_ci		strlcpy(temp, tail, sizeof(temp));
1091c72fcc34Sopenharmony_ci		dbg("format=%i, string='%s', tail='%s'", type ,string, tail);
1092c72fcc34Sopenharmony_ci
1093c72fcc34Sopenharmony_ci		switch (type) {
1094c72fcc34Sopenharmony_ci		case SUBST_CARDINFO:
1095c72fcc34Sopenharmony_ci			if (attr == NULL)
1096c72fcc34Sopenharmony_ci				Perror(space, "missing identification parametr for cardinfo");
1097c72fcc34Sopenharmony_ci			else {
1098c72fcc34Sopenharmony_ci				const char *value = cardinfo_get(space, attr);
1099c72fcc34Sopenharmony_ci				if (value == NULL)
1100c72fcc34Sopenharmony_ci					break;
1101c72fcc34Sopenharmony_ci				strlcat(string, value, maxsize);
1102c72fcc34Sopenharmony_ci				dbg("substitute cardinfo{%s} '%s'", attr, value);
1103c72fcc34Sopenharmony_ci			}
1104c72fcc34Sopenharmony_ci			break;
1105c72fcc34Sopenharmony_ci		case SUBST_CTL:
1106c72fcc34Sopenharmony_ci			if (attr == NULL)
1107c72fcc34Sopenharmony_ci				Perror(space, "missing identification parametr for ctl");
1108c72fcc34Sopenharmony_ci			else {
1109c72fcc34Sopenharmony_ci				const char *value = elemid_get(space, attr);
1110c72fcc34Sopenharmony_ci				if (value == NULL)
1111c72fcc34Sopenharmony_ci					break;
1112c72fcc34Sopenharmony_ci				strlcat(string, value, maxsize);
1113c72fcc34Sopenharmony_ci				dbg("substitute ctl{%s} '%s'", attr, value);
1114c72fcc34Sopenharmony_ci			}
1115c72fcc34Sopenharmony_ci			break;
1116c72fcc34Sopenharmony_ci		case SUBST_RESULT:
1117c72fcc34Sopenharmony_ci			if (space->program_result == NULL)
1118c72fcc34Sopenharmony_ci				break;
1119c72fcc34Sopenharmony_ci			/* get part part of the result string */
1120c72fcc34Sopenharmony_ci			i = 0;
1121c72fcc34Sopenharmony_ci			if (attr != NULL)
1122c72fcc34Sopenharmony_ci				i = strtoul(attr, &rest, 10);
1123c72fcc34Sopenharmony_ci			if (i > 0) {
1124c72fcc34Sopenharmony_ci				dbg("request part #%d of result string", i);
1125c72fcc34Sopenharmony_ci				cpos = space->program_result;
1126c72fcc34Sopenharmony_ci				while (--i) {
1127c72fcc34Sopenharmony_ci					while (cpos[0] != '\0' && !isspace(cpos[0]))
1128c72fcc34Sopenharmony_ci						cpos++;
1129c72fcc34Sopenharmony_ci					while (isspace(cpos[0]))
1130c72fcc34Sopenharmony_ci						cpos++;
1131c72fcc34Sopenharmony_ci				}
1132c72fcc34Sopenharmony_ci				if (i > 0) {
1133c72fcc34Sopenharmony_ci					Perror(space, "requested part of result string not found");
1134c72fcc34Sopenharmony_ci					break;
1135c72fcc34Sopenharmony_ci				}
1136c72fcc34Sopenharmony_ci				strlcpy(temp2, cpos, sizeof(temp2));
1137c72fcc34Sopenharmony_ci				/* %{2+}c copies the whole string from the second part on */
1138c72fcc34Sopenharmony_ci				if (rest[0] != '+') {
1139c72fcc34Sopenharmony_ci					cpos = strchr(temp2, ' ');
1140c72fcc34Sopenharmony_ci					if (cpos)
1141c72fcc34Sopenharmony_ci						cpos[0] = '\0';
1142c72fcc34Sopenharmony_ci				}
1143c72fcc34Sopenharmony_ci				strlcat(string, temp2, maxsize);
1144c72fcc34Sopenharmony_ci				dbg("substitute part of result string '%s'", temp2);
1145c72fcc34Sopenharmony_ci			} else {
1146c72fcc34Sopenharmony_ci				strlcat(string, space->program_result, maxsize);
1147c72fcc34Sopenharmony_ci				dbg("substitute result string '%s'", space->program_result);
1148c72fcc34Sopenharmony_ci			}
1149c72fcc34Sopenharmony_ci			break;
1150c72fcc34Sopenharmony_ci		case SUBST_ATTR:
1151c72fcc34Sopenharmony_ci			if (attr == NULL)
1152c72fcc34Sopenharmony_ci				Perror(space, "missing file parameter for attr");
1153c72fcc34Sopenharmony_ci			else {
1154c72fcc34Sopenharmony_ci				const char *value = NULL;
1155c72fcc34Sopenharmony_ci				size_t size;
1156c72fcc34Sopenharmony_ci
1157c72fcc34Sopenharmony_ci				pair = value_find(space, "sysfs_device");
1158c72fcc34Sopenharmony_ci				if (pair == NULL)
1159c72fcc34Sopenharmony_ci					break;
1160c72fcc34Sopenharmony_ci				value = sysfs_attr_get_value(pair->value, attr);
1161c72fcc34Sopenharmony_ci
1162c72fcc34Sopenharmony_ci				if (value == NULL)
1163c72fcc34Sopenharmony_ci					break;
1164c72fcc34Sopenharmony_ci
1165c72fcc34Sopenharmony_ci				/* strip trailing whitespace and replace untrusted characters of sysfs value */
1166c72fcc34Sopenharmony_ci				size = strlcpy(temp2, value, sizeof(temp2));
1167c72fcc34Sopenharmony_ci				if (size >= sizeof(temp2))
1168c72fcc34Sopenharmony_ci					size = sizeof(temp2)-1;
1169c72fcc34Sopenharmony_ci				while (size > 0 && isspace(temp2[size-1]))
1170c72fcc34Sopenharmony_ci					temp2[--size] = '\0';
1171c72fcc34Sopenharmony_ci				count = replace_untrusted_chars(temp2);
1172c72fcc34Sopenharmony_ci				if (count > 0)
1173c72fcc34Sopenharmony_ci					Perror(space, "%i untrusted character(s) replaced" , count);
1174c72fcc34Sopenharmony_ci				strlcat(string, temp2, maxsize);
1175c72fcc34Sopenharmony_ci				dbg("substitute sysfs value '%s'", temp2);
1176c72fcc34Sopenharmony_ci			}
1177c72fcc34Sopenharmony_ci			break;
1178c72fcc34Sopenharmony_ci		case SUBST_SYSFSROOT:
1179c72fcc34Sopenharmony_ci			strlcat(string, sysfs_path, maxsize);
1180c72fcc34Sopenharmony_ci			dbg("substitute sysfs_path '%s'", sysfs_path);
1181c72fcc34Sopenharmony_ci			break;
1182c72fcc34Sopenharmony_ci		case SUBST_ENV:
1183c72fcc34Sopenharmony_ci			if (attr == NULL) {
1184c72fcc34Sopenharmony_ci				dbg("missing attribute");
1185c72fcc34Sopenharmony_ci				break;
1186c72fcc34Sopenharmony_ci			}
1187c72fcc34Sopenharmony_ci			pos = getenv(attr);
1188c72fcc34Sopenharmony_ci			if (pos == NULL) {
1189c72fcc34Sopenharmony_ci				dbg("env '%s' not available", attr);
1190c72fcc34Sopenharmony_ci				break;
1191c72fcc34Sopenharmony_ci			}
1192c72fcc34Sopenharmony_ci			dbg("substitute env '%s=%s'", attr, pos);
1193c72fcc34Sopenharmony_ci			strlcat(string, pos, maxsize);
1194c72fcc34Sopenharmony_ci			break;
1195c72fcc34Sopenharmony_ci		case SUBST_CONFIG:
1196c72fcc34Sopenharmony_ci			if (attr == NULL) {
1197c72fcc34Sopenharmony_ci				dbg("missing attribute");
1198c72fcc34Sopenharmony_ci				break;
1199c72fcc34Sopenharmony_ci			}
1200c72fcc34Sopenharmony_ci			pair = value_find(space, attr);
1201c72fcc34Sopenharmony_ci			if (pair == NULL)
1202c72fcc34Sopenharmony_ci				break;
1203c72fcc34Sopenharmony_ci			strlcat(string, pair->value, maxsize);
1204c72fcc34Sopenharmony_ci			break;
1205c72fcc34Sopenharmony_ci		default:
1206c72fcc34Sopenharmony_ci			Perror(space, "unknown substitution type=%i", type);
1207c72fcc34Sopenharmony_ci			break;
1208c72fcc34Sopenharmony_ci		}
1209c72fcc34Sopenharmony_ci		/* possibly truncate to format-char specified length */
1210c72fcc34Sopenharmony_ci		if (len != -1) {
1211c72fcc34Sopenharmony_ci			head[len] = '\0';
1212c72fcc34Sopenharmony_ci			dbg("truncate to %i chars, subtitution string becomes '%s'", len, head);
1213c72fcc34Sopenharmony_ci		}
1214c72fcc34Sopenharmony_ci		strlcat(string, temp, maxsize);
1215c72fcc34Sopenharmony_ci	}
1216c72fcc34Sopenharmony_ci	/* unescape strings */
1217c72fcc34Sopenharmony_ci	head = tail = string;
1218c72fcc34Sopenharmony_ci	while (*head != '\0') {
1219c72fcc34Sopenharmony_ci		if (*head == '\\') {
1220c72fcc34Sopenharmony_ci			head++;
1221c72fcc34Sopenharmony_ci			if (*head == '\0')
1222c72fcc34Sopenharmony_ci				break;
1223c72fcc34Sopenharmony_ci			switch (*head) {
1224c72fcc34Sopenharmony_ci			case 'a': *tail++ = '\a'; break;
1225c72fcc34Sopenharmony_ci			case 'b': *tail++ = '\b'; break;
1226c72fcc34Sopenharmony_ci			case 'n': *tail++ = '\n'; break;
1227c72fcc34Sopenharmony_ci			case 'r': *tail++ = '\r'; break;
1228c72fcc34Sopenharmony_ci			case 't': *tail++ = '\t'; break;
1229c72fcc34Sopenharmony_ci			case 'v': *tail++ = '\v'; break;
1230c72fcc34Sopenharmony_ci			case '\\': *tail++ = '\\'; break;
1231c72fcc34Sopenharmony_ci			default: *tail++ = *head; break;
1232c72fcc34Sopenharmony_ci			}
1233c72fcc34Sopenharmony_ci			head++;
1234c72fcc34Sopenharmony_ci			continue;
1235c72fcc34Sopenharmony_ci		}
1236c72fcc34Sopenharmony_ci		if (*head)
1237c72fcc34Sopenharmony_ci			*tail++ = *head++;
1238c72fcc34Sopenharmony_ci	}
1239c72fcc34Sopenharmony_ci	*tail = 0;
1240c72fcc34Sopenharmony_ci}
1241c72fcc34Sopenharmony_ci
1242c72fcc34Sopenharmony_cistatic
1243c72fcc34Sopenharmony_ciint run_program1(struct space *space,
1244c72fcc34Sopenharmony_ci		 const char *command0, char *result,
1245c72fcc34Sopenharmony_ci		 size_t ressize, size_t *reslen ATTRIBUTE_UNUSED,
1246c72fcc34Sopenharmony_ci		 int log ATTRIBUTE_UNUSED)
1247c72fcc34Sopenharmony_ci{
1248c72fcc34Sopenharmony_ci	if (strncmp(command0, "__ctl_search", 12) == 0) {
1249c72fcc34Sopenharmony_ci		const char *res = elemid_get(space, "do_search");
1250c72fcc34Sopenharmony_ci		if (res == NULL || strcmp(res, "1") != 0)
1251c72fcc34Sopenharmony_ci			return EXIT_FAILURE;
1252c72fcc34Sopenharmony_ci		return EXIT_SUCCESS;
1253c72fcc34Sopenharmony_ci	}
1254c72fcc34Sopenharmony_ci	if (strncmp(command0, "__ctl_count", 11) == 0) {
1255c72fcc34Sopenharmony_ci		const char *res = elemid_get(space, "do_count");
1256c72fcc34Sopenharmony_ci		if (res == NULL || strcmp(res, "0") == 0)
1257c72fcc34Sopenharmony_ci			return EXIT_FAILURE;
1258c72fcc34Sopenharmony_ci		strlcpy(result, res, ressize);
1259c72fcc34Sopenharmony_ci		return EXIT_SUCCESS;
1260c72fcc34Sopenharmony_ci	}
1261c72fcc34Sopenharmony_ci	Perror(space, "unknown buildin command '%s'", command0);
1262c72fcc34Sopenharmony_ci	return EXIT_FAILURE;
1263c72fcc34Sopenharmony_ci}
1264c72fcc34Sopenharmony_ci
1265c72fcc34Sopenharmony_cistatic int parse(struct space *space, const char *filename);
1266c72fcc34Sopenharmony_ci
1267c72fcc34Sopenharmony_cistatic char *new_root_dir(const char *filename)
1268c72fcc34Sopenharmony_ci{
1269c72fcc34Sopenharmony_ci	char *res, *tmp;
1270c72fcc34Sopenharmony_ci
1271c72fcc34Sopenharmony_ci	res = strdup(filename);
1272c72fcc34Sopenharmony_ci	if (res) {
1273c72fcc34Sopenharmony_ci		tmp = strrchr(res, '/');
1274c72fcc34Sopenharmony_ci		if (tmp)
1275c72fcc34Sopenharmony_ci			*tmp = '\0';
1276c72fcc34Sopenharmony_ci	}
1277c72fcc34Sopenharmony_ci	dbg("new_root_dir '%s' '%s'", filename, res);
1278c72fcc34Sopenharmony_ci	return res;
1279c72fcc34Sopenharmony_ci}
1280c72fcc34Sopenharmony_ci
1281c72fcc34Sopenharmony_ci/* return non-zero if the file name has the extension ".conf" */
1282c72fcc34Sopenharmony_cistatic int conf_name_filter(const struct dirent *d)
1283c72fcc34Sopenharmony_ci{
1284c72fcc34Sopenharmony_ci	char *ext = strrchr(d->d_name, '.');
1285c72fcc34Sopenharmony_ci	return ext && !strcmp(ext, ".conf");
1286c72fcc34Sopenharmony_ci}
1287c72fcc34Sopenharmony_ci
1288c72fcc34Sopenharmony_cistatic int parse_line(struct space *space, char *line, size_t linesize ATTRIBUTE_UNUSED)
1289c72fcc34Sopenharmony_ci{
1290c72fcc34Sopenharmony_ci	char *linepos;
1291c72fcc34Sopenharmony_ci	char *key, *value, *attr, *temp;
1292c72fcc34Sopenharmony_ci	struct pair *pair;
1293c72fcc34Sopenharmony_ci	enum key_op op;
1294c72fcc34Sopenharmony_ci	int err = 0, count;
1295c72fcc34Sopenharmony_ci	char string[PATH_SIZE];
1296c72fcc34Sopenharmony_ci	char result[PATH_SIZE];
1297c72fcc34Sopenharmony_ci
1298c72fcc34Sopenharmony_ci	linepos = line;
1299c72fcc34Sopenharmony_ci	while (*linepos != '\0') {
1300c72fcc34Sopenharmony_ci		op = KEY_OP_UNSET;
1301c72fcc34Sopenharmony_ci
1302c72fcc34Sopenharmony_ci		err = get_key(&linepos, &key, &op, &value);
1303c72fcc34Sopenharmony_ci		if (err < 0)
1304c72fcc34Sopenharmony_ci			goto invalid;
1305c72fcc34Sopenharmony_ci
1306c72fcc34Sopenharmony_ci		if (strncasecmp(key, "LABEL", 5) == 0) {
1307c72fcc34Sopenharmony_ci			if (op != KEY_OP_ASSIGN) {
1308c72fcc34Sopenharmony_ci				Perror(space, "invalid LABEL operation");
1309c72fcc34Sopenharmony_ci				goto invalid;
1310c72fcc34Sopenharmony_ci			}
1311c72fcc34Sopenharmony_ci			if (space->go_to && strcmp(space->go_to, value) == 0) {
1312c72fcc34Sopenharmony_ci				free(space->go_to);
1313c72fcc34Sopenharmony_ci				space->go_to = NULL;
1314c72fcc34Sopenharmony_ci			}
1315c72fcc34Sopenharmony_ci			continue;
1316c72fcc34Sopenharmony_ci		}
1317c72fcc34Sopenharmony_ci
1318c72fcc34Sopenharmony_ci		if (space->go_to) {
1319c72fcc34Sopenharmony_ci			dbg("skip (GOTO '%s')", space->go_to);
1320c72fcc34Sopenharmony_ci			break;		/* not for us */
1321c72fcc34Sopenharmony_ci		}
1322c72fcc34Sopenharmony_ci
1323c72fcc34Sopenharmony_ci		if (strncasecmp(key, "CTL{", 4) == 0) {
1324c72fcc34Sopenharmony_ci			attr = get_key_attribute(space, key + 3, string, sizeof(string));
1325c72fcc34Sopenharmony_ci			if (attr == NULL) {
1326c72fcc34Sopenharmony_ci				Perror(space, "error parsing CTL attribute");
1327c72fcc34Sopenharmony_ci				goto invalid;
1328c72fcc34Sopenharmony_ci			}
1329c72fcc34Sopenharmony_ci			if (op == KEY_OP_ASSIGN) {
1330c72fcc34Sopenharmony_ci				strlcpy(result, value, sizeof(result));
1331c72fcc34Sopenharmony_ci				apply_format(space, result, sizeof(result));
1332c72fcc34Sopenharmony_ci				dbg("ctl assign: '%s' '%s'", value, attr);
1333c72fcc34Sopenharmony_ci				err = elemid_set(space, attr, result);
1334c72fcc34Sopenharmony_ci				if (space->program_result) {
1335c72fcc34Sopenharmony_ci					free(space->program_result);
1336c72fcc34Sopenharmony_ci					space->program_result = NULL;
1337c72fcc34Sopenharmony_ci				}
1338c72fcc34Sopenharmony_ci				snprintf(string, sizeof(string), "%i", err);
1339c72fcc34Sopenharmony_ci				space->program_result = strdup(string);
1340c72fcc34Sopenharmony_ci				err = 0;
1341c72fcc34Sopenharmony_ci				if (space->program_result == NULL)
1342c72fcc34Sopenharmony_ci					break;
1343c72fcc34Sopenharmony_ci			} else if (op == KEY_OP_MATCH || op == KEY_OP_NOMATCH) {
1344c72fcc34Sopenharmony_ci				if (strncmp(attr, "write", 5) == 0) {
1345c72fcc34Sopenharmony_ci					strlcpy(result, value, sizeof(result));
1346c72fcc34Sopenharmony_ci					apply_format(space, result, sizeof(result));
1347c72fcc34Sopenharmony_ci					dbg("ctl write: '%s' '%s'", value, attr);
1348c72fcc34Sopenharmony_ci					err = elemid_set(space, "values", result);
1349c72fcc34Sopenharmony_ci					if (err == 0 && op == KEY_OP_NOMATCH)
1350c72fcc34Sopenharmony_ci						break;
1351c72fcc34Sopenharmony_ci					if (err != 0 && op == KEY_OP_MATCH)
1352c72fcc34Sopenharmony_ci						break;
1353c72fcc34Sopenharmony_ci				} else {
1354c72fcc34Sopenharmony_ci					temp = (char *)elemid_get(space, attr);
1355c72fcc34Sopenharmony_ci					dbg("ctl match: '%s' '%s' '%s'", attr, value, temp);
1356c72fcc34Sopenharmony_ci					if (!do_match(key, op, value, temp))
1357c72fcc34Sopenharmony_ci						break;
1358c72fcc34Sopenharmony_ci				}
1359c72fcc34Sopenharmony_ci			} else {
1360c72fcc34Sopenharmony_ci				Perror(space, "invalid CTL{} operation");
1361c72fcc34Sopenharmony_ci				goto invalid;
1362c72fcc34Sopenharmony_ci			}
1363c72fcc34Sopenharmony_ci			continue;
1364c72fcc34Sopenharmony_ci		}
1365c72fcc34Sopenharmony_ci		if (strcasecmp(key, "RESULT") == 0) {
1366c72fcc34Sopenharmony_ci			if (op == KEY_OP_MATCH || op == KEY_OP_NOMATCH) {
1367c72fcc34Sopenharmony_ci				if (!do_match(key, op, value, space->program_result))
1368c72fcc34Sopenharmony_ci					break;
1369c72fcc34Sopenharmony_ci			} else if (op == KEY_OP_ASSIGN) {
1370c72fcc34Sopenharmony_ci				if (space->program_result) {
1371c72fcc34Sopenharmony_ci					free(space->program_result);
1372c72fcc34Sopenharmony_ci					space->program_result = NULL;
1373c72fcc34Sopenharmony_ci				}
1374c72fcc34Sopenharmony_ci				strlcpy(string, value, sizeof(string));
1375c72fcc34Sopenharmony_ci				apply_format(space, string, sizeof(string));
1376c72fcc34Sopenharmony_ci				space->program_result = strdup(string);
1377c72fcc34Sopenharmony_ci				if (space->program_result == NULL)
1378c72fcc34Sopenharmony_ci					break;
1379c72fcc34Sopenharmony_ci 			} else {
1380c72fcc34Sopenharmony_ci				Perror(space, "invalid RESULT operation");
1381c72fcc34Sopenharmony_ci				goto invalid;
1382c72fcc34Sopenharmony_ci			}
1383c72fcc34Sopenharmony_ci			continue;
1384c72fcc34Sopenharmony_ci		}
1385c72fcc34Sopenharmony_ci		if (strcasecmp(key, "PROGRAM") == 0) {
1386c72fcc34Sopenharmony_ci			if (op == KEY_OP_UNSET)
1387c72fcc34Sopenharmony_ci				continue;
1388c72fcc34Sopenharmony_ci			strlcpy(string, value, sizeof(string));
1389c72fcc34Sopenharmony_ci			apply_format(space, string, sizeof(string));
1390c72fcc34Sopenharmony_ci			if (space->program_result) {
1391c72fcc34Sopenharmony_ci				free(space->program_result);
1392c72fcc34Sopenharmony_ci				space->program_result = NULL;
1393c72fcc34Sopenharmony_ci			}
1394c72fcc34Sopenharmony_ci			if (run_program(space, string, result, sizeof(result), NULL, space->log_run) != 0) {
1395c72fcc34Sopenharmony_ci				dbg("PROGRAM '%s' is false", string);
1396c72fcc34Sopenharmony_ci				if (op != KEY_OP_NOMATCH)
1397c72fcc34Sopenharmony_ci					break;
1398c72fcc34Sopenharmony_ci			} else {
1399c72fcc34Sopenharmony_ci				remove_trailing_chars(result, '\n');
1400c72fcc34Sopenharmony_ci				count = replace_untrusted_chars(result);
1401c72fcc34Sopenharmony_ci				if (count)
1402c72fcc34Sopenharmony_ci					info("%i untrusted character(s) replaced", count);
1403c72fcc34Sopenharmony_ci				dbg("PROGRAM '%s' result is '%s'", string, result);
1404c72fcc34Sopenharmony_ci				space->program_result = strdup(result);
1405c72fcc34Sopenharmony_ci				if (space->program_result == NULL)
1406c72fcc34Sopenharmony_ci					break;
1407c72fcc34Sopenharmony_ci				dbg("PROGRAM returned successful");
1408c72fcc34Sopenharmony_ci				if (op == KEY_OP_NOMATCH)
1409c72fcc34Sopenharmony_ci					break;
1410c72fcc34Sopenharmony_ci			}
1411c72fcc34Sopenharmony_ci			dbg("PROGRAM key is true");
1412c72fcc34Sopenharmony_ci			continue;
1413c72fcc34Sopenharmony_ci		}
1414c72fcc34Sopenharmony_ci		if (strncasecmp(key, "CARDINFO{", 9) == 0) {
1415c72fcc34Sopenharmony_ci			attr = get_key_attribute(space, key + 8, string, sizeof(string));
1416c72fcc34Sopenharmony_ci			if (attr == NULL) {
1417c72fcc34Sopenharmony_ci				Perror(space, "error parsing CARDINFO attribute");
1418c72fcc34Sopenharmony_ci				goto invalid;
1419c72fcc34Sopenharmony_ci			}
1420c72fcc34Sopenharmony_ci			if (op == KEY_OP_MATCH || op == KEY_OP_NOMATCH) {
1421c72fcc34Sopenharmony_ci				dbg("cardinfo: '%s' '%s'", value, attr);
1422c72fcc34Sopenharmony_ci				temp = (char *)cardinfo_get(space, attr);
1423c72fcc34Sopenharmony_ci				if (!do_match(key, op, value, temp))
1424c72fcc34Sopenharmony_ci					break;
1425c72fcc34Sopenharmony_ci			} else {
1426c72fcc34Sopenharmony_ci				Perror(space, "invalid CARDINFO{} operation");
1427c72fcc34Sopenharmony_ci				goto invalid;
1428c72fcc34Sopenharmony_ci			}
1429c72fcc34Sopenharmony_ci			continue;
1430c72fcc34Sopenharmony_ci		}
1431c72fcc34Sopenharmony_ci		if (strncasecmp(key, "ATTR{", 5) == 0) {
1432c72fcc34Sopenharmony_ci			attr = get_key_attribute(space, key + 4, string, sizeof(string));
1433c72fcc34Sopenharmony_ci			if (attr == NULL) {
1434c72fcc34Sopenharmony_ci				Perror(space, "error parsing ATTR attribute");
1435c72fcc34Sopenharmony_ci				goto invalid;
1436c72fcc34Sopenharmony_ci			}
1437c72fcc34Sopenharmony_ci			if (op == KEY_OP_MATCH || op == KEY_OP_NOMATCH) {
1438c72fcc34Sopenharmony_ci				pair = value_find(space, "sysfs_device");
1439c72fcc34Sopenharmony_ci				if (pair == NULL)
1440c72fcc34Sopenharmony_ci					break;
1441c72fcc34Sopenharmony_ci				dbg("sysfs_attr: '%s' '%s'", pair->value, attr);
1442c72fcc34Sopenharmony_ci				temp = sysfs_attr_get_value(pair->value, attr);
1443c72fcc34Sopenharmony_ci				if (!do_match(key, op, value, temp))
1444c72fcc34Sopenharmony_ci					break;
1445c72fcc34Sopenharmony_ci			} else {
1446c72fcc34Sopenharmony_ci				Perror(space, "invalid ATTR{} operation");
1447c72fcc34Sopenharmony_ci				goto invalid;
1448c72fcc34Sopenharmony_ci			}
1449c72fcc34Sopenharmony_ci			continue;
1450c72fcc34Sopenharmony_ci		}
1451c72fcc34Sopenharmony_ci		if (strncasecmp(key, "ENV{", 4) == 0) {
1452c72fcc34Sopenharmony_ci			attr = get_key_attribute(space, key + 3, string, sizeof(string));
1453c72fcc34Sopenharmony_ci			if (attr == NULL) {
1454c72fcc34Sopenharmony_ci				Perror(space, "error parsing ENV attribute");
1455c72fcc34Sopenharmony_ci				goto invalid;
1456c72fcc34Sopenharmony_ci			}
1457c72fcc34Sopenharmony_ci			if (op == KEY_OP_MATCH || op == KEY_OP_NOMATCH) {
1458c72fcc34Sopenharmony_ci				temp = getenv(attr);
1459c72fcc34Sopenharmony_ci				dbg("env: '%s' '%s'", attr, temp);
1460c72fcc34Sopenharmony_ci				if (!do_match(key, op, value, temp))
1461c72fcc34Sopenharmony_ci					break;
1462c72fcc34Sopenharmony_ci			} else if (op == KEY_OP_ASSIGN ||
1463c72fcc34Sopenharmony_ci				   op == KEY_OP_ASSIGN_FINAL) {
1464c72fcc34Sopenharmony_ci				strlcpy(result, value, sizeof(result));
1465c72fcc34Sopenharmony_ci				apply_format(space, result, sizeof(result));
1466c72fcc34Sopenharmony_ci				dbg("env set: '%s' '%s'", attr, result);
1467c72fcc34Sopenharmony_ci				if (setenv(attr, result, op == KEY_OP_ASSIGN_FINAL))
1468c72fcc34Sopenharmony_ci					break;
1469c72fcc34Sopenharmony_ci			} else {
1470c72fcc34Sopenharmony_ci				Perror(space, "invalid ENV{} operation");
1471c72fcc34Sopenharmony_ci				goto invalid;
1472c72fcc34Sopenharmony_ci			}
1473c72fcc34Sopenharmony_ci			continue;
1474c72fcc34Sopenharmony_ci		}
1475c72fcc34Sopenharmony_ci		if (strcasecmp(key, "GOTO") == 0) {
1476c72fcc34Sopenharmony_ci			if (op != KEY_OP_ASSIGN) {
1477c72fcc34Sopenharmony_ci				Perror(space, "invalid GOTO operation");
1478c72fcc34Sopenharmony_ci				goto invalid;
1479c72fcc34Sopenharmony_ci			}
1480c72fcc34Sopenharmony_ci			space->go_to = strdup(value);
1481c72fcc34Sopenharmony_ci			if (space->go_to == NULL) {
1482c72fcc34Sopenharmony_ci				err = -ENOMEM;
1483c72fcc34Sopenharmony_ci				break;
1484c72fcc34Sopenharmony_ci			}
1485c72fcc34Sopenharmony_ci			continue;
1486c72fcc34Sopenharmony_ci		}
1487c72fcc34Sopenharmony_ci		if (strcasecmp(key, "INCLUDE") == 0) {
1488c72fcc34Sopenharmony_ci			char *rootdir, *go_to;
1489c72fcc34Sopenharmony_ci			const char *filename;
1490c72fcc34Sopenharmony_ci			struct stat st;
1491c72fcc34Sopenharmony_ci			int linenum;
1492c72fcc34Sopenharmony_ci			if (op != KEY_OP_ASSIGN) {
1493c72fcc34Sopenharmony_ci				Perror(space, "invalid INCLUDE operation");
1494c72fcc34Sopenharmony_ci				goto invalid;
1495c72fcc34Sopenharmony_ci			}
1496c72fcc34Sopenharmony_ci			if (value[0] == '/')
1497c72fcc34Sopenharmony_ci				strlcpy(string, value, sizeof(string));
1498c72fcc34Sopenharmony_ci			else {
1499c72fcc34Sopenharmony_ci				strlcpy(string, space->rootdir, sizeof(string));
1500c72fcc34Sopenharmony_ci				strlcat(string, "/", sizeof(string));
1501c72fcc34Sopenharmony_ci				strlcat(string, value, sizeof(string));
1502c72fcc34Sopenharmony_ci			}
1503c72fcc34Sopenharmony_ci			rootdir = space->rootdir;
1504c72fcc34Sopenharmony_ci			go_to = space->go_to;
1505c72fcc34Sopenharmony_ci			filename = space->filename;
1506c72fcc34Sopenharmony_ci			linenum = space->linenum;
1507c72fcc34Sopenharmony_ci			if (stat(string, &st)) {
1508c72fcc34Sopenharmony_ci				Perror(space, "invalid filename '%s'", string);
1509c72fcc34Sopenharmony_ci				continue;
1510c72fcc34Sopenharmony_ci			}
1511c72fcc34Sopenharmony_ci			if (S_ISDIR(st.st_mode)) {
1512c72fcc34Sopenharmony_ci				struct dirent **list;
1513c72fcc34Sopenharmony_ci				int i, num;
1514c72fcc34Sopenharmony_ci				num = scandir(string, &list, conf_name_filter,
1515c72fcc34Sopenharmony_ci					      alphasort);
1516c72fcc34Sopenharmony_ci				if (num < 0) {
1517c72fcc34Sopenharmony_ci					Perror(space, "invalid directory '%s'", string);
1518c72fcc34Sopenharmony_ci					continue;
1519c72fcc34Sopenharmony_ci				}
1520c72fcc34Sopenharmony_ci				count = strlen(string);
1521c72fcc34Sopenharmony_ci				for (i = 0; i < num; i++) {
1522c72fcc34Sopenharmony_ci					string[count] = '\0';
1523c72fcc34Sopenharmony_ci					strlcat(string, "/", sizeof(string));
1524c72fcc34Sopenharmony_ci					strlcat(string, list[i]->d_name, sizeof(string));
1525c72fcc34Sopenharmony_ci					space->go_to = NULL;
1526c72fcc34Sopenharmony_ci					space->rootdir = new_root_dir(string);
1527c72fcc34Sopenharmony_ci					free(list[i]);
1528c72fcc34Sopenharmony_ci					if (space->rootdir) {
1529c72fcc34Sopenharmony_ci						err = parse(space, string);
1530c72fcc34Sopenharmony_ci						free(space->rootdir);
1531c72fcc34Sopenharmony_ci					} else
1532c72fcc34Sopenharmony_ci						err = -ENOMEM;
1533c72fcc34Sopenharmony_ci					if (space->go_to) {
1534c72fcc34Sopenharmony_ci						Perror(space, "unterminated GOTO '%s'", space->go_to);
1535c72fcc34Sopenharmony_ci						free(space->go_to);
1536c72fcc34Sopenharmony_ci					}
1537c72fcc34Sopenharmony_ci					if (err)
1538c72fcc34Sopenharmony_ci						break;
1539c72fcc34Sopenharmony_ci				}
1540c72fcc34Sopenharmony_ci				free(list);
1541c72fcc34Sopenharmony_ci			} else {
1542c72fcc34Sopenharmony_ci				space->go_to = NULL;
1543c72fcc34Sopenharmony_ci				space->rootdir = new_root_dir(string);
1544c72fcc34Sopenharmony_ci				if (space->rootdir) {
1545c72fcc34Sopenharmony_ci					err = parse(space, string);
1546c72fcc34Sopenharmony_ci					free(space->rootdir);
1547c72fcc34Sopenharmony_ci				} else
1548c72fcc34Sopenharmony_ci					err = -ENOMEM;
1549c72fcc34Sopenharmony_ci				if (space->go_to) {
1550c72fcc34Sopenharmony_ci					Perror(space, "unterminated GOTO '%s'", space->go_to);
1551c72fcc34Sopenharmony_ci					free(space->go_to);
1552c72fcc34Sopenharmony_ci				}
1553c72fcc34Sopenharmony_ci			}
1554c72fcc34Sopenharmony_ci			space->go_to = go_to;
1555c72fcc34Sopenharmony_ci			space->rootdir = rootdir;
1556c72fcc34Sopenharmony_ci			space->filename = filename;
1557c72fcc34Sopenharmony_ci			space->linenum = linenum;
1558c72fcc34Sopenharmony_ci			if (space->quit)
1559c72fcc34Sopenharmony_ci				break;
1560c72fcc34Sopenharmony_ci			if (err)
1561c72fcc34Sopenharmony_ci				break;
1562c72fcc34Sopenharmony_ci			continue;
1563c72fcc34Sopenharmony_ci		}
1564c72fcc34Sopenharmony_ci		if (strncasecmp(key, "ACCESS", 6) == 0) {
1565c72fcc34Sopenharmony_ci			if (op == KEY_OP_MATCH || op == KEY_OP_NOMATCH) {
1566c72fcc34Sopenharmony_ci				if (value[0] == '$') {
1567c72fcc34Sopenharmony_ci					strlcpy(string, value, sizeof(string));
1568c72fcc34Sopenharmony_ci					apply_format(space, string, sizeof(string));
1569c72fcc34Sopenharmony_ci					if (string[0] == '/')
1570c72fcc34Sopenharmony_ci						goto __access1;
1571c72fcc34Sopenharmony_ci				}
1572c72fcc34Sopenharmony_ci				if (value[0] != '/') {
1573c72fcc34Sopenharmony_ci					strlcpy(string, space->rootdir, sizeof(string));
1574c72fcc34Sopenharmony_ci					strlcat(string, "/", sizeof(string));
1575c72fcc34Sopenharmony_ci					strlcat(string, value, sizeof(string));
1576c72fcc34Sopenharmony_ci				} else {
1577c72fcc34Sopenharmony_ci					strlcpy(string, value, sizeof(string));
1578c72fcc34Sopenharmony_ci				}
1579c72fcc34Sopenharmony_ci				apply_format(space, string, sizeof(string));
1580c72fcc34Sopenharmony_ci			      __access1:
1581c72fcc34Sopenharmony_ci				count = access(string, F_OK);
1582c72fcc34Sopenharmony_ci				dbg("access(%s) = %i (%s)", string, count, value);
1583c72fcc34Sopenharmony_ci				if (op == KEY_OP_MATCH && count != 0)
1584c72fcc34Sopenharmony_ci					break;
1585c72fcc34Sopenharmony_ci				if (op == KEY_OP_NOMATCH && count == 0)
1586c72fcc34Sopenharmony_ci					break;
1587c72fcc34Sopenharmony_ci			} else {
1588c72fcc34Sopenharmony_ci				Perror(space, "invalid ACCESS operation");
1589c72fcc34Sopenharmony_ci				goto invalid;
1590c72fcc34Sopenharmony_ci			}
1591c72fcc34Sopenharmony_ci			continue;
1592c72fcc34Sopenharmony_ci		}
1593c72fcc34Sopenharmony_ci		if (strncasecmp(key, "PRINT", 5) == 0) {
1594c72fcc34Sopenharmony_ci			if (op != KEY_OP_ASSIGN) {
1595c72fcc34Sopenharmony_ci				Perror(space, "invalid PRINT operation");
1596c72fcc34Sopenharmony_ci				goto invalid;
1597c72fcc34Sopenharmony_ci			}
1598c72fcc34Sopenharmony_ci			strlcpy(string, value, sizeof(string));
1599c72fcc34Sopenharmony_ci			apply_format(space, string, sizeof(string));
1600c72fcc34Sopenharmony_ci			fwrite(string, strlen(string), 1, stdout);
1601c72fcc34Sopenharmony_ci			continue;
1602c72fcc34Sopenharmony_ci		}
1603c72fcc34Sopenharmony_ci		if (strncasecmp(key, "ERROR", 5) == 0) {
1604c72fcc34Sopenharmony_ci			if (op != KEY_OP_ASSIGN) {
1605c72fcc34Sopenharmony_ci				Perror(space, "invalid ERROR operation");
1606c72fcc34Sopenharmony_ci				goto invalid;
1607c72fcc34Sopenharmony_ci			}
1608c72fcc34Sopenharmony_ci			strlcpy(string, value, sizeof(string));
1609c72fcc34Sopenharmony_ci			apply_format(space, string, sizeof(string));
1610c72fcc34Sopenharmony_ci			fwrite(string, strlen(string), 1, stderr);
1611c72fcc34Sopenharmony_ci			continue;
1612c72fcc34Sopenharmony_ci		}
1613c72fcc34Sopenharmony_ci		if (strncasecmp(key, "EXIT", 4) == 0) {
1614c72fcc34Sopenharmony_ci			if (op != KEY_OP_ASSIGN) {
1615c72fcc34Sopenharmony_ci				Perror(space, "invalid EXIT operation");
1616c72fcc34Sopenharmony_ci				goto invalid;
1617c72fcc34Sopenharmony_ci			}
1618c72fcc34Sopenharmony_ci			strlcpy(string, value, sizeof(string));
1619c72fcc34Sopenharmony_ci			apply_format(space, string, sizeof(string));
1620c72fcc34Sopenharmony_ci			if (strcmp(string, "return") == 0)
1621c72fcc34Sopenharmony_ci				return -EJUSTRETURN;
1622c72fcc34Sopenharmony_ci			space->exit_code = strtol(string, NULL, 0);
1623c72fcc34Sopenharmony_ci			space->quit = 1;
1624c72fcc34Sopenharmony_ci			break;
1625c72fcc34Sopenharmony_ci		}
1626c72fcc34Sopenharmony_ci		if (strncasecmp(key, "CONFIG{", 7) == 0) {
1627c72fcc34Sopenharmony_ci			attr = get_key_attribute(space, key + 6, string, sizeof(string));
1628c72fcc34Sopenharmony_ci			if (attr == NULL) {
1629c72fcc34Sopenharmony_ci				Perror(space, "error parsing CONFIG attribute");
1630c72fcc34Sopenharmony_ci				goto invalid;
1631c72fcc34Sopenharmony_ci			}
1632c72fcc34Sopenharmony_ci			strlcpy(result, value, sizeof(result));
1633c72fcc34Sopenharmony_ci			apply_format(space, result, sizeof(result));
1634c72fcc34Sopenharmony_ci			if (op == KEY_OP_ASSIGN) {
1635c72fcc34Sopenharmony_ci				err = value_set(space, attr, result);
1636c72fcc34Sopenharmony_ci				dbg("CONFIG{%s}='%s'", attr, result);
1637c72fcc34Sopenharmony_ci				break;
1638c72fcc34Sopenharmony_ci			} else if (op == KEY_OP_MATCH || op == KEY_OP_NOMATCH) {
1639c72fcc34Sopenharmony_ci				pair = value_find(space, attr);
1640c72fcc34Sopenharmony_ci				if (pair == NULL)
1641c72fcc34Sopenharmony_ci					break;
1642c72fcc34Sopenharmony_ci				if (!do_match(key, op, result, pair->value))
1643c72fcc34Sopenharmony_ci					break;
1644c72fcc34Sopenharmony_ci			} else {
1645c72fcc34Sopenharmony_ci				Perror(space, "invalid CONFIG{} operation");
1646c72fcc34Sopenharmony_ci				goto invalid;
1647c72fcc34Sopenharmony_ci			}
1648c72fcc34Sopenharmony_ci		}
1649c72fcc34Sopenharmony_ci
1650c72fcc34Sopenharmony_ci		Perror(space, "unknown key '%s'", key);
1651c72fcc34Sopenharmony_ci	}
1652c72fcc34Sopenharmony_ci	return err;
1653c72fcc34Sopenharmony_ci
1654c72fcc34Sopenharmony_ciinvalid:
1655c72fcc34Sopenharmony_ci	Perror(space, "invalid rule");
1656c72fcc34Sopenharmony_ci	return -EINVAL;
1657c72fcc34Sopenharmony_ci}
1658c72fcc34Sopenharmony_ci
1659c72fcc34Sopenharmony_cistatic int parse(struct space *space, const char *filename)
1660c72fcc34Sopenharmony_ci{
1661c72fcc34Sopenharmony_ci	char *buf, *bufline, *line;
1662c72fcc34Sopenharmony_ci	size_t bufsize, pos, count, linesize;
1663c72fcc34Sopenharmony_ci	unsigned int linenum, i, j, linenum_adj;
1664c72fcc34Sopenharmony_ci	int err;
1665c72fcc34Sopenharmony_ci
1666c72fcc34Sopenharmony_ci	dbg("start of file '%s'", filename);
1667c72fcc34Sopenharmony_ci
1668c72fcc34Sopenharmony_ci	if (file_map(filename, &buf, &bufsize) != 0) {
1669c72fcc34Sopenharmony_ci		err = errno;
1670c72fcc34Sopenharmony_ci		error("Unable to open file '%s': %s", filename, strerror(err));
1671c72fcc34Sopenharmony_ci		return -err;
1672c72fcc34Sopenharmony_ci	}
1673c72fcc34Sopenharmony_ci
1674c72fcc34Sopenharmony_ci	err = 0;
1675c72fcc34Sopenharmony_ci	pos = 0;
1676c72fcc34Sopenharmony_ci	linenum = 0;
1677c72fcc34Sopenharmony_ci	linesize = 128;
1678c72fcc34Sopenharmony_ci	line = malloc(linesize);
1679c72fcc34Sopenharmony_ci	if (line == NULL) {
1680c72fcc34Sopenharmony_ci		file_unmap(buf, bufsize);
1681c72fcc34Sopenharmony_ci		return -ENOMEM;
1682c72fcc34Sopenharmony_ci	}
1683c72fcc34Sopenharmony_ci	space->filename = filename;
1684c72fcc34Sopenharmony_ci	while (!err && pos < bufsize && !space->quit) {
1685c72fcc34Sopenharmony_ci		count = line_width(buf, bufsize, pos);
1686c72fcc34Sopenharmony_ci		bufline = buf + pos;
1687c72fcc34Sopenharmony_ci		pos += count + 1;
1688c72fcc34Sopenharmony_ci		linenum++;
1689c72fcc34Sopenharmony_ci
1690c72fcc34Sopenharmony_ci		/* skip whitespaces */
1691c72fcc34Sopenharmony_ci		while (count > 0 && isspace(bufline[0])) {
1692c72fcc34Sopenharmony_ci			bufline++;
1693c72fcc34Sopenharmony_ci			count--;
1694c72fcc34Sopenharmony_ci		}
1695c72fcc34Sopenharmony_ci		if (count == 0)
1696c72fcc34Sopenharmony_ci			continue;
1697c72fcc34Sopenharmony_ci
1698c72fcc34Sopenharmony_ci		/* comment check */
1699c72fcc34Sopenharmony_ci		if (bufline[0] == '#')
1700c72fcc34Sopenharmony_ci			continue;
1701c72fcc34Sopenharmony_ci
1702c72fcc34Sopenharmony_ci		if (count > linesize - 1) {
1703c72fcc34Sopenharmony_ci			free(line);
1704c72fcc34Sopenharmony_ci			line = NULL;
1705c72fcc34Sopenharmony_ci			linesize = (count + 127 + 1) & ~127;
1706c72fcc34Sopenharmony_ci			if (linesize > 2048) {
1707c72fcc34Sopenharmony_ci				error("file %s, line %i too long", filename, linenum);
1708c72fcc34Sopenharmony_ci				err = -EINVAL;
1709c72fcc34Sopenharmony_ci				break;
1710c72fcc34Sopenharmony_ci			}
1711c72fcc34Sopenharmony_ci			line = malloc(linesize);
1712c72fcc34Sopenharmony_ci			if (line == NULL) {
1713c72fcc34Sopenharmony_ci				err = -EINVAL;
1714c72fcc34Sopenharmony_ci				break;
1715c72fcc34Sopenharmony_ci			}
1716c72fcc34Sopenharmony_ci		}
1717c72fcc34Sopenharmony_ci
1718c72fcc34Sopenharmony_ci		/* skip backslash and newline from multiline rules */
1719c72fcc34Sopenharmony_ci		linenum_adj = 0;
1720c72fcc34Sopenharmony_ci		for (i = j = 0; i < count; i++) {
1721c72fcc34Sopenharmony_ci			if (bufline[i] == '\\' && bufline[i+1] == '\n') {
1722c72fcc34Sopenharmony_ci				linenum_adj++;
1723c72fcc34Sopenharmony_ci				continue;
1724c72fcc34Sopenharmony_ci			}
1725c72fcc34Sopenharmony_ci			line[j++] = bufline[i];
1726c72fcc34Sopenharmony_ci		}
1727c72fcc34Sopenharmony_ci		line[j] = '\0';
1728c72fcc34Sopenharmony_ci
1729c72fcc34Sopenharmony_ci		dbg("read (%i) '%s'", linenum, line);
1730c72fcc34Sopenharmony_ci		space->linenum = linenum;
1731c72fcc34Sopenharmony_ci		err = parse_line(space, line, linesize);
1732c72fcc34Sopenharmony_ci		if (err == -EJUSTRETURN) {
1733c72fcc34Sopenharmony_ci			err = 0;
1734c72fcc34Sopenharmony_ci			break;
1735c72fcc34Sopenharmony_ci		}
1736c72fcc34Sopenharmony_ci		linenum += linenum_adj;
1737c72fcc34Sopenharmony_ci	}
1738c72fcc34Sopenharmony_ci
1739c72fcc34Sopenharmony_ci	free(line);
1740c72fcc34Sopenharmony_ci	space->filename = NULL;
1741c72fcc34Sopenharmony_ci	space->linenum = -1;
1742c72fcc34Sopenharmony_ci	file_unmap(buf, bufsize);
1743c72fcc34Sopenharmony_ci	dbg("end of file '%s'", filename);
1744c72fcc34Sopenharmony_ci	return err ? err : -abs(space->exit_code);
1745c72fcc34Sopenharmony_ci}
1746c72fcc34Sopenharmony_ci
1747c72fcc34Sopenharmony_ciint init(const char *cfgdir, const char *filename, int flags, const char *cardname)
1748c72fcc34Sopenharmony_ci{
1749c72fcc34Sopenharmony_ci	struct space *space;
1750c72fcc34Sopenharmony_ci	struct snd_card_iterator iter;
1751c72fcc34Sopenharmony_ci	int err = 0, lasterr = 0;
1752c72fcc34Sopenharmony_ci
1753c72fcc34Sopenharmony_ci	sysfs_init();
1754c72fcc34Sopenharmony_ci	err = snd_card_iterator_sinit(&iter, cardname);
1755c72fcc34Sopenharmony_ci	if (err < 0)
1756c72fcc34Sopenharmony_ci		goto out;
1757c72fcc34Sopenharmony_ci	while (snd_card_iterator_next(&iter)) {
1758c72fcc34Sopenharmony_ci		err = snd_card_clean_cfgdir(cfgdir, iter.card);
1759c72fcc34Sopenharmony_ci		if (err < 0) {
1760c72fcc34Sopenharmony_ci			if (lasterr == 0)
1761c72fcc34Sopenharmony_ci				lasterr = err;
1762c72fcc34Sopenharmony_ci			continue;
1763c72fcc34Sopenharmony_ci		}
1764c72fcc34Sopenharmony_ci		err = init_ucm(flags, iter.card);
1765c72fcc34Sopenharmony_ci		if (err == 0)
1766c72fcc34Sopenharmony_ci			continue;
1767c72fcc34Sopenharmony_ci		err = init_space(&space, iter.card);
1768c72fcc34Sopenharmony_ci		if (err != 0)
1769c72fcc34Sopenharmony_ci			continue;
1770c72fcc34Sopenharmony_ci		space->rootdir = new_root_dir(filename);
1771c72fcc34Sopenharmony_ci		if (space->rootdir != NULL) {
1772c72fcc34Sopenharmony_ci			err = parse(space, filename);
1773c72fcc34Sopenharmony_ci			if (!cardname && err <= -99) { /* non-fatal errors */
1774c72fcc34Sopenharmony_ci				if (lasterr == 0)
1775c72fcc34Sopenharmony_ci					lasterr = err;
1776c72fcc34Sopenharmony_ci				err = 0;
1777c72fcc34Sopenharmony_ci			}
1778c72fcc34Sopenharmony_ci		}
1779c72fcc34Sopenharmony_ci		free_space(space);
1780c72fcc34Sopenharmony_ci		if (err < 0)
1781c72fcc34Sopenharmony_ci			goto out;
1782c72fcc34Sopenharmony_ci	}
1783c72fcc34Sopenharmony_ci	err = lasterr ? lasterr : snd_card_iterator_error(&iter);
1784c72fcc34Sopenharmony_ciout:
1785c72fcc34Sopenharmony_ci	sysfs_cleanup();
1786c72fcc34Sopenharmony_ci	return err;
1787c72fcc34Sopenharmony_ci}
1788