[Openmcl-devel] Hemlock file save anomaly

Gary Byers 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
right way.

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
Apple-approved way.

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
> http://clozure.com/mailman/listinfo/openmcl-devel

More information about the Openmcl-devel mailing list