On a couple of occasions now I’ve wanted to read from STDIN into an Objective-C command line tool, and both times I’ve had to hunt quite a bit to find the answer because nothing shows up in google for the search terms I used. “Objective-c read from stdin” and “objc read stdin” both turn up results ranging from using NSInputStream to dropping some C++ in there.
The answer is quite simple really, just use NSFileHandle. More specifically +[NSFileHandle fileHandleWithStandardInput]. You can then read all data currently in STDIN, monitor it for new data and anything else you can do with a normal NSFileHandle.
And here’s some example code, reads all data from STDIN and stores it into an NSString:
Backstory: Got myself a first generation iPhone second hand and unlocked it to work on my existing T-Mobile (Official iPhone network in the UK is O2.) Noticed after a week or so of owning it that when you change the volume on the phone, the bezel that comes up says “ringer” across the top. But when you have headphones plugged in, it says “Headphones”. (Note the capitalisation difference.)
Now I’m not usually bothered by stuff like this (honest!) but as soon as I’d noticed the “bug”, I couldn’t help but think of it everytime I changed the volume, whether I was looking at the screen or not. Seeing as I’m running a jailbroken phone, and therefore have SSH access to it, I figured the string would be defined in a .strings file somewhere in the /System folder. And I’d be able to change it!
Fast-forward a few months and I install the iPhone OS 3.0 update (jailbroken of course), and finally decide to turn the phone’s SSH server on and go looking for the setting. To do so I figured I’d just need grep installed on the phone - I could copy the file itself to my mac and edit it there.
So I connect to the phone, have a poke around the filesystem and then start a search to find the correct file:
At which point I stopped the grep search (^C) because I know the home screen of the iPhone is the SpringBoard.app, so I figured it would be in the file SpringBoard.app/English.lproj/SpringBoard.strings. Making sure to have SSH enabled on your mac, a simple scp CoreServices/SpringBoard.app/English.lproj/SpringBoard.strings user@your_mac.local: later and the file is sat in my home folder on my mac.
Switching to the mac, now I try and open the file with TextMate, only to realise its in binary format. I need it in the nice XML format to edit it, so a quick google later and I’ve found a hint on MacOSXHints telling me how to convert from binary to xml plist format.
# On the mac
$ plutil -convert xml1 SpringBoard.strings
Then opening the file in TextMate was a bit more successful! I can actually understand what its defining now. Search through the file for “ringer” and I found the following lines:
Change the “ringer” to “Ringer” between the <string> and my editing work is complete! Yes, it really is that easy to edit an interface string that is defined in a .string. Now I just need to convert the file back to binary, and copy it back to the phone. Converting back to binary file is one line, just change the xml1 in the previous command to binary1.
# On the mac
$ plutil -convert binary1 SpringBoard.strings
And then scp it back to the phone, make a backup of the existing file, and overwrite the existing file with the new one I’ve edited:
# On the iPhone
$ cd ~
$ scp user@mac_name.local:SpringBoard.strings .
$ cd /System/Library/CoreServices/SpringBoard.app/English.lproj/
$ mv SpringBoard.strings SpringBoard.strings.bak
$ cp ~/SpringBoard.strings SpringBoard.strings
And then restart the phone, either in the usual manner or just run reboot on the phone via SSH. Lo and behold once its rebooted and I changed the volume, it read “Ringer”!
So I have this command in my $PATH, apachectl. Because I’m on a mac and I’ve installed apache2 through MacPorts, the command that gets found first is my macports install in /opt. Up until now I’ve always known that which apachectl will find that location, but to find any other locations of apachectl I’d usually use locate and egrep together.
Here’s my original workflow, lets find the location of the apachectl being called when I don’t specify a path.
Julius:~ caius$ which apachectl
Simple enough. Now lets figure out what other locations there’s an apachectl installed at.
Right, so now I know where else a command exists in the filesystem called apachectl, but I don’t know if any of those is in my $PATH, or what order they come in when searching through my $PATH. In this (old) workflow I’d have compared them to my $PATH manually as there’s so few of them.
So I noticed Ali googling for the which man page on IRC, and (quite stupidly) poked fun at him for doing so. I then swallowed my ego and actually followed the link to the man page, and boy was I glad I did. Just shows with even a fairly simple command like which, you sure don’t know everything!
What I discovered was that which has a single flag you can pass it, -a. From the man page:
-a print all matching pathnames of each argument
Right. So that locate | grep command plus manually figuring out what is in my $PATH is really hard work then. which -a should give us the same results, but a lot faster and with a lot less manual thought.
Julius:~ caius$ which -a apachectl
And hey presto, yet another useful bit of bash knowledge for me, thanks to Ali not being afraid to RTFM!
So, I keep having to reinstall mysql5 and rubygems from time to time for various reasons. I always install mysql5 through MacPorts as a dependency for the php5 port (along with various other bits for the LA*P stack).
sudo port install php5 +mysql5 +pear +readline +sockets +apache2 +sqlite
Once this is installed then I have mysql and can setup my databases, etc.
Ignoring the rest of the LAMP stack, I then need to connect Ruby to the Mysql I just installed through MacPorts. Its quite simple to do, once you know the right argument to pass to it. The easiest way is to just tell it where the mysql5_conf file is and let it figure out the rest for itself.
sudo gem install mysql -- --with-mysql-config=/opt/local/bin/mysql_config5
Hopefully this will save me 10 minutes of googling next time I need to do this!
I’m an idiot and typed the gem install command by hand, and ended up with --with-mysql-conf instead of --with-mysql-config. Updated now.
On Snow Leopard I needed to tell rubygems to install the gem as a 64-bit binary. Hattip to Philipp
sudo env ARCHFLAGS="-arch x86_64" gem install mysql -- --with-mysql-config=/opt/local/bin/mysql_config5
I finally wrapped up some code I’ve been meaning to write for a while, its a wrapper for the Google Translate API. Its also the first serious time I’ve used method_missing in a class, in this case its to add methods for translating between all the various languages.
Its fairly simple to use, there is an examples.rb included with it, but the basic usage is just this:
# Convert from english to french
Google::Translate.english_to_french( "Hello" ) # => "Bonjour"
# There is also a short(er)-hand version
Google::Tr.en_to_fr( "Hello" )
As per usual with all my code its available on my github account, as the GTranslate project. I’ll throw some specs together for it and package it up as a gem soon.