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