summaryrefslogtreecommitdiffhomepage
path: root/mrbgems/mruby-io
diff options
context:
space:
mode:
Diffstat (limited to 'mrbgems/mruby-io')
-rw-r--r--mrbgems/mruby-io/README.md1
-rw-r--r--mrbgems/mruby-io/include/mruby/ext/io.h48
-rw-r--r--mrbgems/mruby-io/mrbgem.rake12
-rw-r--r--mrbgems/mruby-io/mrblib/file.rb66
-rw-r--r--mrbgems/mruby-io/mrblib/file_constants.rb16
-rw-r--r--mrbgems/mruby-io/mrblib/io.rb19
-rw-r--r--mrbgems/mruby-io/src/file.c162
-rw-r--r--mrbgems/mruby-io/src/file_test.c6
-rw-r--r--mrbgems/mruby-io/src/io.c445
-rw-r--r--mrbgems/mruby-io/test/file.rb46
-rw-r--r--mrbgems/mruby-io/test/io.rb32
-rw-r--r--mrbgems/mruby-io/test/mruby_io_test.c127
12 files changed, 681 insertions, 299 deletions
diff --git a/mrbgems/mruby-io/README.md b/mrbgems/mruby-io/README.md
index ccf56f970..2c5b762d8 100644
--- a/mrbgems/mruby-io/README.md
+++ b/mrbgems/mruby-io/README.md
@@ -171,6 +171,7 @@ Add the line below to your `build_config.rb`:
## License
Copyright (c) 2013 Internet Initiative Japan Inc.
+Copyright (c) 2017 mruby developers
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
diff --git a/mrbgems/mruby-io/include/mruby/ext/io.h b/mrbgems/mruby-io/include/mruby/ext/io.h
index ba088156e..dfff8e0e0 100644
--- a/mrbgems/mruby-io/include/mruby/ext/io.h
+++ b/mrbgems/mruby-io/include/mruby/ext/io.h
@@ -5,10 +5,24 @@
#ifndef MRUBY_IO_H
#define MRUBY_IO_H
+#include <mruby.h>
+
+#ifdef MRB_DISABLE_STDIO
+# error IO and File conflicts 'MRB_DISABLE_STDIO' configuration in your 'build_config.rb'
+#endif
+
#if defined(__cplusplus)
extern "C" {
#endif
+#if defined(MRB_WITHOUT_IO_PREAD_PWRITE)
+# undef MRB_WITH_IO_PREAD_PWRITE
+#elif !defined(MRB_WITH_IO_PREAD_PWRITE)
+# if defined(__unix__) || defined(__MACH__)
+# define MRB_WITH_IO_PREAD_PWRITE
+# endif
+#endif
+
struct mrb_io {
int fd; /* file descriptor, or -1 */
int fd2; /* file descriptor to write if it's different from fd, or -1 */
@@ -19,18 +33,36 @@ struct mrb_io {
is_socket:1;
};
-#define FMODE_READABLE 0x00000001
-#define FMODE_WRITABLE 0x00000002
-#define FMODE_READWRITE (FMODE_READABLE|FMODE_WRITABLE)
-#define FMODE_BINMODE 0x00000004
-#define FMODE_APPEND 0x00000040
-#define FMODE_CREATE 0x00000080
-#define FMODE_TRUNC 0x00000800
+#define MRB_O_RDONLY 0x0000
+#define MRB_O_WRONLY 0x0001
+#define MRB_O_RDWR 0x0002
+#define MRB_O_ACCMODE (MRB_O_RDONLY | MRB_O_WRONLY | MRB_O_RDWR)
+#define MRB_O_NONBLOCK 0x0004
+#define MRB_O_APPEND 0x0008
+#define MRB_O_SYNC 0x0010
+#define MRB_O_NOFOLLOW 0x0020
+#define MRB_O_CREAT 0x0040
+#define MRB_O_TRUNC 0x0080
+#define MRB_O_EXCL 0x0100
+#define MRB_O_NOCTTY 0x0200
+#define MRB_O_DIRECT 0x0400
+#define MRB_O_BINARY 0x0800
+#define MRB_O_SHARE_DELETE 0x1000
+#define MRB_O_TMPFILE 0x2000
+#define MRB_O_NOATIME 0x4000
+#define MRB_O_DSYNC 0x00008000
+#define MRB_O_RSYNC 0x00010000
+
+#define MRB_O_RDONLY_P(f) ((mrb_bool)(((f) & MRB_O_ACCMODE) == MRB_O_RDONLY))
+#define MRB_O_WRONLY_P(f) ((mrb_bool)(((f) & MRB_O_ACCMODE) == MRB_O_WRONLY))
+#define MRB_O_RDWR_P(f) ((mrb_bool)(((f) & MRB_O_ACCMODE) == MRB_O_RDWR))
+#define MRB_O_READABLE_P(f) ((mrb_bool)((((f) & MRB_O_ACCMODE) | 2) == 2))
+#define MRB_O_WRITABLE_P(f) ((mrb_bool)(((((f) & MRB_O_ACCMODE) + 1) & 2) == 2))
#define E_IO_ERROR (mrb_class_get(mrb, "IOError"))
#define E_EOF_ERROR (mrb_class_get(mrb, "EOFError"))
-mrb_value mrb_io_fileno(mrb_state *mrb, mrb_value io);
+int mrb_io_fileno(mrb_state *mrb, mrb_value io);
#if defined(__cplusplus)
} /* extern "C" { */
diff --git a/mrbgems/mruby-io/mrbgem.rake b/mrbgems/mruby-io/mrbgem.rake
index e4f0b7bb6..d0c8fb052 100644
--- a/mrbgems/mruby-io/mrbgem.rake
+++ b/mrbgems/mruby-io/mrbgem.rake
@@ -1,18 +1,12 @@
MRuby::Gem::Specification.new('mruby-io') do |spec|
spec.license = 'MIT'
- spec.authors = 'Internet Initiative Japan Inc.'
+ spec.authors = ['Internet Initiative Japan Inc.', 'mruby developers']
spec.summary = 'IO and File class'
spec.cc.include_paths << "#{build.root}/src"
- case RUBY_PLATFORM
- when /mingw|mswin|msys/
- spec.linker.libraries += ['Ws2_32']
- #spec.cc.include_paths += ["C:/Windows/system/include"]
- spec.linker.library_paths += ["C:/Windows/system"]
- end
- if build.kind_of?(MRuby::CrossBuild) && %w(x86_64-w64-mingw32 i686-w64-mingw32).include?(build.host_target)
- spec.linker.libraries += ['ws2_32']
+ if for_windows?
+ spec.linker.libraries << "ws2_32"
end
spec.add_test_dependency 'mruby-time', core: 'mruby-time'
end
diff --git a/mrbgems/mruby-io/mrblib/file.rb b/mrbgems/mruby-io/mrblib/file.rb
index 710333d6f..d3a4b1ec7 100644
--- a/mrbgems/mruby-io/mrblib/file.rb
+++ b/mrbgems/mruby-io/mrblib/file.rb
@@ -55,46 +55,46 @@ class File < IO
s
end
- def self.expand_path(path, default_dir = '.')
- def concat_path(path, base_path)
- if path[0] == "/" || path[1] == ':' # Windows root!
- expanded_path = path
- elsif path[0] == "~"
- if (path[1] == "/" || path[1] == nil)
- dir = path[1, path.size]
- home_dir = _gethome
-
- unless home_dir
- raise ArgumentError, "couldn't find HOME environment -- expanding '~'"
- end
-
- expanded_path = home_dir
- expanded_path += dir if dir
- expanded_path += "/"
- else
- splitted_path = path.split("/")
- user = splitted_path[0][1, splitted_path[0].size]
- dir = "/" + splitted_path[1, splitted_path.size].join("/")
+ def self._concat_path(path, base_path)
+ if path[0] == "/" || path[1] == ':' # Windows root!
+ expanded_path = path
+ elsif path[0] == "~"
+ if (path[1] == "/" || path[1] == nil)
+ dir = path[1, path.size]
+ home_dir = _gethome
+
+ unless home_dir
+ raise ArgumentError, "couldn't find HOME environment -- expanding '~'"
+ end
- home_dir = _gethome(user)
+ expanded_path = home_dir
+ expanded_path += dir if dir
+ expanded_path += "/"
+ else
+ splitted_path = path.split("/")
+ user = splitted_path[0][1, splitted_path[0].size]
+ dir = "/" + splitted_path[1, splitted_path.size].join("/")
- unless home_dir
- raise ArgumentError, "user #{user} doesn't exist"
- end
+ home_dir = _gethome(user)
- expanded_path = home_dir
- expanded_path += dir if dir
- expanded_path += "/"
+ unless home_dir
+ raise ArgumentError, "user #{user} doesn't exist"
end
- else
- expanded_path = concat_path(base_path, _getwd)
- expanded_path += "/" + path
- end
- expanded_path
+ expanded_path = home_dir
+ expanded_path += dir if dir
+ expanded_path += "/"
+ end
+ else
+ expanded_path = _concat_path(base_path, _getwd)
+ expanded_path += "/" + path
end
- expanded_path = concat_path(path, default_dir)
+ expanded_path
+ end
+
+ def self.expand_path(path, default_dir = '.')
+ expanded_path = _concat_path(path, default_dir)
drive_prefix = ""
if File::ALT_SEPARATOR && expanded_path.size > 2 &&
("A".."Z").include?(expanded_path[0].upcase) && expanded_path[1] == ":"
diff --git a/mrbgems/mruby-io/mrblib/file_constants.rb b/mrbgems/mruby-io/mrblib/file_constants.rb
index a68ee2598..bd77d53fe 100644
--- a/mrbgems/mruby-io/mrblib/file_constants.rb
+++ b/mrbgems/mruby-io/mrblib/file_constants.rb
@@ -1,21 +1,5 @@
class File
module Constants
- RDONLY = 0
- WRONLY = 1
- RDWR = 2
- NONBLOCK = 4
- APPEND = 8
-
- BINARY = 0
- SYNC = 128
- NOFOLLOW = 256
- CREAT = 512
- TRUNC = 1024
- EXCL = 2048
-
- NOCTTY = 131072
- DSYNC = 4194304
-
FNM_SYSCASE = 0
FNM_NOESCAPE = 1
FNM_PATHNAME = 2
diff --git a/mrbgems/mruby-io/mrblib/io.rb b/mrbgems/mruby-io/mrblib/io.rb
index 5df1932df..e597db886 100644
--- a/mrbgems/mruby-io/mrblib/io.rb
+++ b/mrbgems/mruby-io/mrblib/io.rb
@@ -170,21 +170,16 @@ class IO
end
def _read_buf
- return @buf if @buf && @buf.bytesize >= 4 # maximum UTF-8 character is 4 bytes
- @buf ||= ""
- begin
- @buf += sysread(BUF_SIZE)
- rescue EOFError => e
- raise e if @buf.empty?
- end
+ return @buf if @buf && @buf.bytesize > 0
+ sysread(BUF_SIZE, @buf)
end
def ungetc(substr)
raise TypeError.new "expect String, got #{substr.class}" unless substr.is_a?(String)
if @buf.empty?
- @buf = substr.dup
+ @buf.replace(substr)
else
- @buf = substr + @buf
+ @buf[0,0] = substr
end
nil
end
@@ -288,15 +283,15 @@ class IO
def readchar
_read_buf
- c = @buf[0]
- @buf[0] = ""
- c
+ _readchar(@buf)
end
def getc
begin
readchar
rescue EOFError
+ c = @buf[0]
+ @buf[0,1]="" if c
nil
end
end
diff --git a/mrbgems/mruby-io/src/file.c b/mrbgems/mruby-io/src/file.c
index f9633535c..004eb0a5f 100644
--- a/mrbgems/mruby-io/src/file.c
+++ b/mrbgems/mruby-io/src/file.c
@@ -7,12 +7,7 @@
#include "mruby/data.h"
#include "mruby/string.h"
#include "mruby/ext/io.h"
-
-#if MRUBY_RELEASE_NO < 10000
-#include "error.h"
-#else
#include "mruby/error.h"
-#endif
#include <sys/types.h>
#include <sys/stat.h>
@@ -53,6 +48,7 @@
#if defined(_WIN32) || defined(_WIN64)
#define PATH_SEPARATOR ";"
#define FILE_ALT_SEPARATOR "\\"
+ #define VOLUME_SEPARATOR ":"
#else
#define PATH_SEPARATOR ":"
#endif
@@ -70,7 +66,15 @@
#define LOCK_UN 8
#endif
-#define STAT(p, s) stat(p, s)
+#ifndef _WIN32
+typedef struct stat mrb_stat;
+# define mrb_stat(path, sb) stat(path, sb)
+# define mrb_fstat(fd, sb) fstat(fd, sb)
+#else
+typedef struct __stat64 mrb_stat;
+# define mrb_stat(path, sb) _stat64(path, sb)
+# define mrb_fstat(fd, sb) _fstat64(fd, sb)
+#endif
#ifdef _WIN32
static int
@@ -276,12 +280,59 @@ mrb_file__getwd(mrb_state *mrb, mrb_value klass)
return path;
}
+#ifdef _WIN32
+#define IS_FILESEP(x) (x == (*(char*)(FILE_SEPARATOR)) || x == (*(char*)(FILE_ALT_SEPARATOR)))
+#define IS_VOLSEP(x) (x == (*(char*)(VOLUME_SEPARATOR)))
+#define IS_DEVICEID(x) (x == '.' || x == '?')
+#define CHECK_UNCDEV_PATH (IS_FILESEP(path[0]) && IS_FILESEP(path[1]))
+
+static int
+is_absolute_traditional_path(const char *path, size_t len)
+{
+ if (len < 3) return 0;
+ return (ISALPHA(path[0]) && IS_VOLSEP(path[1]) && IS_FILESEP(path[2]));
+}
+
+static int
+is_aboslute_unc_path(const char *path, size_t len) {
+ if (len < 2) return 0;
+ return (CHECK_UNCDEV_PATH && !IS_DEVICEID(path[2]));
+}
+
+static int
+is_absolute_device_path(const char *path, size_t len) {
+ if (len < 4) return 0;
+ return (CHECK_UNCDEV_PATH && IS_DEVICEID(path[2]) && IS_FILESEP(path[3]));
+}
+
static int
mrb_file_is_absolute_path(const char *path)
{
- return (path[0] == '/');
+ size_t len = strlen(path);
+ if (IS_FILESEP(path[0])) return 1;
+ if (len > 0)
+ return (
+ is_absolute_traditional_path(path, len) ||
+ is_aboslute_unc_path(path, len) ||
+ is_absolute_device_path(path, len)
+ );
+ else
+ return 0;
}
+#undef IS_FILESEP
+#undef IS_VOLSEP
+#undef IS_DEVICEID
+#undef CHECK_UNCDEV_PATH
+
+#else
+static int
+mrb_file_is_absolute_path(const char *path)
+{
+ return (path[0] == *(char*)(FILE_SEPARATOR));
+}
+#endif
+
static mrb_value
mrb_file__gethome(mrb_state *mrb, mrb_value klass)
{
@@ -316,7 +367,7 @@ mrb_file__gethome(mrb_state *mrb, mrb_value klass)
path = mrb_str_new_cstr(mrb, home);
mrb_locale_free(home);
return path;
-#else
+#else /* _WIN32 */
argc = mrb_get_argc(mrb);
if (argc == 0) {
home = getenv("USERPROFILE");
@@ -344,7 +395,7 @@ mrb_file_mtime(mrb_state *mrb, mrb_value self)
int fd;
obj = mrb_obj_value(mrb_class_get(mrb, "Time"));
- fd = (int)mrb_fixnum(mrb_io_fileno(mrb, self));
+ fd = mrb_io_fileno(mrb, self);
if (fstat(fd, &st) == -1)
return mrb_false_value();
return mrb_funcall(mrb, obj, "at", 1, mrb_fixnum_value(st.st_mtime));
@@ -360,7 +411,7 @@ mrb_file_flock(mrb_state *mrb, mrb_value self)
int fd;
mrb_get_args(mrb, "i", &operation);
- fd = (int)mrb_fixnum(mrb_io_fileno(mrb, self));
+ fd = mrb_io_fileno(mrb, self);
while (flock(fd, (int)operation) == -1) {
switch (errno) {
@@ -385,6 +436,75 @@ mrb_file_flock(mrb_state *mrb, mrb_value self)
}
static mrb_value
+mrb_file_size(mrb_state *mrb, mrb_value self)
+{
+ mrb_stat st;
+ int fd;
+
+ fd = mrb_io_fileno(mrb, self);
+ if (mrb_fstat(fd, &st) == -1) {
+ mrb_raise(mrb, E_RUNTIME_ERROR, "fstat failed");
+ }
+
+ if (st.st_size > MRB_INT_MAX) {
+#ifdef MRB_WITHOUT_FLOAT
+ mrb_raise(mrb, E_RUNTIME_ERROR, "File#size too large for MRB_WITHOUT_FLOAT");
+#else
+ return mrb_float_value(mrb, (mrb_float)st.st_size);
+#endif
+ }
+
+ return mrb_fixnum_value((mrb_int)st.st_size);
+}
+
+static int
+mrb_ftruncate(int fd, mrb_int length)
+{
+#ifndef _WIN32
+ return ftruncate(fd, (off_t)length);
+#else
+ HANDLE file;
+ __int64 cur;
+
+ 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 */
+}
+
+static mrb_value
+mrb_file_truncate(mrb_state *mrb, mrb_value self)
+{
+ int fd;
+ mrb_int length;
+ mrb_value lenv;
+
+ fd = mrb_io_fileno(mrb, self);
+ mrb_get_args(mrb, "o", &lenv);
+ length = mrb_int(mrb, lenv);
+ if (mrb_ftruncate(fd, length) != 0) {
+ mrb_raise(mrb, E_IO_ERROR, "ftruncate failed");
+ }
+
+ return mrb_fixnum_value(0);
+}
+
+static mrb_value
mrb_file_s_symlink(mrb_state *mrb, mrb_value klass)
{
#if defined(_WIN32) || defined(_WIN64)
@@ -474,7 +594,7 @@ mrb_init_file(mrb_state *mrb)
io = mrb_class_get(mrb, "IO");
file = mrb_define_class(mrb, "File", io);
MRB_SET_INSTANCE_TT(file, MRB_TT_DATA);
- mrb_define_class_method(mrb, file, "umask", mrb_file_s_umask, MRB_ARGS_REQ(1));
+ mrb_define_class_method(mrb, file, "umask", mrb_file_s_umask, MRB_ARGS_OPT(1));
mrb_define_class_method(mrb, file, "delete", mrb_file_s_unlink, MRB_ARGS_ANY());
mrb_define_class_method(mrb, file, "unlink", mrb_file_s_unlink, MRB_ARGS_ANY());
mrb_define_class_method(mrb, file, "rename", mrb_file_s_rename, MRB_ARGS_REQ(2));
@@ -490,6 +610,8 @@ mrb_init_file(mrb_state *mrb)
mrb_define_method(mrb, file, "flock", mrb_file_flock, MRB_ARGS_REQ(1));
mrb_define_method(mrb, file, "mtime", mrb_file_mtime, MRB_ARGS_NONE());
+ mrb_define_method(mrb, file, "size", mrb_file_size, MRB_ARGS_NONE());
+ mrb_define_method(mrb, file, "truncate", mrb_file_truncate, MRB_ARGS_REQ(1));
cnst = mrb_define_module_under(mrb, file, "Constants");
mrb_define_const(mrb, cnst, "LOCK_SH", mrb_fixnum_value(LOCK_SH));
@@ -505,4 +627,22 @@ mrb_init_file(mrb_state *mrb)
#endif
mrb_define_const(mrb, cnst, "NULL", mrb_str_new_cstr(mrb, NULL_FILE));
+ mrb_define_const(mrb, cnst, "RDONLY", mrb_fixnum_value(MRB_O_RDONLY));
+ mrb_define_const(mrb, cnst, "WRONLY", mrb_fixnum_value(MRB_O_WRONLY));
+ mrb_define_const(mrb, cnst, "RDWR", mrb_fixnum_value(MRB_O_RDWR));
+ mrb_define_const(mrb, cnst, "APPEND", mrb_fixnum_value(MRB_O_APPEND));
+ mrb_define_const(mrb, cnst, "CREAT", mrb_fixnum_value(MRB_O_CREAT));
+ mrb_define_const(mrb, cnst, "EXCL", mrb_fixnum_value(MRB_O_EXCL));
+ mrb_define_const(mrb, cnst, "TRUNC", mrb_fixnum_value(MRB_O_TRUNC));
+ mrb_define_const(mrb, cnst, "NONBLOCK", mrb_fixnum_value(MRB_O_NONBLOCK));
+ mrb_define_const(mrb, cnst, "NOCTTY", mrb_fixnum_value(MRB_O_NOCTTY));
+ mrb_define_const(mrb, cnst, "BINARY", mrb_fixnum_value(MRB_O_BINARY));
+ mrb_define_const(mrb, cnst, "SHARE_DELETE", mrb_fixnum_value(MRB_O_SHARE_DELETE));
+ mrb_define_const(mrb, cnst, "SYNC", mrb_fixnum_value(MRB_O_SYNC));
+ mrb_define_const(mrb, cnst, "DSYNC", mrb_fixnum_value(MRB_O_DSYNC));
+ mrb_define_const(mrb, cnst, "RSYNC", mrb_fixnum_value(MRB_O_RSYNC));
+ mrb_define_const(mrb, cnst, "NOFOLLOW", mrb_fixnum_value(MRB_O_NOFOLLOW));
+ mrb_define_const(mrb, cnst, "NOATIME", mrb_fixnum_value(MRB_O_NOATIME));
+ mrb_define_const(mrb, cnst, "DIRECT", mrb_fixnum_value(MRB_O_DIRECT));
+ mrb_define_const(mrb, cnst, "TMPFILE", mrb_fixnum_value(MRB_O_TMPFILE));
}
diff --git a/mrbgems/mruby-io/src/file_test.c b/mrbgems/mruby-io/src/file_test.c
index 445bafde9..d75cbd598 100644
--- a/mrbgems/mruby-io/src/file_test.c
+++ b/mrbgems/mruby-io/src/file_test.c
@@ -7,12 +7,7 @@
#include "mruby/data.h"
#include "mruby/string.h"
#include "mruby/ext/io.h"
-
-#if MRUBY_RELEASE_NO < 10000
-#include "error.h"
-#else
#include "mruby/error.h"
-#endif
#include <sys/types.h>
#include <sys/stat.h>
@@ -33,7 +28,6 @@
#include <fcntl.h>
#include <errno.h>
-#include <stdio.h>
#include <stdlib.h>
#include <string.h>
diff --git a/mrbgems/mruby-io/src/io.c b/mrbgems/mruby-io/src/io.c
index 3a6932b3a..3bf3d28be 100644
--- a/mrbgems/mruby-io/src/io.c
+++ b/mrbgems/mruby-io/src/io.c
@@ -10,12 +10,7 @@
#include "mruby/string.h"
#include "mruby/variable.h"
#include "mruby/ext/io.h"
-
-#if MRUBY_RELEASE_NO < 10000
-#include "error.h"
-#else
#include "mruby/error.h"
-#endif
#include <sys/types.h>
#include <sys/stat.h>
@@ -36,14 +31,21 @@
typedef long ftime_t;
typedef long fsuseconds_t;
typedef int fmode_t;
+ typedef int mrb_io_read_write_size;
+
+ #ifndef O_TMPFILE
+ #define O_TMPFILE O_TEMPORARY
+ #endif
#else
#include <sys/wait.h>
+ #include <sys/time.h>
#include <unistd.h>
typedef size_t fsize_t;
typedef time_t ftime_t;
typedef suseconds_t fsuseconds_t;
typedef mode_t fmode_t;
+ typedef ssize_t mrb_io_read_write_size;
#endif
#ifdef _MSC_VER
@@ -53,9 +55,14 @@ typedef mrb_int pid_t;
#include <fcntl.h>
#include <errno.h>
-#include <stdio.h>
#include <string.h>
+#define OPEN_ACCESS_MODE_FLAGS (O_RDONLY | O_WRONLY | O_RDWR)
+#define OPEN_RDONLY_P(f) ((mrb_bool)(((f) & OPEN_ACCESS_MODE_FLAGS) == O_RDONLY))
+#define OPEN_WRONLY_P(f) ((mrb_bool)(((f) & OPEN_ACCESS_MODE_FLAGS) == O_WRONLY))
+#define OPEN_RDWR_P(f) ((mrb_bool)(((f) & OPEN_ACCESS_MODE_FLAGS) == O_RDWR))
+#define OPEN_READABLE_P(f) ((mrb_bool)(OPEN_RDONLY_P(f) || OPEN_RDWR_P(f)))
+#define OPEN_WRITABLE_P(f) ((mrb_bool)(OPEN_WRONLY_P(f) || OPEN_RDWR_P(f)))
static void mrb_io_free(mrb_state *mrb, void *ptr);
struct mrb_data_type mrb_io_type = { "IO", mrb_io_free };
@@ -63,17 +70,9 @@ struct mrb_data_type mrb_io_type = { "IO", mrb_io_free };
static struct mrb_io *io_get_open_fptr(mrb_state *mrb, mrb_value self);
static int mrb_io_modestr_to_flags(mrb_state *mrb, const char *modestr);
-static int mrb_io_flags_to_modenum(mrb_state *mrb, int flags);
+static int mrb_io_mode_to_flags(mrb_state *mrb, mrb_value mode);
static void fptr_finalize(mrb_state *mrb, struct mrb_io *fptr, int quiet);
-#if MRUBY_RELEASE_NO < 10000
-static struct RClass *
-mrb_module_get(mrb_state *mrb, const char *name)
-{
- return mrb_class_get(mrb, name);
-}
-#endif
-
static struct mrb_io *
io_get_open_fptr(mrb_state *mrb, mrb_value self)
{
@@ -113,30 +112,33 @@ io_set_process_status(mrb_state *mrb, pid_t pid, int status)
static int
mrb_io_modestr_to_flags(mrb_state *mrb, const char *mode)
{
- int flags = 0;
+ int flags;
const char *m = mode;
switch (*m++) {
case 'r':
- flags |= FMODE_READABLE;
+ flags = O_RDONLY;
break;
case 'w':
- flags |= FMODE_WRITABLE | FMODE_CREATE | FMODE_TRUNC;
+ flags = O_WRONLY | O_CREAT | O_TRUNC;
break;
case 'a':
- flags |= FMODE_WRITABLE | FMODE_APPEND | FMODE_CREATE;
+ flags = O_WRONLY | O_CREAT | O_APPEND;
break;
default:
mrb_raisef(mrb, E_ARGUMENT_ERROR, "illegal access mode %s", mode);
+ flags = 0; /* not reached */
}
while (*m) {
switch (*m++) {
case 'b':
- flags |= FMODE_BINMODE;
+#ifdef O_BINARY
+ flags |= O_BINARY;
+#endif
break;
case '+':
- flags |= FMODE_READWRITE;
+ flags = (flags & ~OPEN_ACCESS_MODE_FLAGS) | O_RDWR;
break;
case ':':
/* XXX: PASSTHROUGH*/
@@ -149,38 +151,72 @@ mrb_io_modestr_to_flags(mrb_state *mrb, const char *mode)
}
static int
-mrb_io_flags_to_modenum(mrb_state *mrb, int flags)
+mrb_io_mode_to_flags(mrb_state *mrb, mrb_value mode)
{
- int modenum = 0;
-
- switch(flags & (FMODE_READABLE|FMODE_WRITABLE|FMODE_READWRITE)) {
- case FMODE_READABLE:
- modenum = O_RDONLY;
- break;
- case FMODE_WRITABLE:
- modenum = O_WRONLY;
- break;
- case FMODE_READWRITE:
- modenum = O_RDWR;
- break;
- }
-
- if (flags & FMODE_APPEND) {
- modenum |= O_APPEND;
- }
- if (flags & FMODE_TRUNC) {
- modenum |= O_TRUNC;
+ if (mrb_nil_p(mode)) {
+ return mrb_io_modestr_to_flags(mrb, "r");
}
- if (flags & FMODE_CREATE) {
- modenum |= O_CREAT;
+ else if (mrb_string_p(mode)) {
+ return mrb_io_modestr_to_flags(mrb, RSTRING_CSTR(mrb, mode));
}
+ else {
+ int flags = 0;
+ mrb_int flags0 = mrb_int(mrb, mode);
+
+ switch (flags0 & MRB_O_ACCMODE) {
+ case MRB_O_RDONLY:
+ flags |= O_RDONLY;
+ break;
+ case MRB_O_WRONLY:
+ flags |= O_WRONLY;
+ break;
+ case MRB_O_RDWR:
+ flags |= O_RDWR;
+ break;
+ default:
+ mrb_raisef(mrb, E_ARGUMENT_ERROR, "illegal access mode %v", mode);
+ }
+
+ if (flags0 & MRB_O_APPEND) flags |= O_APPEND;
+ if (flags0 & MRB_O_CREAT) flags |= O_CREAT;
+ if (flags0 & MRB_O_EXCL) flags |= O_EXCL;
+ if (flags0 & MRB_O_TRUNC) flags |= O_TRUNC;
+#ifdef O_NONBLOCK
+ if (flags0 & MRB_O_NONBLOCK) flags |= O_NONBLOCK;
+#endif
+#ifdef O_NOCTTY
+ if (flags0 & MRB_O_NOCTTY) flags |= O_NOCTTY;
+#endif
#ifdef O_BINARY
- if (flags & FMODE_BINMODE) {
- modenum |= O_BINARY;
- }
+ if (flags0 & MRB_O_BINARY) flags |= O_BINARY;
+#endif
+#ifdef O_SHARE_DELETE
+ if (flags0 & MRB_O_SHARE_DELETE) flags |= O_SHARE_DELETE;
+#endif
+#ifdef O_SYNC
+ if (flags0 & MRB_O_SYNC) flags |= O_SYNC;
+#endif
+#ifdef O_DSYNC
+ if (flags0 & MRB_O_DSYNC) flags |= O_DSYNC;
+#endif
+#ifdef O_RSYNC
+ if (flags0 & MRB_O_RSYNC) flags |= O_RSYNC;
+#endif
+#ifdef O_NOFOLLOW
+ if (flags0 & MRB_O_NOFOLLOW) flags |= O_NOFOLLOW;
+#endif
+#ifdef O_NOATIME
+ if (flags0 & MRB_O_NOATIME) flags |= O_NOATIME;
+#endif
+#ifdef O_DIRECT
+ if (flags0 & MRB_O_DIRECT) flags |= O_DIRECT;
+#endif
+#ifdef O_TMPFILE
+ if (flags0 & MRB_O_TMPFILE) flags |= O_TMPFILE;
#endif
- return modenum;
+ return flags;
+ }
}
static void
@@ -294,7 +330,7 @@ option_to_fd(mrb_state *mrb, mrb_value hash, const char *key)
switch (mrb_type(opt)) {
case MRB_TT_DATA: /* IO */
- return (int)mrb_fixnum(mrb_io_fileno(mrb, opt));
+ return mrb_io_fileno(mrb, opt);
case MRB_TT_FIXNUM:
return (int)mrb_fixnum(opt);
default:
@@ -330,11 +366,11 @@ mrb_io_s_popen(mrb_state *mrb, mrb_value klass)
ofd[0] = INVALID_HANDLE_VALUE;
ofd[1] = INVALID_HANDLE_VALUE;
- mrb_get_args(mrb, "S|SH", &cmd, &mode, &opt);
+ mrb_get_args(mrb, "S|oH", &cmd, &mode, &opt);
io = mrb_obj_value(mrb_data_object_alloc(mrb, mrb_class_ptr(klass), NULL, &mrb_io_type));
pname = RSTRING_CSTR(mrb, cmd);
- flags = mrb_io_modestr_to_flags(mrb, RSTRING_CSTR(mrb, mode));
+ flags = mrb_io_mode_to_flags(mrb, mode);
doexec = (strcmp("-", pname) != 0);
opt_in = option_to_fd(mrb, opt, "in");
@@ -345,14 +381,14 @@ mrb_io_s_popen(mrb_state *mrb, mrb_value klass)
saAttr.bInheritHandle = TRUE;
saAttr.lpSecurityDescriptor = NULL;
- if (flags & FMODE_READABLE) {
+ if (OPEN_READABLE_P(flags)) {
if (!CreatePipe(&ofd[0], &ofd[1], &saAttr, 0)
|| !SetHandleInformation(ofd[0], HANDLE_FLAG_INHERIT, 0)) {
mrb_sys_fail(mrb, "pipe");
}
}
- if (flags & FMODE_WRITABLE) {
+ if (OPEN_WRITABLE_P(flags)) {
if (!CreatePipe(&ifd[0], &ifd[1], &saAttr, 0)
|| !SetHandleInformation(ifd[1], HANDLE_FLAG_INHERIT, 0)) {
mrb_sys_fail(mrb, "pipe");
@@ -366,11 +402,11 @@ mrb_io_s_popen(mrb_state *mrb, mrb_value klass)
si.dwFlags |= STARTF_USESHOWWINDOW;
si.wShowWindow = SW_HIDE;
si.dwFlags |= STARTF_USESTDHANDLES;
- if (flags & FMODE_READABLE) {
+ if (OPEN_READABLE_P(flags)) {
si.hStdOutput = ofd[1];
si.hStdError = ofd[1];
}
- if (flags & FMODE_WRITABLE) {
+ if (OPEN_WRITABLE_P(flags)) {
si.hStdInput = ifd[0];
}
if (!CreateProcess(
@@ -394,8 +430,8 @@ mrb_io_s_popen(mrb_state *mrb, mrb_value klass)
fptr->fd = _open_osfhandle((intptr_t)ofd[0], 0);
fptr->fd2 = _open_osfhandle((intptr_t)ifd[1], 0);
fptr->pid = pid;
- fptr->readable = ((flags & FMODE_READABLE) != 0);
- fptr->writable = ((flags & FMODE_WRITABLE) != 0);
+ fptr->readable = OPEN_READABLE_P(flags);
+ fptr->writable = OPEN_WRITABLE_P(flags);
fptr->sync = 0;
DATA_TYPE(io) = &mrb_io_type;
@@ -426,18 +462,18 @@ mrb_io_s_popen(mrb_state *mrb, mrb_value klass)
int saved_errno;
int opt_in, opt_out, opt_err;
- mrb_get_args(mrb, "S|SH", &cmd, &mode, &opt);
+ mrb_get_args(mrb, "S|oH", &cmd, &mode, &opt);
io = mrb_obj_value(mrb_data_object_alloc(mrb, mrb_class_ptr(klass), NULL, &mrb_io_type));
pname = RSTRING_CSTR(mrb, cmd);
- flags = mrb_io_modestr_to_flags(mrb, RSTRING_CSTR(mrb, mode));
+ flags = mrb_io_mode_to_flags(mrb, mode);
doexec = (strcmp("-", pname) != 0);
opt_in = option_to_fd(mrb, opt, "in");
opt_out = option_to_fd(mrb, opt, "out");
opt_err = option_to_fd(mrb, opt, "err");
- if (flags & FMODE_READABLE) {
+ if (OPEN_READABLE_P(flags)) {
if (pipe(pr) == -1) {
mrb_sys_fail(mrb, "pipe");
}
@@ -445,7 +481,7 @@ mrb_io_s_popen(mrb_state *mrb, mrb_value klass)
mrb_fd_cloexec(mrb, pr[1]);
}
- if (flags & FMODE_WRITABLE) {
+ if (OPEN_WRITABLE_P(flags)) {
if (pipe(pw) == -1) {
if (pr[0] != -1) close(pr[0]);
if (pr[1] != -1) close(pr[1]);
@@ -474,14 +510,14 @@ mrb_io_s_popen(mrb_state *mrb, mrb_value klass)
if (opt_err != -1) {
dup2(opt_err, 2);
}
- if (flags & FMODE_READABLE) {
+ if (OPEN_READABLE_P(flags)) {
close(pr[0]);
if (pr[1] != 1) {
dup2(pr[1], 1);
close(pr[1]);
}
}
- if (flags & FMODE_WRITABLE) {
+ if (OPEN_WRITABLE_P(flags)) {
close(pw[1]);
if (pw[0] != 0) {
dup2(pw[0], 0);
@@ -500,12 +536,12 @@ mrb_io_s_popen(mrb_state *mrb, mrb_value klass)
break;
default: /* parent */
- if ((flags & FMODE_READABLE) && (flags & FMODE_WRITABLE)) {
+ if (OPEN_RDWR_P(flags)) {
close(pr[1]);
fd = pr[0];
close(pw[0]);
write_fd = pw[1];
- } else if (flags & FMODE_READABLE) {
+ } else if (OPEN_RDONLY_P(flags)) {
close(pr[1]);
fd = pr[0];
} else {
@@ -519,8 +555,8 @@ mrb_io_s_popen(mrb_state *mrb, mrb_value klass)
fptr->fd = fd;
fptr->fd2 = write_fd;
fptr->pid = pid;
- fptr->readable = ((flags & FMODE_READABLE) != 0);
- fptr->writable = ((flags & FMODE_WRITABLE) != 0);
+ fptr->readable = OPEN_READABLE_P(flags);
+ fptr->writable = OPEN_WRITABLE_P(flags);
fptr->sync = 0;
DATA_TYPE(io) = &mrb_io_type;
@@ -530,11 +566,11 @@ mrb_io_s_popen(mrb_state *mrb, mrb_value klass)
case -1: /* error */
saved_errno = errno;
- if (flags & FMODE_READABLE) {
+ if (OPEN_READABLE_P(flags)) {
close(pr[0]);
close(pr[1]);
}
- if (flags & FMODE_WRITABLE) {
+ if (OPEN_WRITABLE_P(flags)) {
close(pw[0]);
close(pw[1]);
}
@@ -609,6 +645,43 @@ mrb_io_initialize_copy(mrb_state *mrb, mrb_value copy)
return copy;
}
+static void
+check_file_descriptor(mrb_state *mrb, mrb_int fd)
+{
+ struct stat sb;
+ int fdi = (int)fd;
+
+#if MRB_INT_MIN < INT_MIN || MRB_INT_MAX > INT_MAX
+ if (fdi != fd) {
+ goto badfd;
+ }
+#endif
+
+#ifdef _WIN32
+ {
+ DWORD err;
+ int len = sizeof(err);
+
+ if (getsockopt(fdi, SOL_SOCKET, SO_ERROR, (char*)&err, &len) == 0) {
+ return;
+ }
+ }
+
+ if (fdi < 0 || fdi > _getmaxstdio()) {
+ goto badfd;
+ }
+#endif /* _WIN32 */
+
+ if (fstat(fdi, &sb) != 0) {
+ goto badfd;
+ }
+
+ return;
+
+badfd:
+ mrb_sys_fail(mrb, "bad file descriptor");
+}
+
mrb_value
mrb_io_initialize(mrb_state *mrb, mrb_value io)
{
@@ -619,7 +692,8 @@ mrb_io_initialize(mrb_state *mrb, mrb_value io)
mode = opt = mrb_nil_value();
- mrb_get_args(mrb, "i|So", &fd, &mode, &opt);
+ mrb_get_args(mrb, "i|oo", &fd, &mode, &opt);
+ check_file_descriptor(mrb, fd);
if (mrb_nil_p(mode)) {
mode = mrb_str_new_cstr(mrb, "r");
}
@@ -627,7 +701,7 @@ mrb_io_initialize(mrb_state *mrb, mrb_value io)
opt = mrb_hash_new(mrb);
}
- flags = mrb_io_modestr_to_flags(mrb, RSTRING_CSTR(mrb, mode));
+ flags = mrb_io_mode_to_flags(mrb, mode);
mrb_iv_set(mrb, io, mrb_intern_cstr(mrb, "@buf"), mrb_str_new_cstr(mrb, ""));
@@ -642,8 +716,8 @@ mrb_io_initialize(mrb_state *mrb, mrb_value io)
DATA_PTR(io) = fptr;
fptr->fd = (int)fd;
- fptr->readable = ((flags & FMODE_READABLE) != 0);
- fptr->writable = ((flags & FMODE_WRITABLE) != 0);
+ fptr->readable = OPEN_READABLE_P(flags);
+ fptr->writable = OPEN_WRITABLE_P(flags);
fptr->sync = 0;
return io;
}
@@ -798,32 +872,48 @@ mrb_io_s_sysopen(mrb_state *mrb, mrb_value klass)
mrb_value mode = mrb_nil_value();
mrb_int fd, perm = -1;
const char *pat;
- int flags, modenum;
+ int flags;
- mrb_get_args(mrb, "S|Si", &path, &mode, &perm);
- if (mrb_nil_p(mode)) {
- mode = mrb_str_new_cstr(mrb, "r");
- }
+ mrb_get_args(mrb, "S|oi", &path, &mode, &perm);
if (perm < 0) {
perm = 0666;
}
pat = RSTRING_CSTR(mrb, path);
- flags = mrb_io_modestr_to_flags(mrb, RSTRING_CSTR(mrb, mode));
- modenum = mrb_io_flags_to_modenum(mrb, flags);
- fd = mrb_cloexec_open(mrb, pat, modenum, perm);
+ flags = mrb_io_mode_to_flags(mrb, mode);
+ fd = mrb_cloexec_open(mrb, pat, flags, perm);
return mrb_fixnum_value(fd);
}
+static mrb_value mrb_io_sysread_common(mrb_state *mrb,
+ mrb_io_read_write_size (*readfunc)(int, void *, fsize_t, off_t),
+ mrb_value io, mrb_value buf, mrb_int maxlen, off_t offset);
+
+static mrb_io_read_write_size
+mrb_sysread_dummy(int fd, void *buf, fsize_t nbytes, off_t offset)
+{
+ return (mrb_io_read_write_size)read(fd, buf, nbytes);
+}
+
mrb_value
mrb_io_sysread(mrb_state *mrb, mrb_value io)
{
- struct mrb_io *fptr;
mrb_value buf = mrb_nil_value();
mrb_int maxlen;
- int ret;
mrb_get_args(mrb, "i|S", &maxlen, &buf);
+
+ return mrb_io_sysread_common(mrb, mrb_sysread_dummy, io, buf, maxlen, 0);
+}
+
+static mrb_value
+mrb_io_sysread_common(mrb_state *mrb,
+ mrb_io_read_write_size (*readfunc)(int, void *, fsize_t, off_t),
+ mrb_value io, mrb_value buf, mrb_int maxlen, off_t offset)
+{
+ struct mrb_io *fptr;
+ int ret;
+
if (maxlen < 0) {
mrb_raise(mrb, E_ARGUMENT_ERROR, "negative expanding string size");
}
@@ -837,7 +927,8 @@ mrb_io_sysread(mrb_state *mrb, mrb_value io)
if (RSTRING_LEN(buf) != maxlen) {
buf = mrb_str_resize(mrb, buf, maxlen);
- } else {
+ }
+ else {
mrb_str_modify(mrb, RSTRING(buf));
}
@@ -845,25 +936,16 @@ mrb_io_sysread(mrb_state *mrb, mrb_value io)
if (!fptr->readable) {
mrb_raise(mrb, E_IO_ERROR, "not opened for reading");
}
- ret = read(fptr->fd, RSTRING_PTR(buf), (fsize_t)maxlen);
- switch (ret) {
- case 0: /* EOF */
- if (maxlen == 0) {
- buf = mrb_str_new_cstr(mrb, "");
- } else {
- mrb_raise(mrb, E_EOF_ERROR, "sysread failed: End of File");
- }
- break;
- case -1: /* Error */
- mrb_sys_fail(mrb, "sysread failed");
- break;
- default:
- if (RSTRING_LEN(buf) != ret) {
- buf = mrb_str_resize(mrb, buf, ret);
- }
- break;
+ ret = readfunc(fptr->fd, RSTRING_PTR(buf), (fsize_t)maxlen, offset);
+ if (ret < 0) {
+ mrb_sys_fail(mrb, "sysread failed");
+ }
+ if (RSTRING_LEN(buf) != ret) {
+ buf = mrb_str_resize(mrb, buf, ret);
+ }
+ if (ret == 0 && maxlen > 0) {
+ mrb_raise(mrb, E_EOF_ERROR, "sysread failed: End of File");
}
-
return buf;
}
@@ -895,11 +977,12 @@ mrb_io_sysseek(mrb_state *mrb, mrb_value io)
}
}
-mrb_value
-mrb_io_syswrite(mrb_state *mrb, mrb_value io)
+static mrb_value
+mrb_io_syswrite_common(mrb_state *mrb,
+ mrb_io_read_write_size (*writefunc)(int, const void *, fsize_t, off_t),
+ mrb_value io, mrb_value buf, off_t offset)
{
struct mrb_io *fptr;
- mrb_value str, buf;
int fd, length;
fptr = io_get_open_fptr(mrb, io);
@@ -907,15 +990,12 @@ mrb_io_syswrite(mrb_state *mrb, mrb_value io)
mrb_raise(mrb, E_IO_ERROR, "not opened for writing");
}
- mrb_get_args(mrb, "S", &str);
- buf = str;
-
if (fptr->fd2 == -1) {
fd = fptr->fd;
} else {
fd = fptr->fd2;
}
- length = write(fd, RSTRING_PTR(buf), (fsize_t)RSTRING_LEN(buf));
+ length = writefunc(fd, RSTRING_PTR(buf), (fsize_t)RSTRING_LEN(buf), offset);
if (length == -1) {
mrb_sys_fail(mrb, 0);
}
@@ -923,6 +1003,22 @@ mrb_io_syswrite(mrb_state *mrb, mrb_value io)
return mrb_fixnum_value(length);
}
+static mrb_io_read_write_size
+mrb_syswrite_dummy(int fd, const void *buf, fsize_t nbytes, off_t offset)
+{
+ return (mrb_io_read_write_size)write(fd, buf, nbytes);
+}
+
+mrb_value
+mrb_io_syswrite(mrb_state *mrb, mrb_value io)
+{
+ mrb_value buf;
+
+ mrb_get_args(mrb, "S", &buf);
+
+ return mrb_io_syswrite_common(mrb, mrb_syswrite_dummy, io, buf, 0);
+}
+
mrb_value
mrb_io_close(mrb_state *mrb, mrb_value self)
{
@@ -1060,7 +1156,7 @@ mrb_io_s_select(mrb_state *mrb, mrb_value klass)
mrb_get_args(mrb, "*", &argv, &argc);
if (argc < 1 || argc > 4) {
- mrb_raisef(mrb, E_ARGUMENT_ERROR, "wrong number of arguments (%i for 1..4)", argc);
+ mrb_argnum_error(mrb, argc, 1, 4);
}
timeout = mrb_nil_value();
@@ -1089,6 +1185,7 @@ mrb_io_s_select(mrb_state *mrb, mrb_value klass)
for (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);
if (mrb_io_read_data_pending(mrb, read_io)) {
pending++;
@@ -1111,6 +1208,7 @@ mrb_io_s_select(mrb_state *mrb, mrb_value klass)
FD_ZERO(wp);
for (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);
if (max < fptr->fd)
max = fptr->fd;
@@ -1130,6 +1228,7 @@ mrb_io_s_select(mrb_state *mrb, mrb_value klass)
FD_ZERO(ep);
for (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);
if (max < fptr->fd)
max = fptr->fd;
@@ -1203,12 +1302,19 @@ retry:
return result;
}
-mrb_value
+int
mrb_io_fileno(mrb_state *mrb, mrb_value io)
{
struct mrb_io *fptr;
fptr = io_get_open_fptr(mrb, io);
- return mrb_fixnum_value(fptr->fd);
+ return fptr->fd;
+}
+
+static mrb_value
+mrb_io_fileno_m(mrb_state *mrb, mrb_value io)
+{
+ int fd = mrb_io_fileno(mrb, io);
+ return mrb_fixnum_value(fd);
}
mrb_value
@@ -1291,15 +1397,55 @@ mrb_io_sync(mrb_state *mrb, mrb_value self)
return mrb_bool_value(fptr->sync);
}
+#ifndef MRB_WITH_IO_PREAD_PWRITE
+# define mrb_io_pread mrb_notimplement_m
+# define mrb_io_pwrite mrb_notimplement_m
+#else
+static off_t
+value2off(mrb_state *mrb, mrb_value offv)
+{
+ return (off_t)mrb_int(mrb, offv);
+}
+
+/*
+ * call-seq:
+ * pread(maxlen, offset, outbuf = "") -> outbuf
+ */
+static mrb_value
+mrb_io_pread(mrb_state *mrb, mrb_value io)
+{
+ mrb_value buf = mrb_nil_value();
+ mrb_value off;
+ mrb_int maxlen;
+
+ mrb_get_args(mrb, "io|S!", &maxlen, &off, &buf);
+
+ return mrb_io_sysread_common(mrb, pread, io, buf, maxlen, value2off(mrb, off));
+}
+
+/*
+ * call-seq:
+ * pwrite(buffer, offset) -> wrote_bytes
+ */
+static mrb_value
+mrb_io_pwrite(mrb_state *mrb, mrb_value io)
+{
+ mrb_value buf, off;
+
+ mrb_get_args(mrb, "So", &buf, &off);
+
+ return mrb_io_syswrite_common(mrb, pwrite, io, buf, value2off(mrb, off));
+}
+#endif /* MRB_WITH_IO_PREAD_PWRITE */
+
static mrb_value
-io_bufread(mrb_state *mrb, mrb_value self)
+io_bufread(mrb_state *mrb, mrb_value str, mrb_int len)
{
- mrb_value str, str2;
- mrb_int len, newlen;
+ mrb_value str2;
+ mrb_int newlen;
struct RString *s;
char *p;
- mrb_get_args(mrb, "Si", &str, &len);
s = RSTRING(str);
mrb_str_modify(mrb, s);
p = RSTR_PTR(s);
@@ -1312,6 +1458,54 @@ io_bufread(mrb_state *mrb, mrb_value self)
return str2;
}
+static mrb_value
+mrb_io_bufread(mrb_state *mrb, mrb_value self)
+{
+ mrb_value str;
+ mrb_int len;
+
+ mrb_get_args(mrb, "Si", &str, &len);
+ return io_bufread(mrb, str, len);
+}
+
+static mrb_value
+mrb_io_readchar(mrb_state *mrb, mrb_value self)
+{
+ mrb_value buf;
+ mrb_int len = 1;
+#ifdef MRB_UTF8_STRING
+ unsigned char c;
+#endif
+
+ mrb_get_args(mrb, "S", &buf);
+ mrb_assert(RSTRING_LEN(buf) > 0);
+ mrb_assert(RSTRING_PTR(buf) != NULL);
+ mrb_str_modify(mrb, RSTRING(buf));
+#ifdef MRB_UTF8_STRING
+ c = RSTRING_PTR(buf)[0];
+ if (c & 0x80) {
+ len = mrb_utf8len(RSTRING_PTR(buf), RSTRING_END(buf));
+ if (len == 1 && RSTRING_LEN(buf) < 4) { /* partial UTF-8 */
+ mrb_int blen = RSTRING_LEN(buf);
+ ssize_t n;
+
+ struct mrb_io *fptr = (struct mrb_io*)io_get_open_fptr(mrb, self);
+
+ if (!fptr->readable) {
+ mrb_raise(mrb, E_IO_ERROR, "not opened for reading");
+ }
+ /* refill the buffer */
+ mrb_str_resize(mrb, buf, 4096);
+ n = read(fptr->fd, RSTRING_PTR(buf)+blen, 4096-blen);
+ if (n < 0) mrb_sys_fail(mrb, "sysread failed");
+ mrb_str_resize(mrb, buf, blen+n);
+ }
+ len = mrb_utf8len(RSTRING_PTR(buf), RSTRING_END(buf));
+ }
+#endif
+ return io_bufread(mrb, buf, len);
+}
+
void
mrb_init_io(mrb_state *mrb)
{
@@ -1321,23 +1515,23 @@ mrb_init_io(mrb_state *mrb)
MRB_SET_INSTANCE_TT(io, MRB_TT_DATA);
mrb_include_module(mrb, io, mrb_module_get(mrb, "Enumerable")); /* 15.2.20.3 */
- mrb_define_class_method(mrb, io, "_popen", mrb_io_s_popen, MRB_ARGS_ANY());
+ mrb_define_class_method(mrb, io, "_popen", mrb_io_s_popen, MRB_ARGS_ARG(1,2));
mrb_define_class_method(mrb, io, "_sysclose", mrb_io_s_sysclose, MRB_ARGS_REQ(1));
- mrb_define_class_method(mrb, io, "for_fd", mrb_io_s_for_fd, MRB_ARGS_ANY());
- mrb_define_class_method(mrb, io, "select", mrb_io_s_select, MRB_ARGS_ANY());
- mrb_define_class_method(mrb, io, "sysopen", mrb_io_s_sysopen, MRB_ARGS_ANY());
+ mrb_define_class_method(mrb, io, "for_fd", mrb_io_s_for_fd, MRB_ARGS_ARG(1,2));
+ mrb_define_class_method(mrb, io, "select", mrb_io_s_select, MRB_ARGS_ARG(1,3));
+ mrb_define_class_method(mrb, io, "sysopen", mrb_io_s_sysopen, MRB_ARGS_ARG(1,2));
#if !defined(_WIN32) && !(defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE)
mrb_define_class_method(mrb, io, "_pipe", mrb_io_s_pipe, MRB_ARGS_NONE());
#endif
- mrb_define_method(mrb, io, "initialize", mrb_io_initialize, MRB_ARGS_ANY()); /* 15.2.20.5.21 (x)*/
+ mrb_define_method(mrb, io, "initialize", mrb_io_initialize, MRB_ARGS_ARG(1,2)); /* 15.2.20.5.21 (x)*/
mrb_define_method(mrb, io, "initialize_copy", mrb_io_initialize_copy, MRB_ARGS_REQ(1));
mrb_define_method(mrb, io, "_check_readable", mrb_io_check_readable, MRB_ARGS_NONE());
mrb_define_method(mrb, io, "isatty", mrb_io_isatty, MRB_ARGS_NONE());
mrb_define_method(mrb, io, "sync", mrb_io_sync, MRB_ARGS_NONE());
mrb_define_method(mrb, io, "sync=", mrb_io_set_sync, MRB_ARGS_REQ(1));
- mrb_define_method(mrb, io, "sysread", mrb_io_sysread, MRB_ARGS_ANY());
- mrb_define_method(mrb, io, "sysseek", mrb_io_sysseek, MRB_ARGS_REQ(1));
+ mrb_define_method(mrb, io, "sysread", mrb_io_sysread, MRB_ARGS_ARG(1,1));
+ mrb_define_method(mrb, io, "sysseek", mrb_io_sysseek, MRB_ARGS_ARG(1,1));
mrb_define_method(mrb, io, "syswrite", mrb_io_syswrite, MRB_ARGS_REQ(1));
mrb_define_method(mrb, io, "close", mrb_io_close, MRB_ARGS_NONE()); /* 15.2.20.5.1 */
mrb_define_method(mrb, io, "close_write", mrb_io_close_write, MRB_ARGS_NONE());
@@ -1345,7 +1539,10 @@ mrb_init_io(mrb_state *mrb)
mrb_define_method(mrb, io, "close_on_exec?", mrb_io_close_on_exec_p, MRB_ARGS_NONE());
mrb_define_method(mrb, io, "closed?", mrb_io_closed, MRB_ARGS_NONE()); /* 15.2.20.5.2 */
mrb_define_method(mrb, io, "pid", mrb_io_pid, MRB_ARGS_NONE()); /* 15.2.20.5.2 */
- mrb_define_method(mrb, io, "fileno", mrb_io_fileno, MRB_ARGS_NONE());
+ mrb_define_method(mrb, io, "fileno", mrb_io_fileno_m, MRB_ARGS_NONE());
+ mrb_define_method(mrb, io, "pread", mrb_io_pread, MRB_ARGS_ANY()); /* ruby 2.5 feature */
+ mrb_define_method(mrb, io, "pwrite", mrb_io_pwrite, MRB_ARGS_ANY()); /* ruby 2.5 feature */
- mrb_define_class_method(mrb, io, "_bufread", io_bufread, MRB_ARGS_REQ(2));
+ mrb_define_method(mrb, io, "_readchar", mrb_io_readchar, MRB_ARGS_REQ(1));
+ mrb_define_class_method(mrb, io, "_bufread", mrb_io_bufread, MRB_ARGS_REQ(2));
}
diff --git a/mrbgems/mruby-io/test/file.rb b/mrbgems/mruby-io/test/file.rb
index 143096759..ef4d7fcb1 100644
--- a/mrbgems/mruby-io/test/file.rb
+++ b/mrbgems/mruby-io/test/file.rb
@@ -80,6 +80,22 @@ assert('File#mtime') do
end
end
+assert('File#size and File#truncate') do
+ fname = "#{$mrbtest_io_wfname}.resize"
+ begin
+ File.open(fname, 'w') do |f|
+ assert_equal 0, f.size
+ assert_equal 0, f.truncate(100)
+ assert_equal 100, f.size
+ assert_equal 0, f.pos
+ assert_equal 0, f.truncate(5)
+ assert_equal 5, f.size
+ end
+ ensure
+ File.delete(fname)
+ end
+end
+
assert('File.join') do
assert_equal "", File.join()
assert_equal "a", File.join("a")
@@ -94,18 +110,22 @@ assert('File.join') do
end
assert('File.realpath') do
- if File::ALT_SEPARATOR
- readme_path = File._getwd + File::ALT_SEPARATOR + "README.md"
- assert_equal readme_path, File.realpath("README.md")
- else
- dir = MRubyIOTestUtil.mkdtemp("mruby-io-test.XXXXXX")
- begin
- dir1 = File.realpath($mrbtest_io_rfname)
- dir2 = File.realpath("./#{dir}//./../#{$mrbtest_io_symlinkname}")
- assert_equal dir1, dir2
- ensure
- MRubyIOTestUtil.rmdir dir
+ dir = MRubyIOTestUtil.mkdtemp("mruby-io-test.XXXXXX")
+ begin
+ sep = File::ALT_SEPARATOR || File::SEPARATOR
+ relative_path = "#{File.basename(dir)}#{sep}realpath_test"
+ path = "#{File._getwd}#{sep}#{relative_path}"
+ File.open(path, "w"){}
+ assert_equal path, File.realpath(relative_path)
+
+ unless MRubyIOTestUtil.win?
+ path1 = File.realpath($mrbtest_io_rfname)
+ path2 = File.realpath($mrbtest_io_symlinkname)
+ assert_equal path1, path2
end
+ ensure
+ File.delete path rescue nil
+ MRubyIOTestUtil.rmdir dir
end
assert_raise(ArgumentError) { File.realpath("TO\0DO") }
@@ -113,7 +133,9 @@ end
assert("File.readlink") do
begin
- assert_equal $mrbtest_io_rfname, File.readlink($mrbtest_io_symlinkname)
+ exp = File.basename($mrbtest_io_rfname)
+ act = File.readlink($mrbtest_io_symlinkname)
+ assert_equal exp, act
rescue NotImplementedError => e
skip e.message
end
diff --git a/mrbgems/mruby-io/test/io.rb b/mrbgems/mruby-io/test/io.rb
index e3024cf9a..2088a61e3 100644
--- a/mrbgems/mruby-io/test/io.rb
+++ b/mrbgems/mruby-io/test/io.rb
@@ -24,6 +24,10 @@ def assert_io_open(meth)
end
end
io2.close unless meth == :open
+
+ assert_raise(RuntimeError) { IO.__send__(meth, 1023) } # For Windows
+ assert_raise(RuntimeError) { IO.__send__(meth, 1 << 26) }
+ assert_raise(RuntimeError) { IO.__send__(meth, 1 << 32) } if (1 << 32).kind_of?(Integer)
end
end
@@ -564,6 +568,34 @@ assert('IO#sysseek') do
end
end
+assert('IO#pread') do
+ skip "IO#pread is not implemented on this configuration" unless MRubyIOTestUtil::MRB_WITH_IO_PREAD_PWRITE
+
+ IO.open(IO.sysopen($mrbtest_io_rfname, 'r'), 'r') do |io|
+ assert_equal $mrbtest_io_msg.byteslice(5, 8), io.pread(8, 5)
+ assert_equal 0, io.pos
+ assert_equal $mrbtest_io_msg.byteslice(1, 5), io.pread(5, 1)
+ assert_equal 0, io.pos
+ assert_raise(RuntimeError) { io.pread(20, -9) }
+ end
+end
+
+assert('IO#pwrite') do
+ skip "IO#pwrite is not implemented on this configuration" unless MRubyIOTestUtil::MRB_WITH_IO_PREAD_PWRITE
+
+ IO.open(IO.sysopen($mrbtest_io_wfname, 'w+'), 'w+') do |io|
+ assert_equal 6, io.pwrite("Warld!", 7)
+ assert_equal 0, io.pos
+ assert_equal 7, io.pwrite("Hello, ", 0)
+ assert_equal 0, io.pos
+ assert_equal "Hello, Warld!", io.read
+ assert_equal 6, io.pwrite("world!", 7)
+ assert_equal 13, io.pos
+ io.pos = 0
+ assert_equal "Hello, world!", io.read
+ end
+end
+
assert('IO.pipe') do
begin
called = false
diff --git a/mrbgems/mruby-io/test/mruby_io_test.c b/mrbgems/mruby-io/test/mruby_io_test.c
index eb552c41a..d4c8eb13c 100644
--- a/mrbgems/mruby-io/test/mruby_io_test.c
+++ b/mrbgems/mruby-io/test/mruby_io_test.c
@@ -1,5 +1,7 @@
+#include <mruby/common.h>
#include <sys/types.h>
#include <errno.h>
+#include <string.h>
#if defined(_WIN32) || defined(_WIN64)
@@ -7,7 +9,6 @@
#include <io.h>
#include <fcntl.h>
#include <direct.h>
-#include <string.h>
#include <stdlib.h>
#include <malloc.h>
@@ -18,7 +19,9 @@ typedef int mode_t;
#define open _open
#define close _close
-#ifdef _MSC_VER
+#if defined(_MSC_VER) || \
+ (defined(MRB_MINGW32_VERSION) && MRB_MINGW32_VERSION < 3021) || \
+ (defined(MRB_MINGW64_VERSION) && MRB_MINGW64_VERSION < 4000)
#include <sys/stat.h>
static int
@@ -50,10 +53,11 @@ mkdtemp(char *temp)
#include <sys/socket.h>
#include <unistd.h>
#include <sys/un.h>
+ #include <fcntl.h>
+ #include <libgen.h>
#endif
#include <sys/stat.h>
-#include <stdio.h>
#include <stdlib.h>
#include "mruby.h"
@@ -61,51 +65,55 @@ mkdtemp(char *temp)
#include "mruby/error.h"
#include "mruby/string.h"
#include "mruby/variable.h"
+#include <mruby/ext/io.h>
static mrb_value
mrb_io_test_io_setup(mrb_state *mrb, mrb_value self)
{
- char rfname[] = "tmp.mruby-io-test-r.XXXXXXXX";
- char wfname[] = "tmp.mruby-io-test-w.XXXXXXXX";
- char symlinkname[] = "tmp.mruby-io-test-l.XXXXXXXX";
- char socketname[] = "tmp.mruby-io-test-s.XXXXXXXX";
+#define GVNAME(n) "$mrbtest_io_" #n "name"
+ enum {IDX_READ, IDX_WRITE, IDX_LINK, IDX_SOCKET, IDX_COUNT};
+ const char *gvnames[] = {GVNAME(rf), GVNAME(wf), GVNAME(symlink), GVNAME(socket)};
+ char *fnames[IDX_COUNT];
+ int fds[IDX_COUNT];
char msg[] = "mruby io test\n";
mode_t mask;
- int fd0, fd1;
FILE *fp;
-
+ int i;
#if !defined(_WIN32) && !defined(_WIN64)
- int fd2, fd3;
struct sockaddr_un sun0;
#endif
- mask = umask(077);
- fd0 = mkstemp(rfname);
- fd1 = mkstemp(wfname);
- if (fd0 == -1 || fd1 == -1) {
- mrb_raise(mrb, E_RUNTIME_ERROR, "can't create temporary file");
- return mrb_nil_value();
- }
- close(fd0);
- close(fd1);
+ mrb_gv_set(mrb, mrb_intern_cstr(mrb, "$mrbtest_io_msg"), mrb_str_new_cstr(mrb, msg));
+ mask = umask(077);
+ for (i = 0; i < IDX_COUNT; i++) {
+ mrb_value fname = mrb_str_new_capa(mrb, 0);
#if !defined(_WIN32) && !defined(_WIN64)
- fd2 = mkstemp(symlinkname);
- fd3 = mkstemp(socketname);
- if (fd2 == -1 || fd3 == -1) {
- mrb_raise(mrb, E_RUNTIME_ERROR, "can't create temporary file");
- return mrb_nil_value();
- }
+ /*
+ * Workaround for not being able to bind a socket to some file systems
+ * (e.g. vboxsf, NFS). [#4981]
+ */
+ char *tmpdir = getenv("TMPDIR");
+ if (tmpdir && strlen(tmpdir) > 0) {
+ mrb_str_cat_cstr(mrb, fname, tmpdir);
+ if (*(RSTRING_END(fname)-1) != '/') mrb_str_cat_lit(mrb, fname, "/");
+ } else {
+ mrb_str_cat_lit(mrb, fname, "/tmp/");
+ }
#endif
+ mrb_str_cat_cstr(mrb, fname, gvnames[i]+1);
+ mrb_str_cat_cstr(mrb, fname, ".XXXXXXXX");
+ fnames[i] = RSTRING_PTR(fname);
+ fds[i] = mkstemp(fnames[i]);
+ if (fds[i] == -1) {
+ mrb_raise(mrb, E_RUNTIME_ERROR, "can't create temporary file");
+ }
+ close(fds[i]);
+ mrb_gv_set(mrb, mrb_intern_cstr(mrb, gvnames[i]), fname);
+ }
umask(mask);
- mrb_gv_set(mrb, mrb_intern_cstr(mrb, "$mrbtest_io_rfname"), mrb_str_new_cstr(mrb, rfname));
- mrb_gv_set(mrb, mrb_intern_cstr(mrb, "$mrbtest_io_wfname"), mrb_str_new_cstr(mrb, wfname));
- mrb_gv_set(mrb, mrb_intern_cstr(mrb, "$mrbtest_io_symlinkname"), mrb_str_new_cstr(mrb, symlinkname));
- mrb_gv_set(mrb, mrb_intern_cstr(mrb, "$mrbtest_io_socketname"), mrb_str_new_cstr(mrb, socketname));
- mrb_gv_set(mrb, mrb_intern_cstr(mrb, "$mrbtest_io_msg"), mrb_str_new_cstr(mrb, msg));
-
- fp = fopen(rfname, "wb");
+ fp = fopen(fnames[IDX_READ], "wb");
if (fp == NULL) {
mrb_raise(mrb, E_RUNTIME_ERROR, "can't open temporary file");
return mrb_nil_value();
@@ -113,7 +121,7 @@ mrb_io_test_io_setup(mrb_state *mrb, mrb_value self)
fputs(msg, fp);
fclose(fp);
- fp = fopen(wfname, "wb");
+ fp = fopen(fnames[IDX_WRITE], "wb");
if (fp == NULL) {
mrb_raise(mrb, E_RUNTIME_ERROR, "can't open temporary file");
return mrb_nil_value();
@@ -121,29 +129,29 @@ mrb_io_test_io_setup(mrb_state *mrb, mrb_value self)
fclose(fp);
#if !defined(_WIN32) && !defined(_WIN64)
- unlink(symlinkname);
- close(fd2);
- if (symlink(rfname, symlinkname) == -1) {
+ unlink(fnames[IDX_LINK]);
+ if (symlink(basename(fnames[IDX_READ]), fnames[IDX_LINK]) == -1) {
mrb_raise(mrb, E_RUNTIME_ERROR, "can't make a symbolic link");
}
- unlink(socketname);
- close(fd3);
- fd3 = socket(AF_UNIX, SOCK_STREAM, 0);
- if (fd3 == -1) {
+ unlink(fnames[IDX_SOCKET]);
+ fds[IDX_SOCKET] = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (fds[IDX_SOCKET] == -1) {
mrb_raise(mrb, E_RUNTIME_ERROR, "can't make a socket");
}
sun0.sun_family = AF_UNIX;
- snprintf(sun0.sun_path, sizeof(sun0.sun_path), "%s", socketname);
- if (bind(fd3, (struct sockaddr *)&sun0, sizeof(sun0)) == -1) {
+ strncpy(sun0.sun_path, fnames[IDX_SOCKET], sizeof(sun0.sun_path)-1);
+ sun0.sun_path[sizeof(sun0.sun_path)-1] = 0;
+ if (bind(fds[IDX_SOCKET], (struct sockaddr *)&sun0, sizeof(sun0)) == -1) {
mrb_raisef(mrb, E_RUNTIME_ERROR, "can't bind AF_UNIX socket to %s: %d",
sun0.sun_path,
errno);
}
- close(fd3);
+ close(fds[IDX_SOCKET]);
#endif
return mrb_true_value();
+#undef GVNAME
}
static mrb_value
@@ -177,28 +185,6 @@ mrb_io_test_io_cleanup(mrb_state *mrb, mrb_value self)
}
static mrb_value
-mrb_io_test_file_setup(mrb_state *mrb, mrb_value self)
-{
- mrb_value ary = mrb_io_test_io_setup(mrb, self);
-#if !defined(_WIN32) && !defined(_WIN64)
- if (symlink("/usr/bin", "test-bin") == -1) {
- mrb_raise(mrb, E_RUNTIME_ERROR, "can't make a symbolic link");
- }
-#endif
-
- return ary;
-}
-
-static mrb_value
-mrb_io_test_file_cleanup(mrb_state *mrb, mrb_value self)
-{
- mrb_io_test_io_cleanup(mrb, self);
- remove("test-bin");
-
- return mrb_nil_value();
-}
-
-static mrb_value
mrb_io_test_mkdtemp(mrb_state *mrb, mrb_value klass)
{
mrb_value str;
@@ -238,6 +224,12 @@ mrb_io_win_p(mrb_state *mrb, mrb_value klass)
#endif
}
+#ifdef MRB_WITH_IO_PREAD_PWRITE
+# define MRB_WITH_IO_PREAD_PWRITE_ENABLED TRUE
+#else
+# define MRB_WITH_IO_PREAD_PWRITE_ENABLED FALSE
+#endif
+
void
mrb_mruby_io_gem_test(mrb_state* mrb)
{
@@ -245,10 +237,9 @@ mrb_mruby_io_gem_test(mrb_state* mrb)
mrb_define_class_method(mrb, io_test, "io_test_setup", mrb_io_test_io_setup, MRB_ARGS_NONE());
mrb_define_class_method(mrb, io_test, "io_test_cleanup", mrb_io_test_io_cleanup, MRB_ARGS_NONE());
- mrb_define_class_method(mrb, io_test, "file_test_setup", mrb_io_test_file_setup, MRB_ARGS_NONE());
- mrb_define_class_method(mrb, io_test, "file_test_cleanup", mrb_io_test_file_cleanup, MRB_ARGS_NONE());
-
mrb_define_class_method(mrb, io_test, "mkdtemp", mrb_io_test_mkdtemp, MRB_ARGS_REQ(1));
mrb_define_class_method(mrb, io_test, "rmdir", mrb_io_test_rmdir, MRB_ARGS_REQ(1));
mrb_define_class_method(mrb, io_test, "win?", mrb_io_win_p, MRB_ARGS_NONE());
+
+ mrb_define_const(mrb, io_test, "MRB_WITH_IO_PREAD_PWRITE", mrb_bool_value(MRB_WITH_IO_PREAD_PWRITE_ENABLED));
}