package compiler and linker metadata toolkit https://www.pkgconf.org/

tuple.c 10KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381
  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. #include <libpkgconf/stdinc.h>
  16. #include <libpkgconf/libpkgconf.h>
  17. /*
  18. * !doc
  19. *
  20. * libpkgconf `tuple` module
  21. * =========================
  22. *
  23. * The `tuple` module provides key-value mappings backed by a linked list. The key-value
  24. * mapping is mainly used for variable substitution when parsing .pc files.
  25. *
  26. * There are two sets of mappings: a ``pkgconf_pkg_t`` specific mapping, and a `global` mapping.
  27. * The `tuple` module provides convenience wrappers for managing the `global` mapping, which is
  28. * attached to a given client object.
  29. */
  30. /*
  31. * !doc
  32. *
  33. * .. c:function:: void pkgconf_tuple_add_global(pkgconf_client_t *client, const char *key, const char *value)
  34. *
  35. * Defines a global variable, replacing the previous declaration if one was set.
  36. *
  37. * :param pkgconf_client_t* client: The pkgconf client object to modify.
  38. * :param char* key: The key for the mapping (variable name).
  39. * :param char* value: The value for the mapped entry.
  40. * :return: nothing
  41. */
  42. void
  43. pkgconf_tuple_add_global(pkgconf_client_t *client, const char *key, const char *value)
  44. {
  45. pkgconf_tuple_add(client, &client->global_vars, key, value, false);
  46. }
  47. /*
  48. * !doc
  49. *
  50. * .. c:function:: void pkgconf_tuple_find_global(const pkgconf_client_t *client, const char *key)
  51. *
  52. * Looks up a global variable.
  53. *
  54. * :param pkgconf_client_t* client: The pkgconf client object to access.
  55. * :param char* key: The key or variable name to look up.
  56. * :return: the contents of the variable or ``NULL``
  57. * :rtype: char *
  58. */
  59. char *
  60. pkgconf_tuple_find_global(const pkgconf_client_t *client, const char *key)
  61. {
  62. pkgconf_node_t *node;
  63. PKGCONF_FOREACH_LIST_ENTRY(client->global_vars.head, node)
  64. {
  65. pkgconf_tuple_t *tuple = node->data;
  66. if (!strcmp(tuple->key, key))
  67. return tuple->value;
  68. }
  69. return NULL;
  70. }
  71. /*
  72. * !doc
  73. *
  74. * .. c:function:: void pkgconf_tuple_free_global(pkgconf_client_t *client)
  75. *
  76. * Delete all global variables associated with a pkgconf client object.
  77. *
  78. * :param pkgconf_client_t* client: The pkgconf client object to modify.
  79. * :return: nothing
  80. */
  81. void
  82. pkgconf_tuple_free_global(pkgconf_client_t *client)
  83. {
  84. pkgconf_tuple_free(&client->global_vars);
  85. }
  86. /*
  87. * !doc
  88. *
  89. * .. c:function:: void pkgconf_tuple_define_global(pkgconf_client_t *client, const char *kv)
  90. *
  91. * Parse and define a global variable.
  92. *
  93. * :param pkgconf_client_t* client: The pkgconf client object to modify.
  94. * :param char* kv: The variable in the form of ``key=value``.
  95. * :return: nothing
  96. */
  97. void
  98. pkgconf_tuple_define_global(pkgconf_client_t *client, const char *kv)
  99. {
  100. char *workbuf = strdup(kv);
  101. char *value;
  102. value = strchr(workbuf, '=');
  103. if (value == NULL)
  104. goto out;
  105. *value++ = '\0';
  106. pkgconf_tuple_add_global(client, workbuf, value);
  107. out:
  108. free(workbuf);
  109. }
  110. static void
  111. pkgconf_tuple_find_delete(pkgconf_list_t *list, const char *key)
  112. {
  113. pkgconf_node_t *node, *next;
  114. PKGCONF_FOREACH_LIST_ENTRY_SAFE(list->head, next, node)
  115. {
  116. pkgconf_tuple_t *tuple = node->data;
  117. if (!strcmp(tuple->key, key))
  118. {
  119. pkgconf_tuple_free_entry(tuple, list);
  120. return;
  121. }
  122. }
  123. }
  124. static char *
  125. dequote(const char *value)
  126. {
  127. char *buf = calloc((strlen(value) + 1) * 2, 1);
  128. char *bptr = buf;
  129. const char *i;
  130. char quote = 0;
  131. for (i = value; *i != '\0'; i++)
  132. {
  133. if (!quote && (*i == '\'' || *i == '"'))
  134. quote = *i;
  135. else if (*i != quote)
  136. *bptr++ = *i;
  137. else if (*i == '\\' && *(i + 1) == quote)
  138. {
  139. i++;
  140. *bptr++ = *i;
  141. }
  142. }
  143. return buf;
  144. }
  145. /*
  146. * !doc
  147. *
  148. * .. 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)
  149. *
  150. * Optionally parse and then define a variable.
  151. *
  152. * :param pkgconf_client_t* client: The pkgconf client object to access.
  153. * :param pkgconf_list_t* list: The variable list to add the new variable to.
  154. * :param char* key: The name of the variable being added.
  155. * :param char* value: The value of the variable being added.
  156. * :param bool parse: Whether or not to parse the value for variable substitution.
  157. * :return: a variable object
  158. * :rtype: pkgconf_tuple_t *
  159. */
  160. pkgconf_tuple_t *
  161. pkgconf_tuple_add(const pkgconf_client_t *client, pkgconf_list_t *list, const char *key, const char *value, bool parse)
  162. {
  163. char *dequote_value;
  164. pkgconf_tuple_t *tuple = calloc(sizeof(pkgconf_tuple_t), 1);
  165. pkgconf_tuple_find_delete(list, key);
  166. dequote_value = dequote(value);
  167. PKGCONF_TRACE(client, "adding tuple to @%p: %s => %s (parsed? %d)", list, key, dequote_value, parse);
  168. tuple->key = strdup(key);
  169. if (parse)
  170. tuple->value = pkgconf_tuple_parse(client, list, dequote_value);
  171. else
  172. tuple->value = strdup(dequote_value);
  173. pkgconf_node_insert(&tuple->iter, tuple, list);
  174. free(dequote_value);
  175. return tuple;
  176. }
  177. /*
  178. * !doc
  179. *
  180. * .. c:function:: char *pkgconf_tuple_find(const pkgconf_client_t *client, pkgconf_list_t *list, const char *key)
  181. *
  182. * Look up a variable in a variable list.
  183. *
  184. * :param pkgconf_client_t* client: The pkgconf client object to access.
  185. * :param pkgconf_list_t* list: The variable list to search.
  186. * :param char* key: The variable name to search for.
  187. * :return: the value of the variable or ``NULL``
  188. * :rtype: char *
  189. */
  190. char *
  191. pkgconf_tuple_find(const pkgconf_client_t *client, pkgconf_list_t *list, const char *key)
  192. {
  193. pkgconf_node_t *node;
  194. char *res;
  195. if ((res = pkgconf_tuple_find_global(client, key)) != NULL)
  196. return res;
  197. PKGCONF_FOREACH_LIST_ENTRY(list->head, node)
  198. {
  199. pkgconf_tuple_t *tuple = node->data;
  200. if (!strcmp(tuple->key, key))
  201. return tuple->value;
  202. }
  203. return NULL;
  204. }
  205. /*
  206. * !doc
  207. *
  208. * .. c:function:: char *pkgconf_tuple_parse(const pkgconf_client_t *client, pkgconf_list_t *vars, const char *value)
  209. *
  210. * Parse an expression for variable substitution.
  211. *
  212. * :param pkgconf_client_t* client: The pkgconf client object to access.
  213. * :param pkgconf_list_t* list: The variable list to search for variables (along side the global variable list).
  214. * :param char* value: The ``key=value`` string to parse.
  215. * :return: the variable data with any variables substituted
  216. * :rtype: char *
  217. */
  218. char *
  219. pkgconf_tuple_parse(const pkgconf_client_t *client, pkgconf_list_t *vars, const char *value)
  220. {
  221. char buf[PKGCONF_BUFSIZE];
  222. const char *ptr;
  223. char *bptr = buf;
  224. if (*value == '/' && client->sysroot_dir != NULL && strncmp(value, client->sysroot_dir, strlen(client->sysroot_dir)))
  225. bptr += pkgconf_strlcpy(buf, client->sysroot_dir, sizeof buf);
  226. for (ptr = value; *ptr != '\0' && bptr - buf < PKGCONF_BUFSIZE; ptr++)
  227. {
  228. if (*ptr != '$' || (*ptr == '$' && *(ptr + 1) != '{'))
  229. *bptr++ = *ptr;
  230. else if (*(ptr + 1) == '{')
  231. {
  232. char varname[PKGCONF_ITEM_SIZE];
  233. char *vptr = varname;
  234. const char *pptr;
  235. char *kv, *parsekv;
  236. *vptr = '\0';
  237. for (pptr = ptr + 2; *pptr != '\0'; pptr++)
  238. {
  239. if (*pptr != '}')
  240. *vptr++ = *pptr;
  241. else
  242. {
  243. *vptr = '\0';
  244. break;
  245. }
  246. }
  247. ptr += (pptr - ptr);
  248. kv = pkgconf_tuple_find_global(client, varname);
  249. if (kv != NULL)
  250. {
  251. strncpy(bptr, kv, PKGCONF_BUFSIZE - (bptr - buf));
  252. bptr += strlen(kv);
  253. }
  254. else
  255. {
  256. kv = pkgconf_tuple_find(client, vars, varname);
  257. if (kv != NULL)
  258. {
  259. parsekv = pkgconf_tuple_parse(client, vars, kv);
  260. strncpy(bptr, parsekv, PKGCONF_BUFSIZE - (bptr - buf));
  261. bptr += strlen(parsekv);
  262. free(parsekv);
  263. }
  264. }
  265. }
  266. }
  267. *bptr = '\0';
  268. /*
  269. * Sigh. Somebody actually attempted to use freedesktop.org pkg-config's broken sysroot support,
  270. * which was written by somebody who did not understand how sysroots are supposed to work. This
  271. * results in an incorrect path being built as the sysroot will be prepended twice, once explicitly,
  272. * and once by variable expansion (the pkgconf approach). We could simply make ${pc_sysrootdir} blank,
  273. * but sometimes it is necessary to know the explicit sysroot path for other reasons, so we can't really
  274. * do that.
  275. *
  276. * As a result, we check to see if ${pc_sysrootdir} is prepended as a duplicate, and if so, remove the
  277. * prepend. This allows us to handle both our approach and the broken freedesktop.org implementation's
  278. * approach. Because a path can be shorter than ${pc_sysrootdir}, we do some checks first to ensure it's
  279. * safe to skip ahead in the string to scan for our sysroot dir.
  280. *
  281. * Finally, we call pkgconf_path_relocate() to clean the path of spurious elements.
  282. */
  283. if (*buf == '/' &&
  284. client->sysroot_dir != NULL &&
  285. strcmp(client->sysroot_dir, "/") != 0 &&
  286. strlen(buf) > strlen(client->sysroot_dir) &&
  287. strstr(buf + strlen(client->sysroot_dir), client->sysroot_dir) != NULL)
  288. {
  289. char cleanpath[PKGCONF_ITEM_SIZE];
  290. pkgconf_strlcpy(cleanpath, buf + strlen(client->sysroot_dir), sizeof cleanpath);
  291. pkgconf_path_relocate(cleanpath, sizeof cleanpath);
  292. return strdup(cleanpath);
  293. }
  294. return strdup(buf);
  295. }
  296. /*
  297. * !doc
  298. *
  299. * .. c:function:: void pkgconf_tuple_free_entry(pkgconf_tuple_t *tuple, pkgconf_list_t *list)
  300. *
  301. * Deletes a variable object, removing it from any variable lists and releasing any memory associated
  302. * with it.
  303. *
  304. * :param pkgconf_tuple_t* tuple: The variable object to release.
  305. * :param pkgconf_list_t* list: The variable list the variable object is attached to.
  306. * :return: nothing
  307. */
  308. void
  309. pkgconf_tuple_free_entry(pkgconf_tuple_t *tuple, pkgconf_list_t *list)
  310. {
  311. pkgconf_node_delete(&tuple->iter, list);
  312. free(tuple->key);
  313. free(tuple->value);
  314. free(tuple);
  315. }
  316. /*
  317. * !doc
  318. *
  319. * .. c:function:: void pkgconf_tuple_free(pkgconf_list_t *list)
  320. *
  321. * Deletes a variable list and any variables attached to it.
  322. *
  323. * :param pkgconf_list_t* list: The variable list to delete.
  324. * :return: nothing
  325. */
  326. void
  327. pkgconf_tuple_free(pkgconf_list_t *list)
  328. {
  329. pkgconf_node_t *node, *next;
  330. PKGCONF_FOREACH_LIST_ENTRY_SAFE(list->head, next, node)
  331. pkgconf_tuple_free_entry(node->data, list);
  332. }