Directory recursivity - Delete/copy/move a whole tree Topic is solved

If you are using the main C++ distribution of wxWidgets, Feel free to ask any question related to wxWidgets development here. This means questions regarding to C++ and wxWidgets, not compile problems.
Post Reply
mathieumg
Earned a small fee
Earned a small fee
Posts: 17
Joined: Fri Aug 06, 2010 5:16 pm

Directory recursivity - Delete/copy/move a whole tree

Post by mathieumg » Mon Aug 23, 2010 1:32 pm

Hi,

I'm looking to delete a whole directory (including all it's subdirectories and files), though I might need to do copy/move operations in the same manner.

I'm using 2.8, and I was told that wxRmdir isn't recursive under this version, in other words the directory to remove needs to be empty.

So I'm looking for a way to create a recursive loop to do all that. For example
recursive function (folder)
{
for every item in folder:
if its a file delete/move/copy it
if its a directory, call the function itself on it
}
The easiest would be the delete one I presume, but for the move/copy one, you need to make sure each directory exists in the two places before copying files (as to keep the same directory structure after the whole operation)

I would like to have some clues to help myself to proceed please! I have tried to inspire myself from 2.9's wxRmdir function ( http://svn.wxwidgets.org/svn/wx/wxWidge ... lename.cpp ), without much success.

Also, I'm leaving from here in a week (after which this project will be put on hold) so compiling/upgrading to 2.9 isn't really an option since I have no environment set up at all (I used wxPack).

Thanks a lot in advance!

DavidHart
Site Admin
Site Admin
Posts: 4008
Joined: Thu Jan 12, 2006 6:23 pm
Location: IoW, UK

Post by DavidHart » Mon Aug 23, 2010 3:17 pm

Hi,
So I'm looking for a way to create a recursive loop to do all that
Yes, this is what I do:

Code: Select all

bool MyGenericDirCtrl::ReallyDelete( wxFileName* PathName )
{
// Snip lots of linux-specific stuff

// You need to add a line here to check if PathName is a file. If so:
  if ( ! wxRemoveFile( PathName->GetFullPath() ) )        // It's alive, so kill it
          { wxMessageBox(_("Deletion Failed!?!")); return false; }
      }
    return true;
  }

   // If we're here, it's a dir
wxBusyCursor busy;

wxDir dir( PathName->GetPath() );                               // Deal sequentially with each child file & subdir
if ( !dir.IsOpened() )        return false;

wxString filename;
while ( dir.GetFirst( &filename ) )                             // Go thru the dir, committing infanticide 1 child @ a time
  {
    wxFileName child( PathName->GetPath(), filename );          // Make a new wxFileName for child
    if ( ! ReallyDelete( &child ) )                             // Slaughter it by recursion, whether it's a dir or file
        return false;                                           // If this fails, bug out
  }
  
if ( ! PathName->Rmdir() )                                      // Finally, kill the dir itself
  { wxMessageBox(_("Directory deletion Failed!?!")); return false; }

return true; 
}
but for the move/copy one, you need to make sure each directory exists in the two places before copying files (as to keep the same directory structure after the whole operation)
That's easy: for each directory, just use wxFileName::Mkdir with the wxPATH_MKDIR_FULL flag. This doesn't do any harm if the directory already exists.

It would be wise to check each file for pre-existence, though.

Regards,

David

mathieumg
Earned a small fee
Earned a small fee
Posts: 17
Joined: Fri Aug 06, 2010 5:16 pm

Post by mathieumg » Tue Aug 24, 2010 3:29 pm

Thanks for the help David!

Here is what I tried to come up with for the copy version:

Code: Select all

bool copyTree( wxFileName* source, wxFileName* destination )
{
    // Copy file if it isn't a directory.
    if ( ! source->IsDir() )
    {
        if ( ! wxCopyFile(source->GetFullPath(), destination->GetPath(wxPATH_GET_VOLUME | wxPATH_GET_SEPARATOR) + source->GetFullName()) + wxT("\\\") )
        {
            // Tried to output some stuff to see where problem could lie.
            cout << "copy failed" << endl;
            cout << source->GetFullPath().mb_str(wxConvUTF8) << " to " << destination->GetPath(wxPATH_GET_VOLUME | wxPATH_GET_SEPARATOR).mb_str(wxConvUTF8) << source->GetFullName().mb_str(wxConvUTF8) << "\\\" << endl;
            return false;
        }

        return true;
    }
    else
    {
        wxDir newdir( source->GetPath() );

        wxFileName::Mkdir(destination->GetPath(wxPATH_GET_VOLUME | wxPATH_GET_SEPARATOR) + newdir.GetName(), 0777, wxPATH_MKDIR_FULL);
    }

    wxDir dir( source->GetPath() );                               // Deal sequentially with each child file & subdir
    if ( !dir.IsOpened() )        return false;

    wxString filename;
    while ( dir.GetFirst( &filename ) )                             // Go thru the dir, cloning 1 child @ a time
      {
        wxFileName child( source->GetPath(), filename );          // Make a new wxFileName for child
        wxFileName newdestination( destination->GetPath(wxPATH_GET_VOLUME | wxPATH_GET_SEPARATOR));
        if ( ! copyTree( &child, &newdestination ) )                             // Clone it by recursion, whether it's a dir or file
            return false;                                           // If this fails, bug out
      }

    return true;
}
And I call it this way:

Code: Select all

    wxFileName test1(wxT("C:\\test\\\"));
    wxFileName test2(wxT("C:\\testdump\\\"));

    if ( copyTree( &test1, &test2 ) )
    {
        cout << "HUGE SUCCESS!!!" << endl;
    }
    else
    {
        cout << "HUGE FAIL!!!" << endl;
    }
For now it's a huge fail :( I didn't put the trailing slashes at first but things were even worse!

I feel like I'm not too far from a working solution, but I would need some more help with it :)

Thanks a lot in advance!

P.S. I know that the function copies source in destination (as opposed to content of source in destination), this is what I want.

Auria
Site Admin
Site Admin
Posts: 6695
Joined: Thu Sep 28, 2006 12:23 am
Contact:

Post by Auria » Tue Aug 24, 2010 3:42 pm

Star by checking with a debugger or with logging at which point in the function it returns false
"Keyboard not detected. Press F1 to continue"
-- Windows

mathieumg
Earned a small fee
Earned a small fee
Posts: 17
Joined: Fri Aug 06, 2010 5:16 pm

Post by mathieumg » Tue Aug 24, 2010 6:56 pm

The logging says it couldn't copy "C:\test\1" to "C:\testdump\1" with an access denied error. It sounds like it thinks that 1 is a file (while it actually is a directory). Could it be that source->IsDir() doesn't work well? David said on IRC that it could be related to using forward slashes instead of backslashes, but that doesn't seem to have changed much.

Here is my test tree:

C:\test\
C:\test\1\
C:\test\1\2\
C:\test\1\2\somefile.txt
C:\test\1\2\3\
C:\test\1\2\3\someotherfile.txt
C:\test\A\
C:\test\B\
C:\test\B\othertest.txt
C:\test\test.txt
C:\test\testing.jpg


Thanks for the help!

DavidHart
Site Admin
Site Admin
Posts: 4008
Joined: Thu Jan 12, 2006 6:23 pm
Location: IoW, UK

Post by DavidHart » Tue Aug 24, 2010 7:58 pm

It looks as if, despite the terminal wxFILE_SEP_PATH, that wxFileName still gets confused.
Why not try the methods suggested at the top of http://docs.wxwidgets.org/stable/wx_wxfilename.html

mathieumg
Earned a small fee
Earned a small fee
Posts: 17
Joined: Fri Aug 06, 2010 5:16 pm

Post by mathieumg » Wed Aug 25, 2010 11:47 am

David/Auria, as we discussed on IRC yesterday, I still have problems making this work :(

If I put a breakpoint at the beginning of the function and follow it line-by-line, it will always go in the else instruction of the DirExists condition. When I do that, I also see that copyTree gets called infinitely in my callstack.

Here is the latest version of my code:

http://pastebin.com/rMmdbLJa

mathieumg
Earned a small fee
Earned a small fee
Posts: 17
Joined: Fri Aug 06, 2010 5:16 pm

Post by mathieumg » Wed Aug 25, 2010 2:30 pm

I finally got things to work! The code is far from optimal/proper, but it does the job for now based on my needs!

Here is the code if anyone ends up on this page after a search and needs a starting point!

Code: Select all

bool copyTree( wxFileName* source, wxFileName* destination )
{
    // Copy file if it isn't a directory.
    if ( ! wxDir::Exists(source->GetFullPath()) )
    {
        if ( ! wxCopyFile(source->GetFullPath(), destination->GetPath(wxPATH_GET_VOLUME | wxPATH_GET_SEPARATOR) + source->GetFullName() ))
            return false;

        return true;
    }
    else
    {
        if( ! wxFileName::Mkdir(destination->GetPath(wxPATH_GET_VOLUME | wxPATH_GET_SEPARATOR) + source->GetDirs()[source->GetDirCount() - 1] + wxT("\\\"), 0777, wxPATH_MKDIR_FULL) )
        {
            return false;
        }
    }

    // Deal sequentially with each child file & subdir.
    wxDir dir( source->GetPath() );
    if ( !dir.IsOpened() )        return false;

    // Go thru the dir, cloning 1 child @ a time.
    wxString filename;
    bool cont = dir.GetFirst( &filename );
    while ( cont )
    {
        wxString childPath = source->GetPath(wxPATH_GET_VOLUME | wxPATH_GET_SEPARATOR) + filename;
        wxString newDestinationPath = destination->GetPath(wxPATH_GET_VOLUME | wxPATH_GET_SEPARATOR) + source->GetDirs()[source->GetDirCount() - 1] + wxT("\\\");
        wxFileName child;

        if ( wxDir::Exists(childPath) )
        {
            child.Assign( childPath + wxT("\\\") );
        }
        else
        {
            child.Assign( source->GetPath(wxPATH_GET_VOLUME | wxPATH_GET_SEPARATOR), filename );
        }

        // Make a new wxFileName for child
        wxFileName newDestination( newDestinationPath );

        // Clone it by recursion, whether it's a dir or file.
        if ( ! copyTree( &child, &newDestination ) )
            // If this fails, bug out.
            return false;

        cont = dir.GetNext(&filename);
    }

    return true;
}
Thank you to everyone who contributed!

Post Reply