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
And here’s some example code, reads all data from
STDIN and stores it into an NSString:
NSFileHandle *input = [NSFileHandle fileHandleWithStandardInput];
NSData *inputData = [NSData dataWithData:[input readDataToEndOfFile]];
NSString *inputString = [[NSString alloc] initWithData:inputData encoding:NSUTF8StringEncoding];
I’m using this in GarbageCollected apps, memory management without GC is left as an exercise to the user.
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:
# On the iPhone
$ cd /System/Library/
$ grep -r "ringer" *
Binary file CoreServices/SpringBoard.app/English.lproj/SpringBoard.strings matches
Binary file CoreServices/SpringBoard.app/M68AP.plist matches
Binary file CoreServices/SpringBoard.app/SpringBoard matches
Binary file Frameworks/CFNetwork.framework/CFNetwork matches
Binary file Frameworks/CFNetwork.framework/da.lproj/Localizable.strings matches
Binary file Frameworks/CFNetwork.framework/no.lproj/Localizable.strings matches
Binary file Frameworks/Foundation.framework/da.lproj/URL.strings matches
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
# 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
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.
Julius:~ caius$ locate apachectl | egrep "\/apachectl$"
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.