headers/bsd: Add sys/queue.h.
[haiku.git] / src / system / libroot / os / find_paths.cpp
blob2660cd12c96bbbc04930b7897cad80c7e5ef2278
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 */
8 #include <find_directory_private.h>
10 #include <errno.h>
11 #include <stdio.h>
12 #include <string.h>
13 #include <sys/stat.h>
15 #include <algorithm>
17 #include <fs_attr.h>
19 #include <architecture_private.h>
20 #include <AutoDeleter.h>
21 #include <directories.h>
22 #include <syscalls.h>
24 #include "PathBuffer.h"
27 static size_t kHomeInstallationLocationIndex = 1;
29 static 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,
36 static size_t kArchitectureSpecificBaseDirectoryCount =
37 sizeof(kArchitectureSpecificBaseDirectories)
38 / sizeof(kArchitectureSpecificBaseDirectories[0]);
41 namespace {
44 struct InstallationLocations {
45 public:
46 static const size_t kCount = 4;
48 public:
49 InstallationLocations()
51 fReferenceCount(1)
53 fLocations[0] = kUserNonpackagedDirectory;
54 fLocations[1] = kUserConfigDirectory;
55 fLocations[2] = kSystemNonpackagedDirectory;
56 fLocations[3] = kSystemDirectory;
59 InstallationLocations(const char* home)
61 fReferenceCount(1)
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);
72 fLocations[1] = strdup(home);
74 fLocations[2] = kSystemNonpackagedDirectory;
75 fLocations[3] = kSystemDirectory;
78 ~InstallationLocations()
80 free(const_cast<char*>(fLocations[0]));
81 free(const_cast<char*>(fLocations[1]));
84 bool IsValid() const
86 return fLocations[0] != NULL && fLocations[1] != NULL;
89 bool IsUserIndex(size_t index) const
91 return index==0 || index==1;
94 bool IsSystemIndex(size_t index) const
96 return index==2 || index==3;
99 static InstallationLocations* Default()
101 static char sBuffer[sizeof(InstallationLocations)];
102 static InstallationLocations* sDefaultLocations
103 = new(&sBuffer) InstallationLocations;
104 return sDefaultLocations;
107 static InstallationLocations* Get()
109 InstallationLocations* defaultLocations = Default();
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;
132 atomic_add(&defaultLocations->fReferenceCount, 1);
133 return defaultLocations;
136 void Put()
138 if (atomic_add(&fReferenceCount, -1) == 1)
139 delete this;
142 const char* At(size_t index) const
144 return fLocations[index];
147 const char* LocationFor(const char* path, size_t& _index)
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];
158 return NULL;
161 private:
162 int32 fReferenceCount;
163 const char* fLocations[kCount];
167 } // unnamed namespace
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.
175 static const char*
176 get_relative_directory_path(size_t installationLocationIndex,
177 path_base_directory baseDirectory)
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 installationLocationIndex == kHomeInstallationLocationIndex
220 ? "/settings/global" : "/settings";
221 case B_FIND_PATH_SOUNDS_DIRECTORY:
222 return "/data/sounds";
223 case B_FIND_PATH_SPOOL_DIRECTORY:
224 return "/var/spool";
225 case B_FIND_PATH_TRANSLATORS_DIRECTORY:
226 return "/add-ons%/Translators";
227 case B_FIND_PATH_VAR_DIRECTORY:
228 return "/var";
230 case B_FIND_PATH_IMAGE_PATH:
231 case B_FIND_PATH_PACKAGE_PATH:
232 default:
233 return NULL;
238 static status_t
239 create_directory(char* path)
241 // find the first directory that doesn't exist
242 char* slash = path;
243 bool found = false;
244 while (!found && (slash = strchr(slash + 1, '/')) != NULL) {
245 *slash = '\0';
246 struct stat st;
247 if (lstat(path, &st) != 0)
248 break;
249 *slash = '/';
252 if (found)
253 return B_OK;
255 // create directories
256 while (slash != NULL) {
257 *slash = '\0';
258 bool created = mkdir(path, 0755);
259 *slash = '/';
261 if (!created)
262 return errno;
264 slash = strchr(slash + 1, '/');
267 return B_OK;
271 static bool
272 is_in_range(const void* pointer, const void* base, size_t size)
274 return pointer >= base && (addr_t)pointer < (addr_t)base + size;
278 static status_t
279 find_image(const void* codePointer, image_info& _info)
281 int32 cookie = 0;
283 while (get_next_image_info(B_CURRENT_TEAM, &cookie, &_info) == B_OK) {
284 if (codePointer == NULL ? _info.type == B_APP_IMAGE
285 : (is_in_range(codePointer, _info.text, _info.text_size)
286 || is_in_range(codePointer, _info.data, _info.data_size))) {
287 return B_OK;
291 return B_ENTRY_NOT_FOUND;
295 static status_t
296 copy_path(const char* path, char* buffer, size_t bufferSize)
298 if (strlcpy(buffer, path, bufferSize) >= bufferSize)
299 return B_BUFFER_OVERFLOW;
300 return B_OK;
304 static status_t
305 normalize_path(const char* path, char* buffer, size_t bufferSize)
307 status_t error;
308 if (bufferSize >= B_PATH_NAME_LENGTH) {
309 error = _kern_normalize_path(path, true, buffer);
310 } else {
311 char normalizedPath[B_PATH_NAME_LENGTH];
312 error = _kern_normalize_path(path, true, normalizedPath);
313 if (error == B_OK)
314 error = copy_path(path, buffer, bufferSize);
317 if (error != B_OK)
318 return error;
320 // make sure the path exists
321 struct stat st;
322 if (lstat(buffer, &st) != 0)
323 return errno;
325 return B_OK;
329 static status_t
330 normalize_longest_existing_path_prefix(const char* path, char* buffer,
331 size_t bufferSize)
333 if (strlcpy(buffer, path, bufferSize) >= bufferSize)
334 return B_NAME_TOO_LONG;
336 // Until we have an existing path, chop off leaf components.
337 for (;;) {
338 struct stat st;
339 if (lstat(buffer, &st) == 0)
340 break;
342 // Chop off the leaf, but fail, it it's "..", since then we'd actually
343 // construct a subpath.
344 char* lastSlash = strrchr(buffer, '/');
345 if (lastSlash == NULL || strcmp(lastSlash + 1, "..") == 0)
346 return B_ENTRY_NOT_FOUND;
348 *lastSlash = '\0';
351 // normalize the existing prefix path
352 size_t prefixLength = strlen(buffer);
353 status_t error = normalize_path(buffer, buffer, bufferSize);
354 if (error != B_OK)
355 return error;
357 // Re-append the non-existent suffix. Remove duplicate slashes and "."
358 // components.
359 const char* bufferEnd = buffer + bufferSize;
360 char* end = buffer + strlen(buffer);
361 const char* remainder = path + prefixLength + 1;
362 while (*remainder != '\0') {
363 // find component start
364 if (*remainder == '/') {
365 remainder++;
366 continue;
369 // find component end
370 const char* componentEnd = strchr(remainder, '/');
371 if (componentEnd == NULL)
372 componentEnd = remainder + strlen(remainder);
374 // skip "." components
375 size_t componentLength = componentEnd - remainder;
376 if (componentLength == 1 && *remainder == '.') {
377 remainder++;
378 continue;
381 // append the component
382 if (end + 1 + componentLength >= bufferEnd)
383 return B_BUFFER_OVERFLOW;
385 *end++ = '/';
386 memcpy(end, remainder, componentLength);
387 end += componentLength;
388 remainder += componentLength;
391 *end = '\0';
392 return B_OK;
396 static status_t
397 get_file_attribute(const char* path, const char* attribute, char* nameBuffer,
398 size_t bufferSize)
400 int fd = fs_open_attr(path, attribute, B_STRING_TYPE,
401 O_RDONLY | O_NOTRAVERSE);
402 if (fd < 0)
403 return errno;
405 status_t error = B_OK;
406 ssize_t bytesRead = read(fd, nameBuffer, bufferSize - 1);
407 if (bytesRead < 0)
408 error = bytesRead;
409 else if (bytesRead == 0)
410 error = B_ENTRY_NOT_FOUND;
411 else
412 nameBuffer[bytesRead] = '\0';
414 fs_close_attr(fd);
416 return error;
420 static status_t
421 normalize_dependency(const char* dependency, char* buffer, size_t bufferSize)
423 if (strlcpy(buffer, dependency, bufferSize) >= bufferSize)
424 return B_NAME_TOO_LONG;
426 // replace all ':' with '~'
427 char* colon = buffer - 1;
428 while ((colon = strchr(colon + 1, ':')) != NULL)
429 *colon = '~';
431 return B_OK;
435 static ssize_t
436 process_path(const char* installationLocation, const char* architecture,
437 const char* relativePath, const char* subPath, uint32 flags,
438 char* pathBuffer, size_t bufferSize)
440 // copy the installation location
441 PathBuffer buffer(pathBuffer, bufferSize);
442 buffer.Append(installationLocation);
444 // append the relative path, expanding the architecture placeholder
445 if (const char* placeholder = strchr(relativePath, '%')) {
446 buffer.Append(relativePath, placeholder - relativePath);
448 if (architecture != NULL) {
449 buffer.Append("/", 1);
450 buffer.Append(architecture);
453 buffer.Append(placeholder + 1);
454 } else
455 buffer.Append(relativePath);
457 // append subpath, if given
458 if (subPath != NULL) {
459 buffer.Append("/", 1);
460 buffer.Append(subPath);
463 size_t totalLength = buffer.Length();
464 if (totalLength >= bufferSize)
465 return B_BUFFER_OVERFLOW;
467 // handle the flags
468 char* path = pathBuffer;
470 status_t error = B_OK;
471 if ((flags & B_FIND_PATH_CREATE_DIRECTORY) != 0) {
472 // create the directory
473 error = create_directory(path);
474 } else if ((flags & B_FIND_PATH_CREATE_PARENT_DIRECTORY) != 0) {
475 // create the parent directory
476 char* lastSlash = strrchr(path, '/');
477 *lastSlash = '\0';
478 error = create_directory(path);
479 *lastSlash = '/';
482 if (error != B_OK)
483 return error;
485 if ((flags & B_FIND_PATH_EXISTING_ONLY) != 0) {
486 // check if the entry exists
487 struct stat st;
488 if (lstat(path, &st) != 0)
489 return 0;
492 return totalLength + 1;
496 status_t
497 internal_path_for_path(char* referencePath, size_t referencePathSize,
498 const char* dependency, const char* architecture,
499 path_base_directory baseDirectory, const char* subPath, uint32 flags,
500 char* pathBuffer, size_t bufferSize)
502 if (strcmp(architecture, __get_primary_architecture()) == 0)
503 architecture = NULL;
505 // resolve dependency
506 char packageName[B_FILE_NAME_LENGTH];
507 // Temporarily used here, permanently used below where
508 // B_FIND_PATH_PACKAGE_PATH is handled.
509 if (dependency != NULL) {
510 // get the versioned package name
511 status_t error = get_file_attribute(referencePath, "SYS:PACKAGE",
512 packageName, sizeof(packageName));
513 if (error != B_OK)
514 return error;
516 // normalize the dependency name
517 char normalizedDependency[B_FILE_NAME_LENGTH];
518 error = normalize_dependency(dependency, normalizedDependency,
519 sizeof(normalizedDependency));
520 if (error != B_OK)
521 return error;
523 // Compute the path of the dependency symlink. This will yield the
524 // installation location path when normalized.
525 if (snprintf(referencePath, referencePathSize,
526 kSystemPackageLinksDirectory "/%s/%s", packageName,
527 normalizedDependency)
528 >= (ssize_t)referencePathSize) {
529 return B_BUFFER_OVERFLOW;
533 // handle B_FIND_PATH_IMAGE_PATH
534 if (baseDirectory == B_FIND_PATH_IMAGE_PATH)
535 return copy_path(referencePath, pathBuffer, bufferSize);
537 // Handle B_FIND_PATH_PACKAGE_PATH: get the package file name and
538 // simply adjust our arguments to look the package file up in the packages
539 // directory.
540 if (baseDirectory == B_FIND_PATH_PACKAGE_PATH) {
541 status_t error = get_file_attribute(referencePath, "SYS:PACKAGE_FILE",
542 packageName, sizeof(packageName));
543 if (error != B_OK)
544 return error;
546 dependency = NULL;
547 subPath = packageName;
548 baseDirectory = B_FIND_PATH_PACKAGES_DIRECTORY;
549 flags = B_FIND_PATH_EXISTING_ONLY;
552 // normalize
553 status_t error = normalize_path(referencePath, referencePath,
554 referencePathSize);
555 if (error != B_OK)
556 return error;
558 // get the installation location
559 InstallationLocations* installationLocations = InstallationLocations::Get();
560 MethodDeleter<InstallationLocations> installationLocationsDeleter(
561 installationLocations, &InstallationLocations::Put);
563 size_t installationLocationIndex;
564 const char* installationLocation = installationLocations->LocationFor(
565 referencePath, installationLocationIndex);
566 if (installationLocation == NULL)
567 return B_ENTRY_NOT_FOUND;
569 // get base dir and process the path
570 const char* relativePath = get_relative_directory_path(
571 installationLocationIndex, baseDirectory);
572 if (relativePath == NULL)
573 return B_BAD_VALUE;
575 ssize_t pathSize = process_path(installationLocation, architecture,
576 relativePath, subPath, flags, pathBuffer, bufferSize);
577 if (pathSize <= 0)
578 return pathSize == 0 ? B_ENTRY_NOT_FOUND : pathSize;
579 return B_OK;
583 // #pragma mark -
586 status_t
587 __find_path(const void* codePointer, path_base_directory baseDirectory,
588 const char* subPath, char* pathBuffer, size_t bufferSize)
590 return __find_path_etc(codePointer, NULL, NULL, baseDirectory, subPath, 0,
591 pathBuffer, bufferSize);
595 status_t
596 __find_path_etc(const void* codePointer, const char* dependency,
597 const char* architecture, path_base_directory baseDirectory,
598 const char* subPath, uint32 flags, char* pathBuffer, size_t bufferSize)
600 if (pathBuffer == NULL)
601 return B_BAD_VALUE;
603 // resolve codePointer to image info
604 image_info imageInfo;
605 status_t error = find_image(codePointer, imageInfo);
606 if (error != B_OK)
607 return error;
609 if (architecture == NULL)
610 architecture = __get_architecture();
612 return internal_path_for_path(imageInfo.name, sizeof(imageInfo.name),
613 dependency, architecture, baseDirectory, subPath, flags, pathBuffer,
614 bufferSize);
618 status_t
619 __find_path_for_path(const char* path, path_base_directory baseDirectory,
620 const char* subPath, char* pathBuffer, size_t bufferSize)
622 return __find_path_for_path_etc(path, NULL, NULL, baseDirectory, subPath, 0,
623 pathBuffer, bufferSize);
627 status_t
628 __find_path_for_path_etc(const char* path, const char* dependency,
629 const char* architecture, path_base_directory baseDirectory,
630 const char* subPath, uint32 flags, char* pathBuffer, size_t bufferSize)
632 if (baseDirectory == B_FIND_PATH_IMAGE_PATH)
633 return B_BAD_VALUE;
635 char referencePath[B_PATH_NAME_LENGTH];
636 if (strlcpy(referencePath, path, sizeof(referencePath))
637 >= sizeof(referencePath)) {
638 return B_NAME_TOO_LONG;
641 if (architecture == NULL)
642 architecture = __guess_architecture_for_path(path);
644 return internal_path_for_path(referencePath, sizeof(referencePath),
645 dependency, architecture, baseDirectory, subPath, flags, pathBuffer,
646 bufferSize);
650 status_t
651 __find_paths(path_base_directory baseDirectory, const char* subPath,
652 char*** _paths, size_t* _pathCount)
654 return __find_paths_etc(NULL, baseDirectory, subPath, 0, _paths,
655 _pathCount);
659 status_t
660 __find_paths_etc(const char* architecture, path_base_directory baseDirectory,
661 const char* subPath, uint32 flags, char*** _paths, size_t* _pathCount)
663 if (_paths == NULL || _pathCount == NULL)
664 return B_BAD_VALUE;
666 // Analyze architecture. If NULL, use the caller's architecture. If the
667 // effective architecture is the primary one, set architecture to NULL to
668 // indicate that we don't need to insert an architecture subdirectory
669 // component.
670 if (architecture == NULL)
671 architecture = __get_architecture();
672 if (strcmp(architecture, __get_primary_architecture()) == 0)
673 architecture = NULL;
674 size_t architectureSize = architecture != NULL
675 ? strlen(architecture) + 1 : 0;
677 size_t subPathLength = subPath != NULL ? strlen(subPath) + 1 : 0;
679 // get the installation locations
680 InstallationLocations* installationLocations = InstallationLocations::Get();
681 MethodDeleter<InstallationLocations> installationLocationsDeleter(
682 installationLocations, &InstallationLocations::Put);
684 // Get the relative paths and compute the total size to allocate.
685 const char* relativePaths[InstallationLocations::kCount];
686 size_t totalSize = 0;
688 for (size_t i = 0; i < InstallationLocations::kCount; i++) {
689 if (((flags & B_FIND_PATHS_USER_ONLY) != 0
690 && !installationLocations->IsUserIndex(i))
691 || ((flags & B_FIND_PATHS_SYSTEM_ONLY) != 0
692 && !installationLocations->IsSystemIndex(i)))
693 continue;
695 relativePaths[i] = get_relative_directory_path(i, baseDirectory);
696 if (relativePaths[i] == NULL)
697 return B_BAD_VALUE;
699 totalSize += strlen(installationLocations->At(i))
700 + strlen(relativePaths[i]) + subPathLength + 1;
701 if (strchr(relativePaths[i], '%') != NULL)
702 totalSize += architectureSize - 1;
705 // allocate storage
706 char** paths = (char**)malloc(sizeof(char*) * InstallationLocations::kCount
707 + totalSize);
708 if (paths == NULL)
709 return B_NO_MEMORY;
710 MemoryDeleter pathsDeleter(paths);
712 // construct and process the paths
713 size_t count = 0;
714 char* pathBuffer = (char*)(paths + InstallationLocations::kCount);
715 const char* pathBufferEnd = pathBuffer + totalSize;
716 for (size_t i = 0; i < InstallationLocations::kCount; i++) {
717 if (((flags & B_FIND_PATHS_USER_ONLY) != 0
718 && !installationLocations->IsUserIndex(i))
719 || ((flags & B_FIND_PATHS_SYSTEM_ONLY) != 0
720 && !installationLocations->IsSystemIndex(i)))
721 continue;
723 ssize_t pathSize = process_path(installationLocations->At(i),
724 architecture, relativePaths[i], subPath, flags, pathBuffer,
725 pathBufferEnd - pathBuffer);
726 if (pathSize < 0)
727 return pathSize;
728 if (pathSize > 0) {
729 paths[count++] = pathBuffer;
730 pathBuffer += pathSize;
734 if (count == 0)
735 return B_ENTRY_NOT_FOUND;
737 *_paths = paths;
738 *_pathCount = count;
739 pathsDeleter.Detach();
741 return B_OK;
745 const char*
746 __guess_secondary_architecture_from_path(const char* path,
747 const char* const* secondaryArchitectures,
748 size_t secondaryArchitectureCount)
750 // Get the longest existing prefix path and normalize it.
751 char prefix[B_PATH_NAME_LENGTH];
752 if (normalize_longest_existing_path_prefix(path, prefix, sizeof(prefix))
753 != B_OK) {
754 return NULL;
757 // get an installation location relative path
758 InstallationLocations* installationLocations = InstallationLocations::Get();
759 MethodDeleter<InstallationLocations> installationLocationsDeleter(
760 installationLocations, &InstallationLocations::Put);
762 size_t installationLocationIndex;
763 const char* installationLocation = installationLocations->LocationFor(
764 prefix, installationLocationIndex);
765 if (installationLocation == NULL)
766 return NULL;
768 const char* relativePath = prefix + strlen(installationLocation);
769 if (relativePath[0] != '/')
770 return NULL;
772 // Iterate through the known paths that would indicate a secondary
773 // architecture and try to match them with our given path.
774 for (size_t i = 0; i < kArchitectureSpecificBaseDirectoryCount; i++) {
775 const char* basePath = get_relative_directory_path(
776 installationLocationIndex, kArchitectureSpecificBaseDirectories[i]);
777 const char* placeholder = strchr(basePath, '%');
778 if (placeholder == NULL)
779 continue;
781 // match the part up to the architecture placeholder
782 size_t prefixLength = placeholder - basePath;
783 if (strncmp(relativePath, basePath, prefixLength) != 0
784 || relativePath[prefixLength] != '/') {
785 continue;
788 // match the architecture
789 const char* architecturePart = relativePath + prefixLength + 1;
790 for (size_t k = 0; k < secondaryArchitectureCount; k++) {
791 const char* architecture = secondaryArchitectures[k];
792 size_t architectureLength = strlen(architecture);
793 if (strncmp(architecturePart, architecture, architectureLength) == 0
794 && (architecturePart[architectureLength] == '/'
795 || architecturePart[architectureLength] == '\0')) {
796 return architecture;
801 return NULL;
805 B_DEFINE_WEAK_ALIAS(__find_path, find_path);
806 B_DEFINE_WEAK_ALIAS(__find_path_etc, find_path_etc);
807 B_DEFINE_WEAK_ALIAS(__find_path_for_path, find_path_for_path);
808 B_DEFINE_WEAK_ALIAS(__find_path_for_path_etc, find_path_for_path_etc);
809 B_DEFINE_WEAK_ALIAS(__find_paths, find_paths);
810 B_DEFINE_WEAK_ALIAS(__find_paths_etc, find_paths_etc);