aboutsummaryrefslogtreecommitdiffstats
path: root/util
diff options
context:
space:
mode:
authorkerzum <kerzum@yandex-team.ru>2022-02-10 16:49:33 +0300
committerDaniil Cherednik <dcherednik@yandex-team.ru>2022-02-10 16:49:33 +0300
commit47a7e7b29636bfb2deb1df5f92363b3c75229c95 (patch)
tree5d5cb817648f650d76cf1076100726fd9b8448e8 /util
parent9a7232babfd763ccfe827bc70e82e0f50cfd8276 (diff)
downloadydb-47a7e7b29636bfb2deb1df5f92363b3c75229c95.tar.gz
Restoring authorship annotation for <kerzum@yandex-team.ru>. Commit 2 of 2.
Diffstat (limited to 'util')
-rw-r--r--util/charset/wide.cpp4
-rw-r--r--util/charset/wide.h8
-rw-r--r--util/datetime/parser.rl62
-rw-r--r--util/folder/path.cpp84
-rw-r--r--util/folder/path.h34
-rw-r--r--util/generic/buffer.h2
-rw-r--r--util/generic/buffer_ut.cpp10
-rw-r--r--util/generic/string_ut.cpp10
-rw-r--r--util/generic/ylimits_ut.cpp2
-rw-r--r--util/memory/tempbuf.h4
-rw-r--r--util/stream/zerocopy.h10
-rw-r--r--util/string/escape.cpp20
-rw-r--r--util/string/escape_ut.cpp8
-rw-r--r--util/string/util.cpp2
-rw-r--r--util/system/shellcommand.cpp552
-rw-r--r--util/system/shellcommand.h138
-rw-r--r--util/system/shellcommand_ut.cpp224
17 files changed, 557 insertions, 557 deletions
diff --git a/util/charset/wide.cpp b/util/charset/wide.cpp
index ce2d9dcad8..a287438ddd 100644
--- a/util/charset/wide.cpp
+++ b/util/charset/wide.cpp
@@ -1,5 +1,5 @@
-#include "wide.h"
-
+#include "wide.h"
+
#include <util/generic/mem_copy.h>
#include <util/string/strip.h>
diff --git a/util/charset/wide.h b/util/charset/wide.h
index 8bb3bc2409..04e6928aab 100644
--- a/util/charset/wide.h
+++ b/util/charset/wide.h
@@ -1,5 +1,5 @@
#pragma once
-
+
#include "recode_result.h"
#include "unidata.h"
#include "utf8.h"
@@ -257,7 +257,7 @@ public:
return Begin;
}
};
-
+
namespace NDetail {
template <bool robust, typename TCharType>
inline void UTF8ToWideImplScalar(const unsigned char*& cur, const unsigned char* last, TCharType*& dest) noexcept {
@@ -622,7 +622,7 @@ template <typename TChar1, typename TChar2>
inline void Copy(const TChar1* first, size_t len, TChar2* result) {
Copy(first, first + len, result);
}
-
+
//! copies symbols from one character sequence to another without any conversion
//! @note this function can be used instead of the template constructor of @c std::basic_string:
//! template <typename InputIterator>
@@ -635,7 +635,7 @@ inline TStringType CopyTo(const TChar* first, const TChar* last) {
Copy(first, last, str.begin());
return str;
}
-
+
template <typename TStringType, typename TChar>
inline TStringType CopyTo(const TChar* s, size_t n) {
TStringType str = TStringType::Uninitialized(n);
diff --git a/util/datetime/parser.rl6 b/util/datetime/parser.rl6
index 879550727a..931f09eae1 100644
--- a/util/datetime/parser.rl6
+++ b/util/datetime/parser.rl6
@@ -209,7 +209,7 @@ secondFrac = digit {1,6} >clear_int $update_int @{
secondFracTail = (digit*);
zoneZ = [Zz] @set_zone_utc;
-zoneOffset = space? . ('+' | '-') >{ Sign = fc == '+' ? 1 : -1; } . int2 @{ DateTimeFields.ZoneOffsetMinutes = Sign * (i32)TDuration::Hours(I).Minutes(); } . (':')? . (int2 @{ DateTimeFields.ZoneOffsetMinutes += I * Sign; })?;
+zoneOffset = space? . ('+' | '-') >{ Sign = fc == '+' ? 1 : -1; } . int2 @{ DateTimeFields.ZoneOffsetMinutes = Sign * (i32)TDuration::Hours(I).Minutes(); } . (':')? . (int2 @{ DateTimeFields.ZoneOffsetMinutes += I * Sign; })?;
zone = zoneZ | zoneOffset;
iso8601date = (year . '-' . month . '-' . day) | (year . month . day);
diff --git a/util/folder/path.cpp b/util/folder/path.cpp
index ad32a522a8..bfe0c67d68 100644
--- a/util/folder/path.cpp
+++ b/util/folder/path.cpp
@@ -21,10 +21,10 @@ void TFsPath::CheckDefined() const {
}
}
-bool TFsPath::IsSubpathOf(const TFsPath& that) const {
- const TSplit& split = GetSplit();
- const TSplit& rsplit = that.GetSplit();
-
+bool TFsPath::IsSubpathOf(const TFsPath& that) const {
+ const TSplit& split = GetSplit();
+ const TSplit& rsplit = that.GetSplit();
+
if (rsplit.IsAbsolute != split.IsAbsolute) {
return false;
}
@@ -34,12 +34,12 @@ bool TFsPath::IsSubpathOf(const TFsPath& that) const {
}
if (rsplit.size() >= split.size()) {
- return false;
+ return false;
}
-
+
return std::equal(rsplit.begin(), rsplit.end(), split.begin());
-}
-
+}
+
bool TFsPath::IsNonStrictSubpathOf(const TFsPath& that) const {
const TSplit& split = GetSplit();
const TSplit& rsplit = that.GetSplit();
@@ -59,12 +59,12 @@ bool TFsPath::IsNonStrictSubpathOf(const TFsPath& that) const {
return std::equal(rsplit.begin(), rsplit.end(), split.begin());
}
-TFsPath TFsPath::RelativeTo(const TFsPath& root) const {
- TSplit split = GetSplit();
- const TSplit& rsplit = root.GetSplit();
+TFsPath TFsPath::RelativeTo(const TFsPath& root) const {
+ TSplit split = GetSplit();
+ const TSplit& rsplit = root.GetSplit();
if (split.Reconstruct() == rsplit.Reconstruct()) {
- return TFsPath();
+ return TFsPath();
}
if (!this->IsSubpathOf(root)) {
@@ -74,9 +74,9 @@ TFsPath TFsPath::RelativeTo(const TFsPath& root) const {
split.erase(split.begin(), split.begin() + rsplit.size());
split.IsAbsolute = false;
- return TFsPath(split.Reconstruct());
-}
-
+ return TFsPath(split.Reconstruct());
+}
+
TFsPath TFsPath::RelativePath(const TFsPath& root) const {
TSplit split = GetSplit();
const TSplit& rsplit = root.GetSplit();
@@ -100,22 +100,22 @@ TFsPath TFsPath::RelativePath(const TFsPath& root) const {
return r.size() ? TFsPath(r) : TFsPath();
}
-TFsPath TFsPath::Parent() const {
+TFsPath TFsPath::Parent() const {
if (!IsDefined()) {
return TFsPath();
}
- TSplit split = GetSplit();
+ TSplit split = GetSplit();
if (split.size()) {
split.pop_back();
}
if (!split.size() && !split.IsAbsolute) {
- return TFsPath(".");
+ return TFsPath(".");
}
- return TFsPath(split.Reconstruct());
-}
-
-TFsPath& TFsPath::operator/=(const TFsPath& that) {
+ return TFsPath(split.Reconstruct());
+}
+
+TFsPath& TFsPath::operator/=(const TFsPath& that) {
if (!IsDefined()) {
*this = that;
@@ -127,18 +127,18 @@ TFsPath& TFsPath::operator/=(const TFsPath& that) {
TSplit split = GetSplit();
const TSplit& rsplit = that.GetSplit();
split.insert(split.end(), rsplit.begin(), rsplit.end());
- *this = TFsPath(split.Reconstruct());
- }
- return *this;
-}
-
-TFsPath& TFsPath::Fix() {
- // just normalize via reconstruction
+ *this = TFsPath(split.Reconstruct());
+ }
+ return *this;
+}
+
+TFsPath& TFsPath::Fix() {
+ // just normalize via reconstruction
TFsPath(GetSplit().Reconstruct()).Swap(*this);
- return *this;
-}
-
+ return *this;
+}
+
TString TFsPath::GetName() const {
if (!IsDefined()) {
return TString();
@@ -194,20 +194,20 @@ TFsPath::TFsPath() {
}
TFsPath::TFsPath(const TString& path)
- : Path_(path)
-{
+ : Path_(path)
+{
VerifyPath(Path_);
}
TFsPath::TFsPath(const TStringBuf path)
- : Path_(ToString(path))
-{
+ : Path_(ToString(path))
+{
VerifyPath(Path_);
-}
-
-TFsPath::TFsPath(const char* path)
- : Path_(path)
-{
+}
+
+TFsPath::TFsPath(const char* path)
+ : Path_(path)
+{
}
TFsPath TFsPath::Child(const TString& name) const {
@@ -215,7 +215,7 @@ TFsPath TFsPath::Child(const TString& name) const {
ythrow TIoException() << "child name must not be empty";
}
- return *this / name;
+ return *this / name;
}
struct TClosedir {
diff --git a/util/folder/path.h b/util/folder/path.h
index ce5a18f394..2fb4d6b4ef 100644
--- a/util/folder/path.h
+++ b/util/folder/path.h
@@ -46,7 +46,7 @@ public:
inline const char* c_str() const {
return Path_.c_str();
- }
+ }
inline operator const TString&() const {
return Path_;
@@ -60,17 +60,17 @@ public:
return Path_ != that.Path_;
}
- TFsPath& operator/=(const TFsPath& that);
+ TFsPath& operator/=(const TFsPath& that);
friend TFsPath operator/(const TFsPath& s, const TFsPath& p) {
TFsPath ret(s);
return ret /= p;
- }
-
+ }
+
const TPathSplit& PathSplit() const;
- TFsPath& Fix();
-
+ TFsPath& Fix();
+
inline const TString& GetPath() const {
return Path_;
}
@@ -97,7 +97,7 @@ public:
* @param that - presumable parent path of this
* @return True if this is a subpath of that and false otherwise.
*/
- bool IsSubpathOf(const TFsPath& that) const;
+ bool IsSubpathOf(const TFsPath& that) const;
/**
* TFsPath("/a/b").IsNonStrictSubpathOf("/a") -> true
@@ -110,9 +110,9 @@ public:
*/
bool IsNonStrictSubpathOf(const TFsPath& that) const;
- bool IsContainerOf(const TFsPath& that) const {
- return that.IsSubpathOf(*this);
- }
+ bool IsContainerOf(const TFsPath& that) const {
+ return that.IsSubpathOf(*this);
+ }
TFsPath RelativeTo(const TFsPath& root) const; //must be subpath of root
@@ -127,11 +127,11 @@ public:
TFsPath Parent() const;
TString Basename() const {
- return GetName();
- }
+ return GetName();
+ }
TString Dirname() const {
- return Parent();
- }
+ return Parent();
+ }
TFsPath Child(const TString& name) const;
@@ -168,8 +168,8 @@ public:
inline bool Stat(TFileStat& stat) const {
stat = TFileStat(Path_.data());
- return stat.Mode;
- }
+ return stat.Mode;
+ }
bool Exists() const;
/// false if not exists
@@ -211,7 +211,7 @@ private:
/// cache
mutable TSimpleIntrusivePtr<TSplit> Split_;
};
-
+
namespace NPrivate {
inline void AppendToFsPath(TFsPath&) {
}
diff --git a/util/generic/buffer.h b/util/generic/buffer.h
index bdf469da08..9576467404 100644
--- a/util/generic/buffer.h
+++ b/util/generic/buffer.h
@@ -161,7 +161,7 @@ public:
Reserve(len);
Pos_ = len;
}
-
+
// Method works like Resize, but allocates exact specified number of bytes
// rather than rounded up to next power of 2
// Use with care
diff --git a/util/generic/buffer_ut.cpp b/util/generic/buffer_ut.cpp
index d6c7f70e0c..437d7122ec 100644
--- a/util/generic/buffer_ut.cpp
+++ b/util/generic/buffer_ut.cpp
@@ -56,22 +56,22 @@ Y_UNIT_TEST_SUITE(TBufferTest) {
buf.Resize(10);
UNIT_ASSERT_VALUES_EQUAL(buf.size(), 10u);
-
+
buf.Resize(0);
UNIT_ASSERT_VALUES_EQUAL(buf.size(), 0u);
-
+
buf.Resize(9);
memcpy(buf.data(), content, 9);
UNIT_ASSERT_VALUES_EQUAL(TString(buf.data(), buf.size()), "some text");
-
+
buf.Resize(4);
UNIT_ASSERT_VALUES_EQUAL(TString(buf.data(), buf.size()), "some");
}
-
+
Y_UNIT_TEST(TestReserve) {
TBuffer buf;
UNIT_ASSERT_EQUAL(buf.Capacity(), 0);
-
+
buf.Reserve(4);
UNIT_ASSERT_EQUAL(buf.Capacity(), 4);
diff --git a/util/generic/string_ut.cpp b/util/generic/string_ut.cpp
index b9099371b0..ac82e9091d 100644
--- a/util/generic/string_ut.cpp
+++ b/util/generic/string_ut.cpp
@@ -23,9 +23,9 @@ static_assert(sizeof(TString) == sizeof(const char*), "expect sizeof(TString) ==
class TStringTestZero: public TTestBase {
UNIT_TEST_SUITE(TStringTestZero);
UNIT_TEST(TestZero);
- UNIT_TEST_SUITE_END();
+ UNIT_TEST_SUITE_END();
-public:
+public:
void TestZero() {
const char data[] = "abc\0def\0";
TString s(data, sizeof(data));
@@ -33,7 +33,7 @@ public:
UNIT_ASSERT(s.StartsWith(s));
UNIT_ASSERT(s.EndsWith(s));
UNIT_ASSERT(s.Contains('\0'));
-
+
const char raw_def[] = "def";
const char raw_zero[] = "\0";
TString def(raw_def, sizeof(raw_def) - 1);
@@ -52,7 +52,7 @@ public:
UNIT_ASSERT(s != copy);
copy.replace(copy.size() - 1, 1, "\0", 0, 1);
UNIT_ASSERT(s == copy);
-
+
TString prefix(data, 5);
UNIT_ASSERT(s.StartsWith(prefix));
UNIT_ASSERT(s != prefix);
@@ -60,7 +60,7 @@ public:
UNIT_ASSERT(s > s.data());
UNIT_ASSERT(s == TString(s.data(), s.size()));
UNIT_ASSERT(data < s);
-
+
s.remove(5);
UNIT_ASSERT(s == prefix);
}
diff --git a/util/generic/ylimits_ut.cpp b/util/generic/ylimits_ut.cpp
index 896043a145..f1b3c6858c 100644
--- a/util/generic/ylimits_ut.cpp
+++ b/util/generic/ylimits_ut.cpp
@@ -133,7 +133,7 @@ void TLimitTest::TestLimits() {
UNIT_ASSERT(TestFloatLimits(float()));
UNIT_ASSERT(TestFloatLimits(double()));
using long_double = long double;
- UNIT_ASSERT(RUNNING_ON_VALGRIND || TestFloatLimits(long_double()));
+ UNIT_ASSERT(RUNNING_ON_VALGRIND || TestFloatLimits(long_double()));
}
void TLimitTest::TestNan() {
diff --git a/util/memory/tempbuf.h b/util/memory/tempbuf.h
index 315c30ed86..334670eb1e 100644
--- a/util/memory/tempbuf.h
+++ b/util/memory/tempbuf.h
@@ -51,7 +51,7 @@ private:
};
template <typename T>
-class TTempArray: private TTempBuf {
+class TTempArray: private TTempBuf {
private:
static T* TypedPointer(char* pointer) noexcept {
return reinterpret_cast<T*>(pointer);
@@ -100,4 +100,4 @@ public:
T* Proceed(size_t off) {
return (T*)TTempBuf::Proceed(RawSize(off));
}
-};
+};
diff --git a/util/stream/zerocopy.h b/util/stream/zerocopy.h
index dffe304457..3315aa3a51 100644
--- a/util/stream/zerocopy.h
+++ b/util/stream/zerocopy.h
@@ -1,9 +1,9 @@
#pragma once
-
+
#include <util/system/yassert.h>
#include <util/system/defaults.h>
#include <util/generic/ylimits.h>
-
+
#include "input.h"
class IOutputStream;
@@ -22,7 +22,7 @@ class IZeroCopyInput: public IInputStream {
public:
IZeroCopyInput() noexcept = default;
~IZeroCopyInput() override;
-
+
IZeroCopyInput(IZeroCopyInput&&) noexcept = default;
IZeroCopyInput& operator=(IZeroCopyInput&&) noexcept = default;
@@ -54,8 +54,8 @@ protected:
size_t DoSkip(size_t len) override;
ui64 DoReadAll(IOutputStream& out) override;
virtual size_t DoNext(const void** ptr, size_t len) = 0;
-};
-
+};
+
/**
* Input stream with direct access to the input buffer and ability to undo read
*
diff --git a/util/string/escape.cpp b/util/string/escape.cpp
index 3378a14a17..cd09a7dbd0 100644
--- a/util/string/escape.cpp
+++ b/util/string/escape.cpp
@@ -5,8 +5,8 @@
#include <util/charset/utf8.h>
#include <util/charset/wide.h>
-/// @todo: escape trigraphs (eg "??/" is "\")
-
+/// @todo: escape trigraphs (eg "??/" is "\")
+
/* REFEREBCES FOR ESCAPE SEQUENCE INTERPRETATION:
* C99 p. 6.4.3 Universal character names.
* C99 p. 6.4.4.4 Character constants.
@@ -31,14 +31,14 @@
* - Hexadecimal escape sequence spans until rightmost non-hexadecimal-digit character.
* - Universal character name consists of exactly 4 or 8 hexadecimal digit.
*
- * by kerzum@
- * It is also required to escape trigraphs that are enabled in compilers by default and
- * are also processed inside string literals
- * The nine trigraphs and their replacements are
- *
- * Trigraph: ??( ??) ??< ??> ??= ??/ ??' ??! ??-
- * Replacement: [ ] { } # \ ^ | ~
- *
+ * by kerzum@
+ * It is also required to escape trigraphs that are enabled in compilers by default and
+ * are also processed inside string literals
+ * The nine trigraphs and their replacements are
+ *
+ * Trigraph: ??( ??) ??< ??> ??= ??/ ??' ??! ??-
+ * Replacement: [ ] { } # \ ^ | ~
+ *
*/
namespace {
template <typename TChar>
diff --git a/util/string/escape_ut.cpp b/util/string/escape_ut.cpp
index 49fb122972..cd38ecffd3 100644
--- a/util/string/escape_ut.cpp
+++ b/util/string/escape_ut.cpp
@@ -41,7 +41,7 @@ static const TExample CommonTestData[] = {
{"Slash\\\\dash!", "Slash\\dash!"},
{R"(There\nare\r\nnewlines.)", "There\nare\r\nnewlines."},
{"There\\tare\\ttabs.", "There\tare\ttabs."},
-
+
{"There are questions \\x3F\\x3F?", "There are questions ???"},
{"There are questions \\x3F?", "There are questions ??"},
};
@@ -94,15 +94,15 @@ Y_UNIT_TEST_SUITE(TEscapeCTest) {
UNIT_ASSERT_VALUES_EQUAL(u"h", EscapeC(u'h'));
UNIT_ASSERT_VALUES_EQUAL(u"\\xFF", EscapeC(wchar16(255)));
}
-
+
Y_UNIT_TEST(TestEscapeTrigraphs) {
UNIT_ASSERT_VALUES_EQUAL("?", EscapeC(TString("?")));
UNIT_ASSERT_VALUES_EQUAL("\\x3F?", EscapeC(TString("??")));
UNIT_ASSERT_VALUES_EQUAL("\\x3F\\x3F?", EscapeC(TString("???")));
- // ok but may cause warning about trigraphs
+ // ok but may cause warning about trigraphs
// UNIT_ASSERT_VALUES_EQUAL("[x]?z", EscapeC(TString("??(x??)?z")));
UNIT_ASSERT_VALUES_EQUAL("\\x3F?x\\x3F\\x3F?z", EscapeC(TString("??x???z")));
- }
+ }
Y_UNIT_TEST(TestUnescapeCCharLen) {
auto test = [](const char* str, size_t len) {
diff --git a/util/string/util.cpp b/util/string/util.cpp
index 4e2cc82437..b14f20bf75 100644
--- a/util/string/util.cpp
+++ b/util/string/util.cpp
@@ -48,7 +48,7 @@ Tr::Tr(const char* from, const char* to) {
Map[(ui8)*from] = *to;
}
}
-
+
size_t Tr::FindFirstChangePosition(const TString& str) const {
for (auto it = str.begin(); it != str.end(); ++it) {
if (ConvertChar(*it) != *it) {
diff --git a/util/system/shellcommand.cpp b/util/system/shellcommand.cpp
index 3cc23600cf..b1989b5c8c 100644
--- a/util/system/shellcommand.cpp
+++ b/util/system/shellcommand.cpp
@@ -4,7 +4,7 @@
#include "sigset.h"
#include "atomic.h"
-#include <util/folder/dirut.h>
+#include <util/folder/dirut.h>
#include <util/generic/algorithm.h>
#include <util/generic/buffer.h>
#include <util/generic/vector.h>
@@ -18,12 +18,12 @@
#include <errno.h>
-#if defined(_unix_)
+#if defined(_unix_)
#include <unistd.h>
#include <fcntl.h>
#include <grp.h>
#include <sys/wait.h>
-
+
using TPid = pid_t;
using TWaitResult = pid_t;
using TExitStatus = int;
@@ -34,25 +34,25 @@ using TGetGroupListGid = int;
#else
using TGetGroupListGid = gid_t;
#endif
-#elif defined(_win_)
+#elif defined(_win_)
#include <string>
#include "winint.h"
-
+
using TPid = HANDLE;
using TWaitResult = DWORD;
using TExitStatus = DWORD;
#define WAIT_PROCEED WAIT_TIMEOUT
#pragma warning(disable : 4296) // 'wait_result >= WAIT_OBJECT_0' : expression is always tru
-#else
+#else
#error("unknown os, shell command is not implemented")
-#endif
-
+#endif
+
#define DBG(stmt) \
{}
-// #define DBG(stmt) stmt
-
+// #define DBG(stmt) stmt
+
namespace {
constexpr static size_t DATA_BUFFER_SIZE = 128 * 1024;
@@ -107,89 +107,89 @@ namespace {
#endif
}
-// temporary measure to avoid rewriting all poll calls on win TPipeHandle
-#if defined(_win_)
+// temporary measure to avoid rewriting all poll calls on win TPipeHandle
+#if defined(_win_)
using REALPIPEHANDLE = HANDLE;
#define INVALID_REALPIPEHANDLE INVALID_HANDLE_VALUE
-
-class TRealPipeHandle
+
+class TRealPipeHandle
: public TNonCopyable {
-public:
+public:
inline TRealPipeHandle() noexcept
- : Fd_(INVALID_REALPIPEHANDLE)
- {
- }
-
+ : Fd_(INVALID_REALPIPEHANDLE)
+ {
+ }
+
inline TRealPipeHandle(REALPIPEHANDLE fd) noexcept
- : Fd_(fd)
- {
- }
-
+ : Fd_(fd)
+ {
+ }
+
inline ~TRealPipeHandle() {
- Close();
- }
-
+ Close();
+ }
+
bool Close() noexcept {
- bool ok = true;
- if (Fd_ != INVALID_REALPIPEHANDLE)
- ok = CloseHandle(Fd_);
- Fd_ = INVALID_REALPIPEHANDLE;
- return ok;
- }
-
+ bool ok = true;
+ if (Fd_ != INVALID_REALPIPEHANDLE)
+ ok = CloseHandle(Fd_);
+ Fd_ = INVALID_REALPIPEHANDLE;
+ return ok;
+ }
+
inline REALPIPEHANDLE Release() noexcept {
- REALPIPEHANDLE ret = Fd_;
- Fd_ = INVALID_REALPIPEHANDLE;
- return ret;
- }
-
+ REALPIPEHANDLE ret = Fd_;
+ Fd_ = INVALID_REALPIPEHANDLE;
+ return ret;
+ }
+
inline void Swap(TRealPipeHandle& r) noexcept {
- DoSwap(Fd_, r.Fd_);
- }
-
+ DoSwap(Fd_, r.Fd_);
+ }
+
inline operator REALPIPEHANDLE() const noexcept {
- return Fd_;
- }
-
+ return Fd_;
+ }
+
inline bool IsOpen() const noexcept {
- return Fd_ != INVALID_REALPIPEHANDLE;
- }
-
+ return Fd_ != INVALID_REALPIPEHANDLE;
+ }
+
ssize_t Read(void* buffer, size_t byteCount) const noexcept {
- DWORD doneBytes;
+ DWORD doneBytes;
if (!ReadFile(Fd_, buffer, byteCount, &doneBytes, nullptr))
- return -1;
- return doneBytes;
- }
+ return -1;
+ return doneBytes;
+ }
ssize_t Write(const void* buffer, size_t byteCount) const noexcept {
- DWORD doneBytes;
+ DWORD doneBytes;
if (!WriteFile(Fd_, buffer, byteCount, &doneBytes, nullptr))
- return -1;
- return doneBytes;
- }
-
+ return -1;
+ return doneBytes;
+ }
+
static void Pipe(TRealPipeHandle& reader, TRealPipeHandle& writer, EOpenMode mode) {
(void)mode;
- REALPIPEHANDLE fds[2];
+ REALPIPEHANDLE fds[2];
if (!CreatePipe(&fds[0], &fds[1], nullptr /* handles are not inherited */, 0))
- ythrow TFileError() << "failed to create a pipe";
- TRealPipeHandle(fds[0]).Swap(reader);
- TRealPipeHandle(fds[1]).Swap(writer);
- }
-
+ ythrow TFileError() << "failed to create a pipe";
+ TRealPipeHandle(fds[0]).Swap(reader);
+ TRealPipeHandle(fds[1]).Swap(writer);
+ }
+
private:
- REALPIPEHANDLE Fd_;
-};
-
-#else
+ REALPIPEHANDLE Fd_;
+};
+
+#else
using TRealPipeHandle = TPipeHandle;
using REALPIPEHANDLE = PIPEHANDLE;
#define INVALID_REALPIPEHANDLE INVALID_PIPEHANDLE
-#endif
-
-class TShellCommand::TImpl
+#endif
+
+class TShellCommand::TImpl
: public TAtomicRefCount<TShellCommand::TImpl> {
-private:
+private:
TPid Pid;
TString Command;
TList<TString> Arguments;
@@ -208,7 +208,7 @@ private:
TFileHandle OutputHandle;
TFileHandle ErrorHandle;
- /// @todo: store const TShellCommandOptions, no need for so many vars
+ /// @todo: store const TShellCommandOptions, no need for so many vars
bool TerminateFlag = false;
bool ClearSignalMask = false;
bool CloseAllFdsOnExec = false;
@@ -230,9 +230,9 @@ private:
struct TProcessInfo {
TImpl* Parent;
- TRealPipeHandle InputFd;
- TRealPipeHandle OutputFd;
- TRealPipeHandle ErrorFd;
+ TRealPipeHandle InputFd;
+ TRealPipeHandle OutputFd;
+ TRealPipeHandle ErrorFd;
TProcessInfo(TImpl* parent, REALPIPEHANDLE inputFd, REALPIPEHANDLE outputFd, REALPIPEHANDLE errorFd)
: Parent(parent)
, InputFd(inputFd)
@@ -242,12 +242,12 @@ private:
}
};
- struct TPipes {
- TRealPipeHandle OutputPipeFd[2];
- TRealPipeHandle ErrorPipeFd[2];
- TRealPipeHandle InputPipeFd[2];
- // pipes are closed by automatic dtor
- void PrepareParents() {
+ struct TPipes {
+ TRealPipeHandle OutputPipeFd[2];
+ TRealPipeHandle ErrorPipeFd[2];
+ TRealPipeHandle InputPipeFd[2];
+ // pipes are closed by automatic dtor
+ void PrepareParents() {
if (OutputPipeFd[1].IsOpen()) {
OutputPipeFd[1].Close();
}
@@ -255,16 +255,16 @@ private:
ErrorPipeFd[1].Close();
}
if (InputPipeFd[1].IsOpen()) {
- InputPipeFd[0].Close();
+ InputPipeFd[0].Close();
}
- }
- void ReleaseParents() {
- InputPipeFd[1].Release();
- OutputPipeFd[0].Release();
- ErrorPipeFd[0].Release();
- }
- };
-
+ }
+ void ReleaseParents() {
+ InputPipeFd[1].Release();
+ OutputPipeFd[0].Release();
+ ErrorPipeFd[0].Release();
+ }
+ };
+
struct TPipePump {
TRealPipeHandle* Pipe;
IOutputStream* OutputStream;
@@ -273,12 +273,12 @@ private:
TString InternalError;
};
-#if defined(_unix_)
+#if defined(_unix_)
void OnFork(TPipes& pipes, sigset_t oldmask, char* const* argv, char* const* envp, const std::function<void()>& afterFork) const;
-#else
- void StartProcess(TPipes& pipes);
-#endif
-
+#else
+ void StartProcess(TPipes& pipes);
+#endif
+
public:
inline TImpl(const TStringBuf cmd, const TList<TString>& args, const TShellCommandOptions& options, const TString& workdir)
: Pid(0)
@@ -298,7 +298,7 @@ public:
, UseShell(options.UseShell)
, QuoteArguments(options.QuoteArguments)
, DetachSession(options.DetachSession)
- , CloseStreams(options.CloseStreams)
+ , CloseStreams(options.CloseStreams)
, ShouldCloseInput(options.ShouldCloseInput)
, InputMode(options.InputMode)
, OutputMode(options.OutputMode)
@@ -386,24 +386,24 @@ public:
return ErrorHandle;
}
- // start child process
- void Run();
+ // start child process
+ void Run();
inline void Terminate() {
if (!!Pid && (AtomicGet(ExecutionStatus) == SHELL_RUNNING)) {
- bool ok =
-#if defined(_unix_)
+ bool ok =
+#if defined(_unix_)
kill(DetachSession ? -1 * Pid : Pid, SIGTERM) == 0;
if (!ok && (errno == ESRCH) && DetachSession) {
// this could fail when called before child proc completes setsid().
ok = kill(Pid, SIGTERM) == 0;
kill(-Pid, SIGTERM); // between a failed kill(-Pid) and a successful kill(Pid) a grandchild could have been spawned
}
-#else
+#else
TerminateProcess(Pid, 1 /* exit code */);
-#endif
+#endif
if (!ok) {
- ythrow TSystemError() << "cannot terminate " << Pid;
+ ythrow TSystemError() << "cannot terminate " << Pid;
}
}
}
@@ -426,28 +426,28 @@ public:
pi->InputFd.Close();
pi->ErrorFd.Close();
pi->OutputFd.Close();
-
- if (pi->Parent->CloseStreams) {
+
+ if (pi->Parent->CloseStreams) {
if (pi->Parent->ErrorStream) {
- pi->Parent->ErrorStream->Finish();
+ pi->Parent->ErrorStream->Finish();
}
if (pi->Parent->OutputStream) {
- pi->Parent->OutputStream->Finish();
+ pi->Parent->OutputStream->Finish();
}
- }
-
+ }
+
delete pi;
return true;
}
- // interchange io while process is alive
+ // interchange io while process is alive
inline static void Communicate(TProcessInfo* pi);
inline static void* WatchProcess(void* data) {
TProcessInfo* pi = reinterpret_cast<TProcessInfo*>(data);
- Communicate(pi);
+ Communicate(pi);
return nullptr;
- }
+ }
inline static void* ReadStream(void* data) noexcept {
TPipePump* pump = reinterpret_cast<TPipePump*>(data);
@@ -510,16 +510,16 @@ public:
}
TString GetQuotedCommand() const;
-};
+};
-#if defined(_win_)
-void TShellCommand::TImpl::StartProcess(TShellCommand::TImpl::TPipes& pipes) {
- // Setup STARTUPINFO to redirect handles.
+#if defined(_win_)
+void TShellCommand::TImpl::StartProcess(TShellCommand::TImpl::TPipes& pipes) {
+ // Setup STARTUPINFO to redirect handles.
STARTUPINFOW startup_info;
- ZeroMemory(&startup_info, sizeof(startup_info));
- startup_info.cb = sizeof(startup_info);
- startup_info.dwFlags = STARTF_USESTDHANDLES;
-
+ ZeroMemory(&startup_info, sizeof(startup_info));
+ startup_info.cb = sizeof(startup_info);
+ startup_info.dwFlags = STARTF_USESTDHANDLES;
+
if (OutputMode != TShellCommandOptions::HANDLE_INHERIT) {
if (!SetHandleInformation(pipes.OutputPipeFd[1], HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT)) {
ythrow TSystemError() << "cannot set handle info";
@@ -531,11 +531,11 @@ void TShellCommand::TImpl::StartProcess(TShellCommand::TImpl::TPipes& pipes) {
}
}
if (InputMode != TShellCommandOptions::HANDLE_INHERIT) {
- if (!SetHandleInformation(pipes.InputPipeFd[0], HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT))
- ythrow TSystemError() << "cannot set handle info";
+ if (!SetHandleInformation(pipes.InputPipeFd[0], HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT))
+ ythrow TSystemError() << "cannot set handle info";
}
-
- // A sockets do not work as std streams for some reason
+
+ // A sockets do not work as std streams for some reason
if (OutputMode != TShellCommandOptions::HANDLE_INHERIT) {
startup_info.hStdOutput = pipes.OutputPipeFd[1];
} else {
@@ -547,20 +547,20 @@ void TShellCommand::TImpl::StartProcess(TShellCommand::TImpl::TPipes& pipes) {
startup_info.hStdError = GetStdHandle(STD_ERROR_HANDLE);
}
if (InputMode != TShellCommandOptions::HANDLE_INHERIT) {
- startup_info.hStdInput = pipes.InputPipeFd[0];
+ startup_info.hStdInput = pipes.InputPipeFd[0];
} else {
// Don't leave hStdInput unfilled, otherwise any attempt to retrieve the operating-system file handle
// that is associated with the specified file descriptor will led to errors.
startup_info.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
}
-
- PROCESS_INFORMATION process_info;
+
+ PROCESS_INFORMATION process_info;
// TString cmd = "cmd /U" + TUtf16String can be used to read unicode messages from cmd
- // /A - ansi charset /Q - echo off, /C - command, /Q - special quotes
+ // /A - ansi charset /Q - echo off, /C - command, /Q - special quotes
TString qcmd = GetQuotedCommand();
TString cmd = UseShell ? "cmd /A /Q /S /C \"" + qcmd + "\"" : qcmd;
- // winapi can modify command text, copy it
-
+ // winapi can modify command text, copy it
+
Y_ENSURE_EX(cmd.size() < MAX_COMMAND_LINE, yexception() << "Command is too long (length=" << cmd.size() << ")");
TTempArray<wchar_t> cmdcopy(MAX_COMMAND_LINE);
Copy(cmd.data(), cmd.data() + cmd.size(), cmdcopy.Data());
@@ -617,16 +617,16 @@ void TShellCommand::TImpl::StartProcess(TShellCommand::TImpl::TPipes& pipes) {
if (!res) {
AtomicSet(ExecutionStatus, SHELL_ERROR);
- /// @todo: write to error stream if set
+ /// @todo: write to error stream if set
TStringOutput out(CollectedError);
out << "Process was not created: " << LastSystemErrorText() << " command text was: '" << GetAString(cmdcopy.Data()) << "'";
- }
+ }
Pid = process_info.hProcess;
CloseHandle(process_info.hThread);
DBG(Cerr << "created process id " << Pid << " in dir: " << cwd << ", cmd: " << cmdcopy.Data() << Endl);
-}
-#endif
-
+}
+#endif
+
void ShellQuoteArg(TString& dst, TStringBuf argument) {
dst.append("\"");
TStringBuf l, r;
@@ -657,14 +657,14 @@ TString TShellCommand::TImpl::GetQuotedCommand() const {
// Don't add unnecessary quotes. It's especially important for the windows with a 32k command line length limit.
if (QuoteArguments && ArgNeedsQuotes(argument)) {
::ShellQuoteArgSp(quoted, argument);
- } else {
+ } else {
quoted.append(" ").append(argument);
- }
- }
- return quoted;
-}
-
-#if defined(_unix_)
+ }
+ }
+ return quoted;
+}
+
+#if defined(_unix_)
void TShellCommand::TImpl::OnFork(TPipes& pipes, sigset_t oldmask, char* const* argv, char* const* envp, const std::function<void()>& afterFork) const {
try {
if (DetachSession) {
@@ -715,7 +715,7 @@ void TShellCommand::TImpl::OnFork(TPipes& pipes, sigset_t oldmask, char* const*
sErr.Release();
sErrNew.Release();
}
-
+
if (WorkDir.size()) {
NFs::SetCurrentWorkingDirectory(WorkDir);
}
@@ -724,8 +724,8 @@ void TShellCommand::TImpl::OnFork(TPipes& pipes, sigset_t oldmask, char* const*
for (int fd = NSystemInfo::MaxOpenFiles(); fd > STDERR_FILENO; --fd) {
fcntl(fd, F_SETFD, FD_CLOEXEC);
}
- }
-
+ }
+
if (!User.Name.empty()) {
ImpersonateUser(User);
}
@@ -750,18 +750,18 @@ void TShellCommand::TImpl::OnFork(TPipes& pipes, sigset_t oldmask, char* const*
Cerr << "Process was not created: "
<< "unknown error" << Endl;
}
-
+
_exit(-1);
-}
-#endif
-
-void TShellCommand::TImpl::Run() {
+}
+#endif
+
+void TShellCommand::TImpl::Run() {
Y_ENSURE(AtomicGet(ExecutionStatus) != SHELL_RUNNING, TStringBuf("Process is already running"));
- // Prepare I/O streams
- CollectedOutput.clear();
- CollectedError.clear();
- TPipes pipes;
-
+ // Prepare I/O streams
+ CollectedOutput.clear();
+ CollectedError.clear();
+ TPipes pipes;
+
if (OutputMode != TShellCommandOptions::HANDLE_INHERIT) {
TRealPipeHandle::Pipe(pipes.OutputPipeFd[0], pipes.OutputPipeFd[1], CloseOnExec);
}
@@ -770,11 +770,11 @@ void TShellCommand::TImpl::Run() {
}
if (InputMode != TShellCommandOptions::HANDLE_INHERIT) {
TRealPipeHandle::Pipe(pipes.InputPipeFd[0], pipes.InputPipeFd[1], CloseOnExec);
- }
-
+ }
+
AtomicSet(ExecutionStatus, SHELL_RUNNING);
-
-#if defined(_unix_)
+
+#if defined(_unix_)
// block all signals to avoid signal handler race after fork()
sigset_t oldmask, newmask;
SigFillSet(&newmask);
@@ -817,12 +817,12 @@ void TShellCommand::TImpl::Run() {
envp.push_back(nullptr);
}
- pid_t pid = fork();
- if (pid == -1) {
+ pid_t pid = fork();
+ if (pid == -1) {
AtomicSet(ExecutionStatus, SHELL_ERROR);
- /// @todo check if pipes are still open
+ /// @todo check if pipes are still open
ythrow TSystemError() << "Cannot fork";
- } else if (pid == 0) { // child
+ } else if (pid == 0) { // child
if (envp.size() != 0) {
OnFork(pipes, oldmask, qargv.data(), envp.data(), FuncAfterFork);
} else {
@@ -833,17 +833,17 @@ void TShellCommand::TImpl::Run() {
if (SigProcMask(SIG_SETMASK, &oldmask, nullptr) != 0) {
ythrow TSystemError() << "Cannot restore signal mask in parent";
}
- }
- Pid = pid;
-#else
- StartProcess(pipes);
-#endif
- pipes.PrepareParents();
-
+ }
+ Pid = pid;
+#else
+ StartProcess(pipes);
+#endif
+ pipes.PrepareParents();
+
if (AtomicGet(ExecutionStatus) != SHELL_RUNNING) {
- return;
+ return;
}
-
+
if (InputMode == TShellCommandOptions::HANDLE_PIPE) {
TFileHandle inputHandle(pipes.InputPipeFd[1].Release());
InputHandle.Swap(inputHandle);
@@ -861,32 +861,32 @@ void TShellCommand::TImpl::Run() {
TProcessInfo* processInfo = new TProcessInfo(this,
pipes.InputPipeFd[1].Release(), pipes.OutputPipeFd[0].Release(), pipes.ErrorPipeFd[0].Release());
- if (AsyncMode) {
+ if (AsyncMode) {
WatchThread = new TThread(&TImpl::WatchProcess, processInfo);
- WatchThread->Start();
- /// @todo wait for child to start its process session (if options.Detach)
+ WatchThread->Start();
+ /// @todo wait for child to start its process session (if options.Detach)
} else {
- Communicate(processInfo);
- }
+ Communicate(processInfo);
+ }
+
+ pipes.ReleaseParents(); // not needed
+}
- pipes.ReleaseParents(); // not needed
-}
-
-void TShellCommand::TImpl::Communicate(TProcessInfo* pi) {
+void TShellCommand::TImpl::Communicate(TProcessInfo* pi) {
THolder<IOutputStream> outputHolder;
IOutputStream* output = pi->Parent->OutputStream;
if (!output) {
- outputHolder.Reset(output = new TStringOutput(pi->Parent->CollectedOutput));
+ outputHolder.Reset(output = new TStringOutput(pi->Parent->CollectedOutput));
}
-
+
THolder<IOutputStream> errorHolder;
IOutputStream* error = pi->Parent->ErrorStream;
if (!error) {
- errorHolder.Reset(error = new TStringOutput(pi->Parent->CollectedError));
+ errorHolder.Reset(error = new TStringOutput(pi->Parent->CollectedError));
}
-
+
IInputStream*& input = pi->Parent->InputStream;
-
+
#if defined(_unix_)
// not really needed, io is done via poll
if (pi->OutputFd.IsOpen()) {
@@ -900,7 +900,7 @@ void TShellCommand::TImpl::Communicate(TProcessInfo* pi) {
}
#endif
- try {
+ try {
#if defined(_win_)
TPipePump pumps[3] = {0};
pumps[0] = {&pi->ErrorFd, error};
@@ -920,172 +920,172 @@ void TShellCommand::TImpl::Communicate(TProcessInfo* pi) {
#else
TBuffer buffer(DATA_BUFFER_SIZE);
TBuffer inputBuffer(DATA_BUFFER_SIZE);
- int bytes;
- int bytesToWrite = 0;
+ int bytes;
+ int bytesToWrite = 0;
char* bufPos = nullptr;
#endif
- TWaitResult waitPidResult;
+ TWaitResult waitPidResult;
TExitStatus status = 0;
-
- while (true) {
- {
+
+ while (true) {
+ {
with_lock (pi->Parent->TerminateMutex) {
if (TerminateIsRequired(pi)) {
return;
}
}
- waitPidResult =
-#if defined(_unix_)
+ waitPidResult =
+#if defined(_unix_)
waitpid(pi->Parent->Pid, &status, WNOHANG);
-#else
+#else
WaitForSingleObject(pi->Parent->Pid /* process_info.hProcess */, pi->Parent->PollDelayMs /* ms */);
Y_UNUSED(status);
-#endif
- // DBG(Cerr << "wait result: " << waitPidResult << Endl);
+#endif
+ // DBG(Cerr << "wait result: " << waitPidResult << Endl);
if (waitPidResult != WAIT_PROCEED) {
- break;
+ break;
}
- }
+ }
/// @todo factor out (poll + wfmo)
#if defined(_unix_)
bool haveIn = false;
bool haveOut = false;
bool haveErr = false;
- if (!input && pi->InputFd.IsOpen()) {
- DBG(Cerr << "closing input stream..." << Endl);
- pi->InputFd.Close();
- }
- if (!output && pi->OutputFd.IsOpen()) {
- DBG(Cerr << "closing output stream..." << Endl);
- pi->OutputFd.Close();
- }
- if (!error && pi->ErrorFd.IsOpen()) {
- DBG(Cerr << "closing error stream..." << Endl);
- pi->ErrorFd.Close();
- }
-
+ if (!input && pi->InputFd.IsOpen()) {
+ DBG(Cerr << "closing input stream..." << Endl);
+ pi->InputFd.Close();
+ }
+ if (!output && pi->OutputFd.IsOpen()) {
+ DBG(Cerr << "closing output stream..." << Endl);
+ pi->OutputFd.Close();
+ }
+ if (!error && pi->ErrorFd.IsOpen()) {
+ DBG(Cerr << "closing error stream..." << Endl);
+ pi->ErrorFd.Close();
+ }
+
if (!input && !output && !error) {
- continue;
+ continue;
}
-
+
struct pollfd fds[] = {
{REALPIPEHANDLE(pi->InputFd), POLLOUT, 0},
{REALPIPEHANDLE(pi->OutputFd), POLLIN, 0},
{REALPIPEHANDLE(pi->ErrorFd), POLLIN, 0}};
- int res;
-
+ int res;
+
if (!input) {
- fds[0].events = 0;
+ fds[0].events = 0;
}
if (!output) {
- fds[1].events = 0;
+ fds[1].events = 0;
}
if (!error) {
- fds[2].events = 0;
+ fds[2].events = 0;
}
-
- res = PollD(fds, 3, TInstant::Now() + TDuration::MilliSeconds(pi->Parent->PollDelayMs));
- // DBG(Cerr << "poll result: " << res << Endl);
+
+ res = PollD(fds, 3, TInstant::Now() + TDuration::MilliSeconds(pi->Parent->PollDelayMs));
+ // DBG(Cerr << "poll result: " << res << Endl);
if (-res == ETIMEDOUT || res == 0) {
- // DBG(Cerr << "poll again..." << Endl);
- continue;
- }
+ // DBG(Cerr << "poll again..." << Endl);
+ continue;
+ }
if (res < 0) {
- ythrow yexception() << "poll failed: " << LastSystemErrorText();
+ ythrow yexception() << "poll failed: " << LastSystemErrorText();
}
-
+
if ((fds[1].revents & POLLIN) == POLLIN) {
- haveOut = true;
+ haveOut = true;
} else if (fds[1].revents & (POLLERR | POLLHUP)) {
output = nullptr;
}
-
+
if ((fds[2].revents & POLLIN) == POLLIN) {
- haveErr = true;
+ haveErr = true;
} else if (fds[2].revents & (POLLERR | POLLHUP)) {
error = nullptr;
}
-
+
if (input && ((fds[0].revents & POLLOUT) == POLLOUT)) {
- haveIn = true;
+ haveIn = true;
}
- if (haveOut) {
- bytes = pi->OutputFd.Read(buffer.Data(), buffer.Capacity());
- DBG(Cerr << "transferred " << bytes << " bytes of output" << Endl);
+ if (haveOut) {
+ bytes = pi->OutputFd.Read(buffer.Data(), buffer.Capacity());
+ DBG(Cerr << "transferred " << bytes << " bytes of output" << Endl);
if (bytes > 0) {
output->Write(buffer.Data(), bytes);
} else {
output = nullptr;
}
- }
- if (haveErr) {
- bytes = pi->ErrorFd.Read(buffer.Data(), buffer.Capacity());
- DBG(Cerr << "transferred " << bytes << " bytes of error" << Endl);
+ }
+ if (haveErr) {
+ bytes = pi->ErrorFd.Read(buffer.Data(), buffer.Capacity());
+ DBG(Cerr << "transferred " << bytes << " bytes of error" << Endl);
if (bytes > 0) {
error->Write(buffer.Data(), bytes);
} else {
error = nullptr;
}
- }
-
- if (haveIn) {
- if (!bytesToWrite) {
- bytesToWrite = input->Read(inputBuffer.Data(), inputBuffer.Capacity());
- if (bytesToWrite == 0) {
+ }
+
+ if (haveIn) {
+ if (!bytesToWrite) {
+ bytesToWrite = input->Read(inputBuffer.Data(), inputBuffer.Capacity());
+ if (bytesToWrite == 0) {
if (AtomicGet(pi->Parent->ShouldCloseInput)) {
input = nullptr;
}
- continue;
+ continue;
}
- bufPos = inputBuffer.Data();
+ bufPos = inputBuffer.Data();
}
-
+
bytes = pi->InputFd.Write(bufPos, bytesToWrite);
- if (bytes > 0) {
- bytesToWrite -= bytes;
- bufPos += bytes;
+ if (bytes > 0) {
+ bytesToWrite -= bytes;
+ bufPos += bytes;
} else {
input = nullptr;
}
-
- DBG(Cerr << "transferred " << bytes << " bytes of input" << Endl);
+
+ DBG(Cerr << "transferred " << bytes << " bytes of input" << Endl);
}
#endif
- }
- DBG(Cerr << "process finished" << Endl);
-
+ }
+ DBG(Cerr << "process finished" << Endl);
+
// What's the reason of process exit.
// We need to set exit code before waiting for input thread
// Otherwise there is no way for input stream provider to discover
// that process has exited and stream shouldn't wait for new data.
- bool cleanExit = false;
+ bool cleanExit = false;
TMaybe<int> processExitCode;
-#if defined(_unix_)
+#if defined(_unix_)
processExitCode = WEXITSTATUS(status);
if (WIFEXITED(status) && processExitCode == 0) {
- cleanExit = true;
+ cleanExit = true;
} else if (WIFSIGNALED(status)) {
processExitCode = -WTERMSIG(status);
}
-#else
- if (waitPidResult == WAIT_OBJECT_0) {
- DWORD exitCode = STILL_ACTIVE;
+#else
+ if (waitPidResult == WAIT_OBJECT_0) {
+ DWORD exitCode = STILL_ACTIVE;
if (!GetExitCodeProcess(pi->Parent->Pid, &exitCode)) {
- ythrow yexception() << "GetExitCodeProcess: " << LastSystemErrorText();
+ ythrow yexception() << "GetExitCodeProcess: " << LastSystemErrorText();
}
- if (exitCode == 0)
- cleanExit = true;
+ if (exitCode == 0)
+ cleanExit = true;
processExitCode = static_cast<int>(exitCode);
- DBG(Cerr << "exit code: " << exitCode << Endl);
+ DBG(Cerr << "exit code: " << exitCode << Endl);
}
-#endif
+#endif
pi->Parent->ExitCode = processExitCode;
- if (cleanExit) {
+ if (cleanExit) {
AtomicSet(pi->Parent->ExecutionStatus, SHELL_FINISHED);
- } else {
+ } else {
AtomicSet(pi->Parent->ExecutionStatus, SHELL_ERROR);
}
@@ -1108,18 +1108,18 @@ void TShellCommand::TImpl::Communicate(TProcessInfo* pi) {
}
#endif
} catch (const yexception& e) {
- // Some error in watch occured, set result to error
+ // Some error in watch occured, set result to error
AtomicSet(pi->Parent->ExecutionStatus, SHELL_INTERNAL_ERROR);
- pi->Parent->InternalError = e.what();
+ pi->Parent->InternalError = e.what();
if (input) {
- pi->InputFd.Close();
+ pi->InputFd.Close();
}
- Cdbg << "shell command internal error: " << pi->Parent->InternalError << Endl;
+ Cdbg << "shell command internal error: " << pi->Parent->InternalError << Endl;
}
- // Now we can safely delete process info struct and other data
- pi->Parent->TerminateFlag = true;
+ // Now we can safely delete process info struct and other data
+ pi->Parent->TerminateFlag = true;
TerminateIsRequired(pi);
-}
+}
TShellCommand::TShellCommand(const TStringBuf cmd, const TList<TString>& args, const TShellCommandOptions& options,
const TString& workdir)
diff --git a/util/system/shellcommand.h b/util/system/shellcommand.h
index 4304202c68..8730627fe5 100644
--- a/util/system/shellcommand.h
+++ b/util/system/shellcommand.h
@@ -88,7 +88,7 @@ public:
* @note in default close-on-exec mode is off.
* @return self
*/
- inline TShellCommandOptions& SetCloseAllFdsOnExec(bool closeAllFdsOnExec) {
+ inline TShellCommandOptions& SetCloseAllFdsOnExec(bool closeAllFdsOnExec) {
CloseAllFdsOnExec = closeAllFdsOnExec;
return *this;
}
@@ -98,30 +98,30 @@ public:
* in separate thread, and control will be returned immediately
*
* @param async true if asynchonous mode is needed
- * @note in default async mode launcher will need 100% cpu for rapid process termination
+ * @note in default async mode launcher will need 100% cpu for rapid process termination
* @return self
*/
- inline TShellCommandOptions& SetAsync(bool async) {
+ inline TShellCommandOptions& SetAsync(bool async) {
AsyncMode = async;
- if (AsyncMode)
- PollDelayMs = 0;
+ if (AsyncMode)
+ PollDelayMs = 0;
+ return *this;
+ }
+
+ /**
+ * @brief specify delay for process controlling loop
+ * @param ms number of milliseconds to poll for
+ * @note for synchronous process default of 1s should generally fit
+ * for async process default is no latency and that consumes 100% one cpu
+ * SetAsync(true) will reset this delay to 0, so call this method after
+ * @return self
+ */
+ inline TShellCommandOptions& SetLatency(size_t ms) {
+ PollDelayMs = ms;
return *this;
}
/**
- * @brief specify delay for process controlling loop
- * @param ms number of milliseconds to poll for
- * @note for synchronous process default of 1s should generally fit
- * for async process default is no latency and that consumes 100% one cpu
- * SetAsync(true) will reset this delay to 0, so call this method after
- * @return self
- */
- inline TShellCommandOptions& SetLatency(size_t ms) {
- PollDelayMs = ms;
- return *this;
- }
-
- /**
* @brief set the stream, which is input fetched from
*
* @param stream Pointer to stream.
@@ -168,17 +168,17 @@ public:
}
/**
- * @brief set if Finish() should be called on user-supplied streams
- * if process is run in async mode Finish will be called in process' thread
- * @param val if Finish() should be called
- * @return self
- */
- inline TShellCommandOptions& SetCloseStreams(bool val) {
- CloseStreams = val;
- return *this;
- }
-
- /**
+ * @brief set if Finish() should be called on user-supplied streams
+ * if process is run in async mode Finish will be called in process' thread
+ * @param val if Finish() should be called
+ * @return self
+ */
+ inline TShellCommandOptions& SetCloseStreams(bool val) {
+ CloseStreams = val;
+ return *this;
+ }
+
+ /**
* @brief set if input stream should be closed after all data is read
* call SetCloseInput(false) for interactive process
* @param val if input stream should be closed
@@ -190,21 +190,21 @@ public:
}
/**
- * @brief set if command should be interpreted by OS shell (/bin/sh or cmd.exe)
- * shell is enabled by default
- * call SetUseShell(false) for command to be sent to OS verbatim
- * @note shell operators > < | && || will not work if this option is off
- * @param useShell if command should be run in shell
- * @return self
- */
- inline TShellCommandOptions& SetUseShell(bool useShell) {
- UseShell = useShell;
- if (!useShell)
- QuoteArguments = false;
- return *this;
- }
-
- /**
+ * @brief set if command should be interpreted by OS shell (/bin/sh or cmd.exe)
+ * shell is enabled by default
+ * call SetUseShell(false) for command to be sent to OS verbatim
+ * @note shell operators > < | && || will not work if this option is off
+ * @param useShell if command should be run in shell
+ * @return self
+ */
+ inline TShellCommandOptions& SetUseShell(bool useShell) {
+ UseShell = useShell;
+ if (!useShell)
+ QuoteArguments = false;
+ return *this;
+ }
+
+ /**
* @brief set if the arguments should be wrapped in quotes.
* Please, note that this option makes no difference between
* real arguments and shell syntax, so if you execute something
@@ -214,28 +214,28 @@ public:
* which will never end successfully.
* By default, this option is turned on.
*
- * @note arguments will only be quoted if shell is used
+ * @note arguments will only be quoted if shell is used
* @param quote if the arguments should be quoted
*
* @return self
*/
- inline TShellCommandOptions& SetQuoteArguments(bool quote) {
+ inline TShellCommandOptions& SetQuoteArguments(bool quote) {
QuoteArguments = quote;
return *this;
}
- /**
- * @brief set to run command in new session
- * @note set this option to off to deliver parent's signals to command as well
- * @note currently ignored on windows
- * @param detach if command should be run in new session
- * @return self
- */
- inline TShellCommandOptions& SetDetachSession(bool detach) {
- DetachSession = detach;
- return *this;
- }
-
+ /**
+ * @brief set to run command in new session
+ * @note set this option to off to deliver parent's signals to command as well
+ * @note currently ignored on windows
+ * @param detach if command should be run in new session
+ * @return self
+ */
+ inline TShellCommandOptions& SetDetachSession(bool detach) {
+ DetachSession = detach;
+ return *this;
+ }
+
/**
* @brief specifies pure function to be called in the child process after fork, before calling execve
* @note currently ignored on windows
@@ -295,7 +295,7 @@ public:
return *this;
}
-public:
+public:
bool ClearSignalMask = false;
bool CloseAllFdsOnExec = false;
bool AsyncMode = false;
@@ -309,10 +309,10 @@ public:
EHandleMode OutputMode = HANDLE_STREAM;
EHandleMode ErrorMode = HANDLE_STREAM;
- /// @todo more options
- // bool SearchPath // search exe name in $PATH
- // bool UnicodeConsole
- // bool EmulateConsole // provide isatty == true
+ /// @todo more options
+ // bool SearchPath // search exe name in $PATH
+ // bool UnicodeConsole
+ // bool EmulateConsole // provide isatty == true
/// @todo command's stdin should be exposet as IOutputStream to support dialogue
IInputStream* InputStream;
IOutputStream* OutputStream;
@@ -320,8 +320,8 @@ public:
TUserOptions User;
THashMap<TString, TString> Environment;
int Nice = 0;
-
- static const size_t DefaultSyncPollDelay = 1000; // ms
+
+ static const size_t DefaultSyncPollDelay = 1000; // ms
std::function<void()> FuncAfterFork = {};
};
@@ -349,7 +349,7 @@ public:
* @param cmd binary name
* @param args arguments list
* @param options execution options
- * @todo store entire options structure
+ * @todo store entire options structure
*/
TShellCommand(const TStringBuf cmd, const TList<TString>& args, const TShellCommandOptions& options = TShellCommandOptions(),
const TString& workdir = TString());
@@ -440,7 +440,7 @@ public:
*
* @return self
*/
- TShellCommand& Run();
+ TShellCommand& Run();
/**
* @brief terminate the execution
@@ -448,14 +448,14 @@ public:
*
* @return self
*/
- TShellCommand& Terminate();
+ TShellCommand& Terminate();
/**
* @brief wait until the execution is finished
*
* @return self
*/
- TShellCommand& Wait();
+ TShellCommand& Wait();
/**
* @brief close process' stdin
diff --git a/util/system/shellcommand_ut.cpp b/util/system/shellcommand_ut.cpp
index 5daa23e29f..9d849279d2 100644
--- a/util/system/shellcommand_ut.cpp
+++ b/util/system/shellcommand_ut.cpp
@@ -17,16 +17,16 @@
#include <util/string/strip.h>
#include <util/folder/tempdir.h>
-#if defined(_win_)
+#if defined(_win_)
#define NL "\r\n"
const char catCommand[] = "sort"; // not really cat but ok
const size_t textSize = 1;
-#else
+#else
#define NL "\n"
const char catCommand[] = "/bin/cat";
const size_t textSize = 20000;
-#endif
-
+#endif
+
class TGuardedStringStream: public IInputStream, public IOutputStream {
public:
TGuardedStringStream() {
@@ -72,18 +72,18 @@ Y_UNIT_TEST_SUITE(TShellQuoteTest) {
Y_UNIT_TEST_SUITE(TShellCommandTest) {
Y_UNIT_TEST(TestNoQuotes) {
- TShellCommandOptions options;
- options.SetQuoteArguments(false);
- TShellCommand cmd("echo hello");
- cmd.Run();
- UNIT_ASSERT_VALUES_EQUAL(cmd.GetError(), "");
- UNIT_ASSERT_VALUES_EQUAL(cmd.GetOutput(), "hello" NL);
- UNIT_ASSERT(TShellCommand::SHELL_FINISHED == cmd.GetStatus());
+ TShellCommandOptions options;
+ options.SetQuoteArguments(false);
+ TShellCommand cmd("echo hello");
+ cmd.Run();
+ UNIT_ASSERT_VALUES_EQUAL(cmd.GetError(), "");
+ UNIT_ASSERT_VALUES_EQUAL(cmd.GetOutput(), "hello" NL);
+ UNIT_ASSERT(TShellCommand::SHELL_FINISHED == cmd.GetStatus());
UNIT_ASSERT(cmd.GetExitCode().Defined() && 0 == cmd.GetExitCode());
UNIT_ASSERT_VALUES_EQUAL(cmd.GetQuotedCommand(), "echo hello");
- }
-
+ }
+
Y_UNIT_TEST(TestOnlyNecessaryQuotes) {
TShellCommandOptions options;
options.SetQuoteArguments(true);
@@ -98,75 +98,75 @@ Y_UNIT_TEST_SUITE(TShellCommandTest) {
}
Y_UNIT_TEST(TestRun) {
- TShellCommand cmd("echo");
- cmd << "hello";
- cmd.Run();
- UNIT_ASSERT_VALUES_EQUAL(cmd.GetError(), "");
-#if defined(_win_)
- UNIT_ASSERT_VALUES_EQUAL(cmd.GetOutput(), "\"hello\"\r\n");
-#else
- UNIT_ASSERT_VALUES_EQUAL(cmd.GetOutput(), "hello\n");
-#endif
- UNIT_ASSERT(TShellCommand::SHELL_FINISHED == cmd.GetStatus());
+ TShellCommand cmd("echo");
+ cmd << "hello";
+ cmd.Run();
+ UNIT_ASSERT_VALUES_EQUAL(cmd.GetError(), "");
+#if defined(_win_)
+ UNIT_ASSERT_VALUES_EQUAL(cmd.GetOutput(), "\"hello\"\r\n");
+#else
+ UNIT_ASSERT_VALUES_EQUAL(cmd.GetOutput(), "hello\n");
+#endif
+ UNIT_ASSERT(TShellCommand::SHELL_FINISHED == cmd.GetStatus());
UNIT_ASSERT(cmd.GetExitCode().Defined() && 0 == cmd.GetExitCode());
- }
- // running with no shell is not implemented for win
- // there should be no problem with it as long as SearchPath is on
+ }
+ // running with no shell is not implemented for win
+ // there should be no problem with it as long as SearchPath is on
Y_UNIT_TEST(TestNoShell) {
-#if defined(_win_)
- const char dir[] = "dir";
-#else
- const char dir[] = "ls";
-#endif
-
+#if defined(_win_)
+ const char dir[] = "dir";
+#else
+ const char dir[] = "ls";
+#endif
+
TShellCommandOptions options;
- options.SetQuoteArguments(false);
-
- {
- options.SetUseShell(false);
- TShellCommand cmd(dir, options);
+ options.SetQuoteArguments(false);
+
+ {
+ options.SetUseShell(false);
+ TShellCommand cmd(dir, options);
cmd << "|"
<< "sort";
-
- cmd.Run();
- UNIT_ASSERT(TShellCommand::SHELL_ERROR == cmd.GetStatus());
+
+ cmd.Run();
+ UNIT_ASSERT(TShellCommand::SHELL_ERROR == cmd.GetStatus());
UNIT_ASSERT(cmd.GetExitCode().Defined() && 0 != cmd.GetExitCode());
- }
- {
- options.SetUseShell(true);
- TShellCommand cmd(dir, options);
+ }
+ {
+ options.SetUseShell(true);
+ TShellCommand cmd(dir, options);
cmd << "|"
<< "sort";
- cmd.Run();
- UNIT_ASSERT(TShellCommand::SHELL_FINISHED == cmd.GetStatus());
+ cmd.Run();
+ UNIT_ASSERT(TShellCommand::SHELL_FINISHED == cmd.GetStatus());
UNIT_ASSERT_VALUES_EQUAL(cmd.GetError().size(), 0u);
UNIT_ASSERT(cmd.GetExitCode().Defined() && 0 == cmd.GetExitCode());
- }
- }
+ }
+ }
Y_UNIT_TEST(TestAsyncRun) {
- TShellCommandOptions options;
+ TShellCommandOptions options;
options.SetAsync(true);
-#if defined(_win_)
- // fails with weird error "Input redirection is not supported"
- // TShellCommand cmd("sleep", options);
- // cmd << "3";
- TShellCommand cmd("ping 1.1.1.1 -n 1 -w 2000", options);
-#else
- TShellCommand cmd("sleep", options);
- cmd << "2";
-#endif
+#if defined(_win_)
+ // fails with weird error "Input redirection is not supported"
+ // TShellCommand cmd("sleep", options);
+ // cmd << "3";
+ TShellCommand cmd("ping 1.1.1.1 -n 1 -w 2000", options);
+#else
+ TShellCommand cmd("sleep", options);
+ cmd << "2";
+#endif
UNIT_ASSERT(TShellCommand::SHELL_NONE == cmd.GetStatus());
cmd.Run();
sleep(1);
UNIT_ASSERT(TShellCommand::SHELL_RUNNING == cmd.GetStatus());
cmd.Wait();
- UNIT_ASSERT(TShellCommand::SHELL_RUNNING != cmd.GetStatus());
- UNIT_ASSERT_VALUES_EQUAL(cmd.GetError(), "");
-#if !defined(_win_)
+ UNIT_ASSERT(TShellCommand::SHELL_RUNNING != cmd.GetStatus());
+ UNIT_ASSERT_VALUES_EQUAL(cmd.GetError(), "");
+#if !defined(_win_)
UNIT_ASSERT(TShellCommand::SHELL_FINISHED == cmd.GetStatus());
UNIT_ASSERT_VALUES_EQUAL(cmd.GetOutput().size(), 0u);
UNIT_ASSERT(cmd.GetExitCode().Defined() && 0 == cmd.GetExitCode());
-#endif
+#endif
}
Y_UNIT_TEST(TestQuotes) {
TShellCommandOptions options;
@@ -174,15 +174,15 @@ Y_UNIT_TEST_SUITE(TShellCommandTest) {
TString output;
TStringOutput outputStream(output);
options.SetOutputStream(&outputStream);
- TShellCommand cmd("echo", options);
+ TShellCommand cmd("echo", options);
cmd << input;
cmd.Run().Wait();
output = StripString(output);
-#if defined(_win_)
- UNIT_ASSERT_VALUES_EQUAL("\"a\\\"a a\"", output);
-#else
+#if defined(_win_)
+ UNIT_ASSERT_VALUES_EQUAL("\"a\\\"a a\"", output);
+#else
UNIT_ASSERT_VALUES_EQUAL(input, output);
-#endif
+#endif
UNIT_ASSERT_VALUES_EQUAL(cmd.GetError().size(), 0u);
}
Y_UNIT_TEST(TestRunNonexistent) {
@@ -199,13 +199,13 @@ Y_UNIT_TEST_SUITE(TShellCommandTest) {
UNIT_ASSERT_VALUES_UNEQUAL(cmd.GetError().size(), 0u);
UNIT_ASSERT(cmd.GetExitCode().Defined() && 2 == cmd.GetExitCode());
}
- // 'type con' and 'copy con con' want real console, not stdin, use sort
+ // 'type con' and 'copy con con' want real console, not stdin, use sort
Y_UNIT_TEST(TestInput) {
TShellCommandOptions options;
TString input = (TString("a") * 2000).append(NL) * textSize;
TStringInput inputStream(input);
options.SetInputStream(&inputStream);
- TShellCommand cmd(catCommand, options);
+ TShellCommand cmd(catCommand, options);
cmd.Run().Wait();
UNIT_ASSERT_VALUES_EQUAL(input, cmd.GetOutput());
UNIT_ASSERT_VALUES_EQUAL(cmd.GetError().size(), 0u);
@@ -218,28 +218,28 @@ Y_UNIT_TEST_SUITE(TShellCommandTest) {
TString output;
TStringOutput outputStream(output);
options.SetOutputStream(&outputStream);
- TShellCommand cmd(catCommand, options);
+ TShellCommand cmd(catCommand, options);
cmd.Run().Wait();
UNIT_ASSERT_VALUES_EQUAL(input, output);
UNIT_ASSERT_VALUES_EQUAL(cmd.GetError().size(), 0u);
}
Y_UNIT_TEST(TestIO) {
- // descriptive test: use all options
- TShellCommandOptions options;
+ // descriptive test: use all options
+ TShellCommandOptions options;
options.SetAsync(true);
- options.SetQuoteArguments(false);
- options.SetLatency(10);
+ options.SetQuoteArguments(false);
+ options.SetLatency(10);
options.SetClearSignalMask(true);
- options.SetCloseAllFdsOnExec(true);
+ options.SetCloseAllFdsOnExec(true);
options.SetCloseInput(false);
TGuardedStringStream write;
- options.SetInputStream(&write);
+ options.SetInputStream(&write);
TGuardedStringStream read;
- options.SetOutputStream(&read);
- options.SetUseShell(true);
-
+ options.SetOutputStream(&read);
+ options.SetUseShell(true);
+
TShellCommand cmd("cat", options);
- cmd.Run();
+ cmd.Run();
write << "alpha" << NL;
while (read.Str() != "alpha" NL) {
@@ -252,50 +252,50 @@ Y_UNIT_TEST_SUITE(TShellCommandTest) {
}
write << "zeta" << NL;
- cmd.CloseInput();
- cmd.Wait();
-
- UNIT_ASSERT_VALUES_EQUAL(cmd.GetError(), "");
- UNIT_ASSERT(TShellCommand::SHELL_FINISHED == cmd.GetStatus());
- UNIT_ASSERT_VALUES_EQUAL(read.Str(), "alpha" NL "omega" NL "zeta" NL);
+ cmd.CloseInput();
+ cmd.Wait();
+
+ UNIT_ASSERT_VALUES_EQUAL(cmd.GetError(), "");
+ UNIT_ASSERT(TShellCommand::SHELL_FINISHED == cmd.GetStatus());
+ UNIT_ASSERT_VALUES_EQUAL(read.Str(), "alpha" NL "omega" NL "zeta" NL);
UNIT_ASSERT(cmd.GetExitCode().Defined() && 0 == cmd.GetExitCode());
- }
+ }
Y_UNIT_TEST(TestStreamClose) {
struct TStream: public IOutputStream {
- size_t NumCloses = 0;
+ size_t NumCloses = 0;
void DoWrite(const void* buf, size_t len) override {
Y_UNUSED(buf);
Y_UNUSED(len);
}
void DoFinish() override {
- ++NumCloses;
- }
- } stream;
-
- auto options1 = TShellCommandOptions().SetCloseStreams(false).SetOutputStream(&stream).SetErrorStream(&stream);
- TShellCommand("echo hello", options1).Run().Wait();
- UNIT_ASSERT_VALUES_EQUAL(stream.NumCloses, 0);
-
- auto options = TShellCommandOptions().SetCloseStreams(true).SetOutputStream(&stream).SetErrorStream(&stream);
- TShellCommand("echo hello", options).Run().Wait();
+ ++NumCloses;
+ }
+ } stream;
+
+ auto options1 = TShellCommandOptions().SetCloseStreams(false).SetOutputStream(&stream).SetErrorStream(&stream);
+ TShellCommand("echo hello", options1).Run().Wait();
+ UNIT_ASSERT_VALUES_EQUAL(stream.NumCloses, 0);
+
+ auto options = TShellCommandOptions().SetCloseStreams(true).SetOutputStream(&stream).SetErrorStream(&stream);
+ TShellCommand("echo hello", options).Run().Wait();
UNIT_ASSERT_VALUES_EQUAL(stream.NumCloses, 2);
- }
+ }
Y_UNIT_TEST(TestInterruptSimple) {
- TShellCommandOptions options;
- options.SetAsync(true);
+ TShellCommandOptions options;
+ options.SetAsync(true);
options.SetCloseInput(false);
TGuardedStringStream write;
options.SetInputStream(&write); // set input stream that will be waited by cat
- TShellCommand cmd(catCommand, options);
- cmd.Run();
+ TShellCommand cmd(catCommand, options);
+ cmd.Run();
sleep(1);
- UNIT_ASSERT(TShellCommand::SHELL_RUNNING == cmd.GetStatus());
- cmd.Terminate();
- cmd.Wait();
- UNIT_ASSERT(TShellCommand::SHELL_RUNNING != cmd.GetStatus());
- }
-#if !defined(_win_)
- // this ut is unix-only, port to win using %TEMP%
+ UNIT_ASSERT(TShellCommand::SHELL_RUNNING == cmd.GetStatus());
+ cmd.Terminate();
+ cmd.Wait();
+ UNIT_ASSERT(TShellCommand::SHELL_RUNNING != cmd.GetStatus());
+ }
+#if !defined(_win_)
+ // this ut is unix-only, port to win using %TEMP%
Y_UNIT_TEST(TestInterrupt) {
TString tmpfile = TString("shellcommand_ut.interrupt.") + ToString(RandomNumber<ui32>());
@@ -379,7 +379,7 @@ Y_UNIT_TEST_SUITE(TShellCommandTest) {
UNIT_ASSERT(TShellCommand::SHELL_FINISHED == cmd.GetStatus());
UNIT_ASSERT(cmd.GetExitCode().Defined() && 0 == cmd.GetExitCode());
}
-#endif
+#endif
Y_UNIT_TEST(TestInternalError) {
TString input = (TString("a") * 2000).append("\n");
TStringInput inputStream(input);
@@ -387,7 +387,7 @@ Y_UNIT_TEST_SUITE(TShellCommandTest) {
TShellCommandOptions options;
options.SetInputStream(&inputStream);
options.SetOutputStream(&outputStream);
- TShellCommand cmd(catCommand, options);
+ TShellCommand cmd(catCommand, options);
cmd.Run().Wait();
UNIT_ASSERT(TShellCommand::SHELL_INTERNAL_ERROR == cmd.GetStatus());
UNIT_ASSERT_VALUES_UNEQUAL(cmd.GetInternalError().size(), 0u);