OSDN Git Service

Enhanced configuration syntax (added [ ] block for arrays).
authorJaroslav Kysela <perex@perex.cz>
Wed, 13 Jun 2001 09:31:05 +0000 (09:31 +0000)
committerJaroslav Kysela <perex@perex.cz>
Wed, 13 Jun 2001 09:31:05 +0000 (09:31 +0000)
The snd_config_expand functions expands the runtime contents (@func...).
Removed the environment variable replace code from the configuration parser.
Updated the alsa.conf configuration file.

include/conf.h
src/alsa.conf
src/cards/SI_7018.conf
src/conf.c
src/confmisc.c
src/control/control.c
src/pcm/pcm.c
src/rawmidi/rawmidi.c
src/seq/seq.c

index 43b93e9..1c5e91a 100644 (file)
@@ -39,7 +39,7 @@ int snd_config_search_alias(snd_config_t *config,
                            snd_config_t **result);
 
 int snd_config_expand(snd_config_t *config, const char *args,
-                     snd_config_t **result);
+                     void *private_data, snd_config_t **result);
 
 int snd_config_add(snd_config_t *config, snd_config_t *leaf);
 int snd_config_delete(snd_config_t *config);
@@ -96,11 +96,12 @@ int snd_config_string_replace(const char *src, char idchr,
                              snd_config_string_replace_callback_t *callback,
                              void *private_data,
                              char **dst);
-int snd_config_redirect_load(snd_config_t *root, snd_config_t *config,
-                            char **name, snd_config_t **dst_config,
+int snd_config_redirect_load(snd_config_t *root,
+                            snd_config_t *config,
+                            char **name,
+                            snd_config_t **dst_config,
                             int *dst_dynamic);
 
-
 #ifdef __cplusplus
 }
 #endif
index 5221920..fd74e10 100644 (file)
@@ -6,22 +6,36 @@ pcm.default {
        type plug
        slave.pcm {
                type hw
-               card <@ALSA_PCM_CARD,ALSA_CARD:0@>
-               device <@ALSA_PCM_DEVICE:0@>
+               card {
+                       @func getenv
+                       @type integer
+                       envname [
+                               ALSA_PCM_CARD
+                               ALSA_CARD
+                       ]
+                       default 0
+               }
+               device {
+                       @func getenv
+                       @type integer
+                       envname [
+                               ALSA_PCM_DEVICE
+                       ]
+                       default 0
+               }
+               subdevice -1
        }
 }
 
 pcm.hw {
-       $.0 CARD
-       $.1 DEV
-       $.2 SUBDEV
-       $.CARD {
+       args [ CARD DEV SUBDEV ]
+       args.CARD {
                type integer
        }
-       $.DEV {
+       args.DEV {
                type integer
        }
-       $.SUBDEV {
+       args.SUBDEV {
                type integer
                default -1
        }               
@@ -32,16 +46,14 @@ pcm.hw {
 }
 
 pcm.plughw {
-       $.0 CARD
-       $.1 DEV
-       $.2 SUBDEV
-       $.CARD {
+       args [ CARD DEV SUBDEV ]
+       args.CARD {
                type integer
        }
-       $.DEV {
+       args.DEV {
                type integer
        }
-       $.SUBDEV {
+       args.SUBDEV {
                type integer
                default -1
        }               
@@ -55,8 +67,8 @@ pcm.plughw {
 }
 
 pcm.plug {
-       $.0 SLAVE
-       $.SLAVE {
+       args [ SLAVE ]
+       args.SLAVE {
                type string
        }
        type plug
@@ -64,12 +76,11 @@ pcm.plug {
 }
 
 pcm.shm {
-       $.0 SOCKET
-       $.1 PCM
-       $.SOCKET {
+       args [ SOCKET PCM ]
+       args.SOCKET {
                type string
        }
-       $.PCM {
+       args.PCM {
                type string
        }
        type shm
@@ -78,16 +89,14 @@ pcm.shm {
 }
 
 pcm.tee {
-       $.0 SLAVE
-       $.1 FILE
-       $.2 FORMAT
-       $.SLAVE {
+       args [ SLAVE FILE FORMAT ]
+       args.SLAVE {
                type string
        }
-       $.FILE {
+       args.FILE {
                type string
        }
-       $.FORMAT {
+       args.FORMAT {
                type string
                default raw
        }
@@ -98,12 +107,11 @@ pcm.tee {
 }
 
 pcm.file {
-       $.0 FILE
-       $.1 FORMAT
-       $.FILE {
+       args [ FILE FORMAT ]
+       args.FILE {
                type string
        }
-       $.FORMAT {
+       args.FORMAT {
                type string
                default raw
        }
@@ -114,47 +122,77 @@ pcm.file {
 }
 
 pcm.surround40 {
-       $.0 CARD
-       $.1 DEV
-       $.CARD {
-               type integer
-               default <@ALSA_SURROUND40_CARD,ALSA_PCM_CARD,ALSA_CARD:0@>
-       }
-       $.DEV {
-               type integer
-               default <@ALSA_SURROUND40_DEVICE:0@>
-       }
-       type surround
-       card $(CARD)
-       device $(DEVICE)
-       stype "4.0"
-}
-       
-pcm.surround40_new {
-       $.0 CARD
-       $.1 DEV
-       $.CARD {
+       args [ CARD DEV ]
+       args.CARD {
                type integer
-               default <@ALSA_SURROUND40_CARD,ALSA_PCM_CARD,ALSA_CARD:0@>
+               default {
+                       @func getenv
+                       @type integer
+                       envname [
+                               ALSA_SURROUND40_CARD
+                               ALSA_PCM_CARD
+                               ALSA_CARD
+                       ]
+                       default 0
+               }
        }
-       $.DEV {
+       args.DEV {
                type integer
-               default <@ALSA_SURROUND40_DEVICE:0@>
+               default {
+                       @func getenv
+                       @type integer
+                       envname [
+                               ALSA_SURROUND40_DEVICE
+                       ]
+                       default 0
+               }
        }
        redirect {
-               filename "&(datadir)/cards/&(card_id:$(CARD)).conf"
-               name "pcm.surround40_$(DEV)_&(pcm_id:$(CARD),$(DEV)):$(CARD)"
+               filename {
+                       @func concat
+                       strings [
+                               {
+                                       @func datadir
+                               }
+                               "/cards/"
+                               {
+                                       @func card_strtype
+                                       card $(CARD)
+                               }
+                               ".conf"
+                       ]
+               }
+               name {
+                       @func concat
+                       strings [
+                               "pcm.surround40_" $(DEV) "_"
+                               {
+                                       @func pcm_id
+                                       card $(CARD)
+                                       device 0
+                               }
+                               ":" $(CARD)
+                       ]
+               }
        }
 }
        
 pcm.surround51 {
-       $.0 CARD
-       $.1 DEV
-       $.CARD {
+       args [ CARD DEV ]
+       args.CARD {
                type integer
-               default <@ALSA_SURROUND51_CARD,ALSA_PCM_CARD,ALSA_CARD:0@>
+               default {
+                       @func getenv
+                       @type integer
+                       envname [
+                               ALSA_SURROUND51_CARD
+                               ALSA_PCM_CARD
+                               ALSA_CARD
+                       ]
+                       default 0
+               }
        }
-       $.DEV {
+       args.DEV {
                type integer
                default <@ALSA_SURROUND51_DEVICE:0@>
        }
@@ -174,8 +212,8 @@ ctl.default {
 }
 
 ctl.hw {
-       $.0 CARD
-       $.CARD {
+       args[ CARD ]
+       args.CARD {
                type integer
        }
        type hw
@@ -183,39 +221,51 @@ ctl.hw {
 }
 
 ctl.shm {
-       $.0 SOCKET
-       $.1 PCM
-       $.SOCKET {
+       args [ SOCKET CTL ]
+       args.SOCKET {
                type string
        }
-       $.PCM {
+       args.CTL {
                type string
        }
        type shm
        server $(SOCKET)
-       ctl $(PCM)
+       ctl $(CTL)
 }
 
 rawmidi.default {
        type hw
-       card <@ALSA_RAWMIDI_CARD,ALSA_CARD:0@>
-       device <@ALSA_RAWMIDI_DEVICE:0@>
+       card {
+               @func getenv
+               @type integer
+               envname [
+                       ALSA_RAWMIDI_CARD
+                       ALSA_CARD
+               ]
+               default 0
+       }
+       device {
+               @func getenv
+               @type integer
+               envname [
+                       ALSA_RAWMIDI_DEVICE
+               ]
+               default 0
+       }
 }
 
 rawmidi.hw {
-       $.0 CARD
-       $.1 DEV
-       $.2 SUBDEV
-       $.CARD {
+       args [ CARD DEV SUBDEV ]
+       args.CARD {
                type integer
        }
-       $.DEV {
+       args.DEV {
                type integer
        }
-       $.SUBDEV {
+       args.SUBDEV {
                type integer
                default -1
-       }               
+       }
        type hw
        card $(CARD)
        device $(DEV)
@@ -231,46 +281,44 @@ seq.hw {
 }
 
 pcm.iec958 {
-       $.0 PCM
-        $.1 AES0
-        $.2 AES1
-        $.3 AES2
-        $.4 AES3
-       $.PCM {
+       args [ PCM AES0 AES1 AES2 AES3 ]
+       args.PCM {
                type string
                default default
        }
-       $.AES0 {
+       args.AES0 {
                type integer
                # IEC958_AES0_PROFESSIONAL | IEC958_AES0_NONAUDIO |
                # IEC958_AES0_PRO_EMPHASIS_NONE | IEC958_AES0_PRO_FS_48000
                default 0x87
        }
-       $.AES1 {
+       args.AES1 {
                type integer
                default 0x00
        }
-       $.AES2 {
+       args.AES2 {
                type integer
                default 0x00
        }
-       $.AES3 {
+       args.AES3 {
                type integer
                default 0x00
        }
        type hooks
        slave.pcm $(PCM)
-       hooks.0 {
-               type ctl_elems
-               args.0 {
-                       name "IEC958 Playback PCM Stream"
-                       subdevice 0
-                       preserve true
-                       lock true
-                       value.0 $(AES0)
-                       value.1 $(AES1)
-                       value.2 $(AES2)
-                       value.3 $(AES3)
+       hooks [
+               {
+                       type ctl_elems
+                       args [
+                               name "IEC958 Playback PCM Stream"
+                               subdevice 0
+                               preserve true
+                               lock true
+                               value.0 $(AES0)
+                               value.1 $(AES1)
+                               value.2 $(AES2)
+                               value.3 $(AES3)
+                       ]
                }
-       }
+       ]
 }
index b115f5b..d22942a 100644 (file)
@@ -3,16 +3,14 @@
 #
 
 pcm.front {
-       $.0 CARD
-       $.1 DEV
-       $.2 SUBDEV
-       $.CARD {
+       args [ CARD DEV SUBDEV ]
+       args.CARD {
                type integer
        }
-       $.DEV {
+       args.DEV {
                type integer
        }
-       $.SUBDEV {
+       args.SUBDEV {
                type integer
                default -1
        }
@@ -28,16 +26,14 @@ pcm_slave.front {
 }
 
 pcm.rear {
-       $.0 CARD
-       $.1 DEV
-       $.2 SUBDEV
-       $.CARD {
+       args [ CARD DEV SUBDEV ]
+       args.CARD {
                type integer
        }
-       $.DEV {
+       args.DEV {
                type integer
        }
-       $.SUBDEV {
+       args.SUBDEV {
                type integer
                default -1
        }
@@ -53,19 +49,29 @@ pcm_slave.rear {
 }
 
 pcm.surround40_0_trident_dx_nx {
-       $.0 CARD
-       $.CARD {
+       args [ CARD ]
+       args.CARD {
                type integer
        }
        type multi
-       slaves {
-               0 "pcm_slave.front:$(CARD),0,-1"
-               1 "pcm_slave.rear:$(CARD),0,-1"
-       }
-       bindings {
-               0 { slave 0 channel 0 }
-               1 { slave 0 channel 1 }
-               2 { slave 1 channel 0 }
-               3 { slave 1 channel 1 }
-       }
+       slaves [
+               {
+                       @func concat
+                       strings [
+                               "pcm_slave.front:" $(CARD) ",0,-1"
+                       ]
+               }
+               {
+                       @func concat
+                       strings [
+                               "pcm_slave.rear:" $(CARD) ",0,-1"
+                       ]
+               }
+       ]
+       bindings [
+               { slave 0 channel 0 }
+               { slave 0 channel 1 }
+               { slave 1 channel 0 }
+               { slave 1 channel 1 }
+       ]
 }
index a5885ae..8d8bc1f 100644 (file)
@@ -1,6 +1,7 @@
 /*
  *  Configuration helper functions
- *  Copyright (c) 2000 by Abramo Bagnara <abramo@alsa-project.org>
+ *  Copyright (c) 2000 by Abramo Bagnara <abramo@alsa-project.org>,
+ *                       Jaroslav Kysela <perex@suse.cz>
  *
  *
  *   This library is free software; you can redistribute it and/or modify
@@ -22,6 +23,7 @@
 #include <stdarg.h>
 #include <wordexp.h>
 #include <sys/stat.h>
+#include <dlfcn.h>
 #include "local.h"
 #include "list.h"
 
@@ -151,44 +153,9 @@ static int get_char_skip_comments(input_t *input)
                        int err = get_delimstring(&str, '>', input);
                        if (err < 0)
                                return err;
-                       if (strlen(str) < 3 ||
-                           str[0] != '@' ||
-                           str[strlen(str)-1] != '@' ||
-                           strchr(str, ':') == NULL) {
-                               err = snd_input_stdio_open(&in, str, "r");
-                               if (err < 0)
-                                       return err;
-                       } else {
-                               char *envvar = str + 1;
-                               char *envdef = strchr(str, ':');
-                               char *env, *end;
-                               
-                               str[strlen(str)-1] = '\0';
-                               *envdef++ = '\0';
-                               if (*envdef == '\0') {
-                                       free(str);
-                                       input->error = BAD_ENV_DEFAULT;
-                                       return -EINVAL;
-                               }
-                               while (1) {
-                                       end = strchr(envvar, ',');
-                                       if (end)
-                                               *end = '\0';
-                                       env = getenv(envvar);
-                                       if (env != NULL && *env != '\0')
-                                               break;
-                                       if (end) {
-                                               *end = ',';     /* repair for fd->name */
-                                               envvar = end + 1;
-                                       } else {
-                                               env = envdef;
-                                               break;
-                                       }
-                               }
-                               err = snd_input_buffer_open(&in, env, strlen(env));
-                               if (err < 0)
-                                       return err;
-                       }
+                       err = snd_input_stdio_open(&in, str, "r");
+                       if (err < 0)
+                               return err;
                        fd = malloc(sizeof(*fd));
                        if (!fd)
                                return -ENOMEM;
@@ -272,7 +239,7 @@ static int get_quotedchar(input_t *input)
 
 static int get_freestring(char **string, int id, input_t *input)
 {
-       const size_t bufsize = 256;
+       const size_t bufsize = 64;
        char _buf[bufsize];
        char *buf = _buf;
        size_t alloc = bufsize;
@@ -295,6 +262,8 @@ static int get_freestring(char **string, int id, input_t *input)
                case ';':
                case '{':
                case '}':
+               case '[':
+               case ']':
                case '\'':
                case '"':
                case '\\':
@@ -319,11 +288,17 @@ static int get_freestring(char **string, int id, input_t *input)
                        alloc *= 2;
                        if (old_alloc == bufsize) {
                                buf = malloc(alloc);
+                               if (buf == NULL)
+                                       return -ENOMEM;
                                memcpy(buf, _buf, old_alloc);
-                       } else
-                               buf = realloc(buf, alloc);
-                       if (!buf)
-                               return -ENOMEM;
+                       } else {
+                               char *ptr = realloc(buf, alloc);
+                               if (ptr == NULL) {
+                                       free(buf);
+                                       return -ENOMEM;
+                               }
+                               buf = ptr;
+                       }
                }
                buf[idx++] = c;
        }
@@ -332,7 +307,7 @@ static int get_freestring(char **string, int id, input_t *input)
                        
 static int get_delimstring(char **string, int delim, input_t *input)
 {
-       const size_t bufsize = 256;
+       const size_t bufsize = 64;
        char _buf[bufsize];
        char *buf = _buf;
        size_t alloc = bufsize;
@@ -369,11 +344,17 @@ static int get_delimstring(char **string, int delim, input_t *input)
                        alloc *= 2;
                        if (old_alloc == bufsize) {
                                buf = malloc(alloc);
+                               if (buf == NULL)
+                                       return -ENOMEM;
                                memcpy(buf, _buf, old_alloc);
-                       } else
-                               buf = realloc(buf, alloc);
-                       if (!buf)
-                               return -ENOMEM;
+                       } else {
+                               char *ptr = realloc(buf, alloc);
+                               if (ptr == NULL) {
+                                       free(buf);
+                                       return -ENOMEM;
+                               }
+                               buf = ptr;
+                       }
                }
                buf[idx++] = c;
        }
@@ -394,6 +375,8 @@ static int get_string(char **string, int id, input_t *input)
        case '.':
        case '{':
        case '}':
+       case '[':
+       case ']':
                input->error = UNEXPECTED_CHAR;
                return -EINVAL;
        case '\'':
@@ -411,17 +394,21 @@ static int get_string(char **string, int id, input_t *input)
        }
 }
 
-static int _snd_config_make(snd_config_t **config, char *id,
-                           snd_config_type_t type)
+static int _snd_config_make(snd_config_t **config, char **id, snd_config_type_t type)
 {
        snd_config_t *n;
        n = calloc(1, sizeof(*n));
        if (n == NULL) {
-               if (id)
-                       free(id);
+               if (*id) {
+                       free(*id);
+                       *id = NULL;
+               }
                return -ENOMEM;
        }
-       n->id = id;
+       if (id) {
+               n->id = *id;
+               *id = NULL;
+       }
        n->type = type;
        if (type == SND_CONFIG_TYPE_COMPOUND)
                INIT_LIST_HEAD(&n->u.compound.fields);
@@ -430,7 +417,7 @@ static int _snd_config_make(snd_config_t **config, char *id,
 }
        
 
-static int _snd_config_make_add(snd_config_t **config, char *id,
+static int _snd_config_make_add(snd_config_t **config, char **id,
                                snd_config_type_t type, snd_config_t *father)
 {
        snd_config_t *n;
@@ -457,38 +444,182 @@ static int _snd_config_search(snd_config_t *config,
                } else if (strlen(n->id) != (size_t) len ||
                           memcmp(n->id, id, (size_t) len) != 0)
                        continue;
-               *result = n;
+               if (result)
+                       *result = n;
                return 0;
        }
        return -ENOENT;
 }
 
+static int parse_value(snd_config_t **_n, snd_config_t *father, input_t *input, char **id)
+{
+       snd_config_t *n = *_n;
+       char *s;
+       int err;
+
+       err = get_string(&s, 0, input);
+       if (err < 0)
+               return err;
+       if ((s[0] >= '0' && s[0] <= '9') || s[0] == '-') {
+               long i;
+               errno = 0;
+               err = safe_strtol(s, &i);
+               if (err < 0) {
+                       double r;
+                       err = safe_strtod(s, &r);
+                       if (err >= 0) {
+                               free(s);
+                               if (n) {
+                                       if (n->type != SND_CONFIG_TYPE_REAL) {
+                                               SNDERR("%s is not a real", id);
+                                               return -EINVAL;
+                                       }
+                               } else {
+                                       err = _snd_config_make_add(&n, id, SND_CONFIG_TYPE_REAL, father);
+                                       if (err < 0)
+                                               return err;
+                               }
+                               n->u.real = r;
+                               *_n = n;
+                               return 0;
+                       }
+               } else {
+                       free(s);
+                       if (n) {
+                               if (n->type != SND_CONFIG_TYPE_INTEGER) {
+                                       SNDERR("%s is not an integer", id);
+                                       return -EINVAL;
+                               }
+                       } else {
+                               err = _snd_config_make_add(&n, id, SND_CONFIG_TYPE_INTEGER, father);
+                               if (err < 0)
+                                       return err;
+                       }
+                       n->u.integer = i;
+                       *_n = n;
+                       return 0;
+               }
+       }
+       if (n) {
+               if (n->type != SND_CONFIG_TYPE_STRING) {
+                       SNDERR("%s is not a string", id);
+                       free(s);
+                       return -EINVAL;
+               }
+       } else {
+               err = _snd_config_make_add(&n, id, SND_CONFIG_TYPE_STRING, father);
+               if (err < 0)
+                       return err;
+       }
+       if (n->u.string)
+               free(n->u.string);
+       n->u.string = s;
+       *_n = n;
+       return 0;
+}
+
 static int parse_defs(snd_config_t *father, input_t *input);
+static int parse_array_defs(snd_config_t *farther, input_t *input);
+
+static int parse_array_def(snd_config_t *father, input_t *input, int idx)
+{
+       char static_id[12], *id;
+       int c;
+       int err;
+       snd_config_t *n = NULL;
+
+       snprintf(static_id, sizeof(static_id), "%i", idx);
+       static_id[sizeof(static_id)-1] = '\0';
+       id = strdup(static_id);
+       if (id == NULL)
+               return -ENOMEM;
+       c = get_nonwhite(input);
+       switch (c) {
+       case '{':
+       case '[':
+       {
+               char endchr;
+               if (n) {
+                       if (n->type != SND_CONFIG_TYPE_COMPOUND) {
+                               SNDERR("%s is not a compound", id);
+                               err = -EINVAL;
+                               goto __end;
+                       }
+               } else {
+                       err = _snd_config_make_add(&n, &id, SND_CONFIG_TYPE_COMPOUND, father);
+                       if (err < 0)
+                               goto __end;
+               }
+               if (c == '{') {
+                       err = parse_defs(n, input);
+                       endchr = '}';
+               } else {
+                       err = parse_array_defs(n, input);
+                       endchr = ']';
+               }
+               c = get_nonwhite(input);
+               if (c != endchr) {
+                       snd_config_delete(n);
+                       input->error = (c == EOF ? UNEXPECTED_EOF : UNEXPECTED_CHAR);
+                       err = -EINVAL;
+                       goto __end;
+               }
+               break;
+       }
+       default:
+               unget_char(c, input);
+               err = parse_value(&n, father, input, &id);
+               if (err < 0)
+                       goto __end;
+               break;
+       }
+       err = 0;
+      __end:
+       if (id)
+               free(id);
+       return err;
+}
+
+static int parse_array_defs(snd_config_t *father, input_t *input)
+{
+       int idx = 0;
+       while (1) {
+               int c = get_nonwhite(input);
+               int err;
+               if (c == EOF)
+                       return 0;
+               unget_char(c, input);
+               if (c == ']')
+                       return 0;
+               err = parse_array_def(father, input, idx++);
+               if (err < 0)
+                       return err;
+       }
+       return 0;
+}
 
 static int parse_def(snd_config_t *father, input_t *input)
 {
-       char *id;
+       char *id = NULL;
        int c;
        int err;
        snd_config_t *n;
        enum {MERGE, NOCREATE, REMOVE} mode;
        while (1) {
-#if 0
                c = get_nonwhite(input);
                switch (c) {
+#if 0
                case '?':
                        mode = NOCREATE;
                        break;
                case '!':
                        mode = REMOVE;
                        break;
+#endif
                default:
                        mode = MERGE;
                        unget_char(c, input);
                }
-#else
-               mode = MERGE;
-#endif
                err = get_string(&id, 1, input);
                if (err < 0)
                        return err;
@@ -510,12 +641,12 @@ static int parse_def(snd_config_t *father, input_t *input)
                }
                if (mode == NOCREATE) {
                        SNDERR("%s does not exists", id);
-                       free(id);
-                       return -ENOENT;
+                       err = -ENOENT;
+                       goto __end;
                }
-               err = _snd_config_make_add(&n, id, SND_CONFIG_TYPE_COMPOUND, father);
+               err = _snd_config_make_add(&n, &id, SND_CONFIG_TYPE_COMPOUND, father);
                if (err < 0)
-                       return err;
+                       goto __end;
                n->u.compound.join = 1;
                father = n;
        }
@@ -526,104 +657,52 @@ static int parse_def(snd_config_t *father, input_t *input)
                        snd_config_delete(n);
                        n = NULL;
                }
-               else
-                       free(id);
        } else {
                n = NULL;
                if (mode == NOCREATE) {
                        SNDERR("%s does not exists", id);
-                       free(id);
-                       return -ENOENT;
+                       err = -ENOENT;
+                       goto __end;
                }
        }
        switch (c) {
        case '{':
+       case '[':
        {
+               char endchr;
                if (n) {
                        if (n->type != SND_CONFIG_TYPE_COMPOUND) {
                                SNDERR("%s is not a compound", id);
-                               return -EINVAL;
+                               err = -EINVAL;
+                               goto __end;
                        }
                } else {
-                       err = _snd_config_make_add(&n, id, SND_CONFIG_TYPE_COMPOUND, father);
+                       err = _snd_config_make_add(&n, &id, SND_CONFIG_TYPE_COMPOUND, father);
                        if (err < 0)
-                               return err;
+                               goto __end;
                }
-               err = parse_defs(n, input);
-               if (err < 0) {
-                       snd_config_delete(n);
-                       return err;
+               if (c == '{') {
+                       err = parse_defs(n, input);
+                       endchr = '}';
+               } else {
+                       err = parse_array_defs(n, input);
+                       endchr = ']';
                }
                c = get_nonwhite(input);
-               if (c != '}') {
+               if (c != endchr) {
                        snd_config_delete(n);
                        input->error = (c == EOF ? UNEXPECTED_EOF : UNEXPECTED_CHAR);
-                       return -EINVAL;
+                       err = -EINVAL;
+                       goto __end;
                }
                break;
        }
        default:
-       {
-               char *s;
                unget_char(c, input);
-               err = get_string(&s, 0, input);
+               err = parse_value(&n, father, input, &id);
                if (err < 0)
-                       return err;
-               if (!err && ((s[0] >= '0' && s[0] <= '9') || s[0] == '-')) {
-                       char *ptr;
-                       long i;
-                       errno = 0;
-                       i = strtol(s, &ptr, 0);
-                       if (*ptr == '.' || errno != 0) {
-                               double r;
-                               errno = 0;
-                               r = strtod(s, &ptr);
-                               if (errno == 0) {
-                                       free(s);
-                                       if (n) {
-                                               if (n->type != SND_CONFIG_TYPE_REAL) {
-                                                       SNDERR("%s is not a real", id);
-                                                       return -EINVAL;
-                                               }
-                                       } else {
-                                               err = _snd_config_make_add(&n, id, SND_CONFIG_TYPE_REAL, father);
-                                               if (err < 0)
-                                                       return err;
-                                       }
-                                       n->u.real = r;
-                                       break;
-                               }
-                       } else if (*ptr == '\0') {
-                               free(s);
-                               if (n) {
-                                       if (n->type != SND_CONFIG_TYPE_INTEGER) {
-                                               SNDERR("%s is not an integer", id);
-                                               return -EINVAL;
-                                       }
-                               } else {
-                                       err = _snd_config_make_add(&n, id, SND_CONFIG_TYPE_INTEGER, father);
-                                       if (err < 0)
-                                               return err;
-                               }
-                               n->u.integer = i;
-                               break;
-                       }
-               }
-               if (n) {
-                       if (n->type != SND_CONFIG_TYPE_STRING) {
-                               SNDERR("%s is not a string", id);
-                               free(s);
-                               return -EINVAL;
-                       }
-               } else {
-                       err = _snd_config_make_add(&n, id, SND_CONFIG_TYPE_STRING, father);
-                       if (err < 0)
-                               return err;
-               }
-               if (n->u.string)
-                       free(n->u.string);
-               n->u.string = s;
-       }
+                       goto __end;
+               break;
        }
        c = get_nonwhite(input);
        switch (c) {
@@ -633,6 +712,9 @@ static int parse_def(snd_config_t *father, input_t *input)
        default:
                unget_char(c, input);
        }
+      __end:
+       if (id)
+               free(id);
        return err;
 }
                
@@ -821,6 +903,33 @@ static int _snd_config_save_leaves(snd_config_t *config, snd_output_t *out, unsi
 
 
 /**
+ * \brief Return type of a config node from an ASCII string
+ * \param config Config node handle
+ * \return node type
+ */
+int snd_config_get_type_ascii(const char *ascii, snd_config_type_t *type)
+{
+       assert(ascii && type);
+       if (!strcmp(ascii, "integer")) {
+               *type = SND_CONFIG_TYPE_INTEGER;
+               return 0;
+       }
+       if (!strcmp(ascii, "real")) {
+               *type = SND_CONFIG_TYPE_REAL;
+               return 0;
+       }
+       if (!strcmp(ascii, "string")) {
+               *type = SND_CONFIG_TYPE_STRING;
+               return 0;
+       }
+       if (!strcmp(ascii, "compound")) {
+               *type = SND_CONFIG_TYPE_COMPOUND;
+               return 0;
+       }
+       return -EINVAL;
+}
+
+/**
  * \brief Return type of a config node
  * \param config Config node handle
  * \return node type
@@ -1012,7 +1121,7 @@ int snd_config_make(snd_config_t **config, const char *id,
                        return -ENOMEM;
        } else
                id1 = NULL;
-       return _snd_config_make(config, id1, type);
+       return _snd_config_make(config, &id1, type);
 }
 
 /**
@@ -1233,6 +1342,15 @@ int snd_config_get_ascii(snd_config_t *config, char **ascii)
                                assert(0);
                                return -ENOMEM;
                        }
+                       if (res[0]) {           /* trim the string */
+                               char *ptr;
+                               ptr = res + strlen(res) - 1;
+                               while (ptr != res && *ptr == ' ')
+                                       ptr--;
+                               if (*ptr != ' ')
+                                       ptr++;
+                               *ptr = '\0';
+                       }
                        *ascii = strdup(res);
                }
                break;
@@ -1268,7 +1386,7 @@ int snd_config_save(snd_config_t *config, snd_output_t *out)
  */
 int snd_config_search(snd_config_t *config, const char *key, snd_config_t **result)
 {
-       assert(config && key && result);
+       assert(config && key);
        while (1) {
                snd_config_t *n;
                int err;
@@ -1544,7 +1662,8 @@ typedef enum _snd_config_walk_pass {
 } snd_config_walk_pass_t;
 
 
-/* Return 1 if node need to be attached to father */
+/* Return 1 if node needs to be attached to father */
+/* Return 2 if compound is replaced with standard node */
 typedef int (*snd_config_walk_callback_t)(snd_config_t *src,
                                          snd_config_t **dst,
                                          snd_config_walk_pass_t pass,
@@ -1656,86 +1775,6 @@ int snd_config_copy(snd_config_t **dst,
        return snd_config_walk(src, dst, _snd_config_copy, NULL);
 }
 
-/**
- * \brief Expand the dynamic contents
- * \param src Source string
- * \param idchr Identification character
- * \param callback Callback function
- * \param private_data Private data for the given callback function
- * \param dst Destination string
- */
-int snd_config_string_replace(const char *src, char idchr,
-                             snd_config_string_replace_callback_t *callback,
-                             void *private_data,
-                             char **dst)
-{
-       int len = 0, len1, err;
-       const char *ptr, *end;
-       char *tmp, *what, *fptr, *rdst = NULL;
-
-       assert(src && idchr && dst);
-       while (*src != '\0') {
-               ptr = strchr(src, idchr);
-               end = NULL;
-               if (ptr == src && *(ptr + 1) == '(' && (end = strchr(ptr + 2, ')')) != NULL) {
-                       src = end + 1;
-                       if (callback == NULL)
-                               continue;
-                       len1 = end - (ptr + 2);
-                       if (len1 == 0)          /* empty */
-                               continue;
-                       what = malloc(len1 + 1);
-                       memcpy(what, ptr + 2, len1);
-                       what[len1] = '\0';
-                       fptr = NULL;
-                       err = callback(what, &fptr, private_data);
-                       free(what);
-                       if (err < 0) {
-                               if (*dst != NULL)
-                                       free(*dst);
-                               return err;
-                       }
-                       if (fptr == NULL)       /* empty */
-                               continue;
-                       len1 = strlen(ptr = fptr);
-               } else {
-                       if (ptr == NULL) {
-                               len1 = strlen(ptr = src);
-                       } else {
-                               len1 = ptr - src;
-                               ptr = src;
-                       }
-                       src += len1;
-                       fptr = NULL;
-               }
-               tmp = realloc(rdst, len + len1 + 1);
-               if (tmp == NULL) {
-                       if (*dst != NULL)
-                               free(*dst);
-                       return -ENOMEM;
-               }
-               memcpy(tmp + len, ptr, len1);
-               tmp[len+=len1] = '\0';
-               if (fptr)
-                       free(fptr);
-               rdst = tmp;
-       }
-       *dst = rdst;
-       return 0;
-}
-
-static int _snd_config_expand_replace(const char *what, char **dst, void *private_data)
-{
-       snd_config_t *vars = private_data;
-       snd_config_t *val;
-
-       assert(dst);
-       if (snd_config_search(vars, what, &val) < 0)
-               return 0;       /* empty */
-       else
-               return snd_config_get_ascii(val, dst);
-}
-
 static int _snd_config_expand(snd_config_t *src,
                              snd_config_t **dst,
                              snd_config_walk_pass_t pass,
@@ -1746,12 +1785,14 @@ static int _snd_config_expand(snd_config_t *src,
        snd_config_type_t type = snd_config_get_type(src);
        switch (pass) {
        case SND_CONFIG_WALK_PASS_PRE:
-               if (strcmp(id, "$") == 0)
+       {
+               if (strcmp(id, "args") == 0)
                        return 0;
                err = snd_config_make_compound(dst, id, src->u.compound.join);
                if (err < 0)
                        return err;
                break;
+       }
        case SND_CONFIG_WALK_PASS_LEAF:
                switch (type) {
                case SND_CONFIG_TYPE_INTEGER:
@@ -1781,11 +1822,12 @@ static int _snd_config_expand(snd_config_t *src,
                        const char *s;
                        snd_config_t *val;
                        snd_config_t *vars = private_data;
-                       err = snd_config_get_string(src, &s);
-                       if (strncmp(s, "$(", 2) == 0 && s[strlen(s) - 1] == ')') {
-                               char *str = alloca((strlen(s) - 3) + 1);
-                               memcpy(str, s + 2, strlen(s) - 3);
-                               str[strlen(s) - 3] = 0;
+                       snd_config_get_string(src, &s);
+                        if (strncmp(s, "$(", 2) == 0 && s[strlen(s) - 1] == ')') {
+                               int len = strlen(s) - 3;
+                               char *str = alloca(len + 1);
+                               memcpy(str, s + 2, len);
+                               str[len] = '\0';
                                if (snd_config_search(vars, str, &val) < 0)
                                        return 0;
                                err = snd_config_copy(dst, val);
@@ -1800,22 +1842,10 @@ static int _snd_config_expand(snd_config_t *src,
                                err = snd_config_make(dst, id, type);
                                if (err < 0)
                                        return err;
-                               if (strstr(s, "$(") != NULL) {
-                                       char *str = NULL;
-                                       err = snd_config_string_replace(s, '$', _snd_config_expand_replace, vars, &str);
-                                       if (err < 0)
-                                               return err;
-                                       if (str == NULL) {
-                                               snd_config_delete(*dst);
-                                               return 0;
-                                       }
-                                       (*dst)->u.string = str;
-                               } else {
-                                       err = snd_config_set_string(*dst, s);
-                                       if (err < 0) {
-                                               snd_config_delete(*dst);
-                                               return err;
-                                       }
+                               err = snd_config_set_string(*dst, s);
+                               if (err < 0) {
+                                       snd_config_delete(*dst);
+                                       return err;
                                }
                        }
                        break;
@@ -1830,6 +1860,181 @@ static int _snd_config_expand(snd_config_t *src,
        return 1;
 }
 
+static int evaluate_node(snd_config_t *father, snd_config_t *src,
+                        void *private_data, snd_config_t **_dst)
+{
+       snd_config_iterator_t i, next;
+       const char *lib = NULL, *func = NULL, *type = NULL;
+       int err;
+
+       assert(father && src && _dst);
+
+       snd_config_for_each(i, next, src) {
+               snd_config_t *n = snd_config_iterator_entry(i);
+               const char *id;
+               if (snd_config_get_type(n) == SND_CONFIG_TYPE_COMPOUND) {
+                       snd_config_t *n1;
+                       err = evaluate_node(src, n, private_data, &n1);
+                       if (err < 0) {
+                               SNDERR("Error in node %s", snd_config_get_id(n));
+                               goto __error;
+                       }
+                       if (n1) {                       /* replace node */
+                               snd_config_delete(n);
+                               err = snd_config_add(src, n1);
+                               if (err < 0)
+                                       goto __error;
+                       }
+               }
+               id = snd_config_get_id(n);
+               if (*id++ != '@')       /* quick look */
+                       continue;
+               if (!strcmp(id, "lib")) {
+                       if ((err = snd_config_get_string(n, &lib)) < 0) {
+                              _invalid_field:
+                               SNDERR("Unknown type of field %s", id);
+                               return err;
+                       }
+                       lib = strdup(lib);
+                       if (lib == NULL)
+                               goto __error;
+                       snd_config_delete(n);
+               } else if (!strcmp(id, "func")) {
+                       if ((err = snd_config_get_string(n, &func)) < 0)
+                               goto _invalid_field;
+                       func = strdup(func);
+                       if (func == NULL)
+                               goto __error;
+                       snd_config_delete(n);
+               } else if (!strcmp(id, "type")) {
+                       if ((err = snd_config_get_string(n, &type)) < 0)
+                               goto _invalid_field;
+                       type = strdup(type);
+                       if (type == NULL)
+                               goto __error;
+                       snd_config_delete(n);
+               }
+       }
+       
+       if (func == NULL) {
+               *_dst = NULL;
+               return 0;
+       }
+
+       {
+               char buf[64], *ptr;
+               snd_config_type_t t;
+               snd_config_t *dst = NULL;
+               char *evaluate_name = NULL;
+               int (*evaluate_func)(char **dst, snd_config_t *src, void *private_data);
+               void *h;
+       
+               if (evaluate_name == NULL) {
+                       snprintf(buf, sizeof(buf), "snd_func_%s", func);
+                       buf[sizeof(buf)-1] = '\0';
+                       evaluate_name = buf;
+               }
+       
+               h = dlopen(lib, RTLD_NOW);
+               if (!h) {
+                       SNDERR("Cannot open shared library %s", lib);
+                       return -ENOENT;
+               }
+               evaluate_func = dlsym(h, evaluate_name);
+               if (!evaluate_func) {
+                       dlclose(h);
+                       SNDERR("symbol %s is not defined inside %s", evaluate_name, lib ? lib : ALSA_LIB);
+                       return -ENXIO;
+               }
+               err = evaluate_func(&ptr, src, private_data);
+               dlclose(h);
+               if (err < 0) {
+                       SNDERR("function %s returned error: %s", evaluate_name, snd_strerror(err));
+                       return err;
+               }
+               if (type == NULL) {
+                       t = SND_CONFIG_TYPE_STRING;
+               } else {
+                       err = snd_config_get_type_ascii(type, &t);
+                       if (err < 0 || t == SND_CONFIG_TYPE_COMPOUND) {
+                               free(ptr);
+                               return -EINVAL;
+                       }
+               }
+               err = snd_config_make(&dst, snd_config_get_id(src), t);
+               if (err < 0) {
+                       free(ptr);
+                       return err;
+               }
+               switch (t) {
+               case SND_CONFIG_TYPE_INTEGER:
+                       {
+                               long v;
+                               err = safe_strtol(ptr, &v);
+                               if (err < 0) {
+                                       free(ptr);
+                                       snd_config_delete(dst);
+                                       return err;
+                               }
+                               snd_config_set_integer(dst, v);
+                       }
+                       break;
+               case SND_CONFIG_TYPE_REAL:
+                       {
+                               double r;
+                               err = safe_strtod(ptr, &r);
+                               if (err < 0) {
+                                       free(ptr);
+                                       snd_config_delete(dst);
+                                       return err;
+                               }
+                               snd_config_set_real(dst, r);
+                       }
+                       break;
+               default:
+                       snd_config_set_string(dst, ptr);
+                       break;
+               }
+               free(ptr);
+               *_dst = dst;
+       }
+       
+       err = 0;
+      __error:
+       if (func)
+               free((void *)func);
+       if (lib)
+               free((void *)lib);
+       if (type)
+               free((void *)type);
+       return err;
+}
+
+int snd_config_evaluate(snd_config_t *conf, void *private_data)
+{
+       snd_config_iterator_t i, next;
+
+       assert(conf);
+       snd_config_for_each(i, next, conf) {
+               snd_config_t *n = snd_config_iterator_entry(i);
+               if (snd_config_get_type(n) == SND_CONFIG_TYPE_COMPOUND) {
+                       snd_config_t *n1;
+                       int err = evaluate_node(conf, n, private_data, &n1);
+                       if (err < 0) {
+                               SNDERR("Error in node %s", snd_config_get_id(n));
+                               return err;
+                       }
+                       if (n1) {                       /* replace node */
+                               snd_config_delete(n);
+                               err = snd_config_add(conf, n1);
+                               if (err < 0)
+                                       return err;
+                       }
+               }
+       }
+       return 0;
+}
+
 static int load_defaults(snd_config_t *subs, snd_config_t *defs)
 {
        snd_config_iterator_t d, dnext;
@@ -2055,6 +2260,8 @@ static int parse_args(snd_config_t *subs, const char *str, snd_config_t *defs)
 {
        int err;
        int arg = 0;
+       if (str == NULL)
+               return 0;
        skip_blank(&str);
        if (!*str)
                return 0;
@@ -2198,35 +2405,54 @@ static int parse_args(snd_config_t *subs, const char *str, snd_config_t *defs)
 }
 
 /**
- * \brief Expand a node applying arguments
+ * \brief Expand a node applying arguments and functions
  * \param config Config node handle
- * \param args Arguments string
+ * \param args Arguments string (optional)
+ * \param private_data Private data for functions
  * \param result Pointer to found node
  * \return 0 on success otherwise a negative error code
  */
 int snd_config_expand(snd_config_t *config, const char *args,
-                     snd_config_t **result)
+                     void *private_data, snd_config_t **result)
 {
        int err;
-       snd_config_t *defs, *subs;
-       err = snd_config_search(config, "$", &defs);
-       if (err < 0)
-               return -EINVAL;
-       err = snd_config_top(&subs);
-       if (err < 0)
-               return err;
-       err = load_defaults(subs, defs);
-       if (err < 0)
-               goto _end;
-       err = parse_args(subs, args, defs);
-       if (err < 0)
-               goto _end;
-       err = snd_config_walk(config, result, _snd_config_expand, subs);
-       if (err < 0)
+       snd_config_t *defs, *subs = NULL, *res;
+       err = snd_config_search(config, "args", &defs);
+       if (err < 0) {
+               err = snd_config_copy(&res, config);
+               if (err < 0)
+                       return err;
+       } else {
+               err = snd_config_top(&subs);
+               if (err < 0)
+                       return err;
+               err = load_defaults(subs, defs);
+               if (err < 0) {
+                       SNDERR("Load defaults error: %s", snd_strerror(err));
+                       goto _end;
+               }
+               err = parse_args(subs, args, defs);
+               if (err < 0) {
+                       SNDERR("Parse arguments error: %s", snd_strerror(err));
+                       goto _end;
+               }
+               err = snd_config_walk(config, &res, _snd_config_expand, subs);
+               if (err < 0) {
+                       SNDERR("Expand error (walk): %s", snd_strerror(err));
+                       goto _end;
+               }
+       }
+       err = snd_config_evaluate(res, private_data);
+       if (err < 0) {
+               SNDERR("Evaluate error: %s", snd_strerror(err));
+               snd_config_delete(res);
                goto _end;
+       }
+       *result = res;
        err = 1;
  _end:
-       snd_config_delete(subs);
+       if (subs)
+               snd_config_delete(subs);
        return err;
 }
        
index a656ec0..0babee8 100644 (file)
@@ -139,88 +139,72 @@ int snd_config_get_ctl_iface(snd_config_t *conf)
        return err;
 }
 
-static int _snd_config_redirect_load_replace(const char *what, char **dst, void *private_data ATTRIBUTE_UNUSED)
+/**
+ * \brief Expand the dynamic contents
+ * \param src Source string
+ * \param idchr Identification character
+ * \param callback Callback function
+ * \param private_data Private data for the given callback function
+ * \param dst Destination string
+ */
+int snd_config_string_replace(const char *src, char idchr,
+                             snd_config_string_replace_callback_t *callback,
+                             void *private_data,
+                             char **dst)
 {
-       enum {
-               CARD_ID,
-               PCM_ID,
-               RAWMIDI_ID
-       } id;
-       int len;
+       int len = 0, len1, err;
+       const char *ptr, *end;
+       char *tmp, *what, *fptr, *rdst = NULL;
 
-       if (!strcmp(what, "datadir")) {
-               *dst = strdup(DATADIR "/alsa");
-               return *dst == NULL ? -ENOMEM : 0;
-       }
-       if (!strncmp(what, "card_id:", len = 8))
-               id = CARD_ID;
-       else if (!strncmp(what, "pcm_id:", len = 7))
-               id = PCM_ID;
-       else if (!strncmp(what, "rawmidi_id:", len = 11))
-               id = RAWMIDI_ID;
-       else
-               return 0;
-       {
-               snd_ctl_t *ctl;
-               int err;
-               char name[12];
-               const char *str = NULL;
-               char *fstr = NULL;
-               sprintf(name, "hw:%d", atoi(what + len));
-               err = snd_ctl_open(&ctl, name, 0);
-               if (err < 0)
-                       return err;
-               switch (id) {
-               case CARD_ID:
-                       {
-                               snd_ctl_card_info_t *info;
-                               snd_ctl_card_info_alloca(&info);
-                               err = snd_ctl_card_info(ctl, info);
-                               if (err < 0)
-                                       return err;
-                               err = snd_card_type_enum_to_string(snd_ctl_card_info_get_type(info), &fstr);
+       assert(src && idchr && dst);
+       while (*src != '\0') {
+               ptr = strchr(src, idchr);
+               end = NULL;
+               if (ptr == src && *(ptr + 1) == '(' && (end = strchr(ptr + 2, ')')) != NULL) {
+                       src = end + 1;
+                       if (callback == NULL)
+                               continue;
+                       len1 = end - (ptr + 2);
+                       if (len1 == 0)          /* empty */
+                               continue;
+                       what = malloc(len1 + 1);
+                       memcpy(what, ptr + 2, len1);
+                       what[len1] = '\0';
+                       fptr = NULL;
+                       err = callback(what, &fptr, private_data);
+                       free(what);
+                       if (err < 0) {
+                               if (*dst != NULL)
+                                       free(*dst);
+                               return err;
                        }
-                       break;
-               case PCM_ID:
-                       {
-                               char *ptr = strchr(what + len, ',');
-                               int dev = atoi(what + len);
-                               int subdev = ptr ? atoi(ptr + 1) : -1;
-                               snd_pcm_info_t *info;
-                               snd_pcm_info_alloca(&info);
-                               snd_pcm_info_set_device(info, dev);
-                               snd_pcm_info_set_subdevice(info, subdev);
-                               err = snd_ctl_pcm_info(ctl, info);
-                               if (err < 0)
-                                       return err;
-                               str = snd_pcm_info_get_id(info);
-                       }
-                       break;
-               case RAWMIDI_ID:
-                       {
-                               char *ptr = strchr(what + len, ',');
-                               int dev = atoi(what + len);
-                               int subdev = ptr ? atoi(ptr + 1) : -1;
-                               snd_rawmidi_info_t *info;
-                               snd_rawmidi_info_alloca(&info);
-                               snd_rawmidi_info_set_device(info, dev);
-                               snd_rawmidi_info_set_subdevice(info, subdev);
-                               err = snd_ctl_rawmidi_info(ctl, info);
-                               if (err < 0)
-                                       return err;
-                               str = snd_rawmidi_info_get_id(info);
+                       if (fptr == NULL)       /* empty */
+                               continue;
+                       len1 = strlen(ptr = fptr);
+               } else {
+                       if (ptr == NULL) {
+                               len1 = strlen(ptr = src);
+                       } else {
+                               len1 = ptr - src;
+                               ptr = src;
                        }
-                       break;
+                       src += len1;
+                       fptr = NULL;
                }
-               if (err < 0)
-                       return err;
-               snd_ctl_close(ctl);
-               *dst = fstr ? fstr : (str ? strdup(str) : NULL);
-               if (*dst == NULL)
-                       return 0;
-               return 0;
+               tmp = realloc(rdst, len + len1 + 1);
+               if (tmp == NULL) {
+                       if (*dst != NULL)
+                               free(*dst);
+                       return -ENOMEM;
+               }
+               memcpy(tmp + len, ptr, len1);
+               tmp[len+=len1] = '\0';
+               if (fptr)
+                       free(fptr);
+               rdst = tmp;
        }
-       return 0;       /* empty */
+       *dst = rdst;
+       return 0;
 }
 
 /**
@@ -262,19 +246,14 @@ int snd_config_redirect_load(snd_config_t *root,
        rname = NULL;
        if (snd_config_search(config, "filename", &c) >= 0) {
                snd_config_t *rconfig;
-               char *filename;
+               const char *filename;
                snd_input_t *input;
                err = snd_config_copy(&rconfig, root);
                if (err < 0)
                        return err;
                if (snd_config_get_type(c) == SND_CONFIG_TYPE_STRING) {
-                       snd_config_get_string(c, (const char **)&filename);
-                       if ((err = snd_config_string_replace(filename, '&', _snd_config_redirect_load_replace, NULL, &filename)) < 0)
-                               goto __filename_error;
-                       if (filename == NULL)
-                               goto __filename_error_einval;
+                       snd_config_get_string(c, &filename);
                } else {
-                     __filename_error_einval:
                        err = -EINVAL;
                      __filename_error:
                        snd_config_delete(rconfig);
@@ -289,10 +268,8 @@ int snd_config_redirect_load(snd_config_t *root,
                if (err < 0) {
                        snd_input_close(input);
                        goto __filename_error;
-                       return err;
                }
                snd_input_close(input);
-               free(filename);
                result = rconfig;
                dynamic = 1;
        }
@@ -300,8 +277,11 @@ int snd_config_redirect_load(snd_config_t *root,
                const char *ptr;
                if ((err = snd_config_get_string(c, &ptr)) < 0)
                        goto __error;
-               if ((err = snd_config_string_replace(ptr, '&', _snd_config_redirect_load_replace, NULL, &rname)) < 0)
+               rname = strdup(ptr);
+               if (rname == NULL) {
+                       err = -ENOMEM;
                        goto __error;
+               }
        }
        if (rname == NULL) {
                err = -EINVAL;
@@ -318,3 +298,320 @@ int snd_config_redirect_load(snd_config_t *root,
                snd_config_delete(result);
        return err;
 }
+
+/*
+ *  Helper functions for the configuration file
+ */
+
+int snd_func_getenv(char **dst, snd_config_t *src, void *private_data ATTRIBUTE_UNUSED)
+{
+       snd_config_t *n, *d, *e;
+       snd_config_iterator_t i, next;
+       const char *res;
+       char *def = NULL;
+       int idx = 0, err;
+       
+       err = snd_config_expand(src, NULL, NULL, &e);
+       if (err < 0)
+               return err;
+       err = snd_config_search(e, "envname", &n);
+       if (err < 0) {
+               SNDERR("field envname not found");
+               goto __error;
+       }
+       err = snd_config_search(e, "default", &d);
+       if (err < 0) {
+               SNDERR("field default not found");
+               goto __error;
+       }
+       err = snd_config_get_ascii(d, &def);
+       if (err < 0) {
+               SNDERR("error getting field default");
+               goto __error;
+       }
+      __retry:
+       snd_config_for_each(i, next, n) {
+               snd_config_t *n = snd_config_iterator_entry(i);
+               const char *id = snd_config_get_id(n);
+               const char *ptr, *env;
+               long i;
+               if (snd_config_get_type(n) != SND_CONFIG_TYPE_STRING) {
+                       SNDERR("field %s is not a string", id);
+                       err = -EINVAL;
+                       goto __error;
+               }
+               err = safe_strtol(id, &i);
+               if (err < 0) {
+                       SNDERR("id of field %s is not an integer", id);
+                       err = -EINVAL;
+                       goto __error;
+               }
+               if (i == idx) {
+                       idx++;
+                       snd_config_get_string(n, &ptr);
+                       env = getenv(ptr);
+                       if (env != NULL && *env != '\0') {
+                               res = strdup(env);
+                               goto __ok;
+                       }
+                       goto __retry;
+               }
+       }
+       res = def;
+       def = NULL;
+      __ok:
+       err = res == NULL ? -ENOMEM : 0;
+       *dst = (char *)res;
+      __error:
+       if (def)
+               free(def);
+       snd_config_delete(e);
+       return err;
+}
+
+int snd_func_concat(char **dst, snd_config_t *src, void *private_data ATTRIBUTE_UNUSED)
+{
+       snd_config_t *n, *e;
+       snd_config_iterator_t i, next;
+       char *res = NULL, *tmp;
+       int idx = 0, len = 0, len1, err;
+       
+       err = snd_config_expand(src, NULL, NULL, &e);
+       if (err < 0)
+               return err;
+       err = snd_config_search(e, "strings", &n);
+       if (err < 0) {
+               SNDERR("field strings not found");
+               goto __error;
+       }
+      __retry:
+       snd_config_for_each(i, next, n) {
+               snd_config_t *n = snd_config_iterator_entry(i);
+               char *ptr;
+               const char *id = snd_config_get_id(n);
+               long i;
+               err = safe_strtol(id, &i);
+               if (err < 0) {
+                       SNDERR("id of field %s is not an integer", id);
+                       err = -EINVAL;
+                       goto __error;
+               }
+               if (i == idx) {
+                       idx++;
+                       snd_config_get_ascii(n, &ptr);
+                       len1 = strlen(ptr);
+                       tmp = realloc(res, len + len1 + 1);
+                       if (tmp == NULL) {
+                               free(ptr);
+                               if (res)
+                                       free(res);
+                               err = -ENOMEM;
+                               goto __error;
+                       }
+                       memcpy(tmp + len, ptr, len1);
+                       free(ptr);
+                       len += len1;
+                       tmp[len] = '\0';
+                       res = tmp;
+                       goto __retry;
+               }
+       }
+       if (res == NULL) {
+               SNDERR("empty string is not accepted");
+               err = -EINVAL;
+               goto __error;
+       }
+       err = 0;
+       *dst = res;
+      __error:
+       snd_config_delete(e);
+       return err;
+}
+
+int snd_func_datadir(char **dst, snd_config_t *src ATTRIBUTE_UNUSED, void *private_data ATTRIBUTE_UNUSED)
+{
+       char *res = strdup(DATADIR "/alsa");
+       if (res == NULL)
+               return -ENOMEM;
+       *dst = res;
+       return 0;
+}
+
+static int open_ctl(long card, snd_ctl_t **ctl)
+{
+       char name[16];
+       snprintf(name, sizeof(name), "hw:%li", card);
+       name[sizeof(name)-1] = '\0';
+       return snd_ctl_open(ctl, name, 0);
+}
+
+#if 0
+static int string_from_integer(char **dst, long v)
+{
+       char str[32];
+       char *res;
+       sprintf(str, "%li", v);
+       res = strdup(str);
+       if (res == NULL)
+               return -ENOMEM;
+       *dst = res;
+       return 0;
+}
+#endif
+
+int snd_func_card_strtype(char **dst, snd_config_t *src, void *private_data ATTRIBUTE_UNUSED)
+{
+       snd_config_t *n, *e;
+       char *res = NULL;
+       snd_ctl_t *ctl = NULL;
+       snd_ctl_card_info_t *info;
+       long v;
+       int err;
+       
+       err = snd_config_expand(src, NULL, NULL, &e);
+       if (err < 0)
+               return err;
+       err = snd_config_search(e, "card", &n);
+       if (err < 0) {
+               SNDERR("field card not found");
+               goto __error;
+       }
+       err = snd_config_get_integer(n, &v);
+       if (err < 0) {
+               SNDERR("field card is not an integer");
+               goto __error;
+       }
+       err = open_ctl(v, &ctl);
+       if (err < 0) {
+               SNDERR("could not open control for card %li", v);
+               goto __error;
+       }
+       snd_ctl_card_info_alloca(&info);
+       err = snd_ctl_card_info(ctl, info);
+       if (err < 0) {
+               SNDERR("snd_ctl_card_info error: %s", snd_strerror(err));
+               goto __error;
+       }
+       err = snd_card_type_enum_to_string(snd_ctl_card_info_get_type(info), &res);
+       if (err < 0) {
+               SNDERR("snd_card_type_enum_to_string failed for %i", (int)snd_ctl_card_info_get_type(info));
+               goto __error;
+       }
+       *dst = res;
+      __error:
+       if (ctl)
+               snd_ctl_close(ctl);
+       snd_config_delete(e);
+       return err;
+}
+
+int snd_func_card_id(char **dst, snd_config_t *src, void *private_data ATTRIBUTE_UNUSED)
+{
+       snd_config_t *n, *e;
+       char *res = NULL;
+       snd_ctl_t *ctl = NULL;
+       snd_ctl_card_info_t *info;
+       long v;
+       int err;
+       
+       err = snd_config_expand(src, NULL, NULL, &e);
+       if (err < 0)
+               return err;
+       err = snd_config_search(e, "card", &n);
+       if (err < 0) {
+               SNDERR("field card not found");
+               goto __error;
+       }
+       err = snd_config_get_integer(n, &v);
+       if (err < 0) {
+               SNDERR("field card is not an integer");
+               goto __error;
+       }
+       err = open_ctl(v, &ctl);
+       if (err < 0) {
+               SNDERR("could not open control for card %li", v);
+               goto __error;
+       }
+       snd_ctl_card_info_alloca(&info);
+       err = snd_ctl_card_info(ctl, info);
+       if (err < 0) {
+               SNDERR("snd_ctl_card_info error: %s", snd_strerror(err));
+               goto __error;
+       }
+       res = strdup(snd_ctl_card_info_get_id(info));
+       if (res == NULL) {
+               err = -ENOMEM;
+               goto __error;
+       }
+       *dst = res;
+      __error:
+       if (ctl)
+               snd_ctl_close(ctl);
+       snd_config_delete(e);
+       return err;
+}
+
+int snd_func_pcm_id(char **dst, snd_config_t *src, void *private_data ATTRIBUTE_UNUSED)
+{
+       snd_config_t *n, *e;
+       char *res = NULL;
+       snd_ctl_t *ctl = NULL;
+       snd_pcm_info_t *info;
+       long card, device, subdevice = 0;
+       int err;
+       
+       err = snd_config_expand(src, NULL, NULL, &e);
+       if (err < 0)
+               return err;
+       err = snd_config_search(e, "card", &n);
+       if (err < 0) {
+               SNDERR("field card not found");
+               goto __error;
+       }
+       err = snd_config_get_integer(n, &card);
+       if (err < 0) {
+               SNDERR("field card is not an integer");
+               goto __error;
+       }
+       err = snd_config_search(e, "device", &n);
+       if (err < 0) {
+               SNDERR("field device not found");
+               goto __error;
+       }
+       err = snd_config_get_integer(n, &device);
+       if (err < 0) {
+               SNDERR("field device is not an integer");
+               goto __error;
+       }
+       if (snd_config_search(e, "subdevice", &n) >= 0) {
+               err = snd_config_get_integer(n, &subdevice);
+               if (err < 0) {
+                       SNDERR("field subdevice is not an integer");
+                       goto __error;
+               }
+       }
+       err = open_ctl(card, &ctl);
+       if (err < 0) {
+               SNDERR("could not open control for card %li", card);
+               goto __error;
+       }
+       snd_pcm_info_alloca(&info);
+       snd_pcm_info_set_device(info, device);
+       snd_pcm_info_set_subdevice(info, subdevice);
+       err = snd_ctl_pcm_info(ctl, info);
+       if (err < 0) {
+               SNDERR("snd_ctl_pcm_info error: %s", snd_strerror(err));
+               goto __error;
+       }
+       res = strdup(snd_pcm_info_get_id(info));
+       if (res == NULL) {
+               err = -ENOMEM;
+               goto __error;
+       }
+       *dst = res;
+      __error:
+       if (ctl)
+               snd_ctl_close(ctl);
+       snd_config_delete(e);
+       return err;
+}
index 9048276..37d00f4 100644 (file)
@@ -478,7 +478,7 @@ int snd_ctl_open_noupdate(snd_ctl_t **ctlp, const char *name, int mode)
                return err;
        }
        if (args) {
-               err = snd_config_expand(ctl_conf, args, &ctl_conf);
+               err = snd_config_expand(ctl_conf, args, NULL, &ctl_conf);
                if (err < 0)
                        return err;
        }
index 5bb93b3..d4f8c35 100644 (file)
@@ -1035,12 +1035,10 @@ static int snd_pcm_open_noupdate(snd_pcm_t **pcmp, snd_config_t *root,
                        return err;
                }
        }
-       if (args == NULL && snd_config_search(pcm_conf, "$", &conf) >= 0) /* expand arguments */
-               args = "";
-       if (args) {
-               err = snd_config_expand(pcm_conf, args, &pcm_conf);
-               if (err < 0)
-                       return err;
+       err = snd_config_expand(pcm_conf, args, NULL, &pcm_conf);
+       if (err < 0) {
+               SNDERR("Could not expand configuration: %s", snd_strerror(err));
+               return err;
        }
        if (snd_config_search(pcm_conf, "redirect", &conf) >= 0) {
                snd_config_t *tmp_conf;
@@ -1049,8 +1047,10 @@ static int snd_pcm_open_noupdate(snd_pcm_t **pcmp, snd_config_t *root,
                err = snd_config_redirect_load(root, conf, &redir_name, &tmp_conf, &conf_free_tmp);
                if (args)
                        snd_config_delete(pcm_conf);
-               if (err < 0)
+               if (err < 0) {
+                       SNDERR("Redirect error: %s", snd_strerror(err));
                        return err;
+               }
                err = snd_pcm_open_noupdate(pcmp, tmp_conf, redir_name, stream, mode);
                if (redir_name)
                        free(redir_name);
@@ -1059,8 +1059,7 @@ static int snd_pcm_open_noupdate(snd_pcm_t **pcmp, snd_config_t *root,
                return err;
        }
        err = snd_pcm_open_conf(pcmp, name, root, pcm_conf, stream, mode);
-       if (args)
-               snd_config_delete(pcm_conf);
+       snd_config_delete(pcm_conf);
        return err;
 }
 
index 4741750..413aaf2 100644 (file)
@@ -175,7 +175,7 @@ int snd_rawmidi_open_noupdate(snd_rawmidi_t **inputp, snd_rawmidi_t **outputp,
                return err;
        }
        if (args) {
-               err = snd_config_expand(rawmidi_conf, args, &rawmidi_conf);
+               err = snd_config_expand(rawmidi_conf, args, NULL, &rawmidi_conf);
                if (err < 0)
                        return err;
        }
index dd42b2a..198f9ef 100644 (file)
@@ -161,7 +161,7 @@ static int snd_seq_open_noupdate(snd_seq_t **seqp, const char *name,
                return err;
        }
        if (args) {
-               err = snd_config_expand(seq_conf, args, &seq_conf);
+               err = snd_config_expand(seq_conf, args, NULL, &seq_conf);
                if (err < 0)
                        return err;
        }