file.c


DEFINITIONS

This source file includes following functions.
  1. apply2files
  2. rb_file_path
  3. stat_new_0
  4. stat_new
  5. get_stat
  6. rb_stat_cmp
  7. rb_stat_dev
  8. rb_stat_ino
  9. rb_stat_mode
  10. rb_stat_nlink
  11. rb_stat_uid
  12. rb_stat_gid
  13. rb_stat_rdev
  14. rb_stat_rdev_major
  15. rb_stat_rdev_minor
  16. rb_stat_size
  17. rb_stat_blksize
  18. rb_stat_blocks
  19. rb_stat_atime
  20. rb_stat_mtime
  21. rb_stat_ctime
  22. rb_stat_inspect
  23. rb_stat
  24. rb_file_s_stat
  25. rb_io_stat
  26. rb_file_s_lstat
  27. rb_file_lstat
  28. group_member
  29. eaccess
  30. test_d
  31. S_ISDIR
  32. test_p
  33. S_ISFIFO
  34. test_l
  35. S_ISLNK
  36. S_ISLNK
  37. S_ISLNK
  38. S_ISLNK
  39. S_ISLNK
  40. test_S
  41. S_ISSOCK
  42. S_ISSOCK
  43. S_ISSOCK
  44. S_ISSOCK
  45. S_ISSOCK
  46. test_b
  47. S_ISBLK
  48. S_ISBLK
  49. test_c
  50. S_ISCHR
  51. test_e
  52. test_r
  53. test_R
  54. test_w
  55. test_W
  56. test_x
  57. test_X
  58. S_ISREG
  59. test_f
  60. test_z
  61. test_s
  62. test_owned
  63. test_rowned
  64. test_grpowned
  65. check3rdbyte
  66. test_suid
  67. test_sgid
  68. test_sticky
  69. rb_file_s_size
  70. rb_file_ftype
  71. rb_file_s_ftype
  72. rb_file_s_atime
  73. rb_file_atime
  74. rb_file_s_mtime
  75. rb_file_mtime
  76. rb_file_s_ctime
  77. rb_file_ctime
  78. chmod_internal
  79. rb_file_s_chmod
  80. rb_file_chmod
  81. lchmod_internal
  82. rb_file_s_lchmod
  83. rb_file_s_lchmod
  84. chown_internal
  85. rb_file_s_chown
  86. rb_file_chown
  87. lchown_internal
  88. rb_file_s_lchown
  89. rb_file_s_lchown
  90. utime_internal
  91. rb_file_s_utime
  92. utime_internal
  93. rb_file_s_utime
  94. rb_file_s_link
  95. rb_file_s_symlink
  96. rb_file_s_readlink
  97. unlink_internal
  98. rb_file_s_unlink
  99. rb_file_s_rename
  100. rb_file_s_umask
  101. CharNext
  102. CharNext
  103. strrdirsep
  104. rb_file_s_expand_path
  105. rmext
  106. rb_file_s_basename
  107. rb_file_s_dirname
  108. rb_file_s_extname
  109. rb_file_s_split
  110. rb_file_s_join
  111. rb_file_s_truncate
  112. rb_file_truncate
  113. rb_thread_flock
  114. rb_file_flock
  115. test_check
  116. rb_f_test
  117. rb_stat_s_alloc
  118. rb_stat_init
  119. rb_stat_become
  120. rb_stat_ftype
  121. rb_stat_d
  122. rb_stat_p
  123. rb_stat_l
  124. rb_stat_S
  125. rb_stat_b
  126. rb_stat_c
  127. rb_stat_owned
  128. rb_stat_rowned
  129. rb_stat_grpowned
  130. rb_stat_r
  131. rb_stat_R
  132. rb_stat_w
  133. rb_stat_W
  134. rb_stat_x
  135. rb_stat_X
  136. rb_stat_f
  137. rb_stat_z
  138. rb_stat_s
  139. rb_stat_suid
  140. rb_stat_sgid
  141. rb_stat_sticky
  142. rb_file_const
  143. is_absolute_path
  144. path_check_1
  145. rb_path_check
  146. is_macos_native_path
  147. file_load_ok
  148. rb_find_file_ext
  149. rb_find_file
  150. define_filetest_function
  151. Init_File


   1  /**********************************************************************
   2  
   3    file.c -
   4  
   5    $Author: matz $
   6    $Date: 2002/09/06 08:59:38 $
   7    created at: Mon Nov 15 12:24:34 JST 1993
   8  
   9    Copyright (C) 1993-2002 Yukihiro Matsumoto
  10    Copyright (C) 2000  Network Applied Communication Laboratory, Inc.
  11    Copyright (C) 2000  Information-technology Promotion Agency, Japan
  12  
  13  **********************************************************************/
  14  
  15  #ifdef NT
  16  #include "missing/file.h"
  17  #endif
  18  
  19  #include "ruby.h"
  20  #include "rubyio.h"
  21  #include "rubysig.h"
  22  #include "util.h"
  23  #include "dln.h"
  24  
  25  #ifdef HAVE_UNISTD_H
  26  #include <unistd.h>
  27  #endif
  28  
  29  #ifdef HAVE_SYS_FILE_H
  30  # include <sys/file.h>
  31  #else
  32  int flock _((int, int));
  33  #endif
  34  
  35  #ifdef HAVE_SYS_PARAM_H
  36  # include <sys/param.h>
  37  #else
  38  # define MAXPATHLEN 1024
  39  #endif
  40  
  41  #include <time.h>
  42  #ifdef HAVE_SYS_TIME_H
  43  # include <sys/time.h>
  44  #else
  45  #ifndef NT
  46  struct timeval {
  47          long    tv_sec;         /* seconds */
  48          long    tv_usec;        /* and microseconds */
  49  };
  50  #endif /* NT */
  51  #endif
  52  
  53  VALUE rb_time_new _((time_t, time_t));
  54  
  55  #ifdef HAVE_UTIME_H
  56  #include <utime.h>
  57  #endif
  58  
  59  #ifdef HAVE_PWD_H
  60  #include <pwd.h>
  61  #endif
  62  
  63  #ifndef HAVE_STRING_H
  64  char *strrchr _((const char*,const char));
  65  #endif
  66  
  67  #include <sys/types.h>
  68  #include <sys/stat.h>
  69  
  70  #ifdef HAVE_SYS_MKDEV_H
  71  #include <sys/mkdev.h>
  72  #endif
  73  
  74  #ifndef HAVE_LSTAT
  75  #define lstat(path,st) stat(path,st)
  76  #endif
  77  
  78  VALUE rb_cFile;
  79  VALUE rb_mFileTest;
  80  static VALUE rb_cStat;
  81  
  82  static long
  83  apply2files(func, vargs, arg)
  84      void (*func)();
  85      VALUE vargs;
  86      void *arg;
  87  {
  88      long i;
  89      VALUE path;
  90      struct RArray *args = RARRAY(vargs);
  91  
  92      for (i=0; i<args->len; i++) {
  93          path = args->ptr[i];
  94          SafeStringValue(path);
  95          (*func)(RSTRING(path)->ptr, arg);
  96      }
  97  
  98      return args->len;
  99  }
 100  
 101  static VALUE
 102  rb_file_path(obj)
 103      VALUE obj;
 104  {
 105      OpenFile *fptr;
 106  
 107      GetOpenFile(obj, fptr);
 108      if (!fptr->path) return Qnil;
 109      return rb_str_new2(fptr->path);
 110  }
 111  
 112  #ifdef NT
 113  #include "missing/file.h"
 114  #endif
 115  
 116  static VALUE
 117  stat_new_0(klass, st)
 118      VALUE klass;
 119      struct stat *st;
 120  {
 121      struct stat *nst = 0;
 122  
 123      if (st) {
 124          nst = ALLOC(struct stat);
 125          *nst = *st;
 126      }
 127      return Data_Wrap_Struct(klass, NULL, free, nst);
 128  }
 129  
 130  static VALUE
 131  stat_new(st)
 132      struct stat *st;
 133  {
 134      return stat_new_0(rb_cStat, st);
 135  }
 136  
 137  static struct stat*
 138  get_stat(self)
 139      VALUE self;
 140  {
 141      struct stat* st;
 142      Data_Get_Struct(self, struct stat, st);
 143      if (!st) rb_raise(rb_eTypeError, "uninitialized File::Stat");
 144      return st;
 145  }
 146  
 147  static VALUE
 148  rb_stat_cmp(self, other)
 149      VALUE self, other;
 150  {
 151      if (rb_obj_is_kind_of(other, rb_obj_class(self))) {
 152          time_t t1 = get_stat(self)->st_mtime;
 153          time_t t2 = get_stat(other)->st_mtime;
 154          if (t1 == t2)
 155              return INT2FIX(0);
 156          else if (t1 < t2)
 157              return INT2FIX(-1);
 158          else
 159              return INT2FIX(1);
 160      }
 161      rb_raise(rb_eTypeError, "operand is not File::Stat");
 162  }
 163  
 164  static VALUE
 165  rb_stat_dev(self)
 166      VALUE self;
 167  {
 168      return INT2NUM(get_stat(self)->st_dev);
 169  }
 170  
 171  static VALUE
 172  rb_stat_ino(self)
 173      VALUE self;
 174  {
 175      return ULONG2NUM(get_stat(self)->st_ino);
 176  }
 177  
 178  static VALUE
 179  rb_stat_mode(self)
 180      VALUE self;
 181  {
 182  #ifdef __BORLANDC__
 183      return UINT2NUM((unsigned short)(get_stat(self)->st_mode));
 184  #else
 185       return UINT2NUM(get_stat(self)->st_mode);
 186  #endif
 187  }
 188  
 189  static VALUE
 190  rb_stat_nlink(self)
 191      VALUE self;
 192  {
 193      return UINT2NUM(get_stat(self)->st_nlink);
 194  }
 195  
 196  static VALUE
 197  rb_stat_uid(self)
 198      VALUE self;
 199  {
 200      return UINT2NUM(get_stat(self)->st_uid);
 201  }
 202  
 203  static VALUE
 204  rb_stat_gid(self)
 205      VALUE self;
 206  {
 207      return UINT2NUM(get_stat(self)->st_gid);
 208  }
 209  
 210  static VALUE
 211  rb_stat_rdev(self)
 212      VALUE self;
 213  {
 214  #ifdef HAVE_ST_RDEV
 215      return ULONG2NUM(get_stat(self)->st_rdev);
 216  #else
 217      return Qnil;
 218  #endif
 219  }
 220  
 221  static VALUE
 222  rb_stat_rdev_major(self)
 223      VALUE self;
 224  {
 225  #if defined(HAVE_ST_RDEV) && defined(major)
 226      long rdev = get_stat(self)->st_rdev;
 227      return ULONG2NUM(major(rdev));
 228  #else
 229      return Qnil;
 230  #endif
 231  }
 232  
 233  static VALUE
 234  rb_stat_rdev_minor(self)
 235      VALUE self;
 236  {
 237  #if defined(HAVE_ST_RDEV) && defined(minor)
 238      long rdev = get_stat(self)->st_rdev;
 239      return ULONG2NUM(minor(rdev));
 240  #else
 241      return Qnil;
 242  #endif
 243  }
 244  
 245  static VALUE
 246  rb_stat_size(self)
 247      VALUE self;
 248  {
 249      return OFFT2NUM(get_stat(self)->st_size);
 250  }
 251  
 252  static VALUE
 253  rb_stat_blksize(self)
 254      VALUE self;
 255  {
 256  #ifdef HAVE_ST_BLKSIZE
 257      return ULONG2NUM(get_stat(self)->st_blksize);
 258  #else
 259      return Qnil;
 260  #endif
 261  }
 262  
 263  static VALUE
 264  rb_stat_blocks(self)
 265      VALUE self;
 266  {
 267  #ifdef HAVE_ST_BLOCKS
 268      return ULONG2NUM(get_stat(self)->st_blocks);
 269  #else
 270      return Qnil;
 271  #endif
 272  }
 273  
 274  static VALUE
 275  rb_stat_atime(self)
 276      VALUE self;
 277  {
 278      return rb_time_new(get_stat(self)->st_atime, 0);
 279  }
 280  
 281  static VALUE
 282  rb_stat_mtime(self)
 283      VALUE self;
 284  {
 285      return rb_time_new(get_stat(self)->st_mtime, 0);
 286  }
 287  
 288  static VALUE
 289  rb_stat_ctime(self)
 290      VALUE self;
 291  {
 292      return rb_time_new(get_stat(self)->st_ctime, 0);
 293  }
 294  
 295  static VALUE
 296  rb_stat_inspect(self)
 297      VALUE self;
 298  {
 299      VALUE str;
 300      int i;
 301      static struct {
 302          char *name;
 303          VALUE (*func)();
 304      } member[] = {
 305          {"dev",     rb_stat_dev},
 306          {"ino",     rb_stat_ino},
 307          {"mode",    rb_stat_mode},
 308          {"nlink",   rb_stat_nlink},
 309          {"uid",     rb_stat_uid},
 310          {"gid",     rb_stat_gid},
 311          {"rdev",    rb_stat_rdev},
 312          {"size",    rb_stat_size},
 313          {"blksize", rb_stat_blksize},
 314          {"blocks",  rb_stat_blocks},
 315          {"atime",   rb_stat_atime},
 316          {"mtime",   rb_stat_mtime},
 317          {"ctime",   rb_stat_ctime},
 318      };
 319  
 320      str = rb_str_buf_new2("#<");
 321      rb_str_buf_cat2(str, rb_class2name(CLASS_OF(self)));
 322      rb_str_buf_cat2(str, " ");
 323  
 324      for (i = 0; i < sizeof(member)/sizeof(member[0]); i++) {
 325          VALUE v;
 326  
 327          if (i > 0) {
 328              rb_str_buf_cat2(str, ", ");
 329          }
 330          rb_str_buf_cat2(str, member[i].name);
 331          rb_str_buf_cat2(str, "=");
 332          v = (*member[i].func)(self);
 333          if (i == 2) {           /* mode */
 334              char buf[32];
 335  
 336              sprintf(buf, "0%lo", NUM2INT(v));
 337              rb_str_buf_cat2(str, buf);
 338          }
 339          else if (i == 0 || i == 6) { /* dev/rdev */
 340              char buf[32];
 341  
 342              sprintf(buf, "0x%lx", NUM2ULONG(v));
 343              rb_str_buf_cat2(str, buf);
 344          }
 345          else {
 346              rb_str_append(str, rb_inspect(v));
 347          }
 348      }
 349      rb_str_buf_cat2(str, ">");
 350      OBJ_INFECT(str, self);
 351  
 352      return str;
 353  }
 354  
 355  static int
 356  rb_stat(file, st)
 357      VALUE file;
 358      struct stat *st;
 359  {
 360      if (TYPE(file) == T_FILE) {
 361          OpenFile *fptr;
 362  
 363          rb_secure(2);
 364          GetOpenFile(file, fptr);
 365          return fstat(fileno(fptr->f), st);
 366      }
 367      SafeStringValue(file);
 368  #if defined DJGPP
 369      if (RSTRING(file)->len == 0) return -1;
 370  #endif
 371      return stat(RSTRING(file)->ptr, st);
 372  }
 373  
 374  static VALUE
 375  rb_file_s_stat(klass, fname)
 376      VALUE klass, fname;
 377  {
 378      struct stat st;
 379  
 380      SafeStringValue(fname);
 381      if (stat(RSTRING(fname)->ptr, &st) == -1) {
 382          rb_sys_fail(RSTRING(fname)->ptr);
 383      }
 384      return stat_new(&st);
 385  }
 386  
 387  static VALUE
 388  rb_io_stat(obj)
 389      VALUE obj;
 390  {
 391      OpenFile *fptr;
 392      struct stat st;
 393  
 394      GetOpenFile(obj, fptr);
 395      if (fstat(fileno(fptr->f), &st) == -1) {
 396          rb_sys_fail(fptr->path);
 397      }
 398      return stat_new(&st);
 399  }
 400  
 401  static VALUE
 402  rb_file_s_lstat(klass, fname)
 403      VALUE klass, fname;
 404  {
 405  #ifdef HAVE_LSTAT
 406      struct stat st;
 407  
 408      SafeStringValue(fname);
 409      if (lstat(RSTRING(fname)->ptr, &st) == -1) {
 410          rb_sys_fail(RSTRING(fname)->ptr);
 411      }
 412      return stat_new(&st);
 413  #else
 414      return rb_file_s_stat(klass, fname);
 415  #endif
 416  }
 417  
 418  static VALUE
 419  rb_file_lstat(obj)
 420      VALUE obj;
 421  {
 422  #ifdef HAVE_LSTAT
 423      OpenFile *fptr;
 424      struct stat st;
 425  
 426      rb_secure(2);
 427      GetOpenFile(obj, fptr);
 428      if (!fptr->path) return Qnil;
 429      if (lstat(fptr->path, &st) == -1) {
 430          rb_sys_fail(fptr->path);
 431      }
 432      return stat_new(&st);
 433  #else
 434      return rb_io_stat(obj);
 435  #endif
 436  }
 437  
 438  static int
 439  group_member(gid)
 440      GETGROUPS_T gid;
 441  {
 442  #if !defined(NT)
 443      if (getgid() ==  gid)
 444          return Qtrue;
 445  
 446  # ifdef HAVE_GETGROUPS
 447  #  ifndef NGROUPS
 448  #   ifdef NGROUPS_MAX
 449  #    define NGROUPS NGROUPS_MAX
 450  #   else
 451  #    define NGROUPS 32
 452  #   endif
 453  #  endif
 454      {
 455          GETGROUPS_T gary[NGROUPS];
 456          int anum;
 457  
 458          anum = getgroups(NGROUPS, gary);
 459          while (--anum >= 0)
 460              if (gary[anum] == gid)
 461                  return Qtrue;
 462      }
 463  # endif
 464  #endif
 465      return Qfalse;
 466  }
 467  
 468  #ifndef S_IXUGO
 469  #  define S_IXUGO               (S_IXUSR | S_IXGRP | S_IXOTH)
 470  #endif
 471  
 472  int
 473  eaccess(path, mode)
 474       const char *path;
 475       int mode;
 476  {
 477  #ifdef S_IXGRP
 478      struct stat st;
 479      int euid;
 480  
 481      if (stat(path, &st) < 0) return -1;
 482  
 483      euid = geteuid();
 484  
 485      if (euid == 0) {
 486          /* Root can read or write any file. */
 487          if (!(mode & X_OK))
 488              return 0;
 489  
 490          /* Root can execute any file that has any one of the execute
 491             bits set. */
 492          if (st.st_mode & S_IXUGO)
 493              return 0;
 494  
 495          return -1;
 496      }
 497  
 498      if (st.st_uid == euid)        /* owner */
 499          mode <<= 6;
 500      else if (getegid() == st.st_gid || group_member(st.st_gid))
 501          mode <<= 3;
 502  
 503      if ((st.st_mode & mode) == mode) return 0;
 504  
 505      return -1;
 506  #else
 507      return access(path, mode);
 508  #endif
 509  }
 510  
 511  static VALUE
 512  test_d(obj, fname)
 513      VALUE obj, fname;
 514  {
 515  #ifndef S_ISDIR
 516  #   define S_ISDIR(m) ((m & S_IFMT) == S_IFDIR)
 517  #endif
 518  
 519      struct stat st;
 520  
 521      if (rb_stat(fname, &st) < 0) return Qfalse;
 522      if (S_ISDIR(st.st_mode)) return Qtrue;
 523      return Qfalse;
 524  }
 525  
 526  static VALUE
 527  test_p(obj, fname)
 528      VALUE obj, fname;
 529  {
 530  #ifdef S_IFIFO
 531  #  ifndef S_ISFIFO
 532  #    define S_ISFIFO(m) ((m & S_IFMT) == S_IFIFO)
 533  #  endif
 534  
 535      struct stat st;
 536  
 537      if (rb_stat(fname, &st) < 0) return Qfalse;
 538      if (S_ISFIFO(st.st_mode)) return Qtrue;
 539  
 540  #endif
 541      return Qfalse;
 542  }
 543  
 544  static VALUE
 545  test_l(obj, fname)
 546      VALUE obj, fname;
 547  {
 548  #ifndef S_ISLNK
 549  #  ifdef _S_ISLNK
 550  #    define S_ISLNK(m) _S_ISLNK(m)
 551  #  elif defined __BORLANDC__
 552  #    ifdef _S_IFLNK
 553  #      define S_ISLNK(m) (((unsigned short)(m) & S_IFMT) == _S_IFLNK)
 554  #    else
 555  #      ifdef S_IFLNK
 556  #        define S_ISLNK(m) (((unsigned short)(m) & S_IFMT) == S_IFLNK)
 557  #      endif
 558  #    endif
 559  #  else
 560  #    ifdef _S_IFLNK
 561  #      define S_ISLNK(m) ((m & S_IFMT) == _S_IFLNK)
 562  #    else
 563  #      ifdef S_IFLNK
 564  #        define S_ISLNK(m) ((m & S_IFMT) == S_IFLNK)
 565  #      endif
 566  #    endif
 567  #  endif
 568  #endif
 569  
 570  #ifdef S_ISLNK
 571      struct stat st;
 572  
 573      SafeStringValue(fname);
 574      if (lstat(RSTRING(fname)->ptr, &st) < 0) return Qfalse;
 575      if (S_ISLNK(st.st_mode)) return Qtrue;
 576  #endif
 577  
 578      return Qfalse;
 579  }
 580  
 581  static VALUE
 582  test_S(obj, fname)
 583      VALUE obj, fname;
 584  {
 585  #ifndef S_ISSOCK
 586  #  ifdef _S_ISSOCK
 587  #    define S_ISSOCK(m) _S_ISSOCK(m)
 588  #  elif defined __BORLANDC__
 589  #    ifdef _S_IFSOCK
 590  #      define S_ISSOCK(m) (((unsigned short)(m) & S_IFMT) == _S_IFSOCK)
 591  #    else
 592  #      ifdef S_IFSOCK
 593  #        define S_ISSOCK(m) (((unsigned short)(m) & S_IFMT) == S_IFSOCK)
 594  #      endif
 595  #    endif
 596  #  else
 597  #    ifdef _S_IFSOCK
 598  #      define S_ISSOCK(m) ((m & S_IFMT) == _S_IFSOCK)
 599  #    else
 600  #      ifdef S_IFSOCK
 601  #        define S_ISSOCK(m) ((m & S_IFMT) == S_IFSOCK)
 602  #      endif
 603  #    endif
 604  #  endif
 605  #endif
 606  
 607  #ifdef S_ISSOCK
 608      struct stat st;
 609  
 610      if (rb_stat(fname, &st) < 0) return Qfalse;
 611      if (S_ISSOCK(st.st_mode)) return Qtrue;
 612  
 613  #endif
 614      return Qfalse;
 615  }
 616  
 617  static VALUE
 618  test_b(obj, fname)
 619      VALUE obj, fname;
 620  {
 621  #ifndef S_ISBLK
 622  #   ifdef S_IFBLK
 623  #       define S_ISBLK(m) ((m & S_IFMT) == S_IFBLK)
 624  #   else
 625  #       define S_ISBLK(m) (0)  /* anytime false */
 626  #   endif
 627  #endif
 628  
 629  #ifdef S_ISBLK
 630      struct stat st;
 631  
 632      if (rb_stat(fname, &st) < 0) return Qfalse;
 633      if (S_ISBLK(st.st_mode)) return Qtrue;
 634  
 635  #endif
 636      return Qfalse;
 637  }
 638  
 639  static VALUE
 640  test_c(obj, fname)
 641      VALUE obj, fname;
 642  {
 643  #ifndef S_ISCHR
 644  #   define S_ISCHR(m) ((m & S_IFMT) == S_IFCHR)
 645  #endif
 646  
 647      struct stat st;
 648  
 649      if (rb_stat(fname, &st) < 0) return Qfalse;
 650      if (S_ISCHR(st.st_mode)) return Qtrue;
 651  
 652      return Qfalse;
 653  }
 654  
 655  static VALUE
 656  test_e(obj, fname)
 657      VALUE obj, fname;
 658  {
 659      struct stat st;
 660  
 661      if (rb_stat(fname, &st) < 0) return Qfalse;
 662      return Qtrue;
 663  }
 664  
 665  static VALUE
 666  test_r(obj, fname)
 667      VALUE obj, fname;
 668  {
 669      SafeStringValue(fname);
 670      if (eaccess(RSTRING(fname)->ptr, R_OK) < 0) return Qfalse;
 671      return Qtrue;
 672  }
 673  
 674  static VALUE
 675  test_R(obj, fname)
 676      VALUE obj, fname;
 677  {
 678      SafeStringValue(fname);
 679      if (access(RSTRING(fname)->ptr, R_OK) < 0) return Qfalse;
 680      return Qtrue;
 681  }
 682  
 683  static VALUE
 684  test_w(obj, fname)
 685      VALUE obj, fname;
 686  {
 687      SafeStringValue(fname);
 688      if (eaccess(RSTRING(fname)->ptr, W_OK) < 0) return Qfalse;
 689      return Qtrue;
 690  }
 691  
 692  static VALUE
 693  test_W(obj, fname)
 694      VALUE obj, fname;
 695  {
 696      SafeStringValue(fname);
 697      if (access(RSTRING(fname)->ptr, W_OK) < 0) return Qfalse;
 698      return Qtrue;
 699  }
 700  
 701  static VALUE
 702  test_x(obj, fname)
 703      VALUE obj, fname;
 704  {
 705      SafeStringValue(fname);
 706      if (eaccess(RSTRING(fname)->ptr, X_OK) < 0) return Qfalse;
 707      return Qtrue;
 708  }
 709  
 710  static VALUE
 711  test_X(obj, fname)
 712      VALUE obj, fname;
 713  {
 714      SafeStringValue(fname);
 715      if (access(RSTRING(fname)->ptr, X_OK) < 0) return Qfalse;
 716      return Qtrue;
 717  }
 718  
 719  #ifndef S_ISREG
 720  #   define S_ISREG(m) ((m & S_IFMT) == S_IFREG)
 721  #endif
 722  
 723  static VALUE
 724  test_f(obj, fname)
 725      VALUE obj, fname;
 726  {
 727      struct stat st;
 728  
 729      if (rb_stat(fname, &st) < 0) return Qfalse;
 730      if (S_ISREG(st.st_mode)) return Qtrue;
 731      return Qfalse;
 732  }
 733  
 734  static VALUE
 735  test_z(obj, fname)
 736      VALUE obj, fname;
 737  {
 738      struct stat st;
 739  
 740      if (rb_stat(fname, &st) < 0) return Qfalse;
 741      if (st.st_size == 0) return Qtrue;
 742      return Qfalse;
 743  }
 744  
 745  static VALUE
 746  test_s(obj, fname)
 747      VALUE obj, fname;
 748  {
 749      struct stat st;
 750  
 751      if (rb_stat(fname, &st) < 0) return Qnil;
 752      if (st.st_size == 0) return Qnil;
 753      return OFFT2NUM(st.st_size);
 754  }
 755  
 756  static VALUE
 757  test_owned(obj, fname)
 758      VALUE obj, fname;
 759  {
 760      struct stat st;
 761  
 762      if (rb_stat(fname, &st) < 0) return Qfalse;
 763      if (st.st_uid == geteuid()) return Qtrue;
 764      return Qfalse;
 765  }
 766  
 767  static VALUE
 768  test_rowned(obj, fname)
 769      VALUE obj, fname;
 770  {
 771      struct stat st;
 772  
 773      if (rb_stat(fname, &st) < 0) return Qfalse;
 774      if (st.st_uid == getuid()) return Qtrue;
 775      return Qfalse;
 776  }
 777  
 778  static VALUE
 779  test_grpowned(obj, fname)
 780      VALUE obj, fname;
 781  {
 782  #ifndef NT
 783      struct stat st;
 784  
 785      if (rb_stat(fname, &st) < 0) return Qfalse;
 786      if (st.st_gid == getegid()) return Qtrue;
 787  #endif
 788      return Qfalse;
 789  }
 790  
 791  #if defined(S_ISUID) || defined(S_ISGID) || defined(S_ISVTX)
 792  static VALUE
 793  check3rdbyte(fname, mode)
 794      VALUE fname;
 795      int mode;
 796  {
 797      struct stat st;
 798  
 799      SafeStringValue(fname);
 800      if (stat(RSTRING(fname)->ptr, &st) < 0) return Qfalse;
 801      if (st.st_mode & mode) return Qtrue;
 802      return Qfalse;
 803  }
 804  #endif
 805  
 806  static VALUE
 807  test_suid(obj, fname)
 808      VALUE obj, fname;
 809  {
 810  #ifdef S_ISUID
 811      return check3rdbyte(fname, S_ISUID);
 812  #else
 813      return Qfalse;
 814  #endif
 815  }
 816  
 817  static VALUE
 818  test_sgid(obj, fname)
 819      VALUE obj, fname;
 820  {
 821  #ifdef S_ISGID
 822      return check3rdbyte(fname, S_ISGID);
 823  #else
 824      return Qfalse;
 825  #endif
 826  }
 827  
 828  static VALUE
 829  test_sticky(obj, fname)
 830      VALUE obj, fname;
 831  {
 832  #ifdef S_ISVTX
 833      return check3rdbyte(fname, S_ISVTX);
 834  #else
 835      return Qnil;
 836  #endif
 837  }
 838  
 839  static VALUE
 840  rb_file_s_size(klass, fname)
 841      VALUE klass, fname;
 842  {
 843      struct stat st;
 844  
 845      if (rb_stat(fname, &st) < 0)
 846          rb_sys_fail(RSTRING(fname)->ptr);
 847      return OFFT2NUM(st.st_size);
 848  }
 849  
 850  static VALUE
 851  rb_file_ftype(st)
 852      struct stat *st;
 853  {
 854      char *t;
 855  
 856      if (S_ISREG(st->st_mode)) {
 857          t = "file";
 858      }
 859      else if (S_ISDIR(st->st_mode)) {
 860          t = "directory";
 861      }
 862      else if (S_ISCHR(st->st_mode)) {
 863          t = "characterSpecial";
 864      }
 865  #ifdef S_ISBLK
 866      else if (S_ISBLK(st->st_mode)) {
 867          t = "blockSpecial";
 868      }
 869  #endif
 870  #ifdef S_ISFIFO
 871      else if (S_ISFIFO(st->st_mode)) {
 872          t = "fifo";
 873      }
 874  #endif
 875  #ifdef S_ISLNK
 876      else if (S_ISLNK(st->st_mode)) {
 877          t = "link";
 878      }
 879  #endif
 880  #ifdef S_ISSOCK
 881      else if (S_ISSOCK(st->st_mode)) {
 882          t = "socket";
 883      }
 884  #endif
 885      else {
 886          t = "unknown";
 887      }
 888  
 889      return rb_str_new2(t);
 890  }
 891  
 892  static VALUE
 893  rb_file_s_ftype(klass, fname)
 894      VALUE klass, fname;
 895  {
 896      struct stat st;
 897  
 898      SafeStringValue(fname);
 899      if (lstat(RSTRING(fname)->ptr, &st) == -1) {
 900          rb_sys_fail(RSTRING(fname)->ptr);
 901      }
 902  
 903      return rb_file_ftype(&st);
 904  }
 905  
 906  static VALUE
 907  rb_file_s_atime(klass, fname)
 908      VALUE klass, fname;
 909  {
 910      struct stat st;
 911  
 912      if (rb_stat(fname, &st) < 0)
 913          rb_sys_fail(RSTRING(fname)->ptr);
 914      return rb_time_new(st.st_atime, 0);
 915  }
 916  
 917  static VALUE
 918  rb_file_atime(obj)
 919      VALUE obj;
 920  {
 921      OpenFile *fptr;
 922      struct stat st;
 923  
 924      GetOpenFile(obj, fptr);
 925      if (fstat(fileno(fptr->f), &st) == -1) {
 926          rb_sys_fail(fptr->path);
 927      }
 928      return rb_time_new(st.st_atime, 0);
 929  }
 930  
 931  static VALUE
 932  rb_file_s_mtime(klass, fname)
 933      VALUE klass, fname;
 934  {
 935      struct stat st;
 936  
 937      if (rb_stat(fname, &st) < 0)
 938          rb_sys_fail(RSTRING(fname)->ptr);
 939      return rb_time_new(st.st_mtime, 0);
 940  }
 941  
 942  static VALUE
 943  rb_file_mtime(obj)
 944      VALUE obj;
 945  {
 946      OpenFile *fptr;
 947      struct stat st;
 948  
 949      GetOpenFile(obj, fptr);
 950      if (fstat(fileno(fptr->f), &st) == -1) {
 951          rb_sys_fail(fptr->path);
 952      }
 953      return rb_time_new(st.st_mtime, 0);
 954  }
 955  
 956  static VALUE
 957  rb_file_s_ctime(klass, fname)
 958      VALUE klass, fname;
 959  {
 960      struct stat st;
 961  
 962      if (rb_stat(fname, &st) < 0)
 963          rb_sys_fail(RSTRING(fname)->ptr);
 964      return rb_time_new(st.st_ctime, 0);
 965  }
 966  
 967  static VALUE
 968  rb_file_ctime(obj)
 969      VALUE obj;
 970  {
 971      OpenFile *fptr;
 972      struct stat st;
 973  
 974      GetOpenFile(obj, fptr);
 975      if (fstat(fileno(fptr->f), &st) == -1) {
 976          rb_sys_fail(fptr->path);
 977      }
 978      return rb_time_new(st.st_ctime, 0);
 979  }
 980  
 981  static void
 982  chmod_internal(path, mode)
 983      const char *path;
 984      int mode;
 985  {
 986      if (chmod(path, mode) < 0)
 987          rb_sys_fail(path);
 988  }
 989  
 990  static VALUE
 991  rb_file_s_chmod(argc, argv)
 992      int argc;
 993      VALUE *argv;
 994  {
 995      VALUE vmode;
 996      VALUE rest;
 997      int mode;
 998      long n;
 999  
1000      rb_secure(2);
1001      rb_scan_args(argc, argv, "1*", &vmode, &rest);
1002      mode = NUM2INT(vmode);
1003  
1004      n = apply2files(chmod_internal, rest, mode);
1005      return LONG2FIX(n);
1006  }
1007  
1008  static VALUE
1009  rb_file_chmod(obj, vmode)
1010      VALUE obj, vmode;
1011  {
1012      OpenFile *fptr;
1013      int mode;
1014  
1015      rb_secure(2);
1016      mode = NUM2INT(vmode);
1017  
1018      GetOpenFile(obj, fptr);
1019  #ifdef HAVE_FCHMOD
1020      if (fchmod(fileno(fptr->f), mode) == -1)
1021          rb_sys_fail(fptr->path);
1022  #else
1023      if (!fptr->path) return Qnil;
1024      if (chmod(fptr->path, mode) == -1)
1025          rb_sys_fail(fptr->path);
1026  #endif
1027  
1028      return INT2FIX(0);
1029  }
1030  
1031  #if defined(HAVE_LCHMOD)
1032  static void
1033  lchmod_internal(path, mode)
1034      const char *path;
1035      int mode;
1036  {
1037      if (lchmod(path, mode) < 0)
1038          rb_sys_fail(path);
1039  }
1040  
1041  static VALUE
1042  rb_file_s_lchmod(argc, argv)
1043      int argc;
1044      VALUE *argv;
1045  {
1046      VALUE vmode;
1047      VALUE rest;
1048      int mode;
1049      long n;
1050  
1051      rb_secure(2);
1052      rb_scan_args(argc, argv, "1*", &vmode, &rest);
1053      mode = NUM2INT(vmode);
1054  
1055      n = apply2files(lchmod_internal, rest, mode);
1056      return LONG2FIX(n);
1057  }
1058  #else
1059  static VALUE
1060  rb_file_s_lchmod(argc, argv)
1061      int argc;
1062      VALUE *argv;
1063  {
1064      rb_notimplement();
1065  }
1066  #endif
1067  
1068  struct chown_args {
1069      int owner, group;
1070  };
1071  
1072  static void
1073  chown_internal(path, args)
1074      const char *path;
1075      struct chown_args *args;
1076  {
1077      if (chown(path, args->owner, args->group) < 0)
1078          rb_sys_fail(path);
1079  }
1080  
1081  static VALUE
1082  rb_file_s_chown(argc, argv)
1083      int argc;
1084      VALUE *argv;
1085  {
1086      VALUE o, g, rest;
1087      struct chown_args arg;
1088      long n;
1089  
1090      rb_secure(2);
1091      rb_scan_args(argc, argv, "2*", &o, &g, &rest);
1092      if (NIL_P(o)) {
1093          arg.owner = -1;
1094      }
1095      else {
1096          arg.owner = NUM2INT(o);
1097      }
1098      if (NIL_P(g)) {
1099          arg.group = -1;
1100      }
1101      else {
1102          arg.group = NUM2INT(g);
1103      }
1104  
1105      n = apply2files(chown_internal, rest, &arg);
1106      return LONG2FIX(n);
1107  }
1108  
1109  static VALUE
1110  rb_file_chown(obj, owner, group)
1111      VALUE obj, owner, group;
1112  {
1113      OpenFile *fptr;
1114  
1115      rb_secure(2);
1116      GetOpenFile(obj, fptr);
1117  #if defined(DJGPP) || defined(__CYGWIN32__) || defined(NT) || defined(__EMX__)
1118      if (!fptr->path) return Qnil;
1119      if (chown(fptr->path, NUM2INT(owner), NUM2INT(group)) == -1)
1120          rb_sys_fail(fptr->path);
1121  #else
1122      if (fchown(fileno(fptr->f), NUM2INT(owner), NUM2INT(group)) == -1)
1123          rb_sys_fail(fptr->path);
1124  #endif
1125  
1126      return INT2FIX(0);
1127  }
1128  
1129  #if defined(HAVE_LCHOWN) && !defined(__CHECKER__)
1130  static void
1131  lchown_internal(path, args)
1132      const char *path;
1133      struct chown_args *args;
1134  {
1135      if (lchown(path, args->owner, args->group) < 0)
1136          rb_sys_fail(path);
1137  }
1138  
1139  static VALUE
1140  rb_file_s_lchown(argc, argv)
1141      int argc;
1142      VALUE *argv;
1143  {
1144      VALUE o, g, rest;
1145      struct chown_args arg;
1146      long n;
1147  
1148      rb_secure(2);
1149      rb_scan_args(argc, argv, "2*", &o, &g, &rest);
1150      if (NIL_P(o)) {
1151          arg.owner = -1;
1152      }
1153      else {
1154          arg.owner = NUM2INT(o);
1155      }
1156      if (NIL_P(g)) {
1157          arg.group = -1;
1158      }
1159      else {
1160          arg.group = NUM2INT(g);
1161      }
1162  
1163      n = apply2files(lchown_internal, rest, &arg);
1164      return LONG2FIX(n);
1165  }
1166  #else
1167  static VALUE
1168  rb_file_s_lchown(argc, argv)
1169      int argc;
1170      VALUE *argv;
1171  {
1172      rb_notimplement();
1173  }
1174  #endif
1175  
1176  struct timeval rb_time_timeval();
1177  
1178  #if defined(HAVE_UTIMES) && !defined(__CHECKER__)
1179  
1180  static void
1181  utime_internal(path, tvp)
1182      char *path;
1183      struct timeval tvp[];
1184  {
1185      if (utimes(path, tvp) < 0)
1186          rb_sys_fail(path);
1187  }
1188  
1189  static VALUE
1190  rb_file_s_utime(argc, argv)
1191      int argc;
1192      VALUE *argv;
1193  {
1194      VALUE atime, mtime, rest;
1195      struct timeval tvp[2];
1196      long n;
1197  
1198      rb_scan_args(argc, argv, "2*", &atime, &mtime, &rest);
1199  
1200      tvp[0] = rb_time_timeval(atime);
1201      tvp[1] = rb_time_timeval(mtime);
1202  
1203      n = apply2files(utime_internal, rest, tvp);
1204      return LONG2FIX(n);
1205  }
1206  
1207  #else
1208  
1209  #ifndef HAVE_UTIME_H
1210  # ifdef NT
1211  #   if defined(__BORLANDC__)
1212  #     include <utime.h>
1213  #   else
1214  #  include <sys/utime.h>
1215  #   endif
1216  #   if defined(_MSC_VER) || defined __MINGW32__
1217  #  define utimbuf _utimbuf
1218  #   endif
1219  # else
1220  struct utimbuf {
1221      long actime;
1222      long modtime;
1223  };
1224  # endif
1225  #endif
1226  
1227  static void
1228  utime_internal(path, utp)
1229      const char *path;
1230      struct utimbuf *utp;
1231  {
1232      if (utime(path, utp) < 0)
1233          rb_sys_fail(path);
1234  }
1235  
1236  static VALUE
1237  rb_file_s_utime(argc, argv)
1238      int argc;
1239      VALUE *argv;
1240  {
1241      VALUE atime, mtime, rest;
1242      long n;
1243      struct timeval tv;
1244      struct utimbuf utbuf;
1245  
1246      rb_scan_args(argc, argv, "2*", &atime, &mtime, &rest);
1247  
1248      tv = rb_time_timeval(atime);
1249      utbuf.actime = tv.tv_sec;
1250      tv = rb_time_timeval(mtime);
1251      utbuf.modtime = tv.tv_sec;
1252  
1253      n = apply2files(utime_internal, rest, &utbuf);
1254      return LONG2FIX(n);
1255  }
1256  
1257  #endif
1258  
1259  static VALUE
1260  rb_file_s_link(klass, from, to)
1261      VALUE klass, from, to;
1262  {
1263      SafeStringValue(from);
1264      SafeStringValue(to);
1265  
1266      if (link(RSTRING(from)->ptr, RSTRING(to)->ptr) < 0)
1267          rb_sys_fail(RSTRING(from)->ptr);
1268      return INT2FIX(0);
1269  }
1270  
1271  static VALUE
1272  rb_file_s_symlink(klass, from, to)
1273      VALUE klass, from, to;
1274  {
1275  #ifdef HAVE_SYMLINK
1276      SafeStringValue(from);
1277      SafeStringValue(to);
1278  
1279      if (symlink(RSTRING(from)->ptr, RSTRING(to)->ptr) < 0)
1280          rb_sys_fail(RSTRING(from)->ptr);
1281      return INT2FIX(0);
1282  #else
1283      rb_notimplement();
1284      return Qnil;                /* not reached */
1285  #endif
1286  }
1287  
1288  static VALUE
1289  rb_file_s_readlink(klass, path)
1290      VALUE klass, path;
1291  {
1292  #ifdef HAVE_READLINK
1293      char *buf;
1294      int size = 100;
1295      int rv;
1296      VALUE v;
1297  
1298      SafeStringValue(path);
1299      buf = xmalloc(size);
1300      if ((rv = readlink(RSTRING(path)->ptr, buf, size)) == size) {
1301          size *= 2;
1302          buf = xrealloc(buf, size);
1303      }
1304      if (rv < 0) {
1305          free(buf);
1306          rb_sys_fail(RSTRING(path)->ptr);
1307      }
1308      v = rb_tainted_str_new(buf, rv);
1309      free(buf);
1310  
1311      return v;
1312  #else
1313      rb_notimplement();
1314      return Qnil;                /* not reached */
1315  #endif
1316  }
1317  
1318  static void
1319  unlink_internal(path)
1320      const char *path;
1321  {
1322      if (unlink(path) < 0)
1323          rb_sys_fail(path);
1324  }
1325  
1326  static VALUE
1327  rb_file_s_unlink(klass, args)
1328      VALUE klass, args;
1329  {
1330      long n;
1331  
1332      rb_secure(2);
1333      n = apply2files(unlink_internal, args, 0);
1334      return LONG2FIX(n);
1335  }
1336  
1337  static VALUE
1338  rb_file_s_rename(klass, from, to)
1339      VALUE klass, from, to;
1340  {
1341      SafeStringValue(from);
1342      SafeStringValue(to);
1343  
1344      if (rename(RSTRING(from)->ptr, RSTRING(to)->ptr) < 0) {
1345  #if defined __CYGWIN__
1346          extern unsigned long __attribute__((stdcall)) GetLastError();
1347          errno = GetLastError(); /* This is a Cygwin bug */
1348  #endif
1349          rb_sys_fail(RSTRING(from)->ptr);
1350      }
1351  
1352      return INT2FIX(0);
1353  }
1354  
1355  static VALUE
1356  rb_file_s_umask(argc, argv)
1357      int argc;
1358      VALUE *argv;
1359  {
1360      int omask = 0;
1361  
1362      rb_secure(2);
1363      if (argc == 0) {
1364          omask = umask(0);
1365          umask(omask);
1366      }
1367      else if (argc == 1) {
1368          omask = umask(NUM2INT(argv[0]));
1369      }
1370      else {
1371          rb_raise(rb_eArgError, "wrong number of argument");
1372      }
1373      return INT2FIX(omask);
1374  }
1375  
1376  #if defined DOSISH
1377  #define isdirsep(x) ((x) == '/' || (x) == '\\')
1378  #else
1379  #define isdirsep(x) ((x) == '/')
1380  #endif
1381  #ifndef CharNext                /* defined as CharNext[AW] on Windows. */
1382  # if defined(DJGPP)
1383  #   define CharNext(p) ((p) + mblen(p, MB_CUR_MAX))
1384  # else
1385  #   define CharNext(p) ((p) + 1)
1386  # endif
1387  #endif
1388  
1389  static char *
1390  strrdirsep(path)
1391      char *path;
1392  {
1393      char *last = NULL;
1394      while (*path) {
1395          if (isdirsep(*path)) {
1396              last = path++;
1397          }
1398          else {
1399              path = CharNext(path);
1400          }
1401      }
1402      return last;
1403  }
1404  
1405  #define BUFCHECK(cond) while (cond) {\
1406      long bdiff = p - buf;\
1407      buflen *= 2;\
1408      rb_str_resize(result, buflen);\
1409      buf = RSTRING(result)->ptr;\
1410      p = buf + bdiff;\
1411      pend = buf + buflen;\
1412  }
1413  
1414  VALUE
1415  rb_file_s_expand_path(argc, argv)
1416      int argc;
1417      VALUE *argv;
1418  {
1419      VALUE fname, dname, result;
1420      char *s, *buf, *b, *p, *pend;
1421      long buflen = MAXPATHLEN;
1422      int tainted;
1423  
1424      rb_scan_args(argc, argv, "11", &fname, &dname);
1425      result = rb_str_new(0, buflen + 2);
1426  
1427      s = StringValuePtr(fname);
1428      p = buf = RSTRING(result)->ptr;
1429      pend = p + buflen;
1430      tainted = OBJ_TAINTED(fname);
1431  
1432      if (s[0] == '~') {
1433          if (isdirsep(s[1]) || s[1] == '\0') {
1434              char *dir = getenv("HOME");
1435  
1436              if (!dir) {
1437                  rb_raise(rb_eArgError, "couldn't find HOME environment -- expanding `%s'", s);
1438              }
1439              BUFCHECK (strlen(dir) > buflen);
1440              strcpy(buf, dir);
1441              p = buf + strlen(dir);
1442              s++;
1443              tainted = 1;
1444          }
1445          else {
1446  #ifdef HAVE_PWD_H
1447              struct passwd *pwPtr;
1448              s++;
1449  #endif
1450              b = s;
1451              while (*s && !isdirsep(*s)) {
1452                  s = CharNext(s);
1453              }
1454              BUFCHECK (p + (s-b) >= pend);
1455              memcpy(p, b, s-b);
1456              p += s-b;
1457              *p = '\0';
1458  #ifdef HAVE_PWD_H
1459              pwPtr = getpwnam(buf);
1460              if (!pwPtr) {
1461                  endpwent();
1462                  rb_raise(rb_eArgError, "user %s doesn't exist", buf);
1463              }
1464              BUFCHECK (strlen(pwPtr->pw_dir) > buflen);
1465              strcpy(buf, pwPtr->pw_dir);
1466              p = buf + strlen(pwPtr->pw_dir);
1467              endpwent();
1468  #endif
1469          }
1470      }
1471  #if defined DOSISH || defined __CYGWIN__
1472      /* skip drive letter */
1473      else if (ISALPHA(s[0]) && s[1] == ':' && isdirsep(s[2])) {
1474          b = s;
1475          while (*s && !isdirsep(*s)) {
1476              s = CharNext(s);
1477          }
1478          BUFCHECK (p + (s-b) >= pend);
1479          memcpy(p, b, s-b);
1480          p += s-b;
1481      }
1482  #endif
1483      else if (!isdirsep(*s)) {
1484          if (!NIL_P(dname)) {
1485              dname = rb_file_s_expand_path(1, &dname);
1486              if (OBJ_TAINTED(dname)) tainted = 1;
1487              BUFCHECK (RSTRING(dname)->len > buflen);
1488              memcpy(buf, RSTRING(dname)->ptr, RSTRING(dname)->len);
1489              p += RSTRING(dname)->len;
1490          }
1491          else {
1492              char *dir = my_getcwd();
1493  
1494              tainted = 1;
1495              BUFCHECK (strlen(dir) > buflen);
1496              strcpy(buf, dir);
1497              free(dir);
1498              p = &buf[strlen(buf)];
1499          }
1500          while (p > buf && *(p - 1) == '/') p--;
1501      }
1502      else {
1503          while (*s && isdirsep(*s)) {
1504              *p++ = '/';
1505              BUFCHECK (p >= pend);
1506              s++;
1507          }
1508          if (p > buf && *s) p--;
1509      }
1510      *p = '/';
1511  
1512      b = s;
1513      while (*s) {
1514          switch (*s) {
1515            case '.':
1516              if (b == s++) {     /* beginning of path element */
1517                  switch (*s) {
1518                    case '\0':
1519                      b = s;
1520                      break;
1521                    case '.':
1522                      if (*(s+1) == '\0' || isdirsep(*(s+1))) {
1523                          /* We must go back to the parent */
1524                          *p = '\0';
1525                          if (!(b = strrdirsep(buf))) {
1526                              *p = '/';
1527                          }
1528                          else {
1529                              p = b;
1530                          }
1531                          b = ++s;
1532                      }
1533                      break;
1534                    case '/':
1535  #if defined DOSISH
1536                    case '\\':
1537  #endif
1538                      b = ++s;
1539                      break;
1540                    default:
1541                      /* ordinary path element, beginning don't move */
1542                      break;
1543                  }
1544              }
1545              break;
1546            case '/':
1547  #if defined DOSISH
1548            case '\\':
1549  #endif
1550              if (s > b) {
1551                  BUFCHECK (p + (s-b+1) >= pend);
1552                  memcpy(++p, b, s-b);
1553                  p += s-b;
1554                  *p = '/';
1555              }
1556              b = ++s;
1557              break;
1558            default:
1559              s = CharNext(s);
1560              break;
1561          }
1562      }
1563  
1564      if (s > b) {
1565          BUFCHECK (p + (s-b) >= pend);
1566          memcpy(++p, b, s-b);
1567          p += s-b;
1568      }
1569  #if defined DOSISH || defined __CYGWIN__
1570      else if (ISALPHA(buf[0]) && (buf[1] == ':') && isdirsep(buf[2])) {
1571          /* root directory needs a trailing backslash,
1572             otherwise it mean the current directory of the drive */
1573          if (p == (buf+2)) p++;
1574      }
1575      else if (isdirsep(buf[0]) && isdirsep(buf[1])) {
1576          if (p == (buf+1)) p++;
1577      }
1578  #endif
1579  
1580      if (tainted) OBJ_TAINT(result);
1581      RSTRING(result)->len = p - buf;
1582      *p = '\0';
1583      return result;
1584  }
1585  
1586  static int
1587  rmext(p, e)
1588      const char *p, *e;
1589  {
1590      int l1, l2;
1591  
1592      if (!e) return 0;
1593  
1594      l1 = strlen(p);
1595      l2 = strlen(e);
1596      if (l2 == 2 && e[1] == '*') {
1597          e = strrchr(p, *e);
1598          if (!e) return 0;
1599          return e - p;
1600      }
1601      if (l1 < l2) return l1;
1602  
1603      if (strcmp(p+l1-l2, e) == 0) {
1604          return l1-l2;
1605      }
1606      return 0;
1607  }
1608  
1609  static VALUE
1610  rb_file_s_basename(argc, argv)
1611      int argc;
1612      VALUE *argv;
1613  {
1614      VALUE fname, fext, basename;
1615      char *name, *p, *ext = NULL;
1616      int f;
1617  
1618      if (rb_scan_args(argc, argv, "11", &fname, &fext) == 2) {
1619          ext = StringValuePtr(fext);
1620      }
1621      name = StringValuePtr(fname);
1622      p = strrdirsep(name);
1623      if (!p) {
1624          if (NIL_P(fext) || !(f = rmext(name, ext)))
1625              return fname;
1626          basename = rb_str_new(name, f);
1627      }
1628      else {
1629          p++;                    /* skip last / */
1630          if (NIL_P(fext) || !(f = rmext(p, ext))) {
1631              basename = rb_str_new2(p);
1632          }
1633          else {
1634              basename = rb_str_new(p, f);
1635          }
1636      }
1637      OBJ_INFECT(basename, fname);
1638      return basename;
1639  }
1640  
1641  static VALUE
1642  rb_file_s_dirname(klass, fname)
1643      VALUE klass, fname;
1644  {
1645      char *name, *p;
1646      VALUE dirname;
1647  
1648      name = StringValuePtr(fname);
1649      p = strrdirsep(name);
1650      if (!p) {
1651          return rb_str_new2(".");
1652      }
1653      if (p == name)
1654          p++;
1655      dirname = rb_str_new(name, p - name);
1656      OBJ_INFECT(dirname, fname);
1657      return dirname;
1658  }
1659  
1660  static VALUE
1661  rb_file_s_extname(klass, fname)
1662      VALUE klass, fname;
1663  {
1664      char *name, *p, *e;
1665      VALUE extname;
1666  
1667      name = StringValuePtr(fname);
1668      p = strrdirsep(name);       /* get the last path component */
1669      if (!p)
1670          p = name;
1671      else
1672          p++;
1673   
1674       e = strrchr(p, '.');       /* get the last dot of the last component */
1675       if (!e || e == p)          /* no dot, or the only dot is first? */
1676           return rb_str_new2("");
1677       extname = rb_str_new2(e);  /* keep the dot, too! */
1678       OBJ_INFECT(extname, fname);
1679       return extname;
1680  }
1681  
1682  static VALUE
1683  rb_file_s_split(klass, path)
1684      VALUE klass, path;
1685  {
1686      return rb_assoc_new(rb_file_s_dirname(Qnil, path), rb_file_s_basename(1,&path));
1687  }
1688  
1689  static VALUE separator;
1690  
1691  static VALUE
1692  rb_file_s_join(klass, args)
1693      VALUE klass, args;
1694  {
1695      return rb_ary_join(args, separator);
1696  }
1697  
1698  static VALUE
1699  rb_file_s_truncate(klass, path, len)
1700      VALUE klass, path, len;
1701  {
1702      rb_secure(2);
1703      SafeStringValue(path);
1704  
1705  #ifdef HAVE_TRUNCATE
1706      if (truncate(RSTRING(path)->ptr, NUM2OFFT(len)) < 0)
1707          rb_sys_fail(RSTRING(path)->ptr);
1708  #else
1709  # ifdef HAVE_CHSIZE
1710      {
1711          int tmpfd;
1712  
1713  #  if defined(NT)
1714          if ((tmpfd = open(RSTRING(path)->ptr, O_RDWR)) < 0) {
1715              rb_sys_fail(RSTRING(path)->ptr);
1716          }
1717  #  else
1718          if ((tmpfd = open(RSTRING(path)->ptr, 0)) < 0) {
1719              rb_sys_fail(RSTRING(path)->ptr);
1720          }
1721  #  endif
1722          if (chsize(tmpfd, NUM2OFFT(len)) < 0) {
1723              close(tmpfd);
1724              rb_sys_fail(RSTRING(path)->ptr);
1725          }
1726          close(tmpfd);
1727      }
1728  # else
1729      rb_notimplement();
1730  # endif
1731  #endif
1732      return INT2FIX(0);
1733  }
1734  
1735  static VALUE
1736  rb_file_truncate(obj, len)
1737      VALUE obj, len;
1738  {
1739      OpenFile *fptr;
1740  
1741      rb_secure(2);
1742      GetOpenFile(obj, fptr);
1743      if (!(fptr->mode & FMODE_WRITABLE)) {
1744          rb_raise(rb_eIOError, "not opened for writing");
1745      }
1746  #ifdef HAVE_TRUNCATE
1747      if (ftruncate(fileno(fptr->f), NUM2OFFT(len)) < 0)
1748          rb_sys_fail(fptr->path);
1749  #else
1750  # ifdef HAVE_CHSIZE
1751      if (chsize(fileno(fptr->f), NUM2OFFT(len)) < 0)
1752          rb_sys_fail(fptr->path);
1753  # else
1754      rb_notimplement();
1755  # endif
1756  #endif
1757      return INT2FIX(0);
1758  }
1759  
1760  # ifndef LOCK_SH
1761  #  define LOCK_SH 1
1762  # endif
1763  # ifndef LOCK_EX
1764  #  define LOCK_EX 2
1765  # endif
1766  # ifndef LOCK_NB
1767  #  define LOCK_NB 4
1768  # endif
1769  # ifndef LOCK_UN
1770  #  define LOCK_UN 8
1771  # endif
1772  
1773  #if 0
1774  static int
1775  rb_thread_flock(fd, op, fptr)
1776      int fd, op;
1777      OpenFile *fptr;
1778  {
1779      if (rb_thread_alone() || (op & LOCK_NB)) {
1780          return flock(fd, op);
1781      }
1782      op |= LOCK_NB;
1783      while (flock(fd, op) < 0) {
1784          switch (errno) {
1785            case EAGAIN:
1786            case EACCES:
1787  #if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN
1788            case EWOULDBLOCK:
1789  #endif
1790              rb_thread_polling();        /* busy wait */
1791              rb_io_check_closed(fptr);
1792              continue;
1793            default:
1794              return -1;
1795          }
1796      }
1797      return 0;
1798  }
1799  #define flock(fd, op) rb_thread_flock(fd, op, fptr)
1800  #endif
1801  
1802  static VALUE
1803  rb_file_flock(obj, operation)
1804      VALUE obj;
1805      VALUE operation;
1806  {
1807  #ifndef __CHECKER__
1808      OpenFile *fptr;
1809      int ret;
1810  
1811      rb_secure(2);
1812      GetOpenFile(obj, fptr);
1813  
1814      if (fptr->mode & FMODE_WRITABLE) {
1815          fflush(GetWriteFile(fptr));
1816      }
1817      TRAP_BEG;
1818      ret = flock(fileno(fptr->f), NUM2INT(operation));
1819      TRAP_END;
1820      if (ret < 0) {
1821          switch (errno) {
1822            case EAGAIN:
1823            case EACCES:
1824  #if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN
1825            case EWOULDBLOCK:
1826  #endif
1827                return Qfalse;
1828          }
1829          rb_sys_fail(fptr->path);
1830      }
1831  #endif
1832      return INT2FIX(0);
1833  }
1834  #undef flock
1835  
1836  static void
1837  test_check(n, argc, argv)
1838      int n, argc;
1839      VALUE *argv;
1840  {
1841      int i;
1842  
1843      n+=1;
1844      if (n != argc) rb_raise(rb_eArgError, "wrong number of arguments(%d for %d)", argc, n);
1845      for (i=1; i<n; i++) {
1846          switch (TYPE(argv[i])) {
1847            case T_STRING:
1848              SafeStringValue(argv[i]);
1849              break;
1850            case T_FILE:
1851              break;
1852            default:
1853              Check_Type(argv[i], T_STRING);
1854              break;
1855          }
1856      }
1857  }
1858  
1859  #define CHECK(n) test_check((n), argc, argv)
1860  
1861  static VALUE
1862  rb_f_test(argc, argv)
1863      int argc;
1864      VALUE *argv;
1865  {
1866      int cmd;
1867  
1868      if (argc == 0) rb_raise(rb_eArgError, "wrong number of arguments");
1869  #if 0 /* 1.7 behavior? */
1870      if (argc == 1) {
1871          return RTEST(argv[0]) ? Qtrue : Qfalse;
1872      }
1873  #endif
1874      cmd = NUM2CHR(argv[0]);
1875      if (cmd == 0) return Qfalse;
1876      if (strchr("bcdefgGkloOprRsSuwWxXz", cmd)) {
1877          CHECK(1);
1878          switch (cmd) {
1879            case 'b':
1880              return test_b(0, argv[1]);
1881  
1882            case 'c':
1883              return test_c(0, argv[1]);
1884  
1885            case 'd':
1886              return test_d(0, argv[1]);
1887  
1888            case 'a':
1889            case 'e':
1890              return test_e(0, argv[1]);
1891  
1892            case 'f':
1893              return test_f(0, argv[1]);
1894  
1895            case 'g':
1896              return test_sgid(0, argv[1]);
1897  
1898            case 'G':
1899              return test_grpowned(0, argv[1]);
1900  
1901            case 'k':
1902              return test_sticky(0, argv[1]);
1903  
1904            case 'l':
1905              return test_l(0, argv[1]);
1906  
1907            case 'o':
1908              return test_owned(0, argv[1]);
1909  
1910            case 'O':
1911              return test_rowned(0, argv[1]);
1912  
1913            case 'p':
1914              return test_p(0, argv[1]);
1915  
1916            case 'r':
1917              return test_r(0, argv[1]);
1918  
1919            case 'R':
1920              return test_R(0, argv[1]);
1921  
1922            case 's':
1923              return test_s(0, argv[1]);
1924  
1925            case 'S':
1926              return test_S(0, argv[1]);
1927  
1928            case 'u':
1929              return test_suid(0, argv[1]);
1930  
1931            case 'w':
1932              return test_w(0, argv[1]);
1933  
1934            case 'W':
1935              return test_W(0, argv[1]);
1936  
1937            case 'x':
1938              return test_x(0, argv[1]);
1939  
1940            case 'X':
1941              return test_X(0, argv[1]);
1942  
1943            case 'z':
1944              return test_z(0, argv[1]);
1945          }
1946      }
1947  
1948      if (strchr("MAC", cmd)) {
1949          struct stat st;
1950  
1951          CHECK(1);
1952          if (rb_stat(argv[1], &st) == -1) {
1953              rb_sys_fail(RSTRING(argv[1])->ptr);
1954          }
1955  
1956          switch (cmd) {
1957            case 'A':
1958              return rb_time_new(st.st_atime, 0);
1959            case 'M':
1960              return rb_time_new(st.st_mtime, 0);
1961            case 'C':
1962              return rb_time_new(st.st_ctime, 0);
1963          }
1964      }
1965  
1966      if (strchr("-=<>", cmd)) {
1967          struct stat st1, st2;
1968  
1969          CHECK(2);
1970          if (rb_stat(argv[1], &st1) < 0) return Qfalse;
1971          if (rb_stat(argv[2], &st2) < 0) return Qfalse;
1972  
1973          switch (cmd) {
1974            case '-':
1975              if (st1.st_dev == st2.st_dev && st1.st_ino == st2.st_ino)
1976                  return Qtrue;
1977              return Qfalse;
1978  
1979            case '=':
1980              if (st1.st_mtime == st2.st_mtime) return Qtrue;
1981              return Qfalse;
1982  
1983            case '>':
1984              if (st1.st_mtime > st2.st_mtime) return Qtrue;
1985              return Qfalse;
1986  
1987            case '<':
1988              if (st1.st_mtime < st2.st_mtime) return Qtrue;
1989              return Qfalse;
1990          }
1991      }
1992      /* unknown command */
1993      rb_raise(rb_eArgError, "unknown command ?%c", cmd);
1994      return Qnil;                /* not reached */
1995  }
1996  
1997  static VALUE
1998  rb_stat_s_alloc(klass)
1999      VALUE klass;
2000  {
2001      return stat_new_0(klass, 0);
2002  }
2003  
2004  static VALUE
2005  rb_stat_init(obj, fname)
2006      VALUE obj, fname;
2007  {
2008      struct stat st, *nst;
2009  
2010      SafeStringValue(fname);
2011  
2012      if (stat(RSTRING(fname)->ptr, &st) == -1) {
2013          rb_sys_fail(RSTRING(fname)->ptr);
2014      }
2015      if (DATA_PTR(obj)) {
2016          free(DATA_PTR(obj));
2017          DATA_PTR(obj) = NULL;
2018      }
2019      nst = ALLOC(struct stat);
2020      *nst = st;
2021      DATA_PTR(obj) = nst;
2022  
2023      return Qnil;
2024  }
2025  
2026  static VALUE
2027  rb_stat_become(copy, orig)
2028      VALUE copy, orig;
2029  {
2030      struct stat *nst;
2031  
2032      if (copy == orig) return orig;
2033      rb_check_frozen(copy);
2034      /* need better argument type check */
2035      if (!rb_obj_is_instance_of(orig, rb_obj_class(copy))) {
2036          rb_raise(rb_eTypeError, "wrong argument class");
2037      }
2038      if (DATA_PTR(copy)) {
2039          free(DATA_PTR(copy));
2040          DATA_PTR(copy) = 0;
2041      }
2042      if (DATA_PTR(orig)) {
2043          nst = ALLOC(struct stat);
2044          *nst = *(struct stat*)DATA_PTR(orig);
2045          DATA_PTR(copy) = nst;
2046      }
2047  
2048      return copy;
2049  }
2050  
2051  static VALUE
2052  rb_stat_ftype(obj)
2053      VALUE obj;
2054  {
2055      return rb_file_ftype(get_stat(obj));
2056  }
2057  
2058  static VALUE
2059  rb_stat_d(obj)
2060      VALUE obj;
2061  {
2062      if (S_ISDIR(get_stat(obj)->st_mode)) return Qtrue;
2063      return Qfalse;
2064  }
2065  
2066  static VALUE
2067  rb_stat_p(obj)
2068      VALUE obj;
2069  {
2070  #ifdef S_IFIFO
2071      if (S_ISFIFO(get_stat(obj)->st_mode)) return Qtrue;
2072  
2073  #endif
2074      return Qfalse;
2075  }
2076  
2077  static VALUE
2078  rb_stat_l(obj)
2079      VALUE obj;
2080  {
2081  #ifdef S_ISLNK
2082      if (S_ISLNK(get_stat(obj)->st_mode)) return Qtrue;
2083  #endif
2084      return Qfalse;
2085  }
2086  
2087  static VALUE
2088  rb_stat_S(obj)
2089      VALUE obj;
2090  {
2091  #ifdef S_ISSOCK
2092      if (S_ISSOCK(get_stat(obj)->st_mode)) return Qtrue;
2093  
2094  #endif
2095      return Qfalse;
2096  }
2097  
2098  static VALUE
2099  rb_stat_b(obj)
2100      VALUE obj;
2101  {
2102  #ifdef S_ISBLK
2103      if (S_ISBLK(get_stat(obj)->st_mode)) return Qtrue;
2104  
2105  #endif
2106      return Qfalse;
2107  }
2108  
2109  static VALUE
2110  rb_stat_c(obj)
2111      VALUE obj;
2112  {
2113      if (S_ISCHR(get_stat(obj)->st_mode)) return Qtrue;
2114  
2115      return Qfalse;
2116  }
2117  
2118  static VALUE
2119  rb_stat_owned(obj)
2120      VALUE obj;
2121  {
2122      if (get_stat(obj)->st_uid == geteuid()) return Qtrue;
2123      return Qfalse;
2124  }
2125  
2126  static VALUE
2127  rb_stat_rowned(obj)
2128      VALUE obj;
2129  {
2130      if (get_stat(obj)->st_uid == getuid()) return Qtrue;
2131      return Qfalse;
2132  }
2133  
2134  static VALUE
2135  rb_stat_grpowned(obj)
2136      VALUE obj;
2137  {
2138  #ifndef NT
2139      if (get_stat(obj)->st_gid == getegid()) return Qtrue;
2140  #endif
2141      return Qfalse;
2142  }
2143  
2144  static VALUE
2145  rb_stat_r(obj)
2146      VALUE obj;
2147  {
2148      struct stat *st = get_stat(obj);
2149  
2150  #ifdef S_IRUSR
2151      if (rb_stat_owned(obj))
2152          return st->st_mode & S_IRUSR ? Qtrue : Qfalse;
2153  #endif
2154  #ifdef S_IRGRP
2155      if (rb_stat_grpowned(obj))
2156          return st->st_mode & S_IRGRP ? Qtrue : Qfalse;
2157  #endif
2158  #ifdef S_IROTH
2159      if (!(st->st_mode & S_IROTH)) return Qfalse;
2160  #endif
2161      return Qtrue;
2162  }
2163  
2164  static VALUE
2165  rb_stat_R(obj)
2166      VALUE obj;
2167  {
2168      struct stat *st = get_stat(obj);
2169  
2170  #ifdef S_IRUSR
2171      if (rb_stat_rowned(obj))
2172          return st->st_mode & S_IRUSR ? Qtrue : Qfalse;
2173  #endif
2174  #ifdef S_IRGRP
2175      if (group_member(get_stat(obj)->st_gid))
2176          return st->st_mode & S_IRGRP ? Qtrue : Qfalse;
2177  #endif
2178  #ifdef S_IROTH
2179      if (!(st->st_mode & S_IROTH)) return Qfalse;
2180  #endif
2181      return Qtrue;
2182  }
2183  
2184  static VALUE
2185  rb_stat_w(obj)
2186      VALUE obj;
2187  {
2188      struct stat *st = get_stat(obj);
2189  
2190  #ifdef S_IWUSR
2191      if (rb_stat_owned(obj))
2192          return st->st_mode & S_IWUSR ? Qtrue : Qfalse;
2193  #endif
2194  #ifdef S_IWGRP
2195      if (rb_stat_grpowned(obj))
2196          return st->st_mode & S_IWGRP ? Qtrue : Qfalse;
2197  #endif
2198  #ifdef S_IWOTH
2199      if (!(st->st_mode & S_IWOTH)) return Qfalse;
2200  #endif
2201      return Qtrue;
2202  }
2203  
2204  static VALUE
2205  rb_stat_W(obj)
2206      VALUE obj;
2207  {
2208      struct stat *st = get_stat(obj);
2209  
2210  #ifdef S_IWUSR
2211      if (rb_stat_rowned(obj))
2212          return st->st_mode & S_IWUSR ? Qtrue : Qfalse;
2213  #endif
2214  #ifdef S_IWGRP
2215      if (group_member(get_stat(obj)->st_gid))
2216          return st->st_mode & S_IWGRP ? Qtrue : Qfalse;
2217  #endif
2218  #ifdef S_IWOTH
2219      if (!(st->st_mode & S_IWOTH)) return Qfalse;
2220  #endif
2221      return Qtrue;
2222  }
2223  
2224  static VALUE
2225  rb_stat_x(obj)
2226      VALUE obj;
2227  {
2228      struct stat *st = get_stat(obj);
2229  
2230  #ifdef S_IXUSR
2231      if (rb_stat_owned(obj))
2232          return st->st_mode & S_IXUSR ? Qtrue : Qfalse;
2233  #endif
2234  #ifdef S_IXGRP
2235      if (rb_stat_grpowned(obj))
2236          return st->st_mode & S_IXGRP ? Qtrue : Qfalse;
2237  #endif
2238  #ifdef S_IXOTH
2239      if (!(st->st_mode & S_IXOTH)) return Qfalse;
2240  #endif
2241      return Qtrue;
2242  }
2243  
2244  static VALUE
2245  rb_stat_X(obj)
2246      VALUE obj;
2247  {
2248      struct stat *st = get_stat(obj);
2249  
2250  #ifdef S_IXUSR
2251      if (rb_stat_rowned(obj))
2252          return st->st_mode & S_IXUSR ? Qtrue : Qfalse;
2253  #endif
2254  #ifdef S_IXGRP
2255      if (group_member(get_stat(obj)->st_gid))
2256          return st->st_mode & S_IXGRP ? Qtrue : Qfalse;
2257  #endif
2258  #ifdef S_IXOTH
2259      if (!(st->st_mode & S_IXOTH)) return Qfalse;
2260  #endif
2261      return Qtrue;
2262  }
2263  
2264  static VALUE
2265  rb_stat_f(obj)
2266      VALUE obj;
2267  {
2268      if (S_ISREG(get_stat(obj)->st_mode)) return Qtrue;
2269      return Qfalse;
2270  }
2271  
2272  static VALUE
2273  rb_stat_z(obj)
2274      VALUE obj;
2275  {
2276      if (get_stat(obj)->st_size == 0) return Qtrue;
2277      return Qfalse;
2278  }
2279  
2280  static VALUE
2281  rb_stat_s(obj)
2282      VALUE obj;
2283  {
2284      off_t size = get_stat(obj)->st_size;
2285  
2286      if (size == 0) return Qnil;
2287      return OFFT2NUM(size);
2288  }
2289  
2290  static VALUE
2291  rb_stat_suid(obj)
2292      VALUE obj;
2293  {
2294  #ifdef S_ISUID
2295      if (get_stat(obj)->st_mode & S_ISUID) return Qtrue;
2296  #endif
2297      return Qfalse;
2298  }
2299  
2300  static VALUE
2301  rb_stat_sgid(obj)
2302      VALUE obj;
2303  {
2304  #ifdef S_ISGID
2305      if (get_stat(obj)->st_mode & S_ISGID) return Qtrue;
2306  #endif
2307      return Qfalse;
2308  }
2309  
2310  static VALUE
2311  rb_stat_sticky(obj)
2312      VALUE obj;
2313  {
2314  #ifdef S_ISVTX
2315      if (get_stat(obj)->st_mode & S_ISVTX) return Qtrue;
2316  #endif
2317      return Qfalse;
2318  }
2319  
2320  static VALUE rb_mFConst;
2321  
2322  void
2323  rb_file_const(name, value)
2324      const char *name;
2325      VALUE value;
2326  {
2327      rb_define_const(rb_mFConst, name, value);
2328      rb_define_const(rb_cIO, name, value);
2329      rb_define_const(rb_cFile, name, value);
2330  }
2331  
2332  static int
2333  is_absolute_path(path)
2334      const char *path;
2335  {
2336  #if defined DOSISH || defined __CYGWIN__
2337      if (ISALPHA(path[0]) && path[1] == ':' && isdirsep(path[2])) return 1;
2338      if (isdirsep(path[0]) && isdirsep(path[1])) return 1;
2339  #endif
2340  #ifndef DOSISH
2341      if (path[0] == '/') return 1;
2342  #endif
2343      return 0;
2344  }
2345  
2346  #ifndef DOSISH
2347  static int
2348  path_check_1(path)
2349       VALUE path;
2350  {
2351      struct stat st;
2352      char *p0 = RSTRING(path)->ptr;
2353      char *p = 0, *s;
2354  
2355      if (!is_absolute_path(p0)) {
2356          char *buf = my_getcwd();
2357          VALUE newpath;
2358  
2359          newpath = rb_str_new2(buf);
2360          free(buf);
2361  
2362          rb_str_cat2(newpath, "/");
2363          rb_str_cat2(newpath, p0);
2364          return path_check_1(newpath);
2365      }
2366      for (;;) {
2367  #ifndef S_IWOTH
2368  # define S_IWOTH 002
2369  #endif
2370          if (stat(p0, &st) == 0 && S_ISDIR(st.st_mode) && (st.st_mode & S_IWOTH)
2371  #ifdef S_ISVTX
2372              && (!p || !(st.st_mode & S_ISVTX))
2373  #endif
2374              ) {
2375              rb_warn("Insecure world writable dir %s, mode 0%o", p0, st.st_mode);
2376              if (p) *p = '/';
2377              return 0;
2378          }
2379          s = strrdirsep(p0);
2380          if (p) *p = '/';
2381          if (!s || s == p0) return 1;
2382          p = s;
2383          *p = '\0';
2384      }
2385  }
2386  #endif
2387  
2388  int
2389  rb_path_check(path)
2390      char *path;
2391  {
2392  #ifndef DOSISH
2393      char *p0, *p, *pend;
2394      const char sep = PATH_SEP_CHAR;
2395  
2396      if (!path) return 1;
2397  
2398      pend = path + strlen(path);
2399      p0 = path;
2400      p = strchr(path, sep);
2401      if (!p) p = pend;
2402  
2403      for (;;) {
2404          if (!path_check_1(rb_str_new(p0, p - p0))) {
2405              return 0;           /* not safe */
2406          }
2407          p0 = p + 1;
2408          if (p0 > pend) break;
2409          p = strchr(p0, sep);
2410          if (!p) p = pend;
2411      }
2412  #endif
2413      return 1;
2414  }
2415  
2416  #if defined(__MACOS__) || defined(riscos)
2417  static int
2418  is_macos_native_path(path)
2419      const char *path;
2420  {
2421      if (strchr(path, ':')) return 1;
2422      return 0;
2423  }
2424  #endif
2425  
2426  static int
2427  file_load_ok(file)
2428      char *file;
2429  {
2430      FILE *f;
2431  
2432      if (!file) return 0;
2433      f = fopen(file, "r");
2434      if (f == NULL) return 0;
2435      fclose(f);
2436      return 1;
2437  }
2438  
2439  extern VALUE rb_load_path;
2440  
2441  int
2442  rb_find_file_ext(filep, ext)
2443      VALUE *filep;
2444      const char * const *ext;
2445  {
2446      char *path, *found;
2447      char *f = RSTRING(*filep)->ptr;
2448      VALUE fname;
2449      long i, j;
2450  
2451      if (f[0] == '~') {
2452          fname = rb_file_s_expand_path(1, filep);
2453          if (rb_safe_level() >= 2 && OBJ_TAINTED(fname)) {
2454              rb_raise(rb_eSecurityError, "loading from unsafe file %s", f);
2455          }
2456          f = StringValuePtr(fname);
2457          *filep = fname;
2458      }
2459  
2460      if (is_absolute_path(f)) {
2461          for (i=0; ext[i]; i++) {
2462              fname = rb_str_dup(*filep);
2463              rb_str_cat2(fname, ext[i]);
2464              if (file_load_ok(RSTRING(fname)->ptr)) {
2465                  *filep = fname;
2466                  return i+1;
2467              }
2468          }
2469          return 0;
2470      }
2471  
2472      if (!rb_load_path) return 0;
2473  
2474      Check_Type(rb_load_path, T_ARRAY);
2475      for (i=0;i<RARRAY(rb_load_path)->len;i++) {
2476          VALUE str = RARRAY(rb_load_path)->ptr[i];
2477  
2478          SafeStringValue(str);
2479          if (RSTRING(str)->len == 0) return 0;
2480          path = RSTRING(str)->ptr;
2481          for (j=0; ext[j]; j++) {
2482              fname = rb_str_dup(*filep);
2483              rb_str_cat2(fname, ext[j]);
2484              found = dln_find_file(RSTRING(fname)->ptr, path);
2485              if (found && file_load_ok(found)) {
2486                  *filep = fname;
2487                  return j+1;
2488              }
2489          }
2490      }
2491      return 0;
2492  }
2493  
2494  VALUE
2495  rb_find_file(path)
2496      VALUE path;
2497  {
2498      VALUE tmp;
2499      char *f = RSTRING(path)->ptr;
2500      char *lpath;
2501  
2502      if (f[0] == '~') {
2503          path = rb_file_s_expand_path(1, &path);
2504          if (rb_safe_level() >= 2 && OBJ_TAINTED(path)) {
2505              rb_raise(rb_eSecurityError, "loading from unsafe file %s", f);
2506          }
2507          f = StringValuePtr(path);
2508      }
2509  
2510  #if defined(__MACOS__) || defined(riscos)
2511      if (is_macos_native_path(f)) {
2512          if (rb_safe_level() >= 2 && !rb_path_check(f)) {
2513              rb_raise(rb_eSecurityError, "loading from unsafe file %s", f);
2514          }
2515          if (file_load_ok(f)) return path;
2516      }
2517  #endif
2518  
2519      if (is_absolute_path(f)) {
2520          if (rb_safe_level() >= 2 && !rb_path_check(f)) {
2521              rb_raise(rb_eSecurityError, "loading from unsafe file %s", f);
2522          }
2523          if (file_load_ok(f)) return path;
2524      }
2525  
2526      if (rb_safe_level() >= 4) {
2527          rb_raise(rb_eSecurityError, "loading from non-absolute path %s", f);
2528      }
2529  
2530      if (rb_load_path) {
2531          long i;
2532  
2533          Check_Type(rb_load_path, T_ARRAY);
2534          tmp = rb_ary_new();
2535          for (i=0;i<RARRAY(rb_load_path)->len;i++) {
2536              VALUE str = RARRAY(rb_load_path)->ptr[i];
2537              SafeStringValue(str);
2538              if (RSTRING(str)->len > 0) {
2539                  rb_ary_push(tmp, str);
2540              }
2541          }
2542          tmp = rb_ary_join(tmp, rb_str_new2(PATH_SEP));
2543          if (RSTRING(tmp)->len == 0) {
2544              lpath = 0;
2545          }
2546          else {
2547              lpath = RSTRING(tmp)->ptr;
2548              if (rb_safe_level() >= 2 && !rb_path_check(lpath)) {
2549                  rb_raise(rb_eSecurityError, "loading from unsafe path %s", lpath);
2550              }
2551          }
2552      }
2553      else {
2554          lpath = 0;
2555      }
2556  
2557      if (!lpath) {
2558          return 0;               /* no path, no load */
2559      }
2560      f = dln_find_file(f, lpath);
2561      if (file_load_ok(f)) {
2562          return rb_str_new2(f);
2563      }
2564      return 0;
2565  }
2566  
2567  static void
2568  define_filetest_function(name, func, argc)
2569      const char *name;
2570      VALUE (*func)();
2571      int argc;
2572  {
2573      rb_define_module_function(rb_mFileTest, name, func, argc);
2574      rb_define_singleton_method(rb_cFile, name, func, argc);
2575  }
2576  
2577  void
2578  Init_File()
2579  {
2580      rb_mFileTest = rb_define_module("FileTest");
2581      rb_cFile = rb_define_class("File", rb_cIO);
2582  
2583      define_filetest_function("directory?", test_d, 1);
2584      define_filetest_function("exist?", test_e, 1);
2585      define_filetest_function("exists?", test_e, 1); /* temporary */
2586      define_filetest_function("readable?", test_r, 1);
2587      define_filetest_function("readable_real?", test_R, 1);
2588      define_filetest_function("writable?", test_w, 1);
2589      define_filetest_function("writable_real?", test_W, 1);
2590      define_filetest_function("executable?", test_x, 1);
2591      define_filetest_function("executable_real?", test_X, 1);
2592      define_filetest_function("file?", test_f, 1);
2593      define_filetest_function("zero?", test_z, 1);
2594      define_filetest_function("size?", test_s, 1);
2595      define_filetest_function("size", rb_file_s_size, 1);
2596      define_filetest_function("owned?", test_owned, 1);
2597      define_filetest_function("grpowned?", test_grpowned, 1);
2598  
2599      define_filetest_function("pipe?", test_p, 1);
2600      define_filetest_function("symlink?", test_l, 1);
2601      define_filetest_function("socket?", test_S, 1);
2602  
2603      define_filetest_function("blockdev?", test_b, 1);
2604      define_filetest_function("chardev?", test_c, 1);
2605  
2606      define_filetest_function("setuid?", test_suid, 1);
2607      define_filetest_function("setgid?", test_sgid, 1);
2608      define_filetest_function("sticky?", test_sticky, 1);
2609  
2610      rb_define_singleton_method(rb_cFile, "stat",  rb_file_s_stat, 1);
2611      rb_define_singleton_method(rb_cFile, "lstat", rb_file_s_lstat, 1);
2612      rb_define_singleton_method(rb_cFile, "ftype", rb_file_s_ftype, 1);
2613  
2614      rb_define_singleton_method(rb_cFile, "atime", rb_file_s_atime, 1);
2615      rb_define_singleton_method(rb_cFile, "mtime", rb_file_s_mtime, 1);
2616      rb_define_singleton_method(rb_cFile, "ctime", rb_file_s_ctime, 1);
2617  
2618      rb_define_singleton_method(rb_cFile, "utime", rb_file_s_utime, -1);
2619      rb_define_singleton_method(rb_cFile, "chmod", rb_file_s_chmod, -1);
2620      rb_define_singleton_method(rb_cFile, "chown", rb_file_s_chown, -1);
2621      rb_define_singleton_method(rb_cFile, "lchmod", rb_file_s_lchmod, -1);
2622      rb_define_singleton_method(rb_cFile, "lchown", rb_file_s_lchown, -1);
2623  
2624      rb_define_singleton_method(rb_cFile, "link", rb_file_s_link, 2);
2625      rb_define_singleton_method(rb_cFile, "symlink", rb_file_s_symlink, 2);
2626      rb_define_singleton_method(rb_cFile, "readlink", rb_file_s_readlink, 1);
2627  
2628      rb_define_singleton_method(rb_cFile, "unlink", rb_file_s_unlink, -2);
2629      rb_define_singleton_method(rb_cFile, "delete", rb_file_s_unlink, -2);
2630      rb_define_singleton_method(rb_cFile, "rename", rb_file_s_rename, 2);
2631      rb_define_singleton_method(rb_cFile, "umask", rb_file_s_umask, -1);
2632      rb_define_singleton_method(rb_cFile, "truncate", rb_file_s_truncate, 2);
2633      rb_define_singleton_method(rb_cFile, "expand_path", rb_file_s_expand_path, -1);
2634      rb_define_singleton_method(rb_cFile, "basename", rb_file_s_basename, -1);
2635      rb_define_singleton_method(rb_cFile, "dirname", rb_file_s_dirname, 1);
2636      rb_define_singleton_method(rb_cFile, "extname", rb_file_s_extname, 1);
2637  
2638      separator = rb_obj_freeze(rb_str_new2("/"));
2639      rb_define_const(rb_cFile, "Separator", separator);
2640      rb_define_const(rb_cFile, "SEPARATOR", separator);
2641      rb_define_singleton_method(rb_cFile, "split",  rb_file_s_split, 1);
2642      rb_define_singleton_method(rb_cFile, "join",   rb_file_s_join, -2);
2643  
2644  #if defined DOSISH && !defined __CYGWIN__
2645      rb_define_const(rb_cFile, "ALT_SEPARATOR", rb_obj_freeze(rb_str_new2("\\")));
2646  #else
2647      rb_define_const(rb_cFile, "ALT_SEPARATOR", Qnil);
2648  #endif
2649      rb_define_const(rb_cFile, "PATH_SEPARATOR", rb_obj_freeze(rb_str_new2(PATH_SEP)));
2650  
2651      rb_define_method(rb_cIO, "stat",  rb_io_stat, 0); /* this is IO's method */
2652      rb_define_method(rb_cFile, "lstat",  rb_file_lstat, 0);
2653  
2654      rb_define_method(rb_cFile, "atime", rb_file_atime, 0);
2655      rb_define_method(rb_cFile, "mtime", rb_file_mtime, 0);
2656      rb_define_method(rb_cFile, "ctime", rb_file_ctime, 0);
2657  
2658      rb_define_method(rb_cFile, "chmod", rb_file_chmod, 1);
2659      rb_define_method(rb_cFile, "chown", rb_file_chown, 2);
2660      rb_define_method(rb_cFile, "truncate", rb_file_truncate, 1);
2661  
2662      rb_define_method(rb_cFile, "flock", rb_file_flock, 1);
2663  
2664      rb_mFConst = rb_define_module_under(rb_cFile, "Constants");
2665      rb_include_module(rb_cFile, rb_mFConst);
2666      rb_file_const("LOCK_SH", INT2FIX(LOCK_SH));
2667      rb_file_const("LOCK_EX", INT2FIX(LOCK_EX));
2668      rb_file_const("LOCK_UN", INT2FIX(LOCK_UN));
2669      rb_file_const("LOCK_NB", INT2FIX(LOCK_NB));
2670  
2671      rb_define_method(rb_cFile, "path",  rb_file_path, 0);
2672      rb_define_global_function("test", rb_f_test, -1);
2673  
2674      rb_cStat = rb_define_class_under(rb_cFile, "Stat", rb_cObject);
2675      rb_define_singleton_method(rb_cStat, "allocate",  rb_stat_s_alloc, 0);
2676      rb_define_method(rb_cStat, "initialize", rb_stat_init, 1);
2677      rb_define_method(rb_cStat, "become", rb_stat_become, 1);
2678  
2679      rb_include_module(rb_cStat, rb_mComparable);
2680  
2681      rb_define_method(rb_cStat, "<=>", rb_stat_cmp, 1);
2682  
2683      rb_define_method(rb_cStat, "dev", rb_stat_dev, 0);
2684      rb_define_method(rb_cStat, "ino", rb_stat_ino, 0);
2685      rb_define_method(rb_cStat, "mode", rb_stat_mode, 0);
2686      rb_define_method(rb_cStat, "nlink", rb_stat_nlink, 0);
2687      rb_define_method(rb_cStat, "uid", rb_stat_uid, 0);
2688      rb_define_method(rb_cStat, "gid", rb_stat_gid, 0);
2689      rb_define_method(rb_cStat, "rdev", rb_stat_rdev, 0);
2690      rb_define_method(rb_cStat, "rdev_major", rb_stat_rdev_major, 0);
2691      rb_define_method(rb_cStat, "rdev_minor", rb_stat_rdev_minor, 0);
2692      rb_define_method(rb_cStat, "size", rb_stat_size, 0);
2693      rb_define_method(rb_cStat, "blksize", rb_stat_blksize, 0);
2694      rb_define_method(rb_cStat, "blocks", rb_stat_blocks, 0);
2695      rb_define_method(rb_cStat, "atime", rb_stat_atime, 0);
2696      rb_define_method(rb_cStat, "mtime", rb_stat_mtime, 0);
2697      rb_define_method(rb_cStat, "ctime", rb_stat_ctime, 0);
2698  
2699      rb_define_method(rb_cStat, "inspect", rb_stat_inspect, 0);
2700  
2701      rb_define_method(rb_cStat, "ftype", rb_stat_ftype, 0);
2702  
2703      rb_define_method(rb_cStat, "directory?",  rb_stat_d, 0);
2704      rb_define_method(rb_cStat, "readable?",  rb_stat_r, 0);
2705      rb_define_method(rb_cStat, "readable_real?",  rb_stat_R, 0);
2706      rb_define_method(rb_cStat, "writable?",  rb_stat_w, 0);
2707      rb_define_method(rb_cStat, "writable_real?",  rb_stat_W, 0);
2708      rb_define_method(rb_cStat, "executable?",  rb_stat_x, 0);
2709      rb_define_method(rb_cStat, "executable_real?",  rb_stat_X, 0);
2710      rb_define_method(rb_cStat, "file?",  rb_stat_f, 0);
2711      rb_define_method(rb_cStat, "zero?",  rb_stat_z, 0);
2712      rb_define_method(rb_cStat, "size?",  rb_stat_s, 0);
2713      rb_define_method(rb_cStat, "owned?",  rb_stat_owned, 0);
2714      rb_define_method(rb_cStat, "grpowned?",  rb_stat_grpowned, 0);
2715  
2716      rb_define_method(rb_cStat, "pipe?",  rb_stat_p, 0);
2717      rb_define_method(rb_cStat, "symlink?",  rb_stat_l, 0);
2718      rb_define_method(rb_cStat, "socket?",  rb_stat_S, 0);
2719  
2720      rb_define_method(rb_cStat, "blockdev?",  rb_stat_b, 0);
2721      rb_define_method(rb_cStat, "chardev?",  rb_stat_c, 0);
2722  
2723      rb_define_method(rb_cStat, "setuid?",  rb_stat_suid, 0);
2724      rb_define_method(rb_cStat, "setgid?",  rb_stat_sgid, 0);
2725      rb_define_method(rb_cStat, "sticky?",  rb_stat_sticky, 0);
2726  }