I'd like to rename a file, in Common Lisp. For specificness: I'd like
to rename
* `foo.new' to
* `foo'
* on Unixy platforms,
* and if I have to do any system-specific weirdness, in CMU CL on
Linux.
Unfortunately, the spec for RENAME-FILE seems deranged. So, I try and
do the obvious thing:
* (rename-file "foo.new" "foo")
#P"/tmp/mwooding/foo/foo.new"
#P"/tmp/mwooding/foo/foo.new"
#P"/tmp/mwooding/foo/foo.new"
Hmm. That doesn't look encouraging. Sure enough:
* (directory "./")
(#P"/tmp/mwooding/foo/foo.new")
What's gone wrong?
: 20.2 The Files Dictionary
:
: [...]
:
: Function RENAME-FILE
:
: [...]
:
: rename-file returns three values if successful. The primary value,
: defaulted-new-name, is the resulting name which is composed of
: new-name with any missing components filled in by performing a
: merge-pathnames operation using filespec as the defaults.
Why? This is deranged!
So:
* I'm currently using unix:unix-rename. I'd like to stop, because
it's a bit grim, really.
* It appears that CLISP can be persuaded to do the right thing if I
say
(rename-file (make-pathname :directory '(:relative)
:name "foo.new")
"foo"
but on CMU CL and SBCL this makes no difference, since they default
new-name using (truename filespec) rather than filespec. Is this
allowed by the spec? (The TRUENAME process causes a reparsing of
the actual name into a pathname, which defeats my chicanery.)
* Is there a sensible way of doing what I want, in Common Lisp?
-- [mdw]
Mark Wooding <···@distorted.org.uk> writes:
> Is there a sensible way of doing what I want, in Common Lisp?
I got the following to work in LispWorks, more through experimentation
than understanding:
> (rename-file (make-pathname :directory '(:absolute "temp")
:name "foo" :type "new")
(make-pathname :type :unspecific))
#P"C:/temp/foo"
#P"C:/temp/foo.new"
#P"C:/temp/foo"
I had expected a type of nil or "" to work, but in both cases the type
of the original filespec gets merged back in.
--
Steven E. Harris
"Steven E. Harris" <···@panix.com> writes:
> Mark Wooding <···@distorted.org.uk> writes:
>
>> Is there a sensible way of doing what I want, in Common Lisp?
>
> I got the following to work in LispWorks, more through experimentation
> than understanding:
>
>> (rename-file (make-pathname :directory '(:absolute "temp")
> :name "foo" :type "new")
> (make-pathname :type :unspecific))
> #P"C:/temp/foo"
> #P"C:/temp/foo.new"
> #P"C:/temp/foo"
>
>
> I had expected a type of nil or "" to work, but in both cases the type
> of the original filespec gets merged back in.
Which is nice, but implementation dependant it seems. clisp doesn't take it:
*** - MAKE-PATHNAME: illegal :TYPE argument :UNSPECIFIC
Indeed, CLHS MAKE-PATHNAME says:
Portable programs should not supply :unspecific for any
component. See Section 19.2.2.2.3 (:UNSPECIFIC as a Component
Value).
--
__Pascal Bourguignon__ http://www.informatimago.com/
CAUTION: The mass of this product contains the energy equivalent of
85 million tons of TNT per net ounce of weight.
Pascal Bourguignon <···@informatimago.com> writes:
> Indeed, CLHS MAKE-PATHNAME says:
>
> Portable programs should not supply :unspecific for any
> component. See Section 19.2.2.2.3 (:UNSPECIFIC as a Component
> Value).
Ouch. I missed that part. I was determined to believe that CL had a
way.
--
Steven E. Harris
Steven E. Harris <···@panix.com> wrote:
+---------------
| Mark Wooding <···@distorted.org.uk> writes:
| > Is there a sensible way of doing what I want, in Common Lisp?
|
| I got the following to work in LispWorks, more through experimentation
| than understanding:
| > (rename-file (make-pathname :directory '(:absolute "temp")
| :name "foo" :type "new")
| (make-pathname :type :unspecific))
| #P"C:/temp/foo"
| #P"C:/temp/foo.new"
| #P"C:/temp/foo"
+---------------
It also works in CMUCL [on Linux or FreeBSD, at least]:
cmu> (rename-file "foo.new"
(make-pathname :name "foo" :type :unspecific))
#p"/usr/u/rpw3/foo"
#p"/usr/u/rpw3/foo.new"
#p"/usr/u/rpw3/foo"
cmu>
But as you saw, I'm sure, Pascal B. warned that this use
of :UNSPECIFIC is non-portable. However, CLHS 19.2.2.2.3
*does* say:
19.2.2.2.3 :UNSPECIFIC as a Component Value
....
However, a conforming program can, if it is careful, successfully
manipulate user-supplied data which contains or refers to non-portable
pathname components.
...
When writing the value of any pathname component, the consequences
are undefined if :unspecific is given for a pathname in a file system
for which it does not make sense.
But in a file system for which it *DOES* make sense... ;-} ;-}
-Rob
-----
Rob Warnock <····@rpw3.org>
627 26th Avenue <URL:http://rpw3.org/>
San Mateo, CA 94403 (650)572-2607
Mark Wooding <···@distorted.org.uk> writes:
> I'd like to rename a file, in Common Lisp. For specificness: I'd like
> to rename
>
> * `foo.new' to
> * `foo'
> * on Unixy platforms,
> * and if I have to do any system-specific weirdness, in CMU CL on
> Linux.
>
> Unfortunately, the spec for RENAME-FILE seems deranged. So, I try and
> do the obvious thing:
>
> * (rename-file "foo.new" "foo")
> #P"/tmp/mwooding/foo/foo.new"
> #P"/tmp/mwooding/foo/foo.new"
> #P"/tmp/mwooding/foo/foo.new"
>
> Hmm. That doesn't look encouraging. Sure enough:
>
> * (directory "./")
> (#P"/tmp/mwooding/foo/foo.new")
>
> What's gone wrong?
>
> : 20.2 The Files Dictionary
> :
> : [...]
> :
> : Function RENAME-FILE
> :
> : [...]
> :
> : rename-file returns three values if successful. The primary value,
> : defaulted-new-name, is the resulting name which is composed of
> : new-name with any missing components filled in by performing a
> : merge-pathnames operation using filespec as the defaults.
>
> Why? This is deranged!
>
> So:
>
> * I'm currently using unix:unix-rename. I'd like to stop, because
> it's a bit grim, really.
You don't have a lot of choices.
Either you limit yourself to logical pathnames (this is portable),
and to the translations you can write,
or you take in the implementation specific physical pathnames quircks,
or you use unix- or posix- level paths.
For example, once you've set up the logical pathname translations for
a logical host DOT, (which can be done in an implementation specifica
way), you can use them portably:
[57]> (setf (logical-pathname-translations "DOT")
(list (list #P"DOT:*.*.*"
(merge-pathnames
(make-pathname :name ".*" #|clisp specific|#
:type :wild
:version :wild)
(user-homedir-pathname)))
(list #P"DOT:*.*"
(merge-pathnames
(make-pathname :name ".*" #|clisp specific|#
:type :wild)
(user-homedir-pathname)))
(list #P"DOT:*"
(merge-pathnames
(make-pathname :name ".*" #|clisp specific|#)
(user-homedir-pathname)))))
((#P"DOT:*.*.*" #P"/home/pjb/.*.*") (#P"DOT:*.*" #P"/home/pjb/.*.*")
(#P"DOT:*" #P"/home/pjb/.*"))
[58]> (translate-logical-pathname #P"DOT:EMACS")
#P"/home/pjb/.emacs"
So now on, you can access to a whole class of unix files for which
there's no proper logical pathname syntax, portably and with a proper
logical pathname syntax: #P"DOT:EMACS", #P"DOT:BASHRC" etc. The good
thing about it, is that it will still work with another lisp
implementation, or on another OS with different rules. You just need
to provide a logical-pathname translations file, and use
LOAD-LOGICAL-PATHNAME-TRANSLATIONS at the beginning of the program.
In the second alternative, you can use physical pathnames. But this is
not portable either across lisp implementations or different file
systems, and this may not be too consistent as you've noticed with RENAME-FILE.
> * It appears that CLISP can be persuaded to do the right thing if I
> say
>
> (rename-file (make-pathname :directory '(:relative)
> :name "foo.new")
> "foo"
That's because clisp accepts dots in names. This allows us to
manipulate file names starting with dots, but this is rather
ill-defined because:
(values
(make-pathname :directory '(:relative) :name "foo.new")
(make-pathname :directory '(:relative) :name "foo" :type "new")
(pathname-name (make-pathname :directory '(:relative) :name "foo.new"))
(pathname-name (make-pathname :directory '(:relative) :name "foo"
:type "new"))
(truename (make-pathname :directory '(:relative) :name "foo.new"))
(truename (make-pathname :directory '(:relative) :name "foo" :type "new")))
--> #P"./foo.new" ;
#P"./foo.new" ;
"foo.new" ; !!!
"foo" ;
#P"/tmp/foo.new" ;
#P"/tmp/foo.new"
This builds pathnames that are externally undistinguishable from
others, different pathnames. Granted, on a unix file system, both
would refer to the same file.
But when reading back a pathname either as #P"foo.new", it parses it
in one way, not the other, and for rename-file you'd want the other.
> * Is there a sensible way of doing what I want, in Common Lisp?
There doesn't seem to be. CLHS specifies that the new name is built by
merging the second argument with the first:
rename-file returns three values if successful. The primary value,
defaulted-new-name, is the resulting name which is composed of
new-name with any missing components filled in by performing a
merge-pathnames operation using filespec as the defaults.
So if you don't provide a type for the new name, it will get the type
from the old name. It's designed to be used as:
(rename-file #P"OLD.LISP" #P"NEW")
and obtain a file: #P"NEW.LISP".
File systems that managed file names and file types didn't allow not
to specify a type, and Common Lisp logical pathnames doesn't allow
types to be an empty string. Either there is no type (in which case,
the merging will copy over the old type), or the type is at least one
character.
In clisp there's an implementation specific behavior, where you can
specify an empty string as a type:
(rename-file (make-pathname :name "foo" :type "new")
(make-pathname :name "foo" :type "")) ; works:
-->
#P"foo." ;
#P"/tmp/foo.new" ;
#P"/tmp/foo."
But this doesn't do what you want.
--
__Pascal Bourguignon__ http://www.informatimago.com/
"This statement is false." In Lisp: (defun Q () (eq nil (Q)))