jcs's openbsd hax
openbsd
at jcs 476 lines 13 kB view raw
1/* 2 * tuple.c 3 * management of key->value tuples 4 * 5 * Copyright (c) 2011, 2012 pkgconf authors (see AUTHORS). 6 * 7 * Permission to use, copy, modify, and/or distribute this software for any 8 * purpose with or without fee is hereby granted, provided that the above 9 * copyright notice and this permission notice appear in all copies. 10 * 11 * This software is provided 'as is' and without any warranty, express or 12 * implied. In no event shall the authors be liable for any damages arising 13 * from the use of this software. 14 */ 15 16#include <libpkgconf/stdinc.h> 17#include <libpkgconf/libpkgconf.h> 18 19/* 20 * !doc 21 * 22 * libpkgconf `tuple` module 23 * ========================= 24 * 25 * The `tuple` module provides key-value mappings backed by a linked list. The key-value 26 * mapping is mainly used for variable substitution when parsing .pc files. 27 * 28 * There are two sets of mappings: a ``pkgconf_pkg_t`` specific mapping, and a `global` mapping. 29 * The `tuple` module provides convenience wrappers for managing the `global` mapping, which is 30 * attached to a given client object. 31 */ 32 33/* 34 * !doc 35 * 36 * .. c:function:: void pkgconf_tuple_add_global(pkgconf_client_t *client, const char *key, const char *value) 37 * 38 * Defines a global variable, replacing the previous declaration if one was set. 39 * 40 * :param pkgconf_client_t* client: The pkgconf client object to modify. 41 * :param char* key: The key for the mapping (variable name). 42 * :param char* value: The value for the mapped entry. 43 * :return: nothing 44 */ 45void 46pkgconf_tuple_add_global(pkgconf_client_t *client, const char *key, const char *value) 47{ 48 pkgconf_tuple_add(client, &client->global_vars, key, value, false, 0); 49} 50 51static pkgconf_tuple_t * 52lookup_global_tuple(const pkgconf_client_t *client, const char *key) 53{ 54 pkgconf_node_t *node; 55 56 PKGCONF_FOREACH_LIST_ENTRY(client->global_vars.head, node) 57 { 58 pkgconf_tuple_t *tuple = node->data; 59 60 if (!strcmp(tuple->key, key)) 61 return tuple; 62 } 63 64 return NULL; 65} 66 67/* 68 * !doc 69 * 70 * .. c:function:: void pkgconf_tuple_find_global(const pkgconf_client_t *client, const char *key) 71 * 72 * Looks up a global variable. 73 * 74 * :param pkgconf_client_t* client: The pkgconf client object to access. 75 * :param char* key: The key or variable name to look up. 76 * :return: the contents of the variable or ``NULL`` 77 * :rtype: char * 78 */ 79char * 80pkgconf_tuple_find_global(const pkgconf_client_t *client, const char *key) 81{ 82 pkgconf_tuple_t *tuple; 83 84 tuple = lookup_global_tuple(client, key); 85 if (tuple == NULL) 86 return NULL; 87 88 return tuple->value; 89} 90 91/* 92 * !doc 93 * 94 * .. c:function:: void pkgconf_tuple_free_global(pkgconf_client_t *client) 95 * 96 * Delete all global variables associated with a pkgconf client object. 97 * 98 * :param pkgconf_client_t* client: The pkgconf client object to modify. 99 * :return: nothing 100 */ 101void 102pkgconf_tuple_free_global(pkgconf_client_t *client) 103{ 104 pkgconf_tuple_free(&client->global_vars); 105} 106 107/* 108 * !doc 109 * 110 * .. c:function:: void pkgconf_tuple_define_global(pkgconf_client_t *client, const char *kv) 111 * 112 * Parse and define a global variable. 113 * 114 * :param pkgconf_client_t* client: The pkgconf client object to modify. 115 * :param char* kv: The variable in the form of ``key=value``. 116 * :return: nothing 117 */ 118void 119pkgconf_tuple_define_global(pkgconf_client_t *client, const char *kv) 120{ 121 char *workbuf = strdup(kv); 122 char *value; 123 pkgconf_tuple_t *tuple; 124 125 value = strchr(workbuf, '='); 126 if (value == NULL) 127 goto out; 128 129 *value++ = '\0'; 130 131 tuple = pkgconf_tuple_add(client, &client->global_vars, workbuf, value, false, 0); 132 if (tuple != NULL) 133 tuple->flags = PKGCONF_PKG_TUPLEF_OVERRIDE; 134 135out: 136 free(workbuf); 137} 138 139static void 140pkgconf_tuple_find_delete(pkgconf_list_t *list, const char *key) 141{ 142 pkgconf_node_t *node, *next; 143 144 PKGCONF_FOREACH_LIST_ENTRY_SAFE(list->head, next, node) 145 { 146 pkgconf_tuple_t *tuple = node->data; 147 148 if (!strcmp(tuple->key, key)) 149 { 150 pkgconf_tuple_free_entry(tuple, list); 151 return; 152 } 153 } 154} 155 156static char * 157dequote(const char *value) 158{ 159 char *buf = calloc(1, (strlen(value) + 1) * 2); 160 char *bptr = buf; 161 const char *i; 162 char quote = 0; 163 164 if (*value == '\'' || *value == '"') 165 quote = *value; 166 167 for (i = value; *i != '\0'; i++) 168 { 169 if (*i == '\\' && quote && *(i + 1) == quote) 170 { 171 i++; 172 *bptr++ = *i; 173 } 174 else if (*i != quote) 175 *bptr++ = *i; 176 } 177 178 return buf; 179} 180 181static const char * 182find_sysroot(const pkgconf_client_t *client, pkgconf_list_t *vars) 183{ 184 const char *sysroot_dir; 185 186 sysroot_dir = pkgconf_tuple_find(client, vars, "pc_sysrootdir"); 187 if (sysroot_dir == NULL) 188 sysroot_dir = client->sysroot_dir; 189 190 return sysroot_dir; 191} 192 193static bool 194should_rewrite_sysroot(const pkgconf_client_t *client, pkgconf_list_t *vars, const char *buf, unsigned int flags) 195{ 196 const char *sysroot_dir; 197 198 if (flags & PKGCONF_PKG_PROPF_UNINSTALLED && !(client->flags & PKGCONF_PKG_PKGF_FDO_SYSROOT_RULES)) 199 return false; 200 201 sysroot_dir = find_sysroot(client, vars); 202 if (sysroot_dir == NULL) 203 return false; 204 205 if (*buf != '/') 206 return false; 207 208 if (!strcmp(sysroot_dir, "/")) 209 return false; 210 211 if (strlen(buf) <= strlen(sysroot_dir)) 212 return false; 213 214 if (strstr(buf + strlen(sysroot_dir), sysroot_dir) == NULL) 215 return false; 216 217 return true; 218} 219 220/* 221 * !doc 222 * 223 * .. 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) 224 * 225 * Optionally parse and then define a variable. 226 * 227 * :param pkgconf_client_t* client: The pkgconf client object to access. 228 * :param pkgconf_list_t* list: The variable list to add the new variable to. 229 * :param char* key: The name of the variable being added. 230 * :param char* value: The value of the variable being added. 231 * :param bool parse: Whether or not to parse the value for variable substitution. 232 * :return: a variable object 233 * :rtype: pkgconf_tuple_t * 234 */ 235pkgconf_tuple_t * 236pkgconf_tuple_add(const pkgconf_client_t *client, pkgconf_list_t *list, const char *key, const char *value, bool parse, unsigned int flags) 237{ 238 char *dequote_value; 239 pkgconf_tuple_t *tuple = calloc(1, sizeof(pkgconf_tuple_t)); 240 241 pkgconf_tuple_find_delete(list, key); 242 243 dequote_value = dequote(value); 244 245 tuple->key = strdup(key); 246 if (parse) 247 tuple->value = pkgconf_tuple_parse(client, list, dequote_value, flags); 248 else 249 tuple->value = strdup(dequote_value); 250 251 PKGCONF_TRACE(client, "adding tuple to @%p: %s => %s (parsed? %d)", list, key, tuple->value, parse); 252 253 pkgconf_node_insert(&tuple->iter, tuple, list); 254 255 free(dequote_value); 256 257 return tuple; 258} 259 260/* 261 * !doc 262 * 263 * .. c:function:: char *pkgconf_tuple_find(const pkgconf_client_t *client, pkgconf_list_t *list, const char *key) 264 * 265 * Look up a variable in a variable list. 266 * 267 * :param pkgconf_client_t* client: The pkgconf client object to access. 268 * :param pkgconf_list_t* list: The variable list to search. 269 * :param char* key: The variable name to search for. 270 * :return: the value of the variable or ``NULL`` 271 * :rtype: char * 272 */ 273char * 274pkgconf_tuple_find(const pkgconf_client_t *client, pkgconf_list_t *list, const char *key) 275{ 276 pkgconf_node_t *node; 277 pkgconf_tuple_t *global_tuple; 278 279 global_tuple = lookup_global_tuple(client, key); 280 if (global_tuple != NULL && global_tuple->flags & PKGCONF_PKG_TUPLEF_OVERRIDE) 281 return global_tuple->value; 282 283 PKGCONF_FOREACH_LIST_ENTRY(list->head, node) 284 { 285 pkgconf_tuple_t *tuple = node->data; 286 287 if (!strcmp(tuple->key, key)) 288 return tuple->value; 289 } 290 291 if (global_tuple != NULL) 292 return global_tuple->value; 293 294 return NULL; 295} 296 297/* 298 * !doc 299 * 300 * .. c:function:: char *pkgconf_tuple_parse(const pkgconf_client_t *client, pkgconf_list_t *vars, const char *value, unsigned int flags) 301 * 302 * Parse an expression for variable substitution. 303 * 304 * :param pkgconf_client_t* client: The pkgconf client object to access. 305 * :param pkgconf_list_t* list: The variable list to search for variables (along side the global variable list). 306 * :param char* value: The ``key=value`` string to parse. 307 * :param uint flags: Any flags to consider while parsing. 308 * :return: the variable data with any variables substituted 309 * :rtype: char * 310 */ 311char * 312pkgconf_tuple_parse(const pkgconf_client_t *client, pkgconf_list_t *vars, const char *value, unsigned int flags) 313{ 314 char buf[PKGCONF_BUFSIZE]; 315 const char *ptr; 316 char *bptr = buf; 317 318 if (!(client->flags & PKGCONF_PKG_PKGF_FDO_SYSROOT_RULES) && 319 (!(flags & PKGCONF_PKG_PROPF_UNINSTALLED) || (client->flags & PKGCONF_PKG_PKGF_PKGCONF1_SYSROOT_RULES))) 320 { 321 if (*value == '/' && client->sysroot_dir != NULL && strncmp(value, client->sysroot_dir, strlen(client->sysroot_dir))) 322 bptr += pkgconf_strlcpy(buf, client->sysroot_dir, sizeof buf); 323 } 324 325 for (ptr = value; *ptr != '\0' && bptr - buf < PKGCONF_BUFSIZE; ptr++) 326 { 327 if (*ptr != '$' || (*ptr == '$' && *(ptr + 1) != '{')) 328 *bptr++ = *ptr; 329 else if (*(ptr + 1) == '{') 330 { 331 char varname[PKGCONF_ITEM_SIZE]; 332 char *vend = varname + PKGCONF_ITEM_SIZE - 1; 333 char *vptr = varname; 334 const char *pptr; 335 char *kv, *parsekv; 336 337 *vptr = '\0'; 338 339 for (pptr = ptr + 2; *pptr != '\0'; pptr++) 340 { 341 if (*pptr != '}') 342 { 343 if (vptr < vend) 344 *vptr++ = *pptr; 345 else 346 { 347 *vptr = '\0'; 348 break; 349 } 350 } 351 else 352 { 353 *vptr = '\0'; 354 break; 355 } 356 } 357 358 PKGCONF_TRACE(client, "lookup tuple %s", varname); 359 360 size_t remain = PKGCONF_BUFSIZE - (bptr - buf); 361 ptr += (pptr - ptr); 362 kv = pkgconf_tuple_find_global(client, varname); 363 if (kv != NULL) 364 { 365 size_t nlen = pkgconf_strlcpy(bptr, kv, remain); 366 if (nlen > remain) 367 { 368 pkgconf_warn(client, "warning: truncating very long variable to 64KB\n"); 369 370 bptr = buf + (PKGCONF_BUFSIZE - 1); 371 break; 372 } 373 374 bptr += nlen; 375 } 376 else 377 { 378 kv = pkgconf_tuple_find(client, vars, varname); 379 380 if (kv != NULL) 381 { 382 size_t nlen; 383 384 parsekv = pkgconf_tuple_parse(client, vars, kv, flags); 385 nlen = pkgconf_strlcpy(bptr, parsekv, remain); 386 free(parsekv); 387 388 if (nlen > remain) 389 { 390 pkgconf_warn(client, "warning: truncating very long variable to 64KB\n"); 391 392 bptr = buf + (PKGCONF_BUFSIZE - 1); 393 break; 394 } 395 396 bptr += nlen; 397 } 398 } 399 } 400 } 401 402 *bptr = '\0'; 403 404 /* 405 * Sigh. Somebody actually attempted to use freedesktop.org pkg-config's broken sysroot support, 406 * which was written by somebody who did not understand how sysroots are supposed to work. This 407 * results in an incorrect path being built as the sysroot will be prepended twice, once explicitly, 408 * and once by variable expansion (the pkgconf approach). We could simply make ${pc_sysrootdir} blank, 409 * but sometimes it is necessary to know the explicit sysroot path for other reasons, so we can't really 410 * do that. 411 * 412 * As a result, we check to see if ${pc_sysrootdir} is prepended as a duplicate, and if so, remove the 413 * prepend. This allows us to handle both our approach and the broken freedesktop.org implementation's 414 * approach. Because a path can be shorter than ${pc_sysrootdir}, we do some checks first to ensure it's 415 * safe to skip ahead in the string to scan for our sysroot dir. 416 * 417 * Finally, we call pkgconf_path_relocate() to clean the path of spurious elements. 418 * 419 * New in 1.9: Only attempt to rewrite the sysroot if we are not processing an uninstalled package. 420 */ 421 if (should_rewrite_sysroot(client, vars, buf, flags)) 422 { 423 char cleanpath[PKGCONF_ITEM_SIZE]; 424 const char *sysroot_dir = find_sysroot(client, vars); 425 426 pkgconf_strlcpy(cleanpath, buf + strlen(sysroot_dir), sizeof cleanpath); 427 pkgconf_path_relocate(cleanpath, sizeof cleanpath); 428 429 return strdup(cleanpath); 430 } 431 432 return strdup(buf); 433} 434 435/* 436 * !doc 437 * 438 * .. c:function:: void pkgconf_tuple_free_entry(pkgconf_tuple_t *tuple, pkgconf_list_t *list) 439 * 440 * Deletes a variable object, removing it from any variable lists and releasing any memory associated 441 * with it. 442 * 443 * :param pkgconf_tuple_t* tuple: The variable object to release. 444 * :param pkgconf_list_t* list: The variable list the variable object is attached to. 445 * :return: nothing 446 */ 447void 448pkgconf_tuple_free_entry(pkgconf_tuple_t *tuple, pkgconf_list_t *list) 449{ 450 pkgconf_node_delete(&tuple->iter, list); 451 452 free(tuple->key); 453 free(tuple->value); 454 free(tuple); 455} 456 457/* 458 * !doc 459 * 460 * .. c:function:: void pkgconf_tuple_free(pkgconf_list_t *list) 461 * 462 * Deletes a variable list and any variables attached to it. 463 * 464 * :param pkgconf_list_t* list: The variable list to delete. 465 * :return: nothing 466 */ 467void 468pkgconf_tuple_free(pkgconf_list_t *list) 469{ 470 pkgconf_node_t *node, *next; 471 472 PKGCONF_FOREACH_LIST_ENTRY_SAFE(list->head, next, node) 473 pkgconf_tuple_free_entry(node->data, list); 474 475 pkgconf_list_zero(list); 476}