Run a command, capture its stdout, return {exit_code, output_lines}.
| 98 | |
| 99 | /// Run a command, capture its stdout, return {exit_code, output_lines}. |
| 100 | std::pair<int, std::vector<std::string>> captureCommand(const std::vector<std::string> & args) |
| 101 | { |
| 102 | int pipefd[2]; |
| 103 | if (pipe(pipefd) < 0) |
| 104 | return {-1, {}}; |
| 105 | |
| 106 | pid_t pid = fork(); |
| 107 | if (pid < 0) |
| 108 | { |
| 109 | (void)close(pipefd[0]); |
| 110 | (void)close(pipefd[1]); |
| 111 | return {-1, {}}; |
| 112 | } |
| 113 | |
| 114 | if (pid == 0) |
| 115 | { |
| 116 | (void)close(pipefd[0]); |
| 117 | if (dup2(pipefd[1], STDOUT_FILENO) < 0) |
| 118 | _exit(127); |
| 119 | (void)close(pipefd[1]); |
| 120 | |
| 121 | /// Suppress stderr to avoid noise from --try extractions. |
| 122 | int devnull = open("/dev/null", O_WRONLY); |
| 123 | if (devnull >= 0) |
| 124 | { |
| 125 | (void)dup2(devnull, STDERR_FILENO); |
| 126 | (void)close(devnull); |
| 127 | } |
| 128 | |
| 129 | auto argv = buildArgv(args); |
| 130 | execvp(argv[0], argv.data()); |
| 131 | _exit(127); |
| 132 | } |
| 133 | |
| 134 | (void)close(pipefd[1]); |
| 135 | |
| 136 | std::string output; |
| 137 | char buf[4096]; |
| 138 | ssize_t n = 0; |
| 139 | while ((n = read(pipefd[0], buf, sizeof(buf))) > 0) |
| 140 | output.append(buf, static_cast<size_t>(n)); |
| 141 | (void)close(pipefd[0]); |
| 142 | |
| 143 | int status = 0; |
| 144 | while (waitpid(pid, &status, 0) < 0) |
| 145 | if (errno != EINTR) |
| 146 | return {-1, {}}; |
| 147 | |
| 148 | /// Split output into non-empty lines. |
| 149 | std::vector<std::string> lines; |
| 150 | { |
| 151 | size_t pos = 0; |
| 152 | while (pos < output.size()) |
| 153 | { |
| 154 | size_t found = output.find('\n', pos); |
| 155 | if (found == std::string::npos) |
| 156 | found = output.size(); |
| 157 | std::string line = output.substr(pos, found - pos); |
no test coverage detected