Synchronizing Folders (using AS, not sync)

I went looking a year or so ago for a means to synch folders on my home Mac and my Laptop, mainly motivated due to some disk problems caused by MS products on the laptop… I wanted to synchronize like-named folders.

I somehow found a script in French (Synchro Folders) , I believe it is from Thomas Robisson (http://tcfj.pagesperso-orange.fr/site/), that seemed to do the trick in a nice fashion. I had to use Google Translate to at least understand what the concepts were in the script, and the comments.

Over time I have modified it, added the progress bar, and generally tweaked it to get the results I want. My next effort is to learn rsync in my spare time and see if that gives me better performance over the network while skipping folders that have not changed since last synch. Of course, I could add skip folders to this script, but then rsync seems a better next step. Let’s see.

So, for simple synchronization of folders, and some entertainment via progress bar (from Spork), here is the script.

NOTE: Through the use of “ignoring application responses” the script is able to ‘parallel process’ file/folder copying.


property pLogEventsOn : true -- generate a log file
property pUseGrowl : true --  Use Growl notifications (under construction)
property pGrowlRunning : false -- assumes Growl is not running...

property pBaseFolder : "" --the desktop, or where the log file should be
property pSyncFileResultsFolder : "Folder Sync Log Messages"
property pLogFolder : "" -- filled in at run time with the desktop folder path
property pLogFileName : "Folder Sync.txt" --version 4.0 changed from log to txt file type

property pLineEndingMac : ASCII number return -- carriage return (13)
property pLineEndingUnix : ASCII character 10 -- line feed character
property pLinefeed : pLineEndingUnix -- use this line feed character in logs

property PSkipFolderFlag : false -- this determines if specific sub-folders should be skipped or checked, if true then they are skipped
--	note that this only checks going from A to B so far
property pSkipFolder1 : "DS2" -- the first folder to skip name
property pSkipFolder2 : "Graphics" --  the second folder skip name

property Mod_Date_Diff_Secs : 30
-- It adds a margin because there are a few seconds difference in the modification date when you copy elements between 2 networked Macs. 
property Copy_Timeout_Value : 400 -- seconds or almost 7 minutes  timeout to allow time if remote gets bogged down over the network

property fldr_A_Cnt : 0 --  this is the count of folders checked on the A side
property fldr_B_Cnt : 0 --  This is the folder count on the B side beyond those matching A
property fldr_A_File_Cnt : 0 --  the number of files done directly (note that some files are not counted as they are part of complete folder copying)
property fldr_B_File_Cnt : 0 --  the number of files done directly (note that some files are not counted as they are part of complete folder copying)
property Files_Deleted_Cnt : 0 --  the number of files deleted directly 
property fldr_A_File_Copied : 0 --  the number of files copied directly to B (note that some files are done as part of complete folder copying)
property fldr_B_File_Copied : 0 --  the number of files copied directly to A (note that some files are done as part of complete folder copying)
property fldr_A_fldr_Copied : 0 --  the number of folders copied directly to B (note that some folders are done as part of complete folder copying, as they are nested)
property fldr_B_fldr_Copied : 0 --  the number of folders copied directly to A (note that some files are done as part of complete folder copying, as they are nested)

--Added as v4.0 to track progress bar progress
property fldr_A_Top_Ttl_Cnt : 0 --  this is the count of all types in the A side folder top level
property fldr_A_Current_Ttl_Cnt : 0 --  this is the count of all types in the A side folder current level
property fldr_B_Top_Ttl_Cnt : 0 --  this is the count of all types in the B side folder top level
property fldr_B_Current_Ttl_Cnt : 0 --  this is the count of all types in the B side folder current level
property fldr_A_Top_Done_Cnt : 0 --  this is the count of all types in the A side folder top level done so far
property fldr_A_Current_Done_Cnt : 0 --  this is the count of all types in the A side folder top level done so far
property fldr_B_Top_Done_Cnt : 0 --  this is the count of all types in the B side folder current level done so far
property fldr_B_Current_Done_Cnt : 0 --  this is the count of all types in the B side folder current level done so far
set fldr_A_Lvl to 0 --  this is the level we are at on the A side (0 is top level then increases from there as we go into levels)
set fldr_B_Lvl to 0 --  This is the level we are at on the B side (0 is top level then increases from there as we go into levels)
property PBexists : false --  Added this to make sure the ProgBar is present or not presumes the PB is not present...

global sync --  sync is detected in the proceedings (otherwise sync would be considered a new variable in the procedure)
global Stop_Handler
global ptimeStart
set ptimeStart to current date

global The_Selection_List
set The_First_Choice to "1. A<=>B Reciprocal - A to B, if B newer, then B to A; unique files/folders copied both ways"
set The_Second_Choice to "2. A=>Bn - A updates B, newer on B goes to A, unique to A copied, unique to B left alone"
set The_Third_Choice to "3. A>>B - Overwrite B except newer on B then copied to A, delete unique on B"
set The_Fourth_Choice to "4. A=>Ba - A replaces B. All and Any of A copied, unique to B deleted, newer in B overwritten with warning"
set The_Fifth_Choice to "5. Report of A versus B" --(under construction)

set The_Selection_List to {The_First_Choice, The_Second_Choice, The_Third_Choice, The_Fourth_Choice, The_Fifth_Choice}

--  V4.0 changes
set fldr_A_Lvl to 0
set fldr_B_Lvl to 0
set fldr_A_Top_Ttl_Cnt to 0
set fldr_A_Current_Ttl_Cnt to 0
set fldr_B_Top_Ttl_Cnt to 0
set fldr_B_Current_Ttl_Cnt to 0
set fldr_A_Top_Done_Cnt to 0
set fldr_A_Current_Done_Cnt to 0
set fldr_B_Top_Done_Cnt to 0
set fldr_B_Current_Done_Cnt to 0

--------------------------------------------------------
(*
The synchro options are:
1. Reciprocal - where all files and folders are harmonized and older ones replaced with newer ones. Is a 2-way process
A<=>B Reciprocal
2. Folder A (first selected) is copied over to folder B, unless Folder B has newer files, then these are copied to A. Files unique to folder A are copied to B but files unique to B are not copied to A.
A=>Bn newer copied either way, unique to A copied, unique to B left alone
3. Files on folder A copied to B, any files unique on B are deleted. Newer files on B are copied to A
A>>>B Overwrite B except newer on B copied to A, delete unique to B
4. Folder A copied to B regardless. All of B's contents are removed and A contents put in their place. (there is a warning if a B file is newer)
A=>Ba All and Any of A copied, unique to B deleted, newer in B overwritten but with warning
5. Folder A evaluated versus B and a report generated. No other actions done.
*)
-------------------------------------------------------
-- set up log file on desktop

set pBaseFolder to (the path to the desktop) as text
set pLogFolder to pBaseFolder & pSyncFileResultsFolder & ":"

if pUseGrowl is true then
	tell application "System Events" to set pGrowlRunning to exists application process "GrowlHelperApp"
	-- if GrowlRunning is true then Growl can be sent notifications
	--set MyName to name of me--this was supposed to get the script's name
end if


if pLogEventsOn is true then
	-- create the Log folder on disk if not created before
	if not my FileExists(pLogFolder) then
		tell application "Finder"
			make new folder at (pBaseFolder as alias) with properties {name:pSyncFileResultsFolder}
		end tell
		my LogEntry("Created folder: " & pLogFolder, 0)
	end if
	
	set ptimeStart to current date
	my LogEntry("................... Starting up...", 1)
	my LogEntry("................... Running automated folder sync between machines", 0)
end if

---==============================================
(* 
For me, this program synchronizes folders between the laptop & the desktop, or between two drives, or on the same drive, if the folders are named differently
NEED TO FIND OUT SECONDARY DRIVE VS NETWORK DRIVE DIFFERENCES, could use local volume to determine if a network drive Y/N, then use startup true or false to know if it is main drive
*)
--================= Select the sync steps here =======================

tell application "Finder"
	activate
	set Stop_Handler to false
	
	try --	V4.1 stuff added to improve performance, avoid errors, and add some sizzle to bars
		ignoring application responses
			open application file id "com.spork.progbar"
		end ignoring
		set PBexists to true --	means the program exists and has started up, else will skip all PB related instructions
	on error m number n
		my LogEntry("................... the Progress Bar app (ProgBar) was not found. Error # " & m, 0)
	end try
	
	set sync to choose from list The_Selection_List with prompt "Select an option" with title "Select a synchro action" default items The_First_Choice without multiple selections allowed
	
	if sync is equal to false then return -- The script stops if cancel selected
	set TheChoice to item 1 of sync --variable used here and confirmation dialog
	my LogEntry("Selected: " & TheChoice, 0)
	repeat with i from 1 to length of The_Selection_List
		if item i of The_Selection_List is equal to item 1 of sync then
			set sync to i
			exit repeat
		end if
	end repeat
	
	--===================== Select the specific Folders to sync ================
	
	set Folder_A_selected to choose folder with prompt "Select the first (A) folder :"
	if Folder_A_selected = false then return -- The script stops because you clicked Cancel, no folder chosen
	
	set Folder_B_selected to choose folder with prompt "Select the second (B) folder :"
	if Folder_B_selected = false then return -- The script stops because you clicked Cancel, no folder chosen
	
	set Folder_A to item Folder_A_selected -- Folder_A is an alias
	set Folder_B to item Folder_B_selected -- Folder_B is an alias
	
	set Disk_A to disk of Folder_A
	--find out if this is start up, attahced disk, or a network disk? I don't know if there is a difference between attached and network, needs rresearch
	--if local volume is true than the disk is on the main computer and so can trash the files...
	
	set fldr_A_Cnt to 1 --this is the first folder checked (top level, level 0), so increment the count by 1
	set fldr_A_File_Cnt to 0 --no files checked as yet, so reset to zero
	set Disk_B to disk of Folder_B --ditto
	set fldr_B_Cnt to 0 --set to 0 as of V4.0 as was giving erroneous result of 1 B folder checked when none were
	set fldr_B_File_Cnt to 0 --ditto
	
	--resulting statistics on  processes, tracking registers, so to speak
	set Files_Deleted_Cnt to 0
	set fldr_A_File_Copied to 0
	set fldr_B_File_Copied to 0
	set fldr_A_fldr_Copied to 0
	set fldr_B_fldr_Copied to 0
	
	if Disk_A = Disk_B then --   if on the same disk, then the same alias, are the same folder so end after a dialog
		if Folder_A = Folder_B then
			beep
			display dialog "You have selected the same folder." with icon 0 buttons {"Stop"} default button 1
			return -- The script stops
		end if
		if ((Folder_A as text) is in (Folder_B as text)) or ((Folder_B as text) is in (Folder_A as text)) then --if on same disk, and one folder is in the other, then end
			beep
			display dialog "You've selected 1 folder inside the other..." with icon 0 buttons {"Stop"} default button 1
			return -- The script stops
		end if
	end if
	
	--confirmation dialog
	set BtnChoice to display dialog "You have chosen option: " & return & TheChoice & return & return & "Folder A: " & Folder_A & return & return & "Folder B: " & Folder_B with icon 1 buttons {"OK", "Cancel"} default button 1 -- in V4.0 added & return to spaces between text and A and A and B
	if BtnChoice = "Cancel" then return
	
	--========================== Establsih trash folders based on disk of folder A or B ===========	
	if sync is not equal to 5 then
		set trash_A to Create_Trash_Folder(Folder_A, "A") of me --  create a trash forlder for A
		if trash_A is false then return -- We stop the script because there was a problem, the alert was given in the previous subroutine
		set trash_B to Create_Trash_Folder(Folder_B, "B") of me --  create a trash folder for B
		if trash_B is false then return -- We stop the script because there was a problem, the alert was given in the previous subroutine 
	end if
	--=========================== Synchronize the folders according to the selected actions ==========	
	
	SyncFolders(Folder_A, Folder_B, trash_A, trash_B, fldr_A_Lvl, fldr_B_Lvl) of me
	my LogEntry("................... Run completed", 2)
	if PBexists is true then my QuitBar() -- modified with V4.1 to include the if statement
	
end tell

--======================== Move, replicate, and/or delete ===========================

-- Copy files in FolderA in FolderB if they are not already       If the file exists, it will not replicate unless it is newer 
on SyncFolders(folderA, folderB, trashA, trashB, fldr_A_Lvl, fldr_B_Lvl)
	my LogEntry(".............................. Folder A being checked: " & name of folderA & " (" & fldr_A_Lvl & ")", 0) -- & tab & "Folder B : " & name of folderB
	tell application "Finder"
		set the FolderAContents to {} & name of items in folderA as list
		-- Set to list FolderAContents for FolderA -- Provides all the elements of FolderA, including hidden files (. DS_Store, icon, ...) 
		-- Must be '{} &' because if there are no files, FolderAContents will not be an array
		
		if PBexists is true then --	V4.1 changes here, although bulk was 4.0
			set BarberPoleOn to false
			set Cnt_Items_Fldr_A to count of items in folderA --	As of V4.0 this cuts time down processing a little
			if fldr_A_Lvl = 0 then --  this added as of V4.0
				if fldr_A_Top_Ttl_Cnt = 0 then
					set fldr_A_Top_Ttl_Cnt to Cnt_Items_Fldr_A --  if top level get folder A items count
					set windowName to "alum_prog_window2"
					set Wndw_title to name of folderA
					my prepProgress(fldr_A_Top_Ttl_Cnt, windowName, Wndw_title)
					set BarberPoleOn to true
					my barberPole(windowName)
					my ShowProgBar(windowName)
				end if
			else
				if fldr_A_Current_Ttl_Cnt ≠ Cnt_Items_Fldr_A then
					set fldr_A_Current_Ttl_Cnt to Cnt_Items_Fldr_A --  if a lower level in A get item count this level
					set windowName to "mini_prog_window"
					set Wndw_title to name of folderA
					my prepProgress(fldr_A_Current_Ttl_Cnt, windowName, Wndw_title)
					my ShowProgBar(windowName)
				end if
			end if
		end if
		
		set the FolderBContents to {} & name of items in folderB as list --  same as for folder A
		
		set AppleScript's text item delimiters to tab -- tab stop
		set the FolderAContents to FolderAContents as text
		set the FolderAContents to every text item of the FolderAContents as list
		set the FolderBContents to FolderBContents as text
		set the FolderBContents to every text item of the FolderBContents as list
		
		set AppleScript's text item delimiters to ""
		if FolderAContents = {""} then set FolderAContents to {}
		if FolderBContents = {""} then set FolderBContents to {}
		
		set TempFolderContents to FolderAContents
		
		repeat with x in FolderAContents
			set IndexQuery to index of item x of folderA --  this added as of V4.0, this line reduces IAC script calls
			set Class_Item_X to class of item x of folderA -- This converts a lookup to a variable
			if fldr_A_Lvl ≠ 0 then --	If this is not the top level then chk if total items in bar is same as total items in A
				if fldr_A_Current_Ttl_Cnt ≠ (Cnt_Items_Fldr_A) then --	If not the same then prep the bar for this
					set fldr_A_Current_Ttl_Cnt to Cnt_Items_Fldr_A --  if a lower level in A get item count this level
					set windowName to "mini_prog_window"
					set Wndw_title to name of folderA
					my prepProgress(fldr_A_Current_Ttl_Cnt, windowName, Wndw_title)
				end if
			end if
			
			if Class_Item_X is folder then --	from V4.0 classify if it is a folder or file being checked, then increment the appropriate counter
				set fldr_A_Lvl to fldr_A_Lvl + 1 --  added V4.0
				set x_Folder_Value to true --  some value to track if this is a folder versus a file
				set fldr_A_Cnt to fldr_A_Cnt + 1
			else
				set fldr_A_File_Cnt to fldr_A_File_Cnt + 1
				set x_Folder_Value to false
				if fldr_A_Lvl ≠ 0 then --  this added as of V4.0
					set windowName to "mini_prog_window" --  added V4.0
					set fldr_A_Current_Done_Cnt to IndexQuery --  if a lower level in A increase items done
					set PassInfo to "Completed: " & IndexQuery & " out of " & Cnt_Items_Fldr_A
					my incrementProgBar(fldr_A_Current_Done_Cnt, windowName, "Level deep: " & fldr_A_Lvl, PassInfo)
				end if
			end if
			
			set x1 to x as text
			set Class_Item_X1 to (class of item x1 of folderA) -- V4.0 This converts a lookup to a variable
			
			set skip_this_folder to false -- this whole thing is to see if we can skip folders
			if x_Folder_Value is true then
				if PSkipFolderFlag is true then
					if x1 = pSkipFolder1 or x1 = pSkipFolder2 then
						set skip_this_folder to true
						my LogEntry("...... there was a folder skipped", 0)
					end if
				end if
			end if
			
			if skip_this_folder is false then
				if x1 is not in FolderBContents then --	Means this is either a file or a folder and does not show up in folder B
					with timeout of Copy_Timeout_Value seconds
						try
							ignoring application responses
								duplicate (item x1 of folderA) to folderB -- Do not bother with replacing to since there is no identical item in folderB
							end ignoring
							if Class_Item_X1 is folder then
								set fldr_A_fldr_Copied to fldr_A_fldr_Copied + 1
							else
								set fldr_A_File_Copied to fldr_A_File_Copied + 1
							end if
							if pLogEventsOn is true then my LogEntry(">>>>>>>>>> Copied " & x1 & " of folder A to " & folderB, 0)
							
						on error
							beep (2)
							display dialog "Cannot copy the file..." & (folderA as text) & x1 & " The disk appears full." with icon 0 buttons {"Stop"} default button 1
							set Stop_Handler to true
							return -- It quits the handler
						end try
					end timeout
				else --	Means that this is matched name for name in Folder B, so must do a compare and then react
					if Class_Item_X1 is folder and class of (item x1 of folderB) is folder then
						SyncFolders((item x1 of folderA), (item x1 of folderB), trashA, trashB, fldr_A_Lvl, fldr_B_Lvl) of me
						-- item must be or we will not record the proceedings but a text string 
						if Stop_Handler then return -- It quits the handler
					else
						set error_cnt to 0
						set Sheet_error to true
						-- On networked computers (remote), sometimes the Finder does not read the modification date of the file 
						repeat until Sheet_error is false
							try
								set date1 to (modification date of (item x1 of folderA)) + 0 -- Triggers an error if the modification date is not a number
								set date2 to (modification date of (item x1 of folderB)) + 0
								set Sheet_error to false -- This line is not executed if an error above
							on error
								set Sheet_error to true --  useless because it was not made false, the error is generated before
								set error_cnt to error_cnt + 1
								if error_cnt > 2 then -- does not try 2 times???
									beep (3)
									display dialog "Impossible to read the modification date of file " & x1 with icon 0 buttons {"Stop"} default button 1
									set Stop_Handler to true
									return -- It quits the handler
								end if
							end try
						end repeat
						
						set Copy_Sheet to false
						if date2 > (date1 + Mod_Date_Diff_Secs) then -- It adds the margin of time
							---   SYNC
							if sync = 1 or sync = 2 or sync = 3 then -- Reciprocal fashion, therefore we copy from folderB to FolderA
								set RemovedDuplicates to Delete_File(folderA, x1, trashA) of me -- You move the element in folderA -> Trash-before
								if RemovedDuplicates > 30 then -- We stop the script because there was a problem, the alert was given in the proceedings
									set Stop_Handler to true
									return -- It quits the handler
								end if
								with timeout of Copy_Timeout_Value seconds
									try
										ignoring application responses
											duplicate (item x1 of folderB) to folderA
										end ignoring
										if class of item x1 of folderB is folder then
											set fldr_B_fldr_Copied to fldr_B_fldr_Copied + 1
										else
											set fldr_B_File_Copied to fldr_B_File_Copied + 1
										end if
										if pLogEventsOn is true then my LogEntry(">>>>>>>>>> Copied " & x1 & " of folder B to " & folderA, 0)
										
										-- Try  update item X1 in folderA -- 
									on error
										beep (2)
										display dialog "Cannot copy the file..." & (folderB as text) & x1 & " The disk appears full" with icon 0 buttons {"Stop"} default button 1
										set Stop_Handler to true
										return -- It quits the handler
									end try
								end timeout
							else -- Not Reciprocal mode, so we request to replace the newer file not the oldest
								beep
								set demav to display dialog "The item " & x1 & (", is older in folder 1 than in folder 2." & return & "Do you wish to copy it anyway?") with icon 2 buttons {"Stop", "Yes", "No"} default button 3 giving up after 25 -- seconds
								if button returned of demav = "Stop" then
									set Stop_Handler to true
									return -- It quits the handler
								else if button returned of demav = "Yes" then
									set Copy_Sheet to true
								end if
							end if
						end if -- date2 > date1
						if (date1 > (date2 + Mod_Date_Diff_Secs)) or (Copy_Sheet = true) then
							set RemovedDuplicates to Delete_File(folderB, x1, trashB) of me -- You move the element in folderB -> Trash-before 
							if RemovedDuplicates > 30 then -- We stop the script because there was a problem, the alert was given in the preceeding
								set Stop_Handler to true
								return -- It quits the handler
							end if
							with timeout of Copy_Timeout_Value seconds
								try
									ignoring application responses
										duplicate (item x1 of folderA) to folderB
									end ignoring
									--to copy because if you tried with replacing folderB item, it will be deleted and can not be retrieved from the trash 
									if Class_Item_X1 is folder then
										set fldr_A_fldr_Copied to fldr_A_fldr_Copied + 1
									else
										set fldr_A_File_Copied to fldr_A_File_Copied + 1
									end if
									if pLogEventsOn is true then my LogEntry(">>>>>>>>>> Copied " & x1 & " of folder A to " & folderB, 0)
									
									-- Furthermore, if the disc is in another Mac (via network),  replacing instruction does not work 
									--because you can not erase files from another Mac without a dialog, unless coming in as admin and dialogs turned off
								on error
									beep (2)
									display dialog "Cannot copy the file..." & (folderA as text) & x1 & " The disk appears full." with icon 0 buttons {"Stop"} default button 1
									set Stop_Handler to true
									return -- It quits the handler
								end try
							end timeout
						end if -- date1 > date2
					end if -- item X1 of folderA   and   item X1 of folderB    is a folder
				end if -- X1 is not in FolderBContents
			end if --	this is the skip_folder is false end
			
			-- ***
			if PBexists then
				if fldr_A_Lvl > 0 then --  V4.0 stuff
					if Class_Item_X1 is folder then
						set fldr_A_Lvl to fldr_A_Lvl - 1 -- done with a level, jump back one
					end if
				end if
				if fldr_A_Lvl = 0 then --  this added as of V4.0, must be after decrement as the sub-folder would not be tracked properly at level 0
					if BarberPoleOn is true then
						my killBarberPole(windowName)
						set BarberPoleOn to false
					end if
					if fldr_A_Top_Done_Cnt ≠ IndexQuery then
						set windowName to "alum_prog_window2"
						set fldr_A_Top_Done_Cnt to IndexQuery --  if top level increase folder A items done
						set PassInfo to "Completed: " & IndexQuery & " out of " & fldr_A_Top_Ttl_Cnt
						my incrementProgBar(fldr_A_Top_Done_Cnt, windowName, "Top Level...", PassInfo)
					end if
				end if
			end if
			
		end repeat
		
		-- It handles files of folderB not previously evaluated
		repeat with x2 in FolderBContents
			set x2 to x2 as text -- Always the problem of filenames with accented characters as text unmanaged
			if x2 is not in TempFolderContents then
				-- It matches none of the elements of TempFolderContents because they have been dealt with (either copied or deleted) 
				-- x2 is not necessarily in Folder A Contents since it is not in TempFolderContents
				if class of item x2 of folderB is folder then
					set fldr_B_Cnt to fldr_B_Cnt + 1
				else
					set fldr_B_File_Cnt to fldr_B_File_Cnt + 1
				end if
				set drap_x_eff to false
				if number of characters of x2 > 4 then
					if (characters 1 thru 4 of x2 as text) is "->t-" then
						set drap_x_eff to true
						set RemovedDuplicates to Delete_File(folderB, x2, trashB) of me -- It moves the file that starts with "-> t-" of folderB to trash storage folder
						if RemovedDuplicates > 30 then -- We stop the script because there was a problem, the alert was given in the proceedings
							set Stop_Handler to true
							return -- It quits the handler
						end if
					end if
				end if
				
				if drap_x_eff is false then
					--   SYNC
					if sync = 1 then -- Reciprocal fashion, therefore we copy folderB to folderA 
						with timeout of Copy_Timeout_Value seconds
							try
								ignoring application responses
									duplicate (item x2 of folderB) to folderA -- Do not bother with replacing to since there are no items in this FolderA
								end ignoring
								if class of item x2 of folderB is folder then
									set fldr_B_fldr_Copied to fldr_B_fldr_Copied + 1
								else
									set fldr_B_File_Copied to fldr_B_File_Copied + 1
								end if
								if pLogEventsOn is true then my LogEntry(">>>>>>>>>> Copied " & x2 & " of folder B to " & folderA, 0)
							on error
								beep (2)
								display dialog "Cannot copy the file..." & (folderB as text) & x2 & " The disk appears full." with icon 0 buttons {"Stop"} default button 1
								set Stop_Handler to true
								return -- It quits the handler
							end try
						end timeout
						-- The 2 files are now identical, we copied a file from folderA to folderB 
						--   SYNC
					else if sync = 3 or sync = 4 then -- Sync choices 3, 4 trigger this
						set RemovedDuplicates to Delete_File(folderB, x2, trashB) of me -- You move the element in folderB ->-Trash
						if RemovedDuplicates > 30 then -- We stop the script because there was a problem, the alert was given in the proceedings
							set Stop_Handler to true
							return -- It quits the handler
						end if
					end if
				end if
			end if -- x2 is not in TempFolderContents
		end repeat
	end tell
	if fldr_A_Lvl > 0 then
		set fldr_A_Lvl to fldr_A_Lvl - 1 -- done with a level, jump back one
	end if
end SyncFolders

--======================== Delete files (move to temp trash folders) ==============

-- You move File2Delete of Original_Folder to the folder Trash_Folder, first check to see if there is already a file of the same name in the trash, if yes, add a digit (up to the # 20) to the end of the file name
on Delete_File(Original_Folder, File2Delete, trash_Folder)
	set RemovedDuplicates to 1
	tell application "Finder"
		set Temp_File_Check to trash_Folder & File2Delete
		if my FileExists(Temp_File_Check) then
			--  if the file to be moved to the 'trash' is named same as a file already in the 'trash,' then append a digit (1-20) to end of file name but first remove the 'dot . and file extension to get rid of confusion
			set item_file2delete to item File2Delete of Original_Folder
			set File2DeleteName to File2Delete
			set find_the_dot to (offset of "." in File2DeleteName)
			set File2DeleteName to characters 1 thru (find_the_dot - 1) of File2DeleteName
			set match_exists to true
			repeat until match_exists = false
				try
					set Temp_File_Record to trash_Folder & File2DeleteName & "-" & RemovedDuplicates
					if pLogEventsOn is true then my LogEntry(".......... checked: " & File2DeleteName & "-" & RemovedDuplicates & tab & "The Temp_File_Record: " & Temp_File_Record, 0)
					if my FileExists(Temp_File_Record) then
						set RemovedDuplicates to RemovedDuplicates + 1
						if RemovedDuplicates > 30 then set match_exists to false
					else
						set match_exists to false
					end if
				on error errStr number errorNumber
					if pLogEventsOn is true then my LogEntry(".......... " & File2DeleteName & " has trouble from " & Original_Folder, 0)
					if pLogEventsOn is true then my LogEntry(".......... Error " & errStr & tab & number & tab & errorNumber, 0)
				end try
			end repeat
			if RemovedDuplicates > 30 then
				beep
				display dialog "Too many items with the same name have been moved for deletion." with icon 0 buttons {"Stop"} default button 1
			else
				set name of item File2Delete of Original_Folder to (File2DeleteName & "-" & RemovedDuplicates) as text
				set File2Delete to (File2DeleteName & "-" & RemovedDuplicates) as text
				move item File2Delete of Original_Folder to trash_Folder
				set Files_Deleted_Cnt to Files_Deleted_Cnt + 1
				if pLogEventsOn is true then my LogEntry("......Deleted: " & File2Delete & " moved to " & trash_Folder & " from " & Original_Folder, 0)
			end if
		else
			move item File2Delete of Original_Folder to trash_Folder
			set Files_Deleted_Cnt to Files_Deleted_Cnt + 1
			if pLogEventsOn is true then my LogEntry("......Deleted: " & File2Delete & " moved to " & trash_Folder & " from " & Original_Folder, 0)
		end if
		return RemovedDuplicates -- To determine whether there was a problem or not
	end tell
end Delete_File

---================= Log File Entry =============================

on LogEntry(someText, timingAct)
	if pLogEventsOn is false then return
	--create text entry to log file, append to end of file to maintain long record
	set logFile to (pLogFolder as text) & pLogFileName
	set logRef to 0
	try
		set logRef to open for access file logFile with write permission
	end try
	if logRef ≠ 0 then
		if timingAct is 1 then
			set ptimeStart to current date
			write return & pLinefeed & pLinefeed starting at eof to logRef
			write FormatDateTime(current date) & ": " & someText & pLinefeed starting at eof to logRef
		else
			write FormatDateTime(current date) & ": " & someText & pLinefeed starting at eof to logRef
		end if
		if timingAct is 2 then
			set TotalSeconds to (current date) - ptimeStart
			set ElapsedMinutes to (((TotalSeconds - (TotalSeconds mod 60)) / 60) as integer)
			set ElapsedSeconds to (TotalSeconds mod 60)
			if ElapsedSeconds < 10 then set ElapsedSeconds to "0" & ElapsedSeconds
			write pLinefeed & "Time to complete (minutes & seconds): " & ElapsedMinutes & ":" & ElapsedSeconds & pLinefeed starting at eof to logRef
			write "Files and Folders checked: " & return & pLinefeed starting at eof to logRef
			write "................ A folders:" & fldr_A_Cnt & return & pLinefeed starting at eof to logRef
			write "................ A files:" & fldr_A_File_Cnt & return & pLinefeed starting at eof to logRef
			write "................ B folders:" & fldr_B_Cnt & return & pLinefeed starting at eof to logRef
			write "................ B files:" & fldr_B_File_Cnt & return & pLinefeed starting at eof to logRef
			write "Total files moved (delete after verification): " & Files_Deleted_Cnt & return & pLinefeed starting at eof to logRef
			write "Files and Folders copied: " & return & pLinefeed starting at eof to logRef
			write "................ A folders to B:" & fldr_A_fldr_Copied & return & pLinefeed starting at eof to logRef
			write "................ A files to B:" & fldr_A_File_Copied & return & pLinefeed starting at eof to logRef
			write "................ B folders to A:" & fldr_B_fldr_Copied & return & pLinefeed starting at eof to logRef
			write "................ B files to A:" & fldr_B_File_Copied & return & pLinefeed starting at eof to logRef
		end if
		close access logRef
	end if
end LogEntry

---================= Date infor for date stamp in Log file =============================

on FormatDateTime(theDate)
	set theDate to theDate as date
	set dd to text -2 thru -1 of ("0" & theDate's day)
	copy theDate to tempDate
	set the month of tempDate to January
	set mm to text -2 thru -1 of ("0" & 1 + (theDate - tempDate + 1314864) div 2629728)
	set yy to text -1 thru -4 of ((year of theDate) as text)
	set hh to time string of theDate
	return (yy & "/" & mm & "/" & dd & " " & hh as text)
end FormatDateTime

---=================== Check to see if a file or folder exists ===========================

on FileExists(someFile)
	try
		set anAlias to (someFile as alias)
		return true
	on error
		return false
	end try
end FileExists

---================== Progress Bars Added Below in v4.0 ============================

-- ============================= Start a bar ==========
on startProgBar()
	tell application "ProgBar"
		launch
	end tell
end startProgBar

-- Prepare progress bar subroutine.
on prepProgress(someMaxCount, windowName, Wndw_title)
	tell application "ProgBar"
		if windowName = "mini_prog_window" then
			tell text field "field_one" of window windowName to set contents to "Initializing..."
			tell text field "field_one" of window windowName to set contents to "Initializing..."
		end if
		tell progress indicator "prog_one" of window windowName
			set content to 0
			set uses threaded animation to true
			start
			set minimum value to 1
			set maximum value to someMaxCount
		end tell
		set title of window windowName to Wndw_title
	end tell
end prepProgress

--Show Progress Bar with no fanfare
on ShowProgBar(windowName)
	tell application "ProgBar"
		show window windowName
	end tell
end ShowProgBar

-- Increment progress bar subroutine.
on incrementProgBar(itemNumber, windowName, Level_Name, Current_Job)
	tell application "ProgBar"
		tell text field "field_one" of window windowName to set contents to Level_Name
		tell progress indicator "prog_one" of window windowName to set content to itemNumber
		tell text field "field_two" of window windowName to set contents to Current_Job
	end tell
end incrementProgBar

-- End progress bar subroutine.
on endProgress(windowName)
	tell application "ProgBar"
		tell progress indicator "prog_one" of window windowName to stop
		tell progress indicator "prog_one" of window windowName to set content to 0
		tell text field "field_two" of window windowName to set contents to ""
	end tell
end endProgress

--===================================== Fade In/Out Window ======================================

-- Fade in a progress bar window.
on fadeIn(windowName)
	tell application "ProgBar"
		set alpha value of window windowName to 0
		show window windowName
		repeat with i from 1 to 9
			set alpha value of window windowName to (("0." & i) as real)
		end repeat
		set alpha value of window windowName to 1.0
		set opaque of window windowName to true
	end tell
end fadeIn

-- Fade out a progress bar window.
on fadeOut(windowName)
	tell application "ProgBar"
		set fadeValue to (1 as real)
		repeat with i from 1 to 9
			set alpha value of window windowName to fadeValue
			set fadeValue to fadeValue - 0.1
		end repeat
		set alpha value of window windowName to 0.1
		set opaque of window windowName to false
		hide window windowName
	end tell
end fadeOut

--============================== Barber Pole behavior on/off =============================

-- Give a progress bar the 'barber pole' behavior.
on barberPole(windowName)
	tell application "ProgBar"
		set indeterminate of progress indicator "prog_one" of window windowName to true
	end tell
end barberPole

-- Disable the 'barber pole' behavior of a progress bar.
on killBarberPole(windowName)
	tell application "ProgBar"
		set indeterminate of progress indicator "prog_one" of window windowName to false
	end tell
end killBarberPole

--======================= Quit ProgBar ========================
on QuitBar()
	try
		tell application "ProgBar"
			quit
		end tell
	end try
end QuitBar
--======================== Create Trash folders for each of the synced folders inside a common folder ==================
(* 
the script creates a folder for trashed files on each drive at the level of A or B selection, with files/folders marked for deletion for that drive placed into these temp trash folders, for later clean up or recovery
Moved down here as of V4.0
*)

-- creates at the desktop of the start up disk a folder named '=>Removed Files:' or a "Synchro Removed Files" folder on a remote disk
on Create_Trash_Folder(Working_Folder, Selected_directory) -- Working_Folder is  Folder A or Folder B passed to handler
	tell application "Finder"
		set disk_parent to the disk of Working_Folder -- Get the disk containing Working_Folder 
		if startup of disk_parent is true then --local volume for a second volume does not work here
			if class of Working_Folder is folder then
				set Common_trash_Folder to pLogFolder & "=>Removed Files:"
				if not my FileExists(Common_trash_Folder) then --If the overall trash folder does not exist, create it
					tell application "Finder"
						make new folder at (pLogFolder as alias) with properties {name:"=>Removed Files"}
					end tell
					my LogEntry("Created folder: " & Common_trash_Folder, 0)
				end if
				set date_String to current date
				set date_String to short date string of date_String as text
				set Working_Folder_Result to name of Working_Folder & "_" & date_String as text
				set trash_Folder to Common_trash_Folder & name of Working_Folder & "_" & date_String & ":"
				if not my FileExists(trash_Folder) then -- If the specific trash folder for the folder to be synced does not exist for this date, create it
					tell application "Finder"
						make new folder at (Common_trash_Folder as alias) with properties {name:Working_Folder_Result}
					end tell
				end if
			else
				set trash_Folder to false
				beep
				if pLogEventsOn is true then my LogEntry("Not a Folder: " & Working_Folder, 0)
				display dialog "Synchronization needs 2 folders. Please select 2 folders." with icon 0 buttons {"Stop"} default button 1
			end if
		else
			if item "Synchro Removed Items" of disk_parent exists then
				if class of item "Synchro Removed Items" of disk_parent is folder then
					set trash_Folder to name of folder "Synchro Removed Items" of disk_parent
					set trash_Folder to name of disk_parent & ":" & trash_Folder & ":" as text
				else
					set trash_Folder to true
					beep
					display dialog "The folder '=>Removed Items' was created on " & disk_parent & ". This acts as a temporary deposit for older files to be deleted" with icon 0 buttons "Stop" default button 1
				end if
			else
				set trash_Folder to make new folder at disk_parent with properties {name:"Synchro Removed Items"}
				set trash_Folder to name of disk_parent & ":" & "Synchro Removed Items:" as text
				display dialog "A folder 'Synchro Removed Items' was created on " & disk_parent & ". All items to be deleted from the far drive will be moved here (old items replaced by more recent ones)" with icon 1 buttons {"OK"} default button 1 giving up after 25 -- seconds
			end if
		end if
		my LogEntry("Directory " & Selected_directory & ":" & return & Working_Folder & return & "Trash Folder: " & return & trash_Folder as text, 0)
		return trash_Folder
	end tell
end Create_Trash_Folder


Model: MacBook (early 2009)
Browser: Safari 533.16
Operating System: Mac OS X (10.6)