ALib C++ Framework
by
Library Version: 2605 R0
Documentation generated by doxygen
Loading...
Searching...
No Matches
shellcommand.hpp
Go to the documentation of this file.
1//==================================================================================================
2/// \file
3/// This header-file is part of module \alib_system of the \aliblong.
4///
5/// Copyright 2013-2026 A-Worx GmbH, Germany.
6/// Published under #"mainpage_license".
7//==================================================================================================
8ALIB_EXPORT namespace alib { namespace system {
9
10//==================================================================================================
11/// #"%TShellCommand" provides a lightweight interface for executing external shell commands and
12/// capturing their output. It reads from the command’s standard output stream and accumulates
13/// the text in a buffer.
14/// The captured output is split into individual lines and stored in the class’s underlying
15/// #"TStringVector";StringVector".
16///
17/// \par Usage options:
18/// - Pure static usage: Call the static #"Run(strings::TAString)" method with a target buffer
19/// and an optional vector for collecting lines.
20/// The new output is appended to the provided buffer; if a vector is
21/// supplied, the newly captured portion is tokenized into lines and added to it.
22/// - Instance-based usage: Create an instance and call its non-static #"Run(lang::CurrentData)"
23/// method.
24/// By default, this clears the instance’s buffer and line vector before executing.
25/// To preserve the current content and append new output, pass
26/// #"CurrentData::Keep".
27///
28/// Method #"Run(lang::CurrentData)" can be sequentially invoked multiple times to aggregate the
29/// output of several shell commands.
30/// With the static #"Run(strings::TAString)" variant, the same buffer and vector is to be passed
31/// to continue appending.
32/// With the non-static #"Run(lang::CurrentData)", the parameter \p{keepCurrent} controls whether
33/// existing content is retained or not.
34///
35/// @see This is a very simple wrapper type. For example, no blocking or timely shell commands
36/// can be handled. While simple, relient commands can be invoked with this class, more
37/// complicated cases should be handled with alternatives, as:
38/// - \https{boost.process,www.boost.org/doc/libs/latest/libs/process/doc/html/index.html}
39/// - \https{POCO Process,docs.pocoproject.org/current/Poco.Process.html}, or
40/// - \https{cpp-subprocess,github.com/arun11299/cpp-subprocess}.
41//==================================================================================================
42template<typename TAllocator= lang::HeapAllocator>
43struct TShellCommand : strings::util::TStringVector<nchar, TAllocator> {
44 /// The allocator type that \p{TAllocator} specifies.
45 using AllocatorType= TAllocator;
46
47 /// The vector type that \p{TAllocator} specifies.
49
50 /// The vector type that \p{TAllocator} specifies.
52
53
54 /// The input buffer, collecting the output of the invoked shell command(s).
56
57 /// Default constructor. Usable with type #"HeapAllocator".
58 TShellCommand() : strings::util::TStringVector<nchar, TAllocator>{} {}
59
60 /// Destructor.
61 /// Deletes all strings in the vector of output lines.
63 for( auto& str : *this ) {
64 vectorBase::get_allocator().AIF().DeleteArray(const_cast<nchar*>(str.Buffer()),
65 str.Length());
66 } }
67
68 /// Constructor taking an allocator.
69 /// @param ma The allocator to use.
71 : strings::util::TStringVector<nchar, TAllocator>{ma}
72 , ReadBuffer{ma} {}
73
74
75 /// Executes the given command-line by invoking the static variant of this method
76 /// passing member #"ReadBuffer" and the inherited string vector (<c>*this</c>).
77 /// @param cmd The command to execute.
78 /// @param keepData Denotes whether any prior results are kept or not.
79 /// @return The exit-code of the command. If -1 errno is set.
81 if ( keepData == lang::CurrentData::Clear ) {
82 ReadBuffer.Reset();
83 this->clear();
84 }
85 return Run( cmd, ReadBuffer, this );
86 }
87
88 /// Executes the given command-line.<br>
89 /// The given \p{readBuffer} and vector \p{lines} are \b not reset. Instead, the command result
90 /// is appended to both. If this is not wanted, the method #"TAString::Reset" has to be
91 /// called on the given \p{readBuffer} and the method \c std::vector::clear on the given
92 /// \p{lines} instance, before calling this method.
93 /// @param cmd The command to execute.
94 /// @param readBuffer A string buffer to receive the command's output.
95 /// @param lines An optional pointer to a vector of strings, which receives the lines
96 /// of the output text.
97 /// @return The exit-code of the command. If -1 errno is set.
98 static int Run( const NCString& cmd,
101
102 constexpr int initialBufferSize = 4096;
103 constexpr int readSize = 1024 - 1;
104
105 integer origBufferLen= readBuffer.Length();
106
107 #if !defined(_WIN32) // true POSIX
108 FILE* pipe= popen(cmd, "r");
109 #elif defined (_WIN32)
110 FILE* pipe= _popen(cmd, "r");
111 #else
112 #pragma message ("Unknown Platform in file: " __FILE__ )
113 #endif
114 if (!pipe)
115 return -1;
116
117 readBuffer.EnsureRemainingCapacity(initialBufferSize);
118 for (;;) {
119 readBuffer.EnsureRemainingCapacity(readSize);
120 if (!fgets(readBuffer.VBuffer()+readBuffer.Length(), readSize, pipe))
121 break;
122 integer len= readBuffer.Length();
123 len= readBuffer.DetectLength(len);
124 readBuffer.TrimEnd("\r\n"); // remove also '\r' in case different platform
125 if ( readBuffer.Length() < len )
126 readBuffer.Append(NNEW_LINE);
127 }
128
129 #if !defined(_WIN32) // true POSIX
130 int resultCode = pclose(pipe);
131 if (resultCode != -1) {
132 // 0..255
133 if (WIFEXITED(resultCode)) resultCode= WEXITSTATUS(resultCode);
134
135 // Conventional shell-style encoding: 128 + signal number
136 else if (WIFSIGNALED(resultCode)) resultCode= 128 + WTERMSIG(resultCode);
137 }
138 #elif defined (_WIN32) // win POSIX
139 int resultCode = _pclose(pipe);
140 #else
141 #pragma message ("Unknown Platform in file: " __FILE__ )
142 #endif
143
144 // prevent tokenizer to add an empty line. If after TimEnd() below, the string is emtpy,
145 // one empty line will be added.
146 if ( readBuffer.IsEmpty() )
147 return resultCode;
148
149 // trim whitespace/newlines
150 readBuffer.TrimEnd();
151
152 // Split to lines
153 if (lines) {
154 TokenizerN tknzr(NString(readBuffer.Buffer() + origBufferLen,
155 readBuffer.Length() - origBufferLen ) , '\n');
156 while (tknzr.HasNext())
157 lines->Add(tknzr.Next(lang::Whitespaces::Keep).TrimEnd());
158 }
159 return resultCode;
160 }
161}; // class ShellCommand
162
163#if !DOXYGEN
165 const NCString&,
168
169#if ALIB_MONOMEM
171 const NCString&,
174#endif
175#endif // !DOXYGEN
176
177} // namespace [ system]
178
179/// Type alias in namespace #"%alib".
181
182#if ALIB_MONOMEM
183/// Type alias in namespace #"%alib".
185#endif
186
187} // namespace [alib]
#define ALIB_DLL
#define ALIB_EXPORT
integer DetectLength(integer offset=0)
Definition tastring.hpp:714
TChar * VBuffer() const
Definition tastring.hpp:631
TAString & Append(const TCharSrc *src, integer srcLength)
Definition tastring.hpp:782
void EnsureRemainingCapacity(integer spaceNeeded)
Definition tastring.hpp:555
TAString & TrimEnd(const TCString< TChar > &trimChars=CStringConstantsTraits< TChar >::DefaultWhitespaces())
constexpr integer Length() const
Definition string.hpp:300
constexpr bool IsEmpty() const
Definition string.hpp:349
constexpr const TChar * Buffer() const
Definition string.hpp:295
TSubstring & TrimEnd(const TCString< TChar > &whiteSpaces=CStringConstantsTraits< TChar >::DefaultWhitespaces())
Definition substring.hpp:89
std::vector< TString< nchar >, lang::StdAllocator< TString< nchar >, TAllocator > > vectorBase
Definition vector.hpp:50
TSubstring< TChar > & Next(lang::Whitespaces trimming=lang::Whitespaces::Trim, TChar newDelim='\0')
Definition tokenizer.cpp:4
@ Clear
Chooses to clear existing data.
@ Keep
Keep whitespaces in string.
Definition alox.cpp:14
strings::TString< nchar > NString
Type alias in namespace #"%alib".
Definition string.hpp:2174
strings::TCString< nchar > NCString
Type alias in namespace #"%alib".
Definition cstring.hpp:408
system::TShellCommand< MonoAllocator > ShellCommandMA
Type alias in namespace #"%alib".
lang::integer integer
Type alias in namespace #"%alib".
Definition integers.hpp:149
system::TShellCommand< lang::HeapAllocator > ShellCommand
Type alias in namespace #"%alib".
constexpr NCString NNEW_LINE
A zero-terminated string containing the new-line character sequence.
Definition cstring.hpp:545
characters::nchar nchar
Type alias in namespace #"%alib".
strings::util::TTokenizer< nchar > TokenizerN
Type alias in namespace #"%alib".
strings::TAString< nchar, AllocatorType > ReadBuffer
int Run(const NCString &cmd, lang::CurrentData keepData=lang::CurrentData::Clear)
TShellCommand()
Default constructor. Usable with type #"HeapAllocator".
TAllocator AllocatorType
The allocator type that TAllocator specifies.
strings::util::TStringVector< nchar, TAllocator > base
The vector type that TAllocator specifies.
base::vectorBase vectorBase
The vector type that TAllocator specifies.
TShellCommand(AllocatorType &ma)
static int Run(const NCString &cmd, strings::TAString< nchar, AllocatorType > &readBuffer, strings::util::TStringVector< nchar, TAllocator > *lines=nullptr)