Archive for the 'Code snippets' Category

Installing things to /usr/bin – Trampolines

Thursday, January 10th, 2008

Some Mac OS X applications can install useful tools to use with Terminal. PDFKey Pro is one of those applications. Currently I simply installed a binary called “pdfkeypro” in /usr/bin via a .pkg file. This had the obvious draw back of needing the user upgrade it manually on every release.

Some applications, like the excellent TextMate place a symlink to an executable inside the application bundle, solving said problem. But introducing another one: as soon as the application bundle is moved, the symlink breaks.

As I am about to update the command line tools for PDFKey Pro, I started thinking about a better way to install said binaries. One which would update automatically when the main bundle is updated, but wouldn’t break if said bundle is moved in another location.

Trampolines to the rescue! Instead of installing the real tools I install 2 trampoline applications in /usr/bin called respectively pdflock and pdfunlock. The trampoline then uses NSWorkspace to locate the bundle, and starts the real executable found in the application’s bundle. The code of a trampoline looks like this:

int main(int argc, char *argv[]) 
{
	NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
	NSWorkspace *env = [NSWorkspace sharedWorkspace];
	NSString *app = [env absolutePathForAppBundleWithIdentifier:@"com.pdfkey.pdfkeypro"];
	NSString *targetPath = [[app stringByAppendingPathComponent:@"Contents/Resources/pdflock"] retain];

	const char *CStringPath = [targetPath UTF8String];
	[pool release];

	execv(CStringPath, argv);

	// You reach this code only if execv returns, which means that something wrong happened.
	[targetPath release];
	printf("PDFKey Pro is not installed. Please download it from http://pdfkey.com\n");
	return 0;
}

I can already read the comment that I could use argv[0] instead of hardcoding the application name. That’s true, but this trampoline once written should not be ever modified again and I want it to perform the minimum number of instructions required to work.

You can use this code liberally to make your very own trampoline, consider this to be of public domain. You can also find it at codebeach.

As a side note, I wonder why most application installing things in /usr/bin usually have their very own installer. Why wouldn’t a .pkg do? This is a sincere question, not an attempt to flame. In PDFKey Pro all I do is bundle a .pkg file and provide a menu item which launches it. It couldn’t be easier!

Oops it crashed again (BSCrashNotifier)

Saturday, September 22nd, 2007

hey there,

there was a time, i think like 2 betas ago, where CuteClips was kinda instable. The current beta is rock solid compared to the previous one. The reason for this instability is that the preview if certain clips was implemented wrong. But it is hard to track bugs down when you can’t reproduce the crash.

What I needed was the data that CuteClips failed to process, so I needed a simple way to have people send me their data if CuteClips crashed. I created the BSJobList and the user was easily able to send me the crashlog and/or the clip-data.

That part works, people are sending me a lot of crash reports now. The downside (or upside) is that these crash reports are not current. That is, they are from crashes of previous versions. The problem that has arisen is that CuteClips thinks that it crashed, while maybe the whole system crashed. How should it know? it simply checks if it was quit properly.

So the obvious solution to track crashes, by setting some preference value to

YES

when your app quits is not the best idea. It is simple, and it’s working, but it has too many false positives.

I came up with another approach… instead of storing that we didn’t crash, lets do it the right way and store that we actually did crash.

I searched around a bit and soon found out that

signal()

should be the right point to start working. I tried to add my own signal handler and it worked just fine, i was notified when the crash happend. The downside was that I didn’t see the crashlog anymore. The fix was kinda simple, you just remove the signal handler and raise the signal again.

So registering the signal handler is done with:

	signal(SIGBUS,crashHandler);
	signal(SIGSEGV,crashHandler);

and the crashHandler looks like this:

void crashHandler(int num)
{
	printf("we just crashed with signal: %i",num);
	// reset the old signal handler
	signal(num,0);
	raise(num);
}	

yup, it’s that simple… and to make things even better, I created a bundle from it and uploaded it to Codebeach, so that you can just add the bundle to your application and you’re all set. The usage is fairly simple, just use this code:

NSBundle* bundle = [NSBundle bundleWithPath:[[NSBundle mainBundle] pathForResource:@"BSCrashNotifier" ofType:@"bundle"]];
Class crashNotifierClass = [bundle principalClass];
if (crashNotifierClass)
{
	[crashNotifierClass onCrashSend:@selector(weCrashed:) to:self];
}
else
{
	NSLog(@"couldn't load bundle");
}

and:

- (void)weCrashed:(int)signalNumber
{
	NSLog(@"we crashed: %i",signalNumber);
}

[BSCrashNotifier on CodeBeach.org]

Have fun
Karsten

p.s. I just wrote this post in MarsEdit, nice tool, works great

BSJobList

Saturday, July 21st, 2007

Last weekend I wanted to add some kind of list with checkboxes to CuteClips in order to execute a list of tasks… it evolved into BSJobList which is a very easy way to open a window and ask for things that need to be performed. After OK is clicked, all these jobs are executed in the order they are displayed.

The usage is pretty easy:

  • load the bundle BSJobList.bundle

    NSBundle* bundle = [NSBundle bundleWithPath:[[NSBundle mainBundle] pathForResource:@"BSJobList" 
    																			ofType:@"bundle"]];
    Class jobListClass = [bundle principalClass];
    
    
  • create an array with jobs:

    NSArray* actionsBig = [NSArray arrayWithObjects:
    	[BSJob jobWithName:@"order me a pizza" action:@selector(callForHelp) target:self position:1],
    	[BSJob jobWithName:@"order me a coke" action:@selector(orderACoke) target:self position:2],
    	nil];
    

    of course the selectors have to be implemented in self 😉

  • ask the BSJobList class to run the jobs and give it nice title

    	
    	[jobListClass askToRunJobs:actions withTitle:@"What should i order?"];
    

    this method call returns YES or NO, if you want to know if the user hit Cancel or Ok

The result should look like this:

Bsjob List Screenshot

you can download it from: CodeBeach

Have fun!
Karsten

How to change your window tags: Camouflage 1.7

Wednesday, December 20th, 2006

New version of Camouflage has just been released today. On of the new things was have the windows not fading out while Exposé is used. To add this functionality is quite interesting, but not so easy when using an undocumented API. This post will show how to use that API and why it’s not really difficult to change the window tags.

(more…)

Making NSTextField REALLY single line

Tuesday, December 12th, 2006

One thing i hate of the NSTextField is that while you can’t use the “enter” key to insert newlines characters you still can insert them, for example, by copying in them text which contains newlines characters. This is plain annoying sometimes! I made this NSFormatter that can be attached to an NSTextField and will just strip the newlines found in input, so if you paste multiple lines they will all be merged on a single line.

Download the BRSingleLineFormatter Class

Checking invisibility of a file

Friday, December 8th, 2006

This NSWorkspace extension add the method “isInvisibleFileAtPath:” to NSWorkspace class. To use it just call that method with a valid pathname you will return a boolean telling you if the file is invisible or not. This method is the union of many partial ways of checking if a file is invisible, which are available on forums. This was needed because Mac OS X has many way of making a file invisible. Note that some paths are hardcoded. Those files apparently do not have any invisible flag, yet they are invisible in Finder.

Download NSWorkspace “isInvisibleFileAtPath:” Extension

Converting byte sizes into human readable strings

Friday, December 8th, 2006

The attached class is an NSFormatter subclass and is useful for taking a size expressed in byte like “2048” and convert it to an human readable string like “2 KB”. It supports unit of measures up to the Terabyte. Note that this class assumes it receives as input an NSNumber or any object that responds to floatValue. Backward translation is also not implemented (and wouldn’t probably make sense as informations are going to be lost anyways).

Download BRByteFormatter Class