Monday, November 08, 2010 at 12:02 AM.

system.verbs.builtins.file.reconcileFolder

on reconcileFolder (sourcefolder, destfolder, adrCopyFilter=nil, adrErrorTable=nil) {
	<<Changes
		<<5/22/10; 7:36:27 PM by DW
			<<If the destfolder doesn't exist, create it.
		<<5/15/10; 9:17:53 AM by DW
			<<Added optional errortable parameter. If it's not nil, errors don't stop the reconciler, they're just added to table and the process continues.
			<<Added error-checking. If adrCopyFilter is nil, we provide one. Error messages are now sentences. Don't roll the beachball (this might be running in a background thread or on a server, waste of cycles, distraction).
			<<http://frontiernews.org/2010/05/15/file-reconcilefolder-rewrite/
		<<3.0.2: validate input parameters as folders. also fixed path calcuation 
			<<when the case of original or clone differed from that used in the file system
	on defaultFilter (f) { //we use this filter if one isn't specified, 5/15/10 by DW
		return (true)};
	if adrCopyFilter == nil {
		adrCopyFilter = @defaultFilter};
	bundle { //make sure the destfolder exists, 5/22/10 by DW
		file.surefilepath (destfolder + "xxx")};
	bundle { //make sure both folders are folders
		on checkIsFolder (folder) {
			if not file.isFolder (folder) {
				scriptError ("Can't reconcile the folder because \"" + file.fileFromPath (folder) + "\" is not a folder.")};
			folder = filespec (folder);
			return (folder)};
		sourcefolder = checkIsFolder (sourcefolder);
		destfolder = checkIsFolder (destfolder)};
	bundle { //set up the error table, 5/15/10 by DW
		if adrErrorTable != nil {
			new (tabletype, adrErrorTable)}};
	on pushErrorTable (errorstring, fsource, fdest) {
		local (adrsub = @adrErrorTable^.[string.padwithzeros (sizeof (adrErrorTable^), 5)]);
		new (tabletype, adrsub);
		adrsub^.errorstring = errorstring;
		adrsub^.fsource = fsource;
		adrsub^.fdest = fdest};
	
	local (extraname = "Extra Files" + file.getPathChar ());
	on checkForMissingFiles (path) {
		local (fsource, fdest);
		fileloop (fsource in path) { //loop over files in the original folder
			try {
				if file.isFolder (fsource) { //skip any folder named Extra Files
					if file.fileFromPath (fsource) == extraname {
						continue}};
				fdest = destfolder + (fsource - sourcefolder);
				if not file.exists (fdest) { //file or folder is missing
					file.filteredCopy (fsource, fdest, adrCopyFilter)}
				else {
					if file.isFolder (fsource) {
						checkForMissingFiles (fsource)}
					else {
						if file.modified (fsource) > file.modified (fdest) { //the user changed the file
							file.filteredCopy (fsource, fdest, adrCopyFilter)}}}}
			else {
				if adrErrorTable == nil { //re-throw the error
					scripterror (tryerror)};
				pushErrorTable (tryerror, fsource, fdest)}}};
	checkForMissingFiles (sourcefolder);
	
	local (userfolder = destfolder + extraname);
	on checkForExtraFiles (path) {
		local (fdest, fsource);
		fileloop (fdest in path) {
			try {
				<<rollBeachBall ()
				if fdest != userfolder { //don't look at or inside of the user's personal folder
					fsource = sourcefolder + (fdest - destfolder);
					if not file.exists (fsource) { //new file created by user, move it to personal folder
						if file.exists (userfolder) {
							if not file.isFolder (userfolder) { //we're really paranoid
								file.delete (userfolder);
								file.newFolder (userfolder)}}
						else {
							msg (userfolder);
							file.newFolder (userfolder)};
						file.move (fdest, userfolder)}
					else { //the file is a copy from the original folder
						if file.isFolder (fdest) {
							checkForExtraFiles (fdest)}}}}
			else {
				if adrErrorTable == nil { //re-throw the error
					scripterror (tryerror)};
				pushErrorTable (tryerror, fsource, fdest)}}};
	checkForExtraFiles (destfolder)} //make sure there aren't any extra files or folders in the clone
<<bundle //test code
	<<local (sourcefolder = "Albert:Stuff from Leon:")
	<<local (destfolder = "Ohio2:Leon:Contents of his hard disk:")
	<<on filter (f) //we use this filter if one isn't specified, 5/15/10 by DW
		<<scratchpad.f = f
		<<return (true)
	<<reconcileFolder (sourcefolder, destfolder, @filter, adrerrortable:@scratchpad.reconcileErrors)



This listing is for code that runs in the OPML Editor environment. I created these listings because I wanted the search engines to index it, so that when I want to look up something in my codebase I don't have to use the much slower search functionality in my object database. Dave Winer.