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
12namespace dart {
13namespace bin {
14
15static 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.
30static 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.
53static 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
68Utils::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