Error: Failed to DELETE file D:/code/lisp/bleh/: 5: Access is denied.(5)
Any chance someone out there can help me?
I think you should not do that. I think it is a serious error for any user
program to assume the role of administrator, even over files it has created.
I am constantly annoyed by user programs that assume that I want to overwrite
files. That is none of their business. If a file exists with the name they
also want to use, it may still be important to me, and I would really like
the user program not to delete its contents so I have to drag it back from a
backup disk. With real file versions, this would never be a problem, but
since Unix and Windows do not suppport file versions, the accidental loss of
information they so strongly favor and cause is a major pain. So I think
that if you want system administration tools, you should not look for help in
the language, but in the system administration subsystem, and you should make
a strong distinction between user program and administrators. Not so strong,
of course, that you would need to do a serious change of identity, but strong
enough that you do not accidentally lose or destroy information.
--
Guide to non-spammers: If you want to send me a business proposal, please be
specific and do not put "business proposal" in the Subject header. If it is
urgent, do not use the word "urgent". If you need an immediate answer, give
me a reason, do not shout "for your immediate attention". Thank you.
I understand your points, and they are good, but the tool I am writing is
not making any assumptions i.e. I am going to ASK it to delete a directory,
not have it do so as a side effect of some other function. I dislike
programs that take power away from the user. This tool is to GIVE me power
over my files, not take it away.
It looks like you might be doing this in windows. If you try to
remove a directory that is currently in use, the error reported by
windows is "Access Denied". Make sure that no running programs
started out of that directory, and that no files are currently open in
that directory, and give it another shot. :)
(Just a guess).
-Tim
--
Turing Stable: (adj) The property of continuing to be Turing complete,
after running for an hour.
I appreciate this idea, but I am running this on a test directory tree that
has nothing open in it nor does it have any programs running in it. *sigh*
With all the reading I have been doing it looks like Common Lisp defines a
facility to create directories (ensure-directories-exist) but no facility to
destroy them.
I could not find this function in Paul Graham's handy dandy Lisp
reference section in the back of ANSI Common Lisp, so I don't know if
this is a standard lisp function or not, but the clisp implementation
of common lisp contains a function "delete-dir" that does the job
nicely.
Example:
(delete-dir "~/blech/")
The function returned true for me if successful, but returned a Unix
EILSEQ error if the directory did not exist. I think that error must
be a bug in the clisp port to Darwin, which is what I am using it on.
"Thomas M. Magee" <t...@tommagee.com> wrote in message
news:7f6ff162.02062...@posting.google.com...
Directories are implementation-dependent by their very nature. What is
slightly painful is that Common Lisp vendors on operating system families
have somehow failed to agree on how to make these simple things portable
within the famiilies. This has some historical reasons, which mostly have to
do with how Common LIsp people tend to regard the Unix or Windows way as
"wrong" and go on to reinvent their own "right" solution differently. There
is a strong tradition from the Lisp machines to do certain things a certain
way, but it tends to be hard to splice Lisp machine semantics into Unix or
Windows. Please try to understand that Common Lisp is not a one-operating-
system-language or a one-implementation language.
You are supposed to know which is language and which is operating system and
behave reasonably accordingly. If it say it seems silly to you, that only
means that you seem silly to those who have figured this out and appreciate
the difference because it does not make undue requirements on the environment
of a Common Lisp implementation.
[ Please quote only the relevant text of your replies and answer below the
relevant text you quote. Microsoft screwed up. Make another choice. ]
In that case, explain to me why *creating* a directory is *not*
implementation-dependent and is, in fact, part of the language
specification.
> You are supposed to know which is language and which is operating system
and
> behave reasonably accordingly. If it say it seems silly to you, that
only
> means that you seem silly to those who have figured this out and
appreciate
> the difference because it does not make undue requirements on the
environment
> of a Common Lisp implementation.
I would like to politely point out that the above paragraph makes you come
off as incredibly arrogant, while the grammatical mistake makes you come off
as incredibly ignorant. Try not to talk like you are the be all and end all
of Lisp coders.
> [ Please quote only the relevant text of your replies and answer below the
> relevant text you quote. Microsoft screwed up. Make another choice. ]
And this, of coursse, cements the above comment.
Have a nice day.
Paul D. Lathrop
I did not know that you had a personality disorder that required that you be
hugged and kissed before you could take criticism. I regret answering you.
The general rule on USENET is: Grow the hell up, _then_ post. Deal with it.
Interesting how you can show that you *know* the rule while showing you
can't follow it. Thanks for the pointer. Sorry I didn't stroke your ego
properly.
Do you feel better now? If so, could you get back on track and try to focus
on the _problem_ you had? It was something with Common Lisp and directories,
I seem to recall. Your personality disorders and personal problems are _not_
the business of this newsgroup, no matter how you feel. So, is there any way
we can help you solve your _problem_ and get you to quit attacking people
like you seem to need when you are given an answer you were not prepared for?
You asked for help, you got an answer and an opportunity to learn from it,
but chose to react like a whining little kid. Nobody cares what some jerk
finds "silly". So you were told to shut up, and had to react even more
childishly, but this is _not_ a forum for whining little kids. It is a forum
for programmers. Egos are equally irrelvant. Learn to take charge of your
own emotions, keep them out of other people's faces, and stick to the task at
hand. You had a _programming_ problem, did you not? Lots of people were
willing to help with your _programming_ problem, but if you insist on being a
whining little kid, you will not get help from anyone for a very long time.
So, which is more important to you: To whine or to get help to solve your
problems? It is a _really_ simple choice. Just make it and behave in a way
that communicates goal-directed intelligence. Do you understand this?
It isn't, really. ENSURE-DIRECTORIES-EXIST is some kind of minimal
get-you-through-the-night thing which any filesystem is likely to
support - `given this path, try and make it be the case that
everything is OK for me to create a file here'. It just avoids
exposing all the complicated issues about directories that deleting
them, or probing them, or pretty much anything else exposes.
Even within the domain of Unix or near Unix-clone filesystems there
are obscurities here - should
(probe-file (make-pathname :directory '(:absolute)
:name "usr"))
Return true on a Unix filesystem? If it does, does it make sense to
delete that `file'? What about
(probe-file (make-pathname :directory '(:absolute "usr")
:name nil :type nil :version nil))
?
Now remember that there are other filesystems, and they don't all work
the same way. On the LMFS, I think that the file corresponding to a
directory FOO> was called FOO.DIRECTORY (what was it on the FEP
filesystem?), VMS I think has something the same. Other filesystems
maybe don't have files corresponding to directories (does DOS, really,
does the mac (and what is its filesystem now, anyway, if it FFS?)).
It's one thing to provide a minimal hack like
ENSURE-DIRECTORIES-EXIST, it's a huge amount more to provide some
general-purpose interface, even if one could be designed. Especially
if there is more than one interesting filesystem in the world. The
hegemony of Unix(oid) filesystems has done almost as much damage to
people's ability to think about Unix as the hegemony of C-on-Unix has
to people's ability to think about characters.
The good news is that, if you only want to live on Unix or DOS, then
the amount of work you need to do to support the filesystem is
probably tiny. In my system I have 55 lines of copiously-commented
(most of those lines are comments, I think, and most of the rest are
to do with signalling appropriate errors, or not)
implementation-specific code to deal with directories, which supports
one implementation. I also have probably several times that amount of
general pathname-bashing code which is sort-of-portable in the sense
that it's not Lisp implementation dependent but would not work except
on Unix or DOS - for instance functions to canonicalise namestrings to
have forward slashes and so on.
Out of interest the exported implementation-specific functions are:
directory-subdirectories - return the subdirectories only of a
directory (not including . or .. on Unix/DOS);
directory-files - return everything *but* subdirectories;
directory-exists-p - does a directory exist?
directory-truename - return the true name of a directory;
delete-directory - delete a directory.
This isn't intended as a complete set, but it's enough to do a lot of
fairly intensive filesystem bashing up to and including mirroring
directory trees. It certainly would not work other than on Unix/Linux
or Windows, but unlike CL, it wasn't designed to.
I'm always amazed by how much time people seem to spend worrying about
issues like this. I've spend the last 6-8 months working on a system
which does really a lot of talking to the OS &c, and these
implementation-specific things have been just in the noise in terms of
time spent, lines of code written, or difficulty.
--tim
If you want to do platform-specific file manipulation, you have to drop
down a notch and access your operating system's service functions.
There is no portable way to do this; Lisp systems have different
FFI's (foreign function interfaces) and of course operating systems
differ from each other as well. Some Lisp systems have a ready made
package of system calls; see if yours has something like that.
In Meta-CVS, I created a little layer which provides some POSIX functions as
Lisp functions; e.g. I can unlink a file using (unlink <pathname>), scan
directories with (opendir ...) (readdir ...) and (closedir ...) and so on. With
some nice macros, a recursive directory deletion (analogous to the POSIX
command ``rm -rf'') ends up looking very slick:
(defun delete-recursive (dir-or-file)
(for-each-file-info (fi dir-or-file :postorder t)
(if (directory-p fi)
(rmdir (file-name fi))
(unlink (file-name fi)))))
It may help to remember that the ANSI C and C++ languages have no directory
deletion function either. It's just that these languages make it far easier to
access the operating system specific functions, since the bindings to these
functions are already expressed in C. Not to mention that there is a class of
operating systems which share the same POSIX bindings. So you are only at
a slight disadvantage in Lisp, due to having to deal with foreign function
interfaces or system call packages that are incompatible among different
Lisps.
A good strategy is to identify what services you need and then write a layer
which provides these services. That layer can be retargetted to different
Lisp systems. You can write a separate file for each Lisp system, or
use #+ and #- feature tests to selectively read code.
In Meta-CVS, I have a file called clisp-linux.lisp, and cmucl-unix.lisp.
The latter is an ongoing, incomplete port to CMUCL. In clisp-linux.lisp
(opendir ...) is defined like this:
(defun opendir (dir)
(cond
((null-to-nil (linux:opendir dir)))
(t (error (make-condition 'open-dir-error :dir dir)))))
This uses the LINUX package of glibc bindings that comes with CLISP
and can be optionally built.
In cmucl-unix.lisp, it looks like this:
(defun opendir (dir)
(cond
((unix:open-dir dir))
(t (error (make-condition 'open-dir-error :dir dir)))))
It's very similar, except for the spelling of open-dir, the package
name, and that NIL is already returned if there is an error.
A more interesting case study is my current-dir-restore macro which
remembers the current working directory location, evaluates some forms
and then restores the memorized location. Under CLISP, I have access
to the fchdir function. So I did it like this:
(defmacro current-dir-restore (&body forms)
(let ((saved-dir (gensym "SAVED-DIR-")))
`(let ((,saved-dir (linux:open "." linux:O_RDONLY 0)))
(when (= ,saved-dir -1)
(error (make-condition 'open-error :path ".")))
(unwind-protect (progn ,@forms)
(fchdir ,saved-dir)
(linux:close ,saved-dir)))))
In other words, I open the directory as a file descriptor,
and bind that descriptor to a gensym'ed variable. I couldn't find
fchdir in CMUCL's UNIX package (hey, who would ever need fchdir, right?)
So I preserve the current directory as a full pathname string instead,
obtained via unix:unix-current-directory:
(defmacro current-dir-restore (&body forms)
(let ((saved-dir (gensym "SAVED-DIR-"))
(getdir-ok (gensym "GETDIR-OK-")))
`(multiple-value-bind (,getdir-ok ,saved-dir)
(unix:unix-current-directory)
(when (not ,getdir-ok)
(error "could not determine current working directory"))
(unwind-protect (progn ,@forms)
(chdir ,saved-dir)))))
Different implementation, but the macro behaves the same way. So that just
shows you that it takes a little bit of thought and work here and there
to access operating system functionality and try to sprinkle on enough
abstraction to keep it portable.
Thank you, Tim, for clearing that up. I had started to come to a similar
conclusion on my own, but I certainly appreciate your parting of the haze on
this matter. Now that I have dug deeper into my implementation I have found
they have provided several methods of dealing with the issues that were
tripping me up.
Paul D. Lathrop
Best
AHz
I guess I could do so, but my point really was that this is a really
negligible amount of code - such a small amount that I wouldn't really
think it was worth providing a library for. It's also plastered with
application-specific error classes &c at the moment.
The more interesting things are code that is (in theory, we don't
currently run on more than one implementation) portable, and does
various useful things with pathnames, mostly in a Unix/DOS context,
such as ensuring a user-inputted string names a directory, and
`demerging' directories. `Demerging' a directory means taking (say)
`/a/b/c/d/f' and `/a/b/c/' and returning `d/f` or `/a/b/c/f/` and
`/a/b/c/d/` and returning `../f/' in Unix terms. It's very useful if
you want to write things like Makefiles with relative directory names
in. Again, I have code that does all this but it's covered in
application stuff that I don't want to give away and don't have time
to expunge, and it's also not really library-quality - it works in the
context of the application but has some known deficiencies (for
instance the demerging code on DOS will happily demerge things whose
drive letters are different, since it only considers directories, so
it will decide that the relative path from `D:/foo/bar/' to `E:/foo/'
is `../').
Really, I'm trying to make a Gabrielesque anti-abstraction argument
here. It would probably be possible to provide a fairly comprehensive
Unix/DOS-portable pathname library which would have all sorts of cool
stuff. But to do that, and to make it library quality code which will
be genuinely reusable and will work outside its original context would
involve a lot of work and several hard decisions - which work and
decisions are a small part of why this stuff isn't in the standard.
It's often much easier to just write stuff which works for an
application, because there you *know* the context.
--tim
Many thanks. This was *extremely* helpful.
Paul
;;;;; Start Source File
;;;;; Directory flattening utility
;;;;; for Windows and Xanalys Lispworks
;;;;;
;;;;; The author of this code gives explicit permission
;;;;; to distribute, changed or unchanged, any or all
;;;;; of this code. This code is distributed as-is
;;;;; without guarantee or warranty of any kind.
(defpackage "FLATTEN"
(:use "COMMON-LISP")
(:export flatten))
(in-package "FLATTEN")
(import 'lw:delete-directory)
(defparameter *working-directory*
(make-pathname :host "d" :directory '(:absolute "code" "lisp"))
"Parameter storing the current working directory.")
(defparameter *file-list* nil
"Parameter used to store the list of files to move.")
(defparameter *directory-list* nil
"Parameter used to store the list of subdirectories to remove.")
(defun flatten(path)
"Recursively moves all files from subdirectories to the directory passed
to flatten."
(progn
;; Set the working directory.
(setf *working-directory* (make-pathname :host (pathname-host path)
:directory (pathname-directory
path)))
;; Collect the files and directories.
(tr-flatten *working-directory*)
;; Move the files.
(move-multiple-files *file-list* *working-directory*)
(setf *file-list* nil)
;; Remove the directories.
(delete-multiple-directories (nreverse *directory-list*))
(setf *directory-list* nil)))
(defun tr-flatten(path)
;; Here we accumulate the files into *file-list*
;; and the directories into *directory-list*
(let ((directory-listing (directory
(make-pathname :host (pathname-host path)
:directory (pathname-directory
path)))))
(dolist (directory-entry directory-listing)
(if (directoryp directory-entry)
(progn
(tr-flatten directory-entry)
(push directory-entry *directory-list*))
(push directory-entry *file-list*)))))
(defun directoryp(path)
"Returns t if its argument refers to a directory."
;; Tests the last character to see if it is a /.
;; This probably only works for Lispworks.
(let ((pathstring (get-full-path-string path)))
(equal (char pathstring (- (length pathstring) 1)) (character "/"))))
(defun move-multiple-files(file-list newpath)
"Moves a list of files."
(dolist (current-file file-list)
(move-file current-file newpath)))
(defun move-file(oldpath newpath)
"Moves a file."
(cond ((not (probe-file oldpath))
(error "\"~A\" is of the wrong form, or does not exist." oldpath))
((not (probe-file (make-pathname :host (pathname-host newpath)
:directory (pathname-directory
newpath))))
(error "\"~A\" is of the wrong form, or does not exist." newpath))
(t (rename-file oldpath newpath))))
(defun delete-multiple-directories(directory-list)
"Deletes a list of directories."
(dolist (current-dir directory-list)
(delete-directory current-dir))) ;; Lispworks only!
(defun get-full-path-string(path)
"Returns the full path of its argument in string format."
;; We just build a string segment by segment.
(let ((hoststring (concatenate 'string (pathname-host path) ":/"))
(dirlist (add-foreslash (cdr (pathname-directory path))))
(filename nil)
(dirstring ""))
(dolist (element dirlist)
(setf dirstring (concatenate 'string dirstring element)))
(setf dirstring (concatenate 'string hoststring dirstring))
(if (not (eq (pathname-name path) :unspecific))
(progn
(setf filename (pathname-name path))
(setf dirstring (concatenate 'string dirstring filename))
(setf dirstring (concatenate 'string dirstring "."))
(setf dirstring (concatenate 'string dirstring (pathname-type
path)))))
dirstring))
(defun add-foreslash(lst)
"Takes a list of strings and places the '/' character at the end of each"
(let ((acc nil)
(elements lst))
(dotimes (i (length lst))
(push (concatenate 'string (car elements) "/") acc)
(setf elements (cdr elements)))
(reverse acc)))
> (defpackage "FLATTEN"
> (:use "COMMON-LISP")
> (:export flatten))
(:export "FLATTEN"))
otherwise you'll intern FLATTEN in the current package, and collide
with it if you try to (use-package "FLATTEN") or something latter.
> (defparameter *working-directory*
> (make-pathname :host "d" :directory '(:absolute "code" "lisp"))
> "Parameter storing the current working directory.")
> (defparameter *file-list* nil
> "Parameter used to store the list of files to move.")
> (defparameter *directory-list* nil
> "Parameter used to store the list of subdirectories to remove.")
Don't do that. There's no reason for these things to be specials.
> (defun flatten(path)
> "Recursively moves all files from subdirectories to the directory passed
> to flatten."
> (progn
> ;; Set the working directory.
> (setf *working-directory* (make-pathname :host (pathname-host path)
> :directory (pathname-directory
> path)))
> ;; Collect the files and directories.
> (tr-flatten *working-directory*)
> ;; Move the files.
> (move-multiple-files *file-list* *working-directory*)
> (setf *file-list* nil)
> ;; Remove the directories.
> (delete-multiple-directories (nreverse *directory-list*))
> (setf *directory-list* nil)))
Is
(defun flatten-directory (path)
(labels ((rec (dir)
(dolist (ent (directory dir))
(cond ((or (pathname-name ent) (pathname-type ent))
(unless (eql dir path)
(rename-file ent (make-pathname
:directory (pathname-directory path)
:defaults ent))))
(t
(rec ent) (lw:delete-directory ent))))))
(rec path)))
close to what you're looking for?
--
Quid enim est stultius quam incerta pro certis habere, falsa pro veris?
-- Cicero
(setq reply-to
(concatenate 'string "Paul Foley " "<mycroft" '(#\@) "actrix.gen.nz>"))
> Tim Bradshaw wrote:
[...]
> > probably tiny. In my system I have 55 lines of copiously-commented
[...]
> > implementation-specific code to deal with directories, which supports
[...]
> What do you think about making it public ? I would assume that a lot
A simple directory access interface is available as part of CLOCC (Common
Lisp Open Code Collection):
See the file src/port/path.lisp.
Paolo
--
EncyCMUCLopedia * Extensive collection of CMU Common Lisp documentation
http://www.paoloamoroso.it/ency/README
This is overly complicated. It's a good rule of thumb that if you end
up using namestrings for something that's not communicating with the
user or passing filenames in and out of Lisp, it's probably the wrong
solution. Pathname computations are better done using pathnames.
In this case, test NAME, TYPE and VERSION fields for being NIL or
:UNSPECIFIC (possibly :VERSION :NEWEST, if your system uses that). In
LispWorks, this is already available as SYSTEM:DIRECTORY-PATHNAME-P.
--
Pekka P. Pirinen
Mail should be private, whether on paper or on disk. Public gatherings
should be a right, whether virtual or in person.
>"Kaz Kylheku" <k...@ashi.footprints.net> wrote in message
>news:afb313$k1u$1...@luna.vcn.bc.ca...
>> In article <uh9sqrh...@corp.supernews.com>, Paul D. Lathrop wrote:
>> > I know this may be a completely newbie question, but please bear with
>me. I
>> > am writing some file-manipulation functions that I would find useful and
>I
>> > have run into a problem I cannot seem to find a method of using lisp to
>> > delete directories. I have tried using 'delete-file' with the directory
>> > pathname as an argument and I get
>> >
Here's some boilerplate code that show how to expose the delete
directory function in the Win32 API if you are using Corman Lisp:
(in-package :win32)
(defwinapi RemoveDirectory
((lpPathName LPCSTR))
:return-type BOOL
:library-name "Kernel32"
:entry-name "RemoveDirectoryA"
:linkage-type :pascal)
(defun delete-directory (path)
"Deletes an empty directory.
Returns true if successful, false otherwise."
(win:RemoveDirectory
(ct:lisp-string-to-c-string (namestring (pathname path)))))
(export 'delete-directory)
This will allow you to use
(win:delete-directory pathname)
to delete an empty directory. Obviously one could add more
bells and whistles.
Roger