Trashing Files: Part 1 (Mac OS)
Monday, January 10, 2011
Most operating systems provide support for sending files to the “trash can” (or sometimes “recycle bin”). Inspired by a python project called “send2trash”, I thought Factor should have a similar cross-platform library for trashing files.
trash
First, we are going to define a trash
vocabulary, and use a
HOOK:
that dispatches to the proper implementation, depending on which
operating system you are running.
USING: combinators system vocabs.loader ;
IN: trash
HOOK: send-to-trash os ( path -- )
{
{ [ os macosx? ] [ "trash.macosx" ] }
{ [ os unix? ] [ "trash.unix" ] }
{ [ os winnt? ] [ "trash.windows" ] }
} cond require
trash.macosx
Next, we will create the trash.macosx
vocabulary.
USING: alien.c-types alien.strings alien.syntax classes.struct
core-foundation io.encodings.utf8 kernel system trash ;
IN: trash.macosx
On the Mac OS, there are several methods of moving files to the trash. A
good discussion on
CocoaDev lists some of them. We are going to use the
alien
vocabulary to make calls into the File
Manager
in the CarbonCore.framework
. Some functions will return an OSStatus
flag (a signed 32-bit integer) to indicate if the operation succeeded.
We will add a
TYPEDEF:
for it, and then define the GetMacOSStatusCommentString
function that
converts the status flag into a human readable error.
TYPEDEF: SInt32 OSStatus
FUNCTION: char* GetMacOSStatusCommentString ( OSStatus err )
: check-err ( err -- )
[ GetMacOSStatusCommentString utf8 alien>string throw ]
unless-zero ;
Many of the file operations act on an FSRef
structure which represents
a path within the file system. We will define the
FSPathMakeRefWithOptions
function which will allow us to create these
references:
STRUCT: FSRef { hidden UInt8[80] } ;
TYPEDEF: UInt32 OptionBits
FUNCTION: OSStatus FSPathMakeRefWithOptions (
UInt8* path,
OptionBits options,
FSRef* ref,
Boolean* isDirectory
)
We can then make a <fs-ref>
word for creating references, given a path
to a file (or directory).
CONSTANT: kFSPathMakeRefDoNotFollowLeafSymlink 0x01
: <fs-ref> ( path -- fs-ref )
utf8 string>alien
kFSPathMakeRefDoNotFollowLeafSymlink
FSRef <struct>
[ f FSPathMakeRefWithOptions check-err ] keep ;
There are several ways of “trashing” files, but one recommended way is
implemented by the FSMoveObjectToTrashSync
function:
FUNCTION: OSStatus FSMoveObjectToTrashSync (
FSRef* source,
FSRef* target,
OptionBits options
)
Implementing the send-to-trash
word is now pretty straightforward:
CONSTANT: kFSFileOperationDefaultOptions 0x00
M: macosx send-to-trash ( path -- )
<fs-ref> f kFSFileOperationDefaultOptions
FSMoveObjectToTrashSync check-err ;
You can test this by creating a temporary file (e.g., /tmp/foo
),
sending it to the trash, and then verifying that it exists by looking in
the Finder’s Trash.
IN: scratchpad USING: trash io.encodings.ascii io.files ;
IN: scratchpad "" "/tmp/foo" ascii set-file-contents
IN: scratchpad "/tmp/foo" send-to-trash
Note: This method does not appear to support the “Put Back” functionality (to “undo” the trash operation). Perhaps there is some metadata that we can add (or a different function we can call) that will track the original file location so that the Finder knows where it should be restored to.
The code for this is on my GitHub.