Release coccinelle-0.2.3rc3
[bpt/coccinelle.git] / main.ml
diff --git a/main.ml b/main.ml
index 35589cc..3e4deab 100644 (file)
--- a/main.ml
+++ b/main.ml
@@ -1,4 +1,6 @@
 (*
+ * Copyright 2010, INRIA, University of Copenhagen
+ * Julia Lawall, Rene Rydhof Hansen, Gilles Muller, Nicolas Palix
  * Copyright 2005-2009, Ecole des Mines de Nantes, University of Copenhagen
  * Yoann Padioleau, Julia Lawall, Rene Rydhof Hansen, Henrik Stuart, Gilles Muller, Nicolas Palix
  * This file is part of Coccinelle.
@@ -35,10 +37,13 @@ module FC = Flag_cocci
 let cocci_file = ref ""
 
 let output_file = ref ""
-let inplace_modif = ref false  (* but keeps a .cocci_orig *)
+let inplace_modif = ref false  (* but keeps nothing *)
+let backup_suffix =
+  ref (None : string option) (* suffix for backup if one is desired *)
 let outplace_modif = ref false (* generates a .cocci_res  *)
 let preprocess = ref false     (* run the C preprocessor before cocci *)
 let compat_mode = ref false
+let ignore_unknown_opt = ref false
 
 (* somehow obsolete now *)
 let dir = ref false
@@ -72,8 +77,54 @@ let mod_distrib   = ref false
 (*****************************************************************************)
 
 (* pair of  (list of flags to set true, list of flags to set false *)
+let very_quiet_profile = (
+  [
+  ],
+  [
+    (* FC.show_diff;   just leave this as it is *)
+
+    Common.print_to_stderr;
+    Flag.show_misc;
+    Flag.show_trying;
+    Flag.show_transinfo;
+
+    FC.show_c;
+    FC.show_cocci;
+    FC.show_flow;
+    FC.show_before_fixed_flow;
+    FC.show_ctl_tex;
+    FC.show_ctl_text;
+    FC.show_binding_in_out;
+
+    FC.verbose_cocci;
+
+    Flag_parsing_c.show_parsing_error;
+
+    Flag_parsing_c.verbose_lexing;
+    Flag_parsing_c.verbose_parsing;
+    Flag_parsing_c.verbose_type;
+    Flag_parsing_c.verbose_cfg;
+    Flag_parsing_c.verbose_unparsing;
+    Flag_parsing_c.verbose_visit;
+    Flag_parsing_c.verbose_cpp_ast;
+
+    Flag_matcher.verbose_matcher;
+    Flag_matcher.debug_engine;
+
+    Flag_parsing_c.debug_unparsing;
+
+    Flag_parsing_cocci.show_SP;
+    Flag_parsing_cocci.show_iso_failures;
+
+    Flag_ctl.verbose_ctl_engine;
+    Flag_ctl.verbose_match;
+
+
+  ])
+
 let quiet_profile = (
   [
+    Common.print_to_stderr
   ],
   [
     (* FC.show_diff;   just leave this as it is *)
@@ -120,6 +171,7 @@ let quiet_profile = (
 work properly *)
 let debug_profile = (
   [
+    Common.print_to_stderr;
     Flag.show_misc;
     Flag.show_transinfo;
 
@@ -162,6 +214,7 @@ let debug_profile = (
 let pad_profile = (
   [
     FC.show_diff;
+    Common.print_to_stderr;
   ],
   [
 
@@ -220,9 +273,11 @@ let short_options = [
 
   "-o", Arg.Set_string output_file,
   "   <file> the output file";
-  "-inplace", Arg.Set inplace_modif,
+  "-in_place", Arg.Set inplace_modif,
   "   do the modification on the file directly";
-  "-outplace", Arg.Set outplace_modif,
+  "-backup_suffix", Arg.String (function s -> backup_suffix := Some s),
+  "   suffix to use when making a backup for inplace";
+  "-out_place", Arg.Set outplace_modif,
   "   store modifications in a .cocci_res file";
 
   "-U", Arg.Int (fun n -> Flag_parsing_c.diff_lines := Some (i_to_s n)),
@@ -246,9 +301,13 @@ let short_options = [
   "-local_includes",
   Arg.Unit (function _ -> FC.include_options := FC.I_NORMAL_INCLUDES),
   "  causes local include files to be used";
+  "-ignore_unknown_options", Arg.Set ignore_unknown_opt,
+  "    For integration in a toolchain (must be set before the first unknown option)";
   "-include_headers", Arg.Set include_headers,
   "    process header files independently";
-  "-I",   Arg.String (function x -> FC.include_path := Some x),
+  "-I",   Arg.String (fun x ->
+                       FC.include_path:= x::!FC.include_path
+                    ),
   "  <dir> containing the header files (optional)";
 
   "-preprocess", Arg.Set preprocess,
@@ -263,7 +322,8 @@ let short_options = [
   "    works with -dir, use info generated by glimpseindex";
   "-use_google", Arg.String (function s -> Flag.scanner := Flag.Google s),
   "    find relevant files using google code search";
-  "-patch", Arg.String (function s -> Flag.patch := Some s),
+  "-patch",
+    Arg.String (function s -> Flag.patch := Some (Cocci.normalize_path s)),
   ("    <dir> path name with respect to which a patch should be created\n"^
    "    \"\" for a file in the current directory");
   "-kbuild_info", Arg.Set_string kbuild_info,
@@ -280,7 +340,7 @@ let short_options = [
     "  guess what";
 
   "-date",   Arg.Unit (fun () ->
-    pr2 "version: $Date: 2010/01/04 11:16:30 $";
+    pr2 "version: $Date: 2010/06/07 09:53:34 $";
     raise (Common.UnixExit 0)
     ),
   "   guess what";
@@ -361,7 +421,10 @@ let other_options = [
        Flag_ctl.graphical_trace := true; Flag_ctl.gt_without_label := true),
        "  remove graph label (requires option -graphical_trace)";
 
-    "-parse_error_msg", Arg.Set Flag_parsing_c.verbose_parsing, " ";
+    "-parse_error_msg", Arg.Set Flag_parsing_c.show_parsing_error, " ";
+    "-verbose_parsing",
+       Arg.Unit (fun _ -> Flag_parsing_c.verbose_parsing := true;
+        Flag_parsing_c.show_parsing_error := true), " ";
     "-type_error_msg",  Arg.Set Flag_parsing_c.verbose_type, " ";
     (* could also use Flag_parsing_c.options_verbose *)
   ];
@@ -405,6 +468,7 @@ let other_options = [
   [
     (* todo: other profile ? *)
     "-quiet",   Arg.Unit (fun () -> run_profile quiet_profile), " ";
+    "-very_quiet",   Arg.Unit (fun () -> run_profile very_quiet_profile), " ";
     "-debug",   Arg.Unit (fun () -> run_profile debug_profile), " ";
     "-pad",     Arg.Unit (fun () -> run_profile pad_profile),   " ";
 
@@ -506,7 +570,7 @@ let other_options = [
     "  spacing of + code follows the conventions of Linux";
     "-smpl_spacing", Arg.Unit Flag_parsing_c.set_smpl_spacing,
     "  spacing of + code follows the semantic patch";
-    "-D", Arg.String Flag_parsing_cocci.set_defined_virtual_rules,
+    "-D", Arg.String Flag.set_defined_virtual_rules,
     "  indicate that a virtual rule should be considered to be matched";
   ];
 
@@ -586,17 +650,48 @@ let all_options =
 let arg_align2 xs =
   Arg.align xs +> List.rev +> Common.drop 2 +> List.rev
 
+(*
+  Ignore unknown option
+
+  This simplifies the integration of Coccinelle in toolchain.  For
+  instance, spatch can then be used as a checker in the Linux build
+  system.
+
+*)
+let check_include_path () =
+  let opt = Array.get Sys.argv !Arg.current in
+  let is_include_re = Str.regexp "-I\\(.*\\)" in
+  if Str.string_match is_include_re opt 0 then
+    let path = Str.matched_group 1 opt in
+       FC.include_path:= path::!FC.include_path
+  else ()
+
+let rec arg_parse_no_fail l f msg =
+  try
+    check_include_path ();
+    Arg.parse_argv Sys.argv l f msg;
+  with
+    | Arg.Bad emsg ->
+       arg_parse_no_fail l f msg
+    | Arg.Help msg -> (* printf "%s" msg; exit 0; *)
+       raise Impossible  (* -help is specified in speclist *)
+
 (* copy paste of Arg.parse. Don't want the default -help msg *)
 let arg_parse2 l f msg =
   (try
     Arg.parse_argv Sys.argv l f msg;
   with
-  | Arg.Bad msg -> (* eprintf "%s" msg; exit 2; *)
-      let xs = Common.lines msg in
-      (* take only head, it's where the error msg is *)
-      pr2 (List.hd xs);
-      !short_usage_func();
-      raise (Common.UnixExit (2))
+  | Arg.Bad emsg -> (* eprintf "%s" msg; exit 2; *)
+      if not !ignore_unknown_opt then
+       begin
+         let xs = Common.lines emsg in
+           (* take only head, it's where the error msg is *)
+           pr2 (List.hd xs);
+           !short_usage_func();
+           raise (Common.UnixExit (2))
+       end
+      else
+       arg_parse_no_fail l f msg;
   | Arg.Help msg -> (* printf "%s" msg; exit 0; *)
       raise Impossible  (* -help is specified in speclist *)
   )
@@ -645,7 +740,7 @@ let glimpse_filter (coccifile, isofile) dir =
     None -> pr2 "no glimpse keyword inferred from snippet"; None
   | Some [query] ->
       (let suffixes = if !include_headers then ["c";"h"] else ["c"] in
-      pr2 ("glimpse request = " ^ query);
+      Printf.fprintf stderr "%s\n" ("glimpse request = " ^ query);
       let command = spf "glimpse -y -H %s -N -W -w '%s'" dir query in
       let (glimpse_res,stat) = Common.cmd_to_list_and_status command in
       match stat with
@@ -680,7 +775,7 @@ let main_action xs =
        if !dir && !Flag.patch =*= None
        then
          (match xs with
-         | [] -> Flag.patch := Some x
+         | [] -> Flag.patch := Some (Cocci.normalize_path x)
          | _ ->
              pr2
                ("warning: patch output can only be created when only one\n"^
@@ -815,7 +910,10 @@ let main_action xs =
            outopt +> Common.do_option (fun outfile ->
              if !inplace_modif
              then begin
-                Common.command2 ("cp "^infile^" "^infile^".cocci_orig");
+               (match !backup_suffix with
+                 Some backup_suffix ->
+                   Common.command2 ("cp "^infile^" "^infile^backup_suffix)
+               | None -> ());
                 Common.command2 ("cp "^outfile^" "^infile);
              end;
 
@@ -886,8 +984,8 @@ let main () =
             chosen
           end
         else List.hd !args in
-      if !FC.include_path =*= None
-      then FC.include_path := Some (Filename.concat chosen_dir "include"));
+      if !FC.include_path =*= []
+      then FC.include_path := [Filename.concat chosen_dir "include"]);
 
     args := List.rev !args;
 
@@ -918,11 +1016,26 @@ let main () =
     (* The test framework. Works with tests/ or .ok and .failed  *)
     (* --------------------------------------------------------- *)
     | [x] when !test_mode    ->
-        FC.include_path := Some "tests/include";
-        Testing.testone x !compare_with_expected
+       begin
+         let prefix = "tests/" in
+         let testfile = x ^ ".cocci" in
+           if Sys.file_exists (prefix ^ testfile) then
+             begin
+               FC.include_path := [prefix^"include"];
+               Testing.testone prefix x !compare_with_expected
+             end
+           else
+             if Sys.file_exists testfile then
+               begin
+                 FC.include_path := ["include"];
+                 Testing.testone "" x !compare_with_expected
+               end
+             else
+               pr2 (spf "ERROR: File %s does not exist" testfile)
+       end
 
     | []  when !test_all ->
-        FC.include_path := Some "tests/include";
+        FC.include_path := ["tests/include"];
         if !expected_score_file <> ""
         then Testing.testall ~expected_score_file:!expected_score_file ()
         else Testing.testall ()
@@ -981,8 +1094,10 @@ let main_with_better_error_report () =
     try
       main ()
     with
-    | Unix.Unix_error (_, "stat", filename) ->
-        pr2 (spf "ERROR: File %s does not exist" filename);
+    | Unix.Unix_error (e, "stat", filename) ->
+        pr2
+         (spf "ERROR: File %s does not exist: %s"
+            filename (Unix.error_message e));
         raise (UnixExit (-1))
 
 (*****************************************************************************)