#include "ruby.h" static VALUE rb_cPathname; static ID id_at_path, id_to_path; static VALUE get_strpath(VALUE obj) { VALUE strpath; strpath = rb_ivar_get(obj, id_at_path); if (TYPE(strpath) != T_STRING) rb_raise(rb_eTypeError, "unexpected @path"); return strpath; } static void set_strpath(VALUE obj, VALUE val) { rb_ivar_set(obj, id_at_path, val); } /* * Create a Pathname object from the given String (or String-like object). * If +path+ contains a NUL character (\0), an ArgumentError is raised. */ static VALUE path_initialize(VALUE self, VALUE arg) { VALUE str; if (TYPE(arg) == T_STRING) { str = arg; } else { str = rb_check_funcall(arg, id_to_path, 0, NULL); if (str == Qundef) str = arg; StringValue(str); } if (memchr(RSTRING_PTR(str), '\0', RSTRING_LEN(str))) rb_raise(rb_eArgError, "pathname contains null byte"); str = rb_obj_dup(str); set_strpath(self, str); OBJ_INFECT(self, str); return self; } static VALUE path_freeze(VALUE self) { rb_call_super(0, 0); rb_str_freeze(get_strpath(self)); return self; } static VALUE path_taint(VALUE self) { rb_call_super(0, 0); rb_obj_taint(get_strpath(self)); return self; } static VALUE path_untaint(VALUE self) { rb_call_super(0, 0); rb_obj_untaint(get_strpath(self)); return self; } /* * Compare this pathname with +other+. The comparison is string-based. * Be aware that two different paths (foo.txt and ./foo.txt) * can refer to the same file. */ static VALUE path_eq(VALUE self, VALUE other) { if (!rb_obj_is_kind_of(other, rb_cPathname)) return Qfalse; return rb_str_equal(get_strpath(self), get_strpath(other)); } /* * Provides for comparing pathnames, case-sensitively. */ static VALUE path_cmp(VALUE self, VALUE other) { VALUE s1, s2; char *p1, *p2; char *e1, *e2; if (!rb_obj_is_kind_of(other, rb_cPathname)) return Qnil; s1 = get_strpath(self); s2 = get_strpath(other); p1 = RSTRING_PTR(s1); p2 = RSTRING_PTR(s2); e1 = p1 + RSTRING_LEN(s1); e2 = p2 + RSTRING_LEN(s2); while (p1 < e1 && p2 < e2) { int c1, c2; c1 = (unsigned char)*p1++; c2 = (unsigned char)*p2++; if (c1 == '/') c1 = '\0'; if (c2 == '/') c2 = '\0'; if (c1 != c2) { if (c1 < c2) return INT2FIX(-1); else return INT2FIX(1); } } if (p1 < e1) return INT2FIX(1); if (p2 < e2) return INT2FIX(-1); return INT2FIX(0); } void Init_pathname() { id_at_path = rb_intern("@path"); id_to_path = rb_intern("to_path"); rb_cPathname = rb_define_class("Pathname", rb_cObject); rb_define_method(rb_cPathname, "initialize", path_initialize, 1); rb_define_method(rb_cPathname, "freeze", path_freeze, 0); rb_define_method(rb_cPathname, "taint", path_taint, 0); rb_define_method(rb_cPathname, "untaint", path_untaint, 0); rb_define_method(rb_cPathname, "==", path_eq, 1); rb_define_method(rb_cPathname, "===", path_eq, 1); rb_define_method(rb_cPathname, "eql?", path_eq, 1); rb_define_method(rb_cPathname, "<=>", path_cmp, 1); }