"Fossies" - the Fresh Open Source Software Archive

Member "alsa-utils-1.1.9/alsactl/init_parse.c" (10 May 2019, 47339 Bytes) of package /linux/misc/alsa-utils-1.1.9.tar.bz2:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "init_parse.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 1.1.8_vs_1.1.9.

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