aboutsummaryrefslogtreecommitdiff
path: root/build2/test/script/builtin.cxx
diff options
context:
space:
mode:
authorKaren Arutyunov <karen@codesynthesis.com>2017-03-09 15:42:32 +0300
committerKaren Arutyunov <karen@codesynthesis.com>2017-03-15 19:52:39 +0300
commitdeb3ed0a579dadbd6cca7ef2e7fb10148387a1ca (patch)
treecf3f0cb4bc68408f391e9c7436fb317c883f48ea /build2/test/script/builtin.cxx
parent52a29dd64479e9313848941078a2619b47f2c61b (diff)
Add support for in place editing for sed builtin
Diffstat (limited to 'build2/test/script/builtin.cxx')
-rw-r--r--build2/test/script/builtin.cxx157
1 files changed, 71 insertions, 86 deletions
diff --git a/build2/test/script/builtin.cxx b/build2/test/script/builtin.cxx
index a971f64..336fa72 100644
--- a/build2/test/script/builtin.cxx
+++ b/build2/test/script/builtin.cxx
@@ -148,11 +148,6 @@ namespace build2
// cat <file>...
//
- // Read files in sequence and write their contents to STDOUT in the same
- // sequence. Read from STDIN if no arguments provided or '-' is specified
- // as a file path. STDIN, STDOUT and file streams are set to binary mode
- // prior to I/O operations.
- //
// Note that POSIX doesn't specify if after I/O operation failure the
// command should proceed with the rest of the arguments. The current
// implementation exits immediatelly in such a case.
@@ -340,12 +335,6 @@ namespace build2
// cp <src-file>... <dst-dir>/
// cp -R|-r <src-path>... <dst-dir>/
//
- // Copy files and/or directories. The first two forms make a copy of a
- // single entity at the specified path. The last two copy one or more
- // entities into the specified directory.
- //
- // For details read the builtin description in the manual.
- //
// Note: can be executed synchronously.
//
static uint8_t
@@ -517,7 +506,9 @@ namespace build2
// false
//
- // Return 1. Failure to close the file descriptors is silently ignored.
+ // Failure to close the file descriptors is silently ignored.
+ //
+ // Note: can be executed synchronously.
//
static future<uint8_t>
false_ (scope&, const strings&, auto_fd, auto_fd, auto_fd)
@@ -527,7 +518,9 @@ namespace build2
// true
//
- // Return 0. Failure to close the file descriptors is silently ignored.
+ // Failure to close the file descriptors is silently ignored.
+ //
+ // Note: can be executed synchronously.
//
static future<uint8_t>
true_ (scope&, const strings&, auto_fd, auto_fd, auto_fd)
@@ -554,10 +547,6 @@ namespace build2
// mkdir [-p] <dir>...
//
- // -p
- // Create any missing intermediate pathname components. Each argument
- // that names an existing directory must be ignored without error.
- //
// Note that POSIX doesn't specify if after a directory creation failure
// the command should proceed with the rest of the arguments. The current
// implementation exits immediatelly in such a case.
@@ -653,19 +642,6 @@ namespace build2
// rm [-r] [-f] <path>...
//
- // 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 the -f option is specified. Note
- // that directories are not removed by default.
- //
- // -r
- // Remove directories recursively. Must be specified to remove even an
- // empty directory.
- //
- // -f
- // Do not fail if path doesn't exist or no paths specified. Removing
- // paths outside the testscript working directory is not an error.
- //
// The implementation deviates from POSIX in a number of ways. It doesn't
// interact with a user and fails immediatelly if unable to process an
// argument. It doesn't check for dots containment in the path, and
@@ -786,14 +762,6 @@ namespace build2
// rmdir [-f] <path>...
//
- // 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
@@ -894,34 +862,7 @@ namespace build2
return 1;
}
- // sed [-n] -e <script> [<file>]
- //
- // Read text from file, make editing changes according to script, and
- // write the result to stdout. If file is not specified or is '-', read
- // from stdin.
- //
- // -n
- // Suppress automatic printing of the pattern space at the end of the
- // script execution.
- //
- // -e <script>
- // Editing commands to be executed (required).
- //
- // Currently, only single-command scripts using the following editing
- // commands are supported.
- //
- // s/<regex>/<replacement>/<flags>
- // The supported flags are 'i' (case-insensitive search), 'g'
- // (substitute globally), 'p' (print if a replacement was made). If
- // regex starts with ^, then it only matches at the beginning of the
- // pattern space. Similarly, if it ends with $, then it only matches
- // at the end of the pattern space.
- //
- // In replacement, besides the standard ECMAScript escape sequences a
- // subset of Perl-specific ones is recognized.
- //
- // For more details read the builtin description in 'The build2
- // Testscript Language'.
+ // sed [-n] [-i] -e <script> [<file>]
//
// Note: must be executed asynchronously.
//
@@ -941,6 +882,11 @@ namespace build2
try
{
+ // Automatically remove a temporary file (used for in place editing)
+ // on failure.
+ //
+ auto_rmfile rm;
+
// Do not throw when failbit is set (getline() failed to extract any
// character).
//
@@ -953,6 +899,7 @@ namespace build2
// Process options.
//
bool auto_prn (true);
+ bool in_place (false);
struct substitute
{
@@ -966,9 +913,13 @@ namespace build2
for (; i != e; ++i)
{
- if (*i == "-n")
+ const string& o (*i);
+
+ if (o == "-n")
auto_prn = false;
- else if (*i == "-e")
+ else if (o == "-i")
+ in_place = true;
+ else if (o == "-e")
{
// Only a single script is supported.
//
@@ -1034,7 +985,7 @@ namespace build2
}
else
{
- if (*i == "--")
+ if (o == "--")
++i;
break;
@@ -1058,6 +1009,43 @@ namespace build2
if (i != e)
error () << "unexpected argument";
+ // If we edit file in place make sure that the file path is specified
+ // and obtain a temporary file path. We will be writing to the
+ // temporary file (rather than to stdout) and will move it to the
+ // original file path afterwards.
+ //
+ path tp;
+ if (in_place)
+ {
+ if (p.empty ())
+ error () << "-i option specified while reading from stdin";
+
+ try
+ {
+ tp = path::temp_path ("build2-sed");
+
+ cout.close (); // Flush and close.
+
+ cout.open (
+ fdopen (tp,
+ fdopen_mode::out | fdopen_mode::truncate |
+ fdopen_mode::create,
+ path_permissions (p)));
+ }
+ catch (const io_error& e)
+ {
+ error_record d (error ());
+ d << "unable to open '" << tp << "': " << e;
+ }
+ catch (const system_error& e)
+ {
+ error_record d (error ());
+ d << "unable to obtain temporary file: " << e;
+ }
+
+ rm = auto_rmfile (tp);
+ }
+
// Note that ECMAScript is implied if no grammar flag is specified.
//
regex re (subst->regex,
@@ -1096,6 +1084,16 @@ namespace build2
cin.close ();
cout.close ();
+
+ if (in_place)
+ {
+ mvfile (
+ tp, p,
+ cpflags::overwrite_content | cpflags::overwrite_permissions);
+
+ rm.cancel ();
+ }
+
r = 0;
}
catch (const io_error& e)
@@ -1127,6 +1125,10 @@ namespace build2
{
error (false) << e;
}
+ catch (const system_error& e)
+ {
+ error (false) << e;
+ }
catch (const failed&)
{
// Diagnostics has already been issued.
@@ -1142,19 +1144,6 @@ namespace build2
// test -f|-d <path>
//
- // Test the specified path according to one of the following options.
- // Succeed (0 exit code) if the test passes and fail (1 exit code)
- // otherwise. In case of an error exit with code 2. Providing no
- // arguments is an error (unlike POSIX).
- //
- // -f
- // Path exists and is to a regular file.
- //
- // -d
- // Path exists and is to a directory.
- //
- // Note that tests dereference symbolic links.
- //
// Note: can be executed synchronously.
//
static uint8_t
@@ -1223,10 +1212,6 @@ namespace build2
// touch <file>...
//
- // Change file access and modification times to the current time. Create
- // a file if doesn't exist. Fail if a file system entry other than file
- // exists for the name specified.
- //
// Note that POSIX doesn't specify the behavior for touching an entry
// other than file.
//