summaryrefslogtreecommitdiff
path: root/pathname.c
diff options
context:
space:
mode:
Diffstat (limited to 'pathname.c')
-rw-r--r--pathname.c117
1 files changed, 117 insertions, 0 deletions
diff --git a/pathname.c b/pathname.c
new file mode 100644
index 0000000000..71c78d89e7
--- /dev/null
+++ b/pathname.c
@@ -0,0 +1,117 @@
+#include "ruby.h"
+
+static VALUE rb_cPathname;
+static ID id_at_path;
+static ID id_sub;
+
+static VALUE
+get_strpath(VALUE obj)
+{
+ VALUE strpath;
+ strpath = rb_ivar_get(obj, id_at_path);
+ if (!RB_TYPE_P(strpath, T_STRING))
+ rb_raise(rb_eTypeError, "unexpected @path");
+ return strpath;
+}
+
+/*
+ * Provides a case-sensitive comparison operator for pathnames.
+ *
+ * Pathname.new('/usr') <=> Pathname.new('/usr/bin')
+ * #=> -1
+ * Pathname.new('/usr/bin') <=> Pathname.new('/usr/bin')
+ * #=> 0
+ * Pathname.new('/usr/bin') <=> Pathname.new('/USR/BIN')
+ * #=> 1
+ *
+ * It will return +-1+, +0+ or +1+ depending on the value of the left argument
+ * relative to the right argument. Or it will return +nil+ if the arguments
+ * are not comparable.
+ */
+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);
+}
+
+/*
+ * Return a pathname which is substituted by String#sub.
+ *
+ * path1 = Pathname.new('/usr/bin/perl')
+ * path1.sub('perl', 'ruby')
+ * #=> #<Pathname:/usr/bin/ruby>
+ */
+static VALUE
+path_sub(int argc, VALUE *argv, VALUE self)
+{
+ VALUE str = get_strpath(self);
+
+ if (rb_block_given_p()) {
+ str = rb_block_call(str, id_sub, argc, argv, 0, 0);
+ }
+ else {
+ str = rb_funcallv(str, id_sub, argc, argv);
+ }
+ return rb_class_new_instance(1, &str, rb_obj_class(self));
+}
+
+#include "pathname_builtin.rbinc"
+
+static void init_ids(void);
+
+void
+Init_pathname(void)
+{
+#ifdef HAVE_RB_EXT_RACTOR_SAFE
+ rb_ext_ractor_safe(true);
+#endif
+
+ init_ids();
+ InitVM(pathname);
+}
+
+void
+InitVM_pathname(void)
+{
+ rb_cPathname = rb_define_class("Pathname", rb_cObject);
+ rb_define_method(rb_cPathname, "<=>", path_cmp, 1);
+ rb_define_method(rb_cPathname, "sub", path_sub, -1);
+
+ rb_provide("pathname.so");
+}
+
+void
+init_ids(void)
+{
+#undef rb_intern
+ id_at_path = rb_intern("@path");
+ id_sub = rb_intern("sub");
+}