1/* 2 * This library is free software; you can redistribute it and/or 3 * modify it under the terms of the GNU Lesser General Public 4 * License as published by the Free Software Foundation; either 5 * version 2 of the License, or (at your option) any later version. 6 * 7 * This library is distributed in the hope that it will be useful, 8 * but WITHOUT ANY WARRANTY; without even the implied warranty of 9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 10 * Lesser General Public License for more details. 11 * 12 * You should have received a copy of the GNU Lesser General Public 13 * License along with this library; if not, write to the Free Software 14 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 15 * 16 * Support for the verb/device/modifier core logic and API, 17 * command line tool and file parser was kindly sponsored by 18 * Texas Instruments Inc. 19 * Support for multiple active modifiers and devices, 20 * transition sequences, multiple client access and user defined use 21 * cases was kindly sponsored by Wolfson Microelectronics PLC. 22 * 23 * Copyright (C) 2019 Red Hat Inc. 24 * Authors: Jaroslav Kysela <perex@perex.cz> 25 */ 26 27#include "ucm_local.h" 28#include <ctype.h> 29#include <regex.h> 30 31static int get_string(snd_config_t *compound, const char *key, const char **str) 32{ 33 snd_config_t *node; 34 int err; 35 36 err = snd_config_search(compound, key, &node); 37 if (err < 0) 38 return err; 39 return snd_config_get_string(node, str); 40} 41 42static char *extract_substring(const char *data, regmatch_t *match) 43{ 44 char *s; 45 size_t len; 46 47 len = match->rm_eo - match->rm_so; 48 s = malloc(len + 1); 49 if (s == NULL) 50 return NULL; 51 memcpy(s, data + match->rm_so, len); 52 s[len] = '\0'; 53 return s; 54} 55 56static int set_variables(snd_use_case_mgr_t *uc_mgr, const char *data, 57 unsigned int match_size, regmatch_t *match, 58 const char *name) 59{ 60 size_t name2_len = strlen(name) + 16; 61 char *name2 = alloca(name2_len); 62 char *s; 63 unsigned int i; 64 int err; 65 66 if (match[0].rm_so < 0 || match[0].rm_eo < 0) 67 return 0; 68 s = extract_substring(data, &match[0]); 69 if (s == NULL) 70 return -ENOMEM; 71 err = uc_mgr_set_variable(uc_mgr, name, s); 72 free(s); 73 if (err < 0) 74 return err; 75 for (i = 1; i < match_size; i++) { 76 if (match[0].rm_so < 0 || match[0].rm_eo < 0) 77 return 0; 78 s = extract_substring(data, &match[i]); 79 if (s == NULL) 80 return -ENOMEM; 81 snprintf(name2, name2_len, "%s%u", name, i); 82 err = uc_mgr_set_variable(uc_mgr, name2, s); 83 free(s); 84 if (err < 0) 85 return err; 86 } 87 return 0; 88} 89 90int uc_mgr_define_regex(snd_use_case_mgr_t *uc_mgr, const char *name, 91 snd_config_t *eval) 92{ 93 const char *string, *regex_string, *flags_string; 94 char *s; 95 regex_t re; 96 int options = 0; 97 regmatch_t match[20]; 98 int err; 99 100 if (uc_mgr->conf_format < 3) { 101 uc_error("define regex is supported in v3+ syntax"); 102 return -EINVAL; 103 } 104 105 if (snd_config_get_type(eval) != SND_CONFIG_TYPE_COMPOUND) { 106 uc_error("compound type expected for DefineRegex"); 107 return -EINVAL; 108 } 109 110 err = get_string(eval, "String", &string); 111 if (err < 0) { 112 uc_error("DefineRegex error (String)"); 113 return -EINVAL; 114 } 115 116 err = get_string(eval, "Regex", ®ex_string); 117 if (err < 0) { 118 uc_error("DefineRegex error (Regex string)"); 119 return -EINVAL; 120 } 121 122 err = get_string(eval, "Flags", &flags_string); 123 if (err == -ENOENT) { 124 options = REG_EXTENDED; 125 } else if (err < 0) { 126 uc_error("DefineRegex error (Flags string)"); 127 return -EINVAL; 128 } else { 129 while (*flags_string) { 130 switch (tolower(*flags_string)) { 131 case 'e': 132 options |= REG_EXTENDED; 133 break; 134 case 'i': 135 options |= REG_ICASE; 136 break; 137 case 's': 138 options |= REG_NOSUB; 139 break; 140 case 'n': 141 options |= REG_NEWLINE; 142 break; 143 default: 144 uc_error("DefineRegex error (unknown flag '%c')", *flags_string); 145 return -EINVAL; 146 } 147 flags_string++; 148 } 149 } 150 151 err = uc_mgr_get_substituted_value(uc_mgr, &s, regex_string); 152 if (err < 0) 153 return err; 154 err = regcomp(&re, s, options); 155 free(s); 156 if (err) { 157 uc_error("Regex '%s' compilation failed (code %d)", err); 158 return -EINVAL; 159 } 160 161 err = uc_mgr_get_substituted_value(uc_mgr, &s, string); 162 if (err < 0) { 163 regfree(&re); 164 return err; 165 } 166 err = regexec(&re, s, ARRAY_SIZE(match), match, 0); 167 if (err < 0) 168 err = -errno; 169 else if (err == REG_NOMATCH) 170 err = 0; 171 else 172 err = set_variables(uc_mgr, s, ARRAY_SIZE(match), match, name); 173 free(s); 174 regfree(&re); 175 return err; 176} 177