Coccinelle release 1.0.0-rc4
[bpt/coccinelle.git] / main.ml
diff --git a/main.ml b/main.ml
index 35589cc..e56383c 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)),
@@ -237,18 +292,25 @@ let short_options = [
   "-macro_file_builtins", Arg.Set_string Config.std_h,
   " <file> (default=" ^ !Config.std_h ^ ")";
 
+  "-recursive_includes",
+  Arg.Unit (function _ -> FC.include_options := FC.I_REALLY_ALL_INCLUDES),
+  "  causes all available include files, both those included in the C file(s) and those included in header files, to be used";
   "-all_includes",
   Arg.Unit (function _ -> FC.include_options := FC.I_ALL_INCLUDES),
-  "  causes all available include files to be used";
+  "  causes all available include files included in the C file(s) to be used";
   "-no_includes",
   Arg.Unit (function _ -> FC.include_options := FC.I_NO_INCLUDES),
   "  causes not even local include files to be used";
   "-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 +325,10 @@ 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),
+  "-use_idutils", Arg.Unit (function s -> Flag.scanner := Flag.IdUtils),
+  "    find relevant files using id-utils";
+  "-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 +345,7 @@ let short_options = [
     "  guess what";
 
   "-date",   Arg.Unit (fun () ->
-    pr2 "version: $Date: 2010/01/04 11:16:30 $";
+    pr2 "version: $Date: 2011/06/23 11:11:16 $";
     raise (Common.UnixExit 0)
     ),
   "   guess what";
@@ -361,7 +426,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 +473,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),   " ";
 
@@ -427,6 +496,11 @@ let other_options = [
     "   disable limit on max depth of iso application";
     "-track_iso", Arg.Set Flag.track_iso_usage,
     "   gather information about isomorphism usage";
+    "-disable_iso",
+    Arg.String
+    (fun s -> Flag_parsing_cocci.disabled_isos :=
+      s :: !Flag_parsing_cocci.disabled_isos),
+    "   disable a specific isomorphism";
     "-profile_iso",
     Arg.Unit
     (function () ->
@@ -506,8 +580,10 @@ 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";
+    "-c++", Arg.Set Flag.c_plus_plus,
+    "  make a small attempt to parse C++ files"
   ];
 
   "misc options",
@@ -538,6 +614,11 @@ let other_options = [
   [
     "-use_cache", Arg.Set Flag_parsing_c.use_cache,
     "   use .ast_raw pre-parsed cached C file";
+    "-cache_prefix",
+    Arg.String (function s ->
+      Flag_parsing_c.cache_prefix := Some s;
+      Flag_parsing_c.use_cache := true),
+    "   directory of cached ASTs, sets -use_cache";
     (* could use Flag_parsing_c.options_pad instead *)
   ];
 
@@ -586,17 +667,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 *)
   )
@@ -622,272 +734,320 @@ let _ = long_usage_func := long_usage
 (* Helpers *)
 (*****************************************************************************)
 
-let adjust_stdin cfile k =
-  if !dir
-  then k()
-  else
-    let newin =
-      try
-        let (dir, base, ext) = Common.dbe_of_filename cfile in
-        let varfile = Common.filename_of_dbe (dir, base, "var") in
-        if ext =$= "c" && Common.lfile_exists varfile
-        then Some varfile
-        else None
-      with Invalid_argument("Filename.chop_extension") -> None
-    in
-    Common.redirect_stdin_opt newin k
+(* for fresh identifier information *)
+let adjust_stdin cfiles k =
+  match cfiles with
+    [] -> failwith "not possible"
+  | cfile::_ ->
+      let newin =
+       try
+          let (dir, base, ext) = Common.dbe_of_filename cfile in
+          let varfile = Common.filename_of_dbe (dir, base, "var") in
+          if ext =$= "c" && Common.lfile_exists varfile
+          then Some varfile
+          else None
+       with Invalid_argument("Filename.chop_extension") -> None in
+      Common.redirect_stdin_opt newin k
 
 let glimpse_filter (coccifile, isofile) dir =
   let (_metavars,astcocci,_free_var_lists,_negated_positions,
-       _used_after_lists,_positions_lists,_,query) =
+       _used_after_lists,_positions_lists,(_,query,_)) =
     Cocci.sp_of_file coccifile (Some isofile) in
   match query with
-    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);
-      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
-       Unix.WEXITED(0) | Unix.WEXITED(1) ->
-         Some
-           (glimpse_res +>
-            List.filter
-              (fun file -> List.mem (Common.filesuffix file) suffixes))
-      |        _ -> None (* error, eg due to pattern too big *))
-  | _ -> failwith "not possible"
+    None -> pr2 "no inferred glimpse keywords"; None
+  | Some queries ->
+      let suffixes = if !include_headers then ["c";"h"] else ["c"] in
+      let rec loop = function
+         [] -> None (* error, eg due to pattern too big *)
+       | query::queries ->
+           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
+             Unix.WEXITED(0) | Unix.WEXITED(1) ->
+      Printf.fprintf stderr "got files\n"; flush stderr;
+               Some
+                 (glimpse_res +>
+                  List.filter
+                    (fun file -> List.mem (Common.filesuffix file) suffixes))
+           |   _ -> loop queries (* error, eg due to pattern too big *) in
+      loop queries
+
+let idutils_filter (coccifile, isofile) dir =
+  let (_metavars,astcocci,_free_var_lists,_negated_positions,
+       _used_after_lists,_positions_lists,(_,_,query)) =
+    Cocci.sp_of_file coccifile (Some isofile) in
+  match query with
+    None -> pr2 "no inferred idutils keywords"; None
+  | Some query ->
+      let suffixes = if !include_headers then ["c";"h"] else ["c"] in
+      let files = Id_utils.interpret dir query in
+      Printf.fprintf stderr "got files\n"; flush stderr;
+      Some
+       (files +>
+        List.filter (fun file -> List.mem (Common.filesuffix file) suffixes))
 
 (*****************************************************************************)
 (* Main action *)
 (*****************************************************************************)
 
-let main_action xs =
-  match xs with
-  | x::xs ->
+let get_files path =
+  let ch =
+    Common.cmd_to_list (* same as "true, "", _" case *)
+      (if !include_headers
+                         (* FIXME : Could we remove xs ?
+                            -use_glimpse requires a singleton.
+                            This is checked some lines before.
+                            then ("find "^(join " " (x::xs))^" -name \"*.[ch]\"")
+                            else ("find "^(join " " (x::xs))^" -name \"*.c\"")
+                         *)
+      then ("find "^ path ^" -name \"*.[ch]\"")
+      else ("find "^ path ^" -name \"*.c\"")) in
+  let cpp =
+    if !Flag.c_plus_plus
+    then Common.cmd_to_list ("find "^ path ^" -name \"*.cpp\"")
+    else [] in
+  cpp @ ch
 
+let main_action xs =
+  Iteration.base_file_list := xs;
+  let rec toploop = function
+      [] -> raise Impossible
+    | x::xs ->
       (* a more general solution would be to use
        * Common.files_of_dir_or_files (x::xs)
        * as some elements in xs may also be directories, or individual
        * files.
        *)
-      if Common.is_directory x
-      then dir := true;
-
-      adjust_stdin x (fun () ->
-        if !cocci_file =$= ""
-        then failwith "I need a cocci file,  use -sp_file <file>";
-
-       if !dir && !Flag.patch =*= None
-       then
-         (match xs with
-         | [] -> Flag.patch := Some x
-         | _ ->
-             pr2
-               ("warning: patch output can only be created when only one\n"^
-                   "directory is specified or when the -patch flag is used")
-          );
-
-        let infiles =
-          Common.profile_code "Main.infiles computation" (fun () ->
-           match !dir, !kbuild_info, !Flag.scanner = Flag.Glimpse with
+         dir := (Common.is_directory x);
+       
+          if !cocci_file =$= ""
+          then failwith "I need a cocci file,  use -sp_file <file>";
+         
+         if !dir && !Flag.patch =*= None
+         then
+           (match xs with
+           | [] -> Flag.patch := Some (Cocci.normalize_path x)
+           | _ ->
+               pr2
+                 ("warning: patch output can only be created when only one\n"^
+                  "directory is specified or when the -patch flag is used")
+                 );
+         Flag.dir := x;
+         
+          let infiles =
+            Common.profile_code "Main.infiles computation" (fun () ->
+             match !dir, !kbuild_info, !Flag.scanner with
             (* glimpse *)
-            | false, _, true ->
-                failwith "-use_glimpse works only with -dir"
-            | true, s, true when s <> "" ->
-                failwith "-use_glimpse does not work with -kbuild"
-            | true, "", true ->
-                if not (null xs)
-                then failwith "-use_glimpse can accept only one dir";
-
-               Flag.dir := x;
-                let files =
-                 match glimpse_filter (!cocci_file, !Config.std_iso) x with
-                 None ->
-                   Common.cmd_to_list (* same as "true, "", _" case *)
-                     (if !include_headers
-                         (* FIXME : Could we remove xs ?
-                            -use_glimpse requires a singleton.
-                            This is checked some lines before.
-                            then ("find "^(join " " (x::xs))^" -name \"*.[ch]\"")
-                            else ("find "^(join " " (x::xs))^" -name \"*.c\""))
-                         *)
-                     then ("find "^ x ^" -name \"*.[ch]\"")
-                       else ("find "^ x ^" -name \"*.c\""))
-                 | Some files -> files in
-                files +> List.map (fun x -> [x])
+              | false, _, (Flag.Glimpse|Flag.IdUtils) -> [x::xs]
+              | true, s, (Flag.Glimpse|Flag.IdUtils) when s <> "" ->
+                  failwith
+                   "-use_glimpse or -id_utils does not work with -kbuild"
+              | true, "", Flag.Glimpse ->
+                  (if not (null xs)
+                  then failwith "-use_glimpse can accept only one dir");
+                 
+                  let files =
+                   match glimpse_filter (!cocci_file, !Config.std_iso) x with
+                     None -> get_files x
+                   | Some files -> files in
+                  files +> List.map (fun x -> [x])
+              | true, "", Flag.IdUtils ->
+                  (if not (null xs)
+                  then failwith "-id_utils can accept only one dir");
+                 
+                  let files =
+                   match idutils_filter (!cocci_file, !Config.std_iso) x with
+                     None -> get_files x
+                   | Some files -> files in
+                  files +> List.map (fun x -> [x])
                   (* normal *)
-           | false, _, _ -> [x::xs]
-           | true, "", _ ->
-               Common.cmd_to_list
-                 (if !include_headers
-                 then ("find "^(join " " (x::xs))^" -name \"*.[ch]\"")
-                   else ("find "^(join " " (x::xs))^" -name \"*.c\""))
-               +> List.map (fun x -> [x])
-
+             | false, _, _ -> [x::xs]
+             | true, "", _ ->
+                 get_files (join " " (x::xs)) +> List.map (fun x -> [x])
+                   
             (* kbuild *)
-           | true, kbuild_info_file,_ ->
-               let dirs =
-                  Common.cmd_to_list ("find "^(join " " (x::xs))^" -type d")
-                in
-               let info = Kbuild.parse_kbuild_info kbuild_info_file in
-               let groups = Kbuild.files_in_dirs dirs info in
-
-               groups +> List.map (function Kbuild.Group xs -> xs)
-         )
-        in
-
-       let infiles =
-         match (!distrib_index,!distrib_max) with
-         (None,None) -> infiles
-         | (Some index,Some max) ->
-             (if index >= max
-             then
-               failwith "index starts at 0, and so must be less than max");
-             if !mod_distrib
-             then
-               let rec loop ct = function
-                 [] -> []
-                 | x::xs ->
-                     if (ct mod max) =|= index
-                     then x::(loop (ct+1) xs)
-                     else loop (ct+1) xs in
-               loop 0 infiles
-             else
-               begin
-                 let all_files = List.length infiles in
-                 let regions = (all_files + (max - 1)) / max in
-                 let this_min = index * regions in
-                 let this_max = (index+1) * regions in
+             | true, kbuild_info_file,_ ->
+                 let dirs =
+                    Common.cmd_to_list ("find "^(join " " (x::xs))^" -type d")
+                  in
+                 let info = Kbuild.parse_kbuild_info kbuild_info_file in
+                 let groups = Kbuild.files_in_dirs dirs info in
+                 
+                 groups +> List.map (function Kbuild.Group xs -> xs)
+                   )
+          in
+
+         let infiles =
+           match (!distrib_index,!distrib_max) with
+             (None,None) -> infiles
+           | (Some index,Some max) ->
+               (if index >= max
+               then
+                 failwith "index starts at 0, and so must be less than max");
+               if !mod_distrib
+               then
                  let rec loop ct = function
-                   [] -> []
+                     [] -> []
                    | x::xs ->
-                       if this_min <= ct && ct < this_max
+                       if (ct mod max) =|= index
                        then x::(loop (ct+1) xs)
                        else loop (ct+1) xs in
                  loop 0 infiles
-               end
-         | _ -> failwith "inconsistent distribution information" in
-
-        let outfiles =
-          Common.profile_code "Main.outfiles computation" (fun () ->
-           let cocci_infos =
-             Cocci.pre_engine (!cocci_file, !Config.std_iso) in
-           let res =
-             infiles +> List.map (fun cfiles ->
-               pr2 ("HANDLING: " ^ (join " " cfiles));
-               Common.timeout_function_opt !FC.timeout (fun () ->
-                 Common.report_if_take_time 10 (join " " cfiles) (fun () ->
-                   (*let s = profile_diagnostic() in*)
-                    (* Unix.sleep 1; *)
-                    try
-                     let optfile =
-                       if !output_file <> "" && !compat_mode then
-                         Some !output_file
-                       else
-                         None
-                     in
-                       Common.redirect_stdout_opt optfile (fun () ->
+               else
+                 begin
+                   let all_files = List.length infiles in
+                   let regions = (all_files + (max - 1)) / max in
+                   let this_min = index * regions in
+                   let this_max = (index+1) * regions in
+                   let rec loop ct = function
+                       [] -> []
+                     | x::xs ->
+                         if this_min <= ct && ct < this_max
+                         then x::(loop (ct+1) xs)
+                         else loop (ct+1) xs in
+                   loop 0 infiles
+                 end
+           | _ -> failwith "inconsistent distribution information" in
+         
+          let (cocci_infos,outfiles) =
+            Common.profile_code "Main.outfiles computation" (fun () ->
+             let cocci_infos =
+               Cocci.pre_engine (!cocci_file, !Config.std_iso) in
+             let res =
+               infiles +> List.map (fun cfiles ->
+                 pr2 ("HANDLING: " ^ (join " " cfiles));
+                 Common.timeout_function_opt !FC.timeout (fun () ->
+                   Common.report_if_take_time 10 (join " " cfiles) (fun () ->
+                      try
+                       let optfile =
+                         if !output_file <> "" && !compat_mode then
+                           Some !output_file
+                         else
+                           None
+                       in
+                       adjust_stdin cfiles (fun () ->
+                         Common.redirect_stdout_opt optfile (fun () ->
                          (* this is the main call *)
-                         Cocci.full_engine cocci_infos cfiles
-                                                          )
-                   with
+                           Cocci.full_engine cocci_infos cfiles
+                       ))
+                     with
                      | Common.UnixExit x -> raise (Common.UnixExit x)
+                     | Pycocci.Pycocciexception ->
+                         raise Pycocci.Pycocciexception
                      | e ->
-                         (*pr2 "previous";
-                         pr2 s;
-                         pr2 "new";
-                         pr2(profile_diagnostic());*)
                          if !dir
                          then begin
                            pr2 ("EXN:" ^ Printexc.to_string e);
                            [] (* *)
                          end
                          else raise e))) in
-             Cocci.post_engine cocci_infos;
-             res
-          ) +> List.concat
-        in
-
-        Common.profile_code "Main.result analysis" (fun () ->
-         Ctlcocci_integration.print_bench();
-          let outfiles = Cocci.check_duplicate_modif outfiles in
-          outfiles +> List.iter (fun (infile, outopt) ->
-           outopt +> Common.do_option (fun outfile ->
-             if !inplace_modif
-             then begin
-                Common.command2 ("cp "^infile^" "^infile^".cocci_orig");
-                Common.command2 ("cp "^outfile^" "^infile);
-             end;
-
-             if !outplace_modif
-             then Common.command2 ("cp "^outfile^" "^infile^".cocci_res");
-
+             (cocci_infos,res)) in
+         let outfiles = List.concat outfiles in
+         (match Iteration.get_pending_instance() with
+           None ->
+             (x,xs,cocci_infos,outfiles)
+         | Some (files,virt_rules,virt_ids) ->
+             if outfiles = [] or outfiles = [] or not !FC.show_diff
+             then
+               begin
+                 Flag.defined_virtual_rules := virt_rules;
+                 Flag.defined_virtual_env := virt_ids;
+                 Common.erase_temp_files();
+                 Common.clear_pr2_once();
+                 toploop files
+               end
+             else
+               begin
+                 Common.pr2
+                   "Transformation not compatible with iteration. Aborting.";
+                 (x,xs,cocci_infos,outfiles)
+               end) in
+      let (x,xs,cocci_infos,outfiles) = toploop xs in
+
+      Cocci.post_engine cocci_infos;
+      Common.profile_code "Main.result analysis" (fun () ->
+       Ctlcocci_integration.print_bench();
+        let outfiles = Cocci.check_duplicate_modif outfiles in
+        outfiles +> List.iter (fun (infile, outopt) ->
+         outopt +> Common.do_option (fun outfile ->
+           if !inplace_modif
+           then begin
+             (match !backup_suffix with
+               Some backup_suffix ->
+                 Common.command2 ("cp "^infile^" "^infile^backup_suffix)
+             | None -> ());
+              Common.command2 ("cp "^outfile^" "^infile);
+           end;
+           
+           if !outplace_modif
+           then Common.command2 ("cp "^outfile^" "^infile^".cocci_res");
+           
              (* potential source of security pb if the /tmp/ file is
-               * a symlink, so simpler to not produce any regular file
-               * (files created by Common.new_temp_file are still ok)
-               * anymore in /tmp.
-               *)
+                * a symlink, so simpler to not produce any regular file
+                * (files created by Common.new_temp_file are still ok)
+                * anymore in /tmp.
+              *)
               (*
-               if !output_file =$= ""
-               then begin
-                  let tmpfile = "/tmp/"^Common.basename infile in
-                  pr2 (spf "One file modified. Result is here: %s" tmpfile);
-                  Common.command2 ("cp "^outfile^" "^tmpfile);
+                if !output_file =$= ""
+                then begin
+                 let tmpfile = "/tmp/"^Common.basename infile in
+                 pr2 (spf "One file modified. Result is here: %s" tmpfile);
+                 Common.command2 ("cp "^outfile^" "^tmpfile);
                 end
               *)
            ));
-          if !output_file <> "" && not !compat_mode then
-           (match outfiles with
-           | [infile, Some outfile] when infile =$= x && null xs ->
-                Common.command2 ("cp " ^outfile^ " " ^ !output_file);
-           | [infile, None] when infile =$= x && null xs ->
-                Common.command2 ("cp " ^infile^ " " ^ !output_file);
-           | _ ->
-                failwith
-                  ("-o can not be applied because there is multiple " ^
-                      "modified files");
-           );
-          if !compare_with_expected
-          then Testing.compare_with_expected outfiles))
-
-  | [] -> raise Impossible
-
-
+        if !output_file <> "" && not !compat_mode then
+         (match outfiles with
+         | [infile, Some outfile] when infile =$= x && null xs ->
+              Common.command2 ("cp " ^outfile^ " " ^ !output_file);
+         | [infile, None] when infile =$= x && null xs ->
+              Common.command2 ("cp " ^infile^ " " ^ !output_file);
+         | _ ->
+              failwith
+                ("-o can not be applied because there are multiple " ^
+                 "modified files");
+             );
+        if !compare_with_expected
+        then Testing.compare_with_expected outfiles)
+       
+       
 (*****************************************************************************)
 (* The coccinelle main entry point *)
 (*****************************************************************************)
 let main () =
   begin
     let arglist = Array.to_list Sys.argv in
-
+    
     if not (null (Common.inter_set arglist
-                    ["-cocci_file";"-sp_file";"-sp";"-test";"-testall";
+                   ["-cocci_file";"-sp_file";"-sp";"-test";"-testall";
                       "-test_okfailed";"-test_regression_okfailed"]))
     then run_profile quiet_profile;
-
+    
     let args = ref [] in
-
+    
     (* Gc.set {(Gc.get ()) with Gc.stack_limit = 1024 * 1024};*)
-
+    
     (* this call can set up many global flag variables via the cmd line *)
     arg_parse2 (Arg.align all_options) (fun x -> args := x::!args) usage_msg;
-
+    
     (* julia hack so that one can override directories specified on
-     * the command line. *)
+       * the command line. *)
     (if !dir
     then
       let chosen_dir =
-        if List.length !args > 1
-        then
-          begin
-            let chosen = List.hd !args in
-            pr2 ("ignoring all but the last specified directory: "^chosen);
-            args := [chosen];
-            chosen
-          end
-        else List.hd !args in
-      if !FC.include_path =*= None
-      then FC.include_path := Some (Filename.concat chosen_dir "include"));
+       if List.length !args > 1
+       then
+         begin
+           let chosen = List.hd !args in
+           Flag.dir := chosen;
+           pr2 ("ignoring all but the last specified directory: "^chosen);
+           args := [chosen];
+           chosen
+         end
+       else List.hd !args in
+      if !FC.include_path =*= []
+      then FC.include_path := [Filename.concat chosen_dir "include"]);
 
     args := List.rev !args;
 
@@ -918,11 +1078,29 @@ 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
+               (if !FC.include_path = []
+               then FC.include_path := [prefix^"include"]);
+               Testing.testone prefix x !compare_with_expected
+             end
+           else
+             if Sys.file_exists testfile then
+               begin
+                 (if !FC.include_path = []
+                 then 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";
+        (if !FC.include_path = []
+       then FC.include_path := ["tests/include"]);
         if !expected_score_file <> ""
         then Testing.testall ~expected_score_file:!expected_score_file ()
         else Testing.testall ()
@@ -930,11 +1108,11 @@ let main () =
     | [] when !test_regression_okfailed ->
         Testing.test_regression_okfailed ()
 
-    | x::xs when !test_okfailed ->
+    | ((x::xs) as cfiles) when !test_okfailed ->
         (* do its own timeout on FC.timeout internally *)
         FC.relax_include_path := true;
-       adjust_stdin x (fun () ->
-          Testing.test_okfailed !cocci_file (x::xs)
+       adjust_stdin cfiles (fun () ->
+          Testing.test_okfailed !cocci_file cfiles
         )
 
     (* --------------------------------------------------------- *)
@@ -945,7 +1123,7 @@ let main () =
         Common.do_action !action xs all_actions
 
     | [file] when !action =$= "-parse_cocci" ->
-        Testing.test_parse_cocci file
+       Testing.test_parse_cocci file
 
      (* I think this is used by some scripts in some Makefile for our
       * big-tests. So dont remove.
@@ -981,8 +1159,13 @@ 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))
+    | Parse_cocci.Bad_virt s ->
+       Common.pr2 (Printf.sprintf "virtual rule %s not supported" s);
         raise (UnixExit (-1))
 
 (*****************************************************************************)