Files

146 lines
4.5 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#ifndef __NOB_POPEN_H__
#define __NOB_POPEN_H__
#include "nob.h"
typedef struct {
Nob_String_Builder out; // stdout 内容
int exit_code; // 实际退出码
bool ok; // 是否成功启动
} Nob_Cmd_Result;
typedef struct {
const char **items;
int capacity;
int count;
} Nob_Files;
// 运行命令,捕获 stdout(Windows 下会合并 stderr)和真实退出码。
// 返回 true 仅表示命令成功启动并完成,不代表 exit_code == 0。
bool nob_cmd_capture_popen(const char *tname, Nob_Cmd_Result *res) {
memset(res, 0, sizeof(*res));
#ifdef _WIN32
// ---------- Windows: CreateProcessA + 匿名管道 ----------
SECURITY_ATTRIBUTES sa = {0};
sa.nLength = sizeof(sa);
sa.bInheritHandle = TRUE;
sa.lpSecurityDescriptor = NULL;
HANDLE hRead = NULL;
HANDLE hWrite = NULL;
if (!CreatePipe(&hRead, &hWrite, &sa, 0)) {
nob_log(NOB_ERROR, "CreatePipe failed: %s", nob_win32_error_message(GetLastError()));
return false;
}
// 读端不能被继承,否则父进程 ReadFile 永远等不到 EOF
if (!SetHandleInformation(hRead, HANDLE_FLAG_INHERIT, 0)) {
nob_log(NOB_ERROR, "SetHandleInformation failed: %s",
nob_win32_error_message(GetLastError()));
CloseHandle(hRead);
CloseHandle(hWrite);
return false;
}
STARTUPINFOA si = {0};
si.cb = sizeof(si);
si.dwFlags = STARTF_USESTDHANDLES;
si.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
si.hStdOutput = hWrite;
si.hStdError = hWrite; // 把 stderr 也并入 out,需要分离的话另开一条管道
PROCESS_INFORMATION pi = {0};
// CreateProcessA 会就地修改 lpCommandLine,必须传可写缓冲
char *cmdline = NULL;
{
size_t n = strlen(tname) + 1;
cmdline = (char *)malloc(n);
if (!cmdline) {
CloseHandle(hRead);
CloseHandle(hWrite);
nob_log(NOB_ERROR, "malloc failed");
return false;
}
memcpy(cmdline, tname, n);
}
BOOL ok = CreateProcessA(NULL, // lpApplicationName: 让系统从 lpCommandLine 里解析
cmdline, // lpCommandLine
NULL, // lpProcessAttributes
NULL, // lpThreadAttributes
TRUE, // bInheritHandles: 必须为 TRUE,才能继承管道写端
CREATE_NO_WINDOW, // 不弹黑窗;想弹就传 0
NULL, // lpEnvironment
NULL, // lpCurrentDirectory
&si, &pi);
free(cmdline);
// 无论成功与否,父进程都不再需要写端(子进程已持有自己的副本)
CloseHandle(hWrite);
if (!ok) {
nob_log(NOB_ERROR, "CreateProcessA failed for \"%s\": %s", tname,
nob_win32_error_message(GetLastError()));
CloseHandle(hRead);
return false;
}
// ---------- 读取 stdout ----------
{
char buffer[4096];
DWORD n = 0;
while (ReadFile(hRead, buffer, sizeof(buffer), &n, NULL) && n > 0) {
nob_sb_append_buf(&res->out, buffer, (size_t)n);
}
}
CloseHandle(hRead);
// ---------- 等待并取真实退出码 ----------
WaitForSingleObject(pi.hProcess, INFINITE);
DWORD code = 0;
if (!GetExitCodeProcess(pi.hProcess, &code)) {
nob_log(NOB_ERROR, "GetExitCodeProcess failed: %s",
nob_win32_error_message(GetLastError()));
code = (DWORD)-1;
}
res->exit_code = (int)code;
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
#else
// ---------- POSIX: popen ----------
FILE *fp = popen(tname, "r");
if (!fp) {
nob_log(NOB_ERROR, "popen failed: %s", strerror(errno));
return false;
}
char buffer[4096];
while (fgets(buffer, sizeof(buffer), fp)) {
nob_sb_append_cstr(&res->out, buffer);
}
int status = pclose(fp);
if (status == -1) {
nob_log(NOB_ERROR, "pclose failed: %s", strerror(errno));
return false;
}
if (WIFEXITED(status))
status = WEXITSTATUS(status);
else if (WIFSIGNALED(status))
status = 128 + WTERMSIG(status);
res->exit_code = status;
#endif
res->ok = true;
return true;
}
#define nob_cmd_result_free(res) nob_sb_free((res).out)
#endif /* __NOB_POPEN_H__ */