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