Maybe I am not understanding the following from the page for
CLOSE from the Hyperspec:
"If abort is true, an attempt is made to clean up any side
effects of having created stream. If stream performs output
to a file that was created when the stream was created, the
file is deleted and any previously existing file is not superseded."
To me it sounds like
a) if the file was created then on close with :abort t it will be deleted
b) if there was a prior existing file which was to be superseded, it
would still be intact if the file was closed with :abort t
I wrote the following test code:
(defun test-close-abort ()
(let ((filename "testfile.txt"))
(labels ((open-file () (open filename :direction :output :if-does-not-exist :create :if-exists :supersede))
(probe ()
(let ((pf (probe-file filename)))
(format t "probe file returns ~w~%" pf)
(when pf
(with-open-file (in filename :direction :input)
(let ((len (file-length in)))
(format t "length is ~a" len)
(unless (zerop len)
(format t ", contents are: ~a" (read-line in)))
(terpri))))))
(writefile (string-to-write abortp)
(format t "open, write \"~a\", close with abort ~a~%" string-to-write abortp)
(close (let ((file (open-file))) (format file "~a~%" string-to-write) file) :abort abortp)
(probe)))
(writefile "First Open" t)
(writefile "Second Open" nil)
(writefile "Third Open" t))))
If my assumptions are true then neither CMU, CLISP, or ACL handle this correctly.
Here is the output from each:
CMU 18C
* (test-close-abort)
open, write "First Open", close with abort T
probe file returns NIL
open, write "Second Open", close with abort NIL
probe file returns #p"/home/jockc/testfile.txt"
length is 12, contents are: Second Open
open, write "Third Open", close with abort T
probe file returns NIL ; <== per (b) above should exist with contents "Second Open"
NIL
CLISP 2000-03-06 (March 2000)
[2]> (test-close-abort)
open, write "First Open", close with abort T
probe file returns #P"/home/jockc/testfile.txt"
length is 11, contents are: First Open ; <== file should have been deleted
open, write "Second Open", close with abort NIL
probe file returns #P"/home/jockc/testfile.txt"
length is 12, contents are: Second Open
open, write "Third Open", close with abort T
probe file returns #P"/home/jockc/testfile.txt"
length is 11, contents are: Third Open ; <== should be Second Open
NIL
CLISP seem to just ignore it completely.
ACL 6.1
open, write "First Open", close with abort T
probe file returns #p"C:\\PROGRA~1\\acl61\\testfile.txt"
length is 0 ; <== sets size to zero but should delete
open, write "Second Open", close with abort NIL
probe file returns #p"C:\\PROGRA~1\\acl61\\testfile.txt"
length is 13, contents are: Second Open
open, write "Third Open", close with abort T
probe file returns #p"C:\\PROGRA~1\\acl61\\testfile.txt"
length is 0
Am I just misinterpereting the wording in CLHS?
Jock
* Jock Cooper wrote:
> "If abort is true, an attempt is made to clean up any side
I think the crucial point is that *an attempt* is made. No promises
are given, and given the nature of Unixoid filesystems, it's quite
hard to do everything you might want.
> CMU 18C
* (test-close-abort)
> open, write "First Open", close with abort T
> probe file returns NIL
> open, write "Second Open", close with abort NIL
> probe file returns #p"/home/jockc/testfile.txt"
> length is 12, contents are: Second Open
> open, write "Third Open", close with abort T
> probe file returns NIL ; <== per (b) above should exist with contents "Second Open"
> NIL
This is the kind of reasonable-best-attempt that I'd expect a
unix-based implementation to make: if the file was created, then a
CLOSE with ABORT T deletes it, but if it was being superseded then you
lose, because in order to do this right under Unix you'd have to write
to some temporary file and then, on close, delete the existing file
and rename the temporary file. This in turn raises all sorts of
exciting issues - for instance things can fail *at close time* which
you might well not expect (this is rather similar to the case with
NFS, where things can fail when the file is closed in a way which many
programs do not check...).
Really, the issue is that Unix doesn't have a notion of superseding a
file. In a versioned filesystem, for instance, superseding a file is
often easy - you just write a new version in the standard way, and if
you want to not do that after all, you delete the new version, leaving
all the old versions intact.
> Am I just misinterpereting the wording in CLHS?
Yes, i think so.
--tim
On 04 Oct 2002 20:30:02 +0100, Tim Bradshaw <···@cley.com> said:
[...]
TB> This is the kind of reasonable-best-attempt that I'd expect a
TB> unix-based implementation to make: if the file was created, then a
TB> CLOSE with ABORT T deletes it, but if it was being superseded then you
TB> lose, because in order to do this right under Unix you'd have to write
TB> to some temporary file and then, on close, delete the existing file
TB> and rename the temporary file. This in turn raises all sorts of
TB> exciting issues - for instance things can fail *at close time* which
TB> you might well not expect
I think it deserves to be noted that there are quite a few exciting
issues to be dealt with even if we ignore, for the sake of
argument, dealing with failures (of course, it may be true that
nothing excites like a failure):
* `unlink and rename' requires write access to the directory;
* `unlink and rename' is in general incompatible with preserving
the ownership and permissions of the original file;
* `unlink and rename' is incompatible with preserving hard links
(of course, it is possible to write to a temporary file first,
and on (normal) closing copy its contents into the original
file...).
On the other hand, without write access to the file, but with write
access to the directory, `unlink and rename' would be the only way
to rewrite the original file. Is doing that OK? Is it OK if it
happens silently, as a side effect of choosing `unlink and rename'?
One good think about the Common Lisp semantics of OPEN and CLOSE is
that, even though written with the experience from other file
systems in mind, and allowing the implementation to take full
advantage of what they offer, it does not impose undue constraints
that would render a Unix implementation damaged.
---Vassil.
On 04 Oct 2002 20:30:02 +0100, Tim Bradshaw <···@cley.com> said:
[...]
TB> This is the kind of reasonable-best-attempt that I'd expect a
TB> unix-based implementation to make: if the file was created, then a
TB> CLOSE with ABORT T deletes it, but if it was being superseded then you
TB> lose, because in order to do this right under Unix you'd have to write
TB> to some temporary file and then, on close, delete the existing file
TB> and rename the temporary file. This in turn raises all sorts of
TB> exciting issues - for instance things can fail *at close time* which
TB> you might well not expect
I think it deserves to be noted that there are quite a few exciting
issues to be dealt with even if we ignore, for the sake of
argument, dealing with failures (of course, it may be true that
nothing excites like a failure):
* `unlink and rename' requires write access to the directory;
* `unlink and rename' is in general incompatible with preserving
the ownership and permissions of the original file;
* `unlink and rename' is incompatible with preserving hard links
(of course, it is possible to write to a temporary file first,
and on (normal) closing copy its contents into the original
file...).
On the other hand, without write access to the file, but with write
access to the directory, `unlink and rename' would be the only way
to rewrite the original file. Is doing that OK? Is it OK if it
happens silently, as a side effect of choosing `unlink and rename'?
One good think about the Common Lisp semantics of OPEN and CLOSE is
that, even though written with the experience from other file
systems in mind, and allowing the implementation to take full
advantage of what they offer, it does not impose undue constraints
that would render a Unix implementation damaged.
---Vassil.
On 04 Oct 2002 20:30:02 +0100, Tim Bradshaw <···@cley.com> said:
[...]
TB> This is the kind of reasonable-best-attempt that I'd expect a
TB> unix-based implementation to make: if the file was created, then a
TB> CLOSE with ABORT T deletes it, but if it was being superseded then you
TB> lose, because in order to do this right under Unix you'd have to write
TB> to some temporary file and then, on close, delete the existing file
TB> and rename the temporary file. This in turn raises all sorts of
TB> exciting issues - for instance things can fail *at close time* which
TB> you might well not expect
I think it deserves to be noted that there are quite a few exciting
issues to be dealt with even if we ignore, for the sake of
argument, dealing with failures (of course, it may be true that
nothing excites like a failure):
* `unlink and rename' requires write access to the directory;
* `unlink and rename' is in general incompatible with preserving
the ownership and permissions of the original file;
* `unlink and rename' is incompatible with preserving hard links
(of course, it is possible to write to a temporary file first,
and on (normal) closing copy its contents into the original
file...).
On the other hand, without write access to the file, but with write
access to the directory, `unlink and rename' would be the only way
to rewrite the original file. Is doing that OK? Is it OK if it
happens silently, as a side effect of choosing `unlink and rename'?
One good think about the Common Lisp semantics of OPEN and CLOSE is
that, even though written with the experience from other file
systems in mind, and allowing the implementation to take full
advantage of what they offer, it does not impose undue constraints
that would render a Unix implementation damaged.
---Vassil.