Show 'info for' properties in a text document

Ever wanted to show the info for properties of a file or folder in a text document (perhaps as a handy reference, while scripting - or for simple copy/paste operations)?

The trouble is that converting a list of properties to text is no trivial matter. Property labels are not easily extracted - and there’s the question of how to effectively parse the values of various different classes (such as text, dates, lists, real numbers, integers, alias files and booleans).

Nevertheless, it should be possible, right?

The following script was prompted by CalvinFold’s question: Output List of Properties and Values. The code is not exactly short, but I hope someone finds it useful. Just save it as an application - for use either as a droplet (to process single or multiple items), or as an applet (to choose and process an individual file).

property cStr : ", "
property cSub : ASCII character 1

to storeItems from r between {b, e}
	set text item delimiters to return
	set r to r as string
	set text item delimiters to cStr
	set s to r's text items
	set text item delimiters to cSub
	tell s to set s to beginning & ({""} & rest)
	set text item delimiters to e & return & b
	{(b & s's paragraphs & e)'s paragraphs, (b & r's paragraphs & e)'s paragraphs}
end storeItems

to storeLists from l
	set s to {}
	set r to {}
	repeat with i in l
		set text item delimiters to cSub
		set s's end to "{" & i & "}"
		set text item delimiters to cStr
		set r's end to "{" & i & "}"
	end repeat
	{s, r}
end storeLists

to getLists from i
	set s to {}
	set r to {}
	considering case
		repeat with t in i's strings
			if cStr is in t then set s's end to t's contents
		end repeat
	end considering
	if (count s) > 0 then set {s, r} to (storeItems from s between {"", ""})
	if (count i's dates) > 0 then tell (storeItems from i's dates between {"date \"", "\""})
		set s to s & item 1
		set r to r & item 2
	end tell
	if (count i's lists) > 0 then tell (storeLists from i's lists)
		set s to s & item 1
		set r to r & item 2
	end tell
	{s, r}
end getLists

on infoToList(i)
	set {s, r} to getLists from i
	try
		i of i
	on error i
	end try
	set text item delimiters to "{"
	set i to i's text from text item 2 to end
	set text item delimiters to "}"
	set i to i's text beginning thru text item -2
	set text item delimiters to cStr
	set i to i's text items
	set text item delimiters to cSub
	tell i to set i to beginning & ({""} & rest)
	repeat with n from 1 to count s
		set text item delimiters to s's item n
		set i to i's text items
		set text item delimiters to r's item n
		tell i to set i to beginning & ({""} & rest)
	end repeat
	set text item delimiters to cSub
	i's text items
end infoToList

on itemProperties(i)
	set i to infoToList(run script ("info for alias \"" & i & "\""))
	set text item delimiters to return
	tell i to set i to beginning & ({""} & rest)
end itemProperties

to open l
	repeat with i in l
		set i's contents to itemProperties(i)
	end repeat
	set text item delimiters to return & return
	tell l to set l to beginning & ({""} & rest)
	set text item delimiters to {""}
	set p to (path to temporary items as Unicode text) & "info for.txt"
	set f to open for access file p with write permission
	set eof f to 0
	write l to f
	close access f
	tell application "Finder" to open p
end open

open {choose file}

Wow…nice, a couple of personal tweak’s and it’s just what I was looking for.

Thanks!

(FWIW: I signed-up recently after every time I did a Google search for “AppleScript, ” and I kept finding the answers on this site. Great knowledgebase here…wow.)

The Force was with you, clearly. I did note the need for try…, parse the error, and after quite a bit of fiddling finally saw why this is a text doc. (I changed it to TextWrangler, the free little brother of BBEdit, and embodies BBEdit’s scriptability for the most part). If you grab the text from TW, and attempt to parse out the escape characters all over the place, you don’t (or at least I don’t) win - you get the text, but it’s full of escape characters. Since everything on that page as presented is basically compatible with Western (Mac OS Roman) encoding, it should be possible to get it all back in your script in parsed form. Somehow…

kai, why use run script here?:

. on itemProperties(i) set i to infoToList(run script ("info for alias \"" & i & "\"")) .

Very good question, Qwerty. :smiley:

When we compile/save a script, it’s compiled as executable code, rather than the form we see in a script editor. So, for example, terms like creation date and modification date will be saved as «class ascd» and «class asmo», respectively. To convert one form to the other, the compiler compares the terms with those listed in various AppleScript dictionaries (core terminology, scripting additions and any relevant application[s]).

When we next open the script in a script editor, the compiler reverses the process, by decompiling the raw code and displaying it in human-readable form. To do this, it needs to refer once again to the appropriate dictionaries. (That’s why, when a script that contains application-specific terms is opened, the app[s] concerned may also be launched automatically.)

To demonstrate how this process works, try a few conversions for yourself:

So far, so good - but here’s the crucial bit…

When a script is saved and run as an application, the raw code is executed directly, without any decompiling. Normally, this is of no concern to us. If we’re trying to determine an object’s properties, we usually want only the value of those properties - not their labels.

In this particular case, however, we want the values and the labels - so we need a way of forcing the compiler to decompile the raw codes into recognisable terms. Otherwise, instead of something like:

… we could end up with something more like:

One way of fixing this is to use the run script command to act upon a text script (which will not yet have been compiled). When the terms contained in the text are encountered, the compiler is called upon to compile them as executable code - so that the script can be executed in the normal way. For this, the relevant dictionaries need to be accessed - after which… hey presto! The required terms become available to our main script. :cool:

Obviously, because of the additional operations involved, using the run script command can take a bit longer than executing compiled code directly. So, ideally, we should perform the operation only once - rather than for each set of properties. The following variation of the above script takes account of this - so that, when several items are processed, the command is invoked for the first one only. Since this version uses a different algorithm to parse results, it’s also shorter:

on get_text(i)
	try
		i of {i}
	on error i
	end try
	set text item delimiters to "{"
	set i to i's text from text item 2 to end
	set text item delimiters to "}"
	i's text 1 thru text item -2
end get_text

on text_list(r)
	set l to r as list
	set r to text 2 thru -2 of get_text(r)
	repeat with i from 1 to (count l) - 1
		set d to get_text(l's item i)
		set text item delimiters to d & ", "
		set l's item i to r's text item 1 & d
		set r to r's text from text item 2 to end
	end repeat
	set l's item -1 to r
	l & ""
end text_list

to write_file(t)
	set p to (path to temporary items as Unicode text) & "info for.txt"
	set f to open for access file p with write permission
	set eof f to 0
	write t to f
	close access f
	tell application "TextEdit"
		activate
		open p
	end tell
end write_file

to open l
	set l's item 1 to text_list(run script ("info for alias \"" & l's item 1 & "\""))
	repeat with i from 2 to count l
		set l's item i to text_list(info for l's item i)
	end repeat
	set text item delimiters to return
	set t to l as Unicode text
	set text item delimiters to {""}
	write_file(t's text 1 thru -2)
end open

open {choose file}

Edit: Cleaned up the codes for classes and constants near the top of the message. (They’d somehow become corrupted.)

Well, there’s another way you could do this with a generic “coerce to string” handler. The trick is to try to coerce to string the easy way (suchAndSuch as string) and capture the error message. Then, if you know the language the script is running in, you can extract a text version of your object from that error message. My version of the handler works in English, but would need to be tweaked to work in another language. Here’s the handler, with a sample “info for.txt” file on the desktop (t make it work in a different language, you’d need to add to the errMsgLeadList and errMsgTrailList variables in the handler):



get info for alias (((path to desktop) as string) & "info for.txt")

set someTestThing to result

set someTestAsString to coerceToString(someTestThing)

set the clipboard to someTestAsString

return someTestAsString


on coerceToString(incomingObject)
	-- version 1.6, Daniel A. Shockley, http://www.danshockley.com
	-- 1.6 -  can add additional errMsg parts (just add to lists to handle other languages. 
	--             Currently handles English in both 10.3 and 10.4 (10.3 uses " into a number." 
	--             while 10.4 uses " into type number.")
	-- 1.5 -  added Unicode Text
	
	set errMsgLeadList to {"Can't make "}
	set errMsgTrailList to {" into a number.", " into type number."}
	
	if class of incomingObject is string then
		set {text:incomingObject} to (incomingObject as string)
		return incomingObject
	else if class of incomingObject is integer then
		set {text:incomingObject} to (incomingObject as string)
		return incomingObject as string
	else if class of incomingObject is real then
		set {text:incomingObject} to (incomingObject as string)
		return incomingObject as string
	else if class of incomingObject is Unicode text then
		set {text:incomingObject} to (incomingObject as string)
		return incomingObject as string
	else
		-- LIST, RECORD, styled text, or unknown
		try
			set testMultiply to 1 * incomingObject
			
			set listText to (first character of incomingObject)
		on error errMsg
			--tell me to log errMsg
			set objectString to errMsg
			repeat with errMsgLead in errMsgLeadList
				if objectString contains errMsgLead then
					set {od, AppleScript's text item delimiters} to {AppleScript's text item delimiters, errMsgLead}
					set objectString to text item 2 of objectString
				end if
			end repeat
			
			repeat with errMsgTrail in errMsgTrailList
				if objectString contains errMsgTrail then
					set AppleScript's text item delimiters to errMsgTrail
					set objectString to text item 1 of objectString
					set AppleScript's text item delimiters to od
				end if
			end repeat
			
			--tell me to log objectString
			--	set newString to text 1 thru -2 of objectString
			set {text:objectString} to (objectString as string)
		end try
		
		return objectString
	end if
end coerceToString

The advantage to doing it this way is that you don’t need to maintain a list of info for properties and their AppleEvent codes. Also, this handler is really helpful for debugging - you can use it to log records, lists, etc to the Console using another handler I wrote (with some advice from applescript-users mailing list, I think):



set someRecord to {firstName:"Bob", lastName:"Smith"}

set someRecordAsString to coerceToString(someRecord)

logConsole("MyAppName", someRecordAsString)

return someRecordAsString

on logConsole(processName, consoleMsg)
	-- version 1.5 - Daniel A. Shockley, http://www.danshockley.com
	-- 1.5- uses standard date-stamp format	

	set timeStamp to do shell script "date +\"%Y-%m-%d %H:%M:%S\""
	set hostName to do shell script "hostname -s" & space
	set shellCommand to "echo" & space & quoted form of (return & timeStamp & space & hostName & space & processName & ":" & space & consoleMsg) & " >> /dev/console"
	
	do shell script shellCommand
	
end logConsole


I probably also got the idea for the coerceToString from others - I can’t remember who. However, it seems that others use this idea to coerce to string. For example, there are a few threads discussing coercing to plain text - here’s one http://bbs.applescript.net/viewtopic.php?id=11425

Language should not be an issue with the original suggestions.

This statement seems to suggest that the methods suggested earlier do need to “maintain a list of info for properties and their AppleEvent codes”. They don’t.

I apologize. I just re-read your code and realize that is quite right. In fact, yours is a very interesting way to do this.