calog/vendor/mruby/mrbgems/mruby-io/src/file_test.c
2026-07-04 20:25:00 -05:00

402 lines
10 KiB
C
Vendored

/*
** file_test.c - FileTest class
*/
#include <mruby.h>
#include <mruby/class.h>
#include <mruby/data.h>
#include <mruby/string.h>
#include <mruby/io.h>
#include <mruby/error.h>
#include <mruby/internal.h>
#include "io_hal.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
extern struct mrb_data_type mrb_io_type;
/* Helper function to convert int64_t to mrb_value with overflow handling */
static mrb_value
mrb_int64_value(mrb_state *mrb, int64_t val)
{
if (sizeof(val) >= sizeof(mrb_int) && val > MRB_INT_MAX) {
#ifdef MRB_USE_BIGINT
return mrb_bint_new_int64(mrb, val);
#elif !defined(MRB_NO_FLOAT)
return mrb_float_value(mrb, (mrb_float)val);
#else
mrb_raise(mrb, E_RANGE_ERROR, "value too large for this platform");
#endif
}
return mrb_int_value(mrb, (mrb_int)val);
}
static int
mrb_stat0(mrb_state *mrb, mrb_value obj, mrb_io_stat *st, int do_lstat)
{
if (mrb_obj_is_kind_of(mrb, obj, mrb_class_get_id(mrb, MRB_SYM(IO)))) {
struct mrb_io *fptr;
fptr = (struct mrb_io*)mrb_data_get_ptr(mrb, obj, &mrb_io_type);
if (fptr && fptr->fd >= 0) {
return mrb_hal_io_fstat(mrb, fptr->fd, st);
}
mrb_raise(mrb, E_IO_ERROR, "closed stream");
return -1;
}
else {
char *path = mrb_locale_from_utf8(RSTRING_CSTR(mrb, obj), -1);
int ret;
if (do_lstat) {
ret = mrb_hal_io_lstat(mrb, path, st);
}
else {
ret = mrb_hal_io_stat(mrb, path, st);
}
mrb_locale_free(path);
return ret;
}
}
static int
mrb_stat(mrb_state *mrb, mrb_value obj, mrb_io_stat *st)
{
return mrb_stat0(mrb, obj, st, 0);
}
#if defined(S_ISLNK) || defined(_S_ISLNK) || defined(S_IFLNK) || defined(_S_IFLNK)
static int
mrb_lstat(mrb_state *mrb, mrb_value obj, mrb_io_stat *st)
{
return mrb_stat0(mrb, obj, st, 1);
}
#endif
/*
* call-seq:
* File.directory?(file_name) -> true or false
* FileTest.directory?(file_name) -> true or false
*
* Returns `true` if the named file is a directory, or a symlink that points at a directory, and `false`
* otherwise.
*
* File.directory?(".") #=> true
* FileTest.directory?(".") #=> true
*/
static 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
mrb_io_stat st;
mrb_value obj = mrb_get_arg1(mrb);
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
* FileTest.pipe?(file_name) -> true or false
*
* Returns `true` if the named file is a pipe.
*
* File.pipe?("/dev/stdin") #=> true
* FileTest.pipe?("/dev/stdin") #=> true
*/
static mrb_value
mrb_filetest_s_pipe_p(mrb_state *mrb, mrb_value klass)
{
#ifdef _WIN32
/* Windows anonymous pipes are not Unix FIFOs */
mrb_raise(mrb, E_NOTIMP_ERROR, "pipe? is not supported on Windows");
#else
#ifdef S_IFIFO
# ifndef S_ISFIFO
# define S_ISFIFO(m) (((m) & S_IFMT) == S_IFIFO)
# endif
mrb_io_stat st;
mrb_value obj = mrb_get_arg1(mrb);
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();
#endif
}
/*
* call-seq:
* File.symlink?(file_name) -> true or false
* FileTest.symlink?(file_name) -> true or false
*
* Returns `true` if the named file is a symbolic link.
*
* File.symlink?("link-to-test") #=> true
* FileTest.symlink?("link-to-test") #=> true
*/
static mrb_value
mrb_filetest_s_symlink_p(mrb_state *mrb, mrb_value klass)
{
#ifdef _WIN32
/* Symlinks not reliably supported on Windows */
mrb_raise(mrb, E_NOTIMP_ERROR, "symlink? is not supported on Windows");
#else
#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
mrb_io_stat st;
mrb_value obj = mrb_get_arg1(mrb);
if (mrb_lstat(mrb, obj, &st) == -1)
return mrb_false_value();
if (S_ISLNK(st.st_mode))
return mrb_true_value();
#endif
#endif
return mrb_false_value();
}
/*
* call-seq:
* File.socket?(file_name) -> true or false
* FileTest.socket?(file_name) -> true or false
*
* Returns `true` if the named file is a socket.
*
* File.socket?("/tmp/.X11-unix/X0") #=> true
* FileTest.socket?("/tmp/.X11-unix/X0") #=> true
*/
static mrb_value
mrb_filetest_s_socket_p(mrb_state *mrb, mrb_value klass)
{
#ifdef _WIN32
/* Unix domain sockets not supported on Windows */
mrb_raise(mrb, E_NOTIMP_ERROR, "socket? is not supported on Windows");
#else
#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
mrb_io_stat st;
mrb_value obj = mrb_get_arg1(mrb);
if (mrb_stat(mrb, obj, &st) < 0)
return mrb_false_value();
if (S_ISSOCK(st.st_mode))
return mrb_true_value();
#endif
#endif
return mrb_false_value();
}
/*
* call-seq:
* File.exist?(file_name) -> true or false
* File.exists?(file_name) -> true or false
* FileTest.exist?(file_name) -> true or false
* FileTest.exists?(file_name) -> true or false
*
* Returns `true` if the named file exists.
*
* File.exist?("config.h") #=> true
* File.exist?("no_such_file") #=> false
* FileTest.exist?("config.h") #=> true
* FileTest.exist?("no_such_file") #=> false
*/
static mrb_value
mrb_filetest_s_exist_p(mrb_state *mrb, mrb_value klass)
{
mrb_io_stat st;
mrb_value obj = mrb_get_arg1(mrb);
if (mrb_stat(mrb, obj, &st) < 0)
return mrb_false_value();
return mrb_true_value();
}
/*
* call-seq:
* File.file?(file_name) -> true or false
* FileTest.file?(file_name) -> true or false
*
* Returns `true` if the named file exists and is a regular file.
*
* File.file?("testfile") #=> true
* FileTest.file?("testfile") #=> true
*/
static 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
mrb_io_stat st;
mrb_value obj = mrb_get_arg1(mrb);
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
* FileTest.zero?(file_name) -> true or false
*
* Returns `true` if the named file exists and has a zero size.
*
* File.zero?("testfile") #=> false
* FileTest.zero?("testfile") #=> false
*/
static mrb_value
mrb_filetest_s_zero_p(mrb_state *mrb, mrb_value klass)
{
mrb_io_stat st;
mrb_value obj = mrb_get_arg1(mrb);
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
* FileTest.size(file_name) -> integer
*
* Returns the size of `file_name`.
*
* `file_name` can be an IO object.
*
* File.size("testfile") #=> 66
* FileTest.size("testfile") #=> 66
*/
static mrb_value
mrb_filetest_s_size(mrb_state *mrb, mrb_value klass)
{
mrb_io_stat st;
mrb_value obj = mrb_get_arg1(mrb);
if (mrb_stat(mrb, obj, &st) < 0)
mrb_sys_fail(mrb, "mrb_stat");
return mrb_int64_value(mrb, st.st_size);
}
/*
* call-seq:
* File.size?(file_name) -> Integer or nil
* FileTest.size?(file_name) -> Integer or nil
*
* Returns `nil` if `file_name` doesn't exist or has zero size, the size of the
* file otherwise.
*
* File.size?("testfile") #=> 66
* FileTest.size?("testfile") #=> 66
*/
static mrb_value
mrb_filetest_s_size_p(mrb_state *mrb, mrb_value klass)
{
mrb_io_stat st;
mrb_value obj = mrb_get_arg1(mrb);
if (mrb_stat(mrb, obj, &st) < 0)
return mrb_nil_value();
if (st.st_size == 0)
return mrb_nil_value();
return mrb_int64_value(mrb, st.st_size);
}
void
mrb_init_file_test(mrb_state *mrb)
{
struct RClass *f;
f = mrb_define_module_id(mrb, MRB_SYM(FileTest));
mrb_define_class_method_id(mrb, f, MRB_SYM_Q(directory), mrb_filetest_s_directory_p, MRB_ARGS_REQ(1));
mrb_define_class_method_id(mrb, f, MRB_SYM_Q(exist), mrb_filetest_s_exist_p, MRB_ARGS_REQ(1));
mrb_define_class_method_id(mrb, f, MRB_SYM_Q(exists), mrb_filetest_s_exist_p, MRB_ARGS_REQ(1));
mrb_define_class_method_id(mrb, f, MRB_SYM_Q(file), mrb_filetest_s_file_p, MRB_ARGS_REQ(1));
mrb_define_class_method_id(mrb, f, MRB_SYM_Q(pipe), mrb_filetest_s_pipe_p, MRB_ARGS_REQ(1));
mrb_define_class_method_id(mrb, f, MRB_SYM(size), mrb_filetest_s_size, MRB_ARGS_REQ(1));
mrb_define_class_method_id(mrb, f, MRB_SYM_Q(size), mrb_filetest_s_size_p, MRB_ARGS_REQ(1));
mrb_define_class_method_id(mrb, f, MRB_SYM_Q(socket), mrb_filetest_s_socket_p, MRB_ARGS_REQ(1));
mrb_define_class_method_id(mrb, f, MRB_SYM_Q(symlink), mrb_filetest_s_symlink_p, MRB_ARGS_REQ(1));
mrb_define_class_method_id(mrb, f, MRB_SYM_Q(zero), mrb_filetest_s_zero_p, MRB_ARGS_REQ(1));
// Also register the same methods on File class
struct RClass *file = mrb_class_get_id(mrb, MRB_SYM(File));
mrb_define_class_method_id(mrb, file, MRB_SYM_Q(directory), mrb_filetest_s_directory_p, MRB_ARGS_REQ(1));
mrb_define_class_method_id(mrb, file, MRB_SYM_Q(exist), mrb_filetest_s_exist_p, MRB_ARGS_REQ(1));
mrb_define_class_method_id(mrb, file, MRB_SYM_Q(exists), mrb_filetest_s_exist_p, MRB_ARGS_REQ(1));
mrb_define_class_method_id(mrb, file, MRB_SYM_Q(file), mrb_filetest_s_file_p, MRB_ARGS_REQ(1));
mrb_define_class_method_id(mrb, file, MRB_SYM_Q(pipe), mrb_filetest_s_pipe_p, MRB_ARGS_REQ(1));
mrb_define_class_method_id(mrb, file, MRB_SYM(size), mrb_filetest_s_size, MRB_ARGS_REQ(1));
mrb_define_class_method_id(mrb, file, MRB_SYM_Q(size), mrb_filetest_s_size_p, MRB_ARGS_REQ(1));
mrb_define_class_method_id(mrb, file, MRB_SYM_Q(socket), mrb_filetest_s_socket_p, MRB_ARGS_REQ(1));
mrb_define_class_method_id(mrb, file, MRB_SYM_Q(symlink), mrb_filetest_s_symlink_p, MRB_ARGS_REQ(1));
mrb_define_class_method_id(mrb, file, MRB_SYM_Q(zero), mrb_filetest_s_zero_p, MRB_ARGS_REQ(1));
}