aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ChangeLog7
-rw-r--r--README.EXT4
-rw-r--r--README.EXT.ja6
-rw-r--r--include/ruby/encoding.h1
-rw-r--r--parse.y28
-rw-r--r--sprintf.c7
-rw-r--r--test/ruby/test_sprintf.rb10
7 files changed, 58 insertions, 5 deletions
diff --git a/ChangeLog b/ChangeLog
index ae83e26a75..3130d8554f 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,10 @@
+Wed Apr 11 22:31:19 2012 Nobuyoshi Nakada <nobu@ruby-lang.org>
+
+ * parse.y (rb_check_id_cstr): new function to check if ID is
+ registered with NUL-terminated C string.
+
+ * sprintf.c (rb_str_format): avoid inadvertent symbol creation.
+
Wed Apr 11 20:28:36 2012 Hiroshi Shirosaki <h.shirosaki@gmail.com>
* io.c (rb_io_eof): use eof() instead of io_fillbuf(). It's because
diff --git a/README.EXT b/README.EXT
index ad45255bca..8368a94708 100644
--- a/README.EXT
+++ b/README.EXT
@@ -452,12 +452,14 @@ argument by using
rb_to_id(VALUE symbol)
rb_check_id(volatile VALUE *name)
+ rb_check_id_cstr(const char *name, long len, rb_encoding *enc)
These functions try to convert the argument to a String if it was not
-a Symbol nor a String. The latter function stores the converted
+a Symbol nor a String. The second function stores the converted
result into *name, and returns 0 if the string is not a known symbol.
After this function returned a non-zero value, *name is always a
Symbol or a String, otherwise it is a String if the result is 0.
+The third function takes NUL-terminated C string, not Ruby VALUE.
You can convert C ID to Ruby Symbol by using
diff --git a/README.EXT.ja b/README.EXT.ja
index 6b0cb11757..9ce9c586a1 100644
--- a/README.EXT.ja
+++ b/README.EXT.ja
@@ -500,12 +500,14 @@ IDとは変数名,メソッド名を表す整数です.Rubyの中では
rb_to_id(VALUE symbol)
rb_check_id(volatile VALUE *name)
+ rb_check_id_cstr(const char *name, long len, rb_encoding *enc)
もし引数がシンボルでも文字列でもなければ、to_strメソッドで文
-字列に変換しようとします.後者の関数はその変換結果を*nameに保
+字列に変換しようとします.第二の関数はその変換結果を*nameに保
存し,その名前が既知のシンボルでない場合は0を返します.この関
数が0以外を返した場合は*nameは常にシンボルか文字列であり、0を
-返した場合は常に文字列です.
+返した場合は常に文字列です.第三の関数はRubyの文字列ではなく
+NUL終端されたCの文字列を使います.
2.2.3 CからRubyのメソッドを呼び出す
diff --git a/include/ruby/encoding.h b/include/ruby/encoding.h
index b4e66c1dc4..41640ec7e0 100644
--- a/include/ruby/encoding.h
+++ b/include/ruby/encoding.h
@@ -220,6 +220,7 @@ char *rb_enc_path_last_separator(const char *,const char *,rb_encoding*);
char *rb_enc_path_end(const char *,const char *,rb_encoding*);
const char *ruby_enc_find_basename(const char *name, long *baselen, long *alllen, rb_encoding *enc);
const char *ruby_enc_find_extname(const char *name, long *len, rb_encoding *enc);
+ID rb_check_id_cstr(const char *ptr, long len, rb_encoding *enc);
RUBY_EXTERN VALUE rb_cEncoding;
#define ENC_DUMMY_FLAG (1<<24)
diff --git a/parse.y b/parse.y
index eaee57e4ad..5d8cc08a74 100644
--- a/parse.y
+++ b/parse.y
@@ -10336,6 +10336,34 @@ rb_check_id(volatile VALUE *namep)
return (ID)0;
}
+ID
+rb_check_id_cstr(const char *ptr, long len, rb_encoding *enc)
+{
+ st_data_t id;
+ struct RString fake_str;
+ const VALUE name = (VALUE)&fake_str;
+ fake_str.basic.flags = T_STRING|RSTRING_NOEMBED;
+ fake_str.basic.klass = rb_cString;
+ fake_str.as.heap.len = len;
+ fake_str.as.heap.ptr = (char *)ptr;
+ fake_str.as.heap.aux.capa = len;
+ rb_enc_associate(name, enc);
+
+ sym_check_asciionly(name);
+
+ if (st_lookup(global_symbols.sym_id, (st_data_t)name, &id))
+ return (ID)id;
+
+ if (rb_is_attrset_name(name)) {
+ fake_str.as.heap.len = len - 1;
+ if (st_lookup(global_symbols.sym_id, (st_data_t)name, &id)) {
+ return rb_id_attrset((ID)id);
+ }
+ }
+
+ return (ID)0;
+}
+
int
rb_is_const_name(VALUE name)
{
diff --git a/sprintf.c b/sprintf.c
index b9e40f4b87..5927c252dd 100644
--- a/sprintf.c
+++ b/sprintf.c
@@ -588,8 +588,11 @@ rb_str_format(int argc, const VALUE *argv, VALUE fmt)
rb_enc_raise(enc, rb_eArgError, "named%.*s after <%s>",
len, start, rb_id2name(id));
}
- id = rb_intern3(start + 1, len - 2 /* without parenthesis */, enc);
- nextvalue = GETNAMEARG(ID2SYM(id), start, len, enc);
+ nextvalue = GETNAMEARG((id = rb_check_id_cstr(start + 1,
+ len - 2 /* without parenthesis */,
+ enc),
+ ID2SYM(id)),
+ start, len, enc);
if (nextvalue == Qundef) {
rb_enc_raise(enc, rb_eKeyError, "key%.*s not found", len, start);
}
diff --git a/test/ruby/test_sprintf.rb b/test/ruby/test_sprintf.rb
index ecbce36942..21329dcd66 100644
--- a/test/ruby/test_sprintf.rb
+++ b/test/ruby/test_sprintf.rb
@@ -331,6 +331,8 @@ class TestSprintf < Test::Unit::TestCase
assert_equal("named<key2> after unnumbered(2)", e.message)
e = assert_raise(ArgumentError) {sprintf("%<key><key2>s", :key => "value")}
assert_equal("named<key2> after <key>", e.message)
+ e = assert_raise(KeyError) {sprintf("%<key>s", {})}
+ assert_equal("key<key> not found", e.message)
end
def test_named_untyped_enc
@@ -349,6 +351,9 @@ class TestSprintf < Test::Unit::TestCase
e = assert_raise(ArgumentError) {sprintf("%<#{k}><key>s", k.to_sym => "value")}
assert_equal(enc, e.message.encoding)
assert_equal("named<key> after <#{k}>", e.message)
+ e = assert_raise(KeyError) {sprintf("%<#{k}>s", {})}
+ assert_equal(enc, e.message.encoding)
+ assert_equal("key<#{k}> not found", e.message)
end
end
@@ -361,6 +366,8 @@ class TestSprintf < Test::Unit::TestCase
e = assert_raise(ArgumentError) {sprintf("%<key>{key2}", :key => "value")}
assert_equal("named{key2} after <key>", e.message)
assert_equal("value{key2}", sprintf("%{key}{key2}", :key => "value"))
+ e = assert_raise(KeyError) {sprintf("%{key}", {})}
+ assert_equal("key{key} not found", e.message)
end
def test_named_typed_enc
@@ -379,6 +386,9 @@ class TestSprintf < Test::Unit::TestCase
e = assert_raise(ArgumentError) {sprintf("%<#{k}>{key}s", k.to_sym => "value")}
assert_equal(enc, e.message.encoding)
assert_equal("named{key} after <#{k}>", e.message)
+ e = assert_raise(KeyError) {sprintf("%{#{k}}", {})}
+ assert_equal(enc, e.message.encoding)
+ assert_equal("key{#{k}} not found", e.message)
end
end
end