forked from dmikushin/cpp-command-output
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcommand.h
123 lines (118 loc) · 4.56 KB
/
command.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
#ifndef COMMAND_H
#define COMMAND_H
// Copyright (C) 2021 Remy van Elst
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#include <array>
#include <ostream>
#include <string>
#include <cstdio>
#include <vector>
namespace raymii {
struct CommandResult {
std::string output;
std::vector<std::string> outputvector
int exitstatus;
friend std::ostream &operator<<(std::ostream &os, const CommandResult &result) {
os << "command exitstatus: " << result.exitstatus << " output: " << result.output;
return os;
}
bool operator==(const CommandResult &rhs) const {
return output == rhs.output &&
outputvector == rhs.outputvector &&
exitstatus == rhs.exitstatus;
}
bool operator!=(const CommandResult &rhs) const {
return !(rhs == *this);
}
};
class Command {
public:
/**
* Execute system command and get STDOUT result.
* Regular system() only gives back exit status, this gives back output as well.
* @param command system command to execute
* @return commandResult containing STDOUT (not stderr) output & exitstatus
* of command. Empty if command failed (or has no output). If you want stderr,
* use shell redirection (2&>1).
*/
static CommandResult exec(const std::string &command) {
int exitcode = 0;
std::array<char, 8192> buffer{};
std::string result;
#ifdef _WIN32
#define popen _popen
#define pclose _pclose
#define WEXITSTATUS
#endif
FILE *pipe = popen(command.c_str(), "r");
if (pipe == nullptr) {
throw std::runtime_error("popen() failed!");
}
try {
std::size_t bytesread;
while ((bytesread = std::fread(buffer.data(), sizeof(buffer.at(0)), sizeof(buffer), pipe)) != 0) {
result += std::string(buffer.data(), bytesread);
}
} catch (...) {
pclose(pipe);
throw;
}
// Workaround "error: cannot take the address of an rvalue of type 'int'" on MacOS
// see e.g. https://github.com/BestImageViewer/geeqie/commit/75c7df8b96592e10f7936dc1a28983be4089578c
int res = pclose(pipe);
exitcode = WEXITSTATUS(res);
return CommandResult{result, exitcode};
}
// Only for reference in the article. Use regular ::exec.
static CommandResult execFgets(const std::string &command) {
int exitcode = 0;
std::array<char, 8192> buffer{};
std::string result;
std::vector<std::string> resultvector;
#ifdef _WIN32
#define popen _popen
#define pclose _pclose
#define WEXITSTATUS
#endif
FILE *pipe = popen(command.c_str(), "r");
if (pipe == nullptr) {
throw std::runtime_error("popen() failed!");
}
try {
while (std::fgets(buffer.data(), buffer.size(), pipe) != nullptr) {
result += buffer.data();
}
std::string row;
for (char c: result) {
if (c == '\n'){
resultvector.push_back(row);
row.clear();
}else{
row += c;
}
}
} catch (...) {
pclose(pipe);
throw;
}
// Workaround "error: cannot take the address of an rvalue of type 'int'" on MacOS
// see e.g. https://github.com/BestImageViewer/geeqie/commit/75c7df8b96592e10f7936dc1a28983be4089578c
int res = pclose(pipe);
exitcode = WEXITSTATUS(res);
return CommandResult{result, resultvector, exitcode};
}
};
}// namespace raymii
#endif//COMMAND_H