this repo has no description
1/*
2This file is part of Darling.
3
4Copyright (C) 2017 Lubos Dolezel
5
6Darling is free software: you can redistribute it and/or modify
7it under the terms of the GNU General Public License as published by
8the Free Software Foundation, either version 3 of the License, or
9(at your option) any later version.
10
11Darling is distributed in the hope that it will be useful,
12but WITHOUT ANY WARRANTY; without even the implied warranty of
13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14GNU General Public License for more details.
15
16You should have received a copy of the GNU General Public License
17along with Darling. If not, see <http://www.gnu.org/licenses/>.
18*/
19#include <stdlib.h>
20#include <fcntl.h>
21#include <unistd.h>
22#include <stdbool.h>
23#include <sys/stat.h>
24#include <stdlib.h>
25#include <errno.h>
26#include <string.h>
27#include <dlfcn.h>
28#include <stdio.h>
29#include "xcselect.h"
30
31static char path_buffer[1024];
32
33static const char* get_developer_dir_from_file(const char* file)
34{
35 int fd = open(file, O_RDONLY);
36 if (fd == -1)
37 return NULL;
38
39 int len = read(fd, path_buffer, sizeof(path_buffer)-1);
40 if (len <= 0)
41 {
42 close(fd);
43 return NULL;
44 }
45
46 path_buffer[len] = 0;
47 close(fd);
48
49 return path_buffer;
50}
51
52const char* get_developer_dir_from_symlink(const char* link)
53{
54 ssize_t len;
55
56 len = readlink(link, path_buffer, sizeof(path_buffer)-1);
57 if (len <= 0)
58 return NULL;
59
60 path_buffer[len] = 0;
61 return path_buffer;
62}
63
64static bool dir_exists(const char* dir)
65{
66 struct stat st;
67
68 if (stat(dir, &st) != 0)
69 return false;
70
71 return S_ISDIR(st.st_mode);
72}
73
74static bool valid_dev_path(const char* path)
75{
76 char buffer[1024];
77 size_t length;
78
79 strcpy(buffer, path);
80 strcat(buffer, "/");
81
82 length = strlen(buffer);
83 strcat(buffer, "usr/lib/libxcrun.dylib");
84 if (access(buffer, F_OK) == 0)
85 return true;
86
87 buffer[length] = 0;
88 strcat(buffer, "usr/bin/xcrun");
89 if (access(buffer, F_OK) == 0)
90 return true;
91
92 return false;
93}
94
95bool xcselect_find_developer_contents_from_path(const char* p, char* dst, bool* is_cmd_line, size_t dst_size)
96{
97 size_t length;
98
99 if (*p != '/')
100 {
101 getcwd(dst, dst_size);
102 strcat(dst, "/");
103 strcat(dst, p);
104 }
105 else
106 strlcpy(dst, p, dst_size);
107
108 length = strlen(dst);
109 if (valid_dev_path(dst))
110 return true;
111
112 dst[length++] = '/';
113 dst[length] = 0;
114
115 strcat(dst, "Library/Developer/CommandLineTools");
116 if (valid_dev_path(dst))
117 {
118 *is_cmd_line = true;
119 return true;
120 }
121
122 dst[length] = 0;
123 strcat(dst, "CommandLineTools");
124 if (valid_dev_path(dst))
125 {
126 *is_cmd_line = true;
127 return true;
128 }
129
130 dst[length] = 0;
131 strcat(dst, "Contents/Developer");
132 if (valid_dev_path(dst))
133 return true;
134
135 return false;
136}
137
138bool xcselect_get_developer_dir_path(char* path, size_t path_len, bool* is_cmd_line)
139{
140 const char* p;
141 char* slash;
142
143 *is_cmd_line = false;
144
145 p = getenv("DEVELOPER_DIR");
146 if (p)
147 {
148 if (xcselect_find_developer_contents_from_path(p, path_buffer, is_cmd_line, sizeof(path_buffer)))
149 {
150 p = path_buffer;
151 goto have_path;
152 }
153 }
154
155 p = get_developer_dir_from_symlink("/var/db/xcode_select_link");
156 if (p)
157 goto have_path;
158
159 p = get_developer_dir_from_symlink("/usr/share/xcode-select/xcode_dir_link");
160 if (p)
161 goto have_path;
162
163 p = get_developer_dir_from_file("/usr/share/xcode-select/xcode_dir_path");
164 if (p)
165 goto have_path;
166
167 if (dir_exists("/Applications/Xcode.app"))
168 {
169 p = "/Applications/Xcode.app/Contents/Developer";
170 goto have_path;
171 }
172 else if (dir_exists("/Library/Developer/CommandLineTools"))
173 {
174 p = "/Library/Developer/CommandLineTools";
175 goto have_path;
176 }
177 else if (dir_exists("/Library/Developer/DarlingCLT"))
178 {
179 p = "/Library/Developer/DarlingCLT";
180 *is_cmd_line = true;
181 goto have_path;
182 }
183
184 return false;
185
186have_path:
187 slash = strrchr(p, '/');
188 if (slash != NULL)
189 {
190 if (strcmp(slash+1, "CommandLineTools") == 0)
191 *is_cmd_line = true;
192 }
193
194 strlcpy(path, p, path_len);
195 strlcat(path, "/", path_len);
196 return true;
197}
198
199
200int xcselect_invoke_xcrun(const char* tool, int argc, char* argv[], int flags)
201{
202 char dev_dir[1024];
203 bool is_cmdline;
204
205 if (xcselect_get_developer_dir_path(dev_dir, sizeof(dev_dir), &is_cmdline))
206 {
207 char* buf = (char*) malloc(2048);
208 size_t length;
209 void* lib;
210
211 if (is_cmdline && (flags & XCSELECT_FLAG_REQUIRE_XCODE))
212 {
213 fprintf(stderr, "xcrun: tool '%s' requires Xcode installation, command line tools are insufficient.\n", tool);
214 exit(1);
215 }
216
217 strcpy(buf, dev_dir);
218 if (buf[strlen(buf) - 1] != '/')
219 {
220 strcat(buf, "/");
221 }
222 length = strlen(buf);
223
224 strcat(buf, "usr/lib/libxcrun.dylib");
225 lib = dlopen(buf, RTLD_LAZY);
226
227 if (lib != NULL)
228 {
229 void(*fn)(const char*, int, char**, const char*);
230
231 *((void**)&fn) = dlsym(lib, "xcrun_main");
232 if (!fn)
233 {
234 fprintf(stderr, "xcrun: broken libxcrun.dylib at '%s'\n", buf);
235 }
236 else
237 {
238 fn(tool, argc, argv, dev_dir);
239 fprintf(stderr, "xcrun: xcrun_main unexpectedly exited\n");
240 }
241 }
242 else
243 {
244 char** argv2;
245 int j = 0;
246
247 buf[length] = 0;
248 strcat(buf, "usr/bin/xcrun");
249
250 // +3: buf, tool, NULL
251 argv2 = (char**) __builtin_alloca((argc+3) * sizeof(char*));
252 argv2[j++] = buf;
253
254 if (tool != NULL)
255 argv2[j++] = (char*) tool;
256
257 for (int i = 0; i < argc; i++, j++)
258 argv2[j] = argv[i];
259 argv2[j] = NULL;
260
261 execv(buf, argv2);
262 fprintf(stderr, "xcrun: developer path '%s' is invalid, failed to execute '%s': %s\n",
263 dev_dir, buf, strerror(errno));
264 }
265 }
266 else
267 {
268 if (dir_exists("/usr/libexec/DeveloperTools") && tool != NULL)
269 {
270 char* buf = __builtin_alloca(strlen(tool) + 30);
271 strcpy(buf, "/usr/libexec/DeveloperTools/");
272 strcat(buf, tool);
273
274 if (access(buf, F_OK) == 0)
275 {
276 char** argv2 = (char **) __builtin_alloca((argc+1+1) * sizeof(char*));
277 argv2[0] = buf;
278 for (int i = 0; i < argc; i++)
279 argv2[i+1] = argv[i];
280 argv2[argc+1] = NULL;
281
282 execv(buf, argv2);
283 fprintf(stderr, "xcrun: failed to exec '%s': %s\n", buf, strerror(errno));
284 exit(1);
285 }
286 }
287
288 fprintf(stderr, "xcrun: cannot find developer tools, set DEVELOPER_DIR if you are using a non-standard location.\n");
289 }
290
291 exit(1);
292}
293
294struct __xcselect_manpaths
295{
296 unsigned int count;
297 char** paths;
298};
299
300void xcselect_manpaths_append(xcselect_manpaths* paths, const char* path)
301{
302 paths->count++;
303 paths->paths = realloc(paths->paths, sizeof(char*) * paths->count);
304 paths->paths[paths->count - 1] = strdup(path);
305}
306
307xcselect_manpaths* xcselect_get_manpaths(const char* sdkname)
308{
309 char path[1024];
310 bool unused;
311 size_t len;
312 xcselect_manpaths* rv;
313
314 if (!xcselect_get_developer_dir_path(path, sizeof(path), &unused))
315 return NULL;
316
317 len = strlen(path);
318 strcat(path, "usr/lib/libxcrun.dylib");
319
320 rv = (xcselect_manpaths*) malloc(sizeof(*rv));
321 memset(rv, 0, sizeof(*rv));
322
323 if (access(path, F_OK) == 0)
324 {
325 void* module = dlopen(path, RTLD_LAZY);
326 void (*fn)(const char* devpath, const char* sdkname, void (^)(const char*));
327
328 if (!module)
329 {
330 free(rv);
331 fprintf(stderr, "%s: error: cannot load libxcrun (%s)\n", getprogname(), dlerror());
332 return NULL;
333 }
334
335 *((void**)&fn) = dlsym(module, "xcrun_iter_manpaths");
336
337 if (fn != NULL)
338 {
339 path[len] = 0;
340 fn(path, sdkname, ^(const char* path) {
341 xcselect_manpaths_append(rv, path);
342 });
343 }
344
345 dlclose(module);
346 }
347
348 // Add standard paths
349 path[len] = 0;
350 strcat(path, "usr/share/man");
351 xcselect_manpaths_append(rv, path);
352
353 path[len] = 0;
354 strcat(path, "usr/llvm-gcc-4,2/share/man");
355 xcselect_manpaths_append(rv, path);
356
357 path[len] = 0;
358 strcat(path, "Toolchains/XcodeDefault.xctoolchain/usr/share/man");
359 xcselect_manpaths_append(rv, path);
360
361 return rv;
362}
363
364unsigned int xcselect_manpaths_get_num_paths(xcselect_manpaths* p)
365{
366 return p->count;
367}
368
369const char* xcselect_manpaths_get_path(xcselect_manpaths* p, unsigned int idx)
370{
371 if (idx < xcselect_manpaths_get_num_paths(p))
372 return p->paths[idx];
373 else
374 return NULL;
375}
376
377void xcselect_manpaths_free(xcselect_manpaths* p)
378{
379 for (unsigned int i = 0; i < p->count; i++)
380 free(p->paths[i]);
381 free(p->paths);
382 free(p);
383}
384