X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~mdw/git/lisp/blobdiff_plain/443fcfc494ed940c60d94c70e3391bf3c79c915f..1a50efd8a9e976bf16a29ce381ea38c6b9a39ea1:/safely.lisp diff --git a/safely.lisp b/safely.lisp index 8cd0f9a..9b4ab45 100644 --- a/safely.lisp +++ b/safely.lisp @@ -33,7 +33,13 @@ (in-package #:safely) #+(or cmu sbcl) (eval-when (:compile-toplevel :execute) (import #+cmu '(ext:unix-namestring unix:unix-link) - #+sbcl '(sb-int:unix-namestring))) + #+sbcl '(sb-ext:native-namestring))) +#+cmu +(progn + (declaim (inline unix-namestring)) + (defun native-namestring (pathname &key as-file) + (declare (ignore as-file)) + (unix-namestring pathname nil))) (defstruct (safely (:predicate safelyp)) "Stores information about how to commit or undo safe writes." @@ -106,8 +112,10 @@ (defun safely-open-output-stream (safe file &rest open-args) (declaim (inline rename)) (defun rename (old new) - #-clisp (rename-file old new) - #+clisp (posix:copy-file old new :method :rename)) + (let ((target (make-pathname :directory '(:relative) + :defaults new))) + #-clisp (rename-file old target) + #+clisp (rename-file old target :if-exists :overwrite))) (defun delete-file-without-moaning (file) "Delete the FILE, ignoring errors." @@ -158,20 +166,22 @@ (defun safe-copy (file tag) ;; Use link(2) where available. (generate-fresh-file-name file tag (lambda (name) - (let ((from (unix-namestring file t)) - (to (unix-namestring name nil))) + (let ((from (native-namestring file + :as-file t)) + (to (native-namestring name + :as-file t))) (and from to (unix-link from to))))) #+clisp (generate-fresh-file-name file tag (lambda (name) - (>= (unix:link (namestring file) - (namestring name)) - 0))) + (posix:copy-file (namestring file) + (namestring name) + :method :hardlink + :if-exists nil))) - - #-(or cmu sbcl) + #-(or cmu sbcl clisp) ;; Otherwise just copy the file contents and hope for the best. (with-open-file (input file :element-type :default) (multiple-value-bind @@ -210,23 +220,23 @@ (defun safely-commit (safe) (loop (unless trail (return)) - (let ((job (pop trail))) - (ecase (car job) - (:shunt (destructuring-bind (tag new file) job - (declare (ignore tag)) - (push `(:rmtmp ,new) revert) - (if (probe-file file) - (let ((old (safe-copy file "old"))) - (push `(:rmtmp ,old) cleanup) - (push `(:revert ,old ,file) revert)) - (push `(:rmtmp ,file) revert)) - (rename new file))) - (:delete (destructuring-bind (tag file) job - (declare (ignore tag)) - (let ((old (safe-copy file "delete"))) - (push `(:revert ,old ,file) revert) - (push `(:rmtmp ,old) cleanup) - (delete-file file))))))) + (let ((job (pop trail))) + (ecase (car job) + (:shunt (destructuring-bind (tag new file) job + (declare (ignore tag)) + (push `(:rmtmp ,new) revert) + (if (probe-file file) + (let ((old (safe-copy file "old"))) + (push `(:rmtmp ,old) cleanup) + (push `(:revert ,old ,file) revert)) + (push `(:rmtmp ,file) revert)) + (rename new file))) + (:delete (destructuring-bind (tag file) job + (declare (ignore tag)) + (let ((old (safe-copy file "delete"))) + (push `(:revert ,old ,file) revert) + (push `(:rmtmp ,old) cleanup) + (delete-file file))))))) (setf revert nil)) (safely-unwind trail) (safely-unwind revert)