14 #include "ruby/internal/config.h"
17 # include "missing/file.h"
26 # include <sys/cygwin.h>
31 # if !(defined(__has_feature) && defined(__has_attribute))
36 # define API_AVAILABLE(...)
37 # define API_DEPRECATED(...)
39 # include <CoreFoundation/CFString.h>
46 #ifdef HAVE_SYS_TIME_H
47 # include <sys/time.h>
50 #ifdef HAVE_SYS_FILE_H
51 # include <sys/file.h>
56 #ifdef HAVE_SYS_PARAM_H
57 # include <sys/param.h>
60 # define MAXPATHLEN 1024
65 #elif defined HAVE_SYS_UTIME_H
66 # include <sys/utime.h>
73 #ifdef HAVE_SYS_SYSMACROS_H
74 # include <sys/sysmacros.h>
77 #include <sys/types.h>
80 #ifdef HAVE_SYS_MKDEV_H
81 # include <sys/mkdev.h>
84 #if defined(HAVE_FCNTL_H)
88 #if defined(HAVE_SYS_TIME_H)
89 # include <sys/time.h>
92 #if !defined HAVE_LSTAT && !defined lstat
98 # include "win32/file.h"
99 # define STAT(p, s) rb_w32_ustati128((p), (s))
101 # define lstat(p, s) rb_w32_ulstati128((p), (s))
103 # define access(p, m) rb_w32_uaccess((p), (m))
105 # define truncate(p, n) rb_w32_utruncate((p), (n))
107 # define chmod(p, m) rb_w32_uchmod((p), (m))
109 # define chown(p, o, g) rb_w32_uchown((p), (o), (g))
111 # define lchown(p, o, g) rb_w32_ulchown((p), (o), (g))
113 # define utimensat(s, p, t, f) rb_w32_uutimensat((s), (p), (t), (f))
115 # define link(f, t) rb_w32_ulink((f), (t))
117 # define unlink(p) rb_w32_uunlink(p)
119 # define readlink(f, t, l) rb_w32_ureadlink((f), (t), (l))
121 # define rename(f, t) rb_w32_urename((f), (t))
123 # define symlink(s, l) rb_w32_usymlink((s), (l))
125 # ifdef HAVE_REALPATH
128 # undef HAVE_REALPATH
131 # define STAT(p, s) stat((p), (s))
134 #if defined _WIN32 || defined __APPLE__
135 # define USE_OSPATH 1
136 # define TO_OSPATH(str) rb_str_encode_ospath(str)
138 # define USE_OSPATH 0
139 # define TO_OSPATH(str) (str)
143 #if defined DOSISH || defined __CYGWIN__
144 # define UTIME_EINVAL
148 #if defined HAVE_REALPATH && defined __sun && defined __SVR4
158 #include "encindex.h"
160 #include "internal.h"
161 #include "internal/compilers.h"
162 #include "internal/dir.h"
163 #include "internal/error.h"
164 #include "internal/file.h"
165 #include "internal/io.h"
166 #include "internal/load.h"
167 #include "internal/object.h"
168 #include "internal/process.h"
169 #include "internal/thread.h"
170 #include "internal/vm.h"
180 file_path_convert(
VALUE name)
185 if (ENCINDEX_US_ASCII != fname_encidx &&
186 ENCINDEX_ASCII_8BIT != fname_encidx &&
201 check_path_encoding(
VALUE str)
212 rb_get_path_check_to_string(
VALUE obj)
221 tmp = rb_check_funcall_default(obj, to_path, 0, 0, obj);
227 rb_get_path_check_convert(
VALUE obj)
229 obj = file_path_convert(obj);
231 check_path_encoding(obj);
232 if (!rb_str_to_cstr(obj)) {
248 return rb_get_path_check_convert(rb_get_path_check_to_string(obj));
256 #if 0 && defined _WIN32
257 if (encidx == ENCINDEX_ASCII_8BIT) {
261 if (encidx != ENCINDEX_ASCII_8BIT && encidx != ENCINDEX_UTF_8) {
271 # define NORMALIZE_UTF8PATH 1
273 # ifdef HAVE_WORKING_FORK
274 static CFMutableStringRef
275 mutable_CFString_new(CFStringRef *s,
const char *
ptr,
long len)
277 const CFAllocatorRef alloc = kCFAllocatorDefault;
278 *s = CFStringCreateWithBytesNoCopy(alloc, (
const UInt8 *)
ptr,
len,
279 kCFStringEncodingUTF8, FALSE,
281 return CFStringCreateMutableCopy(alloc,
len, *s);
284 # define mutable_CFString_release(m, s) (CFRelease(m), CFRelease(s))
287 rb_CFString_class_initialize_before_fork(
void)
310 const char small_str[] =
"/";
311 long len =
sizeof(small_str) - 1;
319 for (
int i = 0; i < 2; i++) {
320 CFMutableStringRef m = mutable_CFString_new(&s, small_str,
len);
321 mutable_CFString_release(m, s);
327 rb_str_append_normalized_ospath(
VALUE str,
const char *
ptr,
long len)
332 CFMutableStringRef m = mutable_CFString_new(&s,
ptr,
len);
335 CFStringNormalize(m, kCFStringNormalizationFormC);
336 all = CFRangeMake(0, CFStringGetLength(m));
337 CFStringGetBytes(m, all, kCFStringEncodingUTF8,
'?', FALSE, NULL, 0, &buflen);
339 CFStringGetBytes(m, all, kCFStringEncodingUTF8,
'?', FALSE,
340 (UInt8 *)(
RSTRING_PTR(str) + oldlen), buflen, &buflen);
342 mutable_CFString_release(m, s);
347 rb_str_normalize_ospath(
const char *
ptr,
long len)
350 const char *e =
ptr +
len;
361 static const char invalid[3] =
"\xEF\xBF\xBD";
362 rb_str_append_normalized_ospath(str, p1, p-p1);
370 if ((0x2000 <= c && c <= 0x2FFF) || (0xF900 <= c && c <= 0xFAFF) ||
371 (0x2F800 <= c && c <= 0x2FAFF)) {
373 rb_str_append_normalized_ospath(str, p1, p-p1);
384 rb_str_append_normalized_ospath(str, p1, p-p1);
391 ignored_char_p(
const char *p,
const char *e,
rb_encoding *enc)
394 if (p+3 > e)
return 0;
395 switch ((
unsigned char)*p) {
397 switch ((
unsigned char)p[1]) {
399 c = (
unsigned char)p[2];
401 if (c >= 0x8c && c <= 0x8f)
return 3;
403 if (c >= 0xaa && c <= 0xae)
return 3;
406 c = (
unsigned char)p[2];
408 if (c >= 0xaa && c <= 0xaf)
return 3;
414 if ((
unsigned char)p[1] == 0xbb &&
415 (
unsigned char)p[2] == 0xbf)
422 # define NORMALIZE_UTF8PATH 0
425 #define apply2args(n) (rb_check_arity(argc, n, UNLIMITED_ARGUMENTS), argc-=n)
436 int (*func)(
const char *,
void *);
442 no_gvl_apply2files(
void *
ptr)
446 for (aa->i = 0; aa->i < aa->argc; aa->i++) {
447 if (aa->func(aa->fn[aa->i].ptr, aa->arg) < 0) {
456 NORETURN(
static void utime_failed(
struct apply_arg *));
457 static int utime_internal(
const char *,
void *);
461 apply2files(
int (*func)(
const char *,
void *),
int argc,
VALUE *argv,
void *arg)
465 const long len = (long)(offsetof(
struct apply_arg, fn) + (size * argc));
473 for (aa->i = 0; aa->i < argc; aa->i++) {
478 aa->fn[aa->i].path = path;
481 IO_WITHOUT_GVL(no_gvl_apply2files, aa);
484 if (func == utime_internal) {
488 rb_syserr_fail_path(aa->errnum, aa->fn[aa->i].path);
503 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_EMBEDDABLE
512 stat_new_0(
VALUE klass,
const struct stat *st)
518 rb_st->initialized =
true;
538 static struct timespec stat_mtimespec(const struct stat *st);
559 struct timespec ts1 = stat_mtimespec(get_stat(
self));
560 struct timespec ts2 = stat_mtimespec(get_stat(other));
561 if (ts1.tv_sec == ts2.tv_sec) {
562 if (ts1.tv_nsec == ts2.tv_nsec)
return INT2FIX(0);
563 if (ts1.tv_nsec < ts2.tv_nsec)
return INT2FIX(-1);
566 if (ts1.tv_sec < ts2.tv_sec)
return INT2FIX(-1);
572 #define ST2UINT(val) ((val) & ~(~1UL << (sizeof(val) * CHAR_BIT - 1)))
575 # define NUM2DEVT(v) NUM2UINT(v)
578 # define DEVT2NUM(v) UINT2NUM(v)
580 #ifndef PRI_DEVT_PREFIX
581 # define PRI_DEVT_PREFIX ""
595 rb_stat_dev(
VALUE self)
597 #if SIZEOF_STRUCT_STAT_ST_DEV <= SIZEOF_DEV_T
598 return DEVT2NUM(get_stat(
self)->st_dev);
599 #elif SIZEOF_STRUCT_STAT_ST_DEV <= SIZEOF_LONG
600 return ULONG2NUM(get_stat(
self)->st_dev);
602 return ULL2NUM(get_stat(
self)->st_dev);
618 rb_stat_dev_major(
VALUE self)
621 return UINT2NUM(major(get_stat(
self)->st_dev));
639 rb_stat_dev_minor(
VALUE self)
642 return UINT2NUM(minor(get_stat(
self)->st_dev));
659 rb_stat_ino(
VALUE self)
661 #ifdef HAVE_STRUCT_STAT_ST_INOHIGH
664 SIZEOF_STRUCT_STAT_ST_INO, 0,
667 #elif SIZEOF_STRUCT_STAT_ST_INO > SIZEOF_LONG
668 return ULL2NUM(get_stat(
self)->st_ino);
670 return ULONG2NUM(get_stat(
self)->st_ino);
688 rb_stat_mode(
VALUE self)
690 return UINT2NUM(ST2UINT(get_stat(
self)->st_mode));
706 rb_stat_nlink(
VALUE self)
709 const struct stat *
ptr = get_stat(
self);
711 if (
sizeof(
ptr->st_nlink) <=
sizeof(
int)) {
714 else if (
sizeof(
ptr->st_nlink) ==
sizeof(
long)) {
721 rb_bug(
":FIXME: don't know what to do");
736 rb_stat_uid(
VALUE self)
738 return UIDT2NUM(get_stat(
self)->st_uid);
752 rb_stat_gid(
VALUE self)
754 return GIDT2NUM(get_stat(
self)->st_gid);
770 rb_stat_rdev(
VALUE self)
772 #ifdef HAVE_STRUCT_STAT_ST_RDEV
773 # if SIZEOF_STRUCT_STAT_ST_RDEV <= SIZEOF_DEV_T
774 return DEVT2NUM(get_stat(
self)->st_rdev);
775 # elif SIZEOF_STRUCT_STAT_ST_RDEV <= SIZEOF_LONG
776 return ULONG2NUM(get_stat(
self)->st_rdev);
778 return ULL2NUM(get_stat(
self)->st_rdev);
797 rb_stat_rdev_major(
VALUE self)
799 #if defined(HAVE_STRUCT_STAT_ST_RDEV) && defined(major)
800 return UINT2NUM(major(get_stat(
self)->st_rdev));
818 rb_stat_rdev_minor(
VALUE self)
820 #if defined(HAVE_STRUCT_STAT_ST_RDEV) && defined(minor)
821 return UINT2NUM(minor(get_stat(
self)->st_rdev));
837 rb_stat_size(
VALUE self)
839 return OFFT2NUM(get_stat(
self)->st_size);
854 rb_stat_blksize(
VALUE self)
856 #ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
857 return ULONG2NUM(get_stat(
self)->st_blksize);
875 rb_stat_blocks(
VALUE self)
877 #ifdef HAVE_STRUCT_STAT_ST_BLOCKS
878 # if SIZEOF_STRUCT_STAT_ST_BLOCKS > SIZEOF_LONG
879 return ULL2NUM(get_stat(
self)->st_blocks);
881 return ULONG2NUM(get_stat(
self)->st_blocks);
889 stat_atimespec(const struct stat *st)
892 ts.tv_sec = st->st_atime;
893 #if defined(HAVE_STRUCT_STAT_ST_ATIM)
894 ts.tv_nsec = st->st_atim.tv_nsec;
895 #elif defined(HAVE_STRUCT_STAT_ST_ATIMESPEC)
896 ts.tv_nsec = st->st_atimespec.tv_nsec;
897 #elif defined(HAVE_STRUCT_STAT_ST_ATIMENSEC)
898 ts.tv_nsec = (long)st->st_atimensec;
912 stat_atime(
const struct stat *st)
914 return stat_time(stat_atimespec(st));
918 stat_mtimespec(const struct stat *st)
921 ts.tv_sec = st->st_mtime;
922 #if defined(HAVE_STRUCT_STAT_ST_MTIM)
923 ts.tv_nsec = st->st_mtim.tv_nsec;
924 #elif defined(HAVE_STRUCT_STAT_ST_MTIMESPEC)
925 ts.tv_nsec = st->st_mtimespec.tv_nsec;
926 #elif defined(HAVE_STRUCT_STAT_ST_MTIMENSEC)
927 ts.tv_nsec = (long)st->st_mtimensec;
935 stat_mtime(
const struct stat *st)
937 return stat_time(stat_mtimespec(st));
941 stat_ctimespec(const struct stat *st)
944 ts.tv_sec = st->st_ctime;
945 #if defined(HAVE_STRUCT_STAT_ST_CTIM)
946 ts.tv_nsec = st->st_ctim.tv_nsec;
947 #elif defined(HAVE_STRUCT_STAT_ST_CTIMESPEC)
948 ts.tv_nsec = st->st_ctimespec.tv_nsec;
949 #elif defined(HAVE_STRUCT_STAT_ST_CTIMENSEC)
950 ts.tv_nsec = (long)st->st_ctimensec;
958 stat_ctime(
const struct stat *st)
960 return stat_time(stat_ctimespec(st));
963 #define HAVE_STAT_BIRTHTIME
964 #if defined(HAVE_STRUCT_STAT_ST_BIRTHTIMESPEC)
965 typedef struct stat statx_data;
967 stat_birthtime(
const struct stat *st)
969 const struct timespec *ts = &st->st_birthtimespec;
972 #elif defined(_WIN32)
973 typedef struct stat statx_data;
974 # define stat_birthtime stat_ctime
976 # undef HAVE_STAT_BIRTHTIME
991 rb_stat_atime(
VALUE self)
993 return stat_atime(get_stat(
self));
1007 rb_stat_mtime(
VALUE self)
1009 return stat_mtime(get_stat(
self));
1027 rb_stat_ctime(
VALUE self)
1029 return stat_ctime(get_stat(
self));
1032 #if defined(HAVE_STAT_BIRTHTIME)
1056 rb_stat_birthtime(
VALUE self)
1058 return stat_birthtime(get_stat(
self));
1061 # define rb_stat_birthtime rb_f_notimplement
1080 rb_stat_inspect(
VALUE self)
1084 static const struct {
1088 {
"dev", rb_stat_dev},
1089 {
"ino", rb_stat_ino},
1090 {
"mode", rb_stat_mode},
1091 {
"nlink", rb_stat_nlink},
1092 {
"uid", rb_stat_uid},
1093 {
"gid", rb_stat_gid},
1094 {
"rdev", rb_stat_rdev},
1095 {
"size", rb_stat_size},
1096 {
"blksize", rb_stat_blksize},
1097 {
"blocks", rb_stat_blocks},
1098 {
"atime", rb_stat_atime},
1099 {
"mtime", rb_stat_mtime},
1100 {
"ctime", rb_stat_ctime},
1101 #if defined(HAVE_STRUCT_STAT_ST_BIRTHTIMESPEC)
1102 {
"birthtime", rb_stat_birthtime},
1108 if (!rb_st->initialized) {
1116 for (i = 0; i <
sizeof(member)/
sizeof(member[0]); i++) {
1124 v = (*member[i].func)(
self);
1128 else if (i == 0 || i == 6) {
1129 rb_str_catf(str,
"0x%"PRI_DEVT_PREFIX
"x", NUM2DEVT(v));
1149 no_gvl_fstat(
void *data)
1152 return (
VALUE)fstat(arg->file.fd, arg->st);
1156 fstat_without_gvl(
rb_io_t *fptr,
struct stat *st)
1160 data.file.fd = fptr->
fd;
1163 return (
int)rb_io_blocking_region(fptr, no_gvl_fstat, &data);
1167 no_gvl_stat(
void * data)
1170 return (
void *)(
VALUE)STAT(arg->file.path, arg->st);
1174 stat_without_gvl(
const char *path,
struct stat *st)
1178 data.file.path = path;
1181 return IO_WITHOUT_GVL_INT(no_gvl_stat, &data);
1184 #if !defined(HAVE_STRUCT_STAT_ST_BIRTHTIMESPEC) && \
1185 defined(HAVE_STRUCT_STATX_STX_BTIME)
1188 # ifdef HAVE_SYSCALL_H
1189 # include <syscall.h>
1190 # elif defined HAVE_SYS_SYSCALL_H
1191 # include <sys/syscall.h>
1193 # if defined __linux__
1194 # include <linux/stat.h>
1196 statx(
int dirfd,
const char *pathname,
int flags,
1197 unsigned int mask,
struct statx *statxbuf)
1199 return (
int)syscall(__NR_statx, dirfd, pathname, flags, mask, statxbuf);
1204 typedef struct no_gvl_statx_data {
1210 } no_gvl_statx_data;
1213 io_blocking_statx(
void *data)
1215 no_gvl_statx_data *arg = data;
1216 return (
VALUE)statx(arg->fd, arg->path, arg->flags, arg->mask, arg->stx);
1220 no_gvl_statx(
void *data)
1222 return (
void *)io_blocking_statx(data);
1226 statx_without_gvl(
const char *path,
struct statx *stx,
unsigned int mask)
1228 no_gvl_statx_data data = {stx, AT_FDCWD, path, 0, mask};
1231 return IO_WITHOUT_GVL_INT(no_gvl_statx, &data);
1235 fstatx_without_gvl(
rb_io_t *fptr,
struct statx *stx,
unsigned int mask)
1237 no_gvl_statx_data data = {stx, fptr->
fd,
"", AT_EMPTY_PATH, mask};
1240 return (
int)rb_io_blocking_region(fptr, io_blocking_statx, &data);
1244 rb_statx(
VALUE file,
struct statx *stx,
unsigned int mask)
1249 tmp = rb_check_convert_type_with_id(file,
T_FILE,
"IO", idTo_io);
1253 result = fstatx_without_gvl(fptr, stx, mask);
1259 result = statx_without_gvl(
RSTRING_PTR(file), stx, mask);
1265 # define statx_has_birthtime(st) ((st)->stx_mask & STATX_BTIME)
1267 NORETURN(
static void statx_notimplement(
const char *field_name));
1272 statx_notimplement(
const char *field_name)
1275 "%s is unimplemented on this filesystem",
1280 statx_birthtime(
const struct statx *stx,
VALUE fname)
1282 if (!statx_has_birthtime(stx)) {
1284 statx_notimplement(
"birthtime");
1286 return rb_time_nano_new((time_t)stx->stx_btime.tv_sec, stx->stx_btime.tv_nsec);
1289 typedef struct statx statx_data;
1290 # define HAVE_STAT_BIRTHTIME
1292 #elif defined(HAVE_STAT_BIRTHTIME)
1293 # define statx_without_gvl(path, st, mask) stat_without_gvl(path, st)
1294 # define fstatx_without_gvl(fptr, st, mask) fstat_without_gvl(fptr, st)
1295 # define statx_birthtime(st, fname) stat_birthtime(st)
1296 # define statx_has_birthtime(st) 1
1297 # define rb_statx(file, st, mask) rb_stat(file, st)
1299 # define statx_has_birthtime(st) 0
1309 tmp = rb_check_convert_type_with_id(file,
T_FILE,
"IO", idTo_io);
1314 result = fstat_without_gvl(fptr, st);
1343 if (stat_without_gvl(
RSTRING_PTR(fname), &st) < 0) {
1344 rb_sys_fail_path(fname);
1365 rb_io_stat(
VALUE obj)
1371 if (fstat(fptr->
fd, &st) == -1) {
1372 rb_sys_fail_path(fptr->
pathv);
1379 no_gvl_lstat(
void *
ptr)
1382 return (
void *)(
VALUE)lstat(arg->file.path, arg->st);
1386 lstat_without_gvl(
const char *path,
struct stat *st)
1390 data.file.path = path;
1393 return IO_WITHOUT_GVL_INT(no_gvl_lstat, &data);
1419 rb_sys_fail_path(fname);
1423 return rb_file_s_stat(klass, fname);
1442 rb_file_lstat(
VALUE obj)
1452 if (lstat_without_gvl(
RSTRING_PTR(path), &st) == -1) {
1453 rb_sys_fail_path(fptr->
pathv);
1457 return rb_io_stat(obj);
1462 rb_group_member(GETGROUPS_T gid)
1464 #if defined(_WIN32) || !defined(HAVE_GETGROUPS)
1473 if (getgid() == gid || getegid() == gid)
1476 groups = getgroups(0, NULL);
1477 gary =
ALLOCV_N(GETGROUPS_T, v, groups);
1478 anum = getgroups(groups, gary);
1479 while (--anum >= 0) {
1480 if (gary[anum] == gid) {
1493 # define S_IXUGO (S_IXUSR | S_IXGRP | S_IXOTH)
1496 #if defined(S_IXGRP) && !defined(_WIN32) && !defined(__CYGWIN__)
1497 #define USE_GETEUID 1
1500 #ifndef HAVE_EACCESS
1502 eaccess(
const char *path,
int mode)
1511 if (getuid() == euid && getgid() == getegid())
1512 return access(path, mode);
1514 if (STAT(path, &st) < 0)
1524 if (st.st_mode & S_IXUGO)
1530 if (st.st_uid == euid)
1532 else if (rb_group_member(st.st_gid))
1535 if ((
int)(st.st_mode & mode) == mode)
return 0;
1539 return access(path, mode);
1550 nogvl_eaccess(
void *
ptr)
1554 return (
void *)(
VALUE)eaccess(aa->path, aa->mode);
1558 rb_eaccess(
VALUE fname,
int mode)
1567 return IO_WITHOUT_GVL_INT(nogvl_eaccess, &aa);
1571 nogvl_access(
void *
ptr)
1575 return (
void *)(
VALUE)access(aa->path, aa->mode);
1579 rb_access(
VALUE fname,
int mode)
1588 return IO_WITHOUT_GVL_INT(nogvl_access, &aa);
1623 # define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
1629 if (S_ISDIR(st.st_mode))
return Qtrue;
1650 # define S_ISFIFO(m) (((m) & S_IFMT) == S_IFIFO)
1656 if (S_ISFIFO(st.st_mode))
return Qtrue;
1679 # define S_ISLNK(m) _S_ISLNK(m)
1682 # define S_ISLNK(m) (((m) & S_IFMT) == _S_IFLNK)
1685 # define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK)
1697 if (S_ISLNK(st.st_mode))
return Qtrue;
1720 # define S_ISSOCK(m) _S_ISSOCK(m)
1723 # define S_ISSOCK(m) (((m) & S_IFMT) == _S_IFSOCK)
1726 # define S_ISSOCK(m) (((m) & S_IFMT) == S_IFSOCK)
1736 if (S_ISSOCK(st.st_mode))
return Qtrue;
1758 # define S_ISBLK(m) (((m) & S_IFMT) == S_IFBLK)
1760 # define S_ISBLK(m) (0)
1768 if (S_ISBLK(st.st_mode))
return Qtrue;
1788 # define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR)
1794 if (S_ISCHR(st.st_mode))
return Qtrue;
1833 return RBOOL(rb_eaccess(fname, R_OK) >= 0);
1848 rb_file_readable_real_p(
VALUE obj,
VALUE fname)
1850 return RBOOL(rb_access(fname, R_OK) >= 0);
1854 # define S_IRUGO (S_IRUSR | S_IRGRP | S_IROTH)
1858 # define S_IWUGO (S_IWUSR | S_IWGRP | S_IWOTH)
1878 rb_file_world_readable_p(
VALUE obj,
VALUE fname)
1884 if ((st.st_mode & (S_IROTH)) == S_IROTH) {
1885 return UINT2NUM(st.st_mode & (S_IRUGO|S_IWUGO|S_IXUGO));
1905 return RBOOL(rb_eaccess(fname, W_OK) >= 0);
1920 rb_file_writable_real_p(
VALUE obj,
VALUE fname)
1922 return RBOOL(rb_access(fname, W_OK) >= 0);
1942 rb_file_world_writable_p(
VALUE obj,
VALUE fname)
1948 if ((st.st_mode & (S_IWOTH)) == S_IWOTH) {
1949 return UINT2NUM(st.st_mode & (S_IRUGO|S_IWUGO|S_IXUGO));
1973 return RBOOL(rb_eaccess(fname, X_OK) >= 0);
1992 rb_file_executable_real_p(
VALUE obj,
VALUE fname)
1994 return RBOOL(rb_access(fname, X_OK) >= 0);
1998 # define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
2019 return RBOOL(S_ISREG(st.st_mode));
2038 return RBOOL(st.st_size == 0);
2057 if (st.st_size == 0)
return Qnil;
2078 return RBOOL(st.st_uid == geteuid());
2087 return RBOOL(st.st_uid == getuid());
2108 if (rb_group_member(st.st_gid))
return Qtrue;
2113 #if defined(S_ISUID) || defined(S_ISGID) || defined(S_ISVTX)
2115 check3rdbyte(
VALUE fname,
int mode)
2120 return RBOOL(st.st_mode & mode);
2137 return check3rdbyte(fname, S_ISUID);
2156 return check3rdbyte(fname, S_ISGID);
2175 return check3rdbyte(fname, S_ISVTX);
2204 struct stat st1, st2;
2208 if (st1.st_dev != st2.st_dev)
return Qfalse;
2209 if (st1.st_ino != st2.st_ino)
return Qfalse;
2213 return rb_w32_file_identical_p(fname1, fname2);
2231 if (
rb_stat(fname, &st) < 0) {
2234 rb_syserr_fail_path(e, fname);
2240 rb_file_ftype(
const struct stat *st)
2244 if (S_ISREG(st->st_mode)) {
2247 else if (S_ISDIR(st->st_mode)) {
2250 else if (S_ISCHR(st->st_mode)) {
2251 t =
"characterSpecial";
2254 else if (S_ISBLK(st->st_mode)) {
2259 else if (S_ISFIFO(st->st_mode)) {
2264 else if (S_ISLNK(st->st_mode)) {
2269 else if (S_ISSOCK(st->st_mode)) {
2303 rb_sys_fail_path(fname);
2306 return rb_file_ftype(&st);
2326 if (
rb_stat(fname, &st) < 0) {
2329 rb_syserr_fail_path(e, fname);
2331 return stat_atime(&st);
2346 rb_file_atime(
VALUE obj)
2352 if (fstat(fptr->
fd, &st) == -1) {
2353 rb_sys_fail_path(fptr->
pathv);
2355 return stat_atime(&st);
2375 if (
rb_stat(fname, &st) < 0) {
2378 rb_syserr_fail_path(e, fname);
2380 return stat_mtime(&st);
2394 rb_file_mtime(
VALUE obj)
2400 if (fstat(fptr->
fd, &st) == -1) {
2401 rb_sys_fail_path(fptr->
pathv);
2403 return stat_mtime(&st);
2427 if (
rb_stat(fname, &st) < 0) {
2430 rb_syserr_fail_path(e, fname);
2432 return stat_ctime(&st);
2449 rb_file_ctime(
VALUE obj)
2455 if (fstat(fptr->
fd, &st) == -1) {
2456 rb_sys_fail_path(fptr->
pathv);
2458 return stat_ctime(&st);
2461 #if defined(HAVE_STAT_BIRTHTIME)
2477 rb_file_s_birthtime(
VALUE klass,
VALUE fname)
2481 if (rb_statx(fname, &st, STATX_BTIME) < 0) {
2484 rb_syserr_fail_path(e, fname);
2486 return statx_birthtime(&st, fname);
2489 # define rb_file_s_birthtime rb_f_notimplement
2492 #if defined(HAVE_STAT_BIRTHTIME)
2506 rb_file_birthtime(
VALUE obj)
2512 if (fstatx_without_gvl(fptr, &st, STATX_BTIME) == -1) {
2513 rb_sys_fail_path(fptr->
pathv);
2515 return statx_birthtime(&st, fptr->
pathv);
2518 # define rb_file_birthtime rb_f_notimplement
2530 rb_io_flush_raw(file, 0);
2533 if (fstat(fptr->
fd, &st) == -1) {
2534 rb_sys_fail_path(fptr->
pathv);
2555 file_size(
VALUE self)
2566 nogvl_chmod(
void *
ptr)
2569 int ret = chmod(data->path, data->mode);
2570 return (
void *)(
VALUE)ret;
2574 rb_chmod(
const char *path, mode_t mode)
2580 return IO_WITHOUT_GVL_INT(nogvl_chmod, &data);
2584 chmod_internal(
const char *path,
void *mode)
2586 return chmod(path, *(mode_t *)mode);
2610 return apply2files(chmod_internal, argc, argv, &mode);
2614 struct nogvl_fchmod_data {
2620 io_blocking_fchmod(
void *
ptr)
2622 struct nogvl_fchmod_data *data =
ptr;
2623 int ret = fchmod(data->fd, data->mode);
2628 rb_fchmod(
int fd, mode_t mode)
2631 struct nogvl_fchmod_data data = {.fd = fd, .mode = mode};
2632 return (
int)rb_thread_io_blocking_region(io_blocking_fchmod, &data, fd);
2654 #if !defined HAVE_FCHMOD || !HAVE_FCHMOD
2662 if (rb_fchmod(fptr->
fd, mode) == -1) {
2663 if (HAVE_FCHMOD ||
errno != ENOSYS)
2664 rb_sys_fail_path(fptr->
pathv);
2667 if (!HAVE_FCHMOD)
return INT2FIX(0);
2670 #if !defined HAVE_FCHMOD || !HAVE_FCHMOD
2674 rb_sys_fail_path(fptr->
pathv);
2680 #if defined(HAVE_LCHMOD)
2682 lchmod_internal(
const char *path,
void *mode)
2684 return lchmod(path, *(mode_t *)mode);
2705 return apply2files(lchmod_internal, argc, argv, &mode);
2708 #define rb_file_s_lchmod rb_f_notimplement
2711 static inline rb_uid_t
2715 return (rb_uid_t)-1;
2720 static inline rb_gid_t
2724 return (rb_gid_t)-1;
2735 chown_internal(
const char *path,
void *arg)
2738 return chown(path, args->owner, args->group);
2762 arg.owner = to_uid(*argv++);
2763 arg.group = to_gid(*argv++);
2765 return apply2files(chown_internal, argc, argv, &arg);
2777 nogvl_chown(
void *
ptr)
2780 return (
void *)(
VALUE)chown(data->as.path, data->new.owner, data->new.group);
2784 rb_chown(
const char *path, rb_uid_t owner, rb_gid_t group)
2787 .as = {.path = path},
2788 .new = {.owner = owner, .group = group},
2790 return IO_WITHOUT_GVL_INT(nogvl_chown, &data);
2795 nogvl_fchown(
void *
ptr)
2798 return (
void *)(
VALUE)fchown(data->as.fd, data->new.owner, data->new.group);
2802 rb_fchown(
int fd, rb_uid_t owner, rb_gid_t group)
2807 .new = {.owner = owner, .group = group},
2809 return IO_WITHOUT_GVL_INT(nogvl_fchown, &data);
2845 rb_sys_fail_path(fptr->
pathv);
2847 if (rb_fchown(fptr->
fd, o, g) == -1)
2848 rb_sys_fail_path(fptr->
pathv);
2854 #if defined(HAVE_LCHOWN)
2856 lchown_internal(
const char *path,
void *arg)
2859 return lchown(path, args->owner, args->group);
2879 arg.owner = to_uid(*argv++);
2880 arg.group = to_gid(*argv++);
2882 return apply2files(lchown_internal, argc, argv, &arg);
2885 #define rb_file_s_lchown rb_f_notimplement
2895 NORETURN(
static void utime_failed(
struct apply_arg *));
2901 VALUE path = aa->fn[aa->i].path;
2904 if (ua->tsp && e == EINVAL) {
2907 VALUE atime = ua->atime;
2908 VALUE mtime = ua->mtime;
2910 if (!
NIL_P(atime)) {
2913 if (!
NIL_P(mtime) && mtime != atime && !
rb_equal(atime, mtime)) {
2916 if (
NIL_P(a)) e[0] = m;
2932 rb_syserr_fail_path(e, path);
2936 #if defined(HAVE_UTIMES)
2938 # if !defined(HAVE_UTIMENSAT)
2940 # elif defined(__APPLE__) && \
2941 (!defined(MAC_OS_X_VERSION_13_0) || (MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_13_0))
2943 # if __has_attribute(availability) && __has_warning("-Wunguarded-availability-new")
2944 typedef int utimensat_func(
int,
const char *,
const struct timespec [2],
int);
2948 static
inline utimensat_func *
2955 # define utimensat rb_utimensat()
2962 utime_internal(
const char *path,
void *arg)
2965 const struct timespec *tsp = v->tsp;
2966 struct timeval tvbuf[2], *tvp = NULL;
2968 #if defined(HAVE_UTIMENSAT)
2969 # if defined(__APPLE__)
2970 const int try_utimensat = utimensat != NULL;
2971 const int try_utimensat_follow = utimensat != NULL;
2973 # define TRY_UTIMENSAT 1
2974 static int try_utimensat = 1;
2975 # ifdef AT_SYMLINK_NOFOLLOW
2976 static int try_utimensat_follow = 1;
2978 const int try_utimensat_follow = 0;
2983 if (v->follow ? try_utimensat_follow : try_utimensat) {
2984 # ifdef AT_SYMLINK_NOFOLLOW
2986 flags = AT_SYMLINK_NOFOLLOW;
2990 int result = utimensat(AT_FDCWD, path, tsp, flags);
2991 # ifdef TRY_UTIMENSAT
2992 if (result < 0 &&
errno == ENOSYS) {
2993 # ifdef AT_SYMLINK_NOFOLLOW
2994 try_utimensat_follow = 0;
3006 tvbuf[0].tv_sec = tsp[0].tv_sec;
3007 tvbuf[0].tv_usec = (int)(tsp[0].tv_nsec / 1000);
3008 tvbuf[1].tv_sec = tsp[1].tv_sec;
3009 tvbuf[1].tv_usec = (int)(tsp[1].tv_nsec / 1000);
3013 if (v->follow)
return lutimes(path, tvp);
3015 return utimes(path, tvp);
3020 #if !defined HAVE_UTIME_H && !defined HAVE_SYS_UTIME_H
3028 utime_internal(
const char *path,
void *arg)
3031 const struct timespec *tsp = v->tsp;
3032 struct utimbuf utbuf, *utp = NULL;
3034 utbuf.actime = tsp[0].tv_sec;
3035 utbuf.modtime = tsp[1].tv_sec;
3038 return utime(path, utp);
3043 utime_internal_i(
int argc,
VALUE *argv,
int follow)
3046 struct timespec tss[2], *tsp = NULL;
3049 args.atime = *argv++;
3050 args.mtime = *argv++;
3052 args.follow = follow;
3054 if (!
NIL_P(args.atime) || !
NIL_P(args.mtime)) {
3057 if (args.atime == args.mtime)
3064 return apply2files(utime_internal, argc, argv, &args);
3081 return utime_internal_i(argc, argv, FALSE);
3084 #if defined(HAVE_UTIMES) && (defined(HAVE_LUTIMES) || (defined(HAVE_UTIMENSAT) && defined(AT_SYMLINK_NOFOLLOW)))
3100 return utime_internal_i(argc, argv, TRUE);
3103 #define rb_file_s_lutime rb_f_notimplement
3106 #ifdef RUBY_FUNCTION_NAME_STRING
3107 # define syserr_fail2(e, s1, s2) syserr_fail2_in(RUBY_FUNCTION_NAME_STRING, e, s1, s2)
3109 # define syserr_fail2_in(func, e, s1, s2) syserr_fail2(e, s1, s2)
3111 #define sys_fail2(s1, s2) syserr_fail2(errno, s1, s2)
3112 NORETURN(
static void syserr_fail2_in(
const char *,
int,
VALUE,
VALUE));
3114 syserr_fail2_in(
const char *func,
int e,
VALUE s1,
VALUE s2)
3118 const int max_pathlen = MAX_PATH;
3120 const int max_pathlen = MAXPATHLEN;
3131 #ifdef RUBY_FUNCTION_NAME_STRING
3132 rb_syserr_fail_path_in(func, e, str);
3134 rb_syserr_fail_path(e, str);
3160 sys_fail2(from, to);
3165 #define rb_file_s_link rb_f_notimplement
3190 sys_fail2(from, to);
3195 #define rb_file_s_symlink rb_f_notimplement
3198 #ifdef HAVE_READLINK
3216 struct readlink_arg {
3223 nogvl_readlink(
void *
ptr)
3225 struct readlink_arg *ra =
ptr;
3227 return (
void *)(
VALUE)readlink(ra->path, ra->buf, ra->size);
3231 readlink_without_gvl(
VALUE path,
VALUE buf,
size_t size)
3233 struct readlink_arg ra;
3239 return (ssize_t)IO_WITHOUT_GVL(nogvl_readlink, &ra);
3252 while ((rv = readlink_without_gvl(path, v, size)) == size
3254 || (rv < 0 &&
errno == ERANGE)
3264 rb_syserr_fail_path(e, path);
3271 #define rb_file_s_readlink rb_f_notimplement
3275 unlink_internal(
const char *path,
void *arg)
3277 return unlink(path);
3297 rb_file_s_unlink(
int argc,
VALUE *argv,
VALUE klass)
3299 return apply2files(unlink_internal, argc, argv, 0);
3308 no_gvl_rename(
void *
ptr)
3312 return (
void *)(
VALUE)rename(ra->src, ra->dst);
3337 #if defined __CYGWIN__
3340 if (IO_WITHOUT_GVL_INT(no_gvl_rename, &ra) < 0) {
3345 if (chmod(ra.dst, 0666) == 0 &&
3346 unlink(ra.dst) == 0 &&
3347 rename(ra.src, ra.dst) == 0)
3351 syserr_fail2(e, from, to);
3386 rb_error_arity(argc, 0, 1);
3394 #if defined __CYGWIN__ || defined DOSISH
3396 #define DOSISH_DRIVE_LETTER
3397 #define FILE_ALT_SEPARATOR '\\'
3399 #ifdef FILE_ALT_SEPARATOR
3400 #define isdirsep(x) ((x) == '/' || (x) == FILE_ALT_SEPARATOR)
3402 static const char file_alt_separator[] = {FILE_ALT_SEPARATOR,
'\0'};
3405 #define isdirsep(x) ((x) == '/')
3416 #ifndef USE_NTFS_ADS
3418 # define USE_NTFS_ADS 1
3420 # define USE_NTFS_ADS 0
3425 #define istrailinggarbage(x) ((x) == '.' || (x) == ' ')
3427 #define istrailinggarbage(x) 0
3431 # define isADS(x) ((x) == ':')
3436 #define Next(p, e, enc) ((p) + rb_enc_mbclen((p), (e), (enc)))
3437 #define Inc(p, e, enc) ((p) = Next((p), (e), (enc)))
3439 #if defined(DOSISH_UNC)
3440 #define has_unc(buf) (isdirsep((buf)[0]) && isdirsep((buf)[1]))
3442 #define has_unc(buf) 0
3445 #ifdef DOSISH_DRIVE_LETTER
3447 has_drive_letter(
const char *buf)
3449 if (
ISALPHA(buf[0]) && buf[1] ==
':') {
3459 getcwdofdrv(
int drv)
3462 char *drvcwd, *oldcwd;
3473 if (chdir(drive) == 0) {
3486 not_same_drive(
VALUE path,
int drive)
3490 if (has_drive_letter(p)) {
3500 static inline char *
3501 skiproot(
const char *path,
const char *end,
rb_encoding *enc)
3503 #ifdef DOSISH_DRIVE_LETTER
3504 if (path + 2 <= end && has_drive_letter(path)) path += 2;
3506 while (path < end && isdirsep(*path)) path++;
3507 return (
char *)path;
3510 #define nextdirsep rb_enc_path_next
3514 while (s < e && !isdirsep(*s)) {
3520 #if defined(DOSISH_UNC) || defined(DOSISH_DRIVE_LETTER)
3521 #define skipprefix rb_enc_path_skip_prefix
3523 #define skipprefix(path, end, enc) (path)
3528 #if defined(DOSISH_UNC) || defined(DOSISH_DRIVE_LETTER)
3530 if (path + 2 <= end && isdirsep(path[0]) && isdirsep(path[1])) {
3532 while (path < end && isdirsep(*path)) path++;
3533 if ((path =
rb_enc_path_next(path, end, enc)) < end && path[0] && path[1] && !isdirsep(path[1]))
3535 return (
char *)path;
3538 #ifdef DOSISH_DRIVE_LETTER
3539 if (has_drive_letter(path))
3540 return (
char *)(path + 2);
3543 return (
char *)path;
3546 static inline char *
3547 skipprefixroot(
const char *path,
const char *end,
rb_encoding *enc)
3549 #if defined(DOSISH_UNC) || defined(DOSISH_DRIVE_LETTER)
3550 char *p = skipprefix(path, end, enc);
3551 while (isdirsep(*p)) p++;
3554 return skiproot(path, end, enc);
3558 #define strrdirsep rb_enc_path_last_separator
3563 while (path < end) {
3564 if (isdirsep(*path)) {
3565 const char *tmp = path++;
3566 while (path < end && isdirsep(*path)) path++;
3567 if (path >= end)
break;
3571 Inc(path, end, enc);
3578 chompdirsep(
const char *path,
const char *end,
rb_encoding *enc)
3580 while (path < end) {
3581 if (isdirsep(*path)) {
3582 const char *last = path++;
3583 while (path < end && isdirsep(*path)) path++;
3584 if (path >= end)
return (
char *)last;
3587 Inc(path, end, enc);
3590 return (
char *)path;
3596 if (path < end && isdirsep(*path)) path++;
3597 return chompdirsep(path, end, enc);
3605 if (encidx == ENCINDEX_US_ASCII) {
3607 if (encidx == ENCINDEX_US_ASCII)
3616 ntfs_tail(
const char *path,
const char *end,
rb_encoding *enc)
3618 while (path < end && *path ==
'.') path++;
3619 while (path < end && !isADS(*path)) {
3620 if (istrailinggarbage(*path)) {
3621 const char *last = path++;
3622 while (path < end && istrailinggarbage(*path)) path++;
3623 if (path >= end || isADS(*path))
return (
char *)last;
3625 else if (isdirsep(*path)) {
3626 const char *last = path++;
3627 while (path < end && isdirsep(*path)) path++;
3628 if (path >= end)
return (
char *)last;
3629 if (isADS(*path)) path++;
3632 Inc(path, end, enc);
3635 return (
char *)path;
3639 #define BUFCHECK(cond) do {\
3642 do {buflen *= 2;} while (cond);\
3643 rb_str_resize(result, buflen);\
3644 buf = RSTRING_PTR(result);\
3646 pend = buf + buflen;\
3650 #define BUFINIT() (\
3651 p = buf = RSTRING_PTR(result),\
3652 buflen = RSTRING_LEN(result),\
3656 # define SKIPPATHSEP(p) ((*(p)) ? 1 : 0)
3658 # define SKIPPATHSEP(p) 1
3661 #define BUFCOPY(srcptr, srclen) do { \
3662 const int skip = SKIPPATHSEP(p); \
3663 rb_str_set_len(result, p-buf+skip); \
3664 BUFCHECK(bdiff + ((srclen)+skip) >= buflen); \
3666 memcpy(p, (srcptr), (srclen)); \
3670 #define WITH_ROOTDIFF(stmt) do { \
3671 long rootdiff = root - buf; \
3673 root = buf + rootdiff; \
3677 copy_home_path(
VALUE result,
const char *dir)
3680 #if defined DOSISH || defined __CYGWIN__
3687 dirlen = strlen(dir);
3692 #if defined DOSISH || defined __CYGWIN__
3694 for (bend = (p = buf) + dirlen; p < bend; Inc(p, bend, enc)) {
3707 VALUE dirname = rb_getpwdirnam_for_login(user);
3708 if (dirname ==
Qnil) {
3713 extern char *getlogin(
void);
3714 const char *pwPtr = 0;
3716 # define endpwent() ((void)0)
3728 if ((login = getlogin()) && strcasecmp(username, login) == 0)
3729 dir = pwPtr = getenv(
"HOME");
3734 copy_home_path(result, dir);
3740 rb_default_home_dir(
VALUE result)
3742 const char *dir = getenv(
"HOME");
3744 #if defined HAVE_PWD_H
3758 VALUE login_name = rb_getlogin();
3760 # if !defined(HAVE_GETPWUID_R) && !defined(HAVE_GETPWUID)
3765 if (
NIL_P(login_name)) {
3770 VALUE pw_dir = rb_getpwdirnam_for_login(login_name);
3771 if (
NIL_P(pw_dir)) {
3772 pw_dir = rb_getpwdiruid();
3773 if (
NIL_P(pw_dir)) {
3787 return copy_home_path(result, dir);
3793 #if NORMALIZE_UTF8PATH
3805 char *buf, *cwdp = dir;
3809 if (NORMALIZE_UTF8PATH || *enc != fsenc) {
3810 dirname = ospath_new(dir, dirlen, fsenc);
3818 rb_encoding *direnc = fs_enc_check(fname, dirname);
3819 if (direnc != fsenc) {
3823 else if (NORMALIZE_UTF8PATH) {
3828 do {buflen *= 2;}
while (dirlen > buflen);
3831 memcpy(buf, cwdp, dirlen);
3835 return buf + dirlen;
3839 rb_file_expand_path_internal(
VALUE fname,
VALUE dname,
int abs_mode,
int long_name,
VALUE result)
3841 const char *s, *b, *fend;
3842 char *buf, *p, *pend, *root;
3843 size_t buflen, bdiff;
3851 if (s[0] ==
'~' && abs_mode == 0) {
3853 if (isdirsep(s[1]) || s[1] ==
'\0') {
3858 rb_default_home_dir(result);
3861 s = nextdirsep(b = s, fend, enc);
3864 BUFCHECK(bdiff + userlen >= buflen);
3865 memcpy(p, b, userlen);
3869 rb_home_dir_of(result, result);
3876 (
int)userlen, b, fname);
3885 #ifdef DOSISH_DRIVE_LETTER
3887 else if (has_drive_letter(s)) {
3888 if (isdirsep(s[2])) {
3891 BUFCHECK(bdiff + 2 >= buflen);
3900 if (!
NIL_P(dname) && !not_same_drive(dname, s[0])) {
3901 rb_file_expand_path_internal(dname,
Qnil, abs_mode, long_name, result);
3909 char *e = append_fspath(result, fname, getcwdofdrv(*s), &enc, fsenc);
3917 p = chompdirsep(skiproot(buf, p, enc), p, enc);
3923 if (!
NIL_P(dname)) {
3924 rb_file_expand_path_internal(dname,
Qnil, abs_mode, long_name, result);
3930 char *e = append_fspath(result, fname,
ruby_getcwd(), &enc, fsenc);
3934 #if defined DOSISH || defined __CYGWIN__
3938 p = skipprefix(buf, p, enc);
3942 p = chompdirsep(skiproot(buf, p, enc), p, enc);
3947 do s++;
while (isdirsep(*s));
3950 BUFCHECK(bdiff >= buflen);
3951 memset(buf,
'/',
len);
3955 if (p > buf && p[-1] ==
'/')
3959 BUFCHECK(bdiff + 1 >= buflen);
3964 BUFCHECK(bdiff + 1 >= buflen);
3966 root = skipprefix(buf, p+1, enc);
3978 if (*(s+1) ==
'\0' || isdirsep(*(s+1))) {
3982 if (!(n = strrdirsep(root, p, enc))) {
3992 do ++s;
while (istrailinggarbage(*s));
3997 #if defined DOSISH || defined __CYGWIN__
4012 while (s < fend && istrailinggarbage(*s)) s++;
4022 #if defined DOSISH || defined __CYGWIN__
4026 WITH_ROOTDIFF(BUFCOPY(b, s-b));
4034 int n = ignored_char_p(s, fend, enc);
4037 WITH_ROOTDIFF(BUFCOPY(b, s-b));
4053 static const char prime[] =
":$DATA";
4054 enum {prime_len =
sizeof(prime) -1};
4058 if (s > b + prime_len && strncasecmp(s - prime_len, prime, prime_len) == 0) {
4061 if (isADS(*(s - (prime_len+1)))) {
4064 else if (memchr(b,
':', s - prime_len - b)) {
4073 if (p == skiproot(buf, p + !!*p, enc) - 1) p++;
4077 if ((s = strrdirsep(b = buf, p, enc)) != 0 && !strpbrk(s,
"*?")) {
4082 WIN32_FIND_DATAW wfd;
4085 #ifdef HAVE_CYGWIN_CONV_PATH
4086 char *w32buf = NULL;
4087 const int flags = CCP_POSIX_TO_WIN_A | CCP_RELATIVE;
4089 char w32buf[MAXPATHLEN];
4093 int lnk_added = 0, is_symlink = 0;
4097 if (lstat_without_gvl(buf, &st) == 0 && S_ISLNK(st.st_mode)) {
4103 path = *buf ? buf :
"/";
4104 #ifdef HAVE_CYGWIN_CONV_PATH
4105 bufsize = cygwin_conv_path(flags, path, NULL, 0);
4108 if (lnk_added) bufsize += 4;
4110 if (cygwin_conv_path(flags, path, w32buf, bufsize) == 0) {
4115 bufsize = MAXPATHLEN;
4116 if (cygwin_conv_to_win32_path(path, w32buf) == 0) {
4120 if (is_symlink && b == w32buf) {
4122 strlcat(w32buf, p, bufsize);
4124 strlcat(w32buf,
".lnk", bufsize);
4135 if (encidx != ENCINDEX_UTF_8 && !is_ascii_string(result)) {
4138 len = MultiByteToWideChar(CP_UTF8, 0,
RSTRING_PTR(tmp), -1, NULL, 0);
4140 MultiByteToWideChar(CP_UTF8, 0,
RSTRING_PTR(tmp), -1, wstr,
len);
4142 h = FindFirstFileW(wstr, &wfd);
4144 if (h != INVALID_HANDLE_VALUE) {
4147 len = lstrlenW(wfd.cFileName);
4149 if (lnk_added &&
len > 4 &&
4150 wcscasecmp(wfd.cFileName +
len - 4, L
".lnk") == 0) {
4151 wfd.cFileName[
len -= 4] = L
'\0';
4158 len = WideCharToMultiByte(CP_UTF8, 0, wfd.cFileName, wlen, NULL, 0, NULL, NULL);
4159 if (tmp == result) {
4160 BUFCHECK(bdiff +
len >= buflen);
4161 WideCharToMultiByte(CP_UTF8, 0, wfd.cFileName, wlen, p,
len + 1, NULL, NULL);
4165 WideCharToMultiByte(CP_UTF8, 0, wfd.cFileName, wlen,
RSTRING_PTR(tmp),
len + 1, NULL, NULL);
4188 #define EXPAND_PATH_BUFFER() rb_usascii_str_new(0, 1)
4191 str_shrink(
VALUE str)
4197 #define expand_path(fname, dname, abs_mode, long_name, result) \
4198 str_shrink(rb_file_expand_path_internal(fname, dname, abs_mode, long_name, result))
4200 #define check_expand_path_args(fname, dname) \
4201 (((fname) = rb_get_path(fname)), \
4202 (void)(NIL_P(dname) ? (dname) : ((dname) = rb_get_path(dname))))
4205 file_expand_path_1(
VALUE fname)
4207 return rb_file_expand_path_internal(fname,
Qnil, 0, 0, EXPAND_PATH_BUFFER());
4213 check_expand_path_args(fname, dname);
4214 return expand_path(fname, dname, 0, 1, EXPAND_PATH_BUFFER());
4218 rb_file_expand_path_fast(
VALUE fname,
VALUE dname)
4220 return expand_path(fname, dname, 0, 0, EXPAND_PATH_BUFFER());
4267 check_expand_path_args(fname, dname);
4268 return expand_path(fname, dname, 1, 1, EXPAND_PATH_BUFFER());
4316 enum rb_realpath_mode {
4320 RB_REALPATH_MODE_MAX
4324 realpath_rec(
long *prefixlenp,
VALUE *resolvedp,
const char *unresolved,
VALUE fallback,
4325 VALUE loopcheck,
enum rb_realpath_mode mode,
int last)
4327 const char *pend = unresolved + strlen(unresolved);
4331 while (unresolved < pend) {
4332 const char *testname = unresolved;
4334 long testnamelen = unresolved_firstsep - unresolved;
4335 const char *unresolved_nextname = unresolved_firstsep;
4336 while (unresolved_nextname < pend && isdirsep(*unresolved_nextname))
4337 unresolved_nextname++;
4338 unresolved = unresolved_nextname;
4339 if (testnamelen == 1 && testname[0] ==
'.') {
4341 else if (testnamelen == 2 && testname[0] ==
'.' && testname[1] ==
'.') {
4343 const char *resolved_str =
RSTRING_PTR(*resolvedp);
4344 const char *resolved_names = resolved_str + *prefixlenp;
4345 const char *lastsep = strrdirsep(resolved_names, resolved_str +
RSTRING_LEN(*resolvedp), enc);
4346 long len = lastsep ? lastsep - resolved_names : 0;
4355 #if defined(DOSISH_UNC) || defined(DOSISH_DRIVE_LETTER)
4356 if (*prefixlenp > 1 && *prefixlenp ==
RSTRING_LEN(testpath)) {
4358 const char *last =
rb_enc_left_char_head(prefix, prefix + *prefixlenp - 1, prefix + *prefixlenp, enc);
4364 if (!
NIL_P(checkval)) {
4365 if (checkval ==
ID2SYM(resolving)) {
4366 if (mode == RB_REALPATH_CHECK) {
4370 rb_syserr_fail_path(ELOOP, testpath);
4379 ret = lstat_without_gvl(
RSTRING_PTR(testpath), &sbuf);
4382 if (e == ENOENT && !
NIL_P(fallback)) {
4383 if (stat_without_gvl(
RSTRING_PTR(fallback), &sbuf) == 0) {
4388 if (mode == RB_REALPATH_CHECK)
return -1;
4390 if (mode == RB_REALPATH_STRICT || !last || *unresolved_firstsep)
4391 rb_syserr_fail_path(e, testpath);
4392 *resolvedp = testpath;
4396 rb_syserr_fail_path(e, testpath);
4399 #ifdef HAVE_READLINK
4400 if (S_ISLNK(sbuf.st_mode)) {
4403 const char *link_prefix, *link_names;
4404 long link_prefixlen;
4406 link = rb_readlink(testpath, enc);
4409 link_prefixlen = link_names - link_prefix;
4410 if (link_prefixlen > 0) {
4414 tmpenc = fs_enc_check(*resolvedp, link);
4417 *prefixlenp = link_prefixlen;
4419 if (realpath_rec(prefixlenp, resolvedp, link_names, testpath,
4420 loopcheck, mode, !*unresolved_firstsep))
4430 *resolvedp = testpath;
4439 rb_check_realpath_emulate(
VALUE basedir,
VALUE path,
rb_encoding *origenc,
enum rb_realpath_mode mode)
4443 VALUE unresolved_path;
4448 char *path_names = NULL, *basedir_names = NULL, *curdir_names = NULL;
4449 char *
ptr, *prefixptr = NULL, *pend;
4454 if (!
NIL_P(basedir)) {
4460 unresolved_path = TO_OSPATH(unresolved_path);
4463 if (
ptr != path_names) {
4468 if (!
NIL_P(basedir)) {
4471 if (
ptr != basedir_names) {
4477 curdir = rb_dir_getwd_ospath();
4484 pend = prefixptr + prefixlen;
4485 ptr = chompdirsep(prefixptr, pend, enc);
4487 prefixlen = ++
ptr - prefixptr;
4490 #ifdef FILE_ALT_SEPARATOR
4491 while (prefixptr <
ptr) {
4492 if (*prefixptr == FILE_ALT_SEPARATOR) {
4495 Inc(prefixptr, pend, enc);
4500 case ENCINDEX_ASCII_8BIT:
4501 case ENCINDEX_US_ASCII:
4507 if (realpath_rec(&prefixlen, &resolved, curdir_names,
Qnil, loopcheck, mode, 0))
4510 if (basedir_names) {
4511 if (realpath_rec(&prefixlen, &resolved, basedir_names,
Qnil, loopcheck, mode, 0))
4514 if (realpath_rec(&prefixlen, &resolved, path_names,
Qnil, loopcheck, mode, 1))
4517 if (origenc && origenc !=
rb_enc_get(resolved)) {
4533 #ifndef HAVE_REALPATH
4535 rb_check_realpath_emulate_try(
VALUE arg)
4538 return rb_check_realpath_emulate(args[0], args[1], (
rb_encoding *)args[2], RB_REALPATH_CHECK);
4542 rb_check_realpath_emulate_rescue(
VALUE arg,
VALUE exc)
4546 #elif !defined(NEEDS_REALPATH_BUFFER) && defined(__APPLE__) && \
4547 (!defined(MAC_OS_X_VERSION_10_6) || (MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_6))
4549 # include <sys/syslimits.h>
4550 # define NEEDS_REALPATH_BUFFER 1
4554 rb_check_realpath_internal(
VALUE basedir,
VALUE path,
rb_encoding *origenc,
enum rb_realpath_mode mode)
4556 #ifdef HAVE_REALPATH
4557 VALUE unresolved_path;
4558 char *resolved_ptr = NULL;
4560 # if defined(NEEDS_REALPATH_BUFFER) && NEEDS_REALPATH_BUFFER
4561 char resolved_buffer[PATH_MAX];
4563 char *
const resolved_buffer = NULL;
4566 if (mode == RB_REALPATH_DIR) {
4567 return rb_check_realpath_emulate(basedir, path, origenc, mode);
4572 unresolved_path = rb_file_join(
rb_assoc_new(basedir, unresolved_path));
4574 if (origenc) unresolved_path = TO_OSPATH(unresolved_path);
4576 if ((resolved_ptr = realpath(
RSTRING_PTR(unresolved_path), resolved_buffer)) == NULL) {
4582 if (
errno == ENOTDIR ||
4583 (
errno == ENOENT && rb_file_exist_p(0, unresolved_path))) {
4584 return rb_check_realpath_emulate(basedir, path, origenc, mode);
4587 if (mode == RB_REALPATH_CHECK) {
4590 rb_sys_fail_path(unresolved_path);
4593 # if !(defined(NEEDS_REALPATH_BUFFER) && NEEDS_REALPATH_BUFFER)
4597 # if !defined(__LINUX__) && !defined(__APPLE__)
4601 if (stat_without_gvl(
RSTRING_PTR(resolved), &st) < 0) {
4602 if (mode == RB_REALPATH_CHECK) {
4605 rb_sys_fail_path(unresolved_path);
4609 if (origenc && origenc !=
rb_enc_get(resolved)) {
4616 if (is_broken_string(resolved)) {
4618 if (is_broken_string(resolved)) {
4626 if (mode == RB_REALPATH_CHECK) {
4630 arg[2] = (
VALUE)origenc;
4633 rb_check_realpath_emulate_rescue,
Qnil);
4636 return rb_check_realpath_emulate(basedir, path, origenc, mode);
4642 rb_realpath_internal(
VALUE basedir,
VALUE path,
int strict)
4644 const enum rb_realpath_mode mode =
4645 strict ? RB_REALPATH_STRICT : RB_REALPATH_DIR;
4646 return rb_check_realpath_internal(basedir, path,
rb_enc_get(path), mode);
4652 return rb_check_realpath_internal(basedir, path, enc, RB_REALPATH_CHECK);
4669 rb_file_s_realpath(
int argc,
VALUE *argv,
VALUE klass)
4672 VALUE path = argv[0];
4674 return rb_realpath_internal(basedir, path, 1);
4690 rb_file_s_realdirpath(
int argc,
VALUE *argv,
VALUE klass)
4693 VALUE path = argv[0];
4695 return rb_realpath_internal(basedir, path, 0);
4699 rmext(
const char *p,
long l0,
long l1,
const char *e,
long l2,
rb_encoding *enc)
4703 const char *s, *last;
4705 if (!e || !l2)
return 0;
4708 if (
rb_enc_ascget(e + len1, e + l2, &len2, enc) ==
'*' && len1 + len2 == l2) {
4709 if (c ==
'.')
return l0;
4719 if (l1 < l2)
return l1;
4722 if (!at_char_boundary(p, s, p+l1, enc))
return 0;
4723 #if CASEFOLD_FILESYSTEM
4724 #define fncomp strncasecmp
4726 #define fncomp strncmp
4728 if (fncomp(s, e, l2) == 0) {
4737 const char *p, *q, *e, *end;
4738 #if defined DOSISH_DRIVE_LETTER || defined DOSISH_UNC
4743 end = name + (alllen ? (size_t)*alllen : strlen(name));
4744 name = skipprefix(name, end, enc);
4745 #if defined DOSISH_DRIVE_LETTER || defined DOSISH_UNC
4748 while (isdirsep(*name))
4753 #if defined DOSISH_DRIVE_LETTER || defined DOSISH_UNC
4757 #ifdef DOSISH_DRIVE_LETTER
4758 else if (*p ==
':') {
4771 if (!(p = strrdirsep(name, end, enc))) {
4775 while (isdirsep(*p)) p++;
4778 n = ntfs_tail(p, end, enc) - p;
4780 n = chompdirsep(p, end, enc) - p;
4782 for (q = p; q - p < n && *q ==
'.'; q++);
4783 for (e = 0; q - p < n; Inc(q, end, enc)) {
4784 if (*q ==
'.') e = q;
4815 rb_file_s_basename(
int argc,
VALUE *argv,
VALUE _)
4817 VALUE fname, fext, basename;
4818 const char *name, *p;
4826 enc = check_path_encoding(fext);
4845 if (!(f = rmext(p, f, n, fp,
RSTRING_LEN(fext), enc))) {
4858 static VALUE rb_file_dirname_n(
VALUE fname,
int n);
4880 rb_file_s_dirname(
int argc,
VALUE *argv,
VALUE klass)
4886 return rb_file_dirname_n(argv[0], n);
4892 return rb_file_dirname_n(fname, 1);
4896 rb_file_dirname_n(
VALUE fname,
int n)
4898 const char *name, *root, *p, *end;
4909 root = skiproot(name, end, enc);
4911 if (root > name + 1 && isdirsep(*name))
4912 root = skipprefix(name = root - 2, end, enc);
4914 if (root > name + 1)
4917 if (n > (end - root + 1) / 2) {
4927 if (!(p = strrdirsep(root, end, enc))) p = root;
4930 seps =
ALLOCV_N(
const char *, sepsv, n);
4931 for (i = 0; i < n; ++i) seps[i] = root;
4933 for (p = root; p < end; ) {
4935 const char *tmp = p++;
4936 while (p < end && isdirsep(*p)) p++;
4937 if (p >= end)
break;
4952 #ifdef DOSISH_DRIVE_LETTER
4953 if (has_drive_letter(name) && isdirsep(*(name + 2))) {
4954 const char *top = skiproot(name + 2, end, enc);
4961 #ifdef DOSISH_DRIVE_LETTER
4962 if (has_drive_letter(name) && root == name + 2 && p - name == 2)
4984 const char *p, *e, *end = name + (
len ? *
len : (long)strlen(name));
4986 p = strrdirsep(name, end, enc);
4990 do name = ++p;
while (isdirsep(*p));
4993 while (*p && *p ==
'.') p++;
4995 if (*p ==
'.' || istrailinggarbage(*p)) {
4997 const char *last = p++, *dot = last;
4998 while (istrailinggarbage(*p)) {
4999 if (*p ==
'.') dot = p;
5002 if (!*p || isADS(*p)) {
5006 if (*last ==
'.' || dot > last) e = dot;
5013 else if (isADS(*p)) {
5017 else if (isdirsep(*p))
5024 if (!e || e == name)
5063 const char *name, *e;
5113 file_inspect_join(
VALUE ary,
VALUE arg,
int recur)
5116 return rb_file_join(arg);
5120 rb_file_join(
VALUE ary)
5124 const char *name, *tail;
5134 check_path_encoding(tmp);
5143 RBASIC_CLEAR_CLASS(result);
5146 switch (OBJ_BUILTIN_TYPE(tmp)) {
5148 if (!checked) check_path_encoding(tmp);
5176 enc = fs_enc_check(result, tmp);
5199 return rb_file_join(args);
5202 #if defined(HAVE_TRUNCATE)
5203 struct truncate_arg {
5209 nogvl_truncate(
void *
ptr)
5211 struct truncate_arg *ta =
ptr;
5212 return (
void *)(
VALUE)truncate(ta->path, ta->pos);
5233 struct truncate_arg ta;
5241 r = IO_WITHOUT_GVL_INT(nogvl_truncate, &ta);
5243 rb_sys_fail_path(path);
5247 #define rb_file_s_truncate rb_f_notimplement
5250 #if defined(HAVE_FTRUNCATE)
5251 struct ftruncate_arg {
5257 nogvl_ftruncate(
void *
ptr)
5259 struct ftruncate_arg *fa =
ptr;
5261 return (
VALUE)ftruncate(fa->fd, fa->pos);
5282 struct ftruncate_arg fa;
5289 rb_io_flush_raw(obj, 0);
5291 if ((
int)rb_io_blocking_region(fptr, nogvl_ftruncate, &fa) < 0) {
5292 rb_sys_fail_path(fptr->
pathv);
5297 #define rb_file_truncate rb_f_notimplement
5314 #include <winerror.h>
5318 rb_thread_flock(
void *data)
5321 int old_errno =
errno;
5323 int *op = data, ret = flock(op[0], op[1]);
5326 if (GetLastError() == ERROR_NOT_LOCKED) {
5384 op[1] = op1 =
NUM2INT(operation);
5389 rb_io_flush_raw(obj, 0);
5391 while ((
int)rb_io_blocking_region(fptr, rb_thread_flock, op) < 0) {
5396 #if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN
5399 if (op1 & LOCK_NB)
return Qfalse;
5402 time.tv_usec = 100 * 1000;
5408 #if defined(ERESTART)
5414 rb_syserr_fail_path(e, fptr->
pathv);
5421 test_check(
int n,
int argc,
VALUE *argv)
5427 for (i=1; i<n; i++) {
5434 #define CHECK(n) test_check((n), argc, argv)
5526 if (strchr(
"bcdefgGkloOprRsSuwWxXz", cmd)) {
5530 return rb_file_blockdev_p(0, argv[1]);
5533 return rb_file_chardev_p(0, argv[1]);
5539 return rb_file_exist_p(0, argv[1]);
5542 return rb_file_file_p(0, argv[1]);
5545 return rb_file_sgid_p(0, argv[1]);
5548 return rb_file_grpowned_p(0, argv[1]);
5551 return rb_file_sticky_p(0, argv[1]);
5554 return rb_file_symlink_p(0, argv[1]);
5557 return rb_file_owned_p(0, argv[1]);
5560 return rb_file_rowned_p(0, argv[1]);
5563 return rb_file_pipe_p(0, argv[1]);
5566 return rb_file_readable_p(0, argv[1]);
5569 return rb_file_readable_real_p(0, argv[1]);
5572 return rb_file_size_p(0, argv[1]);
5575 return rb_file_socket_p(0, argv[1]);
5578 return rb_file_suid_p(0, argv[1]);
5581 return rb_file_writable_p(0, argv[1]);
5584 return rb_file_writable_real_p(0, argv[1]);
5587 return rb_file_executable_p(0, argv[1]);
5590 return rb_file_executable_real_p(0, argv[1]);
5593 return rb_file_zero_p(0, argv[1]);
5597 if (strchr(
"MAC", cmd)) {
5599 VALUE fname = argv[1];
5602 if (
rb_stat(fname, &st) == -1) {
5605 rb_syserr_fail_path(e, fname);
5610 return stat_atime(&st);
5612 return stat_mtime(&st);
5614 return stat_ctime(&st);
5620 return rb_file_identical_p(0, argv[1], argv[2]);
5623 if (strchr(
"=<>", cmd)) {
5624 struct stat st1, st2;
5631 t1 = stat_mtimespec(&st1);
5632 t2 = stat_mtimespec(&st2);
5636 if (t1.tv_sec == t2.tv_sec && t1.tv_nsec == t2.tv_nsec)
return Qtrue;
5640 if (t1.tv_sec > t2.tv_sec)
return Qtrue;
5641 if (t1.tv_sec == t2.tv_sec && t1.tv_nsec > t2.tv_nsec)
return Qtrue;
5645 if (t1.tv_sec < t2.tv_sec)
return Qtrue;
5646 if (t1.tv_sec == t2.tv_sec && t1.tv_nsec < t2.tv_nsec)
return Qtrue;
5675 rb_stat_s_alloc(
VALUE klass)
5677 return stat_new_0(klass, 0);
5697 rb_sys_fail_path(fname);
5704 rb_st->initialized =
true;
5721 *copy_rb_st = *orig_rb_st;
5740 rb_stat_ftype(
VALUE obj)
5742 return rb_file_ftype(get_stat(obj));
5757 rb_stat_d(
VALUE obj)
5759 if (S_ISDIR(get_stat(obj)->st_mode))
return Qtrue;
5772 rb_stat_p(
VALUE obj)
5775 if (S_ISFIFO(get_stat(obj)->st_mode))
return Qtrue;
5798 rb_stat_l(
VALUE obj)
5801 if (S_ISLNK(get_stat(obj)->st_mode))
return Qtrue;
5819 rb_stat_S(
VALUE obj)
5822 if (S_ISSOCK(get_stat(obj)->st_mode))
return Qtrue;
5842 rb_stat_b(
VALUE obj)
5845 if (S_ISBLK(get_stat(obj)->st_mode))
return Qtrue;
5864 rb_stat_c(
VALUE obj)
5866 if (S_ISCHR(get_stat(obj)->st_mode))
return Qtrue;
5884 rb_stat_owned(
VALUE obj)
5886 if (get_stat(obj)->st_uid == geteuid())
return Qtrue;
5891 rb_stat_rowned(
VALUE obj)
5893 if (get_stat(obj)->st_uid == getuid())
return Qtrue;
5910 rb_stat_grpowned(
VALUE obj)
5913 if (rb_group_member(get_stat(obj)->st_gid))
return Qtrue;
5930 rb_stat_r(
VALUE obj)
5932 struct stat *st = get_stat(obj);
5935 if (geteuid() == 0)
return Qtrue;
5938 if (rb_stat_owned(obj))
5939 return RBOOL(st->st_mode & S_IRUSR);
5942 if (rb_stat_grpowned(obj))
5943 return RBOOL(st->st_mode & S_IRGRP);
5946 if (!(st->st_mode & S_IROTH))
return Qfalse;
5963 rb_stat_R(
VALUE obj)
5965 struct stat *st = get_stat(obj);
5968 if (getuid() == 0)
return Qtrue;
5971 if (rb_stat_rowned(obj))
5972 return RBOOL(st->st_mode & S_IRUSR);
5975 if (rb_group_member(get_stat(obj)->st_gid))
5976 return RBOOL(st->st_mode & S_IRGRP);
5979 if (!(st->st_mode & S_IROTH))
return Qfalse;
5998 rb_stat_wr(
VALUE obj)
6001 struct stat *st = get_stat(obj);
6002 if ((st->st_mode & (S_IROTH)) == S_IROTH) {
6003 return UINT2NUM(st->st_mode & (S_IRUGO|S_IWUGO|S_IXUGO));
6021 rb_stat_w(
VALUE obj)
6023 struct stat *st = get_stat(obj);
6026 if (geteuid() == 0)
return Qtrue;
6029 if (rb_stat_owned(obj))
6030 return RBOOL(st->st_mode & S_IWUSR);
6033 if (rb_stat_grpowned(obj))
6034 return RBOOL(st->st_mode & S_IWGRP);
6037 if (!(st->st_mode & S_IWOTH))
return Qfalse;
6054 rb_stat_W(
VALUE obj)
6056 struct stat *st = get_stat(obj);
6059 if (getuid() == 0)
return Qtrue;
6062 if (rb_stat_rowned(obj))
6063 return RBOOL(st->st_mode & S_IWUSR);
6066 if (rb_group_member(get_stat(obj)->st_gid))
6067 return RBOOL(st->st_mode & S_IWGRP);
6070 if (!(st->st_mode & S_IWOTH))
return Qfalse;
6089 rb_stat_ww(
VALUE obj)
6092 struct stat *st = get_stat(obj);
6093 if ((st->st_mode & (S_IWOTH)) == S_IWOTH) {
6094 return UINT2NUM(st->st_mode & (S_IRUGO|S_IWUGO|S_IXUGO));
6114 rb_stat_x(
VALUE obj)
6116 struct stat *st = get_stat(obj);
6119 if (geteuid() == 0) {
6120 return RBOOL(st->st_mode & S_IXUGO);
6124 if (rb_stat_owned(obj))
6125 return RBOOL(st->st_mode & S_IXUSR);
6128 if (rb_stat_grpowned(obj))
6129 return RBOOL(st->st_mode & S_IXGRP);
6132 if (!(st->st_mode & S_IXOTH))
return Qfalse;
6146 rb_stat_X(
VALUE obj)
6148 struct stat *st = get_stat(obj);
6151 if (getuid() == 0) {
6152 return RBOOL(st->st_mode & S_IXUGO);
6156 if (rb_stat_rowned(obj))
6157 return RBOOL(st->st_mode & S_IXUSR);
6160 if (rb_group_member(get_stat(obj)->st_gid))
6161 return RBOOL(st->st_mode & S_IXGRP);
6164 if (!(st->st_mode & S_IXOTH))
return Qfalse;
6181 rb_stat_f(
VALUE obj)
6183 if (S_ISREG(get_stat(obj)->st_mode))
return Qtrue;
6199 rb_stat_z(
VALUE obj)
6201 if (get_stat(obj)->st_size == 0)
return Qtrue;
6218 rb_stat_s(
VALUE obj)
6220 rb_off_t size = get_stat(obj)->st_size;
6222 if (size == 0)
return Qnil;
6238 rb_stat_suid(
VALUE obj)
6241 if (get_stat(obj)->st_mode & S_ISUID)
return Qtrue;
6259 rb_stat_sgid(
VALUE obj)
6262 if (get_stat(obj)->st_mode & S_ISGID)
return Qtrue;
6280 rb_stat_sticky(
VALUE obj)
6283 if (get_stat(obj)->st_mode & S_ISVTX)
return Qtrue;
6288 #if !defined HAVE_MKFIFO && defined HAVE_MKNOD && defined S_IFIFO
6289 #define mkfifo(path, mode) mknod(path, (mode)&~S_IFMT|S_IFIFO, 0)
6300 nogvl_mkfifo(
void *
ptr)
6302 struct mkfifo_arg *ma =
ptr;
6304 return (
void *)(
VALUE)mkfifo(ma->path, ma->mode);
6321 struct mkfifo_arg ma;
6332 if (IO_WITHOUT_GVL(nogvl_mkfifo, &ma)) {
6333 rb_sys_fail_path(path);
6338 #define rb_file_s_mkfifo rb_f_notimplement
6341 static VALUE rb_mFConst;
6344 rb_file_const(
const char *name,
VALUE value)
6352 #ifdef DOSISH_DRIVE_LETTER
6353 if (has_drive_letter(path) && isdirsep(path[2]))
return 1;
6356 if (isdirsep(path[0]) && isdirsep(path[1]))
return 1;
6359 if (path[0] ==
'/')
return 1;
6364 #ifndef ENABLE_PATH_CHECK
6365 # if defined DOSISH || defined __CYGWIN__
6366 # define ENABLE_PATH_CHECK 0
6368 # define ENABLE_PATH_CHECK 1
6372 #if ENABLE_PATH_CHECK
6374 path_check_0(
VALUE path)
6398 # define S_IWOTH 002
6400 if (STAT(p0, &st) == 0 && S_ISDIR(st.st_mode) && (st.st_mode & S_IWOTH)
6402 && !(p && (st.st_mode & S_ISVTX))
6404 && !access(p0, W_OK)) {
6405 rb_enc_warn(enc,
"Insecure world writable dir %s in PATH, mode 0%"
6406 #
if SIZEOF_DEV_T > SIZEOF_INT
6416 s = strrdirsep(p0, e0, enc);
6418 if (!s || s == p0)
return 1;
6429 #if ENABLE_PATH_CHECK
6430 const char *p0, *p, *pend;
6433 if (!path)
return 1;
6435 pend = path + strlen(path);
6437 p = strchr(path, sep);
6445 if (p0 > pend)
break;
6446 p = strchr(p0, sep);
6454 ruby_is_fd_loadable(
int fd)
6461 if (fstat(fd, &st) < 0)
6464 if (S_ISREG(st.st_mode))
6467 if (S_ISFIFO(st.st_mode) || S_ISCHR(st.st_mode))
6470 if (S_ISDIR(st.st_mode))
6481 rb_file_load_ok(
const char *path)
6488 int mode = (O_RDONLY |
6489 #if defined O_NONBLOCK
6491 #elif defined O_NDELAY
6497 if (!rb_gc_for_fd(
errno))
return 0;
6499 if (fd < 0)
return 0;
6502 ret = ruby_is_fd_loadable(fd);
6509 is_explicit_relative(
const char *path)
6511 if (*path++ !=
'.')
return 0;
6512 if (*path ==
'.') path++;
6513 return isdirsep(*path);
6520 if (encidx == ENCINDEX_ASCII_8BIT || encidx == ENCINDEX_US_ASCII)
6533 VALUE fname = *filep, load_path, tmp;
6537 if (!ext[0])
return 0;
6540 fname = file_expand_path_1(fname);
6547 if (!expanded) fname = file_expand_path_1(fname);
6549 for (i=0; ext[i]; i++) {
6552 *filep = copy_path_class(fname, *filep);
6560 RB_GC_GUARD(load_path) = rb_get_expanded_load_path();
6561 if (!load_path)
return 0;
6564 RBASIC_CLEAR_CLASS(fname);
6568 for (j=0; ext[j]; j++) {
6570 for (i = 0; i <
RARRAY_LEN(load_path); i++) {
6575 rb_file_expand_path_internal(fname, str, 0, 0, tmp);
6577 *filep = copy_path_class(tmp, *filep);
6591 VALUE tmp, load_path;
6596 tmp = file_expand_path_1(path);
6597 path = copy_path_class(tmp, path);
6603 if (!rb_file_load_ok(f))
return 0;
6605 path = copy_path_class(file_expand_path_1(path), path);
6609 RB_GC_GUARD(load_path) = rb_get_expanded_load_path();
6615 for (i = 0; i <
RARRAY_LEN(load_path); i++) {
6619 rb_file_expand_path_internal(path, str, 0, 0, tmp);
6621 if (rb_file_load_ok(f))
goto found;
6632 return copy_path_class(tmp, path);
6635 #define define_filetest_function(name, func, argc) do { \
6636 rb_define_module_function(rb_mFileTest, name, func, argc); \
6637 rb_define_singleton_method(rb_cFile, name, func, argc); \
6640 const char ruby_null_device[] =
6643 #elif defined AMIGA || defined __amigaos__
7447 #if defined(__APPLE__) && defined(HAVE_WORKING_FORK)
7448 rb_CFString_class_initialize_before_fork();
7457 define_filetest_function(
"exist?", rb_file_exist_p, 1);
7458 define_filetest_function(
"readable?", rb_file_readable_p, 1);
7459 define_filetest_function(
"readable_real?", rb_file_readable_real_p, 1);
7460 define_filetest_function(
"world_readable?", rb_file_world_readable_p, 1);
7461 define_filetest_function(
"writable?", rb_file_writable_p, 1);
7462 define_filetest_function(
"writable_real?", rb_file_writable_real_p, 1);
7463 define_filetest_function(
"world_writable?", rb_file_world_writable_p, 1);
7464 define_filetest_function(
"executable?", rb_file_executable_p, 1);
7465 define_filetest_function(
"executable_real?", rb_file_executable_real_p, 1);
7466 define_filetest_function(
"file?", rb_file_file_p, 1);
7467 define_filetest_function(
"zero?", rb_file_zero_p, 1);
7468 define_filetest_function(
"empty?", rb_file_zero_p, 1);
7469 define_filetest_function(
"size?", rb_file_size_p, 1);
7470 define_filetest_function(
"size", rb_file_s_size, 1);
7471 define_filetest_function(
"owned?", rb_file_owned_p, 1);
7472 define_filetest_function(
"grpowned?", rb_file_grpowned_p, 1);
7474 define_filetest_function(
"pipe?", rb_file_pipe_p, 1);
7475 define_filetest_function(
"symlink?", rb_file_symlink_p, 1);
7476 define_filetest_function(
"socket?", rb_file_socket_p, 1);
7478 define_filetest_function(
"blockdev?", rb_file_blockdev_p, 1);
7479 define_filetest_function(
"chardev?", rb_file_chardev_p, 1);
7481 define_filetest_function(
"setuid?", rb_file_suid_p, 1);
7482 define_filetest_function(
"setgid?", rb_file_sgid_p, 1);
7483 define_filetest_function(
"sticky?", rb_file_sticky_p, 1);
7485 define_filetest_function(
"identical?", rb_file_identical_p, 2);
7523 separator = rb_fstring_lit(
"/");
7890 #if defined(O_NDELAY) || defined(O_NONBLOCK)
7892 # define O_NONBLOCK O_NDELAY
7908 #ifndef O_SHARE_DELETE
7909 # define O_SHARE_DELETE 0
7952 rb_define_const(rb_mFConst,
"NULL", rb_fstring_cstr(ruby_null_device));
#define rb_define_singleton_method(klass, mid, func, arity)
Defines klass.mid.
#define PATH_SEP
The delimiter of PATH environment variable.
#define PATH_SEP_CHAR
Identical to PATH_SEP, except it is of type char.
#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.
VALUE rb_define_class(const char *name, VALUE super)
Defines a top-level class.
VALUE rb_define_class_under(VALUE outer, const char *name, VALUE super)
Defines a class under the namespace of outer.
VALUE rb_define_module(const char *name)
Defines a top-level module.
VALUE rb_define_module_under(VALUE outer, const char *name)
Defines a module under the namespace of outer.
void rb_define_method(VALUE klass, const char *name, VALUE(*func)(ANYARGS), int argc)
Defines a method.
void rb_define_global_function(const char *name, VALUE(*func)(ANYARGS), int argc)
Defines a global function.
#define rb_str_new2
Old name of rb_str_new_cstr.
#define T_FILE
Old name of RUBY_T_FILE.
#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 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 STRCASECMP
Old name of st_locale_insensitive_strcasecmp.
#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 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 rb_str_new4
Old name of rb_str_new_frozen.
#define ALLOCV_END
Old name of RB_ALLOCV_END.
void rb_raise(VALUE exc_class, const char *fmt,...)
Exception entry point.
VALUE rb_eNotImpError
NotImplementedError exception.
void rb_exc_raise(VALUE mesg)
Raises an exception in the current thread.
void rb_bug(const char *fmt,...)
Interpreter panic switch.
VALUE rb_eIOError
IOError exception.
VALUE rb_eTypeError
TypeError exception.
VALUE rb_eEncCompatError
Encoding::CompatibilityError exception.
VALUE rb_eArgError
ArgumentError 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_rescue(VALUE(*b_proc)(VALUE), VALUE data1, VALUE(*r_proc)(VALUE, VALUE), VALUE data2)
Identical to rb_rescue2(), except it does not take a list of exception classes.
VALUE rb_eSystemCallError
SystemCallError exception.
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)
Just calls rb_obj_freeze_inline() inside.
VALUE rb_mComparable
Comparable module.
VALUE rb_cFile
File class.
VALUE rb_cString
String class.
int rb_enc_precise_mbclen(const char *p, const char *e, rb_encoding *enc)
Queries the number of bytes of the character at the passed pointer.
int rb_enc_get_index(VALUE obj)
Queries the index of the encoding of the passed object, if any.
int rb_filesystem_encindex(void)
Identical to rb_filesystem_encoding(), except it returns the encoding's index instead of the encoding...
VALUE rb_enc_associate(VALUE obj, rb_encoding *enc)
Identical to rb_enc_associate_index(), except it takes an encoding itself instead of its index.
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.
void rb_enc_copy(VALUE dst, VALUE src)
Destructively copies the encoding of the latter object to that of former one.
rb_encoding * rb_enc_compatible(VALUE str1, VALUE str2)
Look for the "common" encoding between the two.
unsigned int rb_enc_codepoint_len(const char *p, const char *e, int *len, rb_encoding *enc)
Queries the code point of character pointed by the passed pointer.
rb_encoding * rb_default_internal_encoding(void)
Queries the "default internal" encoding.
int rb_enc_to_index(rb_encoding *enc)
Queries the index of the encoding.
rb_encoding * rb_ascii8bit_encoding(void)
Queries the encoding that represents ASCII-8BIT a.k.a.
rb_encoding * rb_enc_check(VALUE str1, VALUE str2)
Identical to rb_enc_compatible(), except it raises an exception instead of returning NULL.
static bool rb_enc_asciicompat(rb_encoding *enc)
Queries if the passed encoding is in some sense compatible with ASCII.
rb_encoding * rb_utf8_encoding(void)
Queries the encoding that represents UTF-8.
rb_encoding * rb_enc_from_index(int idx)
Identical to rb_find_encoding(), except it takes an encoding index instead of a Ruby object.
VALUE rb_enc_associate_index(VALUE obj, int encindex)
Identical to rb_enc_set_index(), except it additionally does contents fix-up depending on the passed ...
rb_encoding * rb_enc_get(VALUE obj)
Identical to rb_enc_get_index(), except the return type.
static OnigCodePoint rb_enc_mbc_to_codepoint(const char *p, const char *e, rb_encoding *enc)
Identical to rb_enc_codepoint(), except it assumes the passed character is not broken.
static const char * rb_enc_name(rb_encoding *enc)
Queries the (canonical) name of the passed encoding.
int rb_enc_ascget(const char *p, const char *e, int *len, rb_encoding *enc)
Queries the code point of character pointed by the passed pointer.
int rb_usascii_encindex(void)
Identical to rb_usascii_encoding(), except it returns the encoding's index instead of the encoding it...
rb_encoding * rb_filesystem_encoding(void)
Queries the "filesystem" encoding.
VALUE rb_str_conv_enc(VALUE str, rb_encoding *from, rb_encoding *to)
Encoding conversion main routine.
VALUE rb_enc_str_new(const char *ptr, long len, rb_encoding *enc)
Identical to rb_str_new(), except it additionally takes an encoding.
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.
#define INTEGER_PACK_NATIVE_BYTE_ORDER
Means either INTEGER_PACK_MSBYTE_FIRST or INTEGER_PACK_LSBYTE_FIRST, depending on the host processor'...
VALUE rb_integer_unpack(const void *words, size_t numwords, size_t wordsize, size_t nails, int flags)
Import an integer from a buffer.
#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.
VALUE rb_find_file(VALUE path)
Identical to rb_find_file_ext(), except it takes a feature name and is extension at once,...
VALUE rb_file_s_absolute_path(int argc, const VALUE *argv)
Identical to rb_file_absolute_path(), except how arguments are passed.
VALUE rb_str_encode_ospath(VALUE path)
Converts a string into an "OS Path" encoding, if any.
int rb_is_absolute_path(const char *path)
Queries if the given path is an absolute path.
int rb_find_file_ext(VALUE *feature, const char *const *exts)
Resolves a feature's path.
VALUE rb_file_s_expand_path(int argc, const VALUE *argv)
Identical to rb_file_expand_path(), except how arguments are passed.
rb_off_t rb_file_size(VALUE file)
Queries the file size of the given file.
VALUE rb_file_directory_p(VALUE _, VALUE path)
Queries if the given path is either a directory, or a symlink that (potentially recursively) points t...
VALUE rb_file_expand_path(VALUE fname, VALUE dname)
Identical to rb_file_absolute_path(), except it additionally understands ~.
VALUE rb_file_dirname(VALUE fname)
Strips a file path's last component (and trailing separators if any).
VALUE rb_file_absolute_path(VALUE fname, VALUE dname)
Maps a relative path to its absolute representation.
int rb_path_check(const char *path)
This function is mysterious.
VALUE rb_hash_aref(VALUE hash, VALUE key)
Queries the given key in the given hash table.
VALUE rb_hash_aset(VALUE hash, VALUE key, VALUE val)
Inserts or replaces ("upsert"s) the objects into the given hash table.
VALUE rb_hash_new(void)
Creates a new, empty hash object.
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.
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(long len)
Allocates a "temporary" string.
VALUE rb_str_subseq(VALUE str, long beg, long len)
Identical to rb_str_substr(), except the numbers are interpreted as byte offsets instead of character...
VALUE rb_str_ellipsize(VALUE str, long len)
Shortens str and adds three dots, an ellipsis, if it is longer than len characters.
size_t rb_str_capacity(VALUE str)
Queries the capacity of the given string.
VALUE rb_str_cat2(VALUE, const char *)
Just another name of rb_str_cat_cstr.
VALUE rb_str_dup(VALUE str)
Duplicates a string.
VALUE rb_str_cat(VALUE dst, const char *src, long 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_cat2(VALUE, const char *)
Just another name of rb_str_cat_cstr.
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.
void rb_str_set_len(VALUE str, long len)
Overwrites the length of the string.
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_new(const char *ptr, long len)
Allocates an instance of rb_cString.
VALUE rb_str_dup_frozen(VALUE)
Just another name of rb_str_new_frozen.
VALUE rb_str_new_cstr(const char *ptr)
Identical to rb_str_new(), except it assumes the passed pointer is a pointer to a C string.
VALUE rb_str_resize(VALUE str, long len)
Overwrites the length of the string.
void rb_str_modify_expand(VALUE str, long capa)
Identical to rb_str_modify(), except it additionally expands the capacity of the receiver.
VALUE rb_str_buf_new(long capa)
Allocates a "string buffer".
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.
void rb_define_const(VALUE klass, const char *name, VALUE val)
Defines a Ruby level constant under a namespace.
VALUE rb_stat_new(const struct stat *st)
Constructs an instance of rb_cStat from the passed information.
#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.
char * ptr
Pointer to the underlying memory region, of at least capa bytes.
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 strdup(s)
Just another name of ruby_strdup.
VALUE rb_sprintf(const char *fmt,...)
Ruby's extended sprintf(3).
VALUE rb_str_catf(VALUE dst, const char *fmt,...)
Identical to rb_sprintf(), except it renders the output to the specified object rather than creating ...
#define ALLOCA_N(type, n)
#define RB_GC_GUARD(v)
Prevents premature destruction of local objects.
#define PRI_MODET_PREFIX
A rb_sprintf() format prefix to be used for a mode_t parameter.
#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.
#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.
char * rb_enc_path_next(const char *path, const char *end, rb_encoding *enc)
Returns a path component directly adjacent to the passed pointer.
const char * ruby_enc_find_extname(const char *name, long *len, rb_encoding *enc)
Our own encoding-aware version of extname.
char * rb_enc_path_last_separator(const char *path, const char *end, rb_encoding *enc)
Returns the last path component.
char * rb_enc_path_end(const char *path, const char *end, rb_encoding *enc)
This just returns the passed end basically.
char * rb_enc_path_skip_prefix(const char *path, const char *end, rb_encoding *enc)
Seeks for non-prefix part of a pathname.
const char * ruby_enc_find_basename(const char *name, long *baselen, long *alllen, rb_encoding *enc)
Our own encoding-aware version of basename(3).
#define inline
Old Visual Studio versions do not support the inline keyword, so we need to define it to be __inline.
#define RARRAY_LEN
Just another name of rb_array_len.
#define RARRAY_AREF(a, i)
#define StringValue(v)
Ensures that the parameter object is a String.
#define StringValuePtr(v)
Identical to StringValue, except it returns a char*.
static char * RSTRING_PTR(VALUE str)
Queries the contents pointer of the string.
#define RSTRING_GETMEM(str, ptrvar, lenvar)
Convenient macro to obtain the contents and length at once.
static long RSTRING_LEN(VALUE str)
Queries the length of the string.
#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...
VALUE rb_get_path(VALUE obj)
Converts an object to a path.
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.
VALUE rb_get_path_no_checksafe(VALUE)
#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.
int 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.
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.