From c869a4b0f7a1ff8f5978979f305b70043dd685af4014774e94ea2d5486eae840 Mon Sep 17 00:00:00 2001 From: OBS User unknown Date: Fri, 12 Sep 2008 20:43:46 +0000 Subject: [PATCH] OBS-URL: https://build.opensuse.org/package/show/openSUSE:Factory/alsa-utils?expand=0&rev=23 --- alsa-utils-1.0.17.tar.bz2 | 3 - alsa-utils-1.0.18rc3.tar.bz2 | 3 + alsa-utils-git-fixes.diff | 3845 +--------------------------------- alsa-utils.changes | 11 + alsa-utils.spec | 13 +- 5 files changed, 31 insertions(+), 3844 deletions(-) delete mode 100644 alsa-utils-1.0.17.tar.bz2 create mode 100644 alsa-utils-1.0.18rc3.tar.bz2 diff --git a/alsa-utils-1.0.17.tar.bz2 b/alsa-utils-1.0.17.tar.bz2 deleted file mode 100644 index 20f8955..0000000 --- a/alsa-utils-1.0.17.tar.bz2 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:6a06f457d66eca75ac1ab065e64539e4ccf22ed4e53703a70194cb85203b3125 -size 1016532 diff --git a/alsa-utils-1.0.18rc3.tar.bz2 b/alsa-utils-1.0.18rc3.tar.bz2 new file mode 100644 index 0000000..94f3794 --- /dev/null +++ b/alsa-utils-1.0.18rc3.tar.bz2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0ae03cc58dd2c36e2c7d2b5f3ecb6e4621b4eed7c6b5577ec8c71832a7574ea7 +size 1039366 diff --git a/alsa-utils-git-fixes.diff b/alsa-utils-git-fixes.diff index 6955c5d..c0ec390 100644 --- a/alsa-utils-git-fixes.diff +++ b/alsa-utils-git-fixes.diff @@ -1,3842 +1,13 @@ -diff --git a/alsactl/Makefile.am b/alsactl/Makefile.am -index d21a496..93b3d79 100644 ---- a/alsactl/Makefile.am -+++ b/alsactl/Makefile.am -@@ -1,6 +1,9 @@ - sbin_PROGRAMS=alsactl --man_MANS=alsactl.1 --EXTRA_DIST=alsactl.1 -+man_MANS=alsactl.1 alsactl_init.7 -+EXTRA_DIST=alsactl.1 alsactl_init.xml - --alsactl_SOURCES=alsactl.c state.c names.c -+alsactl_SOURCES=alsactl.c state.c names.c utils.c init_parse.c - noinst_HEADERS=alsactl.h -+ -+%.7: %.xml -+ xmlto man $? diff --git a/alsactl/alsactl.c b/alsactl/alsactl.c -index 78c6cb1..8d3987a 100644 +index 6728fbc..57957bf 100644 --- a/alsactl/alsactl.c +++ b/alsactl/alsactl.c -@@ -40,19 +40,25 @@ char *command; - static void help(void) - { - printf("Usage: alsactl command\n"); -- printf("\nAvailable options:\n"); -+ printf("\nAvailable global options:\n"); - printf(" -h,--help this help\n"); -+ printf(" -d,--debug debug mode\n"); -+ printf(" -v,--version print version of this program\n"); -+ printf("\nAvailable state options:\n"); - printf(" -f,--file # configuration file (default " SYS_ASOUNDRC " or " SYS_ASOUNDNAMES ")\n"); - printf(" -F,--force try to restore the matching controls as much as possible\n"); - printf(" (default mode)\n"); - printf(" -P,--pedantic don't restore mismatching controls (old default)\n"); -- printf(" -d,--debug debug mode\n"); -- printf(" -v,--version print version of this program\n"); -+ printf("\nAvailable init options:\n"); -+ printf(" -E,--env #=# set environment variable for init phase (NAME=VALUE)\n"); -+ printf(" -i,--initfile # main configuation file for init phase (default " DATADIR "/init/00main)\n"); -+ printf("\n"); - printf("\nAvailable commands:\n"); - printf(" store save current driver setup for one or each soundcards\n"); - printf(" to configuration file\n"); - printf(" restore load current driver setup for one or each soundcards\n"); - printf(" from configuration file\n"); -+ printf(" init initialize driver to a default state\n"); - printf(" names dump information about all the known present (sub-)devices\n"); - printf(" into configuration file (DEPRECATED)\n"); - } -@@ -63,6 +69,8 @@ int main(int argc, char *argv[]) - { - {"help", 0, NULL, 'h'}, - {"file", 1, NULL, 'f'}, -+ {"env", 1, NULL, 'E'}, -+ {"initfile", 1, NULL, 'i'}, - {"force", 0, NULL, 'F'}, - {"pedantic", 0, NULL, 'P'}, - {"debug", 0, NULL, 'd'}, -@@ -70,13 +78,14 @@ int main(int argc, char *argv[]) - {NULL, 0, NULL, 0}, - }; - char *cfgfile = SYS_ASOUNDRC; -+ char *initfile = DATADIR "/init/00main"; - int res; - - command = argv[0]; - while (1) { - int c; - -- if ((c = getopt_long(argc, argv, "hf:Fdv", long_option, NULL)) < 0) -+ if ((c = getopt_long(argc, argv, "hdvf:FE:i:", long_option, NULL)) < 0) - break; - switch (c) { - case 'h': -@@ -88,6 +97,15 @@ int main(int argc, char *argv[]) - case 'F': - force_restore = 1; - break; -+ case 'E': -+ if (putenv(optarg)) { -+ fprintf(stderr, "environment string '%s' is wrong\n", optarg); -+ return EXIT_FAILURE; -+ } -+ break; -+ case 'i': -+ initfile = optarg; -+ break; - case 'P': - force_restore = 0; - break; -@@ -111,8 +129,11 @@ int main(int argc, char *argv[]) - return 0; +@@ -150,7 +150,7 @@ int main(int argc, char *argv[]) } -- if (!strcmp(argv[optind], "store")) { -- res = save_state(cfgfile, -+ if (!strcmp(argv[optind], "init")) { -+ res = init(initfile, -+ argc - optind > 1 ? argv[optind + 1] : NULL); -+ } else if (!strcmp(argv[optind], "store")) { -+ res = save_state(cfgfile, - argc - optind > 1 ? argv[optind + 1] : NULL); - } else if (!strcmp(argv[optind], "restore")) { - res = load_state(cfgfile, -diff --git a/alsactl/alsactl.h b/alsactl/alsactl.h -index f1e2b41..51396da 100644 ---- a/alsactl/alsactl.h -+++ b/alsactl/alsactl.h -@@ -3,6 +3,20 @@ extern int force_restore; - extern char *command; - - #if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 95) -+#define info(...) do {\ -+ fprintf(stdout, "%s: %s:%d: ", command, __FUNCTION__, __LINE__); \ -+ fprintf(stdout, __VA_ARGS__); \ -+ putc('\n', stdout); \ -+} while (0) -+#else -+#define info(args...) do {\ -+ fprintf(stdout, "%s: %s:%d: ", command, __FUNCTION__, __LINE__); \ -+ fprintf(stdout, ##args); \ -+ putc('\n', stdout); \ -+} while (0) -+#endif -+ -+#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 95) - #define error(...) do {\ - fprintf(stderr, "%s: %s:%d: ", command, __FUNCTION__, __LINE__); \ - fprintf(stderr, __VA_ARGS__); \ -@@ -16,7 +30,43 @@ extern char *command; - } while (0) - #endif - -+#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 95) -+#define dbg(...) do {\ -+ if (!debugflag) break; \ -+ fprintf(stderr, "%s: %s:%d: ", command, __FUNCTION__, __LINE__); \ -+ fprintf(stderr, __VA_ARGS__); \ -+ putc('\n', stderr); \ -+} while (0) -+#else -+#define dbg(args...) do {\ -+ if (!debugflag) break; \ -+ fprintf(stderr, "%s: %s:%d: ", command, __FUNCTION__, __LINE__); \ -+ fprintf(stderr, ##args); \ -+ putc('\n', stderr); \ -+} while (0) -+#endif -+ -+int init(const char *file, const char *cardname); - int save_state(const char *file, const char *cardname); - int load_state(const char *file, const char *cardname); - int power(const char *argv[], int argc); - int generate_names(const char *cfgfile); -+ -+/* utils */ -+ -+int file_map(const char *filename, char **buf, size_t *bufsize); -+void file_unmap(void *buf, size_t bufsize); -+size_t line_width(const char *buf, size_t bufsize, size_t pos); -+ -+static inline int hextodigit(int c) -+{ -+ if (c >= '0' && c <= '9') -+ c -= '0'; -+ else if (c >= 'a' && c <= 'f') -+ c = c - 'a' + 10; -+ else if (c >= 'A' && c <= 'F') -+ c = c - 'A' + 10; -+ else -+ return -1; -+ return c; -+} -diff --git a/alsactl/alsactl_init.xml b/alsactl/alsactl_init.xml -new file mode 100644 -index 0000000..d4b65fd ---- /dev/null -+++ b/alsactl/alsactl_init.xml -@@ -0,0 +1,534 @@ -+ -+ -+ -+
-+
-+ alsactl init -+ -+ -+ alsactl init -+ July 2008 -+ alsactl -+ -+ -+ -+ alsactl_init -+ 7 -+ -+ -+ -+ -+ alsactl init -+ alsa control management - initialization -+ -+ -+ DESCRIPTION -+ "alsactl init" provides soundcard specific initialization. -+ -+ -+ CONFIGURATION -+ All "alsactl init" configuration files are placed in -+ /usr/share/alsa/init/ directory. The top level -+ configuration file is /usr/share/alsa/init/00main. -+ The default top-level file can be also specified using -i or -+ --initfile parameter for the alsactl tool. -+ Every file consists of a set of lines of text. All empty lines or -+ lines beginning with '#' will be ignored. -+ -+ Rules files -+ The "alsactl init" rules are read from the files located -+ in the /usr/share/alsa/init/*. The top -+ level configuration file is /usr/share/alsa/init/00main. -+ Every line in the rules file contains at least one key value pair. -+ There are two kind of keys, match and assignment keys. If all match -+ keys are matching against its value, the rule gets applied and the -+ assign keys get the specified value assigned. -+ -+ A rule may consists of a list of one or more key value pairs -+ separated by a comma. Each key has a distinct operation, depending -+ on the used operator. Valid operators are: -+ -+ -+ -+ -+ Compare for equality. -+ -+ -+ -+ -+ -+ -+ Compare for non-equality. -+ -+ -+ -+ -+ -+ -+ Assign a value to a key. Keys that represent a list, -+ are reset and only this single value is assigned. -+ -+ -+ -+ -+ -+ -+ Add the value to a key that holds a list -+ of entries. -+ -+ -+ -+ -+ -+ -+ Assign a value to a key finally; disallow any -+ later changes, which may be used to prevent changes by -+ any later rules. -+ -+ -+ -+ -+ The following key names can be used to match against device -+ properties: -+ -+ -+ -+ -+ Match the card index of the ALSA driver. -+ -+ -+ -+ -+ -+ -+ Set or test universal control attribute. Possible -+ attributes: -+ -+ -+ -+ -+ Numeric control identification. -+ -+ -+ -+ , -+ -+ Control interface name (CARD, HWEDEP, MIXER, PCM, RAWMIDI, TIMER, SEQUENCER) -+ -+ -+ -+ , -+ -+ Subdevice number. -+ -+ -+ -+ -+ -+ Control name -+ -+ -+ -+ -+ -+ Control index -+ -+ -+ -+ -+ -+ Control type (BOOLEAN, INTEGER, INTEGER64, ENUMERATED, BYTES, IEC958) -+ -+ -+ -+ , -+ -+ Attributes (stored in a string - use match characters * and ?): -+ -+ -+ -+ -+ control is readable -+ -+ -+ -+ -+ -+ control is writable -+ -+ -+ -+ -+ -+ control is volatile -+ -+ -+ -+ -+ -+ control is inactive -+ -+ -+ -+ -+ -+ control is locked -+ -+ -+ -+ -+ -+ control is TLV readable -+ -+ -+ -+ -+ -+ control is TLV writable -+ -+ -+ -+ -+ -+ control is TLV commandable -+ -+ -+ -+ -+ -+ process is owner of this control -+ -+ -+ -+ -+ -+ control created in user space -+ -+ -+ -+ -+ -+ -+ -+ -+ Control owner process PID number -+ -+ -+ -+ -+ -+ Control count of values -+ -+ -+ -+ -+ -+ Value range - minimum value -+ -+ -+ -+ -+ -+ Value range - maximum value -+ -+ -+ -+ -+ -+ Value range - step value -+ -+ -+ -+ -+ -+ Enumerated value - number of text items -+ -+ -+ -+ -+ -+ Value of control stored to a string delimited by -+ comma (,). -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ The relative path to sysfs subsystem specifying -+ the root directory of a soundcard device. Usually, -+ it should be set to "/class/sound/controlC$cardinfo{card}/device". -+ -+ -+ -+ -+ -+ -+ -+ Match sysfs attribute values of the soundcard device. -+ The relative path to sysfs tree must be defined by -+ SYSFS_DEVICE key. Trailing whitespace in the attribute -+ values is ignored, if the specified match value does -+ not contain trailing whitespace itself. Depending on -+ the type of operator, this key is also used to set -+ the value of a sysfs attribute. -+ -+ -+ -+ -+ -+ -+ -+ Match against the value of an environment variable. Up -+ to five keys can be specified per rule. -+ Depending on the type of operator, this key is also used -+ to export a variable to the environment. -+ -+ -+ -+ -+ -+ -+ Execute external program. The key is true, if -+ the program returns without exit code zero. The whole event -+ environment is available to the executed program. The -+ program's output printed to stdout is available for -+ the RESULT key. -+ Several buildin commands are available: -+ -+ -+ -+ -+ Search for a control. The CTL{name} key might -+ contain match characters * and ?. An control index -+ might be specified as first argument starting from -+ zero (e.g. PROGRAM="__ctl_search 2"). -+ -+ -+ -+ -+ -+ Search for a controls and return total count -+ of matched ones. The CTL{name} key might contain match -+ characters * and ?. -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ Match the returned string of the last PROGRAM call. -+ This key can be used in the same or in any later rule -+ after a PROGRAM call. -+ -+ -+ -+ -+ -+ Most of the fields support a shell style pattern matching. -+ The following pattern characters are supported: -+ -+ -+ -+ -+ Matches zero, or any number of characters. -+ -+ -+ -+ -+ -+ Matches any single character. -+ -+ -+ -+ -+ -+ Matches any single character specified within -+ the brackets. For example, the pattern string 'tty[SR]' -+ would match either 'ttyS' or 'ttyR'. Ranges are also -+ supported within this match with the '-' character. -+ For example, to match on the range of all digits, -+ the pattern [0-9] would be used. If the first character -+ following the '[' is a '!', any characters -+ not enclosed are matched. -+ -+ -+ -+ -+ The following keys can get values assigned: -+ -+ -+ , , -+ , , -+ , , -+ -+ -+ Select universal control element. -+ -+ -+ -+ -+ -+ Value is set (written) also to soundcard's control -+ device and RESULT key is set to errno code. The result of -+ set operation is always true (it means continue with -+ next key on line). -+ -+ -+ -+ -+ -+ -+ Export a variable to the environment. Depending on the type of operator, -+ this key is also to match against an environment variable. -+ -+ -+ -+ -+ -+ -+ Named label where a GOTO can jump to. -+ -+ -+ -+ -+ -+ -+ Jumps to the next LABEL with a matching name -+ -+ -+ -+ -+ -+ -+ The relative path to sysfs subsystem specifying -+ the root directory of a soundcard device. Usually, -+ it should be set to "/class/sound/controlC$cardinfo{card}/device". -+ -+ -+ -+ -+ -+ -+ -+ PRINT value to stdout. -+ -+ -+ -+ -+ -+ -+ PRINT value to stderr. -+ -+ -+ -+ -+ -+ -+ Exit immediately and set program exit code to value (should be integer). -+ -+ -+ -+ -+ -+ The , , -+ , , -+ , -+ fields support simple printf-like string substitutions. -+ It allows the use of the complete environment set by earlier matching -+ rules. For all other fields, substitutions are applied while the individual rule is -+ being processed. The available substitutions are: -+ -+ -+ , -+ -+ See CARDINFO{} for more details. -+ -+ -+ -+ -+ , -+ -+ See CTL{} for more details. -+ -+ -+ -+ -+ , -+ -+ The value of a sysfs attribute found at the device, where -+ all keys of the rule have matched. -+ If the attribute is a symlink, the last element of the symlink target is -+ returned as the value. -+ -+ -+ -+ -+ , -+ -+ The value of an environment variable. -+ -+ -+ -+ -+ , -+ -+ The string returned by the external program requested with PROGRAM. -+ A single part of the string, separated by a space character may be selected -+ by specifying the part number as an attribute: . -+ If the number is followed by the '+' char this part plus all remaining parts -+ of the result string are substituted: -+ -+ -+ -+ -+ , -+ -+ Root directory where sysfs file-system is mounted. -+ Ususally, this value is just "/sys". -+ -+ -+ -+ -+ -+ -+ The '%' character itself. -+ -+ -+ -+ -+ -+ -+ The '$' character itself. -+ -+ -+ -+ The count of characters to be substituted may be limited -+ by specifying the format length value. For example, '%3s{file}' -+ will only insert the first three characters of the sysfs -+ attribute -+ -+ -+ -+ AUTHOR -+ Written by Jaroslav Kysela perex@perex.cz -+ Some portions are written by Greg Kroah-Hartman greg@kroah.com and -+ Kay Sievers kay.sievers@vrfy.org. -+ -+ -+ -+ SEE ALSO -+ -+ alsactl1 -+ -+ -+ -+
-+
-diff --git a/alsactl/init/00main b/alsactl/init/00main -new file mode 100644 -index 0000000..daac952 ---- /dev/null -+++ b/alsactl/init/00main -@@ -0,0 +1,16 @@ -+# This is toplevel configuration for for 'alsactl init'. -+# See 'man alsactl_init' for syntax. -+ -+# set root device directory in sysfs for soundcard for ATTR{} command -+SYSFS_DEVICE="/class/sound/controlC$cardinfo{card}/device" -+ -+# test for extra commands -+ENV{CMD}=="help", INCLUDE="help", GOTO="00main_end" -+ENV{CMD}=="info", INCLUDE="info", GOTO="00main_end" -+ENV{CMD}=="test", INCLUDE="test", GOTO="00main_end" -+ENV{CMD}=="*", ERROR="Unknown command '$env{CMD}'\n", GOTO="00main_end" -+ -+# include files with real configuration -+CARDINFO{driver}=="HDA-Intel", INCLUDE="hda", GOTO="00main_end" -+CARDINFO{driver}=="Test", INCLUDE="test", GOTO="00main_end" -+LABEL="00main_end" -diff --git a/alsactl/init/hda b/alsactl/init/hda -new file mode 100644 -index 0000000..c34daaa ---- /dev/null -+++ b/alsactl/init/hda -@@ -0,0 +1,37 @@ -+# Configuration for HDA Intel driver (High Definition Audio - Azalia) -+ -+CARDINFO{mixername}=="Realtek ALC880", \ -+ ATTR{subsystem_vendor}=="0x1025", ATTR{subsystem_device}=="0x0070", \ -+ GOTO="Acer Travelmate 8100" -+CARDINFO{mixername}=="Analog Devices AD1984", \ -+ ATTR{subsystem_vendor}=="0x17aa", ATTR{subsystem_device}=="0x20ac", \ -+ GOTO="Lenovo T61" -+ -+ERROR="Unknown hardware: \"$cardinfo{mixername}\" \"$cardinfo{components}\" \"$attr{subsystem_vendor}" \"$attr{subsystem_device}\"\n" -+ERROR="Hardware is left uninitialized\n" -+EXIT="99" -+ -+LABEL="Acer Travelmate 8100" -+# playback -+CTL{reset}="mixer" -+CTL{name}="Headphone Playback Switch", CTL{value}="on,on" -+CTL{name}="Front Playback Volume", CTL{value}="35,35" -+CTL{name}="Front Playback Switch", CTL{value}="on,on" -+CTL{name}="PCM Playback Volume", CTL{value}="150,150" -+# capture -+CTL{name}="Input Source", CTL{value}="0" -+CTL{name}="Capture Volume", CTL{value}="65,65" -+CTL{name}="Capture Switch", CTL{value}="on,on" -+EXIT="0" -+ -+LABEL="Lenovo T61" -+# playback -+CTL{reset}="mixer" -+CTL{name}="Headphone Playback Switch", CTL{value}="on,on" -+CTL{name}="PCM Playback Volume", CTL{value}="150,550" -+# capture (Internal Mic) -+CTL{name}="Input Source", CTL{value}="1" -+CTL{name}="Internal Mic Boost", CTL{value}="1" -+CTL{name}="Capture Volume", CTL{value}="45,45" -+CTL{name}="Capture Switch", CTL{value}="on,on" -+EXIT="0" -diff --git a/alsactl/init/help b/alsactl/init/help -new file mode 100644 -index 0000000..60d9b1c ---- /dev/null -+++ b/alsactl/init/help -@@ -0,0 +1,7 @@ -+# help page -+ -+PRINT="Available commands (identified by the environment variable CMD):\n\n" -+PRINT=" (not set) Do a soundcard initialization\n" -+PRINT=" help Show this information\n" -+PRINT=" info Print all available hardware identification\n" -+PRINT=" test Do alsactl utility parser tests\n" -diff --git a/alsactl/init/info b/alsactl/init/info -new file mode 100644 -index 0000000..a4fea19 ---- /dev/null -+++ b/alsactl/init/info -@@ -0,0 +1,22 @@ -+# show information about card -+ -+PRINT="CARDINFO:\n" -+PRINT=" CARDINFO{id}=\"$CARDINFO{id}\"\n" -+PRINT=" CARDINFO{card}=\"$CARDINFO{card}\"\n" -+PRINT=" CARDINFO{driver}=\"$CARDINFO{driver}\"\n" -+PRINT=" CARDINFO{name}=\"$CARDINFO{name}\"\n" -+PRINT=" CARDINFO{longname}=\"$CARDINFO{longname}\"\n" -+PRINT=" CARDINFO{mixername}=\"$CARDINFO{mixername}\"\n" -+PRINT=" CARDINFO{components}=\"$CARDINFO{components}\"\n" -+ -+# sysfs stuff -+PRINT="sysfs:\n" -+ATTR{bus}=="*", PRINT=" ATTR{bus}=\"$ATTR{bus}\"\n" -+ATTR{class}=="*", PRINT=" ATTR{class}=\"$ATTR{class}\"\n" -+ATTR{driver}=="*", PRINT=" ATTR{driver}=\"$ATTR{driver}\"\n" -+ATTR{vendor}=="*", PRINT=" ATTR{vendor}=\"$ATTR{vendor}\"\n" -+ATTR{device}=="*", PRINT=" ATTR{device}=\"$ATTR{device}\"\n" -+ATTR{subsystem_vendor}=="*", \ -+ PRINT=" ATTR{subsystem_vendor}=\"$ATTR{subsystem_vendor}\"\n" -+ATTR{subsystem_device}=="*", \ -+ PRINT=" ATTR{subsystem_device}=\"$ATTR{subsystem_device}\"\n" -diff --git a/alsactl/init/test b/alsactl/init/test -new file mode 100644 -index 0000000..d1f4f11 ---- /dev/null -+++ b/alsactl/init/test -@@ -0,0 +1,256 @@ -+# Test code -+# Just for debugging purposes -+ -+PRINT="Default CTL:\n" -+PRINT=" CTL{numid}=\"$ctl{numid}\"\n" -+PRINT=" CTL{iface}=\"$ctl{iface}\"\n" -+PRINT=" CTL{device}=\"$ctl{device}\"\n" -+PRINT=" CTL{subdevice}=\"$ctl{subdevice}\"\n" -+PRINT=" CTL{name}=\"$ctl{name}\"\n" -+PRINT=" CTL{index}=\"$ctl{index}\"\n" -+ -+CTL{reset}="mixer" -+ -+PRINT="After CTL{reset}=\"mixer\":\n" -+PRINT=" CTL{numid}=\"$ctl{numid}\"\n" -+PRINT=" CTL{iface}=\"$ctl{iface}\"\n" -+PRINT=" CTL{device}=\"$ctl{device}\"\n" -+PRINT=" CTL{subdevice}=\"$ctl{subdevice}\"\n" -+PRINT=" CTL{name}=\"$ctl{name}\"\n" -+PRINT=" CTL{index}=\"$ctl{index}\"\n" -+ -+CTL{numid}="987" -+CTL{iface}="sequencer" -+CTL{device}="10" -+CTL{subdevice}="20" -+CTL{name}="Just Test" -+CTL{index}="999" -+ -+PRINT="After test sequence:\n" -+PRINT=" CTL{numid}=\"$ctl{numid}\"\n" -+PRINT=" CTL{iface}=\"$ctl{iface}\"\n" -+PRINT=" CTL{device}=\"$ctl{device}\"\n" -+PRINT=" CTL{subdevice}=\"$ctl{subdevice}\"\n" -+PRINT=" CTL{name}=\"$ctl{name}\"\n" -+PRINT=" CTL{index}=\"$ctl{index}\"\n" -+ -+ERROR="Ignore following error:\n " -+PROGRAM="__just_test" -+ -+PRINT="__ctl_count test:\n" -+CTL{search}="mixer", CTL{name}="*Switch*", PROGRAM="__ctl_count", \ -+ PRINT=" *Switch* count result: $result\n" -+ -+PRINT="__ctl_search test:\n" -+CTL{search}="mixer", CTL{name}="*Switch*", PROGRAM!="__ctl_search", GOTO="skip_switch_search" -+PRINT=" *Switch 0* search result: $result\n" -+PRINT=" CTL{numid}=\"$ctl{numid}\"\n" -+PRINT=" CTL{iface}=\"$ctl{iface}\"\n" -+PRINT=" CTL{device}=\"$ctl{device}\"\n" -+PRINT=" CTL{subdevice}=\"$ctl{subdevice}\"\n" -+PRINT=" CTL{name}=\"$ctl{name}\"\n" -+PRINT=" CTL{index}=\"$ctl{index}\"\n" -+CTL{search}="mixer", CTL{name}="*Switch*", PROGRAM!="__ctl_search 1", GOTO="skip_switch_search" -+PRINT=" *Switch 1* search result: $result\n" -+PRINT=" CTL{numid}=\"$ctl{numid}\"\n" -+PRINT=" CTL{iface}=\"$ctl{iface}\"\n" -+PRINT=" CTL{device}=\"$ctl{device}\"\n" -+PRINT=" CTL{subdevice}=\"$ctl{subdevice}\"\n" -+PRINT=" CTL{name}=\"$ctl{name}\"\n" -+PRINT=" CTL{index}=\"$ctl{index}\"\n" -+LABEL="skip_switch_search" -+ -+PRINT="First ten elements:\n" -+CTL{search}="mixer", CTL{name}="*", PROGRAM!="__ctl_search 0", GOTO="skip_first_ten_search" -+PRINT=" Element #0:\n" -+PRINT=" CTL{numid}=\"$ctl{numid}\"\n" -+PRINT=" CTL{iface}=\"$ctl{iface}\"\n" -+PRINT=" CTL{device}=\"$ctl{device}\"\n" -+PRINT=" CTL{subdevice}=\"$ctl{subdevice}\"\n" -+PRINT=" CTL{name}=\"$ctl{name}\"\n" -+PRINT=" CTL{index}=\"$ctl{index}\"\n" -+PRINT=" CTL{type}=\"$ctl{type}\"\n" -+PRINT=" CTL{attr}=\"$ctl{attr}\"\n" -+PRINT=" CTL{owner}=\"$ctl{owner}\"\n" -+PRINT=" CTL{count}=\"$ctl{count}\"\n" -+PRINT=" CTL{min}=\"$ctl{min}\"\n" -+PRINT=" CTL{max}=\"$ctl{max}\"\n" -+PRINT=" CTL{step}=\"$ctl{step}\"\n" -+PRINT=" CTL{items}=\"$ctl{items}\"\n" -+PRINT=" CTL{value}=\"$ctl{value}\"\n" -+CTL{search}="mixer", CTL{name}="*", PROGRAM!="__ctl_search 1", GOTO="skip_first_ten_search" -+PRINT=" Element #1:\n" -+PRINT=" CTL{numid}=\"$ctl{numid}\"\n" -+PRINT=" CTL{iface}=\"$ctl{iface}\"\n" -+PRINT=" CTL{device}=\"$ctl{device}\"\n" -+PRINT=" CTL{subdevice}=\"$ctl{subdevice}\"\n" -+PRINT=" CTL{name}=\"$ctl{name}\"\n" -+PRINT=" CTL{index}=\"$ctl{index}\"\n" -+PRINT=" CTL{type}=\"$ctl{type}\"\n" -+PRINT=" CTL{attr}=\"$ctl{attr}\"\n" -+PRINT=" CTL{owner}=\"$ctl{owner}\"\n" -+PRINT=" CTL{count}=\"$ctl{count}\"\n" -+PRINT=" CTL{min}=\"$ctl{min}\"\n" -+PRINT=" CTL{max}=\"$ctl{max}\"\n" -+PRINT=" CTL{step}=\"$ctl{step}\"\n" -+PRINT=" CTL{items}=\"$ctl{items}\"\n" -+PRINT=" CTL{value}=\"$ctl{value}\"\n" -+CTL{search}="mixer", CTL{name}="*", PROGRAM!="__ctl_search 2", GOTO="skip_first_ten_search" -+PRINT=" Element #2:\n" -+PRINT=" CTL{numid}=\"$ctl{numid}\"\n" -+PRINT=" CTL{iface}=\"$ctl{iface}\"\n" -+PRINT=" CTL{device}=\"$ctl{device}\"\n" -+PRINT=" CTL{subdevice}=\"$ctl{subdevice}\"\n" -+PRINT=" CTL{name}=\"$ctl{name}\"\n" -+PRINT=" CTL{index}=\"$ctl{index}\"\n" -+PRINT=" CTL{type}=\"$ctl{type}\"\n" -+PRINT=" CTL{attr}=\"$ctl{attr}\"\n" -+PRINT=" CTL{owner}=\"$ctl{owner}\"\n" -+PRINT=" CTL{count}=\"$ctl{count}\"\n" -+PRINT=" CTL{min}=\"$ctl{min}\"\n" -+PRINT=" CTL{max}=\"$ctl{max}\"\n" -+PRINT=" CTL{step}=\"$ctl{step}\"\n" -+PRINT=" CTL{items}=\"$ctl{items}\"\n" -+PRINT=" CTL{value}=\"$ctl{value}\"\n" -+LABEL="skip_first_ten_search" -+CTL{search}="mixer", CTL{name}="*", PROGRAM!="__ctl_search 3", GOTO="skip_first_ten_search" -+PRINT=" Element #3:\n" -+PRINT=" CTL{numid}=\"$ctl{numid}\"\n" -+PRINT=" CTL{iface}=\"$ctl{iface}\"\n" -+PRINT=" CTL{device}=\"$ctl{device}\"\n" -+PRINT=" CTL{subdevice}=\"$ctl{subdevice}\"\n" -+PRINT=" CTL{name}=\"$ctl{name}\"\n" -+PRINT=" CTL{index}=\"$ctl{index}\"\n" -+PRINT=" CTL{type}=\"$ctl{type}\"\n" -+PRINT=" CTL{attr}=\"$ctl{attr}\"\n" -+PRINT=" CTL{owner}=\"$ctl{owner}\"\n" -+PRINT=" CTL{count}=\"$ctl{count}\"\n" -+PRINT=" CTL{min}=\"$ctl{min}\"\n" -+PRINT=" CTL{max}=\"$ctl{max}\"\n" -+PRINT=" CTL{step}=\"$ctl{step}\"\n" -+PRINT=" CTL{items}=\"$ctl{items}\"\n" -+PRINT=" CTL{value}=\"$ctl{value}\"\n" -+LABEL="skip_first_ten_search" -+CTL{search}="mixer", CTL{name}="*", PROGRAM!="__ctl_search 4", GOTO="skip_first_ten_search" -+PRINT=" Element #4:\n" -+PRINT=" CTL{numid}=\"$ctl{numid}\"\n" -+PRINT=" CTL{iface}=\"$ctl{iface}\"\n" -+PRINT=" CTL{device}=\"$ctl{device}\"\n" -+PRINT=" CTL{subdevice}=\"$ctl{subdevice}\"\n" -+PRINT=" CTL{name}=\"$ctl{name}\"\n" -+PRINT=" CTL{index}=\"$ctl{index}\"\n" -+PRINT=" CTL{type}=\"$ctl{type}\"\n" -+PRINT=" CTL{attr}=\"$ctl{attr}\"\n" -+PRINT=" CTL{owner}=\"$ctl{owner}\"\n" -+PRINT=" CTL{count}=\"$ctl{count}\"\n" -+PRINT=" CTL{min}=\"$ctl{min}\"\n" -+PRINT=" CTL{max}=\"$ctl{max}\"\n" -+PRINT=" CTL{step}=\"$ctl{step}\"\n" -+PRINT=" CTL{items}=\"$ctl{items}\"\n" -+PRINT=" CTL{value}=\"$ctl{value}\"\n" -+LABEL="skip_first_ten_search" -+CTL{search}="mixer", CTL{name}="*", PROGRAM!="__ctl_search 5", GOTO="skip_first_ten_search" -+PRINT=" Element #5:\n" -+PRINT=" CTL{numid}=\"$ctl{numid}\"\n" -+PRINT=" CTL{iface}=\"$ctl{iface}\"\n" -+PRINT=" CTL{device}=\"$ctl{device}\"\n" -+PRINT=" CTL{subdevice}=\"$ctl{subdevice}\"\n" -+PRINT=" CTL{name}=\"$ctl{name}\"\n" -+PRINT=" CTL{index}=\"$ctl{index}\"\n" -+PRINT=" CTL{type}=\"$ctl{type}\"\n" -+PRINT=" CTL{attr}=\"$ctl{attr}\"\n" -+PRINT=" CTL{owner}=\"$ctl{owner}\"\n" -+PRINT=" CTL{count}=\"$ctl{count}\"\n" -+PRINT=" CTL{min}=\"$ctl{min}\"\n" -+PRINT=" CTL{max}=\"$ctl{max}\"\n" -+PRINT=" CTL{step}=\"$ctl{step}\"\n" -+PRINT=" CTL{items}=\"$ctl{items}\"\n" -+PRINT=" CTL{value}=\"$ctl{value}\"\n" -+LABEL="skip_first_ten_search" -+CTL{search}="mixer", CTL{name}="*", PROGRAM!="__ctl_search 6", GOTO="skip_first_ten_search" -+PRINT=" Element #6:\n" -+PRINT=" CTL{numid}=\"$ctl{numid}\"\n" -+PRINT=" CTL{iface}=\"$ctl{iface}\"\n" -+PRINT=" CTL{device}=\"$ctl{device}\"\n" -+PRINT=" CTL{subdevice}=\"$ctl{subdevice}\"\n" -+PRINT=" CTL{name}=\"$ctl{name}\"\n" -+PRINT=" CTL{index}=\"$ctl{index}\"\n" -+PRINT=" CTL{type}=\"$ctl{type}\"\n" -+PRINT=" CTL{attr}=\"$ctl{attr}\"\n" -+PRINT=" CTL{owner}=\"$ctl{owner}\"\n" -+PRINT=" CTL{count}=\"$ctl{count}\"\n" -+PRINT=" CTL{min}=\"$ctl{min}\"\n" -+PRINT=" CTL{max}=\"$ctl{max}\"\n" -+PRINT=" CTL{step}=\"$ctl{step}\"\n" -+PRINT=" CTL{items}=\"$ctl{items}\"\n" -+PRINT=" CTL{value}=\"$ctl{value}\"\n" -+LABEL="skip_first_ten_search" -+CTL{search}="mixer", CTL{name}="*", PROGRAM!="__ctl_search 7", GOTO="skip_first_ten_search" -+PRINT=" Element #7:\n" -+PRINT=" CTL{numid}=\"$ctl{numid}\"\n" -+PRINT=" CTL{iface}=\"$ctl{iface}\"\n" -+PRINT=" CTL{device}=\"$ctl{device}\"\n" -+PRINT=" CTL{subdevice}=\"$ctl{subdevice}\"\n" -+PRINT=" CTL{name}=\"$ctl{name}\"\n" -+PRINT=" CTL{index}=\"$ctl{index}\"\n" -+PRINT=" CTL{type}=\"$ctl{type}\"\n" -+PRINT=" CTL{attr}=\"$ctl{attr}\"\n" -+PRINT=" CTL{owner}=\"$ctl{owner}\"\n" -+PRINT=" CTL{count}=\"$ctl{count}\"\n" -+PRINT=" CTL{min}=\"$ctl{min}\"\n" -+PRINT=" CTL{max}=\"$ctl{max}\"\n" -+PRINT=" CTL{step}=\"$ctl{step}\"\n" -+PRINT=" CTL{items}=\"$ctl{items}\"\n" -+PRINT=" CTL{value}=\"$ctl{value}\"\n" -+LABEL="skip_first_ten_search" -+CTL{search}="mixer", CTL{name}="*", PROGRAM!="__ctl_search 8", GOTO="skip_first_ten_search" -+PRINT=" Element #8:\n" -+PRINT=" CTL{numid}=\"$ctl{numid}\"\n" -+PRINT=" CTL{iface}=\"$ctl{iface}\"\n" -+PRINT=" CTL{device}=\"$ctl{device}\"\n" -+PRINT=" CTL{subdevice}=\"$ctl{subdevice}\"\n" -+PRINT=" CTL{name}=\"$ctl{name}\"\n" -+PRINT=" CTL{index}=\"$ctl{index}\"\n" -+PRINT=" CTL{type}=\"$ctl{type}\"\n" -+PRINT=" CTL{attr}=\"$ctl{attr}\"\n" -+PRINT=" CTL{owner}=\"$ctl{owner}\"\n" -+PRINT=" CTL{count}=\"$ctl{count}\"\n" -+PRINT=" CTL{min}=\"$ctl{min}\"\n" -+PRINT=" CTL{max}=\"$ctl{max}\"\n" -+PRINT=" CTL{step}=\"$ctl{step}\"\n" -+PRINT=" CTL{items}=\"$ctl{items}\"\n" -+PRINT=" CTL{value}=\"$ctl{value}\"\n" -+LABEL="skip_first_ten_search" -+CTL{search}="mixer", CTL{name}="*", PROGRAM!="__ctl_search 9", GOTO="skip_first_ten_search" -+PRINT=" Element #9:\n" -+PRINT=" CTL{numid}=\"$ctl{numid}\"\n" -+PRINT=" CTL{iface}=\"$ctl{iface}\"\n" -+PRINT=" CTL{device}=\"$ctl{device}\"\n" -+PRINT=" CTL{subdevice}=\"$ctl{subdevice}\"\n" -+PRINT=" CTL{name}=\"$ctl{name}\"\n" -+PRINT=" CTL{index}=\"$ctl{index}\"\n" -+PRINT=" CTL{type}=\"$ctl{type}\"\n" -+PRINT=" CTL{attr}=\"$ctl{attr}\"\n" -+PRINT=" CTL{owner}=\"$ctl{owner}\"\n" -+PRINT=" CTL{count}=\"$ctl{count}\"\n" -+PRINT=" CTL{min}=\"$ctl{min}\"\n" -+PRINT=" CTL{max}=\"$ctl{max}\"\n" -+PRINT=" CTL{step}=\"$ctl{step}\"\n" -+PRINT=" CTL{items}=\"$ctl{items}\"\n" -+PRINT=" CTL{value}=\"$ctl{value}\"\n" -+LABEL="skip_first_ten_search" -+ -+PRINT="Elements write test #1:\n", \ -+ CTL{search}="mixer", CTL{name}="Front Playback Switch", \ -+ PROGRAM="__ctl_search", CTL{value}="on,on", \ -+ PRINT=" result=$result\n" -+PRINT="Elements write test #2:\n", \ -+ CTL{search}="mixer", CTL{name}="Front Playback Volume", \ -+ PROGRAM="__ctl_search", CTL{value}="32,32", \ -+ PRINT=" result=$result\n" -+PRINT="Elements write test #3:\n", \ -+ CTL{search}="mixer", CTL{name}="Front Playback Volume Error", \ -+ PROGRAM="__ctl_search", CTL{value}="32,32", \ -+ PRINT=" result=$result\n" -+ -+PRINT="\nAll tests done..\n" -diff --git a/alsactl/init_parse.c b/alsactl/init_parse.c -new file mode 100644 -index 0000000..a99362d ---- /dev/null -+++ b/alsactl/init_parse.c -@@ -0,0 +1,1517 @@ -+/* -+ * Advanced Linux Sound Architecture Control Program - Parse initialization files -+ * Copyright (c) by Jaroslav Kysela , -+ * Greg Kroah-Hartman , -+ * Kay Sievers -+ * -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation; either version 2 of the License, or -+ * (at your option) any later version. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ * GNU General Public License for more details. -+ * -+ * You should have received a copy of the GNU General Public License -+ * along with this program; if not, write to the Free Software -+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -+ * -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include "aconfig.h" -+#include "alsactl.h" -+#include "list.h" -+ -+#define PATH_SIZE 512 -+#define NAME_SIZE 128 -+ -+enum key_op { -+ KEY_OP_UNSET, -+ KEY_OP_MATCH, -+ KEY_OP_NOMATCH, -+ KEY_OP_ADD, -+ KEY_OP_ASSIGN, -+ KEY_OP_ASSIGN_FINAL -+}; -+ -+struct pair { -+ char *key; -+ char *value; -+ struct pair *next; -+}; -+ -+struct space { -+ struct pair *pairs; -+ char *rootdir; -+ char *go_to; -+ char *program_result; -+ const char *filename; -+ int linenum; -+ int log_run; -+ int exit_code; -+ int quit; -+ unsigned int ctl_id_changed; -+ snd_hctl_t *ctl_handle; -+ snd_ctl_card_info_t *ctl_card_info; -+ snd_ctl_elem_id_t *ctl_id; -+ snd_ctl_elem_info_t *ctl_info; -+ snd_ctl_elem_value_t *ctl_value; -+}; -+ -+static void Perror(struct space *space, const char *fmt, ...) -+{ -+ va_list arg; -+ va_start(arg, fmt); -+ fprintf(stderr, "%s:%i: ", space->filename, space->linenum); -+ vfprintf(stderr, fmt, arg); -+ putc('\n', stderr); -+ va_end(arg); -+} -+ -+#include "init_sysdeps.c" -+#include "init_utils_string.c" -+#include "init_utils_run.c" -+#include "init_sysfs.c" -+ -+static void free_space(struct space *space) -+{ -+ struct pair *pair = space->pairs; -+ struct pair *next = pair; -+ -+ while (next) { -+ pair = next; -+ next = pair->next; -+ free(pair->value); -+ free(pair->key); -+ free(pair); -+ } -+ space->pairs = NULL; -+ if (space->ctl_value) { -+ snd_ctl_elem_value_free(space->ctl_value); -+ space->ctl_value = NULL; -+ } -+ if (space->ctl_info) { -+ snd_ctl_elem_info_free(space->ctl_info); -+ space->ctl_info = NULL; -+ } -+ if (space->ctl_id) { -+ snd_ctl_elem_id_free(space->ctl_id); -+ space->ctl_id = NULL; -+ } -+ if (space->ctl_card_info) { -+ snd_ctl_card_info_free(space->ctl_card_info); -+ space->ctl_card_info = NULL; -+ } -+ if (space->ctl_handle) { -+ free(space->ctl_handle); -+ space->ctl_handle = NULL; -+ } -+ if (space->rootdir) -+ free(space->rootdir); -+ if (space->program_result) -+ free(space->program_result); -+} -+ -+struct pair *value_find(struct space *space, const char *key) -+{ -+ struct pair *pair = space->pairs; -+ -+ while (pair && strcmp(pair->key, key) != 0) -+ pair = pair->next; -+ return pair; -+} -+ -+static int value_set(struct space *space, const char *key, const char *value) -+{ -+ struct pair *pair; -+ -+ pair = value_find(space, key); -+ if (pair) { -+ free(pair->value); -+ pair->value = strdup(value); -+ if (pair->value == NULL) -+ return -ENOMEM; -+ } else { -+ pair = malloc(sizeof(struct pair)); -+ if (pair == NULL) -+ return -ENOMEM; -+ pair->key = strdup(key); -+ if (pair->key == NULL) { -+ free(pair); -+ return -ENOMEM; -+ } -+ pair->value = strdup(value); -+ if (pair->value == NULL) { -+ free(pair->key); -+ free(pair); -+ return -ENOMEM; -+ } -+ pair->next = space->pairs; -+ space->pairs = pair; -+ } -+ return 0; -+} -+ -+static int init_space(struct space **space, int card) -+{ -+ struct space *res; -+ char device[16]; -+ int err; -+ -+ res = calloc(1, sizeof(struct space)); -+ if (res == NULL) -+ return -ENOMEM; -+ res->ctl_id_changed = ~0; -+ res->linenum = -1; -+ sprintf(device, "hw:%u", card); -+ err = snd_hctl_open(&res->ctl_handle, device, 0); -+ if (err < 0) -+ goto error; -+ err = snd_hctl_load(res->ctl_handle); -+ if (err < 0) -+ goto error; -+ err = snd_ctl_card_info_malloc(&res->ctl_card_info); -+ if (err < 0) -+ goto error; -+ err = snd_ctl_card_info(snd_hctl_ctl(res->ctl_handle), res->ctl_card_info); -+ if (err < 0) -+ goto error; -+ err = snd_ctl_elem_id_malloc(&res->ctl_id); -+ if (err < 0) -+ goto error; -+ err = snd_ctl_elem_info_malloc(&res->ctl_info); -+ if (err < 0) -+ goto error; -+ err = snd_ctl_elem_value_malloc(&res->ctl_value); -+ if (err < 0) -+ goto error; -+ *space = res; -+ return 0; -+ error: -+ free_space(res); -+ return err; -+} -+ -+static const char *cardinfo_get(struct space *space, const char *attr) -+{ -+ if (strncasecmp(attr, "CARD", 4) == 0) { -+ static char res[16]; -+ sprintf(res, "%u", snd_ctl_card_info_get_card(space->ctl_card_info)); -+ return res; -+ } -+ if (strncasecmp(attr, "ID", 2) == 0) -+ return snd_ctl_card_info_get_id(space->ctl_card_info); -+ if (strncasecmp(attr, "DRIVER", 6) == 0) -+ return snd_ctl_card_info_get_driver(space->ctl_card_info); -+ if (strncasecmp(attr, "NAME", 4) == 0) -+ return snd_ctl_card_info_get_name(space->ctl_card_info); -+ if (strncasecmp(attr, "LONGNAME", 8) == 0) -+ return snd_ctl_card_info_get_longname(space->ctl_card_info); -+ if (strncasecmp(attr, "MIXERNAME", 9) == 0) -+ return snd_ctl_card_info_get_mixername(space->ctl_card_info); -+ if (strncasecmp(attr, "COMPONENTS", 10) == 0) -+ return snd_ctl_card_info_get_components(space->ctl_card_info); -+ Perror(space, "unknown cardinfo{} attribute '%s'", attr); -+ return NULL; -+} -+ -+static int check_id_changed(struct space *space, unsigned int what) -+{ -+ snd_hctl_elem_t *elem; -+ int err; -+ -+ if ((space->ctl_id_changed & what & 1) != 0) { -+ snd_ctl_elem_id_set_numid(space->ctl_id, 0); -+ elem = snd_hctl_find_elem(space->ctl_handle, space->ctl_id); -+ if (!elem) -+ return -ENOENT; -+ err = snd_hctl_elem_info(elem, space->ctl_info); -+ if (err == 0) -+ space->ctl_id_changed &= ~1; -+ return err; -+ } -+ if ((space->ctl_id_changed & what & 2) != 0) { -+ snd_ctl_elem_id_set_numid(space->ctl_id, 0); -+ elem = snd_hctl_find_elem(space->ctl_handle, space->ctl_id); -+ if (!elem) -+ return -ENOENT; -+ err = snd_hctl_elem_read(elem, space->ctl_value); -+ if (err == 0) -+ space->ctl_id_changed &= ~2; -+ return err; -+ } -+ return 0; -+} -+ -+static const char *get_ctl_value(struct space *space) -+{ -+ snd_ctl_elem_type_t type; -+ unsigned int idx, count; -+ static char res[1024], tmp[16]; -+ static const char *hex = "0123456789abcdef"; -+ char *pos; -+ const char *pos1; -+ -+ type = snd_ctl_elem_info_get_type(space->ctl_info); -+ count = snd_ctl_elem_info_get_count(space->ctl_info); -+ res[0] = '\0'; -+ switch (type) { -+ case SND_CTL_ELEM_TYPE_BOOLEAN: -+ for (idx = 0; idx < count; idx++) { -+ if (idx > 0) -+ strlcat(res, ",", sizeof(res)); -+ strlcat(res, snd_ctl_elem_value_get_boolean(space->ctl_value, idx) ? "on" : "off", sizeof(res)); -+ } -+ break; -+ case SND_CTL_ELEM_TYPE_INTEGER: -+ for (idx = 0; idx < count; idx++) { -+ if (idx > 0) -+ strlcat(res, ",", sizeof(res)); -+ snprintf(tmp, sizeof(tmp), "%li", snd_ctl_elem_value_get_integer(space->ctl_value, idx)); -+ strlcat(res, tmp, sizeof(res)); -+ } -+ break; -+ case SND_CTL_ELEM_TYPE_INTEGER64: -+ for (idx = 0; idx < count; idx++) { -+ if (idx > 0) -+ strlcat(res, ",", sizeof(res)); -+ snprintf(tmp, sizeof(tmp), "%lli", snd_ctl_elem_value_get_integer64(space->ctl_value, idx)); -+ strlcat(res, tmp, sizeof(res)); -+ } -+ break; -+ case SND_CTL_ELEM_TYPE_ENUMERATED: -+ for (idx = 0; idx < count; idx++) { -+ if (idx > 0) -+ strlcat(res, ",", sizeof(res)); -+ snprintf(tmp, sizeof(tmp), "%u", snd_ctl_elem_value_get_enumerated(space->ctl_value, idx)); -+ strlcat(res, tmp, sizeof(res)); -+ } -+ break; -+ case SND_CTL_ELEM_TYPE_BYTES: -+ case SND_CTL_ELEM_TYPE_IEC958: -+ if (type == SND_CTL_ELEM_TYPE_IEC958) -+ count = sizeof(snd_aes_iec958_t); -+ if (count > (sizeof(res)-1)/2) -+ count = (sizeof(res)-1/2); -+ pos = res; -+ pos1 = snd_ctl_elem_value_get_bytes(space->ctl_value); -+ while (count > 0) { -+ idx = *pos1++; -+ *pos++ = hex[idx >> 4]; -+ *pos++ = hex[idx & 0x0f]; -+ count++; -+ } -+ *pos++ = '\0'; -+ break; -+ default: -+ Perror(space, "unknown element type '%i'", type); -+ return NULL; -+ } -+ return res; -+} -+ -+static int set_ctl_value(struct space *space, const char *value) -+{ -+ snd_ctl_elem_type_t type; -+ unsigned int idx, count; -+ const char *pos; -+ int val; -+ -+ type = snd_ctl_elem_info_get_type(space->ctl_info); -+ count = snd_ctl_elem_info_get_count(space->ctl_info); -+ switch (type) { -+ case SND_CTL_ELEM_TYPE_BOOLEAN: -+ for (idx = 0; idx < count; idx++) { -+ while (*value == ' ') -+ value++; -+ if (*value == '\0') -+ goto missing; -+ val = strncasecmp(value, "true", 4) == 0 || -+ strncasecmp(value, "yes", 3) == 0 || -+ strncasecmp(value, "on", 2) == 0 || -+ strncasecmp(value, "1", 1) == 0; -+ snd_ctl_elem_value_set_boolean(space->ctl_value, idx, val); -+ pos = strchr(value, ','); -+ value = pos ? pos + 1 : value + strlen(value) - 1; -+ } -+ break; -+ case SND_CTL_ELEM_TYPE_INTEGER: -+ for (idx = 0; idx < count; idx++) { -+ while (*value == ' ') -+ value++; -+ snd_ctl_elem_value_set_integer(space->ctl_value, idx, strtol(value, NULL, 0)); -+ pos = strchr(value, ','); -+ value = pos ? pos + 1 : value + strlen(value) - 1; -+ } -+ break; -+ case SND_CTL_ELEM_TYPE_INTEGER64: -+ for (idx = 0; idx < count; idx++) { -+ while (*value == ' ') -+ value++; -+ snd_ctl_elem_value_set_integer64(space->ctl_value, idx, strtoll(value, NULL, 0)); -+ pos = strchr(value, ','); -+ value = pos ? pos + 1 : value + strlen(value) - 1; -+ } -+ break; -+ case SND_CTL_ELEM_TYPE_ENUMERATED: -+ for (idx = 0; idx < count; idx++) { -+ while (*value == ' ') -+ value++; -+ snd_ctl_elem_value_set_enumerated(space->ctl_value, idx, strtol(value, NULL, 0)); -+ pos = strchr(value, ','); -+ value = pos ? pos + 1 : value + strlen(value) - 1; -+ } -+ break; -+ case SND_CTL_ELEM_TYPE_BYTES: -+ case SND_CTL_ELEM_TYPE_IEC958: -+ if (type == SND_CTL_ELEM_TYPE_IEC958) -+ count = sizeof(snd_aes_iec958_t); -+ while (*value == ' ') -+ value++; -+ if (strlen(value) != count * 2) { -+ Perror(space, "bad ctl value hexa length (should be %u bytes, line %i)", count, space->linenum); -+ return -EINVAL; -+ } -+ for (idx = 0; idx < count; idx += 2) { -+ val = hextodigit(*(value++)) << 4; -+ val |= hextodigit(*(value++)); -+ if (val > 255) { -+ Perror(space, "bad ctl hexa value (line %i)", space->linenum); -+ return -EINVAL; -+ } -+ snd_ctl_elem_value_set_byte(space->ctl_value, idx, val); -+ } -+ break; -+ default: -+ Perror(space, "unknown element type '%i'", type); -+ return -EINVAL; -+ } -+ return 0; -+ missing: -+ printf("%i %i\n", type, count); -+ Perror(space, "missing some ctl values (line %i)", space->linenum); -+ return -EINVAL; -+} -+ -+static const char *elemid_get(struct space *space, const char *attr) -+{ -+ long long val; -+ snd_ctl_elem_type_t type; -+ static char res[16]; -+ -+ if (strncasecmp(attr, "numid", 5) == 0) { -+ val = snd_ctl_elem_id_get_numid(space->ctl_id); -+ goto value; -+ } -+ if (strncasecmp(attr, "iface", 5) == 0 || -+ strncasecmp(attr, "interface", 9) == 0) -+ return snd_ctl_elem_iface_name(snd_ctl_elem_id_get_interface(space->ctl_id)); -+ if (strncasecmp(attr, "device", 6) == 0) { -+ val = snd_ctl_elem_id_get_device(space->ctl_id); -+ goto value; -+ } -+ if (strncasecmp(attr, "subdev", 6) == 0) { -+ val = snd_ctl_elem_id_get_subdevice(space->ctl_id); -+ goto value; -+ } -+ if (strncasecmp(attr, "name", 4) == 0) -+ return snd_ctl_elem_id_get_name(space->ctl_id); -+ if (strncasecmp(attr, "index", 5) == 0) { -+ val = snd_ctl_elem_id_get_index(space->ctl_id); -+ goto value; -+ } -+ if (strncasecmp(attr, "type", 4) == 0) { -+ if (check_id_changed(space, 1)) -+ return NULL; -+ return snd_ctl_elem_type_name(snd_ctl_elem_info_get_type(space->ctl_info)); -+ } -+ if (strncasecmp(attr, "attr", 4) == 0) { -+ if (check_id_changed(space, 1)) -+ return NULL; -+ res[0] = '\0'; -+ if (snd_ctl_elem_info_is_readable(space->ctl_info)) -+ strcat(res, "r"); -+ if (snd_ctl_elem_info_is_writable(space->ctl_info)) -+ strcat(res, "w"); -+ if (snd_ctl_elem_info_is_volatile(space->ctl_info)) -+ strcat(res, "v"); -+ if (snd_ctl_elem_info_is_inactive(space->ctl_info)) -+ strcat(res, "i"); -+ if (snd_ctl_elem_info_is_locked(space->ctl_info)) -+ strcat(res, "l"); -+ if (snd_ctl_elem_info_is_tlv_readable(space->ctl_info)) -+ strcat(res, "R"); -+ if (snd_ctl_elem_info_is_tlv_writable(space->ctl_info)) -+ strcat(res, "W"); -+ if (snd_ctl_elem_info_is_tlv_commandable(space->ctl_info)) -+ strcat(res, "C"); -+ if (snd_ctl_elem_info_is_owner(space->ctl_info)) -+ strcat(res, "o"); -+ if (snd_ctl_elem_info_is_user(space->ctl_info)) -+ strcat(res, "u"); -+ return res; -+ } -+ if (strncasecmp(attr, "owner", 5) == 0) { -+ if (check_id_changed(space, 1)) -+ return NULL; -+ val = snd_ctl_elem_info_get_owner(space->ctl_info); -+ goto value; -+ } -+ if (strncasecmp(attr, "count", 5) == 0) { -+ if (check_id_changed(space, 1)) -+ return NULL; -+ val = snd_ctl_elem_info_get_count(space->ctl_info); -+ goto value; -+ } -+ if (strncasecmp(attr, "min", 3) == 0) { -+ if (check_id_changed(space, 1)) -+ return NULL; -+ type = snd_ctl_elem_info_get_type(space->ctl_info); -+ if (type == SND_CTL_ELEM_TYPE_INTEGER64) -+ val = snd_ctl_elem_info_get_min64(space->ctl_info); -+ else if (type == SND_CTL_ELEM_TYPE_INTEGER) -+ val = snd_ctl_elem_info_get_min(space->ctl_info); -+ else -+ goto empty; -+ goto value; -+ } -+ if (strncasecmp(attr, "max", 3) == 0) { -+ if (check_id_changed(space, 1)) -+ return NULL; -+ type = snd_ctl_elem_info_get_type(space->ctl_info); -+ if (type == SND_CTL_ELEM_TYPE_INTEGER64) -+ val = snd_ctl_elem_info_get_max64(space->ctl_info); -+ else if (type == SND_CTL_ELEM_TYPE_INTEGER) -+ val = snd_ctl_elem_info_get_max(space->ctl_info); -+ else -+ goto empty; -+ goto value; -+ } -+ if (strncasecmp(attr, "step", 3) == 0) { -+ if (check_id_changed(space, 1)) -+ return NULL; -+ type = snd_ctl_elem_info_get_type(space->ctl_info); -+ if (type == SND_CTL_ELEM_TYPE_INTEGER64) -+ val = snd_ctl_elem_info_get_step64(space->ctl_info); -+ else if (type == SND_CTL_ELEM_TYPE_INTEGER) -+ val = snd_ctl_elem_info_get_step(space->ctl_info); -+ else -+ goto empty; -+ goto value; -+ } -+ if (strncasecmp(attr, "items", 5) == 0) { -+ if (check_id_changed(space, 1)) -+ return NULL; -+ if (snd_ctl_elem_info_get_type(space->ctl_info) == SND_CTL_ELEM_TYPE_ENUMERATED) -+ val = snd_ctl_elem_info_get_items(space->ctl_info); -+ else { -+ empty: -+ res[0] = '\0'; -+ return res; -+ } -+ goto value; -+ } -+ if (strncasecmp(attr, "value", 5) == 0) { -+ if (check_id_changed(space, 3)) -+ return NULL; -+ return get_ctl_value(space); -+ } -+ Perror(space, "unknown ctl{} attribute '%s'", attr); -+ return NULL; -+ value: -+ sprintf(res, "%lli", val); -+ return res; -+} -+ -+static int elemid_set(struct space *space, const char *attr, const char *value) -+{ -+ unsigned int val; -+ void (*fcn)(snd_ctl_elem_id_t *, unsigned int); -+ snd_ctl_elem_iface_t iface; -+ int err; -+ -+ if (strncasecmp(attr, "numid", 5) == 0) { -+ fcn = snd_ctl_elem_id_set_numid; -+ goto value; -+ } -+ if (strncasecmp(attr, "iface", 5) == 0 || -+ strncasecmp(attr, "interface", 9) == 0 || -+ strncasecmp(attr, "reset", 5) == 0 || -+ strncasecmp(attr, "search", 6) == 0) { -+ if (strlen(value) == 0 && strncasecmp(attr, "search", 6) == 0) { -+ iface = 0; -+ goto search; -+ } -+ for (iface = 0; iface <= SND_CTL_ELEM_IFACE_LAST; iface++) { -+ if (strcasecmp(value, snd_ctl_elem_iface_name(iface)) == 0) { -+ if (strncasecmp(attr, "reset", 5) == 0) -+ snd_ctl_elem_id_clear(space->ctl_id); -+ if (strncasecmp(attr, "search", 5) == 0) { -+ search: -+ snd_ctl_elem_id_clear(space->ctl_id); -+ /* -1 means all */ -+ snd_ctl_elem_id_set_interface(space->ctl_id, -1); -+ snd_ctl_elem_id_set_device(space->ctl_id, -1); -+ snd_ctl_elem_id_set_subdevice(space->ctl_id, -1); -+ snd_ctl_elem_id_set_name(space->ctl_id, "*"); -+ snd_ctl_elem_id_set_index(space->ctl_id, -1); -+ if (strlen(value) == 0) -+ return 0; -+ } -+ snd_ctl_elem_id_set_interface(space->ctl_id, iface); -+ space->ctl_id_changed = ~0; -+ return 0; -+ } -+ } -+ Perror(space, "unknown control interface name '%s'", value); -+ return -EINVAL; -+ } -+ if (strncasecmp(attr, "device", 6) == 0) { -+ fcn = snd_ctl_elem_id_set_device; -+ goto value; -+ } -+ if (strncasecmp(attr, "subdev", 6) == 0) { -+ fcn = snd_ctl_elem_id_set_subdevice; -+ goto value; -+ } -+ if (strncasecmp(attr, "name", 4) == 0) { -+ snd_ctl_elem_id_set_name(space->ctl_id, value); -+ space->ctl_id_changed = ~0; -+ return 0; -+ } -+ if (strncasecmp(attr, "index", 5) == 0) { -+ fcn = snd_ctl_elem_id_set_index; -+ goto value; -+ } -+ if (strncasecmp(attr, "value", 5) == 0) { -+ err = check_id_changed(space, 1); -+ if (err < 0) { -+ Perror(space, "control element not found"); -+ return err; -+ } -+ err = set_ctl_value(space, value); -+ if (err < 0) { -+ space->ctl_id_changed |= 2; -+ } else { -+ space->ctl_id_changed &= ~2; -+ snd_ctl_elem_value_set_id(space->ctl_value, space->ctl_id); -+ err = snd_ctl_elem_write(snd_hctl_ctl(space->ctl_handle), space->ctl_value); -+ if (err < 0) { -+ Perror(space, "value write error: %s", snd_strerror(err)); -+ return err; -+ } -+ } -+ return err; -+ } -+ Perror(space, "unknown CTL{} attribute '%s'", attr); -+ return -EINVAL; -+ value: -+ val = (unsigned int)strtol(value, NULL, 0); -+ fcn(space->ctl_id, val); -+ space->ctl_id_changed = ~0; -+ return 0; -+} -+ -+static int get_key(char **line, char **key, enum key_op *op, char **value) -+{ -+ char *linepos; -+ char *temp; -+ -+ linepos = *line; -+ if (linepos == NULL && linepos[0] == '\0') -+ return -EINVAL; -+ -+ /* skip whitespace */ -+ while (isspace(linepos[0]) || linepos[0] == ',') -+ linepos++; -+ -+ /* get the key */ -+ if (linepos[0] == '\0') -+ return -EINVAL; -+ *key = linepos; -+ -+ while (1) { -+ linepos++; -+ if (linepos[0] == '\0') -+ return -1; -+ if (isspace(linepos[0])) -+ break; -+ if (linepos[0] == '=') -+ break; -+ if (linepos[0] == '+') -+ break; -+ if (linepos[0] == '!') -+ break; -+ if (linepos[0] == ':') -+ break; -+ } -+ -+ /* remember end of key */ -+ temp = linepos; -+ -+ /* skip whitespace after key */ -+ while (isspace(linepos[0])) -+ linepos++; -+ if (linepos[0] == '\0') -+ return -EINVAL; -+ -+ /* get operation type */ -+ if (linepos[0] == '=' && linepos[1] == '=') { -+ *op = KEY_OP_MATCH; -+ linepos += 2; -+ dbg("operator=match"); -+ } else if (linepos[0] == '!' && linepos[1] == '=') { -+ *op = KEY_OP_NOMATCH; -+ linepos += 2; -+ dbg("operator=nomatch"); -+ } else if (linepos[0] == '+' && linepos[1] == '=') { -+ *op = KEY_OP_ADD; -+ linepos += 2; -+ dbg("operator=add"); -+ } else if (linepos[0] == '=') { -+ *op = KEY_OP_ASSIGN; -+ linepos++; -+ dbg("operator=assign"); -+ } else if (linepos[0] == ':' && linepos[1] == '=') { -+ *op = KEY_OP_ASSIGN_FINAL; -+ linepos += 2; -+ dbg("operator=assign_final"); -+ } else -+ return -EINVAL; -+ -+ /* terminate key */ -+ temp[0] = '\0'; -+ dbg("key='%s'", *key); -+ -+ /* skip whitespace after operator */ -+ while (isspace(linepos[0])) -+ linepos++; -+ if (linepos[0] == '\0') -+ return -EINVAL; -+ -+ /* get the value*/ -+ if (linepos[0] != '"') -+ return -EINVAL; -+ linepos++; -+ *value = linepos; -+ -+ while (1) { -+ temp = strchr(linepos, '"'); -+ if (temp && temp[-1] == '\\') { -+ linepos = temp + 1; -+ continue; -+ } -+ break; -+ } -+ if (!temp) -+ return -EINVAL; -+ temp[0] = '\0'; -+ temp++; -+ dbg("value='%s'", *value); -+ -+ /* move line to next key */ -+ *line = temp; -+ -+ return 0; -+} -+ -+/* extract possible KEY{attr} */ -+static char *get_key_attribute(struct space *space, char *str, char *res, size_t ressize) -+{ -+ char *pos; -+ char *attr; -+ -+ attr = strchr(str, '{'); -+ if (attr != NULL) { -+ attr++; -+ pos = strchr(attr, '}'); -+ if (pos == NULL) { -+ Perror(space, "missing closing brace for format"); -+ return NULL; -+ } -+ pos[0] = '\0'; -+ strlcpy(res, attr, ressize); -+ pos[0] = '}'; -+ dbg("attribute='%s'", res); -+ return res; -+ } -+ -+ return NULL; -+} -+ -+/* extract possible {attr} and move str behind it */ -+static char *get_format_attribute(struct space *space, char **str) -+{ -+ char *pos; -+ char *attr = NULL; -+ -+ if (*str[0] == '{') { -+ pos = strchr(*str, '}'); -+ if (pos == NULL) { -+ Perror(space, "missing closing brace for format"); -+ return NULL; -+ } -+ pos[0] = '\0'; -+ attr = *str+1; -+ *str = pos+1; -+ dbg("attribute='%s', str='%s'", attr, *str); -+ } -+ return attr; -+} -+ -+/* extract possible format length and move str behind it*/ -+static int get_format_len(struct space *space, char **str) -+{ -+ int num; -+ char *tail; -+ -+ if (isdigit(*str[0])) { -+ num = (int) strtoul(*str, &tail, 10); -+ if (num > 0) { -+ *str = tail; -+ dbg("format length=%i", num); -+ return num; -+ } else { -+ Perror(space, "format parsing error '%s'", *str); -+ } -+ } -+ return -1; -+} -+ -+static void apply_format(struct space *space, char *string, size_t maxsize) -+{ -+ char temp[PATH_SIZE]; -+ char temp2[PATH_SIZE]; -+ char *head, *tail, *pos, *cpos, *attr, *rest; -+ struct pair *pair; -+ int len; -+ int i; -+ int count; -+ enum subst_type { -+ SUBST_UNKNOWN, -+ SUBST_CARDINFO, -+ SUBST_CTL, -+ SUBST_RESULT, -+ SUBST_ATTR, -+ SUBST_SYSFSROOT, -+ SUBST_ENV, -+ }; -+ static const struct subst_map { -+ char *name; -+ char fmt; -+ enum subst_type type; -+ } map[] = { -+ { .name = "cardinfo", .fmt = 'i', .type = SUBST_CARDINFO }, -+ { .name = "ctl", .fmt = 'C', .type = SUBST_CTL }, -+ { .name = "result", .fmt = 'c', .type = SUBST_RESULT }, -+ { .name = "attr", .fmt = 's', .type = SUBST_ATTR }, -+ { .name = "sysfsroot", .fmt = 'r', .type = SUBST_SYSFSROOT }, -+ { .name = "env", .fmt = 'E', .type = SUBST_ENV }, -+ { NULL, '\0', 0 } -+ }; -+ enum subst_type type; -+ const struct subst_map *subst; -+ -+ head = string; -+ while (1) { -+ len = -1; -+ while (head[0] != '\0') { -+ if (head[0] == '$') { -+ /* substitute named variable */ -+ if (head[1] == '\0') -+ break; -+ if (head[1] == '$') { -+ strlcpy(temp, head+2, sizeof(temp)); -+ strlcpy(head+1, temp, maxsize); -+ head++; -+ continue; -+ } -+ head[0] = '\0'; -+ for (subst = map; subst->name; subst++) { -+ if (strncasecmp(&head[1], subst->name, strlen(subst->name)) == 0) { -+ type = subst->type; -+ tail = head + strlen(subst->name)+1; -+ dbg("will substitute format name '%s'", subst->name); -+ goto found; -+ } -+ } -+ } else if (head[0] == '%') { -+ /* substitute format char */ -+ if (head[1] == '\0') -+ break; -+ if (head[1] == '%') { -+ strlcpy(temp, head+2, sizeof(temp)); -+ strlcpy(head+1, temp, maxsize); -+ head++; -+ continue; -+ } -+ head[0] = '\0'; -+ tail = head+1; -+ len = get_format_len(space, &tail); -+ for (subst = map; subst->name; subst++) { -+ if (tail[0] == subst->fmt) { -+ type = subst->type; -+ tail++; -+ dbg("will substitute format char '%c'", subst->fmt); -+ goto found; -+ } -+ } -+ } -+ head++; -+ } -+ break; -+found: -+ attr = get_format_attribute(space, &tail); -+ strlcpy(temp, tail, sizeof(temp)); -+ dbg("format=%i, string='%s', tail='%s'", type ,string, tail); -+ -+ switch (type) { -+ case SUBST_CARDINFO: -+ if (attr == NULL) -+ Perror(space, "missing identification parametr for cardinfo"); -+ else { -+ const char *value = cardinfo_get(space, attr); -+ if (value == NULL) -+ break; -+ strlcat(string, value, maxsize); -+ dbg("substitute cardinfo{%s} '%s'", attr, value); -+ } -+ break; -+ case SUBST_CTL: -+ if (attr == NULL) -+ Perror(space, "missing identification parametr for ctl"); -+ else { -+ const char *value = elemid_get(space, attr); -+ if (value == NULL) -+ break; -+ strlcat(string, value, maxsize); -+ dbg("substitute ctl{%s} '%s'", attr, value); -+ } -+ break; -+ case SUBST_RESULT: -+ if (space->program_result == NULL) -+ break; -+ /* get part part of the result string */ -+ i = 0; -+ if (attr != NULL) -+ i = strtoul(attr, &rest, 10); -+ if (i > 0) { -+ dbg("request part #%d of result string", i); -+ cpos = space->program_result; -+ while (--i) { -+ while (cpos[0] != '\0' && !isspace(cpos[0])) -+ cpos++; -+ while (isspace(cpos[0])) -+ cpos++; -+ } -+ if (i > 0) { -+ Perror(space, "requested part of result string not found"); -+ break; -+ } -+ strlcpy(temp2, cpos, sizeof(temp2)); -+ /* %{2+}c copies the whole string from the second part on */ -+ if (rest[0] != '+') { -+ cpos = strchr(temp2, ' '); -+ if (cpos) -+ cpos[0] = '\0'; -+ } -+ strlcat(string, temp2, maxsize); -+ dbg("substitute part of result string '%s'", temp2); -+ } else { -+ strlcat(string, space->program_result, maxsize); -+ dbg("substitute result string '%s'", space->program_result); -+ } -+ break; -+ case SUBST_ATTR: -+ if (attr == NULL) -+ Perror(space, "missing file parameter for attr"); -+ else { -+ const char *value = NULL; -+ size_t size; -+ -+ pair = value_find(space, "SYSFS_DEVICE"); -+ if (pair == NULL) -+ break; -+ value = sysfs_attr_get_value(pair->value, attr); -+ -+ if (value == NULL) -+ break; -+ -+ /* strip trailing whitespace and replace untrusted characters of sysfs value */ -+ size = strlcpy(temp2, value, sizeof(temp2)); -+ if (size >= sizeof(temp2)) -+ size = sizeof(temp2)-1; -+ while (size > 0 && isspace(temp2[size-1])) -+ temp2[--size] = '\0'; -+ count = replace_untrusted_chars(temp2); -+ if (count > 0) -+ Perror(space, "%i untrusted character(s) replaced" , count); -+ strlcat(string, temp2, maxsize); -+ dbg("substitute sysfs value '%s'", temp2); -+ } -+ break; -+ case SUBST_SYSFSROOT: -+ strlcat(string, sysfs_path, maxsize); -+ dbg("substitute sysfs_path '%s'", sysfs_path); -+ break; -+ case SUBST_ENV: -+ if (attr == NULL) { -+ dbg("missing attribute"); -+ break; -+ } -+ pos = getenv(attr); -+ if (pos == NULL) { -+ dbg("env '%s' not available", attr); -+ break; -+ } -+ dbg("substitute env '%s=%s'", attr, pos); -+ strlcat(string, pos, maxsize); -+ break; -+ default: -+ Perror(space, "unknown substitution type=%i", type); -+ break; -+ } -+ /* possibly truncate to format-char specified length */ -+ if (len != -1) { -+ head[len] = '\0'; -+ dbg("truncate to %i chars, subtitution string becomes '%s'", len, head); -+ } -+ strlcat(string, temp, maxsize); -+ } -+ /* unescape strings */ -+ head = tail = string; -+ while (*head != '\0') { -+ if (*head == '\\') { -+ head++; -+ if (*head == '\0') -+ break; -+ switch (*head) { -+ case 'a': *tail++ = '\a'; break; -+ case 'b': *tail++ = '\b'; break; -+ case 'n': *tail++ = '\n'; break; -+ case 'r': *tail++ = '\r'; break; -+ case 't': *tail++ = '\t'; break; -+ case 'v': *tail++ = '\v'; break; -+ case '\\': *tail++ = '\\'; break; -+ default: *tail++ = *head; break; -+ } -+ head++; -+ continue; -+ } -+ if (*head) -+ *tail++ = *head++; -+ } -+ *tail = 0; -+} -+ -+static int do_match(const char *key, enum key_op op, -+ const char *key_value, const char *value) -+{ -+ int match; -+ -+ if (value == NULL) -+ return 0; -+ dbg("match %s '%s' <-> '%s'", key, key_value, value); -+ match = fnmatch(key_value, value, 0) == 0; -+ if (match && op == KEY_OP_MATCH) { -+ dbg("%s is true (matching value)", key); -+ return 1; -+ } -+ if (!match && op == KEY_OP_NOMATCH) { -+ dbg("%s is true (non-matching value)", key); -+ return 1; -+ } -+ dbg("%s is false", key); -+ return 0; -+} -+ -+static int ctl_match(snd_ctl_elem_id_t *pattern, snd_ctl_elem_id_t *id) -+{ -+ if (snd_ctl_elem_id_get_interface(pattern) != -1 && -+ snd_ctl_elem_id_get_interface(pattern) != snd_ctl_elem_id_get_interface(id)) -+ return 0; -+ if (snd_ctl_elem_id_get_device(pattern) != -1 && -+ snd_ctl_elem_id_get_device(pattern) != snd_ctl_elem_id_get_device(id)) -+ return 0; -+ if (snd_ctl_elem_id_get_subdevice(pattern) != -1 && -+ snd_ctl_elem_id_get_subdevice(pattern) != snd_ctl_elem_id_get_subdevice(id)) -+ return 0; -+ if (snd_ctl_elem_id_get_index(pattern) != -1 && -+ snd_ctl_elem_id_get_index(pattern) != snd_ctl_elem_id_get_index(id)) -+ return 0; -+ if (fnmatch(snd_ctl_elem_id_get_name(pattern), snd_ctl_elem_id_get_name(id), 0) != 0) -+ return 0; -+ return 1; -+} -+ -+static -+int run_program1(struct space *space, -+ const char *command0, char *result, -+ size_t ressize, size_t *reslen, int log) -+{ -+ char *pos = strchr(command0, ' '); -+ int cmdlen = pos ? pos - command0 : strlen(command0); -+ int err, index; -+ snd_hctl_elem_t *elem; -+ snd_ctl_elem_id_t *id; -+ -+ if (cmdlen == 12 && strncmp(command0, "__ctl_search", 12) == 0) { -+ index = 0; -+ if (pos) -+ index = strtol(pos, NULL, 0); -+ err = snd_ctl_elem_id_malloc(&id); -+ if (err < 0) -+ return EXIT_FAILURE; -+ elem = snd_hctl_first_elem(space->ctl_handle); -+ while (elem) { -+ snd_hctl_elem_get_id(elem, id); -+ if (!ctl_match(space->ctl_id, id)) -+ goto next_search; -+ if (index > 0) { -+ index--; -+ goto next_search; -+ } -+ strlcpy(result, "0", ressize); -+ snd_ctl_elem_id_copy(space->ctl_id, id); -+ snd_ctl_elem_id_free(id); -+ dbg("__ctl_search found a control"); -+ return EXIT_SUCCESS; -+ next_search: -+ elem = snd_hctl_elem_next(elem); -+ } -+ snd_ctl_elem_id_free(id); -+ return EXIT_FAILURE; -+ } -+ if (cmdlen == 11 && strncmp(command0, "__ctl_count", 11) == 0) { -+ index = 0; -+ err = snd_ctl_elem_id_malloc(&id); -+ if (err < 0) -+ return EXIT_FAILURE; -+ elem = snd_hctl_first_elem(space->ctl_handle); -+ while (elem) { -+ snd_hctl_elem_get_id(elem, id); -+ if (!ctl_match(space->ctl_id, id)) -+ goto next_count; -+ index++; -+ next_count: -+ elem = snd_hctl_elem_next(elem); -+ } -+ snd_ctl_elem_id_free(id); -+ if (index > 0) { -+ snprintf(result, ressize, "%u", index); -+ dbg("__ctl_count found %s controls", result); -+ return EXIT_SUCCESS; -+ } -+ dbg("__ctl_count no match"); -+ return EXIT_FAILURE; -+ } -+ if (cmdlen == 11 && strncmp(command0, "__ctl_write", 11) == 0) { -+ } -+ Perror(space, "unknown buildin command '%s'", command0); -+ return EXIT_FAILURE; -+} -+ -+static int parse(struct space *space, const char *filename); -+ -+static char *new_root_dir(const char *filename) -+{ -+ char *res, *tmp; -+ -+ res = strdup(filename); -+ if (res) { -+ tmp = rindex(res, '/'); -+ if (tmp) -+ *tmp = '\0'; -+ } -+ dbg("new_root_dir '%s' '%s'", filename, res); -+ return res; -+} -+ -+static int parse_line(struct space *space, char *line, size_t linesize) -+{ -+ char *linepos; -+ char *key, *value, *attr, *temp; -+ struct pair *pair; -+ enum key_op op; -+ int err = 0, count; -+ char string[PATH_SIZE]; -+ char result[PATH_SIZE]; -+ -+ linepos = line; -+ while (*linepos != '\0') { -+ op = KEY_OP_UNSET; -+ -+ err = get_key(&linepos, &key, &op, &value); -+ if (err < 0) -+ break; -+ -+ if (strncasecmp(key, "LABEL", 5) == 0) { -+ if (op != KEY_OP_ASSIGN) { -+ Perror(space, "invalid LABEL operation"); -+ goto invalid; -+ } -+ if (space->go_to && strcmp(space->go_to, value) == 0) { -+ free(space->go_to); -+ space->go_to = NULL; -+ } -+ continue; -+ } -+ -+ if (space->go_to) { -+ dbg("skip (GOTO '%s')", space->go_to); -+ break; /* not for us */ -+ } -+ -+ if (strncasecmp(key, "CTL{", 4) == 0) { -+ attr = get_key_attribute(space, key + 3, string, sizeof(string)); -+ if (attr == NULL) { -+ Perror(space, "error parsing CTL attribute"); -+ goto invalid; -+ } -+ if (op == KEY_OP_ASSIGN) { -+ strlcpy(result, value, sizeof(result)); -+ apply_format(space, result, sizeof(result)); -+ dbg("ctl assign: '%s' '%s'", value, attr); -+ err = elemid_set(space, attr, result); -+ if (space->program_result) { -+ free(space->program_result); -+ space->program_result = NULL; -+ } -+ snprintf(string, sizeof(string), "%i", err); -+ space->program_result = strdup(string); -+ if (err < 0 || space->program_result == NULL) { -+ err = 0; -+ break; -+ } -+ } else if (op == KEY_OP_MATCH || op == KEY_OP_NOMATCH) { -+ dbg("ctl match: '%s' '%s'", value, attr); -+ temp = (char *)elemid_get(space, attr); -+ if (!do_match(key, op, value, temp)) -+ break; -+ } else { -+ Perror(space, "invalid CTL{} operation"); -+ goto invalid; -+ } -+ continue; -+ } -+ if (strcasecmp(key, "RESULT") == 0) { -+ if (op == KEY_OP_MATCH || op == KEY_OP_NOMATCH) { -+ if (!do_match(key, op, value, space->program_result)) -+ break; -+ } else { -+ Perror(space, "invalid RESULT operation"); -+ goto invalid; -+ } -+ continue; -+ } -+ if (strcasecmp(key, "PROGRAM") == 0) { -+ if (op == KEY_OP_UNSET) -+ continue; -+ strlcpy(string, value, sizeof(string)); -+ apply_format(space, string, sizeof(string)); -+ if (space->program_result) { -+ free(space->program_result); -+ space->program_result = NULL; -+ } -+ if (run_program(space, string, result, sizeof(result), NULL, space->log_run) != 0) { -+ dbg("PROGRAM '%s' is false", string); -+ if (op != KEY_OP_NOMATCH) -+ break; -+ } else { -+ remove_trailing_chars(result, '\n'); -+ count = replace_untrusted_chars(result); -+ if (count) -+ info("%i untrusted character(s) replaced", count); -+ dbg("PROGRAM '%s' result is '%s'", string, result); -+ space->program_result = strdup(result); -+ if (space->program_result == NULL) -+ break; -+ dbg("PROGRAM returned successful"); -+ if (op == KEY_OP_NOMATCH) -+ break; -+ } -+ dbg("PROGRAM key is true"); -+ continue; -+ } -+ if (strncasecmp(key, "CARDINFO{", 9) == 0) { -+ attr = get_key_attribute(space, key + 8, string, sizeof(string)); -+ if (attr == NULL) { -+ Perror(space, "error parsing CARDINFO attribute"); -+ goto invalid; -+ } -+ if (op == KEY_OP_MATCH || op == KEY_OP_NOMATCH) { -+ dbg("cardinfo: '%s' '%s'", value, attr); -+ temp = (char *)cardinfo_get(space, attr); -+ if (!do_match(key, op, value, temp)) -+ break; -+ } else { -+ Perror(space, "invalid CARDINFO{} operation"); -+ goto invalid; -+ } -+ continue; -+ } -+ if (strncasecmp(key, "ATTR{", 5) == 0) { -+ attr = get_key_attribute(space, key + 4, string, sizeof(string)); -+ if (attr == NULL) { -+ Perror(space, "error parsing ATTR attribute"); -+ goto invalid; -+ } -+ if (op == KEY_OP_MATCH || op == KEY_OP_NOMATCH) { -+ pair = value_find(space, "SYSFS_DEVICE"); -+ if (pair == NULL) -+ break; -+ dbg("sysfs_attr: '%s' '%s'", pair->value, attr); -+ temp = sysfs_attr_get_value(pair->value, attr); -+ if (!do_match(key, op, value, temp)) -+ break; -+ } else { -+ Perror(space, "invalid ATTR{} operation"); -+ goto invalid; -+ } -+ continue; -+ } -+ if (strncasecmp(key, "ENV{", 4) == 0) { -+ attr = get_key_attribute(space, key + 3, string, sizeof(string)); -+ if (attr == NULL) { -+ Perror(space, "error parsing ENV attribute"); -+ goto invalid; -+ } -+ if (op == KEY_OP_MATCH || op == KEY_OP_NOMATCH) { -+ temp = getenv(attr); -+ dbg("env: '%s' '%s'", attr, temp); -+ if (!do_match(key, op, value, temp)) -+ break; -+ } else { -+ Perror(space, "invalid ENV{} operation"); -+ goto invalid; -+ } -+ continue; -+ } -+ if (strcasecmp(key, "GOTO") == 0) { -+ if (op != KEY_OP_ASSIGN) { -+ Perror(space, "invalid GOTO operation"); -+ goto invalid; -+ } -+ space->go_to = strdup(value); -+ if (space->go_to == NULL) { -+ err = -ENOMEM; -+ break; -+ } -+ continue; -+ } -+ if (strcasecmp(key, "INCLUDE") == 0) { -+ char *rootdir, *go_to; -+ const char *filename; -+ int linenum; -+ if (op != KEY_OP_ASSIGN) { -+ Perror(space, "invalid INCLUDE operation"); -+ goto invalid; -+ } -+ if (value[0] == '/') -+ strlcpy(string, value, sizeof(string)); -+ else { -+ strlcpy(string, space->rootdir, sizeof(string)); -+ strlcat(string, "/", sizeof(string)); -+ strlcat(string, value, sizeof(string)); -+ } -+ rootdir = space->rootdir; -+ go_to = space->go_to; -+ filename = space->filename; -+ linenum = space->linenum; -+ space->go_to = NULL; -+ space->rootdir = new_root_dir(string); -+ if (space->rootdir) { -+ err = parse(space, string); -+ free(space->rootdir); -+ } else -+ err = -ENOMEM; -+ if (space->go_to) { -+ Perror(space, "unterminated GOTO '%s'", space->go_to); -+ free(space->go_to); -+ } -+ space->go_to = go_to; -+ space->rootdir = rootdir; -+ space->filename = filename; -+ space->linenum = linenum; -+ if (space->quit) -+ break; -+ continue; -+ } -+ if (strncasecmp(key, "PRINT", 5) == 0) { -+ strlcpy(string, value, sizeof(string)); -+ apply_format(space, string, sizeof(string)); -+ fwrite(string, strlen(string), 1, stdout); -+ continue; -+ } -+ if (strncasecmp(key, "ERROR", 5) == 0) { -+ strlcpy(string, value, sizeof(string)); -+ apply_format(space, string, sizeof(string)); -+ fwrite(string, strlen(string), 1, stderr); -+ continue; -+ } -+ if (strncasecmp(key, "EXIT", 4) == 0) { -+ strlcpy(string, value, sizeof(string)); -+ apply_format(space, string, sizeof(string)); -+ space->exit_code = strtol(string, NULL, 0); -+ space->quit = 1; -+ break; -+ } -+ if (strncasecmp(key, "SYSFS_DEVICE", 12) == 0) { -+ strlcpy(string, value, sizeof(string)); -+ apply_format(space, string, sizeof(string)); -+ err = value_set(space, key, string); -+ dbg("SYSFS_DEVICE='%s'", string); -+ break; -+ } -+ -+ Perror(space, "unknown key '%s'", key); -+ } -+ return err; -+ -+invalid: -+ Perror(space, "invalid rule"); -+ return -EINVAL; -+} -+ -+static int parse(struct space *space, const char *filename) -+{ -+ char *buf, *bufline, *line; -+ size_t bufsize, pos, count, linesize; -+ unsigned int linenum, i, j, linenum_adj; -+ int err; -+ -+ dbg("start of file '%s'", filename); -+ -+ if (file_map(filename, &buf, &bufsize) != 0) { -+ err = errno; -+ error("Unable to open file '%s': %s", filename, strerror(err)); -+ return -err; -+ } -+ -+ err = 0; -+ pos = 0; -+ linenum = 0; -+ linesize = 128; -+ line = malloc(linesize); -+ if (line == NULL) -+ return -ENOMEM; -+ space->filename = filename; -+ while (!err && pos < bufsize && !space->quit) { -+ count = line_width(buf, bufsize, pos); -+ bufline = buf + pos; -+ pos += count + 1; -+ linenum++; -+ -+ /* skip whitespaces */ -+ while (count > 0 && isspace(bufline[0])) { -+ bufline++; -+ count--; -+ } -+ if (count == 0) -+ continue; -+ -+ /* comment check */ -+ if (bufline[0] == '#') -+ continue; -+ -+ if (count > linesize - 1) { -+ free(line); -+ linesize = (count + 127 + 1) & ~127; -+ if (linesize > 2048) { -+ error("file %s, line %i too long", filename, linenum); -+ err = -EINVAL; -+ break; -+ } -+ line = malloc(linesize); -+ if (line == NULL) { -+ err = -EINVAL; -+ break; -+ } -+ } -+ -+ /* skip backslash and newline from multiline rules */ -+ linenum_adj = 0; -+ for (i = j = 0; i < count; i++) { -+ if (bufline[i] == '\\' && bufline[i+1] == '\n') { -+ linenum_adj++; -+ continue; -+ } -+ line[j++] = bufline[i]; -+ } -+ line[j] = '\0'; -+ -+ dbg("read (%i) '%s'", linenum, line); -+ space->linenum = linenum; -+ err = parse_line(space, line, linesize); -+ linenum += linenum_adj; -+ } -+ -+ space->filename = NULL; -+ space->linenum = -1; -+ file_unmap(buf, bufsize); -+ dbg("end of file '%s'", filename); -+ return err; -+} -+ -+int init(const char *filename, const char *cardname) -+{ -+ struct space *space; -+ int err = 0, card, first; -+ -+ sysfs_init(); -+ if (!cardname) { -+ first = 1; -+ card = -1; -+ while (1) { -+ if (snd_card_next(&card) < 0) -+ break; -+ if (card < 0) { -+ if (first) { -+ error("No soundcards found..."); -+ return -ENODEV; -+ } -+ break; -+ } -+ first = 0; -+ err = init_space(&space, card); -+ if (err == 0 && -+ (space->rootdir = new_root_dir(filename)) != NULL) -+ err = parse(space, filename); -+ free_space(space); -+ if (err < 0) -+ break; -+ } -+ } else { -+ card = snd_card_get_index(cardname); -+ if (card < 0) { -+ error("Cannot find soundcard '%s'...", cardname); -+ goto error; -+ } -+ memset(&space, 0, sizeof(space)); -+ err = init_space(&space, card); -+ if (err == 0 && -+ (space->rootdir = new_root_dir(filename)) != NULL) -+ err = parse(space, filename); -+ free_space(space); -+ } -+ error: -+ sysfs_cleanup(); -+ return err; -+} -diff --git a/alsactl/init_sysdeps.c b/alsactl/init_sysdeps.c -new file mode 100644 -index 0000000..f263138 ---- /dev/null -+++ b/alsactl/init_sysdeps.c -@@ -0,0 +1,63 @@ -+/* -+ * Copyright (C) 2003 Greg Kroah-Hartman -+ * Copyright (C) 2005-2006 Kay Sievers -+ * -+ * This program is free software; you can redistribute it and/or modify it -+ * under the terms of the GNU General Public License as published by the -+ * Free Software Foundation version 2 of the License. -+ * -+ * This program is distributed in the hope that it will be useful, but -+ * WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -+ * General Public License for more details. -+ * -+ * You should have received a copy of the GNU General Public License along -+ * with this program; if not, write to the Free Software Foundation, Inc., -+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -+ * -+ */ -+ -+#ifdef __GLIBC__ -+static size_t strlcpy(char *dst, const char *src, size_t size) -+{ -+ size_t bytes = 0; -+ char *q = dst; -+ const char *p = src; -+ char ch; -+ -+ while ((ch = *p++)) { -+ if (bytes+1 < size) -+ *q++ = ch; -+ bytes++; -+ } -+ -+ /* If size == 0 there is no space for a final null... */ -+ if (size) -+ *q = '\0'; -+ return bytes; -+} -+ -+static size_t strlcat(char *dst, const char *src, size_t size) -+{ -+ size_t bytes = 0; -+ char *q = dst; -+ const char *p = src; -+ char ch; -+ -+ while (bytes < size && *q) { -+ q++; -+ bytes++; -+ } -+ if (bytes == size) -+ return (bytes + strlen(src)); -+ -+ while ((ch = *p++)) { -+ if (bytes+1 < size) -+ *q++ = ch; -+ bytes++; -+ } -+ -+ *q = '\0'; -+ return bytes; -+} -+#endif /* __GLIBC__ */ -diff --git a/alsactl/init_sysfs.c b/alsactl/init_sysfs.c -new file mode 100644 -index 0000000..0cbada2 ---- /dev/null -+++ b/alsactl/init_sysfs.c -@@ -0,0 +1,158 @@ -+/* -+ * Copyright (C) 2005-2006 Kay Sievers -+ * 2008 Jaroslav Kysela -+ * -+ * This program is free software; you can redistribute it and/or modify it -+ * under the terms of the GNU General Public License as published by the -+ * Free Software Foundation version 2 of the License. -+ * -+ * This program is distributed in the hope that it will be useful, but -+ * WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -+ * General Public License for more details. -+ * -+ * You should have received a copy of the GNU General Public License along -+ * with this program; if not, write to the Free Software Foundation, Inc., -+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -+ * -+ */ -+ -+ -+static char sysfs_path[PATH_SIZE]; -+ -+/* attribute value cache */ -+static LIST_HEAD(attr_list); -+struct sysfs_attr { -+ struct list_head node; -+ char path[PATH_SIZE]; -+ char *value; /* points to value_local if value is cached */ -+ char value_local[NAME_SIZE]; -+}; -+ -+static int sysfs_init(void) -+{ -+ const char *env; -+ char sysfs_test[PATH_SIZE]; -+ -+ env = getenv("SYSFS_PATH"); -+ if (env) { -+ strlcpy(sysfs_path, env, sizeof(sysfs_path)); -+ remove_trailing_chars(sysfs_path, '/'); -+ } else -+ strlcpy(sysfs_path, "/sys", sizeof(sysfs_path)); -+ dbg("sysfs_path='%s'", sysfs_path); -+ -+ strlcpy(sysfs_test, sysfs_path, sizeof(sysfs_test)); -+ strlcat(sysfs_test, "/kernel/uevent_helper", sizeof(sysfs_test)); -+ if (access(sysfs_test, F_OK)) { -+ error("sysfs path '%s' is invalid\n", sysfs_path); -+ return -errno; -+ } -+ -+ INIT_LIST_HEAD(&attr_list); -+ return 0; -+} -+ -+static void sysfs_cleanup(void) -+{ -+ struct sysfs_attr *attr_loop; -+ struct sysfs_attr *attr_temp; -+ -+ list_for_each_entry_safe(attr_loop, attr_temp, &attr_list, node) { -+ list_del(&attr_loop->node); -+ free(attr_loop); -+ } -+} -+ -+static char *sysfs_attr_get_value(const char *devpath, const char *attr_name) -+{ -+ char path_full[PATH_SIZE]; -+ const char *path; -+ char value[NAME_SIZE]; -+ struct sysfs_attr *attr_loop; -+ struct sysfs_attr *attr; -+ struct stat statbuf; -+ int fd; -+ ssize_t size; -+ size_t sysfs_len; -+ -+ dbg("open '%s'/'%s'", devpath, attr_name); -+ sysfs_len = strlcpy(path_full, sysfs_path, sizeof(path_full)); -+ path = &path_full[sysfs_len]; -+ strlcat(path_full, devpath, sizeof(path_full)); -+ strlcat(path_full, "/", sizeof(path_full)); -+ strlcat(path_full, attr_name, sizeof(path_full)); -+ -+ /* look for attribute in cache */ -+ list_for_each_entry(attr_loop, &attr_list, node) { -+ if (strcmp(attr_loop->path, path) == 0) { -+ dbg("found in cache '%s'", attr_loop->path); -+ return attr_loop->value; -+ } -+ } -+ -+ /* store attribute in cache (also negatives are kept in cache) */ -+ dbg("new uncached attribute '%s'", path_full); -+ attr = malloc(sizeof(struct sysfs_attr)); -+ if (attr == NULL) -+ return NULL; -+ memset(attr, 0x00, sizeof(struct sysfs_attr)); -+ strlcpy(attr->path, path, sizeof(attr->path)); -+ dbg("add to cache '%s'", path_full); -+ list_add(&attr->node, &attr_list); -+ -+ if (lstat(path_full, &statbuf) != 0) { -+ dbg("stat '%s' failed: %s", path_full, strerror(errno)); -+ goto out; -+ } -+ -+ if (S_ISLNK(statbuf.st_mode)) { -+ /* links return the last element of the target path */ -+ char link_target[PATH_SIZE]; -+ int len; -+ const char *pos; -+ -+ len = readlink(path_full, link_target, sizeof(link_target)); -+ if (len > 0) { -+ link_target[len] = '\0'; -+ pos = strrchr(link_target, '/'); -+ if (pos != NULL) { -+ dbg("cache '%s' with link value '%s'", path_full, pos+1); -+ strlcpy(attr->value_local, &pos[1], sizeof(attr->value_local)); -+ attr->value = attr->value_local; -+ } -+ } -+ goto out; -+ } -+ -+ /* skip directories */ -+ if (S_ISDIR(statbuf.st_mode)) -+ goto out; -+ -+ /* skip non-readable files */ -+ if ((statbuf.st_mode & S_IRUSR) == 0) -+ goto out; -+ -+ /* read attribute value */ -+ fd = open(path_full, O_RDONLY); -+ if (fd < 0) { -+ dbg("attribute '%s' does not exist", path_full); -+ goto out; -+ } -+ size = read(fd, value, sizeof(value)); -+ close(fd); -+ if (size < 0) -+ goto out; -+ if (size == sizeof(value)) -+ goto out; -+ -+ /* got a valid value, store and return it */ -+ value[size] = '\0'; -+ remove_trailing_chars(value, '\n'); -+ dbg("cache '%s' with attribute value '%s'", path_full, value); -+ strlcpy(attr->value_local, value, sizeof(attr->value_local)); -+ attr->value = attr->value_local; -+ -+out: -+ return attr->value; -+} -diff --git a/alsactl/init_utils_run.c b/alsactl/init_utils_run.c -new file mode 100644 -index 0000000..dde490b ---- /dev/null -+++ b/alsactl/init_utils_run.c -@@ -0,0 +1,247 @@ -+/* -+ * Copyright (C) 2004-2005 Kay Sievers -+ * -+ * This program is free software; you can redistribute it and/or modify it -+ * under the terms of the GNU General Public License as published by the -+ * Free Software Foundation version 2 of the License. -+ * -+ * This program is distributed in the hope that it will be useful, but -+ * WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -+ * General Public License for more details. -+ * -+ * You should have received a copy of the GNU General Public License along -+ * with this program; if not, write to the Free Software Foundation, Inc., -+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -+ * -+ */ -+ -+#define MY_MAX(a,b) ((a) > (b) ? (a) : (b)) -+ -+#define READ_END 0 -+#define WRITE_END 1 -+ -+static -+int run_program1(struct space *space, -+ const char *command0, char *result, -+ size_t ressize, size_t *reslen, int log); -+ -+static -+int run_program0(struct space *space, -+ const char *command0, char *result, -+ size_t ressize, size_t *reslen, int log) -+{ -+ int retval = 0; -+ int status; -+ int outpipe[2] = {-1, -1}; -+ int errpipe[2] = {-1, -1}; -+ pid_t pid; -+ char arg[PATH_SIZE]; -+ char program[PATH_SIZE]; -+ char *argv[(sizeof(arg) / 2) + 1]; -+ int devnull; -+ int i; -+ -+ /* build argv from comand */ -+ strlcpy(arg, command0, sizeof(arg)); -+ i = 0; -+ if (strchr(arg, ' ') != NULL) { -+ char *pos = arg; -+ -+ while (pos != NULL) { -+ if (pos[0] == '\'') { -+ /* don't separate if in apostrophes */ -+ pos++; -+ argv[i] = strsep(&pos, "\'"); -+ while (pos != NULL && pos[0] == ' ') -+ pos++; -+ } else { -+ argv[i] = strsep(&pos, " "); -+ } -+ dbg("arg[%i] '%s'", i, argv[i]); -+ i++; -+ } -+ argv[i] = NULL; -+ } else { -+ argv[0] = arg; -+ argv[1] = NULL; -+ } -+ info("'%s'", command0); -+ -+ /* prepare pipes from child to parent */ -+ if (result || log) { -+ if (pipe(outpipe) != 0) { -+ Perror(space, "pipe failed: %s", strerror(errno)); -+ return -1; -+ } -+ } -+ if (log) { -+ if (pipe(errpipe) != 0) { -+ Perror(space, "pipe failed: %s", strerror(errno)); -+ return -1; -+ } -+ } -+ -+ /* allow programs in /lib/alsa called without the path */ -+ if (strchr(argv[0], '/') == NULL) { -+ strlcpy(program, "/lib/alsa/", sizeof(program)); -+ strlcat(program, argv[0], sizeof(program)); -+ argv[0] = program; -+ } -+ -+ pid = fork(); -+ switch(pid) { -+ case 0: -+ /* child closes parent ends of pipes */ -+ if (outpipe[READ_END] > 0) -+ close(outpipe[READ_END]); -+ if (errpipe[READ_END] > 0) -+ close(errpipe[READ_END]); -+ -+ /* discard child output or connect to pipe */ -+ devnull = open("/dev/null", O_RDWR); -+ if (devnull > 0) { -+ dup2(devnull, STDIN_FILENO); -+ if (outpipe[WRITE_END] < 0) -+ dup2(devnull, STDOUT_FILENO); -+ if (errpipe[WRITE_END] < 0) -+ dup2(devnull, STDERR_FILENO); -+ close(devnull); -+ } else -+ Perror(space, "open /dev/null failed: %s", strerror(errno)); -+ if (outpipe[WRITE_END] > 0) { -+ dup2(outpipe[WRITE_END], STDOUT_FILENO); -+ close(outpipe[WRITE_END]); -+ } -+ if (errpipe[WRITE_END] > 0) { -+ dup2(errpipe[WRITE_END], STDERR_FILENO); -+ close(errpipe[WRITE_END]); -+ } -+ execv(argv[0], argv); -+ -+ /* we should never reach this */ -+ Perror(space, "exec of program '%s' failed", argv[0]); -+ _exit(1); -+ case -1: -+ Perror(space, "fork of '%s' failed: %s", argv[0], strerror(errno)); -+ return -1; -+ default: -+ /* read from child if requested */ -+ if (outpipe[READ_END] > 0 || errpipe[READ_END] > 0) { -+ ssize_t count; -+ size_t respos = 0; -+ -+ /* parent closes child ends of pipes */ -+ if (outpipe[WRITE_END] > 0) -+ close(outpipe[WRITE_END]); -+ if (errpipe[WRITE_END] > 0) -+ close(errpipe[WRITE_END]); -+ -+ /* read child output */ -+ while (outpipe[READ_END] > 0 || errpipe[READ_END] > 0) { -+ int fdcount; -+ fd_set readfds; -+ -+ FD_ZERO(&readfds); -+ if (outpipe[READ_END] > 0) -+ FD_SET(outpipe[READ_END], &readfds); -+ if (errpipe[READ_END] > 0) -+ FD_SET(errpipe[READ_END], &readfds); -+ fdcount = select(MY_MAX(outpipe[READ_END], errpipe[READ_END])+1, &readfds, NULL, NULL, NULL); -+ if (fdcount < 0) { -+ if (errno == EINTR) -+ continue; -+ retval = -1; -+ break; -+ } -+ -+ /* get stdout */ -+ if (outpipe[READ_END] > 0 && FD_ISSET(outpipe[READ_END], &readfds)) { -+ char inbuf[1024]; -+ char *pos; -+ char *line; -+ -+ count = read(outpipe[READ_END], inbuf, sizeof(inbuf)-1); -+ if (count <= 0) { -+ close(outpipe[READ_END]); -+ outpipe[READ_END] = -1; -+ if (count < 0) { -+ Perror(space, "stdin read failed: %s", strerror(errno)); -+ retval = -1; -+ } -+ continue; -+ } -+ inbuf[count] = '\0'; -+ -+ /* store result for rule processing */ -+ if (result) { -+ if (respos + count < ressize) { -+ memcpy(&result[respos], inbuf, count); -+ respos += count; -+ } else { -+ Perror(space, "ressize %ld too short", (long)ressize); -+ retval = -1; -+ } -+ } -+ pos = inbuf; -+ while ((line = strsep(&pos, "\n"))) -+ if (pos || line[0] != '\0') -+ info("'%s' (stdout) '%s'", argv[0], line); -+ } -+ -+ /* get stderr */ -+ if (errpipe[READ_END] > 0 && FD_ISSET(errpipe[READ_END], &readfds)) { -+ char errbuf[1024]; -+ char *pos; -+ char *line; -+ -+ count = read(errpipe[READ_END], errbuf, sizeof(errbuf)-1); -+ if (count <= 0) { -+ close(errpipe[READ_END]); -+ errpipe[READ_END] = -1; -+ if (count < 0) -+ Perror(space, "stderr read failed: %s", strerror(errno)); -+ continue; -+ } -+ errbuf[count] = '\0'; -+ pos = errbuf; -+ while ((line = strsep(&pos, "\n"))) -+ if (pos || line[0] != '\0') -+ info("'%s' (stderr) '%s'", argv[0], line); -+ } -+ } -+ if (outpipe[READ_END] > 0) -+ close(outpipe[READ_END]); -+ if (errpipe[READ_END] > 0) -+ close(errpipe[READ_END]); -+ -+ /* return the childs stdout string */ -+ if (result) { -+ result[respos] = '\0'; -+ dbg("result='%s'", result); -+ if (reslen) -+ *reslen = respos; -+ } -+ } -+ waitpid(pid, &status, 0); -+ if (WIFEXITED(status)) { -+ info("'%s' returned with status %i", argv[0], WEXITSTATUS(status)); -+ if (WEXITSTATUS(status) != 0) -+ retval = -1; -+ } else { -+ Perror(space, "'%s' abnormal exit", argv[0]); -+ retval = -1; -+ } -+ } -+ -+ return retval; -+} -+ -+static -+int run_program(struct space *space, const char *command0, char *result, -+ size_t ressize, size_t *reslen, int log) -+{ -+ if (command0[0] == '_' && command0[1] == '_') -+ return run_program1(space, command0, result, ressize, reslen, log); -+ return run_program0(space, command0, result, ressize, reslen, log); -+} -diff --git a/alsactl/init_utils_string.c b/alsactl/init_utils_string.c -new file mode 100644 -index 0000000..2598e9f ---- /dev/null -+++ b/alsactl/init_utils_string.c -@@ -0,0 +1,194 @@ -+/* -+ * Copyright (C) 2004-2005 Kay Sievers -+ * -+ * This program is free software; you can redistribute it and/or modify it -+ * under the terms of the GNU General Public License as published by the -+ * Free Software Foundation version 2 of the License. -+ * -+ * This program is distributed in the hope that it will be useful, but -+ * WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -+ * General Public License for more details. -+ * -+ * You should have received a copy of the GNU General Public License along -+ * with this program; if not, write to the Free Software Foundation, Inc., -+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -+ * -+ */ -+ -+ -+static int string_is_true(const char *str) -+{ -+ if (strcasecmp(str, "true") == 0) -+ return 1; -+ if (strcasecmp(str, "yes") == 0) -+ return 1; -+ if (strcasecmp(str, "1") == 0) -+ return 1; -+ return 0; -+} -+ -+static void remove_trailing_chars(char *path, char c) -+{ -+ size_t len; -+ -+ len = strlen(path); -+ while (len > 0 && path[len-1] == c) -+ path[--len] = '\0'; -+} -+ -+/* count of characters used to encode one unicode char */ -+static int utf8_encoded_expected_len(const char *str) -+{ -+ unsigned char c = (unsigned char)str[0]; -+ -+ if (c < 0x80) -+ return 1; -+ if ((c & 0xe0) == 0xc0) -+ return 2; -+ if ((c & 0xf0) == 0xe0) -+ return 3; -+ if ((c & 0xf8) == 0xf0) -+ return 4; -+ if ((c & 0xfc) == 0xf8) -+ return 5; -+ if ((c & 0xfe) == 0xfc) -+ return 6; -+ return 0; -+} -+ -+/* decode one unicode char */ -+static int utf8_encoded_to_unichar(const char *str) -+{ -+ int unichar; -+ int len; -+ int i; -+ -+ len = utf8_encoded_expected_len(str); -+ switch (len) { -+ case 1: -+ return (int)str[0]; -+ case 2: -+ unichar = str[0] & 0x1f; -+ break; -+ case 3: -+ unichar = (int)str[0] & 0x0f; -+ break; -+ case 4: -+ unichar = (int)str[0] & 0x07; -+ break; -+ case 5: -+ unichar = (int)str[0] & 0x03; -+ break; -+ case 6: -+ unichar = (int)str[0] & 0x01; -+ break; -+ default: -+ return -1; -+ } -+ -+ for (i = 1; i < len; i++) { -+ if (((int)str[i] & 0xc0) != 0x80) -+ return -1; -+ unichar <<= 6; -+ unichar |= (int)str[i] & 0x3f; -+ } -+ -+ return unichar; -+} -+ -+/* expected size used to encode one unicode char */ -+static int utf8_unichar_to_encoded_len(int unichar) -+{ -+ if (unichar < 0x80) -+ return 1; -+ if (unichar < 0x800) -+ return 2; -+ if (unichar < 0x10000) -+ return 3; -+ if (unichar < 0x200000) -+ return 4; -+ if (unichar < 0x4000000) -+ return 5; -+ return 6; -+} -+ -+/* check if unicode char has a valid numeric range */ -+static int utf8_unichar_valid_range(int unichar) -+{ -+ if (unichar > 0x10ffff) -+ return 0; -+ if ((unichar & 0xfffff800) == 0xd800) -+ return 0; -+ if ((unichar > 0xfdcf) && (unichar < 0xfdf0)) -+ return 0; -+ if ((unichar & 0xffff) == 0xffff) -+ return 0; -+ return 1; -+} -+ -+/* validate one encoded unicode char and return its length */ -+static int utf8_encoded_valid_unichar(const char *str) -+{ -+ int len; -+ int unichar; -+ int i; -+ -+ len = utf8_encoded_expected_len(str); -+ if (len == 0) -+ return -1; -+ -+ /* ascii is valid */ -+ if (len == 1) -+ return 1; -+ -+ /* check if expected encoded chars are available */ -+ for (i = 0; i < len; i++) -+ if ((str[i] & 0x80) != 0x80) -+ return -1; -+ -+ unichar = utf8_encoded_to_unichar(str); -+ -+ /* check if encoded length matches encoded value */ -+ if (utf8_unichar_to_encoded_len(unichar) != len) -+ return -1; -+ -+ /* check if value has valid range */ -+ if (!utf8_unichar_valid_range(unichar)) -+ return -1; -+ -+ return len; -+} -+ -+/* replace everything but whitelisted plain ascii and valid utf8 */ -+static int replace_untrusted_chars(char *str) -+{ -+ size_t i = 0; -+ int replaced = 0; -+ -+ while (str[i] != '\0') { -+ int len; -+ -+ /* valid printable ascii char */ -+ if ((str[i] >= '0' && str[i] <= '9') || -+ (str[i] >= 'A' && str[i] <= 'Z') || -+ (str[i] >= 'a' && str[i] <= 'z') || -+ strchr(" #$%+-./:=?@_,", str[i])) { -+ i++; -+ continue; -+ } -+ /* valid utf8 is accepted */ -+ len = utf8_encoded_valid_unichar(&str[i]); -+ if (len > 1) { -+ i += len; -+ continue; -+ } -+ -+ /* everything else is garbage */ -+ str[i] = '_'; -+ i++; -+ replaced++; -+ } -+ -+ return replaced; -+} -diff --git a/alsactl/list.h b/alsactl/list.h -new file mode 100644 -index 0000000..8626630 ---- /dev/null -+++ b/alsactl/list.h -@@ -0,0 +1,289 @@ -+/* -+ * Copied from the Linux kernel source tree, version 2.6.0-test1. -+ * -+ * Licensed under the GPL v2 as per the whole kernel source tree. -+ * -+ */ -+ -+#ifndef _LIST_H -+#define _LIST_H -+ -+/** -+ * container_of - cast a member of a structure out to the containing structure -+ * -+ * @ptr: the pointer to the member. -+ * @type: the type of the container struct this is embedded in. -+ * @member: the name of the member within the struct. -+ * -+ */ -+#define container_of(ptr, type, member) ({ \ -+ const typeof( ((type *)0)->member ) *__mptr = (ptr); \ -+ (type *)( (char *)__mptr - offsetof(type,member) );}) -+ -+/* -+ * These are non-NULL pointers that will result in page faults -+ * under normal circumstances, used to verify that nobody uses -+ * non-initialized list entries. -+ */ -+#define LIST_POISON1 ((void *) 0x00100100) -+#define LIST_POISON2 ((void *) 0x00200200) -+ -+/* -+ * Simple doubly linked list implementation. -+ * -+ * Some of the internal functions ("__xxx") are useful when -+ * manipulating whole lists rather than single entries, as -+ * sometimes we already know the next/prev entries and we can -+ * generate better code by using them directly rather than -+ * using the generic single-entry routines. -+ */ -+ -+struct list_head { -+ struct list_head *next, *prev; -+}; -+ -+#define LIST_HEAD_INIT(name) { &(name), &(name) } -+ -+#define LIST_HEAD(name) \ -+ struct list_head name = LIST_HEAD_INIT(name) -+ -+#define INIT_LIST_HEAD(ptr) do { \ -+ (ptr)->next = (ptr); (ptr)->prev = (ptr); \ -+} while (0) -+ -+/* -+ * Insert a new entry between two known consecutive entries. -+ * -+ * This is only for internal list manipulation where we know -+ * the prev/next entries already! -+ */ -+static inline void __list_add(struct list_head *new, -+ struct list_head *prev, -+ struct list_head *next) -+{ -+ next->prev = new; -+ new->next = next; -+ new->prev = prev; -+ prev->next = new; -+} -+ -+/** -+ * list_add - add a new entry -+ * @new: new entry to be added -+ * @head: list head to add it after -+ * -+ * Insert a new entry after the specified head. -+ * This is good for implementing stacks. -+ */ -+static inline void list_add(struct list_head *new, struct list_head *head) -+{ -+ __list_add(new, head, head->next); -+} -+ -+/** -+ * list_add_tail - add a new entry -+ * @new: new entry to be added -+ * @head: list head to add it before -+ * -+ * Insert a new entry before the specified head. -+ * This is useful for implementing queues. -+ */ -+static inline void list_add_tail(struct list_head *new, struct list_head *head) -+{ -+ __list_add(new, head->prev, head); -+} -+ -+/* -+ * Delete a list entry by making the prev/next entries -+ * point to each other. -+ * -+ * This is only for internal list manipulation where we know -+ * the prev/next entries already! -+ */ -+static inline void __list_del(struct list_head * prev, struct list_head * next) -+{ -+ next->prev = prev; -+ prev->next = next; -+} -+ -+/** -+ * list_del - deletes entry from list. -+ * @entry: the element to delete from the list. -+ * Note: list_empty on entry does not return true after this, the entry is -+ * in an undefined state. -+ */ -+static inline void list_del(struct list_head *entry) -+{ -+ __list_del(entry->prev, entry->next); -+ entry->next = LIST_POISON1; -+ entry->prev = LIST_POISON2; -+} -+ -+/** -+ * list_del_init - deletes entry from list and reinitialize it. -+ * @entry: the element to delete from the list. -+ */ -+static inline void list_del_init(struct list_head *entry) -+{ -+ __list_del(entry->prev, entry->next); -+ INIT_LIST_HEAD(entry); -+} -+ -+/** -+ * list_move - delete from one list and add as another's head -+ * @list: the entry to move -+ * @head: the head that will precede our entry -+ */ -+static inline void list_move(struct list_head *list, struct list_head *head) -+{ -+ __list_del(list->prev, list->next); -+ list_add(list, head); -+} -+ -+/** -+ * list_move_tail - delete from one list and add as another's tail -+ * @list: the entry to move -+ * @head: the head that will follow our entry -+ */ -+static inline void list_move_tail(struct list_head *list, -+ struct list_head *head) -+{ -+ __list_del(list->prev, list->next); -+ list_add_tail(list, head); -+} -+ -+/** -+ * list_empty - tests whether a list is empty -+ * @head: the list to test. -+ */ -+static inline int list_empty(struct list_head *head) -+{ -+ return head->next == head; -+} -+ -+static inline void __list_splice(struct list_head *list, -+ struct list_head *head) -+{ -+ struct list_head *first = list->next; -+ struct list_head *last = list->prev; -+ struct list_head *at = head->next; -+ -+ first->prev = head; -+ head->next = first; -+ -+ last->next = at; -+ at->prev = last; -+} -+ -+/** -+ * list_splice - join two lists -+ * @list: the new list to add. -+ * @head: the place to add it in the first list. -+ */ -+static inline void list_splice(struct list_head *list, struct list_head *head) -+{ -+ if (!list_empty(list)) -+ __list_splice(list, head); -+} -+ -+/** -+ * list_splice_init - join two lists and reinitialise the emptied list. -+ * @list: the new list to add. -+ * @head: the place to add it in the first list. -+ * -+ * The list at @list is reinitialised -+ */ -+static inline void list_splice_init(struct list_head *list, -+ struct list_head *head) -+{ -+ if (!list_empty(list)) { -+ __list_splice(list, head); -+ INIT_LIST_HEAD(list); -+ } -+} -+ -+/** -+ * list_entry - get the struct for this entry -+ * @ptr: the &struct list_head pointer. -+ * @type: the type of the struct this is embedded in. -+ * @member: the name of the list_struct within the struct. -+ */ -+#define list_entry(ptr, type, member) \ -+ container_of(ptr, type, member) -+ -+/** -+ * list_for_each - iterate over a list -+ * @pos: the &struct list_head to use as a loop counter. -+ * @head: the head for your list. -+ */ -+#define list_for_each(pos, head) \ -+ for (pos = (head)->next; pos != (head); \ -+ pos = pos->next) -+ -+/** -+ * __list_for_each - iterate over a list -+ * @pos: the &struct list_head to use as a loop counter. -+ * @head: the head for your list. -+ * -+ * This variant differs from list_for_each() in that it's the -+ * simplest possible list iteration code. -+ * Use this for code that knows the list to be very short (empty -+ * or 1 entry) most of the time. -+ */ -+#define __list_for_each(pos, head) \ -+ for (pos = (head)->next; pos != (head); pos = pos->next) -+ -+/** -+ * list_for_each_prev - iterate over a list backwards -+ * @pos: the &struct list_head to use as a loop counter. -+ * @head: the head for your list. -+ */ -+#define list_for_each_prev(pos, head) \ -+ for (pos = (head)->prev; pos != (head); pos = pos->prev) -+ -+/** -+ * list_for_each_safe - iterate over a list safe against removal of list entry -+ * @pos: the &struct list_head to use as a loop counter. -+ * @n: another &struct list_head to use as temporary storage -+ * @head: the head for your list. -+ */ -+#define list_for_each_safe(pos, n, head) \ -+ for (pos = (head)->next, n = pos->next; pos != (head); \ -+ pos = n, n = pos->next) -+ -+/** -+ * list_for_each_entry - iterate over list of given type -+ * @pos: the type * to use as a loop counter. -+ * @head: the head for your list. -+ * @member: the name of the list_struct within the struct. -+ */ -+#define list_for_each_entry(pos, head, member) \ -+ for (pos = list_entry((head)->next, typeof(*pos), member); \ -+ &pos->member != (head); \ -+ pos = list_entry(pos->member.next, typeof(*pos), member)) -+ -+/** -+ * list_for_each_entry_reverse - iterate backwards over list of given type. -+ * @pos: the type * to use as a loop counter. -+ * @head: the head for your list. -+ * @member: the name of the list_struct within the struct. -+ */ -+#define list_for_each_entry_reverse(pos, head, member) \ -+ for (pos = list_entry((head)->prev, typeof(*pos), member); \ -+ &pos->member != (head); \ -+ pos = list_entry(pos->member.prev, typeof(*pos), member)) -+ -+/** -+ * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry -+ * @pos: the type * to use as a loop counter. -+ * @n: another type * to use as temporary storage -+ * @head: the head for your list. -+ * @member: the name of the list_struct within the struct. -+ */ -+#define list_for_each_entry_safe(pos, n, head, member) \ -+ for (pos = list_entry((head)->next, typeof(*pos), member), \ -+ n = list_entry(pos->member.next, typeof(*pos), member); \ -+ &pos->member != (head); \ -+ pos = n, n = list_entry(n->member.next, typeof(*n), member)) -+ -+#endif /* _LIST_H */ -diff --git a/alsactl/state.c b/alsactl/state.c -index 6c7f853..90e58cd 100644 ---- a/alsactl/state.c -+++ b/alsactl/state.c -@@ -151,19 +151,6 @@ static char *tlv_to_str(unsigned int *tlv) - return s; - } - --static int hextodigit(int c) --{ -- if (c >= '0' && c <= '9') -- c -= '0'; -- else if (c >= 'a' && c <= 'f') -- c = c - 'a' + 10; -- else if (c >= 'A' && c <= 'F') -- c = c - 'A' + 10; -- else -- return -1; -- return c; --} -- - static unsigned int *str_to_tlv(const char *s) - { - int i, j, c, len; -diff --git a/alsactl/utils.c b/alsactl/utils.c -new file mode 100644 -index 0000000..eb22ecc ---- /dev/null -+++ b/alsactl/utils.c -@@ -0,0 +1,79 @@ -+/* -+ * Advanced Linux Sound Architecture Control Program - Support routines -+ * Copyright (c) by Jaroslav Kysela -+ * -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation; either version 2 of the License, or -+ * (at your option) any later version. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ * GNU General Public License for more details. -+ * -+ * You should have received a copy of the GNU General Public License -+ * along with this program; if not, write to the Free Software -+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -+ * -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "alsactl.h" -+ -+int file_map(const char *filename, char **buf, size_t *bufsize) -+{ -+ struct stat stats; -+ int fd; -+ -+ fd = open(filename, O_RDONLY); -+ if (fd < 0) { -+ return -1; -+ } -+ -+ if (fstat(fd, &stats) < 0) { -+ close(fd); -+ return -1; -+ } -+ -+ *buf = mmap(NULL, stats.st_size, PROT_READ, MAP_SHARED, fd, 0); -+ if (*buf == MAP_FAILED) { -+ close(fd); -+ return -1; -+ } -+ *bufsize = stats.st_size; -+ -+ close(fd); -+ -+ return 0; -+} -+ -+void file_unmap(void *buf, size_t bufsize) -+{ -+ munmap(buf, bufsize); -+} -+ -+size_t line_width(const char *buf, size_t bufsize, size_t pos) -+{ -+ int esc = 0; -+ size_t count; -+ -+ for (count = pos; count < bufsize; count++) { -+ if (!esc && buf[count] == '\n') -+ break; -+ esc = buf[count] == '\\'; -+ } -+ -+ return count - pos; -+} -diff --git a/iecset/iecbits.c b/iecset/iecbits.c -index 8c62749..84d439b 100644 ---- a/iecset/iecbits.c -+++ b/iecset/iecbits.c -@@ -26,17 +26,39 @@ struct category_str { - }; - - static struct category_str con_category[] = { -+ { IEC958_AES1_CON_GENERAL, "general" }, -+ -+ { IEC958_AES1_CON_IEC908_CD, "CD" }, -+ { IEC958_AES1_CON_NON_IEC908_CD, "non-IEC908 CD" }, -+ { IEC958_AES1_CON_MINI_DISC, "Mini-Disc" }, -+ { IEC958_AES1_CON_DVD, "DVD" }, -+ -+ { IEC958_AES1_CON_PCM_CODER, "PCM coder" }, -+ { IEC958_AES1_CON_MIXER, "digital signal mixer" }, -+ { IEC958_AES1_CON_RATE_CONVERTER, "rate converter" }, -+ { IEC958_AES1_CON_SAMPLER, "sampler" }, -+ { IEC958_AES1_CON_DSP, "digital sound processor" }, -+ - { IEC958_AES1_CON_DAT, "DAT" }, - { IEC958_AES1_CON_VCR, "VCR" }, -- { IEC958_AES1_CON_MICROPHONE, "microphone" }, -+ { IEC958_AES1_CON_DCC, "DCC" }, -+ { IEC958_AES1_CON_MAGNETIC_DISC, "magnetic disc" }, -+ -+ { IEC958_AES1_CON_DAB_JAPAN, "digital audio broadcast (Japan)" }, -+ { IEC958_AES1_CON_DAB_EUROPE, "digital audio broadcast (Europe)" }, -+ { IEC958_AES1_CON_DAB_USA, "digital audio broadcast (USA)" }, -+ { IEC958_AES1_CON_SOFTWARE, "software delivery" }, -+ - { IEC958_AES1_CON_SYNTHESIZER, "synthesizer" }, -- { IEC958_AES1_CON_RATE_CONVERTER, "rate converter" }, -- { IEC958_AES1_CON_MIXER, "mixer" }, -- { IEC958_AES1_CON_SAMPLER, "sampler" }, -- { IEC958_AES1_CON_PCM_CODER, "PCM coder" }, -- { IEC958_AES1_CON_IEC908_CD, "CD" }, -- { IEC958_AES1_CON_NON_IEC908_CD, "non-IEC908 CD" }, -- { IEC958_AES1_CON_GENERAL, "general" }, -+ { IEC958_AES1_CON_MICROPHONE, "microphone" }, -+ -+ { IEC958_AES1_CON_ADC, "ADC without copyright information" }, -+ -+ { IEC958_AES1_CON_ADC_COPYRIGHT, "ADC with copyright information" }, -+ -+ { IEC958_AES1_CON_SOLIDMEM_DIGITAL_RECORDER_PLAYER, "flash memory recorder/player" }, -+ -+ { IEC958_AES1_CON_EXPERIMENTAL, "experimental" }, - }; - - -@@ -57,14 +79,38 @@ void dump_iec958(snd_aes_iec958_t *iec) - } - printf("Rate: "); - switch (iec->status[3] & IEC958_AES3_CON_FS) { -+ case IEC958_AES3_CON_FS_22050: -+ printf("22050 Hz\n"); -+ break; -+ case IEC958_AES3_CON_FS_24000: -+ printf("24000 Hz\n"); -+ break; -+ case IEC958_AES3_CON_FS_32000: -+ printf("32000 Hz\n"); -+ break; - case IEC958_AES3_CON_FS_44100: - printf("44100 Hz\n"); - break; - case IEC958_AES3_CON_FS_48000: - printf("48000 Hz\n"); - break; -- case IEC958_AES3_CON_FS_32000: -- printf("32000 Hz\n"); -+ case IEC958_AES3_CON_FS_88200: -+ printf("88200 Hz\n"); -+ break; -+ case IEC958_AES3_CON_FS_96000: -+ printf("96000 Hz\n"); -+ break; -+ case IEC958_AES3_CON_FS_176400: -+ printf("176400 Hz\n"); -+ break; -+ case IEC958_AES3_CON_FS_192000: -+ printf("192000 Hz\n"); -+ break; -+ case IEC958_AES3_CON_FS_768000: -+ printf("768000 Hz\n"); -+ break; -+ case IEC958_AES3_CON_FS_NOTID: -+ printf("not indicated\n"); - break; - default: - printf("unknown\n"); -diff --git a/iecset/iecset.c b/iecset/iecset.c -index a2fe4d1..44c43ab 100644 ---- a/iecset/iecset.c -+++ b/iecset/iecset.c -@@ -58,7 +58,7 @@ static struct cmdtbl cmds[] = { - { "aud", IDX_NOAUDIO, CMD_BOOL_INV, - "audio (common)\n\ton = audio mode, off = non-audio mode" }, - { "rat", IDX_RATE, CMD_INT, -- "rate (common)\n\tsample rate in Hz" }, -+ "rate (common)\n\tsample rate in Hz (0 = not indicated)" }, - { "emp", IDX_EMP, CMD_INT, - "emphasis (common)\n\t0 = none, 1 = 50/15us, 2 = CCITT" }, - { "loc", IDX_UNLOCK, CMD_BOOL_INV, -@@ -194,14 +194,38 @@ static int update_iec958_status(snd_aes_iec958_t *iec958, int *parms) - } else { - iec958->status[3] &= ~IEC958_AES3_CON_FS; - switch (parms[IDX_RATE]) { -+ case 22050: -+ iec958->status[3] |= IEC958_AES3_CON_FS_22050; -+ break; -+ case 24000: -+ iec958->status[3] |= IEC958_AES3_CON_FS_24000; -+ break; -+ case 32000: -+ iec958->status[3] |= IEC958_AES3_CON_FS_32000; -+ break; - case 44100: - iec958->status[3] |= IEC958_AES3_CON_FS_44100; - break; - case 48000: - iec958->status[3] |= IEC958_AES3_CON_FS_48000; - break; -- case 32000: -- iec958->status[3] |= IEC958_AES3_CON_FS_32000; -+ case 88200: -+ iec958->status[3] |= IEC958_AES3_CON_FS_88200;; -+ break; -+ case 96000: -+ iec958->status[3] |= IEC958_AES3_CON_FS_96000; -+ break; -+ case 176400: -+ iec958->status[3] |= IEC958_AES3_CON_FS_176400; -+ break; -+ case 192000: -+ iec958->status[3] |= IEC958_AES3_CON_FS_192000; -+ break; -+ case 768000: -+ iec958->status[3] |= IEC958_AES3_CON_FS_768000; -+ break; -+ default: -+ iec958->status[3] |= IEC958_AES3_CON_FS_NOTID; - break; - } - } + cardname = argc - optind > 1 ? argv[optind + 1] : NULL; +- for (tmp = devfiles; cardname != NULL && tmp != NULL; tmp++) { ++ for (tmp = devfiles; cardname != NULL && *tmp != NULL; tmp++) { + int len = strlen(*tmp); + if (!strncmp(cardname, *tmp, len)) { + long l = strtol(cardname + len, NULL, 0); diff --git a/alsa-utils.changes b/alsa-utils.changes index cb68c0e..98477b7 100644 --- a/alsa-utils.changes +++ b/alsa-utils.changes @@ -1,3 +1,14 @@ +------------------------------------------------------------------- +Thu Sep 11 14:21:15 CEST 2008 - tiwai@suse.de + +- fix possible segfaults during parsing in alsactl + +------------------------------------------------------------------- +Wed Sep 10 17:58:04 CEST 2008 - tiwai@suse.de + +- updated to 1.0.18rc3: + * more alsactl init implementations + ------------------------------------------------------------------- Wed Aug 6 16:07:41 CEST 2008 - tiwai@suse.de diff --git a/alsa-utils.spec b/alsa-utils.spec index 1dcf321..1e2f3a9 100644 --- a/alsa-utils.spec +++ b/alsa-utils.spec @@ -1,5 +1,5 @@ # -# spec file for package alsa-utils (Version 1.0.17) +# spec file for package alsa-utils (Version 1.0.18) # # Copyright (c) 2008 SUSE LINUX Products GmbH, Nuernberg, Germany. # @@ -20,15 +20,15 @@ Name: alsa-utils BuildRequires: alsa-devel ncurses-devel xmlto -%define package_version 1.0.17 +%define package_version 1.0.18rc3 License: GPL v2 or later Group: Productivity/Multimedia/Sound/Players Provides: alsa-conf Requires: dialog pciutils AutoReqProv: on Summary: Advanced Linux Sound Architecture Utilities -Version: 1.0.17 -Release: 10 +Version: 1.0.18 +Release: 1 Source: ftp://ftp.alsa-project.org/pub/util/alsa-utils-%{package_version}.tar.bz2 Patch: alsa-utils-git-fixes.diff Url: http://www.alsa-project.org/ @@ -76,6 +76,11 @@ make DESTDIR=$RPM_BUILD_ROOT install %{_datadir}/alsa %changelog +* Thu Sep 11 2008 tiwai@suse.de +- fix possible segfaults during parsing in alsactl +* Wed Sep 10 2008 tiwai@suse.de +- updated to 1.0.18rc3: + * more alsactl init implementations * Wed Aug 06 2008 tiwai@suse.de - alsactl init implementation (still non-working status, though) - iecset utility updates for new IEC958 status bits