[Groonga-commit] groonga/groonga [master] check argv boundary

Back to archive index

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




Groonga-commit メーリングリストの案内
Back to archive index