User Forum

Welcome

This is the homepage of Marc Liyanage.

You probably came here for the software in the Mac OS X Software category...

Album Artwork Assistant
Software — 17 Nov 2008 00:10 — 1 days ago
Album Artwork Assistant Application Icon

I just published a free new Mac OS X application. Say Hello to Album Artwork Assistant :-)

Album Artwork Assistant finds album cover artwork on the Internet and adds it to music track files in iTunes. Other tools like it exist, but none of them worked the way I wanted, or they cost too much.

Feel free to try it out if you need something like this, and let me know if you find any bugs.


Finder AppleScript: Eject Volume and Trash its Disk Image
Mac OS X — 29 Oct 2008 22:41 — 20 days ago

I wrote the AppleScript below to eject one or more selected volumes in the Finder. If a volume represents a disk image, the script trashes the image at the same time. I use this a lot with downloaded application distribution images.

I run the script with FastScripts with the keyboard short cut Cmd-Option-E. Unfortunately, there seems to be a Finder bug that makes the script take a very long time do finish.

set myVolumes to getSelectedVolumes()
if (count myVolumes) < 1 then return

set myImages to getImageList()

repeat with myVolume in myVolumes
	set myImagePath to ""
	set myImageFile to null
	repeat with myImage in myImages
		repeat with myEntity in |system-entities| of myImage
			try
				set mountPoint to |mount-point| of myEntity
				displayed name of myVolume
				if mountPoint = ("/Volumes/" & displayed name of myVolume) then
					set myImagePath to |image-path| of myImage
					set myImageFile to POSIX file myImagePath
				end if
			end try
		end repeat
	end repeat
	
	tell application "Finder"
		eject myVolume
		if myImageFile is not null then
			try
				delete file myImageFile
			end try
		end if
	end tell
end repeat



on getSelectedVolumes()
	set volumeList to {}
	tell application "Finder"
		set mySelection to selection
		repeat with myItem in mySelection
			set myItem to contents of myItem
			--			log class of myItem
			if kind of myItem is "Volume" then
				set end of volumeList to myItem
			end if
		end repeat
	end tell
	return volumeList
end getSelectedVolumes



on getImageList()
	set diskinfo to do shell script "hdiutil info -plist"
	tell application "System Events"
		set diskinfo to value of (make new property list item with properties {text:diskinfo})
		set imageList to |images| of diskinfo
	end tell
	return imageList
end getImageList
Sparkle Appcast Automation in Xcode
Developer — 22 Sep 2008 05:17 — 57 days ago

Recent versions of the Sparkle auto-update framework used by many Mac OS X applications require the publisher to protect their users from malicious attacks by either delivering application updates over SSL or digitally signing them, which is a good thing.

Signing involves a few steps with OpenSSL that need to be executed again for every release build. To make this more reliable and convenient, I automated the process with a custom script phase in my “distribution” Xcode target.

The first step is to generate a DSA key pair as documented on the Sparkle website. This gives you the private key in dsa_priv.pem and the public key in dsa_pub.pem.

The private key is sensitive and I did not want it lying around on the file system, so I added it to my keychain as a Secure Note in the Keychain Access utility:

Keychain Access New Secure Note menu item

Keychain Access DSA Key secure note content

For completeness, I also added the public key even though it is not sensitive, resulting in these two secure note items:

Secure note list items

I deleted the private key from the file system, moved the public key to the resources of my Xcode project and included its name in the Info.plist file under the SUPublicDSAKeyFile key, again as documented on the Sparkle Website.

In Xcode, I added a “Distribution” shell script target:

Xcode shell script target

This is the shell script code:

set -o errexit

[ $BUILD_STYLE = Release ] || { echo Distribution target requires "'Release'" build style; false; }

VERSION=$(defaults read "$BUILT_PRODUCTS_DIR/$PROJECT_NAME.app/Contents/Info" CFBundleVersion)
DOWNLOAD_BASE_URL="http://www.example.com/download/"
RELEASENOTES_URL="http://www.example.com/software/my-cool-app/release-notes.html#version-$VERSION"

ARCHIVE_FILENAME="$PROJECT_NAME $VERSION.zip"
DOWNLOAD_URL="$DOWNLOAD_BASE_URL/$ARCHIVE_FILENAME"
KEYCHAIN_PRIVKEY_NAME="Sparkle Private Key 1"

WD=$PWD
cd "$BUILT_PRODUCTS_DIR"
rm -f "$PROJECT_NAME"*.zip
zip -qr "$ARCHIVE_FILENAME" "$PROJECT_NAME.app"

SIZE=$(stat -f %z "$ARCHIVE_FILENAME")
PUBDATE=$(date +"%a, %d %b %G %T %z")
SIGNATURE=$(
	openssl dgst -sha1 -binary < "$ARCHIVE_FILENAME" \
	| openssl dgst -dss1 -sign <(security find-generic-password -g -s "$KEYCHAIN_PRIVKEY_NAME" 2>&1 1>/dev/null | perl -pe '($_) = /"(.+)"/; s/\\012/\n/g') \
	| openssl enc -base64
)

[ $SIGNATURE ] || { echo Unable to load signing private key with name "'$KEYCHAIN_PRIVKEY_NAME'" from keychain; false; }

cat <<EOF
		<item>
			<title>Version $VERSION</title>
			<sparkle:releaseNotesLink>$RELEASENOTES_URL</sparkle:releaseNotesLink>
			<pubDate>$PUBDATE</pubDate>
			<enclosure
				url="$DOWNLOAD_URL"
				sparkle:version="$VERSION"
				type="application/octet-stream"
				length="$SIZE"
				sparkle:dsaSignature="$SIGNATURE"
			/>
		</item>
EOF

echo scp "'$HOME/svn/my-cool-app/build/Release/$ARCHIVE_FILENAME'" www.example.com:download/
echo scp "'$WD/appcast.xml'" www.example.com:web/software/my-cool-app/appcast.xml

You have to set the shell to /bin/bash because this code uses features that are not available otherwise.

This script:

  • enforces a Release build
  • creates a .zip file of the application. You could also package up a .dmg with additional material.
  • fetches the signing private key from the keychain. This step might pop up a dialog: keychain access dialog
  • calculates the SHA1 checksum of the distribution file
  • signs the checksum with the private key
  • converts the signature to Base64
  • emits an item block with all the information (date, size, version) about the update. You add this block to your appcast XML file.

I also let it print out the scp commands required to publish the update.

Xcode Docs: Get Rid of Downloaded From Internet Warnings
Developer — 19 Aug 2008 12:47 — 91 days ago

When I work on Cocoa programs I read the framework documentation with Xcode’s built in documentation viewer, but sometimes I still open the HTML page for a particular class in Safari with LaunchBar because it is very quick (see this older post).

Xcode downloads these documentation sets from the Internet, and unfortunately all of the thousands of files in there are tagged with the “quarantine” extended file system attribute. This means that the system warns you about potentially harmful content every time you open one of the .html files outside of Xcode, such as in the LaunchBar case.

To get around this, you can clear the quarantine attribute on all files with this command:

find /Developer/Documentation -type f -print0 | xargs -n 100 -0 sudo xattr -d com.apple.quarantine
Terminal Tricks: A Fuzzy cd Command
Mac OS X — 14 Aug 2008 07:19 — 96 days ago

Here’s another trick to speed up file system navigation in the shell. A previous tip showed how to use bookmarks to reach deeply nested directories effectively.

I use that a lot, but I frequently use it to enter directories with a large number of subdirectories whose names are long and share a prefix:

...
Arteria-Media-ImageEditor
Arteria-Media-ImageEditor-Base
Arteria-Media-ImageRep-Magick
Arteria-Media-JSONClient
Arteria-Media-JSONSegmenter
Arteria-Media-Webapp-MediaServer
...

I wanted a way to enter one of these directories by typing only a minimal, unique substring of its name (like typing *substring* but only considering directories and ignoring case), so I wrote this shell function:

function c {
	shopt -q nocasematch || resetcase=1
	shopt -s nocasematch
	for i in *; do [ -d "$i" ] && [[ "$i" == *"$1"* ]] && cd "$i" && break; done
	[ $resetcase ] && shopt -u nocasematch
}

Now I can type

c ncl

and, in the example directory above, it takes me directly into the Arteria-Media-JSONClient subdirectory.

Open Terminal Here Finder Keyboard Command
Mac OS X — 7 Aug 2008 12:38 — 103 days ago

A while ago I published a Finder toolbar AppleScript called “Open Terminal Here” to open a terminal window in the directory displayed by the frontmost Finder window or the Finder selection or the files/folders dropped onto the icon.

However, these days I find myself using a similar script triggered by a keyboard shortcut much more often. I assigned it to Cmd-T which is very convenient and quick. As always, I use FastScripts to do the keyboard launching.

Here’s the script:

 tell application "Finder"
	set sel to selection
	if (count sel) > 0 then
		my openTerminal(item 1 of sel)
	else
		my openTerminal(target of window 1)
	end if
end tell

on openTerminal(location)
	set location to location as alias
	set the_path to POSIX path of location
	repeat until the_path ends with "/"
		set the_path to text 1 thru -2 of the_path
	end repeat
	tell application "Terminal"
		activate
		do script "cd " & quoted form of the_path & " && echo $'\\ec'"
	end tell
end openTerminal 
Open this script in Script Editor
Spook Country
Books/ — 28 Jun 2008 07:12 — 143 days ago

Just finished William Gibson’s latest novel, Spook Country. I liked it but if you plan to read it you probably should read “Pattern Recognition” first.

Terminal Tricks: “term” revisited, with tabs
Mac OS X — 27 Jun 2008 07:59 — 144 days ago

Back in 2005 I published a little shell script called “term”. It clones the current Mac OS X Terminal session (working directory) into a new window, or executes a command in a new window. See the old posting for some usage examples.

Here’s a new version that adds a -t option to open a new tab instead of a new window and the ability to specify window position and size:

#!/bin/sh
#
# Open a new Mac OS X terminal window or tab in the current or another
# directory and optionally run a command in the new window or tab.
#
# - Without any arguments, the new terminal window opens in
#   the current directory, i.e. the executed command is "cd $PWD".
# - If the first argument is a directory, the new terminal will "cd" into
#   that directory before executing the remaining arguments as command.
# - The optional "-t" flag executes the command in a new tab 
#   instead of a new window.
# - The optional "-x" flag closes the new window or tab
#   after the executed command finishes.
# - The optional "-p" flag takes an argument of the form x,y (e.g. 40,50) and
#   positions the terminal window to the indicated location on the screen
# - The optional "-s" flag takes an argument of the form w,h (e.g. 800,400) and
#   resizes the terminal window to the indicated width and height in pixels.
#
# Written by Marc Liyanage <http://www.entropy.ch>
#
# Version 2.1
#

set -e

while getopts xtp:s: OPTION; do
[ $OPTION = "x" ] && { EXIT='; exit'; }
[ $OPTION = "t" ] && { TAB=1; }
[ $OPTION = "p" ] && { POSITION="set position of window 1 to {$OPTARG}"; }
[ $OPTION = "s" ] && { SIZE="set size of window 1 to {$OPTARG}"; }
done

for (( $OPTIND; $OPTIND-1; OPTIND=$OPTIND-1 )); do shift; done

if [[ -d "$1" ]]; then WD=$(cd "$1"; pwd); shift; else WD=$PWD; fi


COMMAND="cd '$WD' && echo -n \$'\\\\ec';"
for i in "$@"; do
	COMMAND="$COMMAND '$i'"
done

if [ $TAB ]; then

osascript 2>/dev/null <<EOF
	tell application "System Events"
		tell process "Terminal" to keystroke "t" using command down
	end
	tell application "Terminal"
		activate
		do script with command "$COMMAND $EXIT" in window 1
		$POSITION
		$SIZE
	end tell
EOF

else

osascript <<EOF
	tell application "Terminal"
		activate
		do script with command "$COMMAND $EXIT"
		$POSITION
		$SIZE
	end tell
EOF

fi
del.icio.us Links on 2008-06-14
Links — 14 Jun 2008 23:24 — 157 days ago
Things I Learned About My Dad
Books/ — 14 Jun 2008 10:45 — 157 days ago
Book: Things I Learned About My Dad

I can’t quite remember where I read the tip about this book, it must have been some blog.

Although not all essays are of the same quality, it’s a good and fun read overall...

Compiled by Heather B. Armstrong, award-winning publisher and uber-mistress of the phenomenally popular dooce.com®, this hilarious and heartwarming celebration of "everything dad" features original stories from some of the country's most celebrated bloggers, including Alice Bradley (Finslippy) Doug French (Laid Off Dad), Maggie Mason (Mighty Girl), Matthew Baldwin (Defective Yeti), Sarah Brown (Que Sera Sera), and more.

From a new father's comparison of pregnancy to Tolkien's Lord of the Rings, to a mother's story of bravely surviving a husband-son infatuation with Star Wars, to the mini triumphs and tragedies of toddlerhood, this book provides a unique, no-holds-barred glimpse into the quirks and candid moments of modern dads.

Whether we relish or fear growing up to be like our fathers...whether we've inherited his nose, sense of humor, or entire value system, our dads loom large in who we are and the choices we make. Things I Learned about my Dad in Therapy touches upon the many joys and discoveries of fatherhood, one essay at a time.

Exclude Items From Time Machine Backup With Contextual Menu
Mac OS X — 14 Jun 2008 09:12 — 157 days ago

I often download huge files that I only need temporarily, things like installer packages and disk images. They end up on my desktop because that is my download folder and quite often they also end up in the Time Machine backup, wasting a lot of space there.

I wanted a contextual menu command to exclude these files from the backup in the Finder so I wrote this AppleScript:

on main(filelist)
    set target to quoted form of (POSIX path of (item 1 of filelist as alias))
    do shell script "xattr -w com.apple.metadata:com_apple_backup_excludeItem com.apple.backupd " & target
end main

It adds extended attributes to the file telling Time Machine to ignore it. I got this information from an excellent article about Time Machine.

To run the script as contextual menu in the Finder I use Ranchero Software’s free Big Cat utility. After installing that and storing my script in $HOME/Library/Application Support/Big Cat Scripts/Files, I have a new entry in the Finder contextual menu for files:

Finder Contextual Menu AppleScript

The only problem is that custom contextual menu items are in a “More” submenu on Leopard, which moves the Big Cat scripts down to the inconvenient third level.

Do You Speak Orange...
Miscellaneous — 11 Jun 2008 12:31 — 160 days ago

Do You Speak Orange...

Drink, Don’t Drive
Miscellaneous — 5 Jun 2008 12:24 — 166 days ago

BEER - Now cheaper than gas! Drink, don’t drive

UNIX Tool Tip: cdargs
Mac OS X — 3 Jun 2008 14:22 — 168 days ago

A coworker just showed me the useful UNIX tool cdargs. It provides bookmarks for often used directories in the shell.

You can do something similar with shell aliases and pushd/popd, but I find this far more convenient.

It doesn’t seem to be in MacPorts, so I downloaded, compiled and installed the source package with these commands:

$ curl -s http://www.skamphausen.de/software/cdargs/cdargs-1.35.tar.gz | tar -xzf -
$ cd cdargs-1.35/
$ ./configure && make
$ sudo make install
Password:

$ cp contrib/cdargs-bash.sh /usr/local/bin/

$ echo ". cdargs-bash.sh" >> ~/.bashrc

Reopen the terminal window and you’ll get two new commands:

ca
creates a bookmark of the current directory
cv
displays the list of bookmarks and changes the directory if you pick one.

Here’s cdargs in action:

A bookmark consists of a directory path and a shortcut word. ca without argument uses the current directory name as shortcut. Sometimes that’s not what you want so you can explicitly choose the shortcut by passing it as argument:

ca arteria

cv without argument shows a list of all bookmarks and lets you choose a directory to change to. With a shortcut as argument, it changes immediately to that directory. This usage supports completion of the shortcut so you only have to type an unambiguous prefix and hit the tab and return keys.

The list view can do a few more things, for example

  • it supports vi-style h/j/k/l navigation
  • you can drill down into a bookmarked directory with the arrow or “l” key to enter some subdirectory directly
  • typing “v” edits the list in your text editor of choice
  • typing “d” deletes a bookmark from the list

Update 2008-08-31: I just submitted a port for cdargs to MacPorts, hopefully you can soon get it from there.

Pimp My BBEdit: Execute Scripts on Wakeup With SleepWatcher
Mac OS X — 28 May 2008 19:55 — 174 days ago

This is starting to look like a BBEdit blog :-) Can you tell I spend most of my working hours in this program... I guess I should have a category for it.

Anyway, a few weeks ago I posted a script to move BBEdit’s palettes to the screen edge. After a while, I got tired of executing it manually. My screen resolution usually changes when the machine wakes from sleep, so I looked for utilities that run AppleScripts on wakeup and found the fantastic SleepWatcher, a command line tool that does exactly what I want.

I installed it, along with its startup item. Then I created a .wakeup file in my home directory, made it executable and put this in it:

#!/bin/sh

ps ax | grep -v grep | grep -q BBEdit && osascript ~/Library/Scripts/Applications/BBEdit/Move\ Palettes\ to\ Screen\ Edge.scpt

The other situation where the script should run automatically is when BBEdit is launched, because it is likely that the screen resolution was different when it quit. BBEdit remembers the palette positions and puts them in the wrong spot.

Fortunately, BBEdit can do exactly that. It runs scripts in the $HOME/Library/Application Support/BBEdit/Startup Items folder when it starts so I put an alias pointing to the script there and it works perfectly.

Powered By Mac OS X Powered By blojsom
12. July 2007