]> git.alsa-project.org Git - alsa-utils.git/commitdiff
alsactl init: Add CTL{do_search} and CTL{do_count} parsers
authorJaroslav Kysela <perex@perex.cz>
Thu, 8 Oct 2009 11:47:27 +0000 (13:47 +0200)
committerJaroslav Kysela <perex@perex.cz>
Thu, 8 Oct 2009 11:47:27 +0000 (13:47 +0200)
To increase configuration readability, add CTL{do_search} and CTL{do_count}
actions. The old PROGRAM=="__ctl_search" notion is also allowed.

Add CTL{write} to XML documentation.

Signed-off-by: Jaroslav Kysela <perex@perex.cz>
alsactl/alsactl_init.xml
alsactl/init/default
alsactl/init/test
alsactl/init_parse.c

index dd4239c9853a306aa89abe72182559a823683e28..eefe9ef7393e4d6457117f9cb614191bba9c67c1 100644 (file)
                       comma (,).</para>
                     </listitem>
                   </varlistentry>
+                  <varlistentry>
+                    <term><option>do_search</option></term>
+                    <listitem>
+                      <para>Search for a control. Value "1" is returned
+                      if a control was found. The CTL{name} key might
+                     contain match characters * and ?. An control index
+                     might be specified as first argument starting from
+                     zero (e.g. CTL{do_search 2}="1").</para>
+                     </listitem>
+                  </varlistentry>
+                  <varlistentry>
+                    <term><option>do_count</option></term>
+                    <listitem>
+                      <para>Search for a controls and return total count
+                      of matched ones. The CTL{name} key might contain match
+                      characters * and ?.</para>
+                     </listitem>
+                  </varlistentry>
                 </variablelist>
               </listitem>
             </varlistentry>
                  next key on line).</para>
               </listitem>
             </varlistentry>
+            <varlistentry>
+              <term><option>CTL{write}</option></term>
+              <listitem>
+                <para>Value is set (written) also to soundcard's control
+                device (all control values are set to specified value).
+                The result of set operation is true when operation
+                succeed (it means continue with next key on line).</para>
+              </listitem>
+            </varlistentry>
 
             <varlistentry>
               <term><option>ENV{<replaceable>key</replaceable>}</option></term>
             <varlistentry>
               <term><option>GOTO</option></term>
               <listitem>
-                <para>Jumps to the next LABEL with a matching name</para>
+                <para>Jumps to the next LABEL with a matching name.
+                      The goto cannot jump backward.</para>
               </listitem>
             </varlistentry>
 
index dbe30acfcdc13bc1edcee29fbe964e4a4972305f..55fb48fb12726fdabf0fb31a459d4f960f5b9a18 100644 (file)
@@ -17,22 +17,22 @@ ENV{cvolume}:="12dB"
 ENV{has_pmaster_vol}:="false"
 
 CTL{reset}="mixer"
-CTL{name}="Playback Volume",PROGRAM=="__ctl_search", \
+CTL{name}="Playback Volume",CTL{do_search}=="1", \
   CTL{write}!="$env{pvolume}",CTL{values}="$env{ppercent}"
-CTL{name}="Playback Switch",PROGRAM=="__ctl_search", \
+CTL{name}="Playback Switch",CTL{do_search}=="1", \
   CTL{values}="on"
 
 CTL{reset}="mixer"
-CTL{name}="Master Playback Volume",PROGRAM=="__ctl_search", \
+CTL{name}="Master Playback Volume",CTL{do_search}=="1", \
   ENV{has_pmaster_vol}:="true", \
   CTL{write}!="$env{pvolume}",CTL{values}="$env{ppercent}"
-CTL{name}="Master Playback Switch",PROGRAM=="__ctl_search", \
+CTL{name}="Master Playback Switch",CTL{do_search}=="1", \
   CTL{values}="on"
 
 CTL{reset}="mixer"
-CTL{name}="Master Digital Playback Volume",PROGRAM=="__ctl_search", \
+CTL{name}="Master Digital Playback Volume",CTL{do_search}=="1", \
   CTL{write}!="$env{pvolume}",CTL{values}="$env{ppercent}"
-CTL{name}="Master Digital Playback Switch",PROGRAM=="__ctl_search", \
+CTL{name}="Master Digital Playback Switch",CTL{do_search}=="1", \
   CTL{values}="on"
 
 CTL{reset}="mixer"
@@ -42,7 +42,7 @@ ENV{has_pmaster_vol}=="true",CTL{write}=="0dB",GOTO=""
 ENV{has_pmaster_vol}=="true",CTL{write}=="100%",GOTO=""
 CTL{write}!="$env{pvolume}",CTL{values}="$env{ppercent}"
 LABEL=""
-CTL{name}="Front Playback Switch",PROGRAM=="__ctl_search", \
+CTL{name}="Front Playback Switch",CTL{do_search}=="1", \
   CTL{values}="on"
 
 CTL{reset}="mixer"
@@ -52,7 +52,7 @@ ENV{has_pmaster_vol}=="true",CTL{write}=="0dB",GOTO=""
 ENV{has_pmaster_vol}=="true",CTL{write}=="100%",GOTO=""
 CTL{write}!="$env{pvolume}",CTL{values}="$env{ppercent}"
 LABEL=""
-CTL{name}="Headphone Playback Switch",PROGRAM=="__ctl_search", \
+CTL{name}="Headphone Playback Switch",CTL{do_search}=="1", \
   CTL{values}="on"
 
 CTL{reset}="mixer"
@@ -63,7 +63,7 @@ ENV{has_pmaster_vol}=="true",CTL{write}=="0dB",GOTO=""
 ENV{has_pmaster_vol}=="true",CTL{write}=="100%",GOTO=""
 CTL{write}!="$env{pvolume}",CTL{values}="$env{ppercent}"
 LABEL=""
-CTL{name}="Headphone Playback Switch",CTL{index}="1",PROGRAM=="__ctl_search", \
+CTL{name}="Headphone Playback Switch",CTL{index}="1",CTL{do_search}=="1", \
   CTL{values}="on"
 
 CTL{reset}="mixer"
@@ -73,13 +73,13 @@ ENV{has_pmaster_vol}=="true",CTL{write}=="0dB",GOTO=""
 ENV{has_pmaster_vol}=="true",CTL{write}=="100%",GOTO=""
 CTL{write}!="$env{pvolume}",CTL{values}="$env{ppercent}"
 LABEL=""
-CTL{name}="Speaker Playback Switch",PROGRAM=="__ctl_search", \
+CTL{name}="Speaker Playback Switch",CTL{do_search}=="1", \
   CTL{values}="on"
 
 CTL{reset}="mixer"
-CTL{name}="PC Speaker Playback Volume",PROGRAM=="__ctl_search", \
+CTL{name}="PC Speaker Playback Volume",CTL{do_search}=="1", \
   CTL{values}="$env{pvolume}",RESULT!="0",CTL{values}="$env{ppercent}"
-CTL{name}="PC Speaker Playback Switch",PROGRAM=="__ctl_search", \
+CTL{name}="PC Speaker Playback Switch",CTL{do_search}=="1", \
   CTL{values}="on"
 
 CTL{reset}="mixer"
@@ -93,8 +93,8 @@ CTL{dBmin}=="-34.50dB",CTL{dBmax}=="12.00dB",CTL{write}=="0dB",GOTO=""
 CTL{dBmin}=="-30.00dB",CTL{dBmax}=="0dB",CTL{write}=="0dB",GOTO=""
 CTL{write}!="$env{pvolume}",CTL{values}="75%"
 LABEL=""
-CTL{name}="PCM Playback Switch",PROGRAM=="__ctl_search", CTL{values}="on"
-CTL{name}="PCM Switch",PROGRAM=="__ctl_search",CTL{values}="on"
+CTL{name}="PCM Playback Switch",CTL{do_search}=="1", CTL{values}="on"
+CTL{name}="PCM Switch",CTL{do_search}=="1",CTL{values}="on"
 
 CTL{reset}="mixer"
 CTL{name}="PCM Playback Volume",CTL{index}="1",PROGRAM!="__ctl_search", \
@@ -107,33 +107,33 @@ CTL{dBmin}=="-34.50dB",CTL{dBmax}=="12.00dB",CTL{write}=="0dB",GOTO=""
 CTL{dBmin}=="-30.00dB",CTL{dBmax}=="0dB",CTL{write}=="0dB",GOTO=""
 CTL{write}!="$env{pvolume}",CTL{values}="75%"
 LABEL=""
-CTL{name}="PCM Playback Switch",CTL{index}="1",PROGRAM=="__ctl_search", \
+CTL{name}="PCM Playback Switch",CTL{index}="1",CTL{do_search}=="1", \
   CTL{values}="on"
-CTL{name}="PCM Switch",CTL{index}="1",PROGRAM=="__ctl_search", \
+CTL{name}="PCM Switch",CTL{index}="1",CTL{do_search}=="1", \
   CTL{values}="on"
 
 CTL{reset}="mixer"
-CTL{name}="DAC Playback Volume",PROGRAM=="__ctl_search", \
+CTL{name}="DAC Playback Volume",CTL{do_search}=="1", \
   CTL{values}="$env{pvolume}",RESULT!="0",CTL{values}="$env{ppercent}"
-CTL{name}="DAC Playback Switch",PROGRAM=="__ctl_search", \
+CTL{name}="DAC Playback Switch",CTL{do_search}=="1", \
   CTL{values}="on"
 
 CTL{reset}="mixer"
-CTL{name}="Synth Playback Volume",PROGRAM=="__ctl_search", \
+CTL{name}="Synth Playback Volume",CTL{do_search}=="1", \
   CTL{values}="$env{pvolume}",RESULT!="0",CTL{values}="$env{ppercent}"
-CTL{name}="Synth Playback Switch",PROGRAM=="__ctl_search", \
+CTL{name}="Synth Playback Switch",CTL{do_search}=="1", \
   CTL{values}="on"
 
 CTL{reset}="mixer"
-CTL{name}="Wave Playback Volume",PROGRAM=="__ctl_search", \
+CTL{name}="Wave Playback Volume",CTL{do_search}=="1", \
   CTL{values}="100%"
-CTL{name}="Wave Playback Switch",PROGRAM=="__ctl_search", \
+CTL{name}="Wave Playback Switch",CTL{do_search}=="1", \
   CTL{values}="on"
 
 CTL{reset}="mixer"
-CTL{name}="Music Playback Volume",PROGRAM=="__ctl_search", \
+CTL{name}="Music Playback Volume",CTL{do_search}=="1", \
   CTL{values}="100%"
-CTL{name}="Music Playback Switch",PROGRAM=="__ctl_search", \
+CTL{name}="Music Playback Switch",CTL{do_search}=="1", \
   CTL{values}="on"
 
 CTL{reset}="mixer"
@@ -146,25 +146,25 @@ CTL{dBmin}=="-34.50dB",CTL{dBmax}=="12.00dB",CTL{write}=="0dB",GOTO=""
 CTL{dBmin}=="-30.00dB",CTL{dBmax}=="0dB",CTL{write}=="0dB",GOTO=""
 CTL{write}!="$env{pvolume}",CTL{values}="$env{ppercent}"
 LABEL=""
-CTL{name}="CD Playback Switch",PROGRAM=="__ctl_search", \
+CTL{name}="CD Playback Switch",CTL{do_search}=="1", \
   CTL{values}="on"
 
 CTL{reset}="mixer"
-CTL{name}="Mono Playback Volume",PROGRAM=="__ctl_search", \
+CTL{name}="Mono Playback Volume",CTL{do_search}=="1", \
   CTL{values}="$env{pvolume}",RESULT!="0",CTL{values}="$env{ppercent}"
-CTL{name}="Mono Playback Switch",PROGRAM=="__ctl_search", \
+CTL{name}="Mono Playback Switch",CTL{do_search}=="1", \
   CTL{values}="on"
 
 CTL{reset}="mixer"
-CTL{name}="Master Mono Playback Volume",PROGRAM=="__ctl_search", \
+CTL{name}="Master Mono Playback Volume",CTL{do_search}=="1", \
   CTL{values}="$env{pvolume}",RESULT!="0",CTL{values}="$env{ppercent}"
-CTL{name}="Master Mono Playback Switch",PROGRAM=="__ctl_search", \
+CTL{name}="Master Mono Playback Switch",CTL{do_search}=="1", \
   CTL{values}="on"
 
 CTL{reset}="mixer"
-CTL{name}="AC97 Playback Volume",PROGRAM=="__ctl_search", \
+CTL{name}="AC97 Playback Volume",CTL{do_search}=="1", \
   CTL{values}="100%"
-CTL{name}="AC97 Playback Switch",PROGRAM=="__ctl_search", \
+CTL{name}="AC97 Playback Switch",CTL{do_search}=="1", \
   CTL{values}="on"
 
 #
@@ -172,7 +172,7 @@ CTL{name}="AC97 Playback Switch",PROGRAM=="__ctl_search", \
 #
 
 CTL{reset}="mixer"
-CTL{name}="DRC Range",PROGRAM=="__ctl_search", \
+CTL{name}="DRC Range",CTL{do_search}=="1", \
   CTL{write}!="$env{pvolume}",CTL{values}="$env{ppercent}"
 
 # **************************************************************************
@@ -180,9 +180,9 @@ CTL{name}="DRC Range",PROGRAM=="__ctl_search", \
 # **************************************************************************
 
 CTL{reset}="mixer"
-CTL{name}="Capture Volume",PROGRAM=="__ctl_search", \
+CTL{name}="Capture Volume",CTL{do_search}=="1", \
   CTL{write}!="$env{cvolume}",CTL{values}="$env{cpercent}"
-CTL{name}="Capture Switch",PROGRAM=="__ctl_search", \
+CTL{name}="Capture Switch",CTL{do_search}=="1", \
   CTL{values}="on"
 
 CTL{name}="Input Source",PROGRAM!="__ctl_search", GOTO=""
@@ -191,5 +191,5 @@ CTL{enums}=="*|Internal Mic|*",CTL{values}="Internal Mic", \
 CTL{enums}=="*|Mic|*",CTL{values}="Mic"
 LABEL=""
 
-CTL{name}="Internal Mic Boost",PROGRAM=="__ctl_search", \
+CTL{name}="Internal Mic Boost",CTL{do_search}=="1", \
   CTL{values}="on"
index 26db2a36c562be8cc293a37dbed252fbb2161774..56659f8e3376c0b35f24b599b5ea023fd7d60994 100644 (file)
@@ -37,9 +37,9 @@ 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{do_count} test:\n"
+CTL{search}="mixer", CTL{name}="*Switch*", \
+  PRINT="  *Switch* count result: $ctl{do_count}\n"
 
 PRINT="__ctl_search test:\n"
 CTL{search}="mixer", CTL{name}="*Switch*", PROGRAM!="__ctl_search", GOTO="skip_switch_search"
@@ -60,7 +60,7 @@ PRINT="    CTL{name}=\"$ctl{name}\"\n"
 PRINT="    CTL{index}=\"$ctl{index}\"\n"
 
 PRINT="First ten elements:\n"
-CTL{search}="mixer", CTL{name}="*", PROGRAM!="__ctl_search 0", GOTO="skip_first_ten_search"
+CTL{search}="mixer", CTL{name}="*", CTL{do_search 0}!="1", GOTO="skip_first_ten_search"
 PRINT="  Element #0:\n"
 PRINT="    CTL{numid}=\"$ctl{numid}\"\n"
 PRINT="    CTL{iface}=\"$ctl{iface}\"\n"
@@ -79,7 +79,7 @@ PRINT="    CTL{dBmin}=\"$ctl{dBmin}\"\n"
 PRINT="    CTL{dBmax}=\"$ctl{dBmax}\"\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"
+CTL{search}="mixer", CTL{name}="*", CTL{do_search 1}!="1", GOTO="skip_first_ten_search"
 PRINT="  Element #1:\n"
 PRINT="    CTL{numid}=\"$ctl{numid}\"\n"
 PRINT="    CTL{iface}=\"$ctl{iface}\"\n"
@@ -98,7 +98,7 @@ PRINT="    CTL{dBmin}=\"$ctl{dBmin}\"\n"
 PRINT="    CTL{dBmax}=\"$ctl{dBmax}\"\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"
+CTL{search}="mixer", CTL{name}="*", CTL{do_search 2}!="1", GOTO="skip_first_ten_search"
 PRINT="  Element #2:\n"
 PRINT="    CTL{numid}=\"$ctl{numid}\"\n"
 PRINT="    CTL{iface}=\"$ctl{iface}\"\n"
@@ -117,7 +117,7 @@ PRINT="    CTL{dBmin}=\"$ctl{dBmin}\"\n"
 PRINT="    CTL{dBmax}=\"$ctl{dBmax}\"\n"
 PRINT="    CTL{items}=\"$ctl{items}\"\n"
 PRINT="    CTL{value}=\"$ctl{value}\"\n"
-CTL{search}="mixer", CTL{name}="*", PROGRAM!="__ctl_search 3", GOTO="skip_first_ten_search"
+CTL{search}="mixer", CTL{name}="*", CTL{do_search 3}!="3", GOTO="skip_first_ten_search"
 PRINT="  Element #3:\n"
 PRINT="    CTL{numid}=\"$ctl{numid}\"\n"
 PRINT="    CTL{iface}=\"$ctl{iface}\"\n"
@@ -136,7 +136,7 @@ PRINT="    CTL{dBmax}=\"$ctl{dBmax}\"\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 4", GOTO="skip_first_ten_search"
+CTL{search}="mixer", CTL{name}="*", CTL{do_search 4}!="1", GOTO="skip_first_ten_search"
 PRINT="  Element #4:\n"
 PRINT="    CTL{numid}=\"$ctl{numid}\"\n"
 PRINT="    CTL{iface}=\"$ctl{iface}\"\n"
@@ -155,7 +155,7 @@ PRINT="    CTL{dBmin}=\"$ctl{dBmin}\"\n"
 PRINT="    CTL{dBmax}=\"$ctl{dBmax}\"\n"
 PRINT="    CTL{items}=\"$ctl{items}\"\n"
 PRINT="    CTL{value}=\"$ctl{value}\"\n"
-CTL{search}="mixer", CTL{name}="*", PROGRAM!="__ctl_search 5", GOTO="skip_first_ten_search"
+CTL{search}="mixer", CTL{name}="*", CTL{do_search 5}!="1", GOTO="skip_first_ten_search"
 PRINT="  Element #5:\n"
 PRINT="    CTL{numid}=\"$ctl{numid}\"\n"
 PRINT="    CTL{iface}=\"$ctl{iface}\"\n"
@@ -174,7 +174,7 @@ PRINT="    CTL{dBmin}=\"$ctl{dBmin}\"\n"
 PRINT="    CTL{dBmax}=\"$ctl{dBmax}\"\n"
 PRINT="    CTL{items}=\"$ctl{items}\"\n"
 PRINT="    CTL{value}=\"$ctl{value}\"\n"
-CTL{search}="mixer", CTL{name}="*", PROGRAM!="__ctl_search 6", GOTO="skip_first_ten_search"
+CTL{search}="mixer", CTL{name}="*", CTL{do_search 6}!="1", GOTO="skip_first_ten_search"
 PRINT="  Element #6:\n"
 PRINT="    CTL{numid}=\"$ctl{numid}\"\n"
 PRINT="    CTL{iface}=\"$ctl{iface}\"\n"
@@ -193,7 +193,7 @@ PRINT="    CTL{dBmin}=\"$ctl{dBmin}\"\n"
 PRINT="    CTL{dBmax}=\"$ctl{dBmax}\"\n"
 PRINT="    CTL{items}=\"$ctl{items}\"\n"
 PRINT="    CTL{value}=\"$ctl{value}\"\n"
-CTL{search}="mixer", CTL{name}="*", PROGRAM!="__ctl_search 7", GOTO="skip_first_ten_search"
+CTL{search}="mixer", CTL{name}="*", CTL{do_search 7}!="1", GOTO="skip_first_ten_search"
 PRINT="  Element #7:\n"
 PRINT="    CTL{numid}=\"$ctl{numid}\"\n"
 PRINT="    CTL{iface}=\"$ctl{iface}\"\n"
@@ -212,7 +212,7 @@ PRINT="    CTL{dBmin}=\"$ctl{dBmin}\"\n"
 PRINT="    CTL{dBmax}=\"$ctl{dBmax}\"\n"
 PRINT="    CTL{items}=\"$ctl{items}\"\n"
 PRINT="    CTL{value}=\"$ctl{value}\"\n"
-CTL{search}="mixer", CTL{name}="*", PROGRAM!="__ctl_search 8", GOTO="skip_first_ten_search"
+CTL{search}="mixer", CTL{name}="*", CTL{do_search 8}!="1", GOTO="skip_first_ten_search"
 PRINT="  Element #8:\n"
 PRINT="    CTL{numid}=\"$ctl{numid}\"\n"
 PRINT="    CTL{iface}=\"$ctl{iface}\"\n"
@@ -231,7 +231,7 @@ PRINT="    CTL{dBmin}=\"$ctl{dBmin}\"\n"
 PRINT="    CTL{dBmax}=\"$ctl{dBmax}\"\n"
 PRINT="    CTL{items}=\"$ctl{items}\"\n"
 PRINT="    CTL{value}=\"$ctl{value}\"\n"
-CTL{search}="mixer", CTL{name}="*", PROGRAM!="__ctl_search 9", GOTO="skip_first_ten_search"
+CTL{search}="mixer", CTL{name}="*", CTL{do_search 9}!="1", GOTO="skip_first_ten_search"
 PRINT="  Element #9:\n"
 PRINT="    CTL{numid}=\"$ctl{numid}\"\n"
 PRINT="    CTL{iface}=\"$ctl{iface}\"\n"
@@ -254,15 +254,15 @@ 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", \
+  CTL{do_search}=="1", 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", \
+  CTL{do_search}=="1", 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{do_search}=="1", \
 PRINT="  result=$result\n"
 
 #CTL{reset}="mixer", CTL{name}="Input Source", PRINT="***$ctl{enums}\n"
index 83857a29a52c0889102ec5a34acc9e6e0405e007..8d9c7ae0be90c936047d15791095a924887f1d4f 100644 (file)
@@ -485,6 +485,46 @@ static int set_ctl_value(struct space *space, const char *value, int all)
        return -EINVAL;
 }
 
+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 const char *elemid_get(struct space *space, const char *attr)
 {
        long long val;
@@ -649,6 +689,56 @@ dbvalue:
                }
                return res;
        }
+       if (strncasecmp(attr, "do_search", 9) == 0) {
+               int err, index = 0;
+               snd_hctl_elem_t *elem;
+               snd_ctl_elem_id_t *id;
+               char *pos = strchr(attr, ' ');
+               if (pos)
+                       index = strtol(pos, NULL, 0);
+               err = snd_ctl_elem_id_malloc(&id);
+               if (err < 0)
+                       return NULL;
+               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;
+                       }
+                       strcpy(res, "1");
+                       snd_ctl_elem_id_copy(space->ctl_id, id);
+                       snd_ctl_elem_id_free(id);
+                       dbg("do_ctl_search found a control");
+                       return res;
+                     next_search:
+                       elem = snd_hctl_elem_next(elem);
+               }
+               snd_ctl_elem_id_free(id);
+               strcpy(res, "0");
+               return res;
+       }
+       if (strncasecmp(attr, "do_count", 8) == 0) {
+               int err, index = 0;
+               snd_hctl_elem_t *elem;
+               snd_ctl_elem_id_t *id;
+               err = snd_ctl_elem_id_malloc(&id);
+               if (err < 0)
+                       return NULL;
+               elem = snd_hctl_first_elem(space->ctl_handle);
+               while (elem) {
+                       snd_hctl_elem_get_id(elem, id);
+                       if (ctl_match(space->ctl_id, id))
+                               index++;
+                       elem = snd_hctl_elem_next(elem);
+               }
+               snd_ctl_elem_id_free(id);
+               sprintf(res, "%u", index);
+               dbg("do_ctl_count found %s controls", res);
+               return res;
+       }
        Perror(space, "unknown ctl{} attribute '%s'", attr);
        return NULL;
   value:
@@ -1150,108 +1240,23 @@ found:
        *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)
+       if (strncmp(command0, "__ctl_search", 12) == 0) {
+               const char *res = elemid_get(space, "do_search");
+               if (res == NULL || strcmp(res, "1") != 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;
+               return EXIT_SUCCESS;
        }
-       if (cmdlen == 11 && strncmp(command0, "__ctl_count", 11) == 0) {
-               index = 0;
-               err = snd_ctl_elem_id_malloc(&id);
-               if (err < 0)
+       if (strncmp(command0, "__ctl_count", 11) == 0) {
+               const char *res = elemid_get(space, "do_count");
+               if (res == NULL || strcmp(res, "0") == 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) {
+               strlcpy(result, res, ressize);
+               return EXIT_SUCCESS;
        }
        Perror(space, "unknown buildin command '%s'", command0);
        return EXIT_FAILURE;