[Openmcl-devel] Hemlock file save anomaly
gb at clozure.com
Sat May 5 13:17:50 PDT 2012
This explanation probably isn't too interesting and may not be
entirely correct in some details, but I think that it's generally accurate.
There are at least two ways of identifying files in a filesystem:
- by name: some string, like "/foo/bar/baz.lisp", maps to at most
one sequence of bytes on a disk (or accessible over a network, or
whatever.) Not all strings name files at any given point in time
on any given filesystem ("/contrived/example/missing" probably doesn't),
and symbolic links map strings to other strings (and therefore provide
a way of referring to a file by multiple names); some string (which
doesn't denote a symbolic link) is the canonical name of any file.
(If there are ways in which that isn't literally true, I think that
we can safely ignore those ways for now.)
- by some sort of identifier. On a unix-like system, the comination of
a "device identifier" and "inode" - where both of these things are
integers - uniquely identify at most one file; we can think of an inode
as being something that identifies a sequence of bytes on the device.
(The "open" system call tries to map a string to a device/inode combination.)
Other OSes likely offer similar functionality. A file can be
renamed (so that some string becomes the canonical name of the file); on unix-like
systems, "rename" generally can't be used to move a file from one
device to another. (The "mv" command may enforce this restriction,
or it may shut up and do a copy followed by a delete.)
Deleting a file (as an oversimplification) causes the bytes (sectors, allocation
units) associated with the file to become free space (e.g., deletes the inode
and removes the mapping between the canonical name and that inode.)
A hard link provides a way of mapping another name to an inode on the same
device. Because (a) hard links can't span devices and (b) they're tied to
this low-level implementation artifact (an inode), they're less flexible
and probably less widely-used than symbolic links are. Because they aren't
sensitive to file names, they're very occasionally useful.
Some (roughly) similar concepts were present in Classic MacOS. An "FSRef"
was an opaque 80-byte structure that uniquely identified a file; an "alias"
was a special type of file that could refer to another file and therefore
function sort of like a (hard or soft) link. The mechanism used to establish
and maintain this reference was also opaque; aliases behaved as if they
referenced FSRefs and may have also contained some metainformation that could
help reconnect an alias to its target file. I'm speaking of aliases in the
past tense, but (last time I looked) there was still some support for them
in the OSX finder. The last time I looked, Cocoa was just minimally aware
of aliases (there were some options that controlled how the standard Open/Save
panels handled them, but that was about it.)
Deleting a file in MacOS[X] effectively deletes its FSRef; aliases to that
FSRef became invalid. (Aliases behaved sort of like hard links in this
There are probably several plausible ways in which an editor-like program can
save a document (file), possibly creating a backup copy in the process. Two
of those ways include:
- renaming the existing file in the filesystem (creating a backup copy),
creating a new file with the original filename, and writing the new contents
to that new file. This is generally what most versions of Emacs do (though
Emacs allows for multiple versioned backup files so what actually happens
may be more complicated.) Note that the original inode's (FSRef's) contents
don't actually change in the simple case, though the inode's (FSRef's) name
changes; hard links will continue to refer to that inode and aliases will
continue to refer to the FSRef.
- doing it in the Apple-recommended way (or at least the way that Apple
started doing it after their implementation of aliases made it necessary.)
I've probably implemented it a few times (long ago), but may be even fuzzier
on the details than I've been so far. The general idea involves writing
the file's new contents to a temporary file and then doing some primitive
operation which exchanged the contents of the original file and this temporary.
When this sequence of operations is complete, the original FSRef (inode) still
has its original name(s) but has new contents, and things like hard links
and legacy aliases continue to reference the same inode (FSRef.)
The Apple-approved (-mandated) sequence was bizarre and complicated enough that
developers who had to implement it wouldn't have had time to shop in the Mac
App Store. Fortunately, Cocoa's NSDocument class hides many of the details;
an NSDocument-based application may need to (for instance) provide a method
that maps a document's contents to a sequence of bytes (an NSData object), but
generally doesn't have to worry about where to write these bytes in order for
them to eventually wind up in the right place and for this to happen in the
All of this works reasonably well as long as all applications that
modify a particular file implement those modifications by following
the Apple-approved protocols. If they don't, NSDocument's support for
doing things in the Apple-approved way gets confused. (Apple's TextEdit
application uses its own Document class instead of NSDocument; it gets
confused in similar but slightly different ways if a document that it
has open is saved from another application that doesn't do things in the
None of this is new and none of it has anything to do with Hemlock or
CCL. Apple's certainly aware of the issues and IIRC they have a web
page that offers helpful tips to address the confusion; most of those
tips seem to involve shopping in the Mac App Store for some reason.
As far as I know, the NSDocument behavior that one might want to
customize is pretty far under the hood. I often modify files that're
open in the CCL IDE in Emacs, but I think that somewhere over the last
10 years or so I've stopped expecting NSDocument to not get confused
by that. (It's probably not even fair to blame NSDocument; one could
argue that the real problem is that these ancient alias things require
that file-saving be done in a certain way and it's not generally possible
or desirable to enforce that and never has been.)
On Fri, 4 May 2012, peter wrote:
> I'm unsure whether this is a bug, feature or how things are meant to be.
> I have a file, I open it in the CCL editor. While it is still open (and
> unmodified), I also open the same file in another editor. I change the file
> there and save it. Back in Hemlock I use [File] [Revert] to update the CCL
> buffer. I do some edits in CCL. Now I try to save the file and get:
> The location of the document "xyz.lisp" cannot be determined.
> You can specify where to save it.
> [Cancel] [Save As...]
> I command-mouse the editor window top label, this opens the appropriate
> folder as normal. The file is still there, all seems safe and sound.
> Perhaps I'm being disgusting by trying to edit the same file
> semi-concurrently from two different editors. The other editor does not
> complain, and for multi-platform developments (same source running to two
> different environments) this seems a useful technique at times.
> Openmcl-devel mailing list
> Openmcl-devel at clozure.com
More information about the Openmcl-devel