Files
vercel-labs--zerolang/native/zero-c/src/process_exec.c
T
wehub-resource-sync e7738de6d2
CI / Deep Native Runtime Cases (1/6) (push) Has been skipped
CI / Native Preflight (push) Failing after 1s
CI / Native Runtime Cases (1/2) (push) Failing after 0s
CI / Native Runtime Cases (2/2) (push) Failing after 1s
CI / Native Metadata Reports (push) Failing after 0s
CI / Native Direct Backend Artifacts (push) Failing after 0s
CI / Native Sanitizer Smoke (push) Failing after 1s
CI / Command Contract Snapshots (push) Failing after 1s
CI / Deep Conformance Suite (push) Has been skipped
CI / Graph Build Perf (push) Failing after 1s
CI / Deep Native Preflight (push) Has been skipped
CI / Deep Native Runtime Cases (2/6) (push) Has been skipped
CI / Deep Native Runtime Cases (3/6) (push) Has been skipped
CI / Conformance Suite (push) Failing after 1s
CI / Workspace Checks (push) Failing after 0s
CI / Deep Native Runtime Cases (5/6) (push) Has been skipped
CI / Deep Native Runtime Cases (6/6) (push) Has been skipped
CI / Deep Native Runtime Cases (4/6) (push) Has been skipped
CI / Deep Graph Build Perf (push) Has been skipped
chore: import upstream snapshot with attribution
2026-07-13 12:29:30 +08:00

319 lines
8.5 KiB
C

#ifndef _POSIX_C_SOURCE
#define _POSIX_C_SOURCE 200809L
#endif
#include "process_exec.h"
#include "process_path.h"
#include <ctype.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#if defined(_WIN32)
#include <direct.h>
#include <process.h>
#else
#include <sys/wait.h>
#include <unistd.h>
#endif
void z_process_argv_init(ZProcessArgv *argv) {
argv->items = NULL;
argv->len = 0;
argv->cap = 0;
}
bool z_process_argv_push(ZProcessArgv *argv, const char *value) {
if (!argv || !value || !value[0]) return false;
size_t required = argv->len + 2;
if (required > argv->cap) {
size_t next = z_grow_capacity(argv->cap, required, 8);
argv->items = z_checked_reallocarray(argv->items, next, sizeof(char *));
argv->cap = next;
}
argv->items[argv->len++] = z_strdup(value);
argv->items[argv->len] = NULL;
return true;
}
bool z_process_argv_append_flag_text(ZProcessArgv *argv, const char *text, bool *suppress_stderr) {
const char *cursor = text ? text : "";
while (*cursor) {
while (isspace((unsigned char)*cursor)) cursor++;
if (!*cursor) break;
ZBuf token;
zbuf_init(&token);
while (*cursor && !isspace((unsigned char)*cursor)) {
if (*cursor == '\'') {
cursor++;
bool closed = false;
while (*cursor && *cursor != '\'') zbuf_append_char(&token, *cursor++);
if (*cursor == '\'') {
closed = true;
cursor++;
}
if (!closed) {
zbuf_free(&token);
return false;
}
} else if (*cursor == '\\' && cursor[1]) {
cursor++;
zbuf_append_char(&token, *cursor++);
} else {
zbuf_append_char(&token, *cursor++);
}
}
bool ok = true;
if (token.len > 0) {
if (strcmp(token.data, "2>/dev/null") == 0) {
if (suppress_stderr) *suppress_stderr = true;
} else {
ok = z_process_argv_push(argv, token.data);
}
}
zbuf_free(&token);
if (!ok) return false;
}
return true;
}
void z_process_argv_free(ZProcessArgv *argv) {
if (!argv) return;
for (size_t i = 0; i < argv->len; i++) free(argv->items[i]);
free(argv->items);
argv->items = NULL;
argv->len = 0;
argv->cap = 0;
}
static bool z_process_existing_dir(const char *path) {
#if defined(_WIN32)
struct _stat st;
return _stat(path, &st) == 0 && (st.st_mode & _S_IFDIR) != 0;
#else
struct stat st;
return stat(path, &st) == 0 && S_ISDIR(st.st_mode);
#endif
}
bool z_process_ensure_dir(const char *path) {
if (!path || !path[0]) return false;
#if defined(_WIN32)
if (_mkdir(path) == 0) return true;
#else
if (mkdir(path, 0777) == 0) return true;
#endif
if (errno != EEXIST) return false;
return z_process_existing_dir(path);
}
#if defined(_WIN32)
typedef struct _stat ZProcessOutputStat;
#else
typedef struct stat ZProcessOutputStat;
#endif
static bool z_process_lstat_output(const char *path, ZProcessOutputStat *st) {
if (!path || !path[0] || !st) return false;
#if defined(_WIN32)
return _stat(path, st) == 0;
#else
return lstat(path, st) == 0;
#endif
}
static bool z_process_output_is_regular(const ZProcessOutputStat *st) {
#if defined(_WIN32)
return st && (st->st_mode & _S_IFREG) != 0;
#else
return st && S_ISREG(st->st_mode);
#endif
}
static bool z_process_output_is_symlink(const ZProcessOutputStat *st) {
#if defined(_WIN32)
(void)st;
return false;
#else
return st && S_ISLNK(st->st_mode);
#endif
}
static bool z_process_output_parent_ready(const char *path) {
if (!path || !path[0]) return false;
const char *last_sep = NULL;
for (const char *cursor = path; *cursor; cursor++) {
if (*cursor == '/' || *cursor == '\\') last_sep = cursor;
}
if (!last_sep) return true;
if (last_sep == path) return z_process_existing_dir("/");
#if defined(_WIN32)
if (last_sep == path + 2 && isalpha((unsigned char)path[0]) && path[1] == ':') {
char drive_root[4] = {path[0], ':', '\\', 0};
return z_process_existing_dir(drive_root);
}
#endif
ZBuf parent;
zbuf_init(&parent);
for (const char *cursor = path; cursor < last_sep; cursor++) zbuf_append_char(&parent, *cursor);
bool ok = z_process_existing_dir(parent.data);
zbuf_free(&parent);
return ok;
}
bool z_process_prepare_output_file(const char *path) {
ZProcessOutputStat st;
if (!path || !path[0]) return false;
if (!z_process_output_parent_ready(path)) return false;
if (!z_process_lstat_output(path, &st)) return errno == ENOENT;
if (z_process_output_is_symlink(&st) || !z_process_output_is_regular(&st)) return false;
if (remove(path) == 0) return true;
return errno == ENOENT;
}
bool z_process_output_file_ready(const char *path) {
ZProcessOutputStat st;
if (!z_process_lstat_output(path, &st)) return false;
if (z_process_output_is_symlink(&st) || !z_process_output_is_regular(&st)) return false;
return st.st_size > 0;
}
bool z_process_executable_file_ready(const char *path) {
if (!z_process_output_file_ready(path)) return false;
#if defined(_WIN32)
return true;
#else
return access(path, X_OK) == 0;
#endif
}
bool z_process_mark_executable(const char *path) {
if (!z_process_output_file_ready(path)) return false;
#if defined(_WIN32)
return true;
#else
if (chmod(path, 0755) != 0) return false;
return z_process_executable_file_ready(path);
#endif
}
bool z_process_remove_regular_file(const char *path) {
ZProcessOutputStat st;
if (!path || !path[0]) return false;
if (!z_process_lstat_output(path, &st)) return errno == ENOENT;
if (z_process_output_is_symlink(&st) || !z_process_output_is_regular(&st)) return false;
if (remove(path) == 0) return true;
return errno == ENOENT;
}
#if !defined(_WIN32)
static bool z_process_suppress_stream(FILE *stream) {
return freopen("/dev/null", "w", stream) != NULL;
}
static bool z_process_wait_success(pid_t pid) {
int status = 0;
while (waitpid(pid, &status, 0) < 0) {
if (errno != EINTR) return false;
}
return WIFEXITED(status) && WEXITSTATUS(status) == 0;
}
#endif
bool z_process_run_argv(const ZProcessArgv *argv, bool suppress_stdout, bool suppress_stderr, bool uses_zig_env) {
if (!argv || argv->len == 0 || !argv->items || !argv->items[0]) return false;
char *executable = z_process_resolve_executable(argv->items[0]);
if (!executable) return false;
#if defined(_WIN32)
(void)suppress_stdout;
(void)suppress_stderr;
(void)uses_zig_env;
int rc = _spawnv(_P_WAIT, executable, (const char *const *)argv->items);
free(executable);
return rc == 0;
#else
pid_t pid = fork();
if (pid < 0) {
free(executable);
return false;
}
if (pid == 0) {
if (uses_zig_env) {
if (setenv("ZIG_GLOBAL_CACHE_DIR", ".zero/zig-global-cache", 1) != 0) _exit(127);
if (setenv("ZIG_LOCAL_CACHE_DIR", ".zero/zig-local-cache", 1) != 0) _exit(127);
}
if (suppress_stdout && !z_process_suppress_stream(stdout)) _exit(127);
if (suppress_stderr && !z_process_suppress_stream(stderr)) _exit(127);
execv(executable, argv->items);
_exit(127);
}
free(executable);
return z_process_wait_success(pid);
#endif
}
char *z_process_first_stdout_line(const char *const *argv, bool suppress_stderr) {
if (!argv || !argv[0]) return z_strdup("");
char *executable = z_process_resolve_executable(argv[0]);
if (!executable) return z_strdup("");
#if defined(_WIN32)
(void)suppress_stderr;
free(executable);
return z_strdup("");
#else
int pipe_fd[2];
if (pipe(pipe_fd) != 0) {
free(executable);
return z_strdup("");
}
pid_t pid = fork();
if (pid < 0) {
close(pipe_fd[0]);
close(pipe_fd[1]);
free(executable);
return z_strdup("");
}
if (pid == 0) {
close(pipe_fd[0]);
if (dup2(pipe_fd[1], STDOUT_FILENO) < 0) _exit(127);
close(pipe_fd[1]);
if (suppress_stderr && !z_process_suppress_stream(stderr)) _exit(127);
execv(executable, (char *const *)argv);
_exit(127);
}
free(executable);
close(pipe_fd[1]);
ZBuf line;
zbuf_init(&line);
bool capturing = true;
bool read_ok = true;
char bytes[256];
while (true) {
ssize_t n = read(pipe_fd[0], bytes, sizeof(bytes));
if (n < 0) {
if (errno == EINTR) continue;
read_ok = false;
break;
}
if (n == 0) break;
for (ssize_t i = 0; capturing && i < n; i++) {
if (bytes[i] == '\n' || bytes[i] == '\r') {
capturing = false;
} else if (line.len < 255) {
zbuf_append_char(&line, bytes[i]);
}
}
}
close(pipe_fd[0]);
bool child_ok = z_process_wait_success(pid);
char *out = read_ok && child_ok && line.data ? line.data : z_strdup("");
if (out == line.data) line.data = NULL;
zbuf_free(&line);
return out;
#endif
}