diff --git a/mrbgems/hal-posix-io/mrbgem.rake b/mrbgems/hal-posix-io/mrbgem.rake new file mode 100644 index 000000000..bdd4913d1 --- /dev/null +++ b/mrbgems/hal-posix-io/mrbgem.rake @@ -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 diff --git a/mrbgems/hal-posix-io/src/io_hal.c b/mrbgems/hal-posix-io/src/io_hal.c new file mode 100644 index 000000000..2185e124a --- /dev/null +++ b/mrbgems/hal-posix-io/src/io_hal.c @@ -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 +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#ifndef __DJGPP__ +#include +#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 */ +} diff --git a/mrbgems/hal-win-io/mrbgem.rake b/mrbgems/hal-win-io/mrbgem.rake new file mode 100644 index 000000000..5bad6576b --- /dev/null +++ b/mrbgems/hal-win-io/mrbgem.rake @@ -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 diff --git a/mrbgems/hal-win-io/src/io_hal.c b/mrbgems/hal-win-io/src/io_hal.c new file mode 100644 index 000000000..6473ed43b --- /dev/null +++ b/mrbgems/hal-win-io/src/io_hal.c @@ -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 +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +/* 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 */ +} diff --git a/mrbgems/mruby-io/include/io_hal.h b/mrbgems/mruby-io/include/io_hal.h new file mode 100644 index 000000000..a1efde8e5 --- /dev/null +++ b/mrbgems/mruby-io/include/io_hal.h @@ -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 +#include + +/* + * 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 */ diff --git a/mrbgems/mruby-io/mrbgem.rake b/mrbgems/mruby-io/mrbgem.rake index 3449484e5..6663666df 100644 --- a/mrbgems/mruby-io/mrbgem.rake +++ b/mrbgems/mruby-io/mrbgem.rake @@ -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 diff --git a/mrbgems/mruby-io/src/file.c b/mrbgems/mruby-io/src/file.c index 0902f87bd..1e71b8d43 100644 --- a/mrbgems/mruby-io/src/file.c +++ b/mrbgems/mruby-io/src/file.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include @@ -19,6 +20,18 @@ #include #include #include + +/* 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 #include @@ -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 } /* diff --git a/mrbgems/mruby-io/src/file_test.c b/mrbgems/mruby-io/src/file_test.c index 2c931acfc..eb739ab12 100644 --- a/mrbgems/mruby-io/src/file_test.c +++ b/mrbgems/mruby-io/src/file_test.c @@ -9,27 +9,11 @@ #include #include #include +#include #include #include -#if defined(_WIN32) - #define LSTAT stat - #include -#else - #define LSTAT lstat - #include - #include - #include -#ifndef __DJGPP__ - #include -#endif - #include - #include -#endif - -#include - #include #include #include @@ -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) diff --git a/mrbgems/mruby-io/src/io.c b/mrbgems/mruby-io/src/io.c index d82eeb4a5..0a2fb8057 100644 --- a/mrbgems/mruby-io/src/io.c +++ b/mrbgems/mruby-io/src/io.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include @@ -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; }