1/*
2 * Copyright 2015, Axel D��rfler, axeld@pinc-software.de.
3 * Copyright 2013, Ingo Weinhold, ingo_weinhold@gmx.de.
4 * Distributed under the terms of the MIT License.
5 */
6
7
8#include <find_directory_private.h>
9
10#include <errno.h>
11#include <stdio.h>
12#include <string.h>
13#include <sys/stat.h>
14
15#include <algorithm>
16
17#include <fs_attr.h>
18
19#include <architecture_private.h>
20#include <AutoDeleter.h>
21#include <directories.h>
22#include <syscalls.h>
23
24#include "PathBuffer.h"
25
26
27static size_t kHomeInstallationLocationIndex = 1;
28
29static const path_base_directory kArchitectureSpecificBaseDirectories[] = {
30	B_FIND_PATH_ADD_ONS_DIRECTORY,
31	B_FIND_PATH_BIN_DIRECTORY,
32	B_FIND_PATH_DEVELOP_LIB_DIRECTORY,
33	B_FIND_PATH_HEADERS_DIRECTORY,
34};
35
36static size_t kArchitectureSpecificBaseDirectoryCount =
37	sizeof(kArchitectureSpecificBaseDirectories)
38		/ sizeof(kArchitectureSpecificBaseDirectories[0]);
39
40
41namespace {
42
43
44struct InstallationLocations {
45public:
46	static const size_t	kCount = 4;
47
48public:
49	InstallationLocations()
50		:
51		fReferenceCount(1)
52	{
53		fLocations[0] = kUserNonpackagedDirectory;
54		fLocations[1] = kUserConfigDirectory;
55		fLocations[2] = kSystemNonpackagedDirectory;
56		fLocations[3] = kSystemDirectory;
57	}
58
59	InstallationLocations(const char* home)
60		:
61		fReferenceCount(1)
62	{
63		static const char* const kNonPackagedSuffix = "/non-packaged";
64		char* homeNonPackaged
65			= (char*)malloc(strlen(home) + strlen(kNonPackagedSuffix) + 1);
66		fLocations[0] = homeNonPackaged;
67		if (homeNonPackaged != NULL) {
68			strcpy(homeNonPackaged, home);
69			strcat(homeNonPackaged, kNonPackagedSuffix);
70		}
71
72		fLocations[1] = strdup(home);
73
74		fLocations[2] = kSystemNonpackagedDirectory;
75		fLocations[3] = kSystemDirectory;
76	}
77
78	~InstallationLocations()
79	{
80		free(const_cast<char*>(fLocations[0]));
81		free(const_cast<char*>(fLocations[1]));
82	}
83
84	bool IsValid() const
85	{
86		return fLocations[0] != NULL && fLocations[1] != NULL;
87	}
88
89	bool IsUserIndex(size_t index) const
90	{
91		return index==0 || index==1;
92	}
93
94	bool IsSystemIndex(size_t index) const
95	{
96		return index==2 || index==3;
97	}
98
99	static InstallationLocations* Default()
100	{
101		static char sBuffer[sizeof(InstallationLocations)];
102		static InstallationLocations* sDefaultLocations
103			= new(&sBuffer) InstallationLocations;
104		return sDefaultLocations;
105	}
106
107	static InstallationLocations* Get()
108	{
109		InstallationLocations* defaultLocations = Default();
110
111		// Get the home config installation location and create a new object,
112		// if it differs from the default.
113		char homeInstallationLocation[B_PATH_NAME_LENGTH];
114		if (__find_directory(B_USER_CONFIG_DIRECTORY, -1, false,
115					homeInstallationLocation, sizeof(homeInstallationLocation))
116				== B_OK) {
117			_kern_normalize_path(homeInstallationLocation, true,
118				homeInstallationLocation);
119				// failure is OK
120			if (strcmp(homeInstallationLocation,
121						defaultLocations->At(kHomeInstallationLocationIndex))
122					!= 0) {
123				InstallationLocations* locations
124					= new(std::nothrow) InstallationLocations(
125						homeInstallationLocation);
126				if (locations != NULL && locations->IsValid())
127					return locations;
128				delete locations;
129			}
130		}
131
132		atomic_add(&defaultLocations->fReferenceCount, 1);
133		return defaultLocations;
134	}
135
136	void Put()
137	{
138		if (atomic_add(&fReferenceCount, -1) == 1)
139			delete this;
140	}
141
142	const char* At(size_t index) const
143	{
144		return fLocations[index];
145	}
146
147	const char* LocationFor(const char* path, size_t& _index)
148	{
149		for (size_t i = 0; i < kCount; i++) {
150			size_t length = strlen(fLocations[i]);
151			if (strncmp(path, fLocations[i], length) == 0
152				&& (path[length] == '/' || path[length] == '\0')) {
153				_index = i;
154				return fLocations[i];
155			}
156		}
157
158		return NULL;
159	}
160
161private:
162	int32		fReferenceCount;
163	const char*	fLocations[kCount];
164};
165
166
167}	// unnamed namespace
168
169
170/*!	Returns the installation location relative path for the given base directory
171	constant and installation location index. A '%' in the returned path must be
172	replaced by "" for the primary architecture and by "/<arch>" for a secondary
173	architecture.
174 */
175static const char*
176get_relative_directory_path(size_t installationLocationIndex,
177	path_base_directory baseDirectory)
178{
179	switch (baseDirectory) {
180		case B_FIND_PATH_INSTALLATION_LOCATION_DIRECTORY:
181			return "";
182		case B_FIND_PATH_ADD_ONS_DIRECTORY:
183			return "/add-ons%";
184		case B_FIND_PATH_APPS_DIRECTORY:
185			return "/apps";
186		case B_FIND_PATH_BIN_DIRECTORY:
187			return "/bin%";
188		case B_FIND_PATH_BOOT_DIRECTORY:
189			return "/boot";
190		case B_FIND_PATH_CACHE_DIRECTORY:
191			return "/cache";
192		case B_FIND_PATH_DATA_DIRECTORY:
193			return "/data";
194		case B_FIND_PATH_DEVELOP_DIRECTORY:
195			return "/develop";
196		case B_FIND_PATH_DEVELOP_LIB_DIRECTORY:
197			return "/develop/lib%";
198		case B_FIND_PATH_DOCUMENTATION_DIRECTORY:
199			return "/documentation";
200		case B_FIND_PATH_ETC_DIRECTORY:
201			return "/settings/etc";
202		case B_FIND_PATH_FONTS_DIRECTORY:
203			return "/data/fonts";
204		case B_FIND_PATH_HEADERS_DIRECTORY:
205			return "/develop/headers%";
206		case B_FIND_PATH_LIB_DIRECTORY:
207			return "/lib%";
208		case B_FIND_PATH_LOG_DIRECTORY:
209			return "/log";
210		case B_FIND_PATH_MEDIA_NODES_DIRECTORY:
211			return "/add-ons%/media";
212		case B_FIND_PATH_PACKAGES_DIRECTORY:
213			return "/packages";
214		case B_FIND_PATH_PREFERENCES_DIRECTORY:
215			return "/preferences";
216		case B_FIND_PATH_SERVERS_DIRECTORY:
217			return "/servers";
218		case B_FIND_PATH_SETTINGS_DIRECTORY:
219			return "/settings";
220		case B_FIND_PATH_SOUNDS_DIRECTORY:
221			return "/data/sounds";
222		case B_FIND_PATH_SPOOL_DIRECTORY:
223			return "/var/spool";
224		case B_FIND_PATH_TRANSLATORS_DIRECTORY:
225			return "/add-ons%/Translators";
226		case B_FIND_PATH_VAR_DIRECTORY:
227			return "/var";
228
229		case B_FIND_PATH_IMAGE_PATH:
230		case B_FIND_PATH_PACKAGE_PATH:
231		default:
232			return NULL;
233	}
234}
235
236
237static status_t
238create_directory(char* path)
239{
240	// find the first directory that doesn't exist
241	char* slash = path;
242	bool found = false;
243	while (!found && (slash = strchr(slash + 1, '/')) != NULL) {
244		*slash = '\0';
245		struct stat st;
246		if (lstat(path, &st) != 0)
247			break;
248		*slash = '/';
249	}
250
251	if (found)
252		return B_OK;
253
254	// create directories
255	while (slash != NULL) {
256		*slash = '\0';
257		bool created = mkdir(path, 0755);
258		*slash = '/';
259
260		if (!created)
261			return errno;
262
263		slash = strchr(slash + 1, '/');
264	}
265
266	return B_OK;
267}
268
269
270static bool
271is_in_range(const void* pointer, const void* base, size_t size)
272{
273	return pointer >= base && (addr_t)pointer < (addr_t)base + size;
274}
275
276
277static status_t
278find_image(const void* codePointer, image_info& _info)
279{
280	int32 cookie = 0;
281
282	while (get_next_image_info(B_CURRENT_TEAM, &cookie, &_info) == B_OK) {
283		if (codePointer == NULL ? _info.type == B_APP_IMAGE
284				: (is_in_range(codePointer, _info.text, _info.text_size)
285					|| is_in_range(codePointer, _info.data, _info.data_size))) {
286			return B_OK;
287		}
288	}
289
290	return B_ENTRY_NOT_FOUND;
291}
292
293
294static status_t
295copy_path(const char* path, char* buffer, size_t bufferSize)
296{
297	if (strlcpy(buffer, path, bufferSize) >= bufferSize)
298		return B_BUFFER_OVERFLOW;
299	return B_OK;
300}
301
302
303static status_t
304normalize_path(const char* path, char* buffer, size_t bufferSize)
305{
306	status_t error;
307	if (bufferSize >= B_PATH_NAME_LENGTH) {
308		error = _kern_normalize_path(path, true, buffer);
309	} else {
310		char normalizedPath[B_PATH_NAME_LENGTH];
311		error = _kern_normalize_path(path, true, normalizedPath);
312		if (error == B_OK)
313			error = copy_path(path, buffer, bufferSize);
314	}
315
316	if (error != B_OK)
317		return error;
318
319	// make sure the path exists
320	struct stat st;
321	if (lstat(buffer, &st) != 0)
322		return errno;
323
324	return B_OK;
325}
326
327
328static status_t
329normalize_longest_existing_path_prefix(const char* path, char* buffer,
330	size_t bufferSize)
331{
332	if (strlcpy(buffer, path, bufferSize) >= bufferSize)
333		return B_NAME_TOO_LONG;
334
335	// Until we have an existing path, chop off leaf components.
336	for (;;) {
337		struct stat st;
338		if (lstat(buffer, &st) == 0)
339			break;
340
341		// Chop off the leaf, but fail, it it's "..", since then we'd actually
342		// construct a subpath.
343		char* lastSlash = strrchr(buffer, '/');
344		if (lastSlash == NULL || strcmp(lastSlash + 1, "..") == 0)
345			return B_ENTRY_NOT_FOUND;
346
347		*lastSlash = '\0';
348	}
349
350	// normalize the existing prefix path
351	size_t prefixLength = strlen(buffer);
352	status_t error = normalize_path(buffer, buffer, bufferSize);
353	if (error != B_OK)
354		return error;
355
356	// Re-append the non-existent suffix. Remove duplicate slashes and "."
357	// components.
358	const char* bufferEnd = buffer + bufferSize;
359	char* end = buffer + strlen(buffer);
360	const char* remainder = path + prefixLength + 1;
361	while (*remainder != '\0') {
362		// find component start
363		if (*remainder == '/') {
364			remainder++;
365			continue;
366		}
367
368		// find component end
369		const char* componentEnd = strchr(remainder, '/');
370		if (componentEnd == NULL)
371			componentEnd = remainder + strlen(remainder);
372
373		// skip "." components
374		size_t componentLength = componentEnd - remainder;
375		if (componentLength == 1 && *remainder == '.') {
376			remainder++;
377			continue;
378		}
379
380		// append the component
381		if (end + 1 + componentLength >= bufferEnd)
382			return B_BUFFER_OVERFLOW;
383
384		*end++ = '/';
385		memcpy(end, remainder, componentLength);
386		end += componentLength;
387		remainder += componentLength;
388	}
389
390	*end = '\0';
391	return B_OK;
392}
393
394
395static status_t
396get_file_attribute(const char* path, const char* attribute, char* nameBuffer,
397	size_t bufferSize)
398{
399	int fd = fs_open_attr(path, attribute, B_STRING_TYPE,
400		O_RDONLY | O_NOTRAVERSE);
401	if (fd < 0)
402		return errno;
403
404	status_t error = B_OK;
405	ssize_t bytesRead = read(fd, nameBuffer, bufferSize - 1);
406	if (bytesRead < 0)
407		error = bytesRead;
408	else if (bytesRead == 0)
409		error = B_ENTRY_NOT_FOUND;
410	else
411		nameBuffer[bytesRead] = '\0';
412
413	fs_close_attr(fd);
414
415	return error;
416}
417
418
419static status_t
420normalize_dependency(const char* dependency, char* buffer, size_t bufferSize)
421{
422	if (strlcpy(buffer, dependency, bufferSize) >= bufferSize)
423		return B_NAME_TOO_LONG;
424
425	// replace all ':' with '~'
426	char* colon = buffer - 1;
427	while ((colon = strchr(colon + 1, ':')) != NULL)
428		*colon = '~';
429
430	return B_OK;
431}
432
433
434static ssize_t
435process_path(const char* installationLocation, const char* architecture,
436	const char* relativePath, const char* subPath, uint32 flags,
437	char* pathBuffer, size_t bufferSize)
438{
439	// copy the installation location
440	PathBuffer buffer(pathBuffer, bufferSize);
441	buffer.Append(installationLocation);
442
443	// append the relative path, expanding the architecture placeholder
444	if (const char* placeholder = strchr(relativePath, '%')) {
445		buffer.Append(relativePath, placeholder - relativePath);
446
447		if (architecture != NULL) {
448			buffer.Append("/", 1);
449			buffer.Append(architecture);
450		}
451
452		buffer.Append(placeholder + 1);
453	} else
454		buffer.Append(relativePath);
455
456	// append subpath, if given
457	if (subPath != NULL) {
458		buffer.Append("/", 1);
459		buffer.Append(subPath);
460	}
461
462	size_t totalLength = buffer.Length();
463	if (totalLength >= bufferSize)
464		return B_BUFFER_OVERFLOW;
465
466	// handle the flags
467	char* path = pathBuffer;
468
469	status_t error = B_OK;
470	if ((flags & B_FIND_PATH_CREATE_DIRECTORY) != 0) {
471		// create the directory
472		error = create_directory(path);
473	} else if ((flags & B_FIND_PATH_CREATE_PARENT_DIRECTORY) != 0) {
474		// create the parent directory
475		char* lastSlash = strrchr(path, '/');
476		*lastSlash = '\0';
477		error = create_directory(path);
478		*lastSlash = '/';
479	}
480
481	if (error != B_OK)
482		return error;
483
484	if ((flags & B_FIND_PATH_EXISTING_ONLY) != 0) {
485		// check if the entry exists
486		struct stat st;
487		if (lstat(path, &st) != 0)
488			return 0;
489	}
490
491	return totalLength + 1;
492}
493
494
495status_t
496internal_path_for_path(char* referencePath, size_t referencePathSize,
497	const char* dependency, const char* architecture,
498	path_base_directory baseDirectory, const char* subPath, uint32 flags,
499	char* pathBuffer, size_t bufferSize)
500{
501	if (strcmp(architecture, __get_primary_architecture()) == 0)
502		architecture = NULL;
503
504	// resolve dependency
505	char packageName[B_FILE_NAME_LENGTH];
506		// Temporarily used here, permanently used below where
507		// B_FIND_PATH_PACKAGE_PATH is handled.
508	if (dependency != NULL) {
509		// get the versioned package name
510		status_t error = get_file_attribute(referencePath, "SYS:PACKAGE",
511			packageName, sizeof(packageName));
512		if (error != B_OK)
513			return error;
514
515		// normalize the dependency name
516		char normalizedDependency[B_FILE_NAME_LENGTH];
517		error = normalize_dependency(dependency, normalizedDependency,
518			sizeof(normalizedDependency));
519		if (error != B_OK)
520			return error;
521
522		// Compute the path of the dependency symlink. This will yield the
523		// installation location path when normalized.
524		if (snprintf(referencePath, referencePathSize,
525					kSystemPackageLinksDirectory "/%s/%s", packageName,
526					normalizedDependency)
527				>= (ssize_t)referencePathSize) {
528			return B_BUFFER_OVERFLOW;
529		}
530	}
531
532	// handle B_FIND_PATH_IMAGE_PATH
533	if (baseDirectory == B_FIND_PATH_IMAGE_PATH)
534		return copy_path(referencePath, pathBuffer, bufferSize);
535
536	// Handle B_FIND_PATH_PACKAGE_PATH: get the package file name and
537	// simply adjust our arguments to look the package file up in the packages
538	// directory.
539	if (baseDirectory == B_FIND_PATH_PACKAGE_PATH) {
540		status_t error = get_file_attribute(referencePath, "SYS:PACKAGE_FILE",
541			packageName, sizeof(packageName));
542		if (error != B_OK)
543			return error;
544
545		dependency = NULL;
546		subPath = packageName;
547		baseDirectory = B_FIND_PATH_PACKAGES_DIRECTORY;
548		flags = B_FIND_PATH_EXISTING_ONLY;
549	}
550
551	// normalize
552	status_t error = normalize_path(referencePath, referencePath,
553		referencePathSize);
554	if (error != B_OK)
555		return error;
556
557	// get the installation location
558	InstallationLocations* installationLocations = InstallationLocations::Get();
559	MethodDeleter<InstallationLocations, void, &InstallationLocations::Put>
560		installationLocationsDeleter(installationLocations);
561
562	size_t installationLocationIndex;
563	const char* installationLocation = installationLocations->LocationFor(
564		referencePath, installationLocationIndex);
565	if (installationLocation == NULL)
566		return B_ENTRY_NOT_FOUND;
567
568	// get base dir and process the path
569	const char* relativePath = get_relative_directory_path(
570		installationLocationIndex, baseDirectory);
571	if (relativePath == NULL)
572		return B_BAD_VALUE;
573
574	ssize_t pathSize = process_path(installationLocation, architecture,
575		relativePath, subPath, flags, pathBuffer, bufferSize);
576	if (pathSize <= 0)
577		return pathSize == 0 ? B_ENTRY_NOT_FOUND : pathSize;
578	return B_OK;
579}
580
581
582// #pragma mark -
583
584
585status_t
586__find_path(const void* codePointer, path_base_directory baseDirectory,
587	const char* subPath, char* pathBuffer, size_t bufferSize)
588{
589	return __find_path_etc(codePointer, NULL, NULL, baseDirectory, subPath, 0,
590		pathBuffer, bufferSize);
591}
592
593
594status_t
595__find_path_etc(const void* codePointer, const char* dependency,
596	const char* architecture, path_base_directory baseDirectory,
597	const char* subPath, uint32 flags, char* pathBuffer, size_t bufferSize)
598{
599	if (pathBuffer == NULL)
600		return B_BAD_VALUE;
601
602	// resolve codePointer to image info
603	image_info imageInfo;
604	status_t error = find_image(codePointer, imageInfo);
605	if (error != B_OK)
606		return error;
607
608	if (architecture == NULL)
609		architecture = __get_architecture();
610
611	return internal_path_for_path(imageInfo.name, sizeof(imageInfo.name),
612		dependency, architecture, baseDirectory, subPath, flags, pathBuffer,
613		bufferSize);
614}
615
616
617status_t
618__find_path_for_path(const char* path, path_base_directory baseDirectory,
619	const char* subPath, char* pathBuffer, size_t bufferSize)
620{
621	return __find_path_for_path_etc(path, NULL, NULL, baseDirectory, subPath, 0,
622		pathBuffer, bufferSize);
623}
624
625
626status_t
627__find_path_for_path_etc(const char* path, const char* dependency,
628	const char* architecture, path_base_directory baseDirectory,
629	const char* subPath, uint32 flags, char* pathBuffer, size_t bufferSize)
630{
631	if (baseDirectory == B_FIND_PATH_IMAGE_PATH)
632		return B_BAD_VALUE;
633
634	char referencePath[B_PATH_NAME_LENGTH];
635	if (strlcpy(referencePath, path, sizeof(referencePath))
636			>= sizeof(referencePath)) {
637		return B_NAME_TOO_LONG;
638	}
639
640	if (architecture == NULL)
641		architecture = __guess_architecture_for_path(path);
642
643	return internal_path_for_path(referencePath, sizeof(referencePath),
644		dependency, architecture, baseDirectory, subPath, flags, pathBuffer,
645		bufferSize);
646}
647
648
649status_t
650__find_paths(path_base_directory baseDirectory, const char* subPath,
651	char*** _paths, size_t* _pathCount)
652{
653	return __find_paths_etc(NULL, baseDirectory, subPath, 0, _paths,
654		_pathCount);
655}
656
657
658status_t
659__find_paths_etc(const char* architecture, path_base_directory baseDirectory,
660	const char* subPath, uint32 flags, char*** _paths, size_t* _pathCount)
661{
662	if (_paths == NULL || _pathCount == NULL)
663		return B_BAD_VALUE;
664
665	// Analyze architecture. If NULL, use the caller's architecture. If the
666	// effective architecture is the primary one, set architecture to NULL to
667	// indicate that we don't need to insert an architecture subdirectory
668	// component.
669	if (architecture == NULL)
670		architecture = __get_architecture();
671	if (strcmp(architecture, __get_primary_architecture()) == 0)
672		architecture = NULL;
673	size_t architectureSize = architecture != NULL
674		? strlen(architecture) + 1 : 0;
675
676	size_t subPathLength = subPath != NULL ? strlen(subPath) + 1 : 0;
677
678	// get the installation locations
679	InstallationLocations* installationLocations = InstallationLocations::Get();
680	MethodDeleter<InstallationLocations, void, &InstallationLocations::Put>
681		installationLocationsDeleter(installationLocations);
682
683	// Get the relative paths and compute the total size to allocate.
684	const char* relativePaths[InstallationLocations::kCount];
685	size_t totalSize = 0;
686
687	for (size_t i = 0; i < InstallationLocations::kCount; i++) {
688		if (((flags & B_FIND_PATHS_USER_ONLY) != 0
689				&& !installationLocations->IsUserIndex(i))
690			|| ((flags & B_FIND_PATHS_SYSTEM_ONLY) != 0
691				&& !installationLocations->IsSystemIndex(i)))
692			continue;
693
694		relativePaths[i] = get_relative_directory_path(i, baseDirectory);
695		if (relativePaths[i] == NULL)
696			return B_BAD_VALUE;
697
698		totalSize += strlen(installationLocations->At(i))
699			+ strlen(relativePaths[i]) + subPathLength + 1;
700		if (strchr(relativePaths[i], '%') != NULL)
701			totalSize += architectureSize - 1;
702	}
703
704	// allocate storage
705	char** paths = (char**)malloc(sizeof(char*) * InstallationLocations::kCount
706		+ totalSize);
707	if (paths == NULL)
708		return B_NO_MEMORY;
709	MemoryDeleter pathsDeleter(paths);
710
711	// construct and process the paths
712	size_t count = 0;
713	char* pathBuffer = (char*)(paths + InstallationLocations::kCount);
714	const char* pathBufferEnd = pathBuffer + totalSize;
715	for (size_t i = 0; i < InstallationLocations::kCount; i++) {
716		if (((flags & B_FIND_PATHS_USER_ONLY) != 0
717				&& !installationLocations->IsUserIndex(i))
718			|| ((flags & B_FIND_PATHS_SYSTEM_ONLY) != 0
719				&& !installationLocations->IsSystemIndex(i)))
720			continue;
721
722		ssize_t pathSize = process_path(installationLocations->At(i),
723			architecture, relativePaths[i], subPath, flags, pathBuffer,
724			pathBufferEnd - pathBuffer);
725		if (pathSize < 0)
726			return pathSize;
727		if (pathSize > 0) {
728			paths[count++] = pathBuffer;
729			pathBuffer += pathSize;
730		}
731	}
732
733	if (count == 0)
734		return B_ENTRY_NOT_FOUND;
735
736	*_paths = paths;
737	*_pathCount = count;
738	pathsDeleter.Detach();
739
740	return B_OK;
741}
742
743
744const char*
745__guess_secondary_architecture_from_path(const char* path,
746	const char* const* secondaryArchitectures,
747	size_t secondaryArchitectureCount)
748{
749	// Get the longest existing prefix path and normalize it.
750	char prefix[B_PATH_NAME_LENGTH];
751	if (normalize_longest_existing_path_prefix(path, prefix, sizeof(prefix))
752			!= B_OK) {
753		return NULL;
754	}
755
756	// get an installation location relative path
757	InstallationLocations* installationLocations = InstallationLocations::Get();
758	MethodDeleter<InstallationLocations, void, &InstallationLocations::Put>
759		installationLocationsDeleter(installationLocations);
760
761	size_t installationLocationIndex;
762	const char* installationLocation = installationLocations->LocationFor(
763		prefix, installationLocationIndex);
764	if (installationLocation == NULL)
765		return NULL;
766
767	const char* relativePath = prefix + strlen(installationLocation);
768	if (relativePath[0] != '/')
769		return NULL;
770
771	// Iterate through the known paths that would indicate a secondary
772	// architecture and try to match them with our given path.
773	for (size_t i = 0; i < kArchitectureSpecificBaseDirectoryCount; i++) {
774		const char* basePath = get_relative_directory_path(
775			installationLocationIndex, kArchitectureSpecificBaseDirectories[i]);
776		const char* placeholder = strchr(basePath, '%');
777		if (placeholder == NULL)
778			continue;
779
780		// match the part up to the architecture placeholder
781		size_t prefixLength = placeholder - basePath;
782		if (strncmp(relativePath, basePath, prefixLength) != 0
783			|| relativePath[prefixLength] != '/') {
784			continue;
785		}
786
787		// match the architecture
788		const char* architecturePart = relativePath + prefixLength + 1;
789		for (size_t k = 0; k < secondaryArchitectureCount; k++) {
790			const char* architecture = secondaryArchitectures[k];
791			size_t architectureLength = strlen(architecture);
792			if (strncmp(architecturePart, architecture, architectureLength) == 0
793				&& (architecturePart[architectureLength] == '/'
794					|| architecturePart[architectureLength] == '\0')) {
795				return architecture;
796			}
797		}
798	}
799
800	return NULL;
801}
802
803
804B_DEFINE_WEAK_ALIAS(__find_path, find_path);
805B_DEFINE_WEAK_ALIAS(__find_path_etc, find_path_etc);
806B_DEFINE_WEAK_ALIAS(__find_path_for_path, find_path_for_path);
807B_DEFINE_WEAK_ALIAS(__find_path_for_path_etc, find_path_for_path_etc);
808B_DEFINE_WEAK_ALIAS(__find_paths, find_paths);
809B_DEFINE_WEAK_ALIAS(__find_paths_etc, find_paths_etc);
810