~ubuntu-branches/ubuntu/hardy/ruby1.8/hardy-updates

« back to all changes in this revision

Viewing changes to file.c

  • Committer: Bazaar Package Importer
  • Author(s): akira yamada
  • Date: 2007-03-13 22:11:58 UTC
  • mfrom: (1.1.5 upstream)
  • Revision ID: james.westby@ubuntu.com-20070313221158-h3oql37brlaf2go2
Tags: 1.8.6-1
* new upstream version, 1.8.6.
* libruby1.8 conflicts with libopenssl-ruby1.8 (< 1.8.6) (closes: #410018)
* changed packaging style to cdbs from dbs.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/**********************************************************************
 
2
 
 
3
  file.c -
 
4
 
 
5
  $Author: knu $
 
6
  $Date: 2007-03-04 23:51:31 +0900 (Sun, 04 Mar 2007) $
 
7
  created at: Mon Nov 15 12:24:34 JST 1993
 
8
 
 
9
  Copyright (C) 1993-2003 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 _WIN32
 
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
#endif
 
38
#ifndef MAXPATHLEN
 
39
# define MAXPATHLEN 1024
 
40
#endif
 
41
 
 
42
#include <time.h>
 
43
 
 
44
VALUE rb_time_new _((time_t, time_t));
 
45
 
 
46
#ifdef HAVE_UTIME_H
 
47
#include <utime.h>
 
48
#elif defined HAVE_SYS_UTIME_H
 
49
#include <sys/utime.h>
 
50
#endif
 
51
 
 
52
#ifdef HAVE_PWD_H
 
53
#include <pwd.h>
 
54
#endif
 
55
 
 
56
#ifndef HAVE_STRING_H
 
57
char *strrchr _((const char*,const char));
 
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_LSTAT && !defined lstat
 
68
#define lstat stat
 
69
#endif
 
70
#if !HAVE_FSEEKO && !defined(fseeko)
 
71
# define fseeko  fseek
 
72
#endif
 
73
 
 
74
#ifdef __BEOS__ /* should not change ID if -1 */
 
75
static int
 
76
be_chown(const char *path, uid_t owner, gid_t group)
 
77
{
 
78
    if (owner == -1 || group == -1) {
 
79
        struct stat st;
 
80
        if (stat(path, &st) < 0) return -1;
 
81
        if (owner == -1) owner = st.st_uid;
 
82
        if (group == -1) group = st.st_gid;
 
83
    }
 
84
    return chown(path, owner, group);
 
85
}
 
86
#define chown be_chown
 
87
static int
 
88
be_fchown(int fd, uid_t owner, gid_t group)
 
89
{
 
90
    if (owner == -1 || group == -1) {
 
91
        struct stat st;
 
92
        if (fstat(fd, &st) < 0) return -1;
 
93
        if (owner == -1) owner = st.st_uid;
 
94
        if (group == -1) group = st.st_gid;
 
95
    }
 
96
    return fchown(fd, owner, group);
 
97
}
 
98
#define fchown be_fchown
 
99
#endif /* __BEOS__ */
 
100
 
 
101
VALUE rb_cFile;
 
102
VALUE rb_mFileTest;
 
103
VALUE rb_cStat;
 
104
 
 
105
static long apply2files _((void (*)(const char *, void *), VALUE, void *));
 
106
static long
 
107
apply2files(func, vargs, arg)
 
108
    void (*func)_((const char *, void *));
 
109
    VALUE vargs;
 
110
    void *arg;
 
111
{
 
112
    long i;
 
113
    VALUE path;
 
114
    struct RArray *args = RARRAY(vargs);
 
115
 
 
116
    for (i=0; i<args->len; i++) {
 
117
        path = args->ptr[i];
 
118
        SafeStringValue(path);
 
119
        (*func)(StringValueCStr(path), arg);
 
120
    }
 
121
 
 
122
    return args->len;
 
123
}
 
124
 
 
125
/*
 
126
 *  call-seq:
 
127
 *     file.path -> filename
 
128
 *  
 
129
 *  Returns the pathname used to create <i>file</i> as a string. Does
 
130
 *  not normalize the name.
 
131
 *     
 
132
 *     File.new("testfile").path               #=> "testfile"
 
133
 *     File.new("/tmp/../tmp/xxx", "w").path   #=> "/tmp/../tmp/xxx"
 
134
 *     
 
135
 */
 
136
 
 
137
static VALUE
 
138
rb_file_path(obj)
 
139
    VALUE obj;
 
140
{
 
141
    OpenFile *fptr;
 
142
 
 
143
    fptr = RFILE(rb_io_taint_check(obj))->fptr;
 
144
    rb_io_check_initialized(fptr);
 
145
    if (!fptr->path) return Qnil;
 
146
    return rb_tainted_str_new2(fptr->path);
 
147
}
 
148
 
 
149
static VALUE
 
150
stat_new_0(klass, st)
 
151
    VALUE klass;
 
152
    struct stat *st;
 
153
{
 
154
    struct stat *nst = 0;
 
155
 
 
156
    if (st) {
 
157
        nst = ALLOC(struct stat);
 
158
        *nst = *st;
 
159
    }
 
160
    return Data_Wrap_Struct(klass, NULL, free, nst);
 
161
}
 
162
 
 
163
static VALUE
 
164
stat_new(st)
 
165
    struct stat *st;
 
166
{
 
167
    return stat_new_0(rb_cStat, st);
 
168
}
 
169
 
 
170
static struct stat*
 
171
get_stat(self)
 
172
    VALUE self;
 
173
{
 
174
    struct stat* st;
 
175
    Data_Get_Struct(self, struct stat, st);
 
176
    if (!st) rb_raise(rb_eTypeError, "uninitialized File::Stat");
 
177
    return st;
 
178
}
 
179
 
 
180
/*
 
181
 *  call-seq:
 
182
 *     stat <=> other_stat    => -1, 0, 1
 
183
 *  
 
184
 *  Compares <code>File::Stat</code> objects by comparing their
 
185
 *  respective modification times.
 
186
 *     
 
187
 *     f1 = File.new("f1", "w")
 
188
 *     sleep 1
 
189
 *     f2 = File.new("f2", "w")
 
190
 *     f1.stat <=> f2.stat   #=> -1
 
191
 */
 
192
 
 
193
static VALUE
 
194
rb_stat_cmp(self, other)
 
195
    VALUE self, other;
 
196
{
 
197
    if (rb_obj_is_kind_of(other, rb_obj_class(self))) {
 
198
        time_t t1 = get_stat(self)->st_mtime;
 
199
        time_t t2 = get_stat(other)->st_mtime;
 
200
        if (t1 == t2)
 
201
            return INT2FIX(0);
 
202
        else if (t1 < t2)
 
203
            return INT2FIX(-1);
 
204
        else
 
205
            return INT2FIX(1);
 
206
    }
 
207
    return Qnil;
 
208
}
 
209
 
 
210
static VALUE rb_stat_dev _((VALUE));
 
211
static VALUE rb_stat_ino _((VALUE));
 
212
static VALUE rb_stat_mode _((VALUE));
 
213
static VALUE rb_stat_nlink _((VALUE));
 
214
static VALUE rb_stat_uid _((VALUE));
 
215
static VALUE rb_stat_gid _((VALUE));
 
216
static VALUE rb_stat_rdev _((VALUE));
 
217
static VALUE rb_stat_size _((VALUE));
 
218
static VALUE rb_stat_blksize _((VALUE));
 
219
static VALUE rb_stat_blocks _((VALUE));
 
220
static VALUE rb_stat_atime _((VALUE));
 
221
static VALUE rb_stat_mtime _((VALUE));
 
222
static VALUE rb_stat_ctime _((VALUE));
 
223
 
 
224
/*
 
225
 *  call-seq:
 
226
 *     stat.dev    => fixnum
 
227
 *  
 
228
 *  Returns an integer representing the device on which <i>stat</i>
 
229
 *  resides.
 
230
 *     
 
231
 *     File.stat("testfile").dev   #=> 774
 
232
 */
 
233
 
 
234
static VALUE
 
235
rb_stat_dev(self)
 
236
    VALUE self;
 
237
{
 
238
    return INT2NUM(get_stat(self)->st_dev);
 
239
}
 
240
 
 
241
/*
 
242
 *  call-seq:
 
243
 *     stat.dev_major   => fixnum
 
244
 *  
 
245
 *  Returns the major part of <code>File_Stat#dev</code> or
 
246
 *  <code>nil</code>.
 
247
 *     
 
248
 *     File.stat("/dev/fd1").dev_major   #=> 2
 
249
 *     File.stat("/dev/tty").dev_major   #=> 5
 
250
 */
 
251
 
 
252
static VALUE
 
253
rb_stat_dev_major(self)
 
254
    VALUE self;
 
255
{
 
256
#if defined(major)
 
257
    long dev = get_stat(self)->st_dev;
 
258
    return ULONG2NUM(major(dev));
 
259
#else
 
260
    return Qnil;
 
261
#endif
 
262
}
 
263
 
 
264
/*
 
265
 *  call-seq:
 
266
 *     stat.dev_minor   => fixnum
 
267
 *  
 
268
 *  Returns the minor part of <code>File_Stat#dev</code> or
 
269
 *  <code>nil</code>.
 
270
 *     
 
271
 *     File.stat("/dev/fd1").dev_minor   #=> 1
 
272
 *     File.stat("/dev/tty").dev_minor   #=> 0
 
273
 */
 
274
 
 
275
static VALUE
 
276
rb_stat_dev_minor(self)
 
277
    VALUE self;
 
278
{
 
279
#if defined(minor)
 
280
    long dev = get_stat(self)->st_dev;
 
281
    return ULONG2NUM(minor(dev));
 
282
#else
 
283
    return Qnil;
 
284
#endif
 
285
}
 
286
 
 
287
 
 
288
/*
 
289
 *  call-seq:
 
290
 *     stat.ino   => fixnum
 
291
 *  
 
292
 *  Returns the inode number for <i>stat</i>.
 
293
 *     
 
294
 *     File.stat("testfile").ino   #=> 1083669
 
295
 *     
 
296
 */
 
297
 
 
298
static VALUE
 
299
rb_stat_ino(self)
 
300
    VALUE self;
 
301
{
 
302
#ifdef HUGE_ST_INO
 
303
    return ULL2NUM(get_stat(self)->st_ino);
 
304
#else
 
305
    return ULONG2NUM(get_stat(self)->st_ino);
 
306
#endif
 
307
}
 
308
 
 
309
/*
 
310
 *  call-seq:
 
311
 *     stat.mode   => fixnum
 
312
 *  
 
313
 *  Returns an integer representing the permission bits of
 
314
 *  <i>stat</i>. The meaning of the bits is platform dependent; on
 
315
 *  Unix systems, see <code>stat(2)</code>.
 
316
 *     
 
317
 *     File.chmod(0644, "testfile")   #=> 1
 
318
 *     s = File.stat("testfile")
 
319
 *     sprintf("%o", s.mode)          #=> "100644"
 
320
 */
 
321
 
 
322
static VALUE
 
323
rb_stat_mode(self)
 
324
    VALUE self;
 
325
{
 
326
#ifdef __BORLANDC__
 
327
    return UINT2NUM((unsigned short)(get_stat(self)->st_mode));
 
328
#else
 
329
    return UINT2NUM(get_stat(self)->st_mode);
 
330
#endif
 
331
}
 
332
 
 
333
/*
 
334
 *  call-seq:
 
335
 *     stat.nlink   => fixnum
 
336
 *  
 
337
 *  Returns the number of hard links to <i>stat</i>.
 
338
 *     
 
339
 *     File.stat("testfile").nlink             #=> 1
 
340
 *     File.link("testfile", "testfile.bak")   #=> 0
 
341
 *     File.stat("testfile").nlink             #=> 2
 
342
 *     
 
343
 */
 
344
 
 
345
static VALUE
 
346
rb_stat_nlink(self)
 
347
    VALUE self;
 
348
{
 
349
    return UINT2NUM(get_stat(self)->st_nlink);
 
350
}
 
351
 
 
352
 
 
353
/*
 
354
 *  call-seq:
 
355
 *     stat.uid    => fixnum
 
356
 *  
 
357
 *  Returns the numeric user id of the owner of <i>stat</i>.
 
358
 *     
 
359
 *     File.stat("testfile").uid   #=> 501
 
360
 *     
 
361
 */
 
362
 
 
363
static VALUE
 
364
rb_stat_uid(self)
 
365
    VALUE self;
 
366
{
 
367
    return UINT2NUM(get_stat(self)->st_uid);
 
368
}
 
369
 
 
370
/*
 
371
 *  call-seq:
 
372
 *     stat.gid   => fixnum
 
373
 *  
 
374
 *  Returns the numeric group id of the owner of <i>stat</i>.
 
375
 *     
 
376
 *     File.stat("testfile").gid   #=> 500
 
377
 *     
 
378
 */
 
379
 
 
380
static VALUE
 
381
rb_stat_gid(self)
 
382
    VALUE self;
 
383
{
 
384
    return UINT2NUM(get_stat(self)->st_gid);
 
385
}
 
386
 
 
387
 
 
388
/*
 
389
 *  call-seq:
 
390
 *     stat.rdev   =>  fixnum or nil
 
391
 *  
 
392
 *  Returns an integer representing the device type on which
 
393
 *  <i>stat</i> resides. Returns <code>nil</code> if the operating
 
394
 *  system doesn't support this feature.
 
395
 *     
 
396
 *     File.stat("/dev/fd1").rdev   #=> 513
 
397
 *     File.stat("/dev/tty").rdev   #=> 1280
 
398
 */
 
399
 
 
400
static VALUE
 
401
rb_stat_rdev(self)
 
402
    VALUE self;
 
403
{
 
404
#ifdef HAVE_ST_RDEV
 
405
    return ULONG2NUM(get_stat(self)->st_rdev);
 
406
#else
 
407
    return Qnil;
 
408
#endif
 
409
}
 
410
 
 
411
/*
 
412
 *  call-seq:
 
413
 *     stat.rdev_major   => fixnum
 
414
 *  
 
415
 *  Returns the major part of <code>File_Stat#rdev</code> or
 
416
 *  <code>nil</code>.
 
417
 *     
 
418
 *     File.stat("/dev/fd1").rdev_major   #=> 2
 
419
 *     File.stat("/dev/tty").rdev_major   #=> 5
 
420
 */
 
421
 
 
422
static VALUE
 
423
rb_stat_rdev_major(self)
 
424
    VALUE self;
 
425
{
 
426
#if defined(HAVE_ST_RDEV) && defined(major)
 
427
    long rdev = get_stat(self)->st_rdev;
 
428
    return ULONG2NUM(major(rdev));
 
429
#else
 
430
    return Qnil;
 
431
#endif
 
432
}
 
433
 
 
434
/*
 
435
 *  call-seq:
 
436
 *     stat.rdev_minor   => fixnum
 
437
 *  
 
438
 *  Returns the minor part of <code>File_Stat#rdev</code> or
 
439
 *  <code>nil</code>.
 
440
 *     
 
441
 *     File.stat("/dev/fd1").rdev_minor   #=> 1
 
442
 *     File.stat("/dev/tty").rdev_minor   #=> 0
 
443
 */
 
444
 
 
445
static VALUE
 
446
rb_stat_rdev_minor(self)
 
447
    VALUE self;
 
448
{
 
449
#if defined(HAVE_ST_RDEV) && defined(minor)
 
450
    long rdev = get_stat(self)->st_rdev;
 
451
    return ULONG2NUM(minor(rdev));
 
452
#else
 
453
    return Qnil;
 
454
#endif
 
455
}
 
456
 
 
457
/*
 
458
 *  call-seq:
 
459
 *     stat.size    => fixnum
 
460
 *  
 
461
 *  Returns the size of <i>stat</i> in bytes.
 
462
 *     
 
463
 *     File.stat("testfile").size   #=> 66
 
464
 */
 
465
 
 
466
static VALUE
 
467
rb_stat_size(self)
 
468
    VALUE self;
 
469
{
 
470
    return OFFT2NUM(get_stat(self)->st_size);
 
471
}
 
472
 
 
473
/*
 
474
 *  call-seq:
 
475
 *     stat.blksize   => integer or nil
 
476
 *  
 
477
 *  Returns the native file system's block size. Will return <code>nil</code>
 
478
 *  on platforms that don't support this information.
 
479
 *     
 
480
 *     File.stat("testfile").blksize   #=> 4096
 
481
 *     
 
482
 */
 
483
 
 
484
static VALUE
 
485
rb_stat_blksize(self)
 
486
    VALUE self;
 
487
{
 
488
#ifdef HAVE_ST_BLKSIZE
 
489
    return ULONG2NUM(get_stat(self)->st_blksize);
 
490
#else
 
491
    return Qnil;
 
492
#endif
 
493
}
 
494
 
 
495
/*
 
496
 *  call-seq:
 
497
 *     stat.blocks    => integer or nil
 
498
 *  
 
499
 *  Returns the number of native file system blocks allocated for this
 
500
 *  file, or <code>nil</code> if the operating system doesn't 
 
501
 *  support this feature.
 
502
 *     
 
503
 *     File.stat("testfile").blocks   #=> 2
 
504
 */
 
505
 
 
506
static VALUE
 
507
rb_stat_blocks(self)
 
508
    VALUE self;
 
509
{
 
510
#ifdef HAVE_ST_BLOCKS
 
511
    return ULONG2NUM(get_stat(self)->st_blocks);
 
512
#else
 
513
    return Qnil;
 
514
#endif
 
515
}
 
516
 
 
517
 
 
518
/*
 
519
 *  call-seq:
 
520
 *     stat.atime   => time
 
521
 *  
 
522
 *  Returns the last access time for this file as an object of class
 
523
 *  <code>Time</code>.
 
524
 *     
 
525
 *     File.stat("testfile").atime   #=> Wed Dec 31 18:00:00 CST 1969
 
526
 *     
 
527
 */
 
528
 
 
529
static VALUE
 
530
rb_stat_atime(self)
 
531
    VALUE self;
 
532
{
 
533
    return rb_time_new(get_stat(self)->st_atime, 0);
 
534
}
 
535
 
 
536
/*
 
537
 *  call-seq:
 
538
 *     stat.mtime -> aTime
 
539
 *  
 
540
 *  Returns the modification time of <i>stat</i>.
 
541
 *     
 
542
 *     File.stat("testfile").mtime   #=> Wed Apr 09 08:53:14 CDT 2003
 
543
 *     
 
544
 */
 
545
 
 
546
static VALUE
 
547
rb_stat_mtime(self)
 
548
    VALUE self;
 
549
{
 
550
    return rb_time_new(get_stat(self)->st_mtime, 0);
 
551
}
 
552
 
 
553
/*
 
554
 *  call-seq:
 
555
 *     stat.ctime -> aTime
 
556
 *  
 
557
 *  Returns the change time for <i>stat</i> (that is, the time
 
558
 *  directory information about the file was changed, not the file
 
559
 *  itself).
 
560
 *     
 
561
 *     File.stat("testfile").ctime   #=> Wed Apr 09 08:53:14 CDT 2003
 
562
 *     
 
563
 */
 
564
 
 
565
static VALUE
 
566
rb_stat_ctime(self)
 
567
    VALUE self;
 
568
{
 
569
    return rb_time_new(get_stat(self)->st_ctime, 0);
 
570
}
 
571
 
 
572
/*
 
573
 * call-seq:
 
574
 *   stat.inspect  =>  string
 
575
 *
 
576
 * Produce a nicely formatted description of <i>stat</i>.
 
577
 *
 
578
 *   File.stat("/etc/passwd").inspect
 
579
 *      #=> "#<File::Stat dev=0xe000005, ino=1078078, mode=0100644, 
 
580
 *           nlink=1, uid=0, gid=0, rdev=0x0, size=1374, blksize=4096, 
 
581
 *           blocks=8, atime=Wed Dec 10 10:16:12 CST 2003, 
 
582
 *           mtime=Fri Sep 12 15:41:41 CDT 2003, 
 
583
 *           ctime=Mon Oct 27 11:20:27 CST 2003>"
 
584
 */
 
585
 
 
586
static VALUE
 
587
rb_stat_inspect(self)
 
588
    VALUE self;
 
589
{
 
590
    VALUE str;
 
591
    int i;
 
592
    static const struct {
 
593
        const char *name;
 
594
        VALUE (*func)_((VALUE));
 
595
    } member[] = {
 
596
        {"dev",     rb_stat_dev},
 
597
        {"ino",     rb_stat_ino},
 
598
        {"mode",    rb_stat_mode},
 
599
        {"nlink",   rb_stat_nlink},
 
600
        {"uid",     rb_stat_uid},
 
601
        {"gid",     rb_stat_gid},
 
602
        {"rdev",    rb_stat_rdev},
 
603
        {"size",    rb_stat_size},
 
604
        {"blksize", rb_stat_blksize},
 
605
        {"blocks",  rb_stat_blocks},
 
606
        {"atime",   rb_stat_atime},
 
607
        {"mtime",   rb_stat_mtime},
 
608
        {"ctime",   rb_stat_ctime},
 
609
    };
 
610
 
 
611
    str = rb_str_buf_new2("#<");
 
612
    rb_str_buf_cat2(str, rb_obj_classname(self));
 
613
    rb_str_buf_cat2(str, " ");
 
614
 
 
615
    for (i = 0; i < sizeof(member)/sizeof(member[0]); i++) {
 
616
        VALUE v;
 
617
 
 
618
        if (i > 0) {
 
619
            rb_str_buf_cat2(str, ", ");
 
620
        }
 
621
        rb_str_buf_cat2(str, member[i].name);
 
622
        rb_str_buf_cat2(str, "=");
 
623
        v = (*member[i].func)(self);
 
624
        if (i == 2) {           /* mode */
 
625
            char buf[32];
 
626
 
 
627
            sprintf(buf, "0%lo", NUM2ULONG(v));
 
628
            rb_str_buf_cat2(str, buf);
 
629
        }
 
630
        else if (i == 0 || i == 6) { /* dev/rdev */
 
631
            char buf[32];
 
632
 
 
633
            sprintf(buf, "0x%lx", NUM2ULONG(v));
 
634
            rb_str_buf_cat2(str, buf);
 
635
        }
 
636
        else {
 
637
            rb_str_append(str, rb_inspect(v));
 
638
        }
 
639
    }
 
640
    rb_str_buf_cat2(str, ">");
 
641
    OBJ_INFECT(str, self);
 
642
 
 
643
    return str;
 
644
}
 
645
 
 
646
static int
 
647
rb_stat(file, st)
 
648
    VALUE file;
 
649
    struct stat *st;
 
650
{
 
651
    VALUE tmp;
 
652
 
 
653
    tmp = rb_check_convert_type(file, T_FILE, "IO", "to_io");
 
654
    if (!NIL_P(tmp)) {
 
655
        OpenFile *fptr;
 
656
 
 
657
        rb_secure(2);
 
658
        GetOpenFile(tmp, fptr);
 
659
        return fstat(fileno(fptr->f), st);
 
660
    }
 
661
    SafeStringValue(file);
 
662
    return stat(StringValueCStr(file), st);
 
663
}
 
664
 
 
665
#ifdef _WIN32
 
666
static HANDLE
 
667
w32_io_info(file, st)
 
668
    VALUE *file;
 
669
    BY_HANDLE_FILE_INFORMATION *st;
 
670
{
 
671
    VALUE tmp;
 
672
    HANDLE f, ret = 0;
 
673
 
 
674
    tmp = rb_check_convert_type(*file, T_FILE, "IO", "to_io");
 
675
    if (!NIL_P(tmp)) {
 
676
        OpenFile *fptr;
 
677
 
 
678
        GetOpenFile(tmp, fptr);
 
679
        f = (HANDLE)rb_w32_get_osfhandle(fileno(fptr->f));
 
680
        if (f == (HANDLE)-1) return INVALID_HANDLE_VALUE;
 
681
    }
 
682
    else {
 
683
        SafeStringValue(*file);
 
684
        f = CreateFile(StringValueCStr(*file), 0,
 
685
                       FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
 
686
                       rb_w32_iswin95() ? 0 : FILE_FLAG_BACKUP_SEMANTICS, NULL);
 
687
        if (f == INVALID_HANDLE_VALUE) return f;
 
688
        ret = f;
 
689
    }
 
690
    if (GetFileType(f) == FILE_TYPE_DISK) {
 
691
        ZeroMemory(st, sizeof(*st));
 
692
        if (GetFileInformationByHandle(f, st)) return ret;
 
693
    }
 
694
    if (ret) CloseHandle(ret);
 
695
    return INVALID_HANDLE_VALUE;
 
696
}
 
697
#endif
 
698
 
 
699
/*
 
700
 *  call-seq:
 
701
 *     File.stat(file_name)   =>  stat
 
702
 *  
 
703
 *  Returns a <code>File::Stat</code> object for the named file (see
 
704
 *  <code>File::Stat</code>).
 
705
 *     
 
706
 *     File.stat("testfile").mtime   #=> Tue Apr 08 12:58:04 CDT 2003
 
707
 *     
 
708
 */
 
709
 
 
710
static VALUE
 
711
rb_file_s_stat(klass, fname)
 
712
    VALUE klass, fname;
 
713
{
 
714
    struct stat st;
 
715
 
 
716
    SafeStringValue(fname);
 
717
    if (rb_stat(fname, &st) < 0) {
 
718
        rb_sys_fail(StringValueCStr(fname));
 
719
    }
 
720
    return stat_new(&st);
 
721
}
 
722
 
 
723
/*
 
724
 *  call-seq:
 
725
 *     ios.stat    => stat
 
726
 *  
 
727
 *  Returns status information for <em>ios</em> as an object of type
 
728
 *  <code>File::Stat</code>.
 
729
 *     
 
730
 *     f = File.new("testfile")
 
731
 *     s = f.stat
 
732
 *     "%o" % s.mode   #=> "100644"
 
733
 *     s.blksize       #=> 4096
 
734
 *     s.atime         #=> Wed Apr 09 08:53:54 CDT 2003
 
735
 *     
 
736
 */
 
737
 
 
738
static VALUE
 
739
rb_io_stat(obj)
 
740
    VALUE obj;
 
741
{
 
742
    OpenFile *fptr;
 
743
    struct stat st;
 
744
 
 
745
    GetOpenFile(obj, fptr);
 
746
    if (fstat(fileno(fptr->f), &st) == -1) {
 
747
        rb_sys_fail(fptr->path);
 
748
    }
 
749
    return stat_new(&st);
 
750
}
 
751
 
 
752
/*
 
753
 *  call-seq:
 
754
 *     File.lstat(file_name)   => stat
 
755
 *  
 
756
 *  Same as <code>File::stat</code>, but does not follow the last symbolic
 
757
 *  link. Instead, reports on the link itself.
 
758
 *     
 
759
 *     File.symlink("testfile", "link2test")   #=> 0
 
760
 *     File.stat("testfile").size              #=> 66
 
761
 *     File.lstat("link2test").size            #=> 8
 
762
 *     File.stat("link2test").size             #=> 66
 
763
 *     
 
764
 */
 
765
 
 
766
static VALUE
 
767
rb_file_s_lstat(klass, fname)
 
768
    VALUE klass, fname;
 
769
{
 
770
#ifdef HAVE_LSTAT
 
771
    struct stat st;
 
772
 
 
773
    SafeStringValue(fname);
 
774
    if (lstat(StringValueCStr(fname), &st) == -1) {
 
775
        rb_sys_fail(RSTRING(fname)->ptr);
 
776
    }
 
777
    return stat_new(&st);
 
778
#else
 
779
    return rb_file_s_stat(klass, fname);
 
780
#endif
 
781
}
 
782
 
 
783
 
 
784
/*
 
785
 *  call-seq:
 
786
 *     file.lstat   =>  stat
 
787
 *  
 
788
 *  Same as <code>IO#stat</code>, but does not follow the last symbolic
 
789
 *  link. Instead, reports on the link itself.
 
790
 *     
 
791
 *     File.symlink("testfile", "link2test")   #=> 0
 
792
 *     File.stat("testfile").size              #=> 66
 
793
 *     f = File.new("link2test")
 
794
 *     f.lstat.size                            #=> 8
 
795
 *     f.stat.size                             #=> 66
 
796
 */
 
797
 
 
798
static VALUE
 
799
rb_file_lstat(obj)
 
800
    VALUE obj;
 
801
{
 
802
#ifdef HAVE_LSTAT
 
803
    OpenFile *fptr;
 
804
    struct stat st;
 
805
 
 
806
    rb_secure(2);
 
807
    GetOpenFile(obj, fptr);
 
808
    if (!fptr->path) return Qnil;
 
809
    if (lstat(fptr->path, &st) == -1) {
 
810
        rb_sys_fail(fptr->path);
 
811
    }
 
812
    return stat_new(&st);
 
813
#else
 
814
    return rb_io_stat(obj);
 
815
#endif
 
816
}
 
817
 
 
818
static int
 
819
group_member(gid)
 
820
    GETGROUPS_T gid;
 
821
{
 
822
#ifndef _WIN32
 
823
    if (getgid() == gid || getegid() == gid)
 
824
        return Qtrue;
 
825
 
 
826
# ifdef HAVE_GETGROUPS
 
827
#  ifndef NGROUPS
 
828
#   ifdef NGROUPS_MAX
 
829
#    define NGROUPS NGROUPS_MAX
 
830
#   else
 
831
#    define NGROUPS 32
 
832
#   endif
 
833
#  endif
 
834
    {
 
835
        GETGROUPS_T gary[NGROUPS];
 
836
        int anum;
 
837
 
 
838
        anum = getgroups(NGROUPS, gary);
 
839
        while (--anum >= 0)
 
840
            if (gary[anum] == gid)
 
841
                return Qtrue;
 
842
    }
 
843
# endif
 
844
#endif
 
845
    return Qfalse;
 
846
}
 
847
 
 
848
#ifndef S_IXUGO
 
849
#  define S_IXUGO               (S_IXUSR | S_IXGRP | S_IXOTH)
 
850
#endif
 
851
 
 
852
#if defined(S_IXGRP) && !defined(_WIN32) && !defined(__CYGWIN__)
 
853
#define USE_GETEUID 1
 
854
#endif
 
855
 
 
856
#ifndef HAVE_EACCESS
 
857
int
 
858
eaccess(path, mode)
 
859
     const char *path;
 
860
     int mode;
 
861
{
 
862
#ifdef USE_GETEUID
 
863
    struct stat st;
 
864
    int euid;
 
865
 
 
866
    if (stat(path, &st) < 0) return -1;
 
867
 
 
868
    euid = geteuid();
 
869
 
 
870
    if (euid == 0) {
 
871
        /* Root can read or write any file. */
 
872
        if (!(mode & X_OK))
 
873
            return 0;
 
874
 
 
875
        /* Root can execute any file that has any one of the execute
 
876
           bits set. */
 
877
        if (st.st_mode & S_IXUGO)
 
878
            return 0;
 
879
 
 
880
        return -1;
 
881
    }
 
882
 
 
883
    if (st.st_uid == euid)        /* owner */
 
884
        mode <<= 6;
 
885
    else if (group_member(st.st_gid))
 
886
        mode <<= 3;
 
887
 
 
888
    if ((st.st_mode & mode) == mode) return 0;
 
889
 
 
890
    return -1;
 
891
#else
 
892
# if _MSC_VER >= 1400
 
893
    mode &= 6;
 
894
# endif
 
895
    return access(path, mode);
 
896
#endif
 
897
}
 
898
#endif
 
899
 
 
900
 
 
901
/*
 
902
 * Document-class: FileTest
 
903
 *
 
904
 *  <code>FileTest</code> implements file test operations similar to
 
905
 *  those used in <code>File::Stat</code>. It exists as a standalone
 
906
 *  module, and its methods are also insinuated into the <code>File</code>
 
907
 *  class. (Note that this is not done by inclusion: the interpreter cheats).
 
908
 *     
 
909
 */
 
910
 
 
911
 
 
912
/*
 
913
 * call-seq:
 
914
 *   File.directory?(file_name)   =>  true or false
 
915
 *
 
916
 * Returns <code>true</code> if the named file is a directory,
 
917
 * <code>false</code> otherwise.
 
918
 *
 
919
 *    File.directory?(".")
 
920
 */
 
921
 
 
922
static VALUE
 
923
test_d(obj, fname)
 
924
    VALUE obj, fname;
 
925
{
 
926
#ifndef S_ISDIR
 
927
#   define S_ISDIR(m) ((m & S_IFMT) == S_IFDIR)
 
928
#endif
 
929
 
 
930
    struct stat st;
 
931
 
 
932
    if (rb_stat(fname, &st) < 0) return Qfalse;
 
933
    if (S_ISDIR(st.st_mode)) return Qtrue;
 
934
    return Qfalse;
 
935
}
 
936
 
 
937
/*
 
938
 * call-seq:
 
939
 *   File.pipe?(file_name)   =>  true or false
 
940
 *
 
941
 * Returns <code>true</code> if the named file is a pipe.
 
942
 */
 
943
 
 
944
static VALUE
 
945
test_p(obj, fname)
 
946
    VALUE obj, fname;
 
947
{
 
948
#ifdef S_IFIFO
 
949
#  ifndef S_ISFIFO
 
950
#    define S_ISFIFO(m) ((m & S_IFMT) == S_IFIFO)
 
951
#  endif
 
952
 
 
953
    struct stat st;
 
954
 
 
955
    if (rb_stat(fname, &st) < 0) return Qfalse;
 
956
    if (S_ISFIFO(st.st_mode)) return Qtrue;
 
957
 
 
958
#endif
 
959
    return Qfalse;
 
960
}
 
961
 
 
962
/*
 
963
 * call-seq:
 
964
 *   File.symlink?(file_name)   =>  true or false
 
965
 *
 
966
 * Returns <code>true</code> if the named file is a symbolic link.
 
967
 */
 
968
 
 
969
static VALUE
 
970
test_l(obj, fname)
 
971
    VALUE obj, fname;
 
972
{
 
973
#ifndef S_ISLNK
 
974
#  ifdef _S_ISLNK
 
975
#    define S_ISLNK(m) _S_ISLNK(m)
 
976
#  elif defined __BORLANDC__
 
977
#    ifdef _S_IFLNK
 
978
#      define S_ISLNK(m) (((unsigned short)(m) & S_IFMT) == _S_IFLNK)
 
979
#    else
 
980
#      ifdef S_IFLNK
 
981
#        define S_ISLNK(m) (((unsigned short)(m) & S_IFMT) == S_IFLNK)
 
982
#      endif
 
983
#    endif
 
984
#  else
 
985
#    ifdef _S_IFLNK
 
986
#      define S_ISLNK(m) ((m & S_IFMT) == _S_IFLNK)
 
987
#    else
 
988
#      ifdef S_IFLNK
 
989
#        define S_ISLNK(m) ((m & S_IFMT) == S_IFLNK)
 
990
#      endif
 
991
#    endif
 
992
#  endif
 
993
#endif
 
994
 
 
995
#ifdef S_ISLNK
 
996
    struct stat st;
 
997
 
 
998
    SafeStringValue(fname);
 
999
    if (lstat(StringValueCStr(fname), &st) < 0) return Qfalse;
 
1000
    if (S_ISLNK(st.st_mode)) return Qtrue;
 
1001
#endif
 
1002
 
 
1003
    return Qfalse;
 
1004
}
 
1005
 
 
1006
/*
 
1007
 * call-seq:
 
1008
 *   File.socket?(file_name)   =>  true or false
 
1009
 *
 
1010
 * Returns <code>true</code> if the named file is a socket.
 
1011
 */
 
1012
 
 
1013
static VALUE
 
1014
test_S(obj, fname)
 
1015
    VALUE obj, fname;
 
1016
{
 
1017
#ifndef S_ISSOCK
 
1018
#  ifdef _S_ISSOCK
 
1019
#    define S_ISSOCK(m) _S_ISSOCK(m)
 
1020
#  elif defined __BORLANDC__
 
1021
#    ifdef _S_IFSOCK
 
1022
#      define S_ISSOCK(m) (((unsigned short)(m) & S_IFMT) == _S_IFSOCK)
 
1023
#    else
 
1024
#      ifdef S_IFSOCK
 
1025
#        define S_ISSOCK(m) (((unsigned short)(m) & S_IFMT) == S_IFSOCK)
 
1026
#      endif
 
1027
#    endif
 
1028
#  else
 
1029
#    ifdef _S_IFSOCK
 
1030
#      define S_ISSOCK(m) ((m & S_IFMT) == _S_IFSOCK)
 
1031
#    else
 
1032
#      ifdef S_IFSOCK
 
1033
#        define S_ISSOCK(m) ((m & S_IFMT) == S_IFSOCK)
 
1034
#      endif
 
1035
#    endif
 
1036
#  endif
 
1037
#endif
 
1038
 
 
1039
#ifdef S_ISSOCK
 
1040
    struct stat st;
 
1041
 
 
1042
    if (rb_stat(fname, &st) < 0) return Qfalse;
 
1043
    if (S_ISSOCK(st.st_mode)) return Qtrue;
 
1044
 
 
1045
#endif
 
1046
    return Qfalse;
 
1047
}
 
1048
 
 
1049
/*
 
1050
 * call-seq:
 
1051
 *   File.blockdev?(file_name)   =>  true or false
 
1052
 *
 
1053
 * Returns <code>true</code> if the named file is a block device.
 
1054
 */
 
1055
 
 
1056
static VALUE
 
1057
test_b(obj, fname)
 
1058
    VALUE obj, fname;
 
1059
{
 
1060
#ifndef S_ISBLK
 
1061
#   ifdef S_IFBLK
 
1062
#       define S_ISBLK(m) ((m & S_IFMT) == S_IFBLK)
 
1063
#   else
 
1064
#       define S_ISBLK(m) (0)  /* anytime false */
 
1065
#   endif
 
1066
#endif
 
1067
 
 
1068
#ifdef S_ISBLK
 
1069
    struct stat st;
 
1070
 
 
1071
    if (rb_stat(fname, &st) < 0) return Qfalse;
 
1072
    if (S_ISBLK(st.st_mode)) return Qtrue;
 
1073
 
 
1074
#endif
 
1075
    return Qfalse;
 
1076
}
 
1077
 
 
1078
/*
 
1079
 * call-seq:
 
1080
 *   File.chardev?(file_name)   =>  true or false
 
1081
 *
 
1082
 * Returns <code>true</code> if the named file is a character device.
 
1083
 */
 
1084
static VALUE
 
1085
test_c(obj, fname)
 
1086
    VALUE obj, fname;
 
1087
{
 
1088
#ifndef S_ISCHR
 
1089
#   define S_ISCHR(m) ((m & S_IFMT) == S_IFCHR)
 
1090
#endif
 
1091
 
 
1092
    struct stat st;
 
1093
 
 
1094
    if (rb_stat(fname, &st) < 0) return Qfalse;
 
1095
    if (S_ISCHR(st.st_mode)) return Qtrue;
 
1096
 
 
1097
    return Qfalse;
 
1098
}
 
1099
 
 
1100
 
 
1101
/*
 
1102
 * call-seq:
 
1103
 *    File.exist?(file_name)    =>  true or false
 
1104
 *    File.exists?(file_name)   =>  true or false    (obsolete)
 
1105
 *
 
1106
 * Return <code>true</code> if the named file exists.
 
1107
 */
 
1108
 
 
1109
static VALUE
 
1110
test_e(obj, fname)
 
1111
    VALUE obj, fname;
 
1112
{
 
1113
    struct stat st;
 
1114
 
 
1115
    if (rb_stat(fname, &st) < 0) return Qfalse;
 
1116
    return Qtrue;
 
1117
}
 
1118
 
 
1119
/*
 
1120
 * call-seq:
 
1121
 *    File.readable?(file_name)   => true or false
 
1122
 *
 
1123
 * Returns <code>true</code> if the named file is readable by the effective
 
1124
 * user id of this process.
 
1125
 */
 
1126
 
 
1127
static VALUE
 
1128
test_r(obj, fname)
 
1129
    VALUE obj, fname;
 
1130
{
 
1131
    SafeStringValue(fname);
 
1132
    if (eaccess(StringValueCStr(fname), R_OK) < 0) return Qfalse;
 
1133
    return Qtrue;
 
1134
}
 
1135
 
 
1136
/*
 
1137
 * call-seq:
 
1138
 *    File.readable_real?(file_name)   => true or false
 
1139
 *
 
1140
 * Returns <code>true</code> if the named file is readable by the real
 
1141
 * user id of this process.
 
1142
 */
 
1143
 
 
1144
static VALUE
 
1145
test_R(obj, fname)
 
1146
    VALUE obj, fname;
 
1147
{
 
1148
    SafeStringValue(fname);
 
1149
    if (access(StringValueCStr(fname), R_OK) < 0) return Qfalse;
 
1150
    return Qtrue;
 
1151
}
 
1152
 
 
1153
 
 
1154
/*
 
1155
 * call-seq:
 
1156
 *    File.writable?(file_name)   => true or false
 
1157
 *
 
1158
 * Returns <code>true</code> if the named file is writable by the effective
 
1159
 * user id of this process.
 
1160
 */
 
1161
 
 
1162
static VALUE
 
1163
test_w(obj, fname)
 
1164
    VALUE obj, fname;
 
1165
{
 
1166
    SafeStringValue(fname);
 
1167
    if (eaccess(StringValueCStr(fname), W_OK) < 0) return Qfalse;
 
1168
    return Qtrue;
 
1169
}
 
1170
 
 
1171
/*
 
1172
 * call-seq:
 
1173
 *    File.writable_real?(file_name)   => true or false
 
1174
 *
 
1175
 * Returns <code>true</code> if the named file is writable by the real
 
1176
 * user id of this process.
 
1177
 */
 
1178
 
 
1179
static VALUE
 
1180
test_W(obj, fname)
 
1181
    VALUE obj, fname;
 
1182
{
 
1183
    SafeStringValue(fname);
 
1184
    if (access(StringValueCStr(fname), W_OK) < 0) return Qfalse;
 
1185
    return Qtrue;
 
1186
}
 
1187
 
 
1188
/*
 
1189
 * call-seq:
 
1190
 *    File.executable?(file_name)   => true or false
 
1191
 *
 
1192
 * Returns <code>true</code> if the named file is executable by the effective
 
1193
 * user id of this process.
 
1194
 */
 
1195
 
 
1196
static VALUE
 
1197
test_x(obj, fname)
 
1198
    VALUE obj, fname;
 
1199
{
 
1200
    SafeStringValue(fname);
 
1201
    if (eaccess(StringValueCStr(fname), X_OK) < 0) return Qfalse;
 
1202
    return Qtrue;
 
1203
}
 
1204
 
 
1205
/*
 
1206
 * call-seq:
 
1207
 *    File.executable_real?(file_name)   => true or false
 
1208
 *
 
1209
 * Returns <code>true</code> if the named file is executable by the real
 
1210
 * user id of this process.
 
1211
 */
 
1212
 
 
1213
static VALUE
 
1214
test_X(obj, fname)
 
1215
    VALUE obj, fname;
 
1216
{
 
1217
    SafeStringValue(fname);
 
1218
    if (access(StringValueCStr(fname), X_OK) < 0) return Qfalse;
 
1219
    return Qtrue;
 
1220
}
 
1221
 
 
1222
#ifndef S_ISREG
 
1223
#   define S_ISREG(m) ((m & S_IFMT) == S_IFREG)
 
1224
#endif
 
1225
 
 
1226
/*
 
1227
 * call-seq:
 
1228
 *    File.file?(file_name)   => true or false
 
1229
 *
 
1230
 * Returns <code>true</code> if the named file exists and is a
 
1231
 * regular file.
 
1232
 */
 
1233
 
 
1234
static VALUE
 
1235
test_f(obj, fname)
 
1236
    VALUE obj, fname;
 
1237
{
 
1238
    struct stat st;
 
1239
 
 
1240
    if (rb_stat(fname, &st) < 0) return Qfalse;
 
1241
    if (S_ISREG(st.st_mode)) return Qtrue;
 
1242
    return Qfalse;
 
1243
}
 
1244
 
 
1245
/*
 
1246
 * call-seq:
 
1247
 *    File.zero?(file_name)   => true or false
 
1248
 *
 
1249
 * Returns <code>true</code> if the named file exists and has
 
1250
 * a zero size.
 
1251
 */
 
1252
 
 
1253
static VALUE
 
1254
test_z(obj, fname)
 
1255
    VALUE obj, fname;
 
1256
{
 
1257
    struct stat st;
 
1258
 
 
1259
    if (rb_stat(fname, &st) < 0) return Qfalse;
 
1260
    if (st.st_size == 0) return Qtrue;
 
1261
    return Qfalse;
 
1262
}
 
1263
 
 
1264
/*
 
1265
 * call-seq:
 
1266
 *    File.size?(file_name)   => Integer or nil
 
1267
 *
 
1268
 * Returns +nil+ if +file_name+ doesn't exist or has zero size, the size of the
 
1269
 * file otherwise.
 
1270
 */
 
1271
 
 
1272
static VALUE
 
1273
test_s(obj, fname)
 
1274
    VALUE obj, fname;
 
1275
{
 
1276
    struct stat st;
 
1277
 
 
1278
    if (rb_stat(fname, &st) < 0) return Qnil;
 
1279
    if (st.st_size == 0) return Qnil;
 
1280
    return OFFT2NUM(st.st_size);
 
1281
}
 
1282
 
 
1283
/*
 
1284
 * call-seq:
 
1285
 *    File.owned?(file_name)   => true or false
 
1286
 *
 
1287
 * Returns <code>true</code> if the named file exists and the
 
1288
 * effective used id of the calling process is the owner of
 
1289
 * the file.
 
1290
 */
 
1291
 
 
1292
static VALUE
 
1293
test_owned(obj, fname)
 
1294
    VALUE obj, fname;
 
1295
{
 
1296
    struct stat st;
 
1297
 
 
1298
    if (rb_stat(fname, &st) < 0) return Qfalse;
 
1299
    if (st.st_uid == geteuid()) return Qtrue;
 
1300
    return Qfalse;
 
1301
}
 
1302
 
 
1303
static VALUE
 
1304
test_rowned(obj, fname)
 
1305
    VALUE obj, fname;
 
1306
{
 
1307
    struct stat st;
 
1308
 
 
1309
    if (rb_stat(fname, &st) < 0) return Qfalse;
 
1310
    if (st.st_uid == getuid()) return Qtrue;
 
1311
    return Qfalse;
 
1312
}
 
1313
 
 
1314
/*
 
1315
 * call-seq:
 
1316
 *    File.grpowned?(file_name)   => true or false
 
1317
 *
 
1318
 * Returns <code>true</code> if the named file exists and the
 
1319
 * effective group id of the calling process is the owner of
 
1320
 * the file. Returns <code>false</code> on Windows.
 
1321
 */
 
1322
 
 
1323
static VALUE
 
1324
test_grpowned(obj, fname)
 
1325
    VALUE obj, fname;
 
1326
{
 
1327
#ifndef _WIN32
 
1328
    struct stat st;
 
1329
 
 
1330
    if (rb_stat(fname, &st) < 0) return Qfalse;
 
1331
    if (group_member(st.st_gid)) return Qtrue;
 
1332
#endif
 
1333
    return Qfalse;
 
1334
}
 
1335
 
 
1336
#if defined(S_ISUID) || defined(S_ISGID) || defined(S_ISVTX)
 
1337
static VALUE
 
1338
check3rdbyte(fname, mode)
 
1339
    VALUE fname;
 
1340
    int mode;
 
1341
{
 
1342
    struct stat st;
 
1343
 
 
1344
    SafeStringValue(fname);
 
1345
    if (stat(StringValueCStr(fname), &st) < 0) return Qfalse;
 
1346
    if (st.st_mode & mode) return Qtrue;
 
1347
    return Qfalse;
 
1348
}
 
1349
#endif
 
1350
 
 
1351
/*
 
1352
 * call-seq:
 
1353
 *   File.setuid?(file_name)   =>  true or false
 
1354
 *
 
1355
 * Returns <code>true</code> if the named file has the setuid bit set.
 
1356
 */
 
1357
 
 
1358
static VALUE
 
1359
test_suid(obj, fname)
 
1360
    VALUE obj, fname;
 
1361
{
 
1362
#ifdef S_ISUID
 
1363
    return check3rdbyte(fname, S_ISUID);
 
1364
#else
 
1365
    return Qfalse;
 
1366
#endif
 
1367
}
 
1368
 
 
1369
/*
 
1370
 * call-seq:
 
1371
 *   File.setgid?(file_name)   =>  true or false
 
1372
 *
 
1373
 * Returns <code>true</code> if the named file has the setgid bit set.
 
1374
 */
 
1375
 
 
1376
static VALUE
 
1377
test_sgid(obj, fname)
 
1378
    VALUE obj, fname;
 
1379
{
 
1380
#ifdef S_ISGID
 
1381
    return check3rdbyte(fname, S_ISGID);
 
1382
#else
 
1383
    return Qfalse;
 
1384
#endif
 
1385
}
 
1386
 
 
1387
/*
 
1388
 * call-seq:
 
1389
 *   File.sticky?(file_name)   =>  true or false
 
1390
 *
 
1391
 * Returns <code>true</code> if the named file has the sticky bit set.
 
1392
 */
 
1393
 
 
1394
static VALUE
 
1395
test_sticky(obj, fname)
 
1396
    VALUE obj, fname;
 
1397
{
 
1398
#ifdef S_ISVTX
 
1399
    return check3rdbyte(fname, S_ISVTX);
 
1400
#else
 
1401
    return Qnil;
 
1402
#endif
 
1403
}
 
1404
 
 
1405
/*
 
1406
 * call-seq:
 
1407
 *   File.identical?(file_1, file_2)   =>  true or false
 
1408
 *
 
1409
 * Returns <code>true</code> if the named files are identical.
 
1410
 *
 
1411
 *     open("a", "w") {}
 
1412
 *     p File.identical?("a", "a")      #=> true
 
1413
 *     p File.identical?("a", "./a")    #=> true
 
1414
 *     File.link("a", "b")
 
1415
 *     p File.identical?("a", "b")      #=> true
 
1416
 *     File.symlink("a", "c")
 
1417
 *     p File.identical?("a", "c")      #=> true
 
1418
 *     open("d", "w") {}
 
1419
 *     p File.identical?("a", "d")      #=> false
 
1420
 */
 
1421
 
 
1422
static VALUE
 
1423
test_identical(obj, fname1, fname2)
 
1424
    VALUE obj, fname1, fname2;
 
1425
{
 
1426
#ifndef DOSISH
 
1427
    struct stat st1, st2;
 
1428
 
 
1429
    if (rb_stat(fname1, &st1) < 0) return Qfalse;
 
1430
    if (rb_stat(fname2, &st2) < 0) return Qfalse;
 
1431
    if (st1.st_dev != st2.st_dev) return Qfalse;
 
1432
    if (st1.st_ino != st2.st_ino) return Qfalse;
 
1433
#else
 
1434
#ifdef _WIN32
 
1435
    BY_HANDLE_FILE_INFORMATION st1, st2;
 
1436
    HANDLE f1 = 0, f2 = 0;
 
1437
#endif
 
1438
 
 
1439
    rb_secure(2);
 
1440
#ifdef _WIN32
 
1441
    f1 = w32_io_info(&fname1, &st1);
 
1442
    if (f1 == INVALID_HANDLE_VALUE) return Qfalse;
 
1443
    f2 = w32_io_info(&fname2, &st2);
 
1444
    if (f1) CloseHandle(f1);
 
1445
    if (f2 == INVALID_HANDLE_VALUE) return Qfalse;
 
1446
    if (f2) CloseHandle(f2);
 
1447
 
 
1448
    if (st1.dwVolumeSerialNumber == st2.dwVolumeSerialNumber &&
 
1449
        st1.nFileIndexHigh == st2.nFileIndexHigh &&
 
1450
        st1.nFileIndexLow == st2.nFileIndexLow)
 
1451
        return Qtrue;
 
1452
    if (!f1 || !f2) return Qfalse;
 
1453
    if (rb_w32_iswin95()) return Qfalse;
 
1454
#else
 
1455
    SafeStringValue(fname1);
 
1456
    fname1 = rb_str_new4(fname1);
 
1457
    SafeStringValue(fname2);
 
1458
    if (access(RSTRING(fname1)->ptr, 0)) return Qfalse;
 
1459
    if (access(RSTRING(fname2)->ptr, 0)) return Qfalse;
 
1460
#endif
 
1461
    fname1 = rb_file_expand_path(fname1, Qnil);
 
1462
    fname2 = rb_file_expand_path(fname2, Qnil);
 
1463
    if (RSTRING(fname1)->len != RSTRING(fname2)->len) return Qfalse;
 
1464
    if (rb_memcicmp(RSTRING(fname1)->ptr, RSTRING(fname2)->ptr, RSTRING(fname1)->len))
 
1465
        return Qfalse;
 
1466
#endif
 
1467
    return Qtrue;
 
1468
}
 
1469
 
 
1470
/*
 
1471
 * call-seq:
 
1472
 *    File.size(file_name)   => integer
 
1473
 *
 
1474
 * Returns the size of <code>file_name</code>.
 
1475
 */
 
1476
 
 
1477
static VALUE
 
1478
rb_file_s_size(klass, fname)
 
1479
    VALUE klass, fname;
 
1480
{
 
1481
    struct stat st;
 
1482
 
 
1483
    if (rb_stat(fname, &st) < 0)
 
1484
        rb_sys_fail(StringValueCStr(fname));
 
1485
    return OFFT2NUM(st.st_size);
 
1486
}
 
1487
 
 
1488
static VALUE
 
1489
rb_file_ftype(st)
 
1490
    struct stat *st;
 
1491
{
 
1492
    char *t;
 
1493
 
 
1494
    if (S_ISREG(st->st_mode)) {
 
1495
        t = "file";
 
1496
    }
 
1497
    else if (S_ISDIR(st->st_mode)) {
 
1498
        t = "directory";
 
1499
    }
 
1500
    else if (S_ISCHR(st->st_mode)) {
 
1501
        t = "characterSpecial";
 
1502
    }
 
1503
#ifdef S_ISBLK
 
1504
    else if (S_ISBLK(st->st_mode)) {
 
1505
        t = "blockSpecial";
 
1506
    }
 
1507
#endif
 
1508
#ifdef S_ISFIFO
 
1509
    else if (S_ISFIFO(st->st_mode)) {
 
1510
        t = "fifo";
 
1511
    }
 
1512
#endif
 
1513
#ifdef S_ISLNK
 
1514
    else if (S_ISLNK(st->st_mode)) {
 
1515
        t = "link";
 
1516
    }
 
1517
#endif
 
1518
#ifdef S_ISSOCK
 
1519
    else if (S_ISSOCK(st->st_mode)) {
 
1520
        t = "socket";
 
1521
    }
 
1522
#endif
 
1523
    else {
 
1524
        t = "unknown";
 
1525
    }
 
1526
 
 
1527
    return rb_str_new2(t);
 
1528
}
 
1529
 
 
1530
/*
 
1531
 *  call-seq:
 
1532
 *     File.ftype(file_name)   => string
 
1533
 *  
 
1534
 *  Identifies the type of the named file; the return string is one of
 
1535
 *  ``<code>file</code>'', ``<code>directory</code>'',
 
1536
 *  ``<code>characterSpecial</code>'', ``<code>blockSpecial</code>'',
 
1537
 *  ``<code>fifo</code>'', ``<code>link</code>'',
 
1538
 *  ``<code>socket</code>'', or ``<code>unknown</code>''.
 
1539
 *     
 
1540
 *     File.ftype("testfile")            #=> "file"
 
1541
 *     File.ftype("/dev/tty")            #=> "characterSpecial"
 
1542
 *     File.ftype("/tmp/.X11-unix/X0")   #=> "socket"
 
1543
 */
 
1544
 
 
1545
static VALUE
 
1546
rb_file_s_ftype(klass, fname)
 
1547
    VALUE klass, fname;
 
1548
{
 
1549
    struct stat st;
 
1550
 
 
1551
    SafeStringValue(fname);
 
1552
    if (lstat(StringValueCStr(fname), &st) == -1) {
 
1553
        rb_sys_fail(RSTRING(fname)->ptr);
 
1554
    }
 
1555
 
 
1556
    return rb_file_ftype(&st);
 
1557
}
 
1558
 
 
1559
/*
 
1560
 *  call-seq:
 
1561
 *     File.atime(file_name)  =>  time
 
1562
 *  
 
1563
 *  Returns the last access time for the named file as a Time object).
 
1564
 *     
 
1565
 *     File.atime("testfile")   #=> Wed Apr 09 08:51:48 CDT 2003
 
1566
 *     
 
1567
 */
 
1568
 
 
1569
static VALUE
 
1570
rb_file_s_atime(klass, fname)
 
1571
    VALUE klass, fname;
 
1572
{
 
1573
    struct stat st;
 
1574
 
 
1575
    if (rb_stat(fname, &st) < 0)
 
1576
        rb_sys_fail(StringValueCStr(fname));
 
1577
    return rb_time_new(st.st_atime, 0);
 
1578
}
 
1579
 
 
1580
/*
 
1581
 *  call-seq:
 
1582
 *     file.atime    => time
 
1583
 *  
 
1584
 *  Returns the last access time (a <code>Time</code> object)
 
1585
 *   for <i>file</i>, or epoch if <i>file</i> has not been accessed.
 
1586
 *     
 
1587
 *     File.new("testfile").atime   #=> Wed Dec 31 18:00:00 CST 1969
 
1588
 *     
 
1589
 */
 
1590
 
 
1591
static VALUE
 
1592
rb_file_atime(obj)
 
1593
    VALUE obj;
 
1594
{
 
1595
    OpenFile *fptr;
 
1596
    struct stat st;
 
1597
 
 
1598
    GetOpenFile(obj, fptr);
 
1599
    if (fstat(fileno(fptr->f), &st) == -1) {
 
1600
        rb_sys_fail(fptr->path);
 
1601
    }
 
1602
    return rb_time_new(st.st_atime, 0);
 
1603
}
 
1604
 
 
1605
/*
 
1606
 *  call-seq:
 
1607
 *     File.mtime(file_name)  =>  time
 
1608
 *  
 
1609
 *  Returns the modification time for the named file as a Time object.
 
1610
 *     
 
1611
 *     File.mtime("testfile")   #=> Tue Apr 08 12:58:04 CDT 2003
 
1612
 *     
 
1613
 */
 
1614
 
 
1615
static VALUE
 
1616
rb_file_s_mtime(klass, fname)
 
1617
    VALUE klass, fname;
 
1618
{
 
1619
    struct stat st;
 
1620
 
 
1621
    if (rb_stat(fname, &st) < 0)
 
1622
        rb_sys_fail(RSTRING(fname)->ptr);
 
1623
    return rb_time_new(st.st_mtime, 0);
 
1624
}
 
1625
 
 
1626
/*
 
1627
 *  call-seq:
 
1628
 *     file.mtime -> time
 
1629
 *  
 
1630
 *  Returns the modification time for <i>file</i>.
 
1631
 *     
 
1632
 *     File.new("testfile").mtime   #=> Wed Apr 09 08:53:14 CDT 2003
 
1633
 *     
 
1634
 */
 
1635
 
 
1636
static VALUE
 
1637
rb_file_mtime(obj)
 
1638
    VALUE obj;
 
1639
{
 
1640
    OpenFile *fptr;
 
1641
    struct stat st;
 
1642
 
 
1643
    GetOpenFile(obj, fptr);
 
1644
    if (fstat(fileno(fptr->f), &st) == -1) {
 
1645
        rb_sys_fail(fptr->path);
 
1646
    }
 
1647
    return rb_time_new(st.st_mtime, 0);
 
1648
}
 
1649
 
 
1650
/*
 
1651
 *  call-seq:
 
1652
 *     File.ctime(file_name)  => time
 
1653
 *  
 
1654
 *  Returns the change time for the named file (the time at which
 
1655
 *  directory information about the file was changed, not the file
 
1656
 *  itself).
 
1657
 *     
 
1658
 *     File.ctime("testfile")   #=> Wed Apr 09 08:53:13 CDT 2003
 
1659
 *     
 
1660
 */
 
1661
 
 
1662
static VALUE
 
1663
rb_file_s_ctime(klass, fname)
 
1664
    VALUE klass, fname;
 
1665
{
 
1666
    struct stat st;
 
1667
 
 
1668
    if (rb_stat(fname, &st) < 0)
 
1669
        rb_sys_fail(RSTRING(fname)->ptr);
 
1670
    return rb_time_new(st.st_ctime, 0);
 
1671
}
 
1672
 
 
1673
/*
 
1674
 *  call-seq:
 
1675
 *     file.ctime -> time
 
1676
 *  
 
1677
 *  Returns the change time for <i>file</i> (that is, the time directory
 
1678
 *  information about the file was changed, not the file itself).
 
1679
 *     
 
1680
 *     File.new("testfile").ctime   #=> Wed Apr 09 08:53:14 CDT 2003
 
1681
 *     
 
1682
 */
 
1683
 
 
1684
static VALUE
 
1685
rb_file_ctime(obj)
 
1686
    VALUE obj;
 
1687
{
 
1688
    OpenFile *fptr;
 
1689
    struct stat st;
 
1690
 
 
1691
    GetOpenFile(obj, fptr);
 
1692
    if (fstat(fileno(fptr->f), &st) == -1) {
 
1693
        rb_sys_fail(fptr->path);
 
1694
    }
 
1695
    return rb_time_new(st.st_ctime, 0);
 
1696
}
 
1697
 
 
1698
static void chmod_internal _((const char *, void *));
 
1699
static void
 
1700
chmod_internal(path, mode)
 
1701
    const char *path;
 
1702
    void *mode;
 
1703
{
 
1704
    if (chmod(path, *(int *)mode) < 0)
 
1705
        rb_sys_fail(path);
 
1706
}
 
1707
 
 
1708
/*
 
1709
 *  call-seq:
 
1710
 *     File.chmod(mode_int, file_name, ... ) -> integer
 
1711
 *  
 
1712
 *  Changes permission bits on the named file(s) to the bit pattern
 
1713
 *  represented by <i>mode_int</i>. Actual effects are operating system
 
1714
 *  dependent (see the beginning of this section). On Unix systems, see
 
1715
 *  <code>chmod(2)</code> for details. Returns the number of files
 
1716
 *  processed.
 
1717
 *     
 
1718
 *     File.chmod(0644, "testfile", "out")   #=> 2
 
1719
 */
 
1720
 
 
1721
static VALUE
 
1722
rb_file_s_chmod(argc, argv)
 
1723
    int argc;
 
1724
    VALUE *argv;
 
1725
{
 
1726
    VALUE vmode;
 
1727
    VALUE rest;
 
1728
    int mode;
 
1729
    long n;
 
1730
 
 
1731
    rb_secure(2);
 
1732
    rb_scan_args(argc, argv, "1*", &vmode, &rest);
 
1733
    mode = NUM2INT(vmode);
 
1734
 
 
1735
    n = apply2files(chmod_internal, rest, &mode);
 
1736
    return LONG2FIX(n);
 
1737
}
 
1738
 
 
1739
/*
 
1740
 *  call-seq:
 
1741
 *     file.chmod(mode_int)   => 0
 
1742
 *  
 
1743
 *  Changes permission bits on <i>file</i> to the bit pattern
 
1744
 *  represented by <i>mode_int</i>. Actual effects are platform
 
1745
 *  dependent; on Unix systems, see <code>chmod(2)</code> for details.
 
1746
 *  Follows symbolic links. Also see <code>File#lchmod</code>.
 
1747
 *     
 
1748
 *     f = File.new("out", "w");
 
1749
 *     f.chmod(0644)   #=> 0
 
1750
 */
 
1751
 
 
1752
static VALUE
 
1753
rb_file_chmod(obj, vmode)
 
1754
    VALUE obj, vmode;
 
1755
{
 
1756
    OpenFile *fptr;
 
1757
    int mode;
 
1758
 
 
1759
    rb_secure(2);
 
1760
    mode = NUM2INT(vmode);
 
1761
 
 
1762
    GetOpenFile(obj, fptr);
 
1763
#ifdef HAVE_FCHMOD
 
1764
    if (fchmod(fileno(fptr->f), mode) == -1)
 
1765
        rb_sys_fail(fptr->path);
 
1766
#else
 
1767
    if (!fptr->path) return Qnil;
 
1768
    if (chmod(fptr->path, mode) == -1)
 
1769
        rb_sys_fail(fptr->path);
 
1770
#endif
 
1771
 
 
1772
    return INT2FIX(0);
 
1773
}
 
1774
 
 
1775
#if defined(HAVE_LCHMOD)
 
1776
static void lchmod_internal _((const char *, void *));
 
1777
static void
 
1778
lchmod_internal(path, mode)
 
1779
    const char *path;
 
1780
    void *mode;
 
1781
{
 
1782
    if (lchmod(path, (int)mode) < 0)
 
1783
        rb_sys_fail(path);
 
1784
}
 
1785
 
 
1786
/*
 
1787
 *  call-seq:
 
1788
 *     File.lchmod(mode_int, file_name, ...)  => integer
 
1789
 *  
 
1790
 *  Equivalent to <code>File::chmod</code>, but does not follow symbolic
 
1791
 *  links (so it will change the permissions associated with the link,
 
1792
 *  not the file referenced by the link). Often not available.
 
1793
 *     
 
1794
 */
 
1795
 
 
1796
static VALUE
 
1797
rb_file_s_lchmod(argc, argv)
 
1798
    int argc;
 
1799
    VALUE *argv;
 
1800
{
 
1801
    VALUE vmode;
 
1802
    VALUE rest;
 
1803
    long mode, n;
 
1804
 
 
1805
    rb_secure(2);
 
1806
    rb_scan_args(argc, argv, "1*", &vmode, &rest);
 
1807
    mode = NUM2INT(vmode);
 
1808
 
 
1809
    n = apply2files(lchmod_internal, rest, (void *)(long)mode);
 
1810
    return LONG2FIX(n);
 
1811
}
 
1812
#else
 
1813
static VALUE
 
1814
rb_file_s_lchmod(argc, argv)
 
1815
    int argc;
 
1816
    VALUE *argv;
 
1817
{
 
1818
    rb_notimplement();
 
1819
    return Qnil;                /* not reached */
 
1820
}
 
1821
#endif
 
1822
 
 
1823
struct chown_args {
 
1824
    int owner, group;
 
1825
};
 
1826
 
 
1827
static void chown_internal _((const char *, void *));
 
1828
static void
 
1829
chown_internal(path, argp)
 
1830
    const char *path;
 
1831
    void *argp;
 
1832
{
 
1833
    struct chown_args *args = (struct chown_args *)argp;
 
1834
    if (chown(path, args->owner, args->group) < 0)
 
1835
        rb_sys_fail(path);
 
1836
}
 
1837
 
 
1838
/*
 
1839
 *  call-seq:
 
1840
 *     File.chown(owner_int, group_int, file_name,... ) -> integer
 
1841
 *  
 
1842
 *  Changes the owner and group of the named file(s) to the given
 
1843
 *  numeric owner and group id's. Only a process with superuser
 
1844
 *  privileges may change the owner of a file. The current owner of a
 
1845
 *  file may change the file's group to any group to which the owner
 
1846
 *  belongs. A <code>nil</code> or -1 owner or group id is ignored.
 
1847
 *  Returns the number of files processed.
 
1848
 *     
 
1849
 *     File.chown(nil, 100, "testfile")
 
1850
 *     
 
1851
 */
 
1852
 
 
1853
static VALUE
 
1854
rb_file_s_chown(argc, argv)
 
1855
    int argc;
 
1856
    VALUE *argv;
 
1857
{
 
1858
    VALUE o, g, rest;
 
1859
    struct chown_args arg;
 
1860
    long n;
 
1861
 
 
1862
    rb_secure(2);
 
1863
    rb_scan_args(argc, argv, "2*", &o, &g, &rest);
 
1864
    if (NIL_P(o)) {
 
1865
        arg.owner = -1;
 
1866
    }
 
1867
    else {
 
1868
        arg.owner = NUM2INT(o);
 
1869
    }
 
1870
    if (NIL_P(g)) {
 
1871
        arg.group = -1;
 
1872
    }
 
1873
    else {
 
1874
        arg.group = NUM2INT(g);
 
1875
    }
 
1876
 
 
1877
    n = apply2files(chown_internal, rest, &arg);
 
1878
    return LONG2FIX(n);
 
1879
}
 
1880
 
 
1881
/*
 
1882
 *  call-seq:
 
1883
 *     file.chown(owner_int, group_int )   => 0
 
1884
 *  
 
1885
 *  Changes the owner and group of <i>file</i> to the given numeric
 
1886
 *  owner and group id's. Only a process with superuser privileges may
 
1887
 *  change the owner of a file. The current owner of a file may change
 
1888
 *  the file's group to any group to which the owner belongs. A
 
1889
 *  <code>nil</code> or -1 owner or group id is ignored. Follows
 
1890
 *  symbolic links. See also <code>File#lchown</code>.
 
1891
 *     
 
1892
 *     File.new("testfile").chown(502, 1000)
 
1893
 *     
 
1894
 */
 
1895
 
 
1896
static VALUE
 
1897
rb_file_chown(obj, owner, group)
 
1898
    VALUE obj, owner, group;
 
1899
{
 
1900
    OpenFile *fptr;
 
1901
    int o, g;
 
1902
 
 
1903
    rb_secure(2);
 
1904
    o = NIL_P(owner) ? -1 : NUM2INT(owner);
 
1905
    g = NIL_P(group) ? -1 : NUM2INT(group);
 
1906
    GetOpenFile(obj, fptr);
 
1907
#if defined(DJGPP) || defined(__CYGWIN32__) || defined(_WIN32) || defined(__EMX__)
 
1908
    if (!fptr->path) return Qnil;
 
1909
    if (chown(fptr->path, o, g) == -1)
 
1910
        rb_sys_fail(fptr->path);
 
1911
#else
 
1912
    if (fchown(fileno(fptr->f), o, g) == -1)
 
1913
        rb_sys_fail(fptr->path);
 
1914
#endif
 
1915
 
 
1916
    return INT2FIX(0);
 
1917
}
 
1918
 
 
1919
#if defined(HAVE_LCHOWN) && !defined(__CHECKER__)
 
1920
static void lchown_internal _((const char *, void *));
 
1921
static void
 
1922
lchown_internal(path, argp)
 
1923
    const char *path;
 
1924
    void *argp;
 
1925
{
 
1926
    struct chown_args *args = (struct chown_args *)argp;
 
1927
    if (lchown(path, args->owner, args->group) < 0)
 
1928
        rb_sys_fail(path);
 
1929
}
 
1930
 
 
1931
 
 
1932
/*
 
1933
 *  call-seq:
 
1934
 *     file.lchown(owner_int, group_int, file_name,..) => integer
 
1935
 *  
 
1936
 *  Equivalent to <code>File::chown</code>, but does not follow symbolic
 
1937
 *  links (so it will change the owner associated with the link, not the
 
1938
 *  file referenced by the link). Often not available. Returns number
 
1939
 *  of files in the argument list.
 
1940
 *     
 
1941
 */
 
1942
 
 
1943
static VALUE
 
1944
rb_file_s_lchown(argc, argv)
 
1945
    int argc;
 
1946
    VALUE *argv;
 
1947
{
 
1948
    VALUE o, g, rest;
 
1949
    struct chown_args arg;
 
1950
    long n;
 
1951
 
 
1952
    rb_secure(2);
 
1953
    rb_scan_args(argc, argv, "2*", &o, &g, &rest);
 
1954
    if (NIL_P(o)) {
 
1955
        arg.owner = -1;
 
1956
    }
 
1957
    else {
 
1958
        arg.owner = NUM2INT(o);
 
1959
    }
 
1960
    if (NIL_P(g)) {
 
1961
        arg.group = -1;
 
1962
    }
 
1963
    else {
 
1964
        arg.group = NUM2INT(g);
 
1965
    }
 
1966
 
 
1967
    n = apply2files(lchown_internal, rest, &arg);
 
1968
    return LONG2FIX(n);
 
1969
}
 
1970
#else
 
1971
static VALUE
 
1972
rb_file_s_lchown(argc, argv)
 
1973
    int argc;
 
1974
    VALUE *argv;
 
1975
{
 
1976
    rb_notimplement();
 
1977
}
 
1978
#endif
 
1979
 
 
1980
struct timeval rb_time_timeval();
 
1981
 
 
1982
static void utime_internal _((const char *, void *));
 
1983
 
 
1984
#if defined(HAVE_UTIMES) && !defined(__CHECKER__)
 
1985
 
 
1986
static void
 
1987
utime_internal(path, arg)
 
1988
    const char *path;
 
1989
    void *arg;
 
1990
{
 
1991
    struct timeval *tvp = arg;
 
1992
    if (utimes(path, tvp) < 0)
 
1993
        rb_sys_fail(path);
 
1994
}
 
1995
 
 
1996
/*
 
1997
 * call-seq:
 
1998
 *  File.utime(atime, mtime, file_name,...)   =>  integer
 
1999
 *
 
2000
 * Sets the access and modification times of each
 
2001
 * named file to the first two arguments. Returns
 
2002
 * the number of file names in the argument list.
 
2003
 */
 
2004
 
 
2005
static VALUE
 
2006
rb_file_s_utime(argc, argv)
 
2007
    int argc;
 
2008
    VALUE *argv;
 
2009
{
 
2010
    VALUE atime, mtime, rest;
 
2011
    struct timeval tvs[2], *tvp = NULL;
 
2012
    long n;
 
2013
 
 
2014
    rb_scan_args(argc, argv, "2*", &atime, &mtime, &rest);
 
2015
 
 
2016
    if (!NIL_P(atime) || !NIL_P(mtime)) {
 
2017
        tvp = tvs;
 
2018
        tvp[0] = rb_time_timeval(atime);
 
2019
        tvp[1] = rb_time_timeval(mtime);
 
2020
    }
 
2021
 
 
2022
    n = apply2files(utime_internal, rest, tvp);
 
2023
    return LONG2FIX(n);
 
2024
}
 
2025
 
 
2026
#else
 
2027
 
 
2028
#if !defined HAVE_UTIME_H && !defined HAVE_SYS_UTIME_H
 
2029
struct utimbuf {
 
2030
    long actime;
 
2031
    long modtime;
 
2032
};
 
2033
#endif
 
2034
 
 
2035
static void
 
2036
utime_internal(path, arg)
 
2037
    const char *path;
 
2038
    void *arg;
 
2039
{
 
2040
    struct utimbuf *utp = arg;
 
2041
    if (utime(path, utp) < 0)
 
2042
        rb_sys_fail(path);
 
2043
}
 
2044
 
 
2045
static VALUE
 
2046
rb_file_s_utime(argc, argv)
 
2047
    int argc;
 
2048
    VALUE *argv;
 
2049
{
 
2050
    VALUE atime, mtime, rest;
 
2051
    long n;
 
2052
    struct timeval tv;
 
2053
    struct utimbuf utbuf, *utp = NULL;
 
2054
 
 
2055
    rb_scan_args(argc, argv, "2*", &atime, &mtime, &rest);
 
2056
 
 
2057
    if (!NIL_P(atime) || !NIL_P(mtime)) {
 
2058
        utp = &utbuf;
 
2059
        tv = rb_time_timeval(atime);
 
2060
        utp->actime = tv.tv_sec;
 
2061
        tv = rb_time_timeval(mtime);
 
2062
        utp->modtime = tv.tv_sec;
 
2063
    }
 
2064
 
 
2065
    n = apply2files(utime_internal, rest, utp);
 
2066
    return LONG2FIX(n);
 
2067
}
 
2068
 
 
2069
#endif
 
2070
 
 
2071
NORETURN(static void sys_fail2 _((VALUE,VALUE)));
 
2072
static void
 
2073
sys_fail2(s1, s2)
 
2074
    VALUE s1, s2;
 
2075
{
 
2076
    char *buf;
 
2077
    int len;
 
2078
 
 
2079
    len = RSTRING(s1)->len + RSTRING(s2)->len + 5;
 
2080
    buf = ALLOCA_N(char, len);
 
2081
    snprintf(buf, len, "%s or %s", RSTRING(s1)->ptr, RSTRING(s2)->ptr);
 
2082
    rb_sys_fail(buf);
 
2083
}
 
2084
 
 
2085
/*
 
2086
 *  call-seq:
 
2087
 *     File.link(old_name, new_name)    => 0
 
2088
 *  
 
2089
 *  Creates a new name for an existing file using a hard link. Will not
 
2090
 *  overwrite <i>new_name</i> if it already exists (raising a subclass
 
2091
 *  of <code>SystemCallError</code>). Not available on all platforms.
 
2092
 *     
 
2093
 *     File.link("testfile", ".testfile")   #=> 0
 
2094
 *     IO.readlines(".testfile")[0]         #=> "This is line one\n"
 
2095
 */
 
2096
 
 
2097
static VALUE
 
2098
rb_file_s_link(klass, from, to)
 
2099
    VALUE klass, from, to;
 
2100
{
 
2101
#ifdef HAVE_LINK
 
2102
    SafeStringValue(from);
 
2103
    SafeStringValue(to);
 
2104
 
 
2105
    if (link(StringValueCStr(from), StringValueCStr(to)) < 0) {
 
2106
        sys_fail2(from, to);
 
2107
    }
 
2108
    return INT2FIX(0);
 
2109
#else
 
2110
    rb_notimplement();
 
2111
    return Qnil;                /* not reached */
 
2112
#endif
 
2113
}
 
2114
 
 
2115
/*
 
2116
 *  call-seq:
 
2117
 *     File.symlink(old_name, new_name)   => 0
 
2118
 *  
 
2119
 *  Creates a symbolic link called <i>new_name</i> for the existing file
 
2120
 *  <i>old_name</i>. Raises a <code>NotImplemented</code> exception on
 
2121
 *  platforms that do not support symbolic links.
 
2122
 *     
 
2123
 *     File.symlink("testfile", "link2test")   #=> 0
 
2124
 *     
 
2125
 */
 
2126
 
 
2127
static VALUE
 
2128
rb_file_s_symlink(klass, from, to)
 
2129
    VALUE klass, from, to;
 
2130
{
 
2131
#ifdef HAVE_SYMLINK
 
2132
    SafeStringValue(from);
 
2133
    SafeStringValue(to);
 
2134
 
 
2135
    if (symlink(StringValueCStr(from), StringValueCStr(to)) < 0) {
 
2136
        sys_fail2(from, to);
 
2137
    }
 
2138
    return INT2FIX(0);
 
2139
#else
 
2140
    rb_notimplement();
 
2141
    return Qnil;                /* not reached */
 
2142
#endif
 
2143
}
 
2144
 
 
2145
/*
 
2146
 *  call-seq:
 
2147
 *     File.readlink(link_name) -> file_name
 
2148
 *  
 
2149
 *  Returns the name of the file referenced by the given link.
 
2150
 *  Not available on all platforms.
 
2151
 *     
 
2152
 *     File.symlink("testfile", "link2test")   #=> 0
 
2153
 *     File.readlink("link2test")              #=> "testfile"
 
2154
 */
 
2155
 
 
2156
static VALUE
 
2157
rb_file_s_readlink(klass, path)
 
2158
    VALUE klass, path;
 
2159
{
 
2160
#ifdef HAVE_READLINK
 
2161
    char *buf;
 
2162
    int size = 100;
 
2163
    int rv;
 
2164
    VALUE v;
 
2165
 
 
2166
    SafeStringValue(path);
 
2167
    buf = xmalloc(size);
 
2168
    while ((rv = readlink(RSTRING(path)->ptr, buf, size)) == size
 
2169
#ifdef _AIX
 
2170
            || (rv < 0 && errno == ERANGE) /* quirky behavior of GPFS */
 
2171
#endif
 
2172
        ) {
 
2173
        size *= 2;
 
2174
        buf = xrealloc(buf, size);
 
2175
    }
 
2176
    if (rv < 0) {
 
2177
        free(buf);
 
2178
        rb_sys_fail(RSTRING(path)->ptr);
 
2179
    }
 
2180
    v = rb_tainted_str_new(buf, rv);
 
2181
    free(buf);
 
2182
 
 
2183
    return v;
 
2184
#else
 
2185
    rb_notimplement();
 
2186
    return Qnil;                /* not reached */
 
2187
#endif
 
2188
}
 
2189
 
 
2190
static void unlink_internal _((const char *, void *));
 
2191
static void
 
2192
unlink_internal(path, arg)
 
2193
    const char *path;
 
2194
    void *arg;
 
2195
{
 
2196
    if (unlink(path) < 0)
 
2197
        rb_sys_fail(path);
 
2198
}
 
2199
 
 
2200
/*
 
2201
 *  call-seq:
 
2202
 *     File.delete(file_name, ...)  => integer
 
2203
 *     File.unlink(file_name, ...)  => integer
 
2204
 *  
 
2205
 *  Deletes the named files, returning the number of names
 
2206
 *  passed as arguments. Raises an exception on any error.
 
2207
 *  See also <code>Dir::rmdir</code>.
 
2208
 */
 
2209
 
 
2210
static VALUE
 
2211
rb_file_s_unlink(klass, args)
 
2212
    VALUE klass, args;
 
2213
{
 
2214
    long n;
 
2215
 
 
2216
    rb_secure(2);
 
2217
    n = apply2files(unlink_internal, args, 0);
 
2218
    return LONG2FIX(n);
 
2219
}
 
2220
 
 
2221
/*
 
2222
 *  call-seq:
 
2223
 *     File.rename(old_name, new_name)   => 0
 
2224
 *  
 
2225
 *  Renames the given file to the new name. Raises a
 
2226
 *  <code>SystemCallError</code> if the file cannot be renamed.
 
2227
 *     
 
2228
 *     File.rename("afile", "afile.bak")   #=> 0
 
2229
 */
 
2230
 
 
2231
static VALUE
 
2232
rb_file_s_rename(klass, from, to)
 
2233
    VALUE klass, from, to;
 
2234
{
 
2235
    const char *src, *dst;
 
2236
    SafeStringValue(from);
 
2237
    SafeStringValue(to);
 
2238
 
 
2239
    src = StringValueCStr(from);
 
2240
    dst = StringValueCStr(to);
 
2241
#if defined __CYGWIN__
 
2242
    errno = 0;
 
2243
#endif
 
2244
    if (rename(src, dst) < 0) {
 
2245
#if defined __CYGWIN__
 
2246
        extern unsigned long __attribute__((stdcall)) GetLastError(void);
 
2247
        if (errno == 0) {       /* This is a bug of old Cygwin */
 
2248
            /* incorrect as cygwin errno, but the last resort */
 
2249
            errno = GetLastError();
 
2250
        }
 
2251
#elif defined DOSISH && !defined _WIN32
 
2252
        if (errno == EEXIST
 
2253
#if defined (__EMX__)
 
2254
            || errno == EACCES
 
2255
#endif
 
2256
            ) {
 
2257
            if (chmod(dst, 0666) == 0 &&
 
2258
                unlink(dst) == 0 &&
 
2259
                rename(src, dst) == 0)
 
2260
                return INT2FIX(0);
 
2261
        }
 
2262
#endif
 
2263
        sys_fail2(from, to);
 
2264
    }
 
2265
 
 
2266
    return INT2FIX(0);
 
2267
}
 
2268
 
 
2269
/*
 
2270
 *  call-seq:
 
2271
 *     File.umask()          => integer
 
2272
 *     File.umask(integer)   => integer
 
2273
 *  
 
2274
 *  Returns the current umask value for this process. If the optional
 
2275
 *  argument is given, set the umask to that value and return the
 
2276
 *  previous value. Umask values are <em>subtracted</em> from the
 
2277
 *  default permissions, so a umask of <code>0222</code> would make a
 
2278
 *  file read-only for everyone.
 
2279
 *     
 
2280
 *     File.umask(0006)   #=> 18
 
2281
 *     File.umask         #=> 6
 
2282
 */
 
2283
 
 
2284
static VALUE
 
2285
rb_file_s_umask(argc, argv)
 
2286
    int argc;
 
2287
    VALUE *argv;
 
2288
{
 
2289
    int omask = 0;
 
2290
 
 
2291
    rb_secure(2);
 
2292
    if (argc == 0) {
 
2293
        omask = umask(0);
 
2294
        umask(omask);
 
2295
    }
 
2296
    else if (argc == 1) {
 
2297
        omask = umask(NUM2INT(argv[0]));
 
2298
    }
 
2299
    else {
 
2300
        rb_raise(rb_eArgError, "wrong number of arguments");
 
2301
    }
 
2302
    return INT2FIX(omask);
 
2303
}
 
2304
 
 
2305
#if defined DOSISH
 
2306
#define DOSISH_UNC
 
2307
#define isdirsep(x) ((x) == '/' || (x) == '\\')
 
2308
#else
 
2309
#define isdirsep(x) ((x) == '/')
 
2310
#endif
 
2311
#ifndef CharNext                /* defined as CharNext[AW] on Windows. */
 
2312
# if defined(DJGPP)
 
2313
#   define CharNext(p) ((p) + mblen(p, MB_CUR_MAX))
 
2314
# else
 
2315
#   define CharNext(p) ((p) + 1)
 
2316
# endif
 
2317
#endif
 
2318
 
 
2319
#ifdef __CYGWIN__
 
2320
#undef DOSISH
 
2321
#define DOSISH_UNC
 
2322
#define DOSISH_DRIVE_LETTER
 
2323
#endif
 
2324
 
 
2325
#ifdef DOSISH_DRIVE_LETTER
 
2326
static inline int
 
2327
has_drive_letter(buf)
 
2328
    const char *buf;
 
2329
{
 
2330
    if (ISALPHA(buf[0]) && buf[1] == ':') {
 
2331
        return 1;
 
2332
    }
 
2333
    else {
 
2334
        return 0;
 
2335
    }
 
2336
}
 
2337
 
 
2338
static char*
 
2339
getcwdofdrv(drv)
 
2340
    int drv;
 
2341
{
 
2342
    char drive[4];
 
2343
    char *drvcwd, *oldcwd;
 
2344
 
 
2345
    drive[0] = drv;
 
2346
    drive[1] = ':';
 
2347
    drive[2] = '\0';
 
2348
 
 
2349
    /* the only way that I know to get the current directory
 
2350
       of a particular drive is to change chdir() to that drive,
 
2351
       so save the old cwd before chdir()
 
2352
    */
 
2353
    oldcwd = my_getcwd();
 
2354
    if (chdir(drive) == 0) {
 
2355
        drvcwd = my_getcwd();
 
2356
        chdir(oldcwd);
 
2357
        free(oldcwd);
 
2358
    }
 
2359
    else {
 
2360
        /* perhaps the drive is not exist. we return only drive letter */
 
2361
        drvcwd = strdup(drive);
 
2362
    }
 
2363
    return drvcwd;
 
2364
}
 
2365
#endif
 
2366
 
 
2367
static inline char *
 
2368
skiproot(path)
 
2369
    const char *path;
 
2370
{
 
2371
#ifdef DOSISH_DRIVE_LETTER
 
2372
    if (has_drive_letter(path)) path += 2;
 
2373
#endif
 
2374
    while (isdirsep(*path)) path++;
 
2375
    return (char *)path;
 
2376
}
 
2377
 
 
2378
#define nextdirsep rb_path_next
 
2379
char *
 
2380
rb_path_next(s)
 
2381
    const char *s;
 
2382
{
 
2383
    while (*s && !isdirsep(*s)) {
 
2384
        s = CharNext(s);
 
2385
    }
 
2386
    return (char *)s;
 
2387
}
 
2388
 
 
2389
#if defined(DOSISH_UNC) || defined(DOSISH_DRIVE_LETTER) 
 
2390
#define skipprefix rb_path_skip_prefix
 
2391
#else
 
2392
#define skipprefix(path) (path)
 
2393
#endif
 
2394
char *
 
2395
rb_path_skip_prefix(path)
 
2396
    const char *path;
 
2397
{
 
2398
#if defined(DOSISH_UNC) || defined(DOSISH_DRIVE_LETTER) 
 
2399
#ifdef DOSISH_UNC
 
2400
    if (isdirsep(path[0]) && isdirsep(path[1])) {
 
2401
        path += 2;
 
2402
        while (isdirsep(*path)) path++;
 
2403
        if (*(path = nextdirsep(path)) && path[1] && !isdirsep(path[1]))
 
2404
            path = nextdirsep(path + 1);
 
2405
        return (char *)path;
 
2406
    }
 
2407
#endif
 
2408
#ifdef DOSISH_DRIVE_LETTER
 
2409
    if (has_drive_letter(path))
 
2410
        return (char *)(path + 2);
 
2411
#endif
 
2412
#endif
 
2413
    return (char *)path;
 
2414
}
 
2415
 
 
2416
#define strrdirsep rb_path_last_separator
 
2417
char *
 
2418
rb_path_last_separator(path)
 
2419
    const char *path;
 
2420
{
 
2421
    char *last = NULL;
 
2422
    while (*path) {
 
2423
        if (isdirsep(*path)) {
 
2424
            const char *tmp = path++;
 
2425
            while (isdirsep(*path)) path++;
 
2426
            if (!*path) break;
 
2427
            last = (char *)tmp;
 
2428
        }
 
2429
        else {
 
2430
            path = CharNext(path);
 
2431
        }
 
2432
    }
 
2433
    return last;
 
2434
}
 
2435
 
 
2436
char *
 
2437
chompdirsep(path)
 
2438
    const char *path;
 
2439
{
 
2440
    while (*path) {
 
2441
        if (isdirsep(*path)) {
 
2442
            const char *last = path++;
 
2443
            while (isdirsep(*path)) path++;
 
2444
            if (!*path) return (char *)last;
 
2445
        }
 
2446
        else {
 
2447
            path = CharNext(path);
 
2448
        }
 
2449
    }
 
2450
    return (char *)path;
 
2451
}
 
2452
 
 
2453
char *
 
2454
rb_path_end(path)
 
2455
    const char *path;
 
2456
{
 
2457
    if (isdirsep(*path)) path++;
 
2458
    return chompdirsep(path);
 
2459
}
 
2460
 
 
2461
#define BUFCHECK(cond) do {\
 
2462
    long bdiff = p - buf;\
 
2463
    while (cond) {\
 
2464
        buflen *= 2;\
 
2465
    }\
 
2466
    rb_str_resize(result, buflen);\
 
2467
    buf = RSTRING(result)->ptr;\
 
2468
    p = buf + bdiff;\
 
2469
    pend = buf + buflen;\
 
2470
} while (0)
 
2471
 
 
2472
#define BUFINIT() (\
 
2473
    p = buf = RSTRING(result)->ptr,\
 
2474
    buflen = RSTRING(result)->len,\
 
2475
    pend = p + buflen)
 
2476
 
 
2477
#if !defined(TOLOWER)
 
2478
#define TOLOWER(c) (ISUPPER(c) ? tolower(c) : (c))
 
2479
#endif
 
2480
 
 
2481
static int is_absolute_path _((const char*));
 
2482
 
 
2483
static VALUE
 
2484
file_expand_path(fname, dname, result)
 
2485
    VALUE fname, dname, result;
 
2486
{
 
2487
    char *s, *buf, *b, *p, *pend, *root;
 
2488
    long buflen, dirlen;
 
2489
    int tainted;
 
2490
 
 
2491
    s = StringValuePtr(fname);
 
2492
    BUFINIT();
 
2493
    tainted = OBJ_TAINTED(fname);
 
2494
 
 
2495
    if (s[0] == '~') {
 
2496
        if (isdirsep(s[1]) || s[1] == '\0') {
 
2497
            char *dir = getenv("HOME");
 
2498
 
 
2499
            if (!dir) {
 
2500
                rb_raise(rb_eArgError, "couldn't find HOME environment -- expanding `%s'", s);
 
2501
            }
 
2502
            dirlen = strlen(dir);
 
2503
            BUFCHECK(dirlen > buflen);
 
2504
            strcpy(buf, dir);
 
2505
#if defined DOSISH || defined __CYGWIN__
 
2506
            for (p = buf; *p; p = CharNext(p)) {
 
2507
                if (*p == '\\') {
 
2508
                    *p = '/';
 
2509
                }
 
2510
            }
 
2511
#else
 
2512
            p = buf + strlen(dir);
 
2513
#endif
 
2514
            s++;
 
2515
            tainted = 1;
 
2516
        }
 
2517
        else {
 
2518
#ifdef HAVE_PWD_H
 
2519
            struct passwd *pwPtr;
 
2520
            s++;
 
2521
#endif
 
2522
            s = nextdirsep(b = s);
 
2523
            BUFCHECK(bdiff + (s-b) >= buflen);
 
2524
            memcpy(p, b, s-b);
 
2525
            p += s-b;
 
2526
            *p = '\0';
 
2527
#ifdef HAVE_PWD_H
 
2528
            pwPtr = getpwnam(buf);
 
2529
            if (!pwPtr) {
 
2530
                endpwent();
 
2531
                rb_raise(rb_eArgError, "user %s doesn't exist", buf);
 
2532
            }
 
2533
            dirlen = strlen(pwPtr->pw_dir);
 
2534
            BUFCHECK(dirlen > buflen);
 
2535
            strcpy(buf, pwPtr->pw_dir);
 
2536
            p = buf + strlen(pwPtr->pw_dir);
 
2537
            endpwent();
 
2538
#endif
 
2539
        }
 
2540
    }
 
2541
#ifdef DOSISH_DRIVE_LETTER
 
2542
    /* skip drive letter */
 
2543
    else if (has_drive_letter(s)) {
 
2544
        if (isdirsep(s[2])) {
 
2545
            /* specified drive letter, and full path */
 
2546
            /* skip drive letter */
 
2547
            BUFCHECK(bdiff + 2 >= buflen);
 
2548
            memcpy(p, s, 2);
 
2549
            p += 2;
 
2550
            s += 2;
 
2551
        }
 
2552
        else {
 
2553
            /* specified drive, but not full path */
 
2554
            int same = 0;
 
2555
            if (!NIL_P(dname)) {
 
2556
                file_expand_path(dname, Qnil, result);
 
2557
                BUFINIT();
 
2558
                if (has_drive_letter(p) && TOLOWER(p[0]) == TOLOWER(s[0])) {
 
2559
                    /* ok, same drive */
 
2560
                    same = 1;
 
2561
                }
 
2562
            }
 
2563
            if (!same) {
 
2564
                char *dir = getcwdofdrv(*s);
 
2565
 
 
2566
                tainted = 1;
 
2567
                dirlen = strlen(dir);
 
2568
                BUFCHECK(dirlen > buflen);
 
2569
                strcpy(buf, dir);
 
2570
                free(dir);
 
2571
            }
 
2572
            p = chompdirsep(skiproot(buf));
 
2573
            s += 2;
 
2574
        }
 
2575
    }
 
2576
#endif
 
2577
    else if (!is_absolute_path(s)) {
 
2578
        if (!NIL_P(dname)) {
 
2579
            file_expand_path(dname, Qnil, result);
 
2580
            BUFINIT();
 
2581
        }
 
2582
        else {
 
2583
            char *dir = my_getcwd();
 
2584
 
 
2585
            tainted = 1;
 
2586
            dirlen = strlen(dir);
 
2587
            BUFCHECK(dirlen > buflen);
 
2588
            strcpy(buf, dir);
 
2589
            free(dir);
 
2590
        }
 
2591
#if defined DOSISH || defined __CYGWIN__
 
2592
        if (isdirsep(*s)) {
 
2593
            /* specified full path, but not drive letter nor UNC */
 
2594
            /* we need to get the drive letter or UNC share name */
 
2595
            p = skipprefix(buf);
 
2596
        }
 
2597
        else
 
2598
#endif
 
2599
            p = chompdirsep(skiproot(buf));
 
2600
    }
 
2601
    else {
 
2602
        b = s;
 
2603
        do s++; while (isdirsep(*s));
 
2604
        p = buf + (s - b);
 
2605
        BUFCHECK(bdiff >= buflen);
 
2606
        memset(buf, '/', p - buf);
 
2607
    }
 
2608
    if (p > buf && p[-1] == '/')
 
2609
        --p;
 
2610
    else
 
2611
        *p = '/';
 
2612
 
 
2613
    p[1] = 0;
 
2614
    root = skipprefix(buf);
 
2615
 
 
2616
    b = s;
 
2617
    while (*s) {
 
2618
        switch (*s) {
 
2619
          case '.':
 
2620
            if (b == s++) {     /* beginning of path element */
 
2621
                switch (*s) {
 
2622
                  case '\0':
 
2623
                    b = s;
 
2624
                    break;
 
2625
                  case '.':
 
2626
                    if (*(s+1) == '\0' || isdirsep(*(s+1))) {
 
2627
                        /* We must go back to the parent */
 
2628
                        *p = '\0';
 
2629
                        if (!(b = strrdirsep(root))) {
 
2630
                            *p = '/';
 
2631
                        }
 
2632
                        else {
 
2633
                            p = b;
 
2634
                        }
 
2635
                        b = ++s;
 
2636
                    }
 
2637
                    break;
 
2638
                  case '/':
 
2639
#if defined DOSISH || defined __CYGWIN__
 
2640
                  case '\\':
 
2641
#endif
 
2642
                    b = ++s;
 
2643
                    break;
 
2644
                  default:
 
2645
                    /* ordinary path element, beginning don't move */
 
2646
                    break;
 
2647
                }
 
2648
            }
 
2649
            break;
 
2650
          case '/':
 
2651
#if defined DOSISH || defined __CYGWIN__
 
2652
          case '\\':
 
2653
#endif
 
2654
            if (s > b) {
 
2655
                long rootdiff = root - buf;
 
2656
                BUFCHECK(bdiff + (s-b+1) >= buflen);
 
2657
                root = buf + rootdiff;
 
2658
                memcpy(++p, b, s-b);
 
2659
                p += s-b;
 
2660
                *p = '/';
 
2661
            }
 
2662
            b = ++s;
 
2663
            break;
 
2664
          default:
 
2665
            s = CharNext(s);
 
2666
            break;
 
2667
        }
 
2668
    }
 
2669
 
 
2670
    if (s > b) {
 
2671
        BUFCHECK(bdiff + (s-b) >= buflen);
 
2672
        memcpy(++p, b, s-b);
 
2673
        p += s-b;
 
2674
    }
 
2675
    if (p == skiproot(buf) - 1) p++;
 
2676
 
 
2677
    if (tainted) OBJ_TAINT(result);
 
2678
    RSTRING(result)->len = p - buf;
 
2679
    *p = '\0';
 
2680
    return result;
 
2681
}
 
2682
 
 
2683
VALUE
 
2684
rb_file_expand_path(fname, dname)
 
2685
    VALUE fname, dname;
 
2686
{
 
2687
    return file_expand_path(fname, dname, rb_str_new(0, MAXPATHLEN + 2));
 
2688
}
 
2689
 
 
2690
/*
 
2691
 *  call-seq:
 
2692
 *     File.expand_path(file_name [, dir_string] ) -> abs_file_name
 
2693
 *  
 
2694
 *  Converts a pathname to an absolute pathname. Relative paths are
 
2695
 *  referenced from the current working directory of the process unless
 
2696
 *  <i>dir_string</i> is given, in which case it will be used as the
 
2697
 *  starting point. The given pathname may start with a
 
2698
 *  ``<code>~</code>'', which expands to the process owner's home
 
2699
 *  directory (the environment variable <code>HOME</code> must be set
 
2700
 *  correctly). ``<code>~</code><i>user</i>'' expands to the named
 
2701
 *  user's home directory.
 
2702
 *     
 
2703
 *     File.expand_path("~oracle/bin")           #=> "/home/oracle/bin"
 
2704
 *     File.expand_path("../../bin", "/tmp/x")   #=> "/bin"
 
2705
 */
 
2706
 
 
2707
VALUE
 
2708
rb_file_s_expand_path(argc, argv)
 
2709
    int argc;
 
2710
    VALUE *argv;
 
2711
{
 
2712
    VALUE fname, dname;
 
2713
 
 
2714
    if (argc == 1) {
 
2715
        return rb_file_expand_path(argv[0], Qnil);
 
2716
    }
 
2717
    rb_scan_args(argc, argv, "11", &fname, &dname);
 
2718
 
 
2719
    return rb_file_expand_path(fname, dname);
 
2720
}
 
2721
 
 
2722
static int
 
2723
rmext(p, e)
 
2724
    const char *p, *e;
 
2725
{
 
2726
    int l1, l2;
 
2727
 
 
2728
    if (!e) return 0;
 
2729
 
 
2730
    l1 = chompdirsep(p) - p;
 
2731
    l2 = strlen(e);
 
2732
    if (l2 == 2 && e[1] == '*') {
 
2733
        e = strrchr(p, *e);
 
2734
        if (!e) return 0;
 
2735
        return e - p;
 
2736
    }
 
2737
    if (l1 < l2) return l1;
 
2738
 
 
2739
    if (strncmp(p+l1-l2, e, l2) == 0) {
 
2740
        return l1-l2;
 
2741
    }
 
2742
    return 0;
 
2743
}
 
2744
 
 
2745
/*
 
2746
 *  call-seq:
 
2747
 *     File.basename(file_name [, suffix] ) -> base_name
 
2748
 *  
 
2749
 *  Returns the last component of the filename given in <i>file_name</i>,
 
2750
 *  which must be formed using forward slashes (``<code>/</code>'')
 
2751
 *  regardless of the separator used on the local file system. If
 
2752
 *  <i>suffix</i> is given and present at the end of <i>file_name</i>,
 
2753
 *  it is removed.
 
2754
 *     
 
2755
 *     File.basename("/home/gumby/work/ruby.rb")          #=> "ruby.rb"
 
2756
 *     File.basename("/home/gumby/work/ruby.rb", ".rb")   #=> "ruby"
 
2757
 */
 
2758
 
 
2759
static VALUE
 
2760
rb_file_s_basename(argc, argv)
 
2761
    int argc;
 
2762
    VALUE *argv;
 
2763
{
 
2764
    VALUE fname, fext, basename;
 
2765
    char *name, *p;
 
2766
#if defined DOSISH_DRIVE_LETTER || defined DOSISH_UNC
 
2767
    char *root;
 
2768
#endif
 
2769
    int f;
 
2770
 
 
2771
    if (rb_scan_args(argc, argv, "11", &fname, &fext) == 2) {
 
2772
        StringValue(fext);
 
2773
    }
 
2774
    StringValue(fname);
 
2775
    if (RSTRING(fname)->len == 0 || !*(name = RSTRING(fname)->ptr))
 
2776
        return fname;
 
2777
    name = skipprefix(name);
 
2778
#if defined DOSISH_DRIVE_LETTER || defined DOSISH_UNC
 
2779
    root = name;
 
2780
#endif
 
2781
    while (isdirsep(*name))
 
2782
        name++;
 
2783
    if (!*name) {
 
2784
        p = name - 1;
 
2785
        f = 1;
 
2786
#if defined DOSISH_DRIVE_LETTER || defined DOSISH_UNC
 
2787
        if (name != root) {
 
2788
            /* has slashes */
 
2789
        }
 
2790
#ifdef DOSISH_DRIVE_LETTER
 
2791
        else if (*p == ':') {
 
2792
            p++;
 
2793
            f = 0;
 
2794
        }
 
2795
#endif
 
2796
#ifdef DOSISH_UNC
 
2797
        else {
 
2798
            p = "/";
 
2799
        }
 
2800
#endif
 
2801
#endif
 
2802
    }
 
2803
    else if (!(p = strrdirsep(name))) {
 
2804
        if (NIL_P(fext) || !(f = rmext(name, StringValueCStr(fext)))) {
 
2805
            f = chompdirsep(name) - name;
 
2806
            if (f == RSTRING(fname)->len) return fname;
 
2807
        }
 
2808
        p = name;
 
2809
    }
 
2810
    else {
 
2811
        while (isdirsep(*p)) p++; /* skip last / */
 
2812
        if (NIL_P(fext) || !(f = rmext(p, StringValueCStr(fext)))) {
 
2813
            f = chompdirsep(p) - p;
 
2814
        }
 
2815
    }
 
2816
    basename = rb_str_new(p, f);
 
2817
    OBJ_INFECT(basename, fname);
 
2818
    return basename;
 
2819
}
 
2820
 
 
2821
/*
 
2822
 *  call-seq:
 
2823
 *     File.dirname(file_name ) -> dir_name
 
2824
 *  
 
2825
 *  Returns all components of the filename given in <i>file_name</i>
 
2826
 *  except the last one. The filename must be formed using forward
 
2827
 *  slashes (``<code>/</code>'') regardless of the separator used on the
 
2828
 *  local file system.
 
2829
 *     
 
2830
 *     File.dirname("/home/gumby/work/ruby.rb")   #=> "/home/gumby/work"
 
2831
 */
 
2832
 
 
2833
static VALUE
 
2834
rb_file_s_dirname(klass, fname)
 
2835
    VALUE klass, fname;
 
2836
{
 
2837
    const char *name, *root, *p;
 
2838
    VALUE dirname;
 
2839
 
 
2840
    name = StringValueCStr(fname);
 
2841
    root = skiproot(name);
 
2842
#ifdef DOSISH_UNC
 
2843
    if (root > name + 1 && isdirsep(*name))
 
2844
        root = skipprefix(name = root - 2);
 
2845
#else
 
2846
    if (root > name + 1)
 
2847
        name = root - 1;
 
2848
#endif
 
2849
    p = strrdirsep(root);
 
2850
    if (!p) {
 
2851
        p = root;
 
2852
    }
 
2853
    if (p == name)
 
2854
        return rb_str_new2(".");
 
2855
#ifdef DOSISH_DRIVE_LETTER
 
2856
    if (has_drive_letter(name) && isdirsep(*(name + 2))) {
 
2857
        const char *top = skiproot(name + 2);
 
2858
        dirname = rb_str_new(name, 3);
 
2859
        rb_str_cat(dirname, top, p - top);
 
2860
    }
 
2861
    else
 
2862
#endif
 
2863
    dirname = rb_str_new(name, p - name);
 
2864
#ifdef DOSISH_DRIVE_LETTER
 
2865
    if (has_drive_letter(name) && root == name + 2 && p - name == 2)
 
2866
        rb_str_cat(dirname, ".", 1);
 
2867
#endif
 
2868
    OBJ_INFECT(dirname, fname);
 
2869
    return dirname;
 
2870
}
 
2871
 
 
2872
/*
 
2873
 *  call-seq:
 
2874
 *     File.extname(path) -> string
 
2875
 *  
 
2876
 *  Returns the extension (the portion of file name in <i>path</i>
 
2877
 *  after the period).
 
2878
 *     
 
2879
 *     File.extname("test.rb")         #=> ".rb"
 
2880
 *     File.extname("a/b/d/test.rb")   #=> ".rb"
 
2881
 *     File.extname("test")            #=> ""
 
2882
 *     File.extname(".profile")        #=> ""
 
2883
 *     
 
2884
 */
 
2885
 
 
2886
static VALUE
 
2887
rb_file_s_extname(klass, fname)
 
2888
    VALUE klass, fname;
 
2889
{
 
2890
    char *name, *p, *e;
 
2891
    VALUE extname;
 
2892
 
 
2893
    name = StringValueCStr(fname);
 
2894
    p = strrdirsep(name);       /* get the last path component */
 
2895
    if (!p)
 
2896
        p = name;
 
2897
    else
 
2898
        p++;
 
2899
 
 
2900
     e = strrchr(p, '.');       /* get the last dot of the last component */
 
2901
     if (!e || e == p || !e[1]) /* no dot, or the only dot is first or end? */
 
2902
         return rb_str_new2("");
 
2903
     extname = rb_str_new(e, chompdirsep(e) - e);       /* keep the dot, too! */
 
2904
     OBJ_INFECT(extname, fname);
 
2905
     return extname;
 
2906
}
 
2907
 
 
2908
/*
 
2909
 *  call-seq:
 
2910
 *     File.split(file_name)   => array
 
2911
 *  
 
2912
 *  Splits the given string into a directory and a file component and
 
2913
 *  returns them in a two-element array. See also
 
2914
 *  <code>File::dirname</code> and <code>File::basename</code>.
 
2915
 *     
 
2916
 *     File.split("/home/gumby/.profile")   #=> ["/home/gumby", ".profile"]
 
2917
 */
 
2918
 
 
2919
static VALUE
 
2920
rb_file_s_split(klass, path)
 
2921
    VALUE klass, path;
 
2922
{
 
2923
    StringValue(path);          /* get rid of converting twice */
 
2924
    return rb_assoc_new(rb_file_s_dirname(Qnil, path), rb_file_s_basename(1,&path));
 
2925
}
 
2926
 
 
2927
static VALUE separator;
 
2928
 
 
2929
static VALUE rb_file_join _((VALUE ary, VALUE sep));
 
2930
 
 
2931
static VALUE
 
2932
file_inspect_join(ary, arg)
 
2933
    VALUE ary;
 
2934
    VALUE *arg;
 
2935
{
 
2936
    return rb_file_join(arg[0], arg[1]);
 
2937
}
 
2938
 
 
2939
static VALUE
 
2940
rb_file_join(ary, sep)
 
2941
    VALUE ary, sep;
 
2942
{
 
2943
    long len, i;
 
2944
    int taint = 0;
 
2945
    VALUE result, tmp;
 
2946
    char *name, *tail;
 
2947
 
 
2948
    if (RARRAY(ary)->len == 0) return rb_str_new(0, 0);
 
2949
    if (OBJ_TAINTED(ary)) taint = 1;
 
2950
    if (OBJ_TAINTED(sep)) taint = 1;
 
2951
 
 
2952
    len = 1;
 
2953
    for (i=0; i<RARRAY(ary)->len; i++) {
 
2954
        if (TYPE(RARRAY(ary)->ptr[i]) == T_STRING) {
 
2955
            len += RSTRING(RARRAY(ary)->ptr[i])->len;
 
2956
        }
 
2957
        else {
 
2958
            len += 10;
 
2959
        }
 
2960
    }
 
2961
    if (!NIL_P(sep) && TYPE(sep) == T_STRING) {
 
2962
        len += RSTRING(sep)->len * RARRAY(ary)->len - 1;
 
2963
    }
 
2964
    result = rb_str_buf_new(len);
 
2965
    for (i=0; i<RARRAY(ary)->len; i++) {
 
2966
        tmp = RARRAY(ary)->ptr[i];
 
2967
        switch (TYPE(tmp)) {
 
2968
          case T_STRING:
 
2969
            break;
 
2970
          case T_ARRAY:
 
2971
            if (rb_inspecting_p(tmp)) {
 
2972
                tmp = rb_str_new2("[...]");
 
2973
            }
 
2974
            else {
 
2975
                VALUE args[2];
 
2976
 
 
2977
                args[0] = tmp;
 
2978
                args[1] = sep;
 
2979
                tmp = rb_protect_inspect(file_inspect_join, ary, (VALUE)args);
 
2980
            }
 
2981
            break;
 
2982
          default:
 
2983
            StringValueCStr(tmp);
 
2984
        }
 
2985
        name = StringValueCStr(result);
 
2986
        if (i > 0 && !NIL_P(sep)) {
 
2987
            tail = chompdirsep(name);
 
2988
            if (RSTRING(tmp)->ptr && isdirsep(RSTRING(tmp)->ptr[0])) {
 
2989
                RSTRING(result)->len = tail - name;
 
2990
            }
 
2991
            else if (!*tail) {
 
2992
                rb_str_buf_append(result, sep);
 
2993
            }
 
2994
        }
 
2995
        rb_str_buf_append(result, tmp);
 
2996
        if (OBJ_TAINTED(tmp)) taint = 1;
 
2997
    }
 
2998
 
 
2999
    if (taint) OBJ_TAINT(result);
 
3000
    return result;
 
3001
}
 
3002
 
 
3003
/*
 
3004
 *  call-seq:
 
3005
 *     File.join(string, ...) -> path
 
3006
 *  
 
3007
 *  Returns a new string formed by joining the strings using
 
3008
 *  <code>File::SEPARATOR</code>.
 
3009
 *     
 
3010
 *     File.join("usr", "mail", "gumby")   #=> "usr/mail/gumby"
 
3011
 *     
 
3012
 */
 
3013
 
 
3014
static VALUE
 
3015
rb_file_s_join(klass, args)
 
3016
    VALUE klass, args;
 
3017
{
 
3018
    return rb_file_join(args, separator);
 
3019
}
 
3020
 
 
3021
/*
 
3022
 *  call-seq:
 
3023
 *     File.truncate(file_name, integer)  => 0
 
3024
 *  
 
3025
 *  Truncates the file <i>file_name</i> to be at most <i>integer</i>
 
3026
 *  bytes long. Not available on all platforms.
 
3027
 *     
 
3028
 *     f = File.new("out", "w")
 
3029
 *     f.write("1234567890")     #=> 10
 
3030
 *     f.close                   #=> nil
 
3031
 *     File.truncate("out", 5)   #=> 0
 
3032
 *     File.size("out")          #=> 5
 
3033
 *     
 
3034
 */
 
3035
 
 
3036
static VALUE
 
3037
rb_file_s_truncate(klass, path, len)
 
3038
    VALUE klass, path, len;
 
3039
{
 
3040
    off_t pos;
 
3041
 
 
3042
    rb_secure(2);
 
3043
    pos = NUM2OFFT(len);
 
3044
    SafeStringValue(path);
 
3045
 
 
3046
#ifdef HAVE_TRUNCATE
 
3047
    if (truncate(StringValueCStr(path), pos) < 0)
 
3048
        rb_sys_fail(RSTRING(path)->ptr);
 
3049
#else
 
3050
# ifdef HAVE_CHSIZE
 
3051
    {
 
3052
        int tmpfd;
 
3053
 
 
3054
#  ifdef _WIN32
 
3055
        if ((tmpfd = open(StringValueCStr(path), O_RDWR)) < 0) {
 
3056
            rb_sys_fail(RSTRING(path)->ptr);
 
3057
        }
 
3058
#  else
 
3059
        if ((tmpfd = open(StringValueCStr(path), 0)) < 0) {
 
3060
            rb_sys_fail(RSTRING(path)->ptr);
 
3061
        }
 
3062
#  endif
 
3063
        if (chsize(tmpfd, pos) < 0) {
 
3064
            close(tmpfd);
 
3065
            rb_sys_fail(RSTRING(path)->ptr);
 
3066
        }
 
3067
        close(tmpfd);
 
3068
    }
 
3069
# else
 
3070
    rb_notimplement();
 
3071
# endif
 
3072
#endif
 
3073
    return INT2FIX(0);
 
3074
}
 
3075
 
 
3076
/*
 
3077
 *  call-seq:
 
3078
 *     file.truncate(integer)    => 0
 
3079
 *  
 
3080
 *  Truncates <i>file</i> to at most <i>integer</i> bytes. The file
 
3081
 *  must be opened for writing. Not available on all platforms.
 
3082
 *     
 
3083
 *     f = File.new("out", "w")
 
3084
 *     f.syswrite("1234567890")   #=> 10
 
3085
 *     f.truncate(5)              #=> 0
 
3086
 *     f.close()                  #=> nil
 
3087
 *     File.size("out")           #=> 5
 
3088
 */
 
3089
 
 
3090
static VALUE
 
3091
rb_file_truncate(obj, len)
 
3092
    VALUE obj, len;
 
3093
{
 
3094
    OpenFile *fptr;
 
3095
    FILE *f;
 
3096
    off_t pos;
 
3097
 
 
3098
    rb_secure(2);
 
3099
    pos = NUM2OFFT(len);
 
3100
    GetOpenFile(obj, fptr);
 
3101
    if (!(fptr->mode & FMODE_WRITABLE)) {
 
3102
        rb_raise(rb_eIOError, "not opened for writing");
 
3103
    }
 
3104
    f = GetWriteFile(fptr);
 
3105
    fflush(f);
 
3106
    fseeko(f, (off_t)0, SEEK_CUR);
 
3107
#ifdef HAVE_TRUNCATE
 
3108
    if (ftruncate(fileno(f), pos) < 0)
 
3109
        rb_sys_fail(fptr->path);
 
3110
#else
 
3111
# ifdef HAVE_CHSIZE
 
3112
    if (chsize(fileno(f), pos) < 0)
 
3113
        rb_sys_fail(fptr->path);
 
3114
# else
 
3115
    rb_notimplement();
 
3116
# endif
 
3117
#endif
 
3118
    return INT2FIX(0);
 
3119
}
 
3120
 
 
3121
# ifndef LOCK_SH
 
3122
#  define LOCK_SH 1
 
3123
# endif
 
3124
# ifndef LOCK_EX
 
3125
#  define LOCK_EX 2
 
3126
# endif
 
3127
# ifndef LOCK_NB
 
3128
#  define LOCK_NB 4
 
3129
# endif
 
3130
# ifndef LOCK_UN
 
3131
#  define LOCK_UN 8
 
3132
# endif
 
3133
 
 
3134
#ifdef __CYGWIN__
 
3135
static int
 
3136
#include <winerror.h>
 
3137
cygwin_flock(int fd, int op)
 
3138
{
 
3139
    int old_errno = errno;
 
3140
    int ret = flock(fd, op);
 
3141
    if (GetLastError() == ERROR_NOT_LOCKED) {
 
3142
        ret = 0;
 
3143
        errno = old_errno;
 
3144
    }
 
3145
    return ret;
 
3146
}
 
3147
# define flock(fd, op) cygwin_flock(fd, op)
 
3148
#endif
 
3149
 
 
3150
static int
 
3151
rb_thread_flock(fd, op, fptr)
 
3152
    int fd, op;
 
3153
    OpenFile *fptr;
 
3154
{
 
3155
    if (rb_thread_alone() || (op & LOCK_NB)) {
 
3156
        int ret;
 
3157
        TRAP_BEG;
 
3158
        ret = flock(fd, op);
 
3159
        TRAP_END;
 
3160
        return ret;
 
3161
    }
 
3162
    op |= LOCK_NB;
 
3163
    while (flock(fd, op) < 0) {
 
3164
        switch (errno) {
 
3165
          case EAGAIN:
 
3166
          case EACCES:
 
3167
#if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN
 
3168
          case EWOULDBLOCK:
 
3169
#endif
 
3170
            rb_thread_polling();        /* busy wait */
 
3171
            rb_io_check_closed(fptr);
 
3172
            continue;
 
3173
          default:
 
3174
            return -1;
 
3175
        }
 
3176
    }
 
3177
    return 0;
 
3178
}
 
3179
#ifdef __CYGWIN__
 
3180
# undef flock
 
3181
#endif
 
3182
#define flock(fd, op) rb_thread_flock(fd, op, fptr)
 
3183
 
 
3184
/*
 
3185
 *  call-seq:
 
3186
 *     file.flock (locking_constant ) =>  0 or false
 
3187
 *  
 
3188
 *  Locks or unlocks a file according to <i>locking_constant</i> (a
 
3189
 *  logical <em>or</em> of the values in the table below).
 
3190
 *  Returns <code>false</code> if <code>File::LOCK_NB</code> is
 
3191
 *  specified and the operation would otherwise have blocked. Not
 
3192
 *  available on all platforms.
 
3193
 *     
 
3194
 *  Locking constants (in class File):
 
3195
 *
 
3196
 *     LOCK_EX   | Exclusive lock. Only one process may hold an
 
3197
 *               | exclusive lock for a given file at a time.
 
3198
 *     ----------+------------------------------------------------
 
3199
 *     LOCK_NB   | Don't block when locking. May be combined
 
3200
 *               | with other lock options using logical or.
 
3201
 *     ----------+------------------------------------------------
 
3202
 *     LOCK_SH   | Shared lock. Multiple processes may each hold a
 
3203
 *               | shared lock for a given file at the same time.
 
3204
 *     ----------+------------------------------------------------
 
3205
 *     LOCK_UN   | Unlock.
 
3206
 *
 
3207
 *  Example:
 
3208
 *
 
3209
 *     File.new("testfile").flock(File::LOCK_UN)   #=> 0
 
3210
 *     
 
3211
 */
 
3212
 
 
3213
static VALUE
 
3214
rb_file_flock(obj, operation)
 
3215
    VALUE obj;
 
3216
    VALUE operation;
 
3217
{
 
3218
#ifndef __CHECKER__
 
3219
    OpenFile *fptr;
 
3220
    int op;
 
3221
 
 
3222
    rb_secure(2);
 
3223
    op = NUM2INT(operation);
 
3224
    GetOpenFile(obj, fptr);
 
3225
 
 
3226
    if (fptr->mode & FMODE_WRITABLE) {
 
3227
        fflush(GetWriteFile(fptr));
 
3228
    }
 
3229
  retry:
 
3230
    if (flock(fileno(fptr->f), op) < 0) {
 
3231
        switch (errno) {
 
3232
          case EAGAIN:
 
3233
          case EACCES:
 
3234
#if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN
 
3235
          case EWOULDBLOCK:
 
3236
#endif
 
3237
              return Qfalse;
 
3238
          case EINTR:
 
3239
#if defined(ERESTART)
 
3240
          case ERESTART:
 
3241
#endif
 
3242
            goto retry;
 
3243
        }
 
3244
        rb_sys_fail(fptr->path);
 
3245
    }
 
3246
#endif
 
3247
    return INT2FIX(0);
 
3248
}
 
3249
#undef flock
 
3250
 
 
3251
static void
 
3252
test_check(n, argc, argv)
 
3253
    int n, argc;
 
3254
    VALUE *argv;
 
3255
{
 
3256
    int i;
 
3257
 
 
3258
    n+=1;
 
3259
    if (n != argc) rb_raise(rb_eArgError, "wrong number of arguments (%d for %d)", argc, n);
 
3260
    for (i=1; i<n; i++) {
 
3261
        switch (TYPE(argv[i])) {
 
3262
          case T_STRING:
 
3263
          default:
 
3264
            SafeStringValue(argv[i]);
 
3265
            break;
 
3266
          case T_FILE:
 
3267
            break;
 
3268
        }
 
3269
    }
 
3270
}
 
3271
 
 
3272
#define CHECK(n) test_check((n), argc, argv)
 
3273
 
 
3274
/*
 
3275
 *  call-seq:
 
3276
 *     test(int_cmd, file1 [, file2] ) => obj
 
3277
 *  
 
3278
 *  Uses the integer <i>aCmd</i> to perform various tests on
 
3279
 *  <i>file1</i> (first table below) or on <i>file1</i> and
 
3280
 *  <i>file2</i> (second table).
 
3281
 *     
 
3282
 *  File tests on a single file:
 
3283
 *
 
3284
 *    Test   Returns   Meaning
 
3285
 *     ?A  | Time    | Last access time for file1
 
3286
 *     ?b  | boolean | True if file1 is a block device
 
3287
 *     ?c  | boolean | True if file1 is a character device
 
3288
 *     ?C  | Time    | Last change time for file1
 
3289
 *     ?d  | boolean | True if file1 exists and is a directory
 
3290
 *     ?e  | boolean | True if file1 exists
 
3291
 *     ?f  | boolean | True if file1 exists and is a regular file
 
3292
 *     ?g  | boolean | True if file1 has the \CF{setgid} bit
 
3293
 *         |         | set (false under NT)
 
3294
 *     ?G  | boolean | True if file1 exists and has a group
 
3295
 *         |         | ownership equal to the caller's group
 
3296
 *     ?k  | boolean | True if file1 exists and has the sticky bit set
 
3297
 *     ?l  | boolean | True if file1 exists and is a symbolic link
 
3298
 *     ?M  | Time    | Last modification time for file1
 
3299
 *     ?o  | boolean | True if file1 exists and is owned by 
 
3300
 *         |         | the caller's effective uid
 
3301
 *     ?O  | boolean | True if file1 exists and is owned by
 
3302
 *         |         | the caller's real uid
 
3303
 *     ?p  | boolean | True if file1 exists and is a fifo
 
3304
 *     ?r  | boolean | True if file1 is readable by the effective
 
3305
 *         |         | uid/gid of the caller
 
3306
 *     ?R  | boolean | True if file is readable by the real
 
3307
 *         |         | uid/gid of the caller
 
3308
 *     ?s  | int/nil | If file1 has nonzero size, return the size,
 
3309
 *         |         | otherwise return nil
 
3310
 *     ?S  | boolean | True if file1 exists and is a socket
 
3311
 *     ?u  | boolean | True if file1 has the setuid bit set
 
3312
 *     ?w  | boolean | True if file1 exists and is writable by
 
3313
 *         |         | the effective uid/gid
 
3314
 *     ?W  | boolean | True if file1 exists and is writable by
 
3315
 *         |         | the real uid/gid
 
3316
 *     ?x  | boolean | True if file1 exists and is executable by
 
3317
 *         |         | the effective uid/gid
 
3318
 *     ?X  | boolean | True if file1 exists and is executable by
 
3319
 *         |         | the real uid/gid
 
3320
 *     ?z  | boolean | True if file1 exists and has a zero length
 
3321
 *
 
3322
 * Tests that take two files:
 
3323
 *
 
3324
 *     ?-  | boolean | True if file1 and file2 are identical
 
3325
 *     ?=  | boolean | True if the modification times of file1
 
3326
 *         |         | and file2 are equal
 
3327
 *     ?<  | boolean | True if the modification time of file1
 
3328
 *         |         | is prior to that of file2
 
3329
 *     ?>  | boolean | True if the modification time of file1
 
3330
 *         |         | is after that of file2
 
3331
 */
 
3332
 
 
3333
static VALUE
 
3334
rb_f_test(argc, argv)
 
3335
    int argc;
 
3336
    VALUE *argv;
 
3337
{
 
3338
    int cmd;
 
3339
 
 
3340
    if (argc == 0) rb_raise(rb_eArgError, "wrong number of arguments");
 
3341
#if 0 /* 1.7 behavior? */
 
3342
    if (argc == 1) {
 
3343
        return RTEST(argv[0]) ? Qtrue : Qfalse;
 
3344
    }
 
3345
#endif
 
3346
    cmd = NUM2CHR(argv[0]);
 
3347
    if (cmd == 0) return Qfalse;
 
3348
    if (strchr("bcdefgGkloOprRsSuwWxXz", cmd)) {
 
3349
        CHECK(1);
 
3350
        switch (cmd) {
 
3351
          case 'b':
 
3352
            return test_b(0, argv[1]);
 
3353
 
 
3354
          case 'c':
 
3355
            return test_c(0, argv[1]);
 
3356
 
 
3357
          case 'd':
 
3358
            return test_d(0, argv[1]);
 
3359
 
 
3360
          case 'a':
 
3361
          case 'e':
 
3362
            return test_e(0, argv[1]);
 
3363
 
 
3364
          case 'f':
 
3365
            return test_f(0, argv[1]);
 
3366
 
 
3367
          case 'g':
 
3368
            return test_sgid(0, argv[1]);
 
3369
 
 
3370
          case 'G':
 
3371
            return test_grpowned(0, argv[1]);
 
3372
 
 
3373
          case 'k':
 
3374
            return test_sticky(0, argv[1]);
 
3375
 
 
3376
          case 'l':
 
3377
            return test_l(0, argv[1]);
 
3378
 
 
3379
          case 'o':
 
3380
            return test_owned(0, argv[1]);
 
3381
 
 
3382
          case 'O':
 
3383
            return test_rowned(0, argv[1]);
 
3384
 
 
3385
          case 'p':
 
3386
            return test_p(0, argv[1]);
 
3387
 
 
3388
          case 'r':
 
3389
            return test_r(0, argv[1]);
 
3390
 
 
3391
          case 'R':
 
3392
            return test_R(0, argv[1]);
 
3393
 
 
3394
          case 's':
 
3395
            return test_s(0, argv[1]);
 
3396
 
 
3397
          case 'S':
 
3398
            return test_S(0, argv[1]);
 
3399
 
 
3400
          case 'u':
 
3401
            return test_suid(0, argv[1]);
 
3402
 
 
3403
          case 'w':
 
3404
            return test_w(0, argv[1]);
 
3405
 
 
3406
          case 'W':
 
3407
            return test_W(0, argv[1]);
 
3408
 
 
3409
          case 'x':
 
3410
            return test_x(0, argv[1]);
 
3411
 
 
3412
          case 'X':
 
3413
            return test_X(0, argv[1]);
 
3414
 
 
3415
          case 'z':
 
3416
            return test_z(0, argv[1]);
 
3417
        }
 
3418
    }
 
3419
 
 
3420
    if (strchr("MAC", cmd)) {
 
3421
        struct stat st;
 
3422
 
 
3423
        CHECK(1);
 
3424
        if (rb_stat(argv[1], &st) == -1) {
 
3425
            rb_sys_fail(RSTRING(argv[1])->ptr);
 
3426
        }
 
3427
 
 
3428
        switch (cmd) {
 
3429
          case 'A':
 
3430
            return rb_time_new(st.st_atime, 0);
 
3431
          case 'M':
 
3432
            return rb_time_new(st.st_mtime, 0);
 
3433
          case 'C':
 
3434
            return rb_time_new(st.st_ctime, 0);
 
3435
        }
 
3436
    }
 
3437
 
 
3438
    if (cmd == '-') {
 
3439
        CHECK(2);
 
3440
        return test_identical(0, argv[1], argv[2]);
 
3441
    }
 
3442
 
 
3443
    if (strchr("=<>", cmd)) {
 
3444
        struct stat st1, st2;
 
3445
 
 
3446
        CHECK(2);
 
3447
        if (rb_stat(argv[1], &st1) < 0) return Qfalse;
 
3448
        if (rb_stat(argv[2], &st2) < 0) return Qfalse;
 
3449
 
 
3450
        switch (cmd) {
 
3451
          case '=':
 
3452
            if (st1.st_mtime == st2.st_mtime) return Qtrue;
 
3453
            return Qfalse;
 
3454
 
 
3455
          case '>':
 
3456
            if (st1.st_mtime > st2.st_mtime) return Qtrue;
 
3457
            return Qfalse;
 
3458
 
 
3459
          case '<':
 
3460
            if (st1.st_mtime < st2.st_mtime) return Qtrue;
 
3461
            return Qfalse;
 
3462
        }
 
3463
    }
 
3464
    /* unknown command */
 
3465
    rb_raise(rb_eArgError, "unknown command ?%c", cmd);
 
3466
    return Qnil;                /* not reached */
 
3467
}
 
3468
 
 
3469
 
 
3470
 
 
3471
/*
 
3472
 *  Document-class: File::Stat
 
3473
 *
 
3474
 *  Objects of class <code>File::Stat</code> encapsulate common status
 
3475
 *  information for <code>File</code> objects. The information is
 
3476
 *  recorded at the moment the <code>File::Stat</code> object is
 
3477
 *  created; changes made to the file after that point will not be
 
3478
 *  reflected. <code>File::Stat</code> objects are returned by
 
3479
 *  <code>IO#stat</code>, <code>File::stat</code>,
 
3480
 *  <code>File#lstat</code>, and <code>File::lstat</code>. Many of these
 
3481
 *  methods return platform-specific values, and not all values are
 
3482
 *  meaningful on all systems. See also <code>Kernel#test</code>.
 
3483
 */
 
3484
 
 
3485
static VALUE rb_stat_s_alloc _((VALUE));
 
3486
static VALUE
 
3487
rb_stat_s_alloc(klass)
 
3488
    VALUE klass;
 
3489
{
 
3490
    return stat_new_0(klass, 0);
 
3491
}
 
3492
 
 
3493
/*
 
3494
 * call-seq:
 
3495
 *
 
3496
 *   File::Stat.new(file_name)  => stat
 
3497
 *
 
3498
 * Create a File::Stat object for the given file name (raising an
 
3499
 * exception if the file doesn't exist).
 
3500
 */
 
3501
 
 
3502
static VALUE
 
3503
rb_stat_init(obj, fname)
 
3504
    VALUE obj, fname;
 
3505
{
 
3506
    struct stat st, *nst;
 
3507
 
 
3508
    SafeStringValue(fname);
 
3509
 
 
3510
    if (stat(StringValueCStr(fname), &st) == -1) {
 
3511
        rb_sys_fail(RSTRING(fname)->ptr);
 
3512
    }
 
3513
    if (DATA_PTR(obj)) {
 
3514
        free(DATA_PTR(obj));
 
3515
        DATA_PTR(obj) = NULL;
 
3516
    }
 
3517
    nst = ALLOC(struct stat);
 
3518
    *nst = st;
 
3519
    DATA_PTR(obj) = nst;
 
3520
 
 
3521
    return Qnil;
 
3522
}
 
3523
 
 
3524
/* :nodoc: */
 
3525
static VALUE
 
3526
rb_stat_init_copy(copy, orig)
 
3527
    VALUE copy, orig;
 
3528
{
 
3529
    struct stat *nst;
 
3530
 
 
3531
    if (copy == orig) return orig;
 
3532
    rb_check_frozen(copy);
 
3533
    /* need better argument type check */
 
3534
    if (!rb_obj_is_instance_of(orig, rb_obj_class(copy))) {
 
3535
        rb_raise(rb_eTypeError, "wrong argument class");
 
3536
    }
 
3537
    if (DATA_PTR(copy)) {
 
3538
        free(DATA_PTR(copy));
 
3539
        DATA_PTR(copy) = 0;
 
3540
    }
 
3541
    if (DATA_PTR(orig)) {
 
3542
        nst = ALLOC(struct stat);
 
3543
        *nst = *(struct stat*)DATA_PTR(orig);
 
3544
        DATA_PTR(copy) = nst;
 
3545
    }
 
3546
 
 
3547
    return copy;
 
3548
}
 
3549
 
 
3550
/*
 
3551
 *  call-seq:
 
3552
 *     stat.ftype   => string
 
3553
 *  
 
3554
 *  Identifies the type of <i>stat</i>. The return string is one of:
 
3555
 *  ``<code>file</code>'', ``<code>directory</code>'',
 
3556
 *  ``<code>characterSpecial</code>'', ``<code>blockSpecial</code>'',
 
3557
 *  ``<code>fifo</code>'', ``<code>link</code>'',
 
3558
 *  ``<code>socket</code>'', or ``<code>unknown</code>''.
 
3559
 *     
 
3560
 *     File.stat("/dev/tty").ftype   #=> "characterSpecial"
 
3561
 *     
 
3562
 */
 
3563
 
 
3564
static VALUE
 
3565
rb_stat_ftype(obj)
 
3566
    VALUE obj;
 
3567
{
 
3568
    return rb_file_ftype(get_stat(obj));
 
3569
}
 
3570
 
 
3571
/*
 
3572
 *  call-seq:
 
3573
 *     stat.directory?   => true or false
 
3574
 *  
 
3575
 *  Returns <code>true</code> if <i>stat</i> is a directory,
 
3576
 *  <code>false</code> otherwise.
 
3577
 *     
 
3578
 *     File.stat("testfile").directory?   #=> false
 
3579
 *     File.stat(".").directory?          #=> true
 
3580
 */
 
3581
 
 
3582
static VALUE
 
3583
rb_stat_d(obj)
 
3584
    VALUE obj;
 
3585
{
 
3586
    if (S_ISDIR(get_stat(obj)->st_mode)) return Qtrue;
 
3587
    return Qfalse;
 
3588
}
 
3589
 
 
3590
/*
 
3591
 *  call-seq:
 
3592
 *     stat.pipe?    => true or false
 
3593
 *  
 
3594
 *  Returns <code>true</code> if the operating system supports pipes and
 
3595
 *  <i>stat</i> is a pipe; <code>false</code> otherwise.
 
3596
 */
 
3597
 
 
3598
static VALUE
 
3599
rb_stat_p(obj)
 
3600
    VALUE obj;
 
3601
{
 
3602
#ifdef S_IFIFO
 
3603
    if (S_ISFIFO(get_stat(obj)->st_mode)) return Qtrue;
 
3604
 
 
3605
#endif
 
3606
    return Qfalse;
 
3607
}
 
3608
 
 
3609
/*
 
3610
 *  call-seq:
 
3611
 *     stat.symlink?    => true or false
 
3612
 *  
 
3613
 *  Returns <code>true</code> if <i>stat</i> is a symbolic link,
 
3614
 *  <code>false</code> if it isn't or if the operating system doesn't
 
3615
 *  support this feature. As <code>File::stat</code> automatically
 
3616
 *  follows symbolic links, <code>symlink?</code> will always be
 
3617
 *  <code>false</code> for an object returned by
 
3618
 *  <code>File::stat</code>.
 
3619
 *     
 
3620
 *     File.symlink("testfile", "alink")   #=> 0
 
3621
 *     File.stat("alink").symlink?         #=> false
 
3622
 *     File.lstat("alink").symlink?        #=> true
 
3623
 *     
 
3624
 */
 
3625
 
 
3626
static VALUE
 
3627
rb_stat_l(obj)
 
3628
    VALUE obj;
 
3629
{
 
3630
#ifdef S_ISLNK
 
3631
    if (S_ISLNK(get_stat(obj)->st_mode)) return Qtrue;
 
3632
#endif
 
3633
    return Qfalse;
 
3634
}
 
3635
 
 
3636
/*
 
3637
 *  call-seq:
 
3638
 *     stat.socket?    => true or false
 
3639
 *  
 
3640
 *  Returns <code>true</code> if <i>stat</i> is a socket,
 
3641
 *  <code>false</code> if it isn't or if the operating system doesn't
 
3642
 *  support this feature.
 
3643
 *     
 
3644
 *     File.stat("testfile").socket?   #=> false
 
3645
 *     
 
3646
 */
 
3647
 
 
3648
static VALUE
 
3649
rb_stat_S(obj)
 
3650
    VALUE obj;
 
3651
{
 
3652
#ifdef S_ISSOCK
 
3653
    if (S_ISSOCK(get_stat(obj)->st_mode)) return Qtrue;
 
3654
 
 
3655
#endif
 
3656
    return Qfalse;
 
3657
}
 
3658
 
 
3659
/*
 
3660
 *  call-seq:
 
3661
 *     stat.blockdev?   => true or false
 
3662
 *  
 
3663
 *  Returns <code>true</code> if the file is a block device,
 
3664
 *  <code>false</code> if it isn't or if the operating system doesn't
 
3665
 *  support this feature.
 
3666
 *     
 
3667
 *     File.stat("testfile").blockdev?    #=> false
 
3668
 *     File.stat("/dev/hda1").blockdev?   #=> true
 
3669
 *     
 
3670
 */
 
3671
 
 
3672
static VALUE
 
3673
rb_stat_b(obj)
 
3674
    VALUE obj;
 
3675
{
 
3676
#ifdef S_ISBLK
 
3677
    if (S_ISBLK(get_stat(obj)->st_mode)) return Qtrue;
 
3678
 
 
3679
#endif
 
3680
    return Qfalse;
 
3681
}
 
3682
 
 
3683
/*
 
3684
 *  call-seq:
 
3685
 *     stat.chardev?    => true or false
 
3686
 *  
 
3687
 *  Returns <code>true</code> if the file is a character device,
 
3688
 *  <code>false</code> if it isn't or if the operating system doesn't
 
3689
 *  support this feature.
 
3690
 *     
 
3691
 *     File.stat("/dev/tty").chardev?   #=> true
 
3692
 *     
 
3693
 */
 
3694
 
 
3695
static VALUE
 
3696
rb_stat_c(obj)
 
3697
    VALUE obj;
 
3698
{
 
3699
    if (S_ISCHR(get_stat(obj)->st_mode)) return Qtrue;
 
3700
 
 
3701
    return Qfalse;
 
3702
}
 
3703
 
 
3704
/*
 
3705
 *  call-seq:
 
3706
 *     stat.owned?    => true or false
 
3707
 *  
 
3708
 *  Returns <code>true</code> if the effective user id of the process is
 
3709
 *  the same as the owner of <i>stat</i>.
 
3710
 *     
 
3711
 *     File.stat("testfile").owned?      #=> true
 
3712
 *     File.stat("/etc/passwd").owned?   #=> false
 
3713
 *     
 
3714
 */
 
3715
 
 
3716
static VALUE
 
3717
rb_stat_owned(obj)
 
3718
    VALUE obj;
 
3719
{
 
3720
    if (get_stat(obj)->st_uid == geteuid()) return Qtrue;
 
3721
    return Qfalse;
 
3722
}
 
3723
 
 
3724
static VALUE
 
3725
rb_stat_rowned(obj)
 
3726
    VALUE obj;
 
3727
{
 
3728
    if (get_stat(obj)->st_uid == getuid()) return Qtrue;
 
3729
    return Qfalse;
 
3730
}
 
3731
 
 
3732
/*
 
3733
 *  call-seq:
 
3734
 *     stat.grpowned?   => true or false
 
3735
 *  
 
3736
 *  Returns true if the effective group id of the process is the same as
 
3737
 *  the group id of <i>stat</i>. On Windows NT, returns <code>false</code>.
 
3738
 *     
 
3739
 *     File.stat("testfile").grpowned?      #=> true
 
3740
 *     File.stat("/etc/passwd").grpowned?   #=> false
 
3741
 *     
 
3742
 */
 
3743
 
 
3744
static VALUE
 
3745
rb_stat_grpowned(obj)
 
3746
    VALUE obj;
 
3747
{
 
3748
#ifndef _WIN32
 
3749
    if (group_member(get_stat(obj)->st_gid)) return Qtrue;
 
3750
#endif
 
3751
    return Qfalse;
 
3752
}
 
3753
 
 
3754
/*
 
3755
 *  call-seq:
 
3756
 *     stat.readable?    => true or false
 
3757
 *  
 
3758
 *  Returns <code>true</code> if <i>stat</i> is readable by the
 
3759
 *  effective user id of this process.
 
3760
 *     
 
3761
 *     File.stat("testfile").readable?   #=> true
 
3762
 *     
 
3763
 */
 
3764
 
 
3765
static VALUE
 
3766
rb_stat_r(obj)
 
3767
    VALUE obj;
 
3768
{
 
3769
    struct stat *st = get_stat(obj);
 
3770
 
 
3771
#ifdef USE_GETEUID
 
3772
    if (geteuid() == 0) return Qtrue;
 
3773
#endif
 
3774
#ifdef S_IRUSR
 
3775
    if (rb_stat_owned(obj))
 
3776
        return st->st_mode & S_IRUSR ? Qtrue : Qfalse;
 
3777
#endif
 
3778
#ifdef S_IRGRP
 
3779
    if (rb_stat_grpowned(obj))
 
3780
        return st->st_mode & S_IRGRP ? Qtrue : Qfalse;
 
3781
#endif
 
3782
#ifdef S_IROTH
 
3783
    if (!(st->st_mode & S_IROTH)) return Qfalse;
 
3784
#endif
 
3785
    return Qtrue;
 
3786
}
 
3787
 
 
3788
 
 
3789
 
 
3790
/*
 
3791
 *  call-seq:
 
3792
 *     stat.readable_real? -> true or false
 
3793
 *  
 
3794
 *  Returns <code>true</code> if <i>stat</i> is readable by the real
 
3795
 *  user id of this process.
 
3796
 *     
 
3797
 *     File.stat("testfile").readable_real?   #=> true
 
3798
 *     
 
3799
 */
 
3800
 
 
3801
static VALUE
 
3802
rb_stat_R(obj)
 
3803
    VALUE obj;
 
3804
{
 
3805
    struct stat *st = get_stat(obj);
 
3806
 
 
3807
#ifdef USE_GETEUID
 
3808
    if (getuid() == 0) return Qtrue;
 
3809
#endif
 
3810
#ifdef S_IRUSR
 
3811
    if (rb_stat_rowned(obj))
 
3812
        return st->st_mode & S_IRUSR ? Qtrue : Qfalse;
 
3813
#endif
 
3814
#ifdef S_IRGRP
 
3815
    if (group_member(get_stat(obj)->st_gid))
 
3816
        return st->st_mode & S_IRGRP ? Qtrue : Qfalse;
 
3817
#endif
 
3818
#ifdef S_IROTH
 
3819
    if (!(st->st_mode & S_IROTH)) return Qfalse;
 
3820
#endif
 
3821
    return Qtrue;
 
3822
}
 
3823
 
 
3824
/*
 
3825
 *  call-seq:
 
3826
 *     stat.writable? -> true or false
 
3827
 *  
 
3828
 *  Returns <code>true</code> if <i>stat</i> is writable by the
 
3829
 *  effective user id of this process.
 
3830
 *     
 
3831
 *     File.stat("testfile").writable?   #=> true
 
3832
 *     
 
3833
 */
 
3834
 
 
3835
static VALUE
 
3836
rb_stat_w(obj)
 
3837
    VALUE obj;
 
3838
{
 
3839
    struct stat *st = get_stat(obj);
 
3840
 
 
3841
#ifdef USE_GETEUID
 
3842
    if (geteuid() == 0) return Qtrue;
 
3843
#endif
 
3844
#ifdef S_IWUSR
 
3845
    if (rb_stat_owned(obj))
 
3846
        return st->st_mode & S_IWUSR ? Qtrue : Qfalse;
 
3847
#endif
 
3848
#ifdef S_IWGRP
 
3849
    if (rb_stat_grpowned(obj))
 
3850
        return st->st_mode & S_IWGRP ? Qtrue : Qfalse;
 
3851
#endif
 
3852
#ifdef S_IWOTH
 
3853
    if (!(st->st_mode & S_IWOTH)) return Qfalse;
 
3854
#endif
 
3855
    return Qtrue;
 
3856
}
 
3857
 
 
3858
/*
 
3859
 *  call-seq:
 
3860
 *     stat.writable_real? -> true or false
 
3861
 *  
 
3862
 *  Returns <code>true</code> if <i>stat</i> is writable by the real
 
3863
 *  user id of this process.
 
3864
 *     
 
3865
 *     File.stat("testfile").writable_real?   #=> true
 
3866
 *     
 
3867
 */
 
3868
 
 
3869
static VALUE
 
3870
rb_stat_W(obj)
 
3871
    VALUE obj;
 
3872
{
 
3873
    struct stat *st = get_stat(obj);
 
3874
 
 
3875
#ifdef USE_GETEUID
 
3876
    if (getuid() == 0) return Qtrue;
 
3877
#endif
 
3878
#ifdef S_IWUSR
 
3879
    if (rb_stat_rowned(obj))
 
3880
        return st->st_mode & S_IWUSR ? Qtrue : Qfalse;
 
3881
#endif
 
3882
#ifdef S_IWGRP
 
3883
    if (group_member(get_stat(obj)->st_gid))
 
3884
        return st->st_mode & S_IWGRP ? Qtrue : Qfalse;
 
3885
#endif
 
3886
#ifdef S_IWOTH
 
3887
    if (!(st->st_mode & S_IWOTH)) return Qfalse;
 
3888
#endif
 
3889
    return Qtrue;
 
3890
}
 
3891
 
 
3892
/*
 
3893
 *  call-seq:
 
3894
 *     stat.executable?    => true or false
 
3895
 *  
 
3896
 *  Returns <code>true</code> if <i>stat</i> is executable or if the
 
3897
 *  operating system doesn't distinguish executable files from
 
3898
 *  nonexecutable files. The tests are made using the effective owner of
 
3899
 *  the process.
 
3900
 *     
 
3901
 *     File.stat("testfile").executable?   #=> false
 
3902
 *     
 
3903
 */
 
3904
 
 
3905
static VALUE
 
3906
rb_stat_x(obj)
 
3907
    VALUE obj;
 
3908
{
 
3909
    struct stat *st = get_stat(obj);
 
3910
 
 
3911
#ifdef USE_GETEUID
 
3912
    if (geteuid() == 0) {
 
3913
        return st->st_mode & S_IXUGO ? Qtrue : Qfalse;
 
3914
    }
 
3915
#endif
 
3916
#ifdef S_IXUSR
 
3917
    if (rb_stat_owned(obj))
 
3918
        return st->st_mode & S_IXUSR ? Qtrue : Qfalse;
 
3919
#endif
 
3920
#ifdef S_IXGRP
 
3921
    if (rb_stat_grpowned(obj))
 
3922
        return st->st_mode & S_IXGRP ? Qtrue : Qfalse;
 
3923
#endif
 
3924
#ifdef S_IXOTH
 
3925
    if (!(st->st_mode & S_IXOTH)) return Qfalse;
 
3926
#endif
 
3927
    return Qtrue;
 
3928
}
 
3929
 
 
3930
/*
 
3931
 *  call-seq:
 
3932
 *     stat.executable_real?    => true or false
 
3933
 *  
 
3934
 *  Same as <code>executable?</code>, but tests using the real owner of
 
3935
 *  the process.
 
3936
 */
 
3937
 
 
3938
 
 
3939
static VALUE
 
3940
rb_stat_X(obj)
 
3941
    VALUE obj;
 
3942
{
 
3943
    struct stat *st = get_stat(obj);
 
3944
 
 
3945
#ifdef USE_GETEUID
 
3946
    if (getuid() == 0) {
 
3947
        return st->st_mode & S_IXUGO ? Qtrue : Qfalse;
 
3948
    }
 
3949
#endif
 
3950
#ifdef S_IXUSR
 
3951
    if (rb_stat_rowned(obj))
 
3952
        return st->st_mode & S_IXUSR ? Qtrue : Qfalse;
 
3953
#endif
 
3954
#ifdef S_IXGRP
 
3955
    if (group_member(get_stat(obj)->st_gid))
 
3956
        return st->st_mode & S_IXGRP ? Qtrue : Qfalse;
 
3957
#endif
 
3958
#ifdef S_IXOTH
 
3959
    if (!(st->st_mode & S_IXOTH)) return Qfalse;
 
3960
#endif
 
3961
    return Qtrue;
 
3962
}
 
3963
 
 
3964
/*
 
3965
 *  call-seq:
 
3966
 *     stat.file?    => true or false
 
3967
 *  
 
3968
 *  Returns <code>true</code> if <i>stat</i> is a regular file (not
 
3969
 *  a device file, pipe, socket, etc.).
 
3970
 *     
 
3971
 *     File.stat("testfile").file?   #=> true
 
3972
 *     
 
3973
 */
 
3974
 
 
3975
static VALUE
 
3976
rb_stat_f(obj)
 
3977
    VALUE obj;
 
3978
{
 
3979
    if (S_ISREG(get_stat(obj)->st_mode)) return Qtrue;
 
3980
    return Qfalse;
 
3981
}
 
3982
 
 
3983
/*
 
3984
 *  call-seq:
 
3985
 *     stat.zero?    => true or false
 
3986
 *  
 
3987
 *  Returns <code>true</code> if <i>stat</i> is a zero-length file;
 
3988
 *  <code>false</code> otherwise.
 
3989
 *     
 
3990
 *     File.stat("testfile").zero?   #=> false
 
3991
 *     
 
3992
 */
 
3993
 
 
3994
static VALUE
 
3995
rb_stat_z(obj)
 
3996
    VALUE obj;
 
3997
{
 
3998
    if (get_stat(obj)->st_size == 0) return Qtrue;
 
3999
    return Qfalse;
 
4000
}
 
4001
 
 
4002
 
 
4003
/*
 
4004
 *  call-seq:
 
4005
 *     state.size    => integer
 
4006
 *  
 
4007
 *  Returns the size of <i>stat</i> in bytes.
 
4008
 *     
 
4009
 *     File.stat("testfile").size   #=> 66
 
4010
 *     
 
4011
 */
 
4012
 
 
4013
static VALUE
 
4014
rb_stat_s(obj)
 
4015
    VALUE obj;
 
4016
{
 
4017
    off_t size = get_stat(obj)->st_size;
 
4018
 
 
4019
    if (size == 0) return Qnil;
 
4020
    return OFFT2NUM(size);
 
4021
}
 
4022
 
 
4023
/*
 
4024
 *  call-seq:
 
4025
 *     stat.setuid?    => true or false
 
4026
 *  
 
4027
 *  Returns <code>true</code> if <i>stat</i> has the set-user-id
 
4028
 *  permission bit set, <code>false</code> if it doesn't or if the
 
4029
 *  operating system doesn't support this feature.
 
4030
 *     
 
4031
 *     File.stat("/bin/su").setuid?   #=> true
 
4032
 */
 
4033
 
 
4034
static VALUE
 
4035
rb_stat_suid(obj)
 
4036
    VALUE obj;
 
4037
{
 
4038
#ifdef S_ISUID
 
4039
    if (get_stat(obj)->st_mode & S_ISUID) return Qtrue;
 
4040
#endif
 
4041
    return Qfalse;
 
4042
}
 
4043
 
 
4044
/*
 
4045
 *  call-seq:
 
4046
 *     stat.setgid?   => true or false
 
4047
 *  
 
4048
 *  Returns <code>true</code> if <i>stat</i> has the set-group-id
 
4049
 *  permission bit set, <code>false</code> if it doesn't or if the
 
4050
 *  operating system doesn't support this feature.
 
4051
 *     
 
4052
 *     File.stat("/usr/sbin/lpc").setgid?   #=> true
 
4053
 *     
 
4054
 */
 
4055
 
 
4056
static VALUE
 
4057
rb_stat_sgid(obj)
 
4058
    VALUE obj;
 
4059
{
 
4060
#ifdef S_ISGID
 
4061
    if (get_stat(obj)->st_mode & S_ISGID) return Qtrue;
 
4062
#endif
 
4063
    return Qfalse;
 
4064
}
 
4065
 
 
4066
/*
 
4067
 *  call-seq:
 
4068
 *     stat.sticky?    => true or false
 
4069
 *  
 
4070
 *  Returns <code>true</code> if <i>stat</i> has its sticky bit set,
 
4071
 *  <code>false</code> if it doesn't or if the operating system doesn't
 
4072
 *  support this feature.
 
4073
 *     
 
4074
 *     File.stat("testfile").sticky?   #=> false
 
4075
 *     
 
4076
 */
 
4077
 
 
4078
static VALUE
 
4079
rb_stat_sticky(obj)
 
4080
    VALUE obj;
 
4081
{
 
4082
#ifdef S_ISVTX
 
4083
    if (get_stat(obj)->st_mode & S_ISVTX) return Qtrue;
 
4084
#endif
 
4085
    return Qfalse;
 
4086
}
 
4087
 
 
4088
VALUE rb_mFConst;
 
4089
 
 
4090
void
 
4091
rb_file_const(name, value)
 
4092
    const char *name;
 
4093
    VALUE value;
 
4094
{
 
4095
    rb_define_const(rb_mFConst, name, value);
 
4096
}
 
4097
 
 
4098
static int
 
4099
is_absolute_path(path)
 
4100
    const char *path;
 
4101
{
 
4102
#ifdef DOSISH_DRIVE_LETTER
 
4103
    if (has_drive_letter(path) && isdirsep(path[2])) return 1;
 
4104
#endif
 
4105
#ifdef DOSISH_UNC
 
4106
    if (isdirsep(path[0]) && isdirsep(path[1])) return 1;
 
4107
#endif
 
4108
#ifndef DOSISH
 
4109
    if (path[0] == '/') return 1;
 
4110
#endif
 
4111
    return 0;
 
4112
}
 
4113
 
 
4114
#ifndef ENABLE_PATH_CHECK
 
4115
# if defined DOSISH || defined __CYGWIN__
 
4116
#   define ENABLE_PATH_CHECK 0
 
4117
# else
 
4118
#   define ENABLE_PATH_CHECK 1
 
4119
# endif
 
4120
#endif
 
4121
 
 
4122
#if ENABLE_PATH_CHECK
 
4123
static int
 
4124
path_check_0(fpath, execpath)
 
4125
     VALUE fpath;
 
4126
     int execpath;
 
4127
{
 
4128
    struct stat st;
 
4129
    char *p0 = StringValueCStr(fpath);
 
4130
    char *p = 0, *s;
 
4131
 
 
4132
    if (!is_absolute_path(p0)) {
 
4133
        char *buf = my_getcwd();
 
4134
        VALUE newpath;
 
4135
 
 
4136
        newpath = rb_str_new2(buf);
 
4137
        free(buf);
 
4138
 
 
4139
        rb_str_cat2(newpath, "/");
 
4140
        rb_str_cat2(newpath, p0);
 
4141
        p0 = RSTRING(fpath = newpath)->ptr;
 
4142
    }
 
4143
    for (;;) {
 
4144
#ifndef S_IWOTH
 
4145
# define S_IWOTH 002
 
4146
#endif
 
4147
        if (stat(p0, &st) == 0 && S_ISDIR(st.st_mode) && (st.st_mode & S_IWOTH)
 
4148
#ifdef S_ISVTX
 
4149
            && !(p && execpath && (st.st_mode & S_ISVTX))
 
4150
#endif
 
4151
            ) {
 
4152
            rb_warn("Insecure world writable dir %s in %sPATH, mode 0%o",
 
4153
                    p0, (execpath ? "" : "LOAD_"), st.st_mode);
 
4154
            if (p) *p = '/';
 
4155
            return 0;
 
4156
        }
 
4157
        s = strrdirsep(p0);
 
4158
        if (p) *p = '/';
 
4159
        if (!s || s == p0) return 1;
 
4160
        p = s;
 
4161
        *p = '\0';
 
4162
    }
 
4163
}
 
4164
#endif
 
4165
 
 
4166
static int
 
4167
fpath_check(path)
 
4168
    char *path;
 
4169
{
 
4170
#if ENABLE_PATH_CHECK
 
4171
    return path_check_0(rb_str_new2(path), Qfalse);
 
4172
#else
 
4173
    return 1;
 
4174
#endif
 
4175
}
 
4176
 
 
4177
int
 
4178
rb_path_check(path)
 
4179
    char *path;
 
4180
{
 
4181
#if ENABLE_PATH_CHECK
 
4182
    char *p0, *p, *pend;
 
4183
    const char sep = PATH_SEP_CHAR;
 
4184
 
 
4185
    if (!path) return 1;
 
4186
 
 
4187
    pend = path + strlen(path);
 
4188
    p0 = path;
 
4189
    p = strchr(path, sep);
 
4190
    if (!p) p = pend;
 
4191
 
 
4192
    for (;;) {
 
4193
        if (!path_check_0(rb_str_new(p0, p - p0), Qtrue)) {
 
4194
            return 0;           /* not safe */
 
4195
        }
 
4196
        p0 = p + 1;
 
4197
        if (p0 > pend) break;
 
4198
        p = strchr(p0, sep);
 
4199
        if (!p) p = pend;
 
4200
    }
 
4201
#endif
 
4202
    return 1;
 
4203
}
 
4204
 
 
4205
#if defined(__MACOS__) || defined(riscos)
 
4206
static int
 
4207
is_macos_native_path(path)
 
4208
    const char *path;
 
4209
{
 
4210
    if (strchr(path, ':')) return 1;
 
4211
    return 0;
 
4212
}
 
4213
#endif
 
4214
 
 
4215
static int
 
4216
file_load_ok(file)
 
4217
    char *file;
 
4218
{
 
4219
    FILE *f;
 
4220
 
 
4221
    if (!file) return 0;
 
4222
    f = fopen(file, "r");
 
4223
    if (f == NULL) return 0;
 
4224
    fclose(f);
 
4225
    return 1;
 
4226
}
 
4227
 
 
4228
extern VALUE rb_load_path;
 
4229
 
 
4230
int
 
4231
rb_find_file_ext(filep, ext)
 
4232
    VALUE *filep;
 
4233
    const char * const *ext;
 
4234
{
 
4235
    char *path, *found;
 
4236
    char *f = RSTRING(*filep)->ptr;
 
4237
    VALUE fname;
 
4238
    long i, j;
 
4239
 
 
4240
    if (f[0] == '~') {
 
4241
        fname = rb_file_expand_path(*filep, Qnil);
 
4242
        if (rb_safe_level() >= 2 && OBJ_TAINTED(fname)) {
 
4243
            rb_raise(rb_eSecurityError, "loading from unsafe file %s", f);
 
4244
        }
 
4245
        OBJ_FREEZE(fname);
 
4246
        f = StringValueCStr(fname);
 
4247
        *filep = fname;
 
4248
    }
 
4249
 
 
4250
    if (is_absolute_path(f)) {
 
4251
        for (i=0; ext[i]; i++) {
 
4252
            fname = rb_str_dup(*filep);
 
4253
            rb_str_cat2(fname, ext[i]);
 
4254
            OBJ_FREEZE(fname);
 
4255
            if (file_load_ok(StringValueCStr(fname))) {
 
4256
                *filep = fname;
 
4257
                return i+1;
 
4258
            }
 
4259
        }
 
4260
        return 0;
 
4261
    }
 
4262
 
 
4263
    if (!rb_load_path) return 0;
 
4264
 
 
4265
    Check_Type(rb_load_path, T_ARRAY);
 
4266
    for (i=0;i<RARRAY(rb_load_path)->len;i++) {
 
4267
        VALUE str = RARRAY(rb_load_path)->ptr[i];
 
4268
 
 
4269
        SafeStringValue(str);
 
4270
        if (RSTRING(str)->len == 0) continue;
 
4271
        path = RSTRING(str)->ptr;
 
4272
        for (j=0; ext[j]; j++) {
 
4273
            fname = rb_str_dup(*filep);
 
4274
            rb_str_cat2(fname, ext[j]);
 
4275
            OBJ_FREEZE(fname);
 
4276
            found = dln_find_file(StringValueCStr(fname), path);
 
4277
            if (found && file_load_ok(found)) {
 
4278
                *filep = fname;
 
4279
                return j+1;
 
4280
            }
 
4281
        }
 
4282
    }
 
4283
    return 0;
 
4284
}
 
4285
 
 
4286
VALUE
 
4287
rb_find_file(path)
 
4288
    VALUE path;
 
4289
{
 
4290
    VALUE tmp;
 
4291
    char *f = StringValueCStr(path);
 
4292
    char *lpath;
 
4293
 
 
4294
    if (f[0] == '~') {
 
4295
        path = rb_file_expand_path(path, Qnil);
 
4296
        if (rb_safe_level() >= 1 && OBJ_TAINTED(path)) {
 
4297
            rb_raise(rb_eSecurityError, "loading from unsafe path %s", f);
 
4298
        }
 
4299
        OBJ_FREEZE(path);
 
4300
        f = StringValueCStr(path);
 
4301
    }
 
4302
 
 
4303
#if defined(__MACOS__) || defined(riscos)
 
4304
    if (is_macos_native_path(f)) {
 
4305
        if (rb_safe_level() >= 1 && !fpath_check(f)) {
 
4306
            rb_raise(rb_eSecurityError, "loading from unsafe file %s", f);
 
4307
        }
 
4308
        if (file_load_ok(f)) return path;
 
4309
    }
 
4310
#endif
 
4311
 
 
4312
    if (is_absolute_path(f)) {
 
4313
        if (rb_safe_level() >= 1 && !fpath_check(f)) {
 
4314
            rb_raise(rb_eSecurityError, "loading from unsafe file %s", f);
 
4315
        }
 
4316
        if (file_load_ok(f)) return path;
 
4317
    }
 
4318
 
 
4319
    if (rb_safe_level() >= 4) {
 
4320
        rb_raise(rb_eSecurityError, "loading from non-absolute path %s", f);
 
4321
    }
 
4322
 
 
4323
    if (rb_load_path) {
 
4324
        long i;
 
4325
 
 
4326
        Check_Type(rb_load_path, T_ARRAY);
 
4327
        tmp = rb_ary_new();
 
4328
        for (i=0;i<RARRAY(rb_load_path)->len;i++) {
 
4329
            VALUE str = RARRAY(rb_load_path)->ptr[i];
 
4330
            SafeStringValue(str);
 
4331
            if (RSTRING(str)->len > 0) {
 
4332
                rb_ary_push(tmp, str);
 
4333
            }
 
4334
        }
 
4335
        tmp = rb_ary_join(tmp, rb_str_new2(PATH_SEP));
 
4336
        if (RSTRING(tmp)->len == 0) {
 
4337
            lpath = 0;
 
4338
        }
 
4339
        else {
 
4340
            lpath = RSTRING(tmp)->ptr;
 
4341
        }
 
4342
    }
 
4343
    else {
 
4344
        lpath = 0;
 
4345
    }
 
4346
 
 
4347
    if (!lpath) {
 
4348
        return 0;               /* no path, no load */
 
4349
    }
 
4350
    if (!(f = dln_find_file(f, lpath))) {
 
4351
        return 0;
 
4352
    }
 
4353
    if (rb_safe_level() >= 1 && !fpath_check(f)) {
 
4354
        rb_raise(rb_eSecurityError, "loading from unsafe file %s", f);
 
4355
    }
 
4356
    if (file_load_ok(f)) {
 
4357
        tmp = rb_str_new2(f);
 
4358
        OBJ_FREEZE(tmp);
 
4359
        return tmp;
 
4360
    }
 
4361
    return 0;
 
4362
}
 
4363
 
 
4364
static void
 
4365
define_filetest_function(name, func, argc)
 
4366
    const char *name;
 
4367
    VALUE (*func)();
 
4368
    int argc;
 
4369
{
 
4370
    rb_define_module_function(rb_mFileTest, name, func, argc);
 
4371
    rb_define_singleton_method(rb_cFile, name, func, argc);
 
4372
}
 
4373
 
 
4374
 
 
4375
/*
 
4376
 *  A <code>File</code> is an abstraction of any file object accessible
 
4377
 *  by the program and is closely associated with class <code>IO</code>
 
4378
 *  <code>File</code> includes the methods of module
 
4379
 *  <code>FileTest</code> as class methods, allowing you to write (for
 
4380
 *  example) <code>File.exist?("foo")</code>.
 
4381
 *     
 
4382
 *  In the description of File methods,
 
4383
 *  <em>permission bits</em> are a platform-specific
 
4384
 *  set of bits that indicate permissions of a file. On Unix-based
 
4385
 *  systems, permissions are viewed as a set of three octets, for the
 
4386
 *  owner, the group, and the rest of the world. For each of these
 
4387
 *  entities, permissions may be set to read, write, or execute the
 
4388
 *  file:
 
4389
 *     
 
4390
 *  The permission bits <code>0644</code> (in octal) would thus be
 
4391
 *  interpreted as read/write for owner, and read-only for group and
 
4392
 *  other. Higher-order bits may also be used to indicate the type of
 
4393
 *  file (plain, directory, pipe, socket, and so on) and various other
 
4394
 *  special features. If the permissions are for a directory, the
 
4395
 *  meaning of the execute bit changes; when set the directory can be
 
4396
 *  searched.
 
4397
 *     
 
4398
 *  On non-Posix operating systems, there may be only the ability to
 
4399
 *  make a file read-only or read-write. In this case, the remaining
 
4400
 *  permission bits will be synthesized to resemble typical values. For
 
4401
 *  instance, on Windows NT the default permission bits are
 
4402
 *  <code>0644</code>, which means read/write for owner, read-only for
 
4403
 *  all others. The only change that can be made is to make the file
 
4404
 *  read-only, which is reported as <code>0444</code>.
 
4405
 */
 
4406
 
 
4407
void
 
4408
Init_File()
 
4409
{
 
4410
    rb_mFileTest = rb_define_module("FileTest");
 
4411
    rb_cFile = rb_define_class("File", rb_cIO);
 
4412
 
 
4413
    define_filetest_function("directory?", test_d, 1);
 
4414
    define_filetest_function("exist?", test_e, 1);
 
4415
    define_filetest_function("exists?", test_e, 1); /* temporary */
 
4416
    define_filetest_function("readable?", test_r, 1);
 
4417
    define_filetest_function("readable_real?", test_R, 1);
 
4418
    define_filetest_function("writable?", test_w, 1);
 
4419
    define_filetest_function("writable_real?", test_W, 1);
 
4420
    define_filetest_function("executable?", test_x, 1);
 
4421
    define_filetest_function("executable_real?", test_X, 1);
 
4422
    define_filetest_function("file?", test_f, 1);
 
4423
    define_filetest_function("zero?", test_z, 1);
 
4424
    define_filetest_function("size?", test_s, 1);
 
4425
    define_filetest_function("size", rb_file_s_size, 1);
 
4426
    define_filetest_function("owned?", test_owned, 1);
 
4427
    define_filetest_function("grpowned?", test_grpowned, 1);
 
4428
 
 
4429
    define_filetest_function("pipe?", test_p, 1);
 
4430
    define_filetest_function("symlink?", test_l, 1);
 
4431
    define_filetest_function("socket?", test_S, 1);
 
4432
 
 
4433
    define_filetest_function("blockdev?", test_b, 1);
 
4434
    define_filetest_function("chardev?", test_c, 1);
 
4435
 
 
4436
    define_filetest_function("setuid?", test_suid, 1);
 
4437
    define_filetest_function("setgid?", test_sgid, 1);
 
4438
    define_filetest_function("sticky?", test_sticky, 1);
 
4439
 
 
4440
    define_filetest_function("identical?", test_identical, 2);
 
4441
 
 
4442
    rb_define_singleton_method(rb_cFile, "stat",  rb_file_s_stat, 1);
 
4443
    rb_define_singleton_method(rb_cFile, "lstat", rb_file_s_lstat, 1);
 
4444
    rb_define_singleton_method(rb_cFile, "ftype", rb_file_s_ftype, 1);
 
4445
 
 
4446
    rb_define_singleton_method(rb_cFile, "atime", rb_file_s_atime, 1);
 
4447
    rb_define_singleton_method(rb_cFile, "mtime", rb_file_s_mtime, 1);
 
4448
    rb_define_singleton_method(rb_cFile, "ctime", rb_file_s_ctime, 1);
 
4449
 
 
4450
    rb_define_singleton_method(rb_cFile, "utime", rb_file_s_utime, -1);
 
4451
    rb_define_singleton_method(rb_cFile, "chmod", rb_file_s_chmod, -1);
 
4452
    rb_define_singleton_method(rb_cFile, "chown", rb_file_s_chown, -1);
 
4453
    rb_define_singleton_method(rb_cFile, "lchmod", rb_file_s_lchmod, -1);
 
4454
    rb_define_singleton_method(rb_cFile, "lchown", rb_file_s_lchown, -1);
 
4455
 
 
4456
    rb_define_singleton_method(rb_cFile, "link", rb_file_s_link, 2);
 
4457
    rb_define_singleton_method(rb_cFile, "symlink", rb_file_s_symlink, 2);
 
4458
    rb_define_singleton_method(rb_cFile, "readlink", rb_file_s_readlink, 1);
 
4459
 
 
4460
    rb_define_singleton_method(rb_cFile, "unlink", rb_file_s_unlink, -2);
 
4461
    rb_define_singleton_method(rb_cFile, "delete", rb_file_s_unlink, -2);
 
4462
    rb_define_singleton_method(rb_cFile, "rename", rb_file_s_rename, 2);
 
4463
    rb_define_singleton_method(rb_cFile, "umask", rb_file_s_umask, -1);
 
4464
    rb_define_singleton_method(rb_cFile, "truncate", rb_file_s_truncate, 2);
 
4465
    rb_define_singleton_method(rb_cFile, "expand_path", rb_file_s_expand_path, -1);
 
4466
    rb_define_singleton_method(rb_cFile, "basename", rb_file_s_basename, -1);
 
4467
    rb_define_singleton_method(rb_cFile, "dirname", rb_file_s_dirname, 1);
 
4468
    rb_define_singleton_method(rb_cFile, "extname", rb_file_s_extname, 1);
 
4469
 
 
4470
    separator = rb_obj_freeze(rb_str_new2("/"));
 
4471
    rb_define_const(rb_cFile, "Separator", separator);
 
4472
    rb_define_const(rb_cFile, "SEPARATOR", separator);
 
4473
    rb_define_singleton_method(rb_cFile, "split",  rb_file_s_split, 1);
 
4474
    rb_define_singleton_method(rb_cFile, "join",   rb_file_s_join, -2);
 
4475
 
 
4476
#ifdef DOSISH
 
4477
    rb_define_const(rb_cFile, "ALT_SEPARATOR", rb_obj_freeze(rb_str_new2("\\")));
 
4478
#else
 
4479
    rb_define_const(rb_cFile, "ALT_SEPARATOR", Qnil);
 
4480
#endif
 
4481
    rb_define_const(rb_cFile, "PATH_SEPARATOR", rb_obj_freeze(rb_str_new2(PATH_SEP)));
 
4482
 
 
4483
    rb_define_method(rb_cIO, "stat",  rb_io_stat, 0); /* this is IO's method */
 
4484
    rb_define_method(rb_cFile, "lstat",  rb_file_lstat, 0);
 
4485
 
 
4486
    rb_define_method(rb_cFile, "atime", rb_file_atime, 0);
 
4487
    rb_define_method(rb_cFile, "mtime", rb_file_mtime, 0);
 
4488
    rb_define_method(rb_cFile, "ctime", rb_file_ctime, 0);
 
4489
 
 
4490
    rb_define_method(rb_cFile, "chmod", rb_file_chmod, 1);
 
4491
    rb_define_method(rb_cFile, "chown", rb_file_chown, 2);
 
4492
    rb_define_method(rb_cFile, "truncate", rb_file_truncate, 1);
 
4493
 
 
4494
    rb_define_method(rb_cFile, "flock", rb_file_flock, 1);
 
4495
 
 
4496
    rb_mFConst = rb_define_module_under(rb_cFile, "Constants");
 
4497
    rb_include_module(rb_cIO, rb_mFConst);
 
4498
    rb_file_const("LOCK_SH", INT2FIX(LOCK_SH));
 
4499
    rb_file_const("LOCK_EX", INT2FIX(LOCK_EX));
 
4500
    rb_file_const("LOCK_UN", INT2FIX(LOCK_UN));
 
4501
    rb_file_const("LOCK_NB", INT2FIX(LOCK_NB));
 
4502
 
 
4503
    rb_define_method(rb_cFile, "path",  rb_file_path, 0);
 
4504
    rb_define_global_function("test", rb_f_test, -1);
 
4505
 
 
4506
    rb_cStat = rb_define_class_under(rb_cFile, "Stat", rb_cObject);
 
4507
    rb_define_alloc_func(rb_cStat,  rb_stat_s_alloc);
 
4508
    rb_define_method(rb_cStat, "initialize", rb_stat_init, 1);
 
4509
    rb_define_method(rb_cStat, "initialize_copy", rb_stat_init_copy, 1);
 
4510
 
 
4511
    rb_include_module(rb_cStat, rb_mComparable);
 
4512
 
 
4513
    rb_define_method(rb_cStat, "<=>", rb_stat_cmp, 1);
 
4514
 
 
4515
    rb_define_method(rb_cStat, "dev", rb_stat_dev, 0);
 
4516
    rb_define_method(rb_cStat, "dev_major", rb_stat_dev_major, 0);
 
4517
    rb_define_method(rb_cStat, "dev_minor", rb_stat_dev_minor, 0);
 
4518
    rb_define_method(rb_cStat, "ino", rb_stat_ino, 0);
 
4519
    rb_define_method(rb_cStat, "mode", rb_stat_mode, 0);
 
4520
    rb_define_method(rb_cStat, "nlink", rb_stat_nlink, 0);
 
4521
    rb_define_method(rb_cStat, "uid", rb_stat_uid, 0);
 
4522
    rb_define_method(rb_cStat, "gid", rb_stat_gid, 0);
 
4523
    rb_define_method(rb_cStat, "rdev", rb_stat_rdev, 0);
 
4524
    rb_define_method(rb_cStat, "rdev_major", rb_stat_rdev_major, 0);
 
4525
    rb_define_method(rb_cStat, "rdev_minor", rb_stat_rdev_minor, 0);
 
4526
    rb_define_method(rb_cStat, "size", rb_stat_size, 0);
 
4527
    rb_define_method(rb_cStat, "blksize", rb_stat_blksize, 0);
 
4528
    rb_define_method(rb_cStat, "blocks", rb_stat_blocks, 0);
 
4529
    rb_define_method(rb_cStat, "atime", rb_stat_atime, 0);
 
4530
    rb_define_method(rb_cStat, "mtime", rb_stat_mtime, 0);
 
4531
    rb_define_method(rb_cStat, "ctime", rb_stat_ctime, 0);
 
4532
 
 
4533
    rb_define_method(rb_cStat, "inspect", rb_stat_inspect, 0);
 
4534
 
 
4535
    rb_define_method(rb_cStat, "ftype", rb_stat_ftype, 0);
 
4536
 
 
4537
    rb_define_method(rb_cStat, "directory?",  rb_stat_d, 0);
 
4538
    rb_define_method(rb_cStat, "readable?",  rb_stat_r, 0);
 
4539
    rb_define_method(rb_cStat, "readable_real?",  rb_stat_R, 0);
 
4540
    rb_define_method(rb_cStat, "writable?",  rb_stat_w, 0);
 
4541
    rb_define_method(rb_cStat, "writable_real?",  rb_stat_W, 0);
 
4542
    rb_define_method(rb_cStat, "executable?",  rb_stat_x, 0);
 
4543
    rb_define_method(rb_cStat, "executable_real?",  rb_stat_X, 0);
 
4544
    rb_define_method(rb_cStat, "file?",  rb_stat_f, 0);
 
4545
    rb_define_method(rb_cStat, "zero?",  rb_stat_z, 0);
 
4546
    rb_define_method(rb_cStat, "size?",  rb_stat_s, 0);
 
4547
    rb_define_method(rb_cStat, "owned?",  rb_stat_owned, 0);
 
4548
    rb_define_method(rb_cStat, "grpowned?",  rb_stat_grpowned, 0);
 
4549
 
 
4550
    rb_define_method(rb_cStat, "pipe?",  rb_stat_p, 0);
 
4551
    rb_define_method(rb_cStat, "symlink?",  rb_stat_l, 0);
 
4552
    rb_define_method(rb_cStat, "socket?",  rb_stat_S, 0);
 
4553
 
 
4554
    rb_define_method(rb_cStat, "blockdev?",  rb_stat_b, 0);
 
4555
    rb_define_method(rb_cStat, "chardev?",  rb_stat_c, 0);
 
4556
 
 
4557
    rb_define_method(rb_cStat, "setuid?",  rb_stat_suid, 0);
 
4558
    rb_define_method(rb_cStat, "setgid?",  rb_stat_sgid, 0);
 
4559
    rb_define_method(rb_cStat, "sticky?",  rb_stat_sticky, 0);
 
4560
}