Here’s the problem. Perl? Great. Love the Perl. CPAN modules? Not so great. Well, yes, they are *great* in a they-kick-ass-in-all-the-functionality-they-provide, but they’re not installed on the default Mac OS X user’s machine. So not so great. Sure, Apple includes the command line cpan utility (man 1 cpan), but don’t you want utilities that just, you know, work? Without any further laborious installation of packages?

Because Perl provides the perfect syntactic leavening for Dashboard widgets and AppleScript Studio Applications, I wanted a away around this. If you want to use Perl to do things like scrape Webpages and sort through XML, and things like that, you need XML processing that’s free of CPAN constraints. And OS X does not provide a built-in Perl XML parser as you might have noticed.

I tried using external command-line XML packages. They were all too much of a pain: not lightweight enough, wanting to install themselves into strange locations. Yuck.

So here’s what I ended up doing. I wrote a C-based libxml utility to create working (but half-assed) Perl hashes from an XML file. Hence, lightweight and portable. Yeah sure, it mostly sucks. But it does seem to work.

Here’s a real-life example. The following Perl utility scans your /tmp directory while Pandora is playing and lists the current and upcoming songs in a playlist. Simple, short and easy to integrate into a Dashboard widget. As with all my code: It is what it is. Use at your own risk. Don’t sue.

Got a better way? I want to know.

% cd Desktop
% ./playlist.pl
Laura by Billy Joel, The Nylon Curtain
        /private/tmp/WebKitPlugInStreamoqZ3yw
Dear John by Elton John, Jump Up
        /private/tmp/WebKitPlugInStream7NZ6CR
Shabby Doll by Elvis Costello, Imperial Bedroom
        /private/tmp/WebKitPlugInStreamauiHr4
Every Breath You Take by The Police, Every Breath You Take - The Classics
        /private/tmp/WebKitPlugInStreamsAY1Uu
To Make You Feel My Love by Billy Joel, Greatest Hits Volume III
        /private/tmp/WebKitPlugInStreamBUGAa2
Real Good Looking Boy by The Who, Then And Now! (1964 - 2004)

Every Breath You Take (Live) by Sting, ...All This Time (Live)

I Know What I Like by Huey Lewis And The News, Fore!

%

Source: Playlist.pl, getxml.c. (Compile getxml as follows: cc getxml.c -o getxml -lxml2 -lcurl -w)

#! /usr/bin/perl
# Pandora Playlist - Erica Sadun, 26 May 06

my $goaway = "Pandora is not yet open and playing.\n";

# Find /tmp/Web* files containing the phrase "focusTrait"
my $foctrait = `grep -l focusTrait /private/tmp/Web*`;
if ($foctrait eq "") {print $goaway; exit(0);}

# Sort XML files by time and split into an array
my $xmlist = join(" ", split(/\n/, $foctrait));
my $lscmd = "ls -tr ".$xmlist;
$xmlist = `$lscmd`;
my @matchfiles = split(/\n/, $xmlist);

# Create a list of possible music files based on 6 digits in a row
my $flist = `ls -ltr /private/tmp/WebKitPlugInStream* | grep [0-9][0-9][0-9][0-9][0-9][0-9][0-9] |sed "s/^.*private/\/private/"`;
my @filelist = split(/\n/, $flist);

# Create a list of songs in order
my $nth = 0;
my @plist = ();
foreach my $fitem (@matchfiles)
{
  # Read in the XML from the file
  my $r1 = "%riz = ".`./getxml $fitem`;
  my %riz; eval($r1);

  # Pandora always lists 4 songs per XML file, including upcoming
  # songs that have not yet downloaded to the cache.
  foreach my $i (0..3)
  {
    my $song = $riz{Response}->{methodResponse}->{params}->{param}->{value}->{array}->{data}->{value}[$i]->{struct}->{member}[1]->{value};
    $song =~ s/%([0-9A-Fa-f]{2})/chr(hex($1))/eg;
    my $artist = $riz{Response}->{methodResponse}->{params}->{param}->{value}->{array}->{data}->{value}[$i]->{struct}->{member}[10]->{value};
    $artist =~ s/%([0-9A-Fa-f]{2})/chr(hex($1))/eg;
    my $album = $riz{Response}->{methodResponse}->{params}->{param}->{value}->{array}->{data}->{value}[$i]->{struct}->{member}[13]->{value};
    $album =~ s/%([0-9A-Fa-f]{2})/chr(hex($1))/eg;

    print "$song by $artist, $album\n\t$filelist[$nth++]\n";
  }
}