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