ParaMonte MATLAB 3.0.0
Parallel Monte Carlo and Machine Learning Library
See the latest version documentation.
abs.m
Go to the documentation of this file.
1%> \brief
2%> Return the Get absolute canonical path of a file or folder.<br>
3%>
4%> \details
5%> Absolute path names are safer than relative paths, e.g., when
6%> a GUI or TIMER callback changes the current directory.<br>
7%> Only canonical paths without ``"."`` and ``".."``
8%> can be recognized uniquely.<br>
9%>
10%> \note
11%> Long path names (>259 characters) require a magic initial
12%> key ``"\\?\"`` to be handled by Windows API functions, e.g. for
13%> MATLAB intrinsic routines ``fopen()``, ``dir()`` and ``exist()``.<br>
14%>
15%> \note
16%> Some functions of the Windows-API still do not support long file names.<br>
17%> For example, the Recycler and the Windows Explorer fail even with the magic ``'\\?\'`` prefix.<br>
18%> Some functions of MATLAB accept 260 characters (value of MAX_PATH), some at 259 already.<br>
19%> The ``'fat'`` style is useful e.g., when MATLAB ``dir()`` command is called for a folder
20%> with less than ``260`` characters, but together with the file name this limit is exceeded.<br>
21%> Then, ``"dir(pm.sys.path.abs([folder, '\*.*'], 'fat'))"`` helps.<br>
22%>
23%> \devnote
24%> A MEX version of this function developed by Jan Simon performs much faster on Windows.<br>
25%> Difference between M- and Mex-version:<br>
26%> <ol>
27%> <li> Mex does not work under MacOS/Unix.
28%> <li> Mex calls Windows API function pm.sys.path.abs.
29%> <li> Mex is much faster.
30%> </ol>
31%>
32%> \param[in] path : The input argument that can be either,<br>
33%> <ol>
34%> <li> a scalar MATLAB string or character, or,
35%> <li> a MATLAB string or cell array of strings or character values,
36%> </ol>
37%> containing the absolute or relative name(s) of a file(s) or folder(s).<br>
38%> The path need not exist. Unicode strings, UNC paths and long
39%> names are supported.<br>
40%> \param[in] style : The optional input scalar MATLAB string or character,
41%> containing the style of the output as string.<br>
42%> The following values are possible:<br>
43%> <ol>
44%> <li> ``'auto'`` : Add ``'\\?\'`` or ``'\\?\UNC\'`` for long names on demand.
45%> <li> ``'lean'`` : Magic string is not added.
46%> <li> ``'fat'`` : Magic string is added for short names also.
47%> </ol>
48%> The input ``style`` is ignored when not running under Windows.<br>
49%> (**optional**, default = 'auto')
50%>
51%> \return
52%> ``path`` : The output scalar MATLAB character string
53%> containing the absolute path corresponding to the input path.<br>
54%> If the input ``path`` is empty, ``pathAbs`` is the current directory.<br>
55%> The optional prefixes ``'\\?\'`` or ``'\\?\UNC'`` are added on demand
56%> as requested by the optional input argument ``style``.<br>
57%>
58%> \interface{abs}
59%> \code{.m}
60%>
61%> path = pm.sys.path.abs(file)
62%> path = pm.sys.path.abs(file, style)
63%>
64%> \endcode
65%>
66%> \see
67%> [A collection of MATLAB mex files for system path manipulation](http://www.n-simon.de/mex)<br>
68%>
69%> \example{abs-raw}
70%> \code{.m}
71%>
72%> cd(tempdir); % Assumed as 'C:\Temp' here
73%> pm.sys.path.abs('File.Ext') % 'C:\Temp\File.Ext'
74%> pm.sys.path.abs('..\File.Ext') % 'C:\File.Ext'
75%> pm.sys.path.abs('..\..\File.Ext') % 'C:\File.Ext'
76%> pm.sys.path.abs('.\File.Ext') % 'C:\Temp\File.Ext'
77%> pm.sys.path.abs('*.txt') % 'C:\Temp\*.txt'
78%> pm.sys.path.abs('..') % 'C:\'
79%> pm.sys.path.abs('..\..\..') % 'C:\'
80%> pm.sys.path.abs('Folder\') % 'C:\Temp\Folder\'
81%> pm.sys.path.abs('D:\A\..\B') % 'D:\B'
82%> pm.sys.path.abs('\\Server\Folder\Sub\..\File.ext') % '\\Server\Folder\File.ext'
83%> pm.sys.path.abs({'..', 'new'}) % {'C:\', 'C:\Temp\new'}
84%> pm.sys.path.abs(["..", "new"]) % ["C:\", "C:\Temp\new"]
85%> pm.sys.path.abs('.', 'fat') % '\\?\C:\Temp\File.Ext'
86%>
87%> \endcode
88%>
89%> \example{abs}
90%> \include{lineno} example/sys/path/abs/main.m
91%> \output{abs}
92%> \include{lineno} example/sys/path/abs/main.out.m
93%>
94%> \final{abs}
95%>
96%> This file is based on a functionality originally developed by Jan Simon.
97%>
98%> \verbatim
99%> Copyright (c) 2016, Jan Simon
100%> All rights reserved.
101%>
102%> Redistribution and use in source and binary forms, with or without
103%> modification, are permitted provided that the following conditions are met:
104%>
105%> * Redistributions of source code must retain the above copyright notice, this
106%> list of conditions and the following disclaimer.
107%>
108%> * Redistributions in binary form must reproduce the above copyright notice,
109%> this list of conditions and the following disclaimer in the documentation
110%> and/or other materials provided with the distribution
111%> * Neither the name of nor the names of its
112%> contributors may be used to endorse or promote products derived from this
113%> software without specific prior written permission.
114%> THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
115%> AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
116%> IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
117%> DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
118%> FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
119%> DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
120%> SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
121%> CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
122%> OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
123%> OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
124%>
125%> Tested: MATLAB 2009a, 2015b(32/64), 2016b, 2018b, Win7/10
126%> Compiler: LCC2.4/3.8, BCC5.5, OWC1.8, MSVC2008/2010
127%> Assumed Compatibility: higher MATLAB versions
128%> Author: Jan Simon, Heidelberg, (C) 2009-2019 matlab.2010(a)n(MINUS)simon.de
129%>
130%> $JRev: R-M V:038 Sum:C/6JMzUYsYsc Date:19-May-2019 17:25:55 $
131%> $License: BSD (use/copy/change/redistribute on own risk, mention the author) $
132%> $UnitTest: uTest_getFullPath $
133%> $File: Tools\GLFile\pm.sys.path.abs.m $
134%> History:
135%> 001: 20-Apr-2010 22:28, Successor of Rel2AbsPath.
136%> 010: 27-Jul-2008 21:59, Consider leading separator in M-version also.
137%> 011: 24-Jan-2011 12:11, Cell strings, '~File' under linux.
138%> Check of input types in the M-version.
139%> 015: 31-Mar-2011 10:48, BUGFIX: Accept [] as input as in the Mex version.
140%> Thanks to Jiro Doke, who found this bug by running the test function for
141%> the M-version.
142%> 020: 18-Oct-2011 00:57, BUGFIX: Linux version created bad results.
143%> Thanks to Daniel.
144%> 024: 10-Dec-2011 14:00, Care for long names under Windows in M-version.
145%> Improved the unittest function for Linux. Thanks to Paul Sexton.
146%> 025: 09-Aug-2012 14:00, In MEX: Paths starting with "\\" can be non-UNC.
147%> The former version treated "\\?\C:\<longpath>\file" as UNC path and
148%> replied "\\?\UNC\?\C:\<longpath>\file".
149%> 032: 12-Jan-2013 21:16, 'auto', 'lean' and 'fat' style.
150%> 038: 19-May-2019 17:25, BUGFIX, Thanks HHang Li, "File(7:..." -> "File(8:..."
151%> \endverbatim
152%> <br>
153%>
154%> \author
155%> \JoshuaOsborne, May 21 2024, 5:15 AM, University of Texas at Arlington<br>
156%> \FatemehBagheri, May 20 2024, 1:25 PM, NASA Goddard Space Flight Center (GSFC), Washington, D.C.<br>
157%> \AmirShahmoradi, May 16 2016, 9:03 AM, Oden Institute for Computational Engineering and Sciences (ICES), UT Austin<br>
158function path = abs(path, style)
159
160 %%%%
161 %%%% Set the magic prefix for long Windows names.
162 %%%%
163
164 if nargin < 2
165 style = 'auto';
166 else
167 style = convertStringsToChars(string(style));
168 end
169
170 if 1 < pm.array.len(path)
171 if iscell(path)
172 for iell = 1 : numel(path)
173 path{iell} = string(pm.sys.path.abs(path{iell}, style));
174 end
175 else
176 for iell = 1 : numel(path)
177 path(iell) = string(pm.sys.path.abs(path(iell), style));
178 end
179 end
180 return;
181 else
182 path = convertStringsToChars(string(path));
183 end
184
185 %%%%
186 %%%% Check this once only.
187 %%%%
188
189 isWindows = strncmpi(computer, 'PC', 2);
190 MAX_PATH = 260;
191
192 %%%%
193 %%%% Warn once per session (disable this under Linux/MacOS).
194 %%%%
195
196 persistent hasDataRead
197 if isempty(hasDataRead)
198 % Test this once only - there is no relation to the existence of DATAREAD!
199 %if isWindows
200 % Show a warning, if the slower MATLAB version is used - commented, because
201 % this is not a problem and it might be even useful when the MEX-folder is
202 % not included in the path yet.
203 % warning ( newline ...
204 % + 'pm.sys.path.abs:NoMex', ...
205 % + ['pm.sys.path.abs: Using slow MATLAB-version instead of fast Mex.', ...
206 % + char(10), 'Compile: InstallMex pm.sys.path.abs.c'] ...
207 % + newline ...
208 % );
209 %end
210 % DATAREAD is deprecated in 2011b, but still available. In MATLAB 6.5, REGEXP
211 % does not know the 'split' command, therefore DATAREAD is preferred.
212 hasDataRead = ~isempty(which('dataread'));
213 end
214
215 if isempty(path) % Accept empty matrix as input.
216 if ischar(path) || isnumeric(path)
217 path = cd;
218 return;
219 else
220 error ( newline ...
221 + "A non-empty input argument `path` must be a string, character, or array of such values." + newline ...
222 + newline ...
223 );
224 end
225 end
226
227 if ~ischar(path)
228 % Non-empty inputs must be strings
229 error ( newline ...
230 + "A non-empty input argument `path` must be a string, character, or array of such values." + newline ...
231 + newline ...
232 );
233 end
234
235 if isWindows % Windows: --------------------------------------------------------
236
237 dirsep = '\';
238 path = strrep(path, '/', dirsep);
239
240 %%%%
241 %%%% Remove the magic key on demand, it is appended finally again.
242 %%%%
243
244 if strncmp(path, '\\?\', 4)
245 if strncmpi(path, '\\?\UNC\', 8)
246 % [BUGFIX] 19-May-2019, Thanks HHang Li, "path(7:..." -> "path(8:..."
247 path = ['\', path(8 : length(path))]; % Two leading backslashes!
248 else
249 path = path(5 : length(path));
250 end
251 end
252
253 isUNC = strncmp(path, '\\', 2);
254 FileLen = length(path);
255 if isUNC == 0 % path is not a UNC path
256
257 %%%%
258 %%%% Leading file separator means relative to current drive or base folder.
259 %%%%
260
261 ThePath = cd;
262 if path(1) == dirsep
263 if strncmp(ThePath, '\\', 2)
264 % Current directory is a UNC path.
265 sepInd = strfind(ThePath, '\');
266 ThePath = ThePath(1 : sepInd(4));
267 else
268 % drive letter only.
269 ThePath = ThePath(1 : 3);
270 end
271 end
272
273 if FileLen < 2 || path(2) ~= ':'
274 % Does not start with drive letter.
275 if ThePath(length(ThePath)) ~= dirsep
276 if path(1) ~= dirsep
277 path = [ThePath, dirsep, path];
278 else
279 % path starts with separator.
280 path = [ThePath, path];
281 end
282 else
283 % Current path ends with separator.
284 if path(1) ~= dirsep
285 path = [ThePath, path];
286 else
287 % path starts with separator.
288 ThePath(length(ThePath)) = [];
289 path = [ThePath, path];
290 end
291 end
292 elseif FileLen == 2 && path(2) == ':'
293 % "C:" current directory on C!
294 % "C:" is the current directory on the C-disk, even if the current
295 % directory is on another disk! This was ignored in MATLAB 6.5, but
296 % modern versions considers this strange behaviour.
297 if strncmpi(ThePath, path, 2)
298 path = ThePath;
299 else
300 try
301 backCD = cd;
302 path = cd(cd(path));
303 cd(backCD);
304 catch me
305 if exist(path, 'dir')
306 % No idea what could cause an error then!
307 rethrow(me);
308 else
309 % Reply "K:\" for not existing disk.
310 path = [path, dirsep];
311 end
312 end
313 end
314 end
315 end
316
317 else % Linux, MacOS: ---------------------------------------------------
318
319 dirsep = '/';
320 path = strrep(path, '\', dirsep);
321
322 if strcmp(path, '~') || strncmp(path, '~/', 2)
323 % Home directory.
324 homedir = getenv('HOME');
325 if ~isempty(homedir)
326 path(1) = [];
327 path = [homedir, path];
328 end
329 elseif strncmpi(path, dirsep, 1) == 0
330 % Append relative path to current folder.
331 ThePath = cd;
332 if ThePath(length(ThePath)) == dirsep
333 path = [ThePath, path];
334 else
335 path = [ThePath, dirsep, path];
336 end
337 end
338
339 end
340
341 %%%%
342 %%%% Care for "\." and "\.." - no efficient algorithm, but the fast Mex is recommended at all!
343 %%%%
344
345 if ~isempty(strfind(path, [dirsep, '.']))
346
347 if isWindows
348 if strncmp(path, '\\', 2)
349 % UNC path
350 index = strfind(path, '\');
351 if length(index) < 4
352 % UNC path without separator after the folder.
353 return;
354 end
355 drive = path(1:index(4));
356 path(1:index(4)) = [];
357 else
358 drive = path(1:3);
359 path(1:3) = [];
360 end
361 else
362 % Unix, MacOS.
363 isUNC = false;
364 drive = dirsep;
365 path(1) = [];
366 end
367
368 hasTrailFSep = (path(length(path)) == dirsep);
369 if hasTrailFSep
370 path(length(path)) = [];
371 end
372
373 if hasDataRead
374 if isWindows
375 % Need "\\" as separator.
376 C = dataread('string', path, '%s', 'delimiter', '\\'); %#ok<REMFF1>
377 else
378 C = dataread('string', path, '%s', 'delimiter', dirsep); %#ok<REMFF1>
379 end
380 else
381 % Use the slower REGEXP, when DATAREAD is not available anymore.
382 C = regexp(path, dirsep, 'split');
383 end
384
385 % Remove '\.\' directly without side effects.
386 C(strcmp(C, '.')) = [];
387
388 % Remove '\..' with the parent recursively.
389 R = 1 : length(C);
390 for dd = reshape(find(strcmp(C, '..')), 1, [])
391 index = find(R == dd);
392 R(index) = [];
393 if index > 1
394 R(index - 1) = [];
395 end
396 end
397
398 if isempty(R)
399 path = drive;
400 if isUNC && ~hasTrailFSep
401 path(length(path)) = [];
402 end
403 elseif isWindows
404 % If you have CStr2String, use the faster.
405 % path = CStr2String(C(R), dirsep, hasTrailFSep);
406 path = sprintf('%s\\', C{R});
407 if hasTrailFSep
408 path = [drive, path];
409 else
410 path = [drive, path(1:length(path) - 1)];
411 end
412 else
413 % Unix.
414 path = [drive, sprintf('%s/', C{R})];
415 if ~hasTrailFSep
416 path(length(path)) = [];
417 end
418 end
419
420 end
421
422 %%%%
423 %%%% "Very" long names under Windows.
424 %%%%
425
426 if isWindows
427 if ~ischar(style)
428 error ( newline ...
429 + "The optional input argument `style` must be a string or character-valued." + newline ...
430 + newline ...
431 );
432 end
433 if (strncmpi(style, 'a', 1) && length(path) >= MAX_PATH) || ...
434 strncmpi(style, 'f', 1)
435 % Do not use [isUNC] here, because this concerns the input, which can
436 % '.\path', while the current directory is an UNC path.
437 if strncmp(path, '\\', 2) % UNC path
438 path = ['\\?\UNC', path(2:end)];
439 else
440 path = ['\\?\', path];
441 end
442 end
443 end
444
445end
function abs(in path, in style)
Return the Get absolute canonical path of a file or folder.