1 /* 2 * tuple.c 3 * management of key->value tuples 4 * 5 * SPDX-License-Identifier: pkgconf 6 * 7 * Copyright (c) 2011, 2012 pkgconf authors (see AUTHORS). 8 * 9 * Permission to use, copy, modify, and/or distribute this software for any 10 * purpose with or without fee is hereby granted, provided that the above 11 * copyright notice and this permission notice appear in all copies. 12 * 13 * This software is provided 'as is' and without any warranty, express or 14 * implied. In no event shall the authors be liable for any damages arising 15 * from the use of this software. 16 */ 17 18 #include <libpkgconf/stdinc.h> 19 #include <libpkgconf/libpkgconf.h> 20 21 /* 22 * !doc 23 * 24 * libpkgconf `tuple` module 25 * ========================= 26 * 27 * The `tuple` module provides key-value mappings backed by a linked list. The key-value 28 * mapping is mainly used for variable substitution when parsing .pc files. 29 * 30 * There are two sets of mappings: a ``pkgconf_pkg_t`` specific mapping, and a `global` mapping. 31 * The `tuple` module provides convenience wrappers for managing the `global` mapping, which is 32 * attached to a given client object. 33 */ 34 35 /* 36 * !doc 37 * 38 * .. c:function:: void pkgconf_tuple_add_global(pkgconf_client_t *client, const char *key, const char *value) 39 * 40 * Defines a global variable, replacing the previous declaration if one was set. 41 * 42 * :param pkgconf_client_t* client: The pkgconf client object to modify. 43 * :param char* key: The key for the mapping (variable name). 44 * :param char* value: The value for the mapped entry. 45 * :return: nothing 46 */ 47 void 48 pkgconf_tuple_add_global(pkgconf_client_t *client, const char *key, const char *value) 49 { 50 pkgconf_tuple_add(client, &client->global_vars, key, value, false, 0); 51 } 52 53 /* 54 * !doc 55 * 56 * .. c:function:: void pkgconf_tuple_find_global(const pkgconf_client_t *client, const char *key) 57 * 58 * Looks up a global variable. 59 * 60 * :param pkgconf_client_t* client: The pkgconf client object to access. 61 * :param char* key: The key or variable name to look up. 62 * :return: the contents of the variable or ``NULL`` 63 * :rtype: char * 64 */ 65 const char * 66 pkgconf_tuple_find_global(pkgconf_client_t *client, const char *key) 67 { 68 pkgconf_variable_t *v; 69 bool saw_sysroot = false; 70 71 if (client == NULL || key == NULL) 72 return NULL; 73 74 v = pkgconf_variable_find(&client->global_vars, key); 75 76 pkgconf_buffer_reset(&client->_scratch_buffer); 77 (void) pkgconf_variable_eval(client, &client->global_vars, v, &client->_scratch_buffer, &saw_sysroot); 78 79 return pkgconf_buffer_str_or_empty(&client->_scratch_buffer); 80 } 81 82 /* 83 * !doc 84 * 85 * .. c:function:: void pkgconf_tuple_free_global(pkgconf_client_t *client) 86 * 87 * Delete all global variables associated with a pkgconf client object. 88 * 89 * :param pkgconf_client_t* client: The pkgconf client object to modify. 90 * :return: nothing 91 */ 92 void 93 pkgconf_tuple_free_global(pkgconf_client_t *client) 94 { 95 pkgconf_tuple_free(&client->global_vars); 96 } 97 98 /* 99 * !doc 100 * 101 * .. c:function:: void pkgconf_tuple_define_global(pkgconf_client_t *client, const char *kv) 102 * 103 * Parse and define a global variable. 104 * 105 * :param pkgconf_client_t* client: The pkgconf client object to modify. 106 * :param char* kv: The variable in the form of ``key=value``. 107 * :return: nothing 108 */ 109 void 110 pkgconf_tuple_define_global(pkgconf_client_t *client, const char *kv) 111 { 112 char *workbuf = strdup(kv); 113 char *value; 114 pkgconf_tuple_t *tuple; 115 116 if (workbuf == NULL) 117 goto out; 118 119 value = strchr(workbuf, '='); 120 if (value == NULL) 121 goto out; 122 123 *value++ = '\0'; 124 125 tuple = pkgconf_tuple_add(client, &client->global_vars, workbuf, value, false, 0); 126 if (tuple != NULL) 127 tuple->flags = PKGCONF_PKG_TUPLEF_OVERRIDE; 128 129 out: 130 free(workbuf); 131 } 132 133 static char * 134 dequote(const char *value) 135 { 136 char *buf = calloc(1, (strlen(value) + 1) * 2); 137 char *bptr = buf; 138 const char *i; 139 char quote = 0; 140 141 if (buf == NULL) 142 return NULL; 143 144 if (*value == '\'' || *value == '"') 145 quote = *value; 146 147 for (i = value; *i != '\0'; i++) 148 { 149 if (*i == '\\' && quote && *(i + 1) == quote) 150 { 151 i++; 152 *bptr++ = *i; 153 } 154 else if (*i != quote) 155 *bptr++ = *i; 156 } 157 158 return buf; 159 } 160 161 /* 162 * !doc 163 * 164 * .. c:function:: pkgconf_tuple_t *pkgconf_tuple_add(const pkgconf_client_t *client, pkgconf_list_t *list, const char *key, const char *value, bool parse) 165 * 166 * Wrapper around pkgconf_variable_get_or_create(list, key) and bytecode compiler. 167 * 168 * :param pkgconf_client_t* client: The pkgconf client object to access. 169 * :param pkgconf_list_t* list: The variable list to add the new variable to. 170 * :param char* key: The name of the variable being added. 171 * :param char* value: The value of the variable being added. 172 * :param bool parse: Whether or not to parse the value for variable substitution. 173 * :return: a variable object 174 * :rtype: pkgconf_tuple_t * 175 */ 176 pkgconf_tuple_t * 177 pkgconf_tuple_add(const pkgconf_client_t *client, pkgconf_list_t *list, const char *key, const char *value, bool parse, unsigned int flags) 178 { 179 char *dequote_value; 180 pkgconf_buffer_t rhs_bcbuf = PKGCONF_BUFFER_INITIALIZER; 181 182 (void) client; 183 184 if (list == NULL || key == NULL || value == NULL) 185 return NULL; 186 187 dequote_value = dequote(value); 188 if (dequote_value == NULL) 189 return NULL; 190 191 pkgconf_variable_t *v = pkgconf_variable_get_or_create(list, key); 192 if (v == NULL) 193 { 194 free(dequote_value); 195 return NULL; 196 } 197 198 v->flags = flags; 199 200 if (!parse) 201 { 202 pkgconf_buffer_reset(&v->bcbuf); 203 pkgconf_bytecode_emit_text(&v->bcbuf, dequote_value, strlen(dequote_value)); 204 pkgconf_bytecode_from_buffer(&v->bc, &v->bcbuf); 205 free(dequote_value); 206 return (pkgconf_tuple_t *) v; 207 } 208 209 pkgconf_bytecode_compile(&rhs_bcbuf, dequote_value); 210 free(dequote_value); 211 212 /* ugh, we are doing var=${var}/foo stuff */ 213 if (pkgconf_bytecode_references_var(&rhs_bcbuf, key)) 214 { 215 pkgconf_buffer_t old_bcbuf = PKGCONF_BUFFER_INITIALIZER; 216 pkgconf_buffer_t new_bcbuf = PKGCONF_BUFFER_INITIALIZER; 217 218 /* preserve the old bytecode */ 219 pkgconf_buffer_copy(&v->bcbuf, &old_bcbuf); 220 221 /* splice the selfrefs, using the old bytecode instead of ${var} */ 222 if (!pkgconf_bytecode_rewrite_selfrefs(&new_bcbuf, &rhs_bcbuf, key, &old_bcbuf)) 223 { 224 pkgconf_buffer_finalize(&old_bcbuf); 225 pkgconf_buffer_finalize(&new_bcbuf); 226 pkgconf_buffer_finalize(&rhs_bcbuf); 227 228 return NULL; 229 } 230 231 /* copy the spliced bytecode back to &rhs_bcbuf, replacing its contents */ 232 pkgconf_buffer_copy(&new_bcbuf, &rhs_bcbuf); 233 234 pkgconf_buffer_finalize(&old_bcbuf); 235 pkgconf_buffer_finalize(&new_bcbuf); 236 } 237 238 pkgconf_buffer_copy(&rhs_bcbuf, &v->bcbuf); 239 pkgconf_bytecode_from_buffer(&v->bc, &v->bcbuf); 240 pkgconf_buffer_finalize(&rhs_bcbuf); 241 242 return (pkgconf_tuple_t *) v; 243 } 244 245 /* 246 * !doc 247 * 248 * .. c:function:: char *pkgconf_tuple_find(const pkgconf_client_t *client, pkgconf_list_t *list, const char *key) 249 * 250 * Look up a variable in a variable list. 251 * 252 * :param pkgconf_client_t* client: The pkgconf client object to access. 253 * :param pkgconf_list_t* list: The variable list to search. 254 * :param char* key: The variable name to search for. 255 * :return: the value of the variable or ``NULL`` 256 * :rtype: char * 257 */ 258 const char * 259 pkgconf_tuple_find(pkgconf_client_t *client, pkgconf_list_t *list, const char *key) 260 { 261 pkgconf_variable_t *v; 262 263 if (client == NULL || list == NULL || key == NULL) 264 return NULL; 265 266 v = pkgconf_variable_find(list, key); 267 if (v == NULL) 268 v = pkgconf_variable_find(&client->global_vars, key); 269 270 pkgconf_buffer_reset(&client->_scratch_buffer); 271 272 (void) pkgconf_variable_eval(client, list, v, &client->_scratch_buffer, NULL); 273 274 return pkgconf_buffer_str_or_empty(&client->_scratch_buffer); 275 } 276 277 /* 278 * !doc 279 * 280 * .. c:function:: void pkgconf_tuple_free_entry(pkgconf_tuple_t *tuple, pkgconf_list_t *list) 281 * 282 * Deletes a variable object, removing it from any variable lists and releasing any memory associated 283 * with it. 284 * 285 * :param pkgconf_tuple_t* tuple: The variable object to release. 286 * :param pkgconf_list_t* list: The variable list the variable object is attached to. 287 * :return: nothing 288 */ 289 void 290 pkgconf_tuple_free_entry(pkgconf_tuple_t *tuple, pkgconf_list_t *list) 291 { 292 pkgconf_node_delete(&tuple->iter, list); 293 pkgconf_variable_free(tuple); 294 } 295 296 /* 297 * !doc 298 * 299 * .. c:function:: void pkgconf_tuple_free(pkgconf_list_t *list) 300 * 301 * Deletes a variable list and any variables attached to it. 302 * 303 * :param pkgconf_list_t* list: The variable list to delete. 304 * :return: nothing 305 */ 306 void 307 pkgconf_tuple_free(pkgconf_list_t *list) 308 { 309 pkgconf_node_t *node, *next; 310 311 PKGCONF_FOREACH_LIST_ENTRY_SAFE(list->head, next, node) 312 pkgconf_tuple_free_entry(node->data, list); 313 314 pkgconf_list_zero(list); 315 } 316