null+****@clear*****
null+****@clear*****
2010年 6月 25日 (金) 14:21:36 JST
Nobuyoshi Nakada 2010-06-25 05:21:36 +0000 (Fri, 25 Jun 2010)
New Revision: 2f315b3e348de346d2dcbd6aba5c8fedda768a51
Log:
check argv boundary
Modified files:
lib/str.c
test/unit/command/test-option.rb
test/unit/lib/ruby/groonga-test-utils.rb
Modified: lib/str.c (+18 -11)
===================================================================
--- lib/str.c 2010-06-25 06:41:25 +0000 (0bae6e3)
+++ lib/str.c 2010-06-25 05:21:36 +0000 (badcb0f)
@@ -1735,9 +1735,9 @@ grn_str_tok(const char *str, size_t str_len, char delim, const char **tokbuf, in
return tok - tokbuf;
}
-inline static void
+inline static int
op_getopt_flag(int *flags, const grn_str_getopt_opt *o,
- int argc, char * const argv[], int *i, const char *optvalue)
+ int argc, char * const argv[], int i, const char *optvalue)
{
switch (o->op) {
case getopt_op_none:
@@ -1752,19 +1752,18 @@ op_getopt_flag(int *flags, const grn_str_getopt_opt *o,
*flags = o->flag;
break;
default:
- return;
+ return i;
}
if (o->arg) {
if (optvalue) {
*o->arg = (char *)optvalue;
+ } else if (++i < argc) {
+ *o->arg = argv[i];
} else {
- if (++(*i) < argc) {
- *o->arg = argv[*i];
- } else {
- /* TODO: error */
- }
+ return -1;
}
}
+ return i;
}
int
@@ -1787,8 +1786,12 @@ grn_str_getopt(int argc, char * const argv[], const grn_str_getopt_opt *opts,
for (o = opts; o->opt != '\0' || o->longopt != NULL; o++) {
if (o->longopt && strlen(o->longopt) == len &&
!memcmp(v, o->longopt, len)) {
- op_getopt_flag(flags, o, argc, argv, &i,
- (*eq == '\0' ? NULL : eq + 1));
+ i = op_getopt_flag(flags, o, argc, argv, i,
+ (*eq == '\0' ? NULL : eq + 1));
+ if (i < 0) {
+ fprintf(stderr, "%s: option '--%s' needs argument.\n", argv[0], o->longopt);
+ return -1;
+ }
found = 1;
break;
}
@@ -1800,7 +1803,11 @@ grn_str_getopt(int argc, char * const argv[], const grn_str_getopt_opt *opts,
found = 0;
for (o = opts; o->opt != '\0' || o->longopt != NULL; o++) {
if (o->opt && *p == o->opt) {
- op_getopt_flag(flags, o, argc, argv, &i, NULL);
+ i = op_getopt_flag(flags, o, argc, argv, i, NULL);
+ if (i < 0) {
+ fprintf(stderr, "%s: option '-%c' needs argument.\n", argv[0], *p);
+ return -1;
+ }
found = 1;
break;
}
Modified: test/unit/command/test-option.rb (+14 -3)
===================================================================
--- test/unit/command/test-option.rb 2010-06-25 06:41:25 +0000 (9127be8)
+++ test/unit/command/test-option.rb 2010-06-25 05:21:36 +0000 (8f9edd2)
@@ -42,9 +42,20 @@ class OptionTest < Test::Unit::TestCase
assert_path_not_exist(pid_file)
end
- def test_help_option
- usage = run_groonga("--help")
+ def test_help
+ assert_run_groonga(/\AUsage: groonga \[options\.\.\.\] \[dest\]$/,
+ "",
+ [CONFIG_ENV, "--help"])
assert_predicate($?, :success?)
- assert_match(/\AUsage: groonga \[options\.\.\.\] \[dest\]$/, usage)
+ end
+
+ def test_mandatory_argument_missing
+ usage = 'Usage: groonga \[options\.\.\.\] \[dest\]$'
+ %w[-e -l -a -p -i -t
+ --admin-html-path --protocol --log-path
+ --query-log-path --pid-file --config-path].each do |option|
+ status = assert_run_groonga("", /: option '#{option}' needs argument\.$/, option)
+ assert_not_predicate(status, :success?)
+ end
end
end
Modified: test/unit/lib/ruby/groonga-test-utils.rb (+86 -0)
===================================================================
--- test/unit/lib/ruby/groonga-test-utils.rb 2010-06-25 06:41:25 +0000 (e3d6375)
+++ test/unit/lib/ruby/groonga-test-utils.rb 2010-06-25 05:21:36 +0000 (8897529)
@@ -137,4 +137,90 @@ module GroongaTestUtils
string.force_encoding("UTF-8") if string.respond_to?(:force_encoding)
string
end
+
+ LANG_ENVS = %w"LANG LC_ALL LC_CTYPE"
+
+ def invoke_groonga(args, stdin_data="", capture_stdout=false, capture_stderr=false, opt={})
+ @groonga ||= guess_groonga_path
+ args = [args] unless args.kind_of?(Array)
+ begin
+ in_child, in_parent = IO.pipe
+ out_parent, out_child = IO.pipe if capture_stdout
+ err_parent, err_child = IO.pipe if capture_stderr
+ pid = fork do
+ c = "C"
+ LANG_ENVS.each {|lc| ENV[lc] = c}
+ case args.first
+ when Hash
+ ENV.update(args.shift)
+ end
+ STDIN.reopen(in_child)
+ in_parent.close
+ if capture_stdout
+ STDOUT.reopen(out_child)
+ out_parent.close
+ end
+ if capture_stderr
+ STDERR.reopen(err_child)
+ err_parent.close
+ end
+ Process.setrlimit(Process::RLIMIT_CORE, 0) rescue nil
+ exec(@groonga, *args)
+ end
+ in_child.close
+ out_child.close if capture_stdout
+ err_child.close if capture_stderr
+ th_stdout = Thread.new { out_parent.read } if capture_stdout
+ th_stderr = Thread.new { err_parent.read } if capture_stderr
+ in_parent.write stdin_data.to_str
+ in_parent.close
+ if (!capture_stdout || th_stdout.join(10)) && (!capture_stderr || th_stderr.join(10))
+ stdout = th_stdout.value if capture_stdout
+ stderr = th_stderr.value if capture_stderr
+ else
+ raise Timeout::Error
+ end
+ Process.wait pid
+ status = $?
+ ensure
+ [in_child, in_parent, out_child, out_parent, err_child, err_parent].each do |io|
+ io.close if io && !io.closed?
+ end
+ [th_stdout, th_stderr].each do |th|
+ (th.kill; th.join) if th
+ end
+ end
+ return stdout, stderr, status
+ end
+
+ def assert_run_groonga(test_stdout, test_stderr, args, *rest)
+ argnum = rest.size + 3
+ opt = (Hash === rest.last ? rest.pop : {})
+ message = (rest.pop if String === rest.last)
+ if String === rest.last
+ stdin = rest.pop
+ else
+ stdin = opt.delete(:stdin) || ""
+ end
+ unless rest.empty?
+ raise ArgumentError, "wrong number of arguments (#{argnum} for 3)"
+ end
+ stdout, stderr, status = invoke_groonga(args, stdin, true, true, opt)
+ assert_not_predicate(status, :signaled?)
+ if block_given?
+ yield(stdout, stderr)
+ else
+ if test_stderr.is_a?(Regexp)
+ assert_match(test_stderr, stderr, message)
+ else
+ assert_equal(test_stderr, stderr, message)
+ end
+ if test_stdout.is_a?(Regexp)
+ assert_match(test_stdout, stdout, message)
+ else
+ assert_equal(test_stdout, stdout, message)
+ end
+ status
+ end
+ end
end