Ruby  1.9.3p448(2013-06-27revision41675)
file.c
Go to the documentation of this file.
1 /**********************************************************************
2 
3  file.c -
4 
5  $Author: usa $
6  created at: Mon Nov 15 12:24:34 JST 1993
7 
8  Copyright (C) 1993-2007 Yukihiro Matsumoto
9  Copyright (C) 2000 Network Applied Communication Laboratory, Inc.
10  Copyright (C) 2000 Information-technology Promotion Agency, Japan
11 
12 **********************************************************************/
13 
14 #ifdef _WIN32
15 #include "missing/file.h"
16 #endif
17 #ifdef __CYGWIN__
18 #include <windows.h>
19 #include <sys/cygwin.h>
20 #include <wchar.h>
21 #endif
22 
23 #include "ruby/ruby.h"
24 #include "ruby/io.h"
25 #include "ruby/util.h"
26 #include "dln.h"
27 #include "internal.h"
28 
29 #ifdef HAVE_UNISTD_H
30 #include <unistd.h>
31 #endif
32 
33 #ifdef HAVE_SYS_FILE_H
34 # include <sys/file.h>
35 #else
36 int flock(int, int);
37 #endif
38 
39 #ifdef HAVE_SYS_PARAM_H
40 # include <sys/param.h>
41 #endif
42 #ifndef MAXPATHLEN
43 # define MAXPATHLEN 1024
44 #endif
45 
46 #include <ctype.h>
47 
48 #include <time.h>
49 
50 #ifdef HAVE_UTIME_H
51 #include <utime.h>
52 #elif defined HAVE_SYS_UTIME_H
53 #include <sys/utime.h>
54 #endif
55 
56 #ifdef HAVE_PWD_H
57 #include <pwd.h>
58 #endif
59 
60 #include <sys/types.h>
61 #include <sys/stat.h>
62 
63 #ifdef HAVE_SYS_MKDEV_H
64 #include <sys/mkdev.h>
65 #endif
66 
67 #if defined(HAVE_FCNTL_H)
68 #include <fcntl.h>
69 #endif
70 
71 #if defined(HAVE_SYS_TIME_H)
72 #include <sys/time.h>
73 #endif
74 
75 #if !defined HAVE_LSTAT && !defined lstat
76 #define lstat stat
77 #endif
78 
79 /* define system APIs */
80 #ifdef _WIN32
81 #define STAT(p, s) rb_w32_ustati64((p), (s))
82 #undef lstat
83 #define lstat(p, s) rb_w32_ustati64((p), (s))
84 #undef access
85 #define access(p, m) rb_w32_uaccess((p), (m))
86 #undef chmod
87 #define chmod(p, m) rb_w32_uchmod((p), (m))
88 #undef chown
89 #define chown(p, o, g) rb_w32_uchown((p), (o), (g))
90 #undef utime
91 #define utime(p, t) rb_w32_uutime((p), (t))
92 #undef link
93 #define link(f, t) rb_w32_ulink((f), (t))
94 #undef unlink
95 #define unlink(p) rb_w32_uunlink(p)
96 #undef rename
97 #define rename(f, t) rb_w32_urename((f), (t))
98 #else
99 #define STAT(p, s) stat((p), (s))
100 #endif
101 
102 #define rb_sys_fail_path(path) rb_sys_fail_str(path)
103 
104 #if defined(__BEOS__) || defined(__HAIKU__) /* should not change ID if -1 */
105 static int
106 be_chown(const char *path, uid_t owner, gid_t group)
107 {
108  if (owner == (uid_t)-1 || group == (gid_t)-1) {
109  struct stat st;
110  if (STAT(path, &st) < 0) return -1;
111  if (owner == (uid_t)-1) owner = st.st_uid;
112  if (group == (gid_t)-1) group = st.st_gid;
113  }
114  return chown(path, owner, group);
115 }
116 #define chown be_chown
117 static int
118 be_fchown(int fd, uid_t owner, gid_t group)
119 {
120  if (owner == (uid_t)-1 || group == (gid_t)-1) {
121  struct stat st;
122  if (fstat(fd, &st) < 0) return -1;
123  if (owner == (uid_t)-1) owner = st.st_uid;
124  if (group == (gid_t)-1) group = st.st_gid;
125  }
126  return fchown(fd, owner, group);
127 }
128 #define fchown be_fchown
129 #endif /* __BEOS__ || __HAIKU__ */
130 
134 
135 #define insecure_obj_p(obj, level) ((level) >= 4 || ((level) > 0 && OBJ_TAINTED(obj)))
136 
137 static VALUE
139 {
140 #ifndef _WIN32 /* non Windows == Unix */
141  rb_encoding *fname_encoding = rb_enc_from_index(ENCODING_GET(name));
142  rb_encoding *fs_encoding;
144  && rb_usascii_encoding() != fname_encoding
145  && rb_ascii8bit_encoding() != fname_encoding
146  && (fs_encoding = rb_filesystem_encoding()) != fname_encoding
147  && !rb_enc_str_asciionly_p(name)) {
148  /* Don't call rb_filesystem_encoding() before US-ASCII and ASCII-8BIT */
149  /* fs_encoding should be ascii compatible */
150  name = rb_str_conv_enc(name, fname_encoding, fs_encoding);
151  }
152 #endif
153  return name;
154 }
155 
156 static VALUE
157 rb_get_path_check(VALUE obj, int level)
158 {
159  VALUE tmp;
160  ID to_path;
161  rb_encoding *enc;
162 
163  if (insecure_obj_p(obj, level)) {
165  }
166 
167  CONST_ID(to_path, "to_path");
168  tmp = rb_check_funcall(obj, to_path, 0, 0);
169  if (tmp == Qundef) {
170  tmp = obj;
171  }
172  StringValue(tmp);
173 
174  tmp = file_path_convert(tmp);
175  if (obj != tmp && insecure_obj_p(tmp, level)) {
177  }
178  enc = rb_enc_get(tmp);
179  if (!rb_enc_asciicompat(enc)) {
180  tmp = rb_str_inspect(tmp);
181  rb_raise(rb_eEncCompatError, "path name must be ASCII-compatible (%s): %s",
182  rb_enc_name(enc), RSTRING_PTR(tmp));
183  }
184 
185  StringValueCStr(tmp);
186 
187  return rb_str_new4(tmp);
188 }
189 
190 VALUE
192 {
193  return rb_get_path_check(obj, 0);
194 }
195 
196 VALUE
198 {
199  return rb_get_path_check(obj, rb_safe_level());
200 }
201 
202 VALUE
204 {
205 #ifdef _WIN32
206  rb_encoding *enc = rb_enc_get(path);
207  if (enc != rb_ascii8bit_encoding()) {
208  rb_encoding *utf8 = rb_utf8_encoding();
209  if (enc != utf8)
210  path = rb_str_encode(path, rb_enc_from_encoding(utf8), 0, Qnil);
211  }
212  else if (RSTRING_LEN(path) > 0) {
213  path = rb_str_dup(path);
216  }
217 #endif
218  return path;
219 }
220 
221 static long
222 apply2files(void (*func)(const char *, VALUE, void *), VALUE vargs, void *arg)
223 {
224  long i;
225  volatile VALUE path;
226 
227  rb_secure(4);
228  for (i=0; i<RARRAY_LEN(vargs); i++) {
229  const char *s;
230  path = rb_get_path(RARRAY_PTR(vargs)[i]);
231  path = rb_str_encode_ospath(path);
232  s = RSTRING_PTR(path);
233  (*func)(s, path, arg);
234  }
235 
236  return RARRAY_LEN(vargs);
237 }
238 
239 /*
240  * call-seq:
241  * file.path -> filename
242  *
243  * Returns the pathname used to create <i>file</i> as a string. Does
244  * not normalize the name.
245  *
246  * File.new("testfile").path #=> "testfile"
247  * File.new("/tmp/../tmp/xxx", "w").path #=> "/tmp/../tmp/xxx"
248  *
249  */
250 
251 static VALUE
253 {
254  rb_io_t *fptr;
255 
256  fptr = RFILE(rb_io_taint_check(obj))->fptr;
258  if (NIL_P(fptr->pathv)) return Qnil;
259  return rb_obj_taint(rb_str_dup(fptr->pathv));
260 }
261 
262 static size_t
263 stat_memsize(const void *p)
264 {
265  return p ? sizeof(struct stat) : 0;
266 }
267 
269  "stat",
271 };
272 
273 static VALUE
274 stat_new_0(VALUE klass, struct stat *st)
275 {
276  struct stat *nst = 0;
277 
278  if (st) {
279  nst = ALLOC(struct stat);
280  *nst = *st;
281  }
282  return TypedData_Wrap_Struct(klass, &stat_data_type, nst);
283 }
284 
285 static VALUE
286 stat_new(struct stat *st)
287 {
288  return stat_new_0(rb_cStat, st);
289 }
290 
291 static struct stat*
293 {
294  struct stat* st;
295  TypedData_Get_Struct(self, struct stat, &stat_data_type, st);
296  if (!st) rb_raise(rb_eTypeError, "uninitialized File::Stat");
297  return st;
298 }
299 
300 static struct timespec stat_mtimespec(struct stat *st);
301 
302 /*
303  * call-seq:
304  * stat <=> other_stat -> -1, 0, 1, nil
305  *
306  * Compares <code>File::Stat</code> objects by comparing their
307  * respective modification times.
308  *
309  * f1 = File.new("f1", "w")
310  * sleep 1
311  * f2 = File.new("f2", "w")
312  * f1.stat <=> f2.stat #=> -1
313  */
314 
315 static VALUE
316 rb_stat_cmp(VALUE self, VALUE other)
317 {
318  if (rb_obj_is_kind_of(other, rb_obj_class(self))) {
319  struct timespec ts1 = stat_mtimespec(get_stat(self));
320  struct timespec ts2 = stat_mtimespec(get_stat(other));
321  if (ts1.tv_sec == ts2.tv_sec) {
322  if (ts1.tv_nsec == ts2.tv_nsec) return INT2FIX(0);
323  if (ts1.tv_nsec < ts2.tv_nsec) return INT2FIX(-1);
324  return INT2FIX(1);
325  }
326  if (ts1.tv_sec < ts2.tv_sec) return INT2FIX(-1);
327  return INT2FIX(1);
328  }
329  return Qnil;
330 }
331 
332 #define ST2UINT(val) ((val) & ~(~1UL << (sizeof(val) * CHAR_BIT - 1)))
333 
334 #ifndef NUM2DEVT
335 # define NUM2DEVT(v) NUM2UINT(v)
336 #endif
337 #ifndef DEVT2NUM
338 # define DEVT2NUM(v) UINT2NUM(v)
339 #endif
340 #ifndef PRI_DEVT_PREFIX
341 # define PRI_DEVT_PREFIX ""
342 #endif
343 
344 /*
345  * call-seq:
346  * stat.dev -> fixnum
347  *
348  * Returns an integer representing the device on which <i>stat</i>
349  * resides.
350  *
351  * File.stat("testfile").dev #=> 774
352  */
353 
354 static VALUE
356 {
357  return DEVT2NUM(get_stat(self)->st_dev);
358 }
359 
360 /*
361  * call-seq:
362  * stat.dev_major -> fixnum
363  *
364  * Returns the major part of <code>File_Stat#dev</code> or
365  * <code>nil</code>.
366  *
367  * File.stat("/dev/fd1").dev_major #=> 2
368  * File.stat("/dev/tty").dev_major #=> 5
369  */
370 
371 static VALUE
373 {
374 #if defined(major)
375  return INT2NUM(major(get_stat(self)->st_dev));
376 #else
377  return Qnil;
378 #endif
379 }
380 
381 /*
382  * call-seq:
383  * stat.dev_minor -> fixnum
384  *
385  * Returns the minor part of <code>File_Stat#dev</code> or
386  * <code>nil</code>.
387  *
388  * File.stat("/dev/fd1").dev_minor #=> 1
389  * File.stat("/dev/tty").dev_minor #=> 0
390  */
391 
392 static VALUE
394 {
395 #if defined(minor)
396  return INT2NUM(minor(get_stat(self)->st_dev));
397 #else
398  return Qnil;
399 #endif
400 }
401 
402 /*
403  * call-seq:
404  * stat.ino -> fixnum
405  *
406  * Returns the inode number for <i>stat</i>.
407  *
408  * File.stat("testfile").ino #=> 1083669
409  *
410  */
411 
412 static VALUE
414 {
415 #if SIZEOF_STRUCT_STAT_ST_INO > SIZEOF_LONG
416  return ULL2NUM(get_stat(self)->st_ino);
417 #else
418  return ULONG2NUM(get_stat(self)->st_ino);
419 #endif
420 }
421 
422 /*
423  * call-seq:
424  * stat.mode -> fixnum
425  *
426  * Returns an integer representing the permission bits of
427  * <i>stat</i>. The meaning of the bits is platform dependent; on
428  * Unix systems, see <code>stat(2)</code>.
429  *
430  * File.chmod(0644, "testfile") #=> 1
431  * s = File.stat("testfile")
432  * sprintf("%o", s.mode) #=> "100644"
433  */
434 
435 static VALUE
437 {
438  return UINT2NUM(ST2UINT(get_stat(self)->st_mode));
439 }
440 
441 /*
442  * call-seq:
443  * stat.nlink -> fixnum
444  *
445  * Returns the number of hard links to <i>stat</i>.
446  *
447  * File.stat("testfile").nlink #=> 1
448  * File.link("testfile", "testfile.bak") #=> 0
449  * File.stat("testfile").nlink #=> 2
450  *
451  */
452 
453 static VALUE
455 {
456  return UINT2NUM(get_stat(self)->st_nlink);
457 }
458 
459 /*
460  * call-seq:
461  * stat.uid -> fixnum
462  *
463  * Returns the numeric user id of the owner of <i>stat</i>.
464  *
465  * File.stat("testfile").uid #=> 501
466  *
467  */
468 
469 static VALUE
471 {
472  return UIDT2NUM(get_stat(self)->st_uid);
473 }
474 
475 /*
476  * call-seq:
477  * stat.gid -> fixnum
478  *
479  * Returns the numeric group id of the owner of <i>stat</i>.
480  *
481  * File.stat("testfile").gid #=> 500
482  *
483  */
484 
485 static VALUE
487 {
488  return GIDT2NUM(get_stat(self)->st_gid);
489 }
490 
491 /*
492  * call-seq:
493  * stat.rdev -> fixnum or nil
494  *
495  * Returns an integer representing the device type on which
496  * <i>stat</i> resides. Returns <code>nil</code> if the operating
497  * system doesn't support this feature.
498  *
499  * File.stat("/dev/fd1").rdev #=> 513
500  * File.stat("/dev/tty").rdev #=> 1280
501  */
502 
503 static VALUE
505 {
506 #ifdef HAVE_ST_RDEV
507  return DEVT2NUM(get_stat(self)->st_rdev);
508 #else
509  return Qnil;
510 #endif
511 }
512 
513 /*
514  * call-seq:
515  * stat.rdev_major -> fixnum
516  *
517  * Returns the major part of <code>File_Stat#rdev</code> or
518  * <code>nil</code>.
519  *
520  * File.stat("/dev/fd1").rdev_major #=> 2
521  * File.stat("/dev/tty").rdev_major #=> 5
522  */
523 
524 static VALUE
526 {
527 #if defined(HAVE_ST_RDEV) && defined(major)
528  return DEVT2NUM(major(get_stat(self)->st_rdev));
529 #else
530  return Qnil;
531 #endif
532 }
533 
534 /*
535  * call-seq:
536  * stat.rdev_minor -> fixnum
537  *
538  * Returns the minor part of <code>File_Stat#rdev</code> or
539  * <code>nil</code>.
540  *
541  * File.stat("/dev/fd1").rdev_minor #=> 1
542  * File.stat("/dev/tty").rdev_minor #=> 0
543  */
544 
545 static VALUE
547 {
548 #if defined(HAVE_ST_RDEV) && defined(minor)
549  return DEVT2NUM(minor(get_stat(self)->st_rdev));
550 #else
551  return Qnil;
552 #endif
553 }
554 
555 /*
556  * call-seq:
557  * stat.size -> fixnum
558  *
559  * Returns the size of <i>stat</i> in bytes.
560  *
561  * File.stat("testfile").size #=> 66
562  */
563 
564 static VALUE
566 {
567  return OFFT2NUM(get_stat(self)->st_size);
568 }
569 
570 /*
571  * call-seq:
572  * stat.blksize -> integer or nil
573  *
574  * Returns the native file system's block size. Will return <code>nil</code>
575  * on platforms that don't support this information.
576  *
577  * File.stat("testfile").blksize #=> 4096
578  *
579  */
580 
581 static VALUE
583 {
584 #ifdef HAVE_ST_BLKSIZE
585  return ULONG2NUM(get_stat(self)->st_blksize);
586 #else
587  return Qnil;
588 #endif
589 }
590 
591 /*
592  * call-seq:
593  * stat.blocks -> integer or nil
594  *
595  * Returns the number of native file system blocks allocated for this
596  * file, or <code>nil</code> if the operating system doesn't
597  * support this feature.
598  *
599  * File.stat("testfile").blocks #=> 2
600  */
601 
602 static VALUE
604 {
605 #ifdef HAVE_STRUCT_STAT_ST_BLOCKS
606 # if SIZEOF_STRUCT_STAT_ST_BLOCKS > SIZEOF_LONG
607  return ULL2NUM(get_stat(self)->st_blocks);
608 # else
609  return ULONG2NUM(get_stat(self)->st_blocks);
610 # endif
611 #else
612  return Qnil;
613 #endif
614 }
615 
616 static struct timespec
617 stat_atimespec(struct stat *st)
618 {
619  struct timespec ts;
620  ts.tv_sec = st->st_atime;
621 #if defined(HAVE_STRUCT_STAT_ST_ATIM)
622  ts.tv_nsec = st->st_atim.tv_nsec;
623 #elif defined(HAVE_STRUCT_STAT_ST_ATIMESPEC)
624  ts.tv_nsec = st->st_atimespec.tv_nsec;
625 #elif defined(HAVE_STRUCT_STAT_ST_ATIMENSEC)
626  ts.tv_nsec = st->st_atimensec;
627 #else
628  ts.tv_nsec = 0;
629 #endif
630  return ts;
631 }
632 
633 static VALUE
634 stat_atime(struct stat *st)
635 {
636  struct timespec ts = stat_atimespec(st);
637  return rb_time_nano_new(ts.tv_sec, ts.tv_nsec);
638 }
639 
640 static struct timespec
641 stat_mtimespec(struct stat *st)
642 {
643  struct timespec ts;
644  ts.tv_sec = st->st_mtime;
645 #if defined(HAVE_STRUCT_STAT_ST_MTIM)
646  ts.tv_nsec = st->st_mtim.tv_nsec;
647 #elif defined(HAVE_STRUCT_STAT_ST_MTIMESPEC)
648  ts.tv_nsec = st->st_mtimespec.tv_nsec;
649 #elif defined(HAVE_STRUCT_STAT_ST_MTIMENSEC)
650  ts.tv_nsec = st->st_mtimensec;
651 #else
652  ts.tv_nsec = 0;
653 #endif
654  return ts;
655 }
656 
657 static VALUE
658 stat_mtime(struct stat *st)
659 {
660  struct timespec ts = stat_mtimespec(st);
661  return rb_time_nano_new(ts.tv_sec, ts.tv_nsec);
662 }
663 
664 static struct timespec
665 stat_ctimespec(struct stat *st)
666 {
667  struct timespec ts;
668  ts.tv_sec = st->st_ctime;
669 #if defined(HAVE_STRUCT_STAT_ST_CTIM)
670  ts.tv_nsec = st->st_ctim.tv_nsec;
671 #elif defined(HAVE_STRUCT_STAT_ST_CTIMESPEC)
672  ts.tv_nsec = st->st_ctimespec.tv_nsec;
673 #elif defined(HAVE_STRUCT_STAT_ST_CTIMENSEC)
674  ts.tv_nsec = st->st_ctimensec;
675 #else
676  ts.tv_nsec = 0;
677 #endif
678  return ts;
679 }
680 
681 static VALUE
682 stat_ctime(struct stat *st)
683 {
684  struct timespec ts = stat_ctimespec(st);
685  return rb_time_nano_new(ts.tv_sec, ts.tv_nsec);
686 }
687 
688 /*
689  * call-seq:
690  * stat.atime -> time
691  *
692  * Returns the last access time for this file as an object of class
693  * <code>Time</code>.
694  *
695  * File.stat("testfile").atime #=> Wed Dec 31 18:00:00 CST 1969
696  *
697  */
698 
699 static VALUE
701 {
702  return stat_atime(get_stat(self));
703 }
704 
705 /*
706  * call-seq:
707  * stat.mtime -> aTime
708  *
709  * Returns the modification time of <i>stat</i>.
710  *
711  * File.stat("testfile").mtime #=> Wed Apr 09 08:53:14 CDT 2003
712  *
713  */
714 
715 static VALUE
717 {
718  return stat_mtime(get_stat(self));
719 }
720 
721 /*
722  * call-seq:
723  * stat.ctime -> aTime
724  *
725  * Returns the change time for <i>stat</i> (that is, the time
726  * directory information about the file was changed, not the file
727  * itself).
728  *
729  * Note that on Windows (NTFS), returns creation time (birth time).
730  *
731  * File.stat("testfile").ctime #=> Wed Apr 09 08:53:14 CDT 2003
732  *
733  */
734 
735 static VALUE
737 {
738  return stat_ctime(get_stat(self));
739 }
740 
741 /*
742  * call-seq:
743  * stat.inspect -> string
744  *
745  * Produce a nicely formatted description of <i>stat</i>.
746  *
747  * File.stat("/etc/passwd").inspect
748  * #=> "#<File::Stat dev=0xe000005, ino=1078078, mode=0100644,
749  * # nlink=1, uid=0, gid=0, rdev=0x0, size=1374, blksize=4096,
750  * # blocks=8, atime=Wed Dec 10 10:16:12 CST 2003,
751  * # mtime=Fri Sep 12 15:41:41 CDT 2003,
752  * # ctime=Mon Oct 27 11:20:27 CST 2003>"
753  */
754 
755 static VALUE
757 {
758  VALUE str;
759  size_t i;
760  static const struct {
761  const char *name;
762  VALUE (*func)(VALUE);
763  } member[] = {
764  {"dev", rb_stat_dev},
765  {"ino", rb_stat_ino},
766  {"mode", rb_stat_mode},
767  {"nlink", rb_stat_nlink},
768  {"uid", rb_stat_uid},
769  {"gid", rb_stat_gid},
770  {"rdev", rb_stat_rdev},
771  {"size", rb_stat_size},
772  {"blksize", rb_stat_blksize},
773  {"blocks", rb_stat_blocks},
774  {"atime", rb_stat_atime},
775  {"mtime", rb_stat_mtime},
776  {"ctime", rb_stat_ctime},
777  };
778 
779  struct stat* st;
780  TypedData_Get_Struct(self, struct stat, &stat_data_type, st);
781  if (!st) {
782  return rb_sprintf("#<%s: uninitialized>", rb_obj_classname(self));
783  }
784 
785  str = rb_str_buf_new2("#<");
786  rb_str_buf_cat2(str, rb_obj_classname(self));
787  rb_str_buf_cat2(str, " ");
788 
789  for (i = 0; i < sizeof(member)/sizeof(member[0]); i++) {
790  VALUE v;
791 
792  if (i > 0) {
793  rb_str_buf_cat2(str, ", ");
794  }
795  rb_str_buf_cat2(str, member[i].name);
796  rb_str_buf_cat2(str, "=");
797  v = (*member[i].func)(self);
798  if (i == 2) { /* mode */
799  rb_str_catf(str, "0%lo", (unsigned long)NUM2ULONG(v));
800  }
801  else if (i == 0 || i == 6) { /* dev/rdev */
802  rb_str_catf(str, "0x%"PRI_DEVT_PREFIX"x", NUM2DEVT(v));
803  }
804  else {
805  rb_str_append(str, rb_inspect(v));
806  }
807  }
808  rb_str_buf_cat2(str, ">");
809  OBJ_INFECT(str, self);
810 
811  return str;
812 }
813 
814 static int
815 rb_stat(VALUE file, struct stat *st)
816 {
817  VALUE tmp;
818 
819  rb_secure(2);
820  tmp = rb_check_convert_type(file, T_FILE, "IO", "to_io");
821  if (!NIL_P(tmp)) {
822  rb_io_t *fptr;
823 
824  GetOpenFile(tmp, fptr);
825  return fstat(fptr->fd, st);
826  }
827  FilePathValue(file);
828  file = rb_str_encode_ospath(file);
829  return STAT(StringValueCStr(file), st);
830 }
831 
832 #ifdef _WIN32
833 static HANDLE
834 w32_io_info(VALUE *file, BY_HANDLE_FILE_INFORMATION *st)
835 {
836  VALUE tmp;
837  HANDLE f, ret = 0;
838 
839  tmp = rb_check_convert_type(*file, T_FILE, "IO", "to_io");
840  if (!NIL_P(tmp)) {
841  rb_io_t *fptr;
842 
843  GetOpenFile(tmp, fptr);
844  f = (HANDLE)rb_w32_get_osfhandle(fptr->fd);
845  if (f == (HANDLE)-1) return INVALID_HANDLE_VALUE;
846  }
847  else {
848  VALUE tmp;
849  WCHAR *ptr;
850  int len;
851  VALUE v;
852 
853  FilePathValue(*file);
854  tmp = rb_str_encode_ospath(*file);
855  len = MultiByteToWideChar(CP_UTF8, 0, RSTRING_PTR(tmp), -1, NULL, 0);
856  ptr = ALLOCV_N(WCHAR, v, len);
857  MultiByteToWideChar(CP_UTF8, 0, RSTRING_PTR(tmp), -1, ptr, len);
858  f = CreateFileW(ptr, 0,
859  FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
860  rb_w32_iswin95() ? 0 : FILE_FLAG_BACKUP_SEMANTICS,
861  NULL);
862  ALLOCV_END(v);
863  if (f == INVALID_HANDLE_VALUE) return f;
864  ret = f;
865  }
866  if (GetFileType(f) == FILE_TYPE_DISK) {
867  ZeroMemory(st, sizeof(*st));
868  if (GetFileInformationByHandle(f, st)) return ret;
869  }
870  if (ret) CloseHandle(ret);
871  return INVALID_HANDLE_VALUE;
872 }
873 #endif
874 
875 /*
876  * call-seq:
877  * File.stat(file_name) -> stat
878  *
879  * Returns a <code>File::Stat</code> object for the named file (see
880  * <code>File::Stat</code>).
881  *
882  * File.stat("testfile").mtime #=> Tue Apr 08 12:58:04 CDT 2003
883  *
884  */
885 
886 static VALUE
888 {
889  struct stat st;
890 
891  rb_secure(4);
892  FilePathValue(fname);
893  if (rb_stat(fname, &st) < 0) {
894  rb_sys_fail_path(fname);
895  }
896  return stat_new(&st);
897 }
898 
899 /*
900  * call-seq:
901  * ios.stat -> stat
902  *
903  * Returns status information for <em>ios</em> as an object of type
904  * <code>File::Stat</code>.
905  *
906  * f = File.new("testfile")
907  * s = f.stat
908  * "%o" % s.mode #=> "100644"
909  * s.blksize #=> 4096
910  * s.atime #=> Wed Apr 09 08:53:54 CDT 2003
911  *
912  */
913 
914 static VALUE
916 {
917  rb_io_t *fptr;
918  struct stat st;
919 
920  GetOpenFile(obj, fptr);
921  if (fstat(fptr->fd, &st) == -1) {
922  rb_sys_fail_path(fptr->pathv);
923  }
924  return stat_new(&st);
925 }
926 
927 /*
928  * call-seq:
929  * File.lstat(file_name) -> stat
930  *
931  * Same as <code>File::stat</code>, but does not follow the last symbolic
932  * link. Instead, reports on the link itself.
933  *
934  * File.symlink("testfile", "link2test") #=> 0
935  * File.stat("testfile").size #=> 66
936  * File.lstat("link2test").size #=> 8
937  * File.stat("link2test").size #=> 66
938  *
939  */
940 
941 static VALUE
943 {
944 #ifdef HAVE_LSTAT
945  struct stat st;
946 
947  rb_secure(2);
948  FilePathValue(fname);
949  fname = rb_str_encode_ospath(fname);
950  if (lstat(StringValueCStr(fname), &st) == -1) {
951  rb_sys_fail_path(fname);
952  }
953  return stat_new(&st);
954 #else
955  return rb_file_s_stat(klass, fname);
956 #endif
957 }
958 
959 /*
960  * call-seq:
961  * file.lstat -> stat
962  *
963  * Same as <code>IO#stat</code>, but does not follow the last symbolic
964  * link. Instead, reports on the link itself.
965  *
966  * File.symlink("testfile", "link2test") #=> 0
967  * File.stat("testfile").size #=> 66
968  * f = File.new("link2test")
969  * f.lstat.size #=> 8
970  * f.stat.size #=> 66
971  */
972 
973 static VALUE
975 {
976 #ifdef HAVE_LSTAT
977  rb_io_t *fptr;
978  struct stat st;
979  VALUE path;
980 
981  rb_secure(2);
982  GetOpenFile(obj, fptr);
983  if (NIL_P(fptr->pathv)) return Qnil;
984  path = rb_str_encode_ospath(fptr->pathv);
985  if (lstat(RSTRING_PTR(path), &st) == -1) {
986  rb_sys_fail_path(fptr->pathv);
987  }
988  return stat_new(&st);
989 #else
990  return rb_io_stat(obj);
991 #endif
992 }
993 
994 static int
995 rb_group_member(GETGROUPS_T gid)
996 {
997  int rv = FALSE;
998 #ifndef _WIN32
999  if (getgid() == gid || getegid() == gid)
1000  return TRUE;
1001 
1002 # ifdef HAVE_GETGROUPS
1003 # ifndef NGROUPS
1004 # ifdef NGROUPS_MAX
1005 # define NGROUPS NGROUPS_MAX
1006 # else
1007 # define NGROUPS 32
1008 # endif
1009 # endif
1010  {
1011  GETGROUPS_T *gary;
1012  int anum;
1013 
1014  gary = xmalloc(NGROUPS * sizeof(GETGROUPS_T));
1015  anum = getgroups(NGROUPS, gary);
1016  while (--anum >= 0) {
1017  if (gary[anum] == gid) {
1018  rv = TRUE;
1019  break;
1020  }
1021  }
1022  xfree(gary);
1023  }
1024 # endif
1025 #endif
1026  return rv;
1027 }
1028 
1029 #ifndef S_IXUGO
1030 # define S_IXUGO (S_IXUSR | S_IXGRP | S_IXOTH)
1031 #endif
1032 
1033 #if defined(S_IXGRP) && !defined(_WIN32) && !defined(__CYGWIN__)
1034 #define USE_GETEUID 1
1035 #endif
1036 
1037 #ifndef HAVE_EACCESS
1038 int
1039 eaccess(const char *path, int mode)
1040 {
1041 #ifdef USE_GETEUID
1042  struct stat st;
1043  rb_uid_t euid;
1044 
1045  if (STAT(path, &st) < 0)
1046  return -1;
1047 
1048  euid = geteuid();
1049 
1050  if (euid == 0) {
1051  /* Root can read or write any file. */
1052  if (!(mode & X_OK))
1053  return 0;
1054 
1055  /* Root can execute any file that has any one of the execute
1056  bits set. */
1057  if (st.st_mode & S_IXUGO)
1058  return 0;
1059 
1060  return -1;
1061  }
1062 
1063  if (st.st_uid == euid) /* owner */
1064  mode <<= 6;
1065  else if (rb_group_member(st.st_gid))
1066  mode <<= 3;
1067 
1068  if ((int)(st.st_mode & mode) == mode) return 0;
1069 
1070  return -1;
1071 #else
1072  return access(path, mode);
1073 #endif
1074 }
1075 #endif
1076 
1077 static inline int
1078 access_internal(const char *path, int mode)
1079 {
1080  return access(path, mode);
1081 }
1082 
1083 
1084 /*
1085  * Document-class: FileTest
1086  *
1087  * <code>FileTest</code> implements file test operations similar to
1088  * those used in <code>File::Stat</code>. It exists as a standalone
1089  * module, and its methods are also insinuated into the <code>File</code>
1090  * class. (Note that this is not done by inclusion: the interpreter cheats).
1091  *
1092  */
1093 
1094 /*
1095  * Document-method: exist?
1096  *
1097  * call-seq:
1098  * Dir.exist?(file_name) -> true or false
1099  * Dir.exists?(file_name) -> true or false
1100  *
1101  * Returns <code>true</code> if the named file is a directory,
1102  * <code>false</code> otherwise.
1103  *
1104  */
1105 
1106 /*
1107  * Document-method: directory?
1108  *
1109  * call-seq:
1110  * File.directory?(file_name) -> true or false
1111  *
1112  * Returns <code>true</code> if the named file is a directory,
1113  * or a symlink that points at a directory, and <code>false</code>
1114  * otherwise.
1115  *
1116  * File.directory?(".")
1117  */
1118 
1119 VALUE
1121 {
1122 #ifndef S_ISDIR
1123 # define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
1124 #endif
1125 
1126  struct stat st;
1127 
1128  if (rb_stat(fname, &st) < 0) return Qfalse;
1129  if (S_ISDIR(st.st_mode)) return Qtrue;
1130  return Qfalse;
1131 }
1132 
1133 /*
1134  * call-seq:
1135  * File.pipe?(file_name) -> true or false
1136  *
1137  * Returns <code>true</code> if the named file is a pipe.
1138  */
1139 
1140 static VALUE
1142 {
1143 #ifdef S_IFIFO
1144 # ifndef S_ISFIFO
1145 # define S_ISFIFO(m) (((m) & S_IFMT) == S_IFIFO)
1146 # endif
1147 
1148  struct stat st;
1149 
1150  if (rb_stat(fname, &st) < 0) return Qfalse;
1151  if (S_ISFIFO(st.st_mode)) return Qtrue;
1152 
1153 #endif
1154  return Qfalse;
1155 }
1156 
1157 /*
1158  * call-seq:
1159  * File.symlink?(file_name) -> true or false
1160  *
1161  * Returns <code>true</code> if the named file is a symbolic link.
1162  */
1163 
1164 static VALUE
1166 {
1167 #ifndef S_ISLNK
1168 # ifdef _S_ISLNK
1169 # define S_ISLNK(m) _S_ISLNK(m)
1170 # else
1171 # ifdef _S_IFLNK
1172 # define S_ISLNK(m) (((m) & S_IFMT) == _S_IFLNK)
1173 # else
1174 # ifdef S_IFLNK
1175 # define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK)
1176 # endif
1177 # endif
1178 # endif
1179 #endif
1180 
1181 #ifdef S_ISLNK
1182  struct stat st;
1183 
1184  rb_secure(2);
1185  FilePathValue(fname);
1186  fname = rb_str_encode_ospath(fname);
1187  if (lstat(StringValueCStr(fname), &st) < 0) return Qfalse;
1188  if (S_ISLNK(st.st_mode)) return Qtrue;
1189 #endif
1190 
1191  return Qfalse;
1192 }
1193 
1194 /*
1195  * call-seq:
1196  * File.socket?(file_name) -> true or false
1197  *
1198  * Returns <code>true</code> if the named file is a socket.
1199  */
1200 
1201 static VALUE
1203 {
1204 #ifndef S_ISSOCK
1205 # ifdef _S_ISSOCK
1206 # define S_ISSOCK(m) _S_ISSOCK(m)
1207 # else
1208 # ifdef _S_IFSOCK
1209 # define S_ISSOCK(m) (((m) & S_IFMT) == _S_IFSOCK)
1210 # else
1211 # ifdef S_IFSOCK
1212 # define S_ISSOCK(m) (((m) & S_IFMT) == S_IFSOCK)
1213 # endif
1214 # endif
1215 # endif
1216 #endif
1217 
1218 #ifdef S_ISSOCK
1219  struct stat st;
1220 
1221  if (rb_stat(fname, &st) < 0) return Qfalse;
1222  if (S_ISSOCK(st.st_mode)) return Qtrue;
1223 
1224 #endif
1225  return Qfalse;
1226 }
1227 
1228 /*
1229  * call-seq:
1230  * File.blockdev?(file_name) -> true or false
1231  *
1232  * Returns <code>true</code> if the named file is a block device.
1233  */
1234 
1235 static VALUE
1237 {
1238 #ifndef S_ISBLK
1239 # ifdef S_IFBLK
1240 # define S_ISBLK(m) (((m) & S_IFMT) == S_IFBLK)
1241 # else
1242 # define S_ISBLK(m) (0) /* anytime false */
1243 # endif
1244 #endif
1245 
1246 #ifdef S_ISBLK
1247  struct stat st;
1248 
1249  if (rb_stat(fname, &st) < 0) return Qfalse;
1250  if (S_ISBLK(st.st_mode)) return Qtrue;
1251 
1252 #endif
1253  return Qfalse;
1254 }
1255 
1256 /*
1257  * call-seq:
1258  * File.chardev?(file_name) -> true or false
1259  *
1260  * Returns <code>true</code> if the named file is a character device.
1261  */
1262 static VALUE
1264 {
1265 #ifndef S_ISCHR
1266 # define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR)
1267 #endif
1268 
1269  struct stat st;
1270 
1271  if (rb_stat(fname, &st) < 0) return Qfalse;
1272  if (S_ISCHR(st.st_mode)) return Qtrue;
1273 
1274  return Qfalse;
1275 }
1276 
1277 /*
1278  * call-seq:
1279  * File.exist?(file_name) -> true or false
1280  * File.exists?(file_name) -> true or false
1281  *
1282  * Return <code>true</code> if the named file exists.
1283  */
1284 
1285 static VALUE
1287 {
1288  struct stat st;
1289 
1290  if (rb_stat(fname, &st) < 0) return Qfalse;
1291  return Qtrue;
1292 }
1293 
1294 /*
1295  * call-seq:
1296  * File.readable?(file_name) -> true or false
1297  *
1298  * Returns <code>true</code> if the named file is readable by the effective
1299  * user id of this process.
1300  */
1301 
1302 static VALUE
1304 {
1305  rb_secure(2);
1306  FilePathValue(fname);
1307  fname = rb_str_encode_ospath(fname);
1308  if (eaccess(StringValueCStr(fname), R_OK) < 0) return Qfalse;
1309  return Qtrue;
1310 }
1311 
1312 /*
1313  * call-seq:
1314  * File.readable_real?(file_name) -> true or false
1315  *
1316  * Returns <code>true</code> if the named file is readable by the real
1317  * user id of this process.
1318  */
1319 
1320 static VALUE
1322 {
1323  rb_secure(2);
1324  FilePathValue(fname);
1325  fname = rb_str_encode_ospath(fname);
1326  if (access_internal(StringValueCStr(fname), R_OK) < 0) return Qfalse;
1327  return Qtrue;
1328 }
1329 
1330 #ifndef S_IRUGO
1331 # define S_IRUGO (S_IRUSR | S_IRGRP | S_IROTH)
1332 #endif
1333 
1334 #ifndef S_IWUGO
1335 # define S_IWUGO (S_IWUSR | S_IWGRP | S_IWOTH)
1336 #endif
1337 
1338 /*
1339  * call-seq:
1340  * File.world_readable?(file_name) -> fixnum or nil
1341  *
1342  * If <i>file_name</i> is readable by others, returns an integer
1343  * representing the file permission bits of <i>file_name</i>. Returns
1344  * <code>nil</code> otherwise. The meaning of the bits is platform
1345  * dependent; on Unix systems, see <code>stat(2)</code>.
1346  *
1347  * File.world_readable?("/etc/passwd") #=> 420
1348  * m = File.world_readable?("/etc/passwd")
1349  * sprintf("%o", m) #=> "644"
1350  */
1351 
1352 static VALUE
1354 {
1355 #ifdef S_IROTH
1356  struct stat st;
1357 
1358  if (rb_stat(fname, &st) < 0) return Qnil;
1359  if ((st.st_mode & (S_IROTH)) == S_IROTH) {
1360  return UINT2NUM(st.st_mode & (S_IRUGO|S_IWUGO|S_IXUGO));
1361  }
1362 #endif
1363  return Qnil;
1364 }
1365 
1366 /*
1367  * call-seq:
1368  * File.writable?(file_name) -> true or false
1369  *
1370  * Returns <code>true</code> if the named file is writable by the effective
1371  * user id of this process.
1372  */
1373 
1374 static VALUE
1376 {
1377  rb_secure(2);
1378  FilePathValue(fname);
1379  fname = rb_str_encode_ospath(fname);
1380  if (eaccess(StringValueCStr(fname), W_OK) < 0) return Qfalse;
1381  return Qtrue;
1382 }
1383 
1384 /*
1385  * call-seq:
1386  * File.writable_real?(file_name) -> true or false
1387  *
1388  * Returns <code>true</code> if the named file is writable by the real
1389  * user id of this process.
1390  */
1391 
1392 static VALUE
1394 {
1395  rb_secure(2);
1396  FilePathValue(fname);
1397  fname = rb_str_encode_ospath(fname);
1398  if (access_internal(StringValueCStr(fname), W_OK) < 0) return Qfalse;
1399  return Qtrue;
1400 }
1401 
1402 /*
1403  * call-seq:
1404  * File.world_writable?(file_name) -> fixnum or nil
1405  *
1406  * If <i>file_name</i> is writable by others, returns an integer
1407  * representing the file permission bits of <i>file_name</i>. Returns
1408  * <code>nil</code> otherwise. The meaning of the bits is platform
1409  * dependent; on Unix systems, see <code>stat(2)</code>.
1410  *
1411  * File.world_writable?("/tmp") #=> 511
1412  * m = File.world_writable?("/tmp")
1413  * sprintf("%o", m) #=> "777"
1414  */
1415 
1416 static VALUE
1418 {
1419 #ifdef S_IWOTH
1420  struct stat st;
1421 
1422  if (rb_stat(fname, &st) < 0) return Qnil;
1423  if ((st.st_mode & (S_IWOTH)) == S_IWOTH) {
1424  return UINT2NUM(st.st_mode & (S_IRUGO|S_IWUGO|S_IXUGO));
1425  }
1426 #endif
1427  return Qnil;
1428 }
1429 
1430 /*
1431  * call-seq:
1432  * File.executable?(file_name) -> true or false
1433  *
1434  * Returns <code>true</code> if the named file is executable by the effective
1435  * user id of this process.
1436  */
1437 
1438 static VALUE
1440 {
1441  rb_secure(2);
1442  FilePathValue(fname);
1443  fname = rb_str_encode_ospath(fname);
1444  if (eaccess(StringValueCStr(fname), X_OK) < 0) return Qfalse;
1445  return Qtrue;
1446 }
1447 
1448 /*
1449  * call-seq:
1450  * File.executable_real?(file_name) -> true or false
1451  *
1452  * Returns <code>true</code> if the named file is executable by the real
1453  * user id of this process.
1454  */
1455 
1456 static VALUE
1458 {
1459  rb_secure(2);
1460  FilePathValue(fname);
1461  fname = rb_str_encode_ospath(fname);
1462  if (access_internal(StringValueCStr(fname), X_OK) < 0) return Qfalse;
1463  return Qtrue;
1464 }
1465 
1466 #ifndef S_ISREG
1467 # define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
1468 #endif
1469 
1470 /*
1471  * call-seq:
1472  * File.file?(file_name) -> true or false
1473  *
1474  * Returns <code>true</code> if the named file exists and is a
1475  * regular file.
1476  */
1477 
1478 static VALUE
1480 {
1481  struct stat st;
1482 
1483  if (rb_stat(fname, &st) < 0) return Qfalse;
1484  if (S_ISREG(st.st_mode)) return Qtrue;
1485  return Qfalse;
1486 }
1487 
1488 /*
1489  * call-seq:
1490  * File.zero?(file_name) -> true or false
1491  *
1492  * Returns <code>true</code> if the named file exists and has
1493  * a zero size.
1494  */
1495 
1496 static VALUE
1498 {
1499  struct stat st;
1500 
1501  if (rb_stat(fname, &st) < 0) return Qfalse;
1502  if (st.st_size == 0) return Qtrue;
1503  return Qfalse;
1504 }
1505 
1506 /*
1507  * call-seq:
1508  * File.size?(file_name) -> Integer or nil
1509  *
1510  * Returns +nil+ if +file_name+ doesn't exist or has zero size, the size of the
1511  * file otherwise.
1512  */
1513 
1514 static VALUE
1516 {
1517  struct stat st;
1518 
1519  if (rb_stat(fname, &st) < 0) return Qnil;
1520  if (st.st_size == 0) return Qnil;
1521  return OFFT2NUM(st.st_size);
1522 }
1523 
1524 /*
1525  * call-seq:
1526  * File.owned?(file_name) -> true or false
1527  *
1528  * Returns <code>true</code> if the named file exists and the
1529  * effective used id of the calling process is the owner of
1530  * the file.
1531  */
1532 
1533 static VALUE
1535 {
1536  struct stat st;
1537 
1538  if (rb_stat(fname, &st) < 0) return Qfalse;
1539  if (st.st_uid == geteuid()) return Qtrue;
1540  return Qfalse;
1541 }
1542 
1543 static VALUE
1545 {
1546  struct stat st;
1547 
1548  if (rb_stat(fname, &st) < 0) return Qfalse;
1549  if (st.st_uid == getuid()) return Qtrue;
1550  return Qfalse;
1551 }
1552 
1553 /*
1554  * call-seq:
1555  * File.grpowned?(file_name) -> true or false
1556  *
1557  * Returns <code>true</code> if the named file exists and the
1558  * effective group id of the calling process is the owner of
1559  * the file. Returns <code>false</code> on Windows.
1560  */
1561 
1562 static VALUE
1564 {
1565 #ifndef _WIN32
1566  struct stat st;
1567 
1568  if (rb_stat(fname, &st) < 0) return Qfalse;
1569  if (rb_group_member(st.st_gid)) return Qtrue;
1570 #endif
1571  return Qfalse;
1572 }
1573 
1574 #if defined(S_ISUID) || defined(S_ISGID) || defined(S_ISVTX)
1575 static VALUE
1576 check3rdbyte(VALUE fname, int mode)
1577 {
1578  struct stat st;
1579 
1580  rb_secure(2);
1581  FilePathValue(fname);
1582  fname = rb_str_encode_ospath(fname);
1583  if (STAT(StringValueCStr(fname), &st) < 0) return Qfalse;
1584  if (st.st_mode & mode) return Qtrue;
1585  return Qfalse;
1586 }
1587 #endif
1588 
1589 /*
1590  * call-seq:
1591  * File.setuid?(file_name) -> true or false
1592  *
1593  * Returns <code>true</code> if the named file has the setuid bit set.
1594  */
1595 
1596 static VALUE
1598 {
1599 #ifdef S_ISUID
1600  return check3rdbyte(fname, S_ISUID);
1601 #else
1602  return Qfalse;
1603 #endif
1604 }
1605 
1606 /*
1607  * call-seq:
1608  * File.setgid?(file_name) -> true or false
1609  *
1610  * Returns <code>true</code> if the named file has the setgid bit set.
1611  */
1612 
1613 static VALUE
1615 {
1616 #ifdef S_ISGID
1617  return check3rdbyte(fname, S_ISGID);
1618 #else
1619  return Qfalse;
1620 #endif
1621 }
1622 
1623 /*
1624  * call-seq:
1625  * File.sticky?(file_name) -> true or false
1626  *
1627  * Returns <code>true</code> if the named file has the sticky bit set.
1628  */
1629 
1630 static VALUE
1632 {
1633 #ifdef S_ISVTX
1634  return check3rdbyte(fname, S_ISVTX);
1635 #else
1636  return Qnil;
1637 #endif
1638 }
1639 
1640 /*
1641  * call-seq:
1642  * File.identical?(file_1, file_2) -> true or false
1643  *
1644  * Returns <code>true</code> if the named files are identical.
1645  *
1646  * open("a", "w") {}
1647  * p File.identical?("a", "a") #=> true
1648  * p File.identical?("a", "./a") #=> true
1649  * File.link("a", "b")
1650  * p File.identical?("a", "b") #=> true
1651  * File.symlink("a", "c")
1652  * p File.identical?("a", "c") #=> true
1653  * open("d", "w") {}
1654  * p File.identical?("a", "d") #=> false
1655  */
1656 
1657 static VALUE
1658 rb_file_identical_p(VALUE obj, VALUE fname1, VALUE fname2)
1659 {
1660 #ifndef DOSISH
1661  struct stat st1, st2;
1662 
1663  if (rb_stat(fname1, &st1) < 0) return Qfalse;
1664  if (rb_stat(fname2, &st2) < 0) return Qfalse;
1665  if (st1.st_dev != st2.st_dev) return Qfalse;
1666  if (st1.st_ino != st2.st_ino) return Qfalse;
1667 #else
1668 # ifdef _WIN32
1669  BY_HANDLE_FILE_INFORMATION st1, st2;
1670  HANDLE f1 = 0, f2 = 0;
1671 # endif
1672 
1673  rb_secure(2);
1674 # ifdef _WIN32
1675  f1 = w32_io_info(&fname1, &st1);
1676  if (f1 == INVALID_HANDLE_VALUE) return Qfalse;
1677  f2 = w32_io_info(&fname2, &st2);
1678  if (f1) CloseHandle(f1);
1679  if (f2 == INVALID_HANDLE_VALUE) return Qfalse;
1680  if (f2) CloseHandle(f2);
1681 
1682  if (st1.dwVolumeSerialNumber == st2.dwVolumeSerialNumber &&
1683  st1.nFileIndexHigh == st2.nFileIndexHigh &&
1684  st1.nFileIndexLow == st2.nFileIndexLow)
1685  return Qtrue;
1686  if (!f1 || !f2) return Qfalse;
1687  if (rb_w32_iswin95()) return Qfalse;
1688 # else
1689  FilePathValue(fname1);
1690  fname1 = rb_str_new4(fname1);
1691  fname1 = rb_str_encode_ospath(fname1);
1692  FilePathValue(fname2);
1693  fname2 = rb_str_encode_ospath(fname2);
1694  if (access(RSTRING_PTR(fname1), 0)) return Qfalse;
1695  if (access(RSTRING_PTR(fname2), 0)) return Qfalse;
1696 # endif
1697  fname1 = rb_file_expand_path(fname1, Qnil);
1698  fname2 = rb_file_expand_path(fname2, Qnil);
1699  if (RSTRING_LEN(fname1) != RSTRING_LEN(fname2)) return Qfalse;
1700  if (rb_memcicmp(RSTRING_PTR(fname1), RSTRING_PTR(fname2), RSTRING_LEN(fname1)))
1701  return Qfalse;
1702 #endif
1703  return Qtrue;
1704 }
1705 
1706 /*
1707  * call-seq:
1708  * File.size(file_name) -> integer
1709  *
1710  * Returns the size of <code>file_name</code>.
1711  */
1712 
1713 static VALUE
1715 {
1716  struct stat st;
1717 
1718  if (rb_stat(fname, &st) < 0) {
1719  FilePathValue(fname);
1720  rb_sys_fail_path(fname);
1721  }
1722  return OFFT2NUM(st.st_size);
1723 }
1724 
1725 static VALUE
1726 rb_file_ftype(const struct stat *st)
1727 {
1728  const char *t;
1729 
1730  if (S_ISREG(st->st_mode)) {
1731  t = "file";
1732  }
1733  else if (S_ISDIR(st->st_mode)) {
1734  t = "directory";
1735  }
1736  else if (S_ISCHR(st->st_mode)) {
1737  t = "characterSpecial";
1738  }
1739 #ifdef S_ISBLK
1740  else if (S_ISBLK(st->st_mode)) {
1741  t = "blockSpecial";
1742  }
1743 #endif
1744 #ifdef S_ISFIFO
1745  else if (S_ISFIFO(st->st_mode)) {
1746  t = "fifo";
1747  }
1748 #endif
1749 #ifdef S_ISLNK
1750  else if (S_ISLNK(st->st_mode)) {
1751  t = "link";
1752  }
1753 #endif
1754 #ifdef S_ISSOCK
1755  else if (S_ISSOCK(st->st_mode)) {
1756  t = "socket";
1757  }
1758 #endif
1759  else {
1760  t = "unknown";
1761  }
1762 
1763  return rb_usascii_str_new2(t);
1764 }
1765 
1766 /*
1767  * call-seq:
1768  * File.ftype(file_name) -> string
1769  *
1770  * Identifies the type of the named file; the return string is one of
1771  * ``<code>file</code>'', ``<code>directory</code>'',
1772  * ``<code>characterSpecial</code>'', ``<code>blockSpecial</code>'',
1773  * ``<code>fifo</code>'', ``<code>link</code>'',
1774  * ``<code>socket</code>'', or ``<code>unknown</code>''.
1775  *
1776  * File.ftype("testfile") #=> "file"
1777  * File.ftype("/dev/tty") #=> "characterSpecial"
1778  * File.ftype("/tmp/.X11-unix/X0") #=> "socket"
1779  */
1780 
1781 static VALUE
1783 {
1784  struct stat st;
1785 
1786  rb_secure(2);
1787  FilePathValue(fname);
1788  fname = rb_str_encode_ospath(fname);
1789  if (lstat(StringValueCStr(fname), &st) == -1) {
1790  rb_sys_fail_path(fname);
1791  }
1792 
1793  return rb_file_ftype(&st);
1794 }
1795 
1796 /*
1797  * call-seq:
1798  * File.atime(file_name) -> time
1799  *
1800  * Returns the last access time for the named file as a Time object).
1801  *
1802  * File.atime("testfile") #=> Wed Apr 09 08:51:48 CDT 2003
1803  *
1804  */
1805 
1806 static VALUE
1808 {
1809  struct stat st;
1810 
1811  if (rb_stat(fname, &st) < 0) {
1812  FilePathValue(fname);
1813  rb_sys_fail_path(fname);
1814  }
1815  return stat_atime(&st);
1816 }
1817 
1818 /*
1819  * call-seq:
1820  * file.atime -> time
1821  *
1822  * Returns the last access time (a <code>Time</code> object)
1823  * for <i>file</i>, or epoch if <i>file</i> has not been accessed.
1824  *
1825  * File.new("testfile").atime #=> Wed Dec 31 18:00:00 CST 1969
1826  *
1827  */
1828 
1829 static VALUE
1831 {
1832  rb_io_t *fptr;
1833  struct stat st;
1834 
1835  GetOpenFile(obj, fptr);
1836  if (fstat(fptr->fd, &st) == -1) {
1837  rb_sys_fail_path(fptr->pathv);
1838  }
1839  return stat_atime(&st);
1840 }
1841 
1842 /*
1843  * call-seq:
1844  * File.mtime(file_name) -> time
1845  *
1846  * Returns the modification time for the named file as a Time object.
1847  *
1848  * File.mtime("testfile") #=> Tue Apr 08 12:58:04 CDT 2003
1849  *
1850  */
1851 
1852 static VALUE
1854 {
1855  struct stat st;
1856 
1857  if (rb_stat(fname, &st) < 0) {
1858  FilePathValue(fname);
1859  rb_sys_fail_path(fname);
1860  }
1861  return stat_mtime(&st);
1862 }
1863 
1864 /*
1865  * call-seq:
1866  * file.mtime -> time
1867  *
1868  * Returns the modification time for <i>file</i>.
1869  *
1870  * File.new("testfile").mtime #=> Wed Apr 09 08:53:14 CDT 2003
1871  *
1872  */
1873 
1874 static VALUE
1876 {
1877  rb_io_t *fptr;
1878  struct stat st;
1879 
1880  GetOpenFile(obj, fptr);
1881  if (fstat(fptr->fd, &st) == -1) {
1882  rb_sys_fail_path(fptr->pathv);
1883  }
1884  return stat_mtime(&st);
1885 }
1886 
1887 /*
1888  * call-seq:
1889  * File.ctime(file_name) -> time
1890  *
1891  * Returns the change time for the named file (the time at which
1892  * directory information about the file was changed, not the file
1893  * itself).
1894  *
1895  * Note that on Windows (NTFS), returns creation time (birth time).
1896  *
1897  * File.ctime("testfile") #=> Wed Apr 09 08:53:13 CDT 2003
1898  *
1899  */
1900 
1901 static VALUE
1903 {
1904  struct stat st;
1905 
1906  if (rb_stat(fname, &st) < 0) {
1907  FilePathValue(fname);
1908  rb_sys_fail_path(fname);
1909  }
1910  return stat_ctime(&st);
1911 }
1912 
1913 /*
1914  * call-seq:
1915  * file.ctime -> time
1916  *
1917  * Returns the change time for <i>file</i> (that is, the time directory
1918  * information about the file was changed, not the file itself).
1919  *
1920  * Note that on Windows (NTFS), returns creation time (birth time).
1921  *
1922  * File.new("testfile").ctime #=> Wed Apr 09 08:53:14 CDT 2003
1923  *
1924  */
1925 
1926 static VALUE
1928 {
1929  rb_io_t *fptr;
1930  struct stat st;
1931 
1932  GetOpenFile(obj, fptr);
1933  if (fstat(fptr->fd, &st) == -1) {
1934  rb_sys_fail_path(fptr->pathv);
1935  }
1936  return stat_ctime(&st);
1937 }
1938 
1939 /*
1940  * call-seq:
1941  * file.size -> integer
1942  *
1943  * Returns the size of <i>file</i> in bytes.
1944  *
1945  * File.new("testfile").size #=> 66
1946  *
1947  */
1948 
1949 static VALUE
1951 {
1952  rb_io_t *fptr;
1953  struct stat st;
1954 
1955  GetOpenFile(obj, fptr);
1956  if (fptr->mode & FMODE_WRITABLE) {
1957  rb_io_flush(obj);
1958  }
1959  if (fstat(fptr->fd, &st) == -1) {
1960  rb_sys_fail_path(fptr->pathv);
1961  }
1962  return OFFT2NUM(st.st_size);
1963 }
1964 
1965 static void
1966 chmod_internal(const char *path, VALUE pathv, void *mode)
1967 {
1968  if (chmod(path, *(int *)mode) < 0)
1969  rb_sys_fail_path(pathv);
1970 }
1971 
1972 /*
1973  * call-seq:
1974  * File.chmod(mode_int, file_name, ... ) -> integer
1975  *
1976  * Changes permission bits on the named file(s) to the bit pattern
1977  * represented by <i>mode_int</i>. Actual effects are operating system
1978  * dependent (see the beginning of this section). On Unix systems, see
1979  * <code>chmod(2)</code> for details. Returns the number of files
1980  * processed.
1981  *
1982  * File.chmod(0644, "testfile", "out") #=> 2
1983  */
1984 
1985 static VALUE
1987 {
1988  VALUE vmode;
1989  VALUE rest;
1990  int mode;
1991  long n;
1992 
1993  rb_secure(2);
1994  rb_scan_args(argc, argv, "1*", &vmode, &rest);
1995  mode = NUM2INT(vmode);
1996 
1997  n = apply2files(chmod_internal, rest, &mode);
1998  return LONG2FIX(n);
1999 }
2000 
2001 /*
2002  * call-seq:
2003  * file.chmod(mode_int) -> 0
2004  *
2005  * Changes permission bits on <i>file</i> to the bit pattern
2006  * represented by <i>mode_int</i>. Actual effects are platform
2007  * dependent; on Unix systems, see <code>chmod(2)</code> for details.
2008  * Follows symbolic links. Also see <code>File#lchmod</code>.
2009  *
2010  * f = File.new("out", "w");
2011  * f.chmod(0644) #=> 0
2012  */
2013 
2014 static VALUE
2016 {
2017  rb_io_t *fptr;
2018  int mode;
2019 #ifndef HAVE_FCHMOD
2020  VALUE path;
2021 #endif
2022 
2023  rb_secure(2);
2024  mode = NUM2INT(vmode);
2025 
2026  GetOpenFile(obj, fptr);
2027 #ifdef HAVE_FCHMOD
2028  if (fchmod(fptr->fd, mode) == -1)
2029  rb_sys_fail_path(fptr->pathv);
2030 #else
2031  if (NIL_P(fptr->pathv)) return Qnil;
2032  path = rb_str_encode_ospath(fptr->pathv);
2033  if (chmod(RSTRING_PTR(path), mode) == -1)
2034  rb_sys_fail_path(fptr->pathv);
2035 #endif
2036 
2037  return INT2FIX(0);
2038 }
2039 
2040 #if defined(HAVE_LCHMOD)
2041 static void
2042 lchmod_internal(const char *path, VALUE pathv, void *mode)
2043 {
2044  if (lchmod(path, (int)(VALUE)mode) < 0)
2045  rb_sys_fail_path(pathv);
2046 }
2047 
2048 /*
2049  * call-seq:
2050  * File.lchmod(mode_int, file_name, ...) -> integer
2051  *
2052  * Equivalent to <code>File::chmod</code>, but does not follow symbolic
2053  * links (so it will change the permissions associated with the link,
2054  * not the file referenced by the link). Often not available.
2055  *
2056  */
2057 
2058 static VALUE
2060 {
2061  VALUE vmode;
2062  VALUE rest;
2063  long mode, n;
2064 
2065  rb_secure(2);
2066  rb_scan_args(argc, argv, "1*", &vmode, &rest);
2067  mode = NUM2INT(vmode);
2068 
2069  n = apply2files(lchmod_internal, rest, (void *)(long)mode);
2070  return LONG2FIX(n);
2071 }
2072 #else
2073 #define rb_file_s_lchmod rb_f_notimplement
2074 #endif
2075 
2076 struct chown_args {
2077  rb_uid_t owner;
2078  rb_gid_t group;
2079 };
2080 
2081 static void
2082 chown_internal(const char *path, VALUE pathv, void *arg)
2083 {
2084  struct chown_args *args = arg;
2085  if (chown(path, args->owner, args->group) < 0)
2086  rb_sys_fail_path(pathv);
2087 }
2088 
2089 /*
2090  * call-seq:
2091  * File.chown(owner_int, group_int, file_name,... ) -> integer
2092  *
2093  * Changes the owner and group of the named file(s) to the given
2094  * numeric owner and group id's. Only a process with superuser
2095  * privileges may change the owner of a file. The current owner of a
2096  * file may change the file's group to any group to which the owner
2097  * belongs. A <code>nil</code> or -1 owner or group id is ignored.
2098  * Returns the number of files processed.
2099  *
2100  * File.chown(nil, 100, "testfile")
2101  *
2102  */
2103 
2104 static VALUE
2105 rb_file_s_chown(int argc, VALUE *argv)
2106 {
2107  VALUE o, g, rest;
2108  struct chown_args arg;
2109  long n;
2110 
2111  rb_secure(2);
2112  rb_scan_args(argc, argv, "2*", &o, &g, &rest);
2113  if (NIL_P(o)) {
2114  arg.owner = -1;
2115  }
2116  else {
2117  arg.owner = NUM2UIDT(o);
2118  }
2119  if (NIL_P(g)) {
2120  arg.group = -1;
2121  }
2122  else {
2123  arg.group = NUM2GIDT(g);
2124  }
2125 
2126  n = apply2files(chown_internal, rest, &arg);
2127  return LONG2FIX(n);
2128 }
2129 
2130 /*
2131  * call-seq:
2132  * file.chown(owner_int, group_int ) -> 0
2133  *
2134  * Changes the owner and group of <i>file</i> to the given numeric
2135  * owner and group id's. Only a process with superuser privileges may
2136  * change the owner of a file. The current owner of a file may change
2137  * the file's group to any group to which the owner belongs. A
2138  * <code>nil</code> or -1 owner or group id is ignored. Follows
2139  * symbolic links. See also <code>File#lchown</code>.
2140  *
2141  * File.new("testfile").chown(502, 1000)
2142  *
2143  */
2144 
2145 static VALUE
2146 rb_file_chown(VALUE obj, VALUE owner, VALUE group)
2147 {
2148  rb_io_t *fptr;
2149  int o, g;
2150 #ifndef HAVE_FCHOWN
2151  VALUE path;
2152 #endif
2153 
2154  rb_secure(2);
2155  o = NIL_P(owner) ? -1 : NUM2INT(owner);
2156  g = NIL_P(group) ? -1 : NUM2INT(group);
2157  GetOpenFile(obj, fptr);
2158 #ifndef HAVE_FCHOWN
2159  if (NIL_P(fptr->pathv)) return Qnil;
2160  path = rb_str_encode_ospath(fptr->pathv);
2161  if (chown(RSTRING_PTR(path), o, g) == -1)
2162  rb_sys_fail_path(fptr->pathv);
2163 #else
2164  if (fchown(fptr->fd, o, g) == -1)
2165  rb_sys_fail_path(fptr->pathv);
2166 #endif
2167 
2168  return INT2FIX(0);
2169 }
2170 
2171 #if defined(HAVE_LCHOWN)
2172 static void
2173 lchown_internal(const char *path, VALUE pathv, void *arg)
2174 {
2175  struct chown_args *args = arg;
2176  if (lchown(path, args->owner, args->group) < 0)
2177  rb_sys_fail_path(pathv);
2178 }
2179 
2180 /*
2181  * call-seq:
2182  * file.lchown(owner_int, group_int, file_name,..) -> integer
2183  *
2184  * Equivalent to <code>File::chown</code>, but does not follow symbolic
2185  * links (so it will change the owner associated with the link, not the
2186  * file referenced by the link). Often not available. Returns number
2187  * of files in the argument list.
2188  *
2189  */
2190 
2191 static VALUE
2192 rb_file_s_lchown(int argc, VALUE *argv)
2193 {
2194  VALUE o, g, rest;
2195  struct chown_args arg;
2196  long n;
2197 
2198  rb_secure(2);
2199  rb_scan_args(argc, argv, "2*", &o, &g, &rest);
2200  if (NIL_P(o)) {
2201  arg.owner = -1;
2202  }
2203  else {
2204  arg.owner = NUM2UIDT(o);
2205  }
2206  if (NIL_P(g)) {
2207  arg.group = -1;
2208  }
2209  else {
2210  arg.group = NUM2GIDT(g);
2211  }
2212 
2213  n = apply2files(lchown_internal, rest, &arg);
2214  return LONG2FIX(n);
2215 }
2216 #else
2217 #define rb_file_s_lchown rb_f_notimplement
2218 #endif
2219 
2220 struct utime_args {
2221  const struct timespec* tsp;
2223 };
2224 
2225 #if defined DOSISH || defined __CYGWIN__
2226 NORETURN(static void utime_failed(VALUE, const struct timespec *, VALUE, VALUE));
2227 
2228 static void
2229 utime_failed(VALUE path, const struct timespec *tsp, VALUE atime, VALUE mtime)
2230 {
2231  if (tsp && errno == EINVAL) {
2232  VALUE e[2], a = Qnil, m = Qnil;
2233  int d = 0;
2234  if (!NIL_P(atime)) {
2235  a = rb_inspect(atime);
2236  }
2237  if (!NIL_P(mtime) && mtime != atime && !rb_equal(atime, mtime)) {
2238  m = rb_inspect(mtime);
2239  }
2240  if (NIL_P(a)) e[0] = m;
2241  else if (NIL_P(m) || rb_str_cmp(a, m) == 0) e[0] = a;
2242  else {
2243  e[0] = rb_str_plus(a, rb_str_new_cstr(" or "));
2244  rb_str_append(e[0], m);
2245  d = 1;
2246  }
2247  if (!NIL_P(e[0])) {
2248  if (path) {
2249  if (!d) e[0] = rb_str_dup(e[0]);
2250  rb_str_append(rb_str_cat2(e[0], " for "), path);
2251  }
2252  e[1] = INT2FIX(EINVAL);
2254  }
2255  errno = EINVAL;
2256  }
2257  rb_sys_fail_path(path);
2258 }
2259 #else
2260 #define utime_failed(path, tsp, atime, mtime) rb_sys_fail_path(path)
2261 #endif
2262 
2263 #if defined(HAVE_UTIMES)
2264 
2265 static void
2266 utime_internal(const char *path, VALUE pathv, void *arg)
2267 {
2268  struct utime_args *v = arg;
2269  const struct timespec *tsp = v->tsp;
2270  struct timeval tvbuf[2], *tvp = NULL;
2271 
2272 #ifdef HAVE_UTIMENSAT
2273  static int try_utimensat = 1;
2274 
2275  if (try_utimensat) {
2276  if (utimensat(AT_FDCWD, path, tsp, 0) < 0) {
2277  if (errno == ENOSYS) {
2278  try_utimensat = 0;
2279  goto no_utimensat;
2280  }
2281  utime_failed(pathv, tsp, v->atime, v->mtime);
2282  }
2283  return;
2284  }
2285 no_utimensat:
2286 #endif
2287 
2288  if (tsp) {
2289  tvbuf[0].tv_sec = tsp[0].tv_sec;
2290  tvbuf[0].tv_usec = (int)(tsp[0].tv_nsec / 1000);
2291  tvbuf[1].tv_sec = tsp[1].tv_sec;
2292  tvbuf[1].tv_usec = (int)(tsp[1].tv_nsec / 1000);
2293  tvp = tvbuf;
2294  }
2295  if (utimes(path, tvp) < 0)
2296  utime_failed(pathv, tsp, v->atime, v->mtime);
2297 }
2298 
2299 #else
2300 
2301 #if !defined HAVE_UTIME_H && !defined HAVE_SYS_UTIME_H
2302 struct utimbuf {
2303  long actime;
2304  long modtime;
2305 };
2306 #endif
2307 
2308 static void
2309 utime_internal(const char *path, VALUE pathv, void *arg)
2310 {
2311  struct utime_args *v = arg;
2312  const struct timespec *tsp = v->tsp;
2313  struct utimbuf utbuf, *utp = NULL;
2314  if (tsp) {
2315  utbuf.actime = tsp[0].tv_sec;
2316  utbuf.modtime = tsp[1].tv_sec;
2317  utp = &utbuf;
2318  }
2319  if (utime(path, utp) < 0)
2320  utime_failed(pathv, tsp, v->atime, v->mtime);
2321 }
2322 
2323 #endif
2324 
2325 /*
2326  * call-seq:
2327  * File.utime(atime, mtime, file_name,...) -> integer
2328  *
2329  * Sets the access and modification times of each
2330  * named file to the first two arguments. Returns
2331  * the number of file names in the argument list.
2332  */
2333 
2334 static VALUE
2335 rb_file_s_utime(int argc, VALUE *argv)
2336 {
2337  VALUE rest;
2338  struct utime_args args;
2339  struct timespec tss[2], *tsp = NULL;
2340  long n;
2341 
2342  rb_secure(2);
2343  rb_scan_args(argc, argv, "2*", &args.atime, &args.mtime, &rest);
2344 
2345  if (!NIL_P(args.atime) || !NIL_P(args.mtime)) {
2346  tsp = tss;
2347  tsp[0] = rb_time_timespec(args.atime);
2348  tsp[1] = rb_time_timespec(args.mtime);
2349  }
2350  args.tsp = tsp;
2351 
2352  n = apply2files(utime_internal, rest, &args);
2353  return LONG2FIX(n);
2354 }
2355 
2356 NORETURN(static void sys_fail2(VALUE,VALUE));
2357 static void
2359 {
2360  VALUE str;
2361 #ifdef MAX_PATH
2362  const int max_pathlen = MAX_PATH;
2363 #else
2364  const int max_pathlen = MAXPATHLEN;
2365 #endif
2366 
2367  str = rb_str_new_cstr("(");
2368  rb_str_append(str, rb_str_ellipsize(s1, max_pathlen));
2369  rb_str_cat2(str, ", ");
2370  rb_str_append(str, rb_str_ellipsize(s2, max_pathlen));
2371  rb_str_cat2(str, ")");
2372  rb_sys_fail_path(str);
2373 }
2374 
2375 #ifdef HAVE_LINK
2376 /*
2377  * call-seq:
2378  * File.link(old_name, new_name) -> 0
2379  *
2380  * Creates a new name for an existing file using a hard link. Will not
2381  * overwrite <i>new_name</i> if it already exists (raising a subclass
2382  * of <code>SystemCallError</code>). Not available on all platforms.
2383  *
2384  * File.link("testfile", ".testfile") #=> 0
2385  * IO.readlines(".testfile")[0] #=> "This is line one\n"
2386  */
2387 
2388 static VALUE
2389 rb_file_s_link(VALUE klass, VALUE from, VALUE to)
2390 {
2391  rb_secure(2);
2392  FilePathValue(from);
2393  FilePathValue(to);
2394  from = rb_str_encode_ospath(from);
2395  to = rb_str_encode_ospath(to);
2396 
2397  if (link(StringValueCStr(from), StringValueCStr(to)) < 0) {
2398  sys_fail2(from, to);
2399  }
2400  return INT2FIX(0);
2401 }
2402 #else
2403 #define rb_file_s_link rb_f_notimplement
2404 #endif
2405 
2406 #ifdef HAVE_SYMLINK
2407 /*
2408  * call-seq:
2409  * File.symlink(old_name, new_name) -> 0
2410  *
2411  * Creates a symbolic link called <i>new_name</i> for the existing file
2412  * <i>old_name</i>. Raises a <code>NotImplemented</code> exception on
2413  * platforms that do not support symbolic links.
2414  *
2415  * File.symlink("testfile", "link2test") #=> 0
2416  *
2417  */
2418 
2419 static VALUE
2420 rb_file_s_symlink(VALUE klass, VALUE from, VALUE to)
2421 {
2422  rb_secure(2);
2423  FilePathValue(from);
2424  FilePathValue(to);
2425  from = rb_str_encode_ospath(from);
2426  to = rb_str_encode_ospath(to);
2427 
2428  if (symlink(StringValueCStr(from), StringValueCStr(to)) < 0) {
2429  sys_fail2(from, to);
2430  }
2431  return INT2FIX(0);
2432 }
2433 #else
2434 #define rb_file_s_symlink rb_f_notimplement
2435 #endif
2436 
2437 #ifdef HAVE_READLINK
2438 static VALUE rb_readlink(VALUE path);
2439 
2440 /*
2441  * call-seq:
2442  * File.readlink(link_name) -> file_name
2443  *
2444  * Returns the name of the file referenced by the given link.
2445  * Not available on all platforms.
2446  *
2447  * File.symlink("testfile", "link2test") #=> 0
2448  * File.readlink("link2test") #=> "testfile"
2449  */
2450 
2451 static VALUE
2452 rb_file_s_readlink(VALUE klass, VALUE path)
2453 {
2454  return rb_readlink(path);
2455 }
2456 
2457 static VALUE
2458 rb_readlink(VALUE path)
2459 {
2460  char *buf;
2461  int size = 100;
2462  ssize_t rv;
2463  VALUE v;
2464 
2465  rb_secure(2);
2466  FilePathValue(path);
2467  path = rb_str_encode_ospath(path);
2468  buf = xmalloc(size);
2469  while ((rv = readlink(RSTRING_PTR(path), buf, size)) == size
2470 #ifdef _AIX
2471  || (rv < 0 && errno == ERANGE) /* quirky behavior of GPFS */
2472 #endif
2473  ) {
2474  size *= 2;
2475  buf = xrealloc(buf, size);
2476  }
2477  if (rv < 0) {
2478  xfree(buf);
2479  rb_sys_fail_path(path);
2480  }
2481  v = rb_filesystem_str_new(buf, rv);
2482  xfree(buf);
2483 
2484  return v;
2485 }
2486 #else
2487 #define rb_file_s_readlink rb_f_notimplement
2488 #endif
2489 
2490 static void
2491 unlink_internal(const char *path, VALUE pathv, void *arg)
2492 {
2493  if (unlink(path) < 0)
2494  rb_sys_fail_path(pathv);
2495 }
2496 
2497 /*
2498  * call-seq:
2499  * File.delete(file_name, ...) -> integer
2500  * File.unlink(file_name, ...) -> integer
2501  *
2502  * Deletes the named files, returning the number of names
2503  * passed as arguments. Raises an exception on any error.
2504  * See also <code>Dir::rmdir</code>.
2505  */
2506 
2507 static VALUE
2509 {
2510  long n;
2511 
2512  rb_secure(2);
2513  n = apply2files(unlink_internal, args, 0);
2514  return LONG2FIX(n);
2515 }
2516 
2517 /*
2518  * call-seq:
2519  * File.rename(old_name, new_name) -> 0
2520  *
2521  * Renames the given file to the new name. Raises a
2522  * <code>SystemCallError</code> if the file cannot be renamed.
2523  *
2524  * File.rename("afile", "afile.bak") #=> 0
2525  */
2526 
2527 static VALUE
2529 {
2530  const char *src, *dst;
2531  VALUE f, t;
2532 
2533  rb_secure(2);
2534  FilePathValue(from);
2535  FilePathValue(to);
2536  f = rb_str_encode_ospath(from);
2537  t = rb_str_encode_ospath(to);
2538  src = StringValueCStr(f);
2539  dst = StringValueCStr(t);
2540 #if defined __CYGWIN__
2541  errno = 0;
2542 #endif
2543  if (rename(src, dst) < 0) {
2544 #if defined DOSISH
2545  switch (errno) {
2546  case EEXIST:
2547 #if defined (__EMX__)
2548  case EACCES:
2549 #endif
2550  if (chmod(dst, 0666) == 0 &&
2551  unlink(dst) == 0 &&
2552  rename(src, dst) == 0)
2553  return INT2FIX(0);
2554  }
2555 #endif
2556  sys_fail2(from, to);
2557  }
2558 
2559  return INT2FIX(0);
2560 }
2561 
2562 /*
2563  * call-seq:
2564  * File.umask() -> integer
2565  * File.umask(integer) -> integer
2566  *
2567  * Returns the current umask value for this process. If the optional
2568  * argument is given, set the umask to that value and return the
2569  * previous value. Umask values are <em>subtracted</em> from the
2570  * default permissions, so a umask of <code>0222</code> would make a
2571  * file read-only for everyone.
2572  *
2573  * File.umask(0006) #=> 18
2574  * File.umask #=> 6
2575  */
2576 
2577 static VALUE
2578 rb_file_s_umask(int argc, VALUE *argv)
2579 {
2580  int omask = 0;
2581 
2582  rb_secure(2);
2583  if (argc == 0) {
2584  omask = umask(0);
2585  umask(omask);
2586  }
2587  else if (argc == 1) {
2588  omask = umask(NUM2INT(argv[0]));
2589  }
2590  else {
2591  rb_raise(rb_eArgError, "wrong number of arguments (%d for 0..1)", argc);
2592  }
2593  return INT2FIX(omask);
2594 }
2595 
2596 #ifdef __CYGWIN__
2597 #undef DOSISH
2598 #endif
2599 #if defined __CYGWIN__ || defined DOSISH
2600 #define DOSISH_UNC
2601 #define DOSISH_DRIVE_LETTER
2602 #define FILE_ALT_SEPARATOR '\\'
2603 #endif
2604 #ifdef FILE_ALT_SEPARATOR
2605 #define isdirsep(x) ((x) == '/' || (x) == FILE_ALT_SEPARATOR)
2606 static const char file_alt_separator[] = {FILE_ALT_SEPARATOR, '\0'};
2607 #else
2608 #define isdirsep(x) ((x) == '/')
2609 #endif
2610 
2611 #ifndef USE_NTFS
2612 #if defined _WIN32 || defined __CYGWIN__
2613 #define USE_NTFS 1
2614 #else
2615 #define USE_NTFS 0
2616 #endif
2617 #endif
2618 
2619 #if USE_NTFS
2620 #define istrailinggarbage(x) ((x) == '.' || (x) == ' ')
2621 #else
2622 #define istrailinggarbage(x) 0
2623 #endif
2624 
2625 #define Next(p, e, enc) ((p) + rb_enc_mbclen((p), (e), (enc)))
2626 #define Inc(p, e, enc) ((p) = Next((p), (e), (enc)))
2627 
2628 #if defined(DOSISH_UNC)
2629 #define has_unc(buf) (isdirsep((buf)[0]) && isdirsep((buf)[1]))
2630 #else
2631 #define has_unc(buf) 0
2632 #endif
2633 
2634 #ifdef DOSISH_DRIVE_LETTER
2635 static inline int
2636 has_drive_letter(const char *buf)
2637 {
2638  if (ISALPHA(buf[0]) && buf[1] == ':') {
2639  return 1;
2640  }
2641  else {
2642  return 0;
2643  }
2644 }
2645 
2646 static char*
2647 getcwdofdrv(int drv)
2648 {
2649  char drive[4];
2650  char *drvcwd, *oldcwd;
2651 
2652  drive[0] = drv;
2653  drive[1] = ':';
2654  drive[2] = '\0';
2655 
2656  /* the only way that I know to get the current directory
2657  of a particular drive is to change chdir() to that drive,
2658  so save the old cwd before chdir()
2659  */
2660  oldcwd = my_getcwd();
2661  if (chdir(drive) == 0) {
2662  drvcwd = my_getcwd();
2663  chdir(oldcwd);
2664  xfree(oldcwd);
2665  }
2666  else {
2667  /* perhaps the drive is not exist. we return only drive letter */
2668  drvcwd = strdup(drive);
2669  }
2670  return drvcwd;
2671 }
2672 
2673 static inline int
2674 not_same_drive(VALUE path, int drive)
2675 {
2676  const char *p = RSTRING_PTR(path);
2677  if (RSTRING_LEN(path) < 2) return 0;
2678  if (has_drive_letter(p)) {
2679  return TOLOWER(p[0]) != TOLOWER(drive);
2680  }
2681  else {
2682  return has_unc(p);
2683  }
2684 }
2685 #endif
2686 
2687 static inline char *
2688 skiproot(const char *path, const char *end, rb_encoding *enc)
2689 {
2690 #ifdef DOSISH_DRIVE_LETTER
2691  if (path + 2 <= end && has_drive_letter(path)) path += 2;
2692 #endif
2693  while (path < end && isdirsep(*path)) path++;
2694  return (char *)path;
2695 }
2696 
2697 #define nextdirsep rb_enc_path_next
2698 char *
2699 rb_enc_path_next(const char *s, const char *e, rb_encoding *enc)
2700 {
2701  while (s < e && !isdirsep(*s)) {
2702  Inc(s, e, enc);
2703  }
2704  return (char *)s;
2705 }
2706 
2707 #if defined(DOSISH_UNC) || defined(DOSISH_DRIVE_LETTER)
2708 #define skipprefix rb_enc_path_skip_prefix
2709 #else
2710 #define skipprefix(path, end, enc) (path)
2711 #endif
2712 char *
2713 rb_enc_path_skip_prefix(const char *path, const char *end, rb_encoding *enc)
2714 {
2715 #if defined(DOSISH_UNC) || defined(DOSISH_DRIVE_LETTER)
2716 #ifdef DOSISH_UNC
2717  if (path + 2 <= end && isdirsep(path[0]) && isdirsep(path[1])) {
2718  path += 2;
2719  while (path < end && isdirsep(*path)) path++;
2720  if ((path = rb_enc_path_next(path, end, enc)) < end && path[0] && path[1] && !isdirsep(path[1]))
2721  path = rb_enc_path_next(path + 1, end, enc);
2722  return (char *)path;
2723  }
2724 #endif
2725 #ifdef DOSISH_DRIVE_LETTER
2726  if (has_drive_letter(path))
2727  return (char *)(path + 2);
2728 #endif
2729 #endif
2730  return (char *)path;
2731 }
2732 
2733 static inline char *
2734 skipprefixroot(const char *path, const char *end, rb_encoding *enc)
2735 {
2736 #if defined(DOSISH_UNC) || defined(DOSISH_DRIVE_LETTER)
2737  char *p = skipprefix(path, end, enc);
2738  while (isdirsep(*p)) p++;
2739  return p;
2740 #else
2741  return skiproot(path, end, enc);
2742 #endif
2743 }
2744 
2745 #define strrdirsep rb_enc_path_last_separator
2746 char *
2747 rb_enc_path_last_separator(const char *path, const char *end, rb_encoding *enc)
2748 {
2749  char *last = NULL;
2750  while (path < end) {
2751  if (isdirsep(*path)) {
2752  const char *tmp = path++;
2753  while (path < end && isdirsep(*path)) path++;
2754  if (path >= end) break;
2755  last = (char *)tmp;
2756  }
2757  else {
2758  Inc(path, end, enc);
2759  }
2760  }
2761  return last;
2762 }
2763 
2764 static char *
2765 chompdirsep(const char *path, const char *end, rb_encoding *enc)
2766 {
2767  while (path < end) {
2768  if (isdirsep(*path)) {
2769  const char *last = path++;
2770  while (path < end && isdirsep(*path)) path++;
2771  if (path >= end) return (char *)last;
2772  }
2773  else {
2774  Inc(path, end, enc);
2775  }
2776  }
2777  return (char *)path;
2778 }
2779 
2780 char *
2781 rb_enc_path_end(const char *path, const char *end, rb_encoding *enc)
2782 {
2783  if (path < end && isdirsep(*path)) path++;
2784  return chompdirsep(path, end, enc);
2785 }
2786 
2787 #if USE_NTFS
2788 static char *
2789 ntfs_tail(const char *path, const char *end, rb_encoding *enc)
2790 {
2791  while (path < end && *path == '.') path++;
2792  while (path < end && *path != ':') {
2793  if (istrailinggarbage(*path)) {
2794  const char *last = path++;
2795  while (path < end && istrailinggarbage(*path)) path++;
2796  if (path >= end || *path == ':') return (char *)last;
2797  }
2798  else if (isdirsep(*path)) {
2799  const char *last = path++;
2800  while (path < end && isdirsep(*path)) path++;
2801  if (path >= end) return (char *)last;
2802  if (*path == ':') path++;
2803  }
2804  else {
2805  Inc(path, end, enc);
2806  }
2807  }
2808  return (char *)path;
2809 }
2810 #endif
2811 
2812 #define BUFCHECK(cond) do {\
2813  bdiff = p - buf;\
2814  if (cond) {\
2815  do {buflen *= 2;} while (cond);\
2816  rb_str_resize(result, buflen);\
2817  buf = RSTRING_PTR(result);\
2818  p = buf + bdiff;\
2819  pend = buf + buflen;\
2820  }\
2821 } while (0)
2822 
2823 #define BUFINIT() (\
2824  p = buf = RSTRING_PTR(result),\
2825  buflen = RSTRING_LEN(result),\
2826  pend = p + buflen)
2827 
2828 VALUE
2829 rb_home_dir(const char *user, VALUE result)
2830 {
2831  const char *dir;
2832  char *buf;
2833 #if defined DOSISH || defined __CYGWIN__
2834  char *p, *bend;
2835 #endif
2836  long dirlen;
2837  rb_encoding *enc;
2838 
2839  if (!user || !*user) {
2840  if (!(dir = getenv("HOME"))) {
2841  rb_raise(rb_eArgError, "couldn't find HOME environment -- expanding `~'");
2842  }
2843  dirlen = strlen(dir);
2844  rb_str_resize(result, dirlen);
2845  memcpy(buf = RSTRING_PTR(result), dir, dirlen);
2846  }
2847  else {
2848 #ifdef HAVE_PWD_H
2849  struct passwd *pwPtr = getpwnam(user);
2850  if (!pwPtr) {
2851  endpwent();
2852  rb_raise(rb_eArgError, "user %s doesn't exist", user);
2853  }
2854  dirlen = strlen(pwPtr->pw_dir);
2855  rb_str_resize(result, dirlen);
2856  memcpy(buf = RSTRING_PTR(result), pwPtr->pw_dir, dirlen + 1);
2857  endpwent();
2858 #else
2859  return Qnil;
2860 #endif
2861  }
2862  enc = rb_filesystem_encoding();
2863  rb_enc_associate(result, enc);
2864 #if defined DOSISH || defined __CYGWIN__
2865  for (bend = (p = buf) + dirlen; p < bend; Inc(p, bend, enc)) {
2866  if (*p == '\\') {
2867  *p = '/';
2868  }
2869  }
2870 #endif
2871  return result;
2872 }
2873 
2874 #ifndef _WIN32
2875 static char *
2876 append_fspath(VALUE result, VALUE fname, char *dir, rb_encoding **enc, rb_encoding *fsenc)
2877 {
2878  char *buf, *cwdp = dir;
2879  VALUE dirname = Qnil;
2880  size_t dirlen = strlen(dir), buflen = rb_str_capacity(result);
2881 
2882  *enc = fsenc;
2883  do {buflen *= 2;} while (dirlen > buflen);
2884  rb_str_resize(result, buflen);
2885  buf = RSTRING_PTR(result);
2886  memcpy(buf, cwdp, dirlen);
2887  xfree(dir);
2888  if (!NIL_P(dirname)) rb_str_resize(dirname, 0);
2889  rb_enc_associate(result, *enc);
2890  return buf + dirlen;
2891 }
2892 
2893 VALUE
2894 rb_file_expand_path_internal(VALUE fname, VALUE dname, int abs_mode, int long_name, VALUE result)
2895 {
2896  const char *s, *b, *fend;
2897  char *buf, *p, *pend, *root;
2898  size_t buflen, bdiff;
2899  int tainted;
2900  rb_encoding *enc, *fsenc = rb_filesystem_encoding();
2901 
2902  s = StringValuePtr(fname);
2903  fend = s + RSTRING_LEN(fname);
2904  enc = rb_enc_get(fname);
2905  BUFINIT();
2906  tainted = OBJ_TAINTED(fname);
2907 
2908  if (s[0] == '~' && abs_mode == 0) { /* execute only if NOT absolute_path() */
2909  long userlen = 0;
2910  tainted = 1;
2911  if (isdirsep(s[1]) || s[1] == '\0') {
2912  buf = 0;
2913  b = 0;
2914  rb_str_set_len(result, 0);
2915  if (*++s) ++s;
2916  }
2917  else {
2918  s = nextdirsep(b = s, fend, enc);
2919  userlen = s - b;
2920  BUFCHECK(bdiff + userlen >= buflen);
2921  memcpy(p, b, userlen);
2922  rb_str_set_len(result, userlen);
2923  buf = p + 1;
2924  p += userlen;
2925  }
2926  if (NIL_P(rb_home_dir(buf, result))) {
2927  rb_raise(rb_eArgError, "can't find user %s", buf);
2928  }
2929  if (!rb_is_absolute_path(RSTRING_PTR(result))) {
2930  if (userlen) {
2931  rb_raise(rb_eArgError, "non-absolute home of %.*s", (int)userlen, b);
2932  }
2933  else {
2934  rb_raise(rb_eArgError, "non-absolute home");
2935  }
2936  }
2937  BUFINIT();
2938  p = pend;
2939  }
2940 #ifdef DOSISH_DRIVE_LETTER
2941  /* skip drive letter */
2942  else if (has_drive_letter(s)) {
2943  if (isdirsep(s[2])) {
2944  /* specified drive letter, and full path */
2945  /* skip drive letter */
2946  BUFCHECK(bdiff + 2 >= buflen);
2947  memcpy(p, s, 2);
2948  p += 2;
2949  s += 2;
2950  rb_enc_copy(result, fname);
2951  }
2952  else {
2953  /* specified drive, but not full path */
2954  int same = 0;
2955  if (!NIL_P(dname) && !not_same_drive(dname, s[0])) {
2956  rb_file_expand_path_internal(dname, Qnil, abs_mode, long_name, result);
2957  BUFINIT();
2958  if (has_drive_letter(p) && TOLOWER(p[0]) == TOLOWER(s[0])) {
2959  /* ok, same drive */
2960  same = 1;
2961  }
2962  }
2963  if (!same) {
2964  char *e = append_fspath(result, fname, getcwdofdrv(*s), &enc, fsenc);
2965  tainted = 1;
2966  BUFINIT();
2967  p = e;
2968  }
2969  else {
2970  rb_enc_associate(result, enc = rb_enc_check(result, fname));
2971  p = pend;
2972  }
2973  p = chompdirsep(skiproot(buf, p, enc), p, enc);
2974  s += 2;
2975  }
2976  }
2977 #endif
2978  else if (!rb_is_absolute_path(s)) {
2979  if (!NIL_P(dname)) {
2980  rb_file_expand_path_internal(dname, Qnil, abs_mode, long_name, result);
2981  rb_enc_associate(result, rb_enc_check(result, fname));
2982  BUFINIT();
2983  p = pend;
2984  }
2985  else {
2986  char *e = append_fspath(result, fname, my_getcwd(), &enc, fsenc);
2987  tainted = 1;
2988  BUFINIT();
2989  p = e;
2990  }
2991 #if defined DOSISH || defined __CYGWIN__
2992  if (isdirsep(*s)) {
2993  /* specified full path, but not drive letter nor UNC */
2994  /* we need to get the drive letter or UNC share name */
2995  p = skipprefix(buf, p, enc);
2996  }
2997  else
2998 #endif
2999  p = chompdirsep(skiproot(buf, p, enc), p, enc);
3000  }
3001  else {
3002  size_t len;
3003  b = s;
3004  do s++; while (isdirsep(*s));
3005  len = s - b;
3006  p = buf + len;
3007  BUFCHECK(bdiff >= buflen);
3008  memset(buf, '/', len);
3009  rb_str_set_len(result, len);
3010  rb_enc_associate(result, rb_enc_check(result, fname));
3011  }
3012  if (p > buf && p[-1] == '/')
3013  --p;
3014  else {
3015  rb_str_set_len(result, p-buf);
3016  BUFCHECK(bdiff + 1 >= buflen);
3017  *p = '/';
3018  }
3019 
3020  rb_str_set_len(result, p-buf+1);
3021  BUFCHECK(bdiff + 1 >= buflen);
3022  p[1] = 0;
3023  root = skipprefix(buf, p+1, enc);
3024 
3025  b = s;
3026  while (*s) {
3027  switch (*s) {
3028  case '.':
3029  if (b == s++) { /* beginning of path element */
3030  switch (*s) {
3031  case '\0':
3032  b = s;
3033  break;
3034  case '.':
3035  if (*(s+1) == '\0' || isdirsep(*(s+1))) {
3036  /* We must go back to the parent */
3037  char *n;
3038  *p = '\0';
3039  if (!(n = strrdirsep(root, p, enc))) {
3040  *p = '/';
3041  }
3042  else {
3043  p = n;
3044  }
3045  b = ++s;
3046  }
3047 #if USE_NTFS
3048  else {
3049  do ++s; while (istrailinggarbage(*s));
3050  }
3051 #endif
3052  break;
3053  case '/':
3054 #if defined DOSISH || defined __CYGWIN__
3055  case '\\':
3056 #endif
3057  b = ++s;
3058  break;
3059  default:
3060  /* ordinary path element, beginning don't move */
3061  break;
3062  }
3063  }
3064 #if USE_NTFS
3065  else {
3066  --s;
3067  case ' ': {
3068  const char *e = s;
3069  while (s < fend && istrailinggarbage(*s)) s++;
3070  if (!*s) {
3071  s = e;
3072  goto endpath;
3073  }
3074  }
3075  }
3076 #endif
3077  break;
3078  case '/':
3079 #if defined DOSISH || defined __CYGWIN__
3080  case '\\':
3081 #endif
3082  if (s > b) {
3083  long rootdiff = root - buf;
3084  rb_str_set_len(result, p-buf+1);
3085  BUFCHECK(bdiff + (s-b+1) >= buflen);
3086  root = buf + rootdiff;
3087  memcpy(++p, b, s-b);
3088  p += s-b;
3089  *p = '/';
3090  }
3091  b = ++s;
3092  break;
3093  default:
3094  Inc(s, fend, enc);
3095  break;
3096  }
3097  }
3098 
3099  if (s > b) {
3100 #if USE_NTFS
3101  static const char prime[] = ":$DATA";
3102  enum {prime_len = sizeof(prime) -1};
3103  endpath:
3104  if (s > b + prime_len && strncasecmp(s - prime_len, prime, prime_len) == 0) {
3105  /* alias of stream */
3106  /* get rid of a bug of x64 VC++ */
3107  if (*(s - (prime_len+1)) == ':') {
3108  s -= prime_len + 1; /* prime */
3109  }
3110  else if (memchr(b, ':', s - prime_len - b)) {
3111  s -= prime_len; /* alternative */
3112  }
3113  }
3114 #endif
3115  rb_str_set_len(result, p-buf+1);
3116  BUFCHECK(bdiff + (s-b) >= buflen);
3117  memcpy(++p, b, s-b);
3118  p += s-b;
3119  rb_str_set_len(result, p-buf);
3120  }
3121  if (p == skiproot(buf, p + !!*p, enc) - 1) p++;
3122 
3123 #if USE_NTFS
3124  *p = '\0';
3125  if ((s = strrdirsep(b = buf, p, enc)) != 0 && !strpbrk(s, "*?")) {
3126  VALUE tmp, v;
3127  size_t len;
3128  rb_encoding *enc;
3129  WCHAR *wstr;
3130  WIN32_FIND_DATAW wfd;
3131  HANDLE h;
3132 #ifdef __CYGWIN__
3133 #ifdef HAVE_CYGWIN_CONV_PATH
3134  char *w32buf = NULL;
3135  const int flags = CCP_POSIX_TO_WIN_A | CCP_RELATIVE;
3136 #else
3137  char w32buf[MAXPATHLEN];
3138 #endif
3139  const char *path;
3140  ssize_t bufsize;
3141  int lnk_added = 0, is_symlink = 0;
3142  struct stat st;
3143  p = (char *)s;
3144  len = strlen(p);
3145  if (lstat(buf, &st) == 0 && S_ISLNK(st.st_mode)) {
3146  is_symlink = 1;
3147  if (len > 4 && STRCASECMP(p + len - 4, ".lnk") != 0) {
3148  lnk_added = 1;
3149  }
3150  }
3151  path = *buf ? buf : "/";
3152 #ifdef HAVE_CYGWIN_CONV_PATH
3153  bufsize = cygwin_conv_path(flags, path, NULL, 0);
3154  if (bufsize > 0) {
3155  bufsize += len;
3156  if (lnk_added) bufsize += 4;
3157  w32buf = ALLOCA_N(char, bufsize);
3158  if (cygwin_conv_path(flags, path, w32buf, bufsize) == 0) {
3159  b = w32buf;
3160  }
3161  }
3162 #else
3163  bufsize = MAXPATHLEN;
3164  if (cygwin_conv_to_win32_path(path, w32buf) == 0) {
3165  b = w32buf;
3166  }
3167 #endif
3168  if (is_symlink && b == w32buf) {
3169  *p = '\\';
3170  strlcat(w32buf, p, bufsize);
3171  if (lnk_added) {
3172  strlcat(w32buf, ".lnk", bufsize);
3173  }
3174  }
3175  else {
3176  lnk_added = 0;
3177  }
3178  *p = '/';
3179 #endif
3180  rb_str_set_len(result, p - buf + strlen(p));
3181  enc = rb_enc_get(result);
3182  tmp = result;
3183  if (enc != rb_utf8_encoding() && rb_enc_str_coderange(result) != ENC_CODERANGE_7BIT) {
3184  tmp = rb_str_encode_ospath(result);
3185  }
3186  len = MultiByteToWideChar(CP_UTF8, 0, RSTRING_PTR(tmp), -1, NULL, 0);
3187  wstr = ALLOCV_N(WCHAR, v, len);
3188  MultiByteToWideChar(CP_UTF8, 0, RSTRING_PTR(tmp), -1, wstr, len);
3189  if (tmp != result) rb_str_resize(tmp, 0);
3190  h = FindFirstFileW(wstr, &wfd);
3191  ALLOCV_END(v);
3192  if (h != INVALID_HANDLE_VALUE) {
3193  size_t wlen;
3194  FindClose(h);
3195  len = lstrlenW(wfd.cFileName);
3196 #ifdef __CYGWIN__
3197  if (lnk_added && len > 4 &&
3198  wcscasecmp(wfd.cFileName + len - 4, L".lnk") == 0) {
3199  wfd.cFileName[len -= 4] = L'\0';
3200  }
3201 #else
3202  p = (char *)s;
3203 #endif
3204  ++p;
3205  wlen = (int)len;
3206  len = WideCharToMultiByte(CP_UTF8, 0, wfd.cFileName, wlen, NULL, 0, NULL, NULL);
3207  BUFCHECK(bdiff + len >= buflen);
3208  WideCharToMultiByte(CP_UTF8, 0, wfd.cFileName, wlen, p, len + 1, NULL, NULL);
3209  if (tmp != result) {
3210  rb_str_buf_cat(tmp, p, len);
3211  tmp = rb_str_encode(tmp, rb_enc_from_encoding(enc), 0, Qnil);
3212  len = RSTRING_LEN(tmp);
3213  BUFCHECK(bdiff + len >= buflen);
3214  memcpy(p, RSTRING_PTR(tmp), len);
3215  rb_str_resize(tmp, 0);
3216  }
3217  p += len;
3218  }
3219 #ifdef __CYGWIN__
3220  else {
3221  p += strlen(p);
3222  }
3223 #endif
3224  }
3225 #endif
3226 
3227  if (tainted) OBJ_TAINT(result);
3228  rb_str_set_len(result, p - buf);
3229  rb_enc_check(fname, result);
3230  ENC_CODERANGE_CLEAR(result);
3231  return result;
3232 }
3233 #endif /* _WIN32 */
3234 
3235 #define EXPAND_PATH_BUFFER() rb_enc_str_new(0, MAXPATHLEN + 2, rb_filesystem_encoding())
3236 
3237 #define check_expand_path_args(fname, dname) \
3238  (((fname) = rb_get_path(fname)), \
3239  (void)(NIL_P(dname) ? (dname) : ((dname) = rb_get_path(dname))))
3240 
3241 static VALUE
3243 {
3244  return rb_file_expand_path_internal(fname, Qnil, 0, 0, EXPAND_PATH_BUFFER());
3245 }
3246 
3247 VALUE
3249 {
3250  check_expand_path_args(fname, dname);
3251  return rb_file_expand_path_internal(fname, dname, 0, 1, EXPAND_PATH_BUFFER());
3252 }
3253 
3254 VALUE
3256 {
3257  check_expand_path_args(fname, dname);
3258  return rb_file_expand_path_internal(fname, dname, 0, 0, EXPAND_PATH_BUFFER());
3259 }
3260 
3261 /*
3262  * call-seq:
3263  * File.expand_path(file_name [, dir_string] ) -> abs_file_name
3264  *
3265  * Converts a pathname to an absolute pathname. Relative paths are
3266  * referenced from the current working directory of the process unless
3267  * <i>dir_string</i> is given, in which case it will be used as the
3268  * starting point. The given pathname may start with a
3269  * ``<code>~</code>'', which expands to the process owner's home
3270  * directory (the environment variable <code>HOME</code> must be set
3271  * correctly). ``<code>~</code><i>user</i>'' expands to the named
3272  * user's home directory.
3273  *
3274  * File.expand_path("~oracle/bin") #=> "/home/oracle/bin"
3275  * File.expand_path("../../bin", "/tmp/x") #=> "/bin"
3276  */
3277 
3278 VALUE
3280 {
3281  VALUE fname, dname;
3282 
3283  if (argc == 1) {
3284  return rb_file_expand_path(argv[0], Qnil);
3285  }
3286  rb_scan_args(argc, argv, "11", &fname, &dname);
3287 
3288  return rb_file_expand_path(fname, dname);
3289 }
3290 
3291 VALUE
3293 {
3294  check_expand_path_args(fname, dname);
3295  return rb_file_expand_path_internal(fname, dname, 1, 1, EXPAND_PATH_BUFFER());
3296 }
3297 
3298 /*
3299  * call-seq:
3300  * File.absolute_path(file_name [, dir_string] ) -> abs_file_name
3301  *
3302  * Converts a pathname to an absolute pathname. Relative paths are
3303  * referenced from the current working directory of the process unless
3304  * <i>dir_string</i> is given, in which case it will be used as the
3305  * starting point. If the given pathname starts with a ``<code>~</code>''
3306  * it is NOT expanded, it is treated as a normal directory name.
3307  *
3308  * File.absolute_path("~oracle/bin") #=> "<relative_path>/~oracle/bin"
3309  */
3310 
3311 VALUE
3313 {
3314  VALUE fname, dname;
3315 
3316  if (argc == 1) {
3317  return rb_file_absolute_path(argv[0], Qnil);
3318  }
3319  rb_scan_args(argc, argv, "11", &fname, &dname);
3320 
3321  return rb_file_absolute_path(fname, dname);
3322 }
3323 
3324 static void
3325 realpath_rec(long *prefixlenp, VALUE *resolvedp, const char *unresolved, VALUE loopcheck, int strict, int last)
3326 {
3327  const char *pend = unresolved + strlen(unresolved);
3328  rb_encoding *enc = rb_enc_get(*resolvedp);
3329  ID resolving;
3330  CONST_ID(resolving, "resolving");
3331  while (unresolved < pend) {
3332  const char *testname = unresolved;
3333  const char *unresolved_firstsep = rb_enc_path_next(unresolved, pend, enc);
3334  long testnamelen = unresolved_firstsep - unresolved;
3335  const char *unresolved_nextname = unresolved_firstsep;
3336  while (unresolved_nextname < pend && isdirsep(*unresolved_nextname))
3337  unresolved_nextname++;
3338  unresolved = unresolved_nextname;
3339  if (testnamelen == 1 && testname[0] == '.') {
3340  }
3341  else if (testnamelen == 2 && testname[0] == '.' && testname[1] == '.') {
3342  if (*prefixlenp < RSTRING_LEN(*resolvedp)) {
3343  const char *resolved_str = RSTRING_PTR(*resolvedp);
3344  const char *resolved_names = resolved_str + *prefixlenp;
3345  const char *lastsep = strrdirsep(resolved_names, resolved_str + RSTRING_LEN(*resolvedp), enc);
3346  long len = lastsep ? lastsep - resolved_names : 0;
3347  rb_str_resize(*resolvedp, *prefixlenp + len);
3348  }
3349  }
3350  else {
3351  VALUE checkval;
3352  VALUE testpath = rb_str_dup(*resolvedp);
3353  if (*prefixlenp < RSTRING_LEN(testpath))
3354  rb_str_cat2(testpath, "/");
3355  rb_str_cat(testpath, testname, testnamelen);
3356  checkval = rb_hash_aref(loopcheck, testpath);
3357  if (!NIL_P(checkval)) {
3358  if (checkval == ID2SYM(resolving)) {
3359  errno = ELOOP;
3360  rb_sys_fail_path(testpath);
3361  }
3362  else {
3363  *resolvedp = rb_str_dup(checkval);
3364  }
3365  }
3366  else {
3367  struct stat sbuf;
3368  int ret;
3369  VALUE testpath2 = rb_str_encode_ospath(testpath);
3370  ret = lstat(RSTRING_PTR(testpath2), &sbuf);
3371  if (ret == -1) {
3372  if (errno == ENOENT) {
3373  if (strict || !last || *unresolved_firstsep)
3374  rb_sys_fail_path(testpath);
3375  *resolvedp = testpath;
3376  break;
3377  }
3378  else {
3379  rb_sys_fail_path(testpath);
3380  }
3381  }
3382 #ifdef HAVE_READLINK
3383  if (S_ISLNK(sbuf.st_mode)) {
3384  VALUE link;
3385  volatile VALUE link_orig = Qnil;
3386  const char *link_prefix, *link_names;
3387  long link_prefixlen;
3388  rb_hash_aset(loopcheck, testpath, ID2SYM(resolving));
3389  link = rb_readlink(testpath);
3390  link_prefix = RSTRING_PTR(link);
3391  link_names = skipprefixroot(link_prefix, link_prefix + RSTRING_LEN(link), rb_enc_get(link));
3392  link_prefixlen = link_names - link_prefix;
3393  if (link_prefixlen > 0) {
3394  rb_encoding *enc, *linkenc = rb_enc_get(link);
3395  link_orig = link;
3396  link = rb_str_subseq(link, 0, link_prefixlen);
3397  enc = rb_enc_check(*resolvedp, link);
3398  if (enc != linkenc) link = rb_str_conv_enc(link, linkenc, enc);
3399  *resolvedp = link;
3400  *prefixlenp = link_prefixlen;
3401  }
3402  realpath_rec(prefixlenp, resolvedp, link_names, loopcheck, strict, *unresolved_firstsep == '\0');
3403  RB_GC_GUARD(link_orig);
3404  rb_hash_aset(loopcheck, testpath, rb_str_dup_frozen(*resolvedp));
3405  }
3406  else
3407 #endif
3408  {
3409  VALUE s = rb_str_dup_frozen(testpath);
3410  rb_hash_aset(loopcheck, s, s);
3411  *resolvedp = testpath;
3412  }
3413  }
3414  }
3415  }
3416 }
3417 
3418 VALUE
3419 rb_realpath_internal(VALUE basedir, VALUE path, int strict)
3420 {
3421  long prefixlen;
3422  VALUE resolved;
3423  volatile VALUE unresolved_path;
3424  VALUE loopcheck;
3425  volatile VALUE curdir = Qnil;
3426 
3427  rb_encoding *enc;
3428  char *path_names = NULL, *basedir_names = NULL, *curdir_names = NULL;
3429  char *ptr, *prefixptr = NULL, *pend;
3430  long len;
3431 
3432  rb_secure(2);
3433 
3434  FilePathValue(path);
3435  unresolved_path = rb_str_dup_frozen(path);
3436 
3437  if (!NIL_P(basedir)) {
3438  FilePathValue(basedir);
3439  basedir = rb_str_dup_frozen(basedir);
3440  }
3441 
3442  RSTRING_GETMEM(unresolved_path, ptr, len);
3443  path_names = skipprefixroot(ptr, ptr + len, rb_enc_get(unresolved_path));
3444  if (ptr != path_names) {
3445  resolved = rb_str_subseq(unresolved_path, 0, path_names - ptr);
3446  goto root_found;
3447  }
3448 
3449  if (!NIL_P(basedir)) {
3450  RSTRING_GETMEM(basedir, ptr, len);
3451  basedir_names = skipprefixroot(ptr, ptr + len, rb_enc_get(basedir));
3452  if (ptr != basedir_names) {
3453  resolved = rb_str_subseq(basedir, 0, basedir_names - ptr);
3454  goto root_found;
3455  }
3456  }
3457 
3458  curdir = rb_dir_getwd();
3459  RSTRING_GETMEM(curdir, ptr, len);
3460  curdir_names = skipprefixroot(ptr, ptr + len, rb_enc_get(curdir));
3461  resolved = rb_str_subseq(curdir, 0, curdir_names - ptr);
3462 
3463  root_found:
3464  RSTRING_GETMEM(resolved, prefixptr, prefixlen);
3465  pend = prefixptr + prefixlen;
3466  enc = rb_enc_get(resolved);
3467  ptr = chompdirsep(prefixptr, pend, enc);
3468  if (ptr < pend) {
3469  prefixlen = ++ptr - prefixptr;
3470  rb_str_set_len(resolved, prefixlen);
3471  }
3472 #ifdef FILE_ALT_SEPARATOR
3473  while (prefixptr < ptr) {
3474  if (*prefixptr == FILE_ALT_SEPARATOR) {
3475  *prefixptr = '/';
3476  }
3477  Inc(prefixptr, pend, enc);
3478  }
3479 #endif
3480 
3481  loopcheck = rb_hash_new();
3482  if (curdir_names)
3483  realpath_rec(&prefixlen, &resolved, curdir_names, loopcheck, 1, 0);
3484  if (basedir_names)
3485  realpath_rec(&prefixlen, &resolved, basedir_names, loopcheck, 1, 0);
3486  realpath_rec(&prefixlen, &resolved, path_names, loopcheck, strict, 1);
3487 
3488  OBJ_TAINT(resolved);
3489  return resolved;
3490 }
3491 
3492 /*
3493  * call-seq:
3494  * File.realpath(pathname [, dir_string]) -> real_pathname
3495  *
3496  * Returns the real (absolute) pathname of _pathname_ in the actual
3497  * filesystem not containing symlinks or useless dots.
3498  *
3499  * If _dir_string_ is given, it is used as a base directory
3500  * for interpreting relative pathname instead of the current directory.
3501  *
3502  * All components of the pathname must exist when this method is
3503  * called.
3504  */
3505 static VALUE
3506 rb_file_s_realpath(int argc, VALUE *argv, VALUE klass)
3507 {
3508  VALUE path, basedir;
3509  rb_scan_args(argc, argv, "11", &path, &basedir);
3510  return rb_realpath_internal(basedir, path, 1);
3511 }
3512 
3513 /*
3514  * call-seq:
3515  * File.realdirpath(pathname [, dir_string]) -> real_pathname
3516  *
3517  * Returns the real (absolute) pathname of _pathname_ in the actual filesystem.
3518  * The real pathname doesn't contain symlinks or useless dots.
3519  *
3520  * If _dir_string_ is given, it is used as a base directory
3521  * for interpreting relative pathname instead of the current directory.
3522  *
3523  * The last component of the real pathname can be nonexistent.
3524  */
3525 static VALUE
3526 rb_file_s_realdirpath(int argc, VALUE *argv, VALUE klass)
3527 {
3528  VALUE path, basedir;
3529  rb_scan_args(argc, argv, "11", &path, &basedir);
3530  return rb_realpath_internal(basedir, path, 0);
3531 }
3532 
3533 static size_t
3534 rmext(const char *p, long l0, long l1, const char *e, long l2, rb_encoding *enc)
3535 {
3536  int len1, len2;
3537  unsigned int c;
3538  const char *s, *last;
3539 
3540  if (!e || !l2) return 0;
3541 
3542  c = rb_enc_codepoint_len(e, e + l2, &len1, enc);
3543  if (rb_enc_ascget(e + len1, e + l2, &len2, enc) == '*' && len1 + len2 == l2) {
3544  if (c == '.') return l0;
3545  s = p;
3546  e = p + l1;
3547  last = e;
3548  while (s < e) {
3549  if (rb_enc_codepoint_len(s, e, &len1, enc) == c) last = s;
3550  s += len1;
3551  }
3552  return last - p;
3553  }
3554  if (l1 < l2) return l1;
3555 
3556  s = p+l1-l2;
3557  if (rb_enc_left_char_head(p, s, p+l1, enc) != s) return 0;
3558 #if CASEFOLD_FILESYSTEM
3559 #define fncomp strncasecmp
3560 #else
3561 #define fncomp strncmp
3562 #endif
3563  if (fncomp(s, e, l2) == 0) {
3564  return l1-l2;
3565  }
3566  return 0;
3567 }
3568 
3569 const char *
3570 ruby_enc_find_basename(const char *name, long *baselen, long *alllen, rb_encoding *enc)
3571 {
3572  const char *p, *q, *e, *end;
3573 #if defined DOSISH_DRIVE_LETTER || defined DOSISH_UNC
3574  const char *root;
3575 #endif
3576  long f = 0, n = -1;
3577 
3578  end = name + *alllen;
3579  name = skipprefix(name, end, enc);
3580 #if defined DOSISH_DRIVE_LETTER || defined DOSISH_UNC
3581  root = name;
3582 #endif
3583  while (isdirsep(*name))
3584  name++;
3585  if (!*name) {
3586  p = name - 1;
3587  f = 1;
3588 #if defined DOSISH_DRIVE_LETTER || defined DOSISH_UNC
3589  if (name != root) {
3590  /* has slashes */
3591  }
3592 #ifdef DOSISH_DRIVE_LETTER
3593  else if (*p == ':') {
3594  p++;
3595  f = 0;
3596  }
3597 #endif
3598 #ifdef DOSISH_UNC
3599  else {
3600  p = "/";
3601  }
3602 #endif
3603 #endif
3604  }
3605  else {
3606  if (!(p = strrdirsep(name, end, enc))) {
3607  p = name;
3608  }
3609  else {
3610  while (isdirsep(*p)) p++; /* skip last / */
3611  }
3612 #if USE_NTFS
3613  n = ntfs_tail(p, end, enc) - p;
3614 #else
3615  n = chompdirsep(p, end, enc) - p;
3616 #endif
3617  for (q = p; q - p < n && *q == '.'; q++);
3618  for (e = 0; q - p < n; Inc(q, end, enc)) {
3619  if (*q == '.') e = q;
3620  }
3621  if (e) f = e - p;
3622  else f = n;
3623  }
3624 
3625  if (baselen)
3626  *baselen = f;
3627  if (alllen)
3628  *alllen = n;
3629  return p;
3630 }
3631 
3632 /*
3633  * call-seq:
3634  * File.basename(file_name [, suffix] ) -> base_name
3635  *
3636  * Returns the last component of the filename given in <i>file_name</i>,
3637  * which must be formed using forward slashes (``<code>/</code>'')
3638  * regardless of the separator used on the local file system. If
3639  * <i>suffix</i> is given and present at the end of <i>file_name</i>,
3640  * it is removed.
3641  *
3642  * File.basename("/home/gumby/work/ruby.rb") #=> "ruby.rb"
3643  * File.basename("/home/gumby/work/ruby.rb", ".rb") #=> "ruby"
3644  */
3645 
3646 static VALUE
3647 rb_file_s_basename(int argc, VALUE *argv)
3648 {
3649  VALUE fname, fext, basename;
3650  const char *name, *p;
3651  long f, n;
3652  rb_encoding *enc;
3653 
3654  if (rb_scan_args(argc, argv, "11", &fname, &fext) == 2) {
3655  rb_encoding *enc;
3656  StringValue(fext);
3657  if (!rb_enc_asciicompat(enc = rb_enc_get(fext))) {
3658  rb_raise(rb_eEncCompatError, "ascii incompatible character encodings: %s",
3659  rb_enc_name(enc));
3660  }
3661  }
3662  FilePathStringValue(fname);
3663  if (!NIL_P(fext)) enc = rb_enc_check(fname, fext);
3664  else enc = rb_enc_get(fname);
3665  if ((n = RSTRING_LEN(fname)) == 0 || !*(name = RSTRING_PTR(fname)))
3666  return rb_str_new_shared(fname);
3667 
3668  p = ruby_enc_find_basename(name, &f, &n, enc);
3669  if (n >= 0) {
3670  if (NIL_P(fext)) {
3671  f = n;
3672  }
3673  else {
3674  rb_encoding *fenc = rb_enc_get(fext);
3675  const char *fp;
3676  if (enc != fenc &&
3678  fext = rb_str_conv_enc(fext, fenc, enc);
3679  }
3680  fp = StringValueCStr(fext);
3681  if (!(f = rmext(p, f, n, fp, RSTRING_LEN(fext), enc))) {
3682  f = n;
3683  }
3684  RB_GC_GUARD(fext);
3685  }
3686  if (f == RSTRING_LEN(fname)) return rb_str_new_shared(fname);
3687  }
3688 
3689  basename = rb_str_new(p, f);
3690  rb_enc_copy(basename, fname);
3691  OBJ_INFECT(basename, fname);
3692  return basename;
3693 }
3694 
3695 /*
3696  * call-seq:
3697  * File.dirname(file_name ) -> dir_name
3698  *
3699  * Returns all components of the filename given in <i>file_name</i>
3700  * except the last one. The filename must be formed using forward
3701  * slashes (``<code>/</code>'') regardless of the separator used on the
3702  * local file system.
3703  *
3704  * File.dirname("/home/gumby/work/ruby.rb") #=> "/home/gumby/work"
3705  */
3706 
3707 static VALUE
3709 {
3710  return rb_file_dirname(fname);
3711 }
3712 
3713 VALUE
3715 {
3716  const char *name, *root, *p, *end;
3717  VALUE dirname;
3718  rb_encoding *enc;
3719 
3720  FilePathStringValue(fname);
3721  name = StringValueCStr(fname);
3722  end = name + RSTRING_LEN(fname);
3723  enc = rb_enc_get(fname);
3724  root = skiproot(name, end, enc);
3725 #ifdef DOSISH_UNC
3726  if (root > name + 1 && isdirsep(*name))
3727  root = skipprefix(name = root - 2, end, enc);
3728 #else
3729  if (root > name + 1)
3730  name = root - 1;
3731 #endif
3732  p = strrdirsep(root, end, enc);
3733  if (!p) {
3734  p = root;
3735  }
3736  if (p == name)
3737  return rb_usascii_str_new2(".");
3738 #ifdef DOSISH_DRIVE_LETTER
3739  if (has_drive_letter(name) && isdirsep(*(name + 2))) {
3740  const char *top = skiproot(name + 2, end, enc);
3741  dirname = rb_str_new(name, 3);
3742  rb_str_cat(dirname, top, p - top);
3743  }
3744  else
3745 #endif
3746  dirname = rb_str_new(name, p - name);
3747 #ifdef DOSISH_DRIVE_LETTER
3748  if (has_drive_letter(name) && root == name + 2 && p - name == 2)
3749  rb_str_cat(dirname, ".", 1);
3750 #endif
3751  rb_enc_copy(dirname, fname);
3752  OBJ_INFECT(dirname, fname);
3753  return dirname;
3754 }
3755 
3756 /*
3757  * accept a String, and return the pointer of the extension.
3758  * if len is passed, set the length of extension to it.
3759  * returned pointer is in ``name'' or NULL.
3760  * returns *len
3761  * no dot NULL 0
3762  * dotfile top 0
3763  * end with dot dot 1
3764  * .ext dot len of .ext
3765  * .ext:stream dot len of .ext without :stream (NT only)
3766  *
3767  */
3768 const char *
3769 ruby_enc_find_extname(const char *name, long *len, rb_encoding *enc)
3770 {
3771  const char *p, *e, *end = name + (len ? *len : (long)strlen(name));
3772 
3773  p = strrdirsep(name, end, enc); /* get the last path component */
3774  if (!p)
3775  p = name;
3776  else
3777  do name = ++p; while (isdirsep(*p));
3778 
3779  e = 0;
3780  while (*p && *p == '.') p++;
3781  while (*p) {
3782  if (*p == '.' || istrailinggarbage(*p)) {
3783 #if USE_NTFS
3784  const char *last = p++, *dot = last;
3785  while (istrailinggarbage(*p)) {
3786  if (*p == '.') dot = p;
3787  p++;
3788  }
3789  if (!*p || *p == ':') {
3790  p = last;
3791  break;
3792  }
3793  if (*last == '.' || dot > last) e = dot;
3794  continue;
3795 #else
3796  e = p; /* get the last dot of the last component */
3797 #endif
3798  }
3799 #if USE_NTFS
3800  else if (*p == ':') {
3801  break;
3802  }
3803 #endif
3804  else if (isdirsep(*p))
3805  break;
3806  Inc(p, end, enc);
3807  }
3808 
3809  if (len) {
3810  /* no dot, or the only dot is first or end? */
3811  if (!e || e == name)
3812  *len = 0;
3813  else if (e+1 == p)
3814  *len = 1;
3815  else
3816  *len = p - e;
3817  }
3818  return e;
3819 }
3820 
3821 /*
3822  * call-seq:
3823  * File.extname(path) -> string
3824  *
3825  * Returns the extension (the portion of file name in <i>path</i>
3826  * after the period).
3827  *
3828  * File.extname("test.rb") #=> ".rb"
3829  * File.extname("a/b/d/test.rb") #=> ".rb"
3830  * File.extname("test") #=> ""
3831  * File.extname(".profile") #=> ""
3832  *
3833  */
3834 
3835 static VALUE
3837 {
3838  const char *name, *e;
3839  long len;
3840  VALUE extname;
3841 
3842  FilePathStringValue(fname);
3843  name = StringValueCStr(fname);
3844  len = RSTRING_LEN(fname);
3845  e = ruby_enc_find_extname(name, &len, rb_enc_get(fname));
3846  if (len <= 1)
3847  return rb_str_new(0, 0);
3848  extname = rb_str_subseq(fname, e - name, len); /* keep the dot, too! */
3849  OBJ_INFECT(extname, fname);
3850  return extname;
3851 }
3852 
3853 /*
3854  * call-seq:
3855  * File.path(path) -> string
3856  *
3857  * Returns the string representation of the path
3858  *
3859  * File.path("/dev/null") #=> "/dev/null"
3860  * File.path(Pathname.new("/tmp")) #=> "/tmp"
3861  *
3862  */
3863 
3864 static VALUE
3866 {
3867  return rb_get_path(fname);
3868 }
3869 
3870 /*
3871  * call-seq:
3872  * File.split(file_name) -> array
3873  *
3874  * Splits the given string into a directory and a file component and
3875  * returns them in a two-element array. See also
3876  * <code>File::dirname</code> and <code>File::basename</code>.
3877  *
3878  * File.split("/home/gumby/.profile") #=> ["/home/gumby", ".profile"]
3879  */
3880 
3881 static VALUE
3883 {
3884  FilePathStringValue(path); /* get rid of converting twice */
3885  return rb_assoc_new(rb_file_s_dirname(Qnil, path), rb_file_s_basename(1,&path));
3886 }
3887 
3889 
3890 static VALUE rb_file_join(VALUE ary, VALUE sep);
3891 
3892 static VALUE
3894 {
3895  VALUE *arg = (VALUE *)argp;
3896  if (recur || ary == arg[0]) rb_raise(rb_eArgError, "recursive array");
3897  return rb_file_join(arg[0], arg[1]);
3898 }
3899 
3900 static VALUE
3902 {
3903  long len, i;
3904  VALUE result, tmp;
3905  const char *name, *tail;
3906 
3907  if (RARRAY_LEN(ary) == 0) return rb_str_new(0, 0);
3908 
3909  len = 1;
3910  for (i=0; i<RARRAY_LEN(ary); i++) {
3911  tmp = RARRAY_PTR(ary)[i];
3912  if (RB_TYPE_P(tmp, T_STRING)) {
3913  len += RSTRING_LEN(tmp);
3914  }
3915  else {
3916  len += 10;
3917  }
3918  }
3919  if (!NIL_P(sep)) {
3920  StringValue(sep);
3921  len += RSTRING_LEN(sep) * RARRAY_LEN(ary) - 1;
3922  }
3923  result = rb_str_buf_new(len);
3924  OBJ_INFECT(result, ary);
3925  for (i=0; i<RARRAY_LEN(ary); i++) {
3926  tmp = RARRAY_PTR(ary)[i];
3927  switch (TYPE(tmp)) {
3928  case T_STRING:
3929  break;
3930  case T_ARRAY:
3931  if (ary == tmp) {
3932  rb_raise(rb_eArgError, "recursive array");
3933  }
3934  else {
3935  VALUE args[2];
3936 
3937  args[0] = tmp;
3938  args[1] = sep;
3939  tmp = rb_exec_recursive(file_inspect_join, ary, (VALUE)args);
3940  }
3941  break;
3942  default:
3943  FilePathStringValue(tmp);
3944  }
3945  name = StringValueCStr(result);
3946  len = RSTRING_LEN(result);
3947  if (i == 0) {
3948  rb_enc_copy(result, tmp);
3949  }
3950  else if (!NIL_P(sep)) {
3951  tail = chompdirsep(name, name + len, rb_enc_get(result));
3952  if (RSTRING_PTR(tmp) && isdirsep(RSTRING_PTR(tmp)[0])) {
3953  rb_str_set_len(result, tail - name);
3954  }
3955  else if (!*tail) {
3956  rb_str_buf_append(result, sep);
3957  }
3958  }
3959  rb_str_buf_append(result, tmp);
3960  }
3961 
3962  return result;
3963 }
3964 
3965 /*
3966  * call-seq:
3967  * File.join(string, ...) -> path
3968  *
3969  * Returns a new string formed by joining the strings using
3970  * <code>File::SEPARATOR</code>.
3971  *
3972  * File.join("usr", "mail", "gumby") #=> "usr/mail/gumby"
3973  *
3974  */
3975 
3976 static VALUE
3978 {
3979  return rb_file_join(args, separator);
3980 }
3981 
3982 #if defined(HAVE_TRUNCATE) || defined(HAVE_CHSIZE)
3983 /*
3984  * call-seq:
3985  * File.truncate(file_name, integer) -> 0
3986  *
3987  * Truncates the file <i>file_name</i> to be at most <i>integer</i>
3988  * bytes long. Not available on all platforms.
3989  *
3990  * f = File.new("out", "w")
3991  * f.write("1234567890") #=> 10
3992  * f.close #=> nil
3993  * File.truncate("out", 5) #=> 0
3994  * File.size("out") #=> 5
3995  *
3996  */
3997 
3998 static VALUE
3999 rb_file_s_truncate(VALUE klass, VALUE path, VALUE len)
4000 {
4001  off_t pos;
4002 
4003  rb_secure(2);
4004  pos = NUM2OFFT(len);
4005  FilePathValue(path);
4006  path = rb_str_encode_ospath(path);
4007 #ifdef HAVE_TRUNCATE
4008  if (truncate(StringValueCStr(path), pos) < 0)
4009  rb_sys_fail_path(path);
4010 #else /* defined(HAVE_CHSIZE) */
4011  {
4012  int tmpfd;
4013 
4014  if ((tmpfd = open(StringValueCStr(path), 0)) < 0) {
4015  rb_sys_fail_path(path);
4016  }
4017  rb_update_max_fd(tmpfd);
4018  if (chsize(tmpfd, pos) < 0) {
4019  close(tmpfd);
4020  rb_sys_fail_path(path);
4021  }
4022  close(tmpfd);
4023  }
4024 #endif
4025  return INT2FIX(0);
4026 }
4027 #else
4028 #define rb_file_s_truncate rb_f_notimplement
4029 #endif
4030 
4031 #if defined(HAVE_FTRUNCATE) || defined(HAVE_CHSIZE)
4032 /*
4033  * call-seq:
4034  * file.truncate(integer) -> 0
4035  *
4036  * Truncates <i>file</i> to at most <i>integer</i> bytes. The file
4037  * must be opened for writing. Not available on all platforms.
4038  *
4039  * f = File.new("out", "w")
4040  * f.syswrite("1234567890") #=> 10
4041  * f.truncate(5) #=> 0
4042  * f.close() #=> nil
4043  * File.size("out") #=> 5
4044  */
4045 
4046 static VALUE
4047 rb_file_truncate(VALUE obj, VALUE len)
4048 {
4049  rb_io_t *fptr;
4050  off_t pos;
4051 
4052  rb_secure(2);
4053  pos = NUM2OFFT(len);
4054  GetOpenFile(obj, fptr);
4055  if (!(fptr->mode & FMODE_WRITABLE)) {
4056  rb_raise(rb_eIOError, "not opened for writing");
4057  }
4058  rb_io_flush(obj);
4059 #ifdef HAVE_FTRUNCATE
4060  if (ftruncate(fptr->fd, pos) < 0)
4061  rb_sys_fail_path(fptr->pathv);
4062 #else /* defined(HAVE_CHSIZE) */
4063  if (chsize(fptr->fd, pos) < 0)
4064  rb_sys_fail_path(fptr->pathv);
4065 #endif
4066  return INT2FIX(0);
4067 }
4068 #else
4069 #define rb_file_truncate rb_f_notimplement
4070 #endif
4071 
4072 # ifndef LOCK_SH
4073 # define LOCK_SH 1
4074 # endif
4075 # ifndef LOCK_EX
4076 # define LOCK_EX 2
4077 # endif
4078 # ifndef LOCK_NB
4079 # define LOCK_NB 4
4080 # endif
4081 # ifndef LOCK_UN
4082 # define LOCK_UN 8
4083 # endif
4084 
4085 #ifdef __CYGWIN__
4086 #include <winerror.h>
4087 extern unsigned long __attribute__((stdcall)) GetLastError(void);
4088 #endif
4089 
4090 static VALUE
4091 rb_thread_flock(void *data)
4092 {
4093 #ifdef __CYGWIN__
4094  int old_errno = errno;
4095 #endif
4096  int *op = data, ret = flock(op[0], op[1]);
4097 
4098 #ifdef __CYGWIN__
4099  if (GetLastError() == ERROR_NOT_LOCKED) {
4100  ret = 0;
4101  errno = old_errno;
4102  }
4103 #endif
4104  return (VALUE)ret;
4105 }
4106 
4107 /*
4108  * call-seq:
4109  * file.flock (locking_constant )-> 0 or false
4110  *
4111  * Locks or unlocks a file according to <i>locking_constant</i> (a
4112  * logical <em>or</em> of the values in the table below).
4113  * Returns <code>false</code> if <code>File::LOCK_NB</code> is
4114  * specified and the operation would otherwise have blocked. Not
4115  * available on all platforms.
4116  *
4117  * Locking constants (in class File):
4118  *
4119  * LOCK_EX | Exclusive lock. Only one process may hold an
4120  * | exclusive lock for a given file at a time.
4121  * ----------+------------------------------------------------
4122  * LOCK_NB | Don't block when locking. May be combined
4123  * | with other lock options using logical or.
4124  * ----------+------------------------------------------------
4125  * LOCK_SH | Shared lock. Multiple processes may each hold a
4126  * | shared lock for a given file at the same time.
4127  * ----------+------------------------------------------------
4128  * LOCK_UN | Unlock.
4129  *
4130  * Example:
4131  *
4132  * # update a counter using write lock
4133  * # don't use "w" because it truncates the file before lock.
4134  * File.open("counter", File::RDWR|File::CREAT, 0644) {|f|
4135  * f.flock(File::LOCK_EX)
4136  * value = f.read.to_i + 1
4137  * f.rewind
4138  * f.write("#{value}\n")
4139  * f.flush
4140  * f.truncate(f.pos)
4141  * }
4142  *
4143  * # read the counter using read lock
4144  * File.open("counter", "r") {|f|
4145  * f.flock(File::LOCK_SH)
4146  * p f.read
4147  * }
4148  *
4149  */
4150 
4151 static VALUE
4152 rb_file_flock(VALUE obj, VALUE operation)
4153 {
4154  rb_io_t *fptr;
4155  int op[2], op1;
4156 
4157  rb_secure(2);
4158  op[1] = op1 = NUM2INT(operation);
4159  GetOpenFile(obj, fptr);
4160  op[0] = fptr->fd;
4161 
4162  if (fptr->mode & FMODE_WRITABLE) {
4163  rb_io_flush(obj);
4164  }
4165  while ((int)rb_thread_io_blocking_region(rb_thread_flock, op, fptr->fd) < 0) {
4166  switch (errno) {
4167  case EAGAIN:
4168  case EACCES:
4169 #if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN
4170  case EWOULDBLOCK:
4171 #endif
4172  if (op1 & LOCK_NB) return Qfalse;
4174  rb_io_check_closed(fptr);
4175  continue;
4176 
4177  case EINTR:
4178 #if defined(ERESTART)
4179  case ERESTART:
4180 #endif
4181  break;
4182 
4183  default:
4184  rb_sys_fail_path(fptr->pathv);
4185  }
4186  }
4187  return INT2FIX(0);
4188 }
4189 #undef flock
4190 
4191 static void
4192 test_check(int n, int argc, VALUE *argv)
4193 {
4194  int i;
4195 
4196  rb_secure(2);
4197  n+=1;
4198  if (n != argc) rb_raise(rb_eArgError, "wrong number of arguments (%d for %d)", argc, n);
4199  for (i=1; i<n; i++) {
4200  switch (TYPE(argv[i])) {
4201  case T_STRING:
4202  default:
4203  FilePathValue(argv[i]);
4204  break;
4205  case T_FILE:
4206  break;
4207  }
4208  }
4209 }
4210 
4211 #define CHECK(n) test_check((n), argc, argv)
4212 
4213 /*
4214  * call-seq:
4215  * test(int_cmd, file1 [, file2] ) -> obj
4216  *
4217  * Uses the integer <i>aCmd</i> to perform various tests on
4218  * <i>file1</i> (first table below) or on <i>file1</i> and
4219  * <i>file2</i> (second table).
4220  *
4221  * File tests on a single file:
4222  *
4223  * Test Returns Meaning
4224  * "A" | Time | Last access time for file1
4225  * "b" | boolean | True if file1 is a block device
4226  * "c" | boolean | True if file1 is a character device
4227  * "C" | Time | Last change time for file1
4228  * "d" | boolean | True if file1 exists and is a directory
4229  * "e" | boolean | True if file1 exists
4230  * "f" | boolean | True if file1 exists and is a regular file
4231  * "g" | boolean | True if file1 has the \CF{setgid} bit
4232  * | | set (false under NT)
4233  * "G" | boolean | True if file1 exists and has a group
4234  * | | ownership equal to the caller's group
4235  * "k" | boolean | True if file1 exists and has the sticky bit set
4236  * "l" | boolean | True if file1 exists and is a symbolic link
4237  * "M" | Time | Last modification time for file1
4238  * "o" | boolean | True if file1 exists and is owned by
4239  * | | the caller's effective uid
4240  * "O" | boolean | True if file1 exists and is owned by
4241  * | | the caller's real uid
4242  * "p" | boolean | True if file1 exists and is a fifo
4243  * "r" | boolean | True if file1 is readable by the effective
4244  * | | uid/gid of the caller
4245  * "R" | boolean | True if file is readable by the real
4246  * | | uid/gid of the caller
4247  * "s" | int/nil | If file1 has nonzero size, return the size,
4248  * | | otherwise return nil
4249  * "S" | boolean | True if file1 exists and is a socket
4250  * "u" | boolean | True if file1 has the setuid bit set
4251  * "w" | boolean | True if file1 exists and is writable by
4252  * | | the effective uid/gid
4253  * "W" | boolean | True if file1 exists and is writable by
4254  * | | the real uid/gid
4255  * "x" | boolean | True if file1 exists and is executable by
4256  * | | the effective uid/gid
4257  * "X" | boolean | True if file1 exists and is executable by
4258  * | | the real uid/gid
4259  * "z" | boolean | True if file1 exists and has a zero length
4260  *
4261  * Tests that take two files:
4262  *
4263  * "-" | boolean | True if file1 and file2 are identical
4264  * "=" | boolean | True if the modification times of file1
4265  * | | and file2 are equal
4266  * "<" | boolean | True if the modification time of file1
4267  * | | is prior to that of file2
4268  * ">" | boolean | True if the modification time of file1
4269  * | | is after that of file2
4270  */
4271 
4272 static VALUE
4273 rb_f_test(int argc, VALUE *argv)
4274 {
4275  int cmd;
4276 
4277  if (argc == 0) rb_raise(rb_eArgError, "wrong number of arguments (0 for 2..3)");
4278  cmd = NUM2CHR(argv[0]);
4279  if (cmd == 0) goto unknown;
4280  if (strchr("bcdefgGkloOprRsSuwWxXz", cmd)) {
4281  CHECK(1);
4282  switch (cmd) {
4283  case 'b':
4284  return rb_file_blockdev_p(0, argv[1]);
4285 
4286  case 'c':
4287  return rb_file_chardev_p(0, argv[1]);
4288 
4289  case 'd':
4290  return rb_file_directory_p(0, argv[1]);
4291 
4292  case 'a':
4293  case 'e':
4294  return rb_file_exist_p(0, argv[1]);
4295 
4296  case 'f':
4297  return rb_file_file_p(0, argv[1]);
4298 
4299  case 'g':
4300  return rb_file_sgid_p(0, argv[1]);
4301 
4302  case 'G':
4303  return rb_file_grpowned_p(0, argv[1]);
4304 
4305  case 'k':
4306  return rb_file_sticky_p(0, argv[1]);
4307 
4308  case 'l':
4309  return rb_file_symlink_p(0, argv[1]);
4310 
4311  case 'o':
4312  return rb_file_owned_p(0, argv[1]);
4313 
4314  case 'O':
4315  return rb_file_rowned_p(0, argv[1]);
4316 
4317  case 'p':
4318  return rb_file_pipe_p(0, argv[1]);
4319 
4320  case 'r':
4321  return rb_file_readable_p(0, argv[1]);
4322 
4323  case 'R':
4324  return rb_file_readable_real_p(0, argv[1]);
4325 
4326  case 's':
4327  return rb_file_size_p(0, argv[1]);
4328 
4329  case 'S':
4330  return rb_file_socket_p(0, argv[1]);
4331 
4332  case 'u':
4333  return rb_file_suid_p(0, argv[1]);
4334 
4335  case 'w':
4336  return rb_file_writable_p(0, argv[1]);
4337 
4338  case 'W':
4339  return rb_file_writable_real_p(0, argv[1]);
4340 
4341  case 'x':
4342  return rb_file_executable_p(0, argv[1]);
4343 
4344  case 'X':
4345  return rb_file_executable_real_p(0, argv[1]);
4346 
4347  case 'z':
4348  return rb_file_zero_p(0, argv[1]);
4349  }
4350  }
4351 
4352  if (strchr("MAC", cmd)) {
4353  struct stat st;
4354  VALUE fname = argv[1];
4355 
4356  CHECK(1);
4357  if (rb_stat(fname, &st) == -1) {
4358  FilePathValue(fname);
4359  rb_sys_fail_path(fname);
4360  }
4361 
4362  switch (cmd) {
4363  case 'A':
4364  return stat_atime(&st);
4365  case 'M':
4366  return stat_mtime(&st);
4367  case 'C':
4368  return stat_ctime(&st);
4369  }
4370  }
4371 
4372  if (cmd == '-') {
4373  CHECK(2);
4374  return rb_file_identical_p(0, argv[1], argv[2]);
4375  }
4376 
4377  if (strchr("=<>", cmd)) {
4378  struct stat st1, st2;
4379 
4380  CHECK(2);
4381  if (rb_stat(argv[1], &st1) < 0) return Qfalse;
4382  if (rb_stat(argv[2], &st2) < 0) return Qfalse;
4383 
4384  switch (cmd) {
4385  case '=':
4386  if (st1.st_mtime == st2.st_mtime) return Qtrue;
4387  return Qfalse;
4388 
4389  case '>':
4390  if (st1.st_mtime > st2.st_mtime) return Qtrue;
4391  return Qfalse;
4392 
4393  case '<':
4394  if (st1.st_mtime < st2.st_mtime) return Qtrue;
4395  return Qfalse;
4396  }
4397  }
4398  unknown:
4399  /* unknown command */
4400  if (ISPRINT(cmd)) {
4401  rb_raise(rb_eArgError, "unknown command '%s%c'", cmd == '\'' || cmd == '\\' ? "\\" : "", cmd);
4402  }
4403  else {
4404  rb_raise(rb_eArgError, "unknown command \"\\x%02X\"", cmd);
4405  }
4406  return Qnil; /* not reached */
4407 }
4408 
4409 
4410 /*
4411  * Document-class: File::Stat
4412  *
4413  * Objects of class <code>File::Stat</code> encapsulate common status
4414  * information for <code>File</code> objects. The information is
4415  * recorded at the moment the <code>File::Stat</code> object is
4416  * created; changes made to the file after that point will not be
4417  * reflected. <code>File::Stat</code> objects are returned by
4418  * <code>IO#stat</code>, <code>File::stat</code>,
4419  * <code>File#lstat</code>, and <code>File::lstat</code>. Many of these
4420  * methods return platform-specific values, and not all values are
4421  * meaningful on all systems. See also <code>Kernel#test</code>.
4422  */
4423 
4424 static VALUE
4426 {
4427  return stat_new_0(klass, 0);
4428 }
4429 
4430 /*
4431  * call-seq:
4432  *
4433  * File::Stat.new(file_name) -> stat
4434  *
4435  * Create a File::Stat object for the given file name (raising an
4436  * exception if the file doesn't exist).
4437  */
4438 
4439 static VALUE
4441 {
4442  struct stat st, *nst;
4443 
4444  rb_secure(2);
4445  FilePathValue(fname);
4446  fname = rb_str_encode_ospath(fname);
4447  if (STAT(StringValueCStr(fname), &st) == -1) {
4448  rb_sys_fail_path(fname);
4449  }
4450  if (DATA_PTR(obj)) {
4451  xfree(DATA_PTR(obj));
4452  DATA_PTR(obj) = NULL;
4453  }
4454  nst = ALLOC(struct stat);
4455  *nst = st;
4456  DATA_PTR(obj) = nst;
4457 
4458  return Qnil;
4459 }
4460 
4461 /* :nodoc: */
4462 static VALUE
4464 {
4465  struct stat *nst;
4466 
4467  if (copy == orig) return orig;
4468  rb_check_frozen(copy);
4469  /* need better argument type check */
4470  if (!rb_obj_is_instance_of(orig, rb_obj_class(copy))) {
4471  rb_raise(rb_eTypeError, "wrong argument class");
4472  }
4473  if (DATA_PTR(copy)) {
4474  xfree(DATA_PTR(copy));
4475  DATA_PTR(copy) = 0;
4476  }
4477  if (DATA_PTR(orig)) {
4478  nst = ALLOC(struct stat);
4479  *nst = *(struct stat*)DATA_PTR(orig);
4480  DATA_PTR(copy) = nst;
4481  }
4482 
4483  return copy;
4484 }
4485 
4486 /*
4487  * call-seq:
4488  * stat.ftype -> string
4489  *
4490  * Identifies the type of <i>stat</i>. The return string is one of:
4491  * ``<code>file</code>'', ``<code>directory</code>'',
4492  * ``<code>characterSpecial</code>'', ``<code>blockSpecial</code>'',
4493  * ``<code>fifo</code>'', ``<code>link</code>'',
4494  * ``<code>socket</code>'', or ``<code>unknown</code>''.
4495  *
4496  * File.stat("/dev/tty").ftype #=> "characterSpecial"
4497  *
4498  */
4499 
4500 static VALUE
4502 {
4503  return rb_file_ftype(get_stat(obj));
4504 }
4505 
4506 /*
4507  * call-seq:
4508  * stat.directory? -> true or false
4509  *
4510  * Returns <code>true</code> if <i>stat</i> is a directory,
4511  * <code>false</code> otherwise.
4512  *
4513  * File.stat("testfile").directory? #=> false
4514  * File.stat(".").directory? #=> true
4515  */
4516 
4517 static VALUE
4519 {
4520  if (S_ISDIR(get_stat(obj)->st_mode)) return Qtrue;
4521  return Qfalse;
4522 }
4523 
4524 /*
4525  * call-seq:
4526  * stat.pipe? -> true or false
4527  *
4528  * Returns <code>true</code> if the operating system supports pipes and
4529  * <i>stat</i> is a pipe; <code>false</code> otherwise.
4530  */
4531 
4532 static VALUE
4534 {
4535 #ifdef S_IFIFO
4536  if (S_ISFIFO(get_stat(obj)->st_mode)) return Qtrue;
4537 
4538 #endif
4539  return Qfalse;
4540 }
4541 
4542 /*
4543  * call-seq:
4544  * stat.symlink? -> true or false
4545  *
4546  * Returns <code>true</code> if <i>stat</i> is a symbolic link,
4547  * <code>false</code> if it isn't or if the operating system doesn't
4548  * support this feature. As <code>File::stat</code> automatically
4549  * follows symbolic links, <code>symlink?</code> will always be
4550  * <code>false</code> for an object returned by
4551  * <code>File::stat</code>.
4552  *
4553  * File.symlink("testfile", "alink") #=> 0
4554  * File.stat("alink").symlink? #=> false
4555  * File.lstat("alink").symlink? #=> true
4556  *
4557  */
4558 
4559 static VALUE
4561 {
4562 #ifdef S_ISLNK
4563  if (S_ISLNK(get_stat(obj)->st_mode)) return Qtrue;
4564 #endif
4565  return Qfalse;
4566 }
4567 
4568 /*
4569  * call-seq:
4570  * stat.socket? -> true or false
4571  *
4572  * Returns <code>true</code> if <i>stat</i> is a socket,
4573  * <code>false</code> if it isn't or if the operating system doesn't
4574  * support this feature.
4575  *
4576  * File.stat("testfile").socket? #=> false
4577  *
4578  */
4579 
4580 static VALUE
4582 {
4583 #ifdef S_ISSOCK
4584  if (S_ISSOCK(get_stat(obj)->st_mode)) return Qtrue;
4585 
4586 #endif
4587  return Qfalse;
4588 }
4589 
4590 /*
4591  * call-seq:
4592  * stat.blockdev? -> true or false
4593  *
4594  * Returns <code>true</code> if the file is a block device,
4595  * <code>false</code> if it isn't or if the operating system doesn't
4596  * support this feature.
4597  *
4598  * File.stat("testfile").blockdev? #=> false
4599  * File.stat("/dev/hda1").blockdev? #=> true
4600  *
4601  */
4602 
4603 static VALUE
4605 {
4606 #ifdef S_ISBLK
4607  if (S_ISBLK(get_stat(obj)->st_mode)) return Qtrue;
4608 
4609 #endif
4610  return Qfalse;
4611 }
4612 
4613 /*
4614  * call-seq:
4615  * stat.chardev? -> true or false
4616  *
4617  * Returns <code>true</code> if the file is a character device,
4618  * <code>false</code> if it isn't or if the operating system doesn't
4619  * support this feature.
4620  *
4621  * File.stat("/dev/tty").chardev? #=> true
4622  *
4623  */
4624 
4625 static VALUE
4627 {
4628  if (S_ISCHR(get_stat(obj)->st_mode)) return Qtrue;
4629 
4630  return Qfalse;
4631 }
4632 
4633 /*
4634  * call-seq:
4635  * stat.owned? -> true or false
4636  *
4637  * Returns <code>true</code> if the effective user id of the process is
4638  * the same as the owner of <i>stat</i>.
4639  *
4640  * File.stat("testfile").owned? #=> true
4641  * File.stat("/etc/passwd").owned? #=> false
4642  *
4643  */
4644 
4645 static VALUE
4647 {
4648  if (get_stat(obj)->st_uid == geteuid()) return Qtrue;
4649  return Qfalse;
4650 }
4651 
4652 static VALUE
4654 {
4655  if (get_stat(obj)->st_uid == getuid()) return Qtrue;
4656  return Qfalse;
4657 }
4658 
4659 /*
4660  * call-seq:
4661  * stat.grpowned? -> true or false
4662  *
4663  * Returns true if the effective group id of the process is the same as
4664  * the group id of <i>stat</i>. On Windows NT, returns <code>false</code>.
4665  *
4666  * File.stat("testfile").grpowned? #=> true
4667  * File.stat("/etc/passwd").grpowned? #=> false
4668  *
4669  */
4670 
4671 static VALUE
4673 {
4674 #ifndef _WIN32
4675  if (rb_group_member(get_stat(obj)->st_gid)) return Qtrue;
4676 #endif
4677  return Qfalse;
4678 }
4679 
4680 /*
4681  * call-seq:
4682  * stat.readable? -> true or false
4683  *
4684  * Returns <code>true</code> if <i>stat</i> is readable by the
4685  * effective user id of this process.
4686  *
4687  * File.stat("testfile").readable? #=> true
4688  *
4689  */
4690 
4691 static VALUE
4693 {
4694  struct stat *st = get_stat(obj);
4695 
4696 #ifdef USE_GETEUID
4697  if (geteuid() == 0) return Qtrue;
4698 #endif
4699 #ifdef S_IRUSR
4700  if (rb_stat_owned(obj))
4701  return st->st_mode & S_IRUSR ? Qtrue : Qfalse;
4702 #endif
4703 #ifdef S_IRGRP
4704  if (rb_stat_grpowned(obj))
4705  return st->st_mode & S_IRGRP ? Qtrue : Qfalse;
4706 #endif
4707 #ifdef S_IROTH
4708  if (!(st->st_mode & S_IROTH)) return Qfalse;
4709 #endif
4710  return Qtrue;
4711 }
4712 
4713 /*
4714  * call-seq:
4715  * stat.readable_real? -> true or false
4716  *
4717  * Returns <code>true</code> if <i>stat</i> is readable by the real
4718  * user id of this process.
4719  *
4720  * File.stat("testfile").readable_real? #=> true
4721  *
4722  */
4723 
4724 static VALUE
4726 {
4727  struct stat *st = get_stat(obj);
4728 
4729 #ifdef USE_GETEUID
4730  if (getuid() == 0) return Qtrue;
4731 #endif
4732 #ifdef S_IRUSR
4733  if (rb_stat_rowned(obj))
4734  return st->st_mode & S_IRUSR ? Qtrue : Qfalse;
4735 #endif
4736 #ifdef S_IRGRP
4737  if (rb_group_member(get_stat(obj)->st_gid))
4738  return st->st_mode & S_IRGRP ? Qtrue : Qfalse;
4739 #endif
4740 #ifdef S_IROTH
4741  if (!(st->st_mode & S_IROTH)) return Qfalse;
4742 #endif
4743  return Qtrue;
4744 }
4745 
4746 /*
4747  * call-seq:
4748  * stat.world_readable? -> fixnum or nil
4749  *
4750  * If <i>stat</i> is readable by others, returns an integer
4751  * representing the file permission bits of <i>stat</i>. Returns
4752  * <code>nil</code> otherwise. The meaning of the bits is platform
4753  * dependent; on Unix systems, see <code>stat(2)</code>.
4754  *
4755  * m = File.stat("/etc/passwd").world_readable? #=> 420
4756  * sprintf("%o", m) #=> "644"
4757  */
4758 
4759 static VALUE
4761 {
4762 #ifdef S_IROTH
4763  if ((get_stat(obj)->st_mode & (S_IROTH)) == S_IROTH) {
4764  return UINT2NUM(get_stat(obj)->st_mode & (S_IRUGO|S_IWUGO|S_IXUGO));
4765  }
4766  else {
4767  return Qnil;
4768  }
4769 #endif
4770 }
4771 
4772 /*
4773  * call-seq:
4774  * stat.writable? -> true or false
4775  *
4776  * Returns <code>true</code> if <i>stat</i> is writable by the
4777  * effective user id of this process.
4778  *
4779  * File.stat("testfile").writable? #=> true
4780  *
4781  */
4782 
4783 static VALUE
4785 {
4786  struct stat *st = get_stat(obj);
4787 
4788 #ifdef USE_GETEUID
4789  if (geteuid() == 0) return Qtrue;
4790 #endif
4791 #ifdef S_IWUSR
4792  if (rb_stat_owned(obj))
4793  return st->st_mode & S_IWUSR ? Qtrue : Qfalse;
4794 #endif
4795 #ifdef S_IWGRP
4796  if (rb_stat_grpowned(obj))
4797  return st->st_mode & S_IWGRP ? Qtrue : Qfalse;
4798 #endif
4799 #ifdef S_IWOTH
4800  if (!(st->st_mode & S_IWOTH)) return Qfalse;
4801 #endif
4802  return Qtrue;
4803 }
4804 
4805 /*
4806  * call-seq:
4807  * stat.writable_real? -> true or false
4808  *
4809  * Returns <code>true</code> if <i>stat</i> is writable by the real
4810  * user id of this process.
4811  *
4812  * File.stat("testfile").writable_real? #=> true
4813  *
4814  */
4815 
4816 static VALUE
4818 {
4819  struct stat *st = get_stat(obj);
4820 
4821 #ifdef USE_GETEUID
4822  if (getuid() == 0) return Qtrue;
4823 #endif
4824 #ifdef S_IWUSR
4825  if (rb_stat_rowned(obj))
4826  return st->st_mode & S_IWUSR ? Qtrue : Qfalse;
4827 #endif
4828 #ifdef S_IWGRP
4829  if (rb_group_member(get_stat(obj)->st_gid))
4830  return st->st_mode & S_IWGRP ? Qtrue : Qfalse;
4831 #endif
4832 #ifdef S_IWOTH
4833  if (!(st->st_mode & S_IWOTH)) return Qfalse;
4834 #endif
4835  return Qtrue;
4836 }
4837 
4838 /*
4839  * call-seq:
4840  * stat.world_writable? -> fixnum or nil
4841  *
4842  * If <i>stat</i> is writable by others, returns an integer
4843  * representing the file permission bits of <i>stat</i>. Returns
4844  * <code>nil</code> otherwise. The meaning of the bits is platform
4845  * dependent; on Unix systems, see <code>stat(2)</code>.
4846  *
4847  * m = File.stat("/tmp").world_writable? #=> 511
4848  * sprintf("%o", m) #=> "777"
4849  */
4850 
4851 static VALUE
4853 {
4854 #ifdef S_IROTH
4855  if ((get_stat(obj)->st_mode & (S_IWOTH)) == S_IWOTH) {
4856  return UINT2NUM(get_stat(obj)->st_mode & (S_IRUGO|S_IWUGO|S_IXUGO));
4857  }
4858  else {
4859  return Qnil;
4860  }
4861 #endif
4862 }
4863 
4864 /*
4865  * call-seq:
4866  * stat.executable? -> true or false
4867  *
4868  * Returns <code>true</code> if <i>stat</i> is executable or if the
4869  * operating system doesn't distinguish executable files from
4870  * nonexecutable files. The tests are made using the effective owner of
4871  * the process.
4872  *
4873  * File.stat("testfile").executable? #=> false
4874  *
4875  */
4876 
4877 static VALUE
4879 {
4880  struct stat *st = get_stat(obj);
4881 
4882 #ifdef USE_GETEUID
4883  if (geteuid() == 0) {
4884  return st->st_mode & S_IXUGO ? Qtrue : Qfalse;
4885  }
4886 #endif
4887 #ifdef S_IXUSR
4888  if (rb_stat_owned(obj))
4889  return st->st_mode & S_IXUSR ? Qtrue : Qfalse;
4890 #endif
4891 #ifdef S_IXGRP
4892  if (rb_stat_grpowned(obj))
4893  return st->st_mode & S_IXGRP ? Qtrue : Qfalse;
4894 #endif
4895 #ifdef S_IXOTH
4896  if (!(st->st_mode & S_IXOTH)) return Qfalse;
4897 #endif
4898  return Qtrue;
4899 }
4900 
4901 /*
4902  * call-seq:
4903  * stat.executable_real? -> true or false
4904  *
4905  * Same as <code>executable?</code>, but tests using the real owner of
4906  * the process.
4907  */
4908 
4909 static VALUE
4911 {
4912  struct stat *st = get_stat(obj);
4913 
4914 #ifdef USE_GETEUID
4915  if (getuid() == 0) {
4916  return st->st_mode & S_IXUGO ? Qtrue : Qfalse;
4917  }
4918 #endif
4919 #ifdef S_IXUSR
4920  if (rb_stat_rowned(obj))
4921  return st->st_mode & S_IXUSR ? Qtrue : Qfalse;
4922 #endif
4923 #ifdef S_IXGRP
4924  if (rb_group_member(get_stat(obj)->st_gid))
4925  return st->st_mode & S_IXGRP ? Qtrue : Qfalse;
4926 #endif
4927 #ifdef S_IXOTH
4928  if (!(st->st_mode & S_IXOTH)) return Qfalse;
4929 #endif
4930  return Qtrue;
4931 }
4932 
4933 /*
4934  * call-seq:
4935  * stat.file? -> true or false
4936  *
4937  * Returns <code>true</code> if <i>stat</i> is a regular file (not
4938  * a device file, pipe, socket, etc.).
4939  *
4940  * File.stat("testfile").file? #=> true
4941  *
4942  */
4943 
4944 static VALUE
4946 {
4947  if (S_ISREG(get_stat(obj)->st_mode)) return Qtrue;
4948  return Qfalse;
4949 }
4950 
4951 /*
4952  * call-seq:
4953  * stat.zero? -> true or false
4954  *
4955  * Returns <code>true</code> if <i>stat</i> is a zero-length file;
4956  * <code>false</code> otherwise.
4957  *
4958  * File.stat("testfile").zero? #=> false
4959  *
4960  */
4961 
4962 static VALUE
4964 {
4965  if (get_stat(obj)->st_size == 0) return Qtrue;
4966  return Qfalse;
4967 }
4968 
4969 /*
4970  * call-seq:
4971  * state.size -> integer
4972  *
4973  * Returns the size of <i>stat</i> in bytes.
4974  *
4975  * File.stat("testfile").size #=> 66
4976  *
4977  */
4978 
4979 static VALUE
4981 {
4982  off_t size = get_stat(obj)->st_size;
4983 
4984  if (size == 0) return Qnil;
4985  return OFFT2NUM(size);
4986 }
4987 
4988 /*
4989  * call-seq:
4990  * stat.setuid? -> true or false
4991  *
4992  * Returns <code>true</code> if <i>stat</i> has the set-user-id
4993  * permission bit set, <code>false</code> if it doesn't or if the
4994  * operating system doesn't support this feature.
4995  *
4996  * File.stat("/bin/su").setuid? #=> true
4997  */
4998 
4999 static VALUE
5001 {
5002 #ifdef S_ISUID
5003  if (get_stat(obj)->st_mode & S_ISUID) return Qtrue;
5004 #endif
5005  return Qfalse;
5006 }
5007 
5008 /*
5009  * call-seq:
5010  * stat.setgid? -> true or false
5011  *
5012  * Returns <code>true</code> if <i>stat</i> has the set-group-id
5013  * permission bit set, <code>false</code> if it doesn't or if the
5014  * operating system doesn't support this feature.
5015  *
5016  * File.stat("/usr/sbin/lpc").setgid? #=> true
5017  *
5018  */
5019 
5020 static VALUE
5022 {
5023 #ifdef S_ISGID
5024  if (get_stat(obj)->st_mode & S_ISGID) return Qtrue;
5025 #endif
5026  return Qfalse;
5027 }
5028 
5029 /*
5030  * call-seq:
5031  * stat.sticky? -> true or false
5032  *
5033  * Returns <code>true</code> if <i>stat</i> has its sticky bit set,
5034  * <code>false</code> if it doesn't or if the operating system doesn't
5035  * support this feature.
5036  *
5037  * File.stat("testfile").sticky? #=> false
5038  *
5039  */
5040 
5041 static VALUE
5043 {
5044 #ifdef S_ISVTX
5045  if (get_stat(obj)->st_mode & S_ISVTX) return Qtrue;
5046 #endif
5047  return Qfalse;
5048 }
5049 
5051 
5052 void
5053 rb_file_const(const char *name, VALUE value)
5054 {
5055  rb_define_const(rb_mFConst, name, value);
5056 }
5057 
5058 int
5059 rb_is_absolute_path(const char *path)
5060 {
5061 #ifdef DOSISH_DRIVE_LETTER
5062  if (has_drive_letter(path) && isdirsep(path[2])) return 1;
5063 #endif
5064 #ifdef DOSISH_UNC
5065  if (isdirsep(path[0]) && isdirsep(path[1])) return 1;
5066 #endif
5067 #ifndef DOSISH
5068  if (path[0] == '/') return 1;
5069 #endif
5070  return 0;
5071 }
5072 
5073 #ifndef ENABLE_PATH_CHECK
5074 # if defined DOSISH || defined __CYGWIN__
5075 # define ENABLE_PATH_CHECK 0
5076 # else
5077 # define ENABLE_PATH_CHECK 1
5078 # endif
5079 #endif
5080 
5081 #if ENABLE_PATH_CHECK
5082 static int
5083 path_check_0(VALUE path, int execpath)
5084 {
5085  struct stat st;
5086  const char *p0 = StringValueCStr(path);
5087  const char *e0;
5088  rb_encoding *enc;
5089  char *p = 0, *s;
5090 
5091  if (!rb_is_absolute_path(p0)) {
5092  char *buf = my_getcwd();
5093  VALUE newpath;
5094 
5095  newpath = rb_str_new2(buf);
5096  xfree(buf);
5097 
5098  rb_str_cat2(newpath, "/");
5099  rb_str_cat2(newpath, p0);
5100  path = newpath;
5101  p0 = RSTRING_PTR(path);
5102  }
5103  e0 = p0 + RSTRING_LEN(path);
5104  enc = rb_enc_get(path);
5105  for (;;) {
5106 #ifndef S_IWOTH
5107 # define S_IWOTH 002
5108 #endif
5109  if (STAT(p0, &st) == 0 && S_ISDIR(st.st_mode) && (st.st_mode & S_IWOTH)
5110 #ifdef S_ISVTX
5111  && !(p && execpath && (st.st_mode & S_ISVTX))
5112 #endif
5113  && !access(p0, W_OK)) {
5114  rb_warn("Insecure world writable dir %s in %sPATH, mode 0%"
5115  PRI_MODET_PREFIX"o",
5116  p0, (execpath ? "" : "LOAD_"), st.st_mode);
5117  if (p) *p = '/';
5118  RB_GC_GUARD(path);
5119  return 0;
5120  }
5121  s = strrdirsep(p0, e0, enc);
5122  if (p) *p = '/';
5123  if (!s || s == p0) return 1;
5124  p = s;
5125  e0 = p;
5126  *p = '\0';
5127  }
5128 }
5129 #endif
5130 
5131 #if ENABLE_PATH_CHECK
5132 #define fpath_check(path) path_check_0((path), FALSE)
5133 #else
5134 #define fpath_check(path) 1
5135 #endif
5136 
5137 int
5138 rb_path_check(const char *path)
5139 {
5140 #if ENABLE_PATH_CHECK
5141  const char *p0, *p, *pend;
5142  const char sep = PATH_SEP_CHAR;
5143 
5144  if (!path) return 1;
5145 
5146  pend = path + strlen(path);
5147  p0 = path;
5148  p = strchr(path, sep);
5149  if (!p) p = pend;
5150 
5151  for (;;) {
5152  if (!path_check_0(rb_str_new(p0, p - p0), TRUE)) {
5153  return 0; /* not safe */
5154  }
5155  p0 = p + 1;
5156  if (p0 > pend) break;
5157  p = strchr(p0, sep);
5158  if (!p) p = pend;
5159  }
5160 #endif
5161  return 1;
5162 }
5163 
5164 #ifndef _WIN32
5165 int
5166 rb_file_load_ok(const char *path)
5167 {
5168  int ret = 1;
5169  int fd = open(path, O_RDONLY);
5170  if (fd == -1) return 0;
5171  rb_update_max_fd(fd);
5172 #if !defined DOSISH
5173  {
5174  struct stat st;
5175  if (fstat(fd, &st) || !S_ISREG(st.st_mode)) {
5176  ret = 0;
5177  }
5178  }
5179 #endif
5180  (void)close(fd);
5181  return ret;
5182 }
5183 #endif
5184 
5185 static int
5186 is_explicit_relative(const char *path)
5187 {
5188  if (*path++ != '.') return 0;
5189  if (*path == '.') path++;
5190  return isdirsep(*path);
5191 }
5192 
5193 static VALUE
5195 {
5196  RBASIC(path)->klass = rb_obj_class(orig);
5197  OBJ_FREEZE(path);
5198  return path;
5199 }
5200 
5201 int
5202 rb_find_file_ext(VALUE *filep, const char *const *ext)
5203 {
5204  return rb_find_file_ext_safe(filep, ext, rb_safe_level());
5205 }
5206 
5207 int
5208 rb_find_file_ext_safe(VALUE *filep, const char *const *ext, int safe_level)
5209 {
5210  const char *f = StringValueCStr(*filep);
5211  VALUE fname = *filep, load_path, tmp;
5212  long i, j, fnlen;
5213  int expanded = 0;
5214 
5215  if (!ext[0]) return 0;
5216 
5217  if (f[0] == '~') {
5218  fname = file_expand_path_1(fname);
5219  if (safe_level >= 1 && OBJ_TAINTED(fname)) {
5220  rb_raise(rb_eSecurityError, "loading from unsafe file %s", f);
5221  }
5222  f = RSTRING_PTR(fname);
5223  *filep = fname;
5224  expanded = 1;
5225  }
5226 
5227  if (expanded || rb_is_absolute_path(f) || is_explicit_relative(f)) {
5228  if (safe_level >= 1 && !fpath_check(fname)) {
5229  rb_raise(rb_eSecurityError, "loading from unsafe path %s", f);
5230  }
5231  if (!expanded) fname = file_expand_path_1(fname);
5232  fnlen = RSTRING_LEN(fname);
5233  for (i=0; ext[i]; i++) {
5234  rb_str_cat2(fname, ext[i]);
5235  if (rb_file_load_ok(RSTRING_PTR(fname))) {
5236  *filep = copy_path_class(fname, *filep);
5237  return (int)(i+1);
5238  }
5239  rb_str_set_len(fname, fnlen);
5240  }
5241  return 0;
5242  }
5243 
5244  if (safe_level >= 4) {
5245  rb_raise(rb_eSecurityError, "loading from non-absolute path %s", f);
5246  }
5247 
5248  RB_GC_GUARD(load_path) = rb_get_load_path();
5249  if (!load_path) return 0;
5250 
5251  fname = rb_str_dup(*filep);
5252  RBASIC(fname)->klass = 0;
5253  fnlen = RSTRING_LEN(fname);
5254  tmp = rb_str_tmp_new(MAXPATHLEN + 2);
5256  for (j=0; ext[j]; j++) {
5257  rb_str_cat2(fname, ext[j]);
5258  for (i = 0; i < RARRAY_LEN(load_path); i++) {
5259  VALUE str = RARRAY_PTR(load_path)[i];
5260 
5261  RB_GC_GUARD(str) = rb_get_path_check(str, safe_level);
5262  if (RSTRING_LEN(str) == 0) continue;
5263  rb_file_expand_path_internal(fname, str, 0, 0, tmp);
5264  if (rb_file_load_ok(RSTRING_PTR(tmp))) {
5265  *filep = copy_path_class(tmp, *filep);
5266  return (int)(j+1);
5267  }
5268  FL_UNSET(tmp, FL_TAINT | FL_UNTRUSTED);
5269  }
5270  rb_str_set_len(fname, fnlen);
5271  }
5272  RB_GC_GUARD(load_path);
5273  return 0;
5274 }
5275 
5276 VALUE
5278 {
5279  return rb_find_file_safe(path, rb_safe_level());
5280 }
5281 
5282 VALUE
5283 rb_find_file_safe(VALUE path, int safe_level)
5284 {
5285  VALUE tmp, load_path;
5286  const char *f = StringValueCStr(path);
5287  int expanded = 0;
5288 
5289  if (f[0] == '~') {
5290  tmp = file_expand_path_1(path);
5291  if (safe_level >= 1 && OBJ_TAINTED(tmp)) {
5292  rb_raise(rb_eSecurityError, "loading from unsafe file %s", f);
5293  }
5294  path = copy_path_class(tmp, path);
5295  f = RSTRING_PTR(path);
5296  expanded = 1;
5297  }
5298 
5299  if (expanded || rb_is_absolute_path(f) || is_explicit_relative(f)) {
5300  if (safe_level >= 1 && !fpath_check(path)) {
5301  rb_raise(rb_eSecurityError, "loading from unsafe path %s", f);
5302  }
5303  if (!rb_file_load_ok(f)) return 0;
5304  if (!expanded)
5305  path = copy_path_class(file_expand_path_1(path), path);
5306  return path;
5307  }
5308 
5309  if (safe_level >= 4) {
5310  rb_raise(rb_eSecurityError, "loading from non-absolute path %s", f);
5311  }
5312 
5313  RB_GC_GUARD(load_path) = rb_get_load_path();
5314  if (load_path) {
5315  long i;
5316 
5317  tmp = rb_str_tmp_new(MAXPATHLEN + 2);
5319  for (i = 0; i < RARRAY_LEN(load_path); i++) {
5320  VALUE str = RARRAY_PTR(load_path)[i];
5321  RB_GC_GUARD(str) = rb_get_path_check(str, safe_level);
5322  if (RSTRING_LEN(str) > 0) {
5323  rb_file_expand_path_internal(path, str, 0, 0, tmp);
5324  f = RSTRING_PTR(tmp);
5325  if (rb_file_load_ok(f)) goto found;
5326  }
5327  }
5328  return 0;
5329  }
5330  else {
5331  return 0; /* no path, no load */
5332  }
5333 
5334  found:
5335  if (safe_level >= 1 && !fpath_check(tmp)) {
5336  rb_raise(rb_eSecurityError, "loading from unsafe file %s", f);
5337  }
5338 
5339  return copy_path_class(tmp, path);
5340 }
5341 
5342 static void
5343 define_filetest_function(const char *name, VALUE (*func)(ANYARGS), int argc)
5344 {
5345  rb_define_module_function(rb_mFileTest, name, func, argc);
5346  rb_define_singleton_method(rb_cFile, name, func, argc);
5347 }
5348 
5349 static const char null_device[] =
5350 #if defined DOSISH
5351  "NUL"
5352 #elif defined AMIGA || defined __amigaos__
5353  "NIL"
5354 #elif defined __VMS
5355  "NL:"
5356 #else
5357  "/dev/null"
5358 #endif
5359  ;
5360 
5361 /*
5362  * A <code>File</code> is an abstraction of any file object accessible
5363  * by the program and is closely associated with class <code>IO</code>
5364  * <code>File</code> includes the methods of module
5365  * <code>FileTest</code> as class methods, allowing you to write (for
5366  * example) <code>File.exist?("foo")</code>.
5367  *
5368  * In the description of File methods,
5369  * <em>permission bits</em> are a platform-specific
5370  * set of bits that indicate permissions of a file. On Unix-based
5371  * systems, permissions are viewed as a set of three octets, for the
5372  * owner, the group, and the rest of the world. For each of these
5373  * entities, permissions may be set to read, write, or execute the
5374  * file:
5375  *
5376  * The permission bits <code>0644</code> (in octal) would thus be
5377  * interpreted as read/write for owner, and read-only for group and
5378  * other. Higher-order bits may also be used to indicate the type of
5379  * file (plain, directory, pipe, socket, and so on) and various other
5380  * special features. If the permissions are for a directory, the
5381  * meaning of the execute bit changes; when set the directory can be
5382  * searched.
5383  *
5384  * On non-Posix operating systems, there may be only the ability to
5385  * make a file read-only or read-write. In this case, the remaining
5386  * permission bits will be synthesized to resemble typical values. For
5387  * instance, on Windows NT the default permission bits are
5388  * <code>0644</code>, which means read/write for owner, read-only for
5389  * all others. The only change that can be made is to make the file
5390  * read-only, which is reported as <code>0444</code>.
5391  */
5392 
5393 void
5395 {
5396  rb_mFileTest = rb_define_module("FileTest");
5397  rb_cFile = rb_define_class("File", rb_cIO);
5398 
5403  define_filetest_function("readable_real?", rb_file_readable_real_p, 1);
5404  define_filetest_function("world_readable?", rb_file_world_readable_p, 1);
5406  define_filetest_function("writable_real?", rb_file_writable_real_p, 1);
5407  define_filetest_function("world_writable?", rb_file_world_writable_p, 1);
5409  define_filetest_function("executable_real?", rb_file_executable_real_p, 1);
5416 
5420 
5423 
5427 
5429 
5430  rb_define_singleton_method(rb_cFile, "stat", rb_file_s_stat, 1);
5431  rb_define_singleton_method(rb_cFile, "lstat", rb_file_s_lstat, 1);
5432  rb_define_singleton_method(rb_cFile, "ftype", rb_file_s_ftype, 1);
5433 
5434  rb_define_singleton_method(rb_cFile, "atime", rb_file_s_atime, 1);
5435  rb_define_singleton_method(rb_cFile, "mtime", rb_file_s_mtime, 1);
5436  rb_define_singleton_method(rb_cFile, "ctime", rb_file_s_ctime, 1);
5437 
5438  rb_define_singleton_method(rb_cFile, "utime", rb_file_s_utime, -1);
5439  rb_define_singleton_method(rb_cFile, "chmod", rb_file_s_chmod, -1);
5440  rb_define_singleton_method(rb_cFile, "chown", rb_file_s_chown, -1);
5441  rb_define_singleton_method(rb_cFile, "lchmod", rb_file_s_lchmod, -1);
5442  rb_define_singleton_method(rb_cFile, "lchown", rb_file_s_lchown, -1);
5443 
5444  rb_define_singleton_method(rb_cFile, "link", rb_file_s_link, 2);
5445  rb_define_singleton_method(rb_cFile, "symlink", rb_file_s_symlink, 2);
5446  rb_define_singleton_method(rb_cFile, "readlink", rb_file_s_readlink, 1);
5447 
5448  rb_define_singleton_method(rb_cFile, "unlink", rb_file_s_unlink, -2);
5449  rb_define_singleton_method(rb_cFile, "delete", rb_file_s_unlink, -2);
5450  rb_define_singleton_method(rb_cFile, "rename", rb_file_s_rename, 2);
5451  rb_define_singleton_method(rb_cFile, "umask", rb_file_s_umask, -1);
5452  rb_define_singleton_method(rb_cFile, "truncate", rb_file_s_truncate, 2);
5453  rb_define_singleton_method(rb_cFile, "expand_path", rb_file_s_expand_path, -1);
5454  rb_define_singleton_method(rb_cFile, "absolute_path", rb_file_s_absolute_path, -1);
5455  rb_define_singleton_method(rb_cFile, "realpath", rb_file_s_realpath, -1);
5456  rb_define_singleton_method(rb_cFile, "realdirpath", rb_file_s_realdirpath, -1);
5457  rb_define_singleton_method(rb_cFile, "basename", rb_file_s_basename, -1);
5458  rb_define_singleton_method(rb_cFile, "dirname", rb_file_s_dirname, 1);
5459  rb_define_singleton_method(rb_cFile, "extname", rb_file_s_extname, 1);
5460  rb_define_singleton_method(rb_cFile, "path", rb_file_s_path, 1);
5461 
5462  separator = rb_obj_freeze(rb_usascii_str_new2("/"));
5463  rb_define_const(rb_cFile, "Separator", separator);
5464  rb_define_const(rb_cFile, "SEPARATOR", separator);
5465  rb_define_singleton_method(rb_cFile, "split", rb_file_s_split, 1);
5466  rb_define_singleton_method(rb_cFile, "join", rb_file_s_join, -2);
5467 
5468 #ifdef DOSISH
5469  rb_define_const(rb_cFile, "ALT_SEPARATOR", rb_obj_freeze(rb_usascii_str_new2(file_alt_separator)));
5470 #else
5471  rb_define_const(rb_cFile, "ALT_SEPARATOR", Qnil);
5472 #endif
5473  rb_define_const(rb_cFile, "PATH_SEPARATOR", rb_obj_freeze(rb_str_new2(PATH_SEP)));
5474 
5475  rb_define_method(rb_cIO, "stat", rb_io_stat, 0); /* this is IO's method */
5476  rb_define_method(rb_cFile, "lstat", rb_file_lstat, 0);
5477 
5478  rb_define_method(rb_cFile, "atime", rb_file_atime, 0);
5479  rb_define_method(rb_cFile, "mtime", rb_file_mtime, 0);
5480  rb_define_method(rb_cFile, "ctime", rb_file_ctime, 0);
5481  rb_define_method(rb_cFile, "size", rb_file_size, 0);
5482 
5483  rb_define_method(rb_cFile, "chmod", rb_file_chmod, 1);
5484  rb_define_method(rb_cFile, "chown", rb_file_chown, 2);
5485  rb_define_method(rb_cFile, "truncate", rb_file_truncate, 1);
5486 
5487  rb_define_method(rb_cFile, "flock", rb_file_flock, 1);
5488 
5489  rb_mFConst = rb_define_module_under(rb_cFile, "Constants");
5490  rb_include_module(rb_cIO, rb_mFConst);
5491  rb_file_const("LOCK_SH", INT2FIX(LOCK_SH));
5492  rb_file_const("LOCK_EX", INT2FIX(LOCK_EX));
5493  rb_file_const("LOCK_UN", INT2FIX(LOCK_UN));
5494  rb_file_const("LOCK_NB", INT2FIX(LOCK_NB));
5495 
5496  rb_file_const("NULL", rb_obj_freeze(rb_usascii_str_new2(null_device)));
5497 
5498  rb_define_method(rb_cFile, "path", rb_file_path, 0);
5499  rb_define_method(rb_cFile, "to_path", rb_file_path, 0);
5500  rb_define_global_function("test", rb_f_test, -1);
5501 
5502  rb_cStat = rb_define_class_under(rb_cFile, "Stat", rb_cObject);
5504  rb_define_method(rb_cStat, "initialize", rb_stat_init, 1);
5505  rb_define_method(rb_cStat, "initialize_copy", rb_stat_init_copy, 1);
5506 
5507  rb_include_module(rb_cStat, rb_mComparable);
5508 
5509  rb_define_method(rb_cStat, "<=>", rb_stat_cmp, 1);
5510 
5511  rb_define_method(rb_cStat, "dev", rb_stat_dev, 0);
5512  rb_define_method(rb_cStat, "dev_major", rb_stat_dev_major, 0);
5513  rb_define_method(rb_cStat, "dev_minor", rb_stat_dev_minor, 0);
5514  rb_define_method(rb_cStat, "ino", rb_stat_ino, 0);
5515  rb_define_method(rb_cStat, "mode", rb_stat_mode, 0);
5516  rb_define_method(rb_cStat, "nlink", rb_stat_nlink, 0);
5517  rb_define_method(rb_cStat, "uid", rb_stat_uid, 0);
5518  rb_define_method(rb_cStat, "gid", rb_stat_gid, 0);
5519  rb_define_method(rb_cStat, "rdev", rb_stat_rdev, 0);
5520  rb_define_method(rb_cStat, "rdev_major", rb_stat_rdev_major, 0);
5521  rb_define_method(rb_cStat, "rdev_minor", rb_stat_rdev_minor, 0);
5522  rb_define_method(rb_cStat, "size", rb_stat_size, 0);
5523  rb_define_method(rb_cStat, "blksize", rb_stat_blksize, 0);
5524  rb_define_method(rb_cStat, "blocks", rb_stat_blocks, 0);
5525  rb_define_method(rb_cStat, "atime", rb_stat_atime, 0);
5526  rb_define_method(rb_cStat, "mtime", rb_stat_mtime, 0);
5527  rb_define_method(rb_cStat, "ctime", rb_stat_ctime, 0);
5528 
5529  rb_define_method(rb_cStat, "inspect", rb_stat_inspect, 0);
5530 
5531  rb_define_method(rb_cStat, "ftype", rb_stat_ftype, 0);
5532 
5533  rb_define_method(rb_cStat, "directory?", rb_stat_d, 0);
5534  rb_define_method(rb_cStat, "readable?", rb_stat_r, 0);
5535  rb_define_method(rb_cStat, "readable_real?", rb_stat_R, 0);
5536  rb_define_method(rb_cStat, "world_readable?", rb_stat_wr, 0);
5537  rb_define_method(rb_cStat, "writable?", rb_stat_w, 0);
5538  rb_define_method(rb_cStat, "writable_real?", rb_stat_W, 0);
5539  rb_define_method(rb_cStat, "world_writable?", rb_stat_ww, 0);
5540  rb_define_method(rb_cStat, "executable?", rb_stat_x, 0);
5541  rb_define_method(rb_cStat, "executable_real?", rb_stat_X, 0);
5542  rb_define_method(rb_cStat, "file?", rb_stat_f, 0);
5543  rb_define_method(rb_cStat, "zero?", rb_stat_z, 0);
5544  rb_define_method(rb_cStat, "size?", rb_stat_s, 0);
5545  rb_define_method(rb_cStat, "owned?", rb_stat_owned, 0);
5546  rb_define_method(rb_cStat, "grpowned?", rb_stat_grpowned, 0);
5547 
5548  rb_define_method(rb_cStat, "pipe?", rb_stat_p, 0);
5549  rb_define_method(rb_cStat, "symlink?", rb_stat_l, 0);
5550  rb_define_method(rb_cStat, "socket?", rb_stat_S, 0);
5551 
5552  rb_define_method(rb_cStat, "blockdev?", rb_stat_b, 0);
5553  rb_define_method(rb_cStat, "chardev?", rb_stat_c, 0);
5554 
5555  rb_define_method(rb_cStat, "setuid?", rb_stat_suid, 0);
5556  rb_define_method(rb_cStat, "setgid?", rb_stat_sgid, 0);
5557  rb_define_method(rb_cStat, "sticky?", rb_stat_sticky, 0);
5558 
5559 #ifdef _WIN32
5560  rb_w32_init_file();
5561 #endif
5562 }
5563