tags:

views:

1052

answers:

4

The simple question is: how to find out the location of an executable file in a Cocoa application.

Remember that, in many Unix-like OS people use PATH environment to assign the preferred location for their executables, especially when they have several versions of same application in their system. As a good practice, our Cocoa application should find the PREFERRED location of the executable file it needs.

For example, there was a SVN 1.4 in Leopard default configuration at /usr/bin, and you installed a much newer version, say SVN 1.5.3 via MacPorts at /opt/local/bin. And you set your PATH using /etc/path.d or .bash_profile or .zshrc like that:

export PATH=/opt/local/bin:$PATH

So you can use the new version of svn instead of the old one from the system. It works well in any terminal environment. But not in Cocoa applications. Cocoa application, as far as I know, only has a default PATH environment like this:

export PATH="/usr/bin:/bin:/usr/sbin:/sbin"

By default it will not using the configuration in /etc/path.d, .bash_profile, .profile, .zshrc, etc.

So how exactly can we do?

p.s. We have a semi-solution here, but it cannot fully satisfied the objective for this question.

+1  A: 

Isn't the path for Finder (and hence, any GUI-launched Cocoa apps) set from your login shell? If your login shell and the shell you're using in Terminal.app aren't the same, that'd probably lead to some confusion.

This information might be helpful: http://lists.apple.com/archives/cocoa-dev/2005/Oct/msg00528.html

Apparently, the "right" way to set environment variables for GUI processes is in a hidden .plist file. I'm sure I knew this at one point, then promptly forgot it.

Mark Bessey
This could be a solution: ~/.MacOSX/environment.plistI also find a way to make this .plist automatically. Thanks for your info.
Neo
+5  A: 

The tricky part of trying to do this is the fact that the user could have their shell set to anything: sh, bash, csh, tcsh, and so on, and each shell sets up its terminal environment differently. I'm not sure if I'd go to the trouble for this myself, but if you really want to, here's the route I would take.

The first step is to figure out the user's shell. On OS X, this information is stored in Directory Services, which can be accesed either through the APIs in DirectoryService.framework or by using the dscl command line tool. The DirectoryService API is a royal pain in the ass, so I would probably go the CLI route. In Cocoa, you can use NSTask to execute the tool with arguments to get the user's shell (I'll leave the details of this for elsewhere). The command would look something like:

dscl -plist localhost -read /Local/Default/Users/username UserShell

This will return XML text that you can interpret as a plist and transform into an NSDictionary, or you can omit the -plist option and parse the textual output yourself.

Once you know the path to the user's shell, the next step would be to execute that shell and tell it to run the env command to print out the user's environment. It looks like most shells accept a -c command line option that lets you pass in a string to execute - I guess you'll just have to assume that as being the common interface for whatever shell the user has chosen.

Once you have the user's environment, you can then grab their list of paths out of that, and do the search for whatever executable you're looking for from that. Like I said, I really don't know whether this is worth the trouble, but that's the direction I would go if I were implementing this.

Brian Webster
I think you have given a brief and clear pathway to the standard solution. I know it's not a very common stuff, but it's indeed useful in some situation. I'll try it. Thanks!
Neo
BTW, I think that in Leopard the suggested way to handle PATH is through /etc/paths.d and it'll work for any shells. But seems not many people use that :(
Neo
Make sure you check whether the shell is in /etc/shells and only run it if so. The shell can be *any* program, not just actual shells, and the human may be running your app under a special user account that has /sbin/nologin or another program as its shell.
Peter Hosey
+3  A: 

Related to Brian Webster's answer:

An easier way to get the User's shell is to use the NSProcessInfo class. e.g

NSDictionary *environmentDict = [[NSProcessInfo processInfo] environment];
NSString *shellString = [environmentDict objectForKey:@"SHELL"];

Which is easier than using dscl and parsing XML input.

Abizern
+2  A: 

How likely is it that your users will have custom versions of the tool you're using (and how likely is it that your app is compatible with arbitrary versions of the tool)? If the answer is "not very", then consider using the path to the system-supplied tool by default, and giving advanced users a way to specify their own path as a preference.

Graham Lee