Release coccinelle-0.2.2-rc1
[bpt/coccinelle.git] / cocci.ml
index 089297f..3ab1f4f 100644 (file)
--- a/cocci.ml
+++ b/cocci.ml
@@ -148,9 +148,72 @@ let show_or_not_cocci2 coccifile isofile =
 let show_or_not_cocci a b =
   Common.profile_code "show_xxx" (fun () -> show_or_not_cocci2 a b)
 
+(* ---------------------------------------------------------------------- *)
 (* the output *)
 
-let show_or_not_diff2 cfile outfile show_only_minus =
+let fix_sgrep_diffs l =
+  let l =
+    List.filter (function s -> (s =~ "^\\+\\+\\+") || not (s =~ "^\\+")) l in
+  let l = List.rev l in
+  (* adjust second number for + code *)
+  let rec loop1 n = function
+      [] -> []
+    | s::ss ->
+       if s =~ "^-" && not(s =~ "^---")
+       then s :: loop1 (n+1) ss
+       else if s =~ "^@@"
+       then
+         (match Str.split (Str.regexp " ") s with
+           bef::min::pl::aft ->
+             (match Str.split (Str.regexp ",") pl with
+               [n1;n2] ->
+                 let n2 = int_of_string n2 in
+                 (Printf.sprintf "%s %s %s,%d %s" bef min n1 (n2-n)
+                    (String.concat " " aft))
+                 :: loop1 0 ss
+             | _ -> failwith "bad + line information")
+         | _ -> failwith "bad @@ information")
+       else s :: loop1 n ss in
+  let rec loop2 n = function
+      [] -> []
+    | s::ss ->
+       if s =~ "^---"
+       then s :: loop2 0 ss
+       else if s =~ "^@@"
+       then
+         (match Str.split (Str.regexp " ") s with
+           bef::min::pl::aft ->
+             (match (Str.split (Str.regexp ",") min,
+                     Str.split (Str.regexp ",") pl) with
+               ([_;m2],[n1;n2]) ->
+                 let n1 =
+                   int_of_string
+                     (String.sub n1 1 ((String.length n1)-1)) in
+                 let m2 = int_of_string m2 in
+                 let n2 = int_of_string n2 in
+                 (Printf.sprintf "%s %s +%d,%d %s" bef min (n1-n) n2
+                    (String.concat " " aft))
+                 :: loop2 (n+(m2-n2)) ss
+             | _ -> failwith "bad -/+ line information")
+         | _ -> failwith "bad @@ information")
+       else s :: loop2 n ss in
+  loop2 0 (List.rev (loop1 0 l))
+
+let normalize_path file =
+  let fullpath =
+    if String.get file 0 = '/' then file else (Sys.getcwd()) ^ "/" ^ file in
+  let elements = Str.split_delim (Str.regexp "/") fullpath in
+  let rec loop prev = function
+      [] -> String.concat "/" (List.rev prev)
+    | "." :: rest -> loop prev rest
+    | ".." :: rest ->
+       (match prev with
+         x::xs -> loop xs rest
+       | _ -> failwith "bad path")
+    | x::rest -> loop (x::prev) rest in
+  loop [] elements
+
+let show_or_not_diff2 cfile outfile =
   if !Flag_cocci.show_diff then begin
     match Common.fst(Compare_c.compare_to_original cfile outfile) with
       Compare_c.Correct -> () (* diff only in spacing, etc *)
@@ -167,12 +230,27 @@ let show_or_not_diff2 cfile outfile show_only_minus =
          match (!Flag.patch,res) with
        (* create something that looks like the output of patch *)
            (Some prefix,minus_file::plus_file::rest) ->
+             let prefix =
+               let lp = String.length prefix in
+               if String.get prefix (lp-1) = '/'
+               then String.sub prefix 0 (lp-1)
+               else prefix in
              let drop_prefix file =
-               if prefix =$= ""
-               then "/"^file
-               else
+               let file = normalize_path file in
+               if Str.string_match (Str.regexp prefix) file 0
+               then
                  let lp = String.length prefix in
-                 String.sub file lp ((String.length file) - lp) in
+                 let lf = String.length file in
+                 if lp < lf
+                 then String.sub file lp (lf - lp)
+                 else
+                   failwith
+                     (Printf.sprintf "prefix %s doesn't match file %s"
+                        prefix file)
+               else
+                 failwith
+                   (Printf.sprintf "prefix %s doesn't match file %s"
+                      prefix file) in
              let diff_line =
                match List.rev(Str.split (Str.regexp " ") line) with
                  new_file::old_file::cmdrev ->
@@ -188,7 +266,7 @@ let show_or_not_diff2 cfile outfile show_only_minus =
                | _ -> failwith "bad command" in
              let (minus_line,plus_line) =
                if !Flag.sgrep_mode2
-               then (minus_file,plus_file)
+               then (minus_file,"+++ /tmp/nothing")
                else
                  match (Str.split (Str.regexp "[ \t]") minus_file,
                         Str.split (Str.regexp "[ \t]") plus_file) with
@@ -204,13 +282,11 @@ let show_or_not_diff2 cfile outfile show_only_minus =
                           (String.concat ":" l1) (String.concat ":" l2)) in
              diff_line::minus_line::plus_line::rest
          | _ -> res in
-       xs +> List.iter (fun s ->
-         if s =~ "^\\+" && show_only_minus
-         then ()
-         else pr s)
+       let xs = if !Flag.sgrep_mode2 then fix_sgrep_diffs xs else xs in
+       xs +> List.iter pr
   end
-let show_or_not_diff a b c  =
-  Common.profile_code "show_xxx" (fun () -> show_or_not_diff2 a b c)
+let show_or_not_diff a b =
+  Common.profile_code "show_xxx" (fun () -> show_or_not_diff2 a b)
 
 
 (* the derived input *)
@@ -460,11 +536,23 @@ let sp_contain_typed_metavar rules =
  * For the moment we base in part our heuristic on the name of the file, e.g.
  * serio.c is related we think to #include <linux/serio.h>
  *)
-
-let interpret_include_path _ =
-  match !Flag_cocci.include_path with
-    None -> "include"
-  | Some x -> x
+let rec search_include_path searchlist relpath =
+  match searchlist with
+      []       -> Some relpath
+    | hd::tail ->
+       let file = Filename.concat hd relpath in
+       if Sys.file_exists file then
+         Some file
+       else
+         search_include_path tail relpath
+
+let interpret_include_path relpath =
+  let searchlist =
+    match !Flag_cocci.include_path with
+       [] -> ["include"]
+      | x -> List.rev x
+  in
+    search_include_path searchlist relpath
 
 let (includes_to_parse:
        (Common.filename * Parse_c.program2) list ->
@@ -484,23 +572,24 @@ let (includes_to_parse:
                 {Ast_c.i_include = ((x,ii)); i_rel_pos = info_h_pos;})  ->
            (match x with
             | Ast_c.Local xs ->
-               let f = Filename.concat dir (Common.join "/" xs) in
+               let relpath = Common.join "/" xs in
+               let f = Filename.concat dir (relpath) in
              (* for our tests, all the files are flat in the current dir *)
                if not (Sys.file_exists f) && !Flag_cocci.relax_include_path
                then
                  let attempt2 = Filename.concat dir (Common.last xs) in
                  if not (Sys.file_exists f) && all_includes
-                 then Some (Filename.concat (interpret_include_path())
-                               (Common.join "/" xs))
+                 then
+                   interpret_include_path relpath
                  else Some attempt2
                else Some f
 
             | Ast_c.NonLocal xs ->
+               let relpath = Common.join "/" xs in
                if all_includes ||
                Common.fileprefix (Common.last xs) =$= Common.fileprefix file
                then
-                  Some (Filename.concat (interpret_include_path())
-                          (Common.join "/" xs))
+                 interpret_include_path relpath
                else None
             | Ast_c.Weird _ -> None
                  )
@@ -754,16 +843,15 @@ let python_code =
     local_python_code ^
     "cocci = Cocci()\n"
 
-let make_init rulenb lang code =
+let make_init rulenb lang deps code =
   let mv = [] in
-  let deps = Ast_cocci.NoDep in
-    {
-      scr_ast_rule = (lang, mv, code);
-      language = lang;
-      scr_dependencies = deps;
-      scr_ruleid = rulenb;
-      script_code = (if lang = "python" then python_code else "") ^code
-    }
+  {
+  scr_ast_rule = (lang, mv, code);
+  language = lang;
+  scr_dependencies = deps;
+  scr_ruleid = rulenb;
+  script_code = (if lang = "python" then python_code else "") ^code
+}
 
 (* --------------------------------------------------------------------- *)
 let prepare_cocci ctls free_var_lists negated_pos_lists
@@ -798,12 +886,11 @@ let prepare_cocci ctls free_var_lists negated_pos_lists
             script_code = code;
           }
           in ScriptRuleCocciInfo r
-      | Ast_cocci.InitialScriptRule (lang,code) ->
-         let r = make_init rulenb lang code in
+      | Ast_cocci.InitialScriptRule (lang,deps,code) ->
+         let r = make_init rulenb lang deps code in
            InitialScriptRuleCocciInfo r
-      | Ast_cocci.FinalScriptRule (lang,code) ->
+      | Ast_cocci.FinalScriptRule (lang,deps,code) ->
          let mv = [] in
-         let deps = Ast_cocci.NoDep in
           let r =
           {
             scr_ast_rule = (lang, mv, code);
@@ -925,10 +1012,6 @@ let rebuild_info_c_and_headers ccs isexp =
 
 
 
-
-
-
-
 let prepare_c files choose_includes : file_info list =
   let cprograms = List.map cprogram_of_file_cached files in
   let includes = includes_to_parse (zip files cprograms) choose_includes in
@@ -937,7 +1020,8 @@ let prepare_c files choose_includes : file_info list =
   let all =
     (includes +> List.map (fun hpath -> Right hpath))
     ++
-    ((zip files cprograms) +> List.map (fun (file, asts) -> Left (file, asts)))
+    ((zip files cprograms) +>
+     List.map (fun (file, asts) -> Left (file, asts)))
   in
 
   let env = ref !TAC.initial_env in
@@ -1105,7 +1189,9 @@ let apply_python_rule r cache newes e rules_that_have_matched
                show_or_not_binding "in" e;
                Pycocci.build_classes (List.map (function (x,y) -> x) ve);
                Pycocci.construct_variables mv ve;
-               let _ = Pycocci.pyrun_simplestring (local_python_code ^r.script_code) in
+               let _ =
+                 Pycocci.pyrun_simplestring
+                   (local_python_code ^r.script_code) in
                relevant_bindings :: cache
              end in
          if !Pycocci.inc_match
@@ -1473,13 +1559,13 @@ let initial_final_bigloop2 ty rebuild r =
 
       adjust_pp_with_indent (fun () ->
        Format.force_newline();
-       Pretty_print_cocci.unparse(rebuild r.scr_ast_rule));
+       Pretty_print_cocci.unparse(rebuild r.scr_ast_rule r.scr_dependencies));
     end;
 
   match r.language with
     "python" ->
       (* include_match makes no sense in an initial or final rule, although
-        er have no way to prevent it *)
+        we have no way to prevent it *)
       let _ = apply_python_rule r [] [] [] [] (ref []) in
       ()
   | _ ->
@@ -1541,10 +1627,16 @@ let pre_engine2 (coccifile, isofile) =
             InitialScriptRuleCocciInfo(r) ->
               (if List.mem r.language languages
                then failwith ("double initializer found for "^r.language));
-              initial_final_bigloop "initial"
-                (function(x,_,y) -> Ast_cocci.InitialScriptRule(x,y))
-                r;
-              r.language::languages
+              if interpret_dependencies [] [] r.scr_dependencies
+              then
+                begin
+                  initial_final_bigloop "initial"
+                    (fun (x,_,y) -> fun deps ->
+                      Ast_cocci.InitialScriptRule(x,deps,y))
+                    r;
+                  r.language::languages
+                end
+              else languages
           | _ -> languages)
       [] cocci_infos in
 
@@ -1555,8 +1647,9 @@ let pre_engine2 (coccifile, isofile) =
   in
     List.iter (fun lgg ->
                 initial_final_bigloop "initial"
-                  (function(x,_,y) -> Ast_cocci.InitialScriptRule(x,y))
-                  (make_init (-1) lgg "");
+                  (fun (x,_,y) -> fun deps ->
+                    Ast_cocci.InitialScriptRule(x,deps,y))
+                  (make_init (-1) lgg Ast_cocci.NoDep "");
              )
       uninitialized_languages;
 
@@ -1614,8 +1707,7 @@ let full_engine2 (cocci_infos,toks) cfiles =
             (* and now unparse everything *)
             cfile_of_program (for_unparser c_or_h.asts) outfile;
 
-            let show_only_minus = !Flag.sgrep_mode2 in
-            show_or_not_diff c_or_h.fpath outfile show_only_minus;
+            show_or_not_diff c_or_h.fpath outfile;
 
             (c_or_h.fpath,
              if !Flag.sgrep_mode2 then None else Some outfile)
@@ -1636,7 +1728,7 @@ let post_engine2 (cocci_infos,_) =
              (if List.mem r.language languages
              then failwith ("double finalizer found for "^r.language));
              initial_final_bigloop "final"
-               (function(x,_,y) -> Ast_cocci.FinalScriptRule(x,y))
+               (fun (x,_,y) -> fun deps -> Ast_cocci.FinalScriptRule(x,deps,y))
                r;
              r.language::languages
          | _ -> languages)