Hi All,
In regards to the dual-behavior of .app files (er, folders) on OSX, how does this affect operating on them with AS? I
n short, I have an Automator service that takes one or more files from the Finder as its input, and this service does not work if a .app file is the input. In this case I can fix it by using the path (HFS or POSIX) of the .app file and removing the terminating “:” or “/” in order to get to its extension, which is what I’m after in this case. But in the general case where I want to operate on a .app file as a file and not as a folder, what is a safe practice?
Now the long version:
*** WARNING ***
THE SCRIPTS IN THIS POST PERFORM ACTIONS THAT COULD RESULT IN SYSTEM INSTABILITY OR LOSS OF DATA. USE THESE SCRIPTS OR MODIFIED VERSIONS OF THESE SCRIPTS AT YOUR OWN RISK.
*** WARNING ***
Below, I present my poorly made solution for the difficulty in adding programs to the “open with” context menu in OSX. Both are scripts meant to be run from Automator.
First is a script for adding applications to the “open with” menu: it takes a list of files, has the user point to one or more applications on the system, and adds all extensions of the list of files to each application selected such that they will now appear in the “open with” menu for every filetype in the list of files.
Second is a script for removing applications from the “open with” menu: it takes a single application and presents the list of the filetypes for which it will appear on the “open with” menu. The user selects one or more filetypes from the list and and the application is removed from the “open with” menu for those types.
Both scripts work by editing the …SomeApp.app/Contents/Info.plist file within each application. This method works for programs so long as they’re not signed in such a way that they break (OS X won’t let them run) after you modify their contents.
(the third script below is the code that can be used in an Applescript action in Automator. It simply points to a script file and runs the appropriate handler with the appropriate arguments)
Script 1: Adds chosen application(s) to the “open with” menu for the filetypes of the files used as the input
on AddDefaultApplications(FileList)
set ExtList to {}
repeat with i in FileList
if text item -1 of (i as text) is not ":" or (i as text) contains ".app" then
set end of ExtList to beginning of parseLine(end of parseLine(i as text, "."), ":")
end if
end repeat
if (count of ExtList) > 0 then
set OpenWithApps to choose file with prompt "Select applications to add to the \"Open With\" menu" default location (path to applications folder) of type {"app"} with multiple selections allowed
if OpenWithApps is false then return 0
repeat with AnApp in OpenWithApps
set AppName to AnApp as text
set InfoPlistFile to missing value
try
set InfoPlistFileName to (AppName & "Contents:Info.plist")
set InfoPlistFile to InfoPlistFileName as alias
on error
display alert "Couldn't get Info.plist file" message (AnApp as text) as critical
end try
if InfoPlistFile is not missing value then
set ExtIsNew to {}
repeat with i in ExtList
set end of ExtIsNew to true
end repeat
set InfoRef to open for access (InfoPlistFile as text)
set InfoContents to read InfoRef
set InfoContents to paragraphs of InfoContents
set NewInfoContents to {}
set SectionNumber to 1
repeat with aLine in InfoContents
if SectionNumber is 1 and (aLine as string) contains "CFBundleTypeExtensions" then
set SectionNumber to SectionNumber + 1
else if SectionNumber is 2 then
if (aLine as string) contains "</array>" then
repeat with i from 1 to count of ExtList
if (item i of ExtIsNew) then
set end of NewInfoContents to (tab & tab & tab & tab & "<string>" & (item i of ExtList) as string) & "</string>"
end if
end repeat
set SectionNumber to SectionNumber + 1
else
repeat with i from 1 to count of ExtList
if (item i of ExtIsNew) and (aLine as string) contains (item i of ExtList) then
set item i of ExtIsNew to false
end if
end repeat
end if
end if
set end of NewInfoContents to aLine as string
end repeat
close access InfoRef
set NewInfoFileName to (InfoPlistFile as text) & ".tmp" as text
set NewInfoRef to open for access NewInfoFileName with write permission
set eof of NewInfoRef to 0
repeat with i from 1 to count of NewInfoContents
if i > 1 then write return to NewInfoRef
write item i of NewInfoContents to NewInfoRef
end repeat
close access NewInfoRef
set NewInfoFile to NewInfoFileName as alias
set Scpt to "cp " & quoted form of POSIX path of InfoPlistFile & space & quoted form of (POSIX path of InfoPlistFile & ".bak")
try
do shell script Scpt with administrator privileges
set Scpt to "mv " & quoted form of POSIX path of NewInfoFile & space & quoted form of POSIX path of InfoPlistFile
do shell script Scpt with administrator privileges
set Scpt to "/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/LaunchServices.framework/Versions/A/Support/lsregister -f " & quoted form of POSIX path of AnApp
do shell script Scpt
on error
display alert "Failed to modify Info.plist file" as critical
return 0
end try
end if
end repeat
end if
end AddDefaultApplications
on parseLine(theLine, delimiter)
-- This came from Nigel Garvey
set astid to AppleScript's text item delimiters
set AppleScript's text item delimiters to {delimiter}
set theTextItems to theLine's text items
set AppleScript's text item delimiters to astid
repeat with i from 1 to (count theTextItems)
if (item i of theTextItems is "") then set item i of theTextItems to missing value
end repeat
return theTextItems's every text
end parseLine
Script 2: Presents user with list of filetypes (extensions) for which the chosen application will appear on the “open with” menu. Selected filetypes are then removed
on ModifyTypeExtensions(FileList)
set AppName to (beginning of FileList) as text
if end of parseLine(AppName, ".") is "app:" then
set AppNameNice to beginning of parseLine(end of parseLine(AppName, ":"), ".")
set InfoPlistFile to missing value
try
set InfoPlistFileName to (AppName & "Contents:Info.plist")
set InfoPlistFile to InfoPlistFileName as alias
on error
display alert "Couldn't get Info.plist file" message (AnApp as text) as critical
end try
if InfoPlistFile is not missing value then
set InfoRef to open for access (InfoPlistFile as text)
set InfoContents to read InfoRef
set InfoContents to paragraphs of InfoContents
set NewInfoContents to {}
set EndInfoContents to {}
set ExtList to {}
set SectionNumber to 1
repeat with aLine in InfoContents
if SectionNumber is 1 then
set end of NewInfoContents to aLine as string
if (aLine as string) contains "CFBundleTypeExtensions" then
set SectionNumber to SectionNumber + 1
end if
else if SectionNumber is 2 then
if (aLine as string) contains "<string>" then
set end of ExtList to beginning of parseLine(end of parseLine(aLine as text, "<string>"), "</string>")
else if (aLine as string) contains "</array>" then
set end of EndInfoContents to aLine as string
set SectionNumber to SectionNumber + 1
else
set end of NewInfoContents to aLine as string
end if
else
set end of EndInfoContents to aLine as string
end if
end repeat
close access InfoRef
set RemoveExtList to choose from list ExtList with prompt "Select type extensions that should be REMOVED for " & AppNameNice with multiple selections allowed without empty selection allowed
if RemoveExtList is false then return 0
set NewInfoFileName to (InfoPlistFile as text) & ".tmp" as text
set NewInfoRef to open for access NewInfoFileName with write permission
set eof of NewInfoRef to 0
repeat with i from 1 to count of NewInfoContents
if i > 1 then write return to NewInfoRef
write item i of NewInfoContents to NewInfoRef
end repeat
repeat with i in ExtList
set AddExt to true
repeat with j in RemoveExtList
if (i as string) is (j as string) then
set AddExt to false
exit repeat
end if
end repeat
if AddExt then write ((return & tab & tab & tab & tab & "<string>" & i as text) & "</string>") as text to NewInfoRef
end repeat
repeat with i in EndInfoContents
write return & i as text to NewInfoRef
end repeat
close access NewInfoRef
set NewInfoFile to NewInfoFileName as alias
set Scpt to "cp " & quoted form of POSIX path of InfoPlistFile & space & quoted form of (POSIX path of InfoPlistFile & ".bak")
try
do shell script Scpt with administrator privileges
set Scpt to "mv " & quoted form of POSIX path of NewInfoFile & space & quoted form of POSIX path of InfoPlistFile
do shell script Scpt with administrator privileges
set Scpt to "/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/LaunchServices.framework/Versions/A/Support/lsregister -f " & quoted form of POSIX path of AppName
do shell script Scpt
on error
display alert "Failed to modify Info.plist file" as critical
return 0
end try
end if
end if
end ModifyTypeExtensions
on parseLine(theLine, delimiter)
-- This came from Nigel Garvey
set astid to AppleScript's text item delimiters
set AppleScript's text item delimiters to {delimiter}
set theTextItems to theLine's text items
set AppleScript's text item delimiters to astid
repeat with i from 1 to (count theTextItems)
if (item i of theTextItems is "") then set item i of theTextItems to missing value
end repeat
return theTextItems's every text
end parseLine
Script 3: Script that goes into an Applescript action in Automator. Points and passes input to a handler in a script file. (this script is for calling Script 1 above)
on run {input, parameters}
set theScript to load script file ((path to scripts folder from user domain as text) & "Applications:Finder:Services:Add to Open With Menu.scpt")
tell theScript
AddDefaultApplications(input)
end tell
end run
*edit: Modified my ghetto check if a folder is in the input. Turns out that there is no “open with” menu for .app files anyways… oh well…