Tuesday, January 19, 2010

I just found came across the glims plugin for Safari, and thus far I'm very please with it.

It gives me the two things I was looking for in a replacement for SAFT, automatic reopening of the previous session and reopening the last closed tab. And it's free.

I did find that I needed to do a bit of configuring of the preferences upon the first launch before I was really happy with it. The biggest thing I changed was that by default it will open all tabs from the previous session in a single window, whereas I prefer them to open using the same window layout they had upon close (the way Reopen All Windows from Last Session does).

Wednesday, December 23, 2009

Enabling nested VMs in VMWare Fusion 2.0

I often segregate my more esoteric development environments into their own individual VM to avoid polluting my host machine and so they're portable from my desktop to my laptop and back.

I recently came across a situation where I needed to run one VM inside another (The simulator for the device I was developing for).

Normally if you attempt to do this you will get a message stating "You may not power on a virtual machine in a virtual machine". With some minor modifications to your outer VM's .vmx file, however, you can get around this restriction.

The best document I found detailing how to do this on a variety of VMWare versions is here.

The relevant lines to add to the outer VM's .vmx file for my situation (VMWare Fusion 2.0) are:

monitor.virtual_exec = hardware
monitor_control.restrict_backdoor = TRUE

Using a setup of Mac OS X 10.6.2 with VMWare Fusion 2.0 installed running an XP VM with VMWare Player installed running a specialized linux-based simulator OS the instructions in the above link seem to enable nested VMs successfully. I would note that the outer VM requires a lot of RAM and that VMWare Tools will not run (meaning you lose the ability to freely resize the VM window, copy/paste, drag files in and out of the VM, etc).

Wednesday, December 9, 2009

Python extended slice syntax

I discovered a new piece of Python syntax the other day.

In a code golf problem on StackOverflow, I came across somebody using the following code to reverse a string (paraphrased).

# Prints "gnirts ym si sihT"
mystring = "This is my string"
print(mystring[::-1])

I've been familiar with Python's slice syntax for quite some time (e.g. "print(mystring[5:7])" prints "is"), but multiple colons was a new one to me.

It turns out that this is referred to as extended slice syntax, and is documented here.

In summery, the first two arguments remain the start and end indexes, and the third argument is a step. Positive values step forward and negative values step backward.

More examples:

# Note that the indexes are reversed and decreased by one 
# from the forward stepping example above
# Prints "si"
mystring = "This is my string"
print(mystring[6:4:-1])
# Reversing a substring can also be accomplished this way, 
# which preserves the original indexes
# Prints "si"
mystring = "This is my string"
print(mystring[5:7][::-1])
# Prints "Ti sm tig" (every other letter)
mystring = "This is my string"
print(mystring[::2])

The link above contains much more extensive and complex examples, should you wish to peruse them.

Monday, December 7, 2009

Issues booting Mac mini from retail Leopard DVD

Just a heads up for anybody out there troubleshooting the same issue, the 2.0 Ghz Intel Mac Minis with product number MB463LL/A may not to be able to boot from a retail Leopard install DVD (the machine in question was purchased around the time Snow Leopard came out).

At least in my case, when I attempt to boot such a machine from a retail Leopard DVD every time it gets to a certain point it just restarts.

The Leopard restore disk they ship with do boot them successfully, however.

Tuesday, November 24, 2009

Editing SVN Repository URL in a working copy

Recently XP-Dev, which offers free SVN repository hosting that I utilize primarily for versioning and synchronizing various preferences, templates, and settings files between computers, decided to remove HTTPS access for free users.

Since all my working copies were set up to use the https:// URL, this meant that they no longer functioned once the HTTPS access was removed.

The repository URL is stored in each .svn/entries file inside every directory inside the working copy and since I didn't feel like going through and editing them all by hand I came up with a one-liner to do it for me.

This is meant to be run from the root of the working copy and will save a copy of all the files it changes using the extension ".old".

find . -name entries -exec sed -i.old 's|OLD_URL|NEW_URL|g' {} \;

You may want to first run this similar one-liner using grep just to make sure you're not going to change anything unexpected:

find . -name entries -exec grep 'OLD_URL' {} \;

Tuesday, July 28, 2009

Python script for generating @property and @synthesize directives for instance variables in Xcode

I created the following script to generate @property and @synthesize directives for instance variables in Xcode.

To set up the script:
  1. Open up the Xcode scripts menu (it looks like a scroll on the menu bar).
  2. Select "Edit User Scripts..."
  3. Chose the group you want to create the new script in, creating it if neccessary (using the "+") button in the bottom left.
  4. In the Edit User Scripts window that comes up click the "+" button in the bottom left corner.
  5. Choose "New Shell Script".
  6. Enter an appropriate name for the script (I use "Generate Properties").
  7. Paste the script below into the right hand pane.
  8. Change the "Input" to "Entire Document".
  9. Optionally, change "Errors" to "Display in Alert".
  10. Assign a keyboard shortcut to the script, if desired.

To use the script:
  1. Select the line(s) with the instance variable(s) you wish to generate @property and @synthesize directives for.
  2. Either use the keyboard shortcut you designated in step 10 above, or click on the Script menu in the menu bar in Xcode, find the group you created the new script in, and choose the Script to invoke it.

Some notes:
  • The script supports creating @property and @synthesize directives for Google-style instance variables with a trailing underscore (e.g. myInstanceVariable_).
  • It supports a fair amount of variation in white-space and asterisk placement in the input but by default outputs using a single space between identifiers and the asterisk if present immediately preceding the name.
  • If the property is a pointer, it uses the copy attribute. If you would prefer to use retain, change the copy to retain in
    pointerPropertyAttributes = "(copy) " # Attributes if variable is pointer

And finally, the script itself:
#!/usr/bin/python

#---------------------------------------------------------------------
# DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
# Version 2, December 2004
#
# Copyright (C) 2009 Lawrence Johnston
# www.cogentcocoa.com, Lawrence.B.Johnston (at) gmail (dot) com
#
# Everyone is permitted to copy and distribute verbatim or modified
# copies of this license document, and changing it is allowed as long
# as the name is changed.
#
# DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
# TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
#
# 0. You just DO WHAT THE FUCK YOU WANT TO.

# This program is free software. It comes without any warranty, to
# the extent permitted by applicable law. You can redistribute it
# and/or modify it under the terms of the Do What The Fuck You Want
# To Public License, Version 2, as published by Sam Hocevar. See
# http://sam.zoy.org/wtfpl/COPYING for more details.
#---------------------------------------------------------------------

# Takes a header file with one or more instance variables selected
# and creates properties and synthesize directives for the selected properties.

# Accepts google-style instance variables with a tailing underscore and
# creates an appropriately named property without underscore.

# Xcode script options should be as follows:
# Entire Document
# Home Directory
# Discard Output
# Display in Alert

import os
import re
import subprocess

# AppleScripts for altering contents of files via Xcode
setFileContentsScript = """\
on run argv
set fileAlias to POSIX file (item 1 of argv)
set newDocText to (item 2 of argv)
tell application "Xcode"
set doc to open fileAlias
set text of doc to newDocText
end tell
end run \
"""

getFileContentsScript = """\
on run argv
set fileAlias to POSIX file (item 1 of argv)
tell application "Xcode"
set doc to open fileAlias
set docText to text of doc
end tell
return docText
end run \
"""

# Get variables from Xcode
headerFileText = """%%%{PBXAllText}%%%"""
selectionStartIndex = %%%{PBXSelectionStart}%%%
selectionEndIndex = %%%{PBXSelectionEnd}%%%
selectedText = headerFileText[selectionStartIndex:selectionEndIndex]

headerFilePath = """%%%{PBXFilePath}%%%"""

# Look for an implementation file with .m or .mm extension
implementationFilePath = headerFilePath[:-1] + "m"
if not os.path.exists(implementationFilePath):
implementationFilePath += "m"

instanceVariablesRegex = re.compile(
"""^\s*((?:(?:\\b\w+\\b)\s+)*(?:(?:\\b\\w+\\b)))\\s*""" + # Identifier(s)
"""([*]?)\\s*""" + # An optional asterisk
"""(\\b\\w+?)(_?\\b);""", # The variable name
re.M)

# Now for each instance variable in the selected section
properties = ""
synthesizes = ""

for lineMatch in instanceVariablesRegex.findall(selectedText):
types = " ".join(lineMatch[0].split()) # Clean up consequtive whitespace
asterisk = lineMatch[1]
variableName = lineMatch[2]
trailingUnderscore = lineMatch[3]

pointerPropertyAttributes = "(copy) " # Attributes if variable is pointer
if not asterisk:
pointerPropertyAttributes = ""

newProperty = "@property %s%s %s%s;\n" % (pointerPropertyAttributes,
types,
asterisk,
variableName)

# If there's a trailing underscore, we need to let the synthesize
# know which backing variable it's using
newSynthesize = "@synthesize %s%s;\n" % (variableName,
trailingUnderscore and
" = %s_" % variableName)

properties += newProperty
synthesizes += newSynthesize

# Check to make sure at least 1 properties was found to generate
if not properties:
os.sys.stderr.writelines("No properties found to generate")
exit(-1)

# We want to insert the new properties either immediately after the last
# existing property or at the end of the instance variable section
findLastPropertyRegex = re.compile("^@interface.*?{.*?}.*?\\n" +
"(?:.*^\\s*@property.*?\\n)?", re.M | re.S)
headerInsertIndex = findLastPropertyRegex.search(headerFileText).end()

# Add new lines on either side if this is the only property in the file
addedNewLine = "\n"
if re.search("^\s*@property", headerFileText, re.M):
# Not the only property, don't add
addedNewLine = ""

newHeaderFileText = "%s%s%s%s" % (headerFileText[:headerInsertIndex],
addedNewLine,
properties,
headerFileText[headerInsertIndex:])

subprocess.call(["osascript",
"-e",
setFileContentsScript,
headerFilePath,
newHeaderFileText])


if not os.path.exists(implementationFilePath):
os.sys.stdout.writelines("No implementation file found")
exit(0)

implementationFileText = subprocess.Popen(
["osascript",
"-e",
getFileContentsScript,
implementationFilePath],
stdout=subprocess.PIPE).communicate()[0]

# We want to insert the synthesizes either immediately after the last existing
# @synthesize or after the @implementation directive
lastSynthesizeRegex = re.compile("^\\s*@implementation.*?\\n" +
"(?:.*^\\s*@synthesize.*?\\n)?", re.M | re.S)

implementationInsertIndex = \
lastSynthesizeRegex.search(implementationFileText).end()

# Add new lines on either side if this is the only synthsize in the file
addedNewLine = "\n"
if re.search("^\s*@synthesize", implementationFileText, re.M):
# Not the only synthesize, don't add
addedNewLine = ""

newImplementationFileText = "%s%s%s%s" % \
(implementationFileText[:implementationInsertIndex],
addedNewLine,
synthesizes,
implementationFileText[implementationInsertIndex:])

subprocess.call(["osascript",
"-e",
setFileContentsScript,
implementationFilePath,
newImplementationFileText])

# Switch Xcode back to header file
subprocess.Popen(["osascript",
"-e",
getFileContentsScript,
headerFilePath],
stdout=subprocess.PIPE).communicate()