mruby-dir: introduce HAL for platform abstraction

platform-specific directory operations separated into hal-posix-dir and
hal-win-dir gems. this allows mruby-dir to support embedded platforms and
simplifies platform-specific implementations.

Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
Yukihiro "Matz" Matsumoto
2025-10-16 08:17:43 +09:00
parent 74a5c840f8
commit 74c0fb9c6e
8 changed files with 635 additions and 231 deletions
+7
View File
@@ -0,0 +1,7 @@
MRuby::Gem::Specification.new('hal-posix-dir') do |spec|
spec.license = 'MIT'
spec.authors = 'mruby developers'
spec.summary = 'POSIX HAL for mruby-dir (Linux, macOS, BSD, Unix)'
spec.add_dependency 'mruby-dir', core: 'mruby-dir'
end
+193
View File
@@ -0,0 +1,193 @@
/*
** dir_hal.c - POSIX HAL implementation for mruby-dir
**
** See Copyright Notice in mruby.h
**
** POSIX implementation for directory operations using standard POSIX APIs.
** Supported platforms: Linux, macOS, BSD, Unix
*/
#include <mruby.h>
#include "dir_hal.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <unistd.h>
#include <errno.h>
#ifdef HAVE_SYS_PARAM_H
#include <sys/param.h>
#endif
/* On POSIX, mrb_dir_handle wraps DIR */
struct mrb_dir_handle {
DIR *dir;
};
/*
* Directory Operations
*/
mrb_dir_handle*
mrb_dir_hal_open(mrb_state *mrb, const char *path)
{
DIR *dir = opendir(path);
if (dir == NULL) {
return NULL;
}
mrb_dir_handle *handle = (mrb_dir_handle*)mrb_malloc(mrb, sizeof(mrb_dir_handle));
handle->dir = dir;
return handle;
}
int
mrb_dir_hal_close(mrb_state *mrb, mrb_dir_handle *handle)
{
int result = closedir(handle->dir);
mrb_free(mrb, handle);
return result;
}
const char*
mrb_dir_hal_read(mrb_state *mrb, mrb_dir_handle *handle)
{
(void)mrb;
struct dirent *dp = readdir(handle->dir);
return dp ? dp->d_name : NULL;
}
void
mrb_dir_hal_rewind(mrb_state *mrb, mrb_dir_handle *handle)
{
(void)mrb;
rewinddir(handle->dir);
}
/*
* Optional Operations
*/
int
mrb_dir_hal_seek(mrb_state *mrb, mrb_dir_handle *handle, long pos)
{
#if defined(__ANDROID__)
/* Android doesn't have reliable seekdir */
(void)mrb; (void)handle; (void)pos;
errno = ENOSYS;
return -1;
#else
(void)mrb;
seekdir(handle->dir, pos);
return 0;
#endif
}
long
mrb_dir_hal_tell(mrb_state *mrb, mrb_dir_handle *handle)
{
#if defined(__ANDROID__)
/* Android doesn't have reliable telldir */
(void)mrb; (void)handle;
errno = ENOSYS;
return -1;
#else
(void)mrb;
return telldir(handle->dir);
#endif
}
/*
* Filesystem Operations
*/
int
mrb_dir_hal_mkdir(mrb_state *mrb, const char *path, int mode)
{
(void)mrb;
return mkdir(path, (mode_t)mode);
}
int
mrb_dir_hal_rmdir(mrb_state *mrb, const char *path)
{
(void)mrb;
return rmdir(path);
}
int
mrb_dir_hal_chdir(mrb_state *mrb, const char *path)
{
(void)mrb;
return chdir(path);
}
int
mrb_dir_hal_getcwd(mrb_state *mrb, char *buf, size_t size)
{
(void)mrb;
return getcwd(buf, size) ? 0 : -1;
}
int
mrb_dir_hal_chroot(mrb_state *mrb, const char *path)
{
#if defined(__ANDROID__) || defined(__MSDOS__)
/* Not available on these platforms */
(void)mrb; (void)path;
errno = ENOSYS;
return -1;
#else
(void)mrb;
return chroot(path);
#endif
}
int
mrb_dir_hal_is_directory(mrb_state *mrb, const char *path)
{
struct stat sb;
(void)mrb;
if (stat(path, &sb) == 0 && S_ISDIR(sb.st_mode)) {
return 1;
}
return 0;
}
/*
* HAL Initialization/Finalization
*/
void
mrb_dir_hal_init(mrb_state *mrb)
{
(void)mrb;
/* No initialization needed for POSIX */
}
void
mrb_dir_hal_final(mrb_state *mrb)
{
(void)mrb;
/* No cleanup needed for POSIX */
}
/*
* Gem initialization
*/
void
mrb_hal_posix_dir_gem_init(mrb_state *mrb)
{
(void)mrb;
/* HAL interface functions are called by mruby-dir gem */
}
void
mrb_hal_posix_dir_gem_final(mrb_state *mrb)
{
(void)mrb;
/* Cleanup handled by mrb_dir_hal_final called from mruby-dir */
}
+7
View File
@@ -0,0 +1,7 @@
MRuby::Gem::Specification.new('hal-win-dir') do |spec|
spec.license = 'MIT and MIT-like license'
spec.authors = ['mruby developers', 'Kevlin Henney']
spec.summary = 'Windows HAL for mruby-dir'
spec.add_dependency 'mruby-dir', core: 'mruby-dir'
end
+249
View File
@@ -0,0 +1,249 @@
/*
** dir_hal.c - Windows HAL implementation for mruby-dir
**
** See Copyright Notice in mruby.h
**
** Windows implementation for directory operations using _findfirst/_findnext APIs.
** Provides POSIX-compatible interface on Windows.
**
** Based on dirent.c by Kevlin Henney (kevlin@acm.org, kevlin@curbralan.com)
** Original implementation: Created March 1997. Updated June 2003 and July 2012.
** See end of file for Kevlin Henney's copyright notice.
*/
#include <mruby.h>
#include "dir_hal.h"
#include <windows.h>
#include <direct.h>
#include <io.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
/* Windows directory handle implementation */
struct mrb_dir_handle {
intptr_t handle; /* _findfirst/_findnext handle */
struct _finddata_t info; /* Current entry info */
char *pattern; /* Search pattern (e.g., "dir/*") */
int first; /* Flag: haven't read first entry yet */
};
/*
* Directory Operations
*/
mrb_dir_handle*
mrb_dir_hal_open(mrb_state *mrb, const char *path)
{
mrb_dir_handle *handle;
size_t len = strlen(path);
const char *suffix;
/* Add wildcard suffix if needed */
suffix = (len > 0 && (path[len-1] == '/' || path[len-1] == '\\')) ? "*" : "/*";
handle = (mrb_dir_handle*)mrb_malloc(mrb, sizeof(mrb_dir_handle));
handle->pattern = (char*)mrb_malloc(mrb, len + strlen(suffix) + 1);
strcpy(handle->pattern, path);
strcat(handle->pattern, suffix);
handle->handle = _findfirst(handle->pattern, &handle->info);
if (handle->handle == -1) {
mrb_free(mrb, handle->pattern);
mrb_free(mrb, handle);
return NULL;
}
handle->first = 1;
return handle;
}
int
mrb_dir_hal_close(mrb_state *mrb, mrb_dir_handle *handle)
{
int result = -1;
if (handle->handle != -1) {
result = _findclose(handle->handle);
}
mrb_free(mrb, handle->pattern);
mrb_free(mrb, handle);
if (result == -1) {
/* Map all errors to EBADF */
errno = EBADF;
}
return result;
}
const char*
mrb_dir_hal_read(mrb_state *mrb, mrb_dir_handle *handle)
{
(void)mrb;
if (handle->handle == -1) {
errno = EBADF;
return NULL;
}
/* First call returns the result from _findfirst */
if (handle->first) {
handle->first = 0;
return handle->info.name;
}
/* Subsequent calls use _findnext */
if (_findnext(handle->handle, &handle->info) == -1) {
return NULL;
}
return handle->info.name;
}
void
mrb_dir_hal_rewind(mrb_state *mrb, mrb_dir_handle *handle)
{
(void)mrb;
if (handle->handle == -1) {
errno = EBADF;
return;
}
/* Close and reopen to rewind */
_findclose(handle->handle);
handle->handle = _findfirst(handle->pattern, &handle->info);
handle->first = 1;
}
/*
* Optional Operations
*/
int
mrb_dir_hal_seek(mrb_state *mrb, mrb_dir_handle *handle, long pos)
{
/* Not supported on Windows */
(void)mrb; (void)handle; (void)pos;
errno = ENOSYS;
return -1;
}
long
mrb_dir_hal_tell(mrb_state *mrb, mrb_dir_handle *handle)
{
/* Not supported on Windows */
(void)mrb; (void)handle;
errno = ENOSYS;
return -1;
}
/*
* Filesystem Operations
*/
int
mrb_dir_hal_mkdir(mrb_state *mrb, const char *path, int mode)
{
/* Windows _mkdir ignores mode parameter */
(void)mrb; (void)mode;
return _mkdir(path);
}
int
mrb_dir_hal_rmdir(mrb_state *mrb, const char *path)
{
(void)mrb;
return _rmdir(path);
}
int
mrb_dir_hal_chdir(mrb_state *mrb, const char *path)
{
(void)mrb;
return _chdir(path);
}
int
mrb_dir_hal_getcwd(mrb_state *mrb, char *buf, size_t size)
{
(void)mrb;
return _getcwd(buf, (int)size) ? 0 : -1;
}
int
mrb_dir_hal_chroot(mrb_state *mrb, const char *path)
{
/* Not available on Windows */
(void)mrb; (void)path;
errno = ENOSYS;
return -1;
}
int
mrb_dir_hal_is_directory(mrb_state *mrb, const char *path)
{
struct _stat sb;
(void)mrb;
if (_stat(path, &sb) == 0 && (sb.st_mode & _S_IFDIR)) {
return 1;
}
return 0;
}
/*
* HAL Initialization/Finalization
*/
void
mrb_dir_hal_init(mrb_state *mrb)
{
(void)mrb;
/* No initialization needed for Windows */
}
void
mrb_dir_hal_final(mrb_state *mrb)
{
(void)mrb;
/* No cleanup needed for Windows */
}
/*
* Gem initialization
*/
void
mrb_hal_win_dir_gem_init(mrb_state *mrb)
{
(void)mrb;
/* HAL interface functions are called by mruby-dir gem */
}
void
mrb_hal_win_dir_gem_final(mrb_state *mrb)
{
(void)mrb;
/* Cleanup handled by mrb_dir_hal_final called from mruby-dir */
}
/*
** Portions derived from dirent.c by Kevlin Henney:
**
** Copyright Kevlin Henney, 1997, 2003, 2012. All rights reserved.
**
** Permission to use, copy, modify, and distribute this software and its
** documentation for any purpose is hereby granted without fee, provided
** that this copyright and permissions notice appear in all copies and
** derivatives.
**
** This software is supplied "as is" without express or implied warranty.
**
** But that said, if there are any problems please get in touch.
*/
+79
View File
@@ -0,0 +1,79 @@
/*
** dir_hal.h - Directory HAL interface for mruby
**
** See Copyright Notice in mruby.h
**
** Hardware Abstraction Layer for directory operations.
** Provides platform-independent interface for filesystem directory operations.
*/
#ifndef MRUBY_DIR_HAL_H
#define MRUBY_DIR_HAL_H
#include <mruby.h>
/*
* Platform-independent directory handle
* Each HAL implementation defines this structure internally
*/
typedef struct mrb_dir_handle mrb_dir_handle;
/*
* Directory Operations
*/
/* Open directory for reading. Returns handle or NULL on error (sets errno). */
mrb_dir_handle* mrb_dir_hal_open(mrb_state *mrb, const char *path);
/* Close directory handle. Returns 0 on success, -1 on error. */
int mrb_dir_hal_close(mrb_state *mrb, mrb_dir_handle *dir);
/* Read next entry from directory. Returns name or NULL at end/error. */
const char* mrb_dir_hal_read(mrb_state *mrb, mrb_dir_handle *dir);
/* Rewind directory to beginning */
void mrb_dir_hal_rewind(mrb_state *mrb, mrb_dir_handle *dir);
/*
* Optional Operations (may not be available on all platforms)
*/
/* Seek to position in directory. Returns -1 if unsupported (sets errno to ENOSYS). */
int mrb_dir_hal_seek(mrb_state *mrb, mrb_dir_handle *dir, long pos);
/* Get current position in directory. Returns -1 if unsupported (sets errno to ENOSYS). */
long mrb_dir_hal_tell(mrb_state *mrb, mrb_dir_handle *dir);
/*
* Filesystem Operations
*/
/* Create directory with mode (mode may be ignored on some platforms). Returns 0 on success, -1 on error. */
int mrb_dir_hal_mkdir(mrb_state *mrb, const char *path, int mode);
/* Remove empty directory. Returns 0 on success, -1 on error. */
int mrb_dir_hal_rmdir(mrb_state *mrb, const char *path);
/* Change current working directory. Returns 0 on success, -1 on error. */
int mrb_dir_hal_chdir(mrb_state *mrb, const char *path);
/* Get current working directory. Returns 0 on success, -1 on error. */
int mrb_dir_hal_getcwd(mrb_state *mrb, char *buf, size_t size);
/* Change root directory (privileged operation). Returns -1 if unsupported (sets errno to ENOSYS). */
int mrb_dir_hal_chroot(mrb_state *mrb, const char *path);
/* Check if path is a directory. Returns 1 if directory, 0 if not. */
int mrb_dir_hal_is_directory(mrb_state *mrb, const char *path);
/*
* HAL Initialization/Finalization
*/
/* Initialize HAL (called once at gem initialization) */
void mrb_dir_hal_init(mrb_state *mrb);
/* Cleanup HAL (called once at gem finalization) */
void mrb_dir_hal_final(mrb_state *mrb);
#endif /* MRUBY_DIR_HAL_H */
+31
View File
@@ -1,4 +1,35 @@
MRuby::Gem::Specification.new('mruby-dir') do |spec|
spec.license = 'MIT and MIT-like license'
spec.authors = ['Internet Initiative Japan Inc.', 'Kevlin Henney']
# 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-.*-dir$/ } or begin
# No HAL found - determine appropriate error message or auto-load
suggested_hal = if ENV['MRUBY_DIR_HAL']
ENV['MRUBY_DIR_HAL']
elsif spec.for_windows?
'hal-win-dir'
elsif RUBY_PLATFORM =~ /linux|darwin|bsd/
'hal-posix-dir'
else
nil
end
if suggested_hal
# Auto-load HAL gem for convenience (for development)
# This works because HAL gems declare dependency on mruby-dir
warn "mruby-dir: No HAL specified, loading #{suggested_hal} (explicit selection recommended)"
spec.build.gem core: suggested_hal
else
# Unknown platform - fail with helpful message
fail "mruby-dir: No HAL available for platform '#{RUBY_PLATFORM}'.\n" \
"Please specify HAL gem explicitly in your build config:\n" \
" conf.gem core: 'hal-posix-dir' # For Linux/macOS/BSD\n" \
" conf.gem core: 'hal-win-dir' # For Windows\n" \
"Or set environment variable:\n" \
" MRUBY_DIR_HAL=hal-myplatform-dir\n" \
"See mrbgems/mruby-dir/README.md for creating custom HAL."
end
end
end
-140
View File
@@ -1,140 +0,0 @@
/*
Implementation of POSIX directory browsing functions and types for Win32.
Author: Kevlin Henney (kevlin@acm.org, kevlin@curbralan.com)
History: Created March 1997. Updated June 2003 and July 2012.
Rights: See end of file.
*/
#include <errno.h>
#include <io.h> /* _findfirst and _findnext set errno iff they return -1 */
#include <stdlib.h>
#include <string.h>
#ifdef __cplusplus
extern "C"
{
#endif
typedef ptrdiff_t handle_type; /* C99's intptr_t not sufficiently portable */
struct dirent
{
char *d_name;
};
struct DIR
{
handle_type handle; /* -1 for failed rewind */
struct _finddata_t info;
struct dirent result; /* d_name null iff first time */
char *name; /* null-terminated char string */
};
typedef struct DIR DIR;
DIR *opendir(const char *name)
{
DIR *dir = 0;
if (name && name[0]) {
size_t base_length = strlen(name);
const char *all = /* search pattern must end with suitable wildcard */
strchr("/\\", name[base_length - 1]) ? "*" : "/*";
if ((dir = (DIR *) malloc(sizeof *dir)) != 0 &&
(dir->name = (char *) malloc(base_length + strlen(all) + 1)) != 0) {
strcat(strcpy(dir->name, name), all);
if ((dir->handle = (handle_type) _findfirst(dir->name, &dir->info)) != -1) {
dir->result.d_name = 0;
}
else { /* rollback */
free(dir->name);
free(dir);
dir = 0;
}
}
else { /* rollback */
free(dir);
dir = 0;
errno = ENOMEM;
}
}
else {
errno = EINVAL;
}
return dir;
}
int closedir(DIR *dir)
{
int result = -1;
if (dir) {
if (dir->handle != -1)
{
result = _findclose(dir->handle);
}
free(dir->name);
free(dir);
}
if (result == -1) { /* map all errors to EBADF */
errno = EBADF;
}
return result;
}
struct dirent *readdir(DIR *dir)
{
struct dirent *result = 0;
if (dir && dir->handle != -1) {
if (!dir->result.d_name || _findnext(dir->handle, &dir->info) != -1) {
result = &dir->result;
result->d_name = dir->info.name;
}
}
else {
errno = EBADF;
}
return result;
}
void rewinddir(DIR *dir)
{
if (dir && dir->handle != -1) {
_findclose(dir->handle);
dir->handle = (handle_type) _findfirst(dir->name, &dir->info);
dir->result.d_name = 0;
}
else {
errno = EBADF;
}
}
#ifdef __cplusplus
}
#endif
/*
Copyright Kevlin Henney, 1997, 2003, 2012. All rights reserved.
Permission to use, copy, modify, and distribute this software and its
documentation for any purpose is hereby granted without fee, provided
that this copyright and permissions notice appear in all copies and
derivatives.
This software is supplied "as is" without express or implied warranty.
But that said, if there are any problems please get in touch.
*/
+69 -91
View File
@@ -11,36 +11,15 @@
#include <mruby/error.h>
#include <mruby/string.h>
#include <mruby/presym.h>
#include <sys/types.h>
#if defined(_WIN32)
#define MAXPATHLEN 1024
#if !defined(PATH_MAX)
#define PATH_MAX MAX_PATH
#endif
#define S_ISDIR(B) ((B)&_S_IFDIR)
#include "Win/dirent.c"
#include <direct.h>
#define rmdir(path) _rmdir(path)
#define getcwd(path,len) _getcwd(path,len)
#define mkdir(path,mode) _mkdir(path)
#define chdir(path) _chdir(path)
#else
#include <sys/param.h>
#include <dirent.h>
#include <unistd.h>
#endif
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include "dir_hal.h"
#include <string.h>
#include <errno.h>
#define E_IO_ERROR mrb_exc_get_id(mrb, MRB_SYM(IOError))
struct mrb_dir {
DIR *dir;
mrb_dir_handle *handle;
};
static void
@@ -48,9 +27,9 @@ mrb_dir_free(mrb_state *mrb, void *ptr)
{
struct mrb_dir *mdir = (struct mrb_dir*)ptr;
if (mdir->dir) {
closedir(mdir->dir);
mdir->dir = NULL;
if (mdir->handle) {
mrb_dir_hal_close(mrb, mdir->handle);
mdir->handle = NULL;
}
mrb_free(mrb, mdir);
}
@@ -73,13 +52,13 @@ mrb_dir_close(mrb_state *mrb, mrb_value self)
struct mrb_dir *mdir;
mdir = (struct mrb_dir*)mrb_get_datatype(mrb, self, &mrb_dir_type);
if (!mdir) return mrb_nil_value();
if (!mdir->dir) {
if (!mdir->handle) {
mrb_raise(mrb, E_IO_ERROR, "closed directory");
}
if (closedir(mdir->dir) == -1) {
if (mrb_dir_hal_close(mrb, mdir->handle) == -1) {
mrb_sys_fail(mrb, "closedir");
}
mdir->dir = NULL;
mdir->handle = NULL;
return mrb_nil_value();
}
@@ -94,7 +73,7 @@ mrb_dir_close(mrb_state *mrb, mrb_value self)
static mrb_value
mrb_dir_init(mrb_state *mrb, mrb_value self)
{
DIR *dir;
mrb_dir_handle *handle;
struct mrb_dir *mdir;
const char *path;
@@ -106,14 +85,14 @@ mrb_dir_init(mrb_state *mrb, mrb_value self)
DATA_PTR(self) = NULL;
mdir = (struct mrb_dir*)mrb_malloc(mrb, sizeof(*mdir));
mdir->dir = NULL;
mdir->handle = NULL;
DATA_PTR(self) = mdir;
mrb_get_args(mrb, "z", &path);
if ((dir = opendir(path)) == NULL) {
if ((handle = mrb_dir_hal_open(mrb, path)) == NULL) {
mrb_sys_fail(mrb, path);
}
mdir->dir = dir;
mdir->handle = handle;
return self;
}
@@ -132,7 +111,7 @@ mrb_dir_delete(mrb_state *mrb, mrb_value klass)
const char *path;
mrb_get_args(mrb, "z", &path);
if (rmdir(path) == -1) {
if (mrb_dir_hal_rmdir(mrb, path) == -1) {
mrb_sys_fail(mrb, path);
}
return mrb_fixnum_value(0);
@@ -150,11 +129,10 @@ mrb_dir_delete(mrb_state *mrb, mrb_value klass)
static mrb_value
mrb_dir_existp(mrb_state *mrb, mrb_value klass)
{
struct stat sb;
const char *path;
mrb_get_args(mrb, "z", &path);
if (stat(path, &sb) == 0 && S_ISDIR(sb.st_mode)) {
if (mrb_dir_hal_is_directory(mrb, path)) {
return mrb_true_value();
}
else {
@@ -178,7 +156,7 @@ mrb_dir_getwd(mrb_state *mrb, mrb_value klass)
mrb_int size = 64;
path = mrb_str_buf_new(mrb, size);
while (getcwd(RSTRING_PTR(path), (size_t)size) == NULL) {
while (mrb_dir_hal_getcwd(mrb, RSTRING_PTR(path), (size_t)size) == -1) {
int e = errno;
if (e != ERANGE) {
mrb_sys_fail(mrb, "getcwd(2)");
@@ -210,7 +188,7 @@ mrb_dir_mkdir(mrb_state *mrb, mrb_value klass)
mode = 0777;
mrb_get_args(mrb, "z|i", &path, &mode);
if (mkdir(path, mode) == -1) {
if (mrb_dir_hal_mkdir(mrb, path, (int)mode) == -1) {
mrb_sys_fail(mrb, path);
}
return mrb_fixnum_value(0);
@@ -223,7 +201,7 @@ mrb_dir_chdir(mrb_state *mrb, mrb_value klass)
const char *path;
mrb_get_args(mrb, "z", &path);
if (chdir(path) == -1) {
if (mrb_dir_hal_chdir(mrb, path) == -1) {
mrb_sys_fail(mrb, path);
}
return mrb_fixnum_value(0);
@@ -241,21 +219,19 @@ mrb_dir_chdir(mrb_state *mrb, mrb_value klass)
static mrb_value
mrb_dir_chroot(mrb_state *mrb, mrb_value self)
{
#if defined(_WIN32) || defined(__ANDROID__) || defined(__MSDOS__)
mrb_raise(mrb, E_NOTIMP_ERROR, "chroot() unreliable on your system");
return mrb_fixnum_value(0);
#else
const char *path;
mrb_int res;
int res;
mrb_get_args(mrb, "z", &path);
res = chroot(path);
res = mrb_dir_hal_chroot(mrb, path);
if (res == -1) {
if (errno == ENOSYS) {
mrb_raise(mrb, E_NOTIMP_ERROR, "chroot() unreliable on your system");
}
mrb_sys_fail(mrb, path);
}
return mrb_fixnum_value(res);
#endif
}
static mrb_bool
@@ -280,22 +256,22 @@ skip_name_p(const char *name)
static mrb_value
mrb_dir_empty(mrb_state *mrb, mrb_value self)
{
DIR *dir;
struct dirent *dp;
mrb_dir_handle *handle;
const char *name;
const char *path;
mrb_value result = mrb_true_value();
mrb_get_args(mrb, "z", &path);
if ((dir = opendir(path)) == NULL) {
if ((handle = mrb_dir_hal_open(mrb, path)) == NULL) {
mrb_sys_fail(mrb, path);
}
while ((dp = readdir(dir))) {
if (!skip_name_p(dp->d_name)) {
while ((name = mrb_dir_hal_read(mrb, handle)) != NULL) {
if (!skip_name_p(name)) {
result = mrb_false_value();
break;
}
}
closedir(dir);
mrb_dir_hal_close(mrb, handle);
return result;
}
@@ -315,16 +291,16 @@ static mrb_value
mrb_dir_read(mrb_state *mrb, mrb_value self)
{
struct mrb_dir *mdir;
struct dirent *dp;
const char *name;
mdir = (struct mrb_dir*)mrb_get_datatype(mrb, self, &mrb_dir_type);
if (!mdir) return mrb_nil_value();
if (!mdir->dir) {
if (!mdir->handle) {
mrb_raise(mrb, E_IO_ERROR, "closed directory");
}
dp = readdir(mdir->dir);
if (dp != NULL) {
return mrb_str_new_cstr(mrb, dp->d_name);
name = mrb_dir_hal_read(mrb, mdir->handle);
if (name != NULL) {
return mrb_str_new_cstr(mrb, name);
}
else {
return mrb_nil_value();
@@ -349,10 +325,10 @@ mrb_dir_rewind(mrb_state *mrb, mrb_value self)
mdir = (struct mrb_dir*)mrb_get_datatype(mrb, self, &mrb_dir_type);
if (!mdir) return mrb_nil_value();
if (!mdir->dir) {
if (!mdir->handle) {
mrb_raise(mrb, E_IO_ERROR, "closed directory");
}
rewinddir(mdir->dir);
mrb_dir_hal_rewind(mrb, mdir->handle);
return self;
}
@@ -372,22 +348,21 @@ mrb_dir_rewind(mrb_state *mrb, mrb_value self)
static mrb_value
mrb_dir_seek(mrb_state *mrb, mrb_value self)
{
#if defined(_WIN32) || defined(__ANDROID__)
mrb_raise(mrb, E_NOTIMP_ERROR, "dirseek() unreliable on your system");
return self;
#else
struct mrb_dir *mdir;
mrb_int pos;
mdir = (struct mrb_dir*)mrb_get_datatype(mrb, self, &mrb_dir_type);
if (!mdir) return mrb_nil_value();
if (!mdir->dir) {
if (!mdir->handle) {
mrb_raise(mrb, E_IO_ERROR, "closed directory");
}
mrb_get_args(mrb, "i", &pos);
seekdir(mdir->dir, (long)pos);
if (mrb_dir_hal_seek(mrb, mdir->handle, (long)pos) == -1) {
if (errno == ENOSYS) {
mrb_raise(mrb, E_NOTIMP_ERROR, "dirseek() unreliable on your system");
}
}
return self;
#endif
}
/*
@@ -405,21 +380,21 @@ mrb_dir_seek(mrb_state *mrb, mrb_value self)
static mrb_value
mrb_dir_tell(mrb_state *mrb, mrb_value self)
{
#if defined(_WIN32) || defined(__ANDROID__)
mrb_raise(mrb, E_NOTIMP_ERROR, "dirtell() unreliable on your system");
return mrb_fixnum_value(0);
#else
struct mrb_dir *mdir;
mrb_int pos;
long pos;
mdir = (struct mrb_dir*)mrb_get_datatype(mrb, self, &mrb_dir_type);
if (!mdir) return mrb_nil_value();
if (!mdir->dir) {
if (!mdir->handle) {
mrb_raise(mrb, E_IO_ERROR, "closed directory");
}
pos = (mrb_int)telldir(mdir->dir);
return mrb_fixnum_value(pos);
#endif
pos = mrb_dir_hal_tell(mrb, mdir->handle);
if (pos == -1) {
if (errno == ENOSYS) {
mrb_raise(mrb, E_NOTIMP_ERROR, "dirtell() unreliable on your system");
}
}
return mrb_fixnum_value((mrb_int)pos);
}
/*
@@ -433,23 +408,23 @@ static mrb_value
mrb_dir_entries(mrb_state *mrb, mrb_value klass)
{
const char *path;
DIR *dir;
struct dirent *dp;
mrb_dir_handle *handle;
const char *name;
mrb_value ary;
mrb_get_args(mrb, "z", &path);
dir = opendir(path);
if (dir == NULL) {
handle = mrb_dir_hal_open(mrb, path);
if (handle == NULL) {
mrb_sys_fail(mrb, path);
}
ary = mrb_ary_new(mrb);
while ((dp = readdir(dir)) != NULL) {
mrb_ary_push(mrb, ary, mrb_str_new_cstr(mrb, dp->d_name));
while ((name = mrb_dir_hal_read(mrb, handle)) != NULL) {
mrb_ary_push(mrb, ary, mrb_str_new_cstr(mrb, name));
}
closedir(dir);
mrb_dir_hal_close(mrb, handle);
return ary;
}
@@ -465,25 +440,25 @@ static mrb_value
mrb_dir_children(mrb_state *mrb, mrb_value klass)
{
const char *path;
DIR *dir;
struct dirent *dp;
mrb_dir_handle *handle;
const char *name;
mrb_value ary;
mrb_get_args(mrb, "z", &path);
dir = opendir(path);
if (dir == NULL) {
handle = mrb_dir_hal_open(mrb, path);
if (handle == NULL) {
mrb_sys_fail(mrb, path);
}
ary = mrb_ary_new(mrb);
while ((dp = readdir(dir)) != NULL) {
if (!skip_name_p(dp->d_name)) {
mrb_ary_push(mrb, ary, mrb_str_new_cstr(mrb, dp->d_name));
while ((name = mrb_dir_hal_read(mrb, handle)) != NULL) {
if (!skip_name_p(name)) {
mrb_ary_push(mrb, ary, mrb_str_new_cstr(mrb, name));
}
}
closedir(dir);
mrb_dir_hal_close(mrb, handle);
return ary;
}
@@ -492,6 +467,8 @@ mrb_mruby_dir_gem_init(mrb_state *mrb)
{
struct RClass *d;
mrb_dir_hal_init(mrb);
d = mrb_define_class_id(mrb, MRB_SYM(Dir), mrb->object_class);
MRB_SET_INSTANCE_TT(d, MRB_TT_DATA);
mrb_define_class_method_id(mrb, d, MRB_SYM(delete), mrb_dir_delete, MRB_ARGS_REQ(1));
@@ -517,4 +494,5 @@ mrb_mruby_dir_gem_init(mrb_state *mrb)
void
mrb_mruby_dir_gem_final(mrb_state *mrb)
{
mrb_dir_hal_final(mrb);
}