Synchronize or merge two directories

I haven't tested this extensively, caveat emptor!

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

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 10

Learn More

Igor XOP Toolkit

Learn More

Igor NIDAQ Tools MX

Learn More