From 78854e832f84d58236ca1009338a62640cd5a543 Mon Sep 17 00:00:00 2001 From: Karen Arutyunov Date: Sat, 14 Jan 2017 21:06:53 +0300 Subject: Add rmdir builtin --- build2/test/script/builtin.cxx | 135 ++++++++++++++++++++++++++++++++--- tests/test/script/builtin/buildfile | 2 +- tests/test/script/builtin/rm.test | 71 +++++++++--------- tests/test/script/builtin/rmdir.test | 125 ++++++++++++++++++++++++++++++++ 4 files changed, 292 insertions(+), 41 deletions(-) create mode 100644 tests/test/script/builtin/rmdir.test diff --git a/build2/test/script/builtin.cxx b/build2/test/script/builtin.cxx index d00a8a1..f911b60 100644 --- a/build2/test/script/builtin.cxx +++ b/build2/test/script/builtin.cxx @@ -374,8 +374,8 @@ namespace build2 // // Remove a file or directory. A path must not be the test working // directory or its parent directory. It also must not be outside the - // testscript working directory unless -f option is specified. Note that - // directories are not removed by default. + // testscript working directory unless the -f option is specified. Note + // that directories are not removed by default. // // -r // Remove directories recursively. Must be specified to remove even an @@ -437,14 +437,16 @@ namespace build2 throw failed (); } + const dir_path& wd (sp.wd_path); + const dir_path& rwd (sp.root->wd_path); + for (; i != args.end (); ++i) { - path p (parse_path (*i, sp.wd_path)); + path p (parse_path (*i, wd)); - const dir_path& wd (sp.root->wd_path); - if (!p.sub (wd) && !force) + if (!p.sub (rwd) && !force) { - cerr << "rm: '" << p << "' is out of working directory '" << wd + cerr << "rm: '" << p << "' is out of working directory '" << rwd << "'" << endl; throw failed (); } @@ -461,10 +463,10 @@ namespace build2 throw failed (); } - if (sp.wd_path.sub (d)) + if (wd.sub (d)) { cerr << "rm: '" << p << "' contains test working directory '" - << sp.wd_path << "'" << endl; + << wd << "'" << endl; throw failed (); } @@ -510,6 +512,122 @@ namespace build2 return 1; } + // rmdir [-f] ... + // + // Remove a directory. The directory must be empty and not be the test + // working directory or its parent directory. It also must not be outside + // the testscript working directory unless the -f option is specified. + // + // -f + // Do not fail if no directory is specified, the directory does not + // exist, or is outside the script working directory. + // + // Note: can be executed synchronously. + // + static uint8_t + rmdir (scope& sp, + const strings& args, + auto_fd in, auto_fd out, auto_fd err) noexcept + try + { + uint8_t r (1); + ofdstream cerr (move (err)); + + try + { + in.close (); + out.close (); + + auto i (args.begin ()); + + // Process options. + // + bool force (false); + for (; i != args.end (); ++i) + { + if (*i == "-f") + force = true; + else + { + if (*i == "--") + ++i; + + break; + } + } + + // Remove directories. + // + if (i == args.end () && !force) + { + cerr << "rmdir: missing directory" << endl; + throw failed (); + } + + const dir_path& wd (sp.wd_path); + const dir_path& rwd (sp.root->wd_path); + + for (; i != args.end (); ++i) + { + dir_path p (path_cast (parse_path (*i, wd))); + + if (wd.sub (p)) + { + cerr << "rmdir: '" << p << "' contains test working directory '" + << wd << "'" << endl; + throw failed (); + } + + if (!p.sub (rwd) && !force) + { + cerr << "rmdir: '" << p << "' is out of working directory '" + << rwd << "'" << endl; + throw failed (); + } + + try + { + rmdir_status s (try_rmdir (p)); + + if (s == rmdir_status::not_empty) + throw system_error (ENOTEMPTY, system_category ()); + else if (s == rmdir_status::not_exist && !force) + throw system_error (ENOENT, system_category ()); + } + catch (const system_error& e) + { + cerr << "rmdir: unable to remove '" << p << "': " << e << endl; + throw failed (); + } + } + + r = 0; + } + catch (const invalid_path& e) + { + cerr << "rmdir: invalid path '" << e.path << "'" << endl; + } + // Can be thrown while closing in, out or writing to cerr (that's why + // need to check its state before writing). + // + catch (const io_error& e) + { + if (cerr.good ()) + cerr << "rmdir: " << e << endl; + } + catch (const failed&) + { + // Diagnostics has already been issued. + } + + cerr.close (); + return r; + } + catch (const std::exception&) + { + return 1; + } + // touch ... // // Change file access and modification times to the current time. Create @@ -689,6 +807,7 @@ namespace build2 {"false", &false_}, {"mkdir", &sync_impl<&mkdir>}, {"rm", &sync_impl<&rm>}, + {"rmdir", &sync_impl<&rmdir>}, {"touch", &sync_impl<&touch>}, {"true", &true_} }; diff --git a/tests/test/script/builtin/buildfile b/tests/test/script/builtin/buildfile index 8c4196d..fe25065 100644 --- a/tests/test/script/builtin/buildfile +++ b/tests/test/script/builtin/buildfile @@ -2,4 +2,4 @@ # copyright : Copyright (c) 2014-2017 Code Synthesis Ltd # license : MIT; see accompanying LICENSE file -./: test{cat echo mkdir rm touch} $b +./: test{cat echo mkdir rm rmdir touch} $b diff --git a/tests/test/script/builtin/rm.test b/tests/test/script/builtin/rm.test index a6de003..937633b 100644 --- a/tests/test/script/builtin/rm.test +++ b/tests/test/script/builtin/rm.test @@ -37,21 +37,25 @@ : not-exists : - : Removing non-existing file fails. - : - $c <>/~%EOE% == 1 - %rm: unable to remove '.+/test/rm/file/not-exists/test/1/a': .+% - EOE - EOI - $b + { + : fail + : + : Removing non-existing file fails. + : + $c <>/~%EOE% == 1 + %rm: unable to remove '.+/file/not-exists/fail/test/1/a': .+% + EOE + EOI + $b - : not-exists-force - : - : Removing non-existing file succeeds with -f option. - : - $c <'rm -f a'; - $b + : force + : + : Removing non-existing file succeeds with -f option. + : + $c <'rm -f a'; + $b + } } : dir @@ -101,23 +105,26 @@ : outside-scope : - : Removing path outside the testscript working directory fails. Need to use - : a path that unlikely exists (not to remove something useful). - : - : - $c <>/~%EOE% == 1 - %rm: '.+/path/outside-scope/a/b/c' is out of working directory '.+/outside-scope/test'% - EOE - EOI - $b + : Need to use a path that unlikely exists (not to remove something useful). + : + { + : fail + : + : Removing path outside the testscript working directory fails. + : + $c <>/~%EOE% == 1 + %rm: '.+/path/outside-scope/fail/a/b/c' is out of working directory '.+/path/outside-scope/fail/test'% + EOE + EOI + $b - : outside-scope-force - : - : Removing path outside the testscript working directory succeeds with -f - : option. Need to use a path that unlikely exists (not to remove something - : useful). - : - $c <'rm -f ../../a/b/c'; - $b + : force + : + : Removing path outside the testscript working directory succeeds with -f + : option. + : + $c <'rm -f ../../a/b/c'; + $b + } } diff --git a/tests/test/script/builtin/rmdir.test b/tests/test/script/builtin/rmdir.test new file mode 100644 index 0000000..7621425 --- /dev/null +++ b/tests/test/script/builtin/rmdir.test @@ -0,0 +1,125 @@ +# file : tests/test/script/builtin/rmdir.test +# copyright : Copyright (c) 2014-2017 Code Synthesis Ltd +# license : MIT; see accompanying LICENSE file + +.include ../common.test + +: no-args +: +{ + : fail + : + : Removing with no arguments fails. + : + $c <'rmdir 2>"rmdir: missing directory" == 1'; + $b + + : force + : + : Removing with no arguments succeeds with -f option. + : + $c <'rmdir -f'; + $b +} + +: dir +: +{ + : empty-path + : + : Removing an empty path fails. + : + $c <"rmdir: invalid path ''" == 1 + EOI + $b + + : test-scope + : + : Removing scope directory fails. + : + $c <"rmdir: '$~' contains test working directory '$~'" == 1 + EOI + $b + + : outside-scope + : + : Need to use a path that unlikely exists (not to remove something useful). + : + { + : fail + : + : Removing directory outside the testscript working directory fails. + : + $c <>/~%EOE% == 1 + %rmdir: '.+/dir/outside-scope/fail/a/b/c' is out of working directory '.+/dir/outside-scope/fail/test'% + EOE + EOI + $b + + : force + : + : Removing path outside the testscript working directory succeeds with -f + : option. + : + $c <'rmdir -f ../../a/b/c'; + $b + } + + : exists + : + : Removing existing directory succeeds. + : + $c <>/~%EOE% == 1 + %rmdir: unable to remove '.+/dir/not-exists/fail/test/1/a': .+% + EOE + EOI + $b + + : force + : + : Removing non-existing directory succeeds with -f option. + : + $c <'rmdir -f a'; + $b + } + + : not-empty + : + : Removing non-empty directory fails. + : + $c <>/~%EOE% == 1 + %rmdir: unable to remove '.+/dir/not-empty/test/1/a': .+% + EOE + EOI + $b + + : not-dir + : + : Removing not a directory path fails. + : + $c <>/~%EOE% == 1 + %rmdir: unable to remove '.+/dir/not-dir/test/1/a': .+% + EOE + EOI + $b +} -- cgit v1.1