getFullPath - Get absolute canonical path of a file or folder Absolute path names are safer than relative paths, when e.g. a GUI or TIMER callback changes the current directory. Only canonical paths without "." and ".." can be recognized uniquely. Long path names (>259 characters) require a magic initial key "\\?\" to be handled by Windows API functions, e.g. for Matlab's FOPEN, DIR and EXIST. FullName = getFullPath(Name, Style) INPUT: Name: String or cell string, absolute or relative name of a file or folder. The path need not exist. Unicode strings, UNC paths and long names are supported. Style: Style of the output as string, optional, default: 'auto'. 'auto': Add '\\?\' or '\\?\UNC\' for long names on demand. 'lean': Magic string is not added. 'fat': Magic string is added for short names also. The Style is ignored when not running under Windows. OUTPUT: FullName: Absolute canonical path name as string or cell string. For empty strings the current directory is replied. '\\?\' or '\\?\UNC' is added on demand. NOTE: The M- and the MEX-version create the same results, the faster MEX function works under Windows only. Some functions of the Windows-API still do not support long file names. E.g. the Recycler and the Windows Explorer fail even with the magic '\\?\' prefix. Some functions of Matlab accept 260 characters (value of MAX_PATH), some at 259 already. Don't blame me. The 'fat' style is useful e.g. when Matlab's DIR command is called for a folder with les than 260 characters, but together with the file name this limit is exceeded. Then "dir(getFullPath([folder, '\*.*], 'fat'))" helps. EXAMPLES: cd(tempdir); % Assumed as 'C:\Temp' here getFullPath('File.Ext') % 'C:\Temp\File.Ext' getFullPath('..\File.Ext') % 'C:\File.Ext' getFullPath('..\..\File.Ext') % 'C:\File.Ext' getFullPath('.\File.Ext') % 'C:\Temp\File.Ext' getFullPath('*.txt') % 'C:\Temp\*.txt' getFullPath('..') % 'C:\' getFullPath('..\..\..') % 'C:\' getFullPath('Folder\') % 'C:\Temp\Folder\' getFullPath('D:\A\..\B') % 'D:\B' getFullPath('\\Server\Folder\Sub\..\File.ext') % '\\Server\Folder\File.ext' getFullPath({'..', 'new'}) % {'C:\', 'C:\Temp\new'} getFullPath('.', 'fat') % '\\?\C:\Temp\File.Ext' COMPILE: Automatic: InstallMex getFullPath.c uTest_getFullPath Manual: mex -O getFullPath.c Download: http://www.n-simon.de/mex Run the unit-test uTest_getFullPath after compiling. Tested: Matlab 6.5, 7.7, 7.8, 7.13, WinXP/32, Win7/64 Compiler: LCC2.4/3.8, BCC5.5, OWC1.8, MSVC2008/2010 Assumed Compatibility: higher Matlab versions Author: Jan Simon, Heidelberg, (C) 2009-2013 matlab.THISYEAR(a)nMINUSsimon.de See also: CD, FULLFILE, FILEPARTS.
0001 function File = getFullPath(File, Style) 0002 % getFullPath - Get absolute canonical path of a file or folder 0003 % Absolute path names are safer than relative paths, when e.g. a GUI or TIMER 0004 % callback changes the current directory. Only canonical paths without "." and 0005 % ".." can be recognized uniquely. 0006 % Long path names (>259 characters) require a magic initial key "\\?\" to be 0007 % handled by Windows API functions, e.g. for Matlab's FOPEN, DIR and EXIST. 0008 % 0009 % FullName = getFullPath(Name, Style) 0010 % INPUT: 0011 % Name: String or cell string, absolute or relative name of a file or 0012 % folder. The path need not exist. Unicode strings, UNC paths and long 0013 % names are supported. 0014 % Style: Style of the output as string, optional, default: 'auto'. 0015 % 'auto': Add '\\?\' or '\\?\UNC\' for long names on demand. 0016 % 'lean': Magic string is not added. 0017 % 'fat': Magic string is added for short names also. 0018 % The Style is ignored when not running under Windows. 0019 % 0020 % OUTPUT: 0021 % FullName: Absolute canonical path name as string or cell string. 0022 % For empty strings the current directory is replied. 0023 % '\\?\' or '\\?\UNC' is added on demand. 0024 % 0025 % NOTE: The M- and the MEX-version create the same results, the faster MEX 0026 % function works under Windows only. 0027 % Some functions of the Windows-API still do not support long file names. 0028 % E.g. the Recycler and the Windows Explorer fail even with the magic '\\?\' 0029 % prefix. Some functions of Matlab accept 260 characters (value of MAX_PATH), 0030 % some at 259 already. Don't blame me. 0031 % The 'fat' style is useful e.g. when Matlab's DIR command is called for a 0032 % folder with les than 260 characters, but together with the file name this 0033 % limit is exceeded. Then "dir(getFullPath([folder, '\*.*], 'fat'))" helps. 0034 % 0035 % EXAMPLES: 0036 % cd(tempdir); % Assumed as 'C:\Temp' here 0037 % getFullPath('File.Ext') % 'C:\Temp\File.Ext' 0038 % getFullPath('..\File.Ext') % 'C:\File.Ext' 0039 % getFullPath('..\..\File.Ext') % 'C:\File.Ext' 0040 % getFullPath('.\File.Ext') % 'C:\Temp\File.Ext' 0041 % getFullPath('*.txt') % 'C:\Temp\*.txt' 0042 % getFullPath('..') % 'C:\' 0043 % getFullPath('..\..\..') % 'C:\' 0044 % getFullPath('Folder\') % 'C:\Temp\Folder\' 0045 % getFullPath('D:\A\..\B') % 'D:\B' 0046 % getFullPath('\\Server\Folder\Sub\..\File.ext') 0047 % % '\\Server\Folder\File.ext' 0048 % getFullPath({'..', 'new'}) % {'C:\', 'C:\Temp\new'} 0049 % getFullPath('.', 'fat') % '\\?\C:\Temp\File.Ext' 0050 % 0051 % COMPILE: 0052 % Automatic: InstallMex getFullPath.c uTest_getFullPath 0053 % Manual: mex -O getFullPath.c 0054 % Download: http://www.n-simon.de/mex 0055 % Run the unit-test uTest_getFullPath after compiling. 0056 % 0057 % Tested: Matlab 6.5, 7.7, 7.8, 7.13, WinXP/32, Win7/64 0058 % Compiler: LCC2.4/3.8, BCC5.5, OWC1.8, MSVC2008/2010 0059 % Assumed Compatibility: higher Matlab versions 0060 % Author: Jan Simon, Heidelberg, (C) 2009-2013 matlab.THISYEAR(a)nMINUSsimon.de 0061 % 0062 % See also: CD, FULLFILE, FILEPARTS. 0063 0064 % $JRev: R-G V:032 Sum:7Xd/JS0+yfax Date:15-Jan-2013 01:06:12 $ 0065 % $License: BSD (use/copy/change/redistribute on own risk, mention the author) $ 0066 % $UnitTest: uTest_getFullPath $ 0067 % $File: Tools\GLFile\getFullPath.m $ 0068 % History: 0069 % 001: 20-Apr-2010 22:28, Successor of Rel2AbsPath. 0070 % 010: 27-Jul-2008 21:59, Consider leading separator in M-version also. 0071 % 011: 24-Jan-2011 12:11, Cell strings, '~File' under linux. 0072 % Check of input types in the M-version. 0073 % 015: 31-Mar-2011 10:48, BUGFIX: Accept [] as input as in the Mex version. 0074 % Thanks to Jiro Doke, who found this bug by running the test function for 0075 % the M-version. 0076 % 020: 18-Oct-2011 00:57, BUGFIX: Linux version created bad results. 0077 % Thanks to Daniel. 0078 % 024: 10-Dec-2011 14:00, Care for long names under Windows in M-version. 0079 % Improved the unittest function for Linux. Thanks to Paul Sexton. 0080 % 025: 09-Aug-2012 14:00, In MEX: Paths starting with "\\" can be non-UNC. 0081 % The former version treated "\\?\C:\<longpath>\file" as UNC path and 0082 % replied "\\?\UNC\?\C:\<longpath>\file". 0083 % 032: 12-Jan-2013 21:16, 'auto', 'lean' and 'fat' style. 0084 0085 % Initialize: ================================================================== 0086 % Do the work: ================================================================= 0087 0088 % ############################################# 0089 % ### USE THE MUCH FASTER MEX ON WINDOWS!!! ### 0090 % ############################################# 0091 0092 % Difference between M- and Mex-version: 0093 % - Mex does not work under MacOS/Unix. 0094 % - Mex calls Windows API function getFullPath. 0095 % - Mex is much faster. 0096 0097 % Magix prefix for long Windows names: 0098 if nargin < 2 0099 Style = 'auto'; 0100 end 0101 0102 % Handle cell strings: NOTE: It is faster to create a function 0103 % @cell\getFullPath.m under Linux, but under Windows this would shadow the 0104 % fast C-Mex. 0105 if isa(File, 'cell') 0106 for iC = 1:numel(File) 0107 File{iC} = getFullPath(File{iC}, Style); 0108 end 0109 return; 0110 end 0111 0112 % Check this once only: 0113 isWIN = strncmpi(computer, 'PC', 2); 0114 MAX_PATH = 260; 0115 0116 % Warn once per session (disable this under Linux/MacOS): 0117 persistent hasDataRead 0118 if isempty(hasDataRead) 0119 % Test this once only - there is no relation to the existence of 0120 % DATAREAD! 0121 %if isWIN 0122 % Show a warning, if the slower Matlab version is used - commented, 0123 % because this is not a problem and it might be even useful when the 0124 % MEX-folder is not inlcuded in the path yet. 0125 % warning('JSimon:getFullPath:NoMex', ... 0126 % ['getFullPath: Using slow Matlab-version instead of fast Mex.', 0127 % ... 0128 % char(10), 'Compile: InstallMex getFullPath.c']); 0129 %end 0130 0131 % DATAREAD is deprecated in 2011b, but still available. In Matlab 6.5, 0132 % REGEXP does not know the 'split' command, therefore DATAREAD is 0133 % preferred: 0134 hasDataRead = ~isempty(which('dataread')); 0135 end 0136 0137 if isempty(File) % Accept empty matrix as input: 0138 if ischar(File) || isnumeric(File) 0139 File = cd; 0140 return; 0141 else 0142 error(['JSimon:', mfilename, ':BadTypeInput1'], ... 0143 ['*** ', mfilename, ': Input must be a string or cell string']); 0144 end 0145 end 0146 0147 if ischar(File) == 0 % Non-empty inputs must be strings 0148 error(['JSimon:', mfilename, ':BadTypeInput1'], ... 0149 ['*** ', mfilename, ': Input must be a string or cell string']); 0150 end 0151 0152 if isWIN % Windows: -------------------------------------------------------- 0153 FSep = '\'; 0154 File = strrep(File, '/', FSep); 0155 0156 % Remove the magic key on demand, it is appended finally again: 0157 if strncmp(File, '\\?\', 4) 0158 if strncmpi(File, '\\?\UNC\', 8) 0159 File = ['\', File(7:length(File))]; % Two leading backslashes! 0160 else 0161 File = File(5:length(File)); 0162 end 0163 end 0164 0165 isUNC = strncmp(File, '\\', 2); 0166 FileLen = length(File); 0167 if isUNC == 0 % File is not a UNC path 0168 % Leading file separator means relative to current drive or base 0169 % folder: 0170 ThePath = cd; 0171 if File(1) == FSep 0172 if strncmp(ThePath, '\\', 2) % Current directory is a UNC path 0173 sepInd = strfind(ThePath, '\'); 0174 ThePath = ThePath(1:sepInd(4)); 0175 else 0176 ThePath = ThePath(1:3); % Drive letter only 0177 end 0178 end 0179 0180 if FileLen < 2 || File(2) ~= ':' % Does not start with drive letter 0181 if ThePath(length(ThePath)) ~= FSep 0182 if File(1) ~= FSep 0183 File = [ThePath, FSep, File]; 0184 else % File starts with separator: 0185 File = [ThePath, File]; 0186 end 0187 else % Current path ends with separator: 0188 if File(1) ~= FSep 0189 File = [ThePath, File]; 0190 else % File starts with separator: 0191 ThePath(length(ThePath)) = []; 0192 File = [ThePath, File]; 0193 end 0194 end 0195 0196 elseif FileLen == 2 && File(2) == ':' % "C:" current directory on C! 0197 % "C:" is the current directory on the C-disk, even if the 0198 % current directory is on another disk! This was ignored in 0199 % Matlab 6.5, but modern versions considers this strange 0200 % behaviour. 0201 if strncmpi(ThePath, File, 2) 0202 File = ThePath; 0203 else 0204 try 0205 File = cd(cd(File)); 0206 catch % No MException to support Matlab6.5... 0207 if exist(File, 'dir') % No idea what could cause an error then! 0208 rethrow(lasterror); 0209 else % Reply "K:\" for not existing disk: 0210 File = [File, FSep]; 0211 end 0212 end 0213 end 0214 end 0215 end 0216 0217 else % Linux, MacOS: --------------------------------------------------- 0218 FSep = '/'; 0219 File = strrep(File, '\', FSep); 0220 0221 if strcmp(File, '~') || strncmp(File, '~/', 2) % Home directory: 0222 HomeDir = getenv('HOME'); 0223 if ~isempty(HomeDir) 0224 File(1) = []; 0225 File = [HomeDir, File]; 0226 end 0227 0228 elseif strncmpi(File, FSep, 1) == 0 0229 % Append relative path to current folder: 0230 ThePath = cd; 0231 if ThePath(length(ThePath)) == FSep 0232 File = [ThePath, File]; 0233 else 0234 File = [ThePath, FSep, File]; 0235 end 0236 end 0237 end 0238 0239 % Care for "\." and "\.." - no efficient algorithm, but the fast Mex is 0240 % recommended at all! 0241 if ~isempty(strfind(File, [FSep, '.'])) 0242 if isWIN 0243 if strncmp(File, '\\', 2) % UNC path 0244 index = strfind(File, '\'); 0245 if length(index) < 4 % UNC path without separator after the folder: 0246 return; 0247 end 0248 Drive = File(1:index(4)); 0249 File(1:index(4)) = []; 0250 else 0251 Drive = File(1:3); 0252 File(1:3) = []; 0253 end 0254 else % Unix, MacOS: 0255 isUNC = false; 0256 Drive = FSep; 0257 File(1) = []; 0258 end 0259 0260 hasTrailFSep = (File(length(File)) == FSep); 0261 if hasTrailFSep 0262 File(length(File)) = []; 0263 end 0264 0265 if hasDataRead 0266 if isWIN % Need "\\" as separator: 0267 C = dataread('string', File, '%s', 'delimiter', '\\'); %#ok<REMFF1> 0268 else 0269 C = dataread('string', File, '%s', 'delimiter', FSep); %#ok<REMFF1> 0270 end 0271 else % Use the slower REGEXP, when DATAREAD is not available anymore: 0272 C = regexp(File, FSep, 'split'); 0273 end 0274 0275 % Remove '\.\' directly without side effects: 0276 C(strcmp(C, '.')) = []; 0277 0278 % Remove '\..' with the parent recursively: 0279 R = 1:length(C); 0280 for dd = reshape(find(strcmp(C, '..')), 1, []) 0281 index = find(R == dd); 0282 R(index) = []; 0283 if index > 1 0284 R(index - 1) = []; 0285 end 0286 end 0287 0288 if isempty(R) 0289 File = Drive; 0290 if isUNC && ~hasTrailFSep 0291 File(length(File)) = []; 0292 end 0293 0294 elseif isWIN 0295 % If you have CStr2String, use the faster: 0296 % File = CStr2String(C(R), FSep, hasTrailFSep); 0297 File = sprintf('%s\\', C{R}); 0298 if hasTrailFSep 0299 File = [Drive, File]; 0300 else 0301 File = [Drive, File(1:length(File) - 1)]; 0302 end 0303 0304 else % Unix: 0305 File = [Drive, sprintf('%s/', C{R})]; 0306 if ~hasTrailFSep 0307 File(length(File)) = []; 0308 end 0309 end 0310 end 0311 0312 % "Very" long names under Windows: 0313 if isWIN 0314 if ~ischar(Style) 0315 error(['JSimon:', mfilename, ':BadTypeInput2'], ... 0316 ['*** ', mfilename, ': Input must be a string or cell string']); 0317 end 0318 0319 if (strncmpi(Style, 'a', 1) && length(File) >= MAX_PATH) || ... 0320 strncmpi(Style, 'f', 1) 0321 % Do not use [isUNC] here, because this concerns the input, which 0322 % can '.\File', while the current directory is an UNC path. 0323 if strncmp(File, '\\', 2) % UNC path 0324 File = ['\\?\UNC', File(2:end)]; 0325 else 0326 File = ['\\?\', File]; 0327 end 0328 end 0329 end 0330 0331 % return; 0332 end