Recent

Author Topic: Copying an entire directory (incl. subdirectories)  (Read 20557 times)

bastla

  • New Member
  • *
  • Posts: 23
Copying an entire directory (incl. subdirectories)
« on: April 30, 2013, 05:37:20 pm »
Hello together,

I searched for a possiblity to copy a whole directory on every platform supported by Lazarus. Thanks to Leledumbo, I found a way to get this working. Maybe there're some guys outside who still search for this, so I publish my work here :D.

I created an simple and easy class, called "TCopyDir", which uses TFileSearcher to enumerate an entire directory and copy its content file by file.

Example Code:
Code: [Select]
procedure TfrmMain.btCopyClick(Sender: TObject);
var
  CopyDir: TCopyDir;
  Log: TStringList;
begin
  CopyDir := TCopyDir.Create('C:\', 'D:\');
  CopyDir.Start;

  Log := CopyDir.GetLog;

  CopyDir.Free;
end;

As you can see, the class is very simple to use and contains a log (TStringList). In this example, the whole content of "C:\" would be copied to "D:\".

[EDIT 2013-06-23]As of 23.06.2013, there's a improved version of TCopyDir (called TCopyDir v2). For more details see here[/EDIT]
[EDIT 2013-08-10]onivan found a bug in TCopyDir and has already created a fix. Make sure to read his post.[/EDIT]
[EDIT 2014-01-06]Caladan found another bug in TCopyDir affecting Unix users only. See this post for a fix![/EDIT]
[EDIT2014-06-03]I uploaded version 2.1 of TCopyDir which fixes two above mentioned bugs, plus I changed the disclaimer![/EDIT]

Download and example application in the attachments.
My work is Free Domain but remain the terms of LCL!

With the use of this work you agree to the following:
THIS SOFTWARE IS PROVIDED BY BASTLA "AS IS" AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL BASTLA BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.


I hope this is useful for some of you!

Greetings,
bastla
« Last Edit: June 03, 2014, 09:47:08 pm by bastla »

jmpessoa

  • Hero Member
  • *****
  • Posts: 1742
Re: Copying an entire directory (incl. subdirectories)
« Reply #1 on: April 30, 2013, 09:34:03 pm »
Thank you bastla!
Lamw: Lazarus Android Module Wizard
https://github.com/jmpessoa/lazandroidmodulewizard

exdatis

  • Hero Member
  • *****
  • Posts: 668
    • exdatis
Re: Copying an entire directory (incl. subdirectories)
« Reply #2 on: April 30, 2013, 10:36:12 pm »
Thank you very much!

LA.Center

  • Full Member
  • ***
  • Posts: 244
    • LA.Center
Re: Copying an entire directory (incl. subdirectories)
« Reply #3 on: June 21, 2013, 11:00:34 am »
Nice - THX a bunch

CM630

  • Hero Member
  • *****
  • Posts: 922
  • Не съм сигурен, че те разбирам.
    • http://sourceforge.net/u/cm630/profile/
Re: Copying an entire directory (incl. subdirectories)
« Reply #4 on: June 21, 2013, 02:00:52 pm »
I have two questions:
1. How does it handle hidden and system files?
2. Is it tested with files with nonlatin filenames? Edit- I tested, works fine for me.

Edit: I found a bug- I copied a folder, which contained a hidden file. It copied the file, but it the file in the target directory lost its "Hidden" flag.

Edit: yet another bug- in the folder I had two subfolder. One of them had a name, ending with .txt and it was not copied. The other was was copied fine.

Edit: I made a third folder. It does not copy it, too. It does not copy more than 1 subfolder.

I did the tests using the example app. There is a bug in the example app, too- ledSource.ReadOnly is set to TRUE, this way users cannot paste the folder path there, but they have to browse the whole way to the folders.
« Last Edit: June 21, 2013, 02:36:19 pm by paskal »
Лазар 2,0,10; W10 or W7 64bit; FPC3,2,0; rev 63526

bastla

  • New Member
  • *
  • Posts: 23
Re: Copying an entire directory (incl. subdirectories)
« Reply #5 on: June 22, 2013, 01:11:28 pm »
@paskal:
Thank you for testing my work!

I don't know why file attributes aren't copied. This class uses CopyFile-Method, maybe it doesn't support file attributes. I have to check this and add this.

I just tested it again: I had up to 4 subfolders with files in it, all subfolders and their files were copied.
A folder containing a "." was not copied for me, too. Mabye TFileSearcher declares this to be a file, I have to work on it.

The led.Source.ReadOnly = TRUE is conscious set, I just thought allowing users to paste their path would cause error because of wrong file paths etc.

Greetings,
bastla

JuhaManninen

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 3939
  • I like bugs.
Re: Copying an entire directory (incl. subdirectories)
« Reply #6 on: June 22, 2013, 04:02:12 pm »
There is function CopyDirTree() in LCL FileUtil unit.
Please test it also. It may still have bugs.
Mostly Lazarus trunk and FPC 3.2 on Manjaro Linux 64-bit.

bastla

  • New Member
  • *
  • Posts: 23
Re: Copying an entire directory (incl. subdirectories)
« Reply #7 on: June 23, 2013, 08:24:54 pm »
As I mentioned above, I worked on TCopyDir and I made some improvements!
I redesigned the whole code, it's so safe and stable now! The main things that changed: When copying a directory, first all files and directories will be cached and stored. Then, the directory structure of the source directory will be transferred to the target directory and only then the files will be copied! File attributes are also supported now!

Let's have a look on a sample code:
Code: [Select]
uses
  CopyDir
[...]
procedure TfrmMain.btCopyClick(Sender: TObject);
var
  CopyDir: TCopyDir;
  Log: TStringList;
begin
  CopyDir := TCopyDir.Create('C:\', 'D:\'); // copy content of "C:" to "D:"
  CopyDir.PrintToTerminal := true;    // print progress information

  CopyDir.CopyReadOnlyFiles := true;  // copy files with attribut "ReadOnly"
  CopyDir.CopyHiddenFiles := false;   // don't copy files with attribut "Hidden"
  CopyDir.CopySystemFiles := false;   // don't copy files with attribut "SystemFile"
  CopyDir.CopyArchiveFiles := true;   // copy files with attribut "Archive"

  CopyDir.PreserverFileDates := true; // preserve dates of copied files
  {$IFNDEF Unix}
  // TCopyDir.PreserveAttributes is not available for Unix systems
  // (Unit "FileUtil" is not able to set attributes on Unix system)
  CopyDir.PreserveAttributes := true; // preserve attributes of copied files
  {$ENDIF}

  CopyDir.Enumerate; // this is optional; files added to directory after enumeration won't be copied
  CopyDir.Start;

  Log := CopyDir.GetLog;
  CopyDir.Free;
end;

I hope this will be enough to satisfy you :)
Improved version will be attachted in the first post of this thread.

Greetings,
bastla

PS: I appreciate feedbacks!

Edit: @JuhaManninen:
I saw Takeda's CopyDirTree(), it's really simple and seems to be effective! But I don't think it can handle file flags like mine class :D
« Last Edit: June 23, 2013, 08:58:47 pm by bastla »

CM630

  • Hero Member
  • *****
  • Posts: 922
  • Не съм сигурен, че те разбирам.
    • http://sourceforge.net/u/cm630/profile/
Re: Copying an entire directory (incl. subdirectories)
« Reply #8 on: June 24, 2013, 09:46:18 am »
Okay, now it copied all my files and preserved the file modification date. It did not preserve the file creation date, but probably that is how it has to be, since the windows copy function does not preserve it, too.
I suppose that there is some more job to do- can this class handle attributes of *nix file systems?
Лазар 2,0,10; W10 or W7 64bit; FPC3,2,0; rev 63526

bastla

  • New Member
  • *
  • Posts: 23
Re: Copying an entire directory (incl. subdirectories)
« Reply #9 on: June 24, 2013, 07:15:02 pm »
As far as I remind, there's no method of LCL which modifies the creation date. This has a point, because there isn't any creation flag on Unix systems.

File attributes are caught by FileGetAttr-method (more precisely, its UTF8 equivalent FileGetAttrUTF8) that supports "readonly", "hidden", "sysfile", "volumeid", "directory" and "archive". TCopyDir supports all except "directory" and "volumeid" because directories are handled in a separate way, "volumeid" requires a plain FAT file system (not FAT32!) and only works on Windows.

So, you see I got the most out of that basic LCL stuff :)

Greetings,
bastla

CM630

  • Hero Member
  • *****
  • Posts: 922
  • Не съм сигурен, че те разбирам.
    • http://sourceforge.net/u/cm630/profile/
Re: Copying an entire directory (incl. subdirectories)
« Reply #10 on: June 25, 2013, 10:48:42 am »
So if I get you right, FileGetAttr method is not useful in Lunux (etc.), or there could be some other method (maybe FileGetPermissions???).
IMHO, currently you functions will cause serious troubles in *nix. Just imagine- after copying an executable file it won't be executable anymore.

Also, I came upon this thread.
Лазар 2,0,10; W10 or W7 64bit; FPC3,2,0; rev 63526

bastla

  • New Member
  • *
  • Posts: 23
Re: Copying an entire directory (incl. subdirectories)
« Reply #11 on: June 29, 2013, 12:28:00 pm »
I don't have a Unix system in use actually, so I can't improve things for Unix at the moment.
But I guess that FileGetAttr() can also handle files on Unix systems.

I did a quick research and haven't found any useful things to deal with Unix permissons explicitly.
There seem to be some special Unix methods in unit "BaseUnix", but, as I just said, I can't test them...

Greetings,
bastla

onivan

  • New member
  • *
  • Posts: 5
Re: Copying an entire directory (incl. subdirectories)
« Reply #12 on: July 29, 2013, 10:27:40 pm »
Hello, bastla!
Your code is very cool but I've found one bug: if there is no any second level directories in the source directory then no files are copied if they exist.
Here is my fix:
in procedure TCopyDir._CopyFiles; :
Code: [Select]
if (self._enumerated) and ((self._dirsCreated) or (Length(self._directories) = 0) ) and
    (Length(self._files) > 0) then       
                                               
« Last Edit: July 29, 2013, 10:29:22 pm by onivan »
Lazarus v1.6.4, fpc 3.0.2 on Ubuntu 16.04 x86_64 /  Windows 7 Pro win64/win32

bastla

  • New Member
  • *
  • Posts: 23
Re: Copying an entire directory (incl. subdirectories)
« Reply #13 on: August 04, 2013, 01:32:53 pm »
Hi onivan!

Thank you for reporting this bug! I did really not noticed this while designing the class  :-[
I'd like to mention your solution in my start post, would this be OK with you?

Greetings,
bastla

onivan

  • New member
  • *
  • Posts: 5
Re: Copying an entire directory (incl. subdirectories)
« Reply #14 on: August 10, 2013, 08:30:04 pm »
Surely that's OK!
Lazarus v1.6.4, fpc 3.0.2 on Ubuntu 16.04 x86_64 /  Windows 7 Pro win64/win32

 

TinyPortal © 2005-2018