15 Most Recent [RSS]
More...
|
Why calling scripts is usually a Bad Idea(tm)
Update: Added mention of an Apple recommendation I had forgotten about.
Update 2: Opened for comments and made this sound less like slamming Andrew.
I just came across one of Andrew Stone's articles, and thought I'd warn people who would emulate this.
Now, no matter what I may think of Stone Design's programs, their company philosophy or Mr. Stone as a person (haven't met him), this article makes Andrew Stone look slightly Carbon-phobic to me. I can understand that it's not easy to get through Carbon these days, especially without prior knowledge of Classic, and yes, all those legacy mechanisms especially in file management are hellishly confusing. Still, libraries and frameworks are just tools, and I firmly believe in using the right tool for the right job.
In aforementioned article, Andrew is trying to create an Alias, after finding out he's discovered one of the few cases where a symlink won't shut up the OS's complaints. Now, Cocoa's support for Aliases, especially at the API level, isn't anything to write home about, and after unusuccessfully trying to find his way around Carbon's NewAlias() function, he resorts to AppleScript.
Now, at least it gets the job done, which is important if you ever wish to ship a product. And I guess in the particular case he's in, it isn't really important how fast it is, because the alias is only created once when his app is first started. Still, on the web one could probably come across some old Classic sample code. Creating Aliases was something programs needed to do since their introduction in System 7.0, and File Manager code hasn't really changed since then and the introduction of OS X.
But I'm not here to tell Andrew how he should have done things. Hindsight is 20/20, and from his article I judge that he exhausted the options available to him at the time. Moreover, if you look at my TADS Workbench, you'll notice that it, too, uses AppleScript to launch a command-line-tool in Terminal.app when you test-run a text adventure. So, if Andrew is guilty of this fault, I'm doubly so, because I knew better and just was lazy, no matter what excuse I may be offering you.
Still, let me tell you here and now that, whatever you may think, adding another layer of indirection and calling AppleScript where a Carbon API call or five would suffice is not the answer. In fact, it just introduces more bugs. Look at Andrew's code. He inserts the path of his application into the string @"tell application \"Finder\"\nset f to \"%@\"\nset al to make new alias file at \"%@\" to f\nset the name of al to \"%@\"\nend tell".
Now, Andrew is a good programmer, and so correctly passes an HFS path in, but he completely forgets to correctly escape special characters. If your application is inside a folder whose name contains the quote character, the above will insistently give a syntax error. And even if he'd thought to escape the quotes, he'd still have had to cope with other special characters, like returns in filenames, which are an oddity, but do occasionally happen, especially when you're listing directory contents and a folder has a custom icon (all of this isn't really applicable in his case, luckily).
So, if you have the choice between auto-generating a script and compiling and running that, or using a built-in API: save yourself the hassle and use the API. That's after all the entire reason why Cocoa is so much better than other frameworks: Because it tries to assist you in not making errors. If you write your entire app in Cocoa, and then resort to running an AppleScript instead of using Carbon because Carbon is a way too dangerous API and doesn't assist you enough in avoiding bugs, you're doing the equivalent of shooting yourself in the head to stop a headache.
And if for some reason you can't manage to use the API, be very careful to check and double-check any user input. And user input isn't just what the user typed into your app, but rather any kind of data that the user has control over, which definitely includes file names and file paths. Better avoid the problem completely and pass in the pathnames as parameters to the script, instead of resorting to -stringWithFormat:.
Update: Bill Monk wrote me to remind me that, back in the Classic days, Apple actually recommended that you send an Apple Event to the Finder to create alias files, because that was the safest way to ensure you'd get a valid one even if the format should change (it also saved you from finding the right type/creator code to get the correct icon etc.). Still, that's no excuse for using -stringWithFormat:. Cocoa has classes of its own for sending Apple Events, and the Carbon code for doing that and NSAppleScript are also available to do the same. By passing it as a parameter you can avoid having to escape your strings. Otherwise we end up where Unix is today, where the OS itself doesn't have the notion of a reserved character, but you can't even use spaces in file names because most of the installer shell-scripts and makefiles fail to correctly escape file paths.
James writes: There is another reason for not pursuing a Carbon solution -- Carbon is on its way out. Now especially with the Intel machines, going all-Cocoa (plus some Applescript) makes perfect sense.
|
Uli Kusterer replies: ★ Not all of Carbon. The parts that apply here are still fully supported. In fact, the File Manager and Alias Manager have been moved into CoreServices. If there's a native API, that's still better than resorting to build scripts in code. There are two many bugs and security exploits out there taking advantage of badly escaped strings in scripts and SQL queries. We really don't want to add to that. |
| |