blob: e4227190a485a8687f5a1f81f151aeb342581074 [file] [log] [blame]
Chris Lattner24943d22010-06-08 16:52:24 +00001//===-- Host.mm -------------------------------------------------*- C++ -*-===//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9
Greg Clayton8f3b21d2010-09-07 20:11:56 +000010#include "lldb/Host/Host.h"
Greg Clayton24b48ff2010-10-17 22:03:32 +000011
Greg Clayton6bd1a9e2010-10-19 18:15:50 +000012#include <libproc.h>
13#include <sys/proc.h>
Greg Clayton24b48ff2010-10-17 22:03:32 +000014#include <sys/stat.h>
Greg Clayton6bd1a9e2010-10-19 18:15:50 +000015#include <sys/types.h>
Greg Clayton24b48ff2010-10-17 22:03:32 +000016
17#include "lldb/Core/ArchSpec.h"
Greg Clayton36f63a92010-10-19 03:25:40 +000018#include "lldb/Core/Communication.h"
19#include "lldb/Core/ConnectionFileDescriptor.h"
Greg Clayton8f3b21d2010-09-07 20:11:56 +000020#include "lldb/Core/FileSpec.h"
21#include "lldb/Core/Log.h"
Greg Clayton24b48ff2010-10-17 22:03:32 +000022#include "lldb/Core/StreamFile.h"
23#include "lldb/Core/StreamString.h"
Chris Lattner24943d22010-06-08 16:52:24 +000024
Greg Clayton54e7afa2010-07-09 20:39:50 +000025#include "cfcpp/CFCBundle.h"
Greg Clayton24b48ff2010-10-17 22:03:32 +000026#include "cfcpp/CFCMutableArray.h"
Greg Clayton4b407112010-09-30 21:49:03 +000027#include "cfcpp/CFCMutableDictionary.h"
Greg Clayton54e7afa2010-07-09 20:39:50 +000028#include "cfcpp/CFCReleaser.h"
29#include "cfcpp/CFCString.h"
Chris Lattner24943d22010-06-08 16:52:24 +000030
Greg Clayton8f3b21d2010-09-07 20:11:56 +000031#include <objc/objc-auto.h>
32
Greg Clayton4b407112010-09-30 21:49:03 +000033#include <ApplicationServices/ApplicationServices.h>
Greg Clayton8f3b21d2010-09-07 20:11:56 +000034#include <Carbon/Carbon.h>
35#include <Foundation/Foundation.h>
Chris Lattner24943d22010-06-08 16:52:24 +000036
37using namespace lldb;
38using namespace lldb_private;
39
Chris Lattner24943d22010-06-08 16:52:24 +000040class MacOSXDarwinThread
41{
42public:
43 MacOSXDarwinThread(const char *thread_name) :
44 m_pool (nil)
45 {
46 // Register our thread with the collector if garbage collection is enabled.
47 if (objc_collectingEnabled())
48 {
49#if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_5
50 // On Leopard and earlier there is no way objc_registerThreadWithCollector
51 // function, so we do it manually.
52 auto_zone_register_thread(auto_zone());
53#else
54 // On SnowLoepard and later we just call the thread registration function.
55 objc_registerThreadWithCollector();
56#endif
57 }
58 else
59 {
60 m_pool = [[NSAutoreleasePool alloc] init];
61 }
62
63
64 Host::SetThreadName (LLDB_INVALID_PROCESS_ID, LLDB_INVALID_THREAD_ID, thread_name);
65 }
66
67 ~MacOSXDarwinThread()
68 {
69 if (m_pool)
70 [m_pool release];
71 }
72
73 static void PThreadDestructor (void *v)
74 {
75 delete (MacOSXDarwinThread*)v;
76 }
77
78protected:
79 NSAutoreleasePool * m_pool;
80private:
81 DISALLOW_COPY_AND_ASSIGN (MacOSXDarwinThread);
82};
83
84static pthread_once_t g_thread_create_once = PTHREAD_ONCE_INIT;
85static pthread_key_t g_thread_create_key = 0;
86
87static void
88InitThreadCreated()
89{
90 ::pthread_key_create (&g_thread_create_key, MacOSXDarwinThread::PThreadDestructor);
91}
92
Chris Lattner24943d22010-06-08 16:52:24 +000093void
94Host::ThreadCreated (const char *thread_name)
95{
96 ::pthread_once (&g_thread_create_once, InitThreadCreated);
97 if (g_thread_create_key)
98 {
99 ::pthread_setspecific (g_thread_create_key, new MacOSXDarwinThread(thread_name));
100 }
101}
102
Chris Lattner24943d22010-06-08 16:52:24 +0000103
104bool
Greg Clayton24b48ff2010-10-17 22:03:32 +0000105Host::ResolveExecutableInBundle (FileSpec &file)
Chris Lattner24943d22010-06-08 16:52:24 +0000106{
Greg Clayton8f3b21d2010-09-07 20:11:56 +0000107#if defined (__APPLE__)
Greg Clayton24b48ff2010-10-17 22:03:32 +0000108 if (file.GetFileType () == FileSpec::eFileTypeDirectory)
Chris Lattner24943d22010-06-08 16:52:24 +0000109 {
Greg Clayton24b48ff2010-10-17 22:03:32 +0000110 char path[PATH_MAX];
111 if (file.GetPath(path, sizeof(path)))
Chris Lattner24943d22010-06-08 16:52:24 +0000112 {
Greg Clayton24b48ff2010-10-17 22:03:32 +0000113 CFCBundle bundle (path);
114 CFCReleaser<CFURLRef> url(bundle.CopyExecutableURL ());
115 if (url.get())
116 {
117 if (::CFURLGetFileSystemRepresentation (url.get(), YES, (UInt8*)path, sizeof(path)))
118 {
119 file.SetFile(path);
120 return true;
121 }
122 }
Chris Lattner24943d22010-06-08 16:52:24 +0000123 }
124 }
Greg Clayton8f3b21d2010-09-07 20:11:56 +0000125#endif
126 return false;
Jim Ingham7508e732010-08-09 23:31:02 +0000127}
Jim Ingham74989e82010-08-30 19:44:40 +0000128
Greg Clayton4b407112010-09-30 21:49:03 +0000129lldb::pid_t
130Host::LaunchApplication (const FileSpec &app_file_spec)
131{
132 char app_path[PATH_MAX];
133 app_file_spec.GetPath(app_path, sizeof(app_path));
134
135 LSApplicationParameters app_params;
136 ::bzero (&app_params, sizeof (app_params));
137 app_params.flags = kLSLaunchDefaults |
138 kLSLaunchDontAddToRecents |
Greg Clayton24b48ff2010-10-17 22:03:32 +0000139 kLSLaunchNewInstance;
140
Greg Clayton4b407112010-09-30 21:49:03 +0000141
142 FSRef app_fsref;
143 CFCString app_cfstr (app_path, kCFStringEncodingUTF8);
144
145 OSStatus error = ::FSPathMakeRef ((const UInt8 *)app_path, &app_fsref, false);
146
147 // If we found the app, then store away the name so we don't have to re-look it up.
148 if (error != noErr)
149 return LLDB_INVALID_PROCESS_ID;
150
151 app_params.application = &app_fsref;
152
153 ProcessSerialNumber psn;
154
155 error = ::LSOpenApplication (&app_params, &psn);
156
157 if (error != noErr)
158 return LLDB_INVALID_PROCESS_ID;
159
160 ::pid_t pid = LLDB_INVALID_PROCESS_ID;
161 error = ::GetProcessPID(&psn, &pid);
162 return pid;
163}
164
Greg Clayton6bd1a9e2010-10-19 18:15:50 +0000165
166static void *
167AcceptPIDFromInferior (void *arg)
Greg Clayton24b48ff2010-10-17 22:03:32 +0000168{
Greg Clayton6bd1a9e2010-10-19 18:15:50 +0000169 const char *connect_url = (const char *)arg;
Greg Claytonea3259d2010-10-19 17:03:58 +0000170 ConnectionFileDescriptor file_conn;
Greg Clayton6bd1a9e2010-10-19 18:15:50 +0000171 Error error;
172 if (file_conn.Connect (connect_url, &error) == eConnectionStatusSuccess)
Greg Claytonea3259d2010-10-19 17:03:58 +0000173 {
174 char pid_str[256];
175 ::bzero (pid_str, sizeof(pid_str));
176 ConnectionStatus status;
177 const size_t pid_str_len = file_conn.Read (pid_str, sizeof(pid_str), status, NULL);
178 if (pid_str_len > 0)
179 {
Greg Clayton6bd1a9e2010-10-19 18:15:50 +0000180 int pid = atoi (pid_str);
181 return (void *)(intptr_t)pid;
Greg Claytonea3259d2010-10-19 17:03:58 +0000182 }
183 }
Greg Clayton6bd1a9e2010-10-19 18:15:50 +0000184 return NULL;
Greg Claytonea3259d2010-10-19 17:03:58 +0000185}
186
Greg Clayton6bd1a9e2010-10-19 18:15:50 +0000187static bool
188WaitForProcessToSIGSTOP (const lldb::pid_t pid, const int timeout_in_seconds)
189{
190 const int time_delta_usecs = 100000;
191 const int num_retries = timeout_in_seconds/time_delta_usecs;
192 for (int i=0; i<num_retries; i++)
193 {
194 struct proc_bsdinfo bsd_info;
195 int error = ::proc_pidinfo (pid, PROC_PIDTBSDINFO,
196 (uint64_t) 0,
197 &bsd_info,
198 PROC_PIDTBSDINFO_SIZE);
199
200 switch (error)
201 {
202 case EINVAL:
203 case ENOTSUP:
204 case ESRCH:
205 case EPERM:
206 return false;
207
208 default:
209 break;
210
211 case 0:
212 if (bsd_info.pbi_status == SSTOP)
213 return true;
214 }
215 ::usleep (time_delta_usecs);
216 }
217 return false;
218}
219
220static lldb::pid_t
221LaunchInNewTerminalWithCommandFile
Greg Claytonea3259d2010-10-19 17:03:58 +0000222(
223 const char **argv,
224 const char **envp,
225 const ArchSpec *arch_spec,
226 bool stop_at_entry,
227 bool disable_aslr
228)
229{
230 if (!argv || !argv[0])
231 return LLDB_INVALID_PROCESS_ID;
Greg Clayton24b48ff2010-10-17 22:03:32 +0000232
233 OSStatus error = 0;
234
235 FileSpec program (argv[0]);
236
237
Greg Clayton36f63a92010-10-19 03:25:40 +0000238 std::string unix_socket_name;
239
Greg Clayton24b48ff2010-10-17 22:03:32 +0000240 char temp_file_path[PATH_MAX];
241 const char *tmpdir = ::getenv ("TMPDIR");
242 if (tmpdir == NULL)
243 tmpdir = "/tmp/";
244 ::snprintf (temp_file_path, sizeof(temp_file_path), "%s%s-XXXXXX", tmpdir, program.GetFilename().AsCString());
245
246 if (::mktemp (temp_file_path) == NULL)
247 return LLDB_INVALID_PROCESS_ID;
248
Greg Clayton36f63a92010-10-19 03:25:40 +0000249 unix_socket_name.assign (temp_file_path);
250
Greg Clayton24b48ff2010-10-17 22:03:32 +0000251 ::strncat (temp_file_path, ".command", sizeof (temp_file_path));
252
253 StreamFile command_file (temp_file_path, "w");
254
255 FileSpec darwin_debug_file_spec;
256 if (!Host::GetLLDBPath (ePathTypeSupportExecutableDir, darwin_debug_file_spec))
257 return LLDB_INVALID_PROCESS_ID;
258 darwin_debug_file_spec.GetFilename().SetCString("darwin-debug");
259
260 if (!darwin_debug_file_spec.Exists())
261 return LLDB_INVALID_PROCESS_ID;
262
263 char launcher_path[PATH_MAX];
264 darwin_debug_file_spec.GetPath(launcher_path, sizeof(launcher_path));
265 command_file.Printf("\"%s\" ", launcher_path);
266
Greg Clayton36f63a92010-10-19 03:25:40 +0000267 command_file.Printf("--unix-socket=%s ", unix_socket_name.c_str());
268
Greg Clayton24b48ff2010-10-17 22:03:32 +0000269 if (arch_spec && arch_spec->IsValid())
270 {
271 command_file.Printf("--arch=%s ", arch_spec->AsCString());
272 }
273
274 if (disable_aslr)
275 {
276 command_file.PutCString("--disable-aslr ");
277 }
278
279 command_file.PutCString("-- ");
280
281 if (argv)
282 {
283 for (size_t i=0; argv[i] != NULL; ++i)
284 {
285 command_file.Printf("\"%s\" ", argv[i]);
286 }
287 }
Greg Claytonc1d37752010-10-18 01:45:30 +0000288 command_file.PutCString("\necho Process exited with status $?\n");
Greg Clayton24b48ff2010-10-17 22:03:32 +0000289 command_file.Close();
290 if (::chmod (temp_file_path, S_IRWXU | S_IRWXG) != 0)
291 return LLDB_INVALID_PROCESS_ID;
292
293 CFCMutableDictionary cf_env_dict;
294
295 const bool can_create = true;
296 if (envp)
297 {
298 for (size_t i=0; envp[i] != NULL; ++i)
299 {
300 const char *env_entry = envp[i];
301 const char *equal_pos = strchr(env_entry, '=');
302 if (equal_pos)
303 {
304 std::string env_key (env_entry, equal_pos);
305 std::string env_val (equal_pos + 1);
306 CFCString cf_env_key (env_key.c_str(), kCFStringEncodingUTF8);
307 CFCString cf_env_val (env_val.c_str(), kCFStringEncodingUTF8);
308 cf_env_dict.AddValue (cf_env_key.get(), cf_env_val.get(), can_create);
309 }
310 }
311 }
312
313 LSApplicationParameters app_params;
314 ::bzero (&app_params, sizeof (app_params));
315 app_params.flags = kLSLaunchDontAddToRecents | kLSLaunchAsync;
316 app_params.argv = NULL;
317 app_params.environment = (CFDictionaryRef)cf_env_dict.get();
318
319 CFCReleaser<CFURLRef> command_file_url (::CFURLCreateFromFileSystemRepresentation (NULL,
320 (const UInt8 *)temp_file_path,
Greg Clayton36f63a92010-10-19 03:25:40 +0000321 strlen(temp_file_path),
Greg Clayton24b48ff2010-10-17 22:03:32 +0000322 false));
323
324 CFCMutableArray urls;
325
326 // Terminal.app will open the ".command" file we have created
327 // and run our process inside it which will wait at the entry point
328 // for us to attach.
329 urls.AppendValue(command_file_url.get());
330
Greg Clayton36f63a92010-10-19 03:25:40 +0000331
332 lldb::pid_t pid = LLDB_INVALID_PROCESS_ID;
333
334 Error lldb_error;
335 // Sleep and wait a bit for debugserver to start to listen...
Greg Clayton36f63a92010-10-19 03:25:40 +0000336 char connect_url[128];
337 ::snprintf (connect_url, sizeof(connect_url), "unix-accept://%s", unix_socket_name.c_str());
338
Greg Clayton6bd1a9e2010-10-19 18:15:50 +0000339 // Spawn a new thread to accept incoming connection on the connect_url
340 // so we can grab the pid from the inferior
341 lldb::thread_t accept_thread = Host::ThreadCreate (unix_socket_name.c_str(),
342 AcceptPIDFromInferior,
343 connect_url,
344 &lldb_error);
345
Greg Clayton24b48ff2010-10-17 22:03:32 +0000346 ProcessSerialNumber psn;
347 error = LSOpenURLsWithRole(urls.get(), kLSRolesShell, NULL, &app_params, &psn, 1);
Greg Clayton36f63a92010-10-19 03:25:40 +0000348 if (error == noErr)
349 {
Greg Clayton6bd1a9e2010-10-19 18:15:50 +0000350 thread_result_t accept_thread_result = NULL;
351 if (Host::ThreadJoin (accept_thread, &accept_thread_result, &lldb_error))
Greg Clayton36f63a92010-10-19 03:25:40 +0000352 {
Greg Clayton6bd1a9e2010-10-19 18:15:50 +0000353 if (accept_thread_result)
Greg Clayton36f63a92010-10-19 03:25:40 +0000354 {
Greg Clayton6bd1a9e2010-10-19 18:15:50 +0000355 pid = (intptr_t)accept_thread_result;
356
357 // Wait for process to be stopped the the entry point by watching
358 // for the process status to be set to SSTOP which indicates it it
359 // SIGSTOP'ed at the entry point
360 WaitForProcessToSIGSTOP (pid, 5);
Greg Clayton36f63a92010-10-19 03:25:40 +0000361 }
362 }
363 }
Greg Clayton6bd1a9e2010-10-19 18:15:50 +0000364 else
365 {
366 Host::ThreadCancel (accept_thread, &lldb_error);
367 }
368
Greg Clayton24b48ff2010-10-17 22:03:32 +0000369 return pid;
370}
Greg Clayton6bd1a9e2010-10-19 18:15:50 +0000371
372
373lldb::pid_t
374LaunchInNewTerminalWithAppleScript
375(
376 const char **argv,
377 const char **envp,
378 const ArchSpec *arch_spec,
379 bool stop_at_entry,
380 bool disable_aslr
381)
382{
383 if (!argv || !argv[0])
384 return LLDB_INVALID_PROCESS_ID;
385
386 std::string unix_socket_name;
387
388 char temp_file_path[PATH_MAX] = "/tmp/XXXXXX";
389 if (::mktemp (temp_file_path) == NULL)
390 return LLDB_INVALID_PROCESS_ID;
391
392 unix_socket_name.assign (temp_file_path);
393
394 StreamString command;
395
396 FileSpec darwin_debug_file_spec;
397 if (!Host::GetLLDBPath (ePathTypeSupportExecutableDir, darwin_debug_file_spec))
398 return LLDB_INVALID_PROCESS_ID;
399 darwin_debug_file_spec.GetFilename().SetCString("darwin-debug");
400
401 if (!darwin_debug_file_spec.Exists())
402 return LLDB_INVALID_PROCESS_ID;
403
404 char launcher_path[PATH_MAX];
405 darwin_debug_file_spec.GetPath(launcher_path, sizeof(launcher_path));
406 command.Printf ("tell application \"Terminal\"\n do script \"'%s'", launcher_path);
407
408 command.Printf(" --unix-socket=%s", unix_socket_name.c_str());
409
410 if (arch_spec && arch_spec->IsValid())
411 {
412 command.Printf(" --arch=%s", arch_spec->AsCString());
413 }
414
415 if (disable_aslr)
416 {
417 command.PutCString(" --disable-aslr");
418 }
419
420 command.PutCString(" --");
421
422 if (argv)
423 {
424 for (size_t i=0; argv[i] != NULL; ++i)
425 {
426 command.Printf(" '%s'", argv[i]);
427 }
428 }
429 command.PutCString (" ; echo Process exited with status $?\"\nend tell\n");
430 const char *script_source = command.GetString().c_str();
431 NSAppleScript* applescript = [[NSAppleScript alloc] initWithSource:[NSString stringWithCString:script_source encoding:NSUTF8StringEncoding]];
432
433 lldb::pid_t pid = LLDB_INVALID_PROCESS_ID;
434
435 Error lldb_error;
436 // Sleep and wait a bit for debugserver to start to listen...
437 ConnectionFileDescriptor file_conn;
438 char connect_url[128];
439 ::snprintf (connect_url, sizeof(connect_url), "unix-accept://%s", unix_socket_name.c_str());
440
441 // Spawn a new thread to accept incoming connection on the connect_url
442 // so we can grab the pid from the inferior
443 lldb::thread_t accept_thread = Host::ThreadCreate (unix_socket_name.c_str(),
444 AcceptPIDFromInferior,
445 connect_url,
446 &lldb_error);
447
448
449 [applescript executeAndReturnError:nil];
450
451 thread_result_t accept_thread_result = NULL;
452 if (Host::ThreadJoin (accept_thread, &accept_thread_result, &lldb_error))
453 {
454 if (accept_thread_result)
455 {
456 pid = (intptr_t)accept_thread_result;
457
458 // Wait for process to be stopped the the entry point by watching
459 // for the process status to be set to SSTOP which indicates it it
460 // SIGSTOP'ed at the entry point
461 WaitForProcessToSIGSTOP (pid, 5);
462 }
463 }
464 [applescript release];
465 return pid;
466}
467
468
469#define LLDB_HOST_USE_APPLESCRIPT
470
471lldb::pid_t
472Host::LaunchInNewTerminal
473(
474 const char **argv,
475 const char **envp,
476 const ArchSpec *arch_spec,
477 bool stop_at_entry,
478 bool disable_aslr
479)
480{
481#if defined (LLDB_HOST_USE_APPLESCRIPT)
482 return LaunchInNewTerminalWithAppleScript (argv, envp, arch_spec, stop_at_entry, disable_aslr);
483#else
484 return LaunchInNewTerminalWithCommandFile (argv, envp, arch_spec, stop_at_entry, disable_aslr);
Greg Claytonea3259d2010-10-19 17:03:58 +0000485#endif
Greg Clayton6bd1a9e2010-10-19 18:15:50 +0000486}
487
Greg Clayton24b48ff2010-10-17 22:03:32 +0000488
Jim Ingham74989e82010-08-30 19:44:40 +0000489bool
Greg Clayton4b407112010-09-30 21:49:03 +0000490Host::OpenFileInExternalEditor (const FileSpec &file_spec, uint32_t line_no)
Jim Ingham74989e82010-08-30 19:44:40 +0000491{
492 // We attach this to an 'odoc' event to specify a particular selection
493 typedef struct {
Greg Claytonf4fa8a92010-08-30 22:00:34 +0000494 int16_t reserved0; // must be zero
495 int16_t fLineNumber;
496 int32_t fSelStart;
497 int32_t fSelEnd;
498 uint32_t reserved1; // must be zero
499 uint32_t reserved2; // must be zero
Jim Ingham74989e82010-08-30 19:44:40 +0000500 } BabelAESelInfo;
501
Jim Inghamb0fff352010-08-31 18:05:13 +0000502 Log *log = lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_HOST);
Jim Ingham74989e82010-08-30 19:44:40 +0000503 char file_path[PATH_MAX];
504 file_spec.GetPath(file_path, PATH_MAX);
505 CFCString file_cfstr (file_path, kCFStringEncodingUTF8);
Greg Claytonf4fa8a92010-08-30 22:00:34 +0000506 CFCReleaser<CFURLRef> file_URL (::CFURLCreateWithFileSystemPath (NULL,
507 file_cfstr.get(),
508 kCFURLPOSIXPathStyle,
509 false));
Jim Inghamb0fff352010-08-31 18:05:13 +0000510
511 if (log)
512 log->Printf("Sending source file: \"%s\" and line: %d to external editor.\n", file_path, line_no);
Greg Claytonf4fa8a92010-08-30 22:00:34 +0000513
Jim Ingham74989e82010-08-30 19:44:40 +0000514 OSStatus error;
Greg Claytonf4fa8a92010-08-30 22:00:34 +0000515 BabelAESelInfo file_and_line_info =
516 {
517 0, // reserved0
518 line_no - 1, // fLineNumber (zero based line number)
519 1, // fSelStart
520 1024, // fSelEnd
521 0, // reserved1
522 0 // reserved2
523 };
Jim Ingham74989e82010-08-30 19:44:40 +0000524
525 AEKeyDesc file_and_line_desc;
526
Greg Claytonf4fa8a92010-08-30 22:00:34 +0000527 error = ::AECreateDesc (typeUTF8Text,
528 &file_and_line_info,
529 sizeof (file_and_line_info),
530 &(file_and_line_desc.descContent));
Jim Ingham74989e82010-08-30 19:44:40 +0000531
Jim Ingham74989e82010-08-30 19:44:40 +0000532 if (error != noErr)
533 {
Jim Inghamb0fff352010-08-31 18:05:13 +0000534 if (log)
535 log->Printf("Error creating AEDesc: %d.\n", error);
Jim Ingham74989e82010-08-30 19:44:40 +0000536 return false;
537 }
538
539 file_and_line_desc.descKey = keyAEPosition;
540
Greg Claytoncb0989a2010-08-31 18:56:24 +0000541 static std::string g_app_name;
Greg Clayton452bf612010-08-31 18:35:14 +0000542 static FSRef g_app_fsref;
543
Jim Ingham74989e82010-08-30 19:44:40 +0000544 LSApplicationParameters app_params;
Greg Clayton452bf612010-08-31 18:35:14 +0000545 ::bzero (&app_params, sizeof (app_params));
Greg Claytonf4fa8a92010-08-30 22:00:34 +0000546 app_params.flags = kLSLaunchDefaults |
547 kLSLaunchDontAddToRecents |
548 kLSLaunchDontSwitch;
Greg Clayton452bf612010-08-31 18:35:14 +0000549
Jim Ingham363180d2010-08-30 23:48:25 +0000550 char *external_editor = ::getenv ("LLDB_EXTERNAL_EDITOR");
551
Greg Clayton452bf612010-08-31 18:35:14 +0000552 if (external_editor)
Jim Ingham363180d2010-08-30 23:48:25 +0000553 {
Jim Inghamb0fff352010-08-31 18:05:13 +0000554 if (log)
555 log->Printf("Looking for external editor \"%s\".\n", external_editor);
556
Greg Claytoncb0989a2010-08-31 18:56:24 +0000557 if (g_app_name.empty() || strcmp (g_app_name.c_str(), external_editor) != 0)
Jim Ingham363180d2010-08-30 23:48:25 +0000558 {
Jim Ingham363180d2010-08-30 23:48:25 +0000559 CFCString editor_name (external_editor, kCFStringEncodingUTF8);
Greg Clayton452bf612010-08-31 18:35:14 +0000560 error = ::LSFindApplicationForInfo (kLSUnknownCreator,
561 NULL,
562 editor_name.get(),
563 &g_app_fsref,
564 NULL);
Jim Ingham363180d2010-08-30 23:48:25 +0000565
566 // If we found the app, then store away the name so we don't have to re-look it up.
Greg Clayton452bf612010-08-31 18:35:14 +0000567 if (error != noErr)
Jim Inghamb0fff352010-08-31 18:05:13 +0000568 {
569 if (log)
570 log->Printf("Could not find External Editor application, error: %d.\n", error);
Jim Ingham363180d2010-08-30 23:48:25 +0000571 return false;
Jim Inghamb0fff352010-08-31 18:05:13 +0000572 }
Jim Ingham363180d2010-08-30 23:48:25 +0000573
574 }
Greg Clayton452bf612010-08-31 18:35:14 +0000575 app_params.application = &g_app_fsref;
Jim Ingham363180d2010-08-30 23:48:25 +0000576 }
577
Jim Ingham74989e82010-08-30 19:44:40 +0000578 ProcessSerialNumber psn;
579 CFCReleaser<CFArrayRef> file_array(CFArrayCreate (NULL, (const void **) file_URL.ptr_address(false), 1, NULL));
Greg Claytonf4fa8a92010-08-30 22:00:34 +0000580 error = ::LSOpenURLsWithRole (file_array.get(),
Jim Ingham363180d2010-08-30 23:48:25 +0000581 kLSRolesAll,
Greg Claytonf4fa8a92010-08-30 22:00:34 +0000582 &file_and_line_desc,
583 &app_params,
584 &psn,
585 1);
586
Jim Ingham74989e82010-08-30 19:44:40 +0000587 AEDisposeDesc (&(file_and_line_desc.descContent));
588
589 if (error != noErr)
590 {
Jim Inghamb0fff352010-08-31 18:05:13 +0000591 if (log)
592 log->Printf("LSOpenURLsWithRole failed, error: %d.\n", error);
593
Jim Ingham74989e82010-08-30 19:44:40 +0000594 return false;
595 }
596
597 ProcessInfoRec which_process;
598 bzero(&which_process, sizeof(which_process));
599 unsigned char ap_name[PATH_MAX];
600 which_process.processName = ap_name;
Greg Claytonf4fa8a92010-08-30 22:00:34 +0000601 error = ::GetProcessInformation (&psn, &which_process);
Jim Ingham74989e82010-08-30 19:44:40 +0000602
Jim Inghamb0fff352010-08-31 18:05:13 +0000603 bool using_xcode;
604 if (error != noErr)
605 {
606 if (log)
607 log->Printf("GetProcessInformation failed, error: %d.\n", error);
608 using_xcode = false;
609 }
610 else
611 using_xcode = strncmp((char *) ap_name+1, "Xcode", (int) ap_name[0]) == 0;
Jim Ingham74989e82010-08-30 19:44:40 +0000612
613 // Xcode doesn't obey the line number in the Open Apple Event. So I have to send
614 // it an AppleScript to focus on the right line.
615
616 if (using_xcode)
617 {
618 static ComponentInstance osa_component = NULL;
619 static const char *as_template = "tell application \"Xcode\"\n"
620 "set doc to the first document whose path is \"%s\"\n"
621 "set the selection to paragraph %d of doc\n"
622 "--- set the selected paragraph range to {%d, %d} of doc\n"
623 "end tell\n";
624 const int chars_for_int = 32;
625 static int as_template_len = strlen (as_template);
626
627
628 char *as_str;
629 AEDesc as_desc;
630
631 if (osa_component == NULL)
632 {
Greg Claytonf4fa8a92010-08-30 22:00:34 +0000633 osa_component = ::OpenDefaultComponent (kOSAComponentType,
634 kAppleScriptSubtype);
Jim Ingham74989e82010-08-30 19:44:40 +0000635 }
636
637 if (osa_component == NULL)
638 {
Jim Inghamb0fff352010-08-31 18:05:13 +0000639 if (log)
640 log->Printf("Could not get default AppleScript component.\n");
Jim Ingham74989e82010-08-30 19:44:40 +0000641 return false;
642 }
643
644 uint32_t as_str_size = as_template_len + strlen (file_path) + 3 * chars_for_int + 1;
645 as_str = (char *) malloc (as_str_size);
Greg Claytonf4fa8a92010-08-30 22:00:34 +0000646 ::snprintf (as_str,
647 as_str_size - 1,
648 as_template,
649 file_path,
650 line_no,
651 line_no,
652 line_no);
Jim Ingham74989e82010-08-30 19:44:40 +0000653
Greg Claytonf4fa8a92010-08-30 22:00:34 +0000654 error = ::AECreateDesc (typeChar,
655 as_str,
656 strlen (as_str),
657 &as_desc);
658
659 ::free (as_str);
Jim Ingham74989e82010-08-30 19:44:40 +0000660
661 if (error != noErr)
Jim Inghamb0fff352010-08-31 18:05:13 +0000662 {
663 if (log)
664 log->Printf("Failed to create AEDesc for Xcode AppleEvent: %d.\n", error);
Jim Ingham74989e82010-08-30 19:44:40 +0000665 return false;
Jim Inghamb0fff352010-08-31 18:05:13 +0000666 }
Jim Ingham74989e82010-08-30 19:44:40 +0000667
668 OSAID ret_OSAID;
Greg Claytonf4fa8a92010-08-30 22:00:34 +0000669 error = ::OSACompileExecute (osa_component,
670 &as_desc,
671 kOSANullScript,
672 kOSAModeNeverInteract,
673 &ret_OSAID);
674
675 ::OSADispose (osa_component, ret_OSAID);
Jim Ingham74989e82010-08-30 19:44:40 +0000676
Greg Claytonf4fa8a92010-08-30 22:00:34 +0000677 ::AEDisposeDesc (&as_desc);
Jim Ingham74989e82010-08-30 19:44:40 +0000678
679 if (error != noErr)
Jim Inghamb0fff352010-08-31 18:05:13 +0000680 {
681 if (log)
682 log->Printf("Sending AppleEvent to Xcode failed, error: %d.\n", error);
Jim Ingham74989e82010-08-30 19:44:40 +0000683 return false;
Jim Inghamb0fff352010-08-31 18:05:13 +0000684 }
Jim Ingham74989e82010-08-30 19:44:40 +0000685 }
686
687 return true;
688}