%% FILESYNC Synchronize data using a USB key.
%
% PLEASE MAKE SURE YOU UNDERSTAND THE CODE COMPLETELY BECAUSE IT DOES OVERWRITE DATA!
%
% This file is designed to synchronize data between two computers using a
% USB key. It only updates files that have been modified since the last
% update. Files with the more recent modified date overwrite existing
% files.
%
% The first time this function is executed, it initializes the USB key.
% However, since it doesn't know when the last backup was performed, no
% data is copied to the USB key. Each time after the initialization,
% this function will update the USB key to keep data between the two
% computer synchronized.
%
% Variables that need to be set are:
% <basedir> - (1 x 2) CELLL ARRAY of STRINGs of full paths to the base directory
% on each computer to be synchronized.
% <staticname> - (1 x 2) CELL ARRAY of STRINGs that include the computer name
% of each computer retruned by "getenv('computername')"
% <USBdir> - STRING of directory path on the USB key used for synchronization
% <USBtmp> - STRING of directory path on the USB key used for temporary
% purposes during synchronization
%
% This function was tested on Windows XP, SP2, running Matlab 2006b (ver 7.3).
% There is a GETENV function call that expects 'computername' to exist as an
% environmet variable. This will need to be modified for other operating
% systems.
%
% Please send comments/suggestions to:
%
% Jesse Norris
% jnorris@wfubmc.edu
% http://jesse.musculoskeletaldynamics.com
%
% Last modified: 15 April 2007
%
%% filesync
function filesync
% Start stopwatch, clear command window and store current directory
%
tic; clc; currdir = cd;
%% ~~~~~ USER SPECIFIC VARIABLES ~~~~~
% Set the static computer names and base directories on those computers
% that will be synchronized. These static computer names should match those
% returned by the GETENV function with 'computername' argument.
%
staticname{1} = '3390-99RGXAZ'; % Computer 1 <computername>
basedir{1} = 'C:\Documents and Settings\jnorris\My Documents'; % Base directory on Computer 1
staticname{2} = '1612-8QDLG11'; % Computer 2 <computername>
basedir{2} = 'C:\Documents and Settings\jnorris\My Documents'; % Base directory on Computer 2
% Set the directories on the USB key used for synchronization.
%
USBdir = 'E:\File Transfer'; % Directories for storing updated files
USBtmp = 'E:\Temp'; % Temporary directory
%% ~~~~~ END USER SPECIFIC VARIABLES ~~~~~
% Retrieve current computer name. NOTE: The <getenv> function assumes
% 'computername' is an enviroment variable.
%
computername = getenv('computername');
if isempty(computername); error('''computername'' does not exist in this computer''s environment variables.'); end;
% Set files for keeping track of synchronization times.
%
USBfile = ['\' '0 Files updated.txt']; % File where updated files are stored.
USBts{1} = ['\1 ' staticname{1} ' to USB.txt']; % Files on the USB key that are used solely for time stamps.
USBts{2} = ['\1 ' staticname{2} ' to USB.txt'];
% Check if directories & file exist. If not, attempt to create. If create fails, return error.
%
chkmkdir(USBdir);
chkmkfile([USBdir USBfile]);
chkmkfile([USBdir USBts{1}]);
chkmkfile([USBdir USBts{2}]);
% Ensure that user is ready to begin synchronization.
%
button = questdlg('Begin synchronization?', 'USB Synchronization', 'Yes', 'No', 'No');
if strcmp(button,'Yes');
% Determine which computer was most recently synchronized to the USB key
%
comp1date = filemodified([USBdir USBts{1}]);
comp2date = filemodified([USBdir USBts{2}]);
if (comp1date > comp2date); USBupdatedfrom = staticname{1}; else USBupdatedfrom = staticname{2}; end;
% Collapse <basedir> and <USBts> to just STRING variables based on the current computer.
%
if strcmp(computername,staticname{1});
basedir = basedir{1};
USBts = USBts{1};
elseif strcmp(computername,staticname{2});
basedir = basedir{2};
USBts = USBts{2};
else
error('filesync:unknowncomp','<computername> ''%s'' does not match either name in the variable <staticname>',computername);
end;
% If the current computer was the most recent to be synchronized with the USB key,
% it is only necessary to update the USB key with any new data.
%
if strcmp(computername,USBupdatedfrom);
updateUSB(basedir,USBdir,USBfile,USBts); % Update the USB key
% Otherwise, it is necessary to
% (1) Update any changes that have occured on the current computer to <USBtmp> on the USB key.
% (2) Then update the current computer with USB data.
% (3) Lastly, copy from <USBtmp> to <USBdir> for updates back to the other computer.
%
else
chkmkdir(USBtmp); % Check if <USBtmp> exists. If not, attempt to create. If create fails, return error.
copyfile([USBdir USBts],[USBtmp USBfile]); % Copy the time stamp file to the update file in the temporary directory
updateUSB(basedir,USBtmp,USBfile,USBts); % Update <USBtmp> with data on computer
updatecomputer(basedir,USBdir,USBfile); % Update computer with data in <USBdir> on USB key,
movefile([USBtmp '\*'],USBdir); % Move all files from the <USBtmp> to <USBdir>, and
rmdir(USBtmp); % Remove <USBtmp> from the USB key
end;
% Return to original directory
%
try
cd(currdir);
catch
cd(basedir);
warning('filesync:cderror','Unable to change directory to: %s',currdir);
end
toc; % Elapsed time
fprintf(' ~~~ \n\n');
else
fprintf('Synchronization Cancelled.\n\n');
end;
end
%% chkmkdir
function message = chkmkdir(dirname)
% Check if <dirname> is a directory. If not, attempt to create.
% If create fails return error.
%
if ~exist(dirname,'dir')
try
mkdir(dirname); % Create directory
message = 'Directory created.';
catch
error('Could not create path: ''%s''.\n~Check initialization value of variable: <%s>.',dirname,inputname(1));
end;
else
message = 'Directory exists.';
end;
end
%% chkmkfile
function message = chkmkfile(fname)
% Check if <fname> is a file. If not, attempt to create.
% If create fails return error.
%
if ~exist(fname,'file')
try
fid = fopen(fname,'w'); fclose(fid); % Create file
message = 'File created.';
catch
error('Could not create file: ''%s''.\n~Check initialization value of variable: <%s>.',fname,inputname(1));
end;
else
message = 'File exists.';
end;
end
%% removeemptyfolders
function removeemptyfolders(rmdirname)
if isdir(rmdirname);
cd(rmdirname); % CD to directory
while length(dir) == 2; % Recurrisvely remove until an unempty folder is encountered
rmdirname = cd; % Current directory
cd ..; % CD to parent directory
rmdir(rmdirname); % Remove the subdirectory
end;
end;
end
%% filemodified
function modified = filemodified(fullpath)
if exist(fullpath,'file')
fileinfo = dir(fullpath); % Determine last modification time
modified = datenum(fileinfo.date,'dd-mmm-yyyy HH:MM:SS');
else
modified = [];
end;
end
%% updatecomputer
function updatecomputer(basedir,USBdir,USBfile)
fprintf('\n... Updating the computer with files on USB key ...\n\n');
USBfile = [USBdir USBfile]; % Update to full path
fid = fopen(USBfile,'r'); % Open file that includes full directory paths and file names
files = textscan(fid,'%s','delimiter',';'); % Load all the files to update
files = files{1};
fclose(fid); % Close the file
% Step through each file
% -> check that file exists on USB key
% -> check that the date on the USB key is more recent than that on the computer
% -> copy file from USB key to computer
for fileind = 1:length(files)
[subdir name ext] = fileparts(files{fileind});
fprintf('%s...',[subdir '\' name ext]); % Update workspace
if exist([USBdir subdir '\' name ext],'file')
USBdate = filemodified([USBdir subdir '\' name ext]);
Cdate = filemodified([basedir subdir '\' name ext]);
if ~isempty(Cdate)
if USBdate > Cdate
movefile([USBdir subdir '\' name ext],[basedir subdir '\' name ext],'f');
fprintf('%s\n','Updated.');
else
switch questdlg(['Confirm Overwrite of ' name ext],'Overwrite','Yes','No - Delete from USB','No - Leave on USB','No - Leave on USB')
case 'Yes'
movefile([USBdir subdir '\' name ext],[basedir subdir '\' name ext],'f');
fprintf('%s\n','Updated.');
case 'No - Delete from USB'
delete([USBdir subdir '\' name ext]);
fprintf('%s\n','Not updated - deleted from USB.');
otherwise
fprintf('%s\n','Not updated - left on USB.');
end;
end;
else
if ~isdir([basedir subdir]); mkdir([basedir subdir]); end; % Create new directory
movefile([USBdir subdir '\' name ext],[basedir subdir '\' name ext],'f');
fprintf('%s\n','Updated.');
end;
else
fprintf('%s\n','File not found on USB key.');
end;
removeemptyfolders([USBdir subdir]); % Recurrisvely remove until an unempty folder is encountered
end;
end
%% updateUSB
function updateUSB(basedir,USBdir,USBfile,USBts)
fprintf('\n... Updating the USB key with files from the computer ...\n\n');
USBfile = [USBdir USBfile]; % Update to full path
USBts = [USBdir USBts]; % Update to full path
USBdate = filemodified(USBfile); % Check when it was modified
fid = fopen(USBfile,'a'); % Open file to append the full path strings to
% Load all the paths to scan. This expression is based on a line in
% Matlab function PARSEDIRS.
%
paths = regexp(genpath(basedir), sprintf('[^\\s%s][^%s]*', pathsep, pathsep), 'match')';
% Step through all paths
% -> examine if files have been modified since <USBdate>
% -> copy updated files to the USB key
% -> append <fid> with directory where file was originally from
%
for pathind = 1:length(paths)
cd(paths{pathind}); % Change to directory
files = dir; % Load file information
% Step through all files
%
for fileind = 3:length(files)
% NOTE: THIS EXPRESSIONS STOPS THIS FUNCTION FROM COPYING OUTLOOK MAILBOX FILES (*.pst)
% The one on my computer is too large to be copied with just a USB key.
%
if ~files(fileind).isdir && (datenum(files(fileind).date,'dd-mmm-yyyy HH:MM:SS') > USBdate) && ~strcmpi(files(fileind).name(end-2:end),'pst')
if (files(fileind).bytes > 10e6)
okay2copy = questdlg(['File ' files(fileind).name ' is ' sprintf('%.0f',files(fileind).bytes/1e6) ' Mb. Copy file to the USB key?'],'Large file.','Yes','No','No');
okay2copy = strcmp(okay2copy,'Yes');
else
okay2copy = true;
end;
if okay2copy
try
if ~isdir([USBdir paths{pathind}(47:end)]); mkdir([USBdir paths{pathind}(47:end)]); end; % Create directory on USB key
copyfile(files(fileind).name,[USBdir paths{pathind}(47:end) '\' files(fileind).name]); % Copy file to USB key
fprintf(fid,'%s\r\n',[paths{pathind}(47:end) '\' files(fileind).name '; ']); % Update the txt file
fprintf( '%s\n',[paths{pathind}(47:end) '\' files(fileind).name]); % Update workspace
catch
warning('updateUSB:copyfile','Error caught in function <updateUSB>');
handleerror(lasterror);
end
else
fidnoupdate = fopen([USBdir '\0 Files NOT updated'],'a');
fprintf(fidnoupdate,'%s\r\n',[paths{pathind}(47:end) '\' files(fileind).name '; ']); % Update the txt file
fclose(fidnoupdate);
end;
end;
end;
end;
fclose(fid); % Close the file
fid = fopen(USBts,'w'); fclose(fid); % Update successful, replace appropriate time stamp file
cd(basedir); % Return to base directory
end
%% genpath
% p = genpath(d)
% OUTPUT arguments
% <p>: a path string that includes all subdirectories of <d>
%
% INPUT arguments
% <d>: returns a path string starting in D, plus, recursively, all
% the subdirectories of D, including empty subdirectories.
%
% This function is based on Matlab's genpath function. It was written because
% the genpath function skips method directories (i.e. those beginning with @),
% and 'private' directories.
%
function p = genpath(d)
p = ''; % Initialize path string
files = dir(d); % Generate path based on given root directory
if isempty(files)
return;
end;
p = [p d pathsep]; % Add d to the path even if it is empty.
isdir = [files.isdir]; % Create logical vector for subdirectory entries in d
dirs = files(isdir); % Select only directory entries from the current list
for i = 1:length(dirs)
dirname = dirs(i).name;
if ~strcmp(dirname,'.') && ~strcmp(dirname,'..')
p = [p genpath(fullfile(d,dirname))]; % recursive calling of this function.
end;
end;
end
%% handleerror
% handleerror(errorin)
% INPUT arguments
% <errorin>: a structure containing at least the following two fields:
%
% message : the text of the error message
% identifier : the message identifier of the error message
%
% Additionally, <errorin> may contain field <stack>.
%
function handleerror(errorin)
try
fprintf('Message: %s\n',errorin.message);
fprintf('Identifier: %s\n',errorin.identifier);
fprintf('Stack:\n');
if(isfield(errorin,'stack'));
while(length(errorin.stack) >=1)
%fprintf(' %s\n',errorin.stack(1).file);
fprintf(' <%s> line: %d\n',errorin.stack(1).name,...
errorin.stack(1).line);
errorin.stack = errorin.stack(2:end);
end;
end;
catch
warning('handleerror:errorcaught','Error in function <handleerror>');
end
end