Commit e8ca5e51 authored by Akira Yumiyama's avatar Akira Yumiyama

initial commit

parents
mruby-io
========
under construction
/*
** io.h - IO class
*/
#ifndef MRUBY_IO_H
#define MRUBY_IO_H
#if defined(__cplusplus)
extern "C" {
#endif
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <stdio.h>
#include <string.h>
#include <limits.h>
struct mrb_io {
int fd; /* file descriptor */
int fd2; /* file descriptor */
int pid; /* child's pid (for pipes) */
};
struct mrb_io_type {
const char *struct_name;
void (*dfree)(mrb_state *mrb, void *);
};
#define FMODE_READABLE 0x00000001
#define FMODE_WRITABLE 0x00000002
#define FMODE_READWRITE (FMODE_READABLE|FMODE_WRITABLE)
#define FMODE_BINMODE 0x00000004
#define FMODE_SYNC 0x00000008
#define FMODE_TTY 0x00000010
#define FMODE_DUPLEX 0x00000020
#define FMODE_APPEND 0x00000040
#define FMODE_CREATE 0x00000080
#define FMODE_WSPLIT 0x00000200
#define FMODE_WSPLIT_INITIALIZED 0x00000400
#define FMODE_TRUNC 0x00000800
#define FMODE_TEXTMODE 0x00001000
#define FMODE_SETENC_BY_BOM 0x00100000
#define E_IO_ERROR (mrb_class_obj_get(mrb, "IOError"))
#define E_EOF_ERROR (mrb_class_obj_get(mrb, "EOFError"))
mrb_value mrb_open_file(mrb_state *mrb, int argc, mrb_value *argv, mrb_value io);
void fptr_finalize(mrb_state *mrb, struct mrb_io *fptr, int noraise);
mrb_value mrb_file_exist(mrb_state *mrb, mrb_value fname);
#if defined(__cplusplus)
} /* extern "C" { */
#endif
#endif /* MRUBY_IO_H */
MRuby::Gem::Specification.new('mruby-io') do |spec|
spec.license = 'MIT'
spec.authors = 'Internet Initiative Japan'
spec.cc.include_paths << "#{build.root}/src"
end
class File < IO
include Enumerable
class FileError < Exception; end
class NoFileError < FileError; end
class UnableToStat < FileError; end
class PermissionError < FileError; end
attr_accessor :path
def initialize(fd_or_path, mode = "r", perm = 0666)
self._bless
if fd_or_path.kind_of? Fixnum
super(fd_or_path, mode)
else
@path = fd_or_path
perm = 0666 unless perm.is_a? Fixnum
fd = IO.sysopen(@path, mode, perm)
if fd < 0 && Object.const_defined?(:Errno)
begin
Errno.handle @path
rescue Errno::EMFILE
GC.run(true)
fd = IO.sysopen(@path, mode, perm)
Errno.handle if fd < 0
end
elsif fd < 0
raise NoFileError.new "no such file or directory"
end
super(fd, mode)
end
end
def self.join(*names)
if names.size == 0
""
elsif names.size == 1
names[0]
else
if names[0][-1] == File::SEPARATOR
s = names[0][0..-2]
else
s = names[0].dup
end
(1..names.size-2).each { |i|
t = names[i]
if t[0] == File::SEPARATOR and t[-1] == File::SEPARATOR
t = t[1..-2]
elsif t[0] == File::SEPARATOR
t = t[1..-1]
elsif t[-1] == File::SEPARATOR
t = t[0..-2]
end
s += File::SEPARATOR + t if t != ""
}
if names[-1][0] == File::SEPARATOR
s += File::SEPARATOR + names[-1][1..-1]
else
s += File::SEPARATOR + names[-1]
end
s
end
end
def self.directory?(file)
FileTest.directory?(file)
end
def self.exist?(file)
FileTest.exist?(file)
end
def self.exists?(file)
FileTest.exists?(file)
end
def self.file?(file)
FileTest.file?(file)
end
def self.pipe?(file)
FileTest.pipe?(file)
end
def self.size?(file)
FileTest.size?(file)
end
def self.socket?(file)
FileTest.socket?(file)
end
def self.symlink?(file)
FileTest.symlink?(file)
end
def self.zero?(file)
FileTest.zero?(file)
end
def self.extname(filename)
fname = self.basename(filename)
return '' if fname[0] == '.' || fname.index('.').nil?
ext = fname.split('.').last
ext.empty? ? '' : ".#{ext}"
end
end
class File
module Constants
NULL = "/dev/null"
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
FNM_DOTMATCH = 4
FNM_CASEFOLD = 8
end
end
class File
include File::Constants
end
##
# IO
class IOError < StandardError; end
class EOFError < IOError; end
class IO
SEEK_SET = 0
SEEK_CUR = 1
SEEK_END = 2
BUF_SIZE = 4096
def self.open(fd, mode = "r", opt = {}, &block)
io = self.new(fd, mode, opt)
return io unless block
begin
yield io
ensure
begin
io.close unless io.closed?
rescue StandardError
end
end
end
def self.popen(command, mode = 'r', &block)
io = self._popen(command, mode)
return io unless block
begin
yield io
ensure
begin
io.close unless io.closed?
rescue IOError
# nothing
end
end
end
def write(string)
str = string.is_a?(String) ? string : string.to_s
return str.size unless str.size > 0
len = syswrite(str)
if str.size == len
@pos += len
return len
end
raise IOError
end
def eof?
# XXX: @buf のことを考えなくてよい??
ret = false
char = ''
begin
char = sysread(1)
rescue EOFError => e
ret = true
ensure
_ungets(char)
end
ret
end
alias_method :eof, :eof?
def pos
raise IOError if closed?
@pos
end
alias_method :tell, :pos
def pos=(i)
seek(i, SEEK_SET)
end
def seek(i, whence = SEEK_SET)
raise IOError if closed?
@pos = sysseek(i, whence)
@buf = ''
0
end
def _read_buf
return @buf if @buf && @buf.size > 0
@buf = sysread(BUF_SIZE)
end
def _ungets(substr)
raise TypeError.new "expect String, got #{substr.class}" unless substr.is_a?(String)
raise IOError if @pos == 0 || @pos.nil?
@pos -= substr.size
if @buf.empty?
@buf = substr
else
@buf = substr + @buf
end
nil
end
def ungetc(char)
raise IOError if @pos == 0 || @pos.nil?
_ungets(char)
nil
end
def read(length = nil)
unless length.nil? or length.class == Fixnum
raise TypeError.new "can't convert #{length.class} into Integer"
end
if length && length < 0
raise ArgumentError.new "negative length: #{length} given"
end
str = ''
while 1
begin
_read_buf
rescue EOFError => e
str = nil if str.empty?
break
end
if length && (str.size + @buf.size) >= length
len = length - str.size
str += @buf[0, len]
@pos += len
@buf = @buf[len, @buf.size - len]
break
else
str += @buf
@pos += @buf.size
@buf = ''
end
end
str
end
def readline(arg = $/, limit = nil)
case arg
when String
rs = arg
when Fixnum
rs = $/
limit = arg
else
raise ArgumentError
end
if rs.nil?
return read
end
if rs == ""
rs = $/ + $/
end
str = ""
while 1
begin
_read_buf
rescue EOFError => e
str = nil if str.empty?
break
end
if limit && (str.size + @buf.size) >= limit
len = limit - str.size
str += @buf[0, len]
@pos += len
@buf = @buf[len, @buf.size - len]
break
elsif idx = @buf.index(rs)
len = idx + rs.size
str += @buf[0, len]
@pos += len
@buf = @buf[len, @buf.size - len]
break
else
str += @buf
@pos += @buf.size
@buf = ''
end
end
raise EOFError.new "end of file reached" if str.nil?
str
end
def gets(*args)
begin
readline(*args)
rescue EOFError => e
nil
end
end
def readchar
_read_buf
c = @buf[0]
@buf = @buf[1, @buf.size]
@pos += 1
c
end
def getc
begin
readchar
rescue EOFError => e
nil
end
end
# 15.2.20.5.3
def each(&block)
while line = self.gets
block.call(line)
end
self
end
# 15.2.20.5.4
def each_byte(&block)
while char = self.getc
block.call(char)
end
self
end
# 15.2.20.5.5
alias each_line each
alias each_char each_byte
def readlines
ary = []
while (line = gets)
ary << line
end
ary
end
def puts(*args)
i = 0
len = args.size
while i < len
s = args[i].to_s
write s
write "\n" if (s[-1] != "\n")
i += 1
end
write "\n" if len == 0
nil
end
def printf(*args)
write sprintf(*args)
nil
end
alias_method :to_i, :fileno
end
#!/usr/bin/env ruby
#
# mrbgems test runner
#
gemname = File.basename(File.dirname(File.expand_path __FILE__))
if __FILE__ == $0
repository, dir = 'https://github.com/mruby/mruby.git', 'tmp/mruby'
build_args = ARGV
build_args = ['all', 'test'] if build_args.nil? or build_args.empty?
Dir.mkdir 'tmp' unless File.exist?('tmp')
unless File.exist?(dir)
system "git clone #{repository} #{dir}"
end
exit system(%Q[cd #{dir}; MRUBY_CONFIG=#{File.expand_path __FILE__} ruby minirake #{build_args.join(' ')}])
end
MRuby::Build.new do |conf|
toolchain :gcc
conf.gems.clear
conf.gem "#{root}/mrbgems/mruby-sprintf"
conf.gem "#{root}/mrbgems/mruby-print"
Dir.glob("#{root}/mrbgems/mruby-*") do |x|
conf.gem x unless x =~ /\/mruby-(print|sprintf)$/
end
conf.gem File.expand_path(File.dirname(__FILE__))
end
/*
** file.c - File class
*/
#include "mruby.h"
#include "mruby/ext/io.h"
#include "mruby/class.h"
#include "mruby/data.h"
#include "mruby/string.h"
#include "error.h"
#include <sys/file.h>
#include <fcntl.h>
#include <libgen.h>
#include <limits.h>
#include <stdlib.h>
#include <string.h>
#include <sys/param.h>
#include <pwd.h>
#define FILE_SEPARATOR "/"
#ifndef LOCK_SH
#define LOCK_SH 1
#endif
#ifndef LOCK_EX
#define LOCK_EX 2
#endif
#ifndef LOCK_NB
#define LOCK_NB 4
#endif
#ifndef LOCK_UN
#define LOCK_UN 8
#endif
#define STAT(p, s) stat(p, s)
mrb_value
mrb_file_s_umask(mrb_state *mrb, mrb_value klass)
{
mrb_value *argv;
int argc;
mrb_get_args(mrb, "*", &argv, &argc);
int omask = 0;
if (argc == 0) {
omask = umask(0);
umask(omask);
} else if (argc == 1) {
mrb_value mask = argv[0];
if (!mrb_nil_p(mask) && !mrb_fixnum_p(mask)) {
mask = mrb_check_convert_type(mrb, mask, MRB_TT_FIXNUM, "Fixnum", "to_int");
}
if (!mrb_fixnum_p(mask)) {
mrb_raise(mrb, E_ARGUMENT_ERROR, "invalid argument type");
}
omask = umask(mrb_fixnum(mask));
} else {
mrb_raisef(mrb, E_ARGUMENT_ERROR, "wrong number of arguments (%d for 0..1)", argc);
}
return mrb_fixnum_value(omask);
}
static mrb_value
mrb_file_s_unlink(mrb_state *mrb, mrb_value obj)
{
mrb_value *argv;
int n, i, argc;
mrb_get_args(mrb, "*", &argv, &argc);
for (i = 0, n = 0; i < argc; i++) {
mrb_value pathv = argv[i];
if (mrb_type(pathv) != MRB_TT_STRING) {
mrb_raisef(mrb, E_TYPE_ERROR, "can't convert %s into String", mrb_obj_classname(mrb, pathv));
}
const char *path = mrb_string_value_cstr(mrb, &pathv);;
if (unlink(path) < 0) {
mrb_sys_fail(mrb, path);
} else {
n++;
}
}
return mrb_fixnum_value(n);
}
static mrb_value
mrb_file_rename_internal(mrb_state *mrb, mrb_value from, mrb_value to)
{
const char *src, *dst;
src = mrb_string_value_cstr(mrb, &from);
dst = mrb_string_value_cstr(mrb, &to);
if (rename(src, dst) < 0) {
if (chmod(dst, 0666) == 0 &&
unlink(dst) == 0 &&
rename(src, dst) == 0)
return mrb_fixnum_value(0);
mrb_sys_fail(mrb, "mrb_file_rename_internal failed.");
}
return mrb_fixnum_value(0);
}
static mrb_value
mrb_file_s_rename(mrb_state *mrb, mrb_value obj)
{
mrb_value *argv;
int argc;
mrb_get_args(mrb, "*", &argv, &argc);
if (argc != 2) {
mrb_raisef(mrb, E_ARGUMENT_ERROR, "wrong number of arguments (%d for 2)", argc);
return mrb_nil_value();
}
if (mrb_type(argv[0]) != MRB_TT_STRING) {
mrb_raisef(mrb, E_TYPE_ERROR, "can't convert %s into String", mrb_obj_classname(mrb, argv[0]));
return mrb_nil_value();
}
if (mrb_type(argv[1]) != MRB_TT_STRING) {
mrb_raisef(mrb, E_TYPE_ERROR, "can't convert %s into String", mrb_obj_classname(mrb, argv[1]));
return mrb_nil_value();
}
return mrb_file_rename_internal(mrb, argv[0], argv[1]);
}
static mrb_value
mrb_file_dirname(mrb_state *mrb, mrb_value klass)
{
char *cp, *dname;
mrb_int n;
mrb_value fname;
mrb_get_args(mrb, "s", &cp, &n);
fname = mrb_str_new(mrb, cp, n);
if ((dname = dirname(RSTRING_PTR(fname))) == NULL) {
mrb_sys_fail(mrb, "mrb_file_dirname failed.");
}
return mrb_str_new(mrb, dname, strlen(dname));
}
static mrb_value
mrb_file_basename(mrb_state *mrb, mrb_value klass)
{
char *cp, *bname;
mrb_int n;
mrb_value fname;
mrb_get_args(mrb, "s", &cp, &n);
fname = mrb_str_new(mrb, cp, n);
if ((bname = basename(RSTRING_PTR(fname))) == NULL) {
mrb_sys_fail(mrb, "mrb_file_basename failed.");
}
return mrb_str_new(mrb, bname, strlen(bname));
}
static mrb_value
mrb_file_realpath(mrb_state *mrb, mrb_value klass)
{
mrb_value pathname, dir_string, s, result;
int argc;
char *cpath;
argc = mrb_get_args(mrb, "S|S", &pathname, &dir_string);
if (argc == 2) {
s = mrb_str_dup(mrb, dir_string);
s = mrb_str_append(mrb, s, mrb_str_new_cstr(mrb, FILE_SEPARATOR));
s = mrb_str_append(mrb, s, pathname);
pathname = s;
}
cpath = mrb_str_to_cstr(mrb, pathname);
result = mrb_str_buf_new(mrb, PATH_MAX);
if (realpath(cpath, RSTRING_PTR(result)) == NULL)
mrb_sys_fail(mrb, cpath);
mrb_str_resize(mrb, result, strlen(RSTRING_PTR(result)));
return result;
}
static mrb_value
mrb_file_size(mrb_state *mrb, mrb_value klass)
{
char *cp;
FILE *fp;
mrb_int n;
mrb_int filesize;
mrb_value filename;
mrb_get_args(mrb, "s", &cp, &n);
filename = mrb_str_new(mrb, cp, n);
fp = fopen(RSTRING_PTR(filename), "rb");
if (fp == NULL) {
mrb_sys_fail(mrb, "mrb_file_size failed.");
return mrb_nil_value();
}
fseek(fp, 0, SEEK_END);
filesize = (mrb_int) ftell(fp);
fclose(fp);
return mrb_fixnum_value(filesize);
}
mrb_value
mrb_file__getwd(mrb_state *mrb, mrb_value klass)
{
mrb_value path;
path = mrb_str_buf_new(mrb, MAXPATHLEN);
if (getcwd(RSTRING_PTR(path), MAXPATHLEN) == NULL) {
mrb_sys_fail(mrb, "getcwd(2)");
}
mrb_str_resize(mrb, path, strlen(RSTRING_PTR(path)));
return path;
}
static int
mrb_file_is_absolute_path(const char *path)
{
if (path[0] == '/')
return 1;
return 0;
}
static mrb_value
mrb_file__gethome(mrb_state *mrb, mrb_value klass)
{
mrb_value username, result;
char *cuser, *home;
int argc;
struct passwd *pwd;
argc = mrb_get_args(mrb, "|S", &username);
if (argc == 0) {
home = getenv("HOME");
} else {
cuser = mrb_str_to_cstr(mrb, username);
if ((pwd = getpwnam(cuser)) == NULL) {
return mrb_nil_value();
} else {
home = pwd->pw_dir;
}
}
if (!mrb_file_is_absolute_path(home)) {
if (argc && strlen(cuser) > 0) {
mrb_raisef(mrb, E_ARGUMENT_ERROR, "non-absolute home of ~%s", cuser);
} else {
mrb_raise(mrb, E_ARGUMENT_ERROR, "non-absolute home");
}
}
result = mrb_str_buf_new(mrb, strlen(home));
strcpy( RSTRING_PTR(result), home);
mrb_str_resize(mrb, result, strlen(RSTRING_PTR(result)));
return result;
}
void
mrb_init_file(mrb_state *mrb)
{
struct RClass *io, *file, *cnst;
io = mrb_class_obj_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, ARGS_REQ(1));
mrb_define_class_method(mrb, file, "unlink", mrb_file_s_unlink, ARGS_ANY());
mrb_define_class_method(mrb, file, "delete", mrb_file_s_unlink, ARGS_ANY());
mrb_define_class_method(mrb, file, "rename", mrb_file_s_rename, ARGS_REQ(2));
mrb_define_class_method(mrb, file, "dirname", mrb_file_dirname, ARGS_REQ(1));
mrb_define_class_method(mrb, file, "basename", mrb_file_basename, ARGS_REQ(1));
mrb_define_class_method(mrb, file, "realpath", mrb_file_realpath, ARGS_REQ(1)|ARGS_OPT(1));
mrb_define_class_method(mrb, file, "size", mrb_file_size, ARGS_REQ(1));
mrb_define_class_method(mrb, file, "_getwd", mrb_file__getwd, ARGS_NONE());
mrb_define_class_method(mrb, file, "_gethome", mrb_file__gethome, ARGS_OPT(1));
cnst = mrb_define_module_under(mrb, file, "Constants");
mrb_define_const(mrb, cnst, "LOCK_SH", mrb_fixnum_value(LOCK_SH));
mrb_define_const(mrb, cnst, "LOCK_EX", mrb_fixnum_value(LOCK_EX));
mrb_define_const(mrb, cnst, "LOCK_UN", mrb_fixnum_value(LOCK_UN));
mrb_define_const(mrb, cnst, "LOCK_NB", mrb_fixnum_value(LOCK_NB));
mrb_define_const(mrb, cnst, "SEPARATOR", mrb_str_new_cstr(mrb, FILE_SEPARATOR));
}
/*
** file.c - File class
*/
#include "mruby.h"
#include "mruby/ext/io.h"
#include "mruby/class.h"
#include "mruby/data.h"
#include "mruby/string.h"
#include "error.h"
#include <sys/file.h>
#include <libgen.h>
#include <limits.h>
#include <stdlib.h>
#include <string.h>
#include <sys/param.h>
#include <pwd.h>
extern struct mrb_data_type mrb_io_type;
static int
mrb_stat(mrb_state *mrb, mrb_value obj, struct stat *st)
{
mrb_value tmp;
mrb_value io_klass, str_klass;
io_klass = mrb_obj_value(mrb_class_obj_get(mrb, "IO"));
str_klass = mrb_obj_value(mrb_class_obj_get(mrb, "String"));
tmp = mrb_funcall(mrb, obj, "is_a?", 1, io_klass);
if (mrb_test(tmp)) {
struct mrb_io *fptr;
fptr = (struct mrb_io *)mrb_get_datatype(mrb, obj, &mrb_io_type);
if (fptr && fptr->fd >= 0) {
return fstat(fptr->fd, st);
}
mrb_raise(mrb, E_IO_ERROR, "closed stream");
return -1;
}
tmp = mrb_funcall(mrb, obj, "is_a?", 1, str_klass);
if (mrb_test(tmp)) {
return stat(mrb_string_value_cstr(mrb, &obj), st);
}
return -1;
}
/*
* Document-method: directory?
*
* call-seq:
* File.directory?(file_name) -> true or false
*
* Returns <code>true</code> if the named file is a directory,
* or a symlink that points at a directory, and <code>false</code>
* otherwise.
*
* File.directory?(".")
*/
mrb_value
mrb_filetest_s_directory_p(mrb_state *mrb, mrb_value klass)
{
#ifndef S_ISDIR
# define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
#endif
struct stat st;
mrb_value obj;
mrb_get_args(mrb, "o", &obj);
if (mrb_stat(mrb, obj, &st) < 0)
return mrb_false_value();
if (S_ISDIR(st.st_mode))
return mrb_true_value();
return mrb_false_value();
}
/*
* call-seq:
* File.pipe?(file_name) -> true or false
*
* Returns <code>true</code> if the named file is a pipe.
*/
mrb_value
mrb_filetest_s_pipe_p(mrb_state *mrb, mrb_value klass)
{
#ifdef S_IFIFO
# ifndef S_ISFIFO
# define S_ISFIFO(m) (((m) & S_IFMT) == S_IFIFO)
# endif
struct stat st;
mrb_value obj;
mrb_get_args(mrb, "o", &obj);
if (mrb_stat(mrb, obj, &st) < 0)
return mrb_false_value();
if (S_ISFIFO(st.st_mode))
return mrb_true_value();
#endif
return mrb_false_value();
}
/*
* call-seq:
* File.symlink?(file_name) -> true or false
*
* Returns <code>true</code> if the named file is a symbolic link.
*/
mrb_value
mrb_filetest_s_symlink_p(mrb_state *mrb, mrb_value klass)
{
#ifndef S_ISLNK
# ifdef _S_ISLNK
# define S_ISLNK(m) _S_ISLNK(m)
# else
# ifdef _S_IFLNK
# define S_ISLNK(m) (((m) & S_IFMT) == _S_IFLNK)
# else
# ifdef S_IFLNK
# define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK)
# endif
# endif
# endif
#endif
#ifdef S_ISLNK
struct stat st;
mrb_value obj;
mrb_get_args(mrb, "o", &obj);
if (mrb_stat(mrb, obj, &st) < 0)
return mrb_false_value();
if (S_ISLNK(st.st_mode))
return mrb_true_value();
#endif
return mrb_false_value();
}
/*
* call-seq:
* File.socket?(file_name) -> true or false
*
* Returns <code>true</code> if the named file is a socket.
*/
mrb_value
mrb_filetest_s_socket_p(mrb_state *mrb, mrb_value klass)
{
#ifndef S_ISSOCK
# ifdef _S_ISSOCK
# define S_ISSOCK(m) _S_ISSOCK(m)
# else
# ifdef _S_IFSOCK
# define S_ISSOCK(m) (((m) & S_IFMT) == _S_IFSOCK)
# else
# ifdef S_IFSOCK
# define S_ISSOCK(m) (((m) & S_IFMT) == S_IFSOCK)
# endif
# endif
# endif
#endif
#ifdef S_ISSOCK
struct stat st;
mrb_value obj;
mrb_get_args(mrb, "o", &obj);
if (mrb_stat(mrb, obj, &st) < 0)
return mrb_false_value();
if (S_ISSOCK(st.st_mode))
return mrb_true_value();
#endif
return mrb_false_value();
}
/*
* call-seq:
* File.exist?(file_name) -> true or false
* File.exists?(file_name) -> true or false
*
* Return <code>true</code> if the named file exists.
*/
mrb_value
mrb_filetest_s_exist_p(mrb_state *mrb, mrb_value klass)
{
struct stat st;
mrb_value obj;
mrb_get_args(mrb, "o", &obj);
if (mrb_stat(mrb, obj, &st) < 0)
return mrb_false_value();
return mrb_true_value();
}
/*
* call-seq:
* File.file?(file_name) -> true or false
*
* Returns <code>true</code> if the named file exists and is a
* regular file.
*/
mrb_value
mrb_filetest_s_file_p(mrb_state *mrb, mrb_value klass)
{
#ifndef S_ISREG
# define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
#endif
struct stat st;
mrb_value obj;
mrb_get_args(mrb, "o", &obj);
if (mrb_stat(mrb, obj, &st) < 0)
return mrb_false_value();
if (S_ISREG(st.st_mode))
return mrb_true_value();
return mrb_false_value();
}
/*
* call-seq:
* File.zero?(file_name) -> true or false
*
* Returns <code>true</code> if the named file exists and has
* a zero size.
*/
mrb_value
mrb_filetest_s_zero_p(mrb_state *mrb, mrb_value klass)
{
struct stat st;
mrb_value obj;
mrb_get_args(mrb, "o", &obj);
if (mrb_stat(mrb, obj, &st) < 0)
return mrb_false_value();
if (st.st_size == 0)
return mrb_true_value();
return mrb_false_value();
}
/*
* call-seq:
* File.size?(file_name) -> Integer or nil
*
* Returns +nil+ if +file_name+ doesn't exist or has zero size, the size of the
* file otherwise.
*/
mrb_value
mrb_filetest_s_size_p(mrb_state *mrb, mrb_value klass)
{
struct stat st;
mrb_value obj;
mrb_get_args(mrb, "o", &obj);
if (mrb_stat(mrb, obj, &st) < 0)
return mrb_nil_value();
if (st.st_size == 0)
return mrb_nil_value();
return mrb_fixnum_value(st.st_size);
}
void
mrb_init_file_test(mrb_state *mrb)
{
struct RClass *f;
f = mrb_define_class(mrb, "FileTest", mrb->object_class);
mrb_define_class_method(mrb, f, "directory?", mrb_filetest_s_directory_p, ARGS_REQ(1));
mrb_define_class_method(mrb, f, "exist?", mrb_filetest_s_exist_p, ARGS_REQ(1));
mrb_define_class_method(mrb, f, "exists?", mrb_filetest_s_exist_p, ARGS_REQ(1));
mrb_define_class_method(mrb, f, "file?", mrb_filetest_s_file_p, ARGS_REQ(1));
mrb_define_class_method(mrb, f, "pipe?", mrb_filetest_s_pipe_p, ARGS_REQ(1));
mrb_define_class_method(mrb, f, "size?", mrb_filetest_s_size_p, ARGS_REQ(1));
mrb_define_class_method(mrb, f, "socket?", mrb_filetest_s_socket_p, ARGS_REQ(1));
mrb_define_class_method(mrb, f, "symlink?", mrb_filetest_s_symlink_p, ARGS_REQ(1));
mrb_define_class_method(mrb, f, "zero?", mrb_filetest_s_zero_p, ARGS_REQ(1));
}
/*
** io.c - IO class
*/
#include "mruby.h"
#include "mruby/hash.h"
#include "mruby/data.h"
#include "mruby/khash.h"
#include "mruby/array.h"
#include "mruby/class.h"
#include "mruby/string.h"
#include "mruby/variable.h"
#include "mruby/ext/io.h"
#include "error.h"
static int mrb_io_modestr_to_flags(mrb_state *mrb, const char *modestr);
static int mrb_io_modenum_to_flags(mrb_state *mrb, int modenum);
static int mrb_io_flags_to_modenum(mrb_state *mrb, int flags);
static int
mrb_io_modestr_to_flags(mrb_state *mrb, const char *mode)
{
int flags = 0;
const char *m = mode;
switch (*m++) {
case 'r':
flags |= FMODE_READABLE;
break;
case 'w':
flags |= FMODE_WRITABLE | FMODE_CREATE;
break;
case 'a':
flags |= FMODE_WRITABLE | FMODE_APPEND | FMODE_CREATE;
break;
default:
mrb_raisef(mrb, E_ARGUMENT_ERROR, "illegal access mode %s", mode);
}
while (*m) {
switch (*m++) {
case 'b':
flags |= FMODE_BINMODE;
break;
case '+':
flags |= FMODE_READWRITE;
break;
case ':':
/* XXX: PASSTHROUGH*/
default:
mrb_raisef(mrb, E_ARGUMENT_ERROR, "illegal access mode %s", mode);
}
}
return flags;
}
static int
mrb_io_modenum_to_flags(mrb_state *mrb, int modenum)
{
int flags = 0;
switch (modenum & (O_RDONLY|O_WRONLY|O_RDWR)) {
case O_RDONLY:
flags = FMODE_READABLE;
break;
case O_WRONLY:
flags = FMODE_WRITABLE;
break;
case O_RDWR:
flags = FMODE_READWRITE;
break;
}
if (modenum & O_APPEND) {
flags |= FMODE_APPEND;
}
if (modenum & O_TRUNC) {
flags |= FMODE_TRUNC;
}
if (modenum & O_CREAT) {
flags |= FMODE_CREATE;
}
#ifdef O_BINARY
if (modenum & O_BINARY) {
flags |= FMODE_BINMODE;
}
#endif
return flags;
}
static int
mrb_io_flags_to_modenum(mrb_state *mrb, int flags)
{
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 (flags & FMODE_CREATE) {
modenum |= O_CREAT;
}
#ifdef O_BINARY
if (flags & FMODE_BINMODE) {
modenum |= O_BINARY
}
#endif
return modenum;
}
static int
mrb_proc_exec(const char *pname)
{
const char *s;
s = pname;
while (*s == ' ' || *s == '\t' || *s == '\n')
s++;
if (!*s) {
errno = ENOENT;
return -1;
}
execl("/bin/sh", "sh", "-c", pname, (char *)NULL);
return -1;
}
static void
mrb_io_free(mrb_state *mrb, void *ptr)
{
struct mrb_io *io = (struct mrb_io *)ptr;
if (io != NULL) {
fptr_finalize(mrb, io, TRUE);
mrb_free(mrb, io);
}
}
struct mrb_data_type mrb_io_type = { "IO", mrb_io_free };
static struct mrb_io*
mrb_io_alloc(mrb_state *mrb)
{
struct mrb_io *fptr;
fptr = (struct mrb_io *)mrb_malloc(mrb, sizeof(struct mrb_io));
fptr->fd = -1;
fptr->fd2 = -1;
fptr->pid = 0;
return fptr;
}
static int
io_open(mrb_state *mrb, mrb_value path, int flags, int perm)
{
const char *pat;
int modenum;
pat = mrb_string_value_cstr(mrb, &path);
modenum = mrb_io_flags_to_modenum(mrb, flags);
return open(pat, modenum, perm);
}
#ifndef NOFILE
#define NOFILE 64
#endif
mrb_value
mrb_io_s_popen(mrb_state *mrb, mrb_value klass)
{
mrb_value cmd, io;
mrb_value mode = mrb_str_new_cstr(mrb, "r");
mrb_value opt = mrb_hash_new(mrb);
struct mrb_io *fptr;
const char *pname;
int pid, flags, fd, write_fd = -1;
int pr[2], pw[2];
int doexec;
mrb_get_args(mrb, "S|SH", &cmd, &mode, &opt);
io = mrb_obj_value(mrb_data_object_alloc(mrb, mrb_class_ptr(klass), NULL, &mrb_io_type));
pname = mrb_string_value_cstr(mrb, &cmd);
flags = mrb_io_modestr_to_flags(mrb, mrb_string_value_cstr(mrb, &mode));
doexec = (strcmp("-", pname) != 0);
if (((flags & FMODE_READABLE) && pipe(pr) == -1)
|| ((flags & FMODE_WRITABLE) && pipe(pw) == -1)) {
mrb_sys_fail(mrb, "pipe_open failed.");
return mrb_nil_value();
}
if (!doexec) {
// XXX
fflush(stdin);
fflush(stdout);
fflush(stderr);
}
retry:
switch (pid = fork()) {
case 0: /* child */
if (flags & FMODE_READABLE) {
close(pr[0]);
if (pr[1] != 1) {
dup2(pr[1], 1);
close(pr[1]);
}
}
if (flags & FMODE_WRITABLE) {
close(pw[1]);
if (pw[0] != 0) {
dup2(pw[0], 0);
close(pw[0]);
}
}
if (doexec) {
for (fd = 3; fd < NOFILE; fd++) {
close(fd);
}
mrb_proc_exec(pname);
mrb_raisef(mrb, E_IO_ERROR, "command not found: %s", pname);
_exit(127);
}
return mrb_nil_value();
case -1: /* error */
if (errno == EAGAIN) {
goto retry;
} else {
int e = errno;
if (flags & FMODE_READABLE) {
close(pr[0]);
close(pr[1]);
}
if (flags & FMODE_WRITABLE) {
close(pw[0]);
close(pw[1]);
}
errno = e;
mrb_sys_fail(mrb, "pipe_open failed.");
}
break;
default: /* parent */
if (pid < 0) {
mrb_sys_fail(mrb, "pipe_open failed.");
return mrb_nil_value();
} else {
if ((flags & FMODE_READABLE) && (flags & FMODE_WRITABLE)) {
close(pr[1]);
fd = pr[0];
close(pw[0]);
write_fd = pw[1];
} else if (flags & FMODE_READABLE) {
close(pr[1]);
fd = pr[0];
} else {
close(pw[0]);
fd = pw[1];
}
mrb_iv_set(mrb, io, mrb_intern(mrb, "@buf"), mrb_str_new_cstr(mrb, ""));
mrb_iv_set(mrb, io, mrb_intern(mrb, "@pos"), mrb_fixnum_value(0));
fptr = mrb_io_alloc(mrb);
fptr->fd = fd;
fptr->fd2 = write_fd;
fptr->pid = pid;
DATA_TYPE(io) = &mrb_io_type;
DATA_PTR(io) = fptr;
return io;
}
}
return mrb_nil_value();
}
mrb_value
mrb_io_initialize(mrb_state *mrb, mrb_value io)
{
struct mrb_io *fptr;
mrb_int fd, flags;
mrb_value mode, opt;
DATA_TYPE(io) = &mrb_io_type;
DATA_PTR(io) = NULL;
mode = opt = mrb_nil_value();
mrb_get_args(mrb, "i|So", &fd, &mode, &opt);
if (mrb_nil_p(mode)) {
mode = mrb_str_new_cstr(mrb, "r");
}
if (mrb_nil_p(opt)) {
opt = mrb_hash_new(mrb);
}
flags = mrb_io_modestr_to_flags(mrb, mrb_string_value_cstr(mrb, &mode));
mrb_iv_set(mrb, io, mrb_intern(mrb, "@buf"), mrb_str_new_cstr(mrb, ""));
mrb_iv_set(mrb, io, mrb_intern(mrb, "@pos"), mrb_fixnum_value(0));
fptr = DATA_PTR(io);
if (fptr == NULL) {
fptr = mrb_io_alloc(mrb);
}
fptr->fd = fd;
DATA_PTR(io) = fptr;
return io;
}
void
fptr_finalize(mrb_state *mrb, struct mrb_io *fptr, int noraise)
{
int n = 0;
if (fptr == NULL) {
return;
}
if (fptr->fd > 2) {
n = close(fptr->fd);
if (n == 0) {
fptr->fd = -1;
}
}
if (fptr->fd2 > 2) {
n = close(fptr->fd2);
if (n == 0) {
fptr->fd2 = -1;
}
}
if (!noraise && n != 0) {
mrb_sys_fail(mrb, "fptr_finalize failed.");
}
}
mrb_value
mrb_io_bless(mrb_state *mrb, mrb_value io)
{
if (mrb_type(io) != MRB_TT_DATA) {
mrb_raise(mrb, E_TYPE_ERROR, "expected IO object");
return mrb_nil_value();
}
DATA_TYPE(io) = &mrb_io_type;
DATA_PTR(io) = NULL;
DATA_PTR(io) = mrb_io_alloc(mrb);
return io;
}
mrb_value
mrb_io_s_for_fd(mrb_state *mrb, mrb_value klass)
{
mrb_value io = mrb_obj_value(mrb_data_object_alloc(mrb, mrb_class_ptr(klass), NULL, &mrb_io_type));
return mrb_io_initialize(mrb, io);
}
mrb_value
mrb_io_s_sysopen(mrb_state *mrb, mrb_value klass)
{
mrb_value path = mrb_nil_value();
mrb_value mode = mrb_nil_value();
mrb_int fd, flags, perm = -1;
mrb_get_args(mrb, "S|Si", &path, &mode, &perm);
if (mrb_nil_p(mode)) {
mode = mrb_str_new_cstr(mrb, "r");
}
if (perm < 0) {
perm = 0666;
}
flags = mrb_io_modestr_to_flags(mrb, mrb_string_value_cstr(mrb, &mode));
fd = io_open(mrb, path, flags, perm);
return mrb_fixnum_value(fd);
}
mrb_value
mrb_io_sysread(mrb_state *mrb, mrb_value io)
{
struct mrb_io *fptr;
mrb_value buf = mrb_nil_value();
int maxlen, ret;
mrb_get_args(mrb, "i|S", &maxlen, &buf);
if (maxlen < 0) {
return mrb_nil_value();
}
if (mrb_nil_p(buf)) {
buf = mrb_str_new(mrb, "", maxlen);
}
if (RSTRING_LEN(buf) != maxlen) {
buf = mrb_str_resize(mrb, buf, maxlen);
}
fptr = (struct mrb_io *)mrb_get_datatype(mrb, io, &mrb_io_type);
ret = read(fptr->fd, RSTRING_PTR(buf), 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");
return mrb_nil_value();
}
break;
case -1: /* Error */
mrb_raise(mrb, E_IO_ERROR, "sysread failed");
return mrb_nil_value();
break;
default:
if (RSTRING_LEN(buf) != ret) {
buf = mrb_str_resize(mrb, buf, ret);
}
break;
}
return buf;
}
mrb_value
mrb_io_sysseek(mrb_state *mrb, mrb_value io)
{
struct mrb_io *fptr;
int pos, offset, whence = -1;
mrb_get_args(mrb, "i|i", &offset, &whence);
if (whence < 0) {
whence = 0;
}
fptr = (struct mrb_io *)mrb_get_datatype(mrb, io, &mrb_io_type);
pos = lseek(fptr->fd, offset, whence);
if (pos < 0) {
mrb_raise(mrb, E_IO_ERROR, "sysseek faield");
return mrb_nil_value();
}
return mrb_fixnum_value(pos);
}
mrb_value
mrb_io_syswrite(mrb_state *mrb, mrb_value io)
{
struct mrb_io *fptr;
mrb_value str, buf;
int length;
mrb_get_args(mrb, "S", &str);
if (mrb_type(str) != MRB_TT_STRING) {
buf = mrb_funcall(mrb, str, "to_s", 0);
} else {
buf = str;
}
fptr = (struct mrb_io *)mrb_get_datatype(mrb, io, &mrb_io_type);
length = write(fptr->fd, RSTRING_PTR(buf), RSTRING_LEN(buf));
return mrb_fixnum_value(length);
}
mrb_value
mrb_io_close(mrb_state *mrb, mrb_value io)
{
struct mrb_io *fptr;
fptr = (struct mrb_io *)mrb_get_datatype(mrb, io, &mrb_io_type);
if (fptr && fptr->fd < 0) {
mrb_raise(mrb, E_IO_ERROR, "closed stream.");
return mrb_nil_value();
}
fptr_finalize(mrb, fptr, FALSE);
return mrb_nil_value();
}
mrb_value
mrb_io_closed(mrb_state *mrb, mrb_value io)
{
struct mrb_io *fptr;
fptr = (struct mrb_io *)mrb_get_datatype(mrb, io, &mrb_io_type);
if (fptr->fd >= 0) {
return mrb_false_value();
}
return mrb_true_value();
}
mrb_value
mrb_io_pid(mrb_state *mrb, mrb_value io)
{
struct mrb_io *fptr;
fptr = (struct mrb_io *)mrb_get_datatype(mrb, io, &mrb_io_type);
if (fptr->pid > 0) {
return mrb_fixnum_value(fptr->pid);
}
return mrb_nil_value();
}
static struct timeval
time2timeval(mrb_state *mrb, mrb_value time)
{
struct timeval t;
switch (mrb_type(time)) {
case MRB_TT_FIXNUM:
t.tv_sec = mrb_fixnum(time);
t.tv_usec = 0;
break;
case MRB_TT_FLOAT:
t.tv_sec = mrb_float(time);
t.tv_usec = (mrb_float(time) - t.tv_sec) * 1000000.0;
break;
default:
mrb_raise(mrb, E_TYPE_ERROR, "wrong argument class");
}
return t;
}
static mrb_value
mrb_io_select(mrb_state *mrb, mrb_value klass)
{
mrb_value *argv;
int argc;
mrb_value read, write, except, timeout, list;
struct timeval *tp, timerec;
fd_set pset, rset, wset, eset;
fd_set *rp, *wp, *ep;
struct mrb_io *fptr;
int pending = 0;
mrb_value result;
int max = 0;
int interrupt_flag = 0;
int i, n;
mrb_get_args(mrb, "*", &argv, &argc);
if (argc < 1 || argc > 4) {
mrb_raisef(mrb, E_ARGUMENT_ERROR,
"wrong number of arguments (%d for 1..4)", argc);
return mrb_nil_value();
}
timeout = mrb_nil_value();
except = mrb_nil_value();
write = mrb_nil_value();
if (argc > 3)
timeout = argv[3];
if (argc > 2)
except = argv[2];
if (argc > 1)
write = argv[1];
read = argv[0];
if (mrb_nil_p(timeout)) {
tp = NULL;
} else {
timerec = time2timeval(mrb, timeout);
tp = &timerec;
}
FD_ZERO(&pset);
if (!mrb_nil_p(read)) {
mrb_check_type(mrb, read, MRB_TT_ARRAY);
rp = &rset;
FD_ZERO(rp);
for (i = 0; i < RARRAY_LEN(read); i++) {
fptr = (struct mrb_io *)mrb_get_datatype(mrb, RARRAY_PTR(read)[i], &mrb_io_type);
FD_SET(fptr->fd, rp);
/* XXX: .....
if (READ_DATA_PENDING(fptr->f)) {
pending++;
FD_SET(fileno(fptr->f), &pset);
}
*/
if (max < fptr->fd)
max = fptr->fd;
}
if (pending) {
timerec.tv_sec = timerec.tv_usec = 0;
tp = &timerec;
}
} else {
rp = NULL;
}
if (!mrb_nil_p(write)) {
mrb_check_type(mrb, write, MRB_TT_ARRAY);
wp = &wset;
FD_ZERO(wp);
for (i = 0; i < RARRAY_LEN(write); i++) {
fptr = (struct mrb_io *)mrb_get_datatype(mrb, RARRAY_PTR(write)[i], &mrb_io_type);
FD_SET(fptr->fd, wp);
if (max < fptr->fd)
max = fptr->fd;
if (fptr->fd2 >= 0) {
FD_SET(fptr->fd2, wp);
if (max < fptr->fd2)
max = fptr->fd2;
}
}
} else {
wp = NULL;
}
if (!mrb_nil_p(except)) {
mrb_check_type(mrb, except, MRB_TT_ARRAY);
ep = &eset;
FD_ZERO(ep);
for (i = 0; i < RARRAY_LEN(except); i++) {
fptr = (struct mrb_io *)mrb_get_datatype(mrb, RARRAY_PTR(except)[i], &mrb_io_type);
FD_SET(fptr->fd, ep);
if (max < fptr->fd)
max = fptr->fd;
if (fptr->fd2) {
FD_SET(fptr->fd2, ep);
if (max < fptr->fd2)
max = fptr->fd2;
}
}
} else {
ep = NULL;
}
max++;
retry:
n = select(max, rp, wp, ep, tp);
if (n < 0) {
if (errno != EINTR)
mrb_sys_fail(mrb, "select failed");
if (tp == NULL)
goto retry;
interrupt_flag = 1;
}
if (!pending && n == 0)
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));
mrb_ary_push(mrb, result, wp? mrb_ary_new(mrb) : mrb_ary_new_capa(mrb, 0));
mrb_ary_push(mrb, result, ep? mrb_ary_new(mrb) : mrb_ary_new_capa(mrb, 0));
if (interrupt_flag == 0) {
if (rp) {
list = RARRAY_PTR(result)[0];
for (i = 0; i < RARRAY_LEN(read); i++) {
fptr = (struct mrb_io *)mrb_get_datatype(mrb, RARRAY_PTR(read)[i], &mrb_io_type);
if (FD_ISSET(fptr->fd, rp) ||
FD_ISSET(fptr->fd, &pset)) {
mrb_ary_push(mrb, list, RARRAY_PTR(read)[i]);
}
}
}
if (wp) {
list = RARRAY_PTR(result)[1];
for (i = 0; i < RARRAY_LEN(write); i++) {
fptr = (struct mrb_io *)mrb_get_datatype(mrb, RARRAY_PTR(write)[i], &mrb_io_type);
if (FD_ISSET(fptr->fd, wp)) {
mrb_ary_push(mrb, list, RARRAY_PTR(write)[i]);
} else if (fptr->fd2 && FD_ISSET(fptr->fd2, wp)) {
mrb_ary_push(mrb, list, RARRAY_PTR(write)[i]);
}
}
}
if (ep) {
list = RARRAY_PTR(result)[2];
for (i = 0; i < RARRAY_LEN(except); i++) {
fptr = (struct mrb_io *)mrb_get_datatype(mrb, RARRAY_PTR(except)[i], &mrb_io_type);
if (FD_ISSET(fptr->fd, ep)) {
mrb_ary_push(mrb, list, RARRAY_PTR(except)[i]);
} else if (fptr->fd2 && FD_ISSET(fptr->fd2, wp)) {
mrb_ary_push(mrb, list, RARRAY_PTR(except)[i]);
}
}
}
}
return result;
}
mrb_value
mrb_io_fileno(mrb_state *mrb, mrb_value io)
{
struct mrb_io *fptr;
fptr = (struct mrb_io *)mrb_get_datatype(mrb, io, &mrb_io_type);
return mrb_fixnum_value(fptr->fd);
}
void
mrb_init_io(mrb_state *mrb)
{
struct RClass *io;
io = mrb_define_class(mrb, "IO", mrb->object_class);
MRB_SET_INSTANCE_TT(io, MRB_TT_DATA);
mrb_include_module(mrb, io, mrb_class_get(mrb, "Enumerable")); /* 15.2.20.3 */
mrb_define_class_method(mrb, io, "_popen", mrb_io_s_popen, ARGS_ANY());
mrb_define_class_method(mrb, io, "for_fd", mrb_io_s_for_fd, ARGS_REQ(1)|ARGS_OPT(2));
mrb_define_class_method(mrb, io, "sysopen", mrb_io_s_sysopen, ARGS_ANY());
mrb_define_method(mrb, io, "_bless", mrb_io_bless, ARGS_NONE());
mrb_define_method(mrb, io, "initialize", mrb_io_initialize, ARGS_ANY()); /* 15.2.20.5.21 (x)*/
mrb_define_method(mrb, io, "sysread", mrb_io_sysread, ARGS_ANY());
mrb_define_method(mrb, io, "sysseek", mrb_io_sysseek, ARGS_REQ(1));
mrb_define_method(mrb, io, "syswrite", mrb_io_syswrite, ARGS_REQ(1));
mrb_define_method(mrb, io, "close", mrb_io_close, ARGS_NONE()); /* 15.2.20.5.1 */
mrb_define_method(mrb, io, "closed?", mrb_io_closed, ARGS_NONE()); /* 15.2.20.5.2 */
mrb_define_method(mrb, io, "pid", mrb_io_pid, ARGS_NONE()); /* 15.2.20.5.2 */
mrb_define_method(mrb, io, "fileno", mrb_io_fileno, ARGS_NONE());
mrb_gv_set(mrb, mrb_intern(mrb, "$/"), mrb_str_new_cstr(mrb, "\n"));
}
#include "mruby.h"
void mrb_init_io(mrb_state *mrb);
void mrb_init_file(mrb_state *mrb);
void mrb_init_file_test(mrb_state *mrb);
#define DONE mrb_gc_arena_restore(mrb, 0)
void
mrb_mruby_io_gem_init(mrb_state* mrb)
{
mrb_init_io(mrb); DONE;
mrb_init_file(mrb); DONE;
mrb_init_file_test(mrb); DONE;
}
void
mrb_mruby_io_gem_final(mrb_state* mrb)
{
}
##
# IO Test
assert('File', '15.2.21') do
File.class == Class
end
assert('File', '15.2.21.2') do
File.superclass == IO
end
assert('File TEST SETUP') do
MRubyIOTestUtil.io_test_setup
end
assert('File#initialize', '15.2.21.4.1') do
io = File.open($mrbtest_io_rfname, "r")
assert_nil io.close
assert_raise IOError do
io.close
end
end
assert('File#path', '15.2.21.4.2') do
io = File.open($mrbtest_io_rfname, "r")
assert_equal $mrbtest_io_msg + "\n", io.read
assert_equal $mrbtest_io_rfname, io.path
io.close
assert_equal $mrbtest_io_rfname, io.path
io.closed?
end
assert('File.dirname') do
path = File.dirname("filename")
"." == path
end
assert('File.basename') do
name = File.basename("../somewhere/filename")
name == "filename"
end
assert('File.extname') do
assert_equal '.txt', File.extname('foo/foo.txt')
assert_equal '.gz', File.extname('foo/foo.tar.gz')
assert_equal '', File.extname('foo/bar')
assert_equal '', File.extname('foo/.bar')
assert_equal '', File.extname('foo.txt/bar')
assert_equal '', File.extname('.foo')
end
assert('File.size') do
File.size($mrbtest_io_rfname) == $mrbtest_io_msg.size + 1 and
File.size($mrbtest_io_wfname) == 0
end
assert('File.join') do
File.join() == "" and
File.join("a") == "a" and
File.join("/a") == "/a" and
File.join("a/") == "a/" and
File.join("a", "b", "c") == "a/b/c" and
File.join("/a", "b", "c") == "/a/b/c" and
File.join("a", "b", "c/") == "a/b/c/" and
File.join("a/", "/b/", "/c") == "a/b/c"
end
assert('File.realpath') do
usrbin = IO.popen("cd bin; /bin/pwd -P") { |f| f.read.chomp }
assert_equal usrbin, File.realpath("bin")
end
assert('File TEST CLEANUP') do
assert_nil MRubyIOTestUtil.io_test_cleanup
end
##
# FileTest
assert('FileTest TEST SETUP') do
MRubyIOTestUtil.io_test_setup
end
assert("FileTest.directory?") do
assert_equal true, FileTest.directory?("/tmp")
assert_equal false, FileTest.directory?("/bin/sh")
end
assert("FileTest.exist?") do
assert_equal true, FileTest.exist?($mrbtest_io_rfname), "filename - exist"
assert_equal false, FileTest.exist?($mrbtest_io_rfname + "-"), "filename - not exist"
io = IO.new(IO.sysopen($mrbtest_io_rfname))
assert_equal true, FileTest.exist?(io), "io obj - exist"
io.close
assert_equal true, io.closed?
assert_raise IOError do
FileTest.exist?(io)
end
end
assert("FileTest.file?") do
assert_equal false, FileTest.file?("/tmp")
assert_equal true, FileTest.file?("/bin/sh")
end
assert("FileTest.pipe?") do
io = IO.popen("ls")
assert_equal true, FileTest.pipe?(io)
assert_equal false, FileTest.pipe?("/tmp")
end
assert("FileTest.size?") do
assert_equal $mrbtest_io_msg.size+1, FileTest.size?($mrbtest_io_rfname)
assert_equal nil, FileTest.size?($mrbtest_io_wfname)
assert_equal nil, FileTest.size?("not-exist-test-target-file")
fp1 = File.open($mrbtest_io_rfname)
fp2 = File.open($mrbtest_io_wfname)
assert_equal $mrbtest_io_msg.size+1, FileTest.size?(fp1)
assert_equal nil, FileTest.size?(fp2)
fp1.close
fp2.close
assert_raise IOError do
FileTest.size?(fp1)
end
assert_raise IOError do
FileTest.size?(fp2)
end
fp1.closed? && fp2.closed?
end
assert("FileTest.socket?") do
skip
end
assert("FileTest.symlink?") do
skip
end
assert("FileTest.zero?") do
assert_equal false, FileTest.zero?($mrbtest_io_rfname)
assert_equal true, FileTest.zero?($mrbtest_io_wfname)
assert_equal false, FileTest.zero?("not-exist-test-target-file")
fp1 = File.open($mrbtest_io_rfname)
fp2 = File.open($mrbtest_io_wfname)
assert_equal false, FileTest.zero?(fp1)
assert_equal true, FileTest.zero?(fp2)
fp1.close
fp2.close
assert_raise IOError do
FileTest.zero?(fp1)
end
assert_raise IOError do
FileTest.zero?(fp2)
end
fp1.closed? && fp2.closed?
end
assert('FileTest TEST CLEANUP') do
assert_nil MRubyIOTestUtil.io_test_cleanup
end
##
# IO Test
assert('IO', '15.2.20') do
assert_equal(Class, IO.class)
end
assert('IO', '15.2.20.2') do
assert_equal(Object, IO.superclass)
end
assert('IO', '15.2.20.3') do
assert_include(IO.included_modules, Enumerable)
end
assert('IO.new') do
IO.new(0)
end
assert('IO gc check') do
100.times { IO.new(0) }
end
assert('IO TEST SETUP') do
MRubyIOTestUtil.io_test_setup
end
assert('IO.sysopen, IO#close, IO#closed?') do
fd = IO.sysopen $mrbtest_io_rfname
assert_equal Fixnum, fd.class
io = IO.new fd
assert_equal IO, io.class
assert_equal false, io.closed?, "IO not closed"
assert_equal nil, io.close, "IO#close should return nil"
assert_equal true, io.closed?, "IO#closed? should return true"
end
assert('IO.sysopen, IO#sysread') do
fd = IO.sysopen $mrbtest_io_rfname
io = IO.new fd
str1 = " "
str2 = io.sysread(5, str1)
assert_equal $mrbtest_io_msg[0,5], str1
assert_equal $mrbtest_io_msg[0,5], str2
assert_raise EOFError do
io.sysread(10000)
io.sysread(10000)
end
io.close
io.closed?
end
assert('IO.sysopen, IO#syswrite') do
fd = IO.sysopen $mrbtest_io_wfname, "w"
io = IO.new fd, "w"
str = "abcdefg"
len = io.syswrite(str)
assert_equal str.size, len
io.close
io.closed?
end
assert('IO#_read_buf') do
fd = IO.sysopen $mrbtest_io_rfname
io = IO.new fd
def io._buf
@buf
end
msg_len = $mrbtest_io_msg.size + 1
assert_equal '', io._buf
assert_equal $mrbtest_io_msg + "\n", io._read_buf
assert_equal $mrbtest_io_msg + "\n", io._buf
assert_equal 'mruby', io.read(5)
assert_equal 5, io.pos
assert_equal msg_len - 5, io._buf.size
assert_equal $mrbtest_io_msg[5,100] + "\n", io.read
assert_equal 0, io._buf.size
assert_raise EOFError do
io._read_buf
end
assert_equal true, io.eof
assert_equal true, io.eof?
io.close
io.closed?
end
assert('IO#read argument check') do
fd = IO.sysopen $mrbtest_io_rfname
io = IO.new fd
assert_raise TypeError do
io.read("str")
end
assert_raise ArgumentError do
io.read(-5)
end
io.close
io.closed?
end
assert('IO#read') do
fd = IO.sysopen $mrbtest_io_rfname
io = IO.new fd
assert_equal 'mruby', io.read(5)
assert_equal $mrbtest_io_msg[5,100] + "\n", io.read
assert_equal nil, io.read
io.close
io.closed?
end
assert('IO#readchar, IO#getc') do
fd = IO.sysopen $mrbtest_io_rfname
io = IO.new fd
def io._buf
@buf
end
assert_equal 'm', io.readchar
assert_equal 1, io.pos
assert_equal 'r', io.getc
assert_equal 2, io.pos
io.gets
assert_raise EOFError do
io.readchar
end
assert_equal nil, io.getc
io.close
io.closed?
end
assert('IO#pos=, IO#seek') do
fd = IO.sysopen $mrbtest_io_rfname
io = IO.new fd
def io._buf
@buf
end
assert_equal 'm', io.getc
assert_equal 1, io.pos
assert_equal 0, io.seek(0)
assert_equal 0, io.pos
io.close
io.closed?
end
assert('IO#gets') do
fd = IO.sysopen $mrbtest_io_rfname
io = IO.new fd
# gets without arguments
assert_equal $mrbtest_io_msg + "\n", io.gets, "gets without arguments"
assert_equal nil, io.gets, "gets returns nil, when EOF"
# gets with limit
io.pos = 0
assert_equal $mrbtest_io_msg[0, 5], io.gets(5), "gets with limit"
# gets with rs
io.pos = 0
assert_equal $mrbtest_io_msg[0, 6], io.gets(' '), "gets with rs"
# gets with rs, limit
io.pos = 0
assert_equal $mrbtest_io_msg[0, 5], io.gets(' ', 5), "gets with rs, limit"
io.close
assert_equal true, io.closed?, "close success"
# reading many-lines file.
fd = IO.sysopen $mrbtest_io_wfname, "w"
io = IO.new fd, "w"
io.write "0123456789" * 2 + "\na"
assert_equal 22, io.pos
io.close
assert_equal true, io.closed?
fd = IO.sysopen $mrbtest_io_wfname
io = IO.new fd
line = io.gets
# gets first line
assert_equal "0123456789" * 2 + "\n", line, "gets first line"
assert_equal 21, line.size
assert_equal 21, io.pos
# gets second line
assert_equal "a", io.gets, "gets second line"
# gets third line
assert_equal nil, io.gets, "gets third line; returns nil"
io.close
io.closed?
end
assert('IO#gets - paragraph mode') do
fd = IO.sysopen $mrbtest_io_wfname, "w"
io = IO.new fd, "w"
io.write "0" * 10 + "\n"
io.write "1" * 10 + "\n\n"
io.write "2" * 10 + "\n"
assert_equal 34, io.pos
io.close
assert_equal true, io.closed?
fd = IO.sysopen $mrbtest_io_wfname
io = IO.new fd
para1 = "#{'0' * 10}\n#{'1' * 10}\n\n"
text1 = io.gets("")
assert_equal para1, text1
para2 = "#{'2' * 10}\n"
text2 = io.gets("")
assert_equal para2, text2
io.close
io.closed?
end
assert('IO.popen') do
io = IO.popen("ls")
assert_equal Fixnum, io.pid.class
ls = io.read
assert_equal ls.class, String
assert_include ls, 'AUTHORS'
assert_include ls, 'mrblib'
io.close
io.closed?
end
assert('IO#fileno') do
fd = IO.sysopen $mrbtest_io_rfname
io = IO.new fd
assert_equal io.fileno, fd
assert_equal io.to_i, fd
io.close
io.closed?
end
assert('IO TEST CLEANUP') do
assert_nil MRubyIOTestUtil.io_test_cleanup
end
#include "mruby.h"
#include "mruby/array.h"
#include "mruby/string.h"
#include "mruby/variable.h"
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
static mrb_value
mrb_io_test_io_setup(mrb_state *mrb, mrb_value self)
{
char rfname[] = "tmp.XXXXXXXX";
char wfname[] = "tmp.XXXXXXXX";
char msg[] = "mruby io test";
FILE *fp;
mrb_value ary = mrb_ary_new(mrb);
mktemp(rfname);
mktemp(wfname);
mrb_gv_set(mrb, mrb_intern(mrb, "$mrbtest_io_rfname"), mrb_str_new_cstr(mrb, rfname));
mrb_gv_set(mrb, mrb_intern(mrb, "$mrbtest_io_wfname"), mrb_str_new_cstr(mrb, wfname));
mrb_gv_set(mrb, mrb_intern(mrb, "$mrbtest_io_msg"), mrb_str_new_cstr(mrb, msg));
mrb_ary_push(mrb, ary, mrb_str_new_cstr(mrb, rfname));
mrb_ary_push(mrb, ary, mrb_str_new_cstr(mrb, wfname));
mrb_ary_push(mrb, ary, mrb_str_new_cstr(mrb, msg));
fp = fopen(rfname, "w");
if (fp == NULL) {
mrb_raise(mrb, E_RUNTIME_ERROR, "can't open temporary file");
return mrb_nil_value();
}
fprintf(fp, "%s\n", msg);
fclose(fp);
fp = fopen(wfname, "w");
if (fp == NULL) {
mrb_raise(mrb, E_RUNTIME_ERROR, "can't open temporary file");
return mrb_nil_value();
}
fclose(fp);
return ary;
}
static mrb_value
mrb_io_test_io_cleanup(mrb_state *mrb, mrb_value self)
{
mrb_value rfname = mrb_gv_get(mrb, mrb_intern(mrb, "$mrbtest_io_rfname"));
mrb_value wfname = mrb_gv_get(mrb, mrb_intern(mrb, "$mrbtest_io_wfname"));
if (mrb_type(rfname) == MRB_TT_STRING) {
remove(RSTRING_PTR(rfname));
}
if (mrb_type(wfname) == MRB_TT_STRING) {
remove(RSTRING_PTR(wfname));
}
mrb_gv_set(mrb, mrb_intern(mrb, "$mrbtest_io_rfname"), mrb_nil_value());
mrb_gv_set(mrb, mrb_intern(mrb, "$mrbtest_io_wfname"), mrb_nil_value());
mrb_gv_set(mrb, mrb_intern(mrb, "$mrbtest_io_msg"), mrb_nil_value());
return mrb_nil_value();
}
static mrb_value
mrb_io_test_file_setup(mrb_state *mrb, mrb_value self)
{
mrb_value ary = mrb_io_test_io_setup(mrb, self);
symlink("/usr/bin", "test-bin");
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();
}
void
mrb_mruby_io_gem_test(mrb_state* mrb)
{
struct RClass *io_test = mrb_define_module(mrb, "MRubyIOTestUtil");
mrb_define_class_method(mrb, io_test, "io_test_setup", mrb_io_test_io_setup, ARGS_NONE());
mrb_define_class_method(mrb, io_test, "io_test_cleanup", mrb_io_test_io_cleanup, ARGS_NONE());
mrb_define_class_method(mrb, io_test, "file_test_setup", mrb_io_test_file_setup, ARGS_NONE());
mrb_define_class_method(mrb, io_test, "file_test_cleanup", mrb_io_test_file_cleanup, ARGS_NONE());
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment