14#include "ruby/internal/config.h"
18# include "missing/file.h"
30# if !(defined(__has_feature) && defined(__has_attribute))
35# define API_AVAILABLE(...)
36# define API_DEPRECATED(...)
38# include <CoreFoundation/CFString.h>
55#ifdef HAVE_SYS_PARAM_H
56# include <sys/param.h>
59# define MAXPATHLEN 1024
64#elif defined HAVE_SYS_UTIME_H
65# include <sys/utime.h>
72#ifdef HAVE_SYS_SYSMACROS_H
73# include <sys/sysmacros.h>
79#ifdef HAVE_SYS_MKDEV_H
80# include <sys/mkdev.h>
83#if defined(HAVE_FCNTL_H)
87#if defined(HAVE_SYS_TIME_H)
91#if !defined HAVE_LSTAT && !defined lstat
97# include "win32/file.h"
99# define chmod(p, m) rb_w32_uchmod((p), (m))
101# define chown(p, o, g) rb_w32_uchown((p), (o), (g))
103# define lchown(p, o, g) rb_w32_ulchown((p), (o), (g))
105# define link(f, t) rb_w32_ulink((f), (t))
107# define readlink(f, t, l) rb_w32_ureadlink((f), (t), (l))
109# define symlink(s, l) rb_w32_usymlink((s), (l))
118#ifdef HAVE_STRUCT_STATX_STX_BTIME
119# define ST_(name) stx_ ## name
122# define ST_(name) st_ ## name
126#if defined _WIN32 || defined __APPLE__
128# define TO_OSPATH(str) rb_str_encode_ospath(str)
131# define TO_OSPATH(str) (str)
135#if defined DOSISH || defined __CYGWIN__
140#if defined HAVE_REALPATH && defined __sun && defined __SVR4
153#include "internal/compilers.h"
154#include "internal/dir.h"
155#include "internal/encoding.h"
156#include "internal/error.h"
157#include "internal/file.h"
158#include "internal/io.h"
159#include "internal/load.h"
160#include "internal/object.h"
161#include "internal/process.h"
162#include "internal/thread.h"
163#include "internal/vm.h"
168#define UIANY2NUM(x) \
169 ((sizeof(x) <= sizeof(unsigned int)) ? \
170 UINT2NUM((unsigned)(x)) : \
171 (sizeof(x) <= sizeof(unsigned long)) ? \
172 ULONG2NUM((unsigned long)(x)) : \
173 ULL2NUM((unsigned LONG_LONG)(x)))
180file_path_convert(
VALUE name)
185 if (ENCINDEX_US_ASCII != fname_encidx &&
186 ENCINDEX_ASCII_8BIT != fname_encidx &&
187 (fs_encidx = rb_filesystem_encindex()) != fname_encidx &&
188 rb_default_internal_encoding() &&
192 rb_encoding *fname_encoding = rb_enc_from_index(fname_encidx);
193 rb_encoding *fs_encoding = rb_enc_from_index(fs_encidx);
201check_path_encoding(
VALUE str)
203 if (RB_UNLIKELY(!rb_str_enc_fastpath(str))) {
205 if (!rb_enc_asciicompat(enc)) {
213rb_get_path_check_to_string(
VALUE obj)
222 tmp = rb_check_funcall_default(obj, to_path, 0, 0, obj);
228rb_get_path_check_convert(
VALUE obj)
230 obj = file_path_convert(obj);
231 rb_get_path_check_no_convert(obj);
237rb_get_path_check_no_convert(
VALUE obj)
239 check_path_encoding(obj);
240 if (!rb_str_to_cstr(obj)) {
241 rb_raise(rb_eArgError,
"path name contains null byte");
250 return rb_get_path(obj);
256 return rb_get_path_check_convert(rb_get_path_check_to_string(obj));
260check_path(
VALUE obj,
const char **cstr)
262 VALUE str = rb_get_path_check_convert(rb_get_path_check_to_string(obj));
266 *cstr = RSTRING_PTR(str);
270#define CheckPath(str, cstr) RB_GC_GUARD(str) = check_path(str, &cstr);
277 if (encidx != ENCINDEX_ASCII_8BIT && encidx != ENCINDEX_UTF_8) {
287# define NORMALIZE_UTF8PATH 1
289# ifdef HAVE_WORKING_FORK
290static CFMutableStringRef
291mutable_CFString_new(CFStringRef *s,
const char *ptr,
rb_len_t len)
293 const CFAllocatorRef alloc = kCFAllocatorDefault;
294 *s = CFStringCreateWithBytesNoCopy(alloc, (
const UInt8 *)ptr,
len,
295 kCFStringEncodingUTF8, FALSE,
297 return CFStringCreateMutableCopy(alloc,
len, *s);
300# define mutable_CFString_release(m, s) (CFRelease(m), CFRelease(s))
303rb_CFString_class_initialize_before_fork(
void)
326 const char small_str[] =
"/";
335 for (
int i = 0; i < 2; i++) {
336 CFMutableStringRef m = mutable_CFString_new(&s, small_str,
len);
337 mutable_CFString_release(m, s);
348 CFMutableStringRef m = mutable_CFString_new(&s, ptr,
len);
351 CFStringNormalize(m, kCFStringNormalizationFormC);
352 all = CFRangeMake(0, CFStringGetLength(m));
353 CFStringGetBytes(m, all, kCFStringEncodingUTF8,
'?', FALSE, NULL, 0, &buflen);
355 CFStringGetBytes(m, all, kCFStringEncodingUTF8,
'?', FALSE,
356 (UInt8 *)(RSTRING_PTR(str) + oldlen), buflen, &buflen);
358 mutable_CFString_release(m, s);
363rb_str_normalize_ospath(
const char *ptr,
rb_len_t len)
366 const char *e = ptr +
len;
375 rb_enc_associate(str, enc);
380 int r = rb_enc_precise_mbclen(p, e, enc);
384 rb_str_append_normalized_ospath(str, p1, p-p1);
391 c = rb_enc_mbc_to_codepoint(p, e, enc);
392 if ((0x2000 <= c && c <= 0x2FFF) || (0xF900 <= c && c <= 0xFAFF) ||
393 (0x2F800 <= c && c <= 0x2FAFF)) {
395 rb_str_append_normalized_ospath(str, p1, p-p1);
406 rb_str_append_normalized_ospath(str, p1, p-p1);
413ignored_char_p(
const char *p,
const char *e,
rb_encoding *enc)
416 if (p+3 > e)
return 0;
417 switch ((
unsigned char)*p) {
419 switch ((
unsigned char)p[1]) {
421 c = (
unsigned char)p[2];
423 if (c >= 0x8c && c <= 0x8f)
return 3;
425 if (c >= 0xaa && c <= 0xae)
return 3;
428 c = (
unsigned char)p[2];
430 if (c >= 0xaa && c <= 0xaf)
return 3;
436 if ((
unsigned char)p[1] == 0xbb &&
437 (
unsigned char)p[2] == 0xbf)
444# define NORMALIZE_UTF8PATH 0
447#define apply2args(n) (rb_check_arity(argc, n, UNLIMITED_ARGUMENTS), argc-=n)
458 int (*func)(
const char *,
void *);
464no_gvl_apply2files(
void *ptr)
468 for (aa->i = 0; aa->i < aa->argc; aa->i++) {
469 if (aa->func(aa->fn[aa->i].ptr, aa->arg) < 0) {
478NORETURN(
static void utime_failed(
struct apply_arg *));
479static int utime_internal(
const char *,
void *);
483apply2files(
int (*func)(
const char *,
void *),
int argc,
VALUE *argv,
void *arg)
495 for (aa->i = 0; aa->i < argc; aa->i++) {
496 VALUE path = rb_get_path(argv[aa->i]);
498 path = rb_str_encode_ospath(path);
499 aa->fn[aa->i].ptr = RSTRING_PTR(path);
500 aa->fn[aa->i].path = path;
503 IO_WITHOUT_GVL(no_gvl_apply2files, aa);
506 if (func == utime_internal) {
510 rb_syserr_fail_path(aa->errnum, aa->fn[aa->i].path);
529 0, 0, RUBY_TYPED_THREAD_SAFE_FREE | RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_EMBEDDABLE
533 rb_io_stat_data stat;
546rb_stat_new(
const struct stat *st)
552# define CP(m) .stx_ ## m = st->st_ ## m
553# define CP_32(m) .stx_ ## m = (uint32_t)st->st_ ## m
554# define CP_TS(m) .stx_ ## m = stat_ ## m ## spec(st)
555 rb_st->stat = (
struct statx){
556 .stx_mask = STATX_BASIC_STATS,
573 rb_st->initialized =
true;
581rb_statx_new(
const rb_io_stat_data *st)
587 rb_st->initialized =
true;
593static rb_io_stat_data*
598 if (!rb_st->initialized) rb_raise(
rb_eTypeError,
"uninitialized File::Stat");
604statx_mtimespec(
const rb_io_stat_data *st)
606 return st->stx_mtime;
609# define statx_mtimespec stat_mtimespec
652 if (ts1.tv_sec == ts2.tv_sec) {
653 if (ts1.tv_nsec == ts2.tv_nsec)
return INT2FIX(0);
654 if (ts1.tv_nsec < ts2.tv_nsec)
return INT2FIX(-1);
657 if (ts1.tv_sec < ts2.tv_sec)
return INT2FIX(-1);
663#define ST2UINT(val) ((val) & ~(~1UL << (sizeof(val) * CHAR_BIT - 1)))
666# define NUM2DEVT(v) NUM2UINT(v)
669# define DEVT2NUM(v) UINT2NUM(v)
671#ifndef PRI_DEVT_PREFIX
672# define PRI_DEVT_PREFIX ""
691rb_stat_dev(
VALUE self)
694 unsigned int m = get_stat(self)->stx_dev_major;
695 unsigned int n = get_stat(self)->stx_dev_minor;
697#elif SIZEOF_STRUCT_STAT_ST_DEV <= SIZEOF_DEV_T
698 return DEVT2NUM(get_stat(self)->st_dev);
699#elif SIZEOF_STRUCT_STAT_ST_DEV <= SIZEOF_LONG
700 return ULONG2NUM(get_stat(self)->st_dev);
702 return ULL2NUM(get_stat(self)->st_dev);
725rb_stat_dev_major(
VALUE self)
728 return UINT2NUM(get_stat(self)->stx_dev_major);
730 return UINT2NUM(major(get_stat(self)->st_dev));
755rb_stat_dev_minor(
VALUE self)
758 return UINT2NUM(get_stat(self)->stx_dev_minor);
760 return UINT2NUM(minor(get_stat(self)->st_dev));
783rb_stat_ino(
VALUE self)
785 rb_io_stat_data *ptr = get_stat(self);
786#ifdef HAVE_STRUCT_STAT_ST_INOHIGH
788 return rb_integer_unpack(&ptr->st_ino, 2,
789 SIZEOF_STRUCT_STAT_ST_INO, 0,
793 return UIANY2NUM(ptr->ST_(ino));
817rb_stat_mode(
VALUE self)
819 return UINT2NUM(ST2UINT(get_stat(self)->ST_(mode)));
851rb_stat_nlink(
VALUE self)
854 const rb_io_stat_data *ptr = get_stat(self);
856 return UIANY2NUM(ptr->ST_(nlink));
884rb_stat_uid(
VALUE self)
886 return UIDT2NUM(get_stat(self)->ST_(uid));
911rb_stat_gid(
VALUE self)
913 return GIDT2NUM(get_stat(self)->ST_(gid));
963rb_stat_rdev(
VALUE self)
966 unsigned int m = get_stat(self)->stx_rdev_major;
967 unsigned int n = get_stat(self)->stx_rdev_minor;
969#elif !defined(HAVE_STRUCT_STAT_ST_RDEV)
971#elif SIZEOF_STRUCT_STAT_ST_RDEV <= SIZEOF_DEV_T
972 return DEVT2NUM(get_stat(self)->ST_(rdev));
973#elif SIZEOF_STRUCT_STAT_ST_RDEV <= SIZEOF_LONG
974 return ULONG2NUM(get_stat(self)->ST_(rdev));
976 return ULL2NUM(get_stat(self)->ST_(rdev));
1013rb_stat_rdev_major(
VALUE self)
1016 return UINT2NUM(get_stat(self)->stx_rdev_major);
1017#elif defined(HAVE_STRUCT_STAT_ST_RDEV) && defined(major)
1018 return UINT2NUM(major(get_stat(self)->ST_(rdev)));
1057rb_stat_rdev_minor(
VALUE self)
1060 return UINT2NUM(get_stat(self)->stx_rdev_minor);
1061#elif defined(HAVE_STRUCT_STAT_ST_RDEV) && defined(minor)
1062 return UINT2NUM(minor(get_stat(self)->ST_(rdev)));
1098rb_stat_size(
VALUE self)
1100 return OFFT2NUM(get_stat(self)->ST_(size));
1122rb_stat_blksize(
VALUE self)
1124#ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
1125 return ULONG2NUM(get_stat(self)->ST_(blksize));
1149rb_stat_blocks(
VALUE self)
1151#ifdef HAVE_STRUCT_STAT_ST_BLOCKS
1152# if SIZEOF_STRUCT_STAT_ST_BLOCKS > SIZEOF_LONG
1153 return ULL2NUM(get_stat(self)->ST_(blocks));
1155 return ULONG2NUM(get_stat(self)->ST_(blocks));
1163stat_atimespec(
const struct stat *st)
1166 ts.tv_sec = st->st_atime;
1167#if defined(HAVE_STRUCT_STAT_ST_ATIM)
1168 ts.tv_nsec = (uint32_t)st->st_atim.tv_nsec;
1169#elif defined(HAVE_STRUCT_STAT_ST_ATIMESPEC)
1170 ts.tv_nsec = (uint32_t)st->st_atimespec.tv_nsec;
1171#elif defined(HAVE_STRUCT_STAT_ST_ATIMENSEC)
1172 ts.tv_nsec = (uint32_t)st->st_atimensec;
1181statx_atimespec(
const rb_io_stat_data *st)
1183 return st->stx_atime;
1186# define statx_atimespec stat_atimespec
1196stat_atime(
const struct stat *st)
1198 return stat_time(stat_atimespec(st));
1202stat_mtimespec(
const struct stat *st)
1205 ts.tv_sec = st->st_mtime;
1206#if defined(HAVE_STRUCT_STAT_ST_MTIM)
1207 ts.tv_nsec = (uint32_t)st->st_mtim.tv_nsec;
1208#elif defined(HAVE_STRUCT_STAT_ST_MTIMESPEC)
1209 ts.tv_nsec = (uint32_t)st->st_mtimespec.tv_nsec;
1210#elif defined(HAVE_STRUCT_STAT_ST_MTIMENSEC)
1211 ts.tv_nsec = (uint32_t)st->st_mtimensec;
1219stat_mtime(
const struct stat *st)
1221 return stat_time(stat_mtimespec(st));
1225stat_ctimespec(
const struct stat *st)
1228 ts.tv_sec = st->st_ctime;
1229#if defined(HAVE_STRUCT_STAT_ST_CTIM)
1230 ts.tv_nsec = (uint32_t)st->st_ctim.tv_nsec;
1231#elif defined(HAVE_STRUCT_STAT_ST_CTIMESPEC)
1232 ts.tv_nsec = (uint32_t)st->st_ctimespec.tv_nsec;
1233#elif defined(HAVE_STRUCT_STAT_ST_CTIMENSEC)
1234 ts.tv_nsec = (uint32_t)st->st_ctimensec;
1243statx_ctimespec(
const rb_io_stat_data *st)
1245 return st->stx_ctime;
1248# define statx_ctimespec stat_ctimespec
1252stat_ctime(
const struct stat *st)
1254 return stat_time(stat_ctimespec(st));
1257#define HAVE_STAT_BIRTHTIME
1258#if defined(HAVE_STRUCT_STAT_ST_BIRTHTIMESPEC)
1260statx_birthtime(
const rb_io_stat_data *st)
1265#elif defined(HAVE_STRUCT_STATX_STX_BTIME)
1266static VALUE statx_birthtime(
const rb_io_stat_data *st);
1267#elif defined(_WIN32)
1268# define statx_birthtime stat_ctime
1270# undef HAVE_STAT_BIRTHTIME
1319rb_stat_atime(
VALUE self)
1321 return stat_time(statx_atimespec(get_stat(self)));
1351rb_stat_mtime(
VALUE self)
1353 return stat_time(statx_mtimespec(get_stat(self)));
1390rb_stat_ctime(
VALUE self)
1392 return stat_time(statx_ctimespec(get_stat(self)));
1395#if defined(HAVE_STAT_BIRTHTIME)
1418rb_stat_birthtime(
VALUE self)
1420 return statx_birthtime(get_stat(self));
1423# define rb_stat_birthtime rb_f_notimplement
1483rb_stat_inspect(
VALUE self)
1487 static const struct {
1491 {
"dev", rb_stat_dev},
1492 {
"ino", rb_stat_ino},
1493 {
"mode", rb_stat_mode},
1494 {
"nlink", rb_stat_nlink},
1495 {
"uid", rb_stat_uid},
1496 {
"gid", rb_stat_gid},
1497 {
"rdev", rb_stat_rdev},
1498 {
"size", rb_stat_size},
1499 {
"blksize", rb_stat_blksize},
1500 {
"blocks", rb_stat_blocks},
1501 {
"atime", rb_stat_atime},
1502 {
"mtime", rb_stat_mtime},
1503 {
"ctime", rb_stat_ctime},
1504#if defined(HAVE_STRUCT_STAT_ST_BIRTHTIMESPEC)
1505 {
"birthtime", rb_stat_birthtime},
1511 if (!rb_st->initialized) {
1519 for (i = 0; i <
sizeof(member)/
sizeof(member[0]); i++) {
1527 v = (*member[i].func)(self);
1529 rb_str_catf(str,
"0%lo", (
unsigned long)
NUM2ULONG(v));
1531 else if (i == 0 || i == 6) {
1532 rb_str_catf(str,
"0x%"PRI_DEVT_PREFIX
"x", NUM2DEVT(v));
1552no_gvl_fstat(
void *data)
1555 return (
VALUE)fstat(arg->file.fd, arg->st);
1559fstat_without_gvl(
rb_io_t *fptr,
struct stat *st)
1563 data.file.fd = fptr->
fd;
1566 return (
int)rb_io_blocking_region(fptr, no_gvl_fstat, &data);
1570no_gvl_stat(
void * data)
1573 return (
void *)(
VALUE)stat(arg->file.path, arg->st);
1577stat_without_gvl(
const char *path,
struct stat *st)
1581 data.file.path = path;
1584 return IO_WITHOUT_GVL_INT(no_gvl_stat, &data);
1587#if !defined(HAVE_STRUCT_STAT_ST_BIRTHTIMESPEC) && \
1588 defined(HAVE_STRUCT_STATX_STX_BTIME)
1590# define STATX(path, st, mask) statx(AT_FDCWD, path, 0, mask, st)
1593# ifdef HAVE_SYSCALL_H
1594# include <syscall.h>
1595# elif defined HAVE_SYS_SYSCALL_H
1596# include <sys/syscall.h>
1598# if defined __linux__
1599# include <linux/stat.h>
1601statx(
int dirfd,
const char *pathname,
int flags,
1602 unsigned int mask,
struct statx *statxbuf)
1604 return (
int)syscall(__NR_statx, dirfd, pathname, flags, mask, statxbuf);
1609typedef struct no_gvl_rb_io_stat_data {
1615} no_gvl_rb_io_stat_data;
1618io_blocking_statx(
void *data)
1620 no_gvl_rb_io_stat_data *arg = data;
1621 return (
VALUE)statx(arg->fd, arg->path, arg->flags, arg->mask, arg->stx);
1625no_gvl_statx(
void *data)
1627 return (
void *)io_blocking_statx(data);
1631statx_without_gvl(
const char *path, rb_io_stat_data *stx,
unsigned int mask)
1633 no_gvl_rb_io_stat_data data = {stx, AT_FDCWD, path, 0, mask};
1636 return IO_WITHOUT_GVL_INT(no_gvl_statx, &data);
1640lstatx_without_gvl(
const char *path, rb_io_stat_data *stx,
unsigned int mask)
1642 no_gvl_rb_io_stat_data data = {stx, AT_FDCWD, path, AT_SYMLINK_NOFOLLOW, mask};
1645 return IO_WITHOUT_GVL_INT(no_gvl_statx, &data);
1649fstatx_without_gvl(
rb_io_t *fptr, rb_io_stat_data *stx,
unsigned int mask)
1651 no_gvl_rb_io_stat_data data = {stx, fptr->
fd,
"", AT_EMPTY_PATH, mask};
1654 return (
int)rb_io_blocking_region(fptr, io_blocking_statx, &data);
1657#define FSTATX(fd, st) statx(fd, "", AT_EMPTY_PATH, STATX_ALL, st)
1660rb_statx(
VALUE file,
struct statx *stx,
unsigned int mask)
1665 tmp = rb_check_convert_type_with_id(file,
T_FILE,
"IO", idTo_io);
1670 result = fstatx_without_gvl(fptr, stx, mask);
1675 file = rb_str_encode_ospath(file);
1676 result = statx_without_gvl(RSTRING_PTR(file), stx, mask);
1682# define statx_has_birthtime(st) ((st)->stx_mask & STATX_BTIME)
1684NORETURN(
static void statx_notimplement(
const char *field_name));
1689statx_notimplement(
const char *field_name)
1692 "%s is unimplemented on this filesystem",
1697statx_birthtime(
const rb_io_stat_data *stx)
1699 if (!statx_has_birthtime(stx)) {
1701 statx_notimplement(
"birthtime");
1703 return rb_time_nano_new((time_t)stx->stx_btime.tv_sec, stx->stx_btime.tv_nsec);
1708# define statx_without_gvl(path, st, mask) stat_without_gvl(path, st)
1709# define fstatx_without_gvl(fptr, st, mask) fstat_without_gvl(fptr, st)
1710# define lstatx_without_gvl(path, st, mask) lstat_without_gvl(path, st)
1711# define rb_statx(file, stx, mask) rb_stat(file, stx)
1712# define STATX(path, st, mask) stat(path, st)
1714#if defined(HAVE_STAT_BIRTHTIME)
1715# define statx_has_birthtime(st) 1
1717# define statx_has_birthtime(st) 0
1724# define FSTAT(fd, st) fstat(fd, st)
1733 tmp = rb_check_convert_type_with_id(file,
T_FILE,
"IO", idTo_io);
1738 result = fstat_without_gvl(fptr, st);
1743 file = rb_str_encode_ospath(file);
1744 result = stat_without_gvl(RSTRING_PTR(file), st);
1782 fname = rb_str_encode_ospath(fname);
1783 if (statx_without_gvl(RSTRING_PTR(fname), &st, STATX_ALL) < 0) {
1784 rb_sys_fail_path(fname);
1786 return rb_statx_new(&st);
1818rb_io_stat(
VALUE obj)
1824 if (fstatx_without_gvl(fptr, &st, STATX_ALL) == -1) {
1825 rb_sys_fail_path(fptr->
pathv);
1827 return rb_statx_new(&st);
1832no_gvl_lstat(
void *ptr)
1835 return (
void *)(
VALUE)lstat(arg->file.path, arg->st);
1839lstat_without_gvl(
const char *path,
struct stat *st)
1843 data.file.path = path;
1846 return IO_WITHOUT_GVL_INT(no_gvl_lstat, &data);
1883 fname = rb_str_encode_ospath(fname);
1884 if (lstatx_without_gvl(
StringValueCStr(fname), &st, STATX_ALL) == -1) {
1885 rb_sys_fail_path(fname);
1887 return rb_statx_new(&st);
1889 return rb_file_s_stat(klass, fname);
1922rb_file_lstat(
VALUE obj)
1931 path = rb_str_encode_ospath(fptr->
pathv);
1932 if (lstatx_without_gvl(RSTRING_PTR(path), &st, STATX_ALL) == -1) {
1933 rb_sys_fail_path(fptr->
pathv);
1935 return rb_statx_new(&st);
1937 return rb_io_stat(obj);
1942rb_group_member(GETGROUPS_T gid)
1944#if !defined(HAVE_GETGROUPS)
1953 if (getgid() == gid || getegid() == gid)
1956 groups = getgroups(0, NULL);
1957 gary =
ALLOCV_N(GETGROUPS_T, v, groups);
1958 anum = getgroups(groups, gary);
1959 while (--anum >= 0) {
1960 if (gary[anum] == gid) {
1973# define S_IXUGO (S_IXUSR | S_IXGRP | S_IXOTH)
1976#if defined(S_IXGRP) && !defined(_WIN32) && !defined(__CYGWIN__)
1977#define USE_GETEUID 1
1982eaccess(
const char *path,
int mode)
1991 if (getuid() == euid && getgid() == getegid())
1992 return access(path, mode);
1994 if (stat(path, &st) < 0)
2004 if (st.st_mode & S_IXUGO)
2010 if (st.st_uid == euid)
2012 else if (rb_group_member(st.st_gid))
2015 if ((
int)(st.st_mode & mode) == mode)
return 0;
2019 return access(path, mode);
2030nogvl_eaccess(
void *ptr)
2034 return (
void *)(
VALUE)eaccess(aa->path, aa->mode);
2038rb_eaccess(
VALUE fname,
int mode)
2043 fname = rb_str_encode_ospath(fname);
2047 return IO_WITHOUT_GVL_INT(nogvl_eaccess, &aa);
2051nogvl_access(
void *ptr)
2055 return (
void *)(
VALUE)access(aa->path, aa->mode);
2059rb_access(
VALUE fname,
int mode)
2064 fname = rb_str_encode_ospath(fname);
2068 return IO_WITHOUT_GVL_INT(nogvl_access, &aa);
2103# define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
2109 if (S_ISDIR(st.st_mode))
return Qtrue;
2138# define S_ISFIFO(m) (((m) & S_IFMT) == S_IFIFO)
2144 if (S_ISFIFO(st.st_mode))
return Qtrue;
2176# define S_ISLNK(m) _S_ISLNK(m)
2179# define S_ISLNK(m) (((m) & S_IFMT) == _S_IFLNK)
2182# define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK)
2192 fname = rb_str_encode_ospath(fname);
2194 if (S_ISLNK(st.st_mode))
return Qtrue;
2230# define S_ISSOCK(m) _S_ISSOCK(m)
2233# define S_ISSOCK(m) (((m) & S_IFMT) == _S_IFSOCK)
2236# define S_ISSOCK(m) (((m) & S_IFMT) == S_IFSOCK)
2246 if (S_ISSOCK(st.st_mode))
return Qtrue;
2278# define S_ISBLK(m) (((m) & S_IFMT) == S_IFBLK)
2280# define S_ISBLK(m) (0)
2288 if (S_ISBLK(st.st_mode))
return Qtrue;
2315# define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR)
2321 if (S_ISCHR(st.st_mode))
return Qtrue;
2391 return RBOOL(rb_eaccess(fname, R_OK) >= 0);
2405rb_file_readable_real_p(
VALUE obj,
VALUE fname)
2407 return RBOOL(rb_access(fname, R_OK) >= 0);
2411# define S_IRUGO (S_IRUSR | S_IRGRP | S_IROTH)
2415# define S_IWUGO (S_IWUSR | S_IWGRP | S_IWOTH)
2444rb_file_world_readable_p(
VALUE obj,
VALUE fname)
2450 if ((st.st_mode & (S_IROTH)) == S_IROTH) {
2451 return UINT2NUM(st.st_mode & (S_IRUGO|S_IWUGO|S_IXUGO));
2485 return RBOOL(rb_eaccess(fname, W_OK) >= 0);
2502rb_file_writable_real_p(
VALUE obj,
VALUE fname)
2504 return RBOOL(rb_access(fname, W_OK) >= 0);
2533rb_file_world_writable_p(
VALUE obj,
VALUE fname)
2539 if ((st.st_mode & (S_IWOTH)) == S_IWOTH) {
2540 return UINT2NUM(st.st_mode & (S_IRUGO|S_IWUGO|S_IXUGO));
2570 return RBOOL(rb_eaccess(fname, X_OK) >= 0);
2584rb_file_executable_real_p(
VALUE obj,
VALUE fname)
2586 return RBOOL(rb_access(fname, X_OK) >= 0);
2590# define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
2618 return RBOOL(S_ISREG(st.st_mode));
2678 return RBOOL(st.st_size == 0);
2716 if (st.st_size == 0)
return Qnil;
2751 return RBOOL(st.st_uid == geteuid());
2760 return RBOOL(st.st_uid == getuid());
2796 if (rb_group_member(st.st_gid))
return Qtrue;
2801#if defined(S_ISUID) || defined(S_ISGID) || defined(S_ISVTX)
2803check3rdbyte(
VALUE fname,
int mode)
2808 return RBOOL(st.st_mode & mode);
2841 return check3rdbyte(fname, S_ISUID);
2876 return check3rdbyte(fname, S_ISGID);
2912 return check3rdbyte(fname, S_ISVTX);
2949 struct stat st1, st2;
2953 if (st1.st_dev != st2.st_dev)
return Qfalse;
2954 if (st1.st_ino != st2.st_ino)
return Qfalse;
2958 return rb_w32_file_identical_p(fname1, fname2);
2984 if (
rb_stat(fname, &st) < 0) {
2987 rb_syserr_fail_path(e, fname);
2993rb_file_ftype(mode_t mode)
2997 if (S_ISREG(mode)) {
3000 else if (S_ISDIR(mode)) {
3003 else if (S_ISCHR(mode)) {
3004 t =
"characterSpecial";
3007 else if (S_ISBLK(mode)) {
3012 else if (S_ISFIFO(mode)) {
3017 else if (S_ISLNK(mode)) {
3022 else if (S_ISSOCK(mode)) {
3030 return rb_fstring_cstr(t);
3079 fname = rb_str_encode_ospath(fname);
3081 rb_sys_fail_path(fname);
3084 return rb_file_ftype(st.st_mode);
3140 if (
rb_stat(fname, &st) < 0) {
3143 rb_syserr_fail_path(e, fname);
3145 return stat_time(stat_atimespec(&st));
3177rb_file_atime(
VALUE obj)
3183 if (fstat(fptr->
fd, &st) == -1) {
3184 rb_sys_fail_path(fptr->
pathv);
3186 return stat_time(stat_atimespec(&st));
3226 if (
rb_stat(fname, &st) < 0) {
3229 rb_syserr_fail_path(e, fname);
3231 return stat_time(stat_mtimespec(&st));
3259rb_file_mtime(
VALUE obj)
3265 if (fstat(fptr->
fd, &st) == -1) {
3266 rb_sys_fail_path(fptr->
pathv);
3268 return stat_time(stat_mtimespec(&st));
3312 if (
rb_stat(fname, &st) < 0) {
3315 rb_syserr_fail_path(e, fname);
3317 return stat_time(stat_ctimespec(&st));
3354rb_file_ctime(
VALUE obj)
3360 if (fstat(fptr->
fd, &st) == -1) {
3361 rb_sys_fail_path(fptr->
pathv);
3363 return stat_time(stat_ctimespec(&st));
3366#if defined(HAVE_STAT_BIRTHTIME)
3392 if (rb_statx(fname, &st, STATX_BTIME) < 0) {
3395 rb_syserr_fail_path(e, fname);
3397 return statx_birthtime(&st);
3400# define rb_file_s_birthtime rb_f_notimplement
3403#if defined(HAVE_STAT_BIRTHTIME)
3424rb_file_birthtime(
VALUE obj)
3430 if (fstatx_without_gvl(fptr, &st, STATX_BTIME) == -1) {
3431 rb_sys_fail_path(fptr->
pathv);
3433 return statx_birthtime(&st);
3436# define rb_file_birthtime rb_f_notimplement
3448 rb_io_flush_raw(file, 0);
3451 if (fstat(fptr->
fd, &st) == -1) {
3452 rb_sys_fail_path(fptr->
pathv);
3478file_size(
VALUE self)
3480 return OFFT2NUM(rb_file_size(self));
3489nogvl_chmod(
void *ptr)
3492 int ret = chmod(data->path, data->mode);
3493 return (
void *)(
VALUE)ret;
3497rb_chmod(
const char *path, mode_t mode)
3503 return IO_WITHOUT_GVL_INT(nogvl_chmod, &data);
3507chmod_internal(
const char *path,
void *mode)
3509 return chmod(path, *(mode_t *)mode);
3542 return apply2files(chmod_internal, argc, argv, &mode);
3546struct nogvl_fchmod_data {
3552io_blocking_fchmod(
void *ptr)
3554 struct nogvl_fchmod_data *data = ptr;
3555 int ret = fchmod(data->fd, data->mode);
3560rb_fchmod(
struct rb_io* io, mode_t mode)
3563 struct nogvl_fchmod_data data = {.fd = io->
fd, .mode = mode};
3564 return (
int)rb_thread_io_blocking_region(io, io_blocking_fchmod, &data);
3593#if !defined HAVE_FCHMOD || !HAVE_FCHMOD
3601 if (rb_fchmod(fptr, mode) == -1) {
3602 if (HAVE_FCHMOD ||
errno != ENOSYS)
3603 rb_sys_fail_path(fptr->
pathv);
3606 if (!HAVE_FCHMOD)
return INT2FIX(0);
3609#if !defined HAVE_FCHMOD || !HAVE_FCHMOD
3611 path = rb_str_encode_ospath(fptr->
pathv);
3612 if (rb_chmod(RSTRING_PTR(path), mode) == -1)
3613 rb_sys_fail_path(fptr->
pathv);
3619#if defined(HAVE_LCHMOD)
3621lchmod_internal(
const char *path,
void *mode)
3623 return lchmod(path, *(mode_t *)mode);
3660 return apply2files(lchmod_internal, argc, argv, &mode);
3663#define rb_file_s_lchmod rb_f_notimplement
3666static inline rb_uid_t
3670 return (rb_uid_t)-1;
3675static inline rb_gid_t
3679 return (rb_gid_t)-1;
3690chown_internal(
const char *path,
void *arg)
3693 return chown(path, args->owner, args->group);
3747 arg.owner = to_uid(*argv++);
3748 arg.group = to_gid(*argv++);
3750 return apply2files(chown_internal, argc, argv, &arg);
3762nogvl_chown(
void *ptr)
3765 return (
void *)(
VALUE)chown(data->as.path, data->new.owner, data->new.group);
3769rb_chown(
const char *path, rb_uid_t owner, rb_gid_t group)
3772 .as = {.path = path},
3773 .new = {.owner = owner, .group = group},
3775 return IO_WITHOUT_GVL_INT(nogvl_chown, &data);
3780nogvl_fchown(
void *ptr)
3783 return (
void *)(
VALUE)fchown(data->as.fd, data->new.owner, data->new.group);
3787rb_fchown(
int fd, rb_uid_t owner, rb_gid_t group)
3792 .new = {.owner = owner, .group = group},
3794 return IO_WITHOUT_GVL_INT(nogvl_fchown, &data);
3828 path = rb_str_encode_ospath(fptr->
pathv);
3829 if (rb_chown(RSTRING_PTR(path), o, g) == -1)
3830 rb_sys_fail_path(fptr->
pathv);
3832 if (rb_fchown(fptr->
fd, o, g) == -1)
3833 rb_sys_fail_path(fptr->
pathv);
3839#if defined(HAVE_LCHOWN)
3841lchown_internal(
const char *path,
void *arg)
3844 return lchown(path, args->owner, args->group);
3901 arg.owner = to_uid(*argv++);
3902 arg.group = to_gid(*argv++);
3904 return apply2files(lchown_internal, argc, argv, &arg);
3907#define rb_file_s_lchown rb_f_notimplement
3917NORETURN(
static void utime_failed(
struct apply_arg *));
3923 VALUE path = aa->fn[aa->i].path;
3926 if (ua->tsp && e == EINVAL) {
3929 VALUE atime = ua->atime;
3930 VALUE mtime = ua->mtime;
3932 if (!
NIL_P(atime)) {
3935 if (!
NIL_P(mtime) && mtime != atime && !
rb_equal(atime, mtime)) {
3938 if (
NIL_P(a)) e[0] = m;
3954 rb_syserr_fail_path(e, path);
3958#if defined(HAVE_UTIMES)
3960# if !defined(HAVE_UTIMENSAT)
3962# elif defined(__APPLE__) && \
3963 (!defined(MAC_OS_X_VERSION_13_0) || (MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_13_0))
3965# if __has_attribute(availability) && __has_warning("-Wunguarded-availability-new")
3966typedef int utimensat_func(
int,
const char *,
const struct timespec [2],
int);
3970static inline utimensat_func *
3977# define utimensat rb_utimensat()
3984utime_internal(
const char *path,
void *arg)
3987 const struct timespec *tsp = v->tsp;
3988 struct timeval tvbuf[2], *tvp = NULL;
3990#if defined(HAVE_UTIMENSAT)
3991# if defined(__APPLE__)
3992 const int try_utimensat = utimensat != NULL;
3993 const int try_utimensat_follow = utimensat != NULL;
3995# define TRY_UTIMENSAT 1
3996 static int try_utimensat = 1;
3997# ifdef AT_SYMLINK_NOFOLLOW
3998 static int try_utimensat_follow = 1;
4000 const int try_utimensat_follow = 0;
4005 if (v->follow ? try_utimensat_follow : try_utimensat) {
4006# ifdef AT_SYMLINK_NOFOLLOW
4008 flags = AT_SYMLINK_NOFOLLOW;
4012 int result = utimensat(AT_FDCWD, path, tsp, flags);
4013# ifdef TRY_UTIMENSAT
4014 if (result < 0 &&
errno == ENOSYS) {
4015# ifdef AT_SYMLINK_NOFOLLOW
4016 try_utimensat_follow = 0;
4028 tvbuf[0].tv_sec = tsp[0].tv_sec;
4029 tvbuf[0].tv_usec = (int)(tsp[0].tv_nsec / 1000);
4030 tvbuf[1].tv_sec = tsp[1].tv_sec;
4031 tvbuf[1].tv_usec = (int)(tsp[1].tv_nsec / 1000);
4035 if (v->follow)
return lutimes(path, tvp);
4037 return utimes(path, tvp);
4042#if !defined HAVE_UTIME_H && !defined HAVE_SYS_UTIME_H
4050utime_internal(
const char *path,
void *arg)
4054 struct utimbuf utbuf, *utp = NULL;
4056 utbuf.actime = tsp[0].tv_sec;
4057 utbuf.modtime = tsp[1].tv_sec;
4060 return utime(path, utp);
4065utime_internal_i(
int argc,
VALUE *argv,
int follow)
4068 struct timespec tss[2], *tsp = NULL;
4071 args.atime = *argv++;
4072 args.mtime = *argv++;
4074 args.follow = follow;
4076 if (!
NIL_P(args.atime) || !
NIL_P(args.mtime)) {
4079 if (args.atime == args.mtime)
4086 return apply2files(utime_internal, argc, argv, &args);
4132 return utime_internal_i(argc, argv, FALSE);
4135#if defined(HAVE_UTIMES) && (defined(HAVE_LUTIMES) || (defined(HAVE_UTIMENSAT) && defined(AT_SYMLINK_NOFOLLOW)))
4193 return utime_internal_i(argc, argv, TRUE);
4196#define rb_file_s_lutime rb_f_notimplement
4199#ifdef RUBY_FUNCTION_NAME_STRING
4200# define syserr_fail2(e, s1, s2) syserr_fail2_in(RUBY_FUNCTION_NAME_STRING, e, s1, s2)
4202# define syserr_fail2_in(func, e, s1, s2) syserr_fail2(e, s1, s2)
4204#define sys_fail2(s1, s2) syserr_fail2(errno, s1, s2)
4205NORETURN(
static void syserr_fail2_in(
const char *,
int,
VALUE,
VALUE));
4207syserr_fail2_in(
const char *func,
int e,
VALUE s1,
VALUE s2)
4211 const int max_pathlen = MAX_PATH;
4213 const int max_pathlen = MAXPATHLEN;
4224#ifdef RUBY_FUNCTION_NAME_STRING
4225 rb_syserr_fail_path_in(func, e, str);
4227 rb_syserr_fail_path(e, str);
4262 from = rb_str_encode_ospath(from);
4263 to = rb_str_encode_ospath(to);
4266 sys_fail2(from, to);
4271#define rb_file_s_link rb_f_notimplement
4312 from = rb_str_encode_ospath(from);
4313 to = rb_str_encode_ospath(to);
4316 sys_fail2(from, to);
4321#define rb_file_s_symlink rb_f_notimplement
4349 return rb_readlink(path, rb_filesystem_encoding());
4352struct readlink_arg {
4359nogvl_readlink(
void *ptr)
4361 struct readlink_arg *ra = ptr;
4363 return (
void *)(
VALUE)readlink(ra->path, ra->buf, ra->size);
4367readlink_without_gvl(
VALUE path,
VALUE buf,
size_t size)
4369 struct readlink_arg ra;
4371 ra.path = RSTRING_PTR(path);
4372 ra.buf = RSTRING_PTR(buf);
4375 return (ssize_t)IO_WITHOUT_GVL(nogvl_readlink, &ra);
4386 path = rb_str_encode_ospath(path);
4387 v = rb_enc_str_new(0, size, enc);
4388 while ((rv = readlink_without_gvl(path, v, size)) == size
4390 || (rv < 0 &&
errno == ERANGE)
4399 rb_str_resize(v, 0);
4400 rb_syserr_fail_path(e, path);
4402 rb_str_resize(v, rv);
4407#define rb_file_s_readlink rb_f_notimplement
4411unlink_internal(
const char *path,
void *arg)
4413 return unlink(path);
4456rb_file_s_unlink(
int argc,
VALUE *argv,
VALUE klass)
4458 return apply2files(unlink_internal, argc, argv, 0);
4467no_gvl_rename(
void *ptr)
4471 return (
void *)(
VALUE)rename(ra->src, ra->dst);
4549 f = rb_str_encode_ospath(from);
4550 t = rb_str_encode_ospath(to);
4553#if defined __CYGWIN__
4556 if (IO_WITHOUT_GVL_INT(no_gvl_rename, &ra) < 0) {
4561 if (chmod(ra.dst, 0666) == 0 &&
4562 unlink(ra.dst) == 0 &&
4563 rename(ra.src, ra.dst) == 0)
4567 syserr_fail2(e, from, to);
4601 rb_error_arity(argc, 0, 1);
4609#if defined __CYGWIN__ || defined DOSISH
4611#define DOSISH_DRIVE_LETTER
4612#define FILE_ALT_SEPARATOR '\\'
4614#ifdef FILE_ALT_SEPARATOR
4615#define isdirsep(x) ((x) == '/' || (x) == FILE_ALT_SEPARATOR)
4617static const char file_alt_separator[] = {FILE_ALT_SEPARATOR,
'\0'};
4620#define isdirsep(x) ((x) == '/')
4632#define istrailinggarbage(x) ((x) == '.' || (x) == ' ')
4633#define isADS(x) ((x) == ':')
4635#define istrailinggarbage(x) 0
4638#define enc_mbclen_needed(enc) (!rb_str_encindex_fastpath(rb_enc_to_index(enc)))
4640#define Next(p, e, mb_enc, enc) ((p) + ((mb_enc) ? rb_enc_mbclen((p), (e), (enc)) : 1))
4641#define Inc(p, e, mb_enc, enc) ((p) = Next((p), (e), (mb_enc), (enc)))
4643#if defined(DOSISH_UNC)
4644#define has_unc(buf) (isdirsep((buf)[0]) && isdirsep((buf)[1]))
4646#define has_unc(buf) 0
4649#ifdef DOSISH_DRIVE_LETTER
4651has_drive_letter(
const char *buf)
4653 if (
ISALPHA(buf[0]) && buf[1] ==
':') {
4678 if (chdir(drive) == 0) {
4679 drvcwd = rb_dir_getwd_ospath();
4691not_same_drive(
VALUE path,
int drive)
4693 const char *p = RSTRING_PTR(path);
4694 if (RSTRING_LEN(path) < 2)
return 0;
4695 if (has_drive_letter(p)) {
4706skiproot(
const char *path,
const char *end)
4708#ifdef DOSISH_DRIVE_LETTER
4709 if (path + 2 <= end && has_drive_letter(path)) path += 2;
4711 while (path < end && isdirsep(*path)) path++;
4712 return (
char *)path;
4716enc_path_next(
const char *s,
const char *e,
bool mb_enc,
rb_encoding *enc)
4718 while (s < e && !isdirsep(*s)) {
4719 Inc(s, e, mb_enc, enc);
4724#define nextdirsep rb_enc_path_next
4728 return enc_path_next(s, e, enc_mbclen_needed(enc), enc);
4731#if defined(DOSISH_UNC) || defined(DOSISH_DRIVE_LETTER)
4732#define skipprefix enc_path_skip_prefix
4734#define skipprefix(path, end, mb_enc, enc) (path)
4737enc_path_skip_prefix(
const char *path,
const char *end,
bool mb_enc,
rb_encoding *enc)
4739#if defined(DOSISH_UNC) || defined(DOSISH_DRIVE_LETTER)
4741 if (path + 2 <= end && isdirsep(path[0]) && isdirsep(path[1])) {
4743 while (path < end && isdirsep(*path)) path++;
4744 if ((path = enc_path_next(path, end, mb_enc, enc)) < end &&
4745 path + 2 <= end && !isdirsep(path[1])) {
4746 path = enc_path_next(path + 1, end, mb_enc, enc);
4748 return (
char *)path;
4751#ifdef DOSISH_DRIVE_LETTER
4752 if (path + 2 <= end && has_drive_letter(path))
4753 return (
char *)(path + 2);
4756 return (
char *)path;
4760rb_enc_path_skip_prefix(
const char *path,
const char *end,
rb_encoding *enc)
4762 return enc_path_skip_prefix(path, end, enc_mbclen_needed(enc), enc);
4766skipprefixroot(
const char *path,
const char *end,
rb_encoding *enc)
4768#if defined(DOSISH_UNC) || defined(DOSISH_DRIVE_LETTER)
4769 char *p = skipprefix(path, end, enc_mbclen_needed(enc), enc);
4770 while (p < end && isdirsep(*p)) p++;
4773 return skiproot(path, end);
4778rb_enc_path_skip_prefix_root(
const char *path,
const char *end,
rb_encoding *enc)
4780 return skipprefixroot(path, end, enc);
4784enc_path_last_separator(
const char *path,
const char *end,
bool mb_enc,
rb_encoding *enc)
4787 while (path < end) {
4788 if (isdirsep(*path)) {
4789 const char *tmp = path++;
4790 while (path < end && isdirsep(*path)) path++;
4791 if (path >= end)
break;
4795 Inc(path, end, mb_enc, enc);
4801rb_enc_path_last_separator(
const char *path,
const char *end,
rb_encoding *enc)
4803 return enc_path_last_separator(path, end, enc_mbclen_needed(enc), enc);
4807strrdirsep(
const char *path,
const char *end,
bool mb_enc,
rb_encoding *enc)
4809 if (RB_UNLIKELY(mb_enc)) {
4810 return enc_path_last_separator(path, end, mb_enc, enc);
4813 const char *cursor = end - 1;
4815 while (cursor >= path && isdirsep(cursor[0])) {
4819 while (cursor >= path) {
4820 if (isdirsep(cursor[0])) {
4821 while (cursor > path && isdirsep(cursor[-1])) {
4824 return (
char *)cursor;
4832chompdirsep(
const char *path,
const char *end,
bool mb_enc,
rb_encoding *enc)
4834 while (path < end) {
4835 if (isdirsep(*path)) {
4836 const char *last = path++;
4837 while (path < end && isdirsep(*path)) path++;
4838 if (path >= end)
return (
char *)last;
4841 Inc(path, end, mb_enc, enc);
4844 return (
char *)path;
4850 if (path < end && isdirsep(*path)) path++;
4851 return chompdirsep(path, end, enc_mbclen_needed(enc), enc);
4857 rb_encoding *enc = rb_enc_check_str(path1, path2);
4858 int encidx = rb_enc_to_index(enc);
4859 if (encidx == ENCINDEX_US_ASCII) {
4860 encidx = rb_enc_get_index(path1);
4861 if (encidx == ENCINDEX_US_ASCII)
4862 encidx = rb_enc_get_index(path2);
4863 enc = rb_enc_from_index(encidx);
4870ntfs_tail(
const char *path,
const char *end,
bool mb_enc,
rb_encoding *enc)
4872 while (path < end && *path ==
'.') path++;
4873 while (path < end && !isADS(*path)) {
4874 if (istrailinggarbage(*path)) {
4875 const char *last = path++;
4876 while (path < end && istrailinggarbage(*path)) path++;
4877 if (path >= end || isADS(*path))
return (
char *)last;
4879 else if (isdirsep(*path)) {
4880 const char *last = path++;
4881 while (path < end && isdirsep(*path)) path++;
4882 if (path >= end)
return (
char *)last;
4883 if (isADS(*path)) path++;
4886 Inc(path, end, mb_enc, enc);
4889 return (
char *)path;
4893#define BUFCHECK(cond) do {\
4896 do {buflen *= 2;} while (cond);\
4897 rb_str_resize(result, buflen);\
4898 buf = RSTRING_PTR(result);\
4900 pend = buf + buflen;\
4904#define BUFINIT(result, buf, p, pend) do {\
4905 if (!result) { result = rb_usascii_str_new(0, 1); } \
4906 p = buf = RSTRING_PTR(result); \
4907 buflen = RSTRING_LEN(result); \
4908 pend = p + buflen; \
4912# define SKIPPATHSEP(p) ((*(p)) ? 1 : 0)
4914# define SKIPPATHSEP(p) 1
4917#define BUFCOPY(srcptr, srclen) do { \
4918 const int skip = SKIPPATHSEP(p); \
4919 rb_str_set_len(result, p-buf+skip); \
4920 BUFCHECK(bdiff + ((srclen)+skip) >= buflen); \
4922 memcpy(p, (srcptr), (srclen)); \
4926#define WITH_ROOTDIFF(stmt) do { \
4927 rb_len_t rootdiff = root - buf; \
4929 root = buf + rootdiff; \
4933copy_home_path(
VALUE result,
const char *dir)
4939 dirlen = strlen(dir);
4940 rb_str_resize(result, dirlen);
4941 memcpy(buf = RSTRING_PTR(result), dir, dirlen);
4942 encidx = rb_filesystem_encindex();
4943 rb_enc_associate_index(result, encidx);
4944#if defined FILE_ALT_SEPARATOR
4946 bool mb_enc = enc_mbclen_needed(enc);
4947 for (
char *p = buf, *bend = p + dirlen; p < bend; Inc(p, bend, mb_enc, enc)) {
4948 if (*p == FILE_ALT_SEPARATOR) {
4960 VALUE dirname = rb_getpwdirnam_for_login(user);
4961 if (dirname ==
Qnil) {
4962 rb_raise(rb_eArgError,
"user %"PRIsVALUE
" doesn't exist", user);
4964 const char *dir = RSTRING_PTR(dirname);
4966 extern char *getlogin(
void);
4967 const char *pwPtr = 0;
4969 # define endpwent() ((void)0)
4970 const char *dir, *username = RSTRING_PTR(user);
4981 if ((login = getlogin()) && strcasecmp(username, login) == 0)
4982 dir = pwPtr = getenv(
"HOME");
4984 rb_raise(rb_eArgError,
"user %"PRIsVALUE
" doesn't exist", user);
4987 copy_home_path(result, dir);
4993rb_default_home_dir(
VALUE result)
4995 const char *dir = getenv(
"HOME");
4997#if defined HAVE_PWD_H
5011 VALUE login_name = rb_getlogin();
5013# if !defined(HAVE_GETPWUID_R) && !defined(HAVE_GETPWUID)
5018 if (
NIL_P(login_name)) {
5019 rb_raise(rb_eArgError,
"couldn't find login name -- expanding '~'");
5023 VALUE pw_dir = rb_getpwdirnam_for_login(login_name);
5024 if (
NIL_P(pw_dir)) {
5025 pw_dir = rb_getpwdiruid();
5026 if (
NIL_P(pw_dir)) {
5027 rb_raise(rb_eArgError,
"couldn't find home for uid '%ld'", (
long)getuid());
5032 copy_home_path(result, RSTRING_PTR(pw_dir));
5033 rb_str_resize(pw_dir, 0);
5038 rb_raise(rb_eArgError,
"couldn't find HOME environment -- expanding '~'");
5040 return copy_home_path(result, dir);
5046#if NORMALIZE_UTF8PATH
5047 VALUE path = rb_str_normalize_ospath(ptr,
len);
5048 rb_enc_associate(path, fsenc);
5051 return rb_enc_str_new(ptr,
len, fsenc);
5058 if (RB_UNLIKELY(!rb_enc_asciicompat(fsenc) || rb_enc_str_coderange(dirname) !=
ENC_CODERANGE_7BIT)) {
5060 rb_enc_associate(dirname, fsenc);
5064 size_t dirlen = RSTRING_LEN(dirname);
5067 if (NORMALIZE_UTF8PATH || *enc != fsenc) {
5068 if (!rb_enc_compatible(fname, dirname)) {
5071 rb_enc_check(fname, dirname);
5072 rb_bug(
"unreachable");
5074 rb_encoding *direnc = fs_enc_check(fname, dirname);
5075 if (direnc != fsenc) {
5082 do {buflen *= 2;}
while (dirlen > buflen);
5083 rb_str_resize(result, buflen);
5084 buf = RSTRING_PTR(result);
5085 memcpy(buf, cwdp, dirlen);
5086 rb_enc_associate(result, *enc);
5087 return buf + dirlen;
5091rb_file_expand_path_internal(
VALUE fname,
VALUE dname,
int abs_mode,
int long_name,
VALUE result)
5093 const char *s, *b, *fend;
5094 char *buf, *p, *pend, *root;
5095 size_t buflen, bdiff;
5096 rb_encoding *enc, *fsenc = rb_filesystem_encoding();
5099 fend = s + RSTRING_LEN(fname);
5100 enc = rb_str_enc_get(fname);
5101 bool mb_enc = enc_mbclen_needed(enc);
5102 if (!mb_enc &&
RTEST(dname)) {
5103 mb_enc = enc_mbclen_needed(rb_str_enc_get(dname));
5106 if (s < fend && s[0] ==
'~' && abs_mode == 0) {
5107 BUFINIT(result, buf, p, pend);
5110 if (s + 1 == fend || isdirsep(s[1])) {
5114 if (++s < fend) ++s;
5115 rb_default_home_dir(result);
5118 s = nextdirsep(b = s, fend, enc);
5121 BUFCHECK(bdiff + userlen >= buflen);
5122 memcpy(p, b, userlen);
5125 rb_enc_associate(result, enc);
5126 rb_home_dir_of(result, result);
5130 if (!rb_is_absolute_path(RSTRING_PTR(result))) {
5132 rb_enc_raise(enc, rb_eArgError,
"non-absolute home of %.*s%.0"PRIsVALUE,
5133 (
int)userlen, b, fname);
5136 rb_raise(rb_eArgError,
"non-absolute home");
5139 BUFINIT(result, buf, p, pend);
5142#ifdef DOSISH_DRIVE_LETTER
5144 else if (s + 1 < fend && has_drive_letter(s)) {
5145 BUFINIT(result, buf, p, pend);
5147 if (s + 2 < fend && isdirsep(s[2])) {
5150 BUFCHECK(bdiff + 2 >= buflen);
5154 rb_enc_copy(result, fname);
5159 if (!
NIL_P(dname) && !not_same_drive(dname, s[0])) {
5160 rb_file_expand_path_internal(dname,
Qnil, abs_mode, long_name, result);
5161 BUFINIT(result, buf, p, pend);
5168 char *e = append_fspath(result, fname, getcwdofdrv(*s), &enc, fsenc);
5169 BUFINIT(result, buf, p, pend);
5173 rb_enc_associate(result, enc = fs_enc_check(result, fname));
5176 p = chompdirsep(skiproot(buf, p), p, mb_enc, enc);
5181 else if (s == fend || !rb_is_absolute_path(s)) {
5183 if (!
NIL_P(dname)) {
5185 rb_file_expand_path_internal(dname,
Qnil, abs_mode, long_name, result);
5189 rb_file_expand_path_internal(dname,
Qnil, abs_mode, long_name, result);
5191 if (RB_UNLIKELY(RSTRING_LEN(result) > RSTRING_LEN(dname))) {
5196 result = resized_result;
5200 rb_enc_associate(result, fs_enc_check(result, fname));
5201 BUFINIT(result, buf, p, pend);
5205 VALUE cwd = rb_dir_getwd_ospath();
5209 char *e = append_fspath(result, fname, rb_dir_getwd_ospath(), &enc, fsenc);
5210 BUFINIT(result, buf, p, pend);
5213#if defined DOSISH_DRIVE_LETTER || defined DOSISH_UNC
5214 if (s < fend && isdirsep(*s)) {
5217 p = skipprefix(buf, p, mb_enc, enc);
5221 p = chompdirsep(skiproot(buf, p), p, mb_enc, enc);
5224 BUFINIT(result, buf, p, pend);
5228 do s++;
while (s < fend && isdirsep(*s));
5231 BUFCHECK(bdiff >= buflen);
5232 memset(buf,
'/',
len);
5234 rb_enc_associate(result, fs_enc_check(result, fname));
5236 if (p > buf && p[-1] ==
'/')
5240 BUFCHECK(bdiff + 1 >= buflen);
5245 BUFCHECK(bdiff + 1 >= buflen);
5247 root = skipprefix(buf, p+1, mb_enc, enc);
5260 if (s+1 == fend || isdirsep(*(s+1))) {
5264 if (!(n = strrdirsep(root, p, mb_enc, enc))) {
5274#if defined FILE_ALT_SEPARATOR
5275 case FILE_ALT_SEPARATOR:
5286#if defined FILE_ALT_SEPARATOR
5287 case FILE_ALT_SEPARATOR:
5290 WITH_ROOTDIFF(BUFCOPY(b, s-b));
5298 int n = ignored_char_p(s, fend, enc);
5301 WITH_ROOTDIFF(BUFCOPY(b, s-b));
5309 Inc(s, fend, mb_enc, enc);
5318 if (p == skiproot(buf, p + !!*p) - 1) p++;
5321 rb_enc_check(fname, result);
5328str_shrink(
VALUE str)
5330 rb_str_resize(str, RSTRING_LEN(str));
5334#define expand_path(fname, dname, abs_mode, long_name, result) \
5335 str_shrink(rb_file_expand_path_internal(fname, dname, abs_mode, long_name, result))
5337#define check_expand_path_args(fname, dname) \
5338 (((fname) = rb_get_path(fname)), \
5339 (void)(NIL_P(dname) ? (dname) : ((dname) = rb_get_path(dname))))
5345 return rb_file_expand_path_internal(fname,
Qnil, 0, 0, buffer);
5351 check_expand_path_args(fname, dname);
5352 return expand_path(fname, dname, 0, 1,
Qfalse);
5356rb_file_expand_path_fast(
VALUE fname,
VALUE dname)
5358 return expand_path(fname, dname, 0, 0,
Qfalse);
5362rb_file_s_expand_path(
int argc,
const VALUE *argv)
5365 return rb_file_expand_path(argv[0], argc > 1 ? argv[1] :
Qnil);
5411 return rb_file_s_expand_path(c, v);
5417 check_expand_path_args(fname, dname);
5418 return expand_path(fname, dname, 1, 1,
Qfalse);
5422rb_file_s_absolute_path(
int argc,
const VALUE *argv)
5425 return rb_file_absolute_path(argv[0], argc > 1 ? argv[1] :
Qnil);
5468 return rb_file_s_absolute_path(c, v);
5496 VALUE path = rb_get_path(fname);
5498 if (!rb_is_absolute_path(RSTRING_PTR(path)))
return Qfalse;
5502enum rb_realpath_mode {
5506 RB_REALPATH_MODE_MAX
5510realpath_rec(
rb_len_t *prefixlenp,
VALUE *resolvedp,
const char *unresolved,
VALUE fallback,
5511 VALUE loopcheck,
enum rb_realpath_mode mode,
int last)
5513 const char *pend = unresolved + strlen(unresolved);
5517 while (unresolved < pend) {
5518 const char *testname = unresolved;
5519 const char *unresolved_firstsep = rb_enc_path_next(unresolved, pend, enc);
5520 rb_len_t testnamelen = unresolved_firstsep - unresolved;
5521 const char *unresolved_nextname = unresolved_firstsep;
5522 while (unresolved_nextname < pend && isdirsep(*unresolved_nextname))
5523 unresolved_nextname++;
5524 unresolved = unresolved_nextname;
5525 if (testnamelen == 1 && testname[0] ==
'.') {
5527 else if (testnamelen == 2 && testname[0] ==
'.' && testname[1] ==
'.') {
5528 if (*prefixlenp < RSTRING_LEN(*resolvedp)) {
5529 bool mb_enc = enc_mbclen_needed(enc);
5530 const char *resolved_str = RSTRING_PTR(*resolvedp);
5531 const char *resolved_names = resolved_str + *prefixlenp;
5532 const char *lastsep = strrdirsep(resolved_names, resolved_str + RSTRING_LEN(*resolvedp), mb_enc, enc);
5533 rb_len_t len = lastsep ? lastsep - resolved_names : 0;
5534 rb_str_resize(*resolvedp, *prefixlenp +
len);
5540 if (*prefixlenp < RSTRING_LEN(testpath))
5542#if defined(DOSISH_UNC) || defined(DOSISH_DRIVE_LETTER)
5543 if (*prefixlenp > 1 && *prefixlenp == RSTRING_LEN(testpath)) {
5544 const char *prefix = RSTRING_PTR(testpath);
5545 const char *last =
rb_enc_left_char_head(prefix, prefix + *prefixlenp - 1, prefix + *prefixlenp, enc);
5550 checkval = rb_hash_aref(loopcheck, testpath);
5551 if (!
NIL_P(checkval)) {
5552 if (checkval ==
ID2SYM(resolving)) {
5553 if (mode == RB_REALPATH_CHECK) {
5557 rb_syserr_fail_path(ELOOP, testpath);
5566 ret = lstat_without_gvl(RSTRING_PTR(testpath), &sbuf);
5569 if (e == ENOENT && !
NIL_P(fallback)) {
5570 if (stat_without_gvl(RSTRING_PTR(fallback), &sbuf) == 0) {
5575 if (mode == RB_REALPATH_CHECK)
return -1;
5577 if (mode == RB_REALPATH_STRICT || !last || *unresolved_firstsep)
5578 rb_syserr_fail_path(e, testpath);
5579 *resolvedp = testpath;
5583 rb_syserr_fail_path(e, testpath);
5587 if (S_ISLNK(sbuf.st_mode)) {
5590 const char *link_prefix, *link_names;
5592 rb_hash_aset(loopcheck, testpath,
ID2SYM(resolving));
5593 link = rb_readlink(testpath, enc);
5594 link_prefix = RSTRING_PTR(link);
5595 link_names = skipprefixroot(link_prefix, link_prefix + RSTRING_LEN(link), rb_enc_get(link));
5596 link_prefixlen = link_names - link_prefix;
5597 if (link_prefixlen > 0) {
5601 tmpenc = fs_enc_check(*resolvedp, link);
5604 *prefixlenp = link_prefixlen;
5606 if (realpath_rec(prefixlenp, resolvedp, link_names, testpath,
5607 loopcheck, mode, !*unresolved_firstsep))
5616 rb_hash_aset(loopcheck, s, s);
5617 *resolvedp = testpath;
5625#ifdef DOSISH_DRIVE_LETTER
5629ospath_for_expand(
VALUE path)
5632 case ENCINDEX_ASCII_8BIT:
5633 case ENCINDEX_US_ASCII:
5634 return rb_enc_associate_index(
rb_str_dup(path), rb_filesystem_encindex());
5636 return TO_OSPATH(path);
5641rb_check_realpath_emulate(
VALUE basedir,
VALUE path,
rb_encoding *origenc,
enum rb_realpath_mode mode)
5645 VALUE unresolved_path;
5650 char *path_names = NULL, *basedir_names = NULL, *curdir_names = NULL;
5651 char *ptr, *prefixptr = NULL, *pend;
5654#ifdef DOSISH_DRIVE_LETTER
5655 VALUE rootdir = path;
5657 if (!
NIL_P(basedir) && skipprefixroot(ptr, ptr +
len, rb_enc_get(path)) == ptr) {
5662 if (
len >= 2 && has_drive_letter(ptr) && (
len == 2 || !isdirsep(ptr[2]))) {
5665 if (!
NIL_P(basedir)) basedir = ospath_for_expand(rb_get_path(basedir));
5666 path = rb_file_absolute_path(ospath_for_expand(path), basedir);
5673 if (!
NIL_P(basedir)) {
5678 enc = rb_enc_get(unresolved_path);
5679 unresolved_path = TO_OSPATH(unresolved_path);
5681 path_names = skipprefixroot(ptr, ptr +
len, rb_enc_get(unresolved_path));
5682 if (ptr != path_names) {
5683 resolved =
rb_str_subseq(unresolved_path, 0, path_names - ptr);
5687 if (!
NIL_P(basedir)) {
5689 basedir_names = skipprefixroot(ptr, ptr +
len, rb_enc_get(basedir));
5690 if (ptr != basedir_names) {
5696 curdir = rb_dir_getwd_ospath();
5698 curdir_names = skipprefixroot(ptr, ptr +
len, rb_enc_get(curdir));
5703 pend = prefixptr + prefixlen;
5704 bool mb_enc = enc_mbclen_needed(enc);
5705 ptr = chompdirsep(prefixptr, pend, mb_enc, enc);
5707 prefixlen = ++ptr - prefixptr;
5710#ifdef FILE_ALT_SEPARATOR
5711 while (prefixptr < ptr) {
5712 if (*prefixptr == FILE_ALT_SEPARATOR) {
5715 Inc(prefixptr, pend, mb_enc, enc);
5719 switch (rb_enc_to_index(enc)) {
5720 case ENCINDEX_ASCII_8BIT:
5721 case ENCINDEX_US_ASCII:
5722 rb_enc_associate_index(resolved, rb_filesystem_encindex());
5725 loopcheck = rb_hash_new();
5727 if (realpath_rec(&prefixlen, &resolved, curdir_names,
Qnil, loopcheck, mode, 0))
5730 if (basedir_names) {
5731 if (realpath_rec(&prefixlen, &resolved, basedir_names,
Qnil, loopcheck, mode, 0))
5734 if (realpath_rec(&prefixlen, &resolved, path_names,
Qnil, loopcheck, mode, 1))
5737 if (origenc && origenc != rb_enc_get(resolved)) {
5739 rb_enc_associate(resolved, origenc);
5753#ifndef HAVE_REALPATH
5755rb_check_realpath_emulate_try(
VALUE arg)
5758 return rb_check_realpath_emulate(args[0], args[1], (
rb_encoding *)args[2], RB_REALPATH_CHECK);
5762rb_check_realpath_emulate_rescue(
VALUE arg,
VALUE exc)
5769rb_check_realpath_internal(
VALUE basedir,
VALUE path,
rb_encoding *origenc,
enum rb_realpath_mode mode)
5772 VALUE unresolved_path;
5773 char *resolved_ptr = NULL;
5776 if (mode == RB_REALPATH_DIR) {
5777 return rb_check_realpath_emulate(basedir, path, origenc, mode);
5781 if (*RSTRING_PTR(unresolved_path) !=
'/' && !
NIL_P(basedir)) {
5782 VALUE paths[2] = {basedir, unresolved_path};
5783 unresolved_path = rb_file_join(2, paths);
5785 if (origenc) unresolved_path = TO_OSPATH(unresolved_path);
5787 if ((resolved_ptr = realpath(RSTRING_PTR(unresolved_path), NULL)) == NULL) {
5796 if (
errno == ENOTSUP ||
5798 (
errno == ENOENT && rb_file_exist_p(0, unresolved_path))) {
5799 return rb_check_realpath_emulate(basedir, path, origenc, mode);
5802 if (mode == RB_REALPATH_CHECK) {
5805 rb_sys_fail_path(unresolved_path);
5807 resolved = ospath_new(resolved_ptr, strlen(resolved_ptr), rb_filesystem_encoding());
5810# if !defined(__linux__) && !defined(__APPLE__)
5814 if (stat_without_gvl(RSTRING_PTR(resolved), &st) < 0) {
5815 if (mode == RB_REALPATH_CHECK) {
5818 rb_sys_fail_path(unresolved_path);
5822 if (origenc && origenc != rb_enc_get(resolved)) {
5826 rb_enc_associate(resolved, origenc);
5829 if (is_broken_string(resolved)) {
5830 rb_enc_associate(resolved, rb_filesystem_encoding());
5831 if (is_broken_string(resolved)) {
5832 rb_enc_associate(resolved, rb_ascii8bit_encoding());
5839 if (mode == RB_REALPATH_CHECK) {
5843 arg[2] = (
VALUE)origenc;
5846 rb_check_realpath_emulate_rescue,
Qnil);
5849 return rb_check_realpath_emulate(basedir, path, origenc, mode);
5855rb_realpath_internal(
VALUE basedir,
VALUE path,
int strict)
5857 const enum rb_realpath_mode mode =
5858 strict ? RB_REALPATH_STRICT : RB_REALPATH_DIR;
5859 return rb_check_realpath_internal(basedir, path, rb_enc_get(path), mode);
5865 return rb_check_realpath_internal(basedir, path, enc, RB_REALPATH_CHECK);
5882rb_file_s_realpath(
int argc,
VALUE *argv,
VALUE klass)
5885 VALUE path = argv[0];
5887 return rb_realpath_internal(basedir, path, 1);
5903rb_file_s_realdirpath(
int argc,
VALUE *argv,
VALUE klass)
5906 VALUE path = argv[0];
5908 return rb_realpath_internal(basedir, path, 0);
5916 const char *s, *last;
5918 if (!e || !l2)
return 0;
5920 c = rb_enc_codepoint_len(e, e + l2, &len1, enc);
5921 if (rb_enc_ascget(e + len1, e + l2, &len2, enc) ==
'*' && len1 + len2 == l2) {
5922 if (c ==
'.')
return l0;
5927 if (rb_enc_codepoint_len(s, e, &len1, enc) == c) last = s;
5932 if (l1 < l2)
return l1;
5935 if (!at_char_boundary(p, s, p+l1, enc))
return 0;
5936#if CASEFOLD_FILESYSTEM
5937#define fncomp strncasecmp
5939#define fncomp strncmp
5941 if (fncomp(s, e, l2) == 0) {
5947static inline const char *
5950 const char *p, *q, *e, *end;
5953 rb_len_t len = (alllen ? (size_t)*alllen : strlen(name));
5960 name = skipprefix(name, end, mb_enc, enc);
5961#if defined DOSISH_DRIVE_LETTER || defined DOSISH_UNC
5962 const char *root = name;
5965 while (name < end && isdirsep(*name)) {
5972#if defined DOSISH_DRIVE_LETTER || defined DOSISH_UNC
5976#ifdef DOSISH_DRIVE_LETTER
5977 else if (*p ==
':') {
5990 p = strrdirsep(name, end, mb_enc, enc);
5995 while (isdirsep(*p)) {
6000 n = ntfs_tail(p, end, mb_enc, enc) - p;
6002 n = chompdirsep(p, end, mb_enc, enc) - p;
6004 for (q = p; q - p < n && *q ==
'.'; q++);
6005 for (e = 0; q - p < n; Inc(q, end, mb_enc, enc)) {
6006 if (*q ==
'.') e = q;
6028 return enc_find_basename(name, baselen, alllen, enc_mbclen_needed(enc), enc);
6072 const char *name, *p, *fp = 0;
6078 CheckPath(fname, name);
6082 check_path_encoding(fext);
6084 if (
NIL_P(fext) || !(enc = rb_enc_compatible(fname, fext))) {
6085 enc = rb_str_enc_get(fname);
6088 n = RSTRING_LEN(fname);
6089 if (n <= 0 || !*name) {
6090 return rb_enc_str_new(0, 0, enc);
6093 bool mb_enc = enc_mbclen_needed(enc);
6094 p = enc_find_basename(name, &f, &n, mb_enc, enc);
6100 if (!(f = rmext(p, f, n, fp, RSTRING_LEN(fext), enc))) {
6105 if (f == RSTRING_LEN(fname)) {
6110 return rb_enc_str_new(p, f, enc);
6113static VALUE rb_file_dirname_n(
VALUE fname,
int n);
6136rb_file_s_dirname(
int argc,
VALUE *argv,
VALUE klass)
6142 return rb_file_dirname_n(argv[0], n);
6148 return rb_file_dirname_n(fname, 1);
6152rb_file_dirname_n(
VALUE fname,
int n)
6154 const char *name, *root, *p, *end;
6157 if (n < 0) rb_raise(rb_eArgError,
"negative level: %d", n);
6158 CheckPath(fname, name);
6159 end = name + RSTRING_LEN(fname);
6161 bool mb_enc = !rb_str_enc_fastpath(fname);
6164 root = skiproot(name, end);
6166 if (root > name + 1 && isdirsep(*name))
6167 root = skipprefix(name = root - 2, end, mb_enc, enc);
6169 if (root > name + 1)
6172 if (n > (end - root + 1) / 2) {
6178 if (!(p = strrdirsep(root, p, mb_enc, enc))) {
6187 return rb_enc_str_new(
".", 1, enc);
6189#ifdef DOSISH_DRIVE_LETTER
6190 if (name + 3 < end && has_drive_letter(name) && isdirsep(*(name + 2))) {
6191 const char *top = skiproot(name + 2, end);
6192 dirname = rb_enc_str_new(name, 3, enc);
6197 dirname = rb_enc_str_new(name, p - name, enc);
6198#ifdef DOSISH_DRIVE_LETTER
6199 if (root == name + 2 && p == root && name[1] ==
':')
6205static inline const char *
6208 const char *p, *e, *end = name + (
len ? *
len : (
rb_len_t)strlen(name));
6210 p = strrdirsep(name, end, mb_enc, enc);
6214 do name = ++p;
while (isdirsep(*p));
6217 while (*p && *p ==
'.') p++;
6219 if (*p ==
'.' || istrailinggarbage(*p)) {
6221 const char *last = p++, *dot = last;
6222 while (istrailinggarbage(*p)) {
6223 if (*p ==
'.') dot = p;
6226 if (!*p || isADS(*p)) {
6230 if (*last ==
'.' || dot > last) e = dot;
6237 else if (isADS(*p)) {
6241 else if (isdirsep(*p))
6243 Inc(p, end, mb_enc, enc);
6248 if (!e || e == name)
6273 return enc_find_extname(name,
len, enc_mbclen_needed(enc), enc);
6320 CheckPath(fname, name);
6324 return rb_enc_str_new(0, 0, rb_str_enc_get(fname));
6327 bool mb_enc = !rb_str_enc_fastpath(fname);
6330 const char *ext = enc_find_extname(name, &
len, mb_enc, enc);
6331 return rb_enc_str_new(ext,
len, enc);
6355 return rb_get_path(fname);
6387file_inspect_join(
VALUE ary,
VALUE arg,
int recur)
6389 if (recur || ary == arg) rb_raise(rb_eArgError,
"recursive array");
6390 return rb_file_join_ary(arg);
6394rb_file_join_ary(
VALUE ary)
6398 const char *name, *tail;
6408 check_path_encoding(tmp);
6409 len += RSTRING_LEN(tmp);
6417 RBASIC_CLEAR_CLASS(result);
6420 switch (OBJ_BUILTIN_TYPE(tmp)) {
6422 if (!checked) check_path_encoding(tmp);
6427 rb_raise(rb_eArgError,
"recursive array");
6439 rb_enc_copy(result, tmp);
6442 tail = chompdirsep(name, name +
len,
true, rb_enc_get(result));
6443 if (RSTRING_LEN(tmp) > 0 && isdirsep(RSTRING_PTR(tmp)[0])) {
6446 else if (tail == name +
len) {
6450 enc = fs_enc_check(result, tmp);
6452 rb_enc_associate(result, enc);
6465 for (i = 0; i < argc; i++) {
6466 VALUE tmp = args[i];
6468 size += RSTRING_LEN(tmp);
6481 const char *name = RSTRING_PTR(result);
6482 for (i = 1; i < argc; i++) {
6483 VALUE tmp = args[i];
6490 if (tmp_len > 0 && isdirsep(tmp_s[0])) {
6493 while (chomp > 0 && isdirsep(name[chomp - 1])) {
6498 else if (
len < 1 || !isdirsep(name[
len - 1])) {
6505 rb_enc_associate(result, new_enc);
6506 encidx = rb_enc_to_index(new_enc);
6512 rb_str_null_check(result);
6519 if (RB_UNLIKELY(argc == 0)) {
6523 VALUE result = rb_file_join_fastpath(argc, args);
6524 if (RB_LIKELY(result)) {
6544rb_file_s_join(
int argc,
VALUE *argv,
VALUE klass)
6546 return rb_file_join(argc, argv);
6549#if defined(HAVE_TRUNCATE)
6550struct truncate_arg {
6556nogvl_truncate(
void *ptr)
6558 struct truncate_arg *ta = ptr;
6559 return (
void *)(
VALUE)truncate(ta->path, ta->pos);
6591 struct truncate_arg ta;
6596 path = rb_str_encode_ospath(path);
6599 r = IO_WITHOUT_GVL_INT(nogvl_truncate, &ta);
6601 rb_sys_fail_path(path);
6605#define rb_file_s_truncate rb_f_notimplement
6608#if defined(HAVE_FTRUNCATE)
6609struct ftruncate_arg {
6615nogvl_ftruncate(
void *ptr)
6617 struct ftruncate_arg *fa = ptr;
6619 return (
VALUE)ftruncate(fa->fd, fa->pos);
6660 struct ftruncate_arg fa;
6667 rb_io_flush_raw(obj, 0);
6669 if ((
int)rb_io_blocking_region(fptr, nogvl_ftruncate, &fa) < 0) {
6670 rb_sys_fail_path(fptr->
pathv);
6675#define rb_file_truncate rb_f_notimplement
6692#include <winerror.h>
6696rb_thread_flock(
void *data)
6699 int old_errno =
errno;
6701 int *op = data, ret = flock(op[0], op[1]);
6704 if (GetLastError() == ERROR_NOT_LOCKED) {
6762 op[1] = op1 =
NUM2INT(operation);
6767 rb_io_flush_raw(obj, 0);
6769 while ((
int)rb_io_blocking_region(fptr, rb_thread_flock, op) < 0) {
6774#if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN
6777 if (op1 & LOCK_NB)
return Qfalse;
6780 time.tv_usec = 100 * 1000;
6786#if defined(ERESTART)
6792 rb_syserr_fail_path(e, fptr->
pathv);
6799test_check(
int n,
int argc,
VALUE *argv)
6805 for (i=1; i<n; i++) {
6812#define CHECK(n) test_check((n), argc, argv)
6904 if (strchr(
"bcdefgGkloOprRsSuwWxXz", cmd)) {
6908 return rb_file_blockdev_p(0, argv[1]);
6911 return rb_file_chardev_p(0, argv[1]);
6914 return rb_file_directory_p(0, argv[1]);
6917 return rb_file_exist_p(0, argv[1]);
6920 return rb_file_file_p(0, argv[1]);
6923 return rb_file_sgid_p(0, argv[1]);
6926 return rb_file_grpowned_p(0, argv[1]);
6929 return rb_file_sticky_p(0, argv[1]);
6932 return rb_file_symlink_p(0, argv[1]);
6935 return rb_file_owned_p(0, argv[1]);
6938 return rb_file_rowned_p(0, argv[1]);
6941 return rb_file_pipe_p(0, argv[1]);
6944 return rb_file_readable_p(0, argv[1]);
6947 return rb_file_readable_real_p(0, argv[1]);
6950 return rb_file_size_p(0, argv[1]);
6953 return rb_file_socket_p(0, argv[1]);
6956 return rb_file_suid_p(0, argv[1]);
6959 return rb_file_writable_p(0, argv[1]);
6962 return rb_file_writable_real_p(0, argv[1]);
6965 return rb_file_executable_p(0, argv[1]);
6968 return rb_file_executable_real_p(0, argv[1]);
6971 return rb_file_zero_p(0, argv[1]);
6975 if (strchr(
"MAC", cmd)) {
6977 VALUE fname = argv[1];
6980 if (
rb_stat(fname, &st) == -1) {
6983 rb_syserr_fail_path(e, fname);
6988 return stat_atime(&st);
6990 return stat_mtime(&st);
6992 return stat_ctime(&st);
6998 return rb_file_identical_p(0, argv[1], argv[2]);
7001 if (strchr(
"=<>", cmd)) {
7002 struct stat st1, st2;
7009 t1 = stat_mtimespec(&st1);
7010 t2 = stat_mtimespec(&st2);
7014 if (t1.tv_sec == t2.tv_sec && t1.tv_nsec == t2.tv_nsec)
return Qtrue;
7018 if (t1.tv_sec > t2.tv_sec)
return Qtrue;
7019 if (t1.tv_sec == t2.tv_sec && t1.tv_nsec > t2.tv_nsec)
return Qtrue;
7023 if (t1.tv_sec < t2.tv_sec)
return Qtrue;
7024 if (t1.tv_sec == t2.tv_sec && t1.tv_nsec < t2.tv_nsec)
return Qtrue;
7031 rb_raise(rb_eArgError,
"unknown command '%s%c'", cmd ==
'\'' || cmd ==
'\\' ?
"\\" :
"", cmd);
7034 rb_raise(rb_eArgError,
"unknown command \"\\x%02X\"", cmd);
7083rb_stat_s_alloc(
VALUE klass)
7086 stat_alloc(klass, &obj);
7112 fname = rb_str_encode_ospath(fname);
7114 rb_sys_fail_path(fname);
7121 rb_st->initialized =
true;
7138 *copy_rb_st = *orig_rb_st;
7184rb_stat_ftype(
VALUE obj)
7186 return rb_file_ftype(get_stat(obj)->ST_(mode));
7208 if (S_ISDIR(get_stat(obj)->ST_(mode)))
return Qtrue;
7237 if (S_ISFIFO(get_stat(obj)->ST_(mode)))
return Qtrue;
7274 if (S_ISLNK(get_stat(obj)->ST_(mode)))
return Qtrue;
7306 if (S_ISSOCK(get_stat(obj)->ST_(mode)))
return Qtrue;
7336 if (S_ISBLK(get_stat(obj)->ST_(mode)))
return Qtrue;
7364 if (S_ISCHR(get_stat(obj)->ST_(mode)))
return Qtrue;
7395rb_stat_owned(
VALUE obj)
7397 if (get_stat(obj)->ST_(uid) == geteuid())
return Qtrue;
7402rb_stat_rowned(
VALUE obj)
7404 if (get_stat(obj)->ST_(uid) == getuid())
return Qtrue;
7431rb_stat_grpowned(
VALUE obj)
7434 if (rb_group_member(get_stat(obj)->ST_(gid)))
return Qtrue;
7463 rb_io_stat_data *st = get_stat(obj);
7466 if (geteuid() == 0)
return Qtrue;
7469 if (rb_stat_owned(obj))
7470 return RBOOL(st->ST_(mode) & S_IRUSR);
7473 if (rb_stat_grpowned(obj))
7474 return RBOOL(st->ST_(mode) & S_IRGRP);
7477 if (!(st->ST_(mode) & S_IROTH))
return Qfalse;
7495 rb_io_stat_data *st = get_stat(obj);
7498 if (getuid() == 0)
return Qtrue;
7501 if (rb_stat_rowned(obj))
7502 return RBOOL(st->ST_(mode) & S_IRUSR);
7505 if (rb_group_member(get_stat(obj)->ST_(gid)))
7506 return RBOOL(st->ST_(mode) & S_IRGRP);
7509 if (!(st->ST_(mode) & S_IROTH))
return Qfalse;
7541rb_stat_wr(
VALUE obj)
7544 rb_io_stat_data *st = get_stat(obj);
7545 if ((st->ST_(mode) & (S_IROTH)) == S_IROTH) {
7546 return UINT2NUM(st->ST_(mode) & (S_IRUGO|S_IWUGO|S_IXUGO));
7578 rb_io_stat_data *st = get_stat(obj);
7581 if (geteuid() == 0)
return Qtrue;
7584 if (rb_stat_owned(obj))
7585 return RBOOL(st->ST_(mode) & S_IWUSR);
7588 if (rb_stat_grpowned(obj))
7589 return RBOOL(st->ST_(mode) & S_IWGRP);
7592 if (!(st->ST_(mode) & S_IWOTH))
return Qfalse;
7613 rb_io_stat_data *st = get_stat(obj);
7616 if (getuid() == 0)
return Qtrue;
7619 if (rb_stat_rowned(obj))
7620 return RBOOL(st->ST_(mode) & S_IWUSR);
7623 if (rb_group_member(get_stat(obj)->ST_(gid)))
7624 return RBOOL(st->ST_(mode) & S_IWGRP);
7627 if (!(st->ST_(mode) & S_IWOTH))
return Qfalse;
7656rb_stat_ww(
VALUE obj)
7659 rb_io_stat_data *st = get_stat(obj);
7660 if ((st->ST_(mode) & (S_IWOTH)) == S_IWOTH) {
7661 return UINT2NUM(st->ST_(mode) & (S_IRUGO|S_IWUGO|S_IXUGO));
7692 rb_io_stat_data *st = get_stat(obj);
7695 if (geteuid() == 0) {
7696 return RBOOL(st->ST_(mode) & S_IXUGO);
7700 if (rb_stat_owned(obj))
7701 return RBOOL(st->ST_(mode) & S_IXUSR);
7704 if (rb_stat_grpowned(obj))
7705 return RBOOL(st->ST_(mode) & S_IXGRP);
7708 if (!(st->ST_(mode) & S_IXOTH))
return Qfalse;
7725 rb_io_stat_data *st = get_stat(obj);
7728 if (getuid() == 0) {
7729 return RBOOL(st->ST_(mode) & S_IXUGO);
7733 if (rb_stat_rowned(obj))
7734 return RBOOL(st->ST_(mode) & S_IXUSR);
7737 if (rb_group_member(get_stat(obj)->ST_(gid)))
7738 return RBOOL(st->ST_(mode) & S_IXGRP);
7741 if (!(st->ST_(mode) & S_IXOTH))
return Qfalse;
7765 if (S_ISREG(get_stat(obj)->ST_(mode)))
return Qtrue;
7810 if (get_stat(obj)->ST_(size) == 0)
return Qtrue;
7842 rb_off_t size = get_stat(obj)->ST_(size);
7844 if (size == 0)
return Qnil;
7877rb_stat_suid(
VALUE obj)
7880 if (get_stat(obj)->ST_(mode) & S_ISUID)
return Qtrue;
7914rb_stat_sgid(
VALUE obj)
7917 if (get_stat(obj)->ST_(mode) & S_ISGID)
return Qtrue;
7951rb_stat_sticky(
VALUE obj)
7954 if (get_stat(obj)->ST_(mode) & S_ISVTX)
return Qtrue;
7959#if !defined HAVE_MKFIFO && defined HAVE_MKNOD && defined S_IFIFO
7960#define mkfifo(path, mode) mknod(path, (mode)&~S_IFMT|S_IFIFO, 0)
7971nogvl_mkfifo(
void *ptr)
7973 struct mkfifo_arg *ma = ptr;
7975 return (
void *)(
VALUE)mkfifo(ma->path, ma->mode);
8003 struct mkfifo_arg ma;
8012 path = rb_str_encode_ospath(path);
8013 ma.path = RSTRING_PTR(path);
8014 if (IO_WITHOUT_GVL(nogvl_mkfifo, &ma)) {
8015 rb_sys_fail_path(path);
8020#define rb_file_s_mkfifo rb_f_notimplement
8023static VALUE rb_mFConst;
8026rb_file_const(
const char *name,
VALUE value)
8028 rb_define_const(rb_mFConst, name, value);
8032rb_is_absolute_path(
const char *path)
8034#ifdef DOSISH_DRIVE_LETTER
8035 if (has_drive_letter(path) && isdirsep(path[2]))
return 1;
8038 if (isdirsep(path[0]) && isdirsep(path[1]))
return 1;
8041 if (path[0] ==
'/')
return 1;
8047ruby_is_fd_loadable(
int fd)
8054 if (fstat(fd, &st) < 0)
8057 if (S_ISREG(st.st_mode))
8060 if (S_ISFIFO(st.st_mode) || S_ISCHR(st.st_mode))
8063 if (S_ISDIR(st.st_mode))
8074rb_file_load_ok(
const char *path)
8081 int mode = (O_RDONLY |
8082#if defined O_NONBLOCK
8084#elif defined O_NDELAY
8090 if (!rb_gc_for_fd(
errno))
return 0;
8092 if (fd < 0)
return 0;
8095 ret = ruby_is_fd_loadable(fd);
8102is_explicit_relative(
const char *path)
8104 if (*path++ !=
'.')
return 0;
8105 if (*path ==
'.') path++;
8106 return isdirsep(*path);
8112 int encidx = rb_enc_get_index(orig);
8113 if (encidx == ENCINDEX_ASCII_8BIT || encidx == ENCINDEX_US_ASCII)
8114 encidx = rb_filesystem_encindex();
8115 rb_enc_associate_index(path, encidx);
8123nav_component_p(
const char *s,
const char *send)
8125 if ((send - s) >= 2 && s[0] ==
'.') {
8126 return s[1] ==
'.' || isdirsep(s[1]);
8132fname_need_expansion_p(
VALUE fname)
8134 const char *s = RSTRING_PTR(fname);
8136 const char *send = s +
len;
8138 if (nav_component_p(s, send)) {
8143 bool mbenc = enc_mbclen_needed(enc);
8145 s = enc_path_next(s, send, mbenc, enc);
8147 if (nav_component_p(s, send)) {
8151 s = enc_path_next(s, send, mbenc, enc);
8157expand_feature(
VALUE fname,
VALUE dname,
VALUE buffer,
bool need_expansion)
8159 rb_len_t dname_len = RSTRING_LEN(dname);
8160 const char *dname_ptr = RSTRING_PTR(dname);
8164 if (need_expansion || dname_ptr[0] ==
'~') {
8165 rb_file_expand_path_internal(fname, dname, 0, 0, buffer);
8170 if (!isdirsep(dname_ptr[dname_len - 1])) {
8179rb_find_file_ext(
VALUE *filep,
const char *
const *ext)
8182 VALUE fname = *filep;
8186 if (!ext[0])
return 0;
8189 fname = file_expand_path_1(fname, DLEXT_MAXLEN);
8190 f = RSTRING_PTR(fname);
8195 if (expanded || rb_is_absolute_path(f) || is_explicit_relative(f)) {
8196 if (!expanded) fname = file_expand_path_1(fname, DLEXT_MAXLEN);
8197 fnlen = RSTRING_LEN(fname);
8198 for (i=0; ext[i]; i++) {
8200 if (rb_file_load_ok(RSTRING_PTR(fname))) {
8201 *filep = copy_path_class(fname, *filep);
8209 rb_len_t expanded_load_path_maxlen;
8210 VALUE load_path = rb_get_expanded_load_path(&expanded_load_path_maxlen);
8211 if (!load_path)
return 0;
8214 RBASIC_CLEAR_CLASS(fname);
8215 fnlen = RSTRING_LEN(fname);
8216 bool need_expansion = fname_need_expansion_p(fname);
8219 rb_enc_associate_index(tmp, rb_usascii_encindex());
8221 for (j=0; ext[j]; j++) {
8223 for (i = 0; i <
RARRAY_LEN(load_path); i++) {
8225 if (!RSTRING_LEN(dname))
continue;
8226 expand_feature(fname, dname, tmp, need_expansion);
8228 if (rb_file_load_ok(RSTRING_PTR(tmp))) {
8229 *filep = copy_path_class(tmp, *filep);
8235 rb_str_resize(tmp, 0);
8248 path = copy_path_class(file_expand_path_1(path, 0), path);
8249 f = RSTRING_PTR(path);
8253 if (expanded || rb_is_absolute_path(f) || is_explicit_relative(f)) {
8254 if (!rb_file_load_ok(f))
return 0;
8256 path = copy_path_class(file_expand_path_1(path, 0), path);
8260 rb_len_t expanded_load_path_maxlen;
8261 VALUE load_path = rb_get_expanded_load_path(&expanded_load_path_maxlen);
8264 bool need_expansion = fname_need_expansion_p(path);
8266 rb_enc_associate_index(tmp, rb_usascii_encindex());
8269 if (!RSTRING_LEN(dname))
continue;
8270 expand_feature(path, dname, tmp, need_expansion);
8272 if (rb_file_load_ok(RSTRING_PTR(tmp))) {
8273 return copy_path_class(tmp, path);
8276 rb_str_resize(tmp, 0);
8284#define define_filetest_function(name, func, argc) do { \
8285 rb_define_module_function(rb_mFileTest, name, func, argc); \
8286 rb_define_singleton_method(rb_cFile, name, func, argc); \
8289const char ruby_null_device[] =
9092#if defined(__APPLE__) && defined(HAVE_WORKING_FORK)
9093 rb_CFString_class_initialize_before_fork();
9101 define_filetest_function(
"directory?", rb_file_directory_p, 1);
9102 define_filetest_function(
"exist?", rb_file_exist_p, 1);
9103 define_filetest_function(
"readable?", rb_file_readable_p, 1);
9104 define_filetest_function(
"readable_real?", rb_file_readable_real_p, 1);
9105 define_filetest_function(
"world_readable?", rb_file_world_readable_p, 1);
9106 define_filetest_function(
"writable?", rb_file_writable_p, 1);
9107 define_filetest_function(
"writable_real?", rb_file_writable_real_p, 1);
9108 define_filetest_function(
"world_writable?", rb_file_world_writable_p, 1);
9109 define_filetest_function(
"executable?", rb_file_executable_p, 1);
9110 define_filetest_function(
"executable_real?", rb_file_executable_real_p, 1);
9111 define_filetest_function(
"file?", rb_file_file_p, 1);
9112 define_filetest_function(
"zero?", rb_file_zero_p, 1);
9113 define_filetest_function(
"empty?", rb_file_zero_p, 1);
9114 define_filetest_function(
"size?", rb_file_size_p, 1);
9115 define_filetest_function(
"size", rb_file_s_size, 1);
9116 define_filetest_function(
"owned?", rb_file_owned_p, 1);
9117 define_filetest_function(
"grpowned?", rb_file_grpowned_p, 1);
9119 define_filetest_function(
"pipe?", rb_file_pipe_p, 1);
9120 define_filetest_function(
"symlink?", rb_file_symlink_p, 1);
9121 define_filetest_function(
"socket?", rb_file_socket_p, 1);
9123 define_filetest_function(
"blockdev?", rb_file_blockdev_p, 1);
9124 define_filetest_function(
"chardev?", rb_file_chardev_p, 1);
9126 define_filetest_function(
"setuid?", rb_file_suid_p, 1);
9127 define_filetest_function(
"setgid?", rb_file_sgid_p, 1);
9128 define_filetest_function(
"sticky?", rb_file_sticky_p, 1);
9130 define_filetest_function(
"identical?", rb_file_identical_p, 2);
9168 separator = rb_fstring_lit(
"/");
9170 rb_define_const(
rb_cFile,
"Separator", separator);
9172 rb_define_const(
rb_cFile,
"SEPARATOR", separator);
9521 rb_mFConst = rb_define_module_under(
rb_cFile,
"Constants");
9524 rb_define_const(rb_mFConst,
"RDONLY",
INT2FIX(O_RDONLY));
9526 rb_define_const(rb_mFConst,
"WRONLY",
INT2FIX(O_WRONLY));
9528 rb_define_const(rb_mFConst,
"RDWR",
INT2FIX(O_RDWR));
9530 rb_define_const(rb_mFConst,
"APPEND",
INT2FIX(O_APPEND));
9532 rb_define_const(rb_mFConst,
"CREAT",
INT2FIX(O_CREAT));
9534 rb_define_const(rb_mFConst,
"EXCL",
INT2FIX(O_EXCL));
9535#if defined(O_NDELAY) || defined(O_NONBLOCK)
9537# define O_NONBLOCK O_NDELAY
9540 rb_define_const(rb_mFConst,
"NONBLOCK",
INT2FIX(O_NONBLOCK));
9543 rb_define_const(rb_mFConst,
"TRUNC",
INT2FIX(O_TRUNC));
9546 rb_define_const(rb_mFConst,
"NOCTTY",
INT2FIX(O_NOCTTY));
9552 rb_define_const(rb_mFConst,
"BINARY",
INT2FIX(O_BINARY));
9553#ifndef O_SHARE_DELETE
9554# define O_SHARE_DELETE 0
9557 rb_define_const(rb_mFConst,
"SHARE_DELETE",
INT2FIX(O_SHARE_DELETE));
9560 rb_define_const(rb_mFConst,
"SYNC",
INT2FIX(O_SYNC));
9564 rb_define_const(rb_mFConst,
"DSYNC",
INT2FIX(O_DSYNC));
9568 rb_define_const(rb_mFConst,
"RSYNC",
INT2FIX(O_RSYNC));
9572 rb_define_const(rb_mFConst,
"NOFOLLOW",
INT2FIX(O_NOFOLLOW));
9576 rb_define_const(rb_mFConst,
"NOATIME",
INT2FIX(O_NOATIME));
9580 rb_define_const(rb_mFConst,
"DIRECT",
INT2FIX(O_DIRECT));
9584 rb_define_const(rb_mFConst,
"TMPFILE",
INT2FIX(O_TMPFILE));
9588 rb_define_const(rb_mFConst,
"LOCK_SH",
INT2FIX(LOCK_SH));
9590 rb_define_const(rb_mFConst,
"LOCK_EX",
INT2FIX(LOCK_EX));
9592 rb_define_const(rb_mFConst,
"LOCK_UN",
INT2FIX(LOCK_UN));
9594 rb_define_const(rb_mFConst,
"LOCK_NB",
INT2FIX(LOCK_NB));
9597 rb_define_const(rb_mFConst,
"NULL", rb_fstring_cstr(ruby_null_device));
#define RUBY_ASSERT(...)
Asserts that the given expression is truthy if and only if RUBY_DEBUG is truthy.
#define rb_define_method(klass, mid, func, arity)
Defines klass#mid.
#define rb_define_singleton_method(klass, mid, func, arity)
Defines klass.mid.
#define rb_define_global_function(mid, func, arity)
Defines rb_mKernel #mid.
#define PATH_SEP
The delimiter of PATH environment variable.
#define GIDT2NUM
Converts a C's gid_t into an instance of rb_cInteger.
#define NUM2GIDT
Converts an instance of rb_cNumeric into C's gid_t.
void rb_include_module(VALUE klass, VALUE module)
Includes a module to a class.
#define ENCODING_SET_INLINED(obj, i)
Old name of RB_ENCODING_SET_INLINED.
#define ENC_CODERANGE_7BIT
Old name of RUBY_ENC_CODERANGE_7BIT.
#define T_FILE
Old name of RUBY_T_FILE.
#define rb_str_buf_cat2
Old name of rb_usascii_str_new_cstr.
#define NUM2ULONG
Old name of RB_NUM2ULONG.
#define ALLOCV
Old name of RB_ALLOCV.
#define OBJ_INIT_COPY(obj, orig)
Old name of RB_OBJ_INIT_COPY.
#define T_STRING
Old name of RUBY_T_STRING.
#define xfree
Old name of ruby_xfree.
#define Qundef
Old name of RUBY_Qundef.
#define INT2FIX
Old name of RB_INT2FIX.
#define rb_str_cat2
Old name of rb_str_cat_cstr.
#define ID2SYM
Old name of RB_ID2SYM.
#define rb_str_buf_new2
Old name of rb_str_buf_new_cstr.
#define OBJ_FREEZE
Old name of RB_OBJ_FREEZE.
#define ULONG2NUM
Old name of RB_ULONG2NUM.
#define UNREACHABLE_RETURN
Old name of RBIMPL_UNREACHABLE_RETURN.
#define ENCODING_GET(obj)
Old name of RB_ENCODING_GET.
#define LONG2FIX
Old name of RB_INT2FIX.
#define MBCLEN_CHARFOUND_LEN(ret)
Old name of ONIGENC_MBCLEN_CHARFOUND_LEN.
#define rb_usascii_str_new2
Old name of rb_usascii_str_new_cstr.
#define ISALPHA
Old name of rb_isalpha.
#define ULL2NUM
Old name of RB_ULL2NUM.
#define TOLOWER
Old name of rb_tolower.
#define Qtrue
Old name of RUBY_Qtrue.
#define NUM2INT
Old name of RB_NUM2INT.
#define Qnil
Old name of RUBY_Qnil.
#define Qfalse
Old name of RUBY_Qfalse.
#define T_ARRAY
Old name of RUBY_T_ARRAY.
#define NIL_P
Old name of RB_NIL_P.
#define ALLOCV_N
Old name of RB_ALLOCV_N.
#define MBCLEN_CHARFOUND_P(ret)
Old name of ONIGENC_MBCLEN_CHARFOUND_P.
#define ISPRINT
Old name of rb_isprint.
#define NUM2CHR
Old name of RB_NUM2CHR.
#define ENCODING_GET_INLINED(obj)
Old name of RB_ENCODING_GET_INLINED.
#define ENC_CODERANGE_CLEAR(obj)
Old name of RB_ENC_CODERANGE_CLEAR.
#define UINT2NUM
Old name of RB_UINT2NUM.
#define CONST_ID
Old name of RUBY_CONST_ID.
#define ALLOCV_END
Old name of RB_ALLOCV_END.
VALUE rb_eNotImpError
NotImplementedError exception.
void rb_exc_raise(VALUE mesg)
Raises an exception in the current thread.
VALUE rb_eIOError
IOError exception.
VALUE rb_eTypeError
TypeError exception.
VALUE rb_eEncCompatError
Encoding::CompatibilityError exception.
void rb_enc_raise(rb_encoding *enc, VALUE exc, const char *fmt,...)
Identical to rb_raise(), except it additionally takes an encoding.
VALUE rb_eSystemCallError
SystemCallError exception.
VALUE rb_cObject
Object class.
VALUE rb_class_new_instance(int argc, const VALUE *argv, VALUE klass)
Allocates, then initialises an instance of the given class.
VALUE rb_cStat
File::Stat class.
VALUE rb_obj_class(VALUE obj)
Queries the class of an object.
VALUE rb_inspect(VALUE obj)
Generates a human-readable textual representation of the given object.
VALUE rb_mFileTest
FileTest module.
VALUE rb_equal(VALUE lhs, VALUE rhs)
This function is an optimised version of calling #==.
VALUE rb_obj_is_kind_of(VALUE obj, VALUE klass)
Queries if the given object is an instance (of possibly descendants) of the given class.
VALUE rb_obj_freeze(VALUE obj)
Same as RB_OBJ_FREEZE(), but returns the given object.
VALUE rb_mComparable
Comparable module.
VALUE rb_cFile
File class.
VALUE rb_cString
String class.
static char * rb_enc_left_char_head(const char *s, const char *p, const char *e, rb_encoding *enc)
Queries the left boundary of a character.
VALUE rb_str_conv_enc(VALUE str, rb_encoding *from, rb_encoding *to)
Encoding conversion main routine.
VALUE rb_enc_str_new_cstr(const char *ptr, rb_encoding *enc)
Identical to rb_enc_str_new(), except it assumes the passed pointer is a pointer to a C string.
int rb_enc_str_asciionly_p(VALUE str)
Queries if the passed string is "ASCII only".
VALUE rb_funcall(VALUE recv, ID mid, int n,...)
Calls a method.
VALUE rb_assoc_new(VALUE car, VALUE cdr)
Identical to rb_ary_new_from_values(), except it expects exactly two parameters.
VALUE rb_ary_new_from_values(rb_len_t n, const VALUE *elts)
Identical to rb_ary_new_from_args(), except how objects are passed.
#define INTEGER_PACK_NATIVE_BYTE_ORDER
Means either INTEGER_PACK_MSBYTE_FIRST or INTEGER_PACK_LSBYTE_FIRST, depending on the host processor'...
#define INTEGER_PACK_2COMP
Uses 2's complement representation.
#define INTEGER_PACK_LSWORD_FIRST
Stores/interprets the least significant word as the first word.
static int rb_check_arity(int argc, int min, int max)
Ensures that the passed integer is in the passed range.
void rb_update_max_fd(int fd)
Informs the interpreter that the passed fd can be the max.
int rb_cloexec_open(const char *pathname, int flags, mode_t mode)
Opens a file that closes on exec.
void rb_str_modify_expand(VALUE str, rb_len_t capa)
Identical to rb_str_modify(), except it additionally expands the capacity of the receiver.
VALUE rb_str_subseq(VALUE str, rb_len_t beg, rb_len_t len)
Identical to rb_str_substr(), except the numbers are interpreted as byte offsets instead of character...
VALUE rb_str_new_shared(VALUE str)
Identical to rb_str_new_cstr(), except it takes a Ruby's string instead of C's.
VALUE rb_str_plus(VALUE lhs, VALUE rhs)
Generates a new string, concatenating the former to the latter.
VALUE rb_str_append(VALUE dst, VALUE src)
Identical to rb_str_buf_append(), except it converts the right hand side before concatenating.
VALUE rb_str_tmp_new(rb_len_t len)
Allocates a "temporary" string.
#define rb_str_new(str, len)
Allocates an instance of rb_cString.
#define rb_str_buf_cat
Just another name of rb_str_cat.
#define rb_usascii_str_new(str, len)
Identical to rb_str_new, except it generates a string of "US ASCII" encoding.
size_t rb_str_capacity(VALUE str)
Queries the capacity of the given string.
VALUE rb_str_new_frozen(VALUE str)
Creates a frozen copy of the string, if necessary.
void rb_str_set_len(VALUE str, rb_len_t len)
Overwrites the length of the string.
VALUE rb_str_dup(VALUE str)
Duplicates a string.
VALUE rb_str_ellipsize(VALUE str, rb_len_t len)
Shortens str and adds three dots, an ellipsis, if it is longer than len characters.
VALUE rb_str_cat(VALUE dst, const char *src, rb_len_t srclen)
Destructively appends the passed contents to the string.
VALUE rb_str_replace(VALUE dst, VALUE src)
Replaces the contents of the former object with the stringised contents of the latter.
VALUE rb_str_buf_append(VALUE dst, VALUE src)
Identical to rb_str_cat_cstr(), except it takes Ruby's string instead of C's.
VALUE rb_str_inspect(VALUE str)
Generates a "readable" version of the receiver.
int rb_str_cmp(VALUE lhs, VALUE rhs)
Compares two strings, as in strcmp(3).
VALUE rb_str_buf_new(rb_len_t capa)
Allocates a "string buffer".
#define rb_str_dup_frozen
Just another name of rb_str_new_frozen.
#define rb_utf8_str_new(str, len)
Identical to rb_str_new, except it generates a string of "UTF-8" encoding.
#define rb_str_new_cstr(str)
Identical to rb_str_new, except it assumes the passed pointer is a pointer to a C string.
VALUE rb_exec_recursive(VALUE(*f)(VALUE g, VALUE h, int r), VALUE g, VALUE h)
"Recursion" API entry point.
void rb_thread_wait_for(struct timeval time)
Identical to rb_thread_sleep(), except it takes struct timeval instead.
VALUE rb_time_nano_new(time_t sec, long nsec)
Identical to rb_time_new(), except it accepts the time in nanoseconds resolution.
struct timespec rb_time_timespec(VALUE time)
Identical to rb_time_timeval(), except for return type.
void rb_define_alloc_func(VALUE klass, rb_alloc_func_t func)
Sets the allocator function of a class.
#define GetOpenFile
This is an old name of RB_IO_POINTER.
#define FMODE_WRITABLE
The IO is opened for writing.
#define RB_IO_POINTER(obj, fp)
Queries the underlying IO pointer.
void rb_io_check_closed(rb_io_t *fptr)
This badly named function asserts that the passed IO is open.
int len
Length of the buffer.
char * ruby_getcwd(void)
This is our own version of getcwd(3) that uses ruby_xmalloc() instead of system malloc (benefits our ...
#define RB_GC_GUARD(v)
Prevents premature destruction of local objects.
#define NUM2MODET
Converts a C's mode_t into an instance of rb_cInteger.
#define MODET2NUM
Converts an instance of rb_cNumeric into C's mode_t.
VALUE rb_rescue(type *q, VALUE w, type *e, VALUE r)
An equivalent of rescue clause.
Defines RBIMPL_ATTR_NONSTRING.
#define RBIMPL_ATTR_NONSTRING()
Wraps (or simulates) __attribute__((nonstring))
#define OFFT2NUM
Converts a C's off_t into an instance of rb_cInteger.
#define NUM2OFFT
Converts an instance of rb_cNumeric into C's off_t.
#define RARRAY_LEN
Just another name of rb_array_len.
#define RARRAY_AREF(a, i)
References an object in an array.
#define StringValue(v)
Ensures that the parameter object is a String.
#define StringValuePtr(v)
Identical to StringValue, except it returns a char*.
#define RSTRING_GETMEM(str, ptrvar, lenvar)
Convenient macro to obtain the contents and length at once.
#define StringValueCStr(v)
Identical to StringValuePtr, except it additionally checks for the contents for viability as a C stri...
#define RUBY_TYPED_DEFAULT_FREE
This is a value you can set to rb_data_type_struct::dfree.
#define TypedData_Get_Struct(obj, type, data_type, sval)
Obtains a C struct from inside of a wrapper Ruby object.
#define TypedData_Make_Struct(klass, type, data_type, sval)
Identical to TypedData_Wrap_Struct, except it allocates a new data region internally instead of takin...
const char * rb_obj_classname(VALUE obj)
Queries the name of the class of the passed object.
#define FilePathValue(v)
Ensures that the parameter object is a path.
#define errno
Ractor-aware version of errno.
#define FilePathStringValue(v)
This macro actually does the same thing as FilePathValue now.
#define RTEST
This is an old name of RB_TEST.
#define _(args)
This was a transition path from K&R to ANSI.
This is the struct that holds necessary info for a struct.
Ruby's IO, metadata and buffers.
enum rb_io_mode mode
mode flags: FMODE_XXXs
VALUE pathv
pathname for file
#define UIDT2NUM
Converts a C's uid_t into an instance of rb_cInteger.
#define NUM2UIDT
Converts an instance of rb_cNumeric into C's uid_t.
long rb_len_t
A signed integer type for lengths and indices of objects such as String and Array.
uintptr_t ID
Type that represents a Ruby identifier such as a variable name.
uintptr_t VALUE
Type that represents a Ruby object.
static bool RB_TYPE_P(VALUE obj, enum ruby_value_type t)
Queries if the given object is of given type.
#define RBIMPL_WARNING_IGNORED(flag)
Suppresses a warning.
#define RBIMPL_WARNING_PUSH()
Pushes compiler warning state.
#define RBIMPL_WARNING_POP()
Pops compiler warning state.