Coccinelle release-1.0.0-rc11
[bpt/coccinelle.git] / cocci.ml
CommitLineData
f537ebc4 1(*
17ba0788
C
2 * Copyright 2012, INRIA
3 * Julia Lawall, Gilles Muller
4 * Copyright 2010-2011, INRIA, University of Copenhagen
f537ebc4
C
5 * Julia Lawall, Rene Rydhof Hansen, Gilles Muller, Nicolas Palix
6 * Copyright 2005-2009, Ecole des Mines de Nantes, University of Copenhagen
7 * Yoann Padioleau, Julia Lawall, Rene Rydhof Hansen, Henrik Stuart, Gilles Muller, Nicolas Palix
8 * This file is part of Coccinelle.
9 *
10 * Coccinelle is free software: you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation, according to version 2 of the License.
13 *
14 * Coccinelle is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with Coccinelle. If not, see <http://www.gnu.org/licenses/>.
21 *
22 * The authors reserve the right to distribute this or future versions of
23 * Coccinelle under other licenses.
24 *)
25
26
34e49164
C
27open Common
28
29module CCI = Ctlcocci_integration
30module TAC = Type_annoter_c
31
708f4980
C
32module Ast_to_flow = Control_flow_c_build
33
34e49164 34(*****************************************************************************)
ae4735db 35(* This file is a kind of driver. It gathers all the important functions
34e49164
C
36 * from coccinelle in one place. The different entities in coccinelle are:
37 * - files
38 * - astc
39 * - astcocci
40 * - flow (contain nodes)
41 * - ctl (contain rule_elems)
42 * This file contains functions to transform one in another.
43 *)
44(*****************************************************************************)
45
46(* --------------------------------------------------------------------- *)
47(* C related *)
48(* --------------------------------------------------------------------- *)
ca417fcf
C
49let cprogram_of_file saved_typedefs saved_macros file =
50 let (program2, _stat) =
51 Parse_c.parse_c_and_cpp_keep_typedefs
993936c0
C
52 (if !Flag_cocci.use_saved_typedefs then (Some saved_typedefs) else None)
53 (Some saved_macros) file in
ae4735db 54 program2
34e49164 55
ae4735db 56let cprogram_of_file_cached file =
ca417fcf 57 let ((program2,typedefs,macros), _stat) = Parse_c.parse_cache file in
485bce71 58 if !Flag_cocci.ifdef_to_if
ae4735db 59 then
ca417fcf
C
60 let p2 =
61 program2 +> Parse_c.with_program2 (fun asts ->
62 Cpp_ast_c.cpp_ifdef_statementize asts
63 ) in
64 (p2,typedefs,macros)
65 else (program2,typedefs,macros)
34e49164 66
ae4735db 67let cfile_of_program program2_with_ppmethod outf =
485bce71 68 Unparse_c.pp_program program2_with_ppmethod outf
34e49164
C
69
70(* for memoization, contains only one entry, the one for the SP *)
71let _hparse = Hashtbl.create 101
3a314143 72let _h_ocaml_init = Hashtbl.create 101
34e49164
C
73let _hctl = Hashtbl.create 101
74
75(* --------------------------------------------------------------------- *)
76(* Cocci related *)
77(* --------------------------------------------------------------------- *)
3a314143
C
78(* for a given pair (file,iso), only keep an instance for the most recent
79virtual rules and virtual_env *)
80
81let sp_of_file2 file iso =
82 let redo _ =
83 let new_code =
84 let (_,xs,_,_,_,_,_) as res = Parse_cocci.process file iso false in
85 (* if there is already a compiled ML code, do nothing and use that *)
86 try let _ = Hashtbl.find _h_ocaml_init (file,iso) in res
87 with Not_found ->
88 begin
89 Hashtbl.add _h_ocaml_init (file,iso) ();
90 match Prepare_ocamlcocci.prepare file xs with
8babbc8f
C
91 None -> res
92 | Some ocaml_script_file ->
93 (* compile file *)
3a314143
C
94 Prepare_ocamlcocci.load_file ocaml_script_file;
95 (if not !Common.save_tmp_files
8babbc8f 96 then Prepare_ocamlcocci.clean_file ocaml_script_file);
3a314143
C
97 res
98 end in
99 Hashtbl.add _hparse (file,iso)
100 (!Flag.defined_virtual_rules,!Flag.defined_virtual_env,new_code);
101 new_code in
102 try
103 let (rules,env,code) = Hashtbl.find _hparse (file,iso) in
104 if rules = !Flag.defined_virtual_rules && env = !Flag.defined_virtual_env
105 then code
106 else (Hashtbl.remove _hparse (file,iso); redo())
107 with Not_found -> redo()
108
ae4735db 109let sp_of_file file iso =
34e49164
C
110 Common.profile_code "parse cocci" (fun () -> sp_of_file2 file iso)
111
112
113(* --------------------------------------------------------------------- *)
114(* Flow related *)
115(* --------------------------------------------------------------------- *)
ae4735db 116let print_flow flow =
34e49164
C
117 Ograph_extended.print_ograph_mutable flow "/tmp/test.dot" true
118
119
120let ast_to_flow_with_error_messages2 x =
ae4735db 121 let flowopt =
34e49164 122 try Ast_to_flow.ast_to_control_flow x
ae4735db 123 with Ast_to_flow.Error x ->
34e49164
C
124 Ast_to_flow.report_error x;
125 None
126 in
ae4735db 127 flowopt +> do_option (fun flow ->
34e49164
C
128 (* This time even if there is a deadcode, we still have a
129 * flow graph, so I can try the transformation and hope the
ae4735db 130 * deadcode will not bother us.
34e49164
C
131 *)
132 try Ast_to_flow.deadcode_detection flow
ae4735db 133 with Ast_to_flow.Error (Ast_to_flow.DeadCode x) ->
34e49164
C
134 Ast_to_flow.report_error (Ast_to_flow.DeadCode x);
135 );
136 flowopt
ae4735db 137let ast_to_flow_with_error_messages a =
34e49164
C
138 Common.profile_code "flow" (fun () -> ast_to_flow_with_error_messages2 a)
139
140
141(* --------------------------------------------------------------------- *)
142(* Ctl related *)
143(* --------------------------------------------------------------------- *)
b1b2de81 144
978fd7e5 145let ctls_of_ast2 ast (ua,fua,fuas) pos =
34e49164 146 List.map2
978fd7e5 147 (function ast -> function (ua,(fua,(fuas,pos))) ->
34e49164
C
148 List.combine
149 (if !Flag_cocci.popl
951c7801 150 then Popl.popl ast
978fd7e5 151 else Asttoctl2.asttoctl ast (ua,fua,fuas) pos)
34e49164 152 (Asttomember.asttomember ast ua))
978fd7e5 153 ast (List.combine ua (List.combine fua (List.combine fuas pos)))
34e49164 154
17ba0788
C
155let ctls_of_ast ast ua pl =
156 Common.profile_code "asttoctl2" (fun () -> ctls_of_ast2 ast ua pl)
34e49164
C
157
158(*****************************************************************************)
159(* Some debugging functions *)
160(*****************************************************************************)
161
162(* the inputs *)
163
164let show_or_not_cfile2 cfile =
165 if !Flag_cocci.show_c then begin
166 Common.pr2_xxxxxxxxxxxxxxxxx ();
167 pr2 ("processing C file: " ^ cfile);
168 Common.pr2_xxxxxxxxxxxxxxxxx ();
169 Common.command2 ("cat " ^ cfile);
170 end
ae4735db 171let show_or_not_cfile a =
34e49164
C
172 Common.profile_code "show_xxx" (fun () -> show_or_not_cfile2 a)
173
174let show_or_not_cfiles cfiles = List.iter show_or_not_cfile cfiles
175
176
ae4735db 177let show_or_not_cocci2 coccifile isofile =
34e49164
C
178 if !Flag_cocci.show_cocci then begin
179 Common.pr2_xxxxxxxxxxxxxxxxx ();
180 pr2 ("processing semantic patch file: " ^ coccifile);
181 isofile +> (fun s -> pr2 ("with isos from: " ^ s));
182 Common.pr2_xxxxxxxxxxxxxxxxx ();
183 Common.command2 ("cat " ^ coccifile);
184 pr2 "";
185 end
ae4735db 186let show_or_not_cocci a b =
34e49164
C
187 Common.profile_code "show_xxx" (fun () -> show_or_not_cocci2 a b)
188
c3e37e97 189(* ---------------------------------------------------------------------- *)
34e49164
C
190(* the output *)
191
c3e37e97
C
192let fix_sgrep_diffs l =
193 let l =
194 List.filter (function s -> (s =~ "^\\+\\+\\+") || not (s =~ "^\\+")) l in
195 let l = List.rev l in
196 (* adjust second number for + code *)
197 let rec loop1 n = function
198 [] -> []
199 | s::ss ->
200 if s =~ "^-" && not(s =~ "^---")
201 then s :: loop1 (n+1) ss
202 else if s =~ "^@@"
203 then
204 (match Str.split (Str.regexp " ") s with
205 bef::min::pl::aft ->
aba5c457
C
206 let (n1,n2) =
207 match Str.split (Str.regexp ",") pl with
208 [n1;n2] -> (n1,n2)
209 | [n1] -> (n1,"1")
210 | _ -> failwith "bad + line information" in
211 let n2 = int_of_string n2 in
212 (Printf.sprintf "%s %s %s,%d %s" bef min n1 (n2-n)
213 (String.concat " " aft))
214 :: loop1 0 ss
c3e37e97
C
215 | _ -> failwith "bad @@ information")
216 else s :: loop1 n ss in
217 let rec loop2 n = function
218 [] -> []
219 | s::ss ->
220 if s =~ "^---"
221 then s :: loop2 0 ss
222 else if s =~ "^@@"
223 then
224 (match Str.split (Str.regexp " ") s with
225 bef::min::pl::aft ->
aba5c457
C
226 let (m2,n1,n2) =
227 match (Str.split (Str.regexp ",") min,
228 Str.split (Str.regexp ",") pl) with
229 ([_;m2],[n1;n2]) -> (m2,n1,n2)
230 | ([_],[n1;n2]) -> ("1",n1,n2)
231 | ([_;m2],[n1]) -> (m2,n1,"1")
232 | ([_],[n1]) -> ("1",n1,"1")
233 | _ -> failwith "bad -/+ line information" in
234 let n1 =
235 int_of_string (String.sub n1 1 ((String.length n1)-1)) in
236 let m2 = int_of_string m2 in
237 let n2 = int_of_string n2 in
238 (Printf.sprintf "%s %s +%d,%d %s" bef min (n1-n) n2
239 (String.concat " " aft))
240 :: loop2 (n+(m2-n2)) ss
c3e37e97
C
241 | _ -> failwith "bad @@ information")
242 else s :: loop2 n ss in
243 loop2 0 (List.rev (loop1 0 l))
244
245let normalize_path file =
246 let fullpath =
247 if String.get file 0 = '/' then file else (Sys.getcwd()) ^ "/" ^ file in
248 let elements = Str.split_delim (Str.regexp "/") fullpath in
249 let rec loop prev = function
250 [] -> String.concat "/" (List.rev prev)
251 | "." :: rest -> loop prev rest
252 | ".." :: rest ->
253 (match prev with
254 x::xs -> loop xs rest
255 | _ -> failwith "bad path")
256 | x::rest -> loop (x::prev) rest in
257 loop [] elements
258
993936c0
C
259let generated_patches = Hashtbl.create(100)
260
c3e37e97 261let show_or_not_diff2 cfile outfile =
34e49164 262 if !Flag_cocci.show_diff then begin
951c7801 263 match Common.fst(Compare_c.compare_to_original cfile outfile) with
34e49164
C
264 Compare_c.Correct -> () (* diff only in spacing, etc *)
265 | _ ->
266 (* may need --strip-trailing-cr under windows *)
267 pr2 "diff = ";
268
269 let line =
270 match !Flag_parsing_c.diff_lines with
271 | None -> "diff -u -p " ^ cfile ^ " " ^ outfile
272 | Some n -> "diff -U "^n^" -p "^cfile^" "^outfile in
993936c0
C
273 let res = Common.cmd_to_list line in
274 let res =
275 List.map
276 (function l ->
277 match Str.split (Str.regexp "[ \t]+") l with
278 "---"::file::date -> "--- "^file
279 | "+++"::file::date -> "+++ "^file
280 | _ -> l)
281 res in
34e49164 282 let xs =
34e49164
C
283 match (!Flag.patch,res) with
284 (* create something that looks like the output of patch *)
285 (Some prefix,minus_file::plus_file::rest) ->
c3e37e97
C
286 let prefix =
287 let lp = String.length prefix in
288 if String.get prefix (lp-1) = '/'
289 then String.sub prefix 0 (lp-1)
290 else prefix in
34e49164 291 let drop_prefix file =
c3e37e97
C
292 let file = normalize_path file in
293 if Str.string_match (Str.regexp prefix) file 0
294 then
b1b2de81 295 let lp = String.length prefix in
c3e37e97
C
296 let lf = String.length file in
297 if lp < lf
298 then String.sub file lp (lf - lp)
299 else
300 failwith
301 (Printf.sprintf "prefix %s doesn't match file %s"
302 prefix file)
303 else
304 failwith
305 (Printf.sprintf "prefix %s doesn't match file %s"
306 prefix file) in
34e49164
C
307 let diff_line =
308 match List.rev(Str.split (Str.regexp " ") line) with
309 new_file::old_file::cmdrev ->
e6509c05 310 let old_base_file = drop_prefix old_file in
34e49164
C
311 if !Flag.sgrep_mode2
312 then
313 String.concat " "
e6509c05
C
314 (List.rev
315 (("/tmp/nothing"^old_base_file)
316 :: old_file :: cmdrev))
34e49164 317 else
34e49164
C
318 String.concat " "
319 (List.rev
993936c0
C
320 (("b"^old_base_file)::("a"^old_base_file)::
321 cmdrev))
34e49164
C
322 | _ -> failwith "bad command" in
323 let (minus_line,plus_line) =
e6509c05
C
324 match (Str.split (Str.regexp "[ \t]") minus_file,
325 Str.split (Str.regexp "[ \t]") plus_file) with
326 ("---"::old_file::old_rest,"+++"::new_file::new_rest) ->
327 let old_base_file = drop_prefix old_file in
328 if !Flag.sgrep_mode2
329 then (minus_file,"+++ /tmp/nothing"^old_base_file)
330 else
34e49164
C
331 (String.concat " "
332 ("---"::("a"^old_base_file)::old_rest),
333 String.concat " "
334 ("+++"::("b"^old_base_file)::new_rest))
993936c0
C
335 | (l1,l2) ->
336 failwith
337 (Printf.sprintf "bad diff header lines: %s %s"
338 (String.concat ":" l1) (String.concat ":" l2)) in
34e49164 339 diff_line::minus_line::plus_line::rest
b1b2de81 340 | _ -> res in
c3e37e97 341 let xs = if !Flag.sgrep_mode2 then fix_sgrep_diffs xs else xs in
993936c0
C
342 let cfile = normalize_path cfile in
343 let patches =
344 try Hashtbl.find generated_patches cfile
345 with Not_found ->
346 let cell = ref [] in
347 Hashtbl.add generated_patches cfile cell;
348 cell in
349 if List.mem xs !patches
350 then ()
351 else
352 begin
353 patches := xs :: !patches;
354 xs +> List.iter pr
355 end
34e49164 356 end
c3e37e97
C
357let show_or_not_diff a b =
358 Common.profile_code "show_xxx" (fun () -> show_or_not_diff2 a b)
ae4735db
C
359
360
34e49164 361(* the derived input *)
ae4735db 362
34e49164
C
363let show_or_not_ctl_tex2 astcocci ctls =
364 if !Flag_cocci.show_ctl_tex then begin
65038c61
C
365 let ctls =
366 List.map
367 (List.map
368 (function ((Asttoctl2.NONDECL ctl | Asttoctl2.CODE ctl),x) ->
369 (ctl,x)))
370 ctls in
34e49164
C
371 Ctltotex.totex ("/tmp/__cocci_ctl.tex") astcocci ctls;
372 Common.command2 ("cd /tmp; latex __cocci_ctl.tex; " ^
373 "dvips __cocci_ctl.dvi -o __cocci_ctl.ps;" ^
374 "gv __cocci_ctl.ps &");
375 end
ae4735db 376let show_or_not_ctl_tex a b =
34e49164 377 Common.profile_code "show_xxx" (fun () -> show_or_not_ctl_tex2 a b)
ae4735db
C
378
379
34e49164
C
380let show_or_not_rule_name ast rulenb =
381 if !Flag_cocci.show_ctl_text or !Flag.show_trying or
faf9a90c 382 !Flag.show_transinfo or !Flag_cocci.show_binding_in_out
34e49164
C
383 then
384 begin
385 let name =
386 match ast with
faf9a90c 387 Ast_cocci.CocciRule (nm, (deps, drops, exists), x, _, _) -> nm
34e49164
C
388 | _ -> i_to_s rulenb in
389 Common.pr_xxxxxxxxxxxxxxxxx ();
390 pr (name ^ " = ");
391 Common.pr_xxxxxxxxxxxxxxxxx ()
392 end
393
394let show_or_not_scr_rule_name rulenb =
395 if !Flag_cocci.show_ctl_text or !Flag.show_trying or
faf9a90c 396 !Flag.show_transinfo or !Flag_cocci.show_binding_in_out
34e49164
C
397 then
398 begin
399 let name = i_to_s rulenb in
400 Common.pr_xxxxxxxxxxxxxxxxx ();
401 pr ("script rule " ^ name ^ " = ");
402 Common.pr_xxxxxxxxxxxxxxxxx ()
403 end
404
405let show_or_not_ctl_text2 ctl ast rulenb =
406 if !Flag_cocci.show_ctl_text then begin
ae4735db
C
407
408 adjust_pp_with_indent (fun () ->
34e49164
C
409 Format.force_newline();
410 Pretty_print_cocci.print_plus_flag := true;
411 Pretty_print_cocci.print_minus_flag := true;
412 Pretty_print_cocci.unparse ast;
413 );
ae4735db 414
34e49164 415 pr "CTL = ";
65038c61 416 let ((Asttoctl2.CODE ctl | Asttoctl2.NONDECL ctl),_) = ctl in
ae4735db 417 adjust_pp_with_indent (fun () ->
34e49164 418 Format.force_newline();
ae4735db 419 Pretty_print_engine.pp_ctlcocci
34e49164
C
420 !Flag_cocci.show_mcodekind_in_ctl !Flag_cocci.inline_let_ctl ctl;
421 );
422 pr "";
423 end
424let show_or_not_ctl_text a b c =
425 Common.profile_code "show_xxx" (fun () -> show_or_not_ctl_text2 a b c)
426
427
428
429(* running information *)
ae4735db
C
430let get_celem celem : string =
431 match celem with
432 Ast_c.Definition ({Ast_c.f_name = namefuncs;},_) ->
b1b2de81 433 Ast_c.str_of_name namefuncs
485bce71 434 | Ast_c.Declaration
ae4735db 435 (Ast_c.DeclList ([{Ast_c.v_namei = Some (name, _);}, _], _)) ->
b1b2de81 436 Ast_c.str_of_name name
485bce71 437 | _ -> ""
34e49164 438
ae4735db 439let show_or_not_celem2 prelude celem =
485bce71 440 let (tag,trying) =
ae4735db
C
441 (match celem with
442 | Ast_c.Definition ({Ast_c.f_name = namefuncs},_) ->
b1b2de81 443 let funcs = Ast_c.str_of_name namefuncs in
485bce71
C
444 Flag.current_element := funcs;
445 (" function: ",funcs)
34e49164 446 | Ast_c.Declaration
b1b2de81
C
447 (Ast_c.DeclList ([{Ast_c.v_namei = Some (name,_)}, _], _)) ->
448 let s = Ast_c.str_of_name name in
485bce71
C
449 Flag.current_element := s;
450 (" variable ",s);
451 | _ ->
452 Flag.current_element := "something_else";
453 (" ","something else");
454 ) in
455 if !Flag.show_trying then pr2 (prelude ^ tag ^ trying)
ae4735db
C
456
457let show_or_not_celem a b =
34e49164
C
458 Common.profile_code "show_xxx" (fun () -> show_or_not_celem2 a b)
459
460
ae4735db 461let show_or_not_trans_info2 trans_info =
708f4980
C
462 (* drop witness tree indices for printing *)
463 let trans_info =
464 List.map (function (index,trans_info) -> trans_info) trans_info in
faf9a90c 465 if !Flag.show_transinfo then begin
34e49164
C
466 if null trans_info then pr2 "transformation info is empty"
467 else begin
468 pr2 "transformation info returned:";
469 let trans_info =
470 List.sort (function (i1,_,_) -> function (i2,_,_) -> compare i1 i2)
ae4735db 471 trans_info
34e49164 472 in
ae4735db
C
473 indent_do (fun () ->
474 trans_info +> List.iter (fun (i, subst, re) ->
34e49164 475 pr2 ("transform state: " ^ (Common.i_to_s i));
ae4735db
C
476 indent_do (fun () ->
477 adjust_pp_with_indent_and_header "with rule_elem: " (fun () ->
34e49164
C
478 Pretty_print_cocci.print_plus_flag := true;
479 Pretty_print_cocci.print_minus_flag := true;
480 Pretty_print_cocci.rule_elem "" re;
481 );
ae4735db 482 adjust_pp_with_indent_and_header "with binding: " (fun () ->
34e49164
C
483 Pretty_print_engine.pp_binding subst;
484 );
485 )
486 );
487 )
488 end
489 end
ae4735db 490let show_or_not_trans_info a =
34e49164
C
491 Common.profile_code "show_xxx" (fun () -> show_or_not_trans_info2 a)
492
493
494
495let show_or_not_binding2 s binding =
496 if !Flag_cocci.show_binding_in_out then begin
ae4735db 497 adjust_pp_with_indent_and_header ("binding " ^ s ^ " = ") (fun () ->
34e49164
C
498 Pretty_print_engine.pp_binding binding
499 )
500 end
ae4735db 501let show_or_not_binding a b =
34e49164
C
502 Common.profile_code "show_xxx" (fun () -> show_or_not_binding2 a b)
503
504
505
506(*****************************************************************************)
507(* Some helper functions *)
508(*****************************************************************************)
509
ae4735db 510let worth_trying cfiles tokens =
34e49164
C
511 (* drop the following line for a list of list by rules. since we don't
512 allow multiple minirules, all the tokens within a rule should be in
513 a single CFG entity *)
90aeb998
C
514 match (!Flag_cocci.windows,tokens) with
515 (true,_) | (_,None) -> true
516 | (_,Some tokens) ->
34e49164 517 (* could also modify the code in get_constants.ml *)
90aeb998
C
518 let tokens = tokens +> List.map (fun s ->
519 match () with
520 | _ when s =~ "^[A-Za-z_][A-Za-z_0-9]*$" ->
521 "\\b" ^ s ^ "\\b"
34e49164 522
90aeb998
C
523 | _ when s =~ "^[A-Za-z_]" ->
524 "\\b" ^ s
34e49164 525
90aeb998
C
526 | _ when s =~ ".*[A-Za-z_]$" ->
527 s ^ "\\b"
528 | _ -> s
34e49164 529
90aeb998
C
530 ) in
531 let com = sprintf "egrep -q '(%s)' %s" (join "|" tokens) (join " " cfiles)
532 in
533 (match Sys.command com with
534 | 0 (* success *) -> true
535 | _ (* failure *) ->
536 (if !Flag.show_misc
537 then Printf.printf "grep failed: %s\n" com);
538 false (* no match, so not worth trying *))
539
540let check_macro_in_sp_and_adjust = function
541 None -> ()
542 | Some tokens ->
543 tokens +> List.iter (fun s ->
544 if Hashtbl.mem !Parse_c._defs s
545 then begin
546 if !Flag_cocci.verbose_cocci then begin
547 pr2 "warning: macro in semantic patch was in macro definitions";
548 pr2 ("disabling macro expansion for " ^ s);
549 end;
550 Hashtbl.remove !Parse_c._defs s
551 end)
34e49164
C
552
553
ae4735db 554let contain_loop gopt =
34e49164 555 match gopt with
ae4735db
C
556 | Some g ->
557 g#nodes#tolist +> List.exists (fun (xi, node) ->
34e49164
C
558 Control_flow_c.extract_is_loop node
559 )
560 | None -> true (* means nothing, if no g then will not model check *)
561
562
563
ae4735db 564let sp_contain_typed_metavar_z toplevel_list_list =
34e49164
C
565 let bind x y = x or y in
566 let option_default = false in
567 let mcode _ _ = option_default in
568 let donothing r k e = k e in
569
570 let expression r k e =
571 match Ast_cocci.unwrap e with
572 | Ast_cocci.MetaExpr (_,_,_,Some t,_,_) -> true
573 | Ast_cocci.MetaExpr (_,_,_,_,Ast_cocci.LocalID,_) -> true
ae4735db 574 | _ -> k e
34e49164
C
575 in
576
ae4735db 577 let combiner =
34e49164
C
578 Visitor_ast.combiner bind option_default
579 mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode
c491d8ee 580 donothing donothing donothing donothing donothing
34e49164 581 donothing expression donothing donothing donothing donothing donothing
ae4735db 582 donothing donothing donothing donothing donothing
34e49164 583 in
ae4735db 584 toplevel_list_list +>
34e49164
C
585 List.exists
586 (function (nm,_,rule) ->
587 (List.exists combiner.Visitor_ast.combiner_top_level rule))
34e49164
C
588
589let sp_contain_typed_metavar rules =
ae4735db 590 sp_contain_typed_metavar_z
34e49164
C
591 (List.map
592 (function x ->
593 match x with
faf9a90c 594 Ast_cocci.CocciRule (a,b,c,d,_) -> (a,b,c)
34e49164
C
595 | _ -> failwith "error in filter")
596 (List.filter
597 (function x ->
faf9a90c
C
598 match x with
599 Ast_cocci.CocciRule (a,b,c,d,Ast_cocci.Normal) -> true
600 | _ -> false)
34e49164
C
601 rules))
602
603
604
605(* finding among the #include the one that we need to parse
606 * because they may contain useful type definition or because
607 * we may have to modify them
ae4735db 608 *
34e49164 609 * For the moment we base in part our heuristic on the name of the file, e.g.
ae4735db 610 * serio.c is related we think to #include <linux/serio.h>
34e49164 611 *)
3a314143 612let include_table = Hashtbl.create(100)
c3e37e97
C
613
614let interpret_include_path relpath =
3a314143
C
615 let maxdepth = List.length relpath in
616 let unique_file_exists dir f =
617 let cmd =
618 Printf.sprintf "find %s -maxdepth %d -mindepth %d -path \"*/%s\""
619 dir maxdepth maxdepth f in
620 match Common.cmd_to_list cmd with
621 [x] -> Some x
622 | _ -> None in
623 let native_file_exists dir f =
624 let f = Filename.concat dir f in
625 if Sys.file_exists f
626 then Some f
627 else None in
628 let rec search_include_path exists searchlist relpath =
629 match searchlist with
630 [] -> None
631 | hd::tail ->
632 (match exists hd relpath with
633 Some x -> Some x
634 | None -> search_include_path exists tail relpath) in
635 let rec search_path exists searchlist = function
636 [] ->
637 let res = Common.concat "/" relpath in
638 Hashtbl.add include_table (searchlist,relpath) res;
639 Some res
640 | (hd::tail) as relpath1 ->
641 let relpath1 = Common.concat "/" relpath1 in
642 (match search_include_path exists searchlist relpath1 with
643 None -> search_path unique_file_exists searchlist tail
644 | Some f ->
645 Hashtbl.add include_table (searchlist,relpath) f;
646 Some f) in
c3e37e97
C
647 let searchlist =
648 match !Flag_cocci.include_path with
3a314143
C
649 [] -> ["include"]
650 | x -> List.rev x in
651 try Some(Hashtbl.find include_table (searchlist,relpath))
652 with Not_found ->
653 search_path native_file_exists searchlist relpath
b1b2de81 654
faf9a90c 655let (includes_to_parse:
ca417fcf 656 (Common.filename * Parse_c.extended_program2) list ->
faf9a90c
C
657 Flag_cocci.include_options -> 'a) = fun xs choose_includes ->
658 match choose_includes with
659 Flag_cocci.I_UNSPECIFIED -> failwith "not possible"
660 | Flag_cocci.I_NO_INCLUDES -> []
661 | x ->
1eddfd50
C
662 let all_includes =
663 List.mem x
664 [Flag_cocci.I_ALL_INCLUDES; Flag_cocci.I_REALLY_ALL_INCLUDES] in
ca417fcf 665 let xs = List.map (function (file,(cs,_,_)) -> (file,cs)) xs in
ae4735db 666 xs +> List.map (fun (file, cs) ->
faf9a90c 667 let dir = Common.dirname file in
ae4735db
C
668
669 cs +> Common.map_filter (fun (c,_info_item) ->
faf9a90c
C
670 match c with
671 | Ast_c.CppTop
672 (Ast_c.Include
ae4735db 673 {Ast_c.i_include = ((x,ii)); i_rel_pos = info_h_pos;}) ->
faf9a90c 674 (match x with
ae4735db 675 | Ast_c.Local xs ->
c3e37e97 676 let relpath = Common.join "/" xs in
3a314143 677 let f = Filename.concat dir relpath in
aba5c457
C
678 if (Sys.file_exists f) then
679 Some f
680 else
681 if !Flag_cocci.relax_include_path
34e49164 682 (* for our tests, all the files are flat in the current dir *)
c3e37e97 683 then
aba5c457
C
684 let attempt2 = Filename.concat dir (Common.last xs) in
685 if not (Sys.file_exists attempt2) && all_includes
686 then
3a314143 687 interpret_include_path xs
aba5c457
C
688 else Some attempt2
689 else
3a314143 690 if all_includes then interpret_include_path xs
aba5c457 691 else None
faf9a90c 692
ae4735db 693 | Ast_c.NonLocal xs ->
faf9a90c 694 if all_includes ||
b1b2de81 695 Common.fileprefix (Common.last xs) =$= Common.fileprefix file
ae4735db 696 then
3a314143 697 interpret_include_path xs
34e49164 698 else None
0708f913 699 | Ast_c.Weird _ -> None
34e49164 700 )
faf9a90c
C
701 | _ -> None))
702 +> List.concat
1eddfd50 703 +> (fun x -> (List.rev (Common.uniq (List.rev x)))) (*uniq keeps last*)
ae4735db 704
7f004419
C
705let rec interpret_dependencies local global = function
706 Ast_cocci.Dep s -> List.mem s local
34e49164
C
707 | Ast_cocci.AntiDep s ->
708 (if !Flag_ctl.steps != None
709 then failwith "steps and ! dependency incompatible");
7f004419
C
710 not (List.mem s local)
711 | Ast_cocci.EverDep s -> List.mem s global
34e49164
C
712 | Ast_cocci.NeverDep s ->
713 (if !Flag_ctl.steps != None
714 then failwith "steps and ! dependency incompatible");
7f004419 715 not (List.mem s global)
34e49164 716 | Ast_cocci.AndDep(s1,s2) ->
7f004419
C
717 (interpret_dependencies local global s1) &&
718 (interpret_dependencies local global s2)
34e49164 719 | Ast_cocci.OrDep(s1,s2) ->
7f004419
C
720 (interpret_dependencies local global s1) or
721 (interpret_dependencies local global s2)
34e49164 722 | Ast_cocci.NoDep -> true
7f004419 723 | Ast_cocci.FailDep -> false
ae4735db 724
7f004419 725let rec print_dependencies str local global dep =
1be43e12
C
726 if !Flag_cocci.show_dependencies
727 then
728 begin
729 pr2 str;
730 let seen = ref [] in
731 let rec loop = function
732 Ast_cocci.Dep s | Ast_cocci.AntiDep s ->
951c7801
C
733 if not (List.mem s !seen)
734 then
735 begin
7f004419 736 if List.mem s local
951c7801
C
737 then pr2 (s^" satisfied")
738 else pr2 (s^" not satisfied");
739 seen := s :: !seen
740 end
1be43e12 741 | Ast_cocci.EverDep s | Ast_cocci.NeverDep s ->
951c7801
C
742 if not (List.mem s !seen)
743 then
744 begin
7f004419 745 if List.mem s global
951c7801
C
746 then pr2 (s^" satisfied")
747 else pr2 (s^" not satisfied");
748 seen := s :: !seen
749 end
1be43e12
C
750 | Ast_cocci.AndDep(s1,s2) ->
751 loop s1;
752 loop s2
753 | Ast_cocci.OrDep(s1,s2) ->
754 loop s1;
755 loop s2
7f004419
C
756 | Ast_cocci.NoDep -> ()
757 | Ast_cocci.FailDep -> pr2 "False not satisfied" in
1be43e12
C
758 loop dep
759 end
951c7801 760
34e49164
C
761(* --------------------------------------------------------------------- *)
762(* #include relative position in the file *)
763(* --------------------------------------------------------------------- *)
ae4735db 764
34e49164 765(* compute the set of new prefixes
ae4735db 766 * on
34e49164
C
767 * "a/b/x"; (* in fact it is now a list of string so ["a";"b";"x"] *)
768 * "a/b/c/x";
769 * "a/x";
770 * "b/x";
ae4735db 771 * it would give for the first element
34e49164
C
772 * ""; "a"; "a/b"; "a/b/x"
773 * for the second
774 * "a/b/c/x"
ae4735db 775 *
34e49164
C
776 * update: if the include is inside a ifdef a put nothing. cf -test incl.
777 * this is because we dont want code added inside ifdef.
778 *)
779
ae4735db
C
780let compute_new_prefixes xs =
781 xs +> Common.map_withenv (fun already xs ->
34e49164 782 let subdirs_prefixes = Common.inits xs in
ae4735db 783 let new_first = subdirs_prefixes +> List.filter (fun x ->
34e49164
C
784 not (List.mem x already)
785 )
786 in
ae4735db 787 new_first,
34e49164
C
788 new_first @ already
789 ) []
790 +> fst
791
792
793(* does via side effect on the ref in the Include in Ast_c *)
794let rec update_include_rel_pos cs =
ae4735db
C
795 let only_include = cs +> Common.map_filter (fun c ->
796 match c with
485bce71
C
797 | Ast_c.CppTop (Ast_c.Include {Ast_c.i_include = ((x,_));
798 i_rel_pos = aref;
799 i_is_in_ifdef = inifdef}) ->
34e49164 800 (match x with
0708f913 801 | Ast_c.Weird _ -> None
ae4735db
C
802 | _ ->
803 if inifdef
34e49164
C
804 then None
805 else Some (x, aref)
806 )
807 | _ -> None
808 )
809 in
ae4735db
C
810 let (locals, nonlocals) =
811 only_include +> Common.partition_either (fun (c, aref) ->
34e49164
C
812 match c with
813 | Ast_c.Local x -> Left (x, aref)
814 | Ast_c.NonLocal x -> Right (x, aref)
0708f913 815 | Ast_c.Weird x -> raise Impossible
34e49164
C
816 ) in
817
818 update_rel_pos_bis locals;
819 update_rel_pos_bis nonlocals;
820 cs
ae4735db 821and update_rel_pos_bis xs =
34e49164
C
822 let xs' = List.map fst xs in
823 let the_first = compute_new_prefixes xs' in
824 let the_last = List.rev (compute_new_prefixes (List.rev xs')) in
825 let merged = Common.zip xs (Common.zip the_first the_last) in
ae4735db
C
826 merged +> List.iter (fun ((x, aref), (the_first, the_last)) ->
827 aref := Some
828 {
34e49164
C
829 Ast_c.first_of = the_first;
830 Ast_c.last_of = the_last;
831 }
832 )
34e49164
C
833
834
835(*****************************************************************************)
836(* All the information needed around the C elements and Cocci rules *)
837(*****************************************************************************)
838
ae4735db 839type toplevel_c_info = {
34e49164
C
840 ast_c: Ast_c.toplevel; (* contain refs so can be modified *)
841 tokens_c: Parser_c.token list;
842 fullstring: string;
843
844 flow: Control_flow_c.cflow option; (* it's the "fixed" flow *)
845 contain_loop: bool;
ae4735db 846
34e49164
C
847 env_typing_before: TAC.environment;
848 env_typing_after: TAC.environment;
849
850 was_modified: bool ref;
851
ca417fcf
C
852 all_typedefs: (string, Lexer_parser.identkind) Common.scoped_h_env;
853 all_macros: (string, Cpp_token_c.define_def) Hashtbl.t;
854
34e49164
C
855 (* id: int *)
856}
857
413ffc02
C
858type rule_info = {
859 rulename: string;
860 dependencies: Ast_cocci.dependency;
861 used_after: Ast_cocci.meta_name list;
862 ruleid: int;
863 was_matched: bool ref;
864}
865
34e49164 866type toplevel_cocci_info_script_rule = {
174d1640 867 scr_ast_rule:
aba5c457
C
868 string *
869 (Ast_cocci.script_meta_name * Ast_cocci.meta_name *
870 Ast_cocci.metavar) list *
413ffc02 871 Ast_cocci.meta_name list (*fresh vars*) *
174d1640 872 string;
34e49164 873 language: string;
34e49164 874 script_code: string;
413ffc02 875 scr_rule_info: rule_info;
34e49164
C
876}
877
878type toplevel_cocci_info_cocci_rule = {
65038c61 879 ctl: Asttoctl2.top_formula * (CCI.pred list list);
faf9a90c 880 metavars: Ast_cocci.metavar list;
34e49164
C
881 ast_rule: Ast_cocci.rule;
882 isexp: bool; (* true if + code is an exp, only for Flag.make_hrule *)
883
34e49164 884 (* There are also some hardcoded rule names in parse_cocci.ml:
ae4735db 885 * let reserved_names = ["all";"optional_storage";"optional_qualifier"]
34e49164
C
886 *)
887 dropped_isos: string list;
888 free_vars: Ast_cocci.meta_name list;
889 negated_pos_vars: Ast_cocci.meta_name list;
34e49164
C
890 positions: Ast_cocci.meta_name list;
891
faf9a90c 892 ruletype: Ast_cocci.ruletype;
34e49164 893
413ffc02 894 rule_info: rule_info;
34e49164
C
895}
896
ae4735db 897type toplevel_cocci_info =
34e49164 898 ScriptRuleCocciInfo of toplevel_cocci_info_script_rule
b1b2de81
C
899 | InitialScriptRuleCocciInfo of toplevel_cocci_info_script_rule
900 | FinalScriptRuleCocciInfo of toplevel_cocci_info_script_rule
34e49164
C
901 | CocciRuleCocciInfo of toplevel_cocci_info_cocci_rule
902
90aeb998 903type cocci_info = toplevel_cocci_info list * string list option (* tokens *)
b1b2de81 904
ae4735db
C
905type kind_file = Header | Source
906type file_info = {
34e49164
C
907 fname : string;
908 full_fname : string;
909 was_modified_once: bool ref;
910 asts: toplevel_c_info list;
911 fpath : string;
912 fkind : kind_file;
913}
914
ae4735db 915let g_contain_typedmetavar = ref false
34e49164
C
916
917
918let last_env_toplevel_c_info xs =
919 (Common.last xs).env_typing_after
920
ae4735db
C
921let concat_headers_and_c (ccs: file_info list)
922 : (toplevel_c_info * string) list =
923 (List.concat (ccs +> List.map (fun x ->
485bce71
C
924 x.asts +> List.map (fun x' ->
925 (x', x.fname)))))
34e49164 926
ae4735db
C
927let for_unparser xs =
928 xs +> List.map (fun x ->
485bce71 929 (x.ast_c, (x.fullstring, x.tokens_c)), Unparse_c.PPviastr
34e49164
C
930 )
931
485bce71 932let gen_pdf_graph () =
ae4735db 933 (Ctl_engine.get_graph_files ()) +> List.iter (fun outfile ->
485bce71
C
934 Printf.printf "Generation of %s%!" outfile;
935 let filename_stack = Ctl_engine.get_graph_comp_files outfile in
936 List.iter (fun filename ->
937 ignore (Unix.system ("dot " ^ filename ^ " -Tpdf -o " ^ filename ^ ".pdf;"))
938 ) filename_stack;
939 let (head,tail) = (List.hd filename_stack, List.tl filename_stack) in
940 ignore(Unix.system ("cp " ^ head ^ ".pdf " ^ outfile ^ ".pdf;"));
941 tail +> List.iter (fun filename ->
942 ignore(Unix.system ("mv " ^ outfile ^ ".pdf /tmp/tmp.pdf;"));
943 ignore(Unix.system ("pdftk " ^ filename ^ ".pdf /tmp/tmp.pdf cat output " ^ outfile ^ ".pdf"));
944 );
945 ignore(Unix.system ("rm /tmp/tmp.pdf;"));
946 List.iter (fun filename ->
947 ignore (Unix.system ("rm " ^ filename ^ " " ^ filename ^ ".pdf;"))
948 ) filename_stack;
949 Printf.printf " - Done\n")
950
ae4735db
C
951let local_python_code =
952 "from coccinelle import *\n"
953
954let python_code =
955 "import coccinelle\n"^
956 "import coccilib\n"^
957 "import coccilib.org\n"^
958 "import coccilib.report\n" ^
959 local_python_code ^
960 "cocci = Cocci()\n"
961
413ffc02 962let make_init lang code rule_info =
ae4735db 963 let mv = [] in
c3e37e97 964 {
413ffc02 965 scr_ast_rule = (lang, mv, [], code);
c3e37e97 966 language = lang;
413ffc02
C
967 script_code = (if lang = "python" then python_code else "") ^code;
968 scr_rule_info = rule_info;
c3e37e97 969}
485bce71 970
34e49164
C
971(* --------------------------------------------------------------------- *)
972let prepare_cocci ctls free_var_lists negated_pos_lists
951c7801 973 (ua,fua,fuas) positions_list metavars astcocci =
34e49164
C
974
975 let gathered = Common.index_list_1
978fd7e5
C
976 (zip (zip (zip (zip (zip (zip (zip (zip ctls metavars) astcocci)
977 free_var_lists)
978 negated_pos_lists) ua) fua) fuas) positions_list)
34e49164 979 in
ae4735db 980 gathered +> List.map
978fd7e5 981 (fun (((((((((ctl_toplevel_list,metavars),ast),free_var_list),
ae4735db
C
982 negated_pos_list),ua),fua),fuas),positions_list),rulenb) ->
983
413ffc02
C
984 let build_rule_info rulename deps =
985 {rulename = rulename;
986 dependencies = deps;
987 used_after = (List.hd ua) @ (List.hd fua);
988 ruleid = rulenb;
989 was_matched = ref false;} in
990
34e49164 991 let is_script_rule r =
b1b2de81
C
992 match r with
993 Ast_cocci.ScriptRule _
994 | Ast_cocci.InitialScriptRule _ | Ast_cocci.FinalScriptRule _ -> true
995 | _ -> false in
34e49164 996
b1b2de81 997 if not (List.length ctl_toplevel_list =|= 1) && not (is_script_rule ast)
34e49164
C
998 then failwith "not handling multiple minirules";
999
1000 match ast with
413ffc02 1001 Ast_cocci.ScriptRule (name,lang,deps,mv,script_vars,code) ->
ae4735db 1002 let r =
413ffc02
C
1003 {
1004 scr_ast_rule = (lang, mv, script_vars, code);
1005 language = lang;
1006 script_code = code;
1007 scr_rule_info = build_rule_info name deps;
1008 }
34e49164 1009 in ScriptRuleCocciInfo r
174d1640 1010 | Ast_cocci.InitialScriptRule (name,lang,deps,code) ->
413ffc02 1011 let r = make_init lang code (build_rule_info name deps) in
ae4735db 1012 InitialScriptRuleCocciInfo r
174d1640 1013 | Ast_cocci.FinalScriptRule (name,lang,deps,code) ->
b1b2de81 1014 let mv = [] in
b1b2de81 1015 let r =
413ffc02
C
1016 {
1017 scr_ast_rule = (lang, mv, [], code);
1018 language = lang;
1019 script_code = code;
1020 scr_rule_info = build_rule_info name deps;
1021 }
b1b2de81 1022 in FinalScriptRuleCocciInfo r
34e49164 1023 | Ast_cocci.CocciRule
faf9a90c 1024 (rulename,(dependencies,dropped_isos,z),restast,isexp,ruletype) ->
413ffc02
C
1025 CocciRuleCocciInfo (
1026 {
1027 ctl = List.hd ctl_toplevel_list;
1028 metavars = metavars;
1029 ast_rule = ast;
1030 isexp = List.hd isexp;
1031 dropped_isos = dropped_isos;
1032 free_vars = List.hd free_var_list;
1033 negated_pos_vars = List.hd negated_pos_list;
1034 positions = List.hd positions_list;
1035 ruletype = ruletype;
1036 rule_info = build_rule_info rulename dependencies;
1037 })
34e49164
C
1038 )
1039
34e49164
C
1040(* --------------------------------------------------------------------- *)
1041
ca417fcf 1042let build_info_program (cprogram,typedefs,macros) env =
ae4735db
C
1043
1044 let (cs, parseinfos) =
0708f913
C
1045 Common.unzip cprogram in
1046
ae4735db 1047 let alltoks =
0708f913 1048 parseinfos +> List.map (fun (s,toks) -> toks) +> List.flatten in
34e49164 1049
0708f913 1050 (* I use cs' but really annotate_xxx work by doing side effects on cs *)
ae4735db 1051 let cs' =
0708f913 1052 Comment_annotater_c.annotate_program alltoks cs in
993936c0 1053
ae4735db 1054 let cs_with_envs =
0708f913
C
1055 Type_annoter_c.annotate_program env (*!g_contain_typedmetavar*) cs'
1056 in
ae4735db 1057
0708f913 1058 zip cs_with_envs parseinfos +> List.map (fun ((c, (enva,envb)), parseinfo)->
34e49164
C
1059 let (fullstr, tokens) = parseinfo in
1060
ae4735db 1061 let flow =
951c7801 1062 ast_to_flow_with_error_messages c +>
ae4735db 1063 Common.map_option (fun flow ->
34e49164
C
1064 let flow = Ast_to_flow.annotate_loop_nodes flow in
1065
1066 (* remove the fake nodes for julia *)
1067 let fixed_flow = CCI.fix_flow_ctl flow in
1068
1069 if !Flag_cocci.show_flow then print_flow fixed_flow;
1070 if !Flag_cocci.show_before_fixed_flow then print_flow flow;
1071
1072 fixed_flow
1073 )
1074 in
1075
1076 {
1077 ast_c = c; (* contain refs so can be modified *)
1078 tokens_c = tokens;
1079 fullstring = fullstr;
1080
1081 flow = flow;
1082
1083 contain_loop = contain_loop flow;
ae4735db 1084
34e49164
C
1085 env_typing_before = enva;
1086 env_typing_after = envb;
1087
1088 was_modified = ref false;
ca417fcf
C
1089
1090 all_typedefs = typedefs;
1091 all_macros = macros;
34e49164
C
1092 }
1093 )
1094
1095
1096
1097(* Optimisation. Try not unparse/reparse the whole file when have modifs *)
ae4735db 1098let rebuild_info_program cs file isexp =
34e49164
C
1099 cs +> List.map (fun c ->
1100 if !(c.was_modified)
1101 then
faf9a90c 1102 let file = Common.new_temp_file "cocci_small_output" ".c" in
ae4735db
C
1103 cfile_of_program
1104 [(c.ast_c, (c.fullstring, c.tokens_c)), Unparse_c.PPnormal]
faf9a90c 1105 file;
ae4735db 1106
faf9a90c 1107 (* Common.command2 ("cat " ^ file); *)
ca417fcf 1108 let cprogram = cprogram_of_file c.all_typedefs c.all_macros file in
faf9a90c 1109 let xs = build_info_program cprogram c.env_typing_before in
ae4735db 1110
faf9a90c
C
1111 (* TODO: assert env has not changed,
1112 * if yes then must also reparse what follows even if not modified.
1113 * Do that only if contain_typedmetavar of course, so good opti.
1114 *)
1115 (* Common.list_init xs *) (* get rid of the FinalDef *)
1116 xs
34e49164
C
1117 else [c]
1118 ) +> List.concat
1119
1120
1121let rebuild_info_c_and_headers ccs isexp =
ae4735db 1122 ccs +> List.iter (fun c_or_h ->
34e49164
C
1123 if c_or_h.asts +> List.exists (fun c -> !(c.was_modified))
1124 then c_or_h.was_modified_once := true;
1125 );
ae4735db 1126 ccs +> List.map (fun c_or_h ->
34e49164 1127 { c_or_h with
951c7801
C
1128 asts =
1129 rebuild_info_program c_or_h.asts c_or_h.full_fname isexp }
34e49164
C
1130 )
1131
1eddfd50
C
1132let rec prepare_h seen env hpath choose_includes : file_info list =
1133 if not (Common.lfile_exists hpath)
1134 then
1135 begin
3a314143 1136 pr2_once ("TYPE: header " ^ hpath ^ " not found");
1eddfd50
C
1137 []
1138 end
1139 else
1140 begin
1141 let h_cs = cprogram_of_file_cached hpath in
1142 let local_includes =
1143 if choose_includes =*= Flag_cocci.I_REALLY_ALL_INCLUDES
1144 then
1145 List.filter
1146 (function x -> not (List.mem x !seen))
1147 (includes_to_parse [(hpath,h_cs)] choose_includes)
1148 else [] in
1149 seen := local_includes @ !seen;
1150 let others =
1151 List.concat
1152 (List.map (function x -> prepare_h seen env x choose_includes)
1153 local_includes) in
1154 let info_h_cs = build_info_program h_cs !env in
1155 env :=
1156 if null info_h_cs
1157 then !env
1158 else last_env_toplevel_c_info info_h_cs;
1159 others@
1160 [{
1161 fname = Common.basename hpath;
1162 full_fname = hpath;
1163 asts = info_h_cs;
1164 was_modified_once = ref false;
1165 fpath = hpath;
1166 fkind = Header;
1167 }]
1168 end
34e49164 1169
ae4735db 1170let prepare_c files choose_includes : file_info list =
34e49164 1171 let cprograms = List.map cprogram_of_file_cached files in
faf9a90c 1172 let includes = includes_to_parse (zip files cprograms) choose_includes in
1eddfd50 1173 let seen = ref includes in
34e49164
C
1174
1175 (* todo?: may not be good to first have all the headers and then all the c *)
91eba41f 1176 let env = ref !TAC.initial_env in
34e49164 1177
1eddfd50
C
1178 let includes =
1179 includes +>
1180 List.map (function hpath -> prepare_h seen env hpath choose_includes) +>
1181 List.concat in
1182
1183 let cfiles =
1184 (zip files cprograms) +>
1185 List.map
1186 (function (file, cprogram) ->
1187 (* todo?: don't update env ? *)
34e49164
C
1188 let cs = build_info_program cprogram !env in
1189 (* we do that only for the c, not for the h *)
1190 ignore(update_include_rel_pos (cs +> List.map (fun x -> x.ast_c)));
1eddfd50
C
1191 {
1192 fname = Common.basename file;
1193 full_fname = file;
1194 asts = cs;
1195 was_modified_once = ref false;
1196 fpath = file;
1197 fkind = Source
1198 }) in
34e49164 1199
1eddfd50 1200 includes @ cfiles
34e49164 1201
97111a47
C
1202(*****************************************************************************)
1203(* Manage environments as they are being built up *)
1204(*****************************************************************************)
1205
1206let init_env _ = Hashtbl.create 101
1207
1208let update_env env v i = Hashtbl.replace env v i; env
1209
1210(* know that there are no conflicts *)
1211let safe_update_env env v i = Hashtbl.add env v i; env
1212
1213let end_env env =
1214 List.sort compare (Hashtbl.fold (fun k v rest -> (k,v) :: rest) env [])
1215
34e49164
C
1216(*****************************************************************************)
1217(* Processing the ctls and toplevel C elements *)
1218(*****************************************************************************)
1219
1220(* The main algorithm =~
ae4735db 1221 * The algorithm is roughly:
34e49164
C
1222 * for_all ctl rules in SP
1223 * for_all minirule in rule (no more)
1224 * for_all binding (computed during previous phase)
1225 * for_all C elements
ae4735db
C
1226 * match control flow of function vs minirule
1227 * with the binding and update the set of possible
34e49164
C
1228 * bindings, and returned the possibly modified function.
1229 * pretty print modified C elements and reparse it.
1230 *
ae4735db 1231 *
34e49164
C
1232 * On ne prends que les newbinding ou returned_any_state est vrai.
1233 * Si ca ne donne rien, on prends ce qu'il y avait au depart.
ae4735db 1234 * Mais au nouveau depart de quoi ?
34e49164 1235 * - si ca donne rien apres avoir traité toutes les fonctions avec ce binding ?
ae4735db 1236 * - ou alors si ca donne rien, apres avoir traité toutes les fonctions
34e49164 1237 * avec tous les bindings du round d'avant ?
ae4735db 1238 *
34e49164
C
1239 * Julia pense qu'il faut prendre la premiere solution.
1240 * Example: on a deux environnements candidats, E1 et E2 apres avoir traité
1241 * la regle ctl 1. On arrive sur la regle ctl 2.
1242 * E1 ne donne rien pour la regle 2, on garde quand meme E1 pour la regle 3.
1243 * E2 donne un match a un endroit et rend E2' alors on utilise ca pour
1244 * la regle 3.
ae4735db 1245 *
34e49164
C
1246 * I have not to look at used_after_list to decide to restart from
1247 * scratch. I just need to look if the binding list is empty.
1248 * Indeed, let's suppose that a SP have 3 regions/rules. If we
1249 * don't find a match for the first region, then if this first
1250 * region does not bind metavariable used after, that is if
1251 * used_after_list is empty, then mysat(), even if does not find a
1252 * match, will return a Left, with an empty transformation_info,
1253 * and so current_binding will grow. On the contrary if the first
1254 * region must bind some metavariables used after, and that we
1255 * dont find any such region, then mysat() will returns lots of
1256 * Right, and current_binding will not grow, and so we will have
ae4735db 1257 * an empty list of binding, and we will catch such a case.
34e49164
C
1258 *
1259 * opti: julia says that because the binding is
1260 * determined by the used_after_list, the items in the list
1261 * are kind of sorted, so could optimise the insert_set operations.
1262 *)
1263
1264
1265(* r(ule), c(element in C code), e(nvironment) *)
1266
b1b2de81 1267let merge_env new_e old_e =
97111a47
C
1268 List.iter
1269 (function (e,rules) ->
1270 let _ = update_env old_e e rules in ()) new_e;
1271 old_e
b1b2de81 1272
174d1640
C
1273let contains_binding e (_,(r,m),_) =
1274 try
1275 let _ = List.find (function ((re, rm), _) -> r =*= re && m =$= rm) e in
1276 true
1277 with Not_found -> false
1278
5427db06
C
1279exception Exited
1280
413ffc02 1281let python_application mv ve script_vars r =
aba5c457
C
1282 let mv =
1283 List.map
1284 (function
1285 ((Some x,None),y,z) -> (x,y,z)
1286 | _ ->
1287 failwith
1288 (Printf.sprintf "unexpected ast metavar in rule %s"
413ffc02 1289 r.scr_rule_info.rulename))
aba5c457
C
1290 mv in
1291 try
1292 Pycocci.build_classes (List.map (function (x,y) -> x) ve);
1293 Pycocci.construct_variables mv ve;
413ffc02 1294 Pycocci.construct_script_variables script_vars;
aba5c457 1295 let _ = Pycocci.pyrun_simplestring (local_python_code ^r.script_code) in
5427db06
C
1296 if !Pycocci.exited
1297 then raise Exited
1298 else if !Pycocci.inc_match
413ffc02
C
1299 then Some (Pycocci.retrieve_script_variables script_vars)
1300 else None
aba5c457 1301 with Pycocci.Pycocciexception ->
413ffc02 1302 (pr2 ("Failure in " ^ r.scr_rule_info.rulename);
aba5c457 1303 raise Pycocci.Pycocciexception)
174d1640 1304
413ffc02 1305let ocaml_application mv ve script_vars r =
aba5c457 1306 try
413ffc02
C
1307 let script_vals =
1308 Run_ocamlcocci.run mv ve script_vars
1309 r.scr_rule_info.rulename r.script_code in
5427db06
C
1310 if !Coccilib.exited
1311 then raise Exited
1312 else if !Coccilib.inc_match
413ffc02
C
1313 then Some script_vals
1314 else None
1315 with e -> (pr2 ("Failure in " ^ r.scr_rule_info.rulename); raise e)
174d1640 1316
3a314143 1317(* returns Left in case of dependency failure, Right otherwise *)
174d1640
C
1318let apply_script_rule r cache newes e rules_that_have_matched
1319 rules_that_have_ever_matched script_application =
1320 Common.profile_code r.language (fun () ->
413ffc02 1321 show_or_not_scr_rule_name r.scr_rule_info.ruleid;
34e49164 1322 if not(interpret_dependencies rules_that_have_matched
413ffc02 1323 !rules_that_have_ever_matched r.scr_rule_info.dependencies)
34e49164
C
1324 then
1325 begin
1be43e12
C
1326 print_dependencies "dependencies for script not satisfied:"
1327 rules_that_have_matched
413ffc02 1328 !rules_that_have_ever_matched r.scr_rule_info.dependencies;
1be43e12 1329 show_or_not_binding "in environment" e;
97111a47 1330 (cache, safe_update_env newes e rules_that_have_matched)
34e49164
C
1331 end
1332 else
1333 begin
413ffc02 1334 let (_, mv, script_vars, _) = r.scr_ast_rule in
ae4735db 1335 let ve =
5636bb2c 1336 (List.map (function (n,v) -> (("virtual",n),Ast_c.MetaIdVal (v,[])))
ae4735db 1337 !Flag.defined_virtual_env) @ e in
174d1640 1338 let not_bound x = not (contains_binding ve x) in
978fd7e5
C
1339 (match List.filter not_bound mv with
1340 [] ->
34e49164
C
1341 let relevant_bindings =
1342 List.filter
1343 (function ((re,rm),_) ->
174d1640 1344 List.exists (function (_,(r,m),_) -> r =*= re && m =$= rm) mv)
34e49164 1345 e in
413ffc02 1346 (try
c491d8ee
C
1347 match List.assoc relevant_bindings cache with
1348 None -> (cache,newes)
1349 | Some script_vals ->
1350 print_dependencies
1351 "dependencies for script satisfied, but cached:"
1352 rules_that_have_matched
1353 !rules_that_have_ever_matched
1354 r.scr_rule_info.dependencies;
1355 show_or_not_binding "in" e;
88e71198
C
1356 (* env might be bigger than what was cached against, so have to
1357 merge with newes anyway *)
c491d8ee
C
1358 let new_e = (List.combine script_vars script_vals) @ e in
1359 let new_e =
1360 new_e +>
1361 List.filter
1362 (fun (s,v) -> List.mem s r.scr_rule_info.used_after) in
97111a47 1363 (cache,update_env newes new_e rules_that_have_matched)
413ffc02 1364 with Not_found ->
174d1640
C
1365 begin
1366 print_dependencies "dependencies for script satisfied:"
1367 rules_that_have_matched
1368 !rules_that_have_ever_matched
413ffc02 1369 r.scr_rule_info.dependencies;
174d1640 1370 show_or_not_binding "in" e;
413ffc02
C
1371 match script_application mv ve script_vars r with
1372 None ->
1373 (* failure means we should drop e, no new bindings *)
c491d8ee 1374 (((relevant_bindings,None) :: cache), newes)
413ffc02
C
1375 | Some script_vals ->
1376 let script_vals =
1377 List.map (function x -> Ast_c.MetaIdVal(x,[]))
1378 script_vals in
97111a47 1379 let new_e = (List.combine script_vars script_vals) @ e in
413ffc02
C
1380 let new_e =
1381 new_e +>
1382 List.filter
1383 (fun (s,v) -> List.mem s r.scr_rule_info.used_after) in
1384 r.scr_rule_info.was_matched := true;
c491d8ee 1385 (((relevant_bindings,Some script_vals) :: cache),
97111a47
C
1386 update_env newes new_e
1387 (r.scr_rule_info.rulename :: rules_that_have_matched))
413ffc02 1388 end)
978fd7e5
C
1389 | unbound ->
1390 (if !Flag_cocci.show_dependencies
1391 then
174d1640 1392 let m2c (_,(r,x),_) = r^"."^x in
978fd7e5
C
1393 pr2 (Printf.sprintf "script not applied: %s not bound"
1394 (String.concat ", " (List.map m2c unbound))));
413ffc02
C
1395 let e =
1396 e +>
97111a47
C
1397 List.filter (fun (s,v) -> List.mem s r.scr_rule_info.used_after) in
1398 (cache, update_env newes e rules_that_have_matched))
fc1ad971 1399 end)
34e49164 1400
7f004419 1401let rec apply_cocci_rule r rules_that_have_ever_matched es
b1b2de81 1402 (ccs:file_info list ref) =
413ffc02
C
1403 Common.profile_code r.rule_info.rulename (fun () ->
1404 show_or_not_rule_name r.ast_rule r.rule_info.ruleid;
1405 show_or_not_ctl_text r.ctl r.ast_rule r.rule_info.ruleid;
34e49164
C
1406
1407 let reorganized_env =
1408 reassociate_positions r.free_vars r.negated_pos_vars !es in
1409
1410 (* looping over the environments *)
1411 let (_,newes (* envs for next round/rule *)) =
1412 List.fold_left
1413 (function (cache,newes) ->
1414 function ((e,rules_that_have_matched),relevant_bindings) ->
1415 if not(interpret_dependencies rules_that_have_matched
7f004419 1416 !rules_that_have_ever_matched
413ffc02 1417 r.rule_info.dependencies)
34e49164
C
1418 then
1419 begin
1be43e12 1420 print_dependencies
413ffc02
C
1421 ("dependencies for rule "^r.rule_info.rulename^
1422 " not satisfied:")
1be43e12 1423 rules_that_have_matched
413ffc02 1424 !rules_that_have_ever_matched r.rule_info.dependencies;
1be43e12 1425 show_or_not_binding "in environment" e;
34e49164 1426 (cache,
97111a47
C
1427 update_env newes
1428 (e +>
1429 List.filter
1430 (fun (s,v) -> List.mem s r.rule_info.used_after))
1431 rules_that_have_matched)
34e49164
C
1432 end
1433 else
1434 let new_bindings =
1435 try List.assoc relevant_bindings cache
1436 with
1437 Not_found ->
1be43e12 1438 print_dependencies
413ffc02
C
1439 ("dependencies for rule "^r.rule_info.rulename^
1440 " satisfied:")
1be43e12 1441 rules_that_have_matched
7f004419 1442 !rules_that_have_ever_matched
413ffc02 1443 r.rule_info.dependencies;
1be43e12
C
1444 show_or_not_binding "in" e;
1445 show_or_not_binding "relevant in" relevant_bindings;
34e49164 1446
faf9a90c
C
1447 (* applying the rule *)
1448 (match r.ruletype with
1449 Ast_cocci.Normal ->
34e49164
C
1450 (* looping over the functions and toplevel elements in
1451 .c and .h *)
708f4980
C
1452 List.rev
1453 (concat_headers_and_c !ccs +>
ae4735db
C
1454 List.fold_left (fun children_e (c,f) ->
1455 if c.flow <> None
708f4980
C
1456 then
1457 (* does also some side effects on c and r *)
1458 let processed =
1459 process_a_ctl_a_env_a_toplevel r
1460 relevant_bindings c f in
1461 match processed with
1462 | None -> children_e
ae4735db 1463 | Some newbindings ->
708f4980
C
1464 newbindings +>
1465 List.fold_left
ae4735db 1466 (fun children_e newbinding ->
708f4980
C
1467 if List.mem newbinding children_e
1468 then children_e
1469 else newbinding :: children_e)
1470 children_e
1471 else children_e)
1472 [])
faf9a90c
C
1473 | Ast_cocci.Generated ->
1474 process_a_generated_a_env_a_toplevel r
1475 relevant_bindings !ccs;
1476 []) in
1477
34e49164
C
1478 let old_bindings_to_keep =
1479 Common.nub
413ffc02
C
1480 (e +>
1481 List.filter
1482 (fun (s,v) -> List.mem s r.rule_info.used_after)) in
34e49164
C
1483 let new_e =
1484 if null new_bindings
1485 then
1486 begin
1487 (*use the old bindings, specialized to the used_after_list*)
1488 if !Flag_ctl.partial_match
1489 then
1490 printf
7f004419 1491 "Empty list of bindings, I will restart from old env\n";
34e49164
C
1492 [(old_bindings_to_keep,rules_that_have_matched)]
1493 end
1494 else
1495 (* combine the new bindings with the old ones, and
1496 specialize to the used_after_list *)
1497 let old_variables = List.map fst old_bindings_to_keep in
1498 (* have to explicitly discard the inherited variables
1499 because we want the inherited value of the positions
1500 variables not the extended one created by
1501 reassociate_positions. want to reassociate freshly
1502 according to the free variables of each rule. *)
1503 let new_bindings_to_add =
1504 Common.nub
1505 (new_bindings +>
1506 List.map
1507 (List.filter
aa721442
C
1508 (function
1509 (* see comment before combine_pos *)
1510 (s,Ast_c.MetaPosValList []) -> false
1511 | (s,v) ->
413ffc02 1512 List.mem s r.rule_info.used_after &&
aa721442 1513 not (List.mem s old_variables)))) in
34e49164
C
1514 List.map
1515 (function new_binding_to_add ->
1be43e12
C
1516 (List.sort compare
1517 (Common.union_set
1518 old_bindings_to_keep new_binding_to_add),
413ffc02 1519 r.rule_info.rulename::rules_that_have_matched))
34e49164
C
1520 new_bindings_to_add in
1521 ((relevant_bindings,new_bindings)::cache,
97111a47
C
1522 Common.profile_code "merge_env" (function _ ->
1523 merge_env new_e newes)))
1524 ([],init_env()) reorganized_env in (* end iter es *)
413ffc02
C
1525 if !(r.rule_info.was_matched)
1526 then Common.push2 r.rule_info.rulename rules_that_have_ever_matched;
34e49164 1527
97111a47 1528 es := end_env newes;
34e49164
C
1529
1530 (* apply the tagged modifs and reparse *)
1531 if not !Flag.sgrep_mode2
b1b2de81 1532 then ccs := rebuild_info_c_and_headers !ccs r.isexp)
34e49164
C
1533
1534and reassociate_positions free_vars negated_pos_vars envs =
1535 (* issues: isolate the bindings that are relevant to a given rule.
1536 separate out the position variables
1537 associate all of the position variables for a given set of relevant
1538 normal variable bindings with each set of relevant normal variable
1539 bindings. Goal: if eg if@p (E) matches in two places, then both inherited
1540 occurrences of E should see both bindings of p, not just its own.
1541 Otherwise, a position constraint for something that matches in two
1542 places will never be useful, because the position can always be
1543 different from the other one. *)
1544 let relevant =
1545 List.map
1546 (function (e,_) ->
1547 List.filter (function (x,_) -> List.mem x free_vars) e)
1548 envs in
1549 let splitted_relevant =
1550 (* separate the relevant variables into the non-position ones and the
1551 position ones *)
1552 List.map
1553 (function r ->
1554 List.fold_left
1555 (function (non_pos,pos) ->
1556 function (v,_) as x ->
1557 if List.mem v negated_pos_vars
1558 then (non_pos,x::pos)
1559 else (x::non_pos,pos))
1560 ([],[]) r)
1561 relevant in
1562 let splitted_relevant =
1563 List.map
1564 (function (non_pos,pos) ->
1565 (List.sort compare non_pos,List.sort compare pos))
1566 splitted_relevant in
1567 let non_poss =
1568 List.fold_left
1569 (function non_pos ->
1570 function (np,_) ->
1571 if List.mem np non_pos then non_pos else np::non_pos)
1572 [] splitted_relevant in
1573 let extended_relevant =
1574 (* extend the position variables with the values found at other identical
1575 variable bindings *)
1576 List.map
1577 (function non_pos ->
1578 let others =
1579 List.filter
1580 (function (other_non_pos,other_pos) ->
1581 (* do we want equal? or just somehow compatible? eg non_pos
1582 binds only E, but other_non_pos binds both E and E1 *)
b1b2de81 1583 non_pos =*= other_non_pos)
34e49164
C
1584 splitted_relevant in
1585 (non_pos,
1586 List.sort compare
1587 (non_pos @
1588 (combine_pos negated_pos_vars
1589 (List.map (function (_,x) -> x) others)))))
1590 non_poss in
1591 List.combine envs
1592 (List.map (function (non_pos,_) -> List.assoc non_pos extended_relevant)
1593 splitted_relevant)
1594
aa721442
C
1595(* If the negated posvar is not bound at all, this function will
1596nevertheless bind it to []. If we get rid of these bindings, then the
1597matching of the term the position variable with the constraints will fail
1598because some variables are unbound. So we let the binding be [] and then
1599we will have to clean these up afterwards. This should be the only way
1600that a position variable can have an empty binding. *)
34e49164
C
1601and combine_pos negated_pos_vars others =
1602 List.map
1603 (function posvar ->
aa721442
C
1604 let positions =
1605 List.sort compare
1606 (List.fold_left
1607 (function positions ->
1608 function other_list ->
1609 try
1610 match List.assoc posvar other_list with
1611 Ast_c.MetaPosValList l1 ->
1612 Common.union_set l1 positions
1613 | _ -> failwith "bad value for a position variable"
1614 with Not_found -> positions)
1615 [] others) in
1616 (posvar,Ast_c.MetaPosValList positions))
34e49164
C
1617 negated_pos_vars
1618
b1b2de81
C
1619and process_a_generated_a_env_a_toplevel2 r env = function
1620 [cfile] ->
1621 let free_vars =
1622 List.filter
1623 (function
413ffc02 1624 (rule,_) when rule =$= r.rule_info.rulename -> false
b1b2de81
C
1625 | (_,"ARGS") -> false
1626 | _ -> true)
1627 r.free_vars in
1628 let env_domain = List.map (function (nm,vl) -> nm) env in
1629 let metavars =
1630 List.filter
1631 (function md ->
413ffc02 1632 let (rl,_) = Ast_cocci.get_meta_name md in rl =$= r.rule_info.rulename)
b1b2de81
C
1633 r.metavars in
1634 if Common.include_set free_vars env_domain
1635 then Unparse_hrule.pp_rule metavars r.ast_rule env cfile.full_fname
1636 | _ -> failwith "multiple files not supported"
34e49164 1637
ae4735db
C
1638and process_a_generated_a_env_a_toplevel rule env ccs =
1639 Common.profile_code "process_a_ctl_a_env_a_toplevel"
b1b2de81 1640 (fun () -> process_a_generated_a_env_a_toplevel2 rule env ccs)
34e49164
C
1641
1642(* does side effects on C ast and on Cocci info rule *)
ae4735db
C
1643and process_a_ctl_a_env_a_toplevel2 r e c f =
1644 indent_do (fun () ->
65038c61
C
1645 show_or_not_celem "trying" c.ast_c;
1646 Flag.currentfile := Some (f ^ ":" ^get_celem c.ast_c);
1647 match (r.ctl,c.ast_c) with
1648 ((Asttoctl2.NONDECL ctl,t),Ast_c.Declaration _) -> None
1649 | ((Asttoctl2.NONDECL ctl,t), _)
1650 | ((Asttoctl2.CODE ctl,t), _) ->
1651 let ctl = (ctl,t) in (* ctl and other info *)
1652 let (trans_info, returned_any_states, inherited_bindings, newbindings) =
1653 Common.save_excursion Flag_ctl.loop_in_src_code (fun () ->
1654 Flag_ctl.loop_in_src_code :=
1655 !Flag_ctl.loop_in_src_code||c.contain_loop;
ae4735db 1656
34e49164
C
1657 (***************************************)
1658 (* !Main point! The call to the engine *)
1659 (***************************************)
65038c61
C
1660 let model_ctl =
1661 CCI.model_for_ctl r.dropped_isos (Common.some c.flow) e
17ba0788
C
1662 in CCI.mysat model_ctl ctl
1663 (r.rule_info.rulename, r.rule_info.used_after, e))
65038c61
C
1664 in
1665 if not returned_any_states
1666 then None
1667 else
1668 begin
1669 show_or_not_celem "found match in" c.ast_c;
1670 show_or_not_trans_info trans_info;
1671 List.iter (show_or_not_binding "out") newbindings;
1672
1673 r.rule_info.was_matched := true;
1674
1675 if not (null trans_info) &&
1676 not (!Flag.sgrep_mode2 && not !Flag_cocci.show_diff)
1677 then
1678 begin
1679 c.was_modified := true;
1680 try
1681 (* les "more than one var in a decl" et "already tagged token"
1682 * font crasher coccinelle. Si on a 5 fichiers, donc on a 5
1683 * failed. Le try limite le scope des crashes pendant la
1684 * trasformation au fichier concerne. *)
1685
1686 (* modify ast via side effect *)
1687 ignore
1688 (Transformation_c.transform r.rule_info.rulename
1689 r.dropped_isos
1690 inherited_bindings trans_info (Common.some c.flow));
1691 with Timeout -> raise Timeout | UnixExit i -> raise (UnixExit i)
1692 end;
1693
1694 Some (List.map (function x -> x@inherited_bindings) newbindings)
1695 end
1696 )
ae4735db
C
1697
1698and process_a_ctl_a_env_a_toplevel a b c f=
1699 Common.profile_code "process_a_ctl_a_env_a_toplevel"
485bce71 1700 (fun () -> process_a_ctl_a_env_a_toplevel2 a b c f)
faf9a90c 1701
34e49164 1702
7f004419
C
1703let rec bigloop2 rs (ccs: file_info list) =
1704 let init_es = [(Ast_c.emptyMetavarsBinding,[])] in
1705 let es = ref init_es in
b1b2de81
C
1706 let ccs = ref ccs in
1707 let rules_that_have_ever_matched = ref [] in
1708
5427db06
C
1709 (try
1710
b1b2de81 1711 (* looping over the rules *)
ae4735db 1712 rs +> List.iter (fun r ->
b1b2de81
C
1713 match r with
1714 InitialScriptRuleCocciInfo r | FinalScriptRuleCocciInfo r -> ()
ae4735db 1715 | ScriptRuleCocciInfo r ->
b1b2de81
C
1716 if !Flag_cocci.show_ctl_text then begin
1717 Common.pr_xxxxxxxxxxxxxxxxx ();
1718 pr ("script: " ^ r.language);
1719 Common.pr_xxxxxxxxxxxxxxxxx ();
ae4735db
C
1720
1721 adjust_pp_with_indent (fun () ->
b1b2de81 1722 Format.force_newline();
413ffc02
C
1723 let (l,mv,script_vars,code) = r.scr_ast_rule in
1724 let nm = r.scr_rule_info.rulename in
1725 let deps = r.scr_rule_info.dependencies in
b1b2de81 1726 Pretty_print_cocci.unparse
413ffc02 1727 (Ast_cocci.ScriptRule (nm,l,deps,mv,script_vars,code)));
b1b2de81
C
1728 end;
1729
17ba0788 1730 (*pr2 (List.hd(cmd_to_list "free -m | grep Mem"));*)
b1b2de81
C
1731 if !Flag.show_misc then print_endline "RESULT =";
1732
1733 let (_, newes) =
1734 List.fold_left
1735 (function (cache, newes) ->
1736 function (e, rules_that_have_matched) ->
1737 match r.language with
1738 "python" ->
174d1640
C
1739 apply_script_rule r cache newes e rules_that_have_matched
1740 rules_that_have_ever_matched python_application
1741 | "ocaml" ->
1742 apply_script_rule r cache newes e rules_that_have_matched
1743 rules_that_have_ever_matched ocaml_application
b1b2de81 1744 | "test" ->
ae4735db
C
1745 concat_headers_and_c !ccs +> List.iter (fun (c,_) ->
1746 if c.flow <> None
b1b2de81
C
1747 then
1748 Printf.printf "Flow: %s\r\nFlow!\r\n%!" c.fullstring);
1749 (cache, newes)
1750 | _ ->
1751 Printf.printf "Unknown language: %s\n" r.language;
174d1640 1752 (cache, newes))
97111a47 1753 ([],init_env()) !es in
b1b2de81 1754
413ffc02
C
1755 (if !(r.scr_rule_info.was_matched)
1756 then
1757 Common.push2 r.scr_rule_info.rulename rules_that_have_ever_matched);
1758
5427db06
C
1759 (* just newes can't work, because if one does include_match false
1760 on everything that binds a variable, then nothing is left *)
97111a47
C
1761 es := (*newes*)
1762 (if Hashtbl.length newes = 0 then init_es else end_env newes)
b1b2de81 1763 | CocciRuleCocciInfo r ->
7f004419 1764 apply_cocci_rule r rules_that_have_ever_matched
5427db06
C
1765 es ccs)
1766 with Exited -> ());
b1b2de81
C
1767
1768 if !Flag.sgrep_mode2
1769 then begin
1770 (* sgrep can lead to code that is not parsable, but we must
ae4735db 1771 * still call rebuild_info_c_and_headers to pretty print the
b1b2de81
C
1772 * action (MINUS), so that later the diff will show what was
1773 * matched by sgrep. But we don't want the parsing error message
1774 * hence the following flag setting. So this code propably
1775 * will generate a NotParsedCorrectly for the matched parts
1776 * and the very final pretty print and diff will work
1777 *)
1778 Flag_parsing_c.verbose_parsing := false;
1779 ccs := rebuild_info_c_and_headers !ccs false
1780 end;
1781 !ccs (* return final C asts *)
1782
ae4735db 1783let bigloop a b =
b1b2de81
C
1784 Common.profile_code "bigloop" (fun () -> bigloop2 a b)
1785
174d1640
C
1786type init_final = Initial | Final
1787
ae4735db 1788let initial_final_bigloop2 ty rebuild r =
b1b2de81
C
1789 if !Flag_cocci.show_ctl_text then
1790 begin
1791 Common.pr_xxxxxxxxxxxxxxxxx ();
174d1640
C
1792 pr ((match ty with Initial -> "initial" | Final -> "final") ^ ": " ^
1793 r.language);
b1b2de81
C
1794 Common.pr_xxxxxxxxxxxxxxxxx ();
1795
ae4735db 1796 adjust_pp_with_indent (fun () ->
b1b2de81 1797 Format.force_newline();
413ffc02 1798 Pretty_print_cocci.unparse(rebuild r.scr_ast_rule r.scr_rule_info.dependencies));
b1b2de81
C
1799 end;
1800
1801 match r.language with
1802 "python" ->
1803 (* include_match makes no sense in an initial or final rule, although
c3e37e97 1804 we have no way to prevent it *)
97111a47
C
1805 let newes = init_env() in
1806 let _ = apply_script_rule r [] newes [] [] (ref []) python_application in
174d1640
C
1807 ()
1808 | "ocaml" when ty = Initial -> () (* nothing to do *)
1809 | "ocaml" ->
1810 (* include_match makes no sense in an initial or final rule, although
1811 we have no way to prevent it *)
97111a47
C
1812 let newes = init_env() in
1813 let _ = apply_script_rule r [] newes [] [] (ref []) ocaml_application in
951c7801 1814 ()
b1b2de81 1815 | _ ->
c491d8ee
C
1816 failwith ("Unknown language for initial/final script: "^
1817 r.language)
b1b2de81 1818
ae4735db 1819let initial_final_bigloop a b c =
b1b2de81
C
1820 Common.profile_code "initial_final_bigloop"
1821 (fun () -> initial_final_bigloop2 a b c)
34e49164
C
1822
1823(*****************************************************************************)
b1b2de81 1824(* The main functions *)
34e49164
C
1825(*****************************************************************************)
1826
b1b2de81
C
1827let pre_engine2 (coccifile, isofile) =
1828 show_or_not_cocci coccifile isofile;
34e49164
C
1829 Pycocci.set_coccifile coccifile;
1830
ae4735db 1831 let isofile =
34e49164 1832 if not (Common.lfile_exists isofile)
ae4735db 1833 then begin
34e49164
C
1834 pr2 ("warning: Can't find default iso file: " ^ isofile);
1835 None
1836 end
b1b2de81 1837 else Some isofile in
34e49164
C
1838
1839 (* useful opti when use -dir *)
174d1640
C
1840 let (metavars,astcocci,
1841 free_var_lists,negated_pos_lists,used_after_lists,
17ba0788
C
1842 positions_lists,(toks,_,_)) = sp_of_file coccifile isofile in
1843
b1b2de81 1844 let ctls = ctls_of_ast astcocci used_after_lists positions_lists in
34e49164 1845
b1b2de81 1846 g_contain_typedmetavar := sp_contain_typed_metavar astcocci;
34e49164 1847
b1b2de81 1848 check_macro_in_sp_and_adjust toks;
34e49164 1849
b1b2de81 1850 show_or_not_ctl_tex astcocci ctls;
34e49164 1851
b1b2de81
C
1852 let cocci_infos =
1853 prepare_cocci ctls free_var_lists negated_pos_lists
1854 used_after_lists positions_lists metavars astcocci in
34e49164 1855
ae4735db 1856 let used_languages =
b1b2de81
C
1857 List.fold_left
1858 (function languages ->
ae4735db
C
1859 function
1860 ScriptRuleCocciInfo(r) ->
1861 if List.mem r.language languages then
1862 languages
1863 else
1864 r.language::languages
1865 | _ -> languages)
b1b2de81 1866 [] cocci_infos in
951c7801 1867
3a314143
C
1868 let runrule r =
1869 let rlang = r.language in
1870 let rname = r.scr_rule_info.rulename in
1871 try
1872 let _ = List.assoc (rlang,rname) !Iteration.initialization_stack in
1873 ()
1874 with Not_found ->
1875 begin
1876 Iteration.initialization_stack :=
1877 ((rlang,rname),!Flag.defined_virtual_rules) ::
1878 !Iteration.initialization_stack;
1879 initial_final_bigloop Initial
1880 (fun (x,_,_,y) -> fun deps ->
1881 Ast_cocci.InitialScriptRule(rname,x,deps,y))
1882 r
1883 end in
1884
ae4735db
C
1885 let initialized_languages =
1886 List.fold_left
1887 (function languages ->
3a314143
C
1888 function
1889 InitialScriptRuleCocciInfo(r) ->
1890 let rlang = r.language in
1891 (if List.mem rlang languages
1892 then failwith ("double initializer found for "^rlang));
1893 if interpret_dependencies [] [] r.scr_rule_info.dependencies
1894 then begin runrule r; rlang::languages end
1895 else languages
1896 | _ -> languages)
ae4735db
C
1897 [] cocci_infos in
1898
1899 let uninitialized_languages =
1900 List.filter
1901 (fun used -> not (List.mem used initialized_languages))
413ffc02 1902 used_languages in
3a314143 1903
413ffc02
C
1904 List.iter
1905 (fun lgg ->
1906 let rule_info =
1907 {rulename = "";
1908 dependencies = Ast_cocci.NoDep;
1909 used_after = [];
1910 ruleid = (-1);
1911 was_matched = ref false;} in
3a314143 1912 runrule (make_init lgg "" rule_info))
413ffc02 1913 uninitialized_languages;
ae4735db 1914
7f004419 1915 (cocci_infos,toks)
34e49164 1916
ae4735db 1917let pre_engine a =
b1b2de81 1918 Common.profile_code "pre_engine" (fun () -> pre_engine2 a)
faf9a90c 1919
ae4735db 1920let full_engine2 (cocci_infos,toks) cfiles =
b1b2de81
C
1921
1922 show_or_not_cfiles cfiles;
1923
1924 (* optimisation allowing to launch coccinelle on all the drivers *)
1925 if !Flag_cocci.worth_trying_opt && not (worth_trying cfiles toks)
1926 then
ae4735db 1927 begin
90aeb998
C
1928 (match toks with
1929 None -> ()
1930 | Some toks ->
1931 pr2 ("No matches found for " ^ (Common.join " " toks)
1932 ^ "\nSkipping:" ^ (Common.join " " cfiles)));
b1b2de81
C
1933 cfiles +> List.map (fun s -> s, None)
1934 end
1935 else
1936 begin
1937
1938 if !Flag.show_misc then Common.pr_xxxxxxxxxxxxxxxxx();
1939 if !Flag.show_misc then pr "let's go";
1940 if !Flag.show_misc then Common.pr_xxxxxxxxxxxxxxxxx();
1941
97111a47
C
1942 if !Flag_cocci.show_binding_in_out
1943 then
1944 begin
1945 (match !Flag.defined_virtual_rules with
1946 [] -> ()
1947 | l -> pr (Printf.sprintf "Defined virtual rules: %s"
1948 (String.concat " " l)));
1949 List.iter
1950 (function (v,vl) ->
1951 pr (Printf.sprintf "%s = %s" v vl))
1952 !Flag.defined_virtual_env;
1953 Common.pr_xxxxxxxxxxxxxxxxx()
1954 end;
1955
b1b2de81
C
1956 let choose_includes =
1957 match !Flag_cocci.include_options with
1958 Flag_cocci.I_UNSPECIFIED ->
1959 if !g_contain_typedmetavar
1960 then Flag_cocci.I_NORMAL_INCLUDES
1961 else Flag_cocci.I_NO_INCLUDES
1962 | x -> x in
1963 let c_infos = prepare_c cfiles choose_includes in
1964
1965 (* ! the big loop ! *)
7f004419 1966 let c_infos' = bigloop cocci_infos c_infos in
b1b2de81
C
1967
1968 if !Flag.show_misc then Common.pr_xxxxxxxxxxxxxxxxx ();
1969 if !Flag.show_misc then pr "Finished";
1970 if !Flag.show_misc then Common.pr_xxxxxxxxxxxxxxxxx ();
1971 if !Flag_ctl.graphical_trace then gen_pdf_graph ();
1972
ae4735db 1973 c_infos' +> List.map (fun c_or_h ->
b1b2de81
C
1974 if !(c_or_h.was_modified_once)
1975 then
1976 begin
1977 let outfile =
1978 Common.new_temp_file "cocci-output" ("-" ^ c_or_h.fname) in
1979
1980 if c_or_h.fkind =*= Header
1981 then pr2 ("a header file was modified: " ^ c_or_h.fname);
1982
1983 (* and now unparse everything *)
1984 cfile_of_program (for_unparser c_or_h.asts) outfile;
1985
c3e37e97 1986 show_or_not_diff c_or_h.fpath outfile;
b1b2de81
C
1987
1988 (c_or_h.fpath,
1989 if !Flag.sgrep_mode2 then None else Some outfile)
1990 end
1991 else (c_or_h.fpath, None))
1992 end
34e49164 1993
ae4735db 1994let full_engine a b =
fc1ad971
C
1995 Common.profile_code "full_engine"
1996 (fun () -> let res = full_engine2 a b in (*Gc.print_stat stderr; *)res)
34e49164 1997
7f004419 1998let post_engine2 (cocci_infos,_) =
3a314143
C
1999 List.iter
2000 (function ((language,_),virt_rules) ->
2001 Flag.defined_virtual_rules := virt_rules;
2002 let _ =
2003 List.fold_left
2004 (function languages ->
2005 function
2006 FinalScriptRuleCocciInfo(r) ->
2007 (if r.language = language && List.mem r.language languages
2008 then failwith ("double finalizer found for "^r.language));
2009 initial_final_bigloop Final
2010 (fun (x,_,_,y) -> fun deps ->
2011 Ast_cocci.FinalScriptRule(r.scr_rule_info.rulename,
2012 x,deps,y))
2013 r;
2014 r.language::languages
2015 | _ -> languages)
2016 [] cocci_infos in
2017 ())
2018 !Iteration.initialization_stack
b1b2de81 2019
ae4735db 2020let post_engine a =
b1b2de81 2021 Common.profile_code "post_engine" (fun () -> post_engine2 a)
34e49164
C
2022
2023(*****************************************************************************)
2024(* check duplicate from result of full_engine *)
2025(*****************************************************************************)
2026
ae4735db 2027let check_duplicate_modif2 xs =
34e49164 2028 (* opti: let groups = Common.groupBy (fun (a,resa) (b,resb) -> a =$= b) xs *)
708f4980
C
2029 if !Flag_cocci.verbose_cocci
2030 then pr2 ("Check duplication for " ^ i_to_s (List.length xs) ^ " files");
2031
34e49164 2032 let groups = Common.group_assoc_bykey_eff xs in
ae4735db 2033 groups +> Common.map_filter (fun (file, xs) ->
34e49164
C
2034 match xs with
2035 | [] -> raise Impossible
2036 | [res] -> Some (file, res)
ae4735db
C
2037 | res::xs ->
2038 match res with
2039 | None ->
b1b2de81 2040 if not (List.for_all (fun res2 -> res2 =*= None) xs)
34e49164
C
2041 then begin
2042 pr2 ("different modification result for " ^ file);
2043 None
2044 end
2045 else Some (file, None)
ae4735db
C
2046 | Some res ->
2047 if not(List.for_all (fun res2 ->
34e49164
C
2048 match res2 with
2049 | None -> false
ae4735db 2050 | Some res2 ->
34e49164
C
2051 let diff = Common.cmd_to_list ("diff -u -b -B "^res^" "^res2)
2052 in
2053 null diff
2054 ) xs) then begin
2055 pr2 ("different modification result for " ^ file);
2056 None
2057 end
2058 else Some (file, Some res)
34e49164 2059 )
ae4735db 2060let check_duplicate_modif a =
34e49164
C
2061 Common.profile_code "check_duplicate" (fun () -> check_duplicate_modif2 a)
2062