jcs's openbsd hax
openbsd
at jcs 365 lines 8.9 kB view raw
1/* 2 * path.c 3 * filesystem path management 4 * 5 * Copyright (c) 2016 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/config.h> 17#include <libpkgconf/stdinc.h> 18#include <libpkgconf/libpkgconf.h> 19 20#if defined(HAVE_SYS_STAT_H) && ! defined(_WIN32) 21# include <sys/stat.h> 22# define PKGCONF_CACHE_INODES 23#endif 24 25static bool 26#ifdef PKGCONF_CACHE_INODES 27path_list_contains_entry(const char *text, pkgconf_list_t *dirlist, struct stat *st) 28#else 29path_list_contains_entry(const char *text, pkgconf_list_t *dirlist) 30#endif 31{ 32 pkgconf_node_t *n; 33 34 PKGCONF_FOREACH_LIST_ENTRY(dirlist->head, n) 35 { 36 pkgconf_path_t *pn = n->data; 37 38#ifdef PKGCONF_CACHE_INODES 39 if (pn->handle_device == (void *)(intptr_t)st->st_dev && pn->handle_path == (void *)(intptr_t)st->st_ino) 40 return true; 41#endif 42 43 if (!strcmp(text, pn->path)) 44 return true; 45 } 46 47 return false; 48} 49 50/* 51 * !doc 52 * 53 * libpkgconf `path` module 54 * ======================== 55 * 56 * The `path` module provides functions for manipulating lists of paths in a cross-platform manner. Notably, 57 * it is used by the `pkgconf client` to parse the ``PKG_CONFIG_PATH``, ``PKG_CONFIG_LIBDIR`` and related environment 58 * variables. 59 */ 60 61static pkgconf_path_t * 62prepare_path_node(const char *text, pkgconf_list_t *dirlist, bool filter) 63{ 64 pkgconf_path_t *node; 65 char path[PKGCONF_ITEM_SIZE]; 66 67 pkgconf_strlcpy(path, text, sizeof path); 68 pkgconf_path_relocate(path, sizeof path); 69 70#ifdef PKGCONF_CACHE_INODES 71 struct stat st; 72 73 if (filter) 74 { 75 if (lstat(path, &st) == -1) 76 return NULL; 77 if (S_ISLNK(st.st_mode)) 78 { 79 char pathbuf[PKGCONF_ITEM_SIZE * 4]; 80 char *linkdest = realpath(path, pathbuf); 81 82 if (linkdest != NULL && stat(linkdest, &st) == -1) 83 return NULL; 84 } 85 if (path_list_contains_entry(path, dirlist, &st)) 86 return NULL; 87 } 88#else 89 if (filter && path_list_contains_entry(path, dirlist)) 90 return NULL; 91#endif 92 93 node = calloc(1, sizeof(pkgconf_path_t)); 94 node->path = strdup(path); 95 96#ifdef PKGCONF_CACHE_INODES 97 if (filter) { 98 node->handle_path = (void *)(intptr_t) st.st_ino; 99 node->handle_device = (void *)(intptr_t) st.st_dev; 100 } 101#endif 102 103 return node; 104} 105 106/* 107 * !doc 108 * 109 * .. c:function:: void pkgconf_path_add(const char *text, pkgconf_list_t *dirlist) 110 * 111 * Adds a path node to a path list. If the path is already in the list, do nothing. 112 * 113 * :param char* text: The path text to add as a path node. 114 * :param pkgconf_list_t* dirlist: The path list to add the path node to. 115 * :param bool filter: Whether to perform duplicate filtering. 116 * :return: nothing 117 */ 118void 119pkgconf_path_add(const char *text, pkgconf_list_t *dirlist, bool filter) 120{ 121 pkgconf_path_t *node = prepare_path_node(text, dirlist, filter); 122 if (node == NULL) 123 return; 124 125 pkgconf_node_insert_tail(&node->lnode, node, dirlist); 126} 127 128/* 129 * !doc 130 * 131 * .. c:function:: void pkgconf_path_prepend(const char *text, pkgconf_list_t *dirlist) 132 * 133 * Prepends a path node to a path list. If the path is already in the list, do nothing. 134 * 135 * :param char* text: The path text to add as a path node. 136 * :param pkgconf_list_t* dirlist: The path list to add the path node to. 137 * :param bool filter: Whether to perform duplicate filtering. 138 * :return: nothing 139 */ 140void 141pkgconf_path_prepend(const char *text, pkgconf_list_t *dirlist, bool filter) 142{ 143 pkgconf_path_t *node = prepare_path_node(text, dirlist, filter); 144 if (node == NULL) 145 return; 146 147 pkgconf_node_insert(&node->lnode, node, dirlist); 148} 149 150/* 151 * !doc 152 * 153 * .. c:function:: size_t pkgconf_path_split(const char *text, pkgconf_list_t *dirlist) 154 * 155 * Splits a given text input and inserts paths into a path list. 156 * 157 * :param char* text: The path text to split and add as path nodes. 158 * :param pkgconf_list_t* dirlist: The path list to have the path nodes added to. 159 * :param bool filter: Whether to perform duplicate filtering. 160 * :return: number of path nodes added to the path list 161 * :rtype: size_t 162 */ 163size_t 164pkgconf_path_split(const char *text, pkgconf_list_t *dirlist, bool filter) 165{ 166 size_t count = 0; 167 char *workbuf, *p, *iter; 168 169 if (text == NULL) 170 return 0; 171 172 iter = workbuf = strdup(text); 173 while ((p = strtok(iter, PKG_CONFIG_PATH_SEP_S)) != NULL) 174 { 175 pkgconf_path_add(p, dirlist, filter); 176 177 count++, iter = NULL; 178 } 179 free(workbuf); 180 181 return count; 182} 183 184/* 185 * !doc 186 * 187 * .. c:function:: size_t pkgconf_path_build_from_environ(const char *envvarname, const char *fallback, pkgconf_list_t *dirlist) 188 * 189 * Adds the paths specified in an environment variable to a path list. If the environment variable is not set, 190 * an optional default set of paths is added. 191 * 192 * :param char* envvarname: The environment variable to look up. 193 * :param char* fallback: The fallback paths to use if the environment variable is not set. 194 * :param pkgconf_list_t* dirlist: The path list to add the path nodes to. 195 * :param bool filter: Whether to perform duplicate filtering. 196 * :return: number of path nodes added to the path list 197 * :rtype: size_t 198 */ 199size_t 200pkgconf_path_build_from_environ(const char *envvarname, const char *fallback, pkgconf_list_t *dirlist, bool filter) 201{ 202 const char *data; 203 204 data = getenv(envvarname); 205 if (data != NULL) 206 return pkgconf_path_split(data, dirlist, filter); 207 208 if (fallback != NULL) 209 return pkgconf_path_split(fallback, dirlist, filter); 210 211 /* no fallback and no environment variable, thusly no nodes added */ 212 return 0; 213} 214 215/* 216 * !doc 217 * 218 * .. c:function:: bool pkgconf_path_match_list(const char *path, const pkgconf_list_t *dirlist) 219 * 220 * Checks whether a path has a matching prefix in a path list. 221 * 222 * :param char* path: The path to check against a path list. 223 * :param pkgconf_list_t* dirlist: The path list to check the path against. 224 * :return: true if the path list has a matching prefix, otherwise false 225 * :rtype: bool 226 */ 227bool 228pkgconf_path_match_list(const char *path, const pkgconf_list_t *dirlist) 229{ 230 pkgconf_node_t *n = NULL; 231 char relocated[PKGCONF_ITEM_SIZE]; 232 const char *cpath = path; 233 234 pkgconf_strlcpy(relocated, path, sizeof relocated); 235 if (pkgconf_path_relocate(relocated, sizeof relocated)) 236 cpath = relocated; 237 238 PKGCONF_FOREACH_LIST_ENTRY(dirlist->head, n) 239 { 240 pkgconf_path_t *pnode = n->data; 241 242 if (!strcmp(pnode->path, cpath)) 243 return true; 244 } 245 246 return false; 247} 248 249/* 250 * !doc 251 * 252 * .. c:function:: void pkgconf_path_copy_list(pkgconf_list_t *dst, const pkgconf_list_t *src) 253 * 254 * Copies a path list to another path list. 255 * 256 * :param pkgconf_list_t* dst: The path list to copy to. 257 * :param pkgconf_list_t* src: The path list to copy from. 258 * :return: nothing 259 */ 260void 261pkgconf_path_copy_list(pkgconf_list_t *dst, const pkgconf_list_t *src) 262{ 263 pkgconf_node_t *n; 264 265 PKGCONF_FOREACH_LIST_ENTRY(src->head, n) 266 { 267 pkgconf_path_t *srcpath = n->data, *path; 268 269 path = calloc(1, sizeof(pkgconf_path_t)); 270 path->path = strdup(srcpath->path); 271 272#ifdef PKGCONF_CACHE_INODES 273 path->handle_path = srcpath->handle_path; 274 path->handle_device = srcpath->handle_device; 275#endif 276 277 pkgconf_node_insert_tail(&path->lnode, path, dst); 278 } 279} 280 281/* 282 * !doc 283 * 284 * .. c:function:: void pkgconf_path_free(pkgconf_list_t *dirlist) 285 * 286 * Releases any path nodes attached to the given path list. 287 * 288 * :param pkgconf_list_t* dirlist: The path list to clean up. 289 * :return: nothing 290 */ 291void 292pkgconf_path_free(pkgconf_list_t *dirlist) 293{ 294 pkgconf_node_t *n, *tn; 295 296 PKGCONF_FOREACH_LIST_ENTRY_SAFE(dirlist->head, tn, n) 297 { 298 pkgconf_path_t *pnode = n->data; 299 300 free(pnode->path); 301 free(pnode); 302 } 303 304 pkgconf_list_zero(dirlist); 305} 306 307static char * 308normpath(const char *path) 309{ 310 if (!path) 311 return NULL; 312 313 char *copy = strdup(path); 314 if (NULL == copy) 315 return NULL; 316 char *ptr = copy; 317 318 for (int ii = 0; copy[ii]; ii++) 319 { 320 *ptr++ = path[ii]; 321 if ('/' == path[ii]) 322 { 323 ii++; 324 while ('/' == path[ii]) 325 ii++; 326 ii--; 327 } 328 } 329 *ptr = '\0'; 330 331 return copy; 332} 333 334/* 335 * !doc 336 * 337 * .. c:function:: bool pkgconf_path_relocate(char *buf, size_t buflen) 338 * 339 * Relocates a path, possibly calling normpath() on it. 340 * 341 * :param char* buf: The path to relocate. 342 * :param size_t buflen: The buffer length the path is contained in. 343 * :return: true on success, false on error 344 * :rtype: bool 345 */ 346bool 347pkgconf_path_relocate(char *buf, size_t buflen) 348{ 349 char *tmpbuf; 350 351 if ((tmpbuf = normpath(buf)) != NULL) 352 { 353 size_t tmpbuflen = strlen(tmpbuf); 354 if (tmpbuflen > buflen) 355 { 356 free(tmpbuf); 357 return false; 358 } 359 360 pkgconf_strlcpy(buf, tmpbuf, buflen); 361 free(tmpbuf); 362 } 363 364 return true; 365}