Skip to content

Commit b9e770c

Browse files
committed
mex: unlink
1 parent 79d7747 commit b9e770c

File tree

6 files changed

+124
-3
lines changed

6 files changed

+124
-3
lines changed

+stdlib/unlink.m

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
%% UNLINK delete file or empty directory
2+
% requires: 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.
6+
% Also, this function returns boolean success status, which factory
7+
% delete() does not.
8+
%
9+
%%% Inputs
10+
% * path (1,1) string
11+
%%% Outputs
12+
% * ok (1,1) logical
13+
%
14+
% This function is written in C++ using STL <filesystem>
15+
16+
function unlink(~)
17+
error("buildtool mex")
18+
end

buildfile.m

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ function publishTask(context)
9595

9696

9797
srcs = {
98+
"src/unlink.cpp", ...
9899
["src/is_admin.cpp", "src/admin_fs.cpp"] ...
99100
"src/is_char_device.cpp", ...
100101
["src/parent.cpp", "src/parent_fs.cpp", normal], ...

octave_build.m

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,11 @@ function octave_build(overwrite)
2020
fullfile(d, "relative_to.cpp"), ...
2121
};
2222

23+
% need this for loop below
24+
unlink_bin = fullfile(t, "unlink.oct");
25+
if ~isfile(unlink_bin)
26+
mkoctfile(fullfile(d, "unlink.cpp"), ["-I", inc], "--output", unlink_bin)
27+
end
2328

2429
%% build C+ Octave
2530
for s = srcs
@@ -32,6 +37,8 @@ function octave_build(overwrite)
3237
end
3338

3439
disp(["mkoctfile: ", src, " => ", bin])
35-
delete(bin)
40+
if isfile(bin)
41+
assert(stdlib.unlink(bin))
42+
end
3643
mkoctfile(src, ["-I", inc], "--output", bin)
3744
end

src/octave/unlink.cpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
#include <octave/oct.h>
2+
3+
#include <string>
4+
#include <filesystem>
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+
}

src/unlink.cpp

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
#include "mex.hpp"
2+
#include "mexAdapter.hpp"
3+
4+
#include <vector>
5+
#include <memory>
6+
7+
#include <filesystem>
8+
#include <system_error>
9+
10+
11+
class MexFunction : public matlab::mex::Function {
12+
public:
13+
void operator()(matlab::mex::ArgumentList outputs, matlab::mex::ArgumentList inputs) {
14+
// boilerplate engine & ArrayFactory setup
15+
std::shared_ptr<matlab::engine::MATLABEngine> matlabEng = getEngine();
16+
matlab::data::ArrayFactory factory;
17+
// wrangle inputs
18+
std::string in;
19+
20+
if (inputs.size() != 1) {
21+
matlabEng->feval(u"error", 0,
22+
std::vector<matlab::data::Array>({ factory.createScalar("Mex: One input required") }));
23+
}
24+
if ((inputs[0].getType() == matlab::data::ArrayType::MATLAB_STRING && inputs[0].getNumberOfElements() == 1)){
25+
matlab::data::TypedArray<matlab::data::MATLABString> stringArr = inputs[0];
26+
in = stringArr[0];
27+
} else if (inputs[0].getType() == matlab::data::ArrayType::CHAR){
28+
matlab::data::CharArray charArr = inputs[0];
29+
in.assign(charArr.begin(), charArr.end());
30+
} else {
31+
matlabEng->feval(u"error", 0,
32+
std::vector<matlab::data::Array>({ factory.createScalar("Mex: First input must be a scalar string or char vector") }));
33+
}
34+
35+
// actual function algorithm / computation
36+
std::error_code ec;
37+
bool y = std::filesystem::remove(in, ec) && !ec;
38+
39+
outputs[0] = factory.createScalar<bool>(y);
40+
}
41+
};

test/TestMex.m

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
function test_is_char_device(tc)
66
tc.applyFixture(matlab.unittest.fixtures.CurrentFolderFixture(".."))
77
% matlab exist() doesn't work for MEX detection with ".." leading path
8-
98
tc.assumeEqual(exist("+stdlib/is_char_device", "file"), 3)
109

1110
% /dev/stdin may not be available on CI systems
@@ -21,11 +20,47 @@ function test_is_char_device(tc)
2120

2221
function test_is_admin(tc)
2322
tc.applyFixture(matlab.unittest.fixtures.CurrentFolderFixture(".."))
24-
2523
tc.assumeEqual(exist("+stdlib/is_admin", "file"), 3)
24+
2625
tc.verifyClass(stdlib.is_admin(), "logical")
2726
end
2827

28+
29+
function test_unlink_file(tc)
30+
tc.applyFixture(matlab.unittest.fixtures.CurrentFolderFixture(".."))
31+
tc.assumeEqual(exist("+stdlib/unlink", "file"), 3)
32+
33+
d = tc.createTemporaryFolder();
34+
35+
f = tempname(d);
36+
37+
tc.verifyFalse(stdlib.unlink(f), "should not succeed at unlinking non-existant path")
38+
39+
tc.assumeTrue(stdlib.touch(f))
40+
tc.verifyTrue(stdlib.unlink(f), "failed to unlink file")
41+
end
42+
43+
44+
function test_unlink_empty_dir(tc)
45+
tc.applyFixture(matlab.unittest.fixtures.CurrentFolderFixture(".."))
46+
tc.assumeEqual(exist("+stdlib/unlink", "file"), 3)
47+
48+
d = tc.createTemporaryFolder();
49+
50+
tc.verifyTrue(stdlib.unlink(d), "failed to unlink empty directory")
51+
end
52+
53+
54+
function test_unlink_recursive(tc)
55+
tc.applyFixture(matlab.unittest.fixtures.CurrentFolderFixture(".."))
56+
tc.assumeEqual(exist("+stdlib/unlink", "file"), 3)
57+
58+
d = tc.createTemporaryFolder();
59+
60+
tc.assumeTrue(stdlib.touch(fullfile(d, "junk.txt")))
61+
tc.verifyFalse(stdlib.unlink(d), "should not unlink directory recursively")
62+
end
63+
2964
end
3065

3166
end

0 commit comments

Comments
 (0)