macdevcenter.com
oreilly.comSafari Books Online.Conferences.

advertisement

AddThis Social Bookmark Button

Using Perl to Manage Plist Files, Part 2
Pages: 1, 2, 3, 4, 5, 6

We can do the same thing in Cocoa. Use the objectEnumerator() or keyEnumerator() methods on an array or dictionary to get an NSEnumerator object. Then use the nextObject() method on the NSEnumberator object to get the each object or key. Let's loop through the root of the NetInfo database and print each key.




#!/usr/bin/perl

use Foundation;
use lib "/Users/yourname/Desktop/";
require "perlplist.pl"; # for perlValue

# dump netinfo database to a temp file
# (the sed is to add missing ";" to some ")")
system "/usr/bin/nidump -r / -t localhost/local | 
       sed -e \"s/)\$/);/g\" > /tmp/netinfo_db";

my $plist = 
NSDictionary->dictionaryWithContentsOfFile_("/tmp/netinfo_db");
my $enumerator = $plist->keyEnumerator();
my $key;
while ( $key = $enumerator->nextObject() and $$key ) {
  printf "Key: %s\n", perlValue( $key );
}

When nextObject reaches the last list item, it will return the nil object. We must check for nil using "$$key" or else the loop will repeat forever. "$$key" will return the value of the nil object (which is 0), which will stop the loop.

If we wanted to use Perl's foreach to scan the Plist files, we can use the following subroutines to convert Cocoa dictionaries and arrays into Perl hashes and arrays.

If we convert all Cocoa float, int, boolean, date, and data objects to Perl scalars using description()->UTF8String() we will not be able to tell one data type from another. A Cocoa int with the value of 1, a Cocoa boolean with the value of true, and a Cocoa string with the value of "1" will all look the same once converted. You might want to leave them as Cocoa objects if you need to know the difference between an int 1, a boolean true, or a string "1". The following subroutines have the option to convert all values to Perl or not.

Here is an example that uses the two subroutines to convert the netinfo database to Perl equivalents. The nidump command outputs ASCII Plist, which does not show data types. So this example converts every value to Perl scalars. It will print each username and the home folder for the user.


#!/usr/bin/perl

use Foundation;
use lib "/Users/yourname/Desktop/";
require "perlplist.pl"; # for perlValue

sub perlArrayFromNSArray {
  my ( $cocoaArray, $convertAll ) = ( @_ );
  my @perlArray = ();
  my $enumerator = $cocoaArray->objectEnumerator();
  my $value;
  while ( $value = $enumerator->nextObject() and $$value ) {
    if ( substr ( ref ( $value ), 0,9 ) eq "NSCFArray" ) {
      my @newarray = perlArrayFromNSArray( $value, $convertAll );
      push (@perlArray, \@newarray);
    } elsif ( substr ( ref ( $value ), 0,14 ) eq "NSCFDictionary" ) {
      my %newhash = perlHashFromNSDict( $value, $convertAll );
      push (@perlArray, \%newhash);
    } else {
      push ( @perlArray, $convertAll ? perlValue( $value ) : $value );
    }
  }
  return @perlArray;
}

sub perlHashFromNSDict {
  my ( $cocoaDict, $convertAll ) = ( @_ );
  my %perlHash = ();
  my $enumerator = $cocoaDict->keyEnumerator();
  my $key;
  while ( $key = $enumerator->nextObject() and $$key ) {
    my $value = $cocoaDict->objectForKey_($key);
    if ( $value ) { # check to make sure an object was set
      if ( substr ( ref ( $value ), 0,9 ) eq "NSCFArray" ) {
        my @newarray = perlArrayFromNSArray( $value, $convertAll );
        $perlHash{ perlValue( $key ) } = \@newarray;
      } elsif ( substr ( ref ( $value ), 0,14 ) eq "NSCFDictionary" ) {
        my %newHash = perlHashFromNSDict( $value, $convertAll );
        $perlHash{ perlValue( $key ) } = \%newHash;
      } else {
        $perlHash{ perlValue( $key ) } = 
        $convertAll ? perlValue( $value ) : $value;
      }
    }
  }
  return %perlHash;
}

# dump netinfo database to a temp file 
# (the sed is to add missing ";" to some ")")
system "/usr/bin/nidump -r / -t localhost/local | 
sed -e \"s/)\$/);/g\" > /tmp/netinfo_db";

my $plist = NSDictionary->dictionaryWithContentsOfFile_("/tmp/netinfo_db");

# convert the entire NSDictionary & its contents to Perl
my %nidb = perlHashFromNSDict( $plist, 1 );

# No more Cocoa is used from here on.

my $childArray = $nidb{ "CHILDREN" }; # get the main array

foreach $childDict ( @$childArray ) {
  if ( $$childDict{ "name" }[0] eq "users" ) { # find the users array
    my $userArray = $$childDict{ "CHILDREN" };
    foreach $userDict ( @$userArray ) { # loop through the users array
      my $userName = $$userDict{ "name" }[0];
      my $userHome = $$userDict{ "home" }[0];
      print "$userName $userHome\n";
    }
  }
}

The netinfo database stores all values in arrays. This is why the example gets the first index array value using [0].

$$childDict{ "name" }[0]

Also, the two conversion subroutines nest each array or hash in another. When accessing a nested array or hash, or a value in a nested array or hash, you add an extra "$," like this:


@$array # accessing an array
%$hash # accessing a hash
$$array[ $i ]  # getting the value from a nested array
$$hash{ "key" } # getting the value from a nested hash

In the above example, the only non-nested container is the parent dictionary, or %nidb. Once we dig into it, everything will be nested and require the extra dollar sign. To learn more about nested arrays and hashes, read the perlref documentation.

Pages: 1, 2, 3, 4, 5, 6

Next Pagearrow