Friday, December 4, 2009

Getting started with Objective-C and GNUstep on Linux

I couldn't find a simple one-stop guide by example, so here should be enough info to get you up and running with with Objective-C on Linux, including the obligatory hello world example. We will create a HelloWorld class, subclassed from the default object class, and show how to compile an example program.

Installation
This is specific to Arch, but you'll just need to install your distro's packages for gcc Objective-C support and the GNUstep base:

pacman -S gcc-objc gnustep-base

On Arch GNUstep installs to /opt/GNUstep.

Interface
We need to declare the interface for our class.

HelloWorld.h:

// use the GNUstep Foundation library
#import <Foundation/NSObject.h>

// declare interface for HelloWorld class as a subclass of NSObject
@interface HelloWorld : NSObject {

// instance variables
char *string;
}

// method definitions
- (id)setString: (char *)s;
- (char *)string;
- (void)speak;

// end interface declaration
@end


Implementation
Now to implement our simple class by writing the methods. Objective-C implementation files traditionally use the .m extension.


HelloWorld.m:

// include our interface declaration
#import <HelloWorld.h>

// start the implementation of the HelloWorld class
@implementation HelloWorld

// constructor
- (HelloWorld *)init
{
[super init]; // call init from superclass
[self setString: "hello world"]; // set default value
}

// accessor method to get value of string
- (char *)string
{
return(string);
}

// set string value
- (id)setString: (char *)s
{
string = s;
}

// print current value of string
- (void)speak
{
printf("%s\n", string);
}

// end the implementation
@end


Main
As with C, the entry point to our program is the main() function.

main.m

#import <HelloWorld.h>

int main (int argc, const char *argv[]) {

// create instance of our class
HelloWorld *hw = [[HelloWorld alloc] init];

// print the default
[hw speak];

// set new value and print
[hw setString: "goodbye"];
[hw speak];

// release memory
[hw release];

return 0;
}


Makefile
An example Makefile, showing required GNUstep libs. You will need to change the value of GNUSTEP to your distro's install location.

Makefile:

GNUSTEP=/opt/GNUstep
EXEC=hello
OBJS=HelloWorld.o main.o
LIBS=-lgnustep-base
LIBDIRS=-L${GNUSTEP}/Local/Library/Libraries
INCLUDES=-I${GNUSTEP}/Local/Library/Headers -I.

all: ${EXEC}

${EXEC}: ${OBJS}
gcc -o ${EXEC} ${OBJS} ${LIBDIRS} ${LIBS}

main.o: main.m
gcc -c ${INCLUDES} main.m

HelloWorld.o: HelloWorld.m HelloWorld.h
gcc -c ${INCLUDES} HelloWorld.m

clean:
rm -f ${OBJS} ${EXEC}


Compile and run

$ make
$ ./hello
hello world
goodbye


Further reading

Tuesday, November 24, 2009

Using URL parameters in Javascript

This is a simple function to parse parameters out of a URL and return them as properties of an object:

<script type="text/javascript">
// example URL: http://example.com/script.html?foo=hello+world&bar=goodbye

// function to parse the URL
function getParams (url) {

// split url into host/path and args, then split args into an array
var argstr = url.split("?")[1] || '';
var args = argstr.split("&");

var params = new Object;

// args will have len=1 even if split() returned empty, so fix that
var length = args[0] ? args.length : 0;

// loop args and load into object properties
for ( var i=0; i<length; i++ ) {
var p = args[i].split("=");
params[p[0]] = unescape(p[1]).replace(/\+/g, ' ');
}

return(params);
}

// send it our URL
var params = getParams(window.location.href);

// use the parameters
document.write("foo = " + params.foo + "<br>");
document.write("bar = " + params.bar + "<br>");

// output is:
// foo = hello world
// bar = goodbye
</script>

Loading Javascript libraries on demand

I needed to load the Google visualization API, but only needed it under certain conditions. Not wanting the overhead if not needed, I looked around for a way for Javascript to include external libraries. Sadly, it appears this facility is lacking, so I came up with the following hack.

// create the script element with no src set
<script language="Javascript" type="text/javascript" id="google_jsapi"></script>
...
// add the src to the script element
if ( some_condition ) {
document.getElementById('google_jsapi').src = 'http://www.google.com/jsapi';
}
...
// use the library
window.onload = do_stuff();

Monday, August 17, 2009

Font-lock for executable files in emacs dired-mode

I often see the face dired-face-executable in people's .emacs files, but it isn't used in the latest dired sources. So I added the following hook to colorize the filename for executable files:

(defface ric-dired-executable '((t (:foreground "PaleGreen"))) "Exe files in dired.")
(defvar ric-dired-face-executable 'ric-dired-executable)

(add-hook
'dired-mode-hook
'(lambda ()
(add-to-list 'dired-font-lock-keywords
(list dired-re-exe
'(".+" (dired-move-to-filename) nil (0 ric-dired-face-executable))))))

Wednesday, July 8, 2009

Different font sizes on different X screens

One of my trusty pair of Dell 1905FP monitors finally gave up the ghost. A little shopping around showed that, despite my luddite tendencies, 16:9 widescreen is the way forward. Today my shiny new ASUS VH222H arrived. It looks beautiful, and emacs is enjoying spreading out into the extra space.

However, my monitors are no longer a perfectly-matched pair. Trusty old Dell is plodding along at 1280x1024, and new ASUS on the block is showing off at a mind-expanding 1920x1080. Most of the time this is not an issue, since the Dell displays browsers almost all the of time, while almost everything else (i.e. emacs) happens on ASUS. For terminals on the Dell I have always used a 10-point font, but at higher resolution I need 12-point (I'm not getting any younger, I'd like to continue to see, and you kids get off my lawn).

Rather than using Xinerama or Twinview (I have an Nvidia card), I fire up a separate X screen on each monitor (:0.0 and :0.1). Since I use a tiling window manager (dwm) I never want to share windows across monitors, and this way I can switch desktops (tags) on one monitor without affecting the other. There is just one X server, however, so how to have different font sizes on different screens?

For reference, my window managers are started as follows from ~/.xinitrc:

DISPLAY=:0.1 dwm &
DISPLAY=:0.0 dwm


I set fonts in ~/.Xdefaults. Resources are loaded by xrdb, which preprocesses Xdefaults using the C pre-processor cpp. Using the pre-loaded symbol WIDTH we can test resolution and set font accordingly:

#if WIDTH >= 1600
#define FONT -b&h-lucidatypewriter-medium-r-normal-sans-12-*-*-*-*-*-*-*
#else
#define FONT -b&h-lucidatypewriter-medium-r-normal-sans-10-*-*-*-*-*-*-*
#endif

XTerm*font: FONT
Emacs*font: FONT


We could also test on the symbol SCREEN_NUM (see xrdb -symbols for all pre-loaded pre-processor symbols), but the above is more portable.

Finally, reload resources:

xrdb -load ~/.Xdefaults

Friday, July 3, 2009

Simple biff for gmail

Gmail publishes an atom feed for your inbox. This makes it possible to build a trivial client to show the number of unread messages in your inbox, for use in a statusbar (dwm/wmii bar, conky, etc).

The following shell one-liner does the job, but slowly:

wget -qO - https://username:password@mail.google.com/mail/feed/atom --no-check-certificate \
| egrep '[0-9]+' \
| sed -e 's/[^0-9]//g'


Here's a simple perl script that is about twice as fast:

#!/usr/bin/perl
use strict;
use warnings;

use XML::Atom::Client;

my $api = XML::Atom::Client->new;

$api->username('username');
$api->password('password');

my $feed = $api->getFeed('https://mail.google.com/mail/feed/atom')
or die ("Error: ", $api->errstr, "\n");

print(scalar($feed->entries)||0, "\n");


On ArchLinux this required the following packages: perl-xml-atom, perl-libwww, perl-digest-sha1, perl-datetime, perl-crypt-ssleay.

Using Unix date command for timezone conversion

I do a lot of work with developers in Bangalore, and am always wanting to do date calculations to figure out what time it is there. Using the TZ environment variable, it is easy to do this using plain old date (I am using 7.4 from GNU coreutils).

For example, what time is it right now in India?

$ TZ=Asia/Calcutta date
Sat Jul 4 00:40:41 IST 2009


I want to hold a meeting at 9am, what time will it be in India?

$ TZ=Asia/Calcutta date -d '9am EDT'
Sat Jul 4 18:30:00 IST 2009


There's a meeting on Friday at 4pm in India, what time will it be here?

$ date -d 'Fri 4pm IST'
Fri Jul 3 06:30:00 EDT 2009


For extra goodness, if you use zsh, do TZ=[TAB] and the shell will complete available timezones for you.

Friday, June 19, 2009

MacPorts registry error on upgrading gettext

MacPorts 1.7.0. I just ran:

sudo port upgrade outdated

and ran into the following problem:

---> Deactivating gettext @0.17_3
>> Error: Deactivating gettext 0.17_3 failed:
>> Error: Unable to upgrade port: dyld: Library not loaded:
>> /opt/local/lib/libintl.8.dylib
>> Referenced from: /opt/local/bin/ln
>> Reason: image not found

The same error occurs when I run ln, which is the GNU version /opt/local/bin/ln in my case. Native BSD versions like /bin/ln are unaffected.

The problem appears to be a port error caused by the port implementation shelling out to utilities like rm and ln without full paths, and getting GNU versions, since I have installed coreutils +with_default_names. Here's the fix. Edit the file /opt/local/etc/macports/macports.conf and set the MacPorts registry path to have BSD utils ahead of GNU utils:

binpath /bin:/sbin:/usr/bin:/usr/sbin:/opt/local/bin:/opt/local/sbin:/usr/X11R6/bin


Now you should be able to deactivate the old gettext, activate the new one, fix your coreutils, and complete the update:

sudo port deactivate gettext
sudo port activate gettext
sudo vi /opt/local/etc/macports/macports.conf ## return binpath to previous value
sudo port uninstall coreutils +with_default_names
sudo port install coreutils


Of course, now your coreutils are installed in /opt/local/bin with names like gls, grm, etc. On the whole, this appears to be the safer option. In cases where you really want to use the GNU version, something like this in your shell init file should do the trick:

case $(uname) in
Darwin)
if [ -x "$(which gdircolors)" ] ; then
eval `gdircolors ~/.dir_colors`
fi
if [ -x "$(which gls)" ] ; then
alias ls='gls --color=tty'
else
alias ls='ls -G'
fi
;;
Linux)
if [ -x "$(which dircolors)" ] ; then
eval `dircolors ~/.dir_colors`
fi
alias ls='ls --color=tty'
;;
esac

Thursday, June 18, 2009

Installing flash plugin on 64-bit Arch Linux

This post on the Arch Linux wiki is now out of date. There is a 64-bit flash plugin, and it is trivial to install:

# pacman -S flashplugin
# file /usr/lib/mozilla/plugins/libflashplayer.so
/usr/lib/mozilla/plugins/libflashplayer.so: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, stripped


Beautiful.

Wednesday, June 17, 2009

Posting to blogger from emacs using e-blog

g-client is a bit of a nightmare to get working. I plan to go back and play with it, since it sets up a lot of useful plumbing for google services. But for now, no go on the blog.

weblogger looks like it may work for other blogging services, but appears to have been broken by blogger API changes.

However, e-blog works great with a minimum of fuss. Simply install in a lisp directory, and add this to ~/.emacs:


(load-file '~/path/to/e-blog/e-blog.el')


Invoke with:

M-x e-blog-new-post


Here's a shortcut function:

(defun blog ()
(interactive)
(e-blog-new-post))

Tuesday, June 16, 2009

Juniper Network Connect VPN on 64-bit Arch Linux

There are several guides out there that describe running Juniper Network Connect VPN client on linux (see particularly MadScientist's guide for Ubuntu). These are just some notes on running the client from the command-line on a 64-bit Arch Linux system.

Since the netsvc client is 32-bit, we need to install some 32-bit support software. The method I use is to install 32-bit compatibility libs (effectively making my system multilib). Another solution would be to install and run netsvc from a 32-bit chroot environment.

To download Network Connect, connect to your VPN server with a browser and click the Network Connect Start button. This will download the software to ~/.juniper_networks.

Install 32-bit libs:

pacman -S lib32-glibc lib32-gcc-libs lib32-zlib


Extract your certificate:

cd ~/.juniper_networks
jar -xf ncLinuxApp.jar getx509certificate.sh
sh getx509certificate.sh your.vpn.server.com certfile


You can now run netsvc as follows:

netsvc -h your.vpn.server.com -u username -f certfile -r realm -p passwd


Your realm can be found from the HTML code of the server page. Look for something like:

<input name="realm" value="Your Realm" type="hidden">


The following simple script takes care of the whole thing:

#!/bin/bash
## Install as vpn.sh.
## Run as 'vpn.sh start' and input your password.
## It should be as simple as that.

## settings
HOST="your.vpn.server.com"
USER="YourUsername"
REALM="Your Realm"
JAR="/opt/java/bin/jar"
JUNIPER="${HOME}/.juniper_networks"
CERT="${JUNIPER}/network_connect/${HOST}.cert"
NCSVC="${JUNIPER}/network_connect/ncsvc"

start () {
## get passwd
read -s -p "Password: " passwd
echo ""

## get server certificate
pushd ${JUNIPER}
if [ ! -x "getx509certificate.sh" ]; then
${JAR} -xf ncLinuxApp.jar getx509certificate.sh
fi
sh getx509certificate.sh "${HOST}" "${CERT}" || die "failed get cert from $HOST"
popd

## run the network connect program
"${NCSVC}" -h "$HOST" -u "$USER" -f "$CERT" -r "$REALM" -p $passwd &
}

stop () {
## kill Network Connect
${NCSVC} -K
}

case "$1" in
start)
start
;;
stop)
stop
;;
restart)
stop
start
;;
*)
echo $"Usage: $0 {start|stop|restart}"
;;
esac