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) 2020 Red Hat Inc. 24 * Authors: Jaroslav Kysela <perex@perex.cz> 25 */ 26 27#include "ucm_local.h" 28 29static int get_string(snd_config_t *compound, const char *key, const char **str) 30{ 31 snd_config_t *node; 32 int err; 33 34 err = snd_config_search(compound, key, &node); 35 if (err < 0) 36 return err; 37 return snd_config_get_string(node, str); 38} 39 40static int include_eval_one(snd_use_case_mgr_t *uc_mgr, 41 snd_config_t *inc, 42 snd_config_t **result, 43 snd_config_t **before, 44 snd_config_t **after) 45{ 46 const char *file; 47 char *s; 48 int err; 49 50 *result = NULL; 51 52 if (snd_config_get_type(inc) != SND_CONFIG_TYPE_COMPOUND) { 53 uc_error("compound type expected for Include.1"); 54 return -EINVAL; 55 } 56 57 err = get_string(inc, "File", &file); 58 if (err < 0) { 59 uc_error("file expected (Include)"); 60 return -EINVAL; 61 } 62 63 err = snd_config_search(inc, "Before", before); 64 if (err < 0 && err != -ENOENT) { 65 uc_error("before block identifier error"); 66 return -EINVAL; 67 } 68 69 err = snd_config_search(inc, "After", after); 70 if (err < 0 && err != -ENOENT) { 71 uc_error("before block identifier error"); 72 return -EINVAL; 73 } 74 75 err = uc_mgr_get_substituted_value(uc_mgr, &s, file); 76 if (err < 0) 77 return err; 78 err = uc_mgr_config_load_file(uc_mgr, s, result); 79 free(s); 80 return err; 81} 82 83#if 0 84static void config_dump(snd_config_t *cfg) 85{ 86 snd_output_t *out; 87 snd_output_stdio_attach(&out, stderr, 0); 88 snd_output_printf(out, "-----\n"); 89 snd_config_save(cfg, out); 90 snd_output_close(out); 91} 92#endif 93 94static int find_position_node(snd_use_case_mgr_t *uc_mgr, 95 snd_config_t **res, snd_config_t *dst, 96 const char *id, snd_config_t *pos) 97{ 98 const char *s; 99 char *s1; 100 int err; 101 102 err = get_string(pos, id, &s); 103 if (err < 0 && err != -ENOENT) 104 return err; 105 if (err == 0) { 106 err = uc_mgr_get_substituted_value(uc_mgr, &s1, s); 107 if (err < 0) 108 return err; 109 err = snd_config_search(dst, s1, res); 110 free(s1); 111 if (err < 0 && err != -ENOENT) 112 return err; 113 } 114 return 0; 115} 116 117static int merge_it(snd_config_t *dst, snd_config_t *n, snd_config_t **_dn) 118{ 119 snd_config_t *dn; 120 const char *id; 121 int err; 122 123 err = snd_config_get_id(n, &id); 124 if (err < 0) 125 return err; 126 err = snd_config_search(dst, id, &dn); 127 if (err < 0) 128 return err; 129 err = snd_config_merge(dn, n, 0); /* merge / append mode */ 130 if (err < 0) 131 snd_config_delete(n); 132 else 133 *_dn = dn; 134 return err; 135} 136 137static int compound_merge(snd_use_case_mgr_t *uc_mgr, const char *id, 138 snd_config_t *dst, snd_config_t *src, 139 snd_config_t *before, snd_config_t *after) 140{ 141 snd_config_iterator_t i, next; 142 snd_config_t *n, *_before = NULL, *_after = NULL; 143 char tmpid[32]; 144 int err, array, idx; 145 146 if (snd_config_get_type(src) != SND_CONFIG_TYPE_COMPOUND) { 147 uc_error("compound type expected for the merged block"); 148 return -EINVAL; 149 } 150 151 if (before) { 152 err = find_position_node(uc_mgr, &_before, dst, id, before); 153 if (err < 0) 154 return err; 155 } 156 if (after) { 157 err = find_position_node(uc_mgr, &_after, dst, id, after); 158 if (err < 0) 159 return err; 160 } 161 162 /* direct merge? */ 163 if (!_before && !_after) 164 return snd_config_merge(dst, src, 0); /* merge / append mode */ 165 166 if (_before && _after) { 167 uc_error("defined both before and after identifiers in the If or Include block"); 168 return -EINVAL; 169 } 170 171 array = snd_config_is_array(dst); 172 if (array < 0) { 173 uc_error("destination configuration node is not a compound"); 174 return array; 175 } 176 if (array && snd_config_is_array(src) <= 0) { 177 uc_error("source configuration node is not an array"); 178 return -EINVAL; 179 } 180 181 idx = 0; 182 183 /* for array, use a temporary non-clashing identifier */ 184 if (array > 0) { 185 snd_config_for_each(i, next, dst) { 186 n = snd_config_iterator_entry(i); 187 snprintf(tmpid, sizeof(tmpid), "_tmp_%d", idx++); 188 err = snd_config_set_id(n, tmpid); 189 if (err < 0) 190 return err; 191 } 192 } 193 194 snd_config_for_each(i, next, src) { 195 n = snd_config_iterator_entry(i); 196 err = snd_config_remove(n); 197 if (err < 0) 198 return err; 199 /* for array, use a temporary non-clashing identifier */ 200 if (array > 0) { 201 snprintf(tmpid, sizeof(tmpid), "_tmp_%d", idx++); 202 err = snd_config_set_id(n, tmpid); 203 if (err < 0) 204 return err; 205 } 206 if (_before) { 207 err = snd_config_add_before(_before, n); 208 if (err == -EEXIST) 209 err = merge_it(dst, n, &n); 210 if (err < 0) 211 return err; 212 _before = NULL; 213 _after = n; 214 } else if (_after) { 215 err = snd_config_add_after(_after, n); 216 if (err == -EEXIST) 217 err = merge_it(dst, n, &n); 218 if (err < 0) 219 return err; 220 _after = n; 221 } 222 } 223 224 /* set new indexes for the final array */ 225 if (array > 0) { 226 idx = 0; 227 snd_config_for_each(i, next, dst) { 228 n = snd_config_iterator_entry(i); 229 snprintf(tmpid, sizeof(tmpid), "%d", idx++); 230 err = snd_config_set_id(n, tmpid); 231 if (err < 0) 232 return err; 233 } 234 } 235 236 snd_config_delete(src); 237 return 0; 238} 239 240int uc_mgr_config_tree_merge(snd_use_case_mgr_t *uc_mgr, 241 snd_config_t *parent, snd_config_t *new_ctx, 242 snd_config_t *before, snd_config_t *after) 243{ 244 snd_config_iterator_t i, next; 245 snd_config_t *n, *parent2; 246 const char *id; 247 int err; 248 249 err = uc_mgr_substitute_tree(uc_mgr, new_ctx); 250 if (err < 0) 251 return err; 252 253 snd_config_for_each(i, next, new_ctx) { 254 n = snd_config_iterator_entry(i); 255 err = snd_config_remove(n); 256 if (err < 0) 257 return err; 258 err = snd_config_get_id(n, &id); 259 if (err < 0) { 260__add: 261 err = snd_config_add(parent, n); 262 if (err < 0) 263 return err; 264 } else { 265 err = snd_config_search(parent, id, &parent2); 266 if (err == -ENOENT) 267 goto __add; 268 err = compound_merge(uc_mgr, id, parent2, n, before, after); 269 if (err < 0) { 270 snd_config_delete(n); 271 return err; 272 } 273 } 274 } 275 return 0; 276} 277 278/* 279 * put back the included configuration to the parent 280 */ 281int uc_mgr_evaluate_include(snd_use_case_mgr_t *uc_mgr, 282 snd_config_t *parent, 283 snd_config_t *inc) 284{ 285 snd_config_iterator_t i, next; 286 snd_config_t *a, *n, *before, *after; 287 int err; 288 289 if (uc_mgr->conf_format < 3) { 290 uc_error("in-place include is supported in v3+ syntax"); 291 return -EINVAL; 292 } 293 294 if (snd_config_get_type(inc) != SND_CONFIG_TYPE_COMPOUND) { 295 uc_error("compound type expected for Include"); 296 return -EINVAL; 297 } 298 299 snd_config_for_each(i, next, inc) { 300 n = snd_config_iterator_entry(i); 301 before = after = NULL; 302 err = include_eval_one(uc_mgr, n, &a, &before, &after); 303 if (err < 0) 304 return err; 305 if (a == NULL) 306 continue; 307 err = uc_mgr_evaluate_inplace(uc_mgr, a); 308 if (err < 0) { 309 snd_config_delete(a); 310 return err; 311 } 312 err = uc_mgr_config_tree_merge(uc_mgr, parent, a, before, after); 313 snd_config_delete(a); 314 if (err < 0) 315 return err; 316 } 317 return 0; 318} 319