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