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