/* copycore.cc
 * This file belongs to Worker, a file manager for UN*X/X11.
 * Copyright (C) 2013-2026 Ralf Hoffmann.
 * You can contact me at: ralf@boomerangsworld.de
 *   or http://www.boomerangsworld.de/worker
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */

#include "copycore.hh"
#include "worker_locale.h"
#include "copyopwin.hh"
#include "nmcopyopdir.hh"
#include "datei.h"
#include "copyorder.hh"
#include "simplelist.hh"
#include "verzeichnis.hh"
#include "aguix/lowlevelfunc.h"
#include "aguix/util.h"
#include "aguix/utf8.hh"
#include <chrono>
#include <ratio>
#include "nwc_path.hh"
#include "nwc_os.hh"
#include "worker.h"

CopyCore::CopyCore( std::shared_ptr< struct copyorder > co ) :
    m_copyorder( co ),
    m_cancel( false ),
    m_bytes_to_copy( 0 ),
    m_finished( false ),
    m_ask_to_cancel( false )
{
    buf = _allocsafe( BUFSIZE );
}

CopyCore::~CopyCore()
{
    _freesafe( buf );
}

static mode_t getModeWithEnsureOption( const mode_t orig_mode,
                                       const struct copyorder &co )
{
    mode_t res = orig_mode;

    if ( co.ensure_file_permissions() == CopyParams::ENSURE_USER_RW_PERMISSION ) {
        res |= S_IRUSR | S_IWUSR;
    } else if ( co.ensure_file_permissions() == CopyParams::ENSURE_USER_RW_GROUP_R_PERMISSION ) {
        res |= S_IRUSR | S_IWUSR | S_IRGRP;
    } else if ( co.ensure_file_permissions() == CopyParams::ENSURE_USER_RW_ALL_R_PERMISSION ) {
        res |= S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
    }

    return res;
}

CopyCore::nm_newname_t CopyCore::getNewName4Dir( const FileEntry *oldfe, const char *dest,
                                                 char **newname_return, bool acceptrename )
{
    char *oldname=oldfe->name;
    char *newname=dupstring(oldname);
    bool nameok=false,skip=false,cancel=false;
    char *buttonstr,*textstr,*newdest;
    int erg;
    Datei::d_fe_t se,lse;
    int round=1;
    nm_newname_t returnvalue=NM_NEWNAME_CANCEL;
    bool trydel,do_rename,strongdirs;
    char *title;
  
    if ( m_copyorder->cowin != NULL ) m_copyorder->cowin->stoptimer();

    title = (char*)_allocsafe( strlen( catalog.getLocale( 500 ) ) + strlen( oldfe->fullname ) + 1 );
    sprintf( title, catalog.getLocale( 500 ), oldfe->fullname );
  
    do_rename=false;
    if(m_copyorder->do_rename==false) do_rename=false;
    else if(acceptrename==true) do_rename=true;
  
    strongdirs=false;

    do {
        if(!((round==1)&&(do_rename==false))) {
            buttonstr=(char*)_allocsafe(strlen(catalog.getLocale(11))+1+
                                        strlen(catalog.getLocale(225))+1+
                                        strlen(catalog.getLocale(8))+1);
            sprintf(buttonstr,"%s|%s|%s",catalog.getLocale(11),
                    catalog.getLocale(225),
                    catalog.getLocale(8));
            textstr=(char*)_allocsafe(strlen(catalog.getLocale(148))+strlen(oldname)+1);
            sprintf(textstr,catalog.getLocale(148),oldname);

            std::string return_str;

            if ( ! UTF8::isValidCharacterString( newname ) ) {
                char *newname2 = dupstring( UTF8::convertToValidCharacterString( newname ).c_str() );
                _freesafe( newname );
                newname = newname2;
            }

            erg = m_copyorder->cowin->string_request( catalog.getLocale( 149 ),
                                                      textstr,
                                                      newname,
                                                      buttonstr,
                                                      return_str,
                                                      Requester::REQUEST_SELECTALL,
                                                      Requester::request_options{} );
            _freesafe(buttonstr);
            _freesafe(textstr);
            if ( ! return_str.empty() ) {
                _freesafe(newname);
                newname = dupstring( return_str.c_str() );
            }
            if(erg==1) {
                skip=true;
                returnvalue=NM_NEWNAME_SKIP;
                break;
            } else if(erg==2) {
                cancel=true;
                returnvalue=NM_NEWNAME_CANCEL;
                break;
            }
        }
    
        if ( strlen( newname ) < 1 ) {
            // empty name->request
            round++;  // not needed since empty names should only come from user input
            // but perhaps there is (will be) a OS which allows empty names
            continue;
        }
    
        newdest=(char*)_allocsafe(strlen(dest)+1+strlen(newname)+1);
        strcpy(newdest,dest);
        if(strlen(dest)>1) strcat(newdest,"/");
        strcat(newdest,newname);
        se=Datei::fileExistsExt(newdest);
        lse=Datei::lfileExistsExt(newdest);

        switch(lse) {
            case Datei::D_FE_DIR:
                if(strongdirs==true) {
                    textstr = (char*)_allocsafe( strlen( catalog.getLocale( 190 ) ) + strlen( newdest ) + 1 );
                    sprintf( textstr, catalog.getLocale( 190 ), newdest );

                    buttonstr=(char*)_allocsafe(strlen(catalog.getLocale(230))+1+
                                                strlen(catalog.getLocale(273))+1+
                                                strlen(catalog.getLocale(225))+1+
                                                strlen(catalog.getLocale(8))+1);
                    sprintf(buttonstr,"%s|%s|%s|%s",catalog.getLocale(230),
                            catalog.getLocale(273),
                            catalog.getLocale(225),
                            catalog.getLocale(8));

                    erg = m_copyorder->cowin->request( title/*catalog.getLocale(123)*/, textstr, buttonstr );
                    _freesafe(buttonstr);
                    _freesafe(textstr);
                    if(erg==1) {
                        nameok=true;
                        returnvalue=NM_NEWNAME_USE;
                    } else if(erg==2) {
                        skip=true;
                        returnvalue=NM_NEWNAME_SKIP;
                    } else if(erg==3) {
                        cancel=true;
                        returnvalue=NM_NEWNAME_CANCEL;
                    }
                } else {
                    // just use the dir
                    nameok=true;
                    returnvalue=NM_NEWNAME_USE;
                }
                break;
            case Datei::D_FE_LINK:
            case Datei::D_FE_FILE:
                if((lse==Datei::D_FE_LINK)&&(se==Datei::D_FE_DIR)) {
                    // Symlink with dir as destination 
                    if(strongdirs==true) {
                        //            textstr="There is already a symlink called %s, which points to a dir|You can use this link or delete it to|create a real directory";
                        //            buttonstr="Enter new name|Use this dir|Delete link|Skip|Cancel";

                        textstr = (char*)_allocsafe( strlen( catalog.getLocale( 275 ) ) + strlen( newdest ) + 1 );
                        sprintf( textstr, catalog.getLocale( 275 ), newdest );
                        buttonstr=(char*)_allocsafe(strlen(catalog.getLocale(230))+1+
                                                    strlen(catalog.getLocale(273))+1+
                                                    strlen(catalog.getLocale(274))+1+
                                                    strlen(catalog.getLocale(225))+1+
                                                    strlen(catalog.getLocale(8))+1);
                        sprintf(buttonstr,"%s|%s|%s|%s|%s",catalog.getLocale(230),
                                catalog.getLocale(273),
                                catalog.getLocale(274),
                                catalog.getLocale(225),
                                catalog.getLocale(8));

                        erg = m_copyorder->cowin->request( title/*catalog.getLocale(123)*/, textstr, buttonstr );
                        _freesafe(buttonstr);
                        _freesafe(textstr);
                        switch(erg) {
                            case 1:
                                nameok=true;
                                returnvalue=NM_NEWNAME_USE;
                                break;
                            case 2:
                                // try to remove the link
                                // if success set nameok to true
                                // in case of failure show a request and repeat the whole
                                if ( worker_unlink( newdest ) == 0 ) {
                                    nameok=true;
                                    returnvalue=NM_NEWNAME_OK;
                                } else {
                                    //                  textstr="Failed to remove this file|Please enter new name!";
                                    //                  buttonstr="Ok|Skip|Cancel";

                                    textstr = dupstring( catalog.getLocale(276) );
                                    buttonstr=(char*)_allocsafe(strlen(catalog.getLocale(11))+1+
                                                                strlen(catalog.getLocale(225))+1+
                                                                strlen(catalog.getLocale(8))+1);
                                    sprintf(buttonstr,"%s|%s|%s",catalog.getLocale(11),
                                            catalog.getLocale(225),
                                            catalog.getLocale(8));

                                    erg = m_copyorder->cowin->request( catalog.getLocale( 347 ), textstr, buttonstr );
                                    _freesafe(buttonstr);
                                    _freesafe( textstr );
                                    if(erg==1) {
                                        skip=true;
                                        returnvalue=NM_NEWNAME_SKIP;
                                    } else if(erg==2) {
                                        cancel=true;
                                        returnvalue=NM_NEWNAME_CANCEL;
                                    }
                                }
                                break;
                            case 3:
                                skip=true;
                                returnvalue=NM_NEWNAME_SKIP;
                                break;
                            case 4:
                                cancel=true;
                                returnvalue=NM_NEWNAME_CANCEL;
                                break;
                        }
                    } else {
                        // just use the link to the dir
                        nameok=true;
                        returnvalue=NM_NEWNAME_USE;
                    }
                } else {
                    trydel=false;
                    if((do_rename==false)&&(m_copyorder->overwrite==m_copyorder->COPY_OVERWRITE_ALWAYS)) {
                        trydel=true;
                    } else if((do_rename==false)&&(m_copyorder->overwrite==m_copyorder->COPY_OVERWRITE_NEVER)) {
                        // skip
                        skip=true;
                        returnvalue=NM_NEWNAME_SKIP;
                    } else {
                        // other overwrite modes cannot be handled for dirs so ask the user
                        if(lse==Datei::D_FE_LINK) {
                            //              textstr="There is already a link named %s";
                            //              buttonstr="Enter new name|Delete link|Skip|Cancel";

                            textstr = (char*)_allocsafe( strlen( catalog.getLocale( 278 ) ) + strlen( newdest ) + 1 );
                            sprintf( textstr, catalog.getLocale( 278 ), newdest );
                            buttonstr=(char*)_allocsafe(strlen(catalog.getLocale(230))+1+
                                                        strlen(catalog.getLocale(274))+1+
                                                        strlen(catalog.getLocale(225))+1+
                                                        strlen(catalog.getLocale(8))+1);
                            sprintf(buttonstr,"%s|%s|%s|%s",catalog.getLocale(230),
                                    catalog.getLocale(274),
                                    catalog.getLocale(225),
                                    catalog.getLocale(8));
                        } else {
                            //              textstr="There is already a file named %s";
                            //              buttonstr="Enter new name|Delete file|Skip|Cancel";

                            textstr = (char*)_allocsafe( strlen( catalog.getLocale( 279 ) ) + strlen( newdest ) + 1 );
                            sprintf( textstr, catalog.getLocale( 279 ), newdest );
                            buttonstr=(char*)_allocsafe(strlen(catalog.getLocale(230))+1+
                                                        strlen(catalog.getLocale(277))+1+
                                                        strlen(catalog.getLocale(225))+1+
                                                        strlen(catalog.getLocale(8))+1);
                            sprintf(buttonstr,"%s|%s|%s|%s",catalog.getLocale(230),
                                    catalog.getLocale(277),
                                    catalog.getLocale(225),
                                    catalog.getLocale(8));
                        }
                        erg = m_copyorder->cowin->request( title/*catalog.getLocale(123)*/, textstr, buttonstr );
                        _freesafe(buttonstr);
                        _freesafe(textstr);
                        switch(erg) {
                            case 1:
                                trydel=true;
                                break;
                            case 2:
                                skip=true;
                                returnvalue=NM_NEWNAME_SKIP;
                                break;
                            case 3:
                                cancel=true;
                                returnvalue=NM_NEWNAME_CANCEL;
                                break;
                        }
                    }
                    if(trydel==true) {
                        if ( worker_unlink( newdest ) == 0 ) {
                            nameok=true;
                            returnvalue=NM_NEWNAME_OK;
                        } else {
                            //              textstr="Failed to remove this file|Please enter new name!";
                            //              buttonstr="Ok|Skip|Cancel";

                            textstr = dupstring( catalog.getLocale(276) );
                            buttonstr=(char*)_allocsafe(strlen(catalog.getLocale(11))+1+
                                                        strlen(catalog.getLocale(225))+1+
                                                        strlen(catalog.getLocale(8))+1);
                            sprintf(buttonstr,"%s|%s|%s",catalog.getLocale(11),
                                    catalog.getLocale(225),
                                    catalog.getLocale(8));

                            erg = m_copyorder->cowin->request( catalog.getLocale( 347 ), textstr, buttonstr );
                            _freesafe(buttonstr);
                            _freesafe( textstr );
                            if(erg==1) {
                                skip=true;
                                returnvalue=NM_NEWNAME_SKIP;
                            } else if(erg==2) {
                                cancel=true;
                                returnvalue=NM_NEWNAME_CANCEL;
                            }
                        }
                    }
                }
                break;
            default:
                nameok=true;
                returnvalue=NM_NEWNAME_OK;
                break;
        }
        _freesafe(newdest);
        round++;
    } while((nameok==false)&&(skip==false)&&(cancel==false));
    if((skip==true)||(cancel==true)) {
        _freesafe(newname);
        *newname_return=NULL;
    } else {
        *newname_return=newname;
    }
    _freesafe( title );

    if ( m_copyorder->cowin != NULL ) m_copyorder->cowin->conttimer();

    return returnvalue;
}

CopyCore::nm_newname_t CopyCore::getNewName4File( const FileEntry *oldfe, const char *dest,
                                                      char **newname_return, bool acceptrename )
{
    char *oldname=oldfe->name;
    char *newname=dupstring(oldname);
    bool nameok=false,skip=false,cancel=false;
    char *buttonstr,*textstr,*newdest;
    int erg;
    Datei::d_fe_t lse;
    int round=1;
    nm_newname_t returnvalue=NM_NEWNAME_CANCEL;
    bool do_rename,trydel;
    char *extrastr,*tstr,*newtime,*oldtime;
    int newsizelen,oldsizelen,maxsizelen;
    char *title;
    time_t tvar;
    std::string newsize, oldsize;
  
    if ( m_copyorder->cowin != NULL ) m_copyorder->cowin->stoptimer();

    title = (char*)_allocsafe( strlen( catalog.getLocale( 116 ) ) + strlen( oldfe->fullname ) + 1 );
    sprintf( title, catalog.getLocale( 116 ), oldfe->fullname );
  
    do_rename=false;
    if(m_copyorder->do_rename==false) do_rename=false;
    else if(acceptrename==true) do_rename=true;

    do {
        if(!((round==1)&&(do_rename==false))) {
            //      buttonstr="Ok|Skip|Cancel";
            //      textstr="Enter new name for ...";
            buttonstr=(char*)_allocsafe(strlen(catalog.getLocale(11))+1+
                                        strlen(catalog.getLocale(225))+1+
                                        strlen(catalog.getLocale(8))+1);
            sprintf(buttonstr,"%s|%s|%s",catalog.getLocale(11),
                    catalog.getLocale(225),
                    catalog.getLocale(8));
            textstr=(char*)_allocsafe(strlen(catalog.getLocale(148))+strlen(oldname)+1);
            sprintf(textstr,catalog.getLocale(148),oldname);

            std::string return_str;

            if ( ! UTF8::isValidCharacterString( newname ) ) {
                char *newname2 = dupstring( UTF8::convertToValidCharacterString( newname ).c_str() );
                _freesafe( newname );
                newname = newname2;
            }

            size_t select_characters = strlen( newname );
            if ( ! m_copyorder->also_select_extension  ) {
                const char *extension = strrchr( newname, '.' );
                if ( extension ) {
                    select_characters = extension - newname;
                }
            }

            Requester::request_options options;
            options.select_characters = select_characters;

            erg = m_copyorder->cowin->string_request( catalog.getLocale( 149 ),
                                                      textstr,
                                                      newname,
                                                      buttonstr,
                                                      return_str,
                                                      Requester::REQUEST_NONE,
                                                      options );
            _freesafe(buttonstr);
            _freesafe(textstr);
            if ( ! return_str.empty() ) {
                _freesafe(newname);
                newname = dupstring( return_str.c_str() );
            }
            if(erg==1) {
                skip=true;
                returnvalue=NM_NEWNAME_SKIP;
                break;
            } else if(erg==2) {
                cancel=true;
                returnvalue=NM_NEWNAME_CANCEL;
                break;
            }
        }
    
        if ( strlen( newname ) < 1 ) {
            // empty name->request
            round++;  // not needed since empty names should only come from user input
            // but perhaps there is (will be) a OS which allows empty names
            continue;
        }
    
        newdest=(char*)_allocsafe(strlen(dest)+1+strlen(newname)+1);
        strcpy(newdest,dest);
        if(strlen(dest)>1) strcat(newdest,"/");
        strcat(newdest,newname);
        lse=Datei::lfileExistsExt(newdest);

        switch(lse) {
            case Datei::D_FE_DIR:
                //        textstr="there is already a dir called %s";
                //        buttonstr="Enter new name|Skip|Cancel";

                textstr = (char*)_allocsafe( strlen( catalog.getLocale( 190 ) ) + strlen( newdest ) + 1 );
                sprintf( textstr, catalog.getLocale( 190 ), newdest );

                buttonstr=(char*)_allocsafe(strlen(catalog.getLocale(230))+1+
                                            strlen(catalog.getLocale(225))+1+
                                            strlen(catalog.getLocale(8))+1);
                sprintf(buttonstr,"%s|%s|%s",catalog.getLocale(230),
                        catalog.getLocale(225),
                        catalog.getLocale(8));

                erg = m_copyorder->cowin->request( title/*catalog.getLocale(123)*/, textstr, buttonstr );
                _freesafe(buttonstr);
                _freesafe(textstr);
                if(erg==1) {
                    skip=true;
                    returnvalue=NM_NEWNAME_SKIP;
                } else if(erg==2) {
                    cancel=true;
                    returnvalue=NM_NEWNAME_CANCEL;
                }
                break;
            case Datei::D_FE_LINK:
                trydel=false;
                if((do_rename==false)&&(m_copyorder->overwrite==m_copyorder->COPY_OVERWRITE_ALWAYS)) {
                    trydel=true;
                } else if((do_rename==false)&&(m_copyorder->overwrite==m_copyorder->COPY_OVERWRITE_NEVER)) {
                    // skip
                    skip=true;
                    returnvalue=NM_NEWNAME_SKIP;
                } else {
                    // other overwrite modes cannot be handled for links so ask the user

                    //          textstr="there is already a link named %s";
                    //          buttonstr="Enter new name|Delete link|Skip|Cancel";

                    textstr = (char*)_allocsafe( strlen( catalog.getLocale( 278 ) ) + strlen( newdest ) + 1 );
                    sprintf( textstr, catalog.getLocale( 278 ), newdest );
                    buttonstr=(char*)_allocsafe(strlen(catalog.getLocale(230))+1+
                                                strlen(catalog.getLocale(274))+1+
                                                strlen(catalog.getLocale(225))+1+
                                                strlen(catalog.getLocale(8))+1);
                    sprintf(buttonstr,"%s|%s|%s|%s",catalog.getLocale(230),
                            catalog.getLocale(274),
                            catalog.getLocale(225),
                            catalog.getLocale(8));

                    erg = m_copyorder->cowin->request( title/*catalog.getLocale(123)*/, textstr, buttonstr );
                    _freesafe(buttonstr);
                    _freesafe(textstr);
                    switch(erg) {
                        case 1:
                            trydel=true;
                            break;
                        case 2:
                            skip=true;
                            returnvalue=NM_NEWNAME_SKIP;
                            break;
                        case 3:
                            cancel=true;
                            returnvalue=NM_NEWNAME_CANCEL;
                            break;
                    }
                }
                if(trydel==true) {
                    if ( worker_unlink( newdest ) == 0 ) {
                        nameok=true;
                        returnvalue=NM_NEWNAME_OK;
                    } else {
                        //            textstr="Failed to remove this file|Please enter new name!";
                        //            buttonstr="Ok|Skip|Cancel";

                        textstr = dupstring( catalog.getLocale(276) );
                        buttonstr=(char*)_allocsafe(strlen(catalog.getLocale(11))+1+
                                                    strlen(catalog.getLocale(225))+1+
                                                    strlen(catalog.getLocale(8))+1);
                        sprintf(buttonstr,"%s|%s|%s",catalog.getLocale(11),
                                catalog.getLocale(225),
                                catalog.getLocale(8));

                        erg = m_copyorder->cowin->request( catalog.getLocale( 347 ), textstr, buttonstr );
                        _freesafe(buttonstr);
                        _freesafe( textstr );
                        if(erg==1) {
                            skip=true;
                            returnvalue=NM_NEWNAME_SKIP;
                        } else if(erg==2) {
                            cancel=true;
                            returnvalue=NM_NEWNAME_CANCEL;
                        }
                    }
                }
                break;
            case Datei::D_FE_FILE: {
                bool target_is_older = false;
                bool target_is_smaller = false;
                bool target_is_different_size = false;

                if ( m_copyorder->overwrite != m_copyorder->COPY_OVERWRITE_ALWAYS &&
                     m_copyorder->overwrite != m_copyorder->COPY_OVERWRITE_NEVER ) {
                    NWC::FSEntry target_fse( newdest );

                    if ( target_fse.stat_lastmod() < oldfe->lastmod() ) {
                        target_is_older = true;
                    }
                    if ( target_fse.stat_size() < oldfe->size() ) {
                        target_is_smaller = true;
                    }
                    if ( target_fse.stat_size() != oldfe->size() ) {
                        target_is_different_size = true;
                    }
                }

                if((do_rename==false)&&(m_copyorder->overwrite==m_copyorder->COPY_OVERWRITE_ALWAYS)) {
                    nameok=true;
                    returnvalue=NM_NEWNAME_OVERWRITE;
                } else if((do_rename==false)&&(m_copyorder->overwrite==m_copyorder->COPY_OVERWRITE_NEVER)) {
                    skip=true;
                    returnvalue=NM_NEWNAME_SKIP;
                } else if ( do_rename == false && m_copyorder->overwrite == m_copyorder->COPY_OVERWRITE_OLDER ) {
                    if ( target_is_older ) {
                        nameok = true;
                        returnvalue = NM_NEWNAME_OVERWRITE;
                    } else {
                        skip = true;
                        returnvalue = NM_NEWNAME_SKIP;
                    }
                } else if ( do_rename == false && m_copyorder->overwrite == m_copyorder->COPY_OVERWRITE_SMALLER ) {
                    if ( target_is_smaller ) {
                        nameok = true;
                        returnvalue = NM_NEWNAME_OVERWRITE;
                    } else {
                        skip = true;
                        returnvalue = NM_NEWNAME_SKIP;
                    }
                } else if ( do_rename == false && m_copyorder->overwrite == m_copyorder->COPY_OVERWRITE_DIFFERENT_SIZE ) {
                    if ( target_is_different_size ) {
                        nameok = true;
                        returnvalue = NM_NEWNAME_OVERWRITE;
                    } else {
                        skip = true;
                        returnvalue = NM_NEWNAME_SKIP;
                    }
                } else {
                    FileEntry *newfe=new FileEntry();
                    if(newfe->name!=NULL) _freesafe(newfe->name);
                    if(newfe->fullname!=NULL) _freesafe(newfe->fullname);
                    newfe->name=dupstring(newname);
                    newfe->fullname=dupstring(newdest);
                    newfe->readInfos();
          
                    MakeLong2NiceStr( newfe->size(), newsize );
                    MakeLong2NiceStr( oldfe->size(), oldsize );
                    oldsizelen = oldsize.length();
                    newsizelen = newsize.length();
                    maxsizelen = ( newsizelen > oldsizelen ) ? newsizelen : oldsizelen;
                    if ( maxsizelen > newsizelen )
                        newsize.insert( newsize.begin(), maxsizelen - newsizelen, ' ' );
                    if ( maxsizelen > oldsizelen )
                        oldsize.insert( oldsize.begin(), maxsizelen - oldsizelen, ' ' );

                    tvar = newfe->lastmod();
                    newtime = dupstring( ctime( &( tvar ) ) );
                    tvar = oldfe->lastmod();
                    oldtime = dupstring( ctime( &( tvar ) ) );
                    newtime[strlen(newtime)-1]='\0';  // remove return
                    oldtime[strlen(oldtime)-1]='\0';  // remove return
          
                    extrastr=(char*)_allocsafe(strlen(catalog.getLocale(137))+newsize.length()+oldsize.length()+
                                               strlen(newtime)+strlen(oldtime)+1);
                    sprintf(extrastr,catalog.getLocale(137),newsize.c_str(),oldsize.c_str(),newtime,oldtime);
          
                    _freesafe(newtime);
                    _freesafe(oldtime);
                    delete newfe;
                    //          textstr="There is already a file named %s";
                    //          buttonstr="Enter new name|Overwrite|Overwrite all|Overwrite none|Skip|Cancel";

                    textstr = (char*)_allocsafe( strlen( catalog.getLocale( 279 ) ) + strlen( newdest ) + 1 );
                    sprintf( textstr, catalog.getLocale( 279 ), newdest );

                    tstr=catstring(textstr,"|");
                    _freesafe(textstr);
                    textstr=catstring(tstr,extrastr);
                    _freesafe(tstr);
                    _freesafe(extrastr);

                    std::vector< std::vector< std::pair< std::string, int > > > buttons = {
                        {
                            { catalog.getLocale(230), 0 },
                            { catalog.getLocale(280), 1 },
                            { catalog.getLocale(135), 2 },
                            { catalog.getLocale(136), 3 },
                            { catalog.getLocale(225), 4 },
                            { catalog.getLocale(8), 5 },
                        },
                        { { "", -1 },
                          { "", -1 },
                          { catalog.getLocale( 1583 ), 6 }
                        },
                        { { "", -1 },
                          { "", -1 },
                          { catalog.getLocale( 1584 ), 7 }
                        },
                        { { "", -1 },
                          { "", -1 },
                          { catalog.getLocale( 1585 ), 8 }
                        }
                    };

                    erg = m_copyorder->cowin->request( title/*catalog.getLocale(123)*/, textstr, buttons );
                    _freesafe(textstr);
                    switch(erg) {
                        case 1: // overwrite only this one
                            nameok=true;
                            returnvalue=NM_NEWNAME_OVERWRITE;
                            break;
                        case 2: // always
                            nameok=true;
                            returnvalue=NM_NEWNAME_OVERWRITE;
                            m_copyorder->overwrite=m_copyorder->COPY_OVERWRITE_ALWAYS;
                            break;
                        case 6: // older
                            if ( target_is_older ) {
                                nameok = true;
                                returnvalue = NM_NEWNAME_OVERWRITE;
                            } else {
                                skip = true;
                                returnvalue = NM_NEWNAME_SKIP;
                            }
                            m_copyorder->overwrite=m_copyorder->COPY_OVERWRITE_OLDER;
                            break;
                        case 7: // smaller
                            if ( target_is_smaller ) {
                                nameok = true;
                                returnvalue = NM_NEWNAME_OVERWRITE;
                            } else {
                                skip = true;
                                returnvalue = NM_NEWNAME_SKIP;
                            }
                            m_copyorder->overwrite=m_copyorder->COPY_OVERWRITE_SMALLER;
                            break;
                        case 8: // different size
                            if ( target_is_different_size ) {
                                nameok = true;
                                returnvalue = NM_NEWNAME_OVERWRITE;
                            } else {
                                skip = true;
                                returnvalue = NM_NEWNAME_SKIP;
                            }
                            m_copyorder->overwrite=m_copyorder->COPY_OVERWRITE_DIFFERENT_SIZE;
                            break;
                        case 3: // never
                            skip=true;
                            returnvalue=NM_NEWNAME_SKIP;
                            m_copyorder->overwrite=m_copyorder->COPY_OVERWRITE_NEVER;
                            break;
                        case 4: // skip
                            skip=true;
                            returnvalue=NM_NEWNAME_SKIP;
                            break;
                        case 5: // cancel
                            cancel=true;
                            returnvalue=NM_NEWNAME_CANCEL;
                            break;
                    }
                }
                break;
            }
            default:
                nameok=true;
                returnvalue=NM_NEWNAME_OK;
                break;
        }
        _freesafe(newdest);
        round++;
    } while((nameok==false)&&(skip==false)&&(cancel==false));
    if((skip==true)||(cancel==true)) {
        _freesafe(newname);
        *newname_return=NULL;
    } else {
        *newname_return=newname;
    }
    _freesafe( title );

    if ( m_copyorder->cowin != NULL ) m_copyorder->cowin->conttimer();

    return returnvalue;
}

CopyCore::nm_copy_t
CopyCore::copydir( const FileEntry *fe, NM_CopyOp_Dir *cod, bool acceptrename, const char *destdir,
                   std::string *effective_target_fullname,
                   bool skip_ok_test )
{
    NM_CopyOp_Dir *cod1 = NULL;
    bool nameok,createdir;
    nm_newname_t e1;
    char *destsubdir;
    Datei::d_fe_t de1;
    char *newname=NULL;
    int id1;
    FileEntry *subfe;
    bool isdir,skip;
    char *buttonstr,*textstr;
    int erg;
    CopyOpWin *cowin=m_copyorder->cowin;
    bool docopy,failedmove=false;
    struct utimbuf utb;
    bool corrupt_entries_skipped = false;

    skip = false;
    if ( cowin != NULL ) {
        // call newfile without destination so the user
        // can see the filename in the copyopwin when he will
        // be asked for the new name
        cowin->newfile( fe->name, "" );
    }

    if ( ! skip_ok_test && ! cod->ok ) {
        CopyOpWin *cowin = m_copyorder->cowin;
        if ( cowin ) {
            cowin->stoptimer();
            auto textstr = AGUIXUtils::formatStringToString( catalog.getLocale( 1456 ),
                                                             fe->fullname );
            std::string buttonstr = catalog.getLocale( 629 );
            buttonstr += "|";
            buttonstr += catalog.getLocale( 225 );
            buttonstr += "|";
            buttonstr += catalog.getLocale( 8 );
            int erg = m_copyorder->cowin->request( catalog.getLocale( 347 ),
                                                   textstr,
                                                   buttonstr );
            if ( erg == 2 ) {
                m_cancel=true;
            } else if ( erg == 1 ) {
                skip = true;
            }
            cowin->conttimer();
        } else {
            skip = true;
        }
    }

    if ( m_cancel == false && skip == false ) {
        e1=getNewName4Dir(fe,destdir,&newname,acceptrename);
        if((e1==NM_NEWNAME_OK)||(e1==NM_NEWNAME_USE)) {
            // build new name
            destsubdir=(char*)_allocsafe(strlen(destdir)+1+strlen(newname)+1);
            strcpy(destsubdir,destdir);
            if(strlen(destsubdir)>1) {
                if ( destsubdir[strlen(destsubdir) - 1] != '/' ) {
                    strcat( destsubdir, "/" );
                }
            }
            strcat(destsubdir,newname);
      
            nameok=false;
            createdir=true;
            if(e1==NM_NEWNAME_USE) {
                // there is already such dir (or a symlink to a dir) -> use it
                nameok=true;
                createdir=false;
            } else if(e1==NM_NEWNAME_OK) {
                // check for fileexists (should not)
                // if nethertheless then skip this dir because the rename-func wasn't able to remove it
                de1=Datei::lfileExistsExt(destsubdir);
                if(de1==Datei::D_FE_NOFILE) {
                    // everything is ok
                    nameok=true;
                } else if(de1==Datei::D_FE_DIR) {
                    // dest is a dir
                    // should not happend but ok
                    nameok=true;
                    createdir=false;
                    e1=NM_NEWNAME_USE; // for moving it's important to set this, so it doesn't try to
                    // move
                }
            }
            if(nameok==true) {
                docopy=true;
                failedmove=false;
                if(m_copyorder->move==true) {
                    if(e1==NM_NEWNAME_USE) {
                        // because of using the destination dir
                        // we need to do as failed rename
                        failedmove=true;
                    } else {
                        if ( m_copyorder->adjust_relative_symlinks != m_copyorder->COPY_ADJUST_SYMLINK_NEVER ) {
                            // skip trying to rename dir so that we can handle symlinks within the dir

                            // assume that rename has failed so the source
                            // dir is removed if copying was successful
                            failedmove = true;
                        } else if ( worker_rename( fe->fullname, destsubdir ) == 0 ) {
                            // success
                            docopy=false;
                        } else {
                            // failure
                            failedmove=true;
                        }
                    }
                }
        
                if(docopy==true) {
                    if(cowin!=NULL) cowin->newfile(fe->name,destsubdir);
                    if(createdir==true) {
                        if ( worker_mkdir( destsubdir, 0700 ) != 0 ) {
                            // failed to create dir -> skip
                            //            textstr="Failed to create dir|I will skip this dir!";
                            //            buttonstr="Ok|Cancel";
              
                            if(cowin!=NULL) cowin->stoptimer();
                            textstr=(char*)_allocsafe(strlen(catalog.getLocale(126))+strlen(destsubdir)+1);
                            sprintf(textstr,catalog.getLocale(126),destsubdir);
                            buttonstr=(char*)_allocsafe(strlen(catalog.getLocale(11))+1+
                                                        strlen(catalog.getLocale(8))+1);
                            sprintf(buttonstr,"%s|%s",catalog.getLocale(11),
                                    catalog.getLocale(8));
                            erg = m_copyorder->cowin->request( catalog.getLocale( 347 ), textstr, buttonstr );
                            _freesafe(buttonstr);
                            _freesafe(textstr);
                            if(erg==1) m_cancel=true;
                            nameok=false;
                            if(cowin!=NULL) cowin->conttimer();
                        }
                    }
                    if(nameok==true) {
                        // first copy all subdirs
                        // then copy all files 
                        id1=cod->subdirs->initEnum();
                        cod1=(NM_CopyOp_Dir*)cod->subdirs->getFirstElement(id1);
                        while(cod1!=NULL) {
                            nm_copy_t ce1;
                            ce1=copydir(cod1->fileentry,cod1,false,destsubdir, NULL, false);
                            if((ce1==NM_COPY_OK)||(ce1==NM_COPY_NEED_DELETE)) {
                                // success
                                cod->error_counter+=cod1->error_counter;
                            } else {
                                cod->error_counter+=cod1->error_counter+1;
                                if(ce1==NM_COPY_CANCEL) {
                                    m_cancel=true;
                                    break;
                                }
                            }
                            cod1=(NM_CopyOp_Dir*)cod->subdirs->getNextElement(id1);
                        }
                        cod->subdirs->closeEnum(id1);
            
                        // next only if read this dir correctly
                        if((cod->ok==true)&&(m_cancel==false)) {
                            for ( Verzeichnis::verz_it subfe_it1 = cod->verz->begin();
                                  subfe_it1 != cod->verz->end() && m_cancel == false;
                                  subfe_it1++ ) {

                                checkAskToCancel();

                                subfe = *subfe_it1;

                                if ( subfe->isCorrupt == true && subfe->isLink == false ) {
                                    if ( cowin != NULL ) cowin->stoptimer();
                    
                                    std::string text = AGUIXUtils::formatStringToString( catalog.getLocale( 909 ),
                                                                                         subfe->fullname );
                                    std::string button = std::string( catalog.getLocale( 225 ) ) + "|" + catalog.getLocale( 8 );
                                    erg = m_copyorder->cowin->request( catalog.getLocale( 347 ), text.c_str(), button.c_str() );
                                    if ( erg == 0 ) {
                                    } else {
                                        m_cancel = true;
                                    }
                    
                                    if ( cowin != NULL ) cowin->conttimer();
                    
                                    corrupt_entries_skipped = true;
                                    continue;
                                }

                                if(strcmp(subfe->name,"..")!=0) {
                                    isdir=false;
                                    if(subfe->isDir()==true) {
                                        if(subfe->isLink==false) isdir=true;
                                        else if(m_copyorder->follow_symlinks==true) isdir=true;
                                    }
                                    if(isdir==false) {
                                        nm_copy_t cf_res;
                                        cf_res=copyfile(subfe,false,destsubdir, NULL);
                                        if((cf_res==NM_COPY_OK)||(cf_res==NM_COPY_NEED_DELETE)) {
                                            // success
                                        } else {
                                            cod->error_counter++;
                                            if(cf_res==NM_COPY_CANCEL) {
                                                m_cancel=true;
                                                break;
                                            }
                                        }
                                    }
                                }
                            }
                        }
                        // finally change the permissions of the dir
                        if((createdir==true)&&(m_copyorder->preserve_attr==true)) {
                            // fe is a dir so if it is a link it is not corrupt
                            mode_t m = ( fe->isLink == true ) ? fe->dmode() : fe->mode();
                            m = getModeWithEnsureOption( m, *m_copyorder );

                            int res = worker_chown( destsubdir, ( fe->isLink == true ) ? fe->duserid() : fe->userid(),
                                                    ( fe->isLink == true ) ? fe->dgroupid() : fe->groupid() );
                            (void)res; // ignore result for now
                            worker_chmod( destsubdir, m );
                            utb.actime = ( fe->isLink == true ) ? fe->dlastaccess() : fe->lastaccess();
                            utb.modtime = ( fe->isLink == true ) ? fe->dlastmod() : fe->lastmod();
                            worker_utime(destsubdir,&utb);
                        }
                    } else skip=true;
                }
            } else skip=true;

            if ( effective_target_fullname != NULL ) {
                *effective_target_fullname = destsubdir;
            }

            _freesafe(destsubdir);
        } else {
            switch(e1) {
                case NM_NEWNAME_CANCEL:
                    m_cancel=true;
                default:
                    skip=true;
                    break;
            }
        }
    }
    if((skip==true)&&(cowin!=NULL)) {
        // dir skiped so dec CopyOpWin counter by the files/dirs skiped
        cowin->dec_file_counter(cod->files);
        cowin->dec_dir_counter(cod->dirs);
        cowin->dec_byte_counter(cod->bytes);
    }

    if ( corrupt_entries_skipped == true ) skip = true;

    if(cowin!=NULL) {
        cowin->dir_finished();
    }
    if(newname!=NULL) _freesafe(newname);
    if(m_cancel==true) {
        std::string d = fe->fullname;
        d += "/";
        m_incomplete_entries.push_back( d );

        return NM_COPY_CANCEL;
    } else if(skip==true) {
        cod->error_counter++;

        std::string d = fe->fullname;
        d += "/";
        m_incomplete_entries.push_back( d );

        return NM_COPY_SKIP;
    } else {
        // in case continue was selected
        if ( ! skip_ok_test && ! cod->ok ) {
            std::string d = fe->fullname;
            d += "/";
            m_incomplete_entries.push_back( d );
        }
    }
    if(m_copyorder->move==true) {
        if((cod->error_counter==0)&&(failedmove==true)) {
            // put fe in list

            // Man koennte das FE auch also Kopie benutzen
            // man muesste nur den Vergleich beim spaeteren Loeschen anpassen
            pushForPostDelete( fe );
            return NM_COPY_NEED_DELETE;
        }
    }
    return NM_COPY_OK;
}

CopyCore::nm_copy_t
CopyCore::copyfile( const FileEntry *fe, bool acceptrename, const char *destdir, std::string *effective_target_fullname )
{
    bool nameok;
    nm_newname_t e1;
    char *newfullname;
    Datei::d_fe_t de1;
    char *newname=NULL;
    bool useregcopy, skip;
    int erg;
    CopyOpWin *cowin=m_copyorder->cowin;
    bool docopy,failedmove=false,opok=false;
    struct utimbuf utb;
    const char *myerrstr;
    loff_t bytesToCopy;
    mode_t modeCheck;
    bool useLinkDest;
    std::string text, button;
    bool user_skip = false;
  
    if ( fe->isLink == true && fe->isCorrupt == false && m_copyorder->follow_symlinks ) {
        bytesToCopy = fe->dsize();
    } else {
        bytesToCopy = fe->size();
    }

    skip = false;
    if ( cowin != NULL ) {
        // call newfile without destination so the user
        // can see the filename in the copyopwin when he will
        // be asked for the new name
        cowin->newfile( fe->name, "" );
    }

    if ( fe->isLink == false && fe->isCorrupt == true ) {
        if ( cowin != NULL ) cowin->stoptimer();

        text = AGUIXUtils::formatStringToString( catalog.getLocale( 909 ),
                                                 fe->fullname );
        button = std::string( catalog.getLocale( 225 ) ) + "|" + catalog.getLocale( 8 );
        erg = cowin->request( catalog.getLocale( 347 ), text.c_str(), button.c_str() );
        if ( erg == 0 ) {
            skip = true;
        } else {
            m_cancel = true;
        }

        if ( cowin != NULL ) cowin->conttimer();
    }

    if ( ! fe->exists ) {
        if ( cowin != NULL ) cowin->stoptimer();
        myerrstr = strerror( errno );
        text = AGUIXUtils::formatStringToString( catalog.getLocale( 1465 ), fe->fullname );
        button = std::string( catalog.getLocale( 225 ) ) + "|" + catalog.getLocale( 8 );
        erg = cowin->request( catalog.getLocale( 347 ), text.c_str(), button.c_str() );
        if ( erg == 0 ) {
            skip = true;
        } else {
            m_cancel = true;
        }
        if ( cowin != NULL ) cowin->conttimer();
    }

    if ( m_cancel == false && skip == false ) {
        e1=getNewName4File(fe,destdir,&newname,acceptrename);
    
        if((e1==NM_NEWNAME_OK)||(e1==NM_NEWNAME_OVERWRITE)) {
            // build new name
            newfullname=(char*)_allocsafe(strlen(destdir)+1+strlen(newname)+1);
            strcpy(newfullname,destdir);
            if(strlen(newfullname)>1) {
                if ( newfullname[strlen(newfullname) - 1] != '/' ) {
                    strcat( newfullname, "/" );
                }
            }
            strcat(newfullname,newname);
      
            nameok=false;
            skip=false;
            if(e1==NM_NEWNAME_OVERWRITE) {
                nameok=true;
            } else if(e1==NM_NEWNAME_OK) {
                // check for fileexists (should not)
                de1=Datei::lfileExistsExt(newfullname);
                if(de1==Datei::D_FE_NOFILE) {
                    // everything is ok
                    nameok=true;
                } else if(de1==Datei::D_FE_FILE) {
                    // dest is a dir
                    // should not happend but ok try to overwrite this
                    nameok=true;
                }
            }
            if(nameok==true) {
                if ( fe->isSameFile( newfullname, m_copyorder->follow_symlinks ) == false ) {
                    // wenn move true, dann rename probieren
                    //        klappt rename nicht, muss flag gesetzt werden und normal weitergemacht
                    //        werden und bei korrekten kopieren FE in liste gepackt werden und NEED_DELETE
                    //        zurueckgeliefert werden
                    //      also: 1.flag docopy am Anfang auf true setzen
                    //            2.bei move==true rename probieren
                    //            2.1.bei Erfolg docopy auf false, Rueckgabewert ist automatisch OK
                    //            2.2.bei Misslingen failedmove auf true setzen
                    //            3.bei docopy==true normal kopieren
                    //            3.1.bei Misslingen kommt halt skip zurueck
                    //            3.2.Bei Erfolg und failedmove==true muss FE in liste gepackt werden
                    //                und NEED_DELETE zurueckgeliefert werden
                    docopy=true;
                    failedmove=false;
          
                    if ( cowin != NULL ) cowin->newfile( fe->name, newfullname );
          
                    if(m_copyorder->move==true) {
                        if ( fe->isLink == true &&
                             m_copyorder->adjust_relative_symlinks != m_copyorder->COPY_ADJUST_SYMLINK_NEVER ) {
                            // skip trying to rename file for symlinks that possibly need to be adjusted

                            // assume that rename has failed so the source
                            // file is removed if copying was successful
                            failedmove = true;
                        } else if ( worker_rename( fe->fullname, newfullname ) == 0 ) {
                            // success
                            docopy=false;
                        } else {
                            // failure
                            failedmove=true;
                        }
                    }
          
                    if(docopy==true) {
                        // first try to open the file
                        // in case of failure give a requester with choose "Delete"
                        // then try to delete it and retry to open
                        // if all fails, skip
                        useregcopy = false;
                        if ( S_ISREG( fe->mode() ) ) useregcopy = true;
                        else if ( ( fe->isLink == true ) &&
                                  ( m_copyorder->follow_symlinks == true ) &&
                                  ( fe->isCorrupt == false ) &&
                                  ( S_ISREG( fe->dmode() ) ) ) useregcopy = true;
            
                        if ( useregcopy == true ) {
                            reg_copy_erg_t rce = copyfile_reg( fe->fullname,
                                                               newfullname,
                                                               destdir,
                                                               newname,
                                                               ( fe->isLink == true ) ? fe->dmode() : fe->mode(),
                                                               m_copyorder->cowin,
                                                               bytesToCopy );
                            if ( rce == REG_COPY_OK ) {
                                opok = true;
                            } else if ( rce == REG_COPY_SKIP ) {
                                skip = true;
                            } else {
                                // everything else (including ERROR) is cancel
                                m_cancel = true;
                            }
                        } else {
                            // no regular copy
                            if ( e1 == NM_NEWNAME_OVERWRITE ) {
                                // there is already a file but because we cannot overwrite
                                // with special file I will delete it
                                // remember the user already choose to overwrite in getNewName!
                                if ( worker_remove( newfullname ) != 0 ) {
                                    // remove failed => cannot continue
                  
                                    if ( cowin != NULL ) cowin->stoptimer();
                                    myerrstr = strerror( errno );
                                    text = AGUIXUtils::formatStringToString( catalog.getLocale( 516 ), fe->fullname, newfullname, myerrstr );
                                    button = std::string( catalog.getLocale( 225 ) ) + "|" + catalog.getLocale( 8 );
                                    erg = cowin->request( catalog.getLocale( 347 ), text.c_str(), button.c_str() );
                                    if ( erg == 0 ) {
                                        skip = true;
                                    } else {
                                        m_cancel = true;
                                    }
                                    if ( cowin != NULL ) cowin->conttimer();
                                }
                            }
                            //TODO: mc will skip corrupt links when follow_symlinks is used
                            //      I could do this too if I set modeCheck = 0 so it will got to
                            //      the else part
                            if ( ( fe->isLink == true ) &&
                                 ( m_copyorder->follow_symlinks == true ) &&
                                 ( fe->isCorrupt == false ) ) {
                                useLinkDest = true;
                                modeCheck = fe->dmode();
                            } else {
                                useLinkDest = false;
                                modeCheck = fe->mode();
                            }
              
                            if ( ( skip == false ) && ( m_cancel == false ) ) {
                                if ( ( S_ISCHR( modeCheck ) ) ||
#ifdef S_ISSOCK
                                     ( S_ISSOCK( modeCheck ) ) ||
#endif
                                     ( S_ISBLK( modeCheck ) ) ) {
                                    worker_struct_stat statbuf;
                  
                                    if ( useLinkDest == true ) {
                                        erg = worker_stat( fe->fullname, &statbuf );
                                    } else {
                                        erg = worker_lstat( fe->fullname, &statbuf );
                                    }
                                    if ( erg == 0 ) {
                                        // mknod
                                        mode_t m = statbuf.st_mode;
                                        m = getModeWithEnsureOption( m, *m_copyorder );

                                        if ( worker_mknod( newfullname, m, statbuf.st_rdev ) != 0 ) {
                                            if ( cowin != NULL ) cowin->stoptimer();
                                            myerrstr = strerror( errno );
                                            text = AGUIXUtils::formatStringToString( catalog.getLocale( 1090 ), fe->fullname, newfullname, "mknod", myerrstr );
                                            button = std::string( catalog.getLocale( 225 ) ) + "|" + catalog.getLocale( 8 );
                                            erg = cowin->request( catalog.getLocale( 347 ), text.c_str(), button.c_str() );
                                            if ( erg == 0 ) {
                                                skip = true;
                                            } else {
                                                m_cancel = true;
                                            }
                                            if ( cowin != NULL ) cowin->conttimer();
                                        } else opok = true;
                                    } else {
                                        if ( cowin != NULL ) cowin->stoptimer();
                                        myerrstr = strerror( errno );
                                        text = AGUIXUtils::formatStringToString( catalog.getLocale( 518 ), fe->fullname, newfullname, myerrstr );
                                        button = std::string( catalog.getLocale( 225 ) ) + "|" + catalog.getLocale( 8 );
                                        erg = cowin->request( catalog.getLocale( 347 ), text.c_str(), button.c_str() );
                                        if ( erg == 0 ) {
                                            skip = true;
                                        } else {
                                            m_cancel = true;
                                        }
                                        if ( cowin != NULL ) cowin->conttimer();
                                    }
                                } else if ( S_ISFIFO( modeCheck ) ) {
                                    // mkfifo
                                    mode_t m = modeCheck;
                                    m = getModeWithEnsureOption( m, *m_copyorder );

                                    if ( mkfifo( newfullname, m ) != 0 ) {
                                        if ( cowin != NULL ) cowin->stoptimer();
                                        myerrstr = strerror( errno );
                                        text = AGUIXUtils::formatStringToString( catalog.getLocale( 1090 ), fe->fullname, newfullname, "mkfifo", myerrstr );
                                        button = std::string( catalog.getLocale( 225 ) ) + "|" + catalog.getLocale( 8 );
                                        erg = cowin->request( catalog.getLocale( 347 ), text.c_str(), button.c_str() );
                                        if ( erg == 0 ) {
                                            skip = true;
                                        } else {
                                            m_cancel = true;
                                        }
                                        if ( cowin != NULL ) cowin->conttimer();
                                    } else opok = true;
                                } else if ( S_ISLNK( modeCheck ) ) {
                                    // it's a link so create a new symlink which points to the same destination
                                    char *linkdest = fe->getDestination();
                                    if ( linkdest != NULL ) {
                                        if ( linkdest[0] != '/' ) {
                                            bool adjust = false;

                                            if ( m_copyorder->adjust_relative_symlinks == m_copyorder->COPY_ADJUST_SYMLINK_ALWAYS ) {
                                                adjust = true;
                                            } else if ( m_copyorder->adjust_relative_symlinks == m_copyorder->COPY_ADJUST_SYMLINK_OUTSIDE ) {
                                                // relative symlink, try to determine new correct one

                                                // that would be the new target directory of the link
                                                std::string dirname_of_target = NWC::Path::get_real_path( NWC::Path::dirname( newfullname ) );
                                                if ( dirname_of_target.empty() ) dirname_of_target = NWC::Path::dirname( newfullname );
                                                
                                                std::string fullnewlink = NWC::Path::join( dirname_of_target,
                                                                                           linkdest );
                                                fullnewlink = NWC::Path::normalize( fullnewlink );

                                                // fullnewlink is the destination of the symlinks

                                                std::string real_destdir = NWC::Path::get_real_path( m_copyorder->destdir );
                                                if ( real_destdir.empty() ) real_destdir = m_copyorder->destdir;

                                                // check if points outside the copy destination dir
                                                // which means that the real_destdir must be a prefix of the link name
                                                if ( ! NWC::Path::is_prefix_dir( real_destdir,
                                                                                 NWC::Path::dirname( fullnewlink ) ) ) {
                                                    adjust = true;
                                                }
                                            }

                                            if ( adjust ) {
                                                std::string dirname_of_source = NWC::Path::get_real_path( NWC::Path::dirname( fe->fullname ) );
                                                if ( dirname_of_source.empty() ) dirname_of_source = NWC::Path::dirname( fe->fullname );

                                                std::string fulloldlink = NWC::Path::join( dirname_of_source,
                                                                                           linkdest );
                                                fulloldlink = NWC::Path::normalize( fulloldlink );

                                                char *newrellinkdest = Datei::getRelativePathExt( fulloldlink.c_str(),
                                                                                                  NWC::Path::dirname( newfullname ).c_str(),
                                                                                                  true );

                                                if ( newrellinkdest && strlen( newrellinkdest ) > 0 ) {
                                                    free( linkdest );
                                                    linkdest = newrellinkdest;
                                                }
                                            }
                                        }
                                        if ( worker_symlink( linkdest, newfullname ) != 0 ) {
                                            if ( cowin != NULL ) cowin->stoptimer();
                                            myerrstr = strerror( errno );
                                            text = AGUIXUtils::formatStringToString( catalog.getLocale( 1090 ), fe->fullname, newfullname, "symlink", myerrstr );
                                            button = std::string( catalog.getLocale( 225 ) ) + "|" + catalog.getLocale( 8 );
                                            erg = cowin->request( catalog.getLocale( 347 ), text.c_str(), button.c_str() );
                                            if ( erg == 0 ) {
                                                skip = true;
                                            } else {
                                                m_cancel = true;
                                            }
                                            if ( cowin != NULL ) cowin->conttimer();
                                        } else opok = true;
                                        _freesafe( linkdest );
                                    } else {
                                        if ( cowin != NULL ) cowin->stoptimer();
                                        myerrstr = strerror( errno );
                                        text = AGUIXUtils::formatStringToString( catalog.getLocale( 519 ), fe->fullname, newfullname, myerrstr );
                                        button = std::string( catalog.getLocale( 225 ) ) + "|" + catalog.getLocale( 8 );
                                        erg = cowin->request( catalog.getLocale( 347 ), text.c_str(), button.c_str() );
                                        if ( erg == 0 ) {
                                            skip = true;
                                        } else {
                                            m_cancel = true;
                                        }
                                        if ( cowin != NULL ) cowin->conttimer();
                                    }
                                } else {
                                    if ( cowin != NULL ) cowin->stoptimer();
                                    myerrstr = strerror( errno );
                                    text = AGUIXUtils::formatStringToString( catalog.getLocale( 520 ), fe->fullname );
                                    button = std::string( catalog.getLocale( 225 ) ) + "|" + catalog.getLocale( 8 );
                                    erg = cowin->request( catalog.getLocale( 347 ), text.c_str(), button.c_str() );
                                    if ( erg == 0 ) {
                                        skip = true;
                                    } else {
                                        m_cancel = true;
                                    }
                                    if ( cowin != NULL ) cowin->conttimer();
                                }
                            }
                            //TODO: muss hier nicht eher add_curbytes_copied aufgerufen werden?
                            //            if(cowin!=NULL) cowin->set_bytes_to_copy_curfile(fe->size);
                            if ( cowin != NULL ) cowin->add_curbytes_copied( fe->size() );
                        }
                    }
                } else {
                    text = AGUIXUtils::formatStringToString( catalog.getLocale( 113 ), fe->fullname );
                    button = catalog.getLocale( 11 );
          
                    cowin->request( catalog.getLocale( 347 ), text.c_str(), button.c_str() );
                }
            } else skip=true;
      
            if ( opok == true ) {
                // try to change the mode because open modify it with umask
                // it doesn't matter if this fails 
                if(m_copyorder->preserve_attr==true) {
                    //TODO:currently deactivated
                    //     chown is necessary because link can have an owner
                    //     but chmod could be optional
                    // only apply new mode and time if it's not a link
                    // doing this would not do anything wrong
                    // but chown/chmod/utime would update the access/change time
                    // of the destination file although we don't have to touch it
                    //if ( ( fe->isLink == false ) || ( copyorder->follow_symlinks == true ) ) {
                    mode_t m = ( ( fe->isLink == true ) && ( fe->isCorrupt == false ) ) ? fe->dmode() : fe->mode();
                    m = getModeWithEnsureOption( m, *m_copyorder );

                    tryApplyOwnerPerm( newfullname,
                                       ( ( fe->isLink == true ) && ( fe->isCorrupt == false ) ) ? fe->duserid() : fe->userid(),
                                       ( ( fe->isLink == true ) && ( fe->isCorrupt == false ) ) ? fe->dgroupid() : fe->groupid(),
                                       m );
                    utb.actime = ( ( fe->isLink == true ) && ( fe->isCorrupt == false ) ) ? fe->dlastaccess() : fe->lastaccess();
                    utb.modtime = ( ( fe->isLink == true ) && ( fe->isCorrupt == false ) ) ? fe->dlastmod() : fe->lastmod();
                    worker_utime(newfullname,&utb);
                    //}
                }
            }

            if ( effective_target_fullname != NULL ) {
                *effective_target_fullname = newfullname;
            }
      
            _freesafe(newfullname);
        } else {
            switch(e1) {
                case NM_NEWNAME_CANCEL:
                    m_cancel=true;
                case NM_NEWNAME_SKIP:
                    skip = true;
                    user_skip = true;
                default:
                    skip=true;
                    break;
            }
        }
    }

    if(cowin!=NULL) {
        if ( skip == true ) {
            // the CopyOpWin need to substract bytesToCopy from global bytes sum
            // and already copied bytes from copyied bytes sum
            // if we called newfile() (which resets this counter)
            cowin->file_skipped( bytesToCopy, true );
        } else {
            cowin->file_finished();
        }
    }
    if(newname!=NULL) _freesafe(newname);
    if(m_cancel==true) {
        m_incomplete_entries.push_back( fe->fullname );

        return NM_COPY_CANCEL;
    } else if(skip==true) {
        if ( ! user_skip ) {
            m_incomplete_entries.push_back( fe->fullname );
        }

        return NM_COPY_SKIP;
    }
    if(m_copyorder->move==true) {
        if((opok==true)&&(failedmove==true)) {
            // remember: failedmove is true when OS function rename failed
            //           in this case we have to copy the file and delete the source
            //           so add this entry only when copy is complete (opok) AND
            //           rename failed (failedmove)
            // put fe in list
            pushForPostDelete( fe );
            return NM_COPY_NEED_DELETE;
        }
    }
    return NM_COPY_OK;
}

CopyCore::reg_copy_erg_t CopyCore::copyfile_reg( const char *sourcename,
                                                 const char *destname,
                                                 const char *destdirname,
                                                 const char *destbasename,
                                                 mode_t create_mode,
                                                 CopyOpWin *cowin,
                                                 loff_t bytesToCopy )
{
    int fdw, fdr;
    std::string text, button;
    ssize_t readbytes;
    reg_copy_erg_t return_value = REG_COPY_ERROR;
    int erg;
    const char *myerrstr;

    create_mode = getModeWithEnsureOption( create_mode, *m_copyorder );

    // firstly open source file
    fdr = worker_open( sourcename, O_RDONLY, 0 );
    if ( fdr == -1 ) {
        // can't open inputfile
        if ( cowin != NULL ) cowin->stoptimer();
    
        text = AGUIXUtils::formatStringToString( catalog.getLocale( 281 ), sourcename );
        button = std::string( catalog.getLocale( 225 ) ) + "|" + catalog.getLocale( 8 );
        erg = cowin->request( catalog.getLocale( 347 ), text.c_str(), button.c_str() );
    
        if ( erg == 1 ) return_value = REG_COPY_CANCEL;
        else return_value = REG_COPY_SKIP;
    
        if ( cowin != NULL ) cowin->conttimer();
        return return_value;
    }

    if ( cowin != NULL ) {
        cowin->set_bytes_to_copy_curfile( bytesToCopy );
    }
  
    // secondly try to open destination file
    // two runs as I first try to open an existing file and only remove it if it doesn't
    // work. This is because we can overwrite a file but not create it
    for ( int i = 0; i < 2; i++ ) {
        // fe is a regular file or a working symlink to a regular file so we can use dmode() without checking
        // for corrupt link
        fdw = worker_open( destname, O_CREAT | O_TRUNC | O_WRONLY, create_mode );

        if ( fdw != -1 ) break;

        // open failed
        if ( cowin != NULL ) cowin->stoptimer();
        if ( errno == ENOSPC ) {
            text = AGUIXUtils::formatStringToString( catalog.getLocale( 545 ), destbasename, destdirname );
            button = std::string( catalog.getLocale( 225 ) ) + "|" + catalog.getLocale( 41 ) + "|" + catalog.getLocale( 8 );
            erg = cowin->request( catalog.getLocale( 347 ), text.c_str(), button.c_str() );

            if ( erg == 2 ) {
                return_value = REG_COPY_CANCEL;
            } else if ( erg == 1 ) {
                // just try again
                i--;
            } else return_value = REG_COPY_SKIP;
        } else if ( errno == EACCES ) {
            if ( Datei::lfileExistsExt( destname ) != Datei::D_FE_NOFILE ) {
                /// error
                // give request
                // textstr="Can't overwrite the file|Shall I try to delete it?";
                // buttonstr="Delete file|Skip|Cancel";
	
                text = AGUIXUtils::formatStringToString( catalog.getLocale( 282 ), destname );
                button = std::string( catalog.getLocale( 277 ) ) +
                    "|" +
                    catalog.getLocale( 225 ) + 
                    "|" +
                    catalog.getLocale( 8 );
                erg = cowin->request( catalog.getLocale( 123 ), text.c_str(), button.c_str() );

                if ( erg == 1 ) return_value = REG_COPY_SKIP;
                else if ( erg == 2 ) return_value = REG_COPY_CANCEL;
                else {
                    if ( worker_remove( destname ) != 0 ) {
                        // textstr="Failed to remove this file|I will skip this file!";
                        // buttonstr="Ok|Cancel";
	    
                        text = AGUIXUtils::formatStringToString( catalog.getLocale( 138 ), destname );
                        button = std::string( catalog.getLocale( 11 ) ) + "|" + catalog.getLocale( 8 );
                        erg = cowin->request( catalog.getLocale( 347 ), text.c_str(), button.c_str() );

                        if ( erg == 1 ) return_value = REG_COPY_CANCEL;
                        else return_value = REG_COPY_SKIP;
                    }
                }
            } else {
                // "Can't open dest file"
                text = AGUIXUtils::formatStringToString( catalog.getLocale( 198 ), destname );
                button = std::string( catalog.getLocale( 225 ) ) + "|" + catalog.getLocale( 8 );
                erg = cowin->request( catalog.getLocale( 347 ), text.c_str(), button.c_str() );

                if ( erg == 1 ) return_value = REG_COPY_CANCEL;
                else return_value = REG_COPY_SKIP;
            }
        } else {
            text = AGUIXUtils::formatStringToString( catalog.getLocale( 198 ), destname );
            button = std::string( catalog.getLocale( 225 ) ) + "|" + catalog.getLocale( 8 );
            erg = cowin->request( catalog.getLocale( 347 ), text.c_str(), button.c_str() );

            if ( erg == 1 ) return_value = REG_COPY_CANCEL;
            else return_value = REG_COPY_SKIP;
        }
        if ( cowin != NULL ) cowin->conttimer();

        if ( ( return_value == REG_COPY_SKIP ) || ( return_value == REG_COPY_CANCEL ) ) break;
    }

    if ( fdw == -1 ) {
        worker_close( fdr );
        return return_value;
    }

    loff_t total_bytes = 0;
    bool asked_about_source_change = false;

    // now copy from fdr to fdw
    do {
        readbytes = worker_read( fdr, buf, BUFSIZE );
        if ( readbytes < 0 ) {
            // error while reading
      
            if ( cowin != NULL ) cowin->stoptimer();
      
            myerrstr = strerror( errno );
            text = AGUIXUtils::formatStringToString( catalog.getLocale( 525 ), sourcename, myerrstr );
            button = std::string( catalog.getLocale( 225 ) ) + "|" + catalog.getLocale( 8 );
            erg = cowin->request( catalog.getLocale( 347 ), text.c_str(), button.c_str() );

            if ( cowin != NULL ) cowin->conttimer();

            if ( erg == 0 ) return_value = REG_COPY_SKIP;
            else return_value = REG_COPY_CANCEL;
            break;
        } else if ( readbytes == 0 ) {
            // end of file
            break;
        }

        ssize_t bytes_written = 0;

        for ( ; bytes_written < readbytes; ) {
            const ssize_t bytes_left_to_write = readbytes - bytes_written;
            ssize_t writebytes;

            writebytes = worker_write( fdw, ((const char *)buf) + bytes_written, bytes_left_to_write );

            if ( writebytes > 0 ) {
                bytes_written += writebytes;
            }

            if ( writebytes > 0 && cowin != NULL ) {
                cowin->add_curbytes_copied( writebytes );
            }

            checkAskToCancel();

            if ( m_cancel ) {
                return_value = REG_COPY_CANCEL;
            }

            if ( writebytes != bytes_left_to_write ) {
                // something went wrong
                // let the user choose to cancel or skip this file
                // Ask to delete the incomplete destfile
                // Attention: This method also moves files so be sure to
                //   NOT delete the source!
                if( ( ( writebytes >= 0 ) && ( writebytes < bytes_left_to_write ) ) ||
                    ( errno == ENOSPC ) ) {
                    // ENOSPC isn't always reported so assume ENOSPC
                    text = AGUIXUtils::formatStringToString( catalog.getLocale( 545 ), destbasename, destdirname );
                    button = std::string( catalog.getLocale( 225 ) ) + "|" + catalog.getLocale( 41 ) + "|" + catalog.getLocale( 8 );
                    erg = cowin->request( catalog.getLocale( 347 ), text.c_str(), button.c_str() );
	
                    if ( erg == 0 ) {
                        return_value = REG_COPY_SKIP;
                    } else if ( erg == 1 ) {
                        // try again
                    } else {
                        return_value = REG_COPY_CANCEL;
                    }
                } else {
                    if ( writebytes < 0 ) {
                        myerrstr = strerror( errno );
                        text = AGUIXUtils::formatStringToString( catalog.getLocale( 526 ), destname, myerrstr );
                    } else {
                        text = AGUIXUtils::formatStringToString( catalog.getLocale( 359 ), destname );
                    }
                    button = std::string( catalog.getLocale( 225 ) ) + "|" + catalog.getLocale( 8 );
                    erg = cowin->request( catalog.getLocale( 347 ), text.c_str(), button.c_str() );
	
                    if ( erg == 0 ) return_value = REG_COPY_SKIP;
                    else return_value = REG_COPY_CANCEL;
                }
            }

            if ( return_value == REG_COPY_CANCEL || return_value == REG_COPY_SKIP ) break;
        }

        total_bytes += bytes_written;

        if ( total_bytes > bytesToCopy ) {
            // more bytes read than initially known
            // probably the source file has changed
            // ask what to do

            if ( ! asked_about_source_change ) {

                text = AGUIXUtils::formatStringToString( catalog.getLocale( 1042 ), sourcename );
                button = std::string( catalog.getLocale( 629 ) ) + "|" + catalog.getLocale( 225 ) + "|" + catalog.getLocale( 8 );
                erg = cowin->request( catalog.getLocale( 347 ), text.c_str(), button.c_str() );
	
                if ( erg == 0 ) {
                } else if ( erg == 1 ) {
                    return_value = REG_COPY_SKIP;
                } else {
                    return_value = REG_COPY_CANCEL;
                }

                asked_about_source_change = true;
            }
        }

    } while ( ( return_value != REG_COPY_CANCEL ) && ( return_value != REG_COPY_SKIP ) );

    cowin->update_file_status();

    if ( m_cancel ) {
        return_value = REG_COPY_CANCEL;
    }

    worker_close( fdr );
    if ( worker_close( fdw ) != 0 ) {
        // oops, close error
        myerrstr = strerror( errno );
        text = AGUIXUtils::formatStringToString( catalog.getLocale( 526 ), destname, myerrstr );
        button = std::string( catalog.getLocale( 225 ) ) + "|" + catalog.getLocale( 8 );
        erg = cowin->request( catalog.getLocale( 347 ), text.c_str(), button.c_str() );
    
        if ( erg == 0 ) return_value = REG_COPY_SKIP;
        else return_value = REG_COPY_CANCEL;
    }
  
    if ( ( return_value == REG_COPY_CANCEL ) || ( return_value == REG_COPY_SKIP ) ) {
        // error while copying!
        // ask to remove the incomplete destination file
    
        if ( cowin != NULL ) cowin->stoptimer();
    
        text = AGUIXUtils::formatStringToString( catalog.getLocale( 393 ), destname );
        button = std::string( catalog.getLocale( 394 ) ) + "|" + catalog.getLocale( 395 );
        erg = cowin->request( catalog.getLocale( 123 ), text.c_str(), button.c_str() );
        if ( erg == 1 ) {
            if ( worker_unlink( destname ) != 0 ) {
                // textstr="Failed to remove the destination file|This file is probably incomplete!";
                text = AGUIXUtils::formatStringToString( catalog.getLocale( 139 ), destname );
                button = catalog.getLocale( 11 );
                erg = cowin->request( catalog.getLocale( 347 ), text.c_str(), button.c_str() );
            }
        }
        if ( cowin != NULL ) cowin->conttimer();
    } else {
        // copy complete
        return_value = REG_COPY_OK;
    }
    return return_value;
}

int CopyCore::deleteCopiedButNotMoved( std::function< void( const std::pair< std::string, bool > & ) > processed_callback )
{
    // now delete remaining files/dirs when moving
    if ( m_copyorder->move == true ) {
        bool delcancel; // additional cancel so we don't need to
        // clear cancel for the special case when move
        // has to copy&delete and was aborted somewhere
        // I ask the user what to do with the correctly copied
        // files
        std::string str1;
  
        if ( m_copyorder->cowin != NULL ) m_copyorder->cowin->setmessage( "", 1 );

        auto itfe1 = m_copy_deletefe_list.begin();

        delcancel = false;

        if ( m_cancel == true && itfe1 != m_copy_deletefe_list.end() ) {
            // copy was canceled
            // ask the user what to do with the correctly moved files
            // (exactly what to do with the sources!!)
            str1 = catalog.getLocale( 11 );
            str1 += "|";
            str1 += catalog.getLocale( 8 );
            int erg = m_copyorder->cowin->request( catalog.getLocale( 123 ),
                                                   catalog.getLocale( 528 ),
                                                   str1.c_str() );
            if ( erg != 0 ) {
                delcancel = true;
            }
        }
    
        while ( itfe1 != m_copy_deletefe_list.end() && delcancel == false ) {
            if ( itfe1->second == true ) {
                if ( m_copyorder->cowin != NULL ) {
                    char *tstr = (char*)_allocsafe( strlen( catalog.getLocale( 141 ) ) +
                                                    strlen( itfe1->first.c_str() ) + 1 );
                    sprintf( tstr, catalog.getLocale( 141 ), itfe1->first.c_str() );
                    m_copyorder->cowin->setmessage( tstr, 0 );
                    _freesafe( tstr );
                }
            }

            int erg;

            // remove tfe
            if ( itfe1->second == true ) {
                erg = worker_rmdir( itfe1->first.c_str() );
            } else {
                erg = worker_unlink( itfe1->first.c_str() );
            }

            if ( erg == 0 ) {
                // success
                processed_callback( *itfe1 );
            } else {
                char *textstr = (char*)_allocsafe( strlen( catalog.getLocale( 291 ) ) +
                                                   strlen( itfe1->first.c_str() ) + 1 );
                sprintf( textstr, catalog.getLocale( 291 ), itfe1->first.c_str() );
                char *buttonstr = (char*)_allocsafe( strlen( catalog.getLocale( 11 ) ) + 1 +
                                                     strlen( catalog.getLocale( 8 ) ) + 1 );
                sprintf( buttonstr, "%s|%s", catalog.getLocale( 11 ),
                         catalog.getLocale( 8 ) );
                erg = m_copyorder->cowin->request( catalog.getLocale( 347 ), textstr, buttonstr );
                _freesafe( buttonstr );
                _freesafe( textstr );
                if ( erg == 1 ) delcancel = true;
            }
            if ( itfe1 != m_copy_deletefe_list.end() ) itfe1++;
        }
        if ( delcancel == true ) m_cancel = true;
    }

    return 0;
}

bool CopyCore::getCancel() const
{
    return m_cancel;
}

void CopyCore::setCancel( bool nv )
{
    // disallow clearing the flag
    if ( nv == true ) {
        m_cancel = nv;
    }
}

void CopyCore::pushForPostDelete( const FileEntry *fe )
{
    if ( fe->isDir() == true && fe->isLink == false ) {
        m_copy_deletefe_list.push_back( std::make_pair( fe->fullname, true ) );
    } else {
        m_copy_deletefe_list.push_back( std::make_pair( fe->fullname, false ) );
    }
}

void CopyCore::registerFEToCopy( const FileEntry &fe,
                                 int row,
                                 const std::vector< NWC::OS::deep_dir_info > &file_base_segments )
{
    m_copy_list.push_back( copy_base_entry( fe, row, file_base_segments ) );
}

CopyCore::copy_base_entry::copy_base_entry( const FileEntry &fe,
                                            int row,
                                            const std::vector< NWC::OS::deep_dir_info > &file_base_segments ) :
    m_row( row ),
    m_cod( NULL ),
    m_fe( fe ),
    m_file_base_segments( file_base_segments )
{
}

CopyCore::copy_base_entry::~copy_base_entry()
{
    delete m_cod;
}

int CopyCore::buildCopyDatabase()
{
    unsigned long files = 0,
        dirs = 0,
        gf = 0,
        gd = 0;
    loff_t bytes = 0;

    CopyOpWin *cowin = m_copyorder->cowin;

    cowin->starttimer();

    if ( cowin->redraw() & 1 ) m_cancel = true;

    // create the NM_CopyOp_Dir for each dir in copylist
  
    for ( auto &cbe : m_copy_list ) {
        if ( m_cancel == true ) break;

        bool enter = false;

        if ( cbe.entry().isDir() == true ) {
            // fe is a dir, check if it is a link and take it only when follow_symlinks==true
            // entry is a dir so it cannot be a corrupt link so no need to check
            if ( cbe.entry().isLink == false ) enter = true;
            else if ( m_copyorder->follow_symlinks == true ) enter = true;
        }

        if ( enter == true ) {
            // fe is a dir so creating corresponding entry
            NM_CopyOp_Dir *cod1 = new NM_CopyOp_Dir( &cbe.entry() );

            if ( cod1->user_abort == false ) {
                // recursive call
                if ( cod1->createSubDirs( m_copyorder, &gf, &gd ) != 0 ) {
                    m_cancel = true;
                }
            } else {
                m_cancel = true;
            }

            // add the values from this subdir to this dir
            files += cod1->files;
            dirs += cod1->dirs;
            bytes += cod1->bytes;

            cbe.m_cod = cod1;
            // this is a dir so inc the counter
            dirs++;
            gd++;

            cowin->set_files_to_copy( gf );
            cowin->set_dirs_to_copy( gd );
            if ( cowin->redraw() & 1 ) m_cancel = true;
        } else {
            // is not dir (mostly a file but can also be links ...)
            files++;
            gf++;

            if ( cbe.entry().isLink == true &&
                 m_copyorder->follow_symlinks == true &&
                 cbe.entry().isCorrupt == false ) {
                bytes += cbe.entry().dsize();
            } else {
                bytes += cbe.entry().size();
            }
            cbe.m_cod = NULL;
        }
    }

    cowin->set_files_to_copy( files );
    cowin->set_dirs_to_copy( dirs );
    cowin->set_bytes_to_copy( bytes );

    m_bytes_to_copy = bytes;

    // reset start timer for better time predictions
    cowin->starttimer();

    return 0;
}

int CopyCore::executeCopy()
{
    m_copy_thread = std::thread( [this]() { executeCopyThread(); } );

    return 0;
}

int CopyCore::prepare_destination_dir( copy_base_entry &cbe,
                                       std::string &destdir,
                                       bool post_run )
{
    destdir = m_copyorder->destdir;
    int res = 0;

    if ( m_copyorder->vdir_preserve_dir_structure &&
         ! cbe.get_file_base_segments().empty() ) {

        if ( post_run == false ||
             m_copyorder->preserve_attr == true ) {
            res = NWC::OS::make_dirs( destdir,
                                      cbe.get_file_base_segments(),
                                      post_run );

            if ( res != 0 ) {
                if ( m_copyorder->cowin != NULL ) m_copyorder->cowin->stoptimer();
                auto text = AGUIXUtils::formatStringToString( catalog.getLocale( 1210 ), destdir.c_str() );
                auto button = std::string( catalog.getLocale( 225 ) ) + "|" + catalog.getLocale( 8 );
                auto erg = m_copyorder->cowin->request( catalog.getLocale( 347 ), text.c_str(), button.c_str() );
                if ( erg == 0 ) {
                } else {
                    m_cancel = true;
                }
                if ( m_copyorder->cowin != NULL ) m_copyorder->cowin->conttimer();
            }
        }

        for ( auto &s : cbe.get_file_base_segments() ) {
            destdir = NWC::Path::join( destdir, s.get_segment_name() );
        }
    }

    return res;
}

int CopyCore::executeCopyThread()
{
    for ( auto &cbe : m_copy_list ) {
        checkAskToCancel();

        if ( m_cancel ) break;

        NM_CopyOp_Dir *tcod;

        tcod = cbe.m_cod;

        if ( m_pre_cb ) {
            loff_t size = 0;

            if ( cbe.entry().isLink && ! cbe.entry().isCorrupt ) {
                size = cbe.entry().dsize();
            } else {
                size = cbe.entry().size();
            }

            m_pre_cb( size, cbe.m_row, cbe.m_cod != NULL ? true : false );
        }

        if ( tcod != NULL ) {
            nm_copy_t ce1 = NM_COPY_OK;
            std::string target_fullname;
            std::string destdir;

            if ( ! tcod->ok ) {
                CopyOpWin *cowin = m_copyorder->cowin;
                if ( cowin ) {
                    cowin->stoptimer();
                    auto textstr = AGUIXUtils::formatStringToString( catalog.getLocale( 1456 ),
                                                                     cbe.entry().fullname );
                    std::string buttonstr = catalog.getLocale( 629 );
                    buttonstr += "|";
                    buttonstr += catalog.getLocale( 225 );
                    buttonstr += "|";
                    buttonstr += catalog.getLocale( 8 );
                    int erg = m_copyorder->cowin->request( catalog.getLocale( 347 ),
                                                           textstr,
                                                           buttonstr );
                    if ( erg == 2 ) {
                        m_cancel=true;
                        ce1 = NM_COPY_CANCEL;
                    } else if ( erg == 1 ) {
                        ce1 = NM_COPY_SKIP;
                    }

                    cowin->conttimer();
                } else {
                    ce1 = NM_COPY_SKIP;
                }

                std::string d = cbe.entry().fullname;
                d += "/";
                m_incomplete_entries.push_back( d );
            }

            if ( ce1 == NM_COPY_OK &&
                 prepare_destination_dir( cbe, destdir, false ) == 0 ) {
                ce1 = copydir( &cbe.entry(), tcod, true, destdir.c_str(), &target_fullname, true );
                if ( ce1 == NM_COPY_OK || ce1 == NM_COPY_NEED_DELETE ) {
                    // success
                    Worker::pushCommandLog( m_copyorder->move ? catalog.getLocale( 1420 ) : catalog.getLocale( 1419 ),
                                            target_fullname,
                                            cbe.entry().fullname,
                                            AGUIXUtils::formatStringToString( catalog.getLocale( 1421 ),
                                                                              cbe.entry().fullname,
                                                                              destdir.c_str() ) );
                } else if ( ce1 == NM_COPY_CANCEL ) {
                    m_cancel = true;
                }

                prepare_destination_dir( cbe, destdir, true );
            } else {
                if ( m_cancel ) {
                    ce1 = NM_COPY_CANCEL;
                } else {
                    ce1 = NM_COPY_SKIP;
                }
            }

            if ( m_post_cb ) {
                m_post_cb( cbe.entry().fullname, cbe.m_row,
                           ce1,
                           cbe.m_cod != NULL ? true : false,
                           cbe.m_cod != NULL ? cbe.m_cod->error_counter : 0,
                           target_fullname );
            }
        } else {
            nm_copy_t ce1;
            std::string target_fullname;
            std::string destdir;

            if ( prepare_destination_dir( cbe, destdir, false ) == 0 ) {
                ce1 = copyfile( &cbe.entry(), true, destdir.c_str(), &target_fullname );

                if ( ce1 == NM_COPY_OK || ce1 == NM_COPY_NEED_DELETE ) {
                    // success
                    Worker::pushCommandLog( m_copyorder->move ? catalog.getLocale( 1420 ) : catalog.getLocale( 1419 ),
                                            target_fullname,
                                            cbe.entry().fullname,
                                            AGUIXUtils::formatStringToString( catalog.getLocale( 1421 ),
                                                                              cbe.entry().fullname,
                                                                              destdir.c_str() ) );
                } else if ( ce1 == NM_COPY_CANCEL ) {
                    m_cancel = true;
                }

                prepare_destination_dir( cbe, destdir, true );
            } else {
                m_incomplete_entries.push_back( cbe.entry().fullname );
                if ( m_cancel ) {
                    ce1 = NM_COPY_CANCEL;
                } else {
                    ce1 = NM_COPY_SKIP;
                }
            }

            if ( m_post_cb ) {
                m_post_cb( cbe.entry().fullname, cbe.m_row,
                           ce1,
                           cbe.m_cod != NULL ? true : false,
                           cbe.m_cod != NULL ? cbe.m_cod->error_counter : 0,
                           target_fullname );
            }
        }
    }

    setFinished();

    return 0;
}

void CopyCore::setPreCopyCallback( std::function< void( loff_t size, int row, bool is_dir ) > cb )
{
    m_pre_cb = cb;
}

void CopyCore::setPostCopyCallback( std::function< void( const std::string &source_fullname, int row,
                                                         nm_copy_t res,
                                                         bool is_dir, unsigned long dir_error_counter,
                                                         const std::string &target_fullname ) > cb )
{
    m_post_cb = cb;
}

loff_t CopyCore::getBytesToCopy() const
{
    return m_bytes_to_copy;
}

/*
 * tryApplyOwnerPerm will try to set owner and permission
 *   it will apply SUID/SGID only if owner or root (as root only
 *   if chown don't fail)
 * this function don't care if chmod/chown fails
 *
 * returnvalue:
 *   0 okay
 *   -1 wrong args
 *   Bit 0 set: SUID cleared
 *   Bit 1 set: SGID cleared
 *   Bit 2 set: chown failed
 *   Bit 3 set: chmod failed
 */
int CopyCore::tryApplyOwnerPerm( const char *filename,
                                 const uid_t wanted_user,
                                 const gid_t wanted_group,
                                 const mode_t wanted_mode )
{
    mode_t use_mode;
    int erg = 0, chownres, chmodres;

    if ( filename == NULL ) return -1;
  
    chownres = worker_chown( filename, wanted_user, wanted_group );
    if ( chownres != 0 ) {
        erg |= 1<<2;
    }
    use_mode = wanted_mode;
    if ( ( use_mode & S_ISUID ) != 0 ) {
        // SUID bit set, check if we still want it
        // root will apply it (when chown didn't failed and)
        // and euid/egid == ower
        if ( ! ( ( geteuid() == wanted_user ) ||
                 ( ( geteuid() == 0 ) && ( chownres == 0 ) ) ) ) {
            use_mode &= ~S_ISUID;
            erg |= 1<<0;
        }
    }
    if ( ( use_mode & S_ISGID ) != 0 ) {
        // SGID bit set, check if we still want it
        // root will apply it (when chown didn't failed and)
        // and euid/egid == ower
        if ( ! ( ( getegid() == wanted_group ) ||
                 ( ( geteuid() == 0 ) && ( chownres == 0 ) ) ) ) {
            use_mode &= ~S_ISGID;
            erg |= 1<<1;
        }
    }
    chmodres = chmod ( filename, use_mode );
    if ( chmodres != 0 ) {
        erg |= 1<<3;
    }
    if ( ( ( erg & 3 ) != 0 ) && ( m_copyorder != NULL ) ) {
        // removed SUID/SGUID bit, let the user know this
        if ( m_copyorder->ignoreLosedAttr != true ) {
            int choose;
            char *textstr;
            std::string str2;

            textstr = (char*)_allocsafe( strlen( catalog.getLocale( 536 ) ) + strlen( filename ) + 1 );
            sprintf( textstr, catalog.getLocale( 536 ), filename );
            str2 = catalog.getLocale( 11 );
            str2 += "|";
            str2 += catalog.getLocale( 537 );
            choose = m_copyorder->cowin->request( catalog.getLocale( 125 ),
                                                  textstr,
                                                  str2.c_str(),
                                                  Requester::REQUEST_CANCELWITHLEFT );
            _freesafe( textstr );
            if ( choose == 1 ) {
                m_copyorder->ignoreLosedAttr = true;
            }
        }
    }
    return erg;
}

std::shared_ptr< struct copyorder > CopyCore::getCopyOrder()
{
    return m_copyorder;
}

bool CopyCore::finished()
{
    std::unique_lock< std::mutex> l( m_finished_mutex );

    return m_finished;
}

void CopyCore::join()
{
    m_copy_thread.join();
}

void CopyCore::checkAskToCancel()
{
    if ( m_ask_to_cancel ) {
        m_ask_to_cancel = false;

        std::string str1;
        int erg;
    
        // user canceled -> request to be sure
        str1 = catalog.getLocale( 11 );
        str1 += "|";
        str1 += catalog.getLocale( 544 );
        erg = m_copyorder->cowin->request( catalog.getLocale( 123 ),
                                           catalog.getLocale( 543 ),
                                           str1.c_str() );
        if ( erg == 0 ) {
            m_cancel = true;
        }
    }
}

void CopyCore::setAskToCancel()
{
    m_ask_to_cancel = true;
}

bool CopyCore::timed_wait_for_finished()
{
    std::unique_lock< std::mutex> l( m_finished_mutex );

    if ( ! m_finished ) {
        m_finished_cond.wait_for( l,
                                  std::chrono::duration<long long, std::milli>( 1000 / 25 ) );
    }

    return m_finished;
}

void CopyCore::setFinished()
{
    std::unique_lock< std::mutex> l( m_finished_mutex );

    m_finished = true;

    m_finished_cond.notify_all();
}

const std::vector< std::string > &CopyCore::incomplete_entries() const
{
    return m_incomplete_entries;
}
