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