OSDN Git Service

ucm: switch to ucm2 directory and v2 format, keep backward compatibility
authorJaroslav Kysela <perex@perex.cz>
Sat, 9 Nov 2019 10:53:32 +0000 (11:53 +0100)
committerJaroslav Kysela <perex@perex.cz>
Sun, 10 Nov 2019 12:10:33 +0000 (13:10 +0100)
Signed-off-by: Jaroslav Kysela <perex@perex.cz>
src/ucm/parser.c
src/ucm/ucm_local.h
src/ucm/utils.c

index d0190b5..3e1fc24 100644 (file)
@@ -62,6 +62,65 @@ static int parse_sequence(snd_use_case_mgr_t *uc_mgr,
                          snd_config_t *cfg);
 
 /*
+ * compose configuration file
+ */
+static void configuration_filename2(char *fn, size_t fn_len, int format,
+                                   const char *dir, const char *file,
+                                   const char *suffix)
+{
+       snprintf(fn, fn_len, "%s/ucm%s/%s/%s%s",
+                snd_config_topdir(), format >= 2 ? "2" : "",
+                dir, file, suffix);
+       fn[fn_len-1] = '\0';
+}
+
+static void configuration_filename(snd_use_case_mgr_t *uc_mgr,
+                                  char *fn, size_t fn_len,
+                                  const char *file, const char *suffix)
+{
+       const char *env, *dir;
+
+       if (uc_mgr->conf_format > 0) {
+               /* known format */
+               env = getenv(uc_mgr->conf_format >= 2 ? ALSA_CONFIG_UCM2_VAR :
+                                                       ALSA_CONFIG_UCM_VAR);
+       } else {
+               /* auto-detect */
+               env = getenv(ALSA_CONFIG_UCM2_VAR);
+               if (env == NULL) {
+                       env = getenv(ALSA_CONFIG_UCM_VAR);
+               } else {
+                       uc_mgr->conf_format = 2;
+               }
+       }
+       if (env) {
+               snprintf(fn, fn_len, "%s/%s/%s%s",
+                       env, uc_mgr->conf_file_name, file, suffix);
+               fn[fn_len-1] = '\0';
+               return;
+       }
+
+       dir = uc_mgr->conf_file_name;
+       if (uc_mgr->conf_format > 0) {
+__format:
+               configuration_filename2(fn, fn_len, uc_mgr->conf_format,
+                                       dir, file, suffix);
+               return;
+       }
+
+       configuration_filename2(fn, fn_len, 2, dir, file, suffix);
+       if (access(fn, R_OK) == 0)
+               return;
+
+       configuration_filename2(fn, fn_len, 0, dir, file, suffix);
+       if (access(fn, R_OK)) {
+               /* make sure that the error message refers to the new path */
+               uc_mgr->conf_format = 2;
+               goto __format;
+       }
+}
+
+/*
  * Parse string
  */
 int parse_string(snd_config_t *n, char **res)
@@ -1052,7 +1111,6 @@ static int parse_verb_file(snd_use_case_mgr_t *uc_mgr,
        struct use_case_verb *verb;
        snd_config_t *cfg;
        char filename[MAX_FILE];
-       char *env = getenv(ALSA_CONFIG_UCM_VAR);
        int err;
 
        /* allocate verb */
@@ -1080,15 +1138,8 @@ static int parse_verb_file(snd_use_case_mgr_t *uc_mgr,
        }
 
        /* open Verb file for reading */
-       if (env)
-               snprintf(filename, sizeof(filename), "%s/%s/%s",
-                        env, uc_mgr->conf_file_name, file);
-       else
-               snprintf(filename, sizeof(filename), "%s/ucm/%s/%s",
-                        snd_config_topdir(), uc_mgr->conf_file_name, file);
-       filename[sizeof(filename)-1] = '\0';
-       
-       err = uc_mgr_config_load(filename, &cfg);
+       configuration_filename(uc_mgr, filename, sizeof(filename), file, "");
+       err = uc_mgr_config_load(uc_mgr->conf_format, filename, &cfg);
        if (err < 0) {
                uc_error("error: failed to open verb file %s : %d",
                        filename, -errno);
@@ -1283,6 +1334,7 @@ static int parse_master_file(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg)
        snd_config_iterator_t i, next;
        snd_config_t *n;
        const char *id;
+       long l;
        int err;
 
        if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
@@ -1290,6 +1342,23 @@ static int parse_master_file(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg)
                return -EINVAL;
        }
 
+       if (uc_mgr->conf_format >= 2) {
+               err = snd_config_search(cfg, "Syntax", &n);
+               if (err < 0) {
+                       uc_error("Syntax field not found in %s", uc_mgr->conf_file_name);
+                       return -EINVAL;
+               }
+               err = snd_config_get_integer(n, &l);
+               if (err < 0) {
+                       uc_error("Syntax field is invalid in %s", uc_mgr->conf_file_name);
+                       return err;
+               }
+               if (l < 2 || l > SYNTAX_VERSION_MAX) {
+                       uc_error("Incompatible syntax %d in %s", l, uc_mgr->conf_file_name);
+                       return -EINVAL;
+               }
+       }
+
        /* parse master config sections */
        snd_config_for_each(i, next, cfg) {
 
@@ -1297,6 +1366,9 @@ static int parse_master_file(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg)
                if (snd_config_get_id(n, &id) < 0)
                        continue;
 
+               if (uc_mgr->conf_format >= 2 && strcmp(id, "Syntax") == 0)
+                       continue;
+
                if (strcmp(id, "Comment") == 0) {
                        err = parse_string(n, &uc_mgr->comment);
                        if (err < 0) {
@@ -1400,10 +1472,10 @@ next_card:
        return -1;
 }
 
-static int load_master_config(const char *card_name, snd_config_t **cfg)
+static int load_master_config(snd_use_case_mgr_t *uc_mgr,
+                             const char *card_name, snd_config_t **cfg)
 {
        char filename[MAX_FILE];
-       char *env = getenv(ALSA_CONFIG_UCM_VAR);
        int err;
 
        if (strnlen(card_name, MAX_CARD_LONG_NAME) == MAX_CARD_LONG_NAME) {
@@ -1412,16 +1484,9 @@ static int load_master_config(const char *card_name, snd_config_t **cfg)
                return -EINVAL;
        }
 
-       if (env)
-               snprintf(filename, sizeof(filename)-1,
-                        "%s/%s/%s.conf", env, card_name, card_name);
-       else
-               snprintf(filename, sizeof(filename)-1,
-                        "%s/ucm/%s/%s.conf", snd_config_topdir(),
-                        card_name, card_name);
-       filename[MAX_FILE-1] = '\0';
-
-       err = uc_mgr_config_load(filename, cfg);
+       configuration_filename(uc_mgr, filename, sizeof(filename),
+                              card_name, ".conf");
+       err = uc_mgr_config_load(uc_mgr->conf_format, filename, cfg);
        if (err < 0) {
                uc_error("error: could not parse configuration for card %s",
                                card_name);
@@ -1452,7 +1517,7 @@ int uc_mgr_import_master_config(snd_use_case_mgr_t *uc_mgr)
 
        err = get_card_long_name(uc_mgr);
        if (err == 0)   /* load file that maches the card long name */
-               err = load_master_config(uc_mgr->card_long_name, &cfg);
+               err = load_master_config(uc_mgr, uc_mgr->card_long_name, &cfg);
 
        if (err == 0) {
                /* got device-specific file that matches the card long name */
@@ -1462,7 +1527,7 @@ int uc_mgr_import_master_config(snd_use_case_mgr_t *uc_mgr)
                 * either short name or long name (users may open a card by
                 * its name or long name).
                 */
-               err = load_master_config(uc_mgr->card_name, &cfg);
+               err = load_master_config(uc_mgr, uc_mgr->card_name, &cfg);
                if (err < 0)
                        return err;
                strncpy(uc_mgr->conf_file_name, uc_mgr->card_name, MAX_CARD_LONG_NAME);
@@ -1512,26 +1577,27 @@ static int is_component_directory(const char *dir)
  *
  * Cards are defined by machines. Each card/machine installs its UCM
  * configuration files in a subdirectory with the same name as the sound
- * card under /usr/share/alsa/ucm. This function will scan all the card
+ * card under /usr/share/alsa/ucm2. This function will scan all the card
  * directories and skip the component directories defined in the array
  * component_dir.
  */
 int uc_mgr_scan_master_configs(const char **_list[])
 {
        char filename[MAX_FILE], dfl[MAX_FILE];
-       char *env = getenv(ALSA_CONFIG_UCM_VAR);
-       const char **list;
+       char *env = getenv(ALSA_CONFIG_UCM2_VAR);
+       const char **list, *d_name;
        snd_config_t *cfg, *c;
        int i, j, cnt, err;
+       long l;
        ssize_t ss;
        struct dirent **namelist;
 
        if (env)
                snprintf(filename, sizeof(filename)-1, "%s", env);
        else
-               snprintf(filename, sizeof(filename)-1, "%s/ucm",
+               snprintf(filename, sizeof(filename)-1, "%s/ucm2",
                         snd_config_topdir());
-       filename[MAX_FILE-1] = '\0';
+       filename[sizeof(filename)-1] = '\0';
 
 #if defined(_GNU_SOURCE) && !defined(__NetBSD__) && !defined(__FreeBSD__) && !defined(__sun) && !defined(ANDROID)
 #define SORTFUNC       versionsort
@@ -1569,13 +1635,32 @@ int uc_mgr_scan_master_configs(const char **_list[])
 
        for (i = j = 0; i < cnt; i++) {
 
+               d_name = namelist[i]->d_name;
+
                /* Skip the directories for component devices */
-               if (is_component_directory(namelist[i]->d_name))
+               if (is_component_directory(d_name))
                        continue;
 
-               err = load_master_config(namelist[i]->d_name, &cfg);
+
+               configuration_filename2(filename, sizeof(filename), 2,
+                                       d_name, d_name, ".conf");
+               err = uc_mgr_config_load(2, filename, &cfg);
                if (err < 0)
                        goto __err;
+               err = snd_config_search(cfg, "Syntax", &c);
+               if (err < 0) {
+                       uc_error("Syntax field not found in %s", d_name);
+                       continue;
+               }
+               err = snd_config_get_integer(c, &l);
+               if (err < 0) {
+                       uc_error("Syntax field is invalid in %s", d_name);
+                       goto __err;
+               }
+               if (l < 2 || l > SYNTAX_VERSION_MAX) {
+                       uc_error("Incompatible syntax %d in %s", l, d_name);
+                       goto __err;
+               }
                err = snd_config_search(cfg, "Comment", &c);
                if (err >= 0) {
                        err = parse_string(c, (char **)&list[j+1]);
@@ -1585,7 +1670,7 @@ int uc_mgr_scan_master_configs(const char **_list[])
                        }
                }
                snd_config_delete(cfg);
-               list[j] = strdup(namelist[i]->d_name);
+               list[j] = strdup(d_name);
                if (list[j] == NULL) {
                        err = -ENOMEM;
                        goto __err;
index 8e7ac7e..ee15d38 100644 (file)
@@ -40,6 +40,8 @@
 #include <pthread.h>
 #include "use-case.h"
 
+#define SYNTAX_VERSION_MAX     2
+
 #define MAX_FILE               256
 #define MAX_CARD_LONG_NAME     80
 
@@ -193,6 +195,7 @@ struct snd_use_case_mgr {
        char card_long_name[MAX_CARD_LONG_NAME];
        char conf_file_name[MAX_CARD_LONG_NAME];
        char *comment;
+       int conf_format;
 
        /* use case verb, devices and modifier configs parsed from files */
        struct list_head verb_list;
@@ -235,7 +238,7 @@ struct snd_use_case_mgr {
 void uc_mgr_error(const char *fmt, ...);
 void uc_mgr_stdout(const char *fmt, ...);
 
-int uc_mgr_config_load(const char *file, snd_config_t **cfg);
+int uc_mgr_config_load(int format, const char *file, snd_config_t **cfg);
 int uc_mgr_import_master_config(snd_use_case_mgr_t *uc_mgr);
 int uc_mgr_scan_master_configs(const char **_list[]);
 
@@ -246,3 +249,6 @@ void uc_mgr_free(snd_use_case_mgr_t *uc_mgr);
 
 /** The name of the environment variable containing the UCM directory */
 #define ALSA_CONFIG_UCM_VAR "ALSA_CONFIG_UCM"
+
+/** The name of the environment variable containing the UCM directory (new syntax) */
+#define ALSA_CONFIG_UCM2_VAR "ALSA_CONFIG_UCM2"
index d6ed50a..84ad5ac 100644 (file)
@@ -49,7 +49,7 @@ void uc_mgr_stdout(const char *fmt,...)
        va_end(va);
 }
 
-int uc_mgr_config_load(const char *file, snd_config_t **cfg)
+int uc_mgr_config_load(int format, const char *file, snd_config_t **cfg)
 {
        FILE *fp;
        snd_input_t *in;
@@ -71,9 +71,15 @@ int uc_mgr_config_load(const char *file, snd_config_t **cfg)
        if (err < 0)
                goto __err1;
 
-       path = getenv(ALSA_CONFIG_UCM_VAR);
-       if (!path || path[0] == '\0')
-               path = ALSA_CONFIG_DIR "/ucm";
+       if (format >= 2) {
+               path = getenv(ALSA_CONFIG_UCM2_VAR);
+               if (!path || path[0] == '\0')
+                       path = ALSA_CONFIG_DIR "/ucm2";
+       } else {
+               path = getenv(ALSA_CONFIG_UCM_VAR);
+               if (!path || path[0] == '\0')
+                       path = ALSA_CONFIG_DIR "/ucm";
+       }
 
        default_paths[0] = path;
        default_paths[1] = NULL;