├── +stdlib ├── absolute.m ├── auto_chunk_size.m ├── canonical.m ├── checkRAM.m ├── cpu_arch.m ├── cpu_count.m ├── cpu_load.m ├── create_symlink.m ├── disk_available.m ├── disk_capacity.m ├── drop_slash.m ├── exists.m ├── expanduser.m ├── file_checksum.m ├── file_size.m ├── filename.m ├── filesystem_type.m ├── get_modtime.m ├── get_owner.m ├── get_permissions.m ├── get_pid.m ├── get_shell.m ├── get_username.m ├── h4exists.m ├── h4size.m ├── h4variables.m ├── h5create_group.m ├── h5exists.m ├── h5get_version.m ├── h5save.m ├── h5save_exist.m ├── h5save_new.m ├── h5size.m ├── h5variables.m ├── handle2filename.m ├── hard_link_count.m ├── has_java.m ├── homedir.m ├── hostname.m ├── ini2struct.m ├── is_absolute.m ├── is_admin.m ├── is_char_device.m ├── is_exe.m ├── is_parallel_worker.m ├── is_prefix.m ├── is_readable.m ├── is_regular_file.m ├── is_rosetta.m ├── is_subdir.m ├── is_symlink.m ├── is_url.m ├── is_writable.m ├── is_wsl.m ├── iscygwin.m ├── isinteractive.m ├── isoctave.m ├── java_api.m ├── java_vendor.m ├── java_version.m ├── join.m ├── makedir.m ├── md5sum.m ├── ncexists.m ├── ncsave.m ├── ncsave_exist.m ├── ncsave_new.m ├── ncsize.m ├── ncvariables.m ├── normalize.m ├── null_file.m ├── os_version.m ├── parent.m ├── posix.m ├── private │ ├── coerce_ds.m │ ├── defaultSize.m │ ├── file_attributes.m │ ├── h5save_scalar.m │ ├── jPosix.m │ ├── javaFileObject.m │ ├── javaLinkOption.m │ ├── javaOSBean.m │ ├── javaPathObject.m │ ├── javaSystemProperty.m │ └── strlength.m ├── proximate_to.m ├── ram_free.m ├── ram_total.m ├── read_symlink.m ├── relative_to.m ├── resolve.m ├── root.m ├── root_name.m ├── samepath.m ├── set_modtime.m ├── set_permissions.m ├── sha256sum.m ├── stem.m ├── subprocess_run.m ├── subprocess_run_octave.m ├── suffix.m ├── touch.m ├── unlink.m ├── version_atleast.m ├── which.m ├── windows_shortname.m └── with_suffix.m ├── .editorconfig ├── .gitattributes ├── .github └── workflows │ ├── ci-nojvm.yml │ ├── ci.yml │ ├── composite-buildtool │ └── action.yml │ ├── composite-install-matlab │ └── action.yml │ └── publish.yml ├── .gitignore ├── CITATION.cff ├── LICENSE ├── Readme.md ├── Readme_java.md ├── Readme_octave.md ├── archive ├── find_fortran_compiler.m ├── md5sum.m └── sha256sum.m ├── buildfile.m ├── codemeta.json ├── example ├── +javafun │ ├── canonical.m │ ├── exists.m │ ├── file_size.m │ ├── homedir.m │ ├── is_absolute.m │ ├── is_exe.m │ ├── is_readable.m │ ├── is_symlink.m │ ├── is_writable.m │ ├── join.m │ ├── normalize.m │ └── root.m ├── Filesystem.java ├── bench_absolute.m ├── bench_exists.m ├── bench_file_size.m ├── bench_homedir.m ├── bench_is_absolute.m ├── bench_is_exe.m ├── bench_is_readable.m ├── bench_is_writable.m ├── bench_join.m ├── bench_normalize.m ├── bench_parent.m ├── bench_stem.m ├── benchmark_get_owner.m ├── javaCreateSymbolicLink.m └── subprocess_run.java ├── matlab-stdlib.prj ├── octave_build.m ├── private └── publish_gen_index_html.m ├── scripts └── MatlabReleaseUpgrade.m ├── src ├── admin_fs.cpp ├── create_symlink.cpp ├── disk_available.cpp ├── disk_capacity.cpp ├── drop_slash.cpp ├── ffilesystem.h ├── is_admin.cpp ├── is_char_device.cpp ├── is_rosetta.cpp ├── is_symlink.cpp ├── is_wsl.cpp ├── linux_fs.cpp ├── macos.cpp ├── mex0.inl ├── mex1string.inl ├── mex2string.inl ├── normalize.cpp ├── normalize_fs.cpp ├── octave │ ├── disk_available.cpp │ ├── disk_capacity.cpp │ ├── drop_slash.cpp │ ├── is_admin.cpp │ ├── is_rosetta.cpp │ ├── is_wsl.cpp │ ├── normalize.cpp │ ├── parent.cpp │ ├── proximate_to.cpp │ ├── relative_to.cpp │ └── unlink.cpp ├── parent.cpp ├── parent_fs.cpp ├── proximate_to.cpp ├── pure.cpp ├── read_symlink.cpp ├── relative_to.cpp ├── set_permissions.cpp ├── symlink_fs.cpp ├── unlink.cpp ├── windows.cpp └── windows_shortname.cpp └── test ├── TestAbsolute.m ├── TestCanonical.m ├── TestDisk.m ├── TestExists.m ├── TestExpanduser.m ├── TestFileImpure.m ├── TestFilePure.m ├── TestFilename.m ├── TestHDF4.m ├── TestHDF5.m ├── TestHash.m ├── TestIni.m ├── TestIsAbsolute.m ├── TestIsExe.m ├── TestIsSubdir.m ├── TestJava.m ├── TestJoin.m ├── TestMex.m ├── TestNetCDF.m ├── TestNormalize.m ├── TestParent.m ├── TestPermissions.m ├── TestRelative.m ├── TestResolve.m ├── TestRoot.m ├── TestStem.m ├── TestSubprocess.m ├── TestSuffix.m ├── TestSymlink.m ├── TestSys.m ├── TestVersion.m ├── TestWhich.m ├── TestWindowsCOM.m ├── TestWithSuffix.m ├── example.ini ├── printenv.cpp ├── sleep.cpp ├── stdin_cpp.cpp ├── stdin_fortran.f90 ├── stdout_stderr_c.c └── stdout_stderr_fortran.f90 /+stdlib/absolute.m: -------------------------------------------------------------------------------- 1 | %% ABSOLUTE Determine absolute path 2 | % c = absolute(p); 3 | % Path "p" need not exist. 4 | % Absolute path will be relative to pwd if path does not exist. 5 | % 6 | % c = absolute(p, base); 7 | % the "base" path is used instead of pwd. 8 | % 9 | %%% Inputs 10 | % * p: path to make absolute 11 | % * base: if present, base on this instead of cwd 12 | % * expand_tilde: expand ~ to username if present 13 | %%% Outputs 14 | % * c: absolute path 15 | % 16 | % does not normalize path 17 | % non-existant path is made absolute relative to pwd 18 | 19 | function c = absolute(p, base, expand_tilde) 20 | arguments 21 | p {mustBeTextScalar} 22 | base {mustBeTextScalar} = '' 23 | expand_tilde (1,1) logical = true 24 | end 25 | 26 | if expand_tilde 27 | c = stdlib.expanduser(p); 28 | else 29 | c = stdlib.posix(p); 30 | end 31 | 32 | if stdlib.is_absolute(c) 33 | return 34 | end 35 | 36 | if strlength(base) == 0 37 | b = pwd(); 38 | elseif expand_tilde 39 | b = stdlib.expanduser(base); 40 | else 41 | b = base; 42 | end 43 | 44 | if ~stdlib.is_absolute(b) 45 | b = strcat(pwd(), '/', b); 46 | end 47 | 48 | b = stdlib.posix(b); 49 | 50 | if strlength(c) == 0 51 | c = b; 52 | else 53 | c = strcat(b, '/', c); 54 | end 55 | 56 | if isstring(p) 57 | c = string(c); 58 | end 59 | 60 | end 61 | 62 | 63 | %!assert(absolute('', '', false), posix(pwd)) 64 | %!assert(absolute('a/b', '', false), posix(strcat(pwd(), '/a/b'))) 65 | -------------------------------------------------------------------------------- /+stdlib/auto_chunk_size.m: -------------------------------------------------------------------------------- 1 | %% AUTO_CHUNK_SIZE determine HDF5 / NetCDF4 chunk size 2 | % 3 | % based on https://github.com/h5py/h5py/blob/master/h5py/_hl/filters.py 4 | % refer to https://support.hdfgroup.org/HDF5/Tutor/layout.html 5 | % 6 | %%% Inputs 7 | % * dims: proposed dataset dimensions (like size()) 8 | 9 | function csize = auto_chunk_size(dims) 10 | arguments 11 | dims (1,:) {mustBeInteger,mustBePositive} 12 | end 13 | 14 | CHUNK_BASE = 16000; % Multiplier by which chunks are adjusted 15 | CHUNK_MIN = 8000; % lower limit: 8 kbyte 16 | CHUNK_MAX = 1000000; % upper limit: 1 Mbyte 17 | TYPESIZE = 8; % bytes, assume real64 for simplicity 18 | 19 | csize = dims; 20 | 21 | if isscalar(dims) || prod(dims) * TYPESIZE < CHUNK_MIN 22 | return 23 | end 24 | 25 | dset_size = prod(csize) * TYPESIZE; 26 | target_size = CHUNK_BASE * (2.*log10(dset_size / 1e6)); 27 | if (target_size > CHUNK_MAX) 28 | target_size = CHUNK_MAX; 29 | end 30 | 31 | % print *,'target_size [bytes]: ',target_size 32 | 33 | i = 0; 34 | while true 35 | % Repeatedly loop over the axes, dividing them by 2. 36 | % Stop when: 37 | % 1a. We're smaller than the target chunk size, OR 38 | % 1b. We're within 50% of the target chunk size, AND 39 | % 2. The chunk is smaller than the maximum chunk size 40 | 41 | chunk_bytes = prod(csize) * TYPESIZE; 42 | 43 | if chunk_bytes < target_size || ... 44 | 2*(abs(chunk_bytes-target_size) / target_size) < 1 && ... 45 | chunk_bytes < CHUNK_MAX 46 | break 47 | end 48 | 49 | if prod(csize) == 1 50 | break 51 | end 52 | % Element size larger than CHUNK_MAX 53 | j = mod(i, length(dims)) + 1; 54 | csize(j) = ceil(csize(j) / 2); 55 | i = i+1; 56 | end 57 | 58 | end 59 | 60 | %!assert(auto_chunk_size([15,250,100]), [2,32,25]) 61 | -------------------------------------------------------------------------------- /+stdlib/canonical.m: -------------------------------------------------------------------------------- 1 | %% CANONICAL Canonicalize path 2 | % c = canonical(p); 3 | % If exists, canonical absolute path is returned. 4 | % if any component of path does not exist, normalized relative path is returned. 5 | % UNC paths are not canonicalized. 6 | % 7 | % This also resolves Windows short paths to long paths. 8 | % 9 | %%% Inputs 10 | % * p: path to make canonical 11 | % * expand_tilde: expand ~ to username if present 12 | %%% Outputs 13 | % * c: canonical path, if determined 14 | 15 | function c = canonical(p, expand_tilde) 16 | arguments 17 | p {mustBeTextScalar} 18 | expand_tilde (1,1) logical = true 19 | end 20 | 21 | c = ""; 22 | 23 | if expand_tilde 24 | e = stdlib.expanduser(p); 25 | else 26 | e = p; 27 | end 28 | 29 | if ~strlength(e) || (ispc && (startsWith(e, "\\") || startsWith(e, "//"))) 30 | % UNC path is not canonicalized 31 | return 32 | end 33 | 34 | if stdlib.isoctave() 35 | % empty if any component of path does not exist 36 | c = canonicalize_file_name(e); 37 | else 38 | % errors if any component of path does not exist. 39 | % disp("builtin") 40 | try %#ok 41 | c = builtin('_canonicalizepath', e); 42 | end 43 | end 44 | 45 | if ~strlength(c) 46 | c = stdlib.normalize(e); 47 | end 48 | 49 | c = string(stdlib.posix(c)); 50 | 51 | end 52 | 53 | %!assert(canonical("", 1), "") 54 | %!assert(canonical("~", 1), homedir()) 55 | -------------------------------------------------------------------------------- /+stdlib/checkRAM.m: -------------------------------------------------------------------------------- 1 | %% CHECKRAM estimate if RAM will fit a new array 2 | % checks that requested memory for the new array won't exceed AVAILABLE RAM with Matlab 3 | % 4 | % This script is optimistic as Matlab won't always be able to 5 | % create an array using ALL available RAM, but at least you know when you 6 | % certainly CAN'T create an array without digging deep into swap or worse. 7 | 8 | function [OK,newSizeBytes,freebytes] = checkRAM(newSize, myclass) 9 | arguments 10 | newSize (1,:) {mustBeNumeric} 11 | myclass {mustBeTextScalar} 12 | end 13 | 14 | % get available RAM 15 | freebytes = stdlib.ram_free(); 16 | % variable sizing 17 | switch(myclass) 18 | case {'single','int32','uint32'}, bits = 32; 19 | case {'double','int64','uint64','float'}, bits = 64; 20 | case {'int16','uint16'}, bits = 16; 21 | case {'int8','uint8'}, bits = 8; 22 | case {'logical','bool'}, bits = 1; 23 | case {'string','char'}, bits = 8; % FIXME is this correct? 24 | otherwise, error('unhandled variable class: %s', myclass) 25 | end 26 | 27 | newSizeBytes = prod(newSize)*bits / 8; 28 | 29 | OK = newSizeBytes < freebytes; 30 | 31 | end 32 | 33 | %!assert(checkRAM([15,2,1], 'double'), true) 34 | -------------------------------------------------------------------------------- /+stdlib/cpu_arch.m: -------------------------------------------------------------------------------- 1 | %% CPU_ARCH get the CPU architecture 2 | % requires: java 3 | 4 | function arch = cpu_arch() 5 | 6 | arch = javaSystemProperty("os.arch"); 7 | 8 | end 9 | 10 | %!assert(!isempty(cpu_arch())) 11 | -------------------------------------------------------------------------------- /+stdlib/cpu_count.m: -------------------------------------------------------------------------------- 1 | %% CPU_COUNT how many CPUs are available 2 | function N = cpu_count() 3 | 4 | N = maxNumCompThreads; 5 | 6 | if N < 2 && ~stdlib.isoctave() % happens on some HPC 7 | N = feature('NumCores'); 8 | end 9 | 10 | % logical CPUs 11 | % https://docs.oracle.com/en/java/javase/22/docs/api/java.base/java/lang/Runtime.html#getRuntime() 12 | % N=java.lang.Runtime.getRuntime().availableProcessors(); 13 | end 14 | 15 | %!assert(cpu_count() > 0) 16 | -------------------------------------------------------------------------------- /+stdlib/cpu_load.m: -------------------------------------------------------------------------------- 1 | %% CPU_LOAD get total physical CPU load 2 | % requires: java 3 | % 4 | % Returns the "recent cpu usage" for the whole system. 5 | % 6 | % This value is a double in the [0.0,1.0] interval. 7 | % A value of 0.0 means that all CPUs were idle during the recent period of time observed, while a value of 1.0 means that all CPUs were actively running 100% of the time during the recent period being observed. 8 | % All values betweens 0.0 and 1.0 are possible depending of the activities going on in the system. 9 | % If the system recent cpu usage is not available, the method returns a negative value. 10 | 11 | function L = cpu_load() 12 | 13 | b = javaOSBean(); 14 | 15 | if stdlib.java_api() < 14 16 | % https://docs.oracle.com/en/java/javase/21/docs/api/jdk.management/com/sun/management/OperatingSystemMXBean.html#getSystemCpuLoad() 17 | L = b.getSystemCpuLoad(); 18 | else 19 | % https://docs.oracle.com/en/java/javase/21/docs/api/jdk.management/com/sun/management/OperatingSystemMXBean.html#getCpuLoad() 20 | L = b.getCpuLoad(); 21 | end 22 | 23 | end 24 | 25 | %!assert(cpu_load()>=0) 26 | %!assert(cpu_load()<=1) 27 | -------------------------------------------------------------------------------- /+stdlib/create_symlink.m: -------------------------------------------------------------------------------- 1 | %% CREATE_SYMLINK create symbolic link 2 | % optional: mex 3 | % 4 | %%% Inputs 5 | % * target: path to link to 6 | % * link: path to create link at 7 | %%% Outputs 8 | % * ok: true if successful 9 | 10 | function ok = create_symlink(target, link) 11 | arguments 12 | target {mustBeTextScalar} 13 | link {mustBeTextScalar} 14 | end 15 | 16 | 17 | try 18 | createSymbolicLink(link, target); 19 | ok = true; 20 | catch e 21 | switch e.identifier 22 | case {"MATLAB:io:filesystem:symlink:NeedsAdminPerms", "MATLAB:UndefinedFunction"} 23 | % windows requires RunAsAdmin 24 | % https://www.mathworks.com/help/releases/R2024b/matlab/ref/createsymboliclink.html 25 | % ok = java.nio.file.Files.createSymbolicLink(java.io.File(link).toPath(), java.io.File(target).toPath()); 26 | % Matlab Java doesn't recognize the optional argument omitted. 27 | % see example/Filesystem.java for this working in plain Java. 28 | % see example/javaCreateSymbolicLink.m for a non-working attempt in Matlab. 29 | warning(e.identifier, "buildtool mex \n%s", e.message) 30 | ok = false; 31 | case "Octave:undefined-function" 32 | err = symlink(target, link); 33 | ok = err == 0; 34 | otherwise 35 | warning(e.identifier, "%s", e.message) 36 | ok = false; 37 | end 38 | end 39 | 40 | end 41 | 42 | %!assert (create_symlink("https://invalid", "https://invalid"), false) 43 | %!test 44 | %! if !ispc 45 | %! assert(create_symlink(tempname, tempname)) 46 | %! endif 47 | -------------------------------------------------------------------------------- /+stdlib/disk_available.m: -------------------------------------------------------------------------------- 1 | %% DISK_AVAILABLE disk available space (bytes) 2 | % requires: mex or java 3 | % 4 | % example: stdlib.disk_available('/') 5 | % 6 | % Ref: https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/io/File.html#getUsableSpace() 7 | 8 | function f = disk_available(d) 9 | arguments 10 | d {mustBeTextScalar} 11 | end 12 | 13 | f = javaFileObject(d).getUsableSpace(); 14 | 15 | f = uint64(f); 16 | 17 | end 18 | 19 | %!assert (disk_available('.') > 0) 20 | -------------------------------------------------------------------------------- /+stdlib/disk_capacity.m: -------------------------------------------------------------------------------- 1 | %% DISK_CAPACITY disk total capacity (bytes) 2 | % requires: mex or java 3 | % 4 | % example: stdlib.disk_capacity('/') 5 | 6 | function f = disk_capacity(d) 7 | arguments 8 | d {mustBeTextScalar} 9 | end 10 | 11 | f = javaFileObject(d).getTotalSpace(); 12 | 13 | f = uint64(f); 14 | 15 | end 16 | 17 | %!assert (disk_capacity('.') > 0) 18 | -------------------------------------------------------------------------------- /+stdlib/drop_slash.m: -------------------------------------------------------------------------------- 1 | %% DROP_SLASH drop repeated and trailing slash 2 | % optional: mex 3 | % 4 | % on Windows, if leading double slash, do not drop 5 | 6 | function d = drop_slash(p) 7 | arguments 8 | p {mustBeTextScalar} 9 | end 10 | 11 | s = stdlib.posix(p); 12 | 13 | uncslash = ispc && startsWith(s, '//'); 14 | 15 | % drop repeated slashes inside string 16 | d = regexprep(s, '/+', '/'); 17 | 18 | L = strlength(d); 19 | 20 | if L < 2 21 | if uncslash 22 | d = '//'; 23 | end 24 | elseif ~ispc || (L ~= 3 || ~strcmp(d, stdlib.root(s))) 25 | d = regexprep(d, '/$', ''); 26 | end 27 | 28 | if uncslash 29 | d = strcat('/', d); 30 | end 31 | 32 | if isstring(p) 33 | d = string(d); 34 | end 35 | 36 | end 37 | 38 | %!assert(drop_slash(''), '') 39 | %!assert(drop_slash('/'), '/') 40 | %!assert(drop_slash('a//b'), 'a/b') 41 | %!assert(drop_slash('a//b/'), 'a/b') 42 | -------------------------------------------------------------------------------- /+stdlib/exists.m: -------------------------------------------------------------------------------- 1 | %% EXISTS does path exist 2 | % 3 | % NOTE: in general on Windows exists("./not-exist/..") is true, but on 4 | % Unix it is false. 5 | % In C/C++ access() or stat() the same behavior is observed Windows vs Unix. 6 | % 7 | %%% Inputs 8 | % * p: path to check 9 | %%% Outputs 10 | % * ok: true if exists 11 | 12 | function y = exists(p) 13 | arguments 14 | p {mustBeTextScalar} 15 | end 16 | 17 | % Matlab >= R2024b allowed URLs to act like files or folders. 18 | % fileattrib() does not consider URLs to be a file or folder 19 | % at least through Matlab R2025a. 20 | 21 | y = strlength(p) && fileattrib(p) == 1; 22 | 23 | end 24 | 25 | %!assert (!exists('')) 26 | %!assert (!exists(tempname)) 27 | %!assert (exists(program_invocation_name)) 28 | -------------------------------------------------------------------------------- /+stdlib/expanduser.m: -------------------------------------------------------------------------------- 1 | %% EXPANDUSER expands tilde ~ into user home directory 2 | % 3 | % Useful for Matlab functions that can't handle ~ 4 | % 5 | %%% Inputs 6 | % * p: path to expand, if tilde present 7 | %%% Outputs 8 | % * e: expanded path 9 | 10 | function e = expanduser(p) 11 | arguments 12 | p {mustBeTextScalar} 13 | end 14 | 15 | e = stdlib.posix(char(p)); 16 | 17 | L = length(e); 18 | if L == 0 || e(1) ~= '~' || (L > 1 && ~strcmp(e(1:2), '~/')) 19 | % noop 20 | else 21 | home = stdlib.homedir(); 22 | if isempty(home) 23 | % noop 24 | elseif L < 2 25 | e = home; 26 | else 27 | e = strcat(home, '/', e(3:end)); 28 | end 29 | end 30 | 31 | if isstring(p) 32 | e = string(e); 33 | end 34 | 35 | end 36 | 37 | 38 | %!assert(expanduser(''), '') 39 | %!assert(expanduser("~"), homedir()) 40 | %!assert(expanduser("~/"), homedir()) 41 | %!assert(expanduser("~user"), "~user") 42 | %!assert(expanduser("~user/"), "~user") 43 | %!assert(expanduser("~///c"), strcat(homedir(), "/c")) 44 | -------------------------------------------------------------------------------- /+stdlib/file_checksum.m: -------------------------------------------------------------------------------- 1 | %% FILE_CHECKSUM compute hash of file 2 | % requires: java 3 | % 4 | % read in chunks to avoid excessive RAM use 5 | % 6 | %%% Inputs 7 | % * file: file or to hash 8 | % * method: "MD5", "SHA-1", "SHA-256", etc. 9 | %%% Outputs 10 | % * hash: string hash 11 | % 12 | % Ref: https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/security/MessageDigest.html#getInstance(java.lang.String) 13 | 14 | function hash = file_checksum(file, method) 15 | arguments 16 | file {mustBeFile} 17 | method {mustBeTextScalar} 18 | end 19 | 20 | if strcmp(method, "sha256") || strcmp(method, "SHA256") 21 | method = "SHA-256"; 22 | end 23 | 24 | file_chunk = 10e6; % arbitrary (bytes) didn't seem to be very sensitive for speed 25 | 26 | if stdlib.isoctave() 27 | inst = javaMethod("getInstance", "java.security.MessageDigest", method); 28 | else 29 | inst = java.security.MessageDigest.getInstance(method); 30 | end 31 | 32 | fid = fopen(file, 'r'); 33 | assert(fid > 1, "could not open file %s", file) 34 | 35 | while ~feof(fid) 36 | % https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/security/MessageDigest.html#update(byte) 37 | inst.update(fread(fid, file_chunk, '*uint8')); 38 | end 39 | fclose(fid); 40 | 41 | % https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/security/MessageDigest.html#digest() 42 | hash = typecast(inst.digest, 'uint8'); 43 | 44 | hash = sprintf('%.2x', hash); 45 | 46 | end 47 | 48 | %!assert(!isempty(file_checksum('file_checksum.m', "sha256"))) 49 | -------------------------------------------------------------------------------- /+stdlib/file_size.m: -------------------------------------------------------------------------------- 1 | %% FILE_SIZE size in bytes of file 2 | % 3 | %%% Inputs 4 | % * p: path to file 5 | %%% Outputs 6 | % * s: size in bytes, or empty if file does not exist 7 | 8 | function s = file_size(p) 9 | arguments 10 | p {mustBeTextScalar,mustBeFile} 11 | end 12 | 13 | if stdlib.isoctave() 14 | s = stat(p); 15 | if ~isempty(s) 16 | s = s.size; 17 | end 18 | else 19 | s = dir(p); 20 | if ~isempty(s) 21 | s = s.bytes; 22 | end 23 | end 24 | 25 | end 26 | 27 | 28 | %!assert (isempty(file_size(''))) 29 | %!assert (file_size('file_size.m') > 0) 30 | -------------------------------------------------------------------------------- /+stdlib/filename.m: -------------------------------------------------------------------------------- 1 | %% FILENAME file name of path 2 | % 3 | %%% Inputs 4 | % p: path to extract filename from 5 | %%% Outputs 6 | % filename (including suffix) without directory 7 | 8 | function f = filename(p) 9 | arguments 10 | p {mustBeTextScalar} 11 | end 12 | 13 | % NOT https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/io/File.html#getName() 14 | % because by our definition, a trailing directory component is not part of the filename 15 | % this is like C++17 filesystem::path::filename 16 | 17 | parts = strsplit(stdlib.posix(p), '/'); 18 | 19 | f = parts{end}; 20 | 21 | if isstring(p) 22 | f = string(f); 23 | end 24 | 25 | end 26 | 27 | 28 | %!assert (filename('a/b/c.txt'), 'c.txt') 29 | %!assert (filename('a/b/'), '') 30 | -------------------------------------------------------------------------------- /+stdlib/filesystem_type.m: -------------------------------------------------------------------------------- 1 | %% FILESYSTEM_TYPE tell type of filesystem 2 | % requires: java 3 | % 4 | % example outputs: ntfs, ext4, apfs, ... 5 | % 6 | % if empty output or error, try specifying the drive root 7 | % like "/" or "C:/" 8 | 9 | function t = filesystem_type(p) 10 | arguments 11 | p {mustBeTextScalar} = '' 12 | end 13 | 14 | assert(stdlib.exists(p), "Path does not exist: %s", p); 15 | 16 | op = javaPathObject(p); 17 | 18 | if stdlib.isoctave() 19 | t = javaMethod("getFileStore", "java.nio.file.Files", op).type; 20 | else 21 | t = java.nio.file.Files.getFileStore(op).type.string; 22 | end 23 | 24 | end 25 | 26 | %!assert(!isempty(filesystem_type(pwd))) 27 | -------------------------------------------------------------------------------- /+stdlib/get_modtime.m: -------------------------------------------------------------------------------- 1 | %% GET_MODTIME get path modification time 2 | % requires: java 3 | % 4 | %%% Inputs 5 | % * p: path to examine 6 | %%% Outputs 7 | % * t: modification time, or empty if path does not exist 8 | 9 | function t = get_modtime(p) 10 | arguments 11 | p {mustBeTextScalar} 12 | end 13 | 14 | 15 | if stdlib.isoctave() 16 | s = stat(p); 17 | if isempty(s) 18 | t = []; 19 | else 20 | t = s.mtime; 21 | end 22 | return 23 | end 24 | 25 | t = javaFileObject(p).lastModified() / 1000; 26 | 27 | if t > 0 28 | t = datetime(t, "ConvertFrom", "PosixTime"); 29 | else 30 | t = datetime.empty; 31 | end 32 | 33 | end 34 | 35 | %!test 36 | %! p = tempname(); 37 | %! assert(touch(p, [])) 38 | %! assert(get_modtime(p) > 0) 39 | %! delete(p) 40 | -------------------------------------------------------------------------------- /+stdlib/get_owner.m: -------------------------------------------------------------------------------- 1 | %% GET_OWNER owner of file or directory 2 | % requires: java 3 | % 4 | %%% Inputs 5 | % * p: path to examine 6 | %%% Outputs 7 | % * n: owner, or empty if path does not exist 8 | function n = get_owner(p) 9 | arguments 10 | p {mustBeTextScalar} 11 | end 12 | 13 | % https://docs.oracle.com/en/java/javase/22/docs/api/java.base/java/nio/file/Files.html#getOwner(java.nio.file.Path,java.nio.file.LinkOption...) 14 | % https://docs.oracle.com/en/java/javase/22/docs/api/java.base/java/nio/file/LinkOption.html 15 | 16 | assert(stdlib.exists(p), "Path %s does not exist.", p) 17 | 18 | op = javaPathObject(p); 19 | opt = javaLinkOption(); 20 | 21 | if stdlib.isoctave() 22 | n = javaMethod("getOwner", "java.nio.file.Files", op, opt).toString(); 23 | else 24 | n = java.nio.file.Files.getOwner(op, opt).string; 25 | end 26 | 27 | end 28 | 29 | %!assert(!isempty(get_owner(pwd))) 30 | %!assert(isempty(get_owner(tempname))) 31 | -------------------------------------------------------------------------------- /+stdlib/get_permissions.m: -------------------------------------------------------------------------------- 1 | %% GET_PERMISSIONS permissions of file or directory 2 | % 3 | % output is char like 'rwxrwxr--' 4 | 5 | function p = get_permissions(f) 6 | arguments 7 | f {mustBeTextScalar} 8 | end 9 | 10 | p = ''; 11 | 12 | try 13 | v = filePermissions(f); 14 | catch e 15 | switch e.identifier 16 | case "MATLAB:io:filesystem:filePermissions:CannotFindLocation", return 17 | case "Octave:undefined-function" 18 | try %#ok 19 | p = stat(f).modestr; 20 | end 21 | return 22 | case "MATLAB:UndefinedFunction" 23 | v = file_attributes(f); 24 | if isempty(v), return, end 25 | otherwise, rethrow(e) 26 | end 27 | end 28 | 29 | p = perm2char(v); 30 | 31 | end 32 | 33 | 34 | function p = perm2char(v) 35 | 36 | p = '---------'; 37 | 38 | if isa(v, "matlab.io.WindowsPermissions") || isa(v, "matlab.io.UnixPermissions") 39 | if v.Readable, p(1) = 'r'; end 40 | if v.Writable, p(2) = 'w'; end 41 | elseif isstruct(v) 42 | if v.UserRead, p(1) = 'r'; end 43 | if v.UserWrite, p(2) = 'w'; end 44 | else 45 | % cloud / remote locations we don't handle 46 | p = []; 47 | return 48 | end 49 | 50 | 51 | if isfield(v, 'UserExecute') || isa(v, "matlab.io.UnixPermissions") 52 | if v.UserExecute, p(3) = 'x'; end 53 | elseif ispc && (isstruct(v) || isa(v, "matlab.io.WindowsPermissions")) 54 | % on Windows, any readable file has executable permission 55 | if p(1) == 'r', p(3) = 'x'; end 56 | end 57 | 58 | 59 | if isstruct(v) || isa(v, "matlab.io.UnixPermissions") 60 | 61 | if v.GroupRead, p(4) = 'r'; end 62 | if v.GroupWrite, p(5) = 'w'; end 63 | if v.GroupExecute, p(6) = 'x'; end 64 | if v.OtherRead, p(7) = 'r'; end 65 | if v.OtherWrite, p(8) = 'w'; end 66 | if v.OtherExecute, p(9) = 'x'; end 67 | 68 | end 69 | 70 | end 71 | 72 | %!assert(length(get_permissions('get_permissions.m')) == 9) 73 | -------------------------------------------------------------------------------- /+stdlib/get_pid.m: -------------------------------------------------------------------------------- 1 | %% GET_PID process ID of Matlab session 2 | 3 | function pid = get_pid() 4 | 5 | try 6 | pid = matlabProcessID; 7 | catch e 8 | switch e.identifier 9 | case "MATLAB:UndefinedFunction", pid = uint64(feature("getpid")); 10 | case "Octave:undefined-function", pid = uint64(getpid()); 11 | otherwise, rethrow(e) 12 | end 13 | end 14 | 15 | end 16 | 17 | %!assert (get_pid() > 0, "expected positive PID") 18 | -------------------------------------------------------------------------------- /+stdlib/get_shell.m: -------------------------------------------------------------------------------- 1 | %% GET_SHELL shell of the current user 2 | % return value of environment variable SHELL 3 | % this is mostly relevant on unix-like systems 4 | 5 | function s = get_shell() 6 | 7 | s = getenv("SHELL"); 8 | 9 | end 10 | 11 | %!assert(ischar(get_shell())) 12 | -------------------------------------------------------------------------------- /+stdlib/get_username.m: -------------------------------------------------------------------------------- 1 | %% GET_USERNAME tell username of current user 2 | % requires: java 3 | % 4 | function n = get_username() 5 | 6 | n = javaSystemProperty("user.name"); 7 | 8 | end 9 | 10 | %!assert(!isempty(get_username())) 11 | -------------------------------------------------------------------------------- /+stdlib/h4exists.m: -------------------------------------------------------------------------------- 1 | function exists = h4exists(file, variable) 2 | %% H4EXISTS check if object exists in HDF4 file 3 | % 4 | %%% Inputs 5 | % * file: data filename 6 | % * variable: path of variable in file 7 | %%% Outputs 8 | % * exists: boolean 9 | arguments 10 | file {mustBeTextScalar} 11 | variable {mustBeTextScalar} 12 | end 13 | 14 | sds = hdfinfo(file).SDS; 15 | i = string(sds.Name) == variable; 16 | exists = any(i); 17 | 18 | 19 | %!testif 0 20 | -------------------------------------------------------------------------------- /+stdlib/h4size.m: -------------------------------------------------------------------------------- 1 | %% H4SIZE get size (shape) of a HDF4 variable 2 | % 3 | %%% Inputs 4 | % filename: data filename 5 | % variable: name of variable inside file 6 | %%% Outputs 7 | % fsize: vector of variable size per dimension. Empty if scalar variable. 8 | 9 | function fsize = h4size(file, variable) 10 | arguments 11 | file {mustBeTextScalar} 12 | variable {mustBeTextScalar} 13 | end 14 | 15 | sds = hdfinfo(file).SDS; 16 | 17 | i = string(sds.Name) == variable; 18 | if ~all(i) 19 | error(variable + " is not an SDS in " + file) 20 | end 21 | 22 | fsize = cell2mat({sds(i).Dims.Size}); 23 | 24 | end 25 | 26 | %!testif 0 27 | -------------------------------------------------------------------------------- /+stdlib/h4variables.m: -------------------------------------------------------------------------------- 1 | %% H4VARIABLES get HDF4 dataset names 2 | % get dataset names in a file under group 3 | % default is datasets under "/", optionally under "/group" 4 | % 5 | %%% Inputs 6 | % * file: filename 7 | % * group: group name (optional) 8 | %%% Outputs 9 | % * names: variable names 10 | 11 | function names = h4variables(file) 12 | arguments 13 | file {mustBeTextScalar} 14 | end 15 | 16 | finf = hdfinfo(file); 17 | 18 | ds = finf.SDS; 19 | 20 | names = string({ds.Name}); 21 | 22 | end 23 | 24 | %!testif 0 25 | -------------------------------------------------------------------------------- /+stdlib/h5create_group.m: -------------------------------------------------------------------------------- 1 | %% H5CREATE_GROUP create HDF5 group 2 | % 3 | %%% Inputs 4 | % * file: HDF5 file (string or H5ML.id) 5 | % * hpath: HDF5 group/dataset -- ensure final character is "/" if hpath is only a group 6 | %%% Outputs 7 | % * HDF5 file handle 8 | 9 | function fid = h5create_group(file, hpath) 10 | arguments 11 | file (1,1) 12 | hpath (1,1) string 13 | end 14 | 15 | % polymorphic fid/filename 16 | if isa(file, 'H5ML.id') 17 | fid = file; 18 | else 19 | % avoid confusing creating file ./~/a.h5 20 | file = stdlib.expanduser(file); 21 | assert(strlength(file) > 0, 'h5create_group:filename_error', "filename must be non-empty") 22 | dcpl = 'H5P_DEFAULT'; 23 | if isfile(file) 24 | fid = H5F.open(file, 'H5F_ACC_RDWR', dcpl); 25 | else 26 | fid = H5F.create(file); 27 | end 28 | end 29 | 30 | % are there any groups 31 | grps = split(hpath, "/"); 32 | if length(grps) < 3 33 | return 34 | end 35 | 36 | % recursively create groups as needed 37 | plist = 'H5P_DEFAULT'; 38 | groot = H5G.open(fid, "/"); 39 | 40 | for i = 0:length(grps) - 3 41 | n = join(grps(1:i+2), "/"); 42 | 43 | if ~H5L.exists(groot, n, plist) 44 | gid = H5G.create(fid, n, plist, plist, plist); 45 | H5G.close(gid) 46 | end 47 | end % for 48 | 49 | H5G.close(groot) 50 | 51 | if nargout == 0 52 | H5F.close(fid); 53 | clear('fid') 54 | end 55 | 56 | end 57 | 58 | 59 | %!testif 0 60 | -------------------------------------------------------------------------------- /+stdlib/h5exists.m: -------------------------------------------------------------------------------- 1 | %% H5EXISTS check if object exists 2 | % 3 | %%% Inputs 4 | % * file: data filename 5 | % * variable: path of variable in file 6 | %%% Outputs 7 | % * exists: boolean 8 | 9 | function exists = h5exists(file, variable) 10 | arguments 11 | file {mustBeTextScalar} 12 | variable {mustBeTextScalar} 13 | end 14 | 15 | exists = false; 16 | 17 | try 18 | h5info(file, variable); 19 | exists = true; 20 | catch e 21 | if ~strcmp(e.identifier, 'MATLAB:imagesci:h5info:unableToFind') && ... 22 | ~strncmp(e.message, "h5info: location", 16) 23 | disp(e) 24 | rethrow(e) 25 | end 26 | end 27 | 28 | end 29 | 30 | %!test 31 | %! if !isempty(pkg('list', 'hdf5oct')) 32 | %! pkg load hdf5oct 33 | %! fn = tempname(); 34 | %! ds = '/a'; 35 | %! h5create(fn, ds, [1]) 36 | %! assert(h5exists(fn, ds)) 37 | %! assert(!h5exists(fn, '/b')) 38 | %! delete(fn) 39 | %! endif 40 | -------------------------------------------------------------------------------- /+stdlib/h5get_version.m: -------------------------------------------------------------------------------- 1 | %% H5GET_VERSION get version of HDF5 library as a string 2 | 3 | function v = h5get_version() 4 | 5 | [major, minor, rel] = H5.get_libversion(); 6 | 7 | v = sprintf("%d.%d.%d", major, minor, rel); 8 | 9 | end 10 | 11 | %!testif 0 -------------------------------------------------------------------------------- /+stdlib/h5save.m: -------------------------------------------------------------------------------- 1 | %% H5SAVE save data to HDF5 file 2 | % create or append to data file 3 | % 4 | % parent folder (file directory) must already exist 5 | % 6 | %%% Inputs 7 | % * filename: data filename 8 | % * varname: variable name to save 9 | % * A: data to write 10 | % * opts.size: variable shape -- helps write scalar or vectors especially 11 | % * opts.type: class of variable e.g. int32, float32 12 | % 13 | % The shape of the dataset can be controlled by specifying the "size" argument. 14 | % This is particularly useful when writing HDF5 files to be used in other programming languages where dimensional shapes are important. 15 | % Matlab may collapse singleton dimensions otherwise. 16 | % h5save(filename, dataset_name, dataset, size=[3,1]) 17 | % 18 | % Likewise, the type of the dataset may be explicitly specified with the "type" argument. 19 | % h5save(filename, dataset_name, dataset, type="int32") 20 | 21 | function h5save(filename, varname, A, opts) 22 | arguments 23 | filename {mustBeTextScalar} 24 | varname {mustBeTextScalar} 25 | A {mustBeNonempty} 26 | opts.size (1,:) double {mustBeInteger,mustBeNonnegative} = [] 27 | opts.type {mustBeTextScalar} = '' 28 | opts.compressLevel (1,1) double {mustBeInteger,mustBeNonnegative} = 0 29 | end 30 | 31 | if isnumeric(A) 32 | mustBeReal(A) 33 | end 34 | 35 | % avoid confusing creating file ./~/a.h5 36 | filename = stdlib.expanduser(filename); 37 | 38 | % coerce if needed 39 | A = coerce_ds(A, opts.type); 40 | 41 | if isfile(filename) 42 | if stdlib.h5exists(filename, varname) 43 | stdlib.h5save_exist(filename, varname, A, opts.size) 44 | else 45 | stdlib.h5save_new(filename, varname, A, opts.size, opts.compressLevel) 46 | end 47 | else 48 | stdlib.h5save_new(filename, varname, A, opts.size, opts.compressLevel) 49 | end 50 | 51 | end 52 | 53 | 54 | %!testif 0 55 | -------------------------------------------------------------------------------- /+stdlib/h5save_exist.m: -------------------------------------------------------------------------------- 1 | %% H5SAVE_EXIST write data to existing HDF5 dataset 2 | % normally users use h5save() instead of this function 3 | 4 | function h5save_exist(filename, varname, A, sizeA) 5 | arguments 6 | filename {mustBeTextScalar} 7 | varname {mustBeTextScalar} 8 | A {mustBeNonempty} 9 | sizeA (1,:) double {mustBeInteger,mustBeNonnegative} = [] 10 | end 11 | 12 | diskshape = stdlib.h5size(filename, varname); 13 | if length(diskshape) >= 2 14 | % start is always a row vector, regardless of shape of array 15 | start = ones(1, ndims(A)); 16 | elseif ~isempty(diskshape) 17 | start = 1; 18 | end 19 | 20 | if isempty(sizeA) 21 | sizeA = defaultSize(A); 22 | elseif all(sizeA > 0) 23 | assert(numel(A) == prod(sizeA), 'h5save:shape_error', "dataset # of elements %d != prod(sizeA) %d", numel(A), prod(sizeA)) 24 | elseif ~isscalar(sizeA) 25 | error('h5save:shape_error', "only scalar size may be 0") 26 | end 27 | 28 | if isscalar(A) 29 | h5write(filename, varname, A) 30 | elseif all(diskshape == sizeA) 31 | h5write(filename, varname, A, start, sizeA) 32 | elseif all(diskshape == fliplr(sizeA)) 33 | h5write(filename, varname, A.', start, fliplr(sizeA)) 34 | else 35 | error('h5save:value_error', ['shape of ',varname,': ', int2str(sizeA), ' does not match existing HDF5 shape ', int2str(diskshape)]) 36 | end 37 | 38 | end 39 | 40 | %!test 41 | %! if !isempty(pkg('list', 'hdf5oct')) 42 | %! pkg load hdf5oct 43 | %! fn = tempname(); 44 | %! ds = '/a'; 45 | %! a = [1,2]; 46 | %! b = [3,4]; 47 | %! h5save_new(fn, ds, a, size(a), 0) 48 | %! h5save_exist(fn, ds, b, size(b)) 49 | %! assert(h5read(fn, ds), b) 50 | %! delete(fn) 51 | %! endif 52 | -------------------------------------------------------------------------------- /+stdlib/h5save_new.m: -------------------------------------------------------------------------------- 1 | %% H5SAVE_NEW Save a variable to an new HDF5 dataset 2 | % normally users will use h5save() instead of this function 3 | 4 | function h5save_new(filename, varname, A, sizeA, compressLevel) 5 | arguments 6 | filename {mustBeTextScalar} 7 | varname {mustBeTextScalar} 8 | A {mustBeNonempty} 9 | sizeA (1,:) double {mustBeInteger,mustBeNonnegative} = [] 10 | compressLevel (1,1) double {mustBeInteger,mustBeNonnegative} = 0 11 | end 12 | 13 | if isempty(sizeA) 14 | sizeA = defaultSize(A); 15 | elseif all(sizeA > 0) 16 | assert(numel(A) == prod(sizeA), 'h5save:shape_error', "dataset # of elements %d != prod(sizeA) %d", numel(A), prod(sizeA)) 17 | elseif ~isscalar(sizeA) 18 | error('h5save:shape_error', "only scalar size may be 0") 19 | end 20 | 21 | assert(strlength(filename) > 0, 'h5save:filename_error', "filename must be non-empty") 22 | 23 | if isscalar(sizeA) 24 | if sizeA == 0 25 | h5save_scalar(filename, varname, A) 26 | return 27 | else 28 | h5create(filename, varname, sizeA, "Datatype", class(A)) 29 | end 30 | elseif ~compressLevel || stdlib.isoctave() 31 | h5create(filename, varname, sizeA, "Datatype", class(A)) 32 | else 33 | h5create(filename, varname, sizeA, "Datatype", class(A), "Fletcher32", true, "Shuffle", true, ... 34 | "Chunksize", stdlib.auto_chunk_size(sizeA), ... 35 | "Deflate", compressLevel) 36 | end 37 | 38 | h5write(filename, varname, A) 39 | 40 | end 41 | 42 | %!test 43 | %! if !isempty(pkg('list', 'hdf5oct')) 44 | %! pkg load hdf5oct 45 | %! fn = tempname(); 46 | %! ds = '/a'; 47 | %! a = [1,2]; 48 | %! h5save_new(fn, ds, a, size(a), 0) 49 | %! assert(h5read(fn, ds), a) 50 | %! delete(fn) 51 | %! endif 52 | -------------------------------------------------------------------------------- /+stdlib/h5size.m: -------------------------------------------------------------------------------- 1 | %% H5SIZE get shape of HDF5 variable 2 | % 3 | %%% Inputs 4 | % filename: data filename 5 | % variable: name of variable inside file 6 | %%% Outputs 7 | % fsize: vector of variable size per dimension. Empty if scalar variable. 8 | 9 | function fsize = h5size(file, variable) 10 | arguments 11 | file {mustBeTextScalar} 12 | variable {mustBeTextScalar} 13 | end 14 | 15 | dsi = h5info(file, variable).Dataspace; 16 | 17 | if ~stdlib.isoctave() && dsi.Type == "scalar" 18 | fsize = []; 19 | else 20 | fsize = dsi.Size; 21 | end 22 | 23 | end 24 | 25 | 26 | %!test 27 | %! if !isempty(pkg('list', 'hdf5oct')) 28 | %! pkg load hdf5oct 29 | %! fn = tempname(); 30 | %! ds = '/a'; 31 | %! a = [1,2]; 32 | %! h5save_new(fn, ds, a, size(a), 0) 33 | %! assert(h5size(fn, ds), uint64([1,2])) 34 | %! delete(fn) 35 | %! endif 36 | -------------------------------------------------------------------------------- /+stdlib/h5variables.m: -------------------------------------------------------------------------------- 1 | %% H5VARIABLES get HDF5 dataset names 2 | % get dataset names in a file under group 3 | % default is datasets under "/", optionally under "/group" 4 | % 5 | %%% Inputs 6 | % * file: filename 7 | % * group: group name (optional) 8 | %%% Outputs 9 | % * names: variable names 10 | 11 | function names = h5variables(file, group) 12 | arguments 13 | file {mustBeTextScalar} 14 | group {mustBeTextScalar} = '' 15 | end 16 | 17 | if ~strlength(group) 18 | finf = h5info(file); 19 | else 20 | finf = h5info(file, group); 21 | end 22 | 23 | ds = finf.Datasets; 24 | 25 | if isempty(ds) 26 | names = []; 27 | else 28 | names = {ds.Name}; 29 | end 30 | 31 | try %#ok 32 | names = string(names); 33 | end 34 | 35 | end 36 | 37 | 38 | %!test 39 | %! if !isempty(pkg('list', 'hdf5oct')) 40 | %! pkg load hdf5oct 41 | %! fn = tempname(); 42 | %! ds = '/a'; 43 | %! h5create(fn, ds, [1]) 44 | %! assert(h5variables(fn, ''), {'/a'}) 45 | %! delete(fn) 46 | %! endif 47 | -------------------------------------------------------------------------------- /+stdlib/handle2filename.m: -------------------------------------------------------------------------------- 1 | %% HANDLE2FILENAME Convert a file handle to a filename 2 | % requires: java 3 | 4 | function n = handle2filename(fileHandle) 5 | arguments 6 | fileHandle (1,1) {mustBeInteger} 7 | end 8 | 9 | n = ''; 10 | 11 | if fileHandle >= 0 12 | n = stdlib.posix(fopen(fileHandle)); 13 | end 14 | 15 | end 16 | 17 | %!assert(handle2filename(0), "stdin") 18 | -------------------------------------------------------------------------------- /+stdlib/hard_link_count.m: -------------------------------------------------------------------------------- 1 | %% HARD_LINK_COUNT get the number of hard links to a file 2 | % requires: java 3 | % 4 | % Ref: 5 | % * https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/nio/file/Files.html#getPosixFileAttributes(java.nio.file.Path,java.nio.file.LinkOption...) 6 | % * https://docs.oracle.com/javase/tutorial/essential/io/links.html 7 | 8 | function c = hard_link_count(p) 9 | arguments 10 | p {mustBeTextScalar} 11 | end 12 | 13 | c = []; 14 | 15 | if stdlib.isoctave() 16 | s = stat(p); 17 | if ~isempty(s) 18 | c = s.nlink; 19 | end 20 | elseif ispc || ~isfile(p) 21 | return 22 | else 23 | c = java.nio.file.Files.getAttribute(javaPathObject(p), "unix:nlink", javaLinkOption()); 24 | end 25 | 26 | end 27 | 28 | %!test 29 | %! if ispc, return; end 30 | %! assert(hard_link_count('hard_link_count.m') >= 1) 31 | -------------------------------------------------------------------------------- /+stdlib/has_java.m: -------------------------------------------------------------------------------- 1 | %% HAS_JAVA detect if JVM is available 2 | % requires: java 3 | 4 | function ok = has_java() 5 | 6 | persistent h; 7 | 8 | if isempty(h) 9 | h = usejava('jvm'); 10 | end 11 | 12 | ok = h; 13 | 14 | end 15 | 16 | %!assert(islogical(has_java())) 17 | -------------------------------------------------------------------------------- /+stdlib/homedir.m: -------------------------------------------------------------------------------- 1 | %% HOMEDIR get user home directory 2 | 3 | function h = homedir() 4 | 5 | if ispc 6 | h = getenv("USERPROFILE"); 7 | else 8 | h = getenv("HOME"); 9 | end 10 | 11 | h = stdlib.posix(h); 12 | 13 | end 14 | 15 | %!assert(!isempty(homedir())) 16 | -------------------------------------------------------------------------------- /+stdlib/hostname.m: -------------------------------------------------------------------------------- 1 | 2 | %% HOSTNAME get hostname of local machine 3 | % requires: java 4 | % 5 | % Ref: https://docs.oracle.com/javase/8/docs/api/java/net/InetAddress.html#getHostName-- 6 | 7 | function n = hostname() 8 | 9 | if stdlib.isoctave() 10 | n = gethostname(); 11 | else 12 | n = string(java.net.InetAddress.getLocalHost().getHostName()); 13 | end 14 | 15 | end 16 | 17 | %!assert (!isempty(hostname())) 18 | -------------------------------------------------------------------------------- /+stdlib/ini2struct.m: -------------------------------------------------------------------------------- 1 | %% INI2STRUCT Parses .ini file 2 | % Returns a structure with section names and keys as fields. 3 | % 4 | % Based on init2struct.m by Andriy Nych 5 | 6 | function Struct = ini2struct(filename) 7 | arguments 8 | filename {mustBeTextScalar,mustBeFile} 9 | end 10 | 11 | f = fopen(filename,'r'); % open file 12 | 13 | while ~feof(f) % and read until it ends 14 | s = strtrim(fgetl(f)); % remove leading/trailing spaces 15 | if is_comment(s) % skip empty & comments lines 16 | continue 17 | end 18 | if s(1)=='[' % section header 19 | Section = matlab.lang.makeValidName(strtok(s(2:end), ']')); 20 | Struct.(Section) = []; % create field 21 | continue 22 | end 23 | 24 | [Key,Val] = strtok(s, '='); % Key = Value ; comment 25 | Val = strtrim(Val(2:end)); % remove spaces after = 26 | 27 | if is_comment(Val) % empty entry 28 | Val = []; 29 | elseif Val(1)=='"' % double-quoted string 30 | Val = strtok(Val, '"'); 31 | elseif Val(1)=='''' % single-quoted string 32 | Val = strtok(Val, ''''); 33 | else 34 | Val = strtok(Val, ";"); % remove inline comment 35 | Val = strtok(Val, "#"); % remove inline comment 36 | Val = strtrim(Val); % remove spaces before comment 37 | 38 | Val = double(string(Val)); 39 | % convert string to number(s) 40 | end 41 | 42 | if ~exist('Section', 'var') % No section found before 43 | Struct.(matlab.lang.makeValidName(Key)) = Val; 44 | else % Section found before, fill it 45 | Struct.(Section).(matlab.lang.makeValidName(Key)) = Val; 46 | end 47 | 48 | end 49 | 50 | fclose(f); 51 | 52 | end 53 | 54 | 55 | function i = is_comment(line) 56 | % a comment line detected 57 | 58 | i = isempty(line) || startsWith(line, [";", "#"]); 59 | 60 | end 61 | 62 | %!testif 0 63 | 64 | % Copyright (c) 2014, freeb 65 | % Copyright (c) 2008, Andriy Nych 66 | % Copyright (c) 2009-2010, Evgeny Prilepin aka Iroln 67 | % All rights reserved. 68 | 69 | % Redistribution and use in source and binary forms, with or without 70 | % modification, are permitted provided that the following conditions are 71 | % met: 72 | 73 | % * Redistributions of source code must retain the above copyright 74 | % notice, this list of conditions and the following disclaimer. 75 | % * Redistributions in binary form must reproduce the above copyright 76 | % notice, this list of conditions and the following disclaimer in 77 | % the documentation and/or other materials provided with the distribution 78 | % * Neither the name of the nor the names 79 | % of its contributors may be used to endorse or promote products derived 80 | % from this software without specific prior written permission. 81 | 82 | % THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 83 | % AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 84 | % IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 85 | % ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 86 | % LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 87 | % CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 88 | % SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 89 | % INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 90 | % CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 91 | % ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 92 | % POSSIBILITY OF SUCH DAMAGE. 93 | -------------------------------------------------------------------------------- /+stdlib/is_absolute.m: -------------------------------------------------------------------------------- 1 | %% IS_ABSOLUTE is path absolute 2 | % 3 | % * Windows, absolute paths must be at least 3 character long, starting with a root name followed by a slash 4 | % 5 | % * non-Windows, absolute paths must start with a slash 6 | 7 | function y = is_absolute(p) 8 | arguments 9 | p (1,:) char 10 | end 11 | 12 | % not Octave is_absolute_filename() because this is a stricter check for "c:" false 13 | 14 | y = false; 15 | 16 | L = strlength(p); 17 | if ~L || (ispc && L < 3) 18 | return 19 | end 20 | 21 | if ispc 22 | y = strlength(stdlib.root_name(p)) && any(p(3) == ['/', '\']); 23 | else 24 | y = p(1) == '/'; 25 | end 26 | 27 | end 28 | 29 | %!assert(is_absolute(''), false) 30 | %!test 31 | %! if ispc 32 | %! assert(is_absolute('C:\')) 33 | %! assert(is_absolute('C:/')) 34 | %! assert(!is_absolute('C:')) 35 | %! assert(!is_absolute('C')) 36 | %! else 37 | %! assert(is_absolute('/')) 38 | %! assert(is_absolute('/usr')) 39 | %! assert(!is_absolute('usr')) 40 | %! endif 41 | -------------------------------------------------------------------------------- /+stdlib/is_admin.m: -------------------------------------------------------------------------------- 1 | %% IS_ADMIN is the process run as root / admin 2 | % requires: mex 3 | 4 | function is_admin() 5 | error("buildtool mex") 6 | end 7 | -------------------------------------------------------------------------------- /+stdlib/is_char_device.m: -------------------------------------------------------------------------------- 1 | %% IS_CHAR_DEVICE is path a character device 2 | % requires: mex 3 | % 4 | % e.g. NUL, /dev/null 5 | % false if file does not exist 6 | 7 | function ok = is_char_device(p) 8 | arguments 9 | p {mustBeTextScalar} 10 | end 11 | 12 | if stdlib.isoctave() 13 | ok = S_ISCHR(stat(p).mode); 14 | else 15 | error("buildtool mex") 16 | end 17 | 18 | end 19 | 20 | %!assert (!is_exe('')) 21 | %!test 22 | %! if ispc 23 | %! n = "NUL"; 24 | %! else 25 | %! n = "/dev/null"; 26 | %! end 27 | %! assert (is_char_device(n)) 28 | -------------------------------------------------------------------------------- /+stdlib/is_exe.m: -------------------------------------------------------------------------------- 1 | %% IS_EXE is file executable 2 | % 3 | % false if file does not exist 4 | 5 | function ok = is_exe(p) 6 | arguments 7 | p {mustBeTextScalar} 8 | end 9 | 10 | a = file_attributes(p); 11 | 12 | ok = ~isempty(a) && (a.UserExecute || a.GroupExecute || a.OtherExecute); 13 | 14 | end 15 | 16 | %!assert (!is_exe('')) 17 | %!assert (!is_exe(tempname)) 18 | %!assert (is_exe(".")) 19 | %!assert (is_exe(program_invocation_name)) 20 | -------------------------------------------------------------------------------- /+stdlib/is_parallel_worker.m: -------------------------------------------------------------------------------- 1 | %% IS_PARALLEL_WORKER being executed by Parallel Computing Toolbox? 2 | % detects if being executed by Parallel Computing Toolbox 3 | % e.g. in a parfor loop 4 | % 5 | % Reference: https://www.mathworks.com/help/parallel-computing/getcurrenttask.html 6 | % 7 | % test by: 8 | % >> is_parallel_worker 9 | % 10 | % ans = logical 0 11 | % 12 | % >> parfor i = 1:1, is_parallel_worker, end 13 | % Starting parallel pool (parpool) using the 'local' profile ... 14 | % 15 | % ans = logical 1 16 | 17 | function ispar = is_parallel_worker() 18 | 19 | ispar = false; 20 | 21 | try 22 | addons = matlab.addons.installedAddons(); 23 | catch 24 | return 25 | end 26 | 27 | if any(contains(addons.Name, 'Parallel Computing Toolbox')) 28 | ispar = ~isempty(getCurrentWorker()); 29 | end 30 | 31 | end 32 | 33 | %!testif 0 34 | -------------------------------------------------------------------------------- /+stdlib/is_prefix.m: -------------------------------------------------------------------------------- 1 | %% IS_PREFIX is prefix a prefix of path? 2 | % canonicalization and normalization are NOT performed 3 | % duplicated slashes are dropped 4 | 5 | function s = is_prefix(prefix, pth) 6 | arguments 7 | prefix {mustBeTextScalar} 8 | pth {mustBeTextScalar} 9 | end 10 | 11 | pr = stdlib.drop_slash(prefix); 12 | p = stdlib.drop_slash(pth); 13 | 14 | Lp = strlength(p); 15 | 16 | s = Lp && startsWith(p, pr) && (Lp >= strlength(pr)); 17 | 18 | end 19 | 20 | %!assert(is_prefix("a", "a")) 21 | %!assert(is_prefix("a", "a/")) 22 | %!assert(is_prefix("a", "a/b")) 23 | %!assert(!is_prefix("a/b/c", "a/b")) 24 | -------------------------------------------------------------------------------- /+stdlib/is_readable.m: -------------------------------------------------------------------------------- 1 | %% IS_READABLE is file readable 2 | % 3 | % non-existant file is false 4 | 5 | function ok = is_readable(p) 6 | arguments 7 | p {mustBeTextScalar} 8 | end 9 | 10 | a = file_attributes(p); 11 | 12 | ok = ~isempty(a) && (a.UserRead || a.GroupRead || a.OtherRead); 13 | 14 | end 15 | 16 | %!assert (is_readable('is_readable.m')) 17 | %!assert (!is_readable('')) 18 | -------------------------------------------------------------------------------- /+stdlib/is_regular_file.m: -------------------------------------------------------------------------------- 1 | %% IS_REGULAR_FILE check if path is a regular file 2 | % requires: java 3 | 4 | function r = is_regular_file(p) 5 | arguments 6 | p {mustBeTextScalar} 7 | end 8 | 9 | % needs absolute() 10 | p = stdlib.absolute(p, '', false); 11 | 12 | op = javaPathObject(p); 13 | opt = javaLinkOption(); 14 | 15 | if stdlib.isoctave() 16 | r = javaMethod("isRegularFile", "java.nio.file.Files", op, opt); 17 | else 18 | r = java.nio.file.Files.isRegularFile(op, opt); 19 | end 20 | 21 | end 22 | 23 | %!assert(is_regular_file('is_regular_file.m')) 24 | -------------------------------------------------------------------------------- /+stdlib/is_rosetta.m: -------------------------------------------------------------------------------- 1 | %% IS_ROSETTA on Apple Silicon via Rosetta 2 | % optional: mex 3 | % 4 | % true if Matlab on Apple Silicon CPU is built for Intel x86_64 5 | 6 | function r = is_rosetta() 7 | r = false; 8 | 9 | if ~ismac 10 | return 11 | end 12 | 13 | % uname -m reports "x86_64" from within Matlab on Apple Silicon if using Rosetta 14 | 15 | [ret, raw] = system("sysctl -n sysctl.proc_translated"); 16 | r = ret == 0 && startsWith(raw, '1'); 17 | 18 | end 19 | 20 | %!assert(islogical(is_rosetta())) 21 | %!test 22 | %! if ~ismac 23 | %! assert(!is_rosetta()) 24 | %! endif 25 | -------------------------------------------------------------------------------- /+stdlib/is_subdir.m: -------------------------------------------------------------------------------- 1 | %% IS_SUBDIR is subdir a subdirectory of dir? 2 | % canonicalization and normalization are NOT performed 3 | % duplicated slashes are dropped 4 | 5 | function s = is_subdir(subdir, dir) 6 | arguments 7 | subdir {mustBeTextScalar} 8 | dir {mustBeTextScalar} 9 | end 10 | 11 | s = stdlib.drop_slash(subdir); 12 | d = stdlib.drop_slash(dir); 13 | 14 | s = startsWith(s, d) && (strlength(s) > strlength(d)); 15 | 16 | end 17 | 18 | %!assert(!is_subdir("/a/b", "/a/b")) 19 | %!assert(!is_subdir("/a/b", "/a/b/c")) 20 | %!assert(!is_subdir("/a/b", "/a/b/c/")) 21 | %!assert(!is_subdir("/a/b", "d")) 22 | %!assert(is_subdir("a/b", "a")) 23 | %!assert(!is_subdir("a", "a/.c")) 24 | %!assert(!is_subdir("a/./b/c", "a/b")) 25 | 26 | % this is incorrect on Windows at least %assert(is_subdir("a/b", "a/b/..")) 27 | -------------------------------------------------------------------------------- /+stdlib/is_symlink.m: -------------------------------------------------------------------------------- 1 | %% IS_SYMLINK is path a symbolic link 2 | % optional: mex 3 | 4 | function ok = is_symlink(p) 5 | arguments 6 | p {mustBeTextScalar} 7 | end 8 | 9 | 10 | try 11 | ok = isSymbolicLink(p); 12 | catch e 13 | switch e.identifier 14 | case "MATLAB:UndefinedFunction", ok = java.nio.file.Files.isSymbolicLink(javaPathObject(stdlib.absolute(p, "", false))); 15 | case "Octave:undefined-function", ok = S_ISLNK(stat(p).mode); 16 | otherwise, rethrow(e) 17 | end 18 | end 19 | 20 | end 21 | 22 | %!test 23 | %! if !ispc 24 | %! p = tempname(); 25 | %! assert(create_symlink(mfilename("fullpath"), p)) 26 | %! assert(is_symlink(p)) 27 | %! endif 28 | -------------------------------------------------------------------------------- /+stdlib/is_url.m: -------------------------------------------------------------------------------- 1 | %% IS_URL is true if the string is a URL. 2 | % e.g. https://example.invalid is true 3 | 4 | function y = is_url(s) 5 | arguments 6 | s {mustBeTextScalar} 7 | end 8 | 9 | try 10 | y = startsWith(s, alphanumericsPattern + "://"); 11 | catch e 12 | % Matlab < R2020b 13 | if ~strcmp(e.identifier, "MATLAB:UndefinedFunction") && ... 14 | ~strcmp(e.identifier, "Octave:undefined-function") 15 | rethrow(e) 16 | end 17 | 18 | % https://www.mathworks.com/help/matlab/import_export/work-with-remote-data.html 19 | 20 | y = startsWith(s, "http://") || startsWith(s, "https://") || ... 21 | startsWith(s, "ftp://") || startsWith(s, "file://") || ... 22 | startsWith(s, "s3://") || startsWith(s, "hdfs://") || ... 23 | startsWith(s, "wasbs://"); 24 | end 25 | 26 | end 27 | 28 | %!assert (is_url("http://example.com"), true) 29 | %!assert (is_url("//server"), false) 30 | -------------------------------------------------------------------------------- /+stdlib/is_writable.m: -------------------------------------------------------------------------------- 1 | %% IS_WRITABLE is path writable 2 | % 3 | % non-existant path is false 4 | 5 | function ok = is_writable(p) 6 | arguments 7 | p {mustBeTextScalar} 8 | end 9 | 10 | a = file_attributes(p); 11 | 12 | ok = ~isempty(a) && (a.UserWrite || a.GroupWrite || a.OtherWrite); 13 | 14 | end 15 | 16 | %!assert (is_writable('is_writable.m')) 17 | %!assert (!is_writable('')) 18 | -------------------------------------------------------------------------------- /+stdlib/is_wsl.m: -------------------------------------------------------------------------------- 1 | %% IS_WSL detect if running under WSL 2 | % optional: mex 3 | % 4 | % Detects if Matlab or GNU Octave is installed and running from within 5 | % Windows Subsystem for Linux 6 | 7 | function w = is_wsl() 8 | 9 | w = 0; 10 | 11 | if isunix && ~ismac 12 | fid = fopen('/proc/version'); 13 | if fid >= 1 14 | v = fscanf(fid, '%s'); 15 | if fclose(fid) ~= 0 16 | w = -1; 17 | elseif endsWith(v, "microsoft-standard-WSL2") 18 | w = 2; 19 | elseif endsWith(v, "-Microsoft") 20 | w = 1; 21 | end 22 | end 23 | end 24 | 25 | w = int32(w); 26 | 27 | end 28 | 29 | %!assert(class(is_wsl()), "int32") 30 | -------------------------------------------------------------------------------- /+stdlib/iscygwin.m: -------------------------------------------------------------------------------- 1 | %% ISCYGWIN Detect if running under Cygwin 2 | 3 | function y = iscygwin() 4 | 5 | y = false; 6 | 7 | if isunix && ~ismac 8 | fid = fopen('/proc/version'); 9 | if fid < 0, return, end 10 | v = fscanf(fid,'%s'); 11 | fclose(fid); 12 | y = ~isempty(strfind(v, 'CYGWIN')); %#ok 13 | end 14 | 15 | end 16 | 17 | %!assert(islogical(iscygwin())) 18 | -------------------------------------------------------------------------------- /+stdlib/isinteractive.m: -------------------------------------------------------------------------------- 1 | %% ISINTERACTIVE tell if being run interactively 2 | % 3 | % NOTE: don't use batchStartupOptionUsed as it neglects the "-nodesktop" case 4 | 5 | function g = isinteractive() 6 | 7 | if stdlib.isoctave() 8 | g = ~isempty(graphics_toolkit()); 9 | else 10 | g = usejava('desktop'); 11 | end 12 | 13 | end 14 | 15 | %!assert (islogical(isinteractive())) 16 | -------------------------------------------------------------------------------- /+stdlib/isoctave.m: -------------------------------------------------------------------------------- 1 | %% ISOCTAVE Detects if this is GNU Octave 2 | 3 | function isoct = isoctave() 4 | 5 | persistent o 6 | 7 | if isempty(o) 8 | o = exist('OCTAVE_VERSION', 'builtin') == 5; 9 | end 10 | 11 | isoct = o; 12 | 13 | end 14 | 15 | %!assert (isoctave()) 16 | -------------------------------------------------------------------------------- /+stdlib/java_api.m: -------------------------------------------------------------------------------- 1 | %% JAVA_API Java API version 2 | % requires: java 3 | 4 | function api = java_api() 5 | 6 | v = stdlib.java_version(); 7 | 8 | % major version is first number before "." 9 | 10 | a = strsplit(v, '.'); 11 | if(isempty(a)) 12 | api = 0; 13 | return 14 | end 15 | 16 | if a{1} == "1" 17 | api = str2double(a{2}); 18 | else 19 | api = str2double(a{1}); 20 | end 21 | 22 | end 23 | 24 | 25 | %!assert(java_api() > 0) 26 | -------------------------------------------------------------------------------- /+stdlib/java_vendor.m: -------------------------------------------------------------------------------- 1 | %% JAVA_VENDOR get vendor of Java Virtual Machine 2 | % requires: java 3 | 4 | function v = java_vendor() 5 | 6 | v = javaSystemProperty("java.vendor"); 7 | 8 | end 9 | 10 | %!assert(!isempty(java_vendor())) 11 | -------------------------------------------------------------------------------- /+stdlib/java_version.m: -------------------------------------------------------------------------------- 1 | %% JAVA_VERSION get version of Java Virtual Machine 2 | % requires: java 3 | % 4 | % this gives a long string with more detail 5 | % version("-java") 6 | 7 | % these give the Matlab version, not the JVM version. 8 | % java.lang.Runtime.version() 9 | % java.lang.Runtime.getRuntime().version 10 | 11 | function v = java_version() 12 | 13 | v = javaSystemProperty("java.version"); 14 | 15 | end 16 | 17 | %!assert(!isempty(java_version())) 18 | -------------------------------------------------------------------------------- /+stdlib/join.m: -------------------------------------------------------------------------------- 1 | %% JOIN join two paths with posix file separator 2 | 3 | function p = join(base, other) 4 | arguments 5 | base {mustBeTextScalar} 6 | other {mustBeTextScalar} 7 | end 8 | 9 | 10 | b = stdlib.posix(base); 11 | o = stdlib.posix(other); 12 | 13 | if startsWith(o, '/') || (ispc && stdlib.is_absolute(o)) 14 | p = o; 15 | return 16 | end 17 | 18 | p = b; 19 | if strlength(o) 20 | if endsWith(p, '/') 21 | p = strcat(p, o); 22 | elseif strlength(p) 23 | p = strcat(p, '/', o); 24 | else 25 | p = o; 26 | end 27 | end 28 | 29 | end 30 | 31 | %!assert(join("", ""), "") 32 | %!assert(join("", "b"), "b") 33 | %!assert(join("a", ""), "a") 34 | %!assert(join("a", "b"), "a/b") 35 | %!assert(join("a", "/b/c"), "/b/c") 36 | -------------------------------------------------------------------------------- /+stdlib/makedir.m: -------------------------------------------------------------------------------- 1 | %% MAKEDIR make dtory and check for success 2 | % malformed paths can be "created" but are not accessible. 3 | % This function works around that bug in Matlab mkdir(). 4 | 5 | function makedir(d) 6 | arguments 7 | d {mustBeTextScalar} 8 | end 9 | 10 | %% to avoid confusing making ./~/mydir 11 | d = stdlib.expanduser(d); 12 | 13 | if isfolder(d) 14 | return 15 | end 16 | 17 | mkdir(d); 18 | 19 | assert(isfolder(d), "stdlib:makedir:mkdir", "Failed to create %s", d) 20 | 21 | end 22 | 23 | %!test 24 | %! d = tempname(); 25 | %! makedir(d); 26 | -------------------------------------------------------------------------------- /+stdlib/md5sum.m: -------------------------------------------------------------------------------- 1 | %% MD5SUM compute MD5 hash of file 2 | % requires: java 3 | function hash = md5sum(file) 4 | arguments 5 | file {mustBeScalarText} 6 | end 7 | 8 | hash = stdlib.file_checksum(file, "md5"); 9 | 10 | end 11 | 12 | %!assert(~isempty(md5sum('md5sum.m'))) 13 | -------------------------------------------------------------------------------- /+stdlib/ncexists.m: -------------------------------------------------------------------------------- 1 | %% NCEXISTS check if variable exists in NetCDF4 file 2 | % 3 | %%% Inputs 4 | % * file: data filename 5 | % * variable: path of variable in file 6 | %%% Outputs 7 | % * exists: boolean 8 | 9 | function exists = ncexists(file, variable) 10 | arguments 11 | file {mustBeTextScalar} 12 | variable {mustBeTextScalar} 13 | end 14 | 15 | exists = false; 16 | 17 | try 18 | ncinfo(file, variable); 19 | exists = true; 20 | catch e 21 | if ~strcmp(e.identifier, "MATLAB:imagesci:netcdf:badLocationString") && ... 22 | ~strcmp(e.identifier, "MATLAB:imagesci:netcdf:unknownLocation") && ... 23 | ~strcmp(e.message, "NetCDF: Variable not found") 24 | 25 | rethrow(e) 26 | end 27 | end 28 | 29 | end 30 | 31 | 32 | %!test 33 | %! if !isempty(pkg('list', 'netcdf')) 34 | %! pkg load netcdf 35 | %! fn = tempname(); 36 | %! ds = 'a'; 37 | %! nccreate(fn, ds) 38 | %! assert(ncexists(fn, ds)) 39 | %! assert(!ncexists(fn, 'b')) 40 | %! delete(fn) 41 | %! endif 42 | -------------------------------------------------------------------------------- /+stdlib/ncsave.m: -------------------------------------------------------------------------------- 1 | %% NCSAVE save data to NetCDF4 file 2 | % create or append to data file 3 | % 4 | % parent folder (file directory) must already exist 5 | % 6 | %%% Inputs 7 | % * filename: data filename 8 | % * varname: variable name to save 9 | % * A: data to write 10 | % * opts.dims: name and size of dimensions 11 | % * opts.type: class of variable e.g. int32, float32 12 | 13 | function ncsave(filename, varname, A, opts) 14 | arguments 15 | filename {mustBeTextScalar} 16 | varname {mustBeTextScalar} 17 | A {mustBeNonempty} 18 | opts.dims cell = {} 19 | opts.type {mustBeTextScalar} = '' 20 | opts.compressLevel (1,1) double {mustBeInteger,mustBeNonnegative} = 0 21 | end 22 | 23 | if isnumeric(A) 24 | mustBeReal(A) 25 | end 26 | 27 | % avoid creating confusing file ./~/a.nc 28 | filename = stdlib.expanduser(filename); 29 | 30 | % coerce if needed 31 | A = coerce_ds(A, opts.type); 32 | 33 | if isscalar(A) 34 | sizeA = 1; 35 | else 36 | if isempty(opts.dims) 37 | error("For non-scalar NetCDF variables, the dimenions must be defined as a cell array") 38 | end 39 | for i = 2:2:length(opts.dims) 40 | sizeA(i/2) = opts.dims{i}; %#ok 41 | end 42 | end 43 | 44 | if isfile(filename) 45 | if stdlib.ncexists(filename, varname) 46 | stdlib.ncsave_exist(filename, varname, A, sizeA) 47 | else 48 | stdlib.ncsave_new(filename, varname, A, sizeA, opts.dims, opts.compressLevel) 49 | end 50 | else 51 | stdlib.ncsave_new(filename, varname, A, sizeA, opts.dims, opts.compressLevel) 52 | end 53 | 54 | end 55 | 56 | %!testif 0 57 | -------------------------------------------------------------------------------- /+stdlib/ncsave_exist.m: -------------------------------------------------------------------------------- 1 | %% NCSAVE_EXIST save a variable to a NetCDF4 existing dataset 2 | % normally users will use ncsave() instead of this function 3 | 4 | function ncsave_exist(filename, varname, A, sizeA) 5 | 6 | diskshape = stdlib.ncsize(filename, varname); 7 | 8 | if all(diskshape == sizeA) 9 | ncwrite(filename, varname, A) 10 | elseif all(diskshape == fliplr(sizeA)) 11 | ncwrite(filename, varname, A.') 12 | else 13 | error('ncsave:value_error', ['shape of ',varname,': ', int2str(sizeA), ' does not match existing NetCDF4 shape ', int2str(diskshape)]) 14 | end 15 | 16 | end 17 | 18 | %!test 19 | %! if !isempty(pkg('list', 'netcdf')) 20 | %! pkg load netcdf 21 | %! fn = tempname(); 22 | %! ds = 'a'; 23 | %! a = [1,2]; 24 | %! b = [3,4]; 25 | %! ncsave_new(fn, ds, a, size(a), {"x", 1, "y", 2}, 0) 26 | %! ncsave_exist(fn, ds, b, size(b)) 27 | %! assert(ncread(fn, ds), b) 28 | %! delete(fn) 29 | %! endif 30 | -------------------------------------------------------------------------------- /+stdlib/ncsave_new.m: -------------------------------------------------------------------------------- 1 | %% NCSAVE_NEW Save a variable to an new NetCDF4 dataset 2 | % normally users will use ncsave() instead of this function 3 | 4 | function ncsave_new(file, varname, A, sizeA, ncdims, compressLevel) 5 | arguments 6 | file {mustBeTextScalar} 7 | varname {mustBeTextScalar} 8 | A {mustBeNonempty} 9 | sizeA (1,:) double {mustBeInteger,mustBeNonnegative} = [] 10 | ncdims (1,:) cell = {} 11 | compressLevel (1,1) double {mustBeInteger,mustBeNonnegative} = 0 12 | end 13 | 14 | assert(strlength(file) > 0, "stdlib:ncsave_new:file", "Empty filename") 15 | 16 | if isscalar(A) 17 | nccreate(file, varname, "Datatype", class(A), "Format", 'netcdf4') 18 | elseif isvector(A) || ischar(A) || isstring(A) 19 | nccreate(file, varname, "Dimensions", ncdims, "Datatype", class(A), "Format", 'netcdf4') 20 | elseif compressLevel 21 | % enable Gzip compression 22 | % Matlab's dim order is flipped from C / Python 23 | nccreate(file, varname, "Dimensions", ncdims, "Datatype", class(A), "Format", 'netcdf4', ... 24 | "DeflateLevel", compressLevel, "Shuffle", true, ... 25 | "ChunkSize", stdlib.auto_chunk_size(sizeA)) 26 | else 27 | nccreate(file, varname, "Dimensions", ncdims, "Datatype", class(A), "Format", 'netcdf4') 28 | end 29 | 30 | ncwrite(file, varname, A) 31 | 32 | end 33 | 34 | %!test 35 | %! if !isempty(pkg('list', 'netcdf')) 36 | %! pkg load netcdf 37 | %! fn = tempname(); 38 | %! ds = 'a'; 39 | %! a = [1,2]; 40 | %! ncsave_new(fn, ds, a, size(a), {"x", 1, "y", 2}, 0) 41 | %! assert(ncread(fn, ds), a) 42 | %! delete(fn) 43 | %! endif 44 | -------------------------------------------------------------------------------- /+stdlib/ncsize.m: -------------------------------------------------------------------------------- 1 | %% NCSIZE get size (shape) of a NetCDF4 variable 2 | % get size (shape) of a data file variable 3 | % 4 | %%% Inputs 5 | % filename: data filename 6 | % variable: name of variable inside file 7 | %%% Outputs 8 | % fsize: vector of variable size per dimension. Empty if scalar variable. 9 | 10 | function fsize = ncsize(file, variable) 11 | arguments 12 | file {mustBeTextScalar} 13 | variable {mustBeTextScalar} 14 | end 15 | 16 | dsi = ncinfo(file, variable); 17 | if isempty(dsi.Dimensions) 18 | fsize = []; 19 | else 20 | fsize = dsi.Size; 21 | end 22 | 23 | end 24 | 25 | %!test 26 | %! if !isempty(pkg('list', 'netcdf')) 27 | %! pkg load netcdf 28 | %! fn = tempname(); 29 | %! nccreate(fn, 'a') 30 | %! assert(ncsize(fn, 'a'), []) 31 | %! nccreate(fn, 'b', 'Dimensions', {'x', 2, 'y', 3}) 32 | %! assert(ncsize(fn, 'b'), [2, 3]) 33 | %! delete(fn) 34 | %! endif 35 | -------------------------------------------------------------------------------- /+stdlib/ncvariables.m: -------------------------------------------------------------------------------- 1 | %% NCVARIABLES get NetCDF dataset names 2 | % get dataset names in a file under group 3 | % default is datasets under "/", optionally under "/group" 4 | % 5 | %%% Inputs 6 | % * file: filename 7 | % * group: group name (optional) 8 | %%% Outputs 9 | % * names: variable names 10 | 11 | function names = ncvariables(file, group) 12 | arguments 13 | file {mustBeTextScalar} 14 | group {mustBeTextScalar} = '' 15 | end 16 | 17 | 18 | if strlength(group) == 0 19 | finf = ncinfo(file); 20 | else 21 | finf = ncinfo(file, group); 22 | end 23 | 24 | ds = finf.Variables(:); 25 | 26 | if isempty(ds) 27 | names = []; 28 | else 29 | names = {ds.Name}; 30 | end 31 | 32 | try %#ok 33 | names = string(names); 34 | end 35 | 36 | 37 | end 38 | 39 | 40 | %!test 41 | %! if !isempty(pkg('list', 'netcdf')) 42 | %! pkg load netcdf 43 | %! fn = tempname(); 44 | %! ds = 'a'; 45 | %! nccreate(fn, ds) 46 | %! assert(ncvariables(fn, ''), {'a'}) 47 | %! delete(fn) 48 | %! endif 49 | -------------------------------------------------------------------------------- /+stdlib/normalize.m: -------------------------------------------------------------------------------- 1 | %% NORMALIZE remove redundant elements of path 2 | % optional: mex 3 | % 4 | % normalize(p) remove redundant elements of path p 5 | % path need not exist, normalized path is returned 6 | % 7 | %%% Inputs 8 | % * p: path to normalize 9 | %%% Outputs 10 | % * c: normalized path 11 | % 12 | % MEX code is about 4-5x faster than plain Matlab below 13 | 14 | function n = normalize(p) 15 | arguments 16 | p (1,1) string 17 | end 18 | 19 | n = stdlib.posix(p); 20 | 21 | uncslash = ispc && startsWith(n, "//"); 22 | 23 | % use split to remove /../ and /./ and duplicated / 24 | parts = split(n, '/'); 25 | i0 = 1; 26 | if strncmp(n, "/", 1) 27 | n = "/"; 28 | elseif ispc && strlength(n) >= 2 && strlength(stdlib.root_name(p)) 29 | n = parts(1); 30 | i0 = 2; 31 | else 32 | n = ""; 33 | end 34 | 35 | for i = i0:length(parts) 36 | if parts(i) == ".." 37 | if n == "" 38 | n = parts(i); 39 | elseif endsWith(n, "..") 40 | n = n + "/" + parts(i); 41 | else 42 | j = strfind(n, "/"); 43 | if isempty(j) 44 | n = ""; 45 | else 46 | n = n{1}(1:j(end)-1); 47 | end 48 | end 49 | elseif all(parts(i) ~= [".", ""]) 50 | if n == "" 51 | n = parts(i); 52 | elseif n == "/" 53 | n = n + parts(i); 54 | else 55 | n = n + "/" + parts(i); 56 | end 57 | end 58 | end 59 | 60 | if uncslash 61 | n = strcat("/", n); 62 | end 63 | 64 | 65 | if ~strlength(n) 66 | n = "."; 67 | end 68 | 69 | end 70 | 71 | 72 | %!testif 0 73 | -------------------------------------------------------------------------------- /+stdlib/null_file.m: -------------------------------------------------------------------------------- 1 | %% NULL_FILE get null file path 2 | 3 | function nul = null_file() 4 | 5 | if ispc 6 | nul = "NUL"; 7 | else 8 | nul = "/dev/null"; 9 | end 10 | 11 | end 12 | 13 | %!assert(ischar(null_file())) 14 | -------------------------------------------------------------------------------- /+stdlib/os_version.m: -------------------------------------------------------------------------------- 1 | %% OS_VERSION Get operating system name and version. 2 | % requires: java 3 | % 4 | % Note: for Windows 11, need new-enough Java version to show Windows 11 5 | % instead of Windows 10. 6 | % Ref: https://bugs.openjdk.org/browse/JDK-8274840 7 | 8 | function [os, version] = os_version() 9 | 10 | os = javaSystemProperty("os.name"); 11 | version = javaSystemProperty("os.version"); 12 | 13 | end 14 | 15 | %!test 16 | %! [os, version] = os_version(); 17 | %! assert(!isempty(os)) 18 | %! assert(!isempty(version)) 19 | -------------------------------------------------------------------------------- /+stdlib/parent.m: -------------------------------------------------------------------------------- 1 | %% PARENT parent directory of path 2 | % optional: mex 3 | % 4 | %% Examples: 5 | % parent("a/b/c") == "a/b" 6 | % parent("a/b/c/") == "a/b" 7 | % 8 | % MEX is about 10x faster than plain Matlab for this function 9 | 10 | function p = parent(pth) 11 | arguments 12 | pth {mustBeTextScalar} 13 | end 14 | 15 | p = stdlib.drop_slash(pth); 16 | 17 | if ~strlength(p) 18 | p = '.'; 19 | elseif is_root_stub(p) 20 | % 2 or 3 char drive letter 21 | if strlength(p) == 2 22 | p = strcat(p, '/'); 23 | end 24 | elseif strcmp(p, stdlib.root(p)) 25 | % noop 26 | else 27 | j = strfind(p, '/'); 28 | if isempty(j) 29 | p = ''; 30 | elseif ischar(p) 31 | p = p(1:j(end)-1); 32 | else 33 | p = p{1}(1:j(end)-1); 34 | end 35 | 36 | if is_root_stub(p) 37 | p = stdlib.root(pth); 38 | return 39 | end 40 | end 41 | 42 | p = stdlib.posix(p); 43 | 44 | if ~strlength(p) 45 | p = '.'; 46 | end 47 | 48 | if isstring(pth) 49 | p = string(p); 50 | end 51 | 52 | end 53 | 54 | 55 | function s = is_root_stub(p) 56 | s = ispc() && any(strlength(p) == [2,3]) && strlength(stdlib.root_name(p)); 57 | end 58 | 59 | 60 | %!assert(parent("/a/b/c"), "/a/b") 61 | %!assert(parent("/a/b/c/"), "/a/b") 62 | %!assert(parent('/a///b'), '/a') 63 | %!assert(parent('a/b/'), 'a') 64 | %!assert(parent('a//b/'), 'a') 65 | %!assert(parent('a//b'), 'a') 66 | %!test 67 | %! if ispc 68 | %! assert(parent('c:/a'), 'c:/') 69 | %! assert(parent('c:\a\'), 'c:/') 70 | %! assert(parent('c:\'), 'c:/') 71 | %! assert(parent('c:'), 'c:/') 72 | %! end 73 | -------------------------------------------------------------------------------- /+stdlib/posix.m: -------------------------------------------------------------------------------- 1 | %% POSIX posix format of path with '/' separator 2 | % convert a path to a Posix string path separated with "/" even on Windows. 3 | % If Windows path also have escaping "\" this breaks 4 | % 5 | 6 | function r = posix(p) 7 | arguments 8 | p {mustBeTextScalar} 9 | end 10 | 11 | if ispc 12 | r = strrep(p, '\', '/'); 13 | else 14 | r = p; 15 | end 16 | 17 | end 18 | 19 | %!assert (posix('/'), '/') 20 | -------------------------------------------------------------------------------- /+stdlib/private/coerce_ds.m: -------------------------------------------------------------------------------- 1 | function A = coerce_ds(A, dtype) 2 | % used by h5save and ncsave 3 | arguments 4 | A 5 | dtype {mustBeTextScalar} 6 | end 7 | 8 | if ischar(A) 9 | A = string(A); 10 | return 11 | end 12 | 13 | if strlength(dtype) == 0 14 | return 15 | end 16 | 17 | switch dtype 18 | case "" 19 | return 20 | case {'double', 'single', 'int8', 'int16', 'int32', 'int64','uint8', 'uint16', 'uint32', 'uint64'} 21 | A = cast(A, dtype); 22 | case {'char', 'string'} 23 | A = string(A); 24 | otherwise, error('create_ds:type_error', 'unknown data type %s', dtype) 25 | end 26 | 27 | end % function 28 | -------------------------------------------------------------------------------- /+stdlib/private/defaultSize.m: -------------------------------------------------------------------------------- 1 | function s = defaultSize(A) 2 | if isscalar(A) 3 | s = 0; 4 | elseif isvector(A) 5 | s = length(A); 6 | else 7 | s = size(A); 8 | end 9 | 10 | end 11 | -------------------------------------------------------------------------------- /+stdlib/private/file_attributes.m: -------------------------------------------------------------------------------- 1 | function a = file_attributes(p) 2 | arguments 3 | p {mustBeTextScalar} 4 | end 5 | 6 | a = []; 7 | 8 | if strlength(p) == 0, return, end 9 | 10 | [status, a] = fileattrib(p); 11 | if status ~= 1 12 | % matlab puts the error message in the struct 13 | a = []; 14 | return 15 | end 16 | 17 | for n = {"GroupRead", "GroupWrite", "GroupExecute", "OtherRead", "OtherWrite", "OtherExecute"} 18 | name = n{1}; 19 | if ~isfield(a, name) || isnan(a.(name)) 20 | a.(name) = false; 21 | end 22 | end 23 | 24 | end 25 | -------------------------------------------------------------------------------- /+stdlib/private/h5save_scalar.m: -------------------------------------------------------------------------------- 1 | function h5save_scalar(file, hpath, A) 2 | %% write HDF5 scalar as a scalar 3 | % h5create doesn't support scalars 4 | arguments 5 | file (1,1) 6 | hpath {mustBeTextScalar} 7 | A (1,1) 8 | end 9 | 10 | dcpl = 'H5P_DEFAULT'; 11 | 12 | fid = stdlib.h5create_group(file, hpath); 13 | 14 | space_id = H5S.create('H5S_SCALAR'); 15 | if isstring(A) 16 | A = char(A); 17 | end 18 | if ischar(A) 19 | type_id = H5T.copy('H5T_C_S1'); 20 | H5T.set_cset(type_id, H5ML.get_constant_value('H5T_CSET_UTF8')); 21 | H5T.set_size(type_id, 'H5T_VARIABLE'); 22 | H5T.set_strpad(type_id, 'H5T_STR_NULLTERM'); 23 | else 24 | type_id = H5T.copy(class2h5t(A)); 25 | end 26 | 27 | dset_id = H5D.create(fid, hpath, type_id, space_id, dcpl); 28 | 29 | H5D.write(dset_id,'H5ML_DEFAULT','H5S_ALL','H5S_ALL', dcpl, A); 30 | 31 | H5S.close(space_id); 32 | H5T.close(type_id); 33 | H5D.close(dset_id); 34 | H5F.close(fid); 35 | 36 | end 37 | 38 | 39 | function t = class2h5t(A) 40 | % gets HDF5 H5T of variable A 41 | 42 | C = class(A); 43 | switch C 44 | case 'double', t = 'H5T_NATIVE_DOUBLE'; 45 | case 'single', t = 'H5T_NATIVE_FLOAT'; 46 | case {'int8', 'int16', 'int32', 'int64'} 47 | t = "H5T_STD_I" + C(4:end) + "LE"; 48 | case {'uint8', 'uint16', 'uint32', 'uint64'} 49 | t = "H5T_STD_U" + C(5:end) + "LE"; 50 | otherwise, error('h5save:class2h5t: unknown data class %s', class(A)) 51 | end 52 | 53 | end 54 | -------------------------------------------------------------------------------- /+stdlib/private/jPosix.m: -------------------------------------------------------------------------------- 1 | function s = jPosix(o) 2 | 3 | s = o; 4 | 5 | if isempty(o) 6 | s = ""; 7 | elseif isa(o, "java.io.File") || isa(o, "java.nio.file.Path") || ... 8 | isa(o, "sun.nio.fs.UnixPath") || isa(o, "sun.nio.fs.WindowsPath") 9 | s = o.toString(); 10 | end 11 | 12 | s = stdlib.posix(s); 13 | 14 | end 15 | -------------------------------------------------------------------------------- /+stdlib/private/javaFileObject.m: -------------------------------------------------------------------------------- 1 | %% JAVAFILEOBJECT Return a Java File object for a given file path. 2 | function o = javaFileObject(p) 3 | 4 | try 5 | o = java.io.File(p); 6 | catch e 7 | if strcmp(e.identifier, "Octave:undefined-function") 8 | o = javaObject("java.io.File", p); 9 | else 10 | rethrow(e); 11 | end 12 | end 13 | 14 | end 15 | -------------------------------------------------------------------------------- /+stdlib/private/javaLinkOption.m: -------------------------------------------------------------------------------- 1 | function o = javaLinkOption() 2 | 3 | try 4 | o = java.nio.file.LinkOption.values; 5 | catch e 6 | if strcmp(e.identifier, "Octave:undefined-function") 7 | o = javaMethod("values", "java.nio.file.LinkOption"); 8 | else 9 | rethrow(e); 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /+stdlib/private/javaOSBean.m: -------------------------------------------------------------------------------- 1 | function b = javaOSBean() 2 | 3 | try 4 | b = java.lang.management.ManagementFactory.getOperatingSystemMXBean(); 5 | catch e 6 | if strcmp(e.identifier, "Octave:undefined-function") 7 | b = javaMethod("getOperatingSystemMXBean", "java.lang.management.ManagementFactory"); 8 | else 9 | rethrow(e); 10 | end 11 | end 12 | 13 | end 14 | -------------------------------------------------------------------------------- /+stdlib/private/javaPathObject.m: -------------------------------------------------------------------------------- 1 | %% JAVAPATHOBJECT Return a Java nio.file.Path object for a given file path. 2 | function o = javaPathObject(p) 3 | 4 | try 5 | o = java.nio.file.Paths.get(p, javaArray('java.lang.String', 0)); 6 | % o = javaFileObject(p).toPath(); % above way about 20% faster 7 | catch e 8 | if strcmp(e.identifier, "Octave:undefined-function") 9 | o = javaFileObject(p).toPath(); 10 | else 11 | rethrow(e); 12 | end 13 | end 14 | 15 | end 16 | -------------------------------------------------------------------------------- /+stdlib/private/javaSystemProperty.m: -------------------------------------------------------------------------------- 1 | function p = javaSystemProperty(k) 2 | 3 | try 4 | p = string(java.lang.System.getProperty(k)); 5 | catch e 6 | if strcmp(e.identifier, "Octave:undefined-function") 7 | p = javaMethod("getProperty", "java.lang.System", k); 8 | else 9 | rethrow(e); 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /+stdlib/private/strlength.m: -------------------------------------------------------------------------------- 1 | %% STRLENGTH get length of character or scalar string 2 | % assumes single string or character vector 3 | % for GNU Octave until it builds in strlength 4 | 5 | function L = strlength(s) 6 | arguments 7 | s {mustBeTextScalar} 8 | end 9 | 10 | if ischar(s) 11 | L = length(s); 12 | else 13 | L = builtin('strlength', char(s)); 14 | % bug in Matlab at least through R2025a confirmed by Xinyue Xia of Mathworks Technical Support 15 | % only works for char and scalar strings until fixed by Mathworks. 16 | % once fixed, wouldn't need char(s) conversion. 17 | end 18 | 19 | end 20 | 21 | %!assert(strlength('abc'), 3) 22 | -------------------------------------------------------------------------------- /+stdlib/proximate_to.m: -------------------------------------------------------------------------------- 1 | %% PROXIMATE_TO relative path to base 2 | % requires: mex 3 | 4 | %%% Inputs 5 | % * base {mustBeTextScalar} 6 | % * other {mustBeTextScalar} 7 | %%% Outputs 8 | % * rel {mustBeTextScalar} 9 | % 10 | % This function is written in C++ using STL 11 | 12 | function proximate_to(~,~) 13 | error("buildtool mex") 14 | end 15 | -------------------------------------------------------------------------------- /+stdlib/ram_free.m: -------------------------------------------------------------------------------- 1 | %% RAM_FREE get free physical RAM 2 | % requires: java 3 | % 4 | % get free physical RAM across operating systems 5 | % https://docs.oracle.com/en/java/javase/21/docs/api/jdk.management/com/sun/management/OperatingSystemMXBean.html#getFreeMemorySize() 6 | % 7 | %%% Outputs 8 | % * freebytes: free physical RAM [bytes] 9 | 10 | function freebytes = ram_free() 11 | 12 | b = javaOSBean(); 13 | 14 | if stdlib.java_api() < 14 15 | freebytes = b.getFreePhysicalMemorySize(); 16 | else 17 | freebytes = b.getFreeMemorySize(); 18 | end 19 | 20 | end 21 | 22 | %!assert(ram_free()>0) 23 | -------------------------------------------------------------------------------- /+stdlib/ram_total.m: -------------------------------------------------------------------------------- 1 | %% RAM_TOTAL get total physical RAM 2 | % requires: java 3 | % 4 | % get total physical RAM across operating systems 5 | % https://docs.oracle.com/en/java/javase/21/docs/api/jdk.management/com/sun/management/OperatingSystemMXBean.html#getTotalPhysicalMemorySize() 6 | % 7 | %%% Outputs 8 | % * bytes: total physical RAM [bytes] 9 | 10 | function bytes = ram_total() 11 | 12 | b = javaOSBean(); 13 | 14 | if stdlib.java_api() < 14 15 | bytes = b.getTotalPhysicalMemorySize(); 16 | else 17 | bytes = b.getTotalMemorySize(); 18 | end 19 | 20 | % https://docs.oracle.com/en/java/javase/21/docs/api/jdk.management/com/sun/management/OperatingSystemMXBean.html#getTotalMemorySize() 21 | 22 | end 23 | 24 | 25 | %!assert(ram_total()>0) 26 | -------------------------------------------------------------------------------- /+stdlib/read_symlink.m: -------------------------------------------------------------------------------- 1 | %% READ_SYMLINK read symbolic link 2 | % 3 | % empty string if path is not a symlink 4 | 5 | function r = read_symlink(p) 6 | arguments 7 | p {mustBeTextScalar} 8 | end 9 | 10 | r = ""; 11 | 12 | try 13 | [ok, t] = isSymbolicLink(p); 14 | if ~ok, return, end 15 | catch e 16 | switch e.identifier 17 | case "Octave:undefined-function", t = readlink(p); 18 | case "MATLAB:UndefinedFunction" 19 | if ~stdlib.is_symlink(p) 20 | return 21 | end 22 | 23 | % must be absolute path 24 | % must not be .canonical or symlink is gobbled! 25 | r = stdlib.absolute(p, "", false); 26 | 27 | % https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/nio/file/Files.html#readSymbolicLink(java.nio.file.Path) 28 | t = java.nio.file.Files.readSymbolicLink(javaPathObject(r)); 29 | otherwise, rethrow(e) 30 | end 31 | end 32 | 33 | r = stdlib.posix(t); 34 | 35 | end 36 | 37 | %!test 38 | %! if !ispc 39 | %! p = tempname(); 40 | %! this = strcat(mfilename("fullpath"), '.m'); 41 | %! assert (read_symlink(p), "") 42 | %! assert (create_symlink(this, p)) 43 | %! assert (read_symlink(p), this) 44 | %! endif 45 | -------------------------------------------------------------------------------- /+stdlib/relative_to.m: -------------------------------------------------------------------------------- 1 | %% RELATIVE_TO relative path to base 2 | % requires: mex 3 | % 4 | %%% Inputs 5 | % * base {mustBeTextScalar} 6 | % * other {mustBeTextScalar} 7 | %%% Outputs 8 | % * rel {mustBeTextScalar} 9 | % 10 | % This function is written in C++ using STL 11 | % 12 | % Note: Java Path.relativize has an algorithm so different that we choose not to use it. 13 | % https://docs.oracle.com/javase/8/docs/api/java/nio/file/Path.html#relativize-java.nio.file.Path- 14 | 15 | function relative_to(~,~) 16 | error("buildtool mex") 17 | end 18 | -------------------------------------------------------------------------------- /+stdlib/resolve.m: -------------------------------------------------------------------------------- 1 | %% RESOLVE resolve path 2 | % resolve path, to cwd if relative 3 | % effectively canonical(absolute(p)) 4 | %%% Inputs 5 | % * p: path to make absolute 6 | % * expand_tilde: expand ~ to username if present 7 | %%% Outputs 8 | % * c: resolved path 9 | % distinct from canonical(), resolve() always returns absolute path 10 | % non-existant path is made absolute relative to pwd 11 | 12 | function r = resolve(p, expand_tilde) 13 | arguments 14 | p {mustBeTextScalar} 15 | expand_tilde (1,1) logical = true 16 | end 17 | 18 | 19 | r = stdlib.canonical(stdlib.absolute(p, '', expand_tilde), false); 20 | 21 | end 22 | 23 | %!assert (resolve('', 1), stdlib.posix(pwd())) 24 | -------------------------------------------------------------------------------- /+stdlib/root.m: -------------------------------------------------------------------------------- 1 | %% ROOT get root path 2 | % ROOT(P) returns the root path of P. 3 | % root is the root_name + root_directory. 4 | 5 | 6 | function r = root(p) 7 | arguments 8 | p {mustBeTextScalar} 9 | end 10 | 11 | r = stdlib.root_name(p); 12 | 13 | if ~strlength(r) 14 | if strncmp(p, '/', 1) 15 | r = '/'; 16 | end 17 | elseif ~(ispc && strcmp(r, p)) 18 | r = strcat(r, '/'); 19 | end 20 | 21 | if isstring(p) 22 | r = string(r); 23 | end 24 | 25 | end 26 | 27 | %!assert(root(''), '') 28 | %!assert(root('/'), '/') 29 | %!test 30 | %! if ispc 31 | %! assert(root('C:\'), 'C:/') 32 | %! assert(root('C:/'), 'C:/') 33 | %! assert(root('C:'), 'C:') 34 | %! assert(root('C'), '') 35 | %! endif 36 | -------------------------------------------------------------------------------- /+stdlib/root_name.m: -------------------------------------------------------------------------------- 1 | %% ROOT_NAME get root name 2 | % ROOT_NAME(P) returns the root name of P. 3 | % root_name is the drive letter on Windows without the trailing slash 4 | % or an empty string if P is not an absolute path. 5 | % on non-Windows platforms, root_name is always an empty string. 6 | 7 | 8 | function r = root_name(p) 9 | arguments 10 | p {mustBeTextScalar} 11 | end 12 | 13 | r = ''; 14 | 15 | if ~ispc || strlength(p) < 2 16 | % noop 17 | else 18 | c = char(p); 19 | 20 | if c(2) == ':' && isletter(c(1)) 21 | r = c(1:2); 22 | end 23 | end 24 | 25 | if isstring(p) 26 | r = string(r); 27 | end 28 | 29 | end 30 | 31 | %!assert(root_name(''), '') 32 | %!assert(root_name('/'), '') 33 | %!test 34 | %! if ispc 35 | %! assert(root_name('C:\'), 'C:') 36 | %! assert(root_name('C:/'), 'C:') 37 | %! assert(root_name('C:'), 'C:') 38 | %! assert(root_name('C'), '') 39 | %! endif 40 | -------------------------------------------------------------------------------- /+stdlib/samepath.m: -------------------------------------------------------------------------------- 1 | %% SAMEPATH is path the same 2 | % 3 | % true if inputs resolve to same path. 4 | % Both paths must exist. 5 | % 6 | % NOTE: in general on Windows same(".", "not-exist/..") is true, but on 7 | % Unix it is false. 8 | % In C/C++ access() or stat() the same behavior is observed Windows vs Unix. 9 | % 10 | %%% Inputs 11 | % * path1, path2: paths to compare 12 | %%% Outputs 13 | % issame: logical 14 | 15 | 16 | function issame = samepath(path1, path2) 17 | arguments 18 | path1 {mustBeTextScalar} 19 | path2 {mustBeTextScalar} 20 | end 21 | 22 | % simpler our way than 23 | % https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/nio/file/Files.html#isSameFile(java.nio.file.Path,java.nio.file.Path) 24 | 25 | issame = stdlib.exists(path1) && stdlib.exists(path2) && ... 26 | stdlib.canonical(path1, false) == stdlib.canonical(path2, false); 27 | 28 | end 29 | 30 | %!assert(samepath(".", ".")) 31 | %!assert(samepath(".", "./")) 32 | %!assert(!samepath("not-exist", "not-exist/..")) 33 | -------------------------------------------------------------------------------- /+stdlib/set_modtime.m: -------------------------------------------------------------------------------- 1 | %% SET_MODTIME set modification time of path 2 | % requires: java 3 | 4 | function ok = set_modtime(p, t) 5 | arguments 6 | p {mustBeTextScalar} 7 | t (1,1) datetime 8 | end 9 | 10 | try 11 | utc = convertTo(datetime(t, "TimeZone", "UTC"), "posixtime"); 12 | catch e 13 | if strcmp(e.identifier, "Octave:undefined-function") 14 | utc = t; 15 | else 16 | rethrow(e); 17 | end 18 | end 19 | 20 | ok = javaFileObject(p).setLastModified(int64(utc) * 1000); 21 | 22 | end 23 | 24 | %!test 25 | %! p = tempname(); 26 | %! t = time() 27 | %! assert(touch(p, [])) 28 | %! assert(set_modtime(p, t)) 29 | %! delete(p) 30 | -------------------------------------------------------------------------------- /+stdlib/set_permissions.m: -------------------------------------------------------------------------------- 1 | %% SET_PERMISSIONS set path permissions 2 | % optional: mex 3 | % 4 | %%% Inputs 5 | % * path {mustBeTextScalar} 6 | % * readable (1,1) int (-1 remove read permission, 0 no change, 1 add read permission) 7 | % * writable (1,1) int (-1 remove write permission, 0 no change, 1 add write permission) 8 | % * executable (1,1) int (-1 remove execute permission, 0 no change, 1 add execute permission) 9 | %%% Outputs 10 | % * ok (1,1) logical 11 | 12 | function ok = set_permissions(path, readable, writable, executable) 13 | arguments 14 | path {mustBeTextScalar,mustBeFile} 15 | readable (1,1) int8 16 | writable (1,1) int8 17 | executable (1,1) int8 18 | end 19 | 20 | ok = false; 21 | 22 | try 23 | p = filePermissions(path); 24 | catch e 25 | switch e.identifier 26 | case "MATLAB:UndefinedFunction", error("buildtool mex") 27 | case "MATLAB:io:filesystem:filePermissions:CannotFindLocation", return 28 | otherwise, rethrow(e) 29 | end 30 | end 31 | 32 | ok = true; 33 | 34 | if readable ~= 0 35 | ok = ok && setPermissions(p, "Readable", readable > 0); 36 | end 37 | if writable ~= 0 38 | ok = ok && setPermissions(p, "Writable", writable > 0); 39 | end 40 | if executable ~= 0 41 | ok = ok && setPermissions(p, "Executable", executable > 0); 42 | end 43 | 44 | end 45 | -------------------------------------------------------------------------------- /+stdlib/sha256sum.m: -------------------------------------------------------------------------------- 1 | %% SHA256SUM compute sha256 hash of file 2 | % requires: java 3 | 4 | function hash = sha256sum(file) 5 | arguments 6 | file {mustBeScalarText} 7 | end 8 | 9 | hash = stdlib.file_checksum(file, "SHA-256"); 10 | 11 | end 12 | 13 | %!assert(~isempty(sha256sum('sha256sum.m'))) 14 | -------------------------------------------------------------------------------- /+stdlib/stem.m: -------------------------------------------------------------------------------- 1 | %% STEM filename without directory or suffix 2 | 3 | function st = stem(p) 4 | arguments 5 | p {mustBeTextScalar} 6 | end 7 | 8 | [~, n, s] = fileparts(p); 9 | 10 | if strlength(n) 11 | st = n; 12 | else 13 | st = s; 14 | end 15 | 16 | end 17 | 18 | %!assert(stem('/a/b.c'), 'b') 19 | %!assert(stem("a/b/.c"), ".c") 20 | -------------------------------------------------------------------------------- /+stdlib/subprocess_run_octave.m: -------------------------------------------------------------------------------- 1 | %% SUBPROCESS_RUN_OCTAVE run process for GNU Octave only 2 | % requires: java 3 | % 4 | % with optional cwd, env. vars, stdin, timeout 5 | % 6 | % handles command lines with spaces 7 | % input each segment of the command as an element in a string array 8 | % this is how python subprocess.run works 9 | % 10 | %%% Inputs 11 | % * cmd_array: cell of char to compose a command line 12 | % * env: environment variable struct to set 13 | % * cwd: working directory to use while running command 14 | % * stdin: string to pass to subprocess stdin pipe 15 | % * timeout: time to wait for process to complete before erroring (seconds) 16 | %%% Outputs 17 | % * status: 0 is generally success. -1 if timeout. Other codes as per the 18 | % program / command run 19 | % * stdout: stdout from process 20 | % * stderr: stderr from process 21 | % 22 | %% Example 23 | % subprocess_run({'mpiexec', '-help2'}) 24 | % 25 | % NOTE: if cwd option used, any paths must be absolute or relative to cwd. 26 | % otherwise, they are relative to pwd. 27 | % 28 | % uses Java ProcessBuilder interface to run subprocess and use stdin/stdout pipes 29 | % https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/lang/ProcessBuilder.html 30 | 31 | function [status, stdout, stderr] = subprocess_run_octave(cmd, env, cwd, stdin, timeout) 32 | if ischar(cmd), cmd = {cmd}; end 33 | if nargin < 2 || isempty(env), env = struct(); end 34 | if nargin < 3, cwd = ''; end 35 | if nargin < 4, stdin = ''; end 36 | if nargin < 5, timeout = 0; end 37 | 38 | %% process instantiation 39 | % https://docs.oracle.com/en/java/javase/23/docs/api/java.base/java/lang/ProcessBuilder.html#command(java.lang.String...) 40 | jcary = javaArray("java.lang.String", length(cmd)); 41 | for i = 1:length(cmd) 42 | jcary(i) = javaObject("java.lang.String", cmd{i}); 43 | end 44 | proc = javaObject("java.lang.ProcessBuilder", jcary); 45 | 46 | if ~isempty(fieldnames(env)) 47 | % https://docs.oracle.com/en/java/javase/23/docs/api/java.base/java/lang/ProcessBuilder.html#environment() 48 | jenv = proc.environment(); 49 | fields = fieldnames(env); 50 | for i = 1:length(fields) 51 | jenv.put(fields{i}, env.(fields{i})); 52 | % jenv.put(fields{i}, javaObject("java.lang.String", env.(fields{i}))); 53 | end 54 | end 55 | 56 | if strlength(cwd) > 0 57 | % https://docs.oracle.com/en/java/javase/23/docs/api/java.base/java/lang/ProcessBuilder.html#directory(java.io.File) 58 | proc.directory(javaFileObject(cwd)); 59 | end 60 | 61 | %% start process 62 | % https://docs.oracle.com/en/java/javase/23/docs/api/java.base/java/lang/ProcessBuilder.html#start() 63 | h = proc.start(); 64 | 65 | %% stdin pipe 66 | if strlength(stdin) > 0 67 | os = javaObject("java.io.OutputStream", h.getOutputStream()); 68 | writer = javaObject("java.io.BufferedWriter", os); 69 | writer.write(stdin); 70 | writer.flush() 71 | writer.close() 72 | end 73 | 74 | %% read stdout, stderr pipes 75 | stdout = read_stream(h.getInputStream()); 76 | stderr = read_stream(h.getErrorStream()); 77 | 78 | %% wait for process to complete 79 | % https://docs.oracle.com/en/java/javase/23/docs/api/java.base/java/lang/Process.html#waitFor() 80 | 81 | tmsg = ''; 82 | if timeout > 0 83 | % returns true if process completed successfully 84 | % returns false if process did not complete within timeout 85 | sec = javaMethod("valueOf", "java.util.concurrent.TimeUnit", "SECONDS"); 86 | b = h.waitFor(timeout, sec); 87 | if b 88 | status = 0; 89 | else 90 | tmsg = 'Subprocess timeout'; 91 | status = -1; 92 | end 93 | else 94 | % returns 0 if process completed successfully 95 | status = h.waitFor(); 96 | end 97 | 98 | %% close process 99 | h.destroy(); 100 | 101 | stderr = strcat(tmsg, stderr); 102 | 103 | if nargout < 2 && strlength(stdout) > 0 104 | disp(stdout) 105 | end 106 | if nargout < 3 && strlength(stderr) > 0 107 | warning(stderr) 108 | end 109 | 110 | end % function subprocess_run 111 | 112 | 113 | function msg = read_stream(stream) 114 | 115 | % https://docs.oracle.com/en/java/javase/23/docs/api/java.base/java/io/BufferedReader.html 116 | reader = javaObject("java.io.BufferedReader", javaObject("java.io.InputStreamReader", stream)); 117 | 118 | line = reader.readLine(); 119 | msg = ''; 120 | while ~isempty(line) 121 | msg = strcat(msg, line, newline); 122 | 123 | line = reader.readLine(); 124 | end 125 | 126 | if strlength(msg) > 0 && msg(end) == newline 127 | msg(end) = []; 128 | end 129 | 130 | reader.close(); 131 | 132 | end 133 | 134 | %!test 135 | %! if ispc, c = "dir"; else, c = "ls"; end 136 | %! [r, m, e] = subprocess_run_octave(c); 137 | %! assert(r == 0) 138 | %! assert(length(m) > 0) 139 | %! assert(length(e) == 0) 140 | %! [r, mc, e] = subprocess_run_octave(c, [], '/'); 141 | %! assert(r == 0) 142 | %! assert(!strcmp(m, mc)) 143 | %!testif 0 144 | %! names = {'TEST1', 'TEST2'}; 145 | %! vals = {'test123', 'test321'}; 146 | %! env = struct(names{1}, vals{1}, names{2}, vals{2}); 147 | %! for i = 1:length(names) 148 | %! if ispc 149 | %! c = {"cmd", "/c", "echo", strcat('%', names{i}, '%')}; 150 | %! else 151 | %! c = {"echo", strcat('$', names{i})}; 152 | %! end 153 | %! [r, m, e] = subprocess_run_octave(c, env); 154 | %! assert(r == 0) 155 | %! assert(strcmp(m, vals{i}), '%s != %s', m, vals{i}) 156 | %! assert(length(e) == 0) 157 | %! end 158 | %!test 159 | %! c = "../test/sleep.exe"; 160 | %! [r, m, e] = subprocess_run_octave(c, [], [], [], 1); 161 | %! assert(r == -1) 162 | %! assert(length(m) == 0) 163 | %! assert(strncmp(e, 'Subprocess timeout', 17)) 164 | -------------------------------------------------------------------------------- /+stdlib/suffix.m: -------------------------------------------------------------------------------- 1 | %% SUFFIX last suffix of filename 2 | 3 | function s = suffix(p) 4 | arguments 5 | p {mustBeTextScalar} 6 | end 7 | 8 | [~, ~, s] = fileparts(p); 9 | 10 | end 11 | 12 | %!assert (suffix('/a/b.c'), '.c') 13 | -------------------------------------------------------------------------------- /+stdlib/touch.m: -------------------------------------------------------------------------------- 1 | %% TOUCH create file if not exists, else update modification time 2 | 3 | function ok = touch(p, t) 4 | arguments 5 | p {mustBeTextScalar} 6 | t datetime {mustBeScalarOrEmpty} = datetime.empty 7 | end 8 | 9 | ok = false; 10 | 11 | if ~stdlib.exists(p) 12 | fid = fopen(p, "w"); 13 | ok = fid > 0 && fclose(fid) == 0; 14 | if isempty(t) 15 | return 16 | end 17 | end 18 | 19 | if isempty(t) 20 | t = datetime("now"); 21 | end 22 | 23 | try 24 | ok = stdlib.set_modtime(p, t); 25 | catch e 26 | if ~strcmp(e.identifier, "MATLAB:undefinedVarOrClass") 27 | rethrow(e) 28 | end 29 | end 30 | 31 | end 32 | 33 | %!test 34 | %! f = tempname(); 35 | %! assert (touch(f, [])) 36 | %! assert (isfile(f)) 37 | -------------------------------------------------------------------------------- /+stdlib/unlink.m: -------------------------------------------------------------------------------- 1 | %% UNLINK delete file or empty directory 2 | % optional: mex 3 | % 4 | % Matlab or GNU Octave delete() has trouble with not being able to delete 5 | % open files on Windows. This function overcomes that limitation by returning 6 | % a boolean success status. 7 | 8 | function ok = unlink(apath) 9 | arguments 10 | apath {mustBeTextScalar} 11 | end 12 | 13 | %% fallback for if MEX not compiled 14 | try 15 | delete(apath); 16 | ok = true; 17 | catch 18 | ok = false; 19 | end 20 | 21 | end 22 | -------------------------------------------------------------------------------- /+stdlib/version_atleast.m: -------------------------------------------------------------------------------- 1 | %% VERSION_ATLEAST compare two string verions: major.minor.rev.patch 2 | % compare two string verions: major.minor.rev.patch 3 | % uses strings to compare so mixed number/string is OK 4 | % 5 | %% Inputs 6 | % * in: version to examine (string) 7 | % * ref: version to compare against (at least this version is true) 8 | %% Outputs 9 | % * r: logical 10 | 11 | function r = version_atleast(in, ref) 12 | arguments 13 | in (1,1) string 14 | ref (1,1) string 15 | end 16 | 17 | 18 | if stdlib.isoctave() 19 | r = compare_versions(in, ref, '>='); 20 | return 21 | end 22 | 23 | inp = split(in, ' '); 24 | in_str = split(inp(1), "."); 25 | 26 | refp = split(ref, ' '); 27 | ref_str = split(refp(1), "."); 28 | 29 | % Compare numeric parts first 30 | for i = 1:min(length(in_str), length(ref_str)) 31 | in_num = double(in_str(i)); 32 | ref_num = double(ref_str(i)); 33 | 34 | if isnan(in_num) || isnan(ref_num) 35 | % assume values are leading integer with trailing string 36 | % extract integer part and compare 37 | in_num = double(regexp(in_str(i), "\d+", "match", "once")); 38 | ref_num = double(regexp(ref_str(i), "\d+", "match", "once")); 39 | 40 | if isnan(in_num) || isnan(ref_num) || in_num == ref_num 41 | % compare string parts 42 | in_str_part = regexp(in_str(i), "\D+", "match", "once"); 43 | ref_str_part = regexp(ref_str(i), "\D+", "match", "once"); 44 | if in_str_part > ref_str_part 45 | r = true; 46 | return 47 | elseif in_str_part < ref_str_part 48 | r = false; 49 | return 50 | end 51 | 52 | continue 53 | end 54 | end 55 | 56 | % Compare numerically 57 | if in_num > ref_num 58 | r = true; 59 | return 60 | elseif in_num < ref_num 61 | r = false; 62 | return 63 | end 64 | end 65 | 66 | % If all compared parts are equal, compare lengths 67 | r = length(in_str) >= length(ref_str); 68 | 69 | end 70 | 71 | %!assert(version_atleast("1.2.3", "1.2")) 72 | %!assert(version_atleast("20.11a", "20.3b")) 73 | -------------------------------------------------------------------------------- /+stdlib/which.m: -------------------------------------------------------------------------------- 1 | %% WHICH find executable in fpath or env var PATH 2 | % like Python shutil.which, find executable in fpath or env var PATH 3 | % does not resolve path. 4 | % That is, can return relative path if executable is in: 5 | % * (Windows) in cwd 6 | % * (all) fpath or Path contains relative paths 7 | % 8 | % find_all option finds all executables specified under PATH, instead of only the first 9 | 10 | function exe = which(filename, fpath, find_all) 11 | arguments 12 | filename (1,1) string 13 | fpath (1,:) string = string.empty 14 | find_all (1,1) logical = false 15 | end 16 | 17 | exe = string.empty; 18 | 19 | if isfile(filename) && stdlib.is_exe(filename) 20 | exe = stdlib.posix(filename); 21 | return 22 | end 23 | 24 | % relative directory component, but path was not a file 25 | if stdlib.filename(filename) ~= filename 26 | return 27 | end 28 | 29 | % path given 30 | if isempty(fpath) 31 | fpath = string(getenv("PATH")); 32 | end 33 | 34 | if isscalar(fpath) 35 | fpath = split(fpath, pathsep).'; 36 | end 37 | 38 | for p = fpath 39 | if ~strlength(p), continue, end 40 | 41 | e = p + "/" + filename; 42 | if isfile(e) && stdlib.is_exe(e) 43 | if find_all 44 | exe(end+1) = stdlib.posix(e); %#ok 45 | else 46 | exe = stdlib.posix(e); 47 | return 48 | end 49 | end 50 | end 51 | 52 | end 53 | 54 | %!testif 0 55 | -------------------------------------------------------------------------------- /+stdlib/windows_shortname.m: -------------------------------------------------------------------------------- 1 | %% WINDOWS_SHORTNAME Retrieves the Windows short name 2 | % optional: mex 3 | % 4 | % (8.3 character) filename 5 | % 6 | % Example of using a COM server (Scripting.FileSystemObject) in Windows 7 | % 8 | % References: 9 | % https://learn.microsoft.com/en-us/previous-versions/windows/internet-explorer/ie-developer/windows-scripting/ch28h2s7 10 | % https://www.mathworks.com/matlabcentral/fileexchange/48950-short-path-name-on-windows-com-server 11 | 12 | function s = windows_shortname(p) 13 | arguments 14 | p {mustBeTextScalar} 15 | end 16 | 17 | s = p; 18 | if ispc 19 | fso = actxserver('Scripting.FileSystemObject'); 20 | 21 | if isfolder(p) 22 | s = fso.GetFolder(p).ShortPath; 23 | elseif isfile(p) 24 | s = fso.GetFile(p).ShortPath; 25 | end 26 | 27 | delete(fso); 28 | end 29 | 30 | s = string(s); 31 | 32 | 33 | end 34 | 35 | %!testif 0 36 | -------------------------------------------------------------------------------- /+stdlib/with_suffix.m: -------------------------------------------------------------------------------- 1 | %% WITH_SUFFIX switch file extension 2 | % 3 | %%% Inputs 4 | % * p: path to modify 5 | % * suffix: file extension with "." e.g. ".dat" 6 | %%% Outputs 7 | % * f: modified filename 8 | 9 | function f = with_suffix(p, suffix) 10 | arguments 11 | p {mustBeTextScalar} 12 | suffix {mustBeTextScalar} 13 | end 14 | 15 | f = ""; 16 | 17 | r = stdlib.parent(p); 18 | if ~strlength(r), return, end 19 | 20 | s = stdlib.stem(p); 21 | 22 | if strlength(s) == 0 23 | f = stdlib.join(p, suffix); 24 | return 25 | end 26 | 27 | if strcmp(r, '.') 28 | f = s; 29 | else 30 | f = strcat(r, '/', s); 31 | end 32 | 33 | f = strcat(f, suffix); 34 | 35 | end 36 | 37 | %!assert(with_suffix("ab.h5", ".nc"), "ab.nc") 38 | %!assert(with_suffix("ab", ".nc"), "ab.nc") 39 | %!assert(with_suffix("ab.h5", ""), "ab") 40 | %!assert(with_suffix("ab", ""), "ab") 41 | %!assert(with_suffix("ab/.h5", ".nc"), "ab/.h5.nc") 42 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | insert_final_newline = true 7 | trim_trailing_whitespace = true 8 | 9 | indent_style = space 10 | indent_size = 2 11 | 12 | [*.py] 13 | indent_size = 4 14 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | 3 | *.fig binary 4 | *.mat binary 5 | *.mdl binary diff merge=mlAutoMerge 6 | *.mdlp binary 7 | *.mex* binary 8 | *.mlapp binary 9 | *.mldatx binary merge=mlAutoMerge 10 | *.mlproj binary 11 | *.mlx binary 12 | *.p binary 13 | *.plprj binary 14 | *.sbproj binary 15 | *.sfx binary 16 | *.sldd binary 17 | *.slreqx binary merge=mlAutoMerge 18 | *.slmx binary merge=mlAutoMerge 19 | *.sltx binary 20 | *.slxc binary 21 | *.slx binary merge=mlAutoMerge 22 | *.slxp binary 23 | 24 | ## MATLAB Project metadata files use LF line endings 25 | /resources/project/**/*.xml text eol=lf 26 | 27 | ## Other common binary file types 28 | *.docx binary 29 | *.exe binary 30 | *.jpg binary 31 | *.pdf binary 32 | *.png binary 33 | *.xlsx binary 34 | -------------------------------------------------------------------------------- /.github/workflows/ci-nojvm.yml: -------------------------------------------------------------------------------- 1 | name: ci-nojvm 2 | 3 | on: 4 | push: 5 | paths: 6 | - "**.m" 7 | - "**.h" 8 | - "**.cpp" 9 | - ".github/workflows/ci-nojvm.yml" 10 | - "!private/publish_gen_index_html.m" 11 | - "!octave_build.m" 12 | - "!scripts/**" 13 | 14 | # avoid wasted runs 15 | concurrency: 16 | group: ${{ github.workflow }}-${{ github.ref }} 17 | cancel-in-progress: true 18 | 19 | 20 | jobs: 21 | 22 | NoJVM: 23 | timeout-minutes: 15 24 | runs-on: ${{ matrix.os }} 25 | 26 | strategy: 27 | fail-fast: false 28 | matrix: 29 | os: [ubuntu-22.04, macos-latest, windows-latest] 30 | release: [latest] 31 | startup-options: ["-nojvm"] 32 | 33 | steps: 34 | - uses: actions/checkout@v4 35 | 36 | - uses: ./.github/workflows/composite-install-matlab 37 | 38 | - name: Fortran FC 39 | if: runner.os == 'macOS' 40 | run: echo "FC=gfortran-14" >> $GITHUB_ENV 41 | 42 | - uses: ./.github/workflows/composite-buildtool 43 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: ci 2 | 3 | on: 4 | push: 5 | paths: 6 | - "**.m" 7 | - "**.h" 8 | - "**.cpp" 9 | - ".github/workflows/ci.yml" 10 | - "!private/publish_gen_index_html.m" 11 | - "!octave_build.m" 12 | - "!scripts/**" 13 | 14 | # avoid wasted runs 15 | concurrency: 16 | group: ${{ github.workflow }}-${{ github.ref }} 17 | cancel-in-progress: true 18 | 19 | jobs: 20 | 21 | matlab: 22 | timeout-minutes: 15 23 | runs-on: ${{ matrix.os }} 24 | 25 | strategy: 26 | fail-fast: false 27 | matrix: 28 | os: [ubuntu-22.04] 29 | release: [R2022a, R2022b, R2023a, R2023b, R2024a, R2024b] 30 | include: 31 | - os: macos-latest 32 | release: R2024b 33 | - os: macos-latest 34 | release: R2023b 35 | - os: windows-latest 36 | release: R2024b 37 | - os: windows-latest 38 | release: R2023a 39 | 40 | 41 | steps: 42 | - uses: actions/checkout@v4 43 | 44 | - uses: ./.github/workflows/composite-install-matlab 45 | 46 | - name: Linux CXX 47 | if: ${{ matrix.os == 'ubuntu-22.04' }} 48 | run: echo "CXXMEX=g++-10" >> $GITHUB_ENV 49 | 50 | - name: Fortran FC 51 | if: runner.os == 'macOS' 52 | run: echo "FC=gfortran-14" >> $GITHUB_ENV 53 | 54 | - uses: ./.github/workflows/composite-buildtool 55 | -------------------------------------------------------------------------------- /.github/workflows/composite-buildtool/action.yml: -------------------------------------------------------------------------------- 1 | name: "matlab-buildtool" 2 | 3 | runs: 4 | 5 | using: "composite" 6 | 7 | steps: 8 | 9 | - name: Check task 10 | if: ${{ matrix.release >= 'R2024a' || startsWith(matrix.release, 'latest') }} 11 | uses: matlab-actions/run-build@v2 12 | with: 13 | startup-options: ${{ matrix.startup-options }} 14 | tasks: check 15 | 16 | - name: Upload CodeIssues 17 | if: failure() 18 | uses: actions/upload-artifact@v4 19 | with: 20 | name: ${{ matrix.release }}-${{ runner.os }}-codeissues 21 | path: CodeIssues.sarif 22 | 23 | - name: Non-Mex Test 24 | if: ${{ matrix.release >= 'R2024b' || startsWith(matrix.release, 'latest') }} 25 | uses: matlab-actions/run-build@v2 26 | with: 27 | startup-options: ${{ matrix.startup-options }} 28 | tasks: test 29 | 30 | - name: Mex Test 31 | if: ${{ matrix.release >= 'R2023a' || startsWith(matrix.release, 'latest') }} 32 | uses: matlab-actions/run-build@v2 33 | with: 34 | startup-options: ${{ matrix.startup-options }} -logfile ${{ matrix.release }}-${{ runner.os }}-test.log 35 | tasks: mex test 36 | 37 | 38 | # note: "source-folder" is necessary, but watch out as it adds all subfolders to path too 39 | # https://github.com/matlab-actions/run-tests?tab=readme-ov-file#run-matlab-tests 40 | - name: Run tests (manual) 41 | if: ${{ matrix.release < 'R2023a' && !startsWith(matrix.release, 'latest') }} 42 | uses: matlab-actions/run-tests@v2 43 | with: 44 | source-folder: ${{ github.workspace }} 45 | select-by-folder: test 46 | strict: false 47 | startup-options: -logfile ${{ matrix.release }}-${{ runner.os }}-test.log 48 | 49 | 50 | - name: upload logfile 51 | if: ${{ hashFiles(matrix.release-runner.os-test.log) != '' }} 52 | uses: actions/upload-artifact@v4 53 | with: 54 | name: ${{ matrix.release }}-${{ runner.os }}-test-log 55 | path: ${{ matrix.release }}-${{ runner.os }}-test.log 56 | retention-days: 1 57 | -------------------------------------------------------------------------------- /.github/workflows/composite-install-matlab/action.yml: -------------------------------------------------------------------------------- 1 | name: "install-matlab" 2 | 3 | runs: 4 | 5 | using: "composite" 6 | 7 | steps: 8 | 9 | - name: Install MATLAB 10 | uses: matlab-actions/setup-matlab@v2 11 | with: 12 | release: ${{ matrix.release }} 13 | cache: true 14 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | # Simple workflow for deploying static content to GitHub Pages 2 | name: Deploy static content to Pages 3 | 4 | on: 5 | # Runs on pushes targeting the default branch 6 | push: 7 | branches: ["main"] 8 | 9 | # Allows you to run this workflow manually from the Actions tab 10 | workflow_dispatch: 11 | 12 | # Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages 13 | permissions: 14 | contents: read 15 | pages: write 16 | id-token: write 17 | 18 | # Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. 19 | # However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. 20 | concurrency: 21 | group: "pages" 22 | cancel-in-progress: false 23 | 24 | 25 | jobs: 26 | 27 | deploy: 28 | 29 | strategy: 30 | matrix: 31 | release: ["latest"] 32 | 33 | environment: 34 | name: github-pages 35 | url: ${{ steps.deployment.outputs.page_url }} 36 | 37 | runs-on: ubuntu-22.04 38 | 39 | steps: 40 | - uses: actions/checkout@v4 41 | 42 | - name: Setup Pages 43 | uses: actions/configure-pages@v5 44 | 45 | - uses: ./.github/workflows/composite-install-matlab 46 | 47 | - name: Run Matlab buildtool 48 | uses: matlab-actions/run-build@v2 49 | with: 50 | tasks: publish 51 | # don't run "test" task as then even "clean" will leave Matlab in cached state where 52 | # it won't publish due to thinking a function is a Mex file. Error is 53 | # "Error using publish Only MATLAB code can be published" 54 | 55 | - name: Upload artifact 56 | uses: actions/upload-pages-artifact@v3 57 | with: 58 | path: 'docs/' 59 | 60 | - name: Deploy to GitHub Pages 61 | id: deployment 62 | uses: actions/deploy-pages@v4 63 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | Filesystem.class 2 | CodeIssues.sarif 3 | TestResults.xml 4 | code-coverage.xml 5 | 6 | resources/ 7 | .buildtool/ 8 | *.asv 9 | docs/ 10 | 11 | *.mex* 12 | *.oct 13 | 14 | test/printenv.exe 15 | test/stdout_stderr_c.exe 16 | test/stdout_stderr_fortran.exe 17 | test/stdin_cpp.exe 18 | test/stdin_fortran.exe 19 | test/sleep.exe 20 | -------------------------------------------------------------------------------- /CITATION.cff: -------------------------------------------------------------------------------- 1 | cff-version: 1.2.0 2 | authors: 3 | - family-names: Hirsch 4 | given-names: Michael 5 | orcid: https://orcid.org/0000-0002-1637-6526 6 | title: Matlab-stdlib 7 | doi: 10.5281/zenodo.3964540 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 SciVision, Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | # Standard library for Matlab 2 | 3 | [![DOI](https://zenodo.org/badge/273830124.svg)](https://zenodo.org/badge/latestdoi/273830124) 4 | [![View stdlib for Matlab on File Exchange](https://www.mathworks.com/matlabcentral/images/matlab-file-exchange.svg)](https://www.mathworks.com/matlabcentral/fileexchange/78673-stdlib-for-matlab) 5 | [![ci](https://github.com/geospace-code/matlab-stdlib/actions/workflows/ci.yml/badge.svg)](https://github.com/geospace-code/matlab-stdlib/actions/workflows/ci.yml) 6 | [![ci-nojvm](https://github.com/geospace-code/matlab-stdlib/actions/workflows/ci-nojvm.yml/badge.svg)](https://github.com/geospace-code/matlab-stdlib/actions/workflows/ci-nojvm.yml) 7 | 8 | Matlab or 9 | [GNU Octave](./Readme_octave.md) 10 | users coming from other languages will benefit from the functionality contained within this user-developed, unofficial "stdlib" standard library of functions. 11 | These system, filesystem, and HDF5 / HDF4 / NetCDF functions are used across numerous projects. 12 | 13 | Matlab R2019b is the minimum required due to use of 14 | [arguments](https://www.mathworks.com/help/matlab/ref/arguments.html) 15 | syntax. 16 | URLs (e.g. https://, s3:// and similar) are treated as not existing. 17 | 18 | Self-tests can be run from the matlab-stdlib/ directory: 19 | 20 | ```matlab 21 | %% Matlab R2023a or newer 22 | buildtool test 23 | 24 | 25 | %% Matlab R2021a or newer 26 | addpath . 27 | runtests("test") 28 | ``` 29 | 30 | Functions requiring or optionally benefiting from MEX are indicated in the 31 | [API Documentation](https://geospace-code.github.io/matlab-stdlib). 32 | 33 | Build the optional high-performance 34 | [MEX](https://www.mathworks.com/help/matlab/cpp-mex-file-applications.html) 35 | functions from the Matlab prompt in Matlab R2023a or newer: 36 | 37 | ```matlab 38 | buildtool mex 39 | ``` 40 | 41 | If just building MEX functions for the first time, to ensure the MEX functions are used instead of the plain Matlab script, one-time type `clear functions` in Matlab. 42 | 43 | ## Java-based functions 44 | 45 | Most Matlab-stdlib filesystem functions work without the 46 | [Java interface](./Readme_java.md). 47 | If Matlab was started without Java using 48 | [-nojvm](https://www.mathworks.com/help/matlab/matlab_env/commonly-used-startup-options.html), 49 | most Matlab-stdlib functions still work. 50 | This 51 | [CI job](https://github.com/geospace-code/matlab-stdlib/actions/workflows/ci-nojvm.yml) 52 | tests without Java. 53 | 54 | ## Acknowledgments 55 | 56 | Stdlib for Matlab was partly funded by NASA NNH19ZDA001N-HDEE grant 80NSSC20K0176. 57 | -------------------------------------------------------------------------------- /Readme_java.md: -------------------------------------------------------------------------------- 1 | # Matlab-Stdlib Java functions 2 | 3 | Some Matlab-Stdlib functions use the factory JRE, and have been tested with JVM versions 8 and 17 and newer. 4 | 5 | In general, Java's java.io.File() and java.nio.file don't work with Windows App Execution Aliases. 6 | However, Matlab's intrinsic file functions do work with Windows App Execution Aliases. 7 | 8 | Matlab's Java interface was 9 | [introduced in Matlab 6.0 R12](http://www.ece.northwestern.edu/local-apps/matlabhelp/base/relnotes/matlab/matlab124.html#20684) 10 | in the year 2000. 11 | GNU Octave also can 12 | [use Java](https://docs.octave.org/latest/Set-up-the-JVM.html). 13 | Matlab-Stdlib uses only factory JRE classes where intrinsic Matlab code isn't easily capable of provided the needed algorithms. 14 | 15 | Tell JVM version: 16 | 17 | ```matlab 18 | version("-java") 19 | ``` 20 | 21 | Get the Java API level: 22 | 23 | ```matlab 24 | stdlib.java_api() 25 | ``` 26 | 27 | Get the Java vendor: 28 | 29 | ```matlab 30 | stdlib.java_vendor() 31 | ``` 32 | 33 | Get the Java version: 34 | 35 | ```matlab 36 | stdlib.java_version() 37 | ``` 38 | 39 | From before Matlab R2019b to at least Matlab R2024b, the Matlab factory Java version is 1.8, which is adequate for all Matlab-stdlib functionality. 40 | 41 | If desired (not used by Matlab-stdlib), one can use non-factory Java classes in 42 | [Matlab](](https://www.mathworks.com/help/matlab/matlab_external/static-path-of-java-class-path.html)) 43 | and 44 | [GNU Octave](https://docs.octave.org/interpreter/Making-Java-Classes-Available.html). 45 | 46 | 47 | ## Configure Matlab JVM 48 | 49 | The Matlab Java interface is like other Matlab external languages such as Python. 50 | The Matlab default 51 | [JVM can be configured](https://www.mathworks.com/help/matlab/matlab_external/configure-your-system-to-use-java.html) 52 | to 53 | [compatible JRE](https://www.mathworks.com/support/requirements/language-interfaces.html) 54 | across 55 | [Matlab versions](https://www.mathworks.com/support/requirements/openjdk.html) 56 | by using the 57 | [jenv](https://www.mathworks.com/help/matlab/ref/jenv.html) 58 | Matlab function. 59 | 60 | Tell JVM details: 61 | 62 | ```matlab 63 | jenv 64 | ``` 65 | 66 | For example, to use the 67 | [JDK 17 on macOS](https://www.oracle.com/java/technologies/downloads/#jdk17-mac) 68 | download and extract the ARM64 Compressed Archive. 69 | Tell Matlab to use this JDK from the Matlab console by: 70 | 71 | ```matlab 72 | jenv("/path/to/jdk-17/Contents/Home") 73 | ``` 74 | 75 | To 76 | [revert back to the factory JRE](https://www.mathworks.com/help/matlab/ref/matlab_jenv.html) 77 | if Matlab can't start or has problems, from system Terminal (not within Matlab): 78 | 79 | ```sh 80 | matlab_jenv factory 81 | ``` 82 | 83 | ## Configure GNU Octave JVM 84 | 85 | GNU 86 | [Octave JVM](https://docs.octave.org/latest/Set-up-the-JVM.html) 87 | can be configured with the JAVA_HOME environment variable. 88 | Some install packages don't include Java. 89 | For example, with Homebrew: 90 | 91 | ```sh 92 | brew install octave openjdk 93 | ``` 94 | 95 | If OpenJDK version updates, GNU Octave might not automatically find the new version: 96 | 97 | > libjvm: failed to load 98 | 99 | To correct this, find the path to the new JVM. 100 | Windows installs Java with Octave, so this usually isn't needed. 101 | For example, on macOS: 102 | 103 | ```sh 104 | gfind $(brew --prefix) -name libjvm.dylib 105 | ``` 106 | 107 | Within Octave, tell Octave the directory that libjvm is under. 108 | This setting is not persistent. 109 | If it works, add the setenv() command to 110 | [.octaverc](https://docs.octave.org/interpreter/Startup-Files.html) 111 | 112 | ```octave 113 | setenv("JAVA_HOME", "/path/to/openjdk/") 114 | ``` 115 | -------------------------------------------------------------------------------- /Readme_octave.md: -------------------------------------------------------------------------------- 1 | # GNU Octave with Matlab-stdlib 2 | 3 | If using GNU Octave instead of Matlab the minimum Octave version is 7.0. 4 | 5 | For HDF5 h5*() functions, install 6 | [hdf5oct](https://gnu-octave.github.io/packages/hdf5oct/) 7 | package from Octave prompt: 8 | 9 | ```octave 10 | pkg install -forge hdf5oct 11 | ``` 12 | 13 | For NetCDF4 nc*() functions, install 14 | [netcdf](https://gnu-octave.github.io/packages/netcdf/) 15 | package from Octave prompt: 16 | 17 | ```octave 18 | pkg install -forge netcdf 19 | ``` 20 | 21 | ## C++ Oct files 22 | 23 | Optionally, to enable higher-performance (faster) C++-based .oct functions, run the 24 | [octave_build](./octave_build.m) 25 | script from GNU Octave. 26 | -------------------------------------------------------------------------------- /archive/find_fortran_compiler.m: -------------------------------------------------------------------------------- 1 | function fc = find_fortran_compiler(FC) 2 | %% find_fortran_compiler(FC) 3 | % Find Fortran compiler 4 | % for Fortran 2008+ compilers 5 | % 6 | % fc = find_fortran_compiler() finds the first Fortran compiler available 7 | % and working on your system. 8 | % 9 | % fc = find_fortran_compiler(FC) searches first for the compiler specified 10 | % in string FC. 11 | 12 | arguments 13 | FC (1,1) string = getenv('FC') 14 | end 15 | 16 | % stops upon finding first working Fortran compiler 17 | FCs = ["gfortran", "ifx", "flang", "nvfortran", "nagfor", "ftn"]; 18 | 19 | if strlength(FC) > 0 20 | FCs = [FC, FCs]; 21 | end 22 | 23 | fstem = tempname; 24 | fn = fstem + ".f90"; 25 | rfn = fstem + ".exe"; 26 | 27 | prog = 'program; use iso_fortran_env; print *,compiler_version(); end program'; 28 | fid = fopen(fn, 'wt'); 29 | fprintf(fid, '%s\n', prog); 30 | fclose(fid); 31 | 32 | for f = FCs 33 | fc = stdlib.which(f); 34 | if isempty(fc) 35 | continue 36 | end 37 | 38 | [stat, ~] = system(fc + " " + fn + " -o" + rfn); % gobble error messages 39 | if stat ~= 0 40 | continue 41 | end 42 | 43 | [stat, msg] = system(rfn); 44 | if stat == 0 45 | disp(msg) 46 | return 47 | end 48 | end 49 | 50 | if ~isempty(fc) && stat ~= 0 51 | fc = string.empty; 52 | end 53 | 54 | end 55 | -------------------------------------------------------------------------------- /archive/md5sum.m: -------------------------------------------------------------------------------- 1 | function hash = md5sum(file) 2 | %% md5sum(file) 3 | % DEPRECATED: use file_checksum(file, "md5") instead 4 | % compute MD5 hash of file 5 | arguments 6 | file (1,1) string {mustBeFile} 7 | end 8 | 9 | if ismac 10 | [stat, hash] = system("md5 -r " + file); 11 | elseif isunix 12 | [stat, hash] = system("md5sum " + file); 13 | elseif ispc 14 | [stat, hash] = system("CertUtil -hashfile " + file + " MD5"); 15 | else 16 | error("no method for your OS") 17 | end 18 | 19 | assert(stat == 0, hash, "failed to compute md5sum of " + file) 20 | 21 | hash = regexp(hash,'^\w{32}','match','once','lineanchors'); 22 | hash = string(hash); 23 | 24 | assert(strlength(hash)==32, 'md5 hash is 32 characters') 25 | 26 | end 27 | -------------------------------------------------------------------------------- /archive/sha256sum.m: -------------------------------------------------------------------------------- 1 | function hash = sha256sum(file) 2 | %% sha256sum 3 | % DEPRECATED: use file_checksum(file, "SHA-256") instead 4 | % compute sha256 checksum of filetemp 5 | arguments 6 | file (1,1) string {mustBeFile} 7 | end 8 | 9 | if ismac 10 | [stat, hash] = system("shasum --algorithm 256 --binary " + file); 11 | elseif isunix 12 | [stat, hash] = system("sha256sum --binary " + file); 13 | elseif ispc 14 | [stat, hash] = system("CertUtil -hashfile " + file + " SHA256"); 15 | else 16 | error("no method for your OS") 17 | end 18 | 19 | assert(stat == 0, hash, "failed to compute SHA256 hash of " + file) 20 | 21 | hash = regexp(hash,'^\w{64}','match','once','lineanchors'); 22 | hash = string(hash); 23 | 24 | assert(strlength(hash)==64, 'SHA256 hash is 64 characters') 25 | 26 | end 27 | -------------------------------------------------------------------------------- /codemeta.json: -------------------------------------------------------------------------------- 1 | { 2 | "@context": "https://doi.org/10.5063/schema/codemeta-2.0", 3 | "@type": "SoftwareSourceCode", 4 | "license": "https://spdx.org/licenses/MIT", 5 | "codeRepository": "https://github.com/geospace-code/matlab-stdlib", 6 | "contIntegration": "https://github.com/geospace-code/matlab-stdlib/actions", 7 | "downloadUrl": "https://github.com/geospace-code/matlab-stdlib/releases", 8 | "issueTracker": "https://github.com/geospace-code/matlab-stdlib/issues", 9 | "name": "matlab-stdlib", 10 | "identifier": "10.5281/zenodo.3964541", 11 | "description": "Matlab unofficial standard library, including HDF5 / NetCDF4 functions that should be included in Matlab.", 12 | "applicationCategory": "computation", 13 | "developmentStatus": "active", 14 | "keywords": [ 15 | "hdf5", "netcdf4" 16 | ], 17 | "programmingLanguage": [ 18 | "Matlab" 19 | ], 20 | "author": [ 21 | { 22 | "@type": "Person", 23 | "@id": "https://orcid.org/0000-0002-1637-6526", 24 | "givenName": "Michael", 25 | "familyName": "Hirsch" 26 | } 27 | ] 28 | } 29 | -------------------------------------------------------------------------------- /example/+javafun/canonical.m: -------------------------------------------------------------------------------- 1 | function c = canonical(p) 2 | % https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/io/File.html#getCanonicalPath() 3 | 4 | % incorrect result if relative path and any component of path does not exist 5 | % disp("java") 6 | c = javaFileObject(p).getCanonicalPath(); 7 | 8 | end 9 | -------------------------------------------------------------------------------- /example/+javafun/exists.m: -------------------------------------------------------------------------------- 1 | %% EXISTS does path exist 2 | % Windows: does NOT detect App Execution Aliases 3 | % https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/nio/file/Files.html#exists(java.nio.file.Path,java.nio.file.LinkOption...) 4 | % this takes 2x longer than java.io.File.exists() 5 | % opt = javaLinkOption(); 6 | % ok = java.nio.file.Files.exists(java.io.File(p).toPath(), opt); 7 | 8 | % https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/io/File.html#exists() 9 | % takes 2x longer than native Matlab isfile || isfolder 10 | 11 | function ok = exists(p) 12 | ok = javaFileObject(p).exists(); 13 | end -------------------------------------------------------------------------------- /example/+javafun/file_size.m: -------------------------------------------------------------------------------- 1 | function s = file_size(p) 2 | 3 | s = javaFileObject(p).length(); 4 | 5 | end -------------------------------------------------------------------------------- /example/+javafun/homedir.m: -------------------------------------------------------------------------------- 1 | function h = homedir() 2 | % * https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/lang/System.html#getProperty(java.lang.String) 3 | % * https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/lang/System.html#getProperties() 4 | 5 | h = javaSystemProperty("user.home"); 6 | end 7 | -------------------------------------------------------------------------------- /example/+javafun/is_absolute.m: -------------------------------------------------------------------------------- 1 | function y = is_absolute() 2 | % java is about 5x to 10x slower than intrinsic 3 | % https://docs.oracle.com/en/java/javase/22/docs/api/java.base/java/io/File.html#isAbsolute() 4 | y = javaFileObject(p).isAbsolute(); 5 | end 6 | -------------------------------------------------------------------------------- /example/+javafun/is_exe.m: -------------------------------------------------------------------------------- 1 | %% IS_EXE is file executable 2 | 3 | function ok = is_exe(p) 4 | 5 | % about the same time as fileattrib 6 | % https://docs.oracle.com/en/java/javase/22/docs/api/java.base/java/io/File.html#canExecute() 7 | % more complicated 8 | % ok = java.nio.file.Files.isExecutable(javaPathObject(stdlib.canonical(p))); 9 | 10 | ok = javaFileObject(p).canExecute(); 11 | 12 | end -------------------------------------------------------------------------------- /example/+javafun/is_readable.m: -------------------------------------------------------------------------------- 1 | % https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/nio/file/Files.html#isReadable(java.nio.file.Path) 2 | % java.nio.file.Files is about 10x slower than fileattrib 3 | % file = stdlib.absolute(file, "", false); 4 | % ok = java.nio.file.Files.isReadable(javaPathObject(file)); 5 | function y = is_readable(p) 6 | y = javaFileObject(p).canRead(); 7 | end 8 | -------------------------------------------------------------------------------- /example/+javafun/is_symlink.m: -------------------------------------------------------------------------------- 1 | function ok = is_symlink(p) 2 | % must be absolute path 3 | % NOT .canonical or symlink is gobbled! 4 | p = stdlib.absolute(p, "", false); 5 | op = javaPathObject(p); 6 | 7 | % https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/nio/file/Files.html#isSymbolicLink(java.nio.file.Path) 8 | % https://dev.java/learn/java-io/file-system/links/ 9 | 10 | ok = java.nio.file.Files.isSymbolicLink(op); 11 | 12 | end 13 | -------------------------------------------------------------------------------- /example/+javafun/is_writable.m: -------------------------------------------------------------------------------- 1 | function y = is_writable(p) 2 | 3 | % https://docs.oracle.com/en/java/javase/22/docs/api/java.base/java/nio/file/Files.html#isWritable(java.nio.file.Path) 4 | % java.nio.file.Files java is about 10x slower than fileattrib 5 | % needs absolute() 6 | % file = stdlib.absolute(file, "", false); 7 | % ok = java.nio.file.Files.isWritable(javaPathObject(file)); 8 | 9 | y = javaFileObject(p).canWrite(); 10 | end 11 | -------------------------------------------------------------------------------- /example/+javafun/join.m: -------------------------------------------------------------------------------- 1 | function p = join(base, other) 2 | 3 | r = javaPathObject(base).resolve(other); 4 | p = jPosix(r); 5 | 6 | end 7 | -------------------------------------------------------------------------------- /example/+javafun/normalize.m: -------------------------------------------------------------------------------- 1 | function n = normalize(p) 2 | 3 | % https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/nio/file/Path.html#normalize() 4 | o = javaPathObject(p).normalize(); 5 | n = stdlib.drop_slash(jPosix(o)); 6 | 7 | end 8 | -------------------------------------------------------------------------------- /example/+javafun/root.m: -------------------------------------------------------------------------------- 1 | function r = root(p) 2 | o = javaPathObject(p).getRoot(); 3 | r = jPosix(o); 4 | end -------------------------------------------------------------------------------- /example/bench_absolute.m: -------------------------------------------------------------------------------- 1 | %% benchmark 2 | 3 | f = mfilename("fullpath") + ".m"; 4 | addpath(fullfile(fileparts(f), "..")) 5 | 6 | %f = "abc"; 7 | 8 | fno = @() stdlib.absolute(f, false); 9 | 10 | t_no = timeit(fno); 11 | 12 | disp("No Java: " + t_no + " s") 13 | -------------------------------------------------------------------------------- /example/bench_exists.m: -------------------------------------------------------------------------------- 1 | f = mfilename("fullpath") + ".m"; 2 | %f = tempname; 3 | 4 | fno = @() stdlib.exists(f); 5 | 6 | t_no = timeit(fno); 7 | 8 | disp("No Java: " + t_no + " s") 9 | -------------------------------------------------------------------------------- /example/bench_file_size.m: -------------------------------------------------------------------------------- 1 | %% benchmark for file_size() 2 | 3 | f = mfilename("fullpath") + ".m"; 4 | 5 | fno = @() stdlib.file_size(f, false); 6 | 7 | t_no = timeit(fno); 8 | 9 | disp("No Java: " + t_no + " s") 10 | -------------------------------------------------------------------------------- /example/bench_homedir.m: -------------------------------------------------------------------------------- 1 | %% benchmark for exists() 2 | 3 | fno = @() stdlib.homedir(); 4 | 5 | t_no = timeit(fno); 6 | 7 | disp("No Java: " + t_no + " s") 8 | -------------------------------------------------------------------------------- /example/bench_is_absolute.m: -------------------------------------------------------------------------------- 1 | a1 = "c:/a/"; 2 | 3 | fno = @() stdlib.is_absolute(a1); 4 | 5 | t_no = timeit(fno); 6 | 7 | disp("No Java: " + t_no + " s") 8 | -------------------------------------------------------------------------------- /example/bench_is_exe.m: -------------------------------------------------------------------------------- 1 | %% benchmark 2 | 3 | f = mfilename("fullpath") + ".m"; 4 | addpath(fullfile(fileparts(f), "..")) 5 | 6 | %f = tempname; 7 | 8 | fno = @() stdlib.is_exe(f); 9 | 10 | t_no = timeit(fno); 11 | 12 | disp("No Java: " + t_no + " s") 13 | -------------------------------------------------------------------------------- /example/bench_is_readable.m: -------------------------------------------------------------------------------- 1 | %% benchmark 2 | 3 | f = mfilename("fullpath") + ".m"; 4 | addpath(fullfile(fileparts(f), "..")) 5 | 6 | %f = tempname; 7 | 8 | fno = @() stdlib.is_readable(f, false); 9 | fjava = @() stdlib.is_readable(f, true); 10 | 11 | t_no = timeit(fno); 12 | t_java = timeit(fjava); 13 | 14 | disp("No Java: " + t_no + " s") 15 | disp("Java: " + t_java + " s") 16 | 17 | disp("Java is " + t_no/t_java + " times faster") 18 | -------------------------------------------------------------------------------- /example/bench_is_writable.m: -------------------------------------------------------------------------------- 1 | %% benchmark 2 | 3 | f = mfilename("fullpath") + ".m"; 4 | addpath(fullfile(fileparts(f), "..")) 5 | 6 | %f = tempname; 7 | 8 | fno = @() stdlib.is_writable(f, false); 9 | fjava = @() stdlib.is_writable(f, true); 10 | 11 | t_no = timeit(fno); 12 | t_java = timeit(fjava); 13 | 14 | disp("No Java: " + t_no + " s") 15 | disp("Java: " + t_java + " s") 16 | 17 | disp("Java is " + t_no/t_java + " times faster") 18 | -------------------------------------------------------------------------------- /example/bench_join.m: -------------------------------------------------------------------------------- 1 | a1 = "a/"; 2 | b1 = "b/c"; 3 | 4 | fno = @() stdlib.join(a1, b1); 5 | 6 | t_no = timeit(fno); 7 | 8 | disp("No Java: " + t_no + " s") 9 | -------------------------------------------------------------------------------- /example/bench_normalize.m: -------------------------------------------------------------------------------- 1 | a1 = "c:/a///.//../////b"; 2 | 3 | fno = @() stdlib.normalize(a1); 4 | 5 | t_no = timeit(fno); 6 | 7 | disp("No Java: " + t_no + " s") -------------------------------------------------------------------------------- /example/bench_parent.m: -------------------------------------------------------------------------------- 1 | f = mfilename("fullpath") + ".m"; 2 | 3 | fno = @() stdlib.parent(f); 4 | 5 | t_no = timeit(fno); 6 | 7 | disp("No Java: " + t_no + " s") 8 | -------------------------------------------------------------------------------- /example/bench_stem.m: -------------------------------------------------------------------------------- 1 | f = mfilename("fullpath") + ".m"; 2 | 3 | fno = @() stdlib.stemed(f); 4 | 5 | t_no = timeit(fno); 6 | 7 | disp("No Java: " + t_no + " s") 8 | -------------------------------------------------------------------------------- /example/benchmark_get_owner.m: -------------------------------------------------------------------------------- 1 | a1 = "."; 2 | 3 | fjava = @() stdlib.get_owner(a1); 4 | 5 | t_java = timeit(fjava); 6 | 7 | disp("Java: " + t_java + " s") 8 | -------------------------------------------------------------------------------- /example/javaCreateSymbolicLink.m: -------------------------------------------------------------------------------- 1 | f = stdlib.absolute("Readme.md"); 2 | link = "alsoread.md"; 3 | 4 | perms = stdlib.get_permissions(f); 5 | 6 | pp = java.nio.file.attribute.PosixFilePermissions.fromString(perms); 7 | 8 | fp = java.nio.file.attribute.PosixFilePermissions.asFileAttribute(pp); 9 | 10 | tp = java.io.File(f).toPath(); 11 | lp = java.io.File(link).toPath(); 12 | 13 | java.nio.file.Files.createSymbolicLink(lp, tp, fp); 14 | % this fails with No method 'java.nio.file.Files.createSymbolicLink' with matching signature found. 15 | -------------------------------------------------------------------------------- /example/subprocess_run.java: -------------------------------------------------------------------------------- 1 | import java.io.BufferedReader; 2 | import java.io.InputStreamReader; 3 | import java.io.IOException; 4 | 5 | public class Filesystem { 6 | public static void main(String[] argv) { 7 | try { 8 | 9 | ProcessBuilder builder = new ProcessBuilder(argv[0]); 10 | Process process = builder.start(); 11 | 12 | BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream())); 13 | String line; 14 | while ((line = reader.readLine()) != null) { 15 | System.out.println(line); 16 | } 17 | 18 | 19 | BufferedReader errorReader = new BufferedReader(new InputStreamReader(process.getErrorStream())); 20 | while ((line = errorReader.readLine()) != null) { 21 | System.err.println(line); 22 | } 23 | 24 | int exitCode = process.waitFor(); 25 | System.out.println("Process exited with code: " + exitCode); 26 | 27 | process.destroy(); 28 | 29 | } catch (IOException | InterruptedException e) { 30 | // ignore 31 | } 32 | 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /matlab-stdlib.prj: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /octave_build.m: -------------------------------------------------------------------------------- 1 | % Build C++-based .oct file for GNU Octave 2 | function octave_build(overwrite) 3 | if nargin < 1, overwrite = false; end 4 | 5 | assert(stdlib.isoctave(), "for GNU Octave only") 6 | 7 | r = fileparts(mfilename("fullpath")); 8 | inc = fullfile(r, "src"); 9 | d = fullfile(inc, "octave"); 10 | t = fullfile(r, "+stdlib"); 11 | 12 | %% specific source 13 | srcs = { 14 | fullfile(d, "disk_available.cpp"), ... 15 | fullfile(d, "disk_capacity.cpp"), ... 16 | fullfile(d, "drop_slash.cpp"), ... 17 | fullfile(d, "is_wsl.cpp"), ... 18 | fullfile(d, "is_rosetta.cpp"), ... 19 | fullfile(d, "is_admin.cpp"), ... 20 | fullfile(d, "normalize.cpp"), ... 21 | fullfile(d, "parent.cpp"), ... 22 | fullfile(d, "proximate_to.cpp"), ... 23 | fullfile(d, "relative_to.cpp"), ... 24 | }; 25 | 26 | % need this for loop below 27 | unlink_bin = fullfile(t, "unlink.oct"); 28 | if ~isfile(unlink_bin) 29 | mkoctfile(fullfile(d, "unlink.cpp"), ["-I", inc], "--output", unlink_bin) 30 | end 31 | 32 | %% build C+ Octave 33 | for s = srcs 34 | src = s{1}; 35 | [~, n] = fileparts(src); 36 | bin = fullfile(t, [n, ".oct"]); 37 | 38 | if ~overwrite && stdlib.get_modtime(src) < stdlib.get_modtime(bin) 39 | continue 40 | end 41 | 42 | disp(["mkoctfile: ", src, " => ", bin]) 43 | if isfile(bin) 44 | assert(stdlib.unlink(bin)) 45 | end 46 | mkoctfile(src, ["-I", inc], "--output", bin) 47 | end 48 | -------------------------------------------------------------------------------- /private/publish_gen_index_html.m: -------------------------------------------------------------------------------- 1 | %% PUBLISH_GEN_INDEX_HTML generate index.html for package docs 2 | % publish (generate) docs from Matlab project 3 | % called from buildfile.m 4 | % 5 | % Ref: 6 | % * https://www.mathworks.com/help/matlab/ref/publish.html 7 | % * https://www.mathworks.com/help/matlab/matlab_prog/marking-up-matlab-comments-for-publishing.html 8 | % 9 | % for package code -- assumes no classes and depth == 1 10 | % 11 | % if *.mex* files are present, publish fails 12 | 13 | function publish_gen_index_html(pkg_name, tagline, project_url, outdir) 14 | arguments 15 | pkg_name (1,1) string 16 | tagline (1,1) string 17 | project_url (1,1) string 18 | outdir (1,1) string 19 | end 20 | 21 | pkg = what("+" + pkg_name); 22 | % "+" avoids picking up cwd of same name 23 | assert(~isempty(pkg), pkg_name + " is not detected as a Matlab directory or package") 24 | 25 | %% Git info 26 | repo = gitrepo(pkg.path); 27 | git_txt = "Git branch / commit: " + repo.CurrentBranch.Name + " " + repo.LastCommit.ID{1}(1:8); 28 | 29 | %% generate docs 30 | readme = fullfile(outdir, "index.html"); 31 | 32 | if ~isfolder(outdir) 33 | mkdir(outdir); 34 | end 35 | 36 | txt = ["", ... 37 | "",... 38 | '', ... 39 | '', ... 40 | '', ... 41 | "" + pkg_name + " API", ... 42 | "", ... 43 | "", ... 44 | "

" + pkg_name + " API

", ... 45 | "

" + tagline + "

", ... 46 | "

" + git_txt + "

", ... 47 | "

Project URL: " + project_url + "

", ... 48 | "

API Reference

"]; 49 | fid = fopen(readme, 'w'); 50 | fprintf(fid, join(txt, "\n")); 51 | 52 | for sub = pkg.m.' 53 | 54 | s = sub{1}; 55 | [~, name] = fileparts(s); 56 | 57 | doc_fn = publish(pkg_name + "." + name, evalCode=false, outputDir=outdir); 58 | disp(doc_fn) 59 | 60 | % inject summary for each function into Readme.md 61 | help_txt = split(string(help(pkg_name + "." + name)), newline); 62 | words = split(strip(help_txt(1)), " "); 63 | 64 | % error if no docstring 65 | fname = words(1); 66 | assert(lower(fname) == lower(name), "fname %s does not match name %s \nis there a docstring at the top of the .m file?", fname, name) 67 | 68 | line = "" + fname + " "; 69 | if(length(words) > 1) 70 | line = line + join(words(2:end)); 71 | end 72 | 73 | req = ""; 74 | 75 | if contains(help_txt(2), "requires:") || contains(help_txt(2), "optional:") 76 | req = "(" + strip(help_txt(2), " ") + ")"; 77 | end 78 | 79 | fprintf(fid, line + " " + req + "
\n"); 80 | 81 | end 82 | 83 | fprintf(fid, " "); 84 | 85 | fclose(fid); 86 | 87 | end 88 | -------------------------------------------------------------------------------- /scripts/MatlabReleaseUpgrade.m: -------------------------------------------------------------------------------- 1 | disp(matlabRelease()) 2 | 3 | r = fullfile(matlabroot, "bin", computer("arch")); 4 | cmd = fullfile(r, "MathWorksUpdateInstaller"); 5 | 6 | fprintf("Run this command in Terminal to check for Matlab upgrade:\n\n%s\n\n", cmd) 7 | -------------------------------------------------------------------------------- /src/admin_fs.cpp: -------------------------------------------------------------------------------- 1 | #if defined(_WIN32) 2 | #define WIN32_LEAN_AND_MEAN 3 | #include // GetTokenInformation 4 | #else 5 | #include // geteuid 6 | #include // geteuid, pid_t 7 | #endif 8 | 9 | #include "ffilesystem.h" 10 | 11 | 12 | bool fs_is_admin(){ 13 | // running as admin / root / superuser 14 | #if defined(_WIN32) 15 | HANDLE hToken = nullptr; 16 | TOKEN_ELEVATION elevation; 17 | DWORD dwSize; 18 | 19 | const bool ok = (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken) && 20 | GetTokenInformation(hToken, TokenElevation, &elevation, sizeof(elevation), &dwSize)); 21 | 22 | if(hToken) 23 | CloseHandle(hToken); 24 | if(ok) 25 | return elevation.TokenIsElevated; 26 | 27 | return false; 28 | #else 29 | return geteuid() == 0; 30 | #endif 31 | } 32 | -------------------------------------------------------------------------------- /src/create_symlink.cpp: -------------------------------------------------------------------------------- 1 | #include "mex.hpp" 2 | #include "mexAdapter.hpp" 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | 9 | #include "ffilesystem.h" 10 | 11 | 12 | class MexFunction : public matlab::mex::Function { 13 | private: 14 | #include "mex2string.inl" 15 | 16 | public: 17 | void operator()(matlab::mex::ArgumentList outputs, matlab::mex::ArgumentList inputs) { 18 | 19 | matlab::data::ArrayFactory factory; 20 | 21 | std::string target, link; 22 | 23 | matlab_2string(inputs, &target, &link); 24 | 25 | // convert to Matlab output 26 | // https://www.mathworks.com/help/matlab/matlab_external/create-matlab-array-with-matlab-data-cpp-api.html 27 | // https://www.mathworks.com/help/matlab/apiref/matlab.data.arrayfactory.html 28 | 29 | outputs[0] = factory.createScalar(fs_create_symlink(target, link)); 30 | } 31 | }; 32 | -------------------------------------------------------------------------------- /src/disk_available.cpp: -------------------------------------------------------------------------------- 1 | #include "mex.hpp" 2 | #include "mexAdapter.hpp" 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | #include 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | 15 | class MexFunction : public matlab::mex::Function { 16 | private: 17 | #include "mex1string.inl" 18 | 19 | public: 20 | void operator()(matlab::mex::ArgumentList outputs, matlab::mex::ArgumentList inputs) { 21 | 22 | matlab::data::ArrayFactory factory; 23 | 24 | std::error_code ec; 25 | std::uintmax_t s = std::filesystem::space(matlab_1string_input(inputs), ec).available; 26 | 27 | if (ec) 28 | s = 0; 29 | 30 | outputs[0] = factory.createScalar(s); 31 | } 32 | }; 33 | -------------------------------------------------------------------------------- /src/disk_capacity.cpp: -------------------------------------------------------------------------------- 1 | #include "mex.hpp" 2 | #include "mexAdapter.hpp" 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | #include 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | 15 | class MexFunction : public matlab::mex::Function { 16 | private: 17 | #include "mex1string.inl" 18 | 19 | public: 20 | void operator()(matlab::mex::ArgumentList outputs, matlab::mex::ArgumentList inputs) { 21 | 22 | matlab::data::ArrayFactory factory; 23 | 24 | std::error_code ec; 25 | std::uintmax_t s = std::filesystem::space(matlab_1string_input(inputs), ec).capacity; 26 | 27 | if (ec) 28 | s = 0; 29 | 30 | outputs[0] = factory.createScalar(s); 31 | } 32 | }; 33 | -------------------------------------------------------------------------------- /src/drop_slash.cpp: -------------------------------------------------------------------------------- 1 | #include "mex.hpp" 2 | #include "mexAdapter.hpp" 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | 9 | #include "ffilesystem.h" 10 | 11 | 12 | class MexFunction : public matlab::mex::Function { 13 | private: 14 | #include "mex1string.inl" 15 | 16 | public: 17 | void operator()(matlab::mex::ArgumentList outputs, matlab::mex::ArgumentList inputs) { 18 | 19 | matlab::data::ArrayFactory factory; 20 | 21 | outputs[0] = factory.createScalar(fs_drop_slash(matlab_1string_input(inputs))); 22 | } 23 | }; 24 | -------------------------------------------------------------------------------- /src/ffilesystem.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | bool fs_is_windows(); 6 | std::size_t fs_get_max_path(); 7 | std::string fs_as_posix(std::string_view); 8 | std::string fs_drop_slash(std::string_view); 9 | std::string fs_normalize(std::string_view path); 10 | std::string fs_as_posix(std::string_view); 11 | 12 | bool fs_is_url(std::string_view); 13 | int fs_is_wsl(); 14 | bool fs_is_rosetta(); 15 | bool fs_is_admin(); 16 | bool fs_is_symlink(std::string_view); 17 | bool fs_create_symlink(std::string_view, std::string_view); 18 | std::string fs_read_symlink(std::string_view); 19 | 20 | std::string fs_parent(std::string_view); 21 | std::string fs_root_name(std::string_view); 22 | 23 | bool fs_win32_is_symlink(std::string_view); 24 | std::string fs_shortname(std::string_view); 25 | -------------------------------------------------------------------------------- /src/is_admin.cpp: -------------------------------------------------------------------------------- 1 | #include "mex.hpp" 2 | #include "mexAdapter.hpp" 3 | 4 | #include 5 | #include 6 | 7 | #include "ffilesystem.h" 8 | 9 | 10 | class MexFunction : public matlab::mex::Function { 11 | private: 12 | #include "mex0.inl" 13 | 14 | public: 15 | void operator()(matlab::mex::ArgumentList outputs, matlab::mex::ArgumentList inputs) { 16 | 17 | matlab_0_input(inputs); 18 | 19 | matlab::data::ArrayFactory factory; 20 | outputs[0] = factory.createScalar(fs_is_admin()); 21 | } 22 | }; 23 | -------------------------------------------------------------------------------- /src/is_char_device.cpp: -------------------------------------------------------------------------------- 1 | #include "mex.hpp" 2 | #include "mexAdapter.hpp" 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | #include 9 | 10 | #if defined(_MSC_VER) 11 | # define WIN32_LEAN_AND_MEAN 12 | # include 13 | #else 14 | # include 15 | # include 16 | #endif 17 | 18 | 19 | static bool fs_is_char_device(std::string_view path) 20 | { 21 | // character device like /dev/null or CONIN$ 22 | #if defined(_MSC_VER) 23 | // currently broken in MSVC STL for 24 | HANDLE h = 25 | CreateFileA(path.data(), GENERIC_READ, FILE_SHARE_READ, 26 | nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr); 27 | if (h == INVALID_HANDLE_VALUE) 28 | return false; 29 | 30 | const DWORD type = GetFileType(h); 31 | CloseHandle(h); 32 | return type == FILE_TYPE_CHAR; 33 | #else 34 | std::error_code ec; 35 | return std::filesystem::is_character_file(path, ec) && !ec; 36 | #endif 37 | } 38 | 39 | 40 | class MexFunction : public matlab::mex::Function { 41 | private: 42 | #include "mex1string.inl" 43 | 44 | public: 45 | void operator()(matlab::mex::ArgumentList outputs, matlab::mex::ArgumentList inputs) 46 | { 47 | matlab::data::ArrayFactory factory; 48 | 49 | outputs[0] = factory.createScalar(fs_is_char_device(matlab_1string_input(inputs))); 50 | } 51 | }; 52 | -------------------------------------------------------------------------------- /src/is_rosetta.cpp: -------------------------------------------------------------------------------- 1 | #include "mex.hpp" 2 | #include "mexAdapter.hpp" 3 | 4 | #include 5 | #include 6 | 7 | #include "ffilesystem.h" 8 | 9 | 10 | class MexFunction : public matlab::mex::Function { 11 | private: 12 | #include "mex0.inl" 13 | 14 | public: 15 | void operator()(matlab::mex::ArgumentList outputs, matlab::mex::ArgumentList inputs) { 16 | 17 | matlab_0_input(inputs); 18 | 19 | matlab::data::ArrayFactory factory; 20 | 21 | outputs[0] = factory.createScalar(fs_is_rosetta()); 22 | } 23 | }; 24 | -------------------------------------------------------------------------------- /src/is_symlink.cpp: -------------------------------------------------------------------------------- 1 | #include "mex.hpp" 2 | #include "mexAdapter.hpp" 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | 9 | #include "ffilesystem.h" 10 | 11 | 12 | class MexFunction : public matlab::mex::Function { 13 | private: 14 | #include "mex1string.inl" 15 | 16 | public: 17 | void operator()(matlab::mex::ArgumentList outputs, matlab::mex::ArgumentList inputs) { 18 | 19 | matlab::data::ArrayFactory factory; 20 | 21 | // convert to Matlab output 22 | // https://www.mathworks.com/help/matlab/matlab_external/create-matlab-array-with-matlab-data-cpp-api.html 23 | // https://www.mathworks.com/help/matlab/apiref/matlab.data.arrayfactory.html 24 | 25 | outputs[0] = factory.createScalar(fs_is_symlink(matlab_1string_input(inputs))); 26 | } 27 | }; 28 | -------------------------------------------------------------------------------- /src/is_wsl.cpp: -------------------------------------------------------------------------------- 1 | #include "mex.hpp" 2 | #include "mexAdapter.hpp" 3 | 4 | #include 5 | #include 6 | 7 | #include "ffilesystem.h" 8 | 9 | 10 | class MexFunction : public matlab::mex::Function { 11 | private: 12 | #include "mex0.inl" 13 | 14 | public: 15 | void operator()(matlab::mex::ArgumentList outputs, matlab::mex::ArgumentList inputs) { 16 | 17 | matlab_0_input(inputs); 18 | 19 | matlab::data::ArrayFactory factory; 20 | 21 | outputs[0] = factory.createScalar(fs_is_wsl()); 22 | } 23 | }; 24 | -------------------------------------------------------------------------------- /src/linux_fs.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #if __has_include() 4 | #define HAVE_UTSNAME 5 | #include 6 | #endif 7 | 8 | #include "ffilesystem.h" 9 | 10 | 11 | int fs_is_wsl() 12 | { 13 | 14 | #ifdef HAVE_UTSNAME 15 | struct utsname buf; 16 | if (uname(&buf) != 0) 17 | return -1; 18 | 19 | std::string_view s(buf.sysname); 20 | std::string_view r(buf.release); 21 | 22 | if(s != "Linux") 23 | return 0; 24 | #ifdef __cpp_lib_starts_ends_with // C++20 25 | if (r.ends_with("microsoft-standard-WSL2")) 26 | return 2; 27 | if (r.ends_with("-Microsoft")) 28 | return 1; 29 | #endif 30 | #endif 31 | return 0; 32 | } 33 | -------------------------------------------------------------------------------- /src/macos.cpp: -------------------------------------------------------------------------------- 1 | #if defined(__APPLE__) && defined(__MACH__) 2 | #include 3 | #include 4 | #endif 5 | 6 | #include "ffilesystem.h" 7 | 8 | 9 | bool fs_is_rosetta() 10 | { 11 | #if defined(__APPLE__) && defined(__MACH__) 12 | // https://developer.apple.com/documentation/apple-silicon/about-the-rosetta-translation-environment 13 | int ret = 0; 14 | size_t size = sizeof(ret); 15 | 16 | if (sysctlbyname("sysctl.proc_translated", &ret, &size, nullptr, 0) < 0) 17 | return false; 18 | 19 | return ret == 1; 20 | #else 21 | return false; 22 | #endif 23 | } 24 | -------------------------------------------------------------------------------- /src/mex0.inl: -------------------------------------------------------------------------------- 1 | void matlab_0_input(matlab::mex::ArgumentList inputs) 2 | { 3 | std::shared_ptr matlabEng = getEngine(); 4 | 5 | matlab::data::ArrayFactory factory; 6 | 7 | if (inputs.size() != 0) { 8 | matlabEng->feval(u"error", 0, 9 | std::vector({ factory.createScalar("MexFunction requires exactly zero inputs.") })); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/mex1string.inl: -------------------------------------------------------------------------------- 1 | std::string matlab_1string_input(matlab::mex::ArgumentList inputs) 2 | { 3 | std::shared_ptr matlabEng = getEngine(); 4 | 5 | matlab::data::ArrayFactory factory; 6 | std::string in; 7 | 8 | if (inputs.size() != 1) { 9 | matlabEng->feval(u"error", 0, 10 | std::vector({ factory.createScalar("MexFunction requires exactly one scalar input argument") })); 11 | } 12 | if ((inputs[0].getType() == matlab::data::ArrayType::MATLAB_STRING && inputs[0].getNumberOfElements() == 1)){ 13 | matlab::data::TypedArray stringArr = inputs[0]; 14 | in = stringArr[0]; 15 | } else if (inputs[0].getType() == matlab::data::ArrayType::CHAR){ 16 | matlab::data::CharArray charArr = inputs[0]; 17 | in.assign(charArr.begin(), charArr.end()); 18 | } else { 19 | matlabEng->feval(u"error", 0, 20 | std::vector({ factory.createScalar("MexFunction: First input must be a scalar string or char vector") })); 21 | } 22 | 23 | return in; 24 | } 25 | -------------------------------------------------------------------------------- /src/mex2string.inl: -------------------------------------------------------------------------------- 1 | void matlab_2string(matlab::mex::ArgumentList inputs, std::string* s1, std::string* s2) 2 | { 3 | std::shared_ptr matlabEng = getEngine(); 4 | 5 | matlab::data::ArrayFactory factory; 6 | 7 | if (inputs.size() != 2) { 8 | matlabEng->feval(u"error", 0, 9 | std::vector({ factory.createScalar("Mex: Two inputs required") })); 10 | } 11 | 12 | if ((inputs[0].getType() == matlab::data::ArrayType::MATLAB_STRING && inputs[0].getNumberOfElements() == 1)){ 13 | matlab::data::TypedArray stringArr = inputs[0]; 14 | *s1 = stringArr[0]; 15 | } else if (inputs[0].getType() == matlab::data::ArrayType::CHAR){ 16 | matlab::data::CharArray charArr = inputs[0]; 17 | *s1 = std::string(charArr.begin(), charArr.end()); 18 | } else { 19 | matlabEng->feval(u"error", 0, 20 | std::vector({ factory.createScalar("Mex: First input must be a scalar string or char vector") })); 21 | } 22 | 23 | if ((inputs[1].getType() == matlab::data::ArrayType::MATLAB_STRING && inputs[1].getNumberOfElements() == 1)){ 24 | matlab::data::TypedArray stringArr = inputs[1]; 25 | *s2 = stringArr[0]; 26 | } else if (inputs[1].getType() == matlab::data::ArrayType::CHAR){ 27 | matlab::data::CharArray charArr = inputs[1]; 28 | *s2 = std::string(charArr.begin(), charArr.end()); 29 | } else { 30 | matlabEng->feval(u"error", 0, 31 | std::vector({ factory.createScalar("Mex: Second input must be a scalar string or char vector") })); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/normalize.cpp: -------------------------------------------------------------------------------- 1 | #include "mex.hpp" 2 | #include "mexAdapter.hpp" 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | 9 | #include "ffilesystem.h" 10 | 11 | 12 | class MexFunction : public matlab::mex::Function { 13 | private: 14 | #include "mex1string.inl" 15 | 16 | public: 17 | void operator()(matlab::mex::ArgumentList outputs, matlab::mex::ArgumentList inputs) { 18 | 19 | matlab::data::ArrayFactory factory; 20 | 21 | outputs[0] = factory.createScalar(fs_normalize(matlab_1string_input(inputs))); 22 | } 23 | }; 24 | -------------------------------------------------------------------------------- /src/normalize_fs.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include // std::unique 4 | 5 | #include 6 | 7 | #include "ffilesystem.h" 8 | 9 | 10 | std::string fs_drop_slash(std::string_view in) 11 | { 12 | // drop all trailing "/" and duplicated internal "/" 13 | 14 | std::filesystem::path p(in); 15 | 16 | std::string s = p.generic_string(); 17 | 18 | if (!fs_is_windows() || p != p.root_path()){ 19 | while(s.length() > 1 && s.back() == '/') 20 | s.pop_back(); 21 | } 22 | 23 | s.erase(std::unique(s.begin(), s.end(), [](char a, char b){ return a == '/' && b == '/'; }), s.end()); 24 | 25 | return s; 26 | } 27 | 28 | 29 | std::string fs_normalize(std::string_view path) 30 | { 31 | std::filesystem::path p(path); 32 | 33 | std::string r = p.lexically_normal().generic_string(); 34 | 35 | // no trailing slash 36 | if (r.length() > 1 && r.back() == '/' && (!fs_is_windows() || p != p.root_path())) 37 | r.pop_back(); 38 | 39 | if (r.empty()) 40 | r.push_back('.'); 41 | 42 | return r; 43 | } 44 | -------------------------------------------------------------------------------- /src/octave/disk_available.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | #include 6 | 7 | 8 | DEFUN_DLD (disk_available, args, nargout, 9 | "disk space available to user") 10 | { 11 | if (args.length() != 1){ 12 | octave_stdout << "Oct: One input required\n"; 13 | return octave_value(0); 14 | } 15 | 16 | std::uintmax_t s = std::filesystem::space(args(0).string_value()).available; 17 | 18 | return octave_value(s); 19 | } 20 | -------------------------------------------------------------------------------- /src/octave/disk_capacity.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | 6 | 7 | DEFUN_DLD (disk_capacity, args, nargout, 8 | "disk space capacity to user") 9 | { 10 | if (args.length() != 1){ 11 | octave_stdout << "Oct: One input required\n"; 12 | return octave_value(0); 13 | } 14 | 15 | std::uintmax_t s = std::filesystem::space(args(0).string_value()).capacity; 16 | 17 | return octave_value(s); 18 | } 19 | -------------------------------------------------------------------------------- /src/octave/drop_slash.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | #include "pure.cpp" 6 | #include "normalize_fs.cpp" 7 | 8 | 9 | DEFUN_DLD (drop_slash, args, nargout, 10 | "drop repeated and trailing slashes from a string") 11 | { 12 | if (args.length() != 1){ 13 | octave_stdout << "Oct: One input required\n"; 14 | return octave_value(""); 15 | } 16 | 17 | std::string out = fs_drop_slash(args(0).string_value()); 18 | 19 | return octave_value(out); 20 | } 21 | -------------------------------------------------------------------------------- /src/octave/is_admin.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "admin_fs.cpp" 4 | 5 | 6 | DEFUN_DLD (is_admin, args, nargout, 7 | "is the process running as admin / root / superuser") 8 | { 9 | if (args.length() != 0){ 10 | octave_stdout << "Oct: No input required\n"; 11 | return octave_value(false); 12 | } 13 | 14 | bool y = fs_is_admin(); 15 | 16 | return octave_value(y); 17 | } 18 | -------------------------------------------------------------------------------- /src/octave/is_rosetta.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "macos.cpp" 4 | 5 | 6 | DEFUN_DLD (is_rosetta, args, nargout, 7 | "is the process running under macOS Rosetta") 8 | { 9 | if (args.length() != 0){ 10 | octave_stdout << "Oct: No input required\n"; 11 | return octave_value(false); 12 | } 13 | 14 | bool y = fs_is_rosetta(); 15 | 16 | return octave_value(y); 17 | } 18 | -------------------------------------------------------------------------------- /src/octave/is_wsl.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "linux_fs.cpp" 4 | 5 | 6 | DEFUN_DLD (is_wsl, args, nargout, 7 | "is Octave running under WSL") 8 | { 9 | if (args.length() != 0){ 10 | octave_stdout << "Oct: No input required\n"; 11 | return octave_value(-1); 12 | } 13 | 14 | int wsl = fs_is_wsl(); 15 | 16 | return octave_value(wsl); 17 | } 18 | -------------------------------------------------------------------------------- /src/octave/normalize.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | #include "pure.cpp" 6 | #include "normalize_fs.cpp" 7 | 8 | 9 | DEFUN_DLD (normalize, args, nargout, 10 | "normalize path") 11 | { 12 | if (args.length() != 1){ 13 | octave_stdout << "Oct: One input required\n"; 14 | return octave_value(""); 15 | } 16 | 17 | std::string out = fs_normalize(args(0).string_value()); 18 | 19 | return octave_value(out); 20 | } 21 | -------------------------------------------------------------------------------- /src/octave/parent.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | 6 | #include "pure.cpp" 7 | #include "normalize_fs.cpp" 8 | 9 | 10 | DEFUN_DLD (parent, args, nargout, 11 | "get parent directory") 12 | { 13 | if (args.length() != 1){ 14 | octave_stdout << "Oct: One input required\n"; 15 | return octave_value(""); 16 | } 17 | 18 | std::string out = std::filesystem::path(fs_drop_slash(args(0).string_value())).parent_path().generic_string(); 19 | 20 | return octave_value(out); 21 | } 22 | -------------------------------------------------------------------------------- /src/octave/proximate_to.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | 8 | DEFUN_DLD (proximate_to, args, nargout, 9 | "path proximate to base path") 10 | { 11 | if (args.length() != 2){ 12 | octave_stdout << "Oct: Two inputs required\n"; 13 | return octave_value(""); 14 | } 15 | 16 | std::error_code ec; 17 | 18 | std::string out = std::filesystem::proximate(args(1).string_value(), 19 | args(0).string_value(), ec).generic_string(); 20 | 21 | if(ec) 22 | octave_stdout << ec.message() << "\n"; 23 | 24 | return octave_value(out); 25 | } 26 | -------------------------------------------------------------------------------- /src/octave/relative_to.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | 8 | DEFUN_DLD (relative_to, args, nargout, 9 | "path relative to base path") 10 | { 11 | if (args.length() != 2){ 12 | octave_stdout << "Oct: Two inputs required\n"; 13 | return octave_value(""); 14 | } 15 | 16 | std::error_code ec; 17 | 18 | std::string out = std::filesystem::relative(args(1).string_value(), 19 | args(0).string_value(), ec).generic_string(); 20 | 21 | if(ec) 22 | octave_stdout << ec.message() << "\n"; 23 | 24 | return octave_value(out); 25 | } 26 | -------------------------------------------------------------------------------- /src/octave/unlink.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | 6 | 7 | DEFUN_DLD (unlink, args, nargout, 8 | "delete file or empty directory") 9 | { 10 | if (args.length() != 1){ 11 | octave_stdout << "Oct: One input required\n"; 12 | return octave_value(false); 13 | } 14 | 15 | std::error_code ec; 16 | bool y = std::filesystem::remove(args(0).string_value(), ec) && !ec; 17 | 18 | return octave_value(y); 19 | } 20 | -------------------------------------------------------------------------------- /src/parent.cpp: -------------------------------------------------------------------------------- 1 | #include "mex.hpp" 2 | #include "mexAdapter.hpp" 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | 9 | #include 10 | 11 | #include "ffilesystem.h" 12 | 13 | 14 | class MexFunction : public matlab::mex::Function { 15 | private: 16 | #include "mex1string.inl" 17 | 18 | public: 19 | void operator()(matlab::mex::ArgumentList outputs, matlab::mex::ArgumentList inputs) { 20 | 21 | matlab::data::ArrayFactory factory; 22 | 23 | outputs[0] = factory.createScalar(fs_parent(matlab_1string_input(inputs))); 24 | } 25 | }; 26 | -------------------------------------------------------------------------------- /src/parent_fs.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | 6 | #include "ffilesystem.h" 7 | 8 | std::string fs_parent(std::string_view in) 9 | { 10 | std::string out; 11 | if(in.empty()) 12 | out = "."; 13 | else 14 | out = std::filesystem::path(fs_drop_slash(in)).parent_path().generic_string(); 15 | 16 | // handle "/" and other no parent cases 17 | if (out.empty()){ 18 | if (!in.empty() && in.front() == '/') 19 | out = "/"; 20 | else 21 | out = "."; 22 | } 23 | 24 | // make x: x:/ 25 | if (fs_is_windows() && out.length() == 2 && !fs_root_name(out).empty()) 26 | out.push_back('/'); 27 | 28 | return out; 29 | } 30 | -------------------------------------------------------------------------------- /src/proximate_to.cpp: -------------------------------------------------------------------------------- 1 | #include "mex.hpp" 2 | #include "mexAdapter.hpp" 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | 9 | #include 10 | #include 11 | 12 | #include "ffilesystem.h" 13 | 14 | 15 | class MexFunction : public matlab::mex::Function { 16 | private: 17 | #include "mex2string.inl" 18 | 19 | public: 20 | void operator()(matlab::mex::ArgumentList outputs, matlab::mex::ArgumentList inputs) { 21 | 22 | std::shared_ptr matlabEng = getEngine(); 23 | 24 | matlab::data::ArrayFactory factory; 25 | 26 | std::string base, other; 27 | 28 | matlab_2string(inputs, &base, &other); 29 | 30 | std::error_code ec; 31 | std::string out = std::filesystem::proximate(other, base, ec).generic_string(); 32 | 33 | if(ec) 34 | matlabEng->feval(u"error", 0, 35 | std::vector({ factory.createScalar(ec.message()) })); 36 | 37 | outputs[0] = factory.createScalar(out); 38 | } 39 | }; 40 | -------------------------------------------------------------------------------- /src/pure.cpp: -------------------------------------------------------------------------------- 1 | #include "ffilesystem.h" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #if defined(__APPLE__) && defined(__MACH__) 8 | #include 9 | #endif 10 | #if __has_include() 11 | #include 12 | #endif 13 | 14 | #include 15 | 16 | 17 | std::size_t fs_get_max_path() 18 | { 19 | #if defined(PATH_MAX) 20 | return PATH_MAX; 21 | #elif defined(_WIN32) 22 | return _MAX_PATH; 23 | #else 24 | return 1024; 25 | #endif 26 | } 27 | 28 | 29 | bool fs_is_windows() 30 | { 31 | #if defined(_WIN32) 32 | return true; 33 | #else 34 | return false; 35 | #endif 36 | } 37 | 38 | 39 | std::string fs_as_posix(std::string_view path) 40 | { 41 | std::filesystem::path p(path); 42 | 43 | return p.generic_string(); 44 | } 45 | 46 | std::string fs_root_name(std::string_view path) 47 | { 48 | return std::filesystem::path(path).root_name().generic_string(); 49 | } 50 | 51 | 52 | bool fs_is_url(std::string_view path) 53 | { 54 | return path.find("://") != std::string::npos; 55 | } 56 | -------------------------------------------------------------------------------- /src/read_symlink.cpp: -------------------------------------------------------------------------------- 1 | #include "mex.hpp" 2 | #include "mexAdapter.hpp" 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | 9 | #include "ffilesystem.h" 10 | 11 | class MexFunction : public matlab::mex::Function { 12 | private: 13 | #include "mex1string.inl" 14 | 15 | public: 16 | void operator()(matlab::mex::ArgumentList outputs, matlab::mex::ArgumentList inputs) { 17 | 18 | matlab::data::ArrayFactory factory; 19 | 20 | outputs[0] = factory.createScalar(fs_read_symlink(matlab_1string_input(inputs))); 21 | } 22 | }; 23 | -------------------------------------------------------------------------------- /src/relative_to.cpp: -------------------------------------------------------------------------------- 1 | #include "mex.hpp" 2 | #include "mexAdapter.hpp" 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | 9 | #include 10 | #include 11 | 12 | #include "ffilesystem.h" 13 | 14 | 15 | class MexFunction : public matlab::mex::Function { 16 | private: 17 | #include "mex2string.inl" 18 | 19 | public: 20 | void operator()(matlab::mex::ArgumentList outputs, matlab::mex::ArgumentList inputs) { 21 | 22 | std::shared_ptr matlabEng = getEngine(); 23 | 24 | matlab::data::ArrayFactory factory; 25 | 26 | std::string base, other; 27 | 28 | matlab_2string(inputs, &base, &other); 29 | 30 | std::error_code ec; 31 | std::string out = std::filesystem::relative(other, base, ec).generic_string(); 32 | 33 | if(ec) 34 | matlabEng->feval(u"error", 0, 35 | std::vector({ factory.createScalar(ec.message()) })); 36 | 37 | outputs[0] = factory.createScalar(out); 38 | } 39 | }; 40 | -------------------------------------------------------------------------------- /src/set_permissions.cpp: -------------------------------------------------------------------------------- 1 | #include "mex.hpp" 2 | #include "mexAdapter.hpp" 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | #include 9 | 10 | #include 11 | #include 12 | 13 | 14 | static bool fs_set_permissions(std::string_view path, int readable, int writable, int executable) 15 | { 16 | 17 | #if defined(__cpp_using_enum) // C++20 18 | using enum std::filesystem::perms; 19 | #else 20 | constexpr std::filesystem::perms owner_read = std::filesystem::perms::owner_read; 21 | constexpr std::filesystem::perms owner_write = std::filesystem::perms::owner_write; 22 | constexpr std::filesystem::perms owner_exec = std::filesystem::perms::owner_exec; 23 | #endif 24 | 25 | std::filesystem::path p(path); 26 | std::error_code ec; 27 | // need to error if path doesn't exist and no operations are requested 28 | if(!std::filesystem::exists(p)) 29 | ec = std::make_error_code(std::errc::no_such_file_or_directory); 30 | 31 | if (!ec && readable != 0) 32 | std::filesystem::permissions(p, owner_read, 33 | (readable > 0) ? std::filesystem::perm_options::add : std::filesystem::perm_options::remove, 34 | ec); 35 | 36 | if (!ec && writable != 0) 37 | std::filesystem::permissions(p, owner_write, 38 | (writable > 0) ? std::filesystem::perm_options::add : std::filesystem::perm_options::remove, 39 | ec); 40 | 41 | if (!ec && executable != 0) 42 | std::filesystem::permissions(p, owner_exec, 43 | (executable > 0) ? std::filesystem::perm_options::add : std::filesystem::perm_options::remove, 44 | ec); 45 | 46 | return !ec; 47 | } 48 | 49 | 50 | 51 | class MexFunction : public matlab::mex::Function { 52 | public: 53 | void operator()(matlab::mex::ArgumentList outputs, matlab::mex::ArgumentList inputs) { 54 | // boilerplate engine & ArrayFactory setup 55 | std::shared_ptr matlabEng = getEngine(); 56 | 57 | matlab::data::ArrayFactory factory; 58 | // wrangle inputs 59 | std::string in; 60 | 61 | if (inputs.size() != 4) { 62 | matlabEng->feval(u"error", 0, 63 | std::vector({ factory.createScalar("Mex: Four inputs required") })); 64 | } 65 | if ((inputs[0].getType() == matlab::data::ArrayType::MATLAB_STRING && inputs[0].getNumberOfElements() == 1)){ 66 | matlab::data::TypedArray stringArr = inputs[0]; 67 | in = stringArr[0]; 68 | } else if (inputs[0].getType() == matlab::data::ArrayType::CHAR){ 69 | matlab::data::CharArray charArr = inputs[0]; 70 | in.assign(charArr.begin(), charArr.end()); 71 | } else { 72 | matlabEng->feval(u"error", 0, 73 | std::vector({ factory.createScalar("Mex: First input must be a scalar string or char vector") })); 74 | } 75 | if (inputs[1].getType() != matlab::data::ArrayType::DOUBLE || 76 | inputs[2].getType() != matlab::data::ArrayType::DOUBLE || 77 | inputs[3].getType() != matlab::data::ArrayType::DOUBLE) { 78 | matlabEng->feval(u"error", 0, 79 | std::vector({ factory.createScalar("Mex: Permission inputs must be scalar doubles") })); 80 | } 81 | 82 | // actual function algorithm / computation 83 | bool y = fs_set_permissions(in, inputs[1][0], inputs[2][0], inputs[3][0]); 84 | 85 | // convert to Matlab output 86 | // https://www.mathworks.com/help/matlab/matlab_external/create-matlab-array-with-matlab-data-cpp-api.html 87 | // https://www.mathworks.com/help/matlab/apiref/matlab.data.arrayfactory.html 88 | 89 | outputs[0] = factory.createScalar(y); 90 | } 91 | }; 92 | -------------------------------------------------------------------------------- /src/symlink_fs.cpp: -------------------------------------------------------------------------------- 1 | #if defined(__MINGW__) 2 | #define WIN32_LEAN_AND_MEAN 3 | #include 4 | #endif 5 | 6 | #include 7 | #include 8 | 9 | #include 10 | #include 11 | 12 | #include "ffilesystem.h" 13 | 14 | 15 | bool fs_create_symlink(std::string_view target, std::string_view link) 16 | { 17 | // confusing program errors if target is "" -- we'd never make such a symlink in real use. 18 | if(target.empty()) 19 | return false; 20 | 21 | // macOS needs empty check to avoid SIGABRT 22 | if(link.empty()) 23 | return false; 24 | 25 | #if defined(__MINGW32__) 26 | 27 | const DWORD attr = GetFileAttributesA(target.data()); 28 | // https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-getfileattributesa 29 | // https://learn.microsoft.com/en-us/windows/win32/fileio/file-attribute-constants 30 | 31 | if (attr == INVALID_FILE_ATTRIBUTES) 32 | return false; 33 | 34 | DWORD p = SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE; 35 | if(attr & FILE_ATTRIBUTE_DIRECTORY) 36 | p |= SYMBOLIC_LINK_FLAG_DIRECTORY; 37 | 38 | return CreateSymbolicLinkA(link.data(), target.data(), p); 39 | #else 40 | std::error_code ec; 41 | std::filesystem::path t(target); 42 | 43 | bool isdir = std::filesystem::is_directory(t, ec); 44 | if (ec) 45 | return false; 46 | 47 | isdir 48 | ? std::filesystem::create_directory_symlink(t, link, ec) 49 | : std::filesystem::create_symlink(t, link, ec); 50 | 51 | return !ec; 52 | #endif 53 | } 54 | 55 | 56 | bool fs_is_symlink(std::string_view path) 57 | { 58 | 59 | #if defined(__MINGW32__) 60 | return fs_win32_is_symlink(path); 61 | #else 62 | // std::filesystem::symlink_status doesn't detect symlinks on MinGW 63 | std::error_code ec; 64 | const auto s = std::filesystem::symlink_status(path, ec); 65 | return !ec && std::filesystem::is_symlink(s); 66 | #endif 67 | } 68 | 69 | 70 | std::string fs_read_symlink(std::string_view path) 71 | { 72 | 73 | #if defined(__MINGW32__) 74 | if(fs_is_symlink(path)) 75 | return fs_win32_final_path(path); 76 | #else 77 | std::error_code ec; 78 | if(auto p = std::filesystem::read_symlink(path, ec); !ec) 79 | return p.generic_string(); 80 | #endif 81 | 82 | return {}; 83 | } 84 | -------------------------------------------------------------------------------- /src/unlink.cpp: -------------------------------------------------------------------------------- 1 | #include "mex.hpp" 2 | #include "mexAdapter.hpp" 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | #include 9 | 10 | 11 | class MexFunction : public matlab::mex::Function { 12 | private: 13 | #include "mex1string.inl" 14 | 15 | public: 16 | void operator()(matlab::mex::ArgumentList outputs, matlab::mex::ArgumentList inputs) { 17 | 18 | matlab::data::ArrayFactory factory; 19 | 20 | std::error_code ec; 21 | bool y = std::filesystem::remove(matlab_1string_input(inputs), ec) && !ec; 22 | 23 | outputs[0] = factory.createScalar(y); 24 | } 25 | }; 26 | -------------------------------------------------------------------------------- /src/windows_shortname.cpp: -------------------------------------------------------------------------------- 1 | #include "mex.hpp" 2 | #include "mexAdapter.hpp" 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | 9 | #include "ffilesystem.h" 10 | 11 | 12 | class MexFunction : public matlab::mex::Function { 13 | private: 14 | #include "mex1string.inl" 15 | 16 | public: 17 | void operator()(matlab::mex::ArgumentList outputs, matlab::mex::ArgumentList inputs) { 18 | 19 | matlab::data::ArrayFactory factory; 20 | 21 | outputs[0] = factory.createScalar(fs_shortname(matlab_1string_input(inputs))); 22 | } 23 | }; 24 | -------------------------------------------------------------------------------- /test/TestAbsolute.m: -------------------------------------------------------------------------------- 1 | classdef TestAbsolute < matlab.unittest.TestCase 2 | 3 | properties 4 | td 5 | end 6 | 7 | properties (TestParameter) 8 | p1 = {'', "", "hi", "./hi", "../hi"}; 9 | p2 = {{'', ''}, {'', ""}, {"", ''}, {"", ""}, {"", "hi"}, {"hi", ""}, {'there', "hi"}}; 10 | end 11 | 12 | 13 | methods(TestClassSetup) 14 | function set_cwd(tc) 15 | import matlab.unittest.fixtures.CurrentFolderFixture 16 | tc.td = stdlib.posix(tc.createTemporaryFolder()); 17 | tc.applyFixture(CurrentFolderFixture(tc.td)) 18 | end 19 | end 20 | 21 | 22 | methods(Test) 23 | 24 | function test_absolute1arg(tc, p1) 25 | 26 | r = tc.td; 27 | 28 | if strlength(p1) 29 | r = strcat(r, '/', p1); 30 | end 31 | 32 | if isstring(p1) 33 | r = string(r); 34 | end 35 | 36 | tc.verifyEqual(stdlib.absolute(p1), r) 37 | end 38 | 39 | 40 | function test_absolute2arg(tc, p2) 41 | 42 | r = tc.td; 43 | 44 | if strlength(p2{2}) 45 | r = strcat(r, '/', p2{2}); 46 | end 47 | 48 | if strlength(p2{1}) 49 | r = strcat(r, '/', p2{1}); 50 | end 51 | 52 | if isstring(p2{1}) 53 | r = string(r); 54 | end 55 | 56 | tc.verifyEqual(stdlib.absolute(p2{1}, p2{2}), r) 57 | end 58 | 59 | end 60 | 61 | end 62 | -------------------------------------------------------------------------------- /test/TestCanonical.m: -------------------------------------------------------------------------------- 1 | classdef TestCanonical < matlab.unittest.TestCase 2 | 3 | properties(TestParameter) 4 | p = {{'', ""}, ... 5 | {"", ""}, ... 6 | {"not-exist", "not-exist"}, ... 7 | {"a/../b", "b"}, ... 8 | {'~', string(stdlib.homedir())}, ... 9 | {"~", string(stdlib.homedir())}, ... 10 | {'~/', string(stdlib.homedir())}, ... 11 | {"~/", string(stdlib.homedir())}, ... 12 | {"~/..", string(stdlib.parent(stdlib.homedir()))}, ... 13 | {mfilename("fullpath") + ".m/..", string(stdlib.parent(mfilename("fullpath")))}, ... 14 | {"~/not-exist/a/..", stdlib.homedir() + "/not-exist"}, ... 15 | {"./not-exist", "not-exist"}, ... 16 | {"../not-exist", "../not-exist"} 17 | }; 18 | end 19 | 20 | 21 | methods(Test) 22 | 23 | function test_canonical(tc, p) 24 | tc.verifyEqual(stdlib.canonical(p{1}, true), p{2}) 25 | end 26 | 27 | end 28 | 29 | end 30 | -------------------------------------------------------------------------------- /test/TestDisk.m: -------------------------------------------------------------------------------- 1 | classdef TestDisk < matlab.unittest.TestCase 2 | 3 | properties (TestParameter) 4 | Ps = {".", "", "not-exist"} 5 | end 6 | 7 | methods (Test) 8 | 9 | function test_disk_available(tc, Ps) 10 | tc.applyFixture(matlab.unittest.fixtures.CurrentFolderFixture("..")) 11 | tc.assumeEqual(exist("+stdlib/disk_available", "file"), 3) 12 | 13 | zero = uint64(0); 14 | 15 | if stdlib.exists(Ps) 16 | tc.verifyGreaterThan(stdlib.disk_available(Ps), zero) 17 | else 18 | tc.verifyEqual(stdlib.disk_available(Ps), zero) 19 | end 20 | end 21 | 22 | function test_disk_capacity(tc, Ps) 23 | tc.applyFixture(matlab.unittest.fixtures.CurrentFolderFixture("..")) 24 | tc.assumeEqual(exist("+stdlib/disk_capacity", "file"), 3) 25 | 26 | zero = uint64(0); 27 | 28 | if stdlib.exists(Ps) 29 | tc.verifyGreaterThan(stdlib.disk_capacity(Ps), zero) 30 | else 31 | tc.verifyEqual(stdlib.disk_capacity(Ps), zero) 32 | end 33 | end 34 | 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /test/TestExists.m: -------------------------------------------------------------------------------- 1 | classdef TestExists < matlab.unittest.TestCase 2 | 3 | properties(TestParameter) 4 | Ps = {{pwd(), true}, {mfilename("fullpath") + ".m", true}, ... 5 | {"TestFileImpure.m", true} {tempname(), false}, ... 6 | {"", false}} 7 | % on CI matlabroot can be writable! 8 | end 9 | 10 | methods (Test) 11 | 12 | function test_exists(tc, Ps) 13 | ok = stdlib.exists(Ps{1}); 14 | tc.verifyEqual(ok, Ps{2}) 15 | end 16 | 17 | 18 | function test_is_readable(tc, Ps) 19 | ok = stdlib.is_readable(Ps{1}); 20 | tc.verifyEqual(ok, Ps{2}) 21 | end 22 | 23 | 24 | end 25 | 26 | end 27 | -------------------------------------------------------------------------------- /test/TestExpanduser.m: -------------------------------------------------------------------------------- 1 | classdef TestExpanduser < matlab.unittest.TestCase 2 | 3 | properties(TestParameter) 4 | 5 | p = {{'', ''}, {"", ""}, ... 6 | {"~abc", "~abc"}, ... 7 | {'~', stdlib.homedir()},... 8 | {"~", string(stdlib.homedir())}, ... 9 | {'~/', strcat(stdlib.homedir(), '/')},... 10 | {"~/", stdlib.homedir() + "/"}, ... 11 | {"~/c", stdlib.homedir() + "/c"}, ... 12 | {"~//c", stdlib.homedir() + "//c"}}; 13 | end 14 | 15 | methods(Test) 16 | 17 | function test_expanduser(tc, p) 18 | tc.verifyEqual(stdlib.expanduser(p{1}), p{2}) 19 | end 20 | 21 | end 22 | 23 | end -------------------------------------------------------------------------------- /test/TestFileImpure.m: -------------------------------------------------------------------------------- 1 | classdef TestFileImpure < matlab.unittest.TestCase 2 | 3 | properties(TestParameter) 4 | p_is_writable = {{pwd(), true}, {"not-exists", false}, {"", false}}; 5 | 6 | p_same = {... 7 | {"","", false}, ... 8 | {tempname(), tempname(), false}, ... 9 | {"..", "./..", true}, ... 10 | {"..", pwd() + "/..", true}} 11 | 12 | ph = {{0, '"stdin"'}, {1, '"stdout"'}, {2, '"stderr"'}, {fopen(tempname()), ''}} 13 | 14 | p_file_size = {mfilename("fullpath") + ".m"} 15 | end 16 | 17 | 18 | methods (Test) 19 | 20 | function test_file_size(tc, p_file_size) 21 | s = stdlib.file_size(p_file_size); 22 | tc.verifyGreaterThan(s, 0) 23 | end 24 | 25 | 26 | function test_is_writable(tc, p_is_writable) 27 | ok = stdlib.is_writable(p_is_writable{1}); 28 | tc.verifyEqual(ok, p_is_writable{2}) 29 | end 30 | 31 | 32 | function test_null_file(tc) 33 | import matlab.unittest.constraints.IsFile 34 | tc.assumeFalse(ispc) 35 | tc.verifyThat(stdlib.null_file, IsFile) 36 | end 37 | 38 | 39 | function test_makedir(tc) 40 | import matlab.unittest.constraints.IsFolder 41 | d = tempname(); 42 | stdlib.makedir(d) 43 | tc.assertThat(d, IsFolder) 44 | rmdir(d) 45 | end 46 | 47 | %% 48 | function test_samepath(tc, p_same) 49 | tc.verifyEqual(stdlib.samepath(p_same{1}, p_same{2}), p_same{3}, ... 50 | "samepath(" + p_same{1} + "," + p_same{2} + ")") 51 | end 52 | 53 | 54 | function test_get_pid(tc) 55 | pid = stdlib.get_pid(); 56 | tc.verifyGreaterThan(pid, 0) 57 | end 58 | 59 | 60 | function test_handle2filename(tc, ph) 61 | tc.verifyEqual(stdlib.handle2filename(ph{1}), ph{2}) 62 | end 63 | 64 | end 65 | 66 | end 67 | -------------------------------------------------------------------------------- /test/TestFilePure.m: -------------------------------------------------------------------------------- 1 | classdef TestFilePure < matlab.unittest.TestCase 2 | 3 | properties (TestParameter) 4 | p = init_root_name() 5 | pds = {{'/', '/'}, {'a//b', 'a/b'}, {'a//b/', 'a/b'}, {'a//b/c', 'a/b/c'}}; 6 | end 7 | 8 | 9 | methods (Test) 10 | 11 | function test_posix(tc) 12 | 13 | tc.verifyEqual(stdlib.posix(''), '') 14 | tc.verifyEqual(stdlib.posix(""), "") 15 | 16 | if ispc 17 | tc.verifyEqual(stdlib.posix("c:\abc"), "c:/abc") 18 | end 19 | end 20 | 21 | 22 | function test_drop_slash(tc, pds) 23 | import matlab.unittest.constraints.Matches 24 | tc.verifyThat(stdlib.drop_slash(pds{1}), Matches(pds{2})) 25 | end 26 | 27 | function test_root_name(tc, p) 28 | tc.verifyEqual(stdlib.root_name(p{1}), p{2}) 29 | end 30 | 31 | end 32 | end 33 | 34 | 35 | function p = init_root_name() 36 | 37 | p = {{"", ""}, ... 38 | {"a/b", ""}, ... 39 | {'/etc', ''}, ... 40 | {"c:/etc", ""} 41 | }; 42 | 43 | if ispc 44 | p{4}{2} = "c:"; 45 | end 46 | 47 | end 48 | -------------------------------------------------------------------------------- /test/TestFilename.m: -------------------------------------------------------------------------------- 1 | classdef TestFilename < matlab.unittest.TestCase 2 | 3 | properties (TestParameter) 4 | p = { 5 | {'', ''}, ... 6 | {"", ""}, ... 7 | {"/a/b/c", "c"}, ... 8 | {'/a/b/c/', ''}, ... 9 | {'a/b/c.txt', 'c.txt'}, ... 10 | {"a/b/c.txt.gz", "c.txt.gz"}, ... 11 | }; 12 | end 13 | 14 | methods (Test) 15 | function test_filename(tc, p) 16 | tc.verifyEqual(stdlib.filename(p{1}), p{2}) 17 | end 18 | end 19 | 20 | end 21 | -------------------------------------------------------------------------------- /test/TestHDF4.m: -------------------------------------------------------------------------------- 1 | classdef TestHDF4 < matlab.unittest.TestCase 2 | 3 | properties 4 | TestData 5 | end 6 | 7 | methods (TestClassSetup) 8 | function setup_file(tc) 9 | import matlab.unittest.constraints.IsFile 10 | tc.TestData.basic = fullfile(matlabroot, "toolbox/matlab/demos/example.hdf"); 11 | tc.assumeThat(tc.TestData.basic, IsFile) 12 | end 13 | end 14 | 15 | methods (Test) 16 | 17 | function test_exists(tc) 18 | import matlab.unittest.constraints.IsScalar 19 | basic = tc.TestData.basic; 20 | 21 | e = stdlib.h4exists(basic, "Example SDS"); 22 | 23 | tc.verifyThat(e, IsScalar) 24 | tc.verifyTrue(e); 25 | 26 | tc.verifyFalse(stdlib.h4exists(basic, "/j")) 27 | 28 | end 29 | 30 | 31 | function test_size(tc) 32 | basic = tc.TestData.basic; 33 | 34 | s = stdlib.h4size(basic, "Example SDS"); 35 | tc.verifyEqual(s, [16, 5]) 36 | 37 | end 38 | 39 | 40 | function test_vars(tc) 41 | basic = tc.TestData.basic; 42 | 43 | v = stdlib.h4variables(basic); 44 | tc.verifyTrue(any(contains(v, "Example SDS"))) 45 | end 46 | 47 | 48 | end 49 | end 50 | -------------------------------------------------------------------------------- /test/TestHash.m: -------------------------------------------------------------------------------- 1 | classdef TestHash < matlab.unittest.TestCase 2 | 3 | properties (TestParameter) 4 | Ph = {{'md5', '5d41402abc4b2a76b9719d911017c592'}, ... 5 | {'sha-256', '2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824'}} 6 | end 7 | 8 | methods(TestClassSetup) 9 | function java_required(tc) 10 | tc.assumeTrue(stdlib.has_java()) 11 | end 12 | end 13 | 14 | 15 | methods (Test) 16 | 17 | 18 | function test_hash_text(tc, Ph) 19 | tc.assumeFalse(isMATLABReleaseOlderThan("R2022a")) 20 | 21 | td = tc.createTemporaryFolder(); 22 | 23 | fn = td + "/hello"; 24 | fid = fopen(fn, "w"); 25 | 26 | tc.assumeGreaterThan(fid, 0); 27 | fprintf(fid, "hello"); 28 | fclose(fid); 29 | 30 | tc.assumeThat(fn, matlab.unittest.constraints.IsFile) 31 | 32 | tc.verifyEqual(stdlib.file_checksum(fn, Ph{1}), Ph{2}) 33 | 34 | end 35 | 36 | 37 | end 38 | 39 | end 40 | -------------------------------------------------------------------------------- /test/TestIni.m: -------------------------------------------------------------------------------- 1 | classdef TestIni < matlab.unittest.TestCase 2 | 3 | 4 | methods (Test) 5 | 6 | function test_example(tc) 7 | import matlab.unittest.constraints.IsFile 8 | 9 | cwd = fileparts(mfilename('fullpath')); 10 | example = stdlib.posix(cwd) + "/example.ini"; 11 | 12 | tc.assumeThat(example, IsFile) 13 | 14 | s = stdlib.ini2struct(example); 15 | tc.verifyClass(s, 'struct') 16 | tc.verifyEqual(s.DATA.keyNum, 113); 17 | 18 | end 19 | 20 | end 21 | 22 | end 23 | -------------------------------------------------------------------------------- /test/TestIsAbsolute.m: -------------------------------------------------------------------------------- 1 | classdef TestIsAbsolute < matlab.unittest.TestCase 2 | 3 | properties (TestParameter) 4 | p = init_is_absolute() 5 | end 6 | 7 | methods (Test) 8 | function test_is_absolute(tc, p) 9 | ok = stdlib.is_absolute(p{1}); 10 | tc.verifyEqual(ok, p{2}, p{1}) 11 | end 12 | end 13 | end 14 | 15 | 16 | function p = init_is_absolute() 17 | p = {{"", false}, {"x", false}, {"x:", false}, {"x:/foo", false}, {"/foo", true}}; 18 | if ispc 19 | p{4}{2} = true; 20 | p{5}{2} = false; 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /test/TestIsExe.m: -------------------------------------------------------------------------------- 1 | classdef TestIsExe < matlab.unittest.TestCase 2 | 3 | properties (TestParameter) 4 | p = init_is_exe() 5 | end 6 | 7 | methods(Test) 8 | function test_is_exe(tc, p) 9 | tc.verifyEqual(stdlib.is_exe(p{1}), p{2}) 10 | end 11 | end 12 | end 13 | 14 | 15 | function p = init_is_exe() 16 | 17 | n = "matlab"; 18 | 19 | f = matlabroot + "/bin/" + n; 20 | if ispc 21 | f = f + ".exe"; 22 | end 23 | 24 | 25 | p = {{"", false}, {tempname(), false}, {".", true}, {f, true}}; 26 | end 27 | -------------------------------------------------------------------------------- /test/TestIsSubdir.m: -------------------------------------------------------------------------------- 1 | classdef TestIsSubdir < matlab.unittest.TestCase 2 | 3 | properties (TestParameter) 4 | p_is_prefix = init_is_prefix() 5 | p_is_subdir = init_is_subdir() 6 | end 7 | 8 | 9 | methods (Test) 10 | 11 | function test_is_subdir(tc, p_is_subdir) 12 | tc.verifyEqual(stdlib.is_subdir(p_is_subdir{1}, p_is_subdir{2}), p_is_subdir{3}, ... 13 | "subdir(" + p_is_subdir{1} + "," + p_is_subdir{2} + ")") 14 | end 15 | 16 | function test_is_prefix(tc, p_is_prefix) 17 | tc.verifyEqual(stdlib.is_prefix(p_is_prefix{1}, p_is_prefix{2}), p_is_prefix{3}, ... 18 | "prefix(" + p_is_prefix{1} + "," + p_is_prefix{2} + ")") 19 | end 20 | 21 | end 22 | 23 | end 24 | 25 | 26 | function p = init_is_subdir() 27 | 28 | p = { 29 | {"a/b", "a/b", false}, ... 30 | {"a//b/c", "a/b", true}, ... 31 | {"a/b", "a//b", false}, ... 32 | {"a/./b/c", "a/b", false}, ... 33 | {"a/b/c", "a/./b", false}, ... 34 | {"a/b", "a/b/", false}, ... 35 | {"a/b", "a", true}, ... 36 | {"a/.c", "a", true}, ... 37 | {"", "", false}, ... 38 | }; 39 | % NOTE: ".." in is_subdir (either argument) is ambiguous 40 | 41 | if ispc 42 | p{end+1} = {"c:/", "c:/", false}; 43 | else 44 | p{end+1} = {"/", "/", false}; 45 | end 46 | end 47 | 48 | function p = init_is_prefix() 49 | p = init_is_subdir(); 50 | p{1}{3} = true; 51 | p{2}{3} = false; 52 | p{3}{3} = true; 53 | p{6}{3} = true; 54 | p{7}{3} = false; 55 | p{8}{3} = false; 56 | p{10}{3} = true; 57 | end 58 | -------------------------------------------------------------------------------- /test/TestJava.m: -------------------------------------------------------------------------------- 1 | classdef TestJava < matlab.unittest.TestCase 2 | 3 | properties (TestParameter) 4 | Ps = {"."} 5 | end 6 | 7 | methods(TestClassSetup) 8 | function java_required(tc) 9 | tc.assumeTrue(stdlib.has_java()) 10 | end 11 | end 12 | 13 | 14 | methods(Test) 15 | 16 | function test_filesystem_type(tc, Ps) 17 | 18 | s = stdlib.filesystem_type(Ps); 19 | tc.verifyClass(s, 'string') 20 | L = strlength(s); 21 | 22 | if strlength(Ps) == 0 || stdlib.exists(Ps) 23 | tc.verifyGreaterThan(L, 0) 24 | else 25 | tc.verifyEqual(L, 0) 26 | end 27 | end 28 | 29 | 30 | function test_owner(tc, Ps) 31 | 32 | s = stdlib.get_owner(Ps); 33 | tc.verifyClass(s, 'string') 34 | L = strlength(s); 35 | 36 | if stdlib.exists(Ps) 37 | tc.verifyGreaterThan(L, 0) 38 | else 39 | tc.verifyEqual(L, 0) 40 | end 41 | end 42 | 43 | 44 | function test_username(tc) 45 | u = stdlib.get_username(); 46 | tc.verifyGreaterThan(strlength(u), 0) 47 | end 48 | 49 | function test_hostname(tc) 50 | h = stdlib.hostname(); 51 | tc.verifyGreaterThan(strlength(h), 0) 52 | end 53 | 54 | function test_java_vendor(tc) 55 | v = stdlib.java_vendor(); 56 | tc.verifyGreaterThan(strlength(v), 0) 57 | end 58 | 59 | 60 | function test_java_version(tc) 61 | v = stdlib.java_version(); 62 | tc.verifyGreaterThanOrEqual(strlength(v), 4) 63 | end 64 | 65 | 66 | function test_java_api(tc) 67 | v = stdlib.java_api(); 68 | tc.verifyGreaterThanOrEqual(v, 8, "Java API >= 8 is required for Matlab-stdlib") 69 | end 70 | 71 | function test_cpu_arch(tc) 72 | arch = stdlib.cpu_arch(); 73 | tc.verifyGreaterThan(strlength(arch), 0) 74 | end 75 | 76 | function test_os_version(tc) 77 | [os, ver] = stdlib.os_version(); 78 | tc.verifyGreaterThan(strlength(os), 0, "expected non-empty os") 79 | tc.verifyGreaterThan(strlength(ver), 0, "expected non-empty version") 80 | end 81 | 82 | function test_hard_link_count(tc) 83 | fn = mfilename("fullpath") + ".m"; 84 | 85 | if ispc 86 | tc.verifyEmpty(stdlib.hard_link_count(fn)) 87 | else 88 | tc.verifyGreaterThanOrEqual(stdlib.hard_link_count(fn), 1) 89 | end 90 | 91 | tc.verifyEmpty(stdlib.hard_link_count(tempname())) 92 | end 93 | 94 | 95 | function test_is_parallel(tc) 96 | tc.verifyClass(stdlib.is_parallel_worker, 'logical') 97 | end 98 | 99 | 100 | function test_ram(tc) 101 | tc.verifyGreaterThan(stdlib.ram_total(), 0) 102 | tc.verifyGreaterThan(stdlib.ram_free(), 0) 103 | end 104 | 105 | 106 | function test_cpu_count(tc) 107 | tc.verifyGreaterThan(stdlib.cpu_count(), 0) 108 | end 109 | 110 | 111 | function test_cpu_load(tc) 112 | tc.verifyGreaterThanOrEqual(stdlib.cpu_load(), 0) 113 | end 114 | 115 | 116 | function test_is_regular_file(tc) 117 | import matlab.unittest.constraints.IsFile 118 | if ~ispc 119 | tc.assumeThat(stdlib.null_file, IsFile) 120 | end 121 | tc.verifyFalse(stdlib.is_regular_file(stdlib.null_file), "null file is not a regular file") 122 | 123 | end 124 | 125 | 126 | function test_touch_modtime(tc) 127 | tc.assumeFalse(isMATLABReleaseOlderThan("R2022a")) 128 | 129 | tf = tc.createTemporaryFolder(); 130 | 131 | fn = fullfile(tf, "modtime.txt"); 132 | 133 | tc.verifyTrue(stdlib.touch(fn, datetime("yesterday"))) 134 | t0 = stdlib.get_modtime(fn); 135 | 136 | tc.verifyTrue(stdlib.set_modtime(fn, datetime("now"))) 137 | t1 = stdlib.get_modtime(fn); 138 | 139 | tc.verifyGreaterThanOrEqual(t1, t0) 140 | end 141 | 142 | 143 | function test_get_modtime(tc) 144 | tc.verifyEmpty(stdlib.get_modtime("")) 145 | end 146 | 147 | function test_set_modtime(tc) 148 | tc.verifyEqual(stdlib.set_modtime("", datetime("now")), false) 149 | end 150 | 151 | 152 | function test_checkRAM(tc) 153 | tc.verifyClass(stdlib.checkRAM(1, "double"), "logical") 154 | end 155 | 156 | end 157 | 158 | end 159 | -------------------------------------------------------------------------------- /test/TestJoin.m: -------------------------------------------------------------------------------- 1 | classdef TestJoin < matlab.unittest.TestCase 2 | 3 | properties (TestParameter) 4 | p = {{"", "", ""}, ... 5 | {"a", "", "a"}, ... 6 | {"", "a", "a"}, ... 7 | {"a/b/", "c", "a/b/c"}, ... 8 | {"/", "", "/"}, ... 9 | {"", "/", "/"}, ... 10 | {"a", "b", "a/b"}, ... 11 | {"a/b/../", "c/d/..", "a/b/../c/d/.."}, ... 12 | {"a/b", "..", "a/b/.."}, ... 13 | {"a/b", "c/d", "a/b/c/d"}, ... 14 | {"ab/cd", "/ef", "/ef"}, ... 15 | {stdlib.homedir(), "", stdlib.homedir()}, ... 16 | {matlabroot, "bin", stdlib.posix(matlabroot + "/bin")} 17 | } 18 | end 19 | 20 | methods (Test) 21 | function test_join(tc, p) 22 | tc.verifyEqual(stdlib.join(p{1}, p{2}), p{3}) 23 | end 24 | end 25 | 26 | end 27 | -------------------------------------------------------------------------------- /test/TestMex.m: -------------------------------------------------------------------------------- 1 | classdef TestMex < matlab.unittest.TestCase 2 | 3 | methods (Test) 4 | 5 | function test_is_char_device(tc) 6 | tc.applyFixture(matlab.unittest.fixtures.CurrentFolderFixture("..")) 7 | % matlab exist() doesn't work for MEX detection with ".." leading path 8 | tc.assumeEqual(exist("+stdlib/is_char_device", "file"), 3) 9 | 10 | % /dev/stdin may not be available on CI systems 11 | if ispc 12 | n = "NUL"; 13 | else 14 | n = "/dev/null"; 15 | end 16 | 17 | tc.verifyTrue(stdlib.is_char_device(n)) 18 | end 19 | 20 | 21 | function test_is_admin(tc) 22 | tc.applyFixture(matlab.unittest.fixtures.CurrentFolderFixture("..")) 23 | tc.assumeEqual(exist("+stdlib/is_admin", "file"), 3) 24 | 25 | tc.verifyClass(stdlib.is_admin(), "logical") 26 | end 27 | 28 | 29 | function test_unlink_file(tc) 30 | tc.assumeFalse(isMATLABReleaseOlderThan("R2022a")) 31 | tc.applyFixture(matlab.unittest.fixtures.CurrentFolderFixture("..")) 32 | tc.assumeEqual(exist("+stdlib/unlink", "file"), 3) 33 | 34 | d = tc.createTemporaryFolder(); 35 | 36 | f = tempname(d); 37 | 38 | tc.verifyFalse(stdlib.unlink(f), "should not succeed at unlinking non-existant path") 39 | 40 | tc.assumeTrue(stdlib.touch(f)) 41 | tc.verifyTrue(stdlib.unlink(f), "failed to unlink file") 42 | end 43 | 44 | 45 | function test_unlink_empty_dir(tc) 46 | tc.assumeFalse(isMATLABReleaseOlderThan("R2022a")) 47 | tc.applyFixture(matlab.unittest.fixtures.CurrentFolderFixture("..")) 48 | tc.assumeEqual(exist("+stdlib/unlink", "file"), 3) 49 | 50 | d = tc.createTemporaryFolder(); 51 | 52 | tc.verifyTrue(stdlib.unlink(d), "failed to unlink empty directory") 53 | end 54 | 55 | 56 | function test_unlink_recursive(tc) 57 | tc.assumeFalse(isMATLABReleaseOlderThan("R2022a")) 58 | tc.applyFixture(matlab.unittest.fixtures.CurrentFolderFixture("..")) 59 | tc.assumeEqual(exist("+stdlib/unlink", "file"), 3) 60 | 61 | d = tc.createTemporaryFolder(); 62 | 63 | tc.assumeTrue(stdlib.touch(fullfile(d, "junk.txt"))) 64 | tc.verifyFalse(stdlib.unlink(d), "should not unlink directory recursively") 65 | end 66 | 67 | end 68 | 69 | end 70 | -------------------------------------------------------------------------------- /test/TestNetCDF.m: -------------------------------------------------------------------------------- 1 | classdef TestNetCDF < matlab.unittest.TestCase 2 | 3 | properties 4 | TestData 5 | end 6 | 7 | methods (TestClassSetup) 8 | 9 | function setup_file(tc) 10 | 11 | tc.assumeFalse(isMATLABReleaseOlderThan("R2022a")) 12 | 13 | td = tc.createTemporaryFolder(); 14 | 15 | A0 = 42.; 16 | A1 = [42.; 43.]; 17 | A2 = magic(4); 18 | A3 = A2(:,1:3,1); 19 | A3(:,:,2) = 2*A3; 20 | A4(:,:,:,5) = A3; 21 | utf0 = 'Hello There 😄'; 22 | utf1 = [utf0; "☎"]; 23 | utf2 = [utf0, "☎"; "📞", "👋"]; 24 | 25 | tc.TestData.A0 = A0; 26 | tc.TestData.A1 = A1; 27 | tc.TestData.A2 = A2; 28 | tc.TestData.A3 = A3; 29 | tc.TestData.A4 = A4; 30 | tc.TestData.utf0 = utf0; 31 | tc.TestData.utf1 = utf1; 32 | tc.TestData.utf2 = utf2; 33 | 34 | basic = td + "/basic.nc"; 35 | tc.TestData.basic = basic; 36 | 37 | % create test data first, so that parallel tests works 38 | stdlib.ncsave(basic, 'A0', A0) 39 | stdlib.ncsave(basic, 'A1', A1, "dims", {'x1', size(A1,1)}) 40 | stdlib.ncsave(basic, 'A2', A2, "dims", {'x2', size(A2,1), 'y2', size(A2,2)}) 41 | stdlib.ncsave(basic, 'A3', A3, "dims", {'x3', size(A3,1), 'y3', size(A3,2), 'z3', size(A3,3)}) 42 | stdlib.ncsave(basic, 'A4', A4, "dims", {'x4', size(A4,1), 'y4', size(A4,2), 'z4', size(A4,3), 'w4', size(A4,4)}) 43 | 44 | if ~isMATLABReleaseOlderThan('R2021b') 45 | stdlib.ncsave(basic, "utf0", utf0) 46 | stdlib.ncsave(basic, "utf1", utf1, "dims", {'s1', size(utf1, 1)}) 47 | stdlib.ncsave(basic, "utf2", utf2, "dims", {'s1', size(utf2, 1), 't1', size(utf2, 2)}) 48 | end 49 | 50 | stdlib.ncsave(basic, '/t/x', 12) 51 | stdlib.ncsave(basic, '/t/y', 13) 52 | stdlib.ncsave(basic, '/j/a/b', 6) 53 | 54 | tc.assumeThat(basic, matlab.unittest.constraints.IsFile) 55 | end 56 | end 57 | 58 | 59 | methods (Test) 60 | function test_get_variables(tc) 61 | basic = tc.TestData.basic; 62 | 63 | k = ["A0", "A1", "A2", "A3", "A4"]; 64 | 65 | if ~isMATLABReleaseOlderThan('R2021b') 66 | k = [k, ["utf0", "utf1", "utf2"]]; 67 | end 68 | 69 | tc.verifyEqual(sort(stdlib.ncvariables(basic)), k) 70 | 71 | % 1-level group 72 | v = stdlib.ncvariables(basic, "/t"); 73 | tc.verifyEqual(sort(v), ["x", "y"]) 74 | 75 | % traversal 76 | tc.verifyEmpty(stdlib.ncvariables(basic, "/j") ) 77 | 78 | tc.verifyEqual(stdlib.ncvariables(basic, "/j/a") , "b") 79 | end 80 | 81 | 82 | function test_exists(tc) 83 | import matlab.unittest.constraints.IsScalar 84 | basic = tc.TestData.basic; 85 | 86 | e = stdlib.ncexists(basic, ""); 87 | 88 | tc.verifyThat(e, IsScalar) 89 | tc.verifyFalse(e) 90 | 91 | tc.verifyTrue(stdlib.ncexists(basic, "A1")) 92 | tc.verifyFalse(stdlib.ncexists(basic, "not-exist")) 93 | 94 | end 95 | 96 | 97 | function test_size(tc) 98 | import matlab.unittest.constraints.IsScalar 99 | basic = tc.TestData.basic; 100 | 101 | s = stdlib.ncsize(basic, 'A0'); 102 | tc.verifyEmpty(s) 103 | 104 | s = stdlib.ncsize(basic, 'A1'); 105 | tc.verifyThat(s, IsScalar) 106 | tc.verifyEqual(s, 2) 107 | 108 | s = stdlib.ncsize(basic, 'A2'); 109 | tc.verifyTrue(isvector(s)) 110 | tc.verifyEqual(s, [4,4]) 111 | 112 | s = stdlib.ncsize(basic, 'A3'); 113 | tc.verifyTrue(isvector(s)) 114 | tc.verifyEqual(s, [4,3,2]) 115 | 116 | s = stdlib.ncsize(basic, 'A4'); 117 | tc.verifyTrue(isvector(s)) 118 | tc.verifyEqual(s, [4,3,2,5]) 119 | 120 | end 121 | 122 | function test_size_string(tc) 123 | basic = tc.TestData.basic; 124 | 125 | tc.assumeFalse(isMATLABReleaseOlderThan('R2021b'), "NetCDF4 string requires Matlab >= R2021b") 126 | 127 | s = stdlib.ncsize(basic, 'utf0'); 128 | tc.verifyEmpty(s) 129 | 130 | s = stdlib.ncsize(basic, 'utf1'); 131 | tc.verifyEqual(s, 2) 132 | 133 | s = stdlib.ncsize(basic, 'utf2'); 134 | tc.verifyEqual(s, [2, 2]) 135 | end 136 | 137 | 138 | function test_read(tc) 139 | import matlab.unittest.constraints.IsScalar 140 | basic = tc.TestData.basic; 141 | 142 | s = ncread(basic, 'A0'); 143 | tc.verifyThat(s, IsScalar) 144 | tc.verifyEqual(s, 42) 145 | 146 | s = ncread(basic, 'A1'); 147 | tc.verifyTrue(isvector(s)) 148 | tc.verifyEqual(s, tc.TestData.A1) 149 | 150 | s = ncread(basic, 'A2'); 151 | tc.verifyTrue(ismatrix(s)) 152 | tc.verifyEqual(s, tc.TestData.A2) 153 | 154 | s = ncread(basic, 'A3'); 155 | tc.verifyEqual(ndims(s), 3) 156 | tc.verifyEqual(s, tc.TestData.A3) 157 | 158 | s = ncread(basic, 'A4'); 159 | tc.verifyEqual(ndims(s), 4) 160 | tc.verifyEqual(s, tc.TestData.A4) 161 | end 162 | 163 | function test_read_string(tc) 164 | basic = tc.TestData.basic; 165 | 166 | tc.assumeFalse(isMATLABReleaseOlderThan('R2021b'), "NetCDF4 string requires Matlab >= R2021b") 167 | 168 | s = ncread(basic, 'utf0'); 169 | tc.verifyClass(s, 'string') 170 | tc.verifyEqual(s, string(tc.TestData.utf0)) 171 | 172 | s = ncread(basic, 'utf1'); 173 | tc.verifyClass(s, 'string') 174 | tc.verifyEqual(s, tc.TestData.utf1) 175 | 176 | s = ncread(basic, 'utf2'); 177 | tc.verifyClass(s, 'string') 178 | tc.verifyEqual(s, tc.TestData.utf2) 179 | end 180 | 181 | 182 | function test_coerce(tc) 183 | basic = tc.TestData.basic; 184 | 185 | for type = ["single", "double", ... 186 | "int8", "int16", "int32", "int64", ... 187 | "uint8", "uint16", "uint32", "uint64"] 188 | 189 | stdlib.ncsave(basic, type, 0, "type", type) 190 | 191 | tc.verifyClass(ncread(basic, type), type) 192 | end 193 | 194 | end 195 | 196 | 197 | function test_rewrite(tc) 198 | basic = tc.TestData.basic; 199 | 200 | A2 = 3*magic(4); 201 | stdlib.ncsave(basic, "A2", A2, "dims", {'x2', size(A2,1), 'y2', size(A2,2)}) 202 | 203 | tc.verifyEqual(ncread(basic, 'A2'), 3*magic(4)) 204 | end 205 | 206 | 207 | function test_real_only(tc) 208 | basic = tc.TestData.basic; 209 | 210 | tc.verifyError(@() stdlib.ncsave(basic, "bad_imag", 1j), 'MATLAB:validators:mustBeReal') 211 | end 212 | 213 | end 214 | 215 | end 216 | -------------------------------------------------------------------------------- /test/TestNormalize.m: -------------------------------------------------------------------------------- 1 | classdef TestNormalize < matlab.unittest.TestCase 2 | 3 | properties (TestParameter) 4 | p = init_norm() 5 | end 6 | 7 | methods (Test) 8 | function test_normalize(tc, p) 9 | tc.verifyEqual(stdlib.normalize(p{1}), p{2}) 10 | end 11 | end 12 | 13 | end 14 | 15 | function p = init_norm() 16 | 17 | p = { 18 | {"", "."}, ... 19 | {"a/..", "."}, ... 20 | {"//a/b/", "/a/b"}, ... 21 | {"/a/b/", "/a/b"}, ... 22 | {"a/b/", "a/b"}, ... 23 | {"a/../c", "c"}, ... 24 | {"a/b/../c", "a/c"}, ... 25 | {"a/b/../../c", "c"}, ... 26 | {"a/b/../../c/..", "."}, ... 27 | {"a/b/../../c/../..", ".."}, ... 28 | {"a////b", "a/b"}, ... 29 | {".a", ".a"}, ... 30 | {"..a", "..a"}, ... 31 | {"a.", "a."}, ... 32 | {"a..", "a.."}, ... 33 | {"./a/.", "a"}, ... 34 | {"../a", "../a"} 35 | }; 36 | 37 | if ispc 38 | p{3}{2} = "//a/b"; 39 | end 40 | end 41 | -------------------------------------------------------------------------------- /test/TestParent.m: -------------------------------------------------------------------------------- 1 | classdef TestParent < matlab.unittest.TestCase 2 | 3 | properties (TestParameter) 4 | p = init_parent() 5 | end 6 | 7 | methods (Test) 8 | function test_parent(tc, p) 9 | import matlab.unittest.constraints.Matches 10 | tc.verifyThat(stdlib.parent(p{1}), Matches(p{2}), p{1}) 11 | end 12 | end 13 | 14 | end 15 | 16 | 17 | function p = init_parent() 18 | 19 | p = { 20 | {"", "\."}, ... 21 | {".", "\."}, ... 22 | {"..", "\."}, ... 23 | {"../..", "\.\."}, ... 24 | {"a/", "\."}, ... 25 | {"a/b", "a"}, ... 26 | {'a/b/', 'a'}, ... 27 | {'a//b', 'a'}, ... 28 | {"ab/.parent", "ab"}, ... 29 | {"ab/.parent.txt", "ab"}, ... 30 | {"a/b/../.parent.txt", "a/b/\.\."}, ... 31 | {"a/////b////c", "a/b"}}; 32 | 33 | if ispc 34 | p{end+1} = {"c:/", "c:/"}; 35 | p{end+1} = {"c:\", "c:/"}; 36 | p{end+1} = {"c:/a/b", "c:/a"}; 37 | p{end+1} = {"c:\a/b", "c:/a"}; 38 | p{end+1} = {"c:/a", "c:/"}; 39 | p{end+1} = {"c:", "c:/"}; 40 | end 41 | 42 | end 43 | -------------------------------------------------------------------------------- /test/TestPermissions.m: -------------------------------------------------------------------------------- 1 | classdef TestPermissions < matlab.unittest.TestCase 2 | 3 | properties (TestParameter) 4 | Ps = {".", tempname(), "", "not-exist"} 5 | end 6 | 7 | methods (Test) 8 | 9 | function test_get_permissions(tc, Ps) 10 | 11 | import matlab.unittest.constraints.StartsWithSubstring 12 | 13 | p = stdlib.get_permissions(Ps); 14 | 15 | if stdlib.exists(Ps) 16 | tc.verifyThat(p, StartsWithSubstring("r")) 17 | tc.verifyClass(p, "char") 18 | else 19 | tc.verifyEmpty(p) 20 | end 21 | 22 | end 23 | 24 | 25 | function test_set_permissions(tc) 26 | 27 | import matlab.unittest.constraints.StartsWithSubstring 28 | import matlab.unittest.fixtures.CurrentFolderFixture 29 | 30 | tc.assumeFalse(isMATLABReleaseOlderThan("R2022a")) 31 | 32 | tc.applyFixture(CurrentFolderFixture("..")) 33 | % matlab exist() doesn't work for MEX detection with ".." leading path 34 | 35 | tc.assumeEqual(exist("+stdlib/set_permissions", "file"), 3) 36 | 37 | tf = tc.createTemporaryFolder(); 38 | 39 | nr = fullfile(tf, "no-read"); 40 | 41 | tc.verifyTrue(stdlib.touch(nr)) 42 | tc.verifyTrue(stdlib.set_permissions(nr, -1, 0, 0)) 43 | p = stdlib.get_permissions(nr); 44 | 45 | if ~ispc 46 | tc.verifyThat(p, StartsWithSubstring("-"), "no-read permission failed to set") 47 | end 48 | 49 | nw = fullfile(tf, "no-write"); 50 | 51 | tc.verifyTrue(stdlib.touch(nw)) 52 | tc.verifyTrue(stdlib.set_permissions(nw, 0, -1, 0)) 53 | p = stdlib.get_permissions(nw); 54 | 55 | if ~ispc 56 | tc.verifyThat(p, StartsWithSubstring("r-"), "no-write permission failed to set") 57 | end 58 | 59 | end 60 | 61 | end 62 | 63 | end 64 | -------------------------------------------------------------------------------- /test/TestRelative.m: -------------------------------------------------------------------------------- 1 | classdef TestRelative < matlab.unittest.TestCase 2 | 3 | properties (TestParameter) 4 | pr = init_rel() 5 | pp = init_prox() 6 | end 7 | 8 | methods(TestClassSetup) 9 | function mex_required(tc) 10 | import matlab.unittest.fixtures.CurrentFolderFixture 11 | 12 | tc.applyFixture(CurrentFolderFixture("..")) 13 | % matlab exist() doesn't work for MEX detection with ".." leading path 14 | 15 | tc.assumeEqual(exist("+stdlib/relative_to", "file"), 3) 16 | tc.assumeEqual(exist("+stdlib/proximate_to", "file"), 3) 17 | end 18 | end 19 | 20 | 21 | methods (Test) 22 | 23 | function test_relative_to(tc, pr) 24 | tc.verifyEqual(stdlib.relative_to(pr{1}, pr{2}), pr{3}, ... 25 | "relative_to(" + pr{1} + "," + pr{2}+")") 26 | end 27 | 28 | function test_proximate_to(tc, pp) 29 | tc.verifyEqual(stdlib.proximate_to(pp{1}, pp{2}), pp{3}, ... 30 | "proximate_to(" + pp{1} + "," + pp{2}+")") 31 | end 32 | 33 | end 34 | end 35 | 36 | 37 | function p = init_rel() 38 | 39 | p = {{"", "", "."}, ... 40 | {"Hello", "Hello", "."}, ... 41 | {"Hello", "Hello/", "."}, ... 42 | {"a/./b", "a/b", "."}, ... 43 | {"a/b", "a/./b", "."}, ... 44 | {"./a/b", "./a/c", "../c"}, ... 45 | {"/", "/", "."}, ... 46 | {"a/b/c/d", "a/b", "../.."}, ... 47 | {"a/b", "a/c", "../c"}, ... 48 | {"a/b", "c", "../../c"}, ... 49 | {"c", "a/b", "../a/b"}, ... 50 | {"a/b", "a/b", "."}, ... 51 | {"a/b", "a", ".."} 52 | }; 53 | % NOTE: ".." in relative_to(base) is ambiguous including for python.pathlib, C++ , etc. 54 | 55 | if ispc 56 | p = [p, ... 57 | {{"C:/a/b", "C:/", "../.."}, ... 58 | {"C:/", "C:/a/b", "a/b"}, ... 59 | {"c:/a/b", "c:/a/b", "."}, ... 60 | {"c:/a/b", "c:/a", ".."}, ... 61 | {"c:\a/b\c/d", "c:/a\b", "../.."}, ... 62 | {"C:/path", "D:/path", ""}}]; 63 | % note: on Windows the drive letter should be uppercase! 64 | else 65 | p = [p, ... 66 | {{"", "a", "a"}, ... 67 | {"/dev/null", "/dev/null", "."}}]; 68 | end 69 | 70 | end 71 | 72 | function p = init_prox() 73 | % NOTE: ".." in proximate_to(base) is ambiguous including for python.pathlib, C++ , etc 74 | 75 | p = init_rel(); 76 | 77 | 78 | if ispc 79 | p{end}{3} = "D:/path"; 80 | end 81 | 82 | end 83 | -------------------------------------------------------------------------------- /test/TestResolve.m: -------------------------------------------------------------------------------- 1 | classdef TestResolve < matlab.unittest.TestCase 2 | 3 | properties (TestParameter) 4 | p = init_resolve() 5 | end 6 | 7 | 8 | methods (Test) 9 | 10 | function test_resolve_relative(tc) 11 | import matlab.unittest.constraints.StartsWithSubstring 12 | import matlab.unittest.constraints.ContainsSubstring 13 | 14 | % all non-existing files 15 | 16 | tc.verifyEqual(stdlib.resolve(""), string(stdlib.posix(pwd()))) 17 | 18 | pabs = stdlib.resolve('2foo', true); 19 | pabs2 = stdlib.resolve('4foo', true); 20 | tc.verifyThat(pabs, ~StartsWithSubstring("2")) 21 | tc.verifyTrue(strncmp(pabs, pabs2, 2)) 22 | 23 | par1 = stdlib.resolve("../2foo", true); 24 | tc.verifyNotEmpty(par1) 25 | tc.verifyThat(par1, ~ContainsSubstring("..")) 26 | 27 | par2 = stdlib.resolve("../4foo", true); 28 | tc.verifyTrue(strncmp(par2, pabs2, 2)) 29 | 30 | pt1 = stdlib.resolve("bar/../2foo", true); 31 | tc.verifyNotEmpty(pt1) 32 | tc.verifyThat(pt1, ~ContainsSubstring("..")) 33 | 34 | va = stdlib.resolve("2foo", true); 35 | vb = stdlib.resolve("4foo", true); 36 | tc.verifyThat(va, ~StartsWithSubstring("2")) 37 | tc.verifyTrue(strncmp(va, vb, 2)) 38 | 39 | end 40 | 41 | function test_resolve_fullpath(tc, p) 42 | tc.verifyEqual(stdlib.resolve(p{1}, true), p{2}) 43 | end 44 | 45 | end 46 | 47 | end 48 | 49 | 50 | function p = init_resolve() 51 | 52 | c = stdlib.posix(pwd()); 53 | 54 | p = {... 55 | {'', string(c)}, ... 56 | {"", string(c)}, ... 57 | {"not-exist", c + "/not-exist"}, ... 58 | {"a/../b", c + "/b"}, ... 59 | {'~', string(stdlib.homedir())}, ... 60 | {"~", string(stdlib.homedir())}, ... 61 | {'~/', string(stdlib.homedir())}, ... 62 | {"~/", string(stdlib.homedir())}, ... 63 | {"~/..", string(stdlib.parent(stdlib.homedir()))}, ... 64 | {mfilename("fullpath") + ".m/..", string(stdlib.parent(mfilename("fullpath")))}, ... 65 | {"~/not-exist/a/..", stdlib.homedir() + "/not-exist"} 66 | }; 67 | end 68 | -------------------------------------------------------------------------------- /test/TestRoot.m: -------------------------------------------------------------------------------- 1 | classdef TestRoot < matlab.unittest.TestCase 2 | 3 | properties (TestParameter) 4 | p = init_root() 5 | end 6 | 7 | methods (Test) 8 | function test_root(tc, p) 9 | tc.verifyEqual(stdlib.root(p{1}), p{2}) 10 | end 11 | end 12 | 13 | end 14 | 15 | 16 | function p = init_root() 17 | 18 | p = {{"", ""}, ... 19 | {"a/b", ""}, ... 20 | {"./a/b", ""}, ... 21 | {'/etc', '/'}, ... 22 | {"c:", ""}, ... 23 | {"c:/etc", ""}}; 24 | 25 | if ispc 26 | p{5}{2} = "c:"; 27 | p{6}{2} = "c:/"; 28 | end 29 | 30 | end 31 | -------------------------------------------------------------------------------- /test/TestStem.m: -------------------------------------------------------------------------------- 1 | classdef TestStem < matlab.unittest.TestCase 2 | 3 | properties (TestParameter) 4 | 5 | p = {... 6 | {"/a/b/c", "c"}, ... 7 | {"/a/b/c/", ""}, ... 8 | {"a/b/c/", ""}, ... 9 | {"a/b/c.txt", "c"}, ... 10 | {"a/b/c.txt.gz", "c.txt"}, ... 11 | {'a/b/.c', '.c'}, ... 12 | {"", ""} 13 | } 14 | 15 | end 16 | 17 | methods (Test) 18 | function test(tc, p) 19 | tc.verifyEqual(stdlib.stem(p{1}), p{2}) 20 | end 21 | end 22 | 23 | end 24 | -------------------------------------------------------------------------------- /test/TestSubprocess.m: -------------------------------------------------------------------------------- 1 | classdef TestSubprocess < matlab.unittest.TestCase 2 | 3 | properties (TestParameter) 4 | lang_out = {"c", "fortran"} 5 | lang_in = {"cpp", "fortran"} 6 | end 7 | 8 | methods(TestClassSetup) 9 | function java_required(tc) 10 | tc.assumeTrue(stdlib.has_java()) 11 | end 12 | end 13 | 14 | 15 | methods (Test) 16 | 17 | function test_stdout_stderr(tc, lang_out) 18 | import matlab.unittest.constraints.IsFile 19 | 20 | cwd = fileparts(mfilename('fullpath')); 21 | exe = cwd + "/stdout_stderr_" + lang_out + ".exe"; 22 | tc.assumeThat(exe, IsFile, exe + " not found") 23 | 24 | [status, msg, err] = stdlib.subprocess_run(exe); 25 | tc.assertEqual(status, 0, err) 26 | tc.verifyEqual(msg, "stdout") 27 | tc.verifyEqual(err, "stderr") 28 | end 29 | 30 | 31 | function test_stdin(tc, lang_in) 32 | import matlab.unittest.constraints.IsFile 33 | 34 | cwd = fileparts(mfilename('fullpath')); 35 | exe = cwd + "/stdin_" + lang_in + ".exe"; 36 | tc.assumeThat(exe, IsFile, exe + " not found") 37 | 38 | [status, msg, err] = stdlib.subprocess_run(exe, stdin="1 2"); 39 | 40 | tc.assertEqual(status, 0, err) 41 | tc.verifyEqual(msg, "3") 42 | tc.verifyEqual(err, "") 43 | end 44 | 45 | 46 | function test_cwd(tc) 47 | tc.assumeFalse(isMATLABReleaseOlderThan("R2022a")) 48 | 49 | if ispc 50 | c = ["cmd", "/c", "dir"]; 51 | else 52 | c = ["ls", "-l"]; 53 | end 54 | 55 | [s, m, e] = stdlib.subprocess_run(c); 56 | tc.assertEqual(s, 0, "status non-zero") 57 | tc.verifyGreaterThan(strlength(m), 0, "empty directory not expected") 58 | tc.verifyEqual(strlength(e), 0, e) 59 | 60 | td = tc.createTemporaryFolder(); 61 | 62 | [s, mc, e] = stdlib.subprocess_run(c, cwd=td); 63 | tc.assertEqual(s, 0, "status non-zero") 64 | tc.verifyNotEqual(m, mc, "expected different directory to have different contents") 65 | tc.verifyEqual(strlength(e), 0, e) 66 | 67 | end 68 | 69 | 70 | function test_env_run(tc) 71 | import matlab.unittest.constraints.IsFile 72 | 73 | cwd = fileparts(mfilename('fullpath')); 74 | exe = cwd + "/printenv.exe"; 75 | tc.assumeThat(exe, IsFile, exe + " not found") 76 | 77 | names = ["TEST1", "TEST2"]; 78 | vals = ["test123", "test321"]; 79 | 80 | env = struct(names(1), vals(1), names(2), vals(2)); 81 | 82 | for i = 1:length(names) 83 | [ret, out] = stdlib.subprocess_run([exe, names(i)], env=env); 84 | tc.verifyEqual(ret, 0) 85 | tc.verifySubstring(out, vals(i)) 86 | end 87 | 88 | end 89 | 90 | 91 | function test_timeout(tc) 92 | import matlab.unittest.constraints.StartsWithSubstring 93 | import matlab.unittest.constraints.IsFile 94 | 95 | cwd = fileparts(mfilename('fullpath')); 96 | exe = cwd + "/sleep.exe"; 97 | tc.assumeThat(exe, IsFile, exe + " not found") 98 | 99 | [ret, ~, err] = stdlib.subprocess_run(exe, timeout=1, stdout=false, stderr=false); 100 | 101 | tc.verifyNotEqual(ret, 0, err) 102 | tc.verifyThat(err, StartsWithSubstring("Subprocess timeout")) 103 | 104 | end 105 | 106 | end 107 | 108 | end 109 | -------------------------------------------------------------------------------- /test/TestSuffix.m: -------------------------------------------------------------------------------- 1 | classdef TestSuffix < matlab.unittest.TestCase 2 | 3 | properties (TestParameter) 4 | p = {{"", ""}, {"/a/b/c", ""}, ... 5 | {"/a/b/c/", ""}, {"a/b/c.txt", ".txt"}, ... 6 | {"a/b/c.txt.gz", ".gz"}, ... 7 | {'.stat', '.stat'}, ... 8 | {'.stat.txt', '.txt'}} 9 | end 10 | 11 | methods (Test) 12 | function test(tc, p) 13 | tc.verifyEqual(stdlib.suffix(p{1}), p{2}) 14 | end 15 | end 16 | 17 | end 18 | -------------------------------------------------------------------------------- /test/TestSymlink.m: -------------------------------------------------------------------------------- 1 | classdef TestSymlink < matlab.unittest.TestCase 2 | 3 | properties 4 | target 5 | link 6 | tempDir 7 | end 8 | 9 | properties (TestParameter) 10 | p = init_symlink() 11 | end 12 | 13 | 14 | methods(TestClassSetup) 15 | function setup_symlink(tc) 16 | tc.assumeFalse(isMATLABReleaseOlderThan("R2022a")) 17 | 18 | tc.tempDir = tc.createTemporaryFolder(); 19 | 20 | tc.link = tc.tempDir + "/my.lnk"; 21 | 22 | tc.target = stdlib.posix(mfilename("fullpath") + ".m"); 23 | 24 | tc.assumeTrue(stdlib.create_symlink(tc.target, tc.link), ... 25 | "failed to create test link " + tc.link) 26 | end 27 | end 28 | 29 | 30 | methods (Test) 31 | 32 | function test_is_symlink(tc, p) 33 | tc.verifyTrue(stdlib.is_symlink(tc.link), "failed to detect own link") 34 | tc.verifyEqual(stdlib.is_symlink(p{1}), p{2}, p{1}) 35 | end 36 | 37 | 38 | function test_read_symlink(tc) 39 | 40 | tc.verifyEqual(stdlib.read_symlink(""), "") 41 | tc.verifyEqual(stdlib.read_symlink("not-exist"), "") 42 | tc.verifyEqual(stdlib.read_symlink(tc.target), "") 43 | 44 | t = stdlib.read_symlink(tc.link); 45 | tc.verifyNotEmpty(t) 46 | tc.verifyClass(t, 'string') 47 | tc.verifyEqual(tc.target, t) 48 | 49 | end 50 | 51 | 52 | function test_create_symlink(tc) 53 | tc.applyFixture(matlab.unittest.fixtures.SuppressedWarningsFixture(["MATLAB:io:filesystem:symlink:TargetNotFound","MATLAB:io:filesystem:symlink:FileExists"])) 54 | 55 | tc.verifyFalse(stdlib.create_symlink("", tempname())) 56 | tc.verifyFalse(stdlib.create_symlink(tc.target, tc.link), "should fail for existing symlink") 57 | 58 | ano = tc.tempDir + "/another.lnk"; 59 | tc.verifyTrue(stdlib.create_symlink(tc.target, ano)) 60 | tc.verifyTrue(stdlib.is_symlink(ano)) 61 | end 62 | 63 | end 64 | end 65 | 66 | 67 | function p = init_symlink() 68 | p = {{"not-exist", false}, ... 69 | {mfilename("fullpath") + ".m", false}, ... 70 | {"", false}}; 71 | end 72 | -------------------------------------------------------------------------------- /test/TestSys.m: -------------------------------------------------------------------------------- 1 | classdef TestSys < matlab.unittest.TestCase 2 | 3 | properties (TestParameter) 4 | fun = {stdlib.iscygwin, stdlib.isoctave, stdlib.is_rosetta, stdlib.isinteractive} 5 | fi32 = {stdlib.is_wsl} 6 | end 7 | 8 | 9 | methods (Test) 10 | 11 | function test_platform_logical(tc, fun) 12 | 13 | tc.verifyClass(fun, 'logical') 14 | end 15 | 16 | function test_platform_int32(tc, fi32) 17 | tc.verifyClass(fi32, 'int32') 18 | end 19 | 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /test/TestVersion.m: -------------------------------------------------------------------------------- 1 | classdef TestVersion < matlab.unittest.TestCase 2 | 3 | properties (TestParameter) 4 | v = {{"11.1", "9.3", true}, ... 5 | {"3.19.0.33", "3.19.0", true}, ... 6 | {"3.19.0.33", "3.19.0.34", false}, ... 7 | {"1.5.0.3", "1.5.0", true}, ... 8 | {"1.5.0", "1.5.0.3", false}, ... 9 | {"11.5.1a", "11.5.1b", false}, ... 10 | {"1.13a", "1.2c", true}, ... 11 | {"1.2c", "1.13b", false}, ... 12 | } 13 | end 14 | 15 | 16 | methods (Test) 17 | 18 | function test_version(tc, v) 19 | tc.verifyEqual(stdlib.version_atleast(v{1}, v{2}), v{3}) 20 | end 21 | 22 | end 23 | 24 | end 25 | -------------------------------------------------------------------------------- /test/TestWhich.m: -------------------------------------------------------------------------------- 1 | classdef TestWhich < matlab.unittest.TestCase 2 | 3 | methods (Test) 4 | 5 | function test_which_name(tc) 6 | 7 | tc.verifyEmpty(stdlib.which(tempname())) 8 | 9 | if ispc 10 | n = "pwsh.exe"; 11 | else 12 | n = "ls"; 13 | end 14 | %% which: Matlab in environment variable PATH 15 | % MacOS Matlab does not source .zshrc so Matlab is not on internal Matlab PATH 16 | % Unix-like OS may have Matlab as alias which is not visible to 17 | % stdlib.which() 18 | % virus scanners may block stdlib.which("cmd.exe") on Windows 19 | tc.verifyNotEmpty(stdlib.which(n)) 20 | 21 | end 22 | 23 | 24 | function test_which_fullpath(tc) 25 | import matlab.unittest.constraints.IsFile 26 | import matlab.unittest.constraints.EndsWithSubstring 27 | 28 | %% is_exe test 29 | p = matlabroot + "/bin/" + matlab_name(); 30 | 31 | tc.assumeTrue(stdlib.is_exe(p), "Matlab not executable " + p) 32 | %% which: test absolute path 33 | exe = stdlib.which(p); 34 | 35 | tc.verifyNotEmpty(exe, "Matlab not found " + p) 36 | 37 | if ispc 38 | tc.verifyThat(exe, EndsWithSubstring(".exe")) 39 | else 40 | tc.verifyThat(exe, ~EndsWithSubstring(".exe")) 41 | end 42 | tc.verifyThat(exe, IsFile) 43 | 44 | end 45 | 46 | function test_which_multipath(tc) 47 | 48 | n = matlab_name(); 49 | 50 | paths = string(getenv("PATH")); 51 | paths = split(paths, pathsep); 52 | paths(end+1) = matlabroot + "/bin"; 53 | 54 | exe = stdlib.which(n, paths); 55 | 56 | tc.verifyNotEmpty(exe, "Matlab not found by which()") 57 | 58 | end 59 | 60 | end 61 | 62 | end 63 | 64 | 65 | function n = matlab_name() 66 | 67 | n = "matlab"; 68 | if ispc 69 | n = n + ".exe"; 70 | end 71 | end 72 | -------------------------------------------------------------------------------- /test/TestWindowsCOM.m: -------------------------------------------------------------------------------- 1 | classdef TestWindowsCOM < matlab.unittest.TestCase 2 | 3 | properties (TestParameter) 4 | Pn = {""} 5 | Pmr = {matlabroot, stdlib.posix(matlabroot)} 6 | end 7 | 8 | methods (Test) 9 | 10 | function test_not(tc, Pn) 11 | tc.verifyEqual(stdlib.windows_shortname(Pn), "") 12 | end 13 | 14 | function test_short_folder(tc) 15 | import matlab.unittest.constraints.IsFolder 16 | 17 | progdir = stdlib.posix(getenv("PROGRAMFILES")); 18 | tc.assumeThat(progdir, IsFolder, "$Env:PROGRAMFILES is not a directory") 19 | 20 | short = stdlib.windows_shortname(progdir); 21 | 22 | if ispc 23 | tc.verifySubstring(short, "PROGRA~1") 24 | tc.verifyEqual(stdlib.canonical(short), string(progdir)) 25 | else 26 | tc.verifyEqual(short, progdir) 27 | end 28 | 29 | end 30 | 31 | 32 | function test_short_file(tc, Pmr) 33 | import matlab.unittest.constraints.IsFile 34 | 35 | s = stdlib.windows_shortname(Pmr); 36 | 37 | if ispc 38 | if contains(Pmr, " ") 39 | tc.verifySubstring(s, "~") 40 | end 41 | tc.verifyEqual(stdlib.canonical(s), string(stdlib.posix(Pmr)), "shortname didn't resolve same as canonical") 42 | else 43 | tc.verifyEqual(s, string(Pmr)) 44 | end 45 | 46 | end 47 | 48 | end 49 | 50 | end 51 | -------------------------------------------------------------------------------- /test/TestWithSuffix.m: -------------------------------------------------------------------------------- 1 | classdef TestWithSuffix < matlab.unittest.TestCase 2 | 3 | properties (TestParameter) 4 | p = {{"foo.h5", ".nc", "foo\.nc"},... 5 | {"c", "", "c"}, ... 6 | {"c.nc", "", "c"}, ... 7 | {"", ".nc", "\.nc"}, ... 8 | {"a/b/c/", ".h5", "a/b/c/\.h5"}, ... 9 | {"a/b/.h5", ".nc", "a/b/\.h5\.nc"}, ... 10 | {'a/b', '.nc', 'a/b\.nc'}}; 11 | end 12 | 13 | methods (Test) 14 | function test_with_suffix(tc, p) 15 | import matlab.unittest.constraints.Matches 16 | tc.verifyThat(stdlib.with_suffix(p{1}, p{2}), Matches(p{3})) 17 | end 18 | end 19 | 20 | end 21 | -------------------------------------------------------------------------------- /test/example.ini: -------------------------------------------------------------------------------- 1 | ; example.ini -- INI file example for the class INiConfig 2 | ; UPD: 21.03.2010 3 | 4 | ; Supported whitespaces in section names 5 | [Section 1] ; allowed the comment to section 6 | key11=10 ; numeric scalar 7 | key12=1.45, 19.5, 0.6, -1.4 ; numeric vector 8 | key13=1.5+3i, -2-2i ; complex numeric vector 9 | key14=Hello, Matlab! ; string 10 | 11 | [Section 2] 12 | ; Description of key 1 13 | key21=3, 5, -1, 2+3i ; real and complex numeric vector 14 | 15 | ; Description of key 2 16 | key23= ; empty value 17 | 18 | [Section 3] ; this empty section 19 | 20 | ; Sect 21 | [DATA] 22 | ; Supported whitespaces in key names 23 | key num = 113 ; supported whitespaces before and after "=" delimiter 24 | key str = INI file example 25 | 26 | [12345] 27 | 01=1, 2, 3, 4, 5 28 | 02=1 2 3 4 5 ; supported whitespace delimiter without comma 29 | key.01=2.45e-005 ; %e format 30 | -------------------------------------------------------------------------------- /test/printenv.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #ifdef _MSC_VER 5 | #pragma warning(disable : 4996) 6 | #endif 7 | 8 | 9 | int main(int argc, char *argv[]) { 10 | 11 | if (argc < 2) { 12 | std::cerr << "Specify environment variable name to print\n"; 13 | return EXIT_FAILURE; 14 | } 15 | 16 | char *value = std::getenv(argv[1]); 17 | 18 | if (value) { 19 | std::cout << value << "\n"; 20 | return EXIT_SUCCESS; 21 | } 22 | 23 | std::cerr << "Environment variable " << argv[1] << " not found\n"; 24 | return EXIT_FAILURE; 25 | 26 | } 27 | -------------------------------------------------------------------------------- /test/sleep.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | #include 6 | 7 | 8 | int main(int argc, char *argv[]) { 9 | 10 | int milliseconds = (argc > 1) ? std::atoi(argv[1]) : 2000; 11 | 12 | std::cout << "Sleeping for " << milliseconds << " milliseconds..." << std::endl; 13 | 14 | std::this_thread::sleep_for(std::chrono::milliseconds(milliseconds)); 15 | 16 | std::cout << "Awake!" << std::endl; 17 | 18 | return EXIT_SUCCESS; 19 | } 20 | -------------------------------------------------------------------------------- /test/stdin_cpp.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int main() { 5 | 6 | int a, b; 7 | 8 | std::cin >> a >> b; 9 | 10 | std::cout << a + b << "\n"; 11 | 12 | return EXIT_SUCCESS; 13 | } 14 | -------------------------------------------------------------------------------- /test/stdin_fortran.f90: -------------------------------------------------------------------------------- 1 | program stdin_pipe 2 | 3 | use, intrinsic :: iso_fortran_env 4 | 5 | implicit none 6 | 7 | integer :: a, b, i 8 | 9 | ! Python and Matlab have the same issue with stdin truncation in Fortran across compilers. 10 | ! >>> subprocess.run("stdin_fortran.exe", input="1 2", capture_output=True, text=True) 11 | ! CompletedProcess(args='stdin_fortran.exe', returncode=0, stdout=' 0.000\n', stderr='') 12 | ! >>> subprocess.run("stdin_fortran.exe", input="1 2\n", capture_output=True, text=True) 13 | ! CompletedProcess(args='stdin_fortran.exe', returncode=0, stdout=' 3.000\n', stderr='') 14 | 15 | read(input_unit, *, iostat=i) a, b 16 | if (i == iostat_end) error stop "stdin was truncated -- add a newline at the end of the stdin input" 17 | if (i /= 0) error stop "stdin read error" 18 | 19 | 20 | write(output_unit, '(i0)') a + b 21 | 22 | end program 23 | -------------------------------------------------------------------------------- /test/stdout_stderr_c.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main(void){ 4 | printf("stdout\n"); 5 | fprintf(stderr, "stderr\n"); 6 | return 0; 7 | } 8 | -------------------------------------------------------------------------------- /test/stdout_stderr_fortran.f90: -------------------------------------------------------------------------------- 1 | program main 2 | 3 | use, intrinsic :: iso_fortran_env 4 | 5 | implicit none 6 | 7 | write(output_unit, '(a)') "stdout" 8 | write(error_unit, '(a)') "stderr" 9 | 10 | end program 11 | --------------------------------------------------------------------------------