Get POSIX paths from «data» in com.apple.recentitems.plist

I tried modifying the scripts posted by Nigel Garvey at these threads:

And came up with something like:

on datatoposix(x)
	set f to (open for access POSIX file ("/tmp/me.lri.datatoposix.dat") with write permission)
	repeat with d in x
		set eof f to 0
		write d to f
		set d's contents to POSIX path of (read f from 1 as alias)
	end repeat
	close access f
	x
end datatoposix

tell application "System Events"
	tell property list file "~/Library/Preferences/com.apple.recentitems.plist"
		set l to property list item "RecentDocuments"'s property list item "CustomListItems"'s property list items's property list item "Bookmark"'s value
	end tell
end tell

datatoposix(l)

I’m always getting the error “Can’t get POSIX path ofCan’t get some object.” though. Any fixes or other (shorter) ways to extract the paths?

Hi, l-ri.

It seems the data have changed since I wrote those scripts ” at least for Apple-menu Recent Items. A data object data no longer represent an alias but some kind of bookmark object. Analysing this very crudely on a Snow Leopard system reveals that, starting at byte 21, there are data for the names in a POSIX path (without the slashes). The data for each name are:

A byte whose value is the number of bytes representing the name in UTF-8 Unicode text.
Seven more bytes with the values 0, 0, 0, 1, 1, 0, and 0 respectively.
The item’s name as UTF-8 Unicode text.
Padding with zero-value bytes, if necessary, to bring the number of bytes up to a multiple of 4.

The following hack should therefore (hopefully) solve the immediate problem:

on datatoposix(x)
	set f to (open for access POSIX file ("/tmp/me.lri.datatoposix.dat") with write permission)
	try
		repeat with d in x
			set eof f to 0
			-- Write the data object to the file.
			write d's contents to f
			-- Reset the file mark to byte 21.
			read f from 21 for 0
			set POSIXpath to ""
			repeat
				-- Read the next 8 bytes as a double integer (interpreted as little-endian on Intel machines).
				set doubleInt to (read f for 8 as double integer)
				-- Finish when the top seven bytes aren't the value for "path item".
				if (doubleInt div 256 is not 4.311744512E+9) then exit repeat
				-- Get the byte length of the item's name from the lowest-order byte.
				set len to doubleInt mod 256 div 1
				-- Read that number of bytes as UTF-8 and append the result to the POSIX path.
				set POSIXpath to POSIXpath & ("/" & (read f for len as «class utf8»))
				-- Advance the file mark past any padding.
				read f for (4 - len mod 4) mod 4
			end repeat
			set d's contents to POSIXpath
		end repeat
	on error msg
		display dialog msg
	end try
	close access f
	x
end datatoposix

tell application "System Events"
	tell property list file "~/Library/Preferences/com.apple.recentitems.plist"
		set l to property list item "RecentDocuments"'s property list item "CustomListItems"'s property list items's property list item "Bookmark"'s value
	end tell
end tell

datatoposix(l)

I couldn’t get that to work either on 10.7.2. It just returns a list of empty strings.

I came up with this though:

[code]#!/bin/sh

plist=$HOME/Library/Preferences/com.apple.recentitems.plist
o0=$(echo $‘\0’); o1=$(echo $‘\1’)
for i in {0…10}; do
/usr/libexec/PlistBuddy -c “Print :RecentDocuments:CustomListItems:$i:Bookmark”
$plist | tr -d ‘\2-\37’ | sed -E “s|$o0$o0$o0$o0$o1$o1$o0$o0|/|g;s|[^/]||"
| tr -d ‘\0’ | sed "s|$o1.
||”
done[/code]
Beginning of sed -n ‘l’ for a single entry:

book\350\002\000\000\000\000\001\020\020\000\000\000\034\ \002\000\000\005\000\000\000\001\001\000\000Users\000\000\ \000\005\000\000\000\001\001\000\000lauri\000\000\000\a\000\ \000\000\001\001\000\000Desktop\000\020\000\000\000\001\001\ \000\000datatoposix.scpt\020\000\000\000\001\006\000\000\ \004\000\000\000\024\000\000\000$\000\000\0004\000\000\000\ \b\000\000\000\004\003\000\0001\277\000\000\000\000\000\000\ \b\000\000\000\004\003\000\0003\001\005\000\000\000\000\000\ \b\000\000\000\004\003\000\000\321\001\005\000\000\000\000\

Well that’s exactly the structure the script’s designed to handle, so presumably 'read’s ‘double integer’ interpretation, which is wrong in Snow Leopard, has been either fixed or just changed in Lion. Here’s a variation which reads the eight-byte groups as strings instead and uses the IDs of the characters. Any good on your system?

on datatoposix(x)
    set f to (open for access POSIX file ("/tmp/me.lri.datatoposix.dat") with write permission)
    try
        repeat with d in x
            set eof f to 0
            -- Write the data object to the file.
            write d's contents to f
            -- Reset the file mark to byte 21.
            read f from 21 for 0
            set POSIXpath to ""
            repeat
                -- Read the next 8 bytes as a string and get the characters' IDs.
                set idList to id of (read f for 8 as string)
                -- Finish when IDs 2 to 8 aren't the values for "path item".
                if (idList does not end with {0, 0, 0, 1, 1, 0, 0}) then exit repeat
                -- Get the byte length of the item's name from the first ID.
                set len to beginning of idList
                -- Read that number of bytes as UTF-8 and append the result to the POSIX path.
                set POSIXpath to POSIXpath & ("/" & (read f for len as «class utf8»))
                -- Advance the file mark past any padding.
                read f for (4 - len mod 4) mod 4
            end repeat
            set d's contents to POSIXpath
        end repeat
    on error msg
        display dialog msg
    end try
    close access f
    x
end datatoposix

tell application "System Events"
    tell property list file "~/Library/Preferences/com.apple.recentitems.plist"
        set l to property list item "RecentDocuments"'s property list item "CustomListItems"'s property list items's property list item "Bookmark"'s value
    end tell
end tell

datatoposix(l)

Yeah, that version does work properly on Lion.