Saturday, November 07, 2009

CLAlert: NSAlert done right

From NSAlert documentation:

Currently, there is no visual difference between informational and warning alerts.

This was written at the time of Panther and this is still true in Snow Leopard. So why the hell is the setAlertStyle: method provided?

NSCriticalAlertStyle
This style causes the icon to be badged with a caution icon.

A caution icon, for a critical alert? That does not make sense!

Here are screenshots of the default behaviours with the three different alert styles:







The documentation is unfortunately right: there is no difference between the informational and the warning style. The critical style is indeed badged with a caution icon.

As you probably have guessed from the title of this post, I am not happy with this behavior, so I have written a class, CLAlert (MIT License) that displays alerts the way I think it should be done. I.e. a note icon for an informational alert, a caution icon for a warning alert and a stop icon for a critical alert as you can see on these screenshots.







CLAlert is a drop-in replacement for NSAlert. It requires at lest Mac OS X 10.5.

Thursday, November 05, 2009

Watch a large number of icons with Preview.app

Browsing .icns files in Preview.app can be painful. If you try to open all the icons of the CoreTypes bundle for example with the following command

open /System/Library/CoreServices/CoreTypes.bundle/Contents/Resources/*icns

you will get this view:


All the different sizes of the .icns are displayed, which makes it quite unpleasant to browse.

F-Script Anywhere to the rescue!

Install F-Script Anywhere into Preview, then do

  1. FSA → New F-Script Workspace

  2. Click the Browser For Target… button

  3. Click on one of the icons

  4. Choose the PVIKImageBrowserView object

  5. In the F-Script Object Browser, name the PVIKImageBrowserView browserView


Finally, in the F-Script interpreter, paste the following code:
groups := browserView layoutManager groups.
count := groups count.
0 to:count-1 do: [:i | browserView collapseGroup:(groups objectAtIndex:i)]


Tada, a nice browsable view of your icons!

Wednesday, August 19, 2009

Booting from a dmg

I was pointed by Jean-Francois Roy that it is possible to boot off a dmg.

It's a simple two steps process using bootfolder:

./makebootfolder /Path/to/OperatingSystem.dmg
sudo ./blessbootfolder /Path/to/OperatingSystem.bootfolder

But beware: you better not have a space in the path to the dmg or in the dmg itself. If you happen to try this trick with a space in the path to the boot folder, then your Mac will brick. In verbose startup, you will get

Error loading kernel 'mach_kernel' (0x9)

If you can't boot anymore, download rEFIt and burn it onto a CD. Then boot on the CD by pressing the C key. You should see something looking like this (maybe without the linux and windows partitions):



Just press enter and you should be able to boot your Mac.

Wednesday, April 08, 2009

Extract UIKit artwork

While writing an iPhone application (more about it on this blog soon), I wanted to tint my UIAlertView with the same color I tinted my other controls. Maybe this is not a good idea, but I wanted to try anyway. Unfortunately, only UISearchBar, UISegmentedControl, UINavigationBar and UIToolbar have the tintColor property. So I started digging into UIKit. The first place to look was obviously the -(void)[UIAlertView(Private) drawRect:] method. To draw its background, a UIAlertView calls _popupAlertBackground which in turn call _UIImageWithName(@"UIPopupAlertSheetBackground.png"). This file probably lies inside the UIKit.framework would think a Mac developer. In fact it is not there. Only two pngs are there: DefaultWallpaper-iPhone.png and DefaultWallpaper-iPod.png. So, where is it?

It is in fact somehow embedded in the Other.artwork file which contains UIKit artwork. This file is memory mapped at application startup, probably for performance reasons and the content is easily accessed with the private _UIImageWithName() function. _UIImageWithName looks up in a dictionary that associates a file name to some memory location that contains the actual image data.

Now, it would be cool to extract all this content to see what artwork UIKit contains. It turns out to be quite easy. The only trick is to find a reference to the dictionary containing all the file names. Check my UIKit Artwork Extractor project on github to see how it works.

In an attempt to encourage comments, I'm offering a beer at NSConference to the first person who will clearly explain how to find the magic address of the images dictionary (except for Nicolas who knows the answer already) and I'm offering two beers to the one who provides a cleaner way to obtain a reference to the dictionary that is not UIKit version specific ;-)

Edit: I have updated the code so that it is not UIKit version specific, so I won the beers myself that I'm going to drink right away.

Monday, March 23, 2009

chmod, ACL and symbolic links

Let's say you want to set an ACL on a symbolic link. So you try the -h option of chmod that is documented to change the mode of the link itself rather than the file that the link points to.

  $ ln -s aFileThatDoesNotExist myLink
$ /bin/chmod -h +a "everyone allow delete" myLink
chmod: Failed to set ACL on file 'myLink': No such file or directory

So, it seems myLink was followed even though we passed the -h option. Let's try with a link that points to an existing file:

  $ touch aFileThatExist
$ ln -fs aFileThatExist myLink
$ /bin/chmod -h +a "everyone allow delete" myLink
$ ls -le myLink aFileThatExist
-rw-r--r--+ 1 cluthi staff 0 5 mar 18:11 aFileThatExist
0: group:everyone allow delete
lrwxr-xr-x 1 cluthi staff 14 5 mar 18:12 myLink -> aFileThatExist

Our hypothesis is confirmed, the link was followed. Let's investigate and have a look at chmod source code (file_cmds-188 on darwinsource).

chmod.c:125 variable hflag is set to true if the -h argument is passed
chmod.c:388 function modify_file_acl() is called, the hflag is not passed to this function

Doh, lazy boys! Let's patch it and add a follow argument to the modify_file_acl() function:
int modify_file_acl(unsigned int optflags, const char *path, acl_t modifier, int position, int inheritance_level, int follow);
and call it with modify_file_acl(..., !hflag)

From acl_set(3) man: The acl_set_link_np() function acts on a symlink rather than its target, if the target of the path is a symlink. Perfect, that's what we need: we must call acl_set_link_np instead of acl_set_file in order not to follow the link.

chmod_acl.c:807 acl_set_file is called: (0 != acl_set_file(path, ACL_TYPE_EXTENDED, oacl))
Replace with (0 != (follow ? acl_set_file(path, ACL_TYPE_EXTENDED, oacl) : acl_set_link_np(path, ACL_TYPE_EXTENDED, oacl)))

With this small modifications, the -h option should be respected. Rebuild chmod and try again:

  $ ./chmod -h +a "everyone allow delete" myLink
chmod: Failed to set ACL on file 'myLink': Operation not supported
$ ln -fs aFileThatDoesNotExist myLink
$ ./chmod -h +a "everyone allow delete" myLink
chmod: Failed to set ACL on file 'myLink': Operation not supported

Now, whether the link points to an existing file or not, we get the Operation not supported error. That's better diagnostic, but not exactly what we expected :-( So, why is it not supported? Let's dig a bit more. acl_set_link_np implementation is found at Libc-498.1.5/posix1e/acl_file.c:175 and is:

  return(acl_set_file1(path, acl_type, acl, 0));

The last argument (follow) passed to acl_set_file1 is 0 and the first lines of acl_set_file1 implementation reads:

  if (follow == 0) {
/* XXX this requires some thought - can links have ACLs? */
errno = ENOTSUP;
return(-1);
}

We have the explanation of the Operation not supported error we got earlier. Note that the comment is not mine, it is actually in the libc source code!

Does it mean it is impossible to set an ACL on a symlink? Does it mean our only option is to file a bug asking some Apple engineer to think harder if links can have ACLs? Fortunately no. There is a third function in the acl_set(3) API: acl_set_fd, which acts on a file descriptor. Hopefully, getting the file descriptor of a symlink is as simple as open(path, O_SYMLINK).

That's it. With this patch, you'll be able to run ./chmod -h +a "everyone allow delete" myLink and have the ACL to be set on the symlink!

If you need to set ACLs on symlinks on a daily basis, I suggest you do not overwrite /bin/chmod but install the patched version of chmod in /usr/local/bin. Well, if you read that post till there, you probably know that already.

This bug has been reported to Apple and is known as radar #6264303 which is a duplicate of radar #5684438.

Sunday, January 04, 2009

Using your own address book in the iPhone Simulator

Jailbroken iPhone with openssh:

scp mobile@iPhone:~/Library/AddressBook/* ~/Library/Application\ Support/iPhone\ Simulator/User/Library/AddressBook

Non-jailbroken iPhone:

Say bye bye to John Appleseed & Co.