[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.lisp

open :direction :output :if-exists :rename

Jim Newton

4/19/2016 9:48:00 AM

Does anyone know if there's a way to tell OPEN to not override the backup file?

Using (open "file" :direction :output :if-exists :rename)
The first time it causes "file" to be created.
The second time "file" is renamed to "file.bak" (on SBCL) and "file" is created anew.
The third time "file" is renamed to "file.bak" losing the previous "file.bak".

If I want to prevent these files from being deleted, I can of course write code to do that,
by just checking and renaming "file" before calling open.
But it seems like OPEN almost has this capability already. Does anyone know whether
it's somehow built-in?

7 Answers

taruss

4/19/2016 5:05:00 PM

0

On Tuesday, April 19, 2016 at 2:47:46 AM UTC-7, Jim Newton wrote:
> Does anyone know if there's a way to tell OPEN to not override the backup file?
>
> Using (open "file" :direction :output :if-exists :rename)
> The first time it causes "file" to be created.
> The second time "file" is renamed to "file.bak" (on SBCL) and "file" is created anew.
> The third time "file" is renamed to "file.bak" losing the previous "file.bak".
>
> If I want to prevent these files from being deleted, I can of course write code to do that,
> by just checking and renaming "file" before calling open.
> But it seems like OPEN almost has this capability already. Does anyone know whether
> it's somehow built-in?

taruss

4/19/2016 6:18:00 PM

0

On Tuesday, April 19, 2016 at 2:47:46 AM UTC-7, Jim Newton wrote:
> Does anyone know if there's a way to tell OPEN to not override the backup file?
>
> Using (open "file" :direction :output :if-exists :rename)
> The first time it causes "file" to be created.
> The second time "file" is renamed to "file.bak" (on SBCL) and "file" is created anew.
> The third time "file" is renamed to "file.bak" losing the previous "file.bak".
>
> If I want to prevent these files from being deleted, I can of course write code to do that,
> by just checking and renaming "file" before calling open.
> But it seems like OPEN almost has this capability already. Does anyone know whether
> it's somehow built-in?

Let's try a real answer this time.

It seems like :if-exists :new-version should be what you want.

But when I tried this in SBCL on Unix, it failed with the somewhat curious
message:
"OPEN :IF-EXISTS :NEW-VERSION is not supported when a new version must be created."

But this is perhaps an issue with native file system support. Under VAX VMS
the native file system supported versions for files, so there was a native
support for this. I suppose one could choose to do something like the Emacs
convention of *.~1~, *.~2~, etc. but that might be too heroic for the lisp
implementation to want to support, since it isn't universal.

I guess .bak is more standard.

But the real answer is that if you want to manage the file actions in detail,
you will probably have to implement your own management strategy.



Pascal J. Bourguignon

4/19/2016 9:02:00 PM

0

taruss@google.com writes:

> Let's try a real answer this time.
>
> It seems like :if-exists :new-version should be what you want.
>
> But when I tried this in SBCL on Unix, it failed with the somewhat curious
> message:
> "OPEN :IF-EXISTS :NEW-VERSION is not supported when a new version must be created."
>
> But this is perhaps an issue with native file system support. Under VAX VMS
> the native file system supported versions for files, so there was a native
> support for this. I suppose one could choose to do something like the Emacs
> convention of *.~1~, *.~2~, etc. but that might be too heroic for the lisp
> implementation to want to support, since it isn't universal.


cmucl does that.

* (defun lsfoo ()
(copy-stream
(extensions:process-output
(extensions:run-program
"bash" '("-c" "ls -l /tmp/foo*")
:output :stream))
*standard-output*))

LSFOO
* (defun newfoo ()
(with-open-file (out "/tmp/foo.txt"
:direction :output
:if-exists :new-version)
(write-line "Hello World!" out)))

NEWFOO
* (loop repeat 4 do (lsfoo) (newfoo) (terpri) finally (lsfoo))
ls: cannot access /tmp/foo*: No such file or directory

-rw-r--r-- 1 pjb pjb 13 Apr 19 22:58 /tmp/foo.txt

-rw-r--r-- 1 pjb pjb 13 Apr 19 22:58 /tmp/foo.txt
-rw-r--r-- 1 pjb pjb 13 Apr 19 22:58 /tmp/foo.txt.~1~

-rw-r--r-- 1 pjb pjb 13 Apr 19 22:58 /tmp/foo.txt
-rw-r--r-- 1 pjb pjb 13 Apr 19 22:58 /tmp/foo.txt.~1~
-rw-r--r-- 1 pjb pjb 13 Apr 19 22:58 /tmp/foo.txt.~2~

-rw-r--r-- 1 pjb pjb 13 Apr 19 22:58 /tmp/foo.txt
-rw-r--r-- 1 pjb pjb 13 Apr 19 22:58 /tmp/foo.txt.~1~
-rw-r--r-- 1 pjb pjb 13 Apr 19 22:58 /tmp/foo.txt.~2~
-rw-r--r-- 1 pjb pjb 13 Apr 19 22:58 /tmp/foo.txt.~3~
NIL
* (directory (make-pathname :directory '(:absolute "tmp") :name "foo" :type "txt" :version :wild))

(#P"/tmp/foo.txt" #P"/tmp/foo.txt.~1~" #P"/tmp/foo.txt.~2~" #P"/tmp/foo.txt.~3~")
* (directory (make-pathname :directory '(:absolute "tmp") :name "foo" :type "txt" :version 2))

(#P"/tmp/foo.txt.~2~")
*


emacs does the same (with the right customization).

I would argue that all CL implementations accessing unix file systems
should do that too.

> I guess .bak is more standard.
>
> But the real answer is that if you want to manage the file actions in detail,
> you will probably have to implement your own management strategy.

--
__Pascal Bourguignon__ http://www.informat...
â??The factory of the future will have only two employees, a man and a
dog. The man will be there to feed the dog. The dog will be there to
keep the man from touching the equipment.� -- Carl Bass CEO Autodesk

Ralph Schleicher

4/19/2016 10:24:00 PM

0

taruss@google.com writes:

> But the real answer is that if you want to manage the file actions in detail,
> you will probably have to implement your own management strategy.

Here's a quick and dirty transliteration from some existing C code
of mine:

(defun backup-file (file-name &optional method)
"Backup a file.

First argument FILE-NAME is the file to be saved.
Second argument METHOD is the backup method. Value is either the
keyword :simple, :numbered, :existing, or :none. Default is to
infer the backup method from the value of the 'VERSION_CONTROL'
environment variable. See the Emacs documentation for more
details.

When this function returns, file FILE-NAME is out of the way."
(let ((backup (backup-file-name file-name method)))
(if (null backup)
(delete-file file-name)
(progn
(ignore-errors
(delete-file backup))
(rename-file file-name backup)))))

(defun backup-file-name (file-name &optional method)
"Return the backup file name for FILE-NAME."
(when (or (null method) (eq method :default))
(let ((env (uiop:getenv "VERSION_CONTROL")))
(setf method (cond ((null env)
:existing)
((or (string= env "simple")
(string= env "never"))
:simple)
((or (string= env "numbered")
(string= env "t"))
:numbered)
((or (string= env "existing")
(string= env "nil"))
:existing)
((or (string= env "none")
(string= env "off"))
:none)
(t
;; Any other value.
:existing)))))
(ecase method
(:simple
(simple-backup-file-name file-name))
(:numbered
(numbered-backup-file-name file-name t))
(:existing
(numbered-backup-file-name file-name))
(:none
nil)))

;; TODO: Check if FILE-NAME is a directory.
(defun simple-backup-file-name (file-name)
(concatenate 'string file-name "~"))

(defun numbered-backup-file-name (file-name &optional force)
(let* ((dir (uiop:pathname-directory-pathname file-name))
(base (file-namestring file-name))
;; Minimum file name length of a numbered backup file name.
(len (length base))
(minlen (+ len 4))
;; Highest version number.
(vernum (if force 0 -1)))
(iter (for dirent :in (uiop:directory-files dir))
(for ent = (file-namestring dirent))
(when (or (< (length ent) minlen)
(string/= ent base :end1 len)
(char/= (char ent len) #\.)
(char/= (char ent (1+ len)) #\~)
(not (digitp (char ent (+ len 2)))))
(next-iteration))
(for pos = (+ len 3))
(for end = (length ent))
(iter (while (and (< pos end) (digitp (char ent pos))))
(incf pos))
(unless (and (= (1+ pos) end) (char= (char ent pos) #\~))
(next-iteration))
;; Found a numbered backup file.
(for num = (parse-integer ent :start (+ len 2) :end pos))
(when (> num vernum)
(setf vernum num)))
;; Make numbered backups for files that have numbered backups.
;; Otherwise, make single backups.
(if (minusp vernum)
(simple-backup-file-name file-name)
(concatenate 'string file-name (format nil ".~~~D~~" (1+ vernum))))))

(defun digitp (c)
(and (standard-char-p c)
(digit-char-p c)))

--
Ralph

Madhu

4/20/2016 1:41:00 AM

0


* rs+usenet@ralph-schleicher.de <86d1pl1bky.fsf@lima.oberreute.mueller-schleicher.de> :
Wrote on Wed, 20 Apr 2016 00:24:29 +0200:

| taruss@google.com writes:
|
|> But the real answer is that if you want to manage the file actions in
|> detail, you will probably have to implement your own management
|> strategy.
|
| Here's a quick and dirty transliteration from some existing C code of
| mine:
|
| (defun backup-file-name (file-name &optional method)
;;; snip
| (ecase method
| (:simple
| (simple-backup-file-name file-name))
| (:numbered
| (numbered-backup-file-name file-name t))
| (:existing
| (numbered-backup-file-name file-name))
| (:none
| nil)))
;;; snip
| (defun numbered-backup-file-name (file-name &optional force)
| (let* ((dir (uiop:pathname-directory-pathname file-name))
;;; snip
| (vernum (if force 0 -1)))
| (iter (for dirent :in (uiop:directory-files dir))
| (for ent = (file-namestring dirent))

[I should've stopped reading when I saw uiop. I stopped reading when I
saw iter]

When handling backups, for most types of files, I've found that
including the file modification date in the backup number to be more
useful than just using a simple incremented (or rotated) "~.[0-9]+~"
backup pattern. `ls' is also able to sort the names usefully

-rw-r--r-- 1 madhu users 14695 Sep 24 2015 foo.l.~20150924~
-rw-r--r-- 1 madhu users 10122 Oct 17 2015 foo.l.~20151017~
-rw-r--r-- 1 madhu users 12303 Oct 18 2015 foo.l.~20151018~

--- Madhu

Carlos

4/20/2016 3:14:00 PM

0

On 20/04/2016 3:41, Madhu wrote:
> [I should've stopped reading when I saw uiop. I stopped reading when I
> saw iter]

What's wrong with iterate?

--


Marco Antoniotti

4/21/2016 12:27:00 PM

0

On Wednesday, April 20, 2016 at 5:14:02 PM UTC+2, Carlos wrote:
> On 20/04/2016 3:41, Madhu wrote:
> > [I should've stopped reading when I saw uiop. I stopped reading when I
> > saw iter]
>
> What's wrong with iterate?

It does not trigger the WJ-bot. Use LOOP :)

Cheers
--
MA