1#include "ruby.h"
2#include "ruby/encoding.h"
3
4static VALUE rb_cPathname;
5static ID id_at_path, id_to_path;
6
7static VALUE
8get_strpath(VALUE obj)
9{
10    VALUE strpath;
11    strpath = rb_ivar_get(obj, id_at_path);
12    if (!RB_TYPE_P(strpath, T_STRING))
13        rb_raise(rb_eTypeError, "unexpected @path");
14    return strpath;
15}
16
17static void
18set_strpath(VALUE obj, VALUE val)
19{
20    rb_ivar_set(obj, id_at_path, val);
21}
22
23/*
24 * Create a Pathname object from the given String (or String-like object).
25 * If +path+ contains a NULL character (<tt>\0</tt>), an ArgumentError is raised.
26 */
27static VALUE
28path_initialize(VALUE self, VALUE arg)
29{
30    VALUE str;
31    if (RB_TYPE_P(arg, T_STRING)) {
32        str = arg;
33    }
34    else {
35        str = rb_check_funcall(arg, id_to_path, 0, NULL);
36        if (str == Qundef)
37            str = arg;
38        StringValue(str);
39    }
40    if (memchr(RSTRING_PTR(str), '\0', RSTRING_LEN(str)))
41        rb_raise(rb_eArgError, "pathname contains null byte");
42    str = rb_obj_dup(str);
43
44    set_strpath(self, str);
45    OBJ_INFECT(self, str);
46    return self;
47}
48
49/*
50 * call-seq:
51 *   pathname.freeze -> obj
52 *
53 * Freezes this Pathname.
54 *
55 * See Object.freeze.
56 */
57static VALUE
58path_freeze(VALUE self)
59{
60    rb_call_super(0, 0);
61    rb_str_freeze(get_strpath(self));
62    return self;
63}
64
65/*
66 * call-seq:
67 *   pathname.taint -> obj
68 *
69 * Taints this Pathname.
70 *
71 * See Object.taint.
72 */
73static VALUE
74path_taint(VALUE self)
75{
76    rb_call_super(0, 0);
77    rb_obj_taint(get_strpath(self));
78    return self;
79}
80
81/*
82 * call-seq:
83 *   pathname.untaint -> obj
84 *
85 * Untaints this Pathname.
86 *
87 * See Object.untaint.
88 */
89static VALUE
90path_untaint(VALUE self)
91{
92    rb_call_super(0, 0);
93    rb_obj_untaint(get_strpath(self));
94    return self;
95}
96
97/*
98 *  Compare this pathname with +other+.  The comparison is string-based.
99 *  Be aware that two different paths (<tt>foo.txt</tt> and <tt>./foo.txt</tt>)
100 *  can refer to the same file.
101 */
102static VALUE
103path_eq(VALUE self, VALUE other)
104{
105    if (!rb_obj_is_kind_of(other, rb_cPathname))
106        return Qfalse;
107    return rb_str_equal(get_strpath(self), get_strpath(other));
108}
109
110/*
111 *  Provides a case-sensitive comparison operator for pathnames.
112 *
113 *	Pathname.new('/usr') <=> Pathname.new('/usr/bin')
114 *	    #=> -1
115 *	Pathname.new('/usr/bin') <=> Pathname.new('/usr/bin')
116 *	    #=> 0
117 *	Pathname.new('/usr/bin') <=> Pathname.new('/USR/BIN')
118 *	    #=> 1
119 *
120 *  It will return +-1+, +0+ or +1+ depending on the value of the left argument
121 *  relative to the right argument. Or it will return +nil+ if the arguments
122 *  are not comparable.
123 */
124static VALUE
125path_cmp(VALUE self, VALUE other)
126{
127    VALUE s1, s2;
128    char *p1, *p2;
129    char *e1, *e2;
130    if (!rb_obj_is_kind_of(other, rb_cPathname))
131        return Qnil;
132    s1 = get_strpath(self);
133    s2 = get_strpath(other);
134    p1 = RSTRING_PTR(s1);
135    p2 = RSTRING_PTR(s2);
136    e1 = p1 + RSTRING_LEN(s1);
137    e2 = p2 + RSTRING_LEN(s2);
138    while (p1 < e1 && p2 < e2) {
139        int c1, c2;
140        c1 = (unsigned char)*p1++;
141        c2 = (unsigned char)*p2++;
142        if (c1 == '/') c1 = '\0';
143        if (c2 == '/') c2 = '\0';
144        if (c1 != c2) {
145            if (c1 < c2)
146                return INT2FIX(-1);
147            else
148                return INT2FIX(1);
149        }
150    }
151    if (p1 < e1)
152        return INT2FIX(1);
153    if (p2 < e2)
154        return INT2FIX(-1);
155    return INT2FIX(0);
156}
157
158/* :nodoc: */
159static VALUE
160path_hash(VALUE self)
161{
162    return INT2FIX(rb_str_hash(get_strpath(self)));
163}
164
165/*
166 *  call-seq:
167 *    pathname.to_s             -> string
168 *    pathname.to_path          -> string
169 *
170 *  Return the path as a String.
171 *
172 *  to_path is implemented so Pathname objects are usable with File.open, etc.
173 */
174static VALUE
175path_to_s(VALUE self)
176{
177    return rb_obj_dup(get_strpath(self));
178}
179
180/* :nodoc: */
181static VALUE
182path_inspect(VALUE self)
183{
184    const char *c = rb_obj_classname(self);
185    VALUE str = get_strpath(self);
186    return rb_sprintf("#<%s:%s>", c, RSTRING_PTR(str));
187}
188
189/*
190 * Return a pathname which is substituted by String#sub.
191 *
192 *	path1 = Pathname.new('/usr/bin/perl')
193 *	path1.sub('perl', 'ruby')
194 *	    #=> #<Pathname:/usr/bin/ruby>
195 */
196static VALUE
197path_sub(int argc, VALUE *argv, VALUE self)
198{
199    VALUE str = get_strpath(self);
200
201    if (rb_block_given_p()) {
202        str = rb_block_call(str, rb_intern("sub"), argc, argv, 0, 0);
203    }
204    else {
205        str = rb_funcall2(str, rb_intern("sub"), argc, argv);
206    }
207    return rb_class_new_instance(1, &str, rb_obj_class(self));
208}
209
210/*
211 * Return a pathname with +repl+ added as a suffix to the basename.
212 *
213 * If self has no extension part, +repl+ is appended.
214 *
215 *	Pathname.new('/usr/bin/shutdown').sub_ext('.rb')
216 *	    #=> #<Pathname:/usr/bin/shutdown.rb>
217 */
218static VALUE
219path_sub_ext(VALUE self, VALUE repl)
220{
221    VALUE str = get_strpath(self);
222    VALUE str2;
223    long extlen;
224    const char *ext;
225    const char *p;
226
227    StringValue(repl);
228    p = RSTRING_PTR(str);
229    extlen = RSTRING_LEN(str);
230    ext = ruby_enc_find_extname(p, &extlen, rb_enc_get(str));
231    if (ext == NULL) {
232        ext = p + RSTRING_LEN(str);
233    }
234    else if (extlen <= 1) {
235        ext += extlen;
236    }
237    str2 = rb_str_subseq(str, 0, ext-p);
238    rb_str_append(str2, repl);
239    OBJ_INFECT(str2, str);
240    return rb_class_new_instance(1, &str2, rb_obj_class(self));
241}
242
243/* Facade for File */
244
245/*
246 * Returns the real (absolute) pathname for +self+ in the actual
247 * filesystem.
248 *
249 * Does not contain symlinks or useless dots, +..+ and +.+.
250 *
251 * All components of the pathname must exist when this method is
252 * called.
253 *
254 */
255static VALUE
256path_realpath(int argc, VALUE *argv, VALUE self)
257{
258    VALUE basedir, str;
259    rb_scan_args(argc, argv, "01", &basedir);
260    str = rb_funcall(rb_cFile, rb_intern("realpath"), 2, get_strpath(self), basedir);
261    return rb_class_new_instance(1, &str, rb_obj_class(self));
262}
263
264/*
265 * Returns the real (absolute) pathname of +self+ in the actual filesystem.
266 *
267 * Does not contain symlinks or useless dots, +..+ and +.+.
268 *
269 * The last component of the real pathname can be nonexistent.
270 */
271static VALUE
272path_realdirpath(int argc, VALUE *argv, VALUE self)
273{
274    VALUE basedir, str;
275    rb_scan_args(argc, argv, "01", &basedir);
276    str = rb_funcall(rb_cFile, rb_intern("realdirpath"), 2, get_strpath(self), basedir);
277    return rb_class_new_instance(1, &str, rb_obj_class(self));
278}
279
280/*
281 * call-seq:
282 *   pathname.each_line {|line| ... }
283 *   pathname.each_line(sep=$/ [, open_args]) {|line| block }     -> nil
284 *   pathname.each_line(limit [, open_args]) {|line| block }      -> nil
285 *   pathname.each_line(sep, limit [, open_args]) {|line| block } -> nil
286 *   pathname.each_line(...)                                      -> an_enumerator
287 *
288 * Iterates over each line in the file and yields a String object for each.
289 */
290static VALUE
291path_each_line(int argc, VALUE *argv, VALUE self)
292{
293    VALUE args[4];
294    int n;
295
296    args[0] = get_strpath(self);
297    n = rb_scan_args(argc, argv, "03", &args[1], &args[2], &args[3]);
298    if (rb_block_given_p()) {
299        return rb_block_call(rb_cIO, rb_intern("foreach"), 1+n, args, 0, 0);
300    }
301    else {
302        return rb_funcall2(rb_cIO, rb_intern("foreach"), 1+n, args);
303    }
304}
305
306/*
307 * call-seq:
308 *   pathname.read([length [, offset]]) -> string
309 *   pathname.read([length [, offset]], open_args) -> string
310 *
311 * Returns all data from the file, or the first +N+ bytes if specified.
312 *
313 * See IO.read.
314 *
315 */
316static VALUE
317path_read(int argc, VALUE *argv, VALUE self)
318{
319    VALUE args[4];
320    int n;
321
322    args[0] = get_strpath(self);
323    n = rb_scan_args(argc, argv, "03", &args[1], &args[2], &args[3]);
324    return rb_funcall2(rb_cIO, rb_intern("read"), 1+n, args);
325}
326
327/*
328 * call-seq:
329 *   pathname.binread([length [, offset]]) -> string
330 *
331 * Returns all the bytes from the file, or the first +N+ if specified.
332 *
333 * See IO.binread.
334 *
335 */
336static VALUE
337path_binread(int argc, VALUE *argv, VALUE self)
338{
339    VALUE args[3];
340    int n;
341
342    args[0] = get_strpath(self);
343    n = rb_scan_args(argc, argv, "02", &args[1], &args[2]);
344    return rb_funcall2(rb_cIO, rb_intern("binread"), 1+n, args);
345}
346
347/*
348 * call-seq:
349 *   pathname.readlines(sep=$/ [, open_args])     -> array
350 *   pathname.readlines(limit [, open_args])      -> array
351 *   pathname.readlines(sep, limit [, open_args]) -> array
352 *
353 * Returns all the lines from the file.
354 *
355 * See IO.readlines.
356 *
357 */
358static VALUE
359path_readlines(int argc, VALUE *argv, VALUE self)
360{
361    VALUE args[4];
362    int n;
363
364    args[0] = get_strpath(self);
365    n = rb_scan_args(argc, argv, "03", &args[1], &args[2], &args[3]);
366    return rb_funcall2(rb_cIO, rb_intern("readlines"), 1+n, args);
367}
368
369/*
370 * call-seq:
371 *   pathname.sysopen([mode, [perm]])  -> fixnum
372 *
373 * See IO.sysopen.
374 *
375 */
376static VALUE
377path_sysopen(int argc, VALUE *argv, VALUE self)
378{
379    VALUE args[3];
380    int n;
381
382    args[0] = get_strpath(self);
383    n = rb_scan_args(argc, argv, "02", &args[1], &args[2]);
384    return rb_funcall2(rb_cIO, rb_intern("sysopen"), 1+n, args);
385}
386
387/*
388 * call-seq:
389 *   pathname.atime	-> time
390 *
391 * Returns the last access time for the file.
392 *
393 * See File.atime.
394 */
395static VALUE
396path_atime(VALUE self)
397{
398    return rb_funcall(rb_cFile, rb_intern("atime"), 1, get_strpath(self));
399}
400
401/*
402 * call-seq:
403 *   pathname.ctime	-> time
404 *
405 * Returns the last change time, using directory information, not the file itself.
406 *
407 * See File.ctime.
408 */
409static VALUE
410path_ctime(VALUE self)
411{
412    return rb_funcall(rb_cFile, rb_intern("ctime"), 1, get_strpath(self));
413}
414
415/*
416 * call-seq:
417 *   pathname.mtime	-> time
418 *
419 * Returns the last modified time of the file.
420 *
421 * See File.mtime.
422 */
423static VALUE
424path_mtime(VALUE self)
425{
426    return rb_funcall(rb_cFile, rb_intern("mtime"), 1, get_strpath(self));
427}
428
429/*
430 * call-seq:
431 *   pathname.chmod	-> integer
432 *
433 * Changes file permissions.
434 *
435 * See File.chmod.
436 */
437static VALUE
438path_chmod(VALUE self, VALUE mode)
439{
440    return rb_funcall(rb_cFile, rb_intern("chmod"), 2, mode, get_strpath(self));
441}
442
443/*
444 * call-seq:
445 *   pathname.lchmod	-> integer
446 *
447 * Same as Pathname.chmod, but does not follow symbolic links.
448 *
449 * See File.lchmod.
450 */
451static VALUE
452path_lchmod(VALUE self, VALUE mode)
453{
454    return rb_funcall(rb_cFile, rb_intern("lchmod"), 2, mode, get_strpath(self));
455}
456
457/*
458 * call-seq:
459 *   pathname.chown	-> integer
460 *
461 * Change owner and group of the file.
462 *
463 * See File.chown.
464 */
465static VALUE
466path_chown(VALUE self, VALUE owner, VALUE group)
467{
468    return rb_funcall(rb_cFile, rb_intern("chown"), 3, owner, group, get_strpath(self));
469}
470
471/*
472 * call-seq:
473 *   pathname.lchown	-> integer
474 *
475 * Same as Pathname.chown, but does not follow symbolic links.
476 *
477 * See File.lchown.
478 */
479static VALUE
480path_lchown(VALUE self, VALUE owner, VALUE group)
481{
482    return rb_funcall(rb_cFile, rb_intern("lchown"), 3, owner, group, get_strpath(self));
483}
484
485/*
486 * call-seq:
487 *    pathname.fnmatch(pattern, [flags])        -> string
488 *    pathname.fnmatch?(pattern, [flags])       -> string
489 *
490 * Return +true+ if the receiver matches the given pattern.
491 *
492 * See File.fnmatch.
493 */
494static VALUE
495path_fnmatch(int argc, VALUE *argv, VALUE self)
496{
497    VALUE str = get_strpath(self);
498    VALUE pattern, flags;
499    if (rb_scan_args(argc, argv, "11", &pattern, &flags) == 1)
500        return rb_funcall(rb_cFile, rb_intern("fnmatch"), 2, pattern, str);
501    else
502        return rb_funcall(rb_cFile, rb_intern("fnmatch"), 3, pattern, str, flags);
503}
504
505/*
506 * call-seq:
507 *   pathname.ftype	-> string
508 *
509 * Returns "type" of file ("file", "directory", etc).
510 *
511 * See File.ftype.
512 */
513static VALUE
514path_ftype(VALUE self)
515{
516    return rb_funcall(rb_cFile, rb_intern("ftype"), 1, get_strpath(self));
517}
518
519/*
520 * call-seq:
521 *   pathname.make_link(old)
522 *
523 * Creates a hard link at _pathname_.
524 *
525 * See File.link.
526 */
527static VALUE
528path_make_link(VALUE self, VALUE old)
529{
530    return rb_funcall(rb_cFile, rb_intern("link"), 2, old, get_strpath(self));
531}
532
533/*
534 * Opens the file for reading or writing.
535 *
536 * See File.open.
537 */
538static VALUE
539path_open(int argc, VALUE *argv, VALUE self)
540{
541    VALUE args[4];
542    int n;
543
544    args[0] = get_strpath(self);
545    n = rb_scan_args(argc, argv, "03", &args[1], &args[2], &args[3]);
546    if (rb_block_given_p()) {
547        return rb_block_call(rb_cFile, rb_intern("open"), 1+n, args, 0, 0);
548    }
549    else {
550        return rb_funcall2(rb_cFile, rb_intern("open"), 1+n, args);
551    }
552}
553
554/*
555 * Read symbolic link.
556 *
557 * See File.readlink.
558 */
559static VALUE
560path_readlink(VALUE self)
561{
562    VALUE str;
563    str = rb_funcall(rb_cFile, rb_intern("readlink"), 1, get_strpath(self));
564    return rb_class_new_instance(1, &str, rb_obj_class(self));
565}
566
567/*
568 * Rename the file.
569 *
570 * See File.rename.
571 */
572static VALUE
573path_rename(VALUE self, VALUE to)
574{
575    return rb_funcall(rb_cFile, rb_intern("rename"), 2, get_strpath(self), to);
576}
577
578/*
579 * Returns a File::Stat object.
580 *
581 * See File.stat.
582 */
583static VALUE
584path_stat(VALUE self)
585{
586    return rb_funcall(rb_cFile, rb_intern("stat"), 1, get_strpath(self));
587}
588
589/*
590 * See File.lstat.
591 */
592static VALUE
593path_lstat(VALUE self)
594{
595    return rb_funcall(rb_cFile, rb_intern("lstat"), 1, get_strpath(self));
596}
597
598/*
599 * call-seq:
600 *   pathname.make_symlink(old)
601 *
602 * Creates a symbolic link.
603 *
604 * See File.symlink.
605 */
606static VALUE
607path_make_symlink(VALUE self, VALUE old)
608{
609    return rb_funcall(rb_cFile, rb_intern("symlink"), 2, old, get_strpath(self));
610}
611
612/*
613 * Truncates the file to +length+ bytes.
614 *
615 * See File.truncate.
616 */
617static VALUE
618path_truncate(VALUE self, VALUE length)
619{
620    return rb_funcall(rb_cFile, rb_intern("truncate"), 2, get_strpath(self), length);
621}
622
623/*
624 * Update the access and modification times of the file.
625 *
626 * See File.utime.
627 */
628static VALUE
629path_utime(VALUE self, VALUE atime, VALUE mtime)
630{
631    return rb_funcall(rb_cFile, rb_intern("utime"), 3, atime, mtime, get_strpath(self));
632}
633
634/*
635 * Returns the last component of the path.
636 *
637 * See File.basename.
638 */
639static VALUE
640path_basename(int argc, VALUE *argv, VALUE self)
641{
642    VALUE str = get_strpath(self);
643    VALUE fext;
644    if (rb_scan_args(argc, argv, "01", &fext) == 0)
645        str = rb_funcall(rb_cFile, rb_intern("basename"), 1, str);
646    else
647        str = rb_funcall(rb_cFile, rb_intern("basename"), 2, str, fext);
648    return rb_class_new_instance(1, &str, rb_obj_class(self));
649}
650
651/*
652 * Returns all but the last component of the path.
653 *
654 * See File.dirname.
655 */
656static VALUE
657path_dirname(VALUE self)
658{
659    VALUE str = get_strpath(self);
660    str = rb_funcall(rb_cFile, rb_intern("dirname"), 1, str);
661    return rb_class_new_instance(1, &str, rb_obj_class(self));
662}
663
664/*
665 * Returns the file's extension.
666 *
667 * See File.extname.
668 */
669static VALUE
670path_extname(VALUE self)
671{
672    VALUE str = get_strpath(self);
673    return rb_funcall(rb_cFile, rb_intern("extname"), 1, str);
674}
675
676/*
677 * Returns the absolute path for the file.
678 *
679 * See File.expand_path.
680 */
681static VALUE
682path_expand_path(int argc, VALUE *argv, VALUE self)
683{
684    VALUE str = get_strpath(self);
685    VALUE dname;
686    if (rb_scan_args(argc, argv, "01", &dname) == 0)
687        str = rb_funcall(rb_cFile, rb_intern("expand_path"), 1, str);
688    else
689        str = rb_funcall(rb_cFile, rb_intern("expand_path"), 2, str, dname);
690    return rb_class_new_instance(1, &str, rb_obj_class(self));
691}
692
693/*
694 * Returns the #dirname and the #basename in an Array.
695 *
696 * See File.split.
697 */
698static VALUE
699path_split(VALUE self)
700{
701    VALUE str = get_strpath(self);
702    VALUE ary, dirname, basename;
703    ary = rb_funcall(rb_cFile, rb_intern("split"), 1, str);
704    ary = rb_check_array_type(ary);
705    dirname = rb_ary_entry(ary, 0);
706    basename = rb_ary_entry(ary, 1);
707    dirname = rb_class_new_instance(1, &dirname, rb_obj_class(self));
708    basename = rb_class_new_instance(1, &basename, rb_obj_class(self));
709    return rb_ary_new3(2, dirname, basename);
710}
711
712/*
713 * See FileTest.blockdev?.
714 */
715static VALUE
716path_blockdev_p(VALUE self)
717{
718    return rb_funcall(rb_mFileTest, rb_intern("blockdev?"), 1, get_strpath(self));
719}
720
721/*
722 * See FileTest.chardev?.
723 */
724static VALUE
725path_chardev_p(VALUE self)
726{
727    return rb_funcall(rb_mFileTest, rb_intern("chardev?"), 1, get_strpath(self));
728}
729
730/*
731 * See FileTest.executable?.
732 */
733static VALUE
734path_executable_p(VALUE self)
735{
736    return rb_funcall(rb_mFileTest, rb_intern("executable?"), 1, get_strpath(self));
737}
738
739/*
740 * See FileTest.executable_real?.
741 */
742static VALUE
743path_executable_real_p(VALUE self)
744{
745    return rb_funcall(rb_mFileTest, rb_intern("executable_real?"), 1, get_strpath(self));
746}
747
748/*
749 * See FileTest.exist?.
750 */
751static VALUE
752path_exist_p(VALUE self)
753{
754    return rb_funcall(rb_mFileTest, rb_intern("exist?"), 1, get_strpath(self));
755}
756
757/*
758 * See FileTest.grpowned?.
759 */
760static VALUE
761path_grpowned_p(VALUE self)
762{
763    return rb_funcall(rb_mFileTest, rb_intern("grpowned?"), 1, get_strpath(self));
764}
765
766/*
767 * See FileTest.directory?.
768 */
769static VALUE
770path_directory_p(VALUE self)
771{
772    return rb_funcall(rb_mFileTest, rb_intern("directory?"), 1, get_strpath(self));
773}
774
775/*
776 * See FileTest.file?.
777 */
778static VALUE
779path_file_p(VALUE self)
780{
781    return rb_funcall(rb_mFileTest, rb_intern("file?"), 1, get_strpath(self));
782}
783
784/*
785 * See FileTest.pipe?.
786 */
787static VALUE
788path_pipe_p(VALUE self)
789{
790    return rb_funcall(rb_mFileTest, rb_intern("pipe?"), 1, get_strpath(self));
791}
792
793/*
794 * See FileTest.socket?.
795 */
796static VALUE
797path_socket_p(VALUE self)
798{
799    return rb_funcall(rb_mFileTest, rb_intern("socket?"), 1, get_strpath(self));
800}
801
802/*
803 * See FileTest.owned?.
804 */
805static VALUE
806path_owned_p(VALUE self)
807{
808    return rb_funcall(rb_mFileTest, rb_intern("owned?"), 1, get_strpath(self));
809}
810
811/*
812 * See FileTest.readable?.
813 */
814static VALUE
815path_readable_p(VALUE self)
816{
817    return rb_funcall(rb_mFileTest, rb_intern("readable?"), 1, get_strpath(self));
818}
819
820/*
821 * See FileTest.world_readable?.
822 */
823static VALUE
824path_world_readable_p(VALUE self)
825{
826    return rb_funcall(rb_mFileTest, rb_intern("world_readable?"), 1, get_strpath(self));
827}
828
829/*
830 * See FileTest.readable_real?.
831 */
832static VALUE
833path_readable_real_p(VALUE self)
834{
835    return rb_funcall(rb_mFileTest, rb_intern("readable_real?"), 1, get_strpath(self));
836}
837
838/*
839 * See FileTest.setuid?.
840 */
841static VALUE
842path_setuid_p(VALUE self)
843{
844    return rb_funcall(rb_mFileTest, rb_intern("setuid?"), 1, get_strpath(self));
845}
846
847/*
848 * See FileTest.setgid?.
849 */
850static VALUE
851path_setgid_p(VALUE self)
852{
853    return rb_funcall(rb_mFileTest, rb_intern("setgid?"), 1, get_strpath(self));
854}
855
856/*
857 * See FileTest.size.
858 */
859static VALUE
860path_size(VALUE self)
861{
862    return rb_funcall(rb_mFileTest, rb_intern("size"), 1, get_strpath(self));
863}
864
865/*
866 * See FileTest.size?.
867 */
868static VALUE
869path_size_p(VALUE self)
870{
871    return rb_funcall(rb_mFileTest, rb_intern("size?"), 1, get_strpath(self));
872}
873
874/*
875 * See FileTest.sticky?.
876 */
877static VALUE
878path_sticky_p(VALUE self)
879{
880    return rb_funcall(rb_mFileTest, rb_intern("sticky?"), 1, get_strpath(self));
881}
882
883/*
884 * See FileTest.symlink?.
885 */
886static VALUE
887path_symlink_p(VALUE self)
888{
889    return rb_funcall(rb_mFileTest, rb_intern("symlink?"), 1, get_strpath(self));
890}
891
892/*
893 * See FileTest.writable?.
894 */
895static VALUE
896path_writable_p(VALUE self)
897{
898    return rb_funcall(rb_mFileTest, rb_intern("writable?"), 1, get_strpath(self));
899}
900
901/*
902 * See FileTest.world_writable?.
903 */
904static VALUE
905path_world_writable_p(VALUE self)
906{
907    return rb_funcall(rb_mFileTest, rb_intern("world_writable?"), 1, get_strpath(self));
908}
909
910/*
911 * See FileTest.writable_real?.
912 */
913static VALUE
914path_writable_real_p(VALUE self)
915{
916    return rb_funcall(rb_mFileTest, rb_intern("writable_real?"), 1, get_strpath(self));
917}
918
919/*
920 * See FileTest.zero?.
921 */
922static VALUE
923path_zero_p(VALUE self)
924{
925    return rb_funcall(rb_mFileTest, rb_intern("zero?"), 1, get_strpath(self));
926}
927
928static VALUE
929glob_i(VALUE elt, VALUE klass, int argc, VALUE *argv)
930{
931    return rb_yield(rb_class_new_instance(1, &elt, klass));
932}
933
934/*
935 * Returns or yields Pathname objects.
936 *
937 *  Pathname.glob("config/" "*.rb")
938 *	#=> [#<Pathname:config/environment.rb>, #<Pathname:config/routes.rb>, ..]
939 *
940 * See Dir.glob.
941 */
942static VALUE
943path_s_glob(int argc, VALUE *argv, VALUE klass)
944{
945    VALUE args[2];
946    int n;
947
948    n = rb_scan_args(argc, argv, "11", &args[0], &args[1]);
949    if (rb_block_given_p()) {
950        return rb_block_call(rb_cDir, rb_intern("glob"), n, args, glob_i, klass);
951    }
952    else {
953        VALUE ary;
954        long i;
955        ary = rb_funcall2(rb_cDir, rb_intern("glob"), n, args);
956        ary = rb_convert_type(ary, T_ARRAY, "Array", "to_ary");
957        for (i = 0; i < RARRAY_LEN(ary); i++) {
958            VALUE elt = RARRAY_PTR(ary)[i];
959            elt = rb_class_new_instance(1, &elt, klass);
960            rb_ary_store(ary, i, elt);
961        }
962        return ary;
963    }
964}
965
966/*
967 * Returns the current working directory as a Pathname.
968 *
969 *	Pathname.getwd
970 *	    #=> #<Pathname:/home/zzak/projects/ruby>
971 *
972 * See Dir.getwd.
973 */
974static VALUE
975path_s_getwd(VALUE klass)
976{
977    VALUE str;
978    str = rb_funcall(rb_cDir, rb_intern("getwd"), 0);
979    return rb_class_new_instance(1, &str, klass);
980}
981
982/*
983 * Return the entries (files and subdirectories) in the directory, each as a
984 * Pathname object.
985 *
986 * The results contains just the names in the directory, without any trailing
987 * slashes or recursive look-up.
988 *
989 *   pp Pathname.new('/usr/local').entries
990 *   #=> [#<Pathname:share>,
991 *   #    #<Pathname:lib>,
992 *   #    #<Pathname:..>,
993 *   #    #<Pathname:include>,
994 *   #    #<Pathname:etc>,
995 *   #    #<Pathname:bin>,
996 *   #    #<Pathname:man>,
997 *   #    #<Pathname:games>,
998 *   #    #<Pathname:.>,
999 *   #    #<Pathname:sbin>,
1000 *   #    #<Pathname:src>]
1001 *
1002 * The result may contain the current directory <code>#<Pathname:.></code> and
1003 * the parent directory <code>#<Pathname:..></code>.
1004 *
1005 * If you don't want +.+ and +..+ and
1006 * want directories, consider Pathname#children.
1007 */
1008static VALUE
1009path_entries(VALUE self)
1010{
1011    VALUE klass, str, ary;
1012    long i;
1013    klass = rb_obj_class(self);
1014    str = get_strpath(self);
1015    ary = rb_funcall(rb_cDir, rb_intern("entries"), 1, str);
1016    ary = rb_convert_type(ary, T_ARRAY, "Array", "to_ary");
1017    for (i = 0; i < RARRAY_LEN(ary); i++) {
1018        VALUE elt = RARRAY_PTR(ary)[i];
1019        elt = rb_class_new_instance(1, &elt, klass);
1020        rb_ary_store(ary, i, elt);
1021    }
1022    return ary;
1023}
1024
1025/*
1026 * Create the referenced directory.
1027 *
1028 * See Dir.mkdir.
1029 */
1030static VALUE
1031path_mkdir(int argc, VALUE *argv, VALUE self)
1032{
1033    VALUE str = get_strpath(self);
1034    VALUE vmode;
1035    if (rb_scan_args(argc, argv, "01", &vmode) == 0)
1036        return rb_funcall(rb_cDir, rb_intern("mkdir"), 1, str);
1037    else
1038        return rb_funcall(rb_cDir, rb_intern("mkdir"), 2, str, vmode);
1039}
1040
1041/*
1042 * Remove the referenced directory.
1043 *
1044 * See Dir.rmdir.
1045 */
1046static VALUE
1047path_rmdir(VALUE self)
1048{
1049    return rb_funcall(rb_cDir, rb_intern("rmdir"), 1, get_strpath(self));
1050}
1051
1052/*
1053 * Opens the referenced directory.
1054 *
1055 * See Dir.open.
1056 */
1057static VALUE
1058path_opendir(VALUE self)
1059{
1060    VALUE args[1];
1061
1062    args[0] = get_strpath(self);
1063    return rb_block_call(rb_cDir, rb_intern("open"), 1, args, 0, 0);
1064}
1065
1066static VALUE
1067each_entry_i(VALUE elt, VALUE klass, int argc, VALUE *argv)
1068{
1069    return rb_yield(rb_class_new_instance(1, &elt, klass));
1070}
1071
1072/*
1073 * Iterates over the entries (files and subdirectories) in the directory,
1074 * yielding a Pathname object for each entry.
1075 */
1076static VALUE
1077path_each_entry(VALUE self)
1078{
1079    VALUE args[1];
1080
1081    args[0] = get_strpath(self);
1082    return rb_block_call(rb_cDir, rb_intern("foreach"), 1, args, each_entry_i, rb_obj_class(self));
1083}
1084
1085static VALUE
1086unlink_body(VALUE str)
1087{
1088    return rb_funcall(rb_cDir, rb_intern("unlink"), 1, str);
1089}
1090
1091static VALUE
1092unlink_rescue(VALUE str, VALUE errinfo)
1093{
1094    return rb_funcall(rb_cFile, rb_intern("unlink"), 1, str);
1095}
1096
1097/*
1098 * Removes a file or directory, using File.unlink if +self+ is a file, or
1099 * Dir.unlink as necessary.
1100 */
1101static VALUE
1102path_unlink(VALUE self)
1103{
1104    VALUE eENOTDIR = rb_const_get_at(rb_mErrno, rb_intern("ENOTDIR"));
1105    VALUE str = get_strpath(self);
1106    return rb_rescue2(unlink_body, str, unlink_rescue, str, eENOTDIR, (VALUE)0);
1107}
1108
1109/*
1110 * Creates a new Pathname object.
1111 */
1112static VALUE
1113path_f_pathname(VALUE self, VALUE str)
1114{
1115    return rb_class_new_instance(1, &str, rb_cPathname);
1116}
1117
1118/*
1119 *
1120 * Pathname represents the name of a file or directory on the filesystem,
1121 * but not the file itself.
1122 *
1123 * The pathname depends on the Operating System: Unix, Windows, etc.
1124 * This library works with pathnames of local OS, however non-Unix pathnames
1125 * are supported experimentally.
1126 *
1127 * A Pathname can be relative or absolute.  It's not until you try to
1128 * reference the file that it even matters whether the file exists or not.
1129 *
1130 * Pathname is immutable.  It has no method for destructive update.
1131 *
1132 * The goal of this class is to manipulate file path information in a neater
1133 * way than standard Ruby provides.  The examples below demonstrate the
1134 * difference.
1135 *
1136 * *All* functionality from File, FileTest, and some from Dir and FileUtils is
1137 * included, in an unsurprising way.  It is essentially a facade for all of
1138 * these, and more.
1139 *
1140 * == Examples
1141 *
1142 * === Example 1: Using Pathname
1143 *
1144 *   require 'pathname'
1145 *   pn = Pathname.new("/usr/bin/ruby")
1146 *   size = pn.size              # 27662
1147 *   isdir = pn.directory?       # false
1148 *   dir  = pn.dirname           # Pathname:/usr/bin
1149 *   base = pn.basename          # Pathname:ruby
1150 *   dir, base = pn.split        # [Pathname:/usr/bin, Pathname:ruby]
1151 *   data = pn.read
1152 *   pn.open { |f| _ }
1153 *   pn.each_line { |line| _ }
1154 *
1155 * === Example 2: Using standard Ruby
1156 *
1157 *   pn = "/usr/bin/ruby"
1158 *   size = File.size(pn)        # 27662
1159 *   isdir = File.directory?(pn) # false
1160 *   dir  = File.dirname(pn)     # "/usr/bin"
1161 *   base = File.basename(pn)    # "ruby"
1162 *   dir, base = File.split(pn)  # ["/usr/bin", "ruby"]
1163 *   data = File.read(pn)
1164 *   File.open(pn) { |f| _ }
1165 *   File.foreach(pn) { |line| _ }
1166 *
1167 * === Example 3: Special features
1168 *
1169 *   p1 = Pathname.new("/usr/lib")   # Pathname:/usr/lib
1170 *   p2 = p1 + "ruby/1.8"            # Pathname:/usr/lib/ruby/1.8
1171 *   p3 = p1.parent                  # Pathname:/usr
1172 *   p4 = p2.relative_path_from(p3)  # Pathname:lib/ruby/1.8
1173 *   pwd = Pathname.pwd              # Pathname:/home/gavin
1174 *   pwd.absolute?                   # true
1175 *   p5 = Pathname.new "."           # Pathname:.
1176 *   p5 = p5 + "music/../articles"   # Pathname:music/../articles
1177 *   p5.cleanpath                    # Pathname:articles
1178 *   p5.realpath                     # Pathname:/home/gavin/articles
1179 *   p5.children                     # [Pathname:/home/gavin/articles/linux, ...]
1180 *
1181 * == Breakdown of functionality
1182 *
1183 * === Core methods
1184 *
1185 * These methods are effectively manipulating a String, because that's
1186 * all a path is.  None of these access the file system except for
1187 * #mountpoint?, #children, #each_child, #realdirpath and #realpath.
1188 *
1189 * - +
1190 * - #join
1191 * - #parent
1192 * - #root?
1193 * - #absolute?
1194 * - #relative?
1195 * - #relative_path_from
1196 * - #each_filename
1197 * - #cleanpath
1198 * - #realpath
1199 * - #realdirpath
1200 * - #children
1201 * - #each_child
1202 * - #mountpoint?
1203 *
1204 * === File status predicate methods
1205 *
1206 * These methods are a facade for FileTest:
1207 * - #blockdev?
1208 * - #chardev?
1209 * - #directory?
1210 * - #executable?
1211 * - #executable_real?
1212 * - #exist?
1213 * - #file?
1214 * - #grpowned?
1215 * - #owned?
1216 * - #pipe?
1217 * - #readable?
1218 * - #world_readable?
1219 * - #readable_real?
1220 * - #setgid?
1221 * - #setuid?
1222 * - #size
1223 * - #size?
1224 * - #socket?
1225 * - #sticky?
1226 * - #symlink?
1227 * - #writable?
1228 * - #world_writable?
1229 * - #writable_real?
1230 * - #zero?
1231 *
1232 * === File property and manipulation methods
1233 *
1234 * These methods are a facade for File:
1235 * - #atime
1236 * - #ctime
1237 * - #mtime
1238 * - #chmod(mode)
1239 * - #lchmod(mode)
1240 * - #chown(owner, group)
1241 * - #lchown(owner, group)
1242 * - #fnmatch(pattern, *args)
1243 * - #fnmatch?(pattern, *args)
1244 * - #ftype
1245 * - #make_link(old)
1246 * - #open(*args, &block)
1247 * - #readlink
1248 * - #rename(to)
1249 * - #stat
1250 * - #lstat
1251 * - #make_symlink(old)
1252 * - #truncate(length)
1253 * - #utime(atime, mtime)
1254 * - #basename(*args)
1255 * - #dirname
1256 * - #extname
1257 * - #expand_path(*args)
1258 * - #split
1259 *
1260 * === Directory methods
1261 *
1262 * These methods are a facade for Dir:
1263 * - Pathname.glob(*args)
1264 * - Pathname.getwd / Pathname.pwd
1265 * - #rmdir
1266 * - #entries
1267 * - #each_entry(&block)
1268 * - #mkdir(*args)
1269 * - #opendir(*args)
1270 *
1271 * === IO
1272 *
1273 * These methods are a facade for IO:
1274 * - #each_line(*args, &block)
1275 * - #read(*args)
1276 * - #binread(*args)
1277 * - #readlines(*args)
1278 * - #sysopen(*args)
1279 *
1280 * === Utilities
1281 *
1282 * These methods are a mixture of Find, FileUtils, and others:
1283 * - #find(&block)
1284 * - #mkpath
1285 * - #rmtree
1286 * - #unlink / #delete
1287 *
1288 *
1289 * == Method documentation
1290 *
1291 * As the above section shows, most of the methods in Pathname are facades.  The
1292 * documentation for these methods generally just says, for instance, "See
1293 * FileTest.writable?", as you should be familiar with the original method
1294 * anyway, and its documentation (e.g. through +ri+) will contain more
1295 * information.  In some cases, a brief description will follow.
1296 */
1297void
1298Init_pathname()
1299{
1300    id_at_path = rb_intern("@path");
1301    id_to_path = rb_intern("to_path");
1302
1303    rb_cPathname = rb_define_class("Pathname", rb_cObject);
1304    rb_define_method(rb_cPathname, "initialize", path_initialize, 1);
1305    rb_define_method(rb_cPathname, "freeze", path_freeze, 0);
1306    rb_define_method(rb_cPathname, "taint", path_taint, 0);
1307    rb_define_method(rb_cPathname, "untaint", path_untaint, 0);
1308    rb_define_method(rb_cPathname, "==", path_eq, 1);
1309    rb_define_method(rb_cPathname, "===", path_eq, 1);
1310    rb_define_method(rb_cPathname, "eql?", path_eq, 1);
1311    rb_define_method(rb_cPathname, "<=>", path_cmp, 1);
1312    rb_define_method(rb_cPathname, "hash", path_hash, 0);
1313    rb_define_method(rb_cPathname, "to_s", path_to_s, 0);
1314    rb_define_method(rb_cPathname, "to_path", path_to_s, 0);
1315    rb_define_method(rb_cPathname, "inspect", path_inspect, 0);
1316    rb_define_method(rb_cPathname, "sub", path_sub, -1);
1317    rb_define_method(rb_cPathname, "sub_ext", path_sub_ext, 1);
1318    rb_define_method(rb_cPathname, "realpath", path_realpath, -1);
1319    rb_define_method(rb_cPathname, "realdirpath", path_realdirpath, -1);
1320    rb_define_method(rb_cPathname, "each_line", path_each_line, -1);
1321    rb_define_method(rb_cPathname, "read", path_read, -1);
1322    rb_define_method(rb_cPathname, "binread", path_binread, -1);
1323    rb_define_method(rb_cPathname, "readlines", path_readlines, -1);
1324    rb_define_method(rb_cPathname, "sysopen", path_sysopen, -1);
1325    rb_define_method(rb_cPathname, "atime", path_atime, 0);
1326    rb_define_method(rb_cPathname, "ctime", path_ctime, 0);
1327    rb_define_method(rb_cPathname, "mtime", path_mtime, 0);
1328    rb_define_method(rb_cPathname, "chmod", path_chmod, 1);
1329    rb_define_method(rb_cPathname, "lchmod", path_lchmod, 1);
1330    rb_define_method(rb_cPathname, "chown", path_chown, 2);
1331    rb_define_method(rb_cPathname, "lchown", path_lchown, 2);
1332    rb_define_method(rb_cPathname, "fnmatch", path_fnmatch, -1);
1333    rb_define_method(rb_cPathname, "fnmatch?", path_fnmatch, -1);
1334    rb_define_method(rb_cPathname, "ftype", path_ftype, 0);
1335    rb_define_method(rb_cPathname, "make_link", path_make_link, 1);
1336    rb_define_method(rb_cPathname, "open", path_open, -1);
1337    rb_define_method(rb_cPathname, "readlink", path_readlink, 0);
1338    rb_define_method(rb_cPathname, "rename", path_rename, 1);
1339    rb_define_method(rb_cPathname, "stat", path_stat, 0);
1340    rb_define_method(rb_cPathname, "lstat", path_lstat, 0);
1341    rb_define_method(rb_cPathname, "make_symlink", path_make_symlink, 1);
1342    rb_define_method(rb_cPathname, "truncate", path_truncate, 1);
1343    rb_define_method(rb_cPathname, "utime", path_utime, 2);
1344    rb_define_method(rb_cPathname, "basename", path_basename, -1);
1345    rb_define_method(rb_cPathname, "dirname", path_dirname, 0);
1346    rb_define_method(rb_cPathname, "extname", path_extname, 0);
1347    rb_define_method(rb_cPathname, "expand_path", path_expand_path, -1);
1348    rb_define_method(rb_cPathname, "split", path_split, 0);
1349    rb_define_method(rb_cPathname, "blockdev?", path_blockdev_p, 0);
1350    rb_define_method(rb_cPathname, "chardev?", path_chardev_p, 0);
1351    rb_define_method(rb_cPathname, "executable?", path_executable_p, 0);
1352    rb_define_method(rb_cPathname, "executable_real?", path_executable_real_p, 0);
1353    rb_define_method(rb_cPathname, "exist?", path_exist_p, 0);
1354    rb_define_method(rb_cPathname, "grpowned?", path_grpowned_p, 0);
1355    rb_define_method(rb_cPathname, "directory?", path_directory_p, 0);
1356    rb_define_method(rb_cPathname, "file?", path_file_p, 0);
1357    rb_define_method(rb_cPathname, "pipe?", path_pipe_p, 0);
1358    rb_define_method(rb_cPathname, "socket?", path_socket_p, 0);
1359    rb_define_method(rb_cPathname, "owned?", path_owned_p, 0);
1360    rb_define_method(rb_cPathname, "readable?", path_readable_p, 0);
1361    rb_define_method(rb_cPathname, "world_readable?", path_world_readable_p, 0);
1362    rb_define_method(rb_cPathname, "readable_real?", path_readable_real_p, 0);
1363    rb_define_method(rb_cPathname, "setuid?", path_setuid_p, 0);
1364    rb_define_method(rb_cPathname, "setgid?", path_setgid_p, 0);
1365    rb_define_method(rb_cPathname, "size", path_size, 0);
1366    rb_define_method(rb_cPathname, "size?", path_size_p, 0);
1367    rb_define_method(rb_cPathname, "sticky?", path_sticky_p, 0);
1368    rb_define_method(rb_cPathname, "symlink?", path_symlink_p, 0);
1369    rb_define_method(rb_cPathname, "writable?", path_writable_p, 0);
1370    rb_define_method(rb_cPathname, "world_writable?", path_world_writable_p, 0);
1371    rb_define_method(rb_cPathname, "writable_real?", path_writable_real_p, 0);
1372    rb_define_method(rb_cPathname, "zero?", path_zero_p, 0);
1373    rb_define_singleton_method(rb_cPathname, "glob", path_s_glob, -1);
1374    rb_define_singleton_method(rb_cPathname, "getwd", path_s_getwd, 0);
1375    rb_define_singleton_method(rb_cPathname, "pwd", path_s_getwd, 0);
1376    rb_define_method(rb_cPathname, "entries", path_entries, 0);
1377    rb_define_method(rb_cPathname, "mkdir", path_mkdir, -1);
1378    rb_define_method(rb_cPathname, "rmdir", path_rmdir, 0);
1379    rb_define_method(rb_cPathname, "opendir", path_opendir, 0);
1380    rb_define_method(rb_cPathname, "each_entry", path_each_entry, 0);
1381    rb_define_method(rb_cPathname, "unlink", path_unlink, 0);
1382    rb_define_method(rb_cPathname, "delete", path_unlink, 0);
1383    rb_undef_method(rb_cPathname, "=~");
1384    rb_define_global_function("Pathname", path_f_pathname, 1);
1385}
1386