tiistai 6. marraskuuta 2012

How to parse a plist that has multiple dictionary items

I was surfing Stack Overflow and everywhere to find out how I would parse a bit more complicated plist XML than the usual examples around the Internet sites. In this case it is a munki catalog file that needs to be parsed. On main level the first item is array and under that next item is dict. Under that is the actual thing where the key value pairs of interest are located. So finally ended up with this (each dict on main level array has to be enumerated first using enumerator and then cast the resulting object to a new dictionary and then parse that), what makes this tricky is that many examples are not working, but they are just crashing. This should work:
    // replace /mypath with your path to your file
    NSString *dataPath = @"/mypath/mycomplexplist.plist";
    NSString *errorDesc = nil;
    NSPropertyListFormat format;
    if (![[NSFileManager defaultManager] fileExistsAtPath:dataPath]) {
        // Handle the error if the file does not exist
     
    }
    NSData *plistXML = [[NSFileManager defaultManager] contentsAtPath:dataPath];
    NSDictionary *dictionary = (NSDictionary *)[NSPropertyListSerialization
                                          propertyListFromData:plistXML
                                          mutabilityOption:NSPropertyListMutableContainersAndLeaves
                                          format:&format
                                          errorDescription:&errorDesc];
    if (!dictionary) {
        // Handle your error
    }
       
    NSEnumerator *enumerator = [dictionary objectEnumerator];
    
    for(id object in enumerator) {
        NSDictionary *innerdic = object;
        NSString *name = [innerdic objectForKey:@"name"];
        NSLog(@"name =%@",name);
        // and parse the rest of the stuff of the interest here.
    }        

tiistai 21. elokuuta 2012

Basics of Mac OS X Package management

The package management in Mac is quite non-existent compared to what Linux has to offer, e.g. Debian packaging which has full dependency resolving capabilities and ability to install package along with dependencies and uninstall along with dependencies.

Mac has a pkgutil command that knows something about installed packages on the operating system. It is also possible to dig information on what applications are installed on the OSX by using the System Profiler. In the System Profiler full report, all installed software is counted.

Here is man page for pkgutil:

https://developer.apple.com/library/mac/#documentation/Darwin/Reference/ManPages/man1/pkgutil.1.html

How to determine what each package contains:

pkgutil --packages list installed packages.

then loop through all the packages and run

pkgutil --pkg-info packageid

for example:
pkgutil --pkg-info com.blackmagic-design.DaVinciResolveApplications

command prints out: "

package-id: com.blackmagic-design.DaVinciResolveApplications
version: 1
volume: /
location: Applications
install-time: 1328796965"

From this we know that the base directory for this package is Applications folder on system root volume /.

Then we can find out what files this package has installed under the base folder.

pkgutil --files packageid

example:
"pkgutil --files com.blackmagic-design.DaVinciResolveApplications
DaVinci Resolve.app
DaVinci Resolve.app/Contents
DaVinci Resolve.app/Contents/Frameworks
DaVinci Resolve.app/Contents/Frameworks/Cg.Framework
DaVinci Resolve.app/Contents/Frameworks/Cg.Framework/Cg
DaVinci Resolve.app/Contents/Frameworks/QtCore.framework
DaVinci Resolve.app/Contents/Frameworks/QtCore.framework/Resources
DaVinci Resolve.app/Contents/Frameworks/QtCore.framework/Versions
DaVinci Resolve.app/Contents/Frameworks/QtCore.framework/Versions/4
DaVinci Resolve.app/Contents/Frameworks/QtCore.framework/Versions/4/QtCore
DaVinci Resolve.app/Contents/Frameworks/QtGui.framework ....
- rest of the printout is clipped because it is large -...."

This information is actually found from a folder, pkgutil just finds the same information that is stored in bom and plist files under:
/private/var/db/receipts

The bom-files can be inspected with command lsbom.
lsbom com.apple.pkg.FinalCut_AppStore.bom

The plist files are typically binary format, but they can be converted to readable xml format using plutil:
plutil -convert xml1 -o -
This is hardly needed because the plist files can be also read with defaults read -command as follows:
Example:
defaults read /private/var/db/receipts/com.apple.pkg.Aperture_AppStore.plist
Please note that you must specify full url to defaults read, if you are in directory /private/var/db/receipts and try to execute defaults read ./com.apple.pkg.Aperture_AppStore.plist - it will not work.

Useful parameters for lsbom:
-s list only files (the numeric information disappears with this):
Example:
cd /private/var/db/receipts
lsbom -dfls com.apple.pkg.FinalCut_AppStore.bom

Someone has made a simple script to uninstall bom:
http://invariance.org/clemens/uninstall_bom
And here are instructions how to use that script:
How to use uninstall_bom