Synchronize or merge two folders

I haven't tested this extensively, caveat emptor!

Files in destination folder are overwritten by newer versions in source folder.

This could potentially be used for client-based synchronization of procedure files over a network.

// this is not a complete synchronization, files are not deleted from the
// destination folder if they don't exist in the source folder. So more
// of a sync-merge.
function SyncTest(int bothways)
    NewPath/Q/O/M="Select Source Folder" pathSource
    PathInfo pathSource
    string source = s_path
    NewPath/Q/O/M="Select Destination Folder" pathDestination
    PathInfo pathDestination
    string destination = s_path
   
    string strOverwrites = SyncFolder(source, destination, test=1, bothways=bothways)
    // check that we don't somehow what to move a file in both directions
    if (CheckForError(source, destination, strOverwrites))
        doAlert 0, "Unexpected sync error, quitting without copying anything"
        return 0
    endif
    int numOverwrites = ItemsInList(strOverwrites)
    if (numOverwrites)
        DoAlert 1, num2str(numOverwrites) + " files will be overwritten.\r\rContinue?"
        if (v_flag==2)
            return 0
        endif
    endif
    SyncFolder(source, destination, bothways=bothways)
end

// Merges folders like copyFolder on Windows
// Files in destination folder are overwritten by more recently modified
// files from source
// Setting test=1 doesn't copy anything but generates a list of files
// that would be overwritten
// ignore is a string list of names of files that should not be copied.
// set bothways=1 for bidirectional synchonization
// Recommended to run with test=1 to check for problems before trying to sync!
function/S SyncFolder(source, destination, [backupPathStr, test, ignore, bothways])
    string source, destination
    int test
    string backupPathStr // must be no more than one sublevel below an existing folder
    string ignore // list of filenames that won't be copied
    int bothways

    test = ParamIsDefault(test) ? 0 : test
    backupPathStr = SelectString(ParamIsDefault(backupPathStr), backupPathStr, "")
    ignore = SelectString(ParamIsDefault(ignore), ignore, "")
    int backup = (strlen(backupPathStr) > 0)
    bothways = ParamIsDefault(bothways) ? 0 : bothways

    // clean up paths
    source = ParseFilePath(2, source, ":", 0, 0)
    destination = ParseFilePath(2, destination, ":", 0, 0)
    if (backup)
        backupPathStr = ParseFilePath(2, backupPathStr, ":", 0, 0)
    endif

    // check that source and destination folders exist
    GetFileFolderInfo/Q/Z source
    int sourceOK = V_isFolder
    GetFileFolderInfo/Q/Z destination
    if (sourceOK==0 || V_isFolder==0)
        return ""
    endif

    int folderIndex, fileIndex, subfolderIndex
    int folderCount = 1, sublevels = 0
    string folderList, fileList, fileName
    string copiedFileList = "", subPathStr = ""
    string destFolderStr = "", sourceFolderStr = "", backupFolderStr = ""
    variable sourceMod
    int filefound

    Make/free/T/N=0 w_folders, w_subfolders
    w_folders = {source}
    do
        // step through folders at current sublevel
        for (folderIndex=0;folderIndex<numpnts(w_folders);folderIndex+=1)
            // figure out destination folder to match current source folder
            sourceFolderStr = w_folders[folderIndex]
            subPathStr = (sourceFolderStr)[strlen(source),strlen(sourceFolderStr)-1]
            destFolderStr = destination + subPathStr
            backupFolderStr = backupPathStr + subPathStr

            // make sure that folder exists at destination
            if (test == 0)
                NewPath/C/O/Q/Z tempPathSync, destFolderStr
                if (backup)
                    NewPath/C/O/Q/Z tempPathSync, backupFolderStr
                endif
            endif

            // get list of source files in indexth folder at current sublevel
            NewPath/O/Q/Z tempPathSync, sourceFolderStr
            fileList = IndexedFile(tempPathSync, -1, "????")
            // remove files from list if they match an entry in ignorefileList
            fileList = RemoveFromListWC(fileList, ignore)
            // copy files
            for (fileIndex=0;fileIndex<ItemsInList(fileList);fileIndex+=1)
                fileName = StringFromList(fileIndex, fileList)
                GetFileFolderInfo/Q/Z sourceFolderStr + fileName
                sourceMod = V_modificationDate // store source file modification date

                GetFileFolderInfo/Q/Z destFolderStr + fileName
                filefound = (v_flag == 0) && v_isFile
                // allow for 10s difference in modification time in case
                // different file systems don't match modification times
                // precisely.
                // this may have to be rethought if problems arise.
                sourceMod = filefound ? V_modificationDate < (sourceMod-10) : 1
                // sourceMod = 1 for file to be copied

                if (test)
                    if (filefound && sourceMod) // file is to be overwritten
                        copiedFileList = AddListItem(destFolderStr + fileName, copiedFileList)
                    endif
                else
                    if (backup) // back up any files that are to be overwritten
                        if (filefound && sourceMod) // file is to be overwritten
                            CopyFile/Z/O destFolderStr + fileName as backupFolderStr + fileName
                        endif
                    endif
                    if (sourceMod)
                        CopyFile/Z/O sourceFolderStr + fileName as destFolderStr + fileName
                        copiedFileList = AddListItem(destFolderStr + fileName, copiedFileList)
                    endif
                endif
            endfor

            // make a list of subfolders in current folder
            folderList = IndexedDir(tempPathSync, -1, 0)

            // add the list of folders to the subfolders wave
            for (subfolderIndex=0;subfolderIndex<ItemsInList(folderList);subfolderIndex+=1)
                w_subfolders[numpnts(w_subfolders)] = {sourceFolderStr + StringFromList(subfolderIndex, folderList) + ":"}
                folderCount += 1
            endfor
        endfor
        // prepare for next sublevel iteration
        Duplicate/T/O/free w_subfolders, w_folders
        Redimension/N=0 w_subfolders
        sublevels += 1

        if (numpnts(w_folders) == 0)
            break
        endif

    while(1)
    KillPath/Z tempPathSync
   
    if (bothways)
        copiedFileList += SyncFolder(destination, source, backupPathStr=backupPathStr, test=test, ignore=ignore, bothways=0)
    endif

    return SortList(copiedFileList)
end

// returns listStr, purged of any items that match an item in ZapListStr.
// Wildcards okay! Case insensitive.
function/S RemoveFromListWC(string listStr, string zapListStr)
    string removeStr = ""
    int i
    for (i=ItemsInList(zapListStr)-1;i>=0;i-=1)
        removeStr += ListMatch(listStr, StringFromList(i, zapListStr))
    endfor
    return RemoveFromList(removeStr, listStr, ";", 0)
end

function CheckForError(string source, string destination, string strFileList)  
   
    if (ItemsInList(strFileList) == 0)
        return 0
    endif
   
    wave/T wText = ListToTextWave(strFileList, ";")
    wText = RemoveStart(wText, source)
    wText = RemoveStart(wText, destination)
    FindDuplicates/Z/free/DT=dupsWave wText
    return numpnts(dupsWave)
end

function/S RemoveStart(string str, string startingStr)
    int len = strlen(startingStr)
    if (len == 0)
        return str
    endif
    if (cmpstr(startingStr, str[0,len-1]) == 0)
        return str[len,Inf]
    endif
    return str
end

 

Forum

Support

Gallery

Igor Pro 9

Learn More

Igor XOP Toolkit

Learn More

Igor NIDAQ Tools MX

Learn More