xref: /freebsd/contrib/pkgconf/libpkgconf/path.c (revision a3cefe7f2b4df0f70ff92d4570ce18e517af43ec)
1*a3cefe7fSPierre Pronchery /*
2*a3cefe7fSPierre Pronchery  * path.c
3*a3cefe7fSPierre Pronchery  * filesystem path management
4*a3cefe7fSPierre Pronchery  *
5*a3cefe7fSPierre Pronchery  * Copyright (c) 2016 pkgconf authors (see AUTHORS).
6*a3cefe7fSPierre Pronchery  *
7*a3cefe7fSPierre Pronchery  * Permission to use, copy, modify, and/or distribute this software for any
8*a3cefe7fSPierre Pronchery  * purpose with or without fee is hereby granted, provided that the above
9*a3cefe7fSPierre Pronchery  * copyright notice and this permission notice appear in all copies.
10*a3cefe7fSPierre Pronchery  *
11*a3cefe7fSPierre Pronchery  * This software is provided 'as is' and without any warranty, express or
12*a3cefe7fSPierre Pronchery  * implied.  In no event shall the authors be liable for any damages arising
13*a3cefe7fSPierre Pronchery  * from the use of this software.
14*a3cefe7fSPierre Pronchery  */
15*a3cefe7fSPierre Pronchery 
16*a3cefe7fSPierre Pronchery #include <libpkgconf/config.h>
17*a3cefe7fSPierre Pronchery #include <libpkgconf/stdinc.h>
18*a3cefe7fSPierre Pronchery #include <libpkgconf/libpkgconf.h>
19*a3cefe7fSPierre Pronchery 
20*a3cefe7fSPierre Pronchery #if defined(HAVE_SYS_STAT_H) && ! defined(_WIN32)
21*a3cefe7fSPierre Pronchery # include <sys/stat.h>
22*a3cefe7fSPierre Pronchery # define PKGCONF_CACHE_INODES
23*a3cefe7fSPierre Pronchery #endif
24*a3cefe7fSPierre Pronchery 
25*a3cefe7fSPierre Pronchery #ifdef _WIN32
26*a3cefe7fSPierre Pronchery # define PKG_CONFIG_REG_KEY "Software\\pkgconfig\\PKG_CONFIG_PATH"
27*a3cefe7fSPierre Pronchery #endif
28*a3cefe7fSPierre Pronchery 
29*a3cefe7fSPierre Pronchery static bool
30*a3cefe7fSPierre Pronchery #ifdef PKGCONF_CACHE_INODES
path_list_contains_entry(const char * text,pkgconf_list_t * dirlist,struct stat * st)31*a3cefe7fSPierre Pronchery path_list_contains_entry(const char *text, pkgconf_list_t *dirlist, struct stat *st)
32*a3cefe7fSPierre Pronchery #else
33*a3cefe7fSPierre Pronchery path_list_contains_entry(const char *text, pkgconf_list_t *dirlist)
34*a3cefe7fSPierre Pronchery #endif
35*a3cefe7fSPierre Pronchery {
36*a3cefe7fSPierre Pronchery 	pkgconf_node_t *n;
37*a3cefe7fSPierre Pronchery 
38*a3cefe7fSPierre Pronchery 	PKGCONF_FOREACH_LIST_ENTRY(dirlist->head, n)
39*a3cefe7fSPierre Pronchery 	{
40*a3cefe7fSPierre Pronchery 		pkgconf_path_t *pn = n->data;
41*a3cefe7fSPierre Pronchery 
42*a3cefe7fSPierre Pronchery #ifdef PKGCONF_CACHE_INODES
43*a3cefe7fSPierre Pronchery 		if (pn->handle_device == (void *)(intptr_t)st->st_dev && pn->handle_path == (void *)(intptr_t)st->st_ino)
44*a3cefe7fSPierre Pronchery 			return true;
45*a3cefe7fSPierre Pronchery #endif
46*a3cefe7fSPierre Pronchery 
47*a3cefe7fSPierre Pronchery 		if (!strcmp(text, pn->path))
48*a3cefe7fSPierre Pronchery 			return true;
49*a3cefe7fSPierre Pronchery 	}
50*a3cefe7fSPierre Pronchery 
51*a3cefe7fSPierre Pronchery 	return false;
52*a3cefe7fSPierre Pronchery }
53*a3cefe7fSPierre Pronchery 
54*a3cefe7fSPierre Pronchery /*
55*a3cefe7fSPierre Pronchery  * !doc
56*a3cefe7fSPierre Pronchery  *
57*a3cefe7fSPierre Pronchery  * libpkgconf `path` module
58*a3cefe7fSPierre Pronchery  * ========================
59*a3cefe7fSPierre Pronchery  *
60*a3cefe7fSPierre Pronchery  * The `path` module provides functions for manipulating lists of paths in a cross-platform manner.  Notably,
61*a3cefe7fSPierre Pronchery  * it is used by the `pkgconf client` to parse the ``PKG_CONFIG_PATH``, ``PKG_CONFIG_LIBDIR`` and related environment
62*a3cefe7fSPierre Pronchery  * variables.
63*a3cefe7fSPierre Pronchery  */
64*a3cefe7fSPierre Pronchery 
65*a3cefe7fSPierre Pronchery static pkgconf_path_t *
prepare_path_node(const char * text,pkgconf_list_t * dirlist,bool filter)66*a3cefe7fSPierre Pronchery prepare_path_node(const char *text, pkgconf_list_t *dirlist, bool filter)
67*a3cefe7fSPierre Pronchery {
68*a3cefe7fSPierre Pronchery 	pkgconf_path_t *node;
69*a3cefe7fSPierre Pronchery 	char path[PKGCONF_ITEM_SIZE];
70*a3cefe7fSPierre Pronchery 
71*a3cefe7fSPierre Pronchery 	pkgconf_strlcpy(path, text, sizeof path);
72*a3cefe7fSPierre Pronchery 	pkgconf_path_relocate(path, sizeof path);
73*a3cefe7fSPierre Pronchery 
74*a3cefe7fSPierre Pronchery #ifdef PKGCONF_CACHE_INODES
75*a3cefe7fSPierre Pronchery 	struct stat st;
76*a3cefe7fSPierre Pronchery 
77*a3cefe7fSPierre Pronchery 	if (filter)
78*a3cefe7fSPierre Pronchery 	{
79*a3cefe7fSPierre Pronchery 		if (lstat(path, &st) == -1)
80*a3cefe7fSPierre Pronchery 			return NULL;
81*a3cefe7fSPierre Pronchery 		if (S_ISLNK(st.st_mode))
82*a3cefe7fSPierre Pronchery 		{
83*a3cefe7fSPierre Pronchery 			char pathbuf[PKGCONF_ITEM_SIZE * 4];
84*a3cefe7fSPierre Pronchery 			char *linkdest = realpath(path, pathbuf);
85*a3cefe7fSPierre Pronchery 
86*a3cefe7fSPierre Pronchery 			if (linkdest != NULL && stat(linkdest, &st) == -1)
87*a3cefe7fSPierre Pronchery 				return NULL;
88*a3cefe7fSPierre Pronchery 		}
89*a3cefe7fSPierre Pronchery 		if (path_list_contains_entry(path, dirlist, &st))
90*a3cefe7fSPierre Pronchery 			return NULL;
91*a3cefe7fSPierre Pronchery 	}
92*a3cefe7fSPierre Pronchery #else
93*a3cefe7fSPierre Pronchery 	if (filter && path_list_contains_entry(path, dirlist))
94*a3cefe7fSPierre Pronchery 		return NULL;
95*a3cefe7fSPierre Pronchery #endif
96*a3cefe7fSPierre Pronchery 
97*a3cefe7fSPierre Pronchery 	node = calloc(1, sizeof(pkgconf_path_t));
98*a3cefe7fSPierre Pronchery 	if (node == NULL)
99*a3cefe7fSPierre Pronchery 		return NULL;
100*a3cefe7fSPierre Pronchery 
101*a3cefe7fSPierre Pronchery 	node->path = strdup(path);
102*a3cefe7fSPierre Pronchery 
103*a3cefe7fSPierre Pronchery #ifdef PKGCONF_CACHE_INODES
104*a3cefe7fSPierre Pronchery 	if (filter) {
105*a3cefe7fSPierre Pronchery 		node->handle_path = (void *)(intptr_t) st.st_ino;
106*a3cefe7fSPierre Pronchery 		node->handle_device = (void *)(intptr_t) st.st_dev;
107*a3cefe7fSPierre Pronchery 	}
108*a3cefe7fSPierre Pronchery #endif
109*a3cefe7fSPierre Pronchery 
110*a3cefe7fSPierre Pronchery 	return node;
111*a3cefe7fSPierre Pronchery }
112*a3cefe7fSPierre Pronchery 
113*a3cefe7fSPierre Pronchery /*
114*a3cefe7fSPierre Pronchery  * !doc
115*a3cefe7fSPierre Pronchery  *
116*a3cefe7fSPierre Pronchery  * .. c:function:: void pkgconf_path_add(const char *text, pkgconf_list_t *dirlist)
117*a3cefe7fSPierre Pronchery  *
118*a3cefe7fSPierre Pronchery  *    Adds a path node to a path list.  If the path is already in the list, do nothing.
119*a3cefe7fSPierre Pronchery  *
120*a3cefe7fSPierre Pronchery  *    :param char* text: The path text to add as a path node.
121*a3cefe7fSPierre Pronchery  *    :param pkgconf_list_t* dirlist: The path list to add the path node to.
122*a3cefe7fSPierre Pronchery  *    :param bool filter: Whether to perform duplicate filtering.
123*a3cefe7fSPierre Pronchery  *    :return: nothing
124*a3cefe7fSPierre Pronchery  */
125*a3cefe7fSPierre Pronchery void
pkgconf_path_add(const char * text,pkgconf_list_t * dirlist,bool filter)126*a3cefe7fSPierre Pronchery pkgconf_path_add(const char *text, pkgconf_list_t *dirlist, bool filter)
127*a3cefe7fSPierre Pronchery {
128*a3cefe7fSPierre Pronchery 	pkgconf_path_t *node = prepare_path_node(text, dirlist, filter);
129*a3cefe7fSPierre Pronchery 	if (node == NULL)
130*a3cefe7fSPierre Pronchery 		return;
131*a3cefe7fSPierre Pronchery 
132*a3cefe7fSPierre Pronchery 	pkgconf_node_insert_tail(&node->lnode, node, dirlist);
133*a3cefe7fSPierre Pronchery }
134*a3cefe7fSPierre Pronchery 
135*a3cefe7fSPierre Pronchery /*
136*a3cefe7fSPierre Pronchery  * !doc
137*a3cefe7fSPierre Pronchery  *
138*a3cefe7fSPierre Pronchery  * .. c:function:: void pkgconf_path_prepend(const char *text, pkgconf_list_t *dirlist)
139*a3cefe7fSPierre Pronchery  *
140*a3cefe7fSPierre Pronchery  *    Prepends a path node to a path list.  If the path is already in the list, do nothing.
141*a3cefe7fSPierre Pronchery  *
142*a3cefe7fSPierre Pronchery  *    :param char* text: The path text to add as a path node.
143*a3cefe7fSPierre Pronchery  *    :param pkgconf_list_t* dirlist: The path list to add the path node to.
144*a3cefe7fSPierre Pronchery  *    :param bool filter: Whether to perform duplicate filtering.
145*a3cefe7fSPierre Pronchery  *    :return: nothing
146*a3cefe7fSPierre Pronchery  */
147*a3cefe7fSPierre Pronchery void
pkgconf_path_prepend(const char * text,pkgconf_list_t * dirlist,bool filter)148*a3cefe7fSPierre Pronchery pkgconf_path_prepend(const char *text, pkgconf_list_t *dirlist, bool filter)
149*a3cefe7fSPierre Pronchery {
150*a3cefe7fSPierre Pronchery 	pkgconf_path_t *node = prepare_path_node(text, dirlist, filter);
151*a3cefe7fSPierre Pronchery 	if (node == NULL)
152*a3cefe7fSPierre Pronchery 		return;
153*a3cefe7fSPierre Pronchery 
154*a3cefe7fSPierre Pronchery 	pkgconf_node_insert(&node->lnode, node, dirlist);
155*a3cefe7fSPierre Pronchery }
156*a3cefe7fSPierre Pronchery 
157*a3cefe7fSPierre Pronchery /*
158*a3cefe7fSPierre Pronchery  * !doc
159*a3cefe7fSPierre Pronchery  *
160*a3cefe7fSPierre Pronchery  * .. c:function:: size_t pkgconf_path_split(const char *text, pkgconf_list_t *dirlist)
161*a3cefe7fSPierre Pronchery  *
162*a3cefe7fSPierre Pronchery  *    Splits a given text input and inserts paths into a path list.
163*a3cefe7fSPierre Pronchery  *
164*a3cefe7fSPierre Pronchery  *    :param char* text: The path text to split and add as path nodes.
165*a3cefe7fSPierre Pronchery  *    :param pkgconf_list_t* dirlist: The path list to have the path nodes added to.
166*a3cefe7fSPierre Pronchery  *    :param bool filter: Whether to perform duplicate filtering.
167*a3cefe7fSPierre Pronchery  *    :return: number of path nodes added to the path list
168*a3cefe7fSPierre Pronchery  *    :rtype: size_t
169*a3cefe7fSPierre Pronchery  */
170*a3cefe7fSPierre Pronchery size_t
pkgconf_path_split(const char * text,pkgconf_list_t * dirlist,bool filter)171*a3cefe7fSPierre Pronchery pkgconf_path_split(const char *text, pkgconf_list_t *dirlist, bool filter)
172*a3cefe7fSPierre Pronchery {
173*a3cefe7fSPierre Pronchery 	size_t count = 0;
174*a3cefe7fSPierre Pronchery 	char *workbuf, *p, *iter;
175*a3cefe7fSPierre Pronchery 
176*a3cefe7fSPierre Pronchery 	if (text == NULL)
177*a3cefe7fSPierre Pronchery 		return 0;
178*a3cefe7fSPierre Pronchery 
179*a3cefe7fSPierre Pronchery 	iter = workbuf = strdup(text);
180*a3cefe7fSPierre Pronchery 	while ((p = strtok(iter, PKG_CONFIG_PATH_SEP_S)) != NULL)
181*a3cefe7fSPierre Pronchery 	{
182*a3cefe7fSPierre Pronchery 		pkgconf_path_add(p, dirlist, filter);
183*a3cefe7fSPierre Pronchery 
184*a3cefe7fSPierre Pronchery 		count++, iter = NULL;
185*a3cefe7fSPierre Pronchery 	}
186*a3cefe7fSPierre Pronchery 	free(workbuf);
187*a3cefe7fSPierre Pronchery 
188*a3cefe7fSPierre Pronchery 	return count;
189*a3cefe7fSPierre Pronchery }
190*a3cefe7fSPierre Pronchery 
191*a3cefe7fSPierre Pronchery /*
192*a3cefe7fSPierre Pronchery  * !doc
193*a3cefe7fSPierre Pronchery  *
194*a3cefe7fSPierre Pronchery  * .. c:function:: size_t pkgconf_path_build_from_environ(const char *envvarname, const char *fallback, pkgconf_list_t *dirlist)
195*a3cefe7fSPierre Pronchery  *
196*a3cefe7fSPierre Pronchery  *    Adds the paths specified in an environment variable to a path list.  If the environment variable is not set,
197*a3cefe7fSPierre Pronchery  *    an optional default set of paths is added.
198*a3cefe7fSPierre Pronchery  *
199*a3cefe7fSPierre Pronchery  *    :param char* envvarname: The environment variable to look up.
200*a3cefe7fSPierre Pronchery  *    :param char* fallback: The fallback paths to use if the environment variable is not set.
201*a3cefe7fSPierre Pronchery  *    :param pkgconf_list_t* dirlist: The path list to add the path nodes to.
202*a3cefe7fSPierre Pronchery  *    :param bool filter: Whether to perform duplicate filtering.
203*a3cefe7fSPierre Pronchery  *    :return: number of path nodes added to the path list
204*a3cefe7fSPierre Pronchery  *    :rtype: size_t
205*a3cefe7fSPierre Pronchery  */
206*a3cefe7fSPierre Pronchery size_t
pkgconf_path_build_from_environ(const char * envvarname,const char * fallback,pkgconf_list_t * dirlist,bool filter)207*a3cefe7fSPierre Pronchery pkgconf_path_build_from_environ(const char *envvarname, const char *fallback, pkgconf_list_t *dirlist, bool filter)
208*a3cefe7fSPierre Pronchery {
209*a3cefe7fSPierre Pronchery 	const char *data;
210*a3cefe7fSPierre Pronchery 
211*a3cefe7fSPierre Pronchery 	data = getenv(envvarname);
212*a3cefe7fSPierre Pronchery 	if (data != NULL)
213*a3cefe7fSPierre Pronchery 		return pkgconf_path_split(data, dirlist, filter);
214*a3cefe7fSPierre Pronchery 
215*a3cefe7fSPierre Pronchery 	if (fallback != NULL)
216*a3cefe7fSPierre Pronchery 		return pkgconf_path_split(fallback, dirlist, filter);
217*a3cefe7fSPierre Pronchery 
218*a3cefe7fSPierre Pronchery 	/* no fallback and no environment variable, thusly no nodes added */
219*a3cefe7fSPierre Pronchery 	return 0;
220*a3cefe7fSPierre Pronchery }
221*a3cefe7fSPierre Pronchery 
222*a3cefe7fSPierre Pronchery /*
223*a3cefe7fSPierre Pronchery  * !doc
224*a3cefe7fSPierre Pronchery  *
225*a3cefe7fSPierre Pronchery  * .. c:function:: bool pkgconf_path_match_list(const char *path, const pkgconf_list_t *dirlist)
226*a3cefe7fSPierre Pronchery  *
227*a3cefe7fSPierre Pronchery  *    Checks whether a path has a matching prefix in a path list.
228*a3cefe7fSPierre Pronchery  *
229*a3cefe7fSPierre Pronchery  *    :param char* path: The path to check against a path list.
230*a3cefe7fSPierre Pronchery  *    :param pkgconf_list_t* dirlist: The path list to check the path against.
231*a3cefe7fSPierre Pronchery  *    :return: true if the path list has a matching prefix, otherwise false
232*a3cefe7fSPierre Pronchery  *    :rtype: bool
233*a3cefe7fSPierre Pronchery  */
234*a3cefe7fSPierre Pronchery bool
pkgconf_path_match_list(const char * path,const pkgconf_list_t * dirlist)235*a3cefe7fSPierre Pronchery pkgconf_path_match_list(const char *path, const pkgconf_list_t *dirlist)
236*a3cefe7fSPierre Pronchery {
237*a3cefe7fSPierre Pronchery 	pkgconf_node_t *n = NULL;
238*a3cefe7fSPierre Pronchery 	char relocated[PKGCONF_ITEM_SIZE];
239*a3cefe7fSPierre Pronchery 	const char *cpath = path;
240*a3cefe7fSPierre Pronchery 
241*a3cefe7fSPierre Pronchery 	pkgconf_strlcpy(relocated, path, sizeof relocated);
242*a3cefe7fSPierre Pronchery 	if (pkgconf_path_relocate(relocated, sizeof relocated))
243*a3cefe7fSPierre Pronchery 		cpath = relocated;
244*a3cefe7fSPierre Pronchery 
245*a3cefe7fSPierre Pronchery 	PKGCONF_FOREACH_LIST_ENTRY(dirlist->head, n)
246*a3cefe7fSPierre Pronchery 	{
247*a3cefe7fSPierre Pronchery 		pkgconf_path_t *pnode = n->data;
248*a3cefe7fSPierre Pronchery 
249*a3cefe7fSPierre Pronchery 		if (!strcmp(pnode->path, cpath))
250*a3cefe7fSPierre Pronchery 			return true;
251*a3cefe7fSPierre Pronchery 	}
252*a3cefe7fSPierre Pronchery 
253*a3cefe7fSPierre Pronchery 	return false;
254*a3cefe7fSPierre Pronchery }
255*a3cefe7fSPierre Pronchery 
256*a3cefe7fSPierre Pronchery /*
257*a3cefe7fSPierre Pronchery  * !doc
258*a3cefe7fSPierre Pronchery  *
259*a3cefe7fSPierre Pronchery  * .. c:function:: void pkgconf_path_copy_list(pkgconf_list_t *dst, const pkgconf_list_t *src)
260*a3cefe7fSPierre Pronchery  *
261*a3cefe7fSPierre Pronchery  *    Copies a path list to another path list.
262*a3cefe7fSPierre Pronchery  *
263*a3cefe7fSPierre Pronchery  *    :param pkgconf_list_t* dst: The path list to copy to.
264*a3cefe7fSPierre Pronchery  *    :param pkgconf_list_t* src: The path list to copy from.
265*a3cefe7fSPierre Pronchery  *    :return: nothing
266*a3cefe7fSPierre Pronchery  */
267*a3cefe7fSPierre Pronchery void
pkgconf_path_copy_list(pkgconf_list_t * dst,const pkgconf_list_t * src)268*a3cefe7fSPierre Pronchery pkgconf_path_copy_list(pkgconf_list_t *dst, const pkgconf_list_t *src)
269*a3cefe7fSPierre Pronchery {
270*a3cefe7fSPierre Pronchery 	pkgconf_node_t *n;
271*a3cefe7fSPierre Pronchery 
272*a3cefe7fSPierre Pronchery 	PKGCONF_FOREACH_LIST_ENTRY(src->head, n)
273*a3cefe7fSPierre Pronchery 	{
274*a3cefe7fSPierre Pronchery 		pkgconf_path_t *srcpath = n->data, *path;
275*a3cefe7fSPierre Pronchery 
276*a3cefe7fSPierre Pronchery 		path = calloc(1, sizeof(pkgconf_path_t));
277*a3cefe7fSPierre Pronchery 		if (path == NULL)
278*a3cefe7fSPierre Pronchery 			continue;
279*a3cefe7fSPierre Pronchery 
280*a3cefe7fSPierre Pronchery 		path->path = strdup(srcpath->path);
281*a3cefe7fSPierre Pronchery 
282*a3cefe7fSPierre Pronchery #ifdef PKGCONF_CACHE_INODES
283*a3cefe7fSPierre Pronchery 		path->handle_path = srcpath->handle_path;
284*a3cefe7fSPierre Pronchery 		path->handle_device = srcpath->handle_device;
285*a3cefe7fSPierre Pronchery #endif
286*a3cefe7fSPierre Pronchery 
287*a3cefe7fSPierre Pronchery 		pkgconf_node_insert_tail(&path->lnode, path, dst);
288*a3cefe7fSPierre Pronchery 	}
289*a3cefe7fSPierre Pronchery }
290*a3cefe7fSPierre Pronchery 
291*a3cefe7fSPierre Pronchery /*
292*a3cefe7fSPierre Pronchery  * !doc
293*a3cefe7fSPierre Pronchery  *
294*a3cefe7fSPierre Pronchery  * .. c:function:: void pkgconf_path_prepend_list(pkgconf_list_t *dst, const pkgconf_list_t *src)
295*a3cefe7fSPierre Pronchery  *
296*a3cefe7fSPierre Pronchery  *    Copies a path list to another path list.
297*a3cefe7fSPierre Pronchery  *
298*a3cefe7fSPierre Pronchery  *    :param pkgconf_list_t* dst: The path list to copy to.
299*a3cefe7fSPierre Pronchery  *    :param pkgconf_list_t* src: The path list to copy from.
300*a3cefe7fSPierre Pronchery  *    :return: nothing
301*a3cefe7fSPierre Pronchery  */
302*a3cefe7fSPierre Pronchery void
pkgconf_path_prepend_list(pkgconf_list_t * dst,const pkgconf_list_t * src)303*a3cefe7fSPierre Pronchery pkgconf_path_prepend_list(pkgconf_list_t *dst, const pkgconf_list_t *src)
304*a3cefe7fSPierre Pronchery {
305*a3cefe7fSPierre Pronchery 	pkgconf_node_t *n;
306*a3cefe7fSPierre Pronchery 
307*a3cefe7fSPierre Pronchery 	PKGCONF_FOREACH_LIST_ENTRY(src->head, n)
308*a3cefe7fSPierre Pronchery 	{
309*a3cefe7fSPierre Pronchery 		pkgconf_path_t *srcpath = n->data, *path;
310*a3cefe7fSPierre Pronchery 
311*a3cefe7fSPierre Pronchery 		path = calloc(1, sizeof(pkgconf_path_t));
312*a3cefe7fSPierre Pronchery 		if (path == NULL)
313*a3cefe7fSPierre Pronchery 			continue;
314*a3cefe7fSPierre Pronchery 
315*a3cefe7fSPierre Pronchery 		path->path = strdup(srcpath->path);
316*a3cefe7fSPierre Pronchery 
317*a3cefe7fSPierre Pronchery #ifdef PKGCONF_CACHE_INODES
318*a3cefe7fSPierre Pronchery 		path->handle_path = srcpath->handle_path;
319*a3cefe7fSPierre Pronchery 		path->handle_device = srcpath->handle_device;
320*a3cefe7fSPierre Pronchery #endif
321*a3cefe7fSPierre Pronchery 
322*a3cefe7fSPierre Pronchery 		pkgconf_node_insert(&path->lnode, path, dst);
323*a3cefe7fSPierre Pronchery 	}
324*a3cefe7fSPierre Pronchery }
325*a3cefe7fSPierre Pronchery 
326*a3cefe7fSPierre Pronchery /*
327*a3cefe7fSPierre Pronchery  * !doc
328*a3cefe7fSPierre Pronchery  *
329*a3cefe7fSPierre Pronchery  * .. c:function:: void pkgconf_path_free(pkgconf_list_t *dirlist)
330*a3cefe7fSPierre Pronchery  *
331*a3cefe7fSPierre Pronchery  *    Releases any path nodes attached to the given path list.
332*a3cefe7fSPierre Pronchery  *
333*a3cefe7fSPierre Pronchery  *    :param pkgconf_list_t* dirlist: The path list to clean up.
334*a3cefe7fSPierre Pronchery  *    :return: nothing
335*a3cefe7fSPierre Pronchery  */
336*a3cefe7fSPierre Pronchery void
pkgconf_path_free(pkgconf_list_t * dirlist)337*a3cefe7fSPierre Pronchery pkgconf_path_free(pkgconf_list_t *dirlist)
338*a3cefe7fSPierre Pronchery {
339*a3cefe7fSPierre Pronchery 	pkgconf_node_t *n, *tn;
340*a3cefe7fSPierre Pronchery 
341*a3cefe7fSPierre Pronchery 	PKGCONF_FOREACH_LIST_ENTRY_SAFE(dirlist->head, tn, n)
342*a3cefe7fSPierre Pronchery 	{
343*a3cefe7fSPierre Pronchery 		pkgconf_path_t *pnode = n->data;
344*a3cefe7fSPierre Pronchery 
345*a3cefe7fSPierre Pronchery 		free(pnode->path);
346*a3cefe7fSPierre Pronchery 		free(pnode);
347*a3cefe7fSPierre Pronchery 	}
348*a3cefe7fSPierre Pronchery 
349*a3cefe7fSPierre Pronchery 	pkgconf_list_zero(dirlist);
350*a3cefe7fSPierre Pronchery }
351*a3cefe7fSPierre Pronchery 
352*a3cefe7fSPierre Pronchery static char *
normpath(const char * path)353*a3cefe7fSPierre Pronchery normpath(const char *path)
354*a3cefe7fSPierre Pronchery {
355*a3cefe7fSPierre Pronchery 	if (!path)
356*a3cefe7fSPierre Pronchery 		return NULL;
357*a3cefe7fSPierre Pronchery 
358*a3cefe7fSPierre Pronchery 	char *copy = strdup(path);
359*a3cefe7fSPierre Pronchery 	if (NULL == copy)
360*a3cefe7fSPierre Pronchery 		return NULL;
361*a3cefe7fSPierre Pronchery 	char *ptr = copy;
362*a3cefe7fSPierre Pronchery 
363*a3cefe7fSPierre Pronchery 	for (int ii = 0; copy[ii]; ii++)
364*a3cefe7fSPierre Pronchery 	{
365*a3cefe7fSPierre Pronchery 		*ptr++ = path[ii];
366*a3cefe7fSPierre Pronchery 		if ('/' == path[ii])
367*a3cefe7fSPierre Pronchery 		{
368*a3cefe7fSPierre Pronchery 			ii++;
369*a3cefe7fSPierre Pronchery 			while ('/' == path[ii])
370*a3cefe7fSPierre Pronchery 				ii++;
371*a3cefe7fSPierre Pronchery 			ii--;
372*a3cefe7fSPierre Pronchery 		}
373*a3cefe7fSPierre Pronchery 	}
374*a3cefe7fSPierre Pronchery 	*ptr = '\0';
375*a3cefe7fSPierre Pronchery 
376*a3cefe7fSPierre Pronchery 	return copy;
377*a3cefe7fSPierre Pronchery }
378*a3cefe7fSPierre Pronchery 
379*a3cefe7fSPierre Pronchery /*
380*a3cefe7fSPierre Pronchery  * !doc
381*a3cefe7fSPierre Pronchery  *
382*a3cefe7fSPierre Pronchery  * .. c:function:: bool pkgconf_path_relocate(char *buf, size_t buflen)
383*a3cefe7fSPierre Pronchery  *
384*a3cefe7fSPierre Pronchery  *    Relocates a path, possibly calling normpath() on it.
385*a3cefe7fSPierre Pronchery  *
386*a3cefe7fSPierre Pronchery  *    :param char* buf: The path to relocate.
387*a3cefe7fSPierre Pronchery  *    :param size_t buflen: The buffer length the path is contained in.
388*a3cefe7fSPierre Pronchery  *    :return: true on success, false on error
389*a3cefe7fSPierre Pronchery  *    :rtype: bool
390*a3cefe7fSPierre Pronchery  */
391*a3cefe7fSPierre Pronchery bool
pkgconf_path_relocate(char * buf,size_t buflen)392*a3cefe7fSPierre Pronchery pkgconf_path_relocate(char *buf, size_t buflen)
393*a3cefe7fSPierre Pronchery {
394*a3cefe7fSPierre Pronchery 	char *tmpbuf;
395*a3cefe7fSPierre Pronchery 
396*a3cefe7fSPierre Pronchery 	if ((tmpbuf = normpath(buf)) != NULL)
397*a3cefe7fSPierre Pronchery 	{
398*a3cefe7fSPierre Pronchery 		size_t tmpbuflen = strlen(tmpbuf);
399*a3cefe7fSPierre Pronchery 		if (tmpbuflen > buflen)
400*a3cefe7fSPierre Pronchery 		{
401*a3cefe7fSPierre Pronchery 			free(tmpbuf);
402*a3cefe7fSPierre Pronchery 			return false;
403*a3cefe7fSPierre Pronchery 		}
404*a3cefe7fSPierre Pronchery 
405*a3cefe7fSPierre Pronchery 		pkgconf_strlcpy(buf, tmpbuf, buflen);
406*a3cefe7fSPierre Pronchery 		free(tmpbuf);
407*a3cefe7fSPierre Pronchery 	}
408*a3cefe7fSPierre Pronchery 
409*a3cefe7fSPierre Pronchery 	return true;
410*a3cefe7fSPierre Pronchery }
411*a3cefe7fSPierre Pronchery 
412*a3cefe7fSPierre Pronchery #ifdef _WIN32
413*a3cefe7fSPierre Pronchery /*
414*a3cefe7fSPierre Pronchery  * !doc
415*a3cefe7fSPierre Pronchery  *
416*a3cefe7fSPierre Pronchery  * .. c:function:: void pkgconf_path_build_from_registry(HKEY hKey, pkgconf_list_t *dir_list, bool filter)
417*a3cefe7fSPierre Pronchery  *
418*a3cefe7fSPierre Pronchery  *    Adds paths to a directory list discovered from a given registry key.
419*a3cefe7fSPierre Pronchery  *
420*a3cefe7fSPierre Pronchery  *    :param HKEY hKey: The registry key to enumerate.
421*a3cefe7fSPierre Pronchery  *    :param pkgconf_list_t* dir_list: The directory list to append enumerated paths to.
422*a3cefe7fSPierre Pronchery  *    :param bool filter: Whether duplicate paths should be filtered.
423*a3cefe7fSPierre Pronchery  *    :return: number of path nodes added to the list
424*a3cefe7fSPierre Pronchery  *    :rtype: size_t
425*a3cefe7fSPierre Pronchery  */
426*a3cefe7fSPierre Pronchery size_t
pkgconf_path_build_from_registry(void * hKey,pkgconf_list_t * dir_list,bool filter)427*a3cefe7fSPierre Pronchery pkgconf_path_build_from_registry(void *hKey, pkgconf_list_t *dir_list, bool filter)
428*a3cefe7fSPierre Pronchery {
429*a3cefe7fSPierre Pronchery 	HKEY key;
430*a3cefe7fSPierre Pronchery 	int i = 0;
431*a3cefe7fSPierre Pronchery 	size_t added = 0;
432*a3cefe7fSPierre Pronchery 
433*a3cefe7fSPierre Pronchery 	char buf[16384]; /* per registry limits */
434*a3cefe7fSPierre Pronchery 	DWORD bufsize = sizeof buf;
435*a3cefe7fSPierre Pronchery 	if (RegOpenKeyEx(hKey, PKG_CONFIG_REG_KEY,
436*a3cefe7fSPierre Pronchery 				0, KEY_READ, &key) != ERROR_SUCCESS)
437*a3cefe7fSPierre Pronchery 		return 0;
438*a3cefe7fSPierre Pronchery 
439*a3cefe7fSPierre Pronchery 	while (RegEnumValue(key, i++, buf, &bufsize, NULL, NULL, NULL, NULL)
440*a3cefe7fSPierre Pronchery 			== ERROR_SUCCESS)
441*a3cefe7fSPierre Pronchery 	{
442*a3cefe7fSPierre Pronchery 		char pathbuf[PKGCONF_ITEM_SIZE];
443*a3cefe7fSPierre Pronchery 		DWORD type;
444*a3cefe7fSPierre Pronchery 		DWORD pathbuflen = sizeof pathbuf;
445*a3cefe7fSPierre Pronchery 
446*a3cefe7fSPierre Pronchery 		if (RegQueryValueEx(key, buf, NULL, &type, (LPBYTE) pathbuf, &pathbuflen)
447*a3cefe7fSPierre Pronchery 				== ERROR_SUCCESS && type == REG_SZ)
448*a3cefe7fSPierre Pronchery 		{
449*a3cefe7fSPierre Pronchery 			pkgconf_path_add(pathbuf, dir_list, filter);
450*a3cefe7fSPierre Pronchery 			added++;
451*a3cefe7fSPierre Pronchery 		}
452*a3cefe7fSPierre Pronchery 
453*a3cefe7fSPierre Pronchery 		bufsize = sizeof buf;
454*a3cefe7fSPierre Pronchery 	}
455*a3cefe7fSPierre Pronchery 
456*a3cefe7fSPierre Pronchery 	RegCloseKey(key);
457*a3cefe7fSPierre Pronchery 	return added;
458*a3cefe7fSPierre Pronchery }
459*a3cefe7fSPierre Pronchery #endif
460