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