14#include "ruby/internal/config.h"
18# include "missing/file.h"
27# include <sys/cygwin.h>
32# if !(defined(__has_feature) && defined(__has_attribute))
37# define API_AVAILABLE(...)
38# define API_DEPRECATED(...)
40# include <CoreFoundation/CFString.h>
57#ifdef HAVE_SYS_PARAM_H
58# include <sys/param.h>
61# define MAXPATHLEN 1024
66#elif defined HAVE_SYS_UTIME_H
67# include <sys/utime.h>
74#ifdef HAVE_SYS_SYSMACROS_H
75# include <sys/sysmacros.h>
81#ifdef HAVE_SYS_MKDEV_H
82# include <sys/mkdev.h>
85#if defined(HAVE_FCNTL_H)
89#if defined(HAVE_SYS_TIME_H)
93#if !defined HAVE_LSTAT && !defined lstat
99# include "win32/file.h"
100# define STAT(p, s) rb_w32_ustati128((p), (s))
102# define lstat(p, s) rb_w32_ulstati128((p), (s))
104# define access(p, m) rb_w32_uaccess((p), (m))
106# define truncate(p, n) rb_w32_utruncate((p), (n))
108# define chmod(p, m) rb_w32_uchmod((p), (m))
110# define chown(p, o, g) rb_w32_uchown((p), (o), (g))
112# define lchown(p, o, g) rb_w32_ulchown((p), (o), (g))
114# define utimensat(s, p, t, f) rb_w32_uutimensat((s), (p), (t), (f))
116# define link(f, t) rb_w32_ulink((f), (t))
118# define unlink(p) rb_w32_uunlink(p)
120# define readlink(f, t, l) rb_w32_ureadlink((f), (t), (l))
122# define rename(f, t) rb_w32_urename((f), (t))
124# define symlink(s, l) rb_w32_usymlink((s), (l))
132# define STAT(p, s) stat((p), (s))
135#ifdef HAVE_STRUCT_STATX_STX_BTIME
136# define ST_(name) stx_ ## name
139# define ST_(name) st_ ## name
143#if defined _WIN32 || defined __APPLE__
145# define TO_OSPATH(str) rb_str_encode_ospath(str)
148# define TO_OSPATH(str) (str)
152#if defined DOSISH || defined __CYGWIN__
157#if defined HAVE_REALPATH && defined __sun && defined __SVR4
170#include "internal/compilers.h"
171#include "internal/dir.h"
172#include "internal/encoding.h"
173#include "internal/error.h"
174#include "internal/file.h"
175#include "internal/io.h"
176#include "internal/load.h"
177#include "internal/object.h"
178#include "internal/process.h"
179#include "internal/thread.h"
180#include "internal/vm.h"
185#define UIANY2NUM(x) \
186 ((sizeof(x) <= sizeof(unsigned int)) ? \
187 UINT2NUM((unsigned)(x)) : \
188 (sizeof(x) <= sizeof(unsigned long)) ? \
189 ULONG2NUM((unsigned long)(x)) : \
190 ULL2NUM((unsigned LONG_LONG)(x)))
197file_path_convert(
VALUE name)
202 if (ENCINDEX_US_ASCII != fname_encidx &&
203 ENCINDEX_ASCII_8BIT != fname_encidx &&
204 (fs_encidx = rb_filesystem_encindex()) != fname_encidx &&
205 rb_default_internal_encoding() &&
209 rb_encoding *fname_encoding = rb_enc_from_index(fname_encidx);
210 rb_encoding *fs_encoding = rb_enc_from_index(fs_encidx);
218check_path_encoding(
VALUE str)
220 if (RB_UNLIKELY(!rb_str_enc_fastpath(str))) {
222 if (!rb_enc_asciicompat(enc)) {
230rb_get_path_check_to_string(
VALUE obj)
239 tmp = rb_check_funcall_default(obj, to_path, 0, 0, obj);
245rb_get_path_check_convert(
VALUE obj)
247 obj = file_path_convert(obj);
249 check_path_encoding(obj);
250 if (!rb_str_to_cstr(obj)) {
251 rb_raise(rb_eArgError,
"path name contains null byte");
260 return rb_get_path(obj);
266 return rb_get_path_check_convert(rb_get_path_check_to_string(obj));
270check_path(
VALUE obj,
const char **cstr)
272 VALUE str = rb_get_path_check_convert(rb_get_path_check_to_string(obj));
276 *cstr = RSTRING_PTR(str);
280#define CheckPath(str, cstr) RB_GC_GUARD(str) = check_path(str, &cstr);
287#if 0 && defined _WIN32
288 if (encidx == ENCINDEX_ASCII_8BIT) {
289 encidx = rb_filesystem_encindex();
292 if (encidx != ENCINDEX_ASCII_8BIT && encidx != ENCINDEX_UTF_8) {
302# define NORMALIZE_UTF8PATH 1
304# ifdef HAVE_WORKING_FORK
305static CFMutableStringRef
306mutable_CFString_new(CFStringRef *s,
const char *ptr,
long len)
308 const CFAllocatorRef alloc = kCFAllocatorDefault;
309 *s = CFStringCreateWithBytesNoCopy(alloc, (
const UInt8 *)ptr,
len,
310 kCFStringEncodingUTF8, FALSE,
312 return CFStringCreateMutableCopy(alloc,
len, *s);
315# define mutable_CFString_release(m, s) (CFRelease(m), CFRelease(s))
318rb_CFString_class_initialize_before_fork(
void)
341 const char small_str[] =
"/";
342 long len =
sizeof(small_str) - 1;
350 for (
int i = 0; i < 2; i++) {
351 CFMutableStringRef m = mutable_CFString_new(&s, small_str,
len);
352 mutable_CFString_release(m, s);
358rb_str_append_normalized_ospath(
VALUE str,
const char *ptr,
long len)
363 CFMutableStringRef m = mutable_CFString_new(&s, ptr,
len);
364 long oldlen = RSTRING_LEN(str);
366 CFStringNormalize(m, kCFStringNormalizationFormC);
367 all = CFRangeMake(0, CFStringGetLength(m));
368 CFStringGetBytes(m, all, kCFStringEncodingUTF8,
'?', FALSE, NULL, 0, &buflen);
370 CFStringGetBytes(m, all, kCFStringEncodingUTF8,
'?', FALSE,
371 (UInt8 *)(RSTRING_PTR(str) + oldlen), buflen, &buflen);
373 mutable_CFString_release(m, s);
378rb_str_normalize_ospath(
const char *ptr,
long len)
381 const char *e = ptr +
len;
385 rb_enc_associate(str, enc);
389 int r = rb_enc_precise_mbclen(p, e, enc);
393 rb_str_append_normalized_ospath(str, p1, p-p1);
400 c = rb_enc_mbc_to_codepoint(p, e, enc);
401 if ((0x2000 <= c && c <= 0x2FFF) || (0xF900 <= c && c <= 0xFAFF) ||
402 (0x2F800 <= c && c <= 0x2FAFF)) {
404 rb_str_append_normalized_ospath(str, p1, p-p1);
415 rb_str_append_normalized_ospath(str, p1, p-p1);
422ignored_char_p(
const char *p,
const char *e,
rb_encoding *enc)
425 if (p+3 > e)
return 0;
426 switch ((
unsigned char)*p) {
428 switch ((
unsigned char)p[1]) {
430 c = (
unsigned char)p[2];
432 if (c >= 0x8c && c <= 0x8f)
return 3;
434 if (c >= 0xaa && c <= 0xae)
return 3;
437 c = (
unsigned char)p[2];
439 if (c >= 0xaa && c <= 0xaf)
return 3;
445 if ((
unsigned char)p[1] == 0xbb &&
446 (
unsigned char)p[2] == 0xbf)
453# define NORMALIZE_UTF8PATH 0
456#define apply2args(n) (rb_check_arity(argc, n, UNLIMITED_ARGUMENTS), argc-=n)
467 int (*func)(
const char *,
void *);
473no_gvl_apply2files(
void *ptr)
477 for (aa->i = 0; aa->i < aa->argc; aa->i++) {
478 if (aa->func(aa->fn[aa->i].ptr, aa->arg) < 0) {
487NORETURN(
static void utime_failed(
struct apply_arg *));
488static int utime_internal(
const char *,
void *);
492apply2files(
int (*func)(
const char *,
void *),
int argc,
VALUE *argv,
void *arg)
496 const long len = (long)(offsetof(
struct apply_arg, fn) + (size * argc));
504 for (aa->i = 0; aa->i < argc; aa->i++) {
505 VALUE path = rb_get_path(argv[aa->i]);
507 path = rb_str_encode_ospath(path);
508 aa->fn[aa->i].ptr = RSTRING_PTR(path);
509 aa->fn[aa->i].path = path;
512 IO_WITHOUT_GVL(no_gvl_apply2files, aa);
515 if (func == utime_internal) {
519 rb_syserr_fail_path(aa->errnum, aa->fn[aa->i].path);
542 rb_io_stat_data stat;
555rb_stat_new(
const struct stat *st)
561# define CP(m) .stx_ ## m = st->st_ ## m
562# define CP_32(m) .stx_ ## m = (uint32_t)st->st_ ## m
563# define CP_TS(m) .stx_ ## m = stat_ ## m ## spec(st)
564 rb_st->stat = (
struct statx){
565 .stx_mask = STATX_BASIC_STATS,
582 rb_st->initialized =
true;
590rb_statx_new(
const rb_io_stat_data *st)
596 rb_st->initialized =
true;
602static rb_io_stat_data*
607 if (!rb_st->initialized) rb_raise(
rb_eTypeError,
"uninitialized File::Stat");
613statx_mtimespec(
const rb_io_stat_data *st)
615 return st->stx_mtime;
618# define statx_mtimespec stat_mtimespec
656 if (ts1.tv_sec == ts2.tv_sec) {
657 if (ts1.tv_nsec == ts2.tv_nsec)
return INT2FIX(0);
658 if (ts1.tv_nsec < ts2.tv_nsec)
return INT2FIX(-1);
661 if (ts1.tv_sec < ts2.tv_sec)
return INT2FIX(-1);
667#define ST2UINT(val) ((val) & ~(~1UL << (sizeof(val) * CHAR_BIT - 1)))
670# define NUM2DEVT(v) NUM2UINT(v)
673# define DEVT2NUM(v) UINT2NUM(v)
675#ifndef PRI_DEVT_PREFIX
676# define PRI_DEVT_PREFIX ""
690rb_stat_dev(
VALUE self)
693 unsigned int m = get_stat(self)->stx_dev_major;
694 unsigned int n = get_stat(self)->stx_dev_minor;
696#elif SIZEOF_STRUCT_STAT_ST_DEV <= SIZEOF_DEV_T
697 return DEVT2NUM(get_stat(self)->st_dev);
698#elif SIZEOF_STRUCT_STAT_ST_DEV <= SIZEOF_LONG
699 return ULONG2NUM(get_stat(self)->st_dev);
701 return ULL2NUM(get_stat(self)->st_dev);
717rb_stat_dev_major(
VALUE self)
720 return UINT2NUM(get_stat(self)->stx_dev_major);
722 return UINT2NUM(major(get_stat(self)->st_dev));
740rb_stat_dev_minor(
VALUE self)
743 return UINT2NUM(get_stat(self)->stx_dev_minor);
745 return UINT2NUM(minor(get_stat(self)->st_dev));
762rb_stat_ino(
VALUE self)
764 rb_io_stat_data *ptr = get_stat(self);
765#ifdef HAVE_STRUCT_STAT_ST_INOHIGH
767 return rb_integer_unpack(&ptr->st_ino, 2,
768 SIZEOF_STRUCT_STAT_ST_INO, 0,
772 return UIANY2NUM(ptr->ST_(ino));
790rb_stat_mode(
VALUE self)
792 return UINT2NUM(ST2UINT(get_stat(self)->ST_(mode)));
808rb_stat_nlink(
VALUE self)
811 const rb_io_stat_data *ptr = get_stat(self);
813 return UIANY2NUM(ptr->ST_(nlink));
827rb_stat_uid(
VALUE self)
829 return UIDT2NUM(get_stat(self)->ST_(uid));
843rb_stat_gid(
VALUE self)
845 return GIDT2NUM(get_stat(self)->ST_(gid));
861rb_stat_rdev(
VALUE self)
864 unsigned int m = get_stat(self)->stx_rdev_major;
865 unsigned int n = get_stat(self)->stx_rdev_minor;
867#elif !defined(HAVE_STRUCT_STAT_ST_RDEV)
869#elif SIZEOF_STRUCT_STAT_ST_RDEV <= SIZEOF_DEV_T
870 return DEVT2NUM(get_stat(self)->ST_(rdev));
871#elif SIZEOF_STRUCT_STAT_ST_RDEV <= SIZEOF_LONG
872 return ULONG2NUM(get_stat(self)->ST_(rdev));
874 return ULL2NUM(get_stat(self)->ST_(rdev));
890rb_stat_rdev_major(
VALUE self)
893 return UINT2NUM(get_stat(self)->stx_rdev_major);
894#elif defined(HAVE_STRUCT_STAT_ST_RDEV) && defined(major)
895 return UINT2NUM(major(get_stat(self)->ST_(rdev)));
913rb_stat_rdev_minor(
VALUE self)
916 return UINT2NUM(get_stat(self)->stx_rdev_minor);
917#elif defined(HAVE_STRUCT_STAT_ST_RDEV) && defined(minor)
918 return UINT2NUM(minor(get_stat(self)->ST_(rdev)));
934rb_stat_size(
VALUE self)
936 return OFFT2NUM(get_stat(self)->ST_(size));
951rb_stat_blksize(
VALUE self)
953#ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
954 return ULONG2NUM(get_stat(self)->ST_(blksize));
972rb_stat_blocks(
VALUE self)
974#ifdef HAVE_STRUCT_STAT_ST_BLOCKS
975# if SIZEOF_STRUCT_STAT_ST_BLOCKS > SIZEOF_LONG
976 return ULL2NUM(get_stat(self)->ST_(blocks));
978 return ULONG2NUM(get_stat(self)->ST_(blocks));
986stat_atimespec(
const struct stat *st)
989 ts.tv_sec = st->st_atime;
990#if defined(HAVE_STRUCT_STAT_ST_ATIM)
991 ts.tv_nsec = (uint32_t)st->st_atim.tv_nsec;
992#elif defined(HAVE_STRUCT_STAT_ST_ATIMESPEC)
993 ts.tv_nsec = (uint32_t)st->st_atimespec.tv_nsec;
994#elif defined(HAVE_STRUCT_STAT_ST_ATIMENSEC)
995 ts.tv_nsec = (uint32_t)st->st_atimensec;
1004statx_atimespec(
const rb_io_stat_data *st)
1006 return st->stx_atime;
1009# define statx_atimespec stat_atimespec
1019stat_atime(
const struct stat *st)
1021 return stat_time(stat_atimespec(st));
1025stat_mtimespec(
const struct stat *st)
1028 ts.tv_sec = st->st_mtime;
1029#if defined(HAVE_STRUCT_STAT_ST_MTIM)
1030 ts.tv_nsec = (uint32_t)st->st_mtim.tv_nsec;
1031#elif defined(HAVE_STRUCT_STAT_ST_MTIMESPEC)
1032 ts.tv_nsec = (uint32_t)st->st_mtimespec.tv_nsec;
1033#elif defined(HAVE_STRUCT_STAT_ST_MTIMENSEC)
1034 ts.tv_nsec = (uint32_t)st->st_mtimensec;
1042stat_mtime(
const struct stat *st)
1044 return stat_time(stat_mtimespec(st));
1048stat_ctimespec(
const struct stat *st)
1051 ts.tv_sec = st->st_ctime;
1052#if defined(HAVE_STRUCT_STAT_ST_CTIM)
1053 ts.tv_nsec = (uint32_t)st->st_ctim.tv_nsec;
1054#elif defined(HAVE_STRUCT_STAT_ST_CTIMESPEC)
1055 ts.tv_nsec = (uint32_t)st->st_ctimespec.tv_nsec;
1056#elif defined(HAVE_STRUCT_STAT_ST_CTIMENSEC)
1057 ts.tv_nsec = (uint32_t)st->st_ctimensec;
1066statx_ctimespec(
const rb_io_stat_data *st)
1068 return st->stx_ctime;
1071# define statx_ctimespec stat_ctimespec
1075stat_ctime(
const struct stat *st)
1077 return stat_time(stat_ctimespec(st));
1080#define HAVE_STAT_BIRTHTIME
1081#if defined(HAVE_STRUCT_STAT_ST_BIRTHTIMESPEC)
1083statx_birthtime(
const rb_io_stat_data *st)
1088#elif defined(HAVE_STRUCT_STATX_STX_BTIME)
1089static VALUE statx_birthtime(
const rb_io_stat_data *st);
1090#elif defined(_WIN32)
1091# define statx_birthtime stat_ctime
1093# undef HAVE_STAT_BIRTHTIME
1108rb_stat_atime(
VALUE self)
1110 return stat_time(statx_atimespec(get_stat(self)));
1124rb_stat_mtime(
VALUE self)
1126 return stat_time(statx_mtimespec(get_stat(self)));
1144rb_stat_ctime(
VALUE self)
1146 return stat_time(statx_ctimespec(get_stat(self)));
1149#if defined(HAVE_STAT_BIRTHTIME)
1173rb_stat_birthtime(
VALUE self)
1175 return statx_birthtime(get_stat(self));
1178# define rb_stat_birthtime rb_f_notimplement
1197rb_stat_inspect(
VALUE self)
1201 static const struct {
1205 {
"dev", rb_stat_dev},
1206 {
"ino", rb_stat_ino},
1207 {
"mode", rb_stat_mode},
1208 {
"nlink", rb_stat_nlink},
1209 {
"uid", rb_stat_uid},
1210 {
"gid", rb_stat_gid},
1211 {
"rdev", rb_stat_rdev},
1212 {
"size", rb_stat_size},
1213 {
"blksize", rb_stat_blksize},
1214 {
"blocks", rb_stat_blocks},
1215 {
"atime", rb_stat_atime},
1216 {
"mtime", rb_stat_mtime},
1217 {
"ctime", rb_stat_ctime},
1218#if defined(HAVE_STRUCT_STAT_ST_BIRTHTIMESPEC)
1219 {
"birthtime", rb_stat_birthtime},
1225 if (!rb_st->initialized) {
1233 for (i = 0; i <
sizeof(member)/
sizeof(member[0]); i++) {
1241 v = (*member[i].func)(self);
1243 rb_str_catf(str,
"0%lo", (
unsigned long)
NUM2ULONG(v));
1245 else if (i == 0 || i == 6) {
1246 rb_str_catf(str,
"0x%"PRI_DEVT_PREFIX
"x", NUM2DEVT(v));
1266no_gvl_fstat(
void *data)
1269 return (
VALUE)fstat(arg->file.fd, arg->st);
1273fstat_without_gvl(
rb_io_t *fptr,
struct stat *st)
1277 data.file.fd = fptr->
fd;
1280 return (
int)rb_io_blocking_region(fptr, no_gvl_fstat, &data);
1284no_gvl_stat(
void * data)
1287 return (
void *)(
VALUE)STAT(arg->file.path, arg->st);
1291stat_without_gvl(
const char *path,
struct stat *st)
1295 data.file.path = path;
1298 return IO_WITHOUT_GVL_INT(no_gvl_stat, &data);
1301#if !defined(HAVE_STRUCT_STAT_ST_BIRTHTIMESPEC) && \
1302 defined(HAVE_STRUCT_STATX_STX_BTIME)
1304# define STATX(path, st, mask) statx(AT_FDCWD, path, 0, mask, st)
1307# ifdef HAVE_SYSCALL_H
1308# include <syscall.h>
1309# elif defined HAVE_SYS_SYSCALL_H
1310# include <sys/syscall.h>
1312# if defined __linux__
1313# include <linux/stat.h>
1315statx(
int dirfd,
const char *pathname,
int flags,
1316 unsigned int mask,
struct statx *statxbuf)
1318 return (
int)syscall(__NR_statx, dirfd, pathname, flags, mask, statxbuf);
1323typedef struct no_gvl_rb_io_stat_data {
1329} no_gvl_rb_io_stat_data;
1332io_blocking_statx(
void *data)
1334 no_gvl_rb_io_stat_data *arg = data;
1335 return (
VALUE)statx(arg->fd, arg->path, arg->flags, arg->mask, arg->stx);
1339no_gvl_statx(
void *data)
1341 return (
void *)io_blocking_statx(data);
1345statx_without_gvl(
const char *path, rb_io_stat_data *stx,
unsigned int mask)
1347 no_gvl_rb_io_stat_data data = {stx, AT_FDCWD, path, 0, mask};
1350 return IO_WITHOUT_GVL_INT(no_gvl_statx, &data);
1354lstatx_without_gvl(
const char *path, rb_io_stat_data *stx,
unsigned int mask)
1356 no_gvl_rb_io_stat_data data = {stx, AT_FDCWD, path, AT_SYMLINK_NOFOLLOW, mask};
1359 return IO_WITHOUT_GVL_INT(no_gvl_statx, &data);
1363fstatx_without_gvl(
rb_io_t *fptr, rb_io_stat_data *stx,
unsigned int mask)
1365 no_gvl_rb_io_stat_data data = {stx, fptr->
fd,
"", AT_EMPTY_PATH, mask};
1368 return (
int)rb_io_blocking_region(fptr, io_blocking_statx, &data);
1371#define FSTATX(fd, st) statx(fd, "", AT_EMPTY_PATH, STATX_ALL, st)
1374rb_statx(
VALUE file,
struct statx *stx,
unsigned int mask)
1379 tmp = rb_check_convert_type_with_id(file,
T_FILE,
"IO", idTo_io);
1384 result = fstatx_without_gvl(fptr, stx, mask);
1389 file = rb_str_encode_ospath(file);
1390 result = statx_without_gvl(RSTRING_PTR(file), stx, mask);
1396# define statx_has_birthtime(st) ((st)->stx_mask & STATX_BTIME)
1398NORETURN(
static void statx_notimplement(
const char *field_name));
1403statx_notimplement(
const char *field_name)
1406 "%s is unimplemented on this filesystem",
1411statx_birthtime(
const rb_io_stat_data *stx)
1413 if (!statx_has_birthtime(stx)) {
1415 statx_notimplement(
"birthtime");
1417 return rb_time_nano_new((time_t)stx->stx_btime.tv_sec, stx->stx_btime.tv_nsec);
1422# define statx_without_gvl(path, st, mask) stat_without_gvl(path, st)
1423# define fstatx_without_gvl(fptr, st, mask) fstat_without_gvl(fptr, st)
1424# define lstatx_without_gvl(path, st, mask) lstat_without_gvl(path, st)
1425# define rb_statx(file, stx, mask) rb_stat(file, stx)
1426# define STATX(path, st, mask) STAT(path, st)
1428#if defined(HAVE_STAT_BIRTHTIME)
1429# define statx_has_birthtime(st) 1
1431# define statx_has_birthtime(st) 0
1438# define FSTAT(fd, st) fstat(fd, st)
1447 tmp = rb_check_convert_type_with_id(file,
T_FILE,
"IO", idTo_io);
1452 result = fstat_without_gvl(fptr, st);
1457 file = rb_str_encode_ospath(file);
1458 result = stat_without_gvl(RSTRING_PTR(file), st);
1480 fname = rb_str_encode_ospath(fname);
1481 if (statx_without_gvl(RSTRING_PTR(fname), &st, STATX_ALL) < 0) {
1482 rb_sys_fail_path(fname);
1484 return rb_statx_new(&st);
1503rb_io_stat(
VALUE obj)
1509 if (fstatx_without_gvl(fptr, &st, STATX_ALL) == -1) {
1510 rb_sys_fail_path(fptr->
pathv);
1512 return rb_statx_new(&st);
1517no_gvl_lstat(
void *ptr)
1520 return (
void *)(
VALUE)lstat(arg->file.path, arg->st);
1524lstat_without_gvl(
const char *path,
struct stat *st)
1528 data.file.path = path;
1531 return IO_WITHOUT_GVL_INT(no_gvl_lstat, &data);
1555 fname = rb_str_encode_ospath(fname);
1556 if (lstatx_without_gvl(
StringValueCStr(fname), &st, STATX_ALL) == -1) {
1557 rb_sys_fail_path(fname);
1559 return rb_statx_new(&st);
1561 return rb_file_s_stat(klass, fname);
1580rb_file_lstat(
VALUE obj)
1589 path = rb_str_encode_ospath(fptr->
pathv);
1590 if (lstatx_without_gvl(RSTRING_PTR(path), &st, STATX_ALL) == -1) {
1591 rb_sys_fail_path(fptr->
pathv);
1593 return rb_statx_new(&st);
1595 return rb_io_stat(obj);
1600rb_group_member(GETGROUPS_T gid)
1602#if defined(_WIN32) || !defined(HAVE_GETGROUPS)
1611 if (getgid() == gid || getegid() == gid)
1614 groups = getgroups(0, NULL);
1615 gary =
ALLOCV_N(GETGROUPS_T, v, groups);
1616 anum = getgroups(groups, gary);
1617 while (--anum >= 0) {
1618 if (gary[anum] == gid) {
1631# define S_IXUGO (S_IXUSR | S_IXGRP | S_IXOTH)
1634#if defined(S_IXGRP) && !defined(_WIN32) && !defined(__CYGWIN__)
1635#define USE_GETEUID 1
1640eaccess(
const char *path,
int mode)
1649 if (getuid() == euid && getgid() == getegid())
1650 return access(path, mode);
1652 if (STAT(path, &st) < 0)
1662 if (st.st_mode & S_IXUGO)
1668 if (st.st_uid == euid)
1670 else if (rb_group_member(st.st_gid))
1673 if ((
int)(st.st_mode & mode) == mode)
return 0;
1677 return access(path, mode);
1688nogvl_eaccess(
void *ptr)
1692 return (
void *)(
VALUE)eaccess(aa->path, aa->mode);
1696rb_eaccess(
VALUE fname,
int mode)
1701 fname = rb_str_encode_ospath(fname);
1705 return IO_WITHOUT_GVL_INT(nogvl_eaccess, &aa);
1709nogvl_access(
void *ptr)
1713 return (
void *)(
VALUE)access(aa->path, aa->mode);
1717rb_access(
VALUE fname,
int mode)
1722 fname = rb_str_encode_ospath(fname);
1726 return IO_WITHOUT_GVL_INT(nogvl_access, &aa);
1761# define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
1767 if (S_ISDIR(st.st_mode))
return Qtrue;
1788# define S_ISFIFO(m) (((m) & S_IFMT) == S_IFIFO)
1794 if (S_ISFIFO(st.st_mode))
return Qtrue;
1817# define S_ISLNK(m) _S_ISLNK(m)
1820# define S_ISLNK(m) (((m) & S_IFMT) == _S_IFLNK)
1823# define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK)
1833 fname = rb_str_encode_ospath(fname);
1835 if (S_ISLNK(st.st_mode))
return Qtrue;
1858# define S_ISSOCK(m) _S_ISSOCK(m)
1861# define S_ISSOCK(m) (((m) & S_IFMT) == _S_IFSOCK)
1864# define S_ISSOCK(m) (((m) & S_IFMT) == S_IFSOCK)
1874 if (S_ISSOCK(st.st_mode))
return Qtrue;
1896# define S_ISBLK(m) (((m) & S_IFMT) == S_IFBLK)
1898# define S_ISBLK(m) (0)
1906 if (S_ISBLK(st.st_mode))
return Qtrue;
1926# define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR)
1932 if (S_ISCHR(st.st_mode))
return Qtrue;
1971 return RBOOL(rb_eaccess(fname, R_OK) >= 0);
1986rb_file_readable_real_p(
VALUE obj,
VALUE fname)
1988 return RBOOL(rb_access(fname, R_OK) >= 0);
1992# define S_IRUGO (S_IRUSR | S_IRGRP | S_IROTH)
1996# define S_IWUGO (S_IWUSR | S_IWGRP | S_IWOTH)
2016rb_file_world_readable_p(
VALUE obj,
VALUE fname)
2022 if ((st.st_mode & (S_IROTH)) == S_IROTH) {
2023 return UINT2NUM(st.st_mode & (S_IRUGO|S_IWUGO|S_IXUGO));
2043 return RBOOL(rb_eaccess(fname, W_OK) >= 0);
2058rb_file_writable_real_p(
VALUE obj,
VALUE fname)
2060 return RBOOL(rb_access(fname, W_OK) >= 0);
2080rb_file_world_writable_p(
VALUE obj,
VALUE fname)
2086 if ((st.st_mode & (S_IWOTH)) == S_IWOTH) {
2087 return UINT2NUM(st.st_mode & (S_IRUGO|S_IWUGO|S_IXUGO));
2111 return RBOOL(rb_eaccess(fname, X_OK) >= 0);
2130rb_file_executable_real_p(
VALUE obj,
VALUE fname)
2132 return RBOOL(rb_access(fname, X_OK) >= 0);
2136# define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
2157 return RBOOL(S_ISREG(st.st_mode));
2176 return RBOOL(st.st_size == 0);
2195 if (st.st_size == 0)
return Qnil;
2216 return RBOOL(st.st_uid == geteuid());
2225 return RBOOL(st.st_uid == getuid());
2246 if (rb_group_member(st.st_gid))
return Qtrue;
2251#if defined(S_ISUID) || defined(S_ISGID) || defined(S_ISVTX)
2253check3rdbyte(
VALUE fname,
int mode)
2258 return RBOOL(st.st_mode & mode);
2275 return check3rdbyte(fname, S_ISUID);
2294 return check3rdbyte(fname, S_ISGID);
2313 return check3rdbyte(fname, S_ISVTX);
2342 struct stat st1, st2;
2346 if (st1.st_dev != st2.st_dev)
return Qfalse;
2347 if (st1.st_ino != st2.st_ino)
return Qfalse;
2351 return rb_w32_file_identical_p(fname1, fname2);
2369 if (
rb_stat(fname, &st) < 0) {
2372 rb_syserr_fail_path(e, fname);
2378rb_file_ftype(mode_t mode)
2382 if (S_ISREG(mode)) {
2385 else if (S_ISDIR(mode)) {
2388 else if (S_ISCHR(mode)) {
2389 t =
"characterSpecial";
2392 else if (S_ISBLK(mode)) {
2397 else if (S_ISFIFO(mode)) {
2402 else if (S_ISLNK(mode)) {
2407 else if (S_ISSOCK(mode)) {
2415 return rb_fstring_cstr(t);
2439 fname = rb_str_encode_ospath(fname);
2441 rb_sys_fail_path(fname);
2444 return rb_file_ftype(st.st_mode);
2464 if (
rb_stat(fname, &st) < 0) {
2467 rb_syserr_fail_path(e, fname);
2469 return stat_time(stat_atimespec(&st));
2484rb_file_atime(
VALUE obj)
2490 if (fstat(fptr->
fd, &st) == -1) {
2491 rb_sys_fail_path(fptr->
pathv);
2493 return stat_time(stat_atimespec(&st));
2513 if (
rb_stat(fname, &st) < 0) {
2516 rb_syserr_fail_path(e, fname);
2518 return stat_time(stat_mtimespec(&st));
2532rb_file_mtime(
VALUE obj)
2538 if (fstat(fptr->
fd, &st) == -1) {
2539 rb_sys_fail_path(fptr->
pathv);
2541 return stat_time(stat_mtimespec(&st));
2565 if (
rb_stat(fname, &st) < 0) {
2568 rb_syserr_fail_path(e, fname);
2570 return stat_time(stat_ctimespec(&st));
2587rb_file_ctime(
VALUE obj)
2593 if (fstat(fptr->
fd, &st) == -1) {
2594 rb_sys_fail_path(fptr->
pathv);
2596 return stat_time(stat_ctimespec(&st));
2599#if defined(HAVE_STAT_BIRTHTIME)
2619 if (rb_statx(fname, &st, STATX_BTIME) < 0) {
2622 rb_syserr_fail_path(e, fname);
2624 return statx_birthtime(&st);
2627# define rb_file_s_birthtime rb_f_notimplement
2630#if defined(HAVE_STAT_BIRTHTIME)
2644rb_file_birthtime(
VALUE obj)
2650 if (fstatx_without_gvl(fptr, &st, STATX_BTIME) == -1) {
2651 rb_sys_fail_path(fptr->
pathv);
2653 return statx_birthtime(&st);
2656# define rb_file_birthtime rb_f_notimplement
2668 rb_io_flush_raw(file, 0);
2671 if (fstat(fptr->
fd, &st) == -1) {
2672 rb_sys_fail_path(fptr->
pathv);
2693file_size(
VALUE self)
2695 return OFFT2NUM(rb_file_size(self));
2704nogvl_chmod(
void *ptr)
2707 int ret = chmod(data->path, data->mode);
2708 return (
void *)(
VALUE)ret;
2712rb_chmod(
const char *path, mode_t mode)
2718 return IO_WITHOUT_GVL_INT(nogvl_chmod, &data);
2722chmod_internal(
const char *path,
void *mode)
2724 return chmod(path, *(mode_t *)mode);
2748 return apply2files(chmod_internal, argc, argv, &mode);
2752struct nogvl_fchmod_data {
2758io_blocking_fchmod(
void *ptr)
2760 struct nogvl_fchmod_data *data = ptr;
2761 int ret = fchmod(data->fd, data->mode);
2766rb_fchmod(
struct rb_io* io, mode_t mode)
2769 struct nogvl_fchmod_data data = {.fd = io->
fd, .mode = mode};
2770 return (
int)rb_thread_io_blocking_region(io, io_blocking_fchmod, &data);
2792#if !defined HAVE_FCHMOD || !HAVE_FCHMOD
2800 if (rb_fchmod(fptr, mode) == -1) {
2801 if (HAVE_FCHMOD ||
errno != ENOSYS)
2802 rb_sys_fail_path(fptr->
pathv);
2805 if (!HAVE_FCHMOD)
return INT2FIX(0);
2808#if !defined HAVE_FCHMOD || !HAVE_FCHMOD
2810 path = rb_str_encode_ospath(fptr->
pathv);
2811 if (rb_chmod(RSTRING_PTR(path), mode) == -1)
2812 rb_sys_fail_path(fptr->
pathv);
2818#if defined(HAVE_LCHMOD)
2820lchmod_internal(
const char *path,
void *mode)
2822 return lchmod(path, *(mode_t *)mode);
2843 return apply2files(lchmod_internal, argc, argv, &mode);
2846#define rb_file_s_lchmod rb_f_notimplement
2849static inline rb_uid_t
2853 return (rb_uid_t)-1;
2858static inline rb_gid_t
2862 return (rb_gid_t)-1;
2873chown_internal(
const char *path,
void *arg)
2876 return chown(path, args->owner, args->group);
2900 arg.owner = to_uid(*argv++);
2901 arg.group = to_gid(*argv++);
2903 return apply2files(chown_internal, argc, argv, &arg);
2915nogvl_chown(
void *ptr)
2918 return (
void *)(
VALUE)chown(data->as.path, data->new.owner, data->new.group);
2922rb_chown(
const char *path, rb_uid_t owner, rb_gid_t group)
2925 .as = {.path = path},
2926 .new = {.owner = owner, .group = group},
2928 return IO_WITHOUT_GVL_INT(nogvl_chown, &data);
2933nogvl_fchown(
void *ptr)
2936 return (
void *)(
VALUE)fchown(data->as.fd, data->new.owner, data->new.group);
2940rb_fchown(
int fd, rb_uid_t owner, rb_gid_t group)
2945 .new = {.owner = owner, .group = group},
2947 return IO_WITHOUT_GVL_INT(nogvl_fchown, &data);
2981 path = rb_str_encode_ospath(fptr->
pathv);
2982 if (rb_chown(RSTRING_PTR(path), o, g) == -1)
2983 rb_sys_fail_path(fptr->
pathv);
2985 if (rb_fchown(fptr->
fd, o, g) == -1)
2986 rb_sys_fail_path(fptr->
pathv);
2992#if defined(HAVE_LCHOWN)
2994lchown_internal(
const char *path,
void *arg)
2997 return lchown(path, args->owner, args->group);
3017 arg.owner = to_uid(*argv++);
3018 arg.group = to_gid(*argv++);
3020 return apply2files(lchown_internal, argc, argv, &arg);
3023#define rb_file_s_lchown rb_f_notimplement
3033NORETURN(
static void utime_failed(
struct apply_arg *));
3039 VALUE path = aa->fn[aa->i].path;
3042 if (ua->tsp && e == EINVAL) {
3045 VALUE atime = ua->atime;
3046 VALUE mtime = ua->mtime;
3048 if (!
NIL_P(atime)) {
3051 if (!
NIL_P(mtime) && mtime != atime && !
rb_equal(atime, mtime)) {
3054 if (
NIL_P(a)) e[0] = m;
3070 rb_syserr_fail_path(e, path);
3074#if defined(HAVE_UTIMES)
3076# if !defined(HAVE_UTIMENSAT)
3078# elif defined(__APPLE__) && \
3079 (!defined(MAC_OS_X_VERSION_13_0) || (MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_13_0))
3081# if __has_attribute(availability) && __has_warning("-Wunguarded-availability-new")
3082typedef int utimensat_func(
int,
const char *,
const struct timespec [2],
int);
3086static
inline utimensat_func *
3093# define utimensat rb_utimensat()
3100utime_internal(
const char *path,
void *arg)
3103 const struct timespec *tsp = v->tsp;
3104 struct timeval tvbuf[2], *tvp = NULL;
3106#if defined(HAVE_UTIMENSAT)
3107# if defined(__APPLE__)
3108 const int try_utimensat = utimensat != NULL;
3109 const int try_utimensat_follow = utimensat != NULL;
3111# define TRY_UTIMENSAT 1
3112 static int try_utimensat = 1;
3113# ifdef AT_SYMLINK_NOFOLLOW
3114 static int try_utimensat_follow = 1;
3116 const int try_utimensat_follow = 0;
3121 if (v->follow ? try_utimensat_follow : try_utimensat) {
3122# ifdef AT_SYMLINK_NOFOLLOW
3124 flags = AT_SYMLINK_NOFOLLOW;
3128 int result = utimensat(AT_FDCWD, path, tsp, flags);
3129# ifdef TRY_UTIMENSAT
3130 if (result < 0 &&
errno == ENOSYS) {
3131# ifdef AT_SYMLINK_NOFOLLOW
3132 try_utimensat_follow = 0;
3144 tvbuf[0].tv_sec = tsp[0].tv_sec;
3145 tvbuf[0].tv_usec = (int)(tsp[0].tv_nsec / 1000);
3146 tvbuf[1].tv_sec = tsp[1].tv_sec;
3147 tvbuf[1].tv_usec = (int)(tsp[1].tv_nsec / 1000);
3151 if (v->follow)
return lutimes(path, tvp);
3153 return utimes(path, tvp);
3158#if !defined HAVE_UTIME_H && !defined HAVE_SYS_UTIME_H
3166utime_internal(
const char *path,
void *arg)
3170 struct utimbuf utbuf, *utp = NULL;
3172 utbuf.actime = tsp[0].tv_sec;
3173 utbuf.modtime = tsp[1].tv_sec;
3176 return utime(path, utp);
3181utime_internal_i(
int argc,
VALUE *argv,
int follow)
3184 struct timespec tss[2], *tsp = NULL;
3187 args.atime = *argv++;
3188 args.mtime = *argv++;
3190 args.follow = follow;
3192 if (!
NIL_P(args.atime) || !
NIL_P(args.mtime)) {
3195 if (args.atime == args.mtime)
3202 return apply2files(utime_internal, argc, argv, &args);
3219 return utime_internal_i(argc, argv, FALSE);
3222#if defined(HAVE_UTIMES) && (defined(HAVE_LUTIMES) || (defined(HAVE_UTIMENSAT) && defined(AT_SYMLINK_NOFOLLOW)))
3238 return utime_internal_i(argc, argv, TRUE);
3241#define rb_file_s_lutime rb_f_notimplement
3244#ifdef RUBY_FUNCTION_NAME_STRING
3245# define syserr_fail2(e, s1, s2) syserr_fail2_in(RUBY_FUNCTION_NAME_STRING, e, s1, s2)
3247# define syserr_fail2_in(func, e, s1, s2) syserr_fail2(e, s1, s2)
3249#define sys_fail2(s1, s2) syserr_fail2(errno, s1, s2)
3250NORETURN(
static void syserr_fail2_in(
const char *,
int,
VALUE,
VALUE));
3252syserr_fail2_in(
const char *func,
int e,
VALUE s1,
VALUE s2)
3256 const int max_pathlen = MAX_PATH;
3258 const int max_pathlen = MAXPATHLEN;
3269#ifdef RUBY_FUNCTION_NAME_STRING
3270 rb_syserr_fail_path_in(func, e, str);
3272 rb_syserr_fail_path(e, str);
3294 from = rb_str_encode_ospath(from);
3295 to = rb_str_encode_ospath(to);
3298 sys_fail2(from, to);
3303#define rb_file_s_link rb_f_notimplement
3324 from = rb_str_encode_ospath(from);
3325 to = rb_str_encode_ospath(to);
3328 sys_fail2(from, to);
3333#define rb_file_s_symlink rb_f_notimplement
3351 return rb_readlink(path, rb_filesystem_encoding());
3354struct readlink_arg {
3361nogvl_readlink(
void *ptr)
3363 struct readlink_arg *ra = ptr;
3365 return (
void *)(
VALUE)readlink(ra->path, ra->buf, ra->size);
3369readlink_without_gvl(
VALUE path,
VALUE buf,
size_t size)
3371 struct readlink_arg ra;
3373 ra.path = RSTRING_PTR(path);
3374 ra.buf = RSTRING_PTR(buf);
3377 return (ssize_t)IO_WITHOUT_GVL(nogvl_readlink, &ra);
3388 path = rb_str_encode_ospath(path);
3389 v = rb_enc_str_new(0, size, enc);
3390 while ((rv = readlink_without_gvl(path, v, size)) == size
3392 || (rv < 0 &&
errno == ERANGE)
3401 rb_str_resize(v, 0);
3402 rb_syserr_fail_path(e, path);
3404 rb_str_resize(v, rv);
3409#define rb_file_s_readlink rb_f_notimplement
3413unlink_internal(
const char *path,
void *arg)
3415 return unlink(path);
3435rb_file_s_unlink(
int argc,
VALUE *argv,
VALUE klass)
3437 return apply2files(unlink_internal, argc, argv, 0);
3446no_gvl_rename(
void *ptr)
3450 return (
void *)(
VALUE)rename(ra->src, ra->dst);
3471 f = rb_str_encode_ospath(from);
3472 t = rb_str_encode_ospath(to);
3475#if defined __CYGWIN__
3478 if (IO_WITHOUT_GVL_INT(no_gvl_rename, &ra) < 0) {
3483 if (chmod(ra.dst, 0666) == 0 &&
3484 unlink(ra.dst) == 0 &&
3485 rename(ra.src, ra.dst) == 0)
3489 syserr_fail2(e, from, to);
3524 rb_error_arity(argc, 0, 1);
3532#if defined __CYGWIN__ || defined DOSISH
3534#define DOSISH_DRIVE_LETTER
3535#define FILE_ALT_SEPARATOR '\\'
3537#ifdef FILE_ALT_SEPARATOR
3538#define isdirsep(x) ((x) == '/' || (x) == FILE_ALT_SEPARATOR)
3540static const char file_alt_separator[] = {FILE_ALT_SEPARATOR,
'\0'};
3543#define isdirsep(x) ((x) == '/')
3556# define USE_NTFS_ADS 1
3558# define USE_NTFS_ADS 0
3563#define istrailinggarbage(x) ((x) == '.' || (x) == ' ')
3565#define istrailinggarbage(x) 0
3569# define isADS(x) ((x) == ':')
3574#define Next(p, e, mb_enc, enc) ((p) + ((mb_enc) ? rb_enc_mbclen((p), (e), (enc)) : 1))
3575#define Inc(p, e, mb_enc, enc) ((p) = Next((p), (e), (mb_enc), (enc)))
3577#if defined(DOSISH_UNC)
3578#define has_unc(buf) (isdirsep((buf)[0]) && isdirsep((buf)[1]))
3580#define has_unc(buf) 0
3583#ifdef DOSISH_DRIVE_LETTER
3585has_drive_letter(
const char *buf)
3587 if (
ISALPHA(buf[0]) && buf[1] ==
':') {
3600 char *drvcwd, *oldcwd;
3611 if (chdir(drive) == 0) {
3624not_same_drive(
VALUE path,
int drive)
3626 const char *p = RSTRING_PTR(path);
3627 if (RSTRING_LEN(path) < 2)
return 0;
3628 if (has_drive_letter(p)) {
3639skiproot(
const char *path,
const char *end)
3641#ifdef DOSISH_DRIVE_LETTER
3642 if (path + 2 <= end && has_drive_letter(path)) path += 2;
3644 while (path < end && isdirsep(*path)) path++;
3645 return (
char *)path;
3649enc_path_next(
const char *s,
const char *e,
bool mb_enc,
rb_encoding *enc)
3651 while (s < e && !isdirsep(*s)) {
3652 Inc(s, e, mb_enc, enc);
3657#define nextdirsep rb_enc_path_next
3661 return enc_path_next(s, e,
true, enc);
3664#if defined(DOSISH_UNC) || defined(DOSISH_DRIVE_LETTER)
3665#define skipprefix enc_path_skip_prefix
3667#define skipprefix(path, end, mb_enc, enc) (path)
3670enc_path_skip_prefix(
const char *path,
const char *end,
bool mb_enc,
rb_encoding *enc)
3672#if defined(DOSISH_UNC) || defined(DOSISH_DRIVE_LETTER)
3674 if (path + 2 <= end && isdirsep(path[0]) && isdirsep(path[1])) {
3676 while (path < end && isdirsep(*path)) path++;
3677 if ((path = enc_path_next(path, end, mb_enc, enc)) < end &&
3678 path + 2 <= end && !isdirsep(path[1])) {
3679 path = enc_path_next(path + 1, end, mb_enc, enc);
3681 return (
char *)path;
3684#ifdef DOSISH_DRIVE_LETTER
3685 if (path + 2 <= end && has_drive_letter(path))
3686 return (
char *)(path + 2);
3689 return (
char *)path;
3693rb_enc_path_skip_prefix(
const char *path,
const char *end,
rb_encoding *enc)
3695 return enc_path_skip_prefix(path, end,
true, enc);
3699skipprefixroot(
const char *path,
const char *end,
rb_encoding *enc)
3701#if defined(DOSISH_UNC) || defined(DOSISH_DRIVE_LETTER)
3702 char *p = skipprefix(path, end,
true, enc);
3703 while (isdirsep(*p)) p++;
3706 return skiproot(path, end);
3711rb_enc_path_last_separator(
const char *path,
const char *end,
rb_encoding *enc)
3714 while (path < end) {
3715 if (isdirsep(*path)) {
3716 const char *tmp = path++;
3717 while (path < end && isdirsep(*path)) path++;
3718 if (path >= end)
break;
3722 Inc(path, end,
true, enc);
3729strrdirsep(
const char *path,
const char *end,
bool mb_enc,
rb_encoding *enc)
3731 if (RB_UNLIKELY(mb_enc)) {
3732 return rb_enc_path_last_separator(path, end, enc);
3735 const char *cursor = end - 1;
3737 while (isdirsep(cursor[0])) {
3741 while (cursor >= path) {
3742 if (isdirsep(cursor[0])) {
3743 while (cursor > path && isdirsep(cursor[-1])) {
3746 return (
char *)cursor;
3754chompdirsep(
const char *path,
const char *end,
bool mb_enc,
rb_encoding *enc)
3756 while (path < end) {
3757 if (isdirsep(*path)) {
3758 const char *last = path++;
3759 while (path < end && isdirsep(*path)) path++;
3760 if (path >= end)
return (
char *)last;
3763 Inc(path, end, mb_enc, enc);
3766 return (
char *)path;
3772 if (path < end && isdirsep(*path)) path++;
3773 return chompdirsep(path, end,
true, enc);
3779 rb_encoding *enc = rb_enc_check_str(path1, path2);
3780 int encidx = rb_enc_to_index(enc);
3781 if (encidx == ENCINDEX_US_ASCII) {
3782 encidx = rb_enc_get_index(path1);
3783 if (encidx == ENCINDEX_US_ASCII)
3784 encidx = rb_enc_get_index(path2);
3785 enc = rb_enc_from_index(encidx);
3792ntfs_tail(
const char *path,
const char *end,
rb_encoding *enc)
3794 while (path < end && *path ==
'.') path++;
3795 while (path < end && !isADS(*path)) {
3796 if (istrailinggarbage(*path)) {
3797 const char *last = path++;
3798 while (path < end && istrailinggarbage(*path)) path++;
3799 if (path >= end || isADS(*path))
return (
char *)last;
3801 else if (isdirsep(*path)) {
3802 const char *last = path++;
3803 while (path < end && isdirsep(*path)) path++;
3804 if (path >= end)
return (
char *)last;
3805 if (isADS(*path)) path++;
3808 Inc(path, end,
true, enc);
3811 return (
char *)path;
3815#define BUFCHECK(cond) do {\
3818 do {buflen *= 2;} while (cond);\
3819 rb_str_resize(result, buflen);\
3820 buf = RSTRING_PTR(result);\
3822 pend = buf + buflen;\
3827 p = buf = RSTRING_PTR(result),\
3828 buflen = RSTRING_LEN(result),\
3832# define SKIPPATHSEP(p) ((*(p)) ? 1 : 0)
3834# define SKIPPATHSEP(p) 1
3837#define BUFCOPY(srcptr, srclen) do { \
3838 const int skip = SKIPPATHSEP(p); \
3839 rb_str_set_len(result, p-buf+skip); \
3840 BUFCHECK(bdiff + ((srclen)+skip) >= buflen); \
3842 memcpy(p, (srcptr), (srclen)); \
3846#define WITH_ROOTDIFF(stmt) do { \
3847 long rootdiff = root - buf; \
3849 root = buf + rootdiff; \
3853copy_home_path(
VALUE result,
const char *dir)
3856#if defined DOSISH || defined __CYGWIN__
3863 dirlen = strlen(dir);
3864 rb_str_resize(result, dirlen);
3865 memcpy(buf = RSTRING_PTR(result), dir, dirlen);
3866 encidx = rb_filesystem_encindex();
3867 rb_enc_associate_index(result, encidx);
3868#if defined DOSISH || defined __CYGWIN__
3869 enc = rb_enc_from_index(encidx);
3870 for (bend = (p = buf) + dirlen; p < bend; Inc(p, bend,
true, enc)) {
3883 VALUE dirname = rb_getpwdirnam_for_login(user);
3884 if (dirname ==
Qnil) {
3885 rb_raise(rb_eArgError,
"user %"PRIsVALUE
" doesn't exist", user);
3887 const char *dir = RSTRING_PTR(dirname);
3889 extern char *getlogin(
void);
3890 const char *pwPtr = 0;
3892 # define endpwent() ((void)0)
3893 const char *dir, *username = RSTRING_PTR(user);
3904 if ((login = getlogin()) && strcasecmp(username, login) == 0)
3905 dir = pwPtr = getenv(
"HOME");
3907 rb_raise(rb_eArgError,
"user %"PRIsVALUE
" doesn't exist", user);
3910 copy_home_path(result, dir);
3916rb_default_home_dir(
VALUE result)
3918 const char *dir = getenv(
"HOME");
3920#if defined HAVE_PWD_H
3934 VALUE login_name = rb_getlogin();
3936# if !defined(HAVE_GETPWUID_R) && !defined(HAVE_GETPWUID)
3941 if (
NIL_P(login_name)) {
3942 rb_raise(rb_eArgError,
"couldn't find login name -- expanding '~'");
3946 VALUE pw_dir = rb_getpwdirnam_for_login(login_name);
3947 if (
NIL_P(pw_dir)) {
3948 pw_dir = rb_getpwdiruid();
3949 if (
NIL_P(pw_dir)) {
3950 rb_raise(rb_eArgError,
"couldn't find home for uid '%ld'", (
long)getuid());
3955 copy_home_path(result, RSTRING_PTR(pw_dir));
3956 rb_str_resize(pw_dir, 0);
3961 rb_raise(rb_eArgError,
"couldn't find HOME environment -- expanding '~'");
3963 return copy_home_path(result, dir);
3969#if NORMALIZE_UTF8PATH
3970 VALUE path = rb_str_normalize_ospath(ptr,
len);
3971 rb_enc_associate(path, fsenc);
3974 return rb_enc_str_new(ptr,
len, fsenc);
3981 char *buf, *cwdp = dir;
3985 if (NORMALIZE_UTF8PATH || *enc != fsenc) {
3986 dirname = ospath_new(dir, dirlen, fsenc);
3987 if (!rb_enc_compatible(fname, dirname)) {
3991 rb_enc_check(fname, dirname);
3992 rb_bug(
"unreachable");
3994 rb_encoding *direnc = fs_enc_check(fname, dirname);
3995 if (direnc != fsenc) {
3999 else if (NORMALIZE_UTF8PATH) {
4004 do {buflen *= 2;}
while (dirlen > buflen);
4005 rb_str_resize(result, buflen);
4006 buf = RSTRING_PTR(result);
4007 memcpy(buf, cwdp, dirlen);
4009 if (!
NIL_P(dirname)) rb_str_resize(dirname, 0);
4010 rb_enc_associate(result, *enc);
4011 return buf + dirlen;
4015rb_file_expand_path_internal(
VALUE fname,
VALUE dname,
int abs_mode,
int long_name,
VALUE result)
4017 const char *s, *b, *fend;
4018 char *buf, *p, *pend, *root;
4019 size_t buflen, bdiff;
4020 rb_encoding *enc, *fsenc = rb_filesystem_encoding();
4023 fend = s + RSTRING_LEN(fname);
4024 enc = rb_enc_get(fname);
4027 if (s < fend && s[0] ==
'~' && abs_mode == 0) {
4029 if (s + 1 == fend || isdirsep(s[1])) {
4033 if (++s < fend) ++s;
4034 rb_default_home_dir(result);
4037 s = nextdirsep(b = s, fend, enc);
4040 BUFCHECK(bdiff + userlen >= buflen);
4041 memcpy(p, b, userlen);
4044 rb_enc_associate(result, enc);
4045 rb_home_dir_of(result, result);
4049 if (!rb_is_absolute_path(RSTRING_PTR(result))) {
4051 rb_enc_raise(enc, rb_eArgError,
"non-absolute home of %.*s%.0"PRIsVALUE,
4052 (
int)userlen, b, fname);
4055 rb_raise(rb_eArgError,
"non-absolute home");
4061#ifdef DOSISH_DRIVE_LETTER
4063 else if (s + 1 < fend && has_drive_letter(s)) {
4064 if (s + 2 < fend && isdirsep(s[2])) {
4067 BUFCHECK(bdiff + 2 >= buflen);
4071 rb_enc_copy(result, fname);
4076 if (!
NIL_P(dname) && !not_same_drive(dname, s[0])) {
4077 rb_file_expand_path_internal(dname,
Qnil, abs_mode, long_name, result);
4085 char *e = append_fspath(result, fname, getcwdofdrv(*s), &enc, fsenc);
4090 rb_enc_associate(result, enc = fs_enc_check(result, fname));
4093 p = chompdirsep(skiproot(buf, p), p,
true, enc);
4098 else if (s == fend || !rb_is_absolute_path(s)) {
4099 if (!
NIL_P(dname)) {
4100 rb_file_expand_path_internal(dname,
Qnil, abs_mode, long_name, result);
4101 rb_enc_associate(result, fs_enc_check(result, fname));
4106 char *e = append_fspath(result, fname,
ruby_getcwd(), &enc, fsenc);
4110#if defined DOSISH || defined __CYGWIN__
4111 if (s < fend && isdirsep(*s)) {
4114 p = skipprefix(buf, p,
true, enc);
4118 p = chompdirsep(skiproot(buf, p), p,
true, enc);
4123 do s++;
while (s < fend && isdirsep(*s));
4126 BUFCHECK(bdiff >= buflen);
4127 memset(buf,
'/',
len);
4129 rb_enc_associate(result, fs_enc_check(result, fname));
4131 if (p > buf && p[-1] ==
'/')
4135 BUFCHECK(bdiff + 1 >= buflen);
4140 BUFCHECK(bdiff + 1 >= buflen);
4142 root = skipprefix(buf, p+1,
true, enc);
4155 if (s+1 == fend || isdirsep(*(s+1))) {
4159 if (!(n = strrdirsep(root, p,
true, enc))) {
4169 do ++s;
while (s < fend && istrailinggarbage(*s));
4174#if defined DOSISH || defined __CYGWIN__
4189 while (s < fend && istrailinggarbage(*s)) s++;
4199#if defined DOSISH || defined __CYGWIN__
4203 WITH_ROOTDIFF(BUFCOPY(b, s-b));
4211 int n = ignored_char_p(s, fend, enc);
4214 WITH_ROOTDIFF(BUFCOPY(b, s-b));
4222 Inc(s, fend,
true, enc);
4230 static const char prime[] =
":$DATA";
4231 enum {prime_len =
sizeof(prime) -1};
4235 if (s > b + prime_len && strncasecmp(s - prime_len, prime, prime_len) == 0) {
4238 if (isADS(*(s - (prime_len+1)))) {
4241 else if (memchr(b,
':', s - prime_len - b)) {
4250 if (p == skiproot(buf, p + !!*p) - 1) p++;
4254 if ((s = strrdirsep(b = buf, p, enc)) != 0 && !strpbrk(s,
"*?")) {
4259 WIN32_FIND_DATAW wfd;
4262#ifdef HAVE_CYGWIN_CONV_PATH
4263 char *w32buf = NULL;
4264 const int flags = CCP_POSIX_TO_WIN_A | CCP_RELATIVE;
4266 char w32buf[MAXPATHLEN];
4270 int lnk_added = 0, is_symlink = 0;
4274 if (lstat_without_gvl(buf, &st) == 0 && S_ISLNK(st.st_mode)) {
4280 path = *buf ? buf :
"/";
4281#ifdef HAVE_CYGWIN_CONV_PATH
4282 bufsize = cygwin_conv_path(flags, path, NULL, 0);
4285 if (lnk_added) bufsize += 4;
4287 if (cygwin_conv_path(flags, path, w32buf, bufsize) == 0) {
4292 bufsize = MAXPATHLEN;
4293 if (cygwin_conv_to_win32_path(path, w32buf) == 0) {
4297 if (is_symlink && b == w32buf) {
4299 strlcat(w32buf, p, bufsize);
4301 strlcat(w32buf,
".lnk", bufsize);
4312 if (encidx != ENCINDEX_UTF_8 && !is_ascii_string(result)) {
4313 tmp = rb_str_encode_ospath(result);
4315 len = MultiByteToWideChar(CP_UTF8, 0, RSTRING_PTR(tmp), -1, NULL, 0);
4317 MultiByteToWideChar(CP_UTF8, 0, RSTRING_PTR(tmp), -1, wstr,
len);
4319 h = FindFirstFileW(wstr, &wfd);
4321 if (h != INVALID_HANDLE_VALUE) {
4324 len = lstrlenW(wfd.cFileName);
4326 if (lnk_added &&
len > 4 &&
4327 wcscasecmp(wfd.cFileName +
len - 4, L
".lnk") == 0) {
4328 wfd.cFileName[
len -= 4] = L
'\0';
4335 len = WideCharToMultiByte(CP_UTF8, 0, wfd.cFileName, wlen, NULL, 0, NULL, NULL);
4336 if (tmp == result) {
4337 BUFCHECK(bdiff +
len >= buflen);
4338 WideCharToMultiByte(CP_UTF8, 0, wfd.cFileName, wlen, p,
len + 1, NULL, NULL);
4342 WideCharToMultiByte(CP_UTF8, 0, wfd.cFileName, wlen, RSTRING_PTR(tmp),
len + 1, NULL, NULL);
4343 rb_str_cat_conv_enc_opts(result, bdiff, RSTRING_PTR(tmp),
len,
4344 rb_utf8_encoding(), 0,
Qnil);
4346 rb_str_resize(tmp, 0);
4359 rb_enc_check(fname, result);
4365#define EXPAND_PATH_BUFFER() rb_usascii_str_new(0, 1)
4368str_shrink(
VALUE str)
4370 rb_str_resize(str, RSTRING_LEN(str));
4374#define expand_path(fname, dname, abs_mode, long_name, result) \
4375 str_shrink(rb_file_expand_path_internal(fname, dname, abs_mode, long_name, result))
4377#define check_expand_path_args(fname, dname) \
4378 (((fname) = rb_get_path(fname)), \
4379 (void)(NIL_P(dname) ? (dname) : ((dname) = rb_get_path(dname))))
4382file_expand_path_1(
VALUE fname)
4384 return rb_file_expand_path_internal(fname,
Qnil, 0, 0, EXPAND_PATH_BUFFER());
4390 check_expand_path_args(fname, dname);
4391 return expand_path(fname, dname, 0, 1, EXPAND_PATH_BUFFER());
4395rb_file_expand_path_fast(
VALUE fname,
VALUE dname)
4397 return expand_path(fname, dname, 0, 0, EXPAND_PATH_BUFFER());
4401rb_file_s_expand_path(
int argc,
const VALUE *argv)
4404 return rb_file_expand_path(argv[0], argc > 1 ? argv[1] :
Qnil);
4438 return rb_file_s_expand_path(c, v);
4444 check_expand_path_args(fname, dname);
4445 return expand_path(fname, dname, 1, 1, EXPAND_PATH_BUFFER());
4449rb_file_s_absolute_path(
int argc,
const VALUE *argv)
4452 return rb_file_absolute_path(argv[0], argc > 1 ? argv[1] :
Qnil);
4471 return rb_file_s_absolute_path(c, v);
4487 VALUE path = rb_get_path(fname);
4489 if (!rb_is_absolute_path(RSTRING_PTR(path)))
return Qfalse;
4493enum rb_realpath_mode {
4497 RB_REALPATH_MODE_MAX
4501realpath_rec(
long *prefixlenp,
VALUE *resolvedp,
const char *unresolved,
VALUE fallback,
4502 VALUE loopcheck,
enum rb_realpath_mode mode,
int last)
4504 const char *pend = unresolved + strlen(unresolved);
4508 while (unresolved < pend) {
4509 const char *testname = unresolved;
4510 const char *unresolved_firstsep = rb_enc_path_next(unresolved, pend, enc);
4511 long testnamelen = unresolved_firstsep - unresolved;
4512 const char *unresolved_nextname = unresolved_firstsep;
4513 while (unresolved_nextname < pend && isdirsep(*unresolved_nextname))
4514 unresolved_nextname++;
4515 unresolved = unresolved_nextname;
4516 if (testnamelen == 1 && testname[0] ==
'.') {
4518 else if (testnamelen == 2 && testname[0] ==
'.' && testname[1] ==
'.') {
4519 if (*prefixlenp < RSTRING_LEN(*resolvedp)) {
4520 const char *resolved_str = RSTRING_PTR(*resolvedp);
4521 const char *resolved_names = resolved_str + *prefixlenp;
4522 const char *lastsep = strrdirsep(resolved_names, resolved_str + RSTRING_LEN(*resolvedp),
true, enc);
4523 long len = lastsep ? lastsep - resolved_names : 0;
4524 rb_str_resize(*resolvedp, *prefixlenp +
len);
4530 if (*prefixlenp < RSTRING_LEN(testpath))
4532#if defined(DOSISH_UNC) || defined(DOSISH_DRIVE_LETTER)
4533 if (*prefixlenp > 1 && *prefixlenp == RSTRING_LEN(testpath)) {
4534 const char *prefix = RSTRING_PTR(testpath);
4535 const char *last =
rb_enc_left_char_head(prefix, prefix + *prefixlenp - 1, prefix + *prefixlenp, enc);
4540 checkval = rb_hash_aref(loopcheck, testpath);
4541 if (!
NIL_P(checkval)) {
4542 if (checkval ==
ID2SYM(resolving)) {
4543 if (mode == RB_REALPATH_CHECK) {
4547 rb_syserr_fail_path(ELOOP, testpath);
4556 ret = lstat_without_gvl(RSTRING_PTR(testpath), &sbuf);
4559 if (e == ENOENT && !
NIL_P(fallback)) {
4560 if (stat_without_gvl(RSTRING_PTR(fallback), &sbuf) == 0) {
4565 if (mode == RB_REALPATH_CHECK)
return -1;
4567 if (mode == RB_REALPATH_STRICT || !last || *unresolved_firstsep)
4568 rb_syserr_fail_path(e, testpath);
4569 *resolvedp = testpath;
4573 rb_syserr_fail_path(e, testpath);
4577 if (S_ISLNK(sbuf.st_mode)) {
4580 const char *link_prefix, *link_names;
4581 long link_prefixlen;
4582 rb_hash_aset(loopcheck, testpath,
ID2SYM(resolving));
4583 link = rb_readlink(testpath, enc);
4584 link_prefix = RSTRING_PTR(link);
4585 link_names = skipprefixroot(link_prefix, link_prefix + RSTRING_LEN(link), rb_enc_get(link));
4586 link_prefixlen = link_names - link_prefix;
4587 if (link_prefixlen > 0) {
4591 tmpenc = fs_enc_check(*resolvedp, link);
4594 *prefixlenp = link_prefixlen;
4596 if (realpath_rec(prefixlenp, resolvedp, link_names, testpath,
4597 loopcheck, mode, !*unresolved_firstsep))
4606 rb_hash_aset(loopcheck, s, s);
4607 *resolvedp = testpath;
4616rb_check_realpath_emulate(
VALUE basedir,
VALUE path,
rb_encoding *origenc,
enum rb_realpath_mode mode)
4620 VALUE unresolved_path;
4625 char *path_names = NULL, *basedir_names = NULL, *curdir_names = NULL;
4626 char *ptr, *prefixptr = NULL, *pend;
4631 if (!
NIL_P(basedir)) {
4636 enc = rb_enc_get(unresolved_path);
4637 unresolved_path = TO_OSPATH(unresolved_path);
4639 path_names = skipprefixroot(ptr, ptr +
len, rb_enc_get(unresolved_path));
4640 if (ptr != path_names) {
4641 resolved =
rb_str_subseq(unresolved_path, 0, path_names - ptr);
4645 if (!
NIL_P(basedir)) {
4647 basedir_names = skipprefixroot(ptr, ptr +
len, rb_enc_get(basedir));
4648 if (ptr != basedir_names) {
4654 curdir = rb_dir_getwd_ospath();
4656 curdir_names = skipprefixroot(ptr, ptr +
len, rb_enc_get(curdir));
4661 pend = prefixptr + prefixlen;
4662 ptr = chompdirsep(prefixptr, pend,
true, enc);
4664 prefixlen = ++ptr - prefixptr;
4667#ifdef FILE_ALT_SEPARATOR
4668 while (prefixptr < ptr) {
4669 if (*prefixptr == FILE_ALT_SEPARATOR) {
4672 Inc(prefixptr, pend,
true, enc);
4676 switch (rb_enc_to_index(enc)) {
4677 case ENCINDEX_ASCII_8BIT:
4678 case ENCINDEX_US_ASCII:
4679 rb_enc_associate_index(resolved, rb_filesystem_encindex());
4682 loopcheck = rb_hash_new();
4684 if (realpath_rec(&prefixlen, &resolved, curdir_names,
Qnil, loopcheck, mode, 0))
4687 if (basedir_names) {
4688 if (realpath_rec(&prefixlen, &resolved, basedir_names,
Qnil, loopcheck, mode, 0))
4691 if (realpath_rec(&prefixlen, &resolved, path_names,
Qnil, loopcheck, mode, 1))
4694 if (origenc && origenc != rb_enc_get(resolved)) {
4696 rb_enc_associate(resolved, origenc);
4708static VALUE rb_file_join(
long argc,
VALUE *args);
4710#ifndef HAVE_REALPATH
4712rb_check_realpath_emulate_try(
VALUE arg)
4715 return rb_check_realpath_emulate(args[0], args[1], (
rb_encoding *)args[2], RB_REALPATH_CHECK);
4719rb_check_realpath_emulate_rescue(
VALUE arg,
VALUE exc)
4723#elif !defined(NEEDS_REALPATH_BUFFER) && defined(__APPLE__) && \
4724 (!defined(MAC_OS_X_VERSION_10_6) || (MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_6))
4726# include <sys/syslimits.h>
4727# define NEEDS_REALPATH_BUFFER 1
4731rb_check_realpath_internal(
VALUE basedir,
VALUE path,
rb_encoding *origenc,
enum rb_realpath_mode mode)
4734 VALUE unresolved_path;
4735 char *resolved_ptr = NULL;
4737# if defined(NEEDS_REALPATH_BUFFER) && NEEDS_REALPATH_BUFFER
4738 char resolved_buffer[PATH_MAX];
4740 char *
const resolved_buffer = NULL;
4743 if (mode == RB_REALPATH_DIR) {
4744 return rb_check_realpath_emulate(basedir, path, origenc, mode);
4748 if (*RSTRING_PTR(unresolved_path) !=
'/' && !
NIL_P(basedir)) {
4749 VALUE paths[2] = {basedir, unresolved_path};
4750 unresolved_path = rb_file_join(2, paths);
4752 if (origenc) unresolved_path = TO_OSPATH(unresolved_path);
4754 if ((resolved_ptr = realpath(RSTRING_PTR(unresolved_path), resolved_buffer)) == NULL) {
4763 if (
errno == ENOTSUP ||
4765 (
errno == ENOENT && rb_file_exist_p(0, unresolved_path))) {
4766 return rb_check_realpath_emulate(basedir, path, origenc, mode);
4769 if (mode == RB_REALPATH_CHECK) {
4772 rb_sys_fail_path(unresolved_path);
4774 resolved = ospath_new(resolved_ptr, strlen(resolved_ptr), rb_filesystem_encoding());
4775# if !(defined(NEEDS_REALPATH_BUFFER) && NEEDS_REALPATH_BUFFER)
4779# if !defined(__linux__) && !defined(__APPLE__)
4783 if (stat_without_gvl(RSTRING_PTR(resolved), &st) < 0) {
4784 if (mode == RB_REALPATH_CHECK) {
4787 rb_sys_fail_path(unresolved_path);
4791 if (origenc && origenc != rb_enc_get(resolved)) {
4795 rb_enc_associate(resolved, origenc);
4798 if (is_broken_string(resolved)) {
4799 rb_enc_associate(resolved, rb_filesystem_encoding());
4800 if (is_broken_string(resolved)) {
4801 rb_enc_associate(resolved, rb_ascii8bit_encoding());
4808 if (mode == RB_REALPATH_CHECK) {
4812 arg[2] = (
VALUE)origenc;
4815 rb_check_realpath_emulate_rescue,
Qnil);
4818 return rb_check_realpath_emulate(basedir, path, origenc, mode);
4824rb_realpath_internal(
VALUE basedir,
VALUE path,
int strict)
4826 const enum rb_realpath_mode mode =
4827 strict ? RB_REALPATH_STRICT : RB_REALPATH_DIR;
4828 return rb_check_realpath_internal(basedir, path, rb_enc_get(path), mode);
4834 return rb_check_realpath_internal(basedir, path, enc, RB_REALPATH_CHECK);
4851rb_file_s_realpath(
int argc,
VALUE *argv,
VALUE klass)
4854 VALUE path = argv[0];
4856 return rb_realpath_internal(basedir, path, 1);
4872rb_file_s_realdirpath(
int argc,
VALUE *argv,
VALUE klass)
4875 VALUE path = argv[0];
4877 return rb_realpath_internal(basedir, path, 0);
4881rmext(
const char *p,
long l0,
long l1,
const char *e,
long l2,
rb_encoding *enc)
4885 const char *s, *last;
4887 if (!e || !l2)
return 0;
4889 c = rb_enc_codepoint_len(e, e + l2, &len1, enc);
4890 if (rb_enc_ascget(e + len1, e + l2, &len2, enc) ==
'*' && len1 + len2 == l2) {
4891 if (c ==
'.')
return l0;
4896 if (rb_enc_codepoint_len(s, e, &len1, enc) == c) last = s;
4901 if (l1 < l2)
return l1;
4904 if (!at_char_boundary(p, s, p+l1, enc))
return 0;
4905#if CASEFOLD_FILESYSTEM
4906#define fncomp strncasecmp
4908#define fncomp strncmp
4910 if (fncomp(s, e, l2) == 0) {
4916static inline const char *
4917enc_find_basename(
const char *name,
long *baselen,
long *alllen,
bool mb_enc,
rb_encoding *enc)
4919 const char *p, *q, *e, *end;
4920#if defined DOSISH_DRIVE_LETTER || defined DOSISH_UNC
4925 long len = (alllen ? (size_t)*alllen : strlen(name));
4932 name = skipprefix(name, end, mb_enc, enc);
4933#if defined DOSISH_DRIVE_LETTER || defined DOSISH_UNC
4937 while (name < end && isdirsep(*name)) {
4944#if defined DOSISH_DRIVE_LETTER || defined DOSISH_UNC
4948#ifdef DOSISH_DRIVE_LETTER
4949 else if (*p ==
':') {
4962 p = strrdirsep(name, end, mb_enc, enc);
4967 while (isdirsep(*p)) {
4972 n = ntfs_tail(p, end, enc) - p;
4974 n = chompdirsep(p, end, mb_enc, enc) - p;
4976 for (q = p; q - p < n && *q ==
'.'; q++);
4977 for (e = 0; q - p < n; Inc(q, end, mb_enc, enc)) {
4978 if (*q ==
'.') e = q;
4998ruby_enc_find_basename(
const char *name,
long *baselen,
long *alllen,
rb_encoding *enc)
5000 return enc_find_basename(name, baselen, alllen,
true, enc);
5024 const char *name, *p, *fp = 0;
5030 CheckPath(fname, name);
5034 check_path_encoding(fext);
5036 if (
NIL_P(fext) || !(enc = rb_enc_compatible(fname, fext))) {
5037 enc = rb_str_enc_get(fname);
5040 n = RSTRING_LEN(fname);
5041 if (n <= 0 || !*name) {
5042 return rb_enc_str_new(0, 0, enc);
5045 bool mb_enc = !rb_str_encindex_fastpath(rb_enc_to_index(enc));
5046 p = enc_find_basename(name, &f, &n, mb_enc, enc);
5052 if (!(f = rmext(p, f, n, fp, RSTRING_LEN(fext), enc))) {
5057 if (f == RSTRING_LEN(fname)) {
5062 return rb_enc_str_new(p, f, enc);
5065static VALUE rb_file_dirname_n(
VALUE fname,
int n);
5087rb_file_s_dirname(
int argc,
VALUE *argv,
VALUE klass)
5093 return rb_file_dirname_n(argv[0], n);
5099 return rb_file_dirname_n(fname, 1);
5103rb_file_dirname_n(
VALUE fname,
int n)
5105 const char *name, *root, *p, *end;
5108 if (n < 0) rb_raise(rb_eArgError,
"negative level: %d", n);
5109 CheckPath(fname, name);
5110 end = name + RSTRING_LEN(fname);
5112 bool mb_enc = !rb_str_enc_fastpath(fname);
5115 root = skiproot(name, end);
5117 if (root > name + 1 && isdirsep(*name))
5118 root = skipprefix(name = root - 2, end, mb_enc, enc);
5120 if (root > name + 1)
5123 if (n > (end - root + 1) / 2) {
5129 if (!(p = strrdirsep(root, p, mb_enc, enc))) {
5138 return rb_enc_str_new(
".", 1, enc);
5140#ifdef DOSISH_DRIVE_LETTER
5141 if (name + 3 < end && has_drive_letter(name) && isdirsep(*(name + 2))) {
5142 const char *top = skiproot(name + 2, end);
5143 dirname = rb_enc_str_new(name, 3, enc);
5148 dirname = rb_enc_str_new(name, p - name, enc);
5149#ifdef DOSISH_DRIVE_LETTER
5150 if (root == name + 2 && p == root && name[1] ==
':')
5156static inline const char *
5157enc_find_extname(
const char *name,
long *
len,
bool mb_enc,
rb_encoding *enc)
5159 const char *p, *e, *end = name + (
len ? *
len : (long)strlen(name));
5161 p = strrdirsep(name, end, mb_enc, enc);
5165 do name = ++p;
while (isdirsep(*p));
5168 while (*p && *p ==
'.') p++;
5170 if (*p ==
'.' || istrailinggarbage(*p)) {
5172 const char *last = p++, *dot = last;
5173 while (istrailinggarbage(*p)) {
5174 if (*p ==
'.') dot = p;
5177 if (!*p || isADS(*p)) {
5181 if (*last ==
'.' || dot > last) e = dot;
5188 else if (isADS(*p)) {
5192 else if (isdirsep(*p))
5194 Inc(p, end, mb_enc, enc);
5199 if (!e || e == name)
5224 return enc_find_extname(name,
len,
true, enc);
5257 CheckPath(fname, name);
5258 long len = RSTRING_LEN(fname);
5261 return rb_enc_str_new(0, 0, rb_str_enc_get(fname));
5264 bool mb_enc = !rb_str_enc_fastpath(fname);
5267 const char *ext = enc_find_extname(name, &
len, mb_enc, enc);
5268 return rb_enc_str_new(ext,
len, enc);
5301 return rb_get_path(fname);
5325file_inspect_join(
VALUE ary,
VALUE arg,
int recur)
5327 if (recur || ary == arg) rb_raise(rb_eArgError,
"recursive array");
5328 return rb_file_join_ary(arg);
5332rb_file_join_ary(
VALUE ary)
5336 const char *name, *tail;
5346 check_path_encoding(tmp);
5347 len += RSTRING_LEN(tmp);
5355 RBASIC_CLEAR_CLASS(result);
5358 switch (OBJ_BUILTIN_TYPE(tmp)) {
5360 if (!checked) check_path_encoding(tmp);
5365 rb_raise(rb_eArgError,
"recursive array");
5377 rb_enc_copy(result, tmp);
5380 tail = chompdirsep(name, name +
len,
true, rb_enc_get(result));
5381 if (RSTRING_PTR(tmp) && isdirsep(RSTRING_PTR(tmp)[0])) {
5388 enc = fs_enc_check(result, tmp);
5390 rb_enc_associate(result, enc);
5398rb_file_join_fastpath(
long argc,
VALUE *args)
5403 for (i = 0; i < argc; i++) {
5404 VALUE tmp = args[i];
5406 size += RSTRING_LEN(tmp);
5419 const char *name = RSTRING_PTR(result);
5420 for (i = 1; i < argc; i++) {
5421 VALUE tmp = args[i];
5422 long len = RSTRING_LEN(result);
5428 if (isdirsep(tmp_s[0])) {
5430 long trailing_seps = 0;
5431 while (isdirsep(name[
len - trailing_seps - 1])) {
5436 else if (!isdirsep(name[
len - 1])) {
5443 rb_enc_associate(result, new_enc);
5444 encidx = rb_enc_to_index(new_enc);
5450 rb_str_null_check(result);
5455rb_file_join(
long argc,
VALUE *args)
5457 if (RB_UNLIKELY(argc == 0)) {
5461 VALUE result = rb_file_join_fastpath(argc, args);
5462 if (RB_LIKELY(result)) {
5480rb_file_s_join(
int argc,
VALUE *argv,
VALUE klass)
5482 return rb_file_join(argc, argv);
5485#if defined(HAVE_TRUNCATE)
5486struct truncate_arg {
5492nogvl_truncate(
void *ptr)
5494 struct truncate_arg *ta = ptr;
5495 return (
void *)(
VALUE)truncate(ta->path, ta->pos);
5516 struct truncate_arg ta;
5521 path = rb_str_encode_ospath(path);
5524 r = IO_WITHOUT_GVL_INT(nogvl_truncate, &ta);
5526 rb_sys_fail_path(path);
5530#define rb_file_s_truncate rb_f_notimplement
5533#if defined(HAVE_FTRUNCATE)
5534struct ftruncate_arg {
5540nogvl_ftruncate(
void *ptr)
5542 struct ftruncate_arg *fa = ptr;
5544 return (
VALUE)ftruncate(fa->fd, fa->pos);
5565 struct ftruncate_arg fa;
5572 rb_io_flush_raw(obj, 0);
5574 if ((
int)rb_io_blocking_region(fptr, nogvl_ftruncate, &fa) < 0) {
5575 rb_sys_fail_path(fptr->
pathv);
5580#define rb_file_truncate rb_f_notimplement
5597#include <winerror.h>
5601rb_thread_flock(
void *data)
5604 int old_errno =
errno;
5606 int *op = data, ret = flock(op[0], op[1]);
5609 if (GetLastError() == ERROR_NOT_LOCKED) {
5667 op[1] = op1 =
NUM2INT(operation);
5672 rb_io_flush_raw(obj, 0);
5674 while ((
int)rb_io_blocking_region(fptr, rb_thread_flock, op) < 0) {
5679#if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN
5682 if (op1 & LOCK_NB)
return Qfalse;
5685 time.tv_usec = 100 * 1000;
5691#if defined(ERESTART)
5697 rb_syserr_fail_path(e, fptr->
pathv);
5704test_check(
int n,
int argc,
VALUE *argv)
5710 for (i=1; i<n; i++) {
5717#define CHECK(n) test_check((n), argc, argv)
5809 if (strchr(
"bcdefgGkloOprRsSuwWxXz", cmd)) {
5813 return rb_file_blockdev_p(0, argv[1]);
5816 return rb_file_chardev_p(0, argv[1]);
5819 return rb_file_directory_p(0, argv[1]);
5822 return rb_file_exist_p(0, argv[1]);
5825 return rb_file_file_p(0, argv[1]);
5828 return rb_file_sgid_p(0, argv[1]);
5831 return rb_file_grpowned_p(0, argv[1]);
5834 return rb_file_sticky_p(0, argv[1]);
5837 return rb_file_symlink_p(0, argv[1]);
5840 return rb_file_owned_p(0, argv[1]);
5843 return rb_file_rowned_p(0, argv[1]);
5846 return rb_file_pipe_p(0, argv[1]);
5849 return rb_file_readable_p(0, argv[1]);
5852 return rb_file_readable_real_p(0, argv[1]);
5855 return rb_file_size_p(0, argv[1]);
5858 return rb_file_socket_p(0, argv[1]);
5861 return rb_file_suid_p(0, argv[1]);
5864 return rb_file_writable_p(0, argv[1]);
5867 return rb_file_writable_real_p(0, argv[1]);
5870 return rb_file_executable_p(0, argv[1]);
5873 return rb_file_executable_real_p(0, argv[1]);
5876 return rb_file_zero_p(0, argv[1]);
5880 if (strchr(
"MAC", cmd)) {
5882 VALUE fname = argv[1];
5885 if (
rb_stat(fname, &st) == -1) {
5888 rb_syserr_fail_path(e, fname);
5893 return stat_atime(&st);
5895 return stat_mtime(&st);
5897 return stat_ctime(&st);
5903 return rb_file_identical_p(0, argv[1], argv[2]);
5906 if (strchr(
"=<>", cmd)) {
5907 struct stat st1, st2;
5914 t1 = stat_mtimespec(&st1);
5915 t2 = stat_mtimespec(&st2);
5919 if (t1.tv_sec == t2.tv_sec && t1.tv_nsec == t2.tv_nsec)
return Qtrue;
5923 if (t1.tv_sec > t2.tv_sec)
return Qtrue;
5924 if (t1.tv_sec == t2.tv_sec && t1.tv_nsec > t2.tv_nsec)
return Qtrue;
5928 if (t1.tv_sec < t2.tv_sec)
return Qtrue;
5929 if (t1.tv_sec == t2.tv_sec && t1.tv_nsec < t2.tv_nsec)
return Qtrue;
5936 rb_raise(rb_eArgError,
"unknown command '%s%c'", cmd ==
'\'' || cmd ==
'\\' ?
"\\" :
"", cmd);
5939 rb_raise(rb_eArgError,
"unknown command \"\\x%02X\"", cmd);
5958rb_stat_s_alloc(
VALUE klass)
5979 fname = rb_str_encode_ospath(fname);
5981 rb_sys_fail_path(fname);
5988 rb_st->initialized =
true;
6005 *copy_rb_st = *orig_rb_st;
6024rb_stat_ftype(
VALUE obj)
6026 return rb_file_ftype(get_stat(obj)->ST_(mode));
6043 if (S_ISDIR(get_stat(obj)->ST_(mode)))
return Qtrue;
6059 if (S_ISFIFO(get_stat(obj)->ST_(mode)))
return Qtrue;
6085 if (S_ISLNK(get_stat(obj)->ST_(mode)))
return Qtrue;
6106 if (S_ISSOCK(get_stat(obj)->ST_(mode)))
return Qtrue;
6129 if (S_ISBLK(get_stat(obj)->ST_(mode)))
return Qtrue;
6150 if (S_ISCHR(get_stat(obj)->ST_(mode)))
return Qtrue;
6168rb_stat_owned(
VALUE obj)
6170 if (get_stat(obj)->ST_(uid) == geteuid())
return Qtrue;
6175rb_stat_rowned(
VALUE obj)
6177 if (get_stat(obj)->ST_(uid) == getuid())
return Qtrue;
6194rb_stat_grpowned(
VALUE obj)
6197 if (rb_group_member(get_stat(obj)->ST_(gid)))
return Qtrue;
6216 rb_io_stat_data *st = get_stat(obj);
6219 if (geteuid() == 0)
return Qtrue;
6222 if (rb_stat_owned(obj))
6223 return RBOOL(st->ST_(mode) & S_IRUSR);
6226 if (rb_stat_grpowned(obj))
6227 return RBOOL(st->ST_(mode) & S_IRGRP);
6230 if (!(st->ST_(mode) & S_IROTH))
return Qfalse;
6249 rb_io_stat_data *st = get_stat(obj);
6252 if (getuid() == 0)
return Qtrue;
6255 if (rb_stat_rowned(obj))
6256 return RBOOL(st->ST_(mode) & S_IRUSR);
6259 if (rb_group_member(get_stat(obj)->ST_(gid)))
6260 return RBOOL(st->ST_(mode) & S_IRGRP);
6263 if (!(st->ST_(mode) & S_IROTH))
return Qfalse;
6282rb_stat_wr(
VALUE obj)
6285 rb_io_stat_data *st = get_stat(obj);
6286 if ((st->ST_(mode) & (S_IROTH)) == S_IROTH) {
6287 return UINT2NUM(st->ST_(mode) & (S_IRUGO|S_IWUGO|S_IXUGO));
6307 rb_io_stat_data *st = get_stat(obj);
6310 if (geteuid() == 0)
return Qtrue;
6313 if (rb_stat_owned(obj))
6314 return RBOOL(st->ST_(mode) & S_IWUSR);
6317 if (rb_stat_grpowned(obj))
6318 return RBOOL(st->ST_(mode) & S_IWGRP);
6321 if (!(st->ST_(mode) & S_IWOTH))
return Qfalse;
6340 rb_io_stat_data *st = get_stat(obj);
6343 if (getuid() == 0)
return Qtrue;
6346 if (rb_stat_rowned(obj))
6347 return RBOOL(st->ST_(mode) & S_IWUSR);
6350 if (rb_group_member(get_stat(obj)->ST_(gid)))
6351 return RBOOL(st->ST_(mode) & S_IWGRP);
6354 if (!(st->ST_(mode) & S_IWOTH))
return Qfalse;
6373rb_stat_ww(
VALUE obj)
6376 rb_io_stat_data *st = get_stat(obj);
6377 if ((st->ST_(mode) & (S_IWOTH)) == S_IWOTH) {
6378 return UINT2NUM(st->ST_(mode) & (S_IRUGO|S_IWUGO|S_IXUGO));
6400 rb_io_stat_data *st = get_stat(obj);
6403 if (geteuid() == 0) {
6404 return RBOOL(st->ST_(mode) & S_IXUGO);
6408 if (rb_stat_owned(obj))
6409 return RBOOL(st->ST_(mode) & S_IXUSR);
6412 if (rb_stat_grpowned(obj))
6413 return RBOOL(st->ST_(mode) & S_IXGRP);
6416 if (!(st->ST_(mode) & S_IXOTH))
return Qfalse;
6432 rb_io_stat_data *st = get_stat(obj);
6435 if (getuid() == 0) {
6436 return RBOOL(st->ST_(mode) & S_IXUGO);
6440 if (rb_stat_rowned(obj))
6441 return RBOOL(st->ST_(mode) & S_IXUSR);
6444 if (rb_group_member(get_stat(obj)->ST_(gid)))
6445 return RBOOL(st->ST_(mode) & S_IXGRP);
6448 if (!(st->ST_(mode) & S_IXOTH))
return Qfalse;
6467 if (S_ISREG(get_stat(obj)->ST_(mode)))
return Qtrue;
6485 if (get_stat(obj)->ST_(size) == 0)
return Qtrue;
6504 rb_off_t size = get_stat(obj)->ST_(size);
6506 if (size == 0)
return Qnil;
6522rb_stat_suid(
VALUE obj)
6525 if (get_stat(obj)->ST_(mode) & S_ISUID)
return Qtrue;
6543rb_stat_sgid(
VALUE obj)
6546 if (get_stat(obj)->ST_(mode) & S_ISGID)
return Qtrue;
6564rb_stat_sticky(
VALUE obj)
6567 if (get_stat(obj)->ST_(mode) & S_ISVTX)
return Qtrue;
6572#if !defined HAVE_MKFIFO && defined HAVE_MKNOD && defined S_IFIFO
6573#define mkfifo(path, mode) mknod(path, (mode)&~S_IFMT|S_IFIFO, 0)
6584nogvl_mkfifo(
void *ptr)
6586 struct mkfifo_arg *ma = ptr;
6588 return (
void *)(
VALUE)mkfifo(ma->path, ma->mode);
6605 struct mkfifo_arg ma;
6614 path = rb_str_encode_ospath(path);
6615 ma.path = RSTRING_PTR(path);
6616 if (IO_WITHOUT_GVL(nogvl_mkfifo, &ma)) {
6617 rb_sys_fail_path(path);
6622#define rb_file_s_mkfifo rb_f_notimplement
6625static VALUE rb_mFConst;
6628rb_file_const(
const char *name,
VALUE value)
6630 rb_define_const(rb_mFConst, name, value);
6634rb_is_absolute_path(
const char *path)
6636#ifdef DOSISH_DRIVE_LETTER
6637 if (has_drive_letter(path) && isdirsep(path[2]))
return 1;
6640 if (isdirsep(path[0]) && isdirsep(path[1]))
return 1;
6643 if (path[0] ==
'/')
return 1;
6649ruby_is_fd_loadable(
int fd)
6656 if (fstat(fd, &st) < 0)
6659 if (S_ISREG(st.st_mode))
6662 if (S_ISFIFO(st.st_mode) || S_ISCHR(st.st_mode))
6665 if (S_ISDIR(st.st_mode))
6676rb_file_load_ok(
const char *path)
6683 int mode = (O_RDONLY |
6684#if defined O_NONBLOCK
6686#elif defined O_NDELAY
6692 if (!rb_gc_for_fd(
errno))
return 0;
6694 if (fd < 0)
return 0;
6697 ret = ruby_is_fd_loadable(fd);
6704is_explicit_relative(
const char *path)
6706 if (*path++ !=
'.')
return 0;
6707 if (*path ==
'.') path++;
6708 return isdirsep(*path);
6714 int encidx = rb_enc_get_index(orig);
6715 if (encidx == ENCINDEX_ASCII_8BIT || encidx == ENCINDEX_US_ASCII)
6716 encidx = rb_filesystem_encindex();
6717 rb_enc_associate_index(path, encidx);
6725rb_find_file_ext(
VALUE *filep,
const char *
const *ext)
6728 VALUE fname = *filep, load_path, tmp;
6732 if (!ext[0])
return 0;
6735 fname = file_expand_path_1(fname);
6736 f = RSTRING_PTR(fname);
6741 if (expanded || rb_is_absolute_path(f) || is_explicit_relative(f)) {
6742 if (!expanded) fname = file_expand_path_1(fname);
6743 fnlen = RSTRING_LEN(fname);
6744 for (i=0; ext[i]; i++) {
6746 if (rb_file_load_ok(RSTRING_PTR(fname))) {
6747 *filep = copy_path_class(fname, *filep);
6755 RB_GC_GUARD(load_path) = rb_get_expanded_load_path();
6756 if (!load_path)
return 0;
6759 RBASIC_CLEAR_CLASS(fname);
6760 fnlen = RSTRING_LEN(fname);
6762 rb_enc_associate_index(tmp, rb_usascii_encindex());
6763 for (j=0; ext[j]; j++) {
6765 for (i = 0; i <
RARRAY_LEN(load_path); i++) {
6769 if (RSTRING_LEN(str) == 0)
continue;
6770 rb_file_expand_path_internal(fname, str, 0, 0, tmp);
6771 if (rb_file_load_ok(RSTRING_PTR(tmp))) {
6772 *filep = copy_path_class(tmp, *filep);
6778 rb_str_resize(tmp, 0);
6786 VALUE tmp, load_path;
6791 tmp = file_expand_path_1(path);
6792 path = copy_path_class(tmp, path);
6793 f = RSTRING_PTR(path);
6797 if (expanded || rb_is_absolute_path(f) || is_explicit_relative(f)) {
6798 if (!rb_file_load_ok(f))
return 0;
6800 path = copy_path_class(file_expand_path_1(path), path);
6804 RB_GC_GUARD(load_path) = rb_get_expanded_load_path();
6809 rb_enc_associate_index(tmp, rb_usascii_encindex());
6810 for (i = 0; i <
RARRAY_LEN(load_path); i++) {
6813 if (RSTRING_LEN(str) > 0) {
6814 rb_file_expand_path_internal(path, str, 0, 0, tmp);
6815 f = RSTRING_PTR(tmp);
6816 if (rb_file_load_ok(f))
goto found;
6819 rb_str_resize(tmp, 0);
6827 return copy_path_class(tmp, path);
6830#define define_filetest_function(name, func, argc) do { \
6831 rb_define_module_function(rb_mFileTest, name, func, argc); \
6832 rb_define_singleton_method(rb_cFile, name, func, argc); \
6835const char ruby_null_device[] =
6838#elif defined AMIGA || defined __amigaos__
7642#if defined(__APPLE__) && defined(HAVE_WORKING_FORK)
7643 rb_CFString_class_initialize_before_fork();
7651 define_filetest_function(
"directory?", rb_file_directory_p, 1);
7652 define_filetest_function(
"exist?", rb_file_exist_p, 1);
7653 define_filetest_function(
"readable?", rb_file_readable_p, 1);
7654 define_filetest_function(
"readable_real?", rb_file_readable_real_p, 1);
7655 define_filetest_function(
"world_readable?", rb_file_world_readable_p, 1);
7656 define_filetest_function(
"writable?", rb_file_writable_p, 1);
7657 define_filetest_function(
"writable_real?", rb_file_writable_real_p, 1);
7658 define_filetest_function(
"world_writable?", rb_file_world_writable_p, 1);
7659 define_filetest_function(
"executable?", rb_file_executable_p, 1);
7660 define_filetest_function(
"executable_real?", rb_file_executable_real_p, 1);
7661 define_filetest_function(
"file?", rb_file_file_p, 1);
7662 define_filetest_function(
"zero?", rb_file_zero_p, 1);
7663 define_filetest_function(
"empty?", rb_file_zero_p, 1);
7664 define_filetest_function(
"size?", rb_file_size_p, 1);
7665 define_filetest_function(
"size", rb_file_s_size, 1);
7666 define_filetest_function(
"owned?", rb_file_owned_p, 1);
7667 define_filetest_function(
"grpowned?", rb_file_grpowned_p, 1);
7669 define_filetest_function(
"pipe?", rb_file_pipe_p, 1);
7670 define_filetest_function(
"symlink?", rb_file_symlink_p, 1);
7671 define_filetest_function(
"socket?", rb_file_socket_p, 1);
7673 define_filetest_function(
"blockdev?", rb_file_blockdev_p, 1);
7674 define_filetest_function(
"chardev?", rb_file_chardev_p, 1);
7676 define_filetest_function(
"setuid?", rb_file_suid_p, 1);
7677 define_filetest_function(
"setgid?", rb_file_sgid_p, 1);
7678 define_filetest_function(
"sticky?", rb_file_sticky_p, 1);
7680 define_filetest_function(
"identical?", rb_file_identical_p, 2);
7718 separator = rb_fstring_lit(
"/");
7720 rb_define_const(
rb_cFile,
"Separator", separator);
7722 rb_define_const(
rb_cFile,
"SEPARATOR", separator);
8074 rb_define_const(rb_mFConst,
"RDONLY",
INT2FIX(O_RDONLY));
8076 rb_define_const(rb_mFConst,
"WRONLY",
INT2FIX(O_WRONLY));
8078 rb_define_const(rb_mFConst,
"RDWR",
INT2FIX(O_RDWR));
8080 rb_define_const(rb_mFConst,
"APPEND",
INT2FIX(O_APPEND));
8082 rb_define_const(rb_mFConst,
"CREAT",
INT2FIX(O_CREAT));
8084 rb_define_const(rb_mFConst,
"EXCL",
INT2FIX(O_EXCL));
8085#if defined(O_NDELAY) || defined(O_NONBLOCK)
8087# define O_NONBLOCK O_NDELAY
8090 rb_define_const(rb_mFConst,
"NONBLOCK",
INT2FIX(O_NONBLOCK));
8093 rb_define_const(rb_mFConst,
"TRUNC",
INT2FIX(O_TRUNC));
8096 rb_define_const(rb_mFConst,
"NOCTTY",
INT2FIX(O_NOCTTY));
8102 rb_define_const(rb_mFConst,
"BINARY",
INT2FIX(O_BINARY));
8103#ifndef O_SHARE_DELETE
8104# define O_SHARE_DELETE 0
8107 rb_define_const(rb_mFConst,
"SHARE_DELETE",
INT2FIX(O_SHARE_DELETE));
8110 rb_define_const(rb_mFConst,
"SYNC",
INT2FIX(O_SYNC));
8114 rb_define_const(rb_mFConst,
"DSYNC",
INT2FIX(O_DSYNC));
8118 rb_define_const(rb_mFConst,
"RSYNC",
INT2FIX(O_RSYNC));
8122 rb_define_const(rb_mFConst,
"NOFOLLOW",
INT2FIX(O_NOFOLLOW));
8126 rb_define_const(rb_mFConst,
"NOATIME",
INT2FIX(O_NOATIME));
8130 rb_define_const(rb_mFConst,
"DIRECT",
INT2FIX(O_DIRECT));
8134 rb_define_const(rb_mFConst,
"TMPFILE",
INT2FIX(O_TMPFILE));
8138 rb_define_const(rb_mFConst,
"LOCK_SH",
INT2FIX(LOCK_SH));
8140 rb_define_const(rb_mFConst,
"LOCK_EX",
INT2FIX(LOCK_EX));
8142 rb_define_const(rb_mFConst,
"LOCK_UN",
INT2FIX(LOCK_UN));
8144 rb_define_const(rb_mFConst,
"LOCK_NB",
INT2FIX(LOCK_NB));
8147 rb_define_const(rb_mFConst,
"NULL", rb_fstring_cstr(ruby_null_device));
#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.
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.
#define ENCODING_SET_INLINED(obj, i)
Old name of RB_ENCODING_SET_INLINED.
#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 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 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)
Just calls rb_obj_freeze_inline() inside.
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.
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_ary_new_from_values(long n, const VALUE *elts)
Identical to rb_ary_new_from_args(), except how objects are passed.
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'...
#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.
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.
#define rb_str_new(str, len)
Allocates an instance of rb_cString.
#define rb_str_buf_cat
Just another name of rb_str_cat.
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.
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_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).
#define rb_str_dup_frozen
Just another name of rb_str_new_frozen.
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".
#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 strdup(s)
Just another name of ruby_strdup.
#define ALLOCA_N(type, n)
#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 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*.
#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 RUBY_TYPED_FREE_IMMEDIATELY
Macros to see if each corresponding flag is defined.
#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 _(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.
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.