mirror of
https://github.com/mruby/mruby
synced 2026-06-08 16:11:16 +00:00
mruby-io: introduce HAL for platform abstraction
separates platform-specific code into hal-posix-io and hal-win-io gems, making mruby-io platform-independent. HAL interface defined in mrbgems/mruby-io/include/io_hal.h covers file operations, I/O operations, and process operations. follows mruby-task dependency pattern where HAL gems depend on feature gem. ws2_32 library linked in hal-win-io gem. Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,8 @@
|
||||
MRuby::Gem::Specification.new('hal-posix-io') do |spec|
|
||||
spec.license = 'MIT'
|
||||
spec.author = 'mruby developers'
|
||||
spec.summary = 'POSIX HAL for mruby-io (Linux, macOS, BSD, Unix)'
|
||||
|
||||
# HAL gem depends on feature gem - brings in mruby-io automatically
|
||||
spec.add_dependency 'mruby-io', core: 'mruby-io'
|
||||
end
|
||||
@@ -0,0 +1,578 @@
|
||||
/*
|
||||
** io_hal.c - POSIX HAL implementation for mruby-io
|
||||
**
|
||||
** See Copyright Notice in mruby.h
|
||||
**
|
||||
** POSIX implementation for I/O operations using standard POSIX APIs.
|
||||
** Supported platforms: Linux, macOS, BSD, Unix
|
||||
*/
|
||||
|
||||
#include <mruby.h>
|
||||
#include <io_hal.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/file.h>
|
||||
#include <sys/param.h>
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <pwd.h>
|
||||
|
||||
#ifndef __DJGPP__
|
||||
#include <libgen.h>
|
||||
#endif
|
||||
|
||||
/* Maximum path length */
|
||||
#ifndef PATH_MAX
|
||||
#define PATH_MAX 4096
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Helper Functions
|
||||
*/
|
||||
|
||||
/* Convert POSIX struct stat to mrb_io_stat */
|
||||
static void
|
||||
convert_stat(const struct stat *src, mrb_io_stat *dst)
|
||||
{
|
||||
/* Save time values to avoid macro expansion issues */
|
||||
time_t atime = src->st_atime;
|
||||
time_t mtime = src->st_mtime;
|
||||
time_t ctime = src->st_ctime;
|
||||
|
||||
dst->st_dev = (uint64_t)src->st_dev;
|
||||
dst->st_ino = (uint64_t)src->st_ino;
|
||||
dst->st_mode = (uint32_t)src->st_mode;
|
||||
dst->st_nlink = (uint32_t)src->st_nlink;
|
||||
dst->st_uid = (uint32_t)src->st_uid;
|
||||
dst->st_gid = (uint32_t)src->st_gid;
|
||||
dst->st_rdev = (uint64_t)src->st_rdev;
|
||||
dst->st_size = (int64_t)src->st_size;
|
||||
|
||||
/* Assign time values after undefinining macros */
|
||||
#undef st_atime
|
||||
#undef st_mtime
|
||||
#undef st_ctime
|
||||
dst->st_atime = (int64_t)atime;
|
||||
dst->st_mtime = (int64_t)mtime;
|
||||
dst->st_ctime = (int64_t)ctime;
|
||||
|
||||
#ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
|
||||
dst->st_blksize = (int64_t)src->st_blksize;
|
||||
#else
|
||||
dst->st_blksize = 512;
|
||||
#endif
|
||||
#ifdef HAVE_STRUCT_STAT_ST_BLOCKS
|
||||
dst->st_blocks = (int64_t)src->st_blocks;
|
||||
#else
|
||||
dst->st_blocks = (dst->st_size + 511) / 512;
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* File Operations
|
||||
*/
|
||||
|
||||
int
|
||||
mrb_io_hal_stat(mrb_state *mrb, const char *path, mrb_io_stat *st)
|
||||
{
|
||||
struct stat s;
|
||||
(void)mrb;
|
||||
|
||||
if (stat(path, &s) == -1) {
|
||||
return -1;
|
||||
}
|
||||
convert_stat(&s, st);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
mrb_io_hal_fstat(mrb_state *mrb, int fd, mrb_io_stat *st)
|
||||
{
|
||||
struct stat s;
|
||||
(void)mrb;
|
||||
|
||||
if (fstat(fd, &s) == -1) {
|
||||
return -1;
|
||||
}
|
||||
convert_stat(&s, st);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
mrb_io_hal_lstat(mrb_state *mrb, const char *path, mrb_io_stat *st)
|
||||
{
|
||||
struct stat s;
|
||||
(void)mrb;
|
||||
|
||||
if (lstat(path, &s) == -1) {
|
||||
return -1;
|
||||
}
|
||||
convert_stat(&s, st);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
mrb_io_hal_chmod(mrb_state *mrb, const char *path, uint32_t mode)
|
||||
{
|
||||
(void)mrb;
|
||||
return chmod(path, (mode_t)mode);
|
||||
}
|
||||
|
||||
uint32_t
|
||||
mrb_io_hal_umask(mrb_state *mrb, int32_t mask)
|
||||
{
|
||||
mode_t old;
|
||||
(void)mrb;
|
||||
|
||||
if (mask < 0) {
|
||||
/* Just query current value */
|
||||
old = umask(0);
|
||||
umask(old);
|
||||
}
|
||||
else {
|
||||
old = umask((mode_t)mask);
|
||||
}
|
||||
return (uint32_t)old;
|
||||
}
|
||||
|
||||
int
|
||||
mrb_io_hal_ftruncate(mrb_state *mrb, int fd, int64_t length)
|
||||
{
|
||||
(void)mrb;
|
||||
return ftruncate(fd, (off_t)length);
|
||||
}
|
||||
|
||||
int
|
||||
mrb_io_hal_flock(mrb_state *mrb, int fd, int operation)
|
||||
{
|
||||
(void)mrb;
|
||||
|
||||
while (flock(fd, operation) == -1) {
|
||||
if (errno == EINTR) {
|
||||
continue; /* Retry on interrupt */
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
mrb_io_hal_unlink(mrb_state *mrb, const char *path)
|
||||
{
|
||||
(void)mrb;
|
||||
return unlink(path);
|
||||
}
|
||||
|
||||
int
|
||||
mrb_io_hal_rename(mrb_state *mrb, const char *oldpath, const char *newpath)
|
||||
{
|
||||
(void)mrb;
|
||||
return rename(oldpath, newpath);
|
||||
}
|
||||
|
||||
int
|
||||
mrb_io_hal_symlink(mrb_state *mrb, const char *target, const char *linkpath)
|
||||
{
|
||||
(void)mrb;
|
||||
return symlink(target, linkpath);
|
||||
}
|
||||
|
||||
int64_t
|
||||
mrb_io_hal_readlink(mrb_state *mrb, const char *path, char *buf, size_t bufsize)
|
||||
{
|
||||
ssize_t rc;
|
||||
(void)mrb;
|
||||
|
||||
rc = readlink(path, buf, bufsize);
|
||||
return (int64_t)rc;
|
||||
}
|
||||
|
||||
char*
|
||||
mrb_io_hal_realpath(mrb_state *mrb, const char *path, char *resolved)
|
||||
{
|
||||
(void)mrb;
|
||||
return realpath(path, resolved);
|
||||
}
|
||||
|
||||
char*
|
||||
mrb_io_hal_getcwd(mrb_state *mrb, char *buf, size_t size)
|
||||
{
|
||||
(void)mrb;
|
||||
return getcwd(buf, size);
|
||||
}
|
||||
|
||||
const char*
|
||||
mrb_io_hal_getenv(mrb_state *mrb, const char *name)
|
||||
{
|
||||
(void)mrb;
|
||||
return getenv(name);
|
||||
}
|
||||
|
||||
const char*
|
||||
mrb_io_hal_gethome(mrb_state *mrb, const char *username)
|
||||
{
|
||||
const char *home;
|
||||
|
||||
if (username == NULL || *username == '\0') {
|
||||
/* Get current user's home */
|
||||
home = getenv("HOME");
|
||||
if (home == NULL) {
|
||||
errno = ENOENT;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* Get specified user's home */
|
||||
struct passwd *pwd = getpwnam(username);
|
||||
if (pwd == NULL) {
|
||||
errno = ENOENT;
|
||||
return NULL;
|
||||
}
|
||||
home = pwd->pw_dir;
|
||||
}
|
||||
|
||||
return home;
|
||||
}
|
||||
|
||||
/*
|
||||
* Core I/O Operations
|
||||
*/
|
||||
|
||||
int
|
||||
mrb_io_hal_open(mrb_state *mrb, const char *path, int flags, uint32_t mode)
|
||||
{
|
||||
int fd;
|
||||
(void)mrb;
|
||||
|
||||
fd = open(path, flags, (mode_t)mode);
|
||||
if (fd == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Set close-on-exec for non-standard descriptors */
|
||||
#if defined(F_GETFD) && defined(F_SETFD) && defined(FD_CLOEXEC)
|
||||
if (fd > 2) {
|
||||
int fd_flags = fcntl(fd, F_GETFD);
|
||||
if (fd_flags != -1) {
|
||||
fcntl(fd, F_SETFD, fd_flags | FD_CLOEXEC);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
int
|
||||
mrb_io_hal_close(mrb_state *mrb, int fd)
|
||||
{
|
||||
(void)mrb;
|
||||
return close(fd);
|
||||
}
|
||||
|
||||
int64_t
|
||||
mrb_io_hal_read(mrb_state *mrb, int fd, void *buf, size_t count)
|
||||
{
|
||||
ssize_t n;
|
||||
(void)mrb;
|
||||
|
||||
n = read(fd, buf, count);
|
||||
return (int64_t)n;
|
||||
}
|
||||
|
||||
int64_t
|
||||
mrb_io_hal_write(mrb_state *mrb, int fd, const void *buf, size_t count)
|
||||
{
|
||||
ssize_t n;
|
||||
(void)mrb;
|
||||
|
||||
n = write(fd, buf, count);
|
||||
return (int64_t)n;
|
||||
}
|
||||
|
||||
int64_t
|
||||
mrb_io_hal_lseek(mrb_state *mrb, int fd, int64_t offset, int whence)
|
||||
{
|
||||
off_t pos;
|
||||
int posix_whence;
|
||||
(void)mrb;
|
||||
|
||||
/* Convert MRB_IO_SEEK_* to POSIX SEEK_* */
|
||||
switch (whence) {
|
||||
case MRB_IO_SEEK_SET: posix_whence = SEEK_SET; break;
|
||||
case MRB_IO_SEEK_CUR: posix_whence = SEEK_CUR; break;
|
||||
case MRB_IO_SEEK_END: posix_whence = SEEK_END; break;
|
||||
default:
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
pos = lseek(fd, (off_t)offset, posix_whence);
|
||||
return (int64_t)pos;
|
||||
}
|
||||
|
||||
int
|
||||
mrb_io_hal_dup(mrb_state *mrb, int fd)
|
||||
{
|
||||
int new_fd;
|
||||
(void)mrb;
|
||||
|
||||
new_fd = dup(fd);
|
||||
if (new_fd == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Set close-on-exec */
|
||||
#if defined(F_GETFD) && defined(F_SETFD) && defined(FD_CLOEXEC)
|
||||
if (new_fd > 2) {
|
||||
int fd_flags = fcntl(new_fd, F_GETFD);
|
||||
if (fd_flags != -1) {
|
||||
fcntl(new_fd, F_SETFD, fd_flags | FD_CLOEXEC);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return new_fd;
|
||||
}
|
||||
|
||||
int
|
||||
mrb_io_hal_fcntl(mrb_state *mrb, int fd, int cmd, int arg)
|
||||
{
|
||||
(void)mrb;
|
||||
return fcntl(fd, cmd, arg);
|
||||
}
|
||||
|
||||
int
|
||||
mrb_io_hal_isatty(mrb_state *mrb, int fd)
|
||||
{
|
||||
(void)mrb;
|
||||
return isatty(fd) ? 1 : 0;
|
||||
}
|
||||
|
||||
int
|
||||
mrb_io_hal_pipe(mrb_state *mrb, int fds[2])
|
||||
{
|
||||
int ret;
|
||||
(void)mrb;
|
||||
|
||||
ret = pipe(fds);
|
||||
if (ret == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Set close-on-exec on both ends */
|
||||
#if defined(F_GETFD) && defined(F_SETFD) && defined(FD_CLOEXEC)
|
||||
for (int i = 0; i < 2; i++) {
|
||||
int fd_flags = fcntl(fds[i], F_GETFD);
|
||||
if (fd_flags != -1) {
|
||||
fcntl(fds[i], F_SETFD, fd_flags | FD_CLOEXEC);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Process Operations
|
||||
*/
|
||||
|
||||
int
|
||||
mrb_io_hal_spawn_process(mrb_state *mrb, const char *cmd,
|
||||
int stdin_fd, int stdout_fd, int stderr_fd,
|
||||
int *pid)
|
||||
{
|
||||
pid_t child_pid;
|
||||
(void)mrb;
|
||||
|
||||
/* Skip leading whitespace */
|
||||
while (*cmd == ' ' || *cmd == '\t' || *cmd == '\n') {
|
||||
cmd++;
|
||||
}
|
||||
|
||||
if (!*cmd) {
|
||||
errno = ENOENT;
|
||||
return -1;
|
||||
}
|
||||
|
||||
child_pid = fork();
|
||||
if (child_pid == -1) {
|
||||
/* Fork failed */
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (child_pid == 0) {
|
||||
/* Child process */
|
||||
|
||||
/* Redirect stdin */
|
||||
if (stdin_fd != -1) {
|
||||
dup2(stdin_fd, STDIN_FILENO);
|
||||
if (stdin_fd > 2) close(stdin_fd);
|
||||
}
|
||||
|
||||
/* Redirect stdout */
|
||||
if (stdout_fd != -1) {
|
||||
dup2(stdout_fd, STDOUT_FILENO);
|
||||
if (stdout_fd > 2) close(stdout_fd);
|
||||
}
|
||||
|
||||
/* Redirect stderr */
|
||||
if (stderr_fd != -1) {
|
||||
dup2(stderr_fd, STDERR_FILENO);
|
||||
if (stderr_fd > 2) close(stderr_fd);
|
||||
}
|
||||
|
||||
/* Close all other file descriptors */
|
||||
int max_fd = sysconf(_SC_OPEN_MAX);
|
||||
if (max_fd == -1) max_fd = 1024;
|
||||
for (int i = 3; i < max_fd; i++) {
|
||||
close(i);
|
||||
}
|
||||
|
||||
/* Execute command via shell */
|
||||
execl("/bin/sh", "sh", "-c", cmd, (char*)NULL);
|
||||
|
||||
/* If execl returns, it failed */
|
||||
_exit(127);
|
||||
}
|
||||
|
||||
/* Parent process */
|
||||
*pid = (int)child_pid;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
mrb_io_hal_waitpid(mrb_state *mrb, int pid, int *status, int options)
|
||||
{
|
||||
pid_t result;
|
||||
int stat;
|
||||
(void)mrb;
|
||||
|
||||
result = waitpid((pid_t)pid, &stat, options);
|
||||
if (result == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (status != NULL) {
|
||||
*status = stat;
|
||||
}
|
||||
|
||||
return (int)result;
|
||||
}
|
||||
|
||||
/*
|
||||
* I/O Multiplexing
|
||||
*/
|
||||
|
||||
struct mrb_io_fdset {
|
||||
fd_set fds;
|
||||
};
|
||||
|
||||
mrb_io_fdset*
|
||||
mrb_io_hal_fdset_alloc(mrb_state *mrb)
|
||||
{
|
||||
mrb_io_fdset *fdset = (mrb_io_fdset*)mrb_malloc(mrb, sizeof(mrb_io_fdset));
|
||||
FD_ZERO(&fdset->fds);
|
||||
return fdset;
|
||||
}
|
||||
|
||||
void
|
||||
mrb_io_hal_fdset_free(mrb_state *mrb, mrb_io_fdset *fdset)
|
||||
{
|
||||
if (fdset) {
|
||||
mrb_free(mrb, fdset);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
mrb_io_hal_fdset_zero(mrb_state *mrb, mrb_io_fdset *fdset)
|
||||
{
|
||||
(void)mrb;
|
||||
if (fdset) {
|
||||
FD_ZERO(&fdset->fds);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
mrb_io_hal_fdset_set(mrb_state *mrb, int fd, mrb_io_fdset *fdset)
|
||||
{
|
||||
(void)mrb;
|
||||
if (fdset) {
|
||||
FD_SET(fd, &fdset->fds);
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
mrb_io_hal_fdset_isset(mrb_state *mrb, int fd, mrb_io_fdset *fdset)
|
||||
{
|
||||
(void)mrb;
|
||||
if (fdset) {
|
||||
return FD_ISSET(fd, &fdset->fds);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
mrb_io_hal_select(mrb_state *mrb, int nfds,
|
||||
mrb_io_fdset *readfds,
|
||||
mrb_io_fdset *writefds,
|
||||
mrb_io_fdset *errorfds,
|
||||
mrb_io_timeval *timeout)
|
||||
{
|
||||
fd_set *r = readfds ? &readfds->fds : NULL;
|
||||
fd_set *w = writefds ? &writefds->fds : NULL;
|
||||
fd_set *e = errorfds ? &errorfds->fds : NULL;
|
||||
struct timeval *tv = NULL;
|
||||
struct timeval tv_storage;
|
||||
(void)mrb;
|
||||
|
||||
if (timeout) {
|
||||
tv_storage.tv_sec = (time_t)timeout->tv_sec;
|
||||
tv_storage.tv_usec = (suseconds_t)timeout->tv_usec;
|
||||
tv = &tv_storage;
|
||||
}
|
||||
|
||||
return select(nfds, r, w, e, tv);
|
||||
}
|
||||
|
||||
/*
|
||||
* HAL Initialization/Finalization
|
||||
*/
|
||||
|
||||
void
|
||||
mrb_io_hal_init(mrb_state *mrb)
|
||||
{
|
||||
(void)mrb;
|
||||
/* No special initialization needed for POSIX */
|
||||
}
|
||||
|
||||
void
|
||||
mrb_io_hal_final(mrb_state *mrb)
|
||||
{
|
||||
(void)mrb;
|
||||
/* No special cleanup needed for POSIX */
|
||||
}
|
||||
|
||||
/*
|
||||
* Gem initialization
|
||||
*/
|
||||
|
||||
void
|
||||
mrb_hal_posix_io_gem_init(mrb_state *mrb)
|
||||
{
|
||||
(void)mrb;
|
||||
/* HAL interface functions are called by mruby-io gem */
|
||||
}
|
||||
|
||||
void
|
||||
mrb_hal_posix_io_gem_final(mrb_state *mrb)
|
||||
{
|
||||
(void)mrb;
|
||||
/* Cleanup handled by mrb_io_hal_final called from mruby-io */
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
MRuby::Gem::Specification.new('hal-win-io') do |spec|
|
||||
spec.license = 'MIT'
|
||||
spec.author = 'mruby developers'
|
||||
spec.summary = 'Windows HAL for mruby-io (Windows, MinGW)'
|
||||
|
||||
# HAL gem depends on feature gem - brings in mruby-io automatically
|
||||
spec.add_dependency 'mruby-io', core: 'mruby-io'
|
||||
|
||||
# Link Windows socket library
|
||||
spec.linker.libraries << "ws2_32"
|
||||
end
|
||||
@@ -0,0 +1,671 @@
|
||||
/*
|
||||
** io_hal.c - Windows HAL implementation for mruby-io
|
||||
**
|
||||
** See Copyright Notice in mruby.h
|
||||
**
|
||||
** Windows implementation for I/O operations using Win32 APIs.
|
||||
** Supported platforms: Windows, MinGW
|
||||
*/
|
||||
|
||||
#include <mruby.h>
|
||||
#include <io_hal.h>
|
||||
|
||||
#include <windows.h>
|
||||
#include <io.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
/* Maximum path length */
|
||||
#ifndef PATH_MAX
|
||||
#define PATH_MAX 4096
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Helper Functions
|
||||
*/
|
||||
|
||||
/* Convert Windows struct _stat64 to mrb_io_stat */
|
||||
static void
|
||||
convert_stat(const struct _stat64 *src, mrb_io_stat *dst)
|
||||
{
|
||||
dst->st_dev = (uint64_t)src->st_dev;
|
||||
dst->st_ino = (uint64_t)src->st_ino;
|
||||
dst->st_mode = (uint32_t)src->st_mode;
|
||||
dst->st_nlink = (uint32_t)src->st_nlink;
|
||||
dst->st_uid = 0; /* Windows doesn't have Unix-style UIDs */
|
||||
dst->st_gid = 0; /* Windows doesn't have Unix-style GIDs */
|
||||
dst->st_rdev = (uint64_t)src->st_rdev;
|
||||
dst->st_size = (int64_t)src->st_size;
|
||||
dst->st_atime = (int64_t)src->st_atime;
|
||||
dst->st_mtime = (int64_t)src->st_mtime;
|
||||
dst->st_ctime = (int64_t)src->st_ctime;
|
||||
dst->st_blksize = 512;
|
||||
dst->st_blocks = (dst->st_size + 511) / 512;
|
||||
}
|
||||
|
||||
/* Convert errno to Windows errno */
|
||||
static void
|
||||
set_errno_from_win_error(DWORD error)
|
||||
{
|
||||
switch (error) {
|
||||
case ERROR_FILE_NOT_FOUND:
|
||||
case ERROR_PATH_NOT_FOUND:
|
||||
errno = ENOENT;
|
||||
break;
|
||||
case ERROR_ACCESS_DENIED:
|
||||
errno = EACCES;
|
||||
break;
|
||||
case ERROR_NOT_ENOUGH_MEMORY:
|
||||
errno = ENOMEM;
|
||||
break;
|
||||
case ERROR_INVALID_HANDLE:
|
||||
errno = EBADF;
|
||||
break;
|
||||
case ERROR_ALREADY_EXISTS:
|
||||
errno = EEXIST;
|
||||
break;
|
||||
default:
|
||||
errno = EIO;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* File Operations
|
||||
*/
|
||||
|
||||
int
|
||||
mrb_io_hal_stat(mrb_state *mrb, const char *path, mrb_io_stat *st)
|
||||
{
|
||||
struct _stat64 s;
|
||||
(void)mrb;
|
||||
|
||||
if (_stat64(path, &s) == -1) {
|
||||
return -1;
|
||||
}
|
||||
convert_stat(&s, st);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
mrb_io_hal_fstat(mrb_state *mrb, int fd, mrb_io_stat *st)
|
||||
{
|
||||
struct _stat64 s;
|
||||
(void)mrb;
|
||||
|
||||
if (_fstat64(fd, &s) == -1) {
|
||||
return -1;
|
||||
}
|
||||
convert_stat(&s, st);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
mrb_io_hal_lstat(mrb_state *mrb, const char *path, mrb_io_stat *st)
|
||||
{
|
||||
/* Windows doesn't distinguish lstat from stat */
|
||||
return mrb_io_hal_stat(mrb, path, st);
|
||||
}
|
||||
|
||||
int
|
||||
mrb_io_hal_chmod(mrb_state *mrb, const char *path, uint32_t mode)
|
||||
{
|
||||
(void)mrb;
|
||||
return _chmod(path, (int)mode);
|
||||
}
|
||||
|
||||
uint32_t
|
||||
mrb_io_hal_umask(mrb_state *mrb, int32_t mask)
|
||||
{
|
||||
int old;
|
||||
(void)mrb;
|
||||
|
||||
if (mask < 0) {
|
||||
/* Just query current value */
|
||||
old = _umask(0);
|
||||
_umask(old);
|
||||
}
|
||||
else {
|
||||
old = _umask((int)mask);
|
||||
}
|
||||
return (uint32_t)old;
|
||||
}
|
||||
|
||||
int
|
||||
mrb_io_hal_ftruncate(mrb_state *mrb, int fd, int64_t length)
|
||||
{
|
||||
(void)mrb;
|
||||
return _chsize_s(fd, length);
|
||||
}
|
||||
|
||||
int
|
||||
mrb_io_hal_flock(mrb_state *mrb, int fd, int operation)
|
||||
{
|
||||
HANDLE h;
|
||||
OVERLAPPED overlapped;
|
||||
DWORD flags = 0;
|
||||
(void)mrb;
|
||||
|
||||
h = (HANDLE)_get_osfhandle(fd);
|
||||
if (h == INVALID_HANDLE_VALUE) {
|
||||
errno = EBADF;
|
||||
return -1;
|
||||
}
|
||||
|
||||
memset(&overlapped, 0, sizeof(overlapped));
|
||||
|
||||
if (operation & MRB_IO_LOCK_UN) {
|
||||
if (!UnlockFileEx(h, 0, MAXDWORD, MAXDWORD, &overlapped)) {
|
||||
set_errno_from_win_error(GetLastError());
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (operation & MRB_IO_LOCK_EX) {
|
||||
flags |= LOCKFILE_EXCLUSIVE_LOCK;
|
||||
}
|
||||
if (operation & MRB_IO_LOCK_NB) {
|
||||
flags |= LOCKFILE_FAIL_IMMEDIATELY;
|
||||
}
|
||||
|
||||
if (!LockFileEx(h, flags, 0, MAXDWORD, MAXDWORD, &overlapped)) {
|
||||
set_errno_from_win_error(GetLastError());
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
mrb_io_hal_unlink(mrb_state *mrb, const char *path)
|
||||
{
|
||||
(void)mrb;
|
||||
return _unlink(path);
|
||||
}
|
||||
|
||||
int
|
||||
mrb_io_hal_rename(mrb_state *mrb, const char *oldpath, const char *newpath)
|
||||
{
|
||||
(void)mrb;
|
||||
return rename(oldpath, newpath);
|
||||
}
|
||||
|
||||
int
|
||||
mrb_io_hal_symlink(mrb_state *mrb, const char *target, const char *linkpath)
|
||||
{
|
||||
DWORD flags = 0;
|
||||
(void)mrb;
|
||||
|
||||
/* Check if target is a directory */
|
||||
DWORD attrs = GetFileAttributes(target);
|
||||
if (attrs != INVALID_FILE_ATTRIBUTES && (attrs & FILE_ATTRIBUTE_DIRECTORY)) {
|
||||
flags = SYMBOLIC_LINK_FLAG_DIRECTORY;
|
||||
}
|
||||
|
||||
if (!CreateSymbolicLink(linkpath, target, flags)) {
|
||||
set_errno_from_win_error(GetLastError());
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int64_t
|
||||
mrb_io_hal_readlink(mrb_state *mrb, const char *path, char *buf, size_t bufsize)
|
||||
{
|
||||
HANDLE h;
|
||||
DWORD ret;
|
||||
char temp[PATH_MAX];
|
||||
(void)mrb;
|
||||
|
||||
h = CreateFile(path, GENERIC_READ, FILE_SHARE_READ, NULL,
|
||||
OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
|
||||
if (h == INVALID_HANDLE_VALUE) {
|
||||
set_errno_from_win_error(GetLastError());
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = GetFinalPathNameByHandle(h, temp, PATH_MAX, FILE_NAME_NORMALIZED);
|
||||
CloseHandle(h);
|
||||
|
||||
if (ret == 0 || ret >= PATH_MAX) {
|
||||
errno = EIO;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Remove \\?\ prefix if present */
|
||||
const char *result = temp;
|
||||
if (strncmp(temp, "\\\\?\\", 4) == 0) {
|
||||
result = temp + 4;
|
||||
}
|
||||
|
||||
size_t len = strlen(result);
|
||||
if (len > bufsize) {
|
||||
len = bufsize;
|
||||
}
|
||||
memcpy(buf, result, len);
|
||||
return (int64_t)len;
|
||||
}
|
||||
|
||||
char*
|
||||
mrb_io_hal_realpath(mrb_state *mrb, const char *path, char *resolved)
|
||||
{
|
||||
DWORD ret;
|
||||
(void)mrb;
|
||||
|
||||
ret = GetFullPathName(path, PATH_MAX, resolved, NULL);
|
||||
if (ret == 0 || ret >= PATH_MAX) {
|
||||
set_errno_from_win_error(GetLastError());
|
||||
return NULL;
|
||||
}
|
||||
return resolved;
|
||||
}
|
||||
|
||||
char*
|
||||
mrb_io_hal_getcwd(mrb_state *mrb, char *buf, size_t size)
|
||||
{
|
||||
(void)mrb;
|
||||
return _getcwd(buf, (int)size);
|
||||
}
|
||||
|
||||
const char*
|
||||
mrb_io_hal_getenv(mrb_state *mrb, const char *name)
|
||||
{
|
||||
(void)mrb;
|
||||
return getenv(name);
|
||||
}
|
||||
|
||||
const char*
|
||||
mrb_io_hal_gethome(mrb_state *mrb, const char *username)
|
||||
{
|
||||
const char *home;
|
||||
(void)mrb;
|
||||
|
||||
if (username != NULL && *username != '\0') {
|
||||
/* Windows doesn't have a simple way to get other users' home directories */
|
||||
errno = ENOSYS;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Try USERPROFILE first, then HOMEDRIVE+HOMEPATH */
|
||||
home = getenv("USERPROFILE");
|
||||
if (home == NULL) {
|
||||
const char *homedrive = getenv("HOMEDRIVE");
|
||||
const char *homepath = getenv("HOMEPATH");
|
||||
if (homedrive && homepath) {
|
||||
static char homebuf[PATH_MAX];
|
||||
snprintf(homebuf, PATH_MAX, "%s%s", homedrive, homepath);
|
||||
return homebuf;
|
||||
}
|
||||
errno = ENOENT;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return home;
|
||||
}
|
||||
|
||||
/*
|
||||
* Core I/O Operations
|
||||
*/
|
||||
|
||||
int
|
||||
mrb_io_hal_open(mrb_state *mrb, const char *path, int flags, uint32_t mode)
|
||||
{
|
||||
int fd;
|
||||
(void)mrb;
|
||||
|
||||
/* Windows uses _open with slightly different flags */
|
||||
fd = _open(path, flags | _O_BINARY, (int)mode);
|
||||
if (fd == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Set close-on-exec for non-standard descriptors */
|
||||
if (fd > 2) {
|
||||
SetHandleInformation((HANDLE)_get_osfhandle(fd), HANDLE_FLAG_INHERIT, 0);
|
||||
}
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
int
|
||||
mrb_io_hal_close(mrb_state *mrb, int fd)
|
||||
{
|
||||
(void)mrb;
|
||||
return _close(fd);
|
||||
}
|
||||
|
||||
int64_t
|
||||
mrb_io_hal_read(mrb_state *mrb, int fd, void *buf, size_t count)
|
||||
{
|
||||
int n;
|
||||
(void)mrb;
|
||||
|
||||
n = _read(fd, buf, (unsigned int)count);
|
||||
return (int64_t)n;
|
||||
}
|
||||
|
||||
int64_t
|
||||
mrb_io_hal_write(mrb_state *mrb, int fd, const void *buf, size_t count)
|
||||
{
|
||||
int n;
|
||||
(void)mrb;
|
||||
|
||||
n = _write(fd, buf, (unsigned int)count);
|
||||
return (int64_t)n;
|
||||
}
|
||||
|
||||
int64_t
|
||||
mrb_io_hal_lseek(mrb_state *mrb, int fd, int64_t offset, int whence)
|
||||
{
|
||||
__int64 pos;
|
||||
int win_whence;
|
||||
(void)mrb;
|
||||
|
||||
/* Convert MRB_IO_SEEK_* to Windows SEEK_* */
|
||||
switch (whence) {
|
||||
case MRB_IO_SEEK_SET: win_whence = SEEK_SET; break;
|
||||
case MRB_IO_SEEK_CUR: win_whence = SEEK_CUR; break;
|
||||
case MRB_IO_SEEK_END: win_whence = SEEK_END; break;
|
||||
default:
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
pos = _lseeki64(fd, (__int64)offset, win_whence);
|
||||
return (int64_t)pos;
|
||||
}
|
||||
|
||||
int
|
||||
mrb_io_hal_dup(mrb_state *mrb, int fd)
|
||||
{
|
||||
int new_fd;
|
||||
(void)mrb;
|
||||
|
||||
new_fd = _dup(fd);
|
||||
if (new_fd == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Set close-on-exec */
|
||||
if (new_fd > 2) {
|
||||
SetHandleInformation((HANDLE)_get_osfhandle(new_fd), HANDLE_FLAG_INHERIT, 0);
|
||||
}
|
||||
|
||||
return new_fd;
|
||||
}
|
||||
|
||||
int
|
||||
mrb_io_hal_fcntl(mrb_state *mrb, int fd, int cmd, int arg)
|
||||
{
|
||||
/* Windows has limited fcntl support */
|
||||
(void)mrb;
|
||||
(void)fd;
|
||||
(void)cmd;
|
||||
(void)arg;
|
||||
errno = ENOSYS;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int
|
||||
mrb_io_hal_isatty(mrb_state *mrb, int fd)
|
||||
{
|
||||
(void)mrb;
|
||||
return _isatty(fd) ? 1 : 0;
|
||||
}
|
||||
|
||||
int
|
||||
mrb_io_hal_pipe(mrb_state *mrb, int fds[2])
|
||||
{
|
||||
int ret;
|
||||
(void)mrb;
|
||||
|
||||
ret = _pipe(fds, 4096, _O_BINARY);
|
||||
if (ret == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Set close-on-exec on both ends */
|
||||
SetHandleInformation((HANDLE)_get_osfhandle(fds[0]), HANDLE_FLAG_INHERIT, 0);
|
||||
SetHandleInformation((HANDLE)_get_osfhandle(fds[1]), HANDLE_FLAG_INHERIT, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Process Operations
|
||||
*/
|
||||
|
||||
int
|
||||
mrb_io_hal_spawn_process(mrb_state *mrb, const char *cmd,
|
||||
int stdin_fd, int stdout_fd, int stderr_fd,
|
||||
int *pid)
|
||||
{
|
||||
STARTUPINFO si;
|
||||
PROCESS_INFORMATION pi;
|
||||
HANDLE h_stdin = INVALID_HANDLE_VALUE;
|
||||
HANDLE h_stdout = INVALID_HANDLE_VALUE;
|
||||
HANDLE h_stderr = INVALID_HANDLE_VALUE;
|
||||
char cmdline[8192];
|
||||
BOOL ret;
|
||||
(void)mrb;
|
||||
|
||||
/* Skip leading whitespace */
|
||||
while (*cmd == ' ' || *cmd == '\t' || *cmd == '\n') {
|
||||
cmd++;
|
||||
}
|
||||
|
||||
if (!*cmd) {
|
||||
errno = ENOENT;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Build command line - use cmd.exe to execute */
|
||||
snprintf(cmdline, sizeof(cmdline), "cmd.exe /c %s", cmd);
|
||||
|
||||
/* Setup startup info */
|
||||
memset(&si, 0, sizeof(si));
|
||||
si.cb = sizeof(si);
|
||||
si.dwFlags = STARTF_USESTDHANDLES;
|
||||
|
||||
/* Convert file descriptors to handles */
|
||||
if (stdin_fd != -1) {
|
||||
h_stdin = (HANDLE)_get_osfhandle(stdin_fd);
|
||||
si.hStdInput = h_stdin;
|
||||
}
|
||||
else {
|
||||
si.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
|
||||
}
|
||||
|
||||
if (stdout_fd != -1) {
|
||||
h_stdout = (HANDLE)_get_osfhandle(stdout_fd);
|
||||
si.hStdOutput = h_stdout;
|
||||
}
|
||||
else {
|
||||
si.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
}
|
||||
|
||||
if (stderr_fd != -1) {
|
||||
h_stderr = (HANDLE)_get_osfhandle(stderr_fd);
|
||||
si.hStdError = h_stderr;
|
||||
}
|
||||
else {
|
||||
si.hStdError = GetStdHandle(STD_ERROR_HANDLE);
|
||||
}
|
||||
|
||||
/* Create process */
|
||||
memset(&pi, 0, sizeof(pi));
|
||||
ret = CreateProcess(NULL, cmdline, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi);
|
||||
|
||||
if (!ret) {
|
||||
set_errno_from_win_error(GetLastError());
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Close thread handle - we don't need it */
|
||||
CloseHandle(pi.hThread);
|
||||
|
||||
/* Store process handle as PID (will be used in waitpid) */
|
||||
*pid = (int)pi.hProcess;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
mrb_io_hal_waitpid(mrb_state *mrb, int pid, int *status, int options)
|
||||
{
|
||||
HANDLE h = (HANDLE)pid;
|
||||
DWORD wait_result;
|
||||
DWORD exit_code;
|
||||
DWORD timeout;
|
||||
(void)mrb;
|
||||
|
||||
/* Convert options to timeout */
|
||||
timeout = (options != 0) ? 0 : INFINITE;
|
||||
|
||||
wait_result = WaitForSingleObject(h, timeout);
|
||||
|
||||
if (wait_result == WAIT_TIMEOUT) {
|
||||
return 0; /* Non-blocking wait, no change */
|
||||
}
|
||||
|
||||
if (wait_result != WAIT_OBJECT_0) {
|
||||
set_errno_from_win_error(GetLastError());
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Get exit code */
|
||||
if (!GetExitCodeProcess(h, &exit_code)) {
|
||||
set_errno_from_win_error(GetLastError());
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (status != NULL) {
|
||||
/* Store exit code in status (shifted to match Unix convention) */
|
||||
*status = (int)(exit_code << 8);
|
||||
}
|
||||
|
||||
/* Close process handle */
|
||||
CloseHandle(h);
|
||||
|
||||
return pid;
|
||||
}
|
||||
|
||||
/*
|
||||
* I/O Multiplexing
|
||||
*/
|
||||
|
||||
struct mrb_io_fdset {
|
||||
fd_set fds;
|
||||
};
|
||||
|
||||
mrb_io_fdset*
|
||||
mrb_io_hal_fdset_alloc(mrb_state *mrb)
|
||||
{
|
||||
mrb_io_fdset *fdset = (mrb_io_fdset*)mrb_malloc(mrb, sizeof(mrb_io_fdset));
|
||||
FD_ZERO(&fdset->fds);
|
||||
return fdset;
|
||||
}
|
||||
|
||||
void
|
||||
mrb_io_hal_fdset_free(mrb_state *mrb, mrb_io_fdset *fdset)
|
||||
{
|
||||
if (fdset) {
|
||||
mrb_free(mrb, fdset);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
mrb_io_hal_fdset_zero(mrb_state *mrb, mrb_io_fdset *fdset)
|
||||
{
|
||||
(void)mrb;
|
||||
if (fdset) {
|
||||
FD_ZERO(&fdset->fds);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
mrb_io_hal_fdset_set(mrb_state *mrb, int fd, mrb_io_fdset *fdset)
|
||||
{
|
||||
(void)mrb;
|
||||
if (fdset) {
|
||||
FD_SET(fd, &fdset->fds);
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
mrb_io_hal_fdset_isset(mrb_state *mrb, int fd, mrb_io_fdset *fdset)
|
||||
{
|
||||
(void)mrb;
|
||||
if (fdset) {
|
||||
return FD_ISSET(fd, &fdset->fds);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
mrb_io_hal_select(mrb_state *mrb, int nfds,
|
||||
mrb_io_fdset *readfds,
|
||||
mrb_io_fdset *writefds,
|
||||
mrb_io_fdset *errorfds,
|
||||
mrb_io_timeval *timeout)
|
||||
{
|
||||
fd_set *r = readfds ? &readfds->fds : NULL;
|
||||
fd_set *w = writefds ? &writefds->fds : NULL;
|
||||
fd_set *e = errorfds ? &errorfds->fds : NULL;
|
||||
struct timeval *tv = NULL;
|
||||
struct timeval tv_storage;
|
||||
(void)mrb;
|
||||
(void)nfds; /* Windows select() doesn't use nfds */
|
||||
|
||||
if (timeout) {
|
||||
tv_storage.tv_sec = (long)timeout->tv_sec;
|
||||
tv_storage.tv_usec = (long)timeout->tv_usec;
|
||||
tv = &tv_storage;
|
||||
}
|
||||
|
||||
return select(0, r, w, e, tv);
|
||||
}
|
||||
|
||||
/*
|
||||
* HAL Initialization/Finalization
|
||||
*/
|
||||
|
||||
void
|
||||
mrb_io_hal_init(mrb_state *mrb)
|
||||
{
|
||||
(void)mrb;
|
||||
/* Initialize Winsock for select() support */
|
||||
WSADATA wsaData;
|
||||
WSAStartup(MAKEWORD(2, 2), &wsaData);
|
||||
}
|
||||
|
||||
void
|
||||
mrb_io_hal_final(mrb_state *mrb)
|
||||
{
|
||||
(void)mrb;
|
||||
/* Cleanup Winsock */
|
||||
WSACleanup();
|
||||
}
|
||||
|
||||
/*
|
||||
* Gem initialization
|
||||
*/
|
||||
|
||||
void
|
||||
mrb_hal_win_io_gem_init(mrb_state *mrb)
|
||||
{
|
||||
(void)mrb;
|
||||
/* HAL interface functions are called by mruby-io gem */
|
||||
}
|
||||
|
||||
void
|
||||
mrb_hal_win_io_gem_final(mrb_state *mrb)
|
||||
{
|
||||
(void)mrb;
|
||||
/* Cleanup handled by mrb_io_hal_final called from mruby-io */
|
||||
}
|
||||
@@ -0,0 +1,451 @@
|
||||
/*
|
||||
** io_hal.h - IO Hardware Abstraction Layer (HAL)
|
||||
**
|
||||
** See Copyright Notice in mruby.h
|
||||
**
|
||||
** This header defines the HAL interface for platform-specific I/O operations.
|
||||
** Platform-specific implementations (hal-posix-io, hal-win-io, etc.) must
|
||||
** provide all functions declared here.
|
||||
*/
|
||||
|
||||
#ifndef MRUBY_IO_HAL_H
|
||||
#define MRUBY_IO_HAL_H
|
||||
|
||||
#include <mruby.h>
|
||||
#include <stdint.h>
|
||||
|
||||
/*
|
||||
* Platform-independent type definitions
|
||||
*/
|
||||
|
||||
/* File status structure - platform-independent representation */
|
||||
typedef struct mrb_io_stat {
|
||||
uint64_t st_dev; /* Device ID */
|
||||
uint64_t st_ino; /* Inode number */
|
||||
uint32_t st_mode; /* File mode/permissions */
|
||||
uint32_t st_nlink; /* Number of hard links */
|
||||
uint32_t st_uid; /* User ID */
|
||||
uint32_t st_gid; /* Group ID */
|
||||
uint64_t st_rdev; /* Device ID (if special file) */
|
||||
int64_t st_size; /* File size in bytes */
|
||||
int64_t st_atime; /* Last access time */
|
||||
int64_t st_mtime; /* Last modification time */
|
||||
int64_t st_ctime; /* Last status change time */
|
||||
int64_t st_blksize; /* Block size for filesystem I/O */
|
||||
int64_t st_blocks; /* Number of 512B blocks allocated */
|
||||
} mrb_io_stat;
|
||||
|
||||
/* Timeval structure for select() */
|
||||
typedef struct mrb_io_timeval {
|
||||
int64_t tv_sec; /* Seconds */
|
||||
int64_t tv_usec; /* Microseconds */
|
||||
} mrb_io_timeval;
|
||||
|
||||
/* File descriptor set for select() */
|
||||
typedef struct mrb_io_fdset mrb_io_fdset;
|
||||
|
||||
/*
|
||||
* File mode constants (POSIX-style)
|
||||
*/
|
||||
|
||||
/* File type masks */
|
||||
#define MRB_IO_S_IFMT 0170000 /* Type of file mask */
|
||||
#define MRB_IO_S_IFSOCK 0140000 /* Socket */
|
||||
#define MRB_IO_S_IFLNK 0120000 /* Symbolic link */
|
||||
#define MRB_IO_S_IFREG 0100000 /* Regular file */
|
||||
#define MRB_IO_S_IFBLK 0060000 /* Block device */
|
||||
#define MRB_IO_S_IFDIR 0040000 /* Directory */
|
||||
#define MRB_IO_S_IFCHR 0020000 /* Character device */
|
||||
#define MRB_IO_S_IFIFO 0010000 /* FIFO */
|
||||
|
||||
/* File type test macros */
|
||||
#define MRB_IO_S_ISREG(m) (((m) & MRB_IO_S_IFMT) == MRB_IO_S_IFREG)
|
||||
#define MRB_IO_S_ISDIR(m) (((m) & MRB_IO_S_IFMT) == MRB_IO_S_IFDIR)
|
||||
#define MRB_IO_S_ISCHR(m) (((m) & MRB_IO_S_IFMT) == MRB_IO_S_IFCHR)
|
||||
#define MRB_IO_S_ISBLK(m) (((m) & MRB_IO_S_IFMT) == MRB_IO_S_IFBLK)
|
||||
#define MRB_IO_S_ISFIFO(m) (((m) & MRB_IO_S_IFMT) == MRB_IO_S_IFIFO)
|
||||
#define MRB_IO_S_ISLNK(m) (((m) & MRB_IO_S_IFMT) == MRB_IO_S_IFLNK)
|
||||
#define MRB_IO_S_ISSOCK(m) (((m) & MRB_IO_S_IFMT) == MRB_IO_S_IFSOCK)
|
||||
|
||||
/* File lock constants */
|
||||
#define MRB_IO_LOCK_SH 1 /* Shared lock */
|
||||
#define MRB_IO_LOCK_EX 2 /* Exclusive lock */
|
||||
#define MRB_IO_LOCK_NB 4 /* Non-blocking */
|
||||
#define MRB_IO_LOCK_UN 8 /* Unlock */
|
||||
|
||||
/* Seek constants */
|
||||
#define MRB_IO_SEEK_SET 0 /* Seek from beginning */
|
||||
#define MRB_IO_SEEK_CUR 1 /* Seek from current position */
|
||||
#define MRB_IO_SEEK_END 2 /* Seek from end */
|
||||
|
||||
/*
|
||||
* HAL Interface - File Operations
|
||||
*/
|
||||
|
||||
/**
|
||||
* Get file status by path
|
||||
*
|
||||
* @param mrb mruby state
|
||||
* @param path File path (UTF-8)
|
||||
* @param st Output stat structure
|
||||
* @return 0 on success, -1 on error (sets errno)
|
||||
*/
|
||||
int mrb_io_hal_stat(mrb_state *mrb, const char *path, mrb_io_stat *st);
|
||||
|
||||
/**
|
||||
* Get file status by descriptor
|
||||
*
|
||||
* @param mrb mruby state
|
||||
* @param fd File descriptor
|
||||
* @param st Output stat structure
|
||||
* @return 0 on success, -1 on error (sets errno)
|
||||
*/
|
||||
int mrb_io_hal_fstat(mrb_state *mrb, int fd, mrb_io_stat *st);
|
||||
|
||||
/**
|
||||
* Get link status (don't follow symlinks)
|
||||
*
|
||||
* @param mrb mruby state
|
||||
* @param path File path (UTF-8)
|
||||
* @param st Output stat structure
|
||||
* @return 0 on success, -1 on error (sets errno)
|
||||
*/
|
||||
int mrb_io_hal_lstat(mrb_state *mrb, const char *path, mrb_io_stat *st);
|
||||
|
||||
/**
|
||||
* Change file permissions
|
||||
*
|
||||
* @param mrb mruby state
|
||||
* @param path File path (UTF-8)
|
||||
* @param mode New permission mode
|
||||
* @return 0 on success, -1 on error (sets errno)
|
||||
*/
|
||||
int mrb_io_hal_chmod(mrb_state *mrb, const char *path, uint32_t mode);
|
||||
|
||||
/**
|
||||
* Set/get file creation mask
|
||||
*
|
||||
* @param mrb mruby state
|
||||
* @param mask New umask value (if < 0, only returns current value)
|
||||
* @return Previous umask value
|
||||
*/
|
||||
uint32_t mrb_io_hal_umask(mrb_state *mrb, int32_t mask);
|
||||
|
||||
/**
|
||||
* Truncate file to specified length
|
||||
*
|
||||
* @param mrb mruby state
|
||||
* @param fd File descriptor
|
||||
* @param length New file length
|
||||
* @return 0 on success, -1 on error (sets errno)
|
||||
*/
|
||||
int mrb_io_hal_ftruncate(mrb_state *mrb, int fd, int64_t length);
|
||||
|
||||
/**
|
||||
* Apply or remove advisory lock on file
|
||||
*
|
||||
* @param mrb mruby state
|
||||
* @param fd File descriptor
|
||||
* @param operation Lock operation (MRB_IO_LOCK_*)
|
||||
* @return 0 on success, -1 on error (sets errno)
|
||||
*/
|
||||
int mrb_io_hal_flock(mrb_state *mrb, int fd, int operation);
|
||||
|
||||
/**
|
||||
* Delete a file
|
||||
*
|
||||
* @param mrb mruby state
|
||||
* @param path File path (UTF-8)
|
||||
* @return 0 on success, -1 on error (sets errno)
|
||||
*/
|
||||
int mrb_io_hal_unlink(mrb_state *mrb, const char *path);
|
||||
|
||||
/**
|
||||
* Rename a file
|
||||
*
|
||||
* @param mrb mruby state
|
||||
* @param oldpath Old file path (UTF-8)
|
||||
* @param newpath New file path (UTF-8)
|
||||
* @return 0 on success, -1 on error (sets errno)
|
||||
*/
|
||||
int mrb_io_hal_rename(mrb_state *mrb, const char *oldpath, const char *newpath);
|
||||
|
||||
/**
|
||||
* Create a symbolic link
|
||||
*
|
||||
* @param mrb mruby state
|
||||
* @param target Target path (UTF-8)
|
||||
* @param linkpath Link path (UTF-8)
|
||||
* @return 0 on success, -1 on error (sets errno)
|
||||
*/
|
||||
int mrb_io_hal_symlink(mrb_state *mrb, const char *target, const char *linkpath);
|
||||
|
||||
/**
|
||||
* Read value of a symbolic link
|
||||
*
|
||||
* @param mrb mruby state
|
||||
* @param path Symlink path (UTF-8)
|
||||
* @param buf Buffer to store result (UTF-8)
|
||||
* @param bufsize Buffer size
|
||||
* @return Number of bytes placed in buf, -1 on error (sets errno)
|
||||
*/
|
||||
int64_t mrb_io_hal_readlink(mrb_state *mrb, const char *path, char *buf, size_t bufsize);
|
||||
|
||||
/**
|
||||
* Resolve pathname to absolute path
|
||||
*
|
||||
* @param mrb mruby state
|
||||
* @param path Pathname (UTF-8)
|
||||
* @param resolved Buffer for resolved path (must be at least PATH_MAX size)
|
||||
* @return Pointer to resolved on success, NULL on error (sets errno)
|
||||
*/
|
||||
char* mrb_io_hal_realpath(mrb_state *mrb, const char *path, char *resolved);
|
||||
|
||||
/**
|
||||
* Get current working directory
|
||||
*
|
||||
* @param mrb mruby state
|
||||
* @param buf Buffer to store result (UTF-8)
|
||||
* @param size Buffer size
|
||||
* @return Pointer to buf on success, NULL on error (sets errno)
|
||||
*/
|
||||
char* mrb_io_hal_getcwd(mrb_state *mrb, char *buf, size_t size);
|
||||
|
||||
/**
|
||||
* Get environment variable
|
||||
*
|
||||
* @param mrb mruby state
|
||||
* @param name Variable name
|
||||
* @return Value string (UTF-8) or NULL if not found
|
||||
*/
|
||||
const char* mrb_io_hal_getenv(mrb_state *mrb, const char *name);
|
||||
|
||||
/**
|
||||
* Get user's home directory
|
||||
*
|
||||
* @param mrb mruby state
|
||||
* @param username User name (NULL for current user)
|
||||
* @return Home directory path (UTF-8) or NULL on error (sets errno)
|
||||
*/
|
||||
const char* mrb_io_hal_gethome(mrb_state *mrb, const char *username);
|
||||
|
||||
/*
|
||||
* HAL Interface - Core I/O Operations
|
||||
*/
|
||||
|
||||
/**
|
||||
* Open file
|
||||
*
|
||||
* @param mrb mruby state
|
||||
* @param path File path (UTF-8)
|
||||
* @param flags Open flags (O_RDONLY, O_WRONLY, O_RDWR, etc.)
|
||||
* @param mode Creation mode (used if O_CREAT is set)
|
||||
* @return File descriptor on success, -1 on error (sets errno)
|
||||
*/
|
||||
int mrb_io_hal_open(mrb_state *mrb, const char *path, int flags, uint32_t mode);
|
||||
|
||||
/**
|
||||
* Close file descriptor
|
||||
*
|
||||
* @param mrb mruby state
|
||||
* @param fd File descriptor
|
||||
* @return 0 on success, -1 on error (sets errno)
|
||||
*/
|
||||
int mrb_io_hal_close(mrb_state *mrb, int fd);
|
||||
|
||||
/**
|
||||
* Read from file descriptor
|
||||
*
|
||||
* @param mrb mruby state
|
||||
* @param fd File descriptor
|
||||
* @param buf Buffer to store data
|
||||
* @param count Maximum bytes to read
|
||||
* @return Number of bytes read, 0 on EOF, -1 on error (sets errno)
|
||||
*/
|
||||
int64_t mrb_io_hal_read(mrb_state *mrb, int fd, void *buf, size_t count);
|
||||
|
||||
/**
|
||||
* Write to file descriptor
|
||||
*
|
||||
* @param mrb mruby state
|
||||
* @param fd File descriptor
|
||||
* @param buf Data to write
|
||||
* @param count Number of bytes to write
|
||||
* @return Number of bytes written, -1 on error (sets errno)
|
||||
*/
|
||||
int64_t mrb_io_hal_write(mrb_state *mrb, int fd, const void *buf, size_t count);
|
||||
|
||||
/**
|
||||
* Reposition file offset
|
||||
*
|
||||
* @param mrb mruby state
|
||||
* @param fd File descriptor
|
||||
* @param offset Offset value
|
||||
* @param whence Reference point (MRB_IO_SEEK_SET/CUR/END)
|
||||
* @return New offset from beginning of file, -1 on error (sets errno)
|
||||
*/
|
||||
int64_t mrb_io_hal_lseek(mrb_state *mrb, int fd, int64_t offset, int whence);
|
||||
|
||||
/**
|
||||
* Duplicate file descriptor
|
||||
*
|
||||
* @param mrb mruby state
|
||||
* @param fd File descriptor to duplicate
|
||||
* @return New descriptor on success, -1 on error (sets errno)
|
||||
*/
|
||||
int mrb_io_hal_dup(mrb_state *mrb, int fd);
|
||||
|
||||
/**
|
||||
* Manipulate file descriptor
|
||||
*
|
||||
* @param mrb mruby state
|
||||
* @param fd File descriptor
|
||||
* @param cmd Command (F_GETFD, F_SETFD, etc.)
|
||||
* @param arg Command argument
|
||||
* @return Depends on command, -1 on error (sets errno)
|
||||
*/
|
||||
int mrb_io_hal_fcntl(mrb_state *mrb, int fd, int cmd, int arg);
|
||||
|
||||
/**
|
||||
* Check if descriptor refers to terminal
|
||||
*
|
||||
* @param mrb mruby state
|
||||
* @param fd File descriptor
|
||||
* @return 1 if TTY, 0 if not, -1 on error (sets errno)
|
||||
*/
|
||||
int mrb_io_hal_isatty(mrb_state *mrb, int fd);
|
||||
|
||||
/**
|
||||
* Create pipe
|
||||
*
|
||||
* @param mrb mruby state
|
||||
* @param fds Array to store two file descriptors [read_end, write_end]
|
||||
* @return 0 on success, -1 on error (sets errno)
|
||||
*/
|
||||
int mrb_io_hal_pipe(mrb_state *mrb, int fds[2]);
|
||||
|
||||
/*
|
||||
* HAL Interface - Process Operations
|
||||
*/
|
||||
|
||||
/**
|
||||
* Spawn a new process
|
||||
*
|
||||
* Creates a new process and executes the command. File descriptors can be
|
||||
* redirected for stdin/stdout/stderr (-1 means don't redirect).
|
||||
*
|
||||
* POSIX: Uses fork() + dup2() + execl()
|
||||
* Windows: Uses CreateProcess() with STARTUPINFO
|
||||
*
|
||||
* @param mrb mruby state
|
||||
* @param cmd Command to execute (shell command)
|
||||
* @param stdin_fd File descriptor to use for stdin (-1 = don't redirect)
|
||||
* @param stdout_fd File descriptor to use for stdout (-1 = don't redirect)
|
||||
* @param stderr_fd File descriptor to use for stderr (-1 = don't redirect)
|
||||
* @param pid Output parameter for process ID
|
||||
* @return 0 on success, -1 on error (sets errno)
|
||||
*/
|
||||
int mrb_io_hal_spawn_process(mrb_state *mrb, const char *cmd,
|
||||
int stdin_fd, int stdout_fd, int stderr_fd,
|
||||
int *pid);
|
||||
|
||||
/**
|
||||
* Wait for process to change state
|
||||
*
|
||||
* @param mrb mruby state
|
||||
* @param pid Process ID to wait for
|
||||
* @param status Output parameter for exit status
|
||||
* @param options Wait options (0 for blocking wait)
|
||||
* @return Process ID on success, -1 on error (sets errno)
|
||||
*/
|
||||
int mrb_io_hal_waitpid(mrb_state *mrb, int pid, int *status, int options);
|
||||
|
||||
/*
|
||||
* HAL Interface - I/O Multiplexing
|
||||
*/
|
||||
|
||||
/**
|
||||
* Allocate file descriptor set
|
||||
*
|
||||
* @param mrb mruby state
|
||||
* @return Pointer to fdset or NULL on error
|
||||
*/
|
||||
mrb_io_fdset* mrb_io_hal_fdset_alloc(mrb_state *mrb);
|
||||
|
||||
/**
|
||||
* Free file descriptor set
|
||||
*
|
||||
* @param mrb mruby state
|
||||
* @param fdset File descriptor set to free
|
||||
*/
|
||||
void mrb_io_hal_fdset_free(mrb_state *mrb, mrb_io_fdset *fdset);
|
||||
|
||||
/**
|
||||
* Clear file descriptor set
|
||||
*
|
||||
* @param mrb mruby state
|
||||
* @param fdset File descriptor set
|
||||
*/
|
||||
void mrb_io_hal_fdset_zero(mrb_state *mrb, mrb_io_fdset *fdset);
|
||||
|
||||
/**
|
||||
* Add descriptor to set
|
||||
*
|
||||
* @param mrb mruby state
|
||||
* @param fd File descriptor
|
||||
* @param fdset File descriptor set
|
||||
*/
|
||||
void mrb_io_hal_fdset_set(mrb_state *mrb, int fd, mrb_io_fdset *fdset);
|
||||
|
||||
/**
|
||||
* Check if descriptor is in set
|
||||
*
|
||||
* @param mrb mruby state
|
||||
* @param fd File descriptor
|
||||
* @param fdset File descriptor set
|
||||
* @return Non-zero if fd is in set, 0 otherwise
|
||||
*/
|
||||
int mrb_io_hal_fdset_isset(mrb_state *mrb, int fd, mrb_io_fdset *fdset);
|
||||
|
||||
/**
|
||||
* Monitor multiple file descriptors
|
||||
*
|
||||
* @param mrb mruby state
|
||||
* @param nfds Highest file descriptor number + 1
|
||||
* @param readfds Set of descriptors to check for reading (NULL = ignore)
|
||||
* @param writefds Set of descriptors to check for writing (NULL = ignore)
|
||||
* @param errorfds Set of descriptors to check for errors (NULL = ignore)
|
||||
* @param timeout Timeout (NULL = block indefinitely)
|
||||
* @return Number of ready descriptors, 0 on timeout, -1 on error (sets errno)
|
||||
*/
|
||||
int mrb_io_hal_select(mrb_state *mrb, int nfds,
|
||||
mrb_io_fdset *readfds,
|
||||
mrb_io_fdset *writefds,
|
||||
mrb_io_fdset *errorfds,
|
||||
mrb_io_timeval *timeout);
|
||||
|
||||
/*
|
||||
* HAL Initialization/Finalization
|
||||
*/
|
||||
|
||||
/**
|
||||
* Initialize I/O HAL
|
||||
*
|
||||
* Called during gem initialization. Platform-specific HAL should perform
|
||||
* any necessary setup here.
|
||||
*
|
||||
* @param mrb mruby state
|
||||
*/
|
||||
void mrb_io_hal_init(mrb_state *mrb);
|
||||
|
||||
/**
|
||||
* Finalize I/O HAL
|
||||
*
|
||||
* Called during gem finalization. Platform-specific HAL should perform
|
||||
* any necessary cleanup here.
|
||||
*
|
||||
* @param mrb mruby state
|
||||
*/
|
||||
void mrb_io_hal_final(mrb_state *mrb);
|
||||
|
||||
#endif /* MRUBY_IO_HAL_H */
|
||||
@@ -3,9 +3,32 @@ MRuby::Gem::Specification.new('mruby-io') do |spec|
|
||||
spec.authors = ['Internet Initiative Japan Inc.', 'mruby developers']
|
||||
spec.summary = 'IO and File class'
|
||||
|
||||
if spec.for_windows?
|
||||
spec.linker.libraries << "ws2_32"
|
||||
end
|
||||
spec.build.defines << "HAVE_MRUBY_IO_GEM"
|
||||
spec.add_test_dependency 'mruby-time', core: 'mruby-time'
|
||||
|
||||
# Check if HAL gem is loaded
|
||||
# HAL gems must be explicitly specified in build config (recommended) or via auto-selection below
|
||||
spec.build.gems.one? { |g| g.name =~ /^hal-.*-io$/ } or begin
|
||||
# No HAL found - determine appropriate error message or auto-load
|
||||
suggested_hal = if spec.for_windows?
|
||||
'hal-win-io'
|
||||
elsif RUBY_PLATFORM =~ /linux|darwin|bsd/
|
||||
'hal-posix-io'
|
||||
else
|
||||
nil
|
||||
end
|
||||
|
||||
if suggested_hal
|
||||
# Auto-load HAL gem for convenience (for development)
|
||||
# This works because HAL gems declare dependency on mruby-io
|
||||
warn "mruby-io: No HAL specified, loading #{suggested_hal} (explicit selection recommended)"
|
||||
spec.build.gem core: suggested_hal
|
||||
else
|
||||
# Unknown platform - fail with helpful message
|
||||
fail "mruby-io: No HAL available for platform '#{RUBY_PLATFORM}'.\n" \
|
||||
"Please specify HAL gem explicitly in your build config:\n" \
|
||||
" conf.gem core: 'hal-posix-io' # For Linux/macOS/BSD\n" \
|
||||
" conf.gem core: 'hal-win-io' # For Windows"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
+50
-79
@@ -10,6 +10,7 @@
|
||||
#include <mruby/ext/io.h>
|
||||
#include <mruby/error.h>
|
||||
#include <mruby/presym.h>
|
||||
#include <io_hal.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
@@ -19,6 +20,18 @@
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
/* Undefine system macros that conflict with mrb_io_stat field names */
|
||||
#ifdef st_atime
|
||||
#undef st_atime
|
||||
#endif
|
||||
#ifdef st_mtime
|
||||
#undef st_mtime
|
||||
#endif
|
||||
#ifdef st_ctime
|
||||
#undef st_ctime
|
||||
#endif
|
||||
|
||||
#if defined(_WIN32)
|
||||
#include <windows.h>
|
||||
#include <io.h>
|
||||
@@ -62,17 +75,18 @@
|
||||
#define DIRSEP_P(ch) ((ch) == '/')
|
||||
#endif
|
||||
|
||||
/* Use HAL lock constants */
|
||||
#ifndef LOCK_SH
|
||||
#define LOCK_SH 1
|
||||
#define LOCK_SH MRB_IO_LOCK_SH
|
||||
#endif
|
||||
#ifndef LOCK_EX
|
||||
#define LOCK_EX 2
|
||||
#define LOCK_EX MRB_IO_LOCK_EX
|
||||
#endif
|
||||
#ifndef LOCK_NB
|
||||
#define LOCK_NB 4
|
||||
#define LOCK_NB MRB_IO_LOCK_NB
|
||||
#endif
|
||||
#ifndef LOCK_UN
|
||||
#define LOCK_UN 8
|
||||
#define LOCK_UN MRB_IO_LOCK_UN
|
||||
#endif
|
||||
|
||||
#if !defined(_WIN32) || defined(MRB_MINGW32_LEGACY)
|
||||
@@ -117,21 +131,16 @@ flock(int fd, int operation)
|
||||
static mrb_value
|
||||
mrb_file_s_umask(mrb_state *mrb, mrb_value klass)
|
||||
{
|
||||
#if defined(_WIN32)
|
||||
/* nothing to do on windows */
|
||||
return mrb_fixnum_value(0);
|
||||
mrb_int mask;
|
||||
uint32_t omask;
|
||||
|
||||
#else
|
||||
mrb_int mask, omask;
|
||||
if (mrb_get_args(mrb, "|i", &mask) == 0) {
|
||||
omask = umask(0);
|
||||
umask(omask);
|
||||
omask = mrb_io_hal_umask(mrb, -1);
|
||||
}
|
||||
else {
|
||||
omask = umask(mask);
|
||||
omask = mrb_io_hal_umask(mrb, (int32_t)mask);
|
||||
}
|
||||
return mrb_fixnum_value(omask);
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -155,7 +164,7 @@ mrb_file_s_unlink(mrb_state *mrb, mrb_value obj)
|
||||
mrb_ensure_string_type(mrb, pathv);
|
||||
const char *utf8_path = RSTRING_CSTR(mrb, pathv);
|
||||
char *path = mrb_locale_from_utf8(utf8_path, -1);
|
||||
if (UNLINK(path) < 0) {
|
||||
if (mrb_io_hal_unlink(mrb, path) < 0) {
|
||||
mrb_locale_free(path);
|
||||
mrb_sys_fail(mrb, utf8_path);
|
||||
}
|
||||
@@ -180,9 +189,12 @@ mrb_file_s_rename(mrb_state *mrb, mrb_value obj)
|
||||
mrb_get_args(mrb, "SS", &from, &to);
|
||||
char *src = mrb_locale_from_utf8(RSTRING_CSTR(mrb, from), -1);
|
||||
char *dst = mrb_locale_from_utf8(RSTRING_CSTR(mrb, to), -1);
|
||||
if (rename(src, dst) < 0) {
|
||||
if (mrb_io_hal_rename(mrb, src, dst) < 0) {
|
||||
#if defined(_WIN32)
|
||||
if (CHMOD(dst, 0666) == 0 && UNLINK(dst) == 0 && rename(src, dst) == 0) {
|
||||
/* Windows retry: try chmod+unlink+rename if initial rename fails */
|
||||
if (mrb_io_hal_chmod(mrb, dst, 0666) == 0 &&
|
||||
mrb_io_hal_unlink(mrb, dst) == 0 &&
|
||||
mrb_io_hal_rename(mrb, src, dst) == 0) {
|
||||
mrb_locale_free(src);
|
||||
mrb_locale_free(dst);
|
||||
return mrb_fixnum_value(0);
|
||||
@@ -389,7 +401,7 @@ mrb_file_realpath(mrb_state *mrb, mrb_value klass)
|
||||
}
|
||||
char *cpath = mrb_locale_from_utf8(RSTRING_CSTR(mrb, pathname), -1);
|
||||
mrb_value result = mrb_str_new_capa(mrb, PATH_MAX);
|
||||
if (realpath(cpath, RSTRING_PTR(result)) == NULL) {
|
||||
if (mrb_io_hal_realpath(mrb, cpath, RSTRING_PTR(result)) == NULL) {
|
||||
mrb_locale_free(cpath);
|
||||
mrb_sys_fail(mrb, RSTRING_CSTR(mrb, pathname));
|
||||
return result; /* not reached */
|
||||
@@ -404,7 +416,7 @@ path_getwd(mrb_state *mrb)
|
||||
{
|
||||
char buf[MAXPATHLEN];
|
||||
|
||||
if (GETCWD(buf, MAXPATHLEN) == NULL) {
|
||||
if (mrb_io_hal_getcwd(mrb, buf, MAXPATHLEN) == NULL) {
|
||||
mrb_sys_fail(mrb, "getcwd(2)");
|
||||
}
|
||||
char *utf8 = mrb_utf8_from_locale(buf, -1);
|
||||
@@ -516,12 +528,7 @@ path_gethome(mrb_state *mrb, const char **pathp)
|
||||
ptrdiff_t len = *pathp - username;
|
||||
|
||||
if (len == 0) {
|
||||
home = getenv("HOME");
|
||||
#ifdef _WIN32
|
||||
if (home == NULL) {
|
||||
home = getenv("USERPROFILE");
|
||||
}
|
||||
#endif
|
||||
home = mrb_io_hal_gethome(mrb, NULL);
|
||||
if (home == NULL) {
|
||||
mrb_raise(mrb, E_ARGUMENT_ERROR, "couldn't find HOME environment -- expanding '~'");
|
||||
}
|
||||
@@ -531,18 +538,13 @@ path_gethome(mrb_state *mrb, const char **pathp)
|
||||
}
|
||||
else {
|
||||
const char *uname = RSTRING_CSTR(mrb, mrb_str_new(mrb, username, (mrb_int)len));
|
||||
#if defined(_WIN32) || defined(MRB_IO_NO_PWNAM)
|
||||
mrb_raisef(mrb, E_ARGUMENT_ERROR, "user %s doesn't exist", uname);
|
||||
#else
|
||||
const struct passwd *pwd = getpwnam(uname);
|
||||
if (pwd == NULL) {
|
||||
home = mrb_io_hal_gethome(mrb, uname);
|
||||
if (home == NULL) {
|
||||
mrb_raisef(mrb, E_ARGUMENT_ERROR, "user %s doesn't exist", uname);
|
||||
}
|
||||
home = pwd->pw_dir;
|
||||
if (!path_absolute_p(home)) {
|
||||
mrb_raisef(mrb, E_ARGUMENT_ERROR, "non-absolute home of ~%s", uname);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
home = mrb_utf8_from_locale(home, -1);
|
||||
path = mrb_str_new_cstr(mrb, home);
|
||||
@@ -689,10 +691,10 @@ static mrb_value
|
||||
mrb_file_atime(mrb_state *mrb, mrb_value self)
|
||||
{
|
||||
int fd = mrb_io_fileno(mrb, self);
|
||||
mrb_stat st;
|
||||
mrb_io_stat st;
|
||||
|
||||
mrb->c->ci->mid = 0;
|
||||
if (mrb_fstat(fd, &st) == -1)
|
||||
if (mrb_io_hal_fstat(mrb, fd, &st) == -1)
|
||||
mrb_sys_fail(mrb, "atime");
|
||||
if (TIME_OVERFLOW_P(st.st_atime)) {
|
||||
TIME_BIGTIME(mrb, st.st_atime);
|
||||
@@ -704,10 +706,10 @@ static mrb_value
|
||||
mrb_file_ctime(mrb_state *mrb, mrb_value self)
|
||||
{
|
||||
int fd = mrb_io_fileno(mrb, self);
|
||||
mrb_stat st;
|
||||
mrb_io_stat st;
|
||||
|
||||
mrb->c->ci->mid = 0;
|
||||
if (mrb_fstat(fd, &st) == -1)
|
||||
if (mrb_io_hal_fstat(mrb, fd, &st) == -1)
|
||||
mrb_sys_fail(mrb, "ctime");
|
||||
if (TIME_OVERFLOW_P(st.st_ctime)) {
|
||||
TIME_BIGTIME(mrb, st.st_ctime);
|
||||
@@ -719,10 +721,10 @@ static mrb_value
|
||||
mrb_file_mtime(mrb_state *mrb, mrb_value self)
|
||||
{
|
||||
int fd = mrb_io_fileno(mrb, self);
|
||||
mrb_stat st;
|
||||
mrb_io_stat st;
|
||||
|
||||
mrb->c->ci->mid = 0;
|
||||
if (mrb_fstat(fd, &st) == -1)
|
||||
if (mrb_io_hal_fstat(mrb, fd, &st) == -1)
|
||||
mrb_sys_fail(mrb, "mtime");
|
||||
if (TIME_OVERFLOW_P(st.st_mtime)) {
|
||||
TIME_BIGTIME(mrb, st.st_mtime);
|
||||
@@ -752,7 +754,7 @@ mrb_file_flock(mrb_state *mrb, mrb_value self)
|
||||
mrb_get_args(mrb, "i", &operation);
|
||||
int fd = mrb_io_fileno(mrb, self);
|
||||
|
||||
while (flock(fd, (int)operation) == -1) {
|
||||
while (mrb_io_hal_flock(mrb, fd, (int)operation) == -1) {
|
||||
switch (errno) {
|
||||
case EINTR:
|
||||
/* retry */
|
||||
@@ -761,7 +763,7 @@ mrb_file_flock(mrb_state *mrb, mrb_value self)
|
||||
#if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN
|
||||
case EWOULDBLOCK: /* FreeBSD OpenBSD Linux */
|
||||
#endif
|
||||
if (operation & LOCK_NB) {
|
||||
if (operation & MRB_IO_LOCK_NB) {
|
||||
return mrb_false_value();
|
||||
}
|
||||
/* FALLTHRU - should not happen */
|
||||
@@ -785,9 +787,9 @@ mrb_file_flock(mrb_state *mrb, mrb_value self)
|
||||
static mrb_value
|
||||
mrb_file_size(mrb_state *mrb, mrb_value self)
|
||||
{
|
||||
mrb_stat st;
|
||||
mrb_io_stat st;
|
||||
int fd = mrb_io_fileno(mrb, self);
|
||||
if (mrb_fstat(fd, &st) == -1) {
|
||||
if (mrb_io_hal_fstat(mrb, fd, &st) == -1) {
|
||||
mrb_sys_fail(mrb, "fstat");
|
||||
}
|
||||
|
||||
@@ -803,31 +805,9 @@ mrb_file_size(mrb_state *mrb, mrb_value self)
|
||||
}
|
||||
|
||||
static int
|
||||
mrb_ftruncate(int fd, mrb_int length)
|
||||
mrb_ftruncate(mrb_state *mrb, int fd, mrb_int length)
|
||||
{
|
||||
#ifndef _WIN32
|
||||
return ftruncate(fd, (off_t)length);
|
||||
#else
|
||||
__int64 cur;
|
||||
HANDLE file = (HANDLE)_get_osfhandle(fd);
|
||||
if (file == INVALID_HANDLE_VALUE) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
cur = _lseeki64(fd, 0, SEEK_CUR);
|
||||
if (cur == -1) return -1;
|
||||
|
||||
if (_lseeki64(fd, (__int64)length, SEEK_SET) == -1) return -1;
|
||||
|
||||
if (!SetEndOfFile(file)) {
|
||||
errno = EINVAL; /* TODO: GetLastError to errno */
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (_lseeki64(fd, cur, SEEK_SET) == -1) return -1;
|
||||
|
||||
return 0;
|
||||
#endif /* _WIN32 */
|
||||
return mrb_io_hal_ftruncate(mrb, fd, (int64_t)length);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -847,7 +827,7 @@ mrb_file_truncate(mrb_state *mrb, mrb_value self)
|
||||
mrb_value lenv = mrb_get_arg1(mrb);
|
||||
int fd = mrb_io_fileno(mrb, self);
|
||||
mrb_int length = mrb_as_int(mrb, lenv);
|
||||
if (mrb_ftruncate(fd, length) != 0) {
|
||||
if (mrb_ftruncate(mrb, fd, length) != 0) {
|
||||
mrb_sys_fail(mrb, "ftruncate");
|
||||
}
|
||||
|
||||
@@ -865,22 +845,18 @@ mrb_file_truncate(mrb_state *mrb, mrb_value self)
|
||||
static mrb_value
|
||||
mrb_file_s_symlink(mrb_state *mrb, mrb_value klass)
|
||||
{
|
||||
#if defined(_WIN32)
|
||||
mrb_raise(mrb, E_NOTIMP_ERROR, "symlink is not supported on this platform");
|
||||
#else
|
||||
mrb_value from, to;
|
||||
|
||||
mrb_get_args(mrb, "SS", &from, &to);
|
||||
const char *src = mrb_locale_from_utf8(RSTRING_CSTR(mrb, from), -1);
|
||||
const char *dst = mrb_locale_from_utf8(RSTRING_CSTR(mrb, to), -1);
|
||||
if (symlink(src, dst) == -1) {
|
||||
if (mrb_io_hal_symlink(mrb, src, dst) == -1) {
|
||||
mrb_locale_free(src);
|
||||
mrb_locale_free(dst);
|
||||
mrb_sys_fail(mrb, RSTRING_CSTR(mrb, mrb_format(mrb, "(%v, %v)", from, to)));
|
||||
}
|
||||
mrb_locale_free(src);
|
||||
mrb_locale_free(dst);
|
||||
#endif
|
||||
return mrb_fixnum_value(0);
|
||||
}
|
||||
|
||||
@@ -906,7 +882,7 @@ mrb_file_s_chmod(mrb_state *mrb, mrb_value klass)
|
||||
mrb_ensure_string_type(mrb, filenames[i]);
|
||||
const char *utf8_path = RSTRING_CSTR(mrb, filenames[i]);
|
||||
char *path = mrb_locale_from_utf8(utf8_path, -1);
|
||||
if (CHMOD(path, mode) == -1) {
|
||||
if (mrb_io_hal_chmod(mrb, path, (uint32_t)mode) == -1) {
|
||||
mrb_locale_free(path);
|
||||
mrb_sys_fail(mrb, utf8_path);
|
||||
}
|
||||
@@ -929,10 +905,6 @@ mrb_file_s_chmod(mrb_state *mrb, mrb_value klass)
|
||||
static mrb_value
|
||||
mrb_file_s_readlink(mrb_state *mrb, mrb_value klass)
|
||||
{
|
||||
#if defined(_WIN32)
|
||||
mrb_raise(mrb, E_NOTIMP_ERROR, "readlink is not supported on this platform");
|
||||
return mrb_nil_value(); // unreachable
|
||||
#else
|
||||
const char *path;
|
||||
size_t bufsize = 100;
|
||||
|
||||
@@ -941,8 +913,8 @@ mrb_file_s_readlink(mrb_state *mrb, mrb_value klass)
|
||||
char *tmp = mrb_locale_from_utf8(path, -1);
|
||||
char *buf = (char*)mrb_malloc(mrb, bufsize);
|
||||
|
||||
ssize_t rc;
|
||||
while ((rc = readlink(tmp, buf, bufsize)) == (ssize_t)bufsize) {
|
||||
int64_t rc;
|
||||
while ((rc = mrb_io_hal_readlink(mrb, tmp, buf, bufsize)) == (int64_t)bufsize) {
|
||||
bufsize += 100;
|
||||
buf = (char*)mrb_realloc(mrb, buf, bufsize);
|
||||
}
|
||||
@@ -958,7 +930,6 @@ mrb_file_s_readlink(mrb_state *mrb, mrb_value klass)
|
||||
mrb_free(mrb, buf);
|
||||
|
||||
return ret;
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -9,27 +9,11 @@
|
||||
#include <mruby/ext/io.h>
|
||||
#include <mruby/error.h>
|
||||
#include <mruby/presym.h>
|
||||
#include <io_hal.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#if defined(_WIN32)
|
||||
#define LSTAT stat
|
||||
#include <winsock.h>
|
||||
#else
|
||||
#define LSTAT lstat
|
||||
#include <sys/file.h>
|
||||
#include <sys/param.h>
|
||||
#include <sys/wait.h>
|
||||
#ifndef __DJGPP__
|
||||
#include <libgen.h>
|
||||
#endif
|
||||
#include <pwd.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include <fcntl.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
@@ -37,14 +21,14 @@
|
||||
extern struct mrb_data_type mrb_io_type;
|
||||
|
||||
static int
|
||||
mrb_stat0(mrb_state *mrb, mrb_value obj, struct stat *st, int do_lstat)
|
||||
mrb_stat0(mrb_state *mrb, mrb_value obj, mrb_io_stat *st, int do_lstat)
|
||||
{
|
||||
if (mrb_obj_is_kind_of(mrb, obj, mrb_class_get_id(mrb, MRB_SYM(IO)))) {
|
||||
struct mrb_io *fptr;
|
||||
fptr = (struct mrb_io*)mrb_data_get_ptr(mrb, obj, &mrb_io_type);
|
||||
|
||||
if (fptr && fptr->fd >= 0) {
|
||||
return fstat(fptr->fd, st);
|
||||
return mrb_io_hal_fstat(mrb, fptr->fd, st);
|
||||
}
|
||||
|
||||
mrb_raise(mrb, E_IO_ERROR, "closed stream");
|
||||
@@ -54,10 +38,10 @@ mrb_stat0(mrb_state *mrb, mrb_value obj, struct stat *st, int do_lstat)
|
||||
char *path = mrb_locale_from_utf8(RSTRING_CSTR(mrb, obj), -1);
|
||||
int ret;
|
||||
if (do_lstat) {
|
||||
ret = LSTAT(path, st);
|
||||
ret = mrb_io_hal_lstat(mrb, path, st);
|
||||
}
|
||||
else {
|
||||
ret = stat(path, st);
|
||||
ret = mrb_io_hal_stat(mrb, path, st);
|
||||
}
|
||||
mrb_locale_free(path);
|
||||
return ret;
|
||||
@@ -65,18 +49,16 @@ mrb_stat0(mrb_state *mrb, mrb_value obj, struct stat *st, int do_lstat)
|
||||
}
|
||||
|
||||
static int
|
||||
mrb_stat(mrb_state *mrb, mrb_value obj, struct stat *st)
|
||||
mrb_stat(mrb_state *mrb, mrb_value obj, mrb_io_stat *st)
|
||||
{
|
||||
return mrb_stat0(mrb, obj, st, 0);
|
||||
}
|
||||
|
||||
#ifdef S_ISLNK
|
||||
static int
|
||||
mrb_lstat(mrb_state *mrb, mrb_value obj, struct stat *st)
|
||||
mrb_lstat(mrb_state *mrb, mrb_value obj, mrb_io_stat *st)
|
||||
{
|
||||
return mrb_stat0(mrb, obj, st, 1);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
@@ -97,7 +79,7 @@ mrb_filetest_s_directory_p(mrb_state *mrb, mrb_value klass)
|
||||
# define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
|
||||
#endif
|
||||
|
||||
struct stat st;
|
||||
mrb_io_stat st;
|
||||
mrb_value obj = mrb_get_arg1(mrb);
|
||||
|
||||
if (mrb_stat(mrb, obj, &st) < 0)
|
||||
@@ -122,15 +104,12 @@ mrb_filetest_s_directory_p(mrb_state *mrb, mrb_value klass)
|
||||
static mrb_value
|
||||
mrb_filetest_s_pipe_p(mrb_state *mrb, mrb_value klass)
|
||||
{
|
||||
#if defined(_WIN32)
|
||||
mrb_raise(mrb, E_NOTIMP_ERROR, "pipe is not supported on this platform");
|
||||
#else
|
||||
#ifdef S_IFIFO
|
||||
# ifndef S_ISFIFO
|
||||
# define S_ISFIFO(m) (((m) & S_IFMT) == S_IFIFO)
|
||||
# endif
|
||||
|
||||
struct stat st;
|
||||
mrb_io_stat st;
|
||||
mrb_value obj = mrb_get_arg1(mrb);
|
||||
|
||||
if (mrb_stat(mrb, obj, &st) < 0)
|
||||
@@ -140,7 +119,6 @@ mrb_filetest_s_pipe_p(mrb_state *mrb, mrb_value klass)
|
||||
|
||||
#endif
|
||||
return mrb_false_value();
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -157,9 +135,6 @@ mrb_filetest_s_pipe_p(mrb_state *mrb, mrb_value klass)
|
||||
static mrb_value
|
||||
mrb_filetest_s_symlink_p(mrb_state *mrb, mrb_value klass)
|
||||
{
|
||||
#if defined(_WIN32)
|
||||
mrb_raise(mrb, E_NOTIMP_ERROR, "symlink is not supported on this platform");
|
||||
#else
|
||||
#ifndef S_ISLNK
|
||||
# ifdef _S_ISLNK
|
||||
# define S_ISLNK(m) _S_ISLNK(m)
|
||||
@@ -175,7 +150,7 @@ mrb_filetest_s_symlink_p(mrb_state *mrb, mrb_value klass)
|
||||
#endif
|
||||
|
||||
#ifdef S_ISLNK
|
||||
struct stat st;
|
||||
mrb_io_stat st;
|
||||
mrb_value obj = mrb_get_arg1(mrb);
|
||||
|
||||
if (mrb_lstat(mrb, obj, &st) == -1)
|
||||
@@ -185,7 +160,6 @@ mrb_filetest_s_symlink_p(mrb_state *mrb, mrb_value klass)
|
||||
#endif
|
||||
|
||||
return mrb_false_value();
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -202,9 +176,6 @@ mrb_filetest_s_symlink_p(mrb_state *mrb, mrb_value klass)
|
||||
static mrb_value
|
||||
mrb_filetest_s_socket_p(mrb_state *mrb, mrb_value klass)
|
||||
{
|
||||
#if defined(_WIN32)
|
||||
mrb_raise(mrb, E_NOTIMP_ERROR, "socket is not supported on this platform");
|
||||
#else
|
||||
#ifndef S_ISSOCK
|
||||
# ifdef _S_ISSOCK
|
||||
# define S_ISSOCK(m) _S_ISSOCK(m)
|
||||
@@ -220,7 +191,7 @@ mrb_filetest_s_socket_p(mrb_state *mrb, mrb_value klass)
|
||||
#endif
|
||||
|
||||
#ifdef S_ISSOCK
|
||||
struct stat st;
|
||||
mrb_io_stat st;
|
||||
mrb_value obj = mrb_get_arg1(mrb);
|
||||
|
||||
if (mrb_stat(mrb, obj, &st) < 0)
|
||||
@@ -230,7 +201,6 @@ mrb_filetest_s_socket_p(mrb_state *mrb, mrb_value klass)
|
||||
#endif
|
||||
|
||||
return mrb_false_value();
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -251,7 +221,7 @@ mrb_filetest_s_socket_p(mrb_state *mrb, mrb_value klass)
|
||||
static mrb_value
|
||||
mrb_filetest_s_exist_p(mrb_state *mrb, mrb_value klass)
|
||||
{
|
||||
struct stat st;
|
||||
mrb_io_stat st;
|
||||
mrb_value obj = mrb_get_arg1(mrb);
|
||||
|
||||
if (mrb_stat(mrb, obj, &st) < 0)
|
||||
@@ -278,7 +248,7 @@ mrb_filetest_s_file_p(mrb_state *mrb, mrb_value klass)
|
||||
# define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
|
||||
#endif
|
||||
|
||||
struct stat st;
|
||||
mrb_io_stat st;
|
||||
mrb_value obj = mrb_get_arg1(mrb);
|
||||
|
||||
if (mrb_stat(mrb, obj, &st) < 0)
|
||||
@@ -303,7 +273,7 @@ mrb_filetest_s_file_p(mrb_state *mrb, mrb_value klass)
|
||||
static mrb_value
|
||||
mrb_filetest_s_zero_p(mrb_state *mrb, mrb_value klass)
|
||||
{
|
||||
struct stat st;
|
||||
mrb_io_stat st;
|
||||
mrb_value obj = mrb_get_arg1(mrb);
|
||||
|
||||
if (mrb_stat(mrb, obj, &st) < 0)
|
||||
@@ -330,7 +300,7 @@ mrb_filetest_s_zero_p(mrb_state *mrb, mrb_value klass)
|
||||
static mrb_value
|
||||
mrb_filetest_s_size(mrb_state *mrb, mrb_value klass)
|
||||
{
|
||||
struct stat st;
|
||||
mrb_io_stat st;
|
||||
mrb_value obj = mrb_get_arg1(mrb);
|
||||
|
||||
if (mrb_stat(mrb, obj, &st) < 0)
|
||||
@@ -354,7 +324,7 @@ mrb_filetest_s_size(mrb_state *mrb, mrb_value klass)
|
||||
static mrb_value
|
||||
mrb_filetest_s_size_p(mrb_state *mrb, mrb_value klass)
|
||||
{
|
||||
struct stat st;
|
||||
mrb_io_stat st;
|
||||
mrb_value obj = mrb_get_arg1(mrb);
|
||||
|
||||
if (mrb_stat(mrb, obj, &st) < 0)
|
||||
|
||||
+54
-49
@@ -13,6 +13,7 @@
|
||||
#include <mruby/error.h>
|
||||
#include <mruby/internal.h>
|
||||
#include <mruby/presym.h>
|
||||
#include <io_hal.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
@@ -1367,21 +1368,21 @@ io_pid(mrb_state *mrb, mrb_value io)
|
||||
return mrb_nil_value();
|
||||
}
|
||||
|
||||
static struct timeval
|
||||
static mrb_io_timeval
|
||||
time2timeval(mrb_state *mrb, mrb_value time)
|
||||
{
|
||||
struct timeval t = { 0, 0 };
|
||||
mrb_io_timeval t = { 0, 0 };
|
||||
|
||||
switch (mrb_type(time)) {
|
||||
case MRB_TT_INTEGER:
|
||||
t.tv_sec = (ftime_t)mrb_integer(time);
|
||||
t.tv_sec = (int64_t)mrb_integer(time);
|
||||
t.tv_usec = 0;
|
||||
break;
|
||||
|
||||
#ifndef MRB_NO_FLOAT
|
||||
case MRB_TT_FLOAT:
|
||||
t.tv_sec = (ftime_t)mrb_float(time);
|
||||
t.tv_usec = (fsuseconds_t)((mrb_float(time) - t.tv_sec) * 1000000.0);
|
||||
t.tv_sec = (int64_t)mrb_float(time);
|
||||
t.tv_usec = (int64_t)((mrb_float(time) - t.tv_sec) * 1000000.0);
|
||||
break;
|
||||
#endif
|
||||
|
||||
@@ -1487,7 +1488,7 @@ io_s_select(mrb_state *mrb, mrb_value klass)
|
||||
write = argv[1];
|
||||
mrb_value read = argv[0];
|
||||
|
||||
struct timeval *tp, timerec;
|
||||
mrb_io_timeval *tp, timerec;
|
||||
if (mrb_nil_p(timeout)) {
|
||||
tp = NULL;
|
||||
}
|
||||
@@ -1496,20 +1497,22 @@ io_s_select(mrb_state *mrb, mrb_value klass)
|
||||
tp = &timerec;
|
||||
}
|
||||
|
||||
fd_set pset, rset, *rp;
|
||||
FD_ZERO(&pset);
|
||||
mrb_io_fdset *pset = mrb_io_hal_fdset_alloc(mrb);
|
||||
mrb_io_fdset *rset = NULL;
|
||||
mrb_io_fdset *rp = NULL;
|
||||
mrb_io_hal_fdset_zero(mrb, pset);
|
||||
if (!mrb_nil_p(read)) {
|
||||
mrb_check_type(mrb, read, MRB_TT_ARRAY);
|
||||
rp = &rset;
|
||||
FD_ZERO(rp);
|
||||
rset = mrb_io_hal_fdset_alloc(mrb);
|
||||
rp = rset;
|
||||
mrb_io_hal_fdset_zero(mrb, rp);
|
||||
for (int i = 0; i < RARRAY_LEN(read); i++) {
|
||||
read_io = RARRAY_PTR(read)[i];
|
||||
fptr = io_get_open_fptr(mrb, read_io);
|
||||
if (fptr->fd >= FD_SETSIZE) continue;
|
||||
FD_SET(fptr->fd, rp);
|
||||
mrb_io_hal_fdset_set(mrb, fptr->fd, rp);
|
||||
if (mrb_io_read_data_pending(mrb, fptr)) {
|
||||
pending++;
|
||||
FD_SET(fptr->fd, &pset);
|
||||
mrb_io_hal_fdset_set(mrb, fptr->fd, pset);
|
||||
}
|
||||
if (max < fptr->fd)
|
||||
max = fptr->fd;
|
||||
@@ -1519,75 +1522,72 @@ io_s_select(mrb_state *mrb, mrb_value klass)
|
||||
tp = &timerec;
|
||||
}
|
||||
}
|
||||
else {
|
||||
rp = NULL;
|
||||
}
|
||||
|
||||
fd_set wset, *wp;
|
||||
mrb_io_fdset *wset = NULL;
|
||||
mrb_io_fdset *wp = NULL;
|
||||
if (!mrb_nil_p(write)) {
|
||||
mrb_check_type(mrb, write, MRB_TT_ARRAY);
|
||||
wp = &wset;
|
||||
FD_ZERO(wp);
|
||||
wset = mrb_io_hal_fdset_alloc(mrb);
|
||||
wp = wset;
|
||||
mrb_io_hal_fdset_zero(mrb, wp);
|
||||
for (int i = 0; i < RARRAY_LEN(write); i++) {
|
||||
fptr = io_get_open_fptr(mrb, RARRAY_PTR(write)[i]);
|
||||
if (fptr->fd >= FD_SETSIZE) continue;
|
||||
FD_SET(fptr->fd, wp);
|
||||
mrb_io_hal_fdset_set(mrb, fptr->fd, wp);
|
||||
if (max < fptr->fd)
|
||||
max = fptr->fd;
|
||||
if (fptr->fd2 >= 0) {
|
||||
FD_SET(fptr->fd2, wp);
|
||||
mrb_io_hal_fdset_set(mrb, fptr->fd2, wp);
|
||||
if (max < fptr->fd2)
|
||||
max = fptr->fd2;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
wp = NULL;
|
||||
}
|
||||
|
||||
fd_set eset, *ep;
|
||||
mrb_io_fdset *eset = NULL;
|
||||
mrb_io_fdset *ep = NULL;
|
||||
if (!mrb_nil_p(except)) {
|
||||
mrb_check_type(mrb, except, MRB_TT_ARRAY);
|
||||
ep = &eset;
|
||||
FD_ZERO(ep);
|
||||
eset = mrb_io_hal_fdset_alloc(mrb);
|
||||
ep = eset;
|
||||
mrb_io_hal_fdset_zero(mrb, ep);
|
||||
for (int i = 0; i < RARRAY_LEN(except); i++) {
|
||||
fptr = io_get_open_fptr(mrb, RARRAY_PTR(except)[i]);
|
||||
if (fptr->fd >= FD_SETSIZE) continue;
|
||||
FD_SET(fptr->fd, ep);
|
||||
mrb_io_hal_fdset_set(mrb, fptr->fd, ep);
|
||||
if (max < fptr->fd)
|
||||
max = fptr->fd;
|
||||
if (fptr->fd2 >= 0) {
|
||||
FD_SET(fptr->fd2, ep);
|
||||
mrb_io_hal_fdset_set(mrb, fptr->fd2, ep);
|
||||
if (max < fptr->fd2)
|
||||
max = fptr->fd2;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
ep = NULL;
|
||||
}
|
||||
|
||||
max++;
|
||||
|
||||
int n;
|
||||
retry:
|
||||
n = select(max, rp, wp, ep, tp);
|
||||
n = mrb_io_hal_select(mrb, max, rp, wp, ep, tp);
|
||||
if (n < 0) {
|
||||
#ifdef _WIN32
|
||||
errno = WSAGetLastError();
|
||||
if (errno != WSAEINTR)
|
||||
if (errno != EINTR) {
|
||||
mrb_io_hal_fdset_free(mrb, pset);
|
||||
mrb_io_hal_fdset_free(mrb, rset);
|
||||
mrb_io_hal_fdset_free(mrb, wset);
|
||||
mrb_io_hal_fdset_free(mrb, eset);
|
||||
mrb_sys_fail(mrb, "select failed");
|
||||
#else
|
||||
if (errno != EINTR)
|
||||
mrb_sys_fail(mrb, "select failed");
|
||||
#endif
|
||||
}
|
||||
if (tp == NULL)
|
||||
goto retry;
|
||||
interrupt_flag = 1;
|
||||
}
|
||||
|
||||
if (!pending && n == 0)
|
||||
if (!pending && n == 0) {
|
||||
mrb_io_hal_fdset_free(mrb, pset);
|
||||
mrb_io_hal_fdset_free(mrb, rset);
|
||||
mrb_io_hal_fdset_free(mrb, wset);
|
||||
mrb_io_hal_fdset_free(mrb, eset);
|
||||
return mrb_nil_value();
|
||||
}
|
||||
|
||||
result = mrb_ary_new_capa(mrb, 3);
|
||||
mrb_ary_push(mrb, result, rp ? mrb_ary_new(mrb) : mrb_ary_new_capa(mrb, 0));
|
||||
@@ -1599,8 +1599,8 @@ retry:
|
||||
list = RARRAY_PTR(result)[0];
|
||||
for (int i = 0; i < RARRAY_LEN(read); i++) {
|
||||
fptr = io_get_open_fptr(mrb, RARRAY_PTR(read)[i]);
|
||||
if (FD_ISSET(fptr->fd, rp) ||
|
||||
FD_ISSET(fptr->fd, &pset)) {
|
||||
if (mrb_io_hal_fdset_isset(mrb, fptr->fd, rp) ||
|
||||
mrb_io_hal_fdset_isset(mrb, fptr->fd, pset)) {
|
||||
mrb_ary_push(mrb, list, RARRAY_PTR(read)[i]);
|
||||
}
|
||||
}
|
||||
@@ -1610,10 +1610,10 @@ retry:
|
||||
list = RARRAY_PTR(result)[1];
|
||||
for (int i = 0; i < RARRAY_LEN(write); i++) {
|
||||
fptr = io_get_open_fptr(mrb, RARRAY_PTR(write)[i]);
|
||||
if (FD_ISSET(fptr->fd, wp)) {
|
||||
if (mrb_io_hal_fdset_isset(mrb, fptr->fd, wp)) {
|
||||
mrb_ary_push(mrb, list, RARRAY_PTR(write)[i]);
|
||||
}
|
||||
else if (fptr->fd2 >= 0 && FD_ISSET(fptr->fd2, wp)) {
|
||||
else if (fptr->fd2 >= 0 && mrb_io_hal_fdset_isset(mrb, fptr->fd2, wp)) {
|
||||
mrb_ary_push(mrb, list, RARRAY_PTR(write)[i]);
|
||||
}
|
||||
}
|
||||
@@ -1623,16 +1623,21 @@ retry:
|
||||
list = RARRAY_PTR(result)[2];
|
||||
for (int i = 0; i < RARRAY_LEN(except); i++) {
|
||||
fptr = io_get_open_fptr(mrb, RARRAY_PTR(except)[i]);
|
||||
if (FD_ISSET(fptr->fd, ep)) {
|
||||
if (mrb_io_hal_fdset_isset(mrb, fptr->fd, ep)) {
|
||||
mrb_ary_push(mrb, list, RARRAY_PTR(except)[i]);
|
||||
}
|
||||
else if (fptr->fd2 >= 0 && FD_ISSET(fptr->fd2, ep)) {
|
||||
else if (fptr->fd2 >= 0 && mrb_io_hal_fdset_isset(mrb, fptr->fd2, ep)) {
|
||||
mrb_ary_push(mrb, list, RARRAY_PTR(except)[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mrb_io_hal_fdset_free(mrb, pset);
|
||||
mrb_io_hal_fdset_free(mrb, rset);
|
||||
mrb_io_hal_fdset_free(mrb, wset);
|
||||
mrb_io_hal_fdset_free(mrb, eset);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user