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