| 1 | // Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file |
| 2 | // for details. All rights reserved. Use of this source code is governed by a |
| 3 | // BSD-style license that can be found in the LICENSE file. |
| 4 | |
| 5 | #include "bin/exe_utils.h" |
| 6 | |
| 7 | #include "bin/directory.h" |
| 8 | #include "bin/file.h" |
| 9 | #include "bin/platform.h" |
| 10 | #include "platform/utils.h" |
| 11 | |
| 12 | namespace dart { |
| 13 | namespace bin { |
| 14 | |
| 15 | static bool StartsWithPathSeparator(const char* path, |
| 16 | const char* sep, |
| 17 | intptr_t sep_length) { |
| 18 | return (strncmp(path, sep, sep_length) == 0 |
| 19 | #if defined(HOST_OS_WINDOWS) |
| 20 | // TODO(aam): GetExecutableName doesn't work reliably on Windows, |
| 21 | || *path == '/' |
| 22 | #endif |
| 23 | ); |
| 24 | } |
| 25 | |
| 26 | // Returns the directory portion of a given path. |
| 27 | // |
| 28 | // If dir is NULL, the result must be freed by the caller. Otherwise, the |
| 29 | // result is copied into dir. |
| 30 | static char* GetDirectoryFromPath(const char* path, char* dir) { |
| 31 | const char* sep = File::PathSeparator(); |
| 32 | const intptr_t sep_length = strlen(sep); |
| 33 | intptr_t path_len = strlen(path); |
| 34 | |
| 35 | for (intptr_t i = path_len - 1; i >= 0; --i) { |
| 36 | const char* str = path + i; |
| 37 | if (StartsWithPathSeparator(str, sep, sep_length)) { |
| 38 | if (dir != nullptr) { |
| 39 | strncpy(dir, path, i); |
| 40 | dir[i] = '\0'; |
| 41 | return dir; |
| 42 | } else { |
| 43 | return Utils::StrNDup(path, i + 1); |
| 44 | } |
| 45 | } |
| 46 | } |
| 47 | return nullptr; |
| 48 | } |
| 49 | |
| 50 | // Returns the file portion of a given path. Returned string is either |
| 51 | // `path` if no path separators are found or `path + separator_loc + sep_length` |
| 52 | // if a separator is found. |
| 53 | static const char* GetFileNameFromPath(const char* path) { |
| 54 | const char* sep = File::PathSeparator(); |
| 55 | const intptr_t sep_length = strlen(sep); |
| 56 | intptr_t path_len = strlen(path); |
| 57 | |
| 58 | for (intptr_t i = path_len - 1; i >= 0; --i) { |
| 59 | const char* str = path + i; |
| 60 | if (StartsWithPathSeparator(str, sep, sep_length)) { |
| 61 | return str + sep_length; |
| 62 | } |
| 63 | } |
| 64 | // No path separators, assume that path is a file name. |
| 65 | return path; |
| 66 | } |
| 67 | |
| 68 | Utils::CStringUniquePtr EXEUtils::GetDirectoryPrefixFromExeName() { |
| 69 | const char* name = nullptr; |
| 70 | const int kTargetSize = PATH_MAX; |
| 71 | char target[kTargetSize]; |
| 72 | intptr_t target_size = |
| 73 | Platform::ResolveExecutablePathInto(target, kTargetSize); |
| 74 | if (target_size > 0 && target_size < kTargetSize - 1) { |
| 75 | target[target_size] = 0; |
| 76 | name = target; |
| 77 | } |
| 78 | if (name == nullptr) { |
| 79 | name = Platform::GetExecutableName(); |
| 80 | target_size = strlen(name); |
| 81 | ASSERT(target_size < kTargetSize); |
| 82 | } |
| 83 | Namespace* namespc = Namespace::Create(Namespace::Default()); |
| 84 | char* result; |
| 85 | if (File::GetType(namespc, name, false) == File::kIsLink) { |
| 86 | char dir_path[kTargetSize]; |
| 87 | // cwd is currently wherever we launched from, so set the cwd to the |
| 88 | // directory of the symlink while we try and resolve it. If we don't |
| 89 | // do this, we won't be able to properly resolve relative paths. |
| 90 | auto initial_dir_path = |
| 91 | Utils::CreateCStringUniquePtr(Directory::CurrentNoScope()); |
| 92 | // We might run into symlinks of symlinks, so make sure we follow the |
| 93 | // links all the way. See https://github.com/dart-lang/sdk/issues/41057 for |
| 94 | // an example where this happens with brew on MacOS. |
| 95 | do { |
| 96 | Directory::SetCurrent(namespc, GetDirectoryFromPath(name, dir_path)); |
| 97 | // Resolve the link without creating Dart scope String. |
| 98 | name = File::LinkTarget(namespc, GetFileNameFromPath(name), target, |
| 99 | kTargetSize); |
| 100 | if (name == nullptr) { |
| 101 | return Utils::CreateCStringUniquePtr(Utils::StrDup("" )); |
| 102 | } |
| 103 | } while (File::GetType(namespc, name, false) == File::kIsLink); |
| 104 | target_size = strlen(name); |
| 105 | |
| 106 | char absolute_path[kTargetSize]; |
| 107 | |
| 108 | // Get the absolute path after we've resolved all the symlinks and before |
| 109 | // we reset the cwd, otherwise path resolution will fail. |
| 110 | File::GetCanonicalPath(namespc, name, absolute_path, kTargetSize); |
| 111 | |
| 112 | // Reset cwd to the original value. |
| 113 | Directory::SetCurrent(namespc, initial_dir_path.get()); |
| 114 | |
| 115 | result = GetDirectoryFromPath(absolute_path, nullptr); |
| 116 | } else { |
| 117 | result = GetDirectoryFromPath(target, nullptr); |
| 118 | } |
| 119 | namespc->Release(); |
| 120 | return Utils::CreateCStringUniquePtr(result == nullptr ? Utils::StrDup("" ) |
| 121 | : result); |
| 122 | } |
| 123 | |
| 124 | } // namespace bin |
| 125 | } // namespace dart |
| 126 | |