rincewind666
asked on
Listing files & sub-directories (everything) in a directory of a web server.
At a click of a button, I want to give the user the option of deleting a directory on a web server. The path to this directory is always the same (home/username/http-docs/l ist) where a directory called "list" can be deleted. Unfortunately a directory cannot be deleted when it has other directories & files in it. I already have the code to log in/out of the server. All I need is some code to do EITHER of the following:
1. List all the directories and files in a directory and ALSO their sub-directories. Everything has to be listed so I can set up a delete routine. I already have the path to the top directory.
OR....
2. Code to delete a directory even if it is not empty.
OR....
3. Code to rename a directory even if not empty.
I am using Delphi 6. This is URGENT so I am giving the maximum 500 points for this. Many thanks for your help.
1. List all the directories and files in a directory and ALSO their sub-directories. Everything has to be listed so I can set up a delete routine. I already have the path to the top directory.
OR....
2. Code to delete a directory even if it is not empty.
OR....
3. Code to rename a directory even if not empty.
I am using Delphi 6. This is URGENT so I am giving the maximum 500 points for this. Many thanks for your help.
Oops...you'll need to make a few changes:
Std_AddBackSlash()
- Adds a backslash to the path if it does not end in one..
Replace with IncludeTrailingBackslash()
Std_EraseFile()
-Erases the given file...
Replace with DeleteFile()
Std_AddBackSlash()
- Adds a backslash to the path if it does not end in one..
Replace with IncludeTrailingBackslash()
Std_EraseFile()
-Erases the given file...
Replace with DeleteFile()
Oops again...
Std_String can be either "String" or "ShortString".
(Just pulling a routine out of my library...I didn't realize how dependant it was on other items!)
Std_String can be either "String" or "ShortString".
(Just pulling a routine out of my library...I didn't realize how dependant it was on other items!)
ASKER
That was quick!
I am getting the following errors:
[Error] Unit1.pas(4821): Undeclared identifier: 'Std_String'
[Error] Unit1.pas(4837): Undeclared identifier: 'Std_AddBackSlash'
[Error] Unit1.pas(4849): There is no overloaded version of 'RmDir' that can be called with these arguments
[Error] Unit1.pas(4857): Undeclared identifier: 'Std_EraseFile'
[Error] Unit1.pas(4858): Undeclared identifier: 'STD_IOResult'
[Warning] Unit1.pas(4858): Comparing signed and unsigned types - widened both operands
[Fatal Error] CGI_Installer.dpr(27): Could not compile used unit 'Unit1.pas'
Maybe I should have said that I am a beginner in Delphi so be gentle! Many thanks.
I am getting the following errors:
[Error] Unit1.pas(4821): Undeclared identifier: 'Std_String'
[Error] Unit1.pas(4837): Undeclared identifier: 'Std_AddBackSlash'
[Error] Unit1.pas(4849): There is no overloaded version of 'RmDir' that can be called with these arguments
[Error] Unit1.pas(4857): Undeclared identifier: 'Std_EraseFile'
[Error] Unit1.pas(4858): Undeclared identifier: 'STD_IOResult'
[Warning] Unit1.pas(4858): Comparing signed and unsigned types - widened both operands
[Fatal Error] CGI_Installer.dpr(27): Could not compile used unit 'Unit1.pas'
Maybe I should have said that I am a beginner in Delphi so be gentle! Many thanks.
Here it is updated as I suggested. I use Delphi7 .. the RmDir seems OK. What version do you use?
function Std_ClearPath(aPath:String ):boolean;
const
cAny=faAnyFile;
cDir=faDirectory;
var
SR:TSearchRec;
DosError:integer;
DP:boolean;
begin
if aPath='' then begin
{ Failsafe to prevent ROOT DIRECTORY delete! }
Result:=False;
exit;
end;
Result:=True;
aPath:=IncludeTrailingBack Slash(aPat h);
DosError:=
FindFirst(aPath+'*.*',cAny ,SR);
while DosError=0 do begin
if (SR.Name[1]<>'.') then begin
if (SR.Attr and cDir)=cDir then begin
{ A directory }
DP:=Std_ClearPath(aPath+SR .Name);
Result:=Result and DP;
if DP then begin
{ Kill directory }
Try
RemoveDir(aPath+SR.Name);
Except
Result:=False;
end;
end;
end
else begin
{ A file }
DP:=DeleteFile(aPath+SR.na me);
Result:=Result and DP;
end;
end;
DosError:=
FindNext(SR);
end;
FindClose(SR);
end;
function Std_ClearPath(aPath:String
const
cAny=faAnyFile;
cDir=faDirectory;
var
SR:TSearchRec;
DosError:integer;
DP:boolean;
begin
if aPath='' then begin
{ Failsafe to prevent ROOT DIRECTORY delete! }
Result:=False;
exit;
end;
Result:=True;
aPath:=IncludeTrailingBack
DosError:=
FindFirst(aPath+'*.*',cAny
while DosError=0 do begin
if (SR.Name[1]<>'.') then begin
if (SR.Attr and cDir)=cDir then begin
{ A directory }
DP:=Std_ClearPath(aPath+SR
Result:=Result and DP;
if DP then begin
{ Kill directory }
Try
RemoveDir(aPath+SR.Name);
Except
Result:=False;
end;
end;
end
else begin
{ A file }
DP:=DeleteFile(aPath+SR.na
Result:=Result and DP;
end;
end;
DosError:=
FindNext(SR);
end;
FindClose(SR);
end;
procedure _SearchDirectory(List: TStrings; Directory: String;
const Recursive: Boolean);
var
bFoundFile: Boolean;
mySearchRec: TSearchRec;
sFileName: String;
begin
Directory := IncludeTrailingPathDelimit er(Directo ry);
bFoundFile := FindFirst(Directory + '*.*', faAnyFile, mySearchRec) = 0;
while bFoundFile do
begin
// skip "." and ".."
if (mySearchRec.Name[1] <> '.') then
begin
sFileName := Directory + mySearchRec.Name;
if ((mySearchRec.Attr and faDirectory) = 0) then
begin // found a file
List.Add(sFileName);
end
else
begin // found a directory
sFileName := IncludeTrailingPathDelimit er(sFileNa me);
List.Add(sFileName);
// list the subdirectory
if Recursive then
_SearchDirectory(List, sFileName, Recursive);
end;
end;
// find next file
bFoundFile := FindNext(mySearchRec) = 0;
end;
FindClose(mySearchRec);
end;
an example to use it, drop a TMemo component on hte form:
procedure TForm1.Button1Click(Sender : TObject);
begin
Memo1.Lines.Clear;
_SearchDirectory(Memo1.Lin es, 'D:\Work\', True);
end;
const Recursive: Boolean);
var
bFoundFile: Boolean;
mySearchRec: TSearchRec;
sFileName: String;
begin
Directory := IncludeTrailingPathDelimit
bFoundFile := FindFirst(Directory + '*.*', faAnyFile, mySearchRec) = 0;
while bFoundFile do
begin
// skip "." and ".."
if (mySearchRec.Name[1] <> '.') then
begin
sFileName := Directory + mySearchRec.Name;
if ((mySearchRec.Attr and faDirectory) = 0) then
begin // found a file
List.Add(sFileName);
end
else
begin // found a directory
sFileName := IncludeTrailingPathDelimit
List.Add(sFileName);
// list the subdirectory
if Recursive then
_SearchDirectory(List, sFileName, Recursive);
end;
end;
// find next file
bFoundFile := FindNext(mySearchRec) = 0;
end;
FindClose(mySearchRec);
end;
an example to use it, drop a TMemo component on hte form:
procedure TForm1.Button1Click(Sender
begin
Memo1.Lines.Clear;
_SearchDirectory(Memo1.Lin
end;
I now see you're using D6, so what I posted should work A-OK!
You can use IncludeTrailingPathDelimit er instead of IncludeTrailingBackslash if you wish. The former if platform independant.
You can use IncludeTrailingPathDelimit
Oh, I really hate it when people use a recursive function to enumerate folders...
uses
SysUtils, Classes;
procedure EnumFolders(Root: string; List: TStringList);
var
SearchRec: TSearchRec;
Index: Integer;
begin
List := TStringList.Create;
List.Add(IncludeTrailingPa thDelimite r(Root));
Index := 0;
while (Index < List.Count) do begin
if (FindFirst(IncludeTrailing PathDelimi ter(List[I ndex]) + '*.*', faAnyFile, SearchRec) = 0) then begin
repeat
if ((SearchRec.Attr and faDirectory) <> 0) and (SearchRec.Name <> '.') and (SearchRec.Name <> '..') then List.Add(IncludeTrailingPa thDelimite r(List[Ind ex]) + SearchRec.Name);
until (FindNext(SearchRec) <> 0);
end;
Inc(Index);
end;
end;
Now guys, please stop using that stupid recursive version because IT STINKS!!! ;-)
My version doesn't add files to the list, though. Only folders. But adding files would not be a real problem. If need be, use two lists. One for folders only and one for files only. Then walk through the files list first to delete all these files, then walk through the folders list, bottom to top, to remove all folders.
uses
SysUtils, Classes;
procedure EnumFolders(Root: string; List: TStringList);
var
SearchRec: TSearchRec;
Index: Integer;
begin
List := TStringList.Create;
List.Add(IncludeTrailingPa
Index := 0;
while (Index < List.Count) do begin
if (FindFirst(IncludeTrailing
repeat
if ((SearchRec.Attr and faDirectory) <> 0) and (SearchRec.Name <> '.') and (SearchRec.Name <> '..') then List.Add(IncludeTrailingPa
until (FindNext(SearchRec) <> 0);
end;
Inc(Index);
end;
end;
Now guys, please stop using that stupid recursive version because IT STINKS!!! ;-)
My version doesn't add files to the list, though. Only folders. But adding files would not be a real problem. If need be, use two lists. One for folders only and one for files only. Then walk through the files list first to delete all these files, then walk through the folders list, bottom to top, to remove all folders.
Oh, and remove the line: List := TStringList.Create;
I copied the code from a small console application I've once written. The code created the list, would save it to file, then free it. Now you have to create and free the list outside this function and call the function like:
begin
List := TStringList.Create;
EnumFolders('C:\WinNT', List);
List.SaveToFile('Or whatever you like');
List.Free;
end;
I copied the code from a small console application I've once written. The code created the list, would save it to file, then free it. Now you have to create and free the list outside this function and call the function like:
begin
List := TStringList.Create;
EnumFolders('C:\WinNT', List);
List.SaveToFile('Or whatever you like');
List.Free;
end;
maybe your solution is better looking to you :-)
i personally prefere the order the items are added to the list using recursion
i personally prefere the order the items are added to the list using recursion
Well, bpana, since it's a stringlist you can always execute List.Sort to get it in your order. :-P
Then again, if you want performance, the non-recursive method will probably perform a little faster, although it's difficult to measure since a lot of action is done by disk access. And actually, all I'm saying is that enumerating folders doesn't require any recursive functionality. I just showed an easy, non-recursive technique. I wished more people were aware of this much simpler solution.
Then again, if you want performance, the non-recursive method will probably perform a little faster, although it's difficult to measure since a lot of action is done by disk access. And actually, all I'm saying is that enumerating folders doesn't require any recursive functionality. I just showed an easy, non-recursive technique. I wished more people were aware of this much simpler solution.
ASKER
I am trying to use the following from LRHGuy (as it deletes which is the nearest to my requirements). Unfortunately it doesn't do anything - I'm not sure that I'm using the correct path for aPath. I try it and connect using another FTP program and the folder & files are still there. It is a Unix server. I am using using a FTP component from argosoft.com
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, Mssocket, msFTP;
type
TForm1 = class(TForm)
Button1: TButton;
Label1: TLabel;
FTPClient1: TmsFTPClient;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
function Std_ClearPath(aPath:String ):boolean;
const
cAny=faAnyFile;
cDir=faDirectory;
var
SR:TSearchRec;
DosError:integer;
DP:boolean;
begin
if aPath='' then begin
{ Failsafe to prevent ROOT DIRECTORY delete! }
Result:=False;
exit;
end;
Result:=True;
aPath:=IncludeTrailingPath Delimiter( aPath);
DosError:=
FindFirst(aPath+'*.*',cAny ,SR);
while DosError=0 do begin
if (SR.Name[1]<>'.') then begin
if (SR.Attr and cDir)=cDir then begin
{ A directory }
DP:=Std_ClearPath(aPath+SR .Name);
Result:=Result and DP;
if DP then begin
{ Kill directory }
Try
RemoveDir(aPath+SR.Name);
Except
Result:=False;
end;
end;
end
else begin
{ A file }
DP:=DeleteFile(aPath+SR.na me);
Result:=Result and DP;
end;
end;
DosError:=
FindNext(SR);
end;
FindClose(SR);
end;
procedure TForm1.Button1Click(Sender : TObject);
begin
Screen.Cursor := crHourGlass;
FTPClient1.Host := 'ftp.something.com';
FTPClient1.UserName := 'something';
FTPClient1.Password := 'password';
FTPClient1.Login;
Std_ClearPath('/home/somet hing/http- docs/list' );
FTPClient1.Logout;
Label1.Caption := 'TEST COMPLETE.......';
Screen.Cursor := crDefault;
end;
end.
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, Mssocket, msFTP;
type
TForm1 = class(TForm)
Button1: TButton;
Label1: TLabel;
FTPClient1: TmsFTPClient;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
function Std_ClearPath(aPath:String
const
cAny=faAnyFile;
cDir=faDirectory;
var
SR:TSearchRec;
DosError:integer;
DP:boolean;
begin
if aPath='' then begin
{ Failsafe to prevent ROOT DIRECTORY delete! }
Result:=False;
exit;
end;
Result:=True;
aPath:=IncludeTrailingPath
DosError:=
FindFirst(aPath+'*.*',cAny
while DosError=0 do begin
if (SR.Name[1]<>'.') then begin
if (SR.Attr and cDir)=cDir then begin
{ A directory }
DP:=Std_ClearPath(aPath+SR
Result:=Result and DP;
if DP then begin
{ Kill directory }
Try
RemoveDir(aPath+SR.Name);
Except
Result:=False;
end;
end;
end
else begin
{ A file }
DP:=DeleteFile(aPath+SR.na
Result:=Result and DP;
end;
end;
DosError:=
FindNext(SR);
end;
FindClose(SR);
end;
procedure TForm1.Button1Click(Sender
begin
Screen.Cursor := crHourGlass;
FTPClient1.Host := 'ftp.something.com';
FTPClient1.UserName := 'something';
FTPClient1.Password := 'password';
FTPClient1.Login;
Std_ClearPath('/home/somet
FTPClient1.Logout;
Label1.Caption := 'TEST COMPLETE.......';
Screen.Cursor := crDefault;
end;
end.
If we are talking about performance, the sort thing is an extra step :)
But anyway, your non-recursive method it's also a good example ...
But anyway, your non-recursive method it's also a good example ...
Oops! That's a slightly different problem! I didn't know you were deleting a remote directory. And through FTP it's even harder!
You'd have to send the commands to read the directory and delete the files through the ftp client.
I don't know anything about the argosfot ftp, but the "removedir" command for ftp is "rmdir", and the "deletefile" command is "delete". Plus, you'll have to read each directory from the remote system with the correct ftp command (dir).
In general, it's the same procedure, but you have to use the proper ftp commands in the clearpath procedure.
You'd have to send the commands to read the directory and delete the files through the ftp client.
I don't know anything about the argosfot ftp, but the "removedir" command for ftp is "rmdir", and the "deletefile" command is "delete". Plus, you'll have to read each directory from the remote system with the correct ftp command (dir).
In general, it's the same procedure, but you have to use the proper ftp commands in the clearpath procedure.
Oops... Deleting through FTP. Hadn't noticed that either. Oh, well... Still, same technique. Enumerate all the files and folders, then Delete() and RemoveDir() them with the TidFTP Indy component. Using List() you get a list of files and directories in a certain folder. By using *.* as specifier you should get all files while if you use just one * you should get all folders in the current folder. Make sure to NOT ask details. Unfortunately I don't have time right now to be more specific... Maybe later, if no one else beats me to it. :-)
listening
You could work with the Indy components... it is free and easy. And Indy has a FTP component and you can find in Delphi Demos... exactly what you are looking for... except for the deletion of the non-empty directory. For the deletion you could modify the procedures above... and using the demo it would be an easy job.
Cheers
Cheers
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
bpana, Where can I get TIdFTP? Thanks.
ASKER
Also, where can I get the Indy component? I've made a search of Delphi Superpage, Torrys etc but can find nothing. Thanks for your help.
you said you use Delphi 6, so in the components pallete, on the Indy Clients tab you'll find the TIdFTP component.
i made the previous example in delphi 7 which has a newer version of Indy than delphi 6, and now i've seen that in delphi 6 is missing the idFTP.DirectoryListing property.
you should try to upgrade to a newer version
let me know if you have any problem
you should try to upgrade to a newer version
let me know if you have any problem
ASKER
Many thanks for your help.
Here's what I use:
{ Recursive! } {LRH 02-12-12}
{ Returns true if it thinks it's successful }
function Std_ClearPath(aPath:Std_St
const
cAny=faAnyFile;
cDir=faDirectory;
var
SR:TSearchRec;
DosError:integer;
DP:boolean;
begin
if aPath='' then begin
{ Failsafe to prevent ROOT DIRECTORY delete! }
Result:=False;
exit;
end;
Result:=True;
aPath:=Std_AddBackSlash(aP
DosError:=
FindFirst(aPath+'*.*',cAny
while DosError=0 do begin
if (SR.Name[1]<>'.') then begin
if (SR.Attr and cDir)=cDir then begin
{ A directory }
DP:=Std_ClearPath(aPath+SR
Result:=Result and DP;
if DP then begin
{ Kill directory }
Try
RMDir(aPath+SR.Name);
Except
Result:=False;
end;
end;
end
else begin
{ A file }
Std_EraseFile(aPath+SR.nam
Result:=Result and (STD_IOResult=0);
end;
end;
DosError:=
FindNext(SR);
end;
FindClose(SR);
end;