-(*
- * Copyright 2005-2010, Ecole des Mines de Nantes, University of Copenhagen
- * Yoann Padioleau, Julia Lawall, Rene Rydhof Hansen, Henrik Stuart, Gilles Muller, Nicolas Palix
- * This file is part of Coccinelle.
- *
- * Coccinelle is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, according to version 2 of the License.
- *
- * Coccinelle is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Coccinelle. If not, see <http://www.gnu.org/licenses/>.
- *
- * The authors reserve the right to distribute this or future versions of
- * Coccinelle under other licenses.
- *)
-
-
-open Common
-
-module CP = Classic_patch
-
-(* ./split_patch ../demos/janitorings/patch-kzalloc-vnew3.patch /tmp/xx "0 -> NULL" ../bodymail.doc
-./split_patch /tmp/badzero.patch /tmp/xx ../mailbody.doc ../kernel_dirs.meta
-
-update: see http://lwn.net/Articles/284469/
-for a script using git annotate to find automatically to who send
-a patch (by looking at authors of lines close concerning the patch I guess
-*)
-
-(*****************************************************************************)
-(* Flags *)
-(*****************************************************************************)
-
-let just_patch = ref false
-let verbose = ref true
-
-let pr2 s =
- if !verbose then pr2 s
-
-(*****************************************************************************)
-(* Helpers *)
-(*****************************************************************************)
-
-let print_header_patch patch =
- patch +> List.iter (function CP.File (s, header, body) -> pr s)
-
-
-(*****************************************************************************)
-(* Grouping strategies *)
-(*****************************************************************************)
-
-let group_patch_depth_2 patch =
- let patch_with_dir = patch +> List.map (function (CP.File (s,header,body)) ->
- Common.split "/" (Common.dirname s),
- (CP.File (s, header, body))
- )
- in
- let rec aux_patch xs =
- match xs with
- | [] -> []
- | (dir_elems,x)::xs ->
- let cond, base =
- if List.length dir_elems >= 2 then
- let base = Common.take 2 dir_elems in
- (fun dir_elems' ->
- List.length dir_elems' >= 2 && Common.take 2 dir_elems' = base),
- base
- else
- (fun dir_elems' -> dir_elems' = dir_elems),
- dir_elems
- in
-
- let (yes, no) = xs +> Common.partition_either (fun (dir_elems', x) ->
- if cond dir_elems'
- then Left x
- else Right (dir_elems', x)
- ) in
- (Common.join "/" base, ["NOEMAIL"], (x::yes))::aux_patch no
- in
- aux_patch patch_with_dir
-
-
-
-let group_patch_subsystem_info patch subinfo =
- let patch_with_dir = patch +> List.map (function (CP.File (s,header,body)) ->
- (Common.dirname s), (CP.File (s, header, body))
- )
- in
- let index = Maintainers.mk_inverted_index_subsystem subinfo in
- let hash = Maintainers.subsystem_to_hash subinfo in
-
- let rec aux_patch xs =
- match xs with
- | [] -> []
- | ((dir,patchitem)::xs) as xs' ->
- let leader =
- try Hashtbl.find index dir
- with Not_found -> failwith ("cant find leader for : " ^ dir)
- in
- let (emailsleader, subdirs) =
- try Hashtbl.find hash leader
- with Not_found -> failwith ("cant find subdirs of : " ^ leader)
- in
-
- (match emailsleader with
- | ["NOEMAIL"] | [] | [""] -> pr2 ("no leader maintainer for: "^leader);
- | _ -> ()
- );
-
- let emails = ref emailsleader in
- let allsubdirs = (leader, emailsleader)::subdirs in
-
- let (yes, no) = xs' +> Common.partition_either (fun (dir',patchitem')->
- try (
- let emailsdir' = List.assoc dir' allsubdirs in
- emails := !emails $+$ emailsdir';
- Left patchitem'
- ) with Not_found -> Right (dir', patchitem')
- (*
- if List.mem dir' (leader::subdirs)
- then Left x
- else Right (dir', x)
- *)
- ) in
- (leader, !emails, yes)::aux_patch no
- in
- aux_patch patch_with_dir
-
-
-(*****************************************************************************)
-(* Split patch *)
-(*****************************************************************************)
-let i_to_s_padded i total =
- match i with
- | i when i < 10 && total >= 10 -> "0" ^ i_to_s i
- | i when i < 100 -> i_to_s i
- | i -> i_to_s i
-
-let split_patch file prefix bodymail subinfofile =
- let patch = CP.parse_patch file in
-
- let subsystem_info = Maintainers.parse_subsystem_info subinfofile in
- let minipatches = group_patch_subsystem_info patch subsystem_info in
- (* let minipatches = group_patch_depth_2 patch in *)
-
- let total = List.length minipatches in
- let minipatches_indexed = Common.index_list_1 minipatches in
-
- let (subject, bodymail_rest) =
- match Common.cat bodymail with
- | x::y::xs ->
- if x =~ "Subject: \\(.*\\)"
- then
- let subject = matched1 x in
- if y =~ "[-]+$"
- then
- subject, xs
- else failwith ("wrong format for mailbody in:" ^ bodymail)
- else failwith ("wrong format for mailbody in:" ^ bodymail)
- | _ -> failwith ("wrong format for mailbody in:" ^ bodymail)
- in
-
- Common.command2_y_or_no ("rm -f " ^ prefix ^ "*");
-
-
- minipatches_indexed +> List.iter (fun ((dir,emails, minipatch), i) ->
- let numpatch = i_to_s_padded i total in
- let tmpfile = prefix ^ numpatch ^ ".mail" in
- let patchfile = "/tmp/x.patch" in
- pr2 ("generating :" ^ tmpfile ^ " for " ^ dir);
-
- CP.unparse_patch minipatch patchfile;
-
- let emails =
- (match emails with
- | ["NOEMAIL"] | [] | [""] ->
- pr2 "no maintainer"; []
- | xs -> xs
- ) @ ["akpm@linux-foundation.org"]
- in
-
-
- if !just_patch
- then command2(sprintf "cat %s > %s" patchfile tmpfile)
- else begin
- Common.with_open_outfile tmpfile (fun (pr_no_nl, chan) ->
- let pr s = pr_no_nl (s ^ "\n") in
- pr "To: kernel-janitors@vger.kernel.org";
- pr (sprintf "Subject: [PATCH %s/%d] %s, for %s"
- numpatch total subject dir);
- pr ("Cc: " ^ (Common.join ", " (emails @ ["linux-kernel@vger.kernel.org"])));
- pr "BCC: padator@wanadoo.fr";
- pr "From: Yoann Padioleau <padator@wanadoo.fr>";
- pr "--text follows this line--";
-
- pr "";
- bodymail_rest +> List.iter pr;
- pr "";
- pr "Signed-off-by: Yoann Padioleau <padator@wanadoo.fr>";
- emails +> List.iter (fun s ->
- pr ("Cc: " ^ s)
- );
- pr "---";
-
- pr "";
- );
-
- command2(sprintf "diffstat -p1 %s >> %s" patchfile tmpfile);
- command2(sprintf "echo >> %s" tmpfile);
- command2(sprintf "cat %s >> %s" patchfile tmpfile);
- end
- )
-
-
-
-(*****************************************************************************)
-(* Test *)
-(*****************************************************************************)
-
-let test_patch file =
- let patch = CP.parse_patch file in
- let groups = group_patch_depth_2 patch in
- groups +> List.iter (fun (dir, email, minipatch) ->
- print_header_patch minipatch;
- pr ""
- )
-
-
-(*****************************************************************************)
-(* Main entry point *)
-(*****************************************************************************)
-
-let main () =
- begin
- let args = ref [] in
- let options = [
- "-just_patch", Arg.Set just_patch, "";
- "-no_verbose", Arg.Clear verbose, "";
- ] in
- let usage_msg =
- "Usage: " ^ basename Sys.argv.(0) ^
- " <patch> <prefix> <bodymailfile> <maintainerfile> [options]" ^ "\n"
- ^ "Options are:"
- in
-
- Arg.parse (Arg.align options) (fun x -> args := x::!args) usage_msg;
- args := List.rev !args;
-
- (match (!args) with
- | [patch] -> test_patch patch
- | [patch;prefix;bodymail;subinfofile] ->
- split_patch patch prefix bodymail subinfofile;
-
- command2("rm -f /tmp/split_check*");
- let checkfile = "/tmp/split_check.all" in
- let checkprefix = "/tmp/split_check-xx" in
- save_excursion verbose (fun () ->
- save_excursion just_patch (fun () ->
- just_patch := true;
- verbose := false;
- split_patch patch checkprefix bodymail subinfofile;
- ));
- command2("cat /tmp/split_check*.mail > " ^ checkfile);
-
- let diff = Common.cmd_to_list (sprintf "diff %s %s " patch checkfile)
- in
- let samesize = Common.filesize patch = Common.filesize checkfile in
- if (List.length diff <> 0)
- then
- if samesize
- then pr2 "diff but at least same size"
- else pr2 "PB: diff and not same size"
-
- | _ -> Arg.usage (Arg.align options) usage_msg;
- )
- end
-
-(*****************************************************************************)
-let _ =
- main ()
-
+(*
+ * Copyright 2005-2010, Ecole des Mines de Nantes, University of Copenhagen
+ * Yoann Padioleau, Julia Lawall, Rene Rydhof Hansen, Henrik Stuart, Gilles Muller, Nicolas Palix
+ * This file is part of Coccinelle.
+ *
+ * Coccinelle is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, according to version 2 of the License.
+ *
+ * Coccinelle is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Coccinelle. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * The authors reserve the right to distribute this or future versions of
+ * Coccinelle under other licenses.
+ *)
+
+
+(* split patch per file *)
+
+let is_diff = Str.regexp "diff "
+let split_patch i =
+ let patches = ref [] in
+ let cur = ref [] in
+ let rec loop _ =
+ let l = input_line i in
+ (if Str.string_match is_diff l 0
+ then
+ (if List.length !cur > 0
+ then begin patches := List.rev !cur :: !patches; cur := [l] end)
+ else cur := l :: !cur);
+ loop() in
+ try loop() with End_of_file -> !patches
+
+(* ------------------------------------------------------------------------ *)
+
+(* can get_maintainers takea file as an argument, or only a patch? *)
+let resolve_maintainers cmd patches =
+ let maintainer_table = Hashtbl.create (List.length patches) in
+ List.iter
+ (function
+ diff_line::rest ->
+ (match Str.split (Str.regexp " a/") diff_line with
+ [before;after] ->
+ (match Str.split (Str.regexp " ") after with
+ file::_ ->
+ (match Common.cmd_to_list (cmd ^ " " ^ file) with
+ [info] ->
+ let cell =
+ try Hashtbl.find maintainer_table info
+ with Not_found ->
+ let cell = ref [] in
+ Hashtbl.add maintainer_table info cell;
+ cell in
+ cell := (diff_line :: rest) :: !cell
+ | _ -> failwith "badly formatted maintainer result")
+ | _ -> failwith "filename not found")
+ | _ ->
+ failwith (Printf.sprintf "prefix a/ not found in %s" diff_line))
+ | _ -> failwith "bad diff line")
+ patches;
+ maintainer_table
+
+(* ------------------------------------------------------------------------ *)
+
+let print_all o l =
+ List.iter (function x -> Printf.fprintf o "%s\n" x) l
+
+let make_output_files template maintainer_table patch =
+ let ctr = ref 0 in
+ Hashtbl.iter
+ (function maintainers ->
+ function diffs ->
+ ctr := !ctr + 1;
+ let o = open_out (Printf.sprintf "%s%d" patch !ctr) in
+ Printf.fprintf o "To: %s\n\n" maintainers;
+ print_all o template;
+ List.iter (print_all o) (List.rev !diffs);
+ close_out o)
+ maintainer_table
+
+(* ------------------------------------------------------------------------ *)
+
+let command = ref "get_maintainers.pl"
+let file = ref ""
+
+let options =
+ ["-cmd", Arg.String (function x -> command := x), "get maintainer command"]
+
+let usage = ""
+
+let anonymous x = file := x
+
+let _ =
+ Arg.parse (Arg.align options) (fun x -> file := x) usage;
+ let i = open_in !file in
+ let patches = split_patch i in
+ let maintainer_table = resolve_maintainers !command patches in
+ let template = Common.cmd_to_list (Printf.sprintf "cat %s.tmp" !file) in
+ make_output_files template maintainer_table !file