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 | |