2 * Copyright 2005-2010, Ecole des Mines de Nantes, University of Copenhagen
3 * Yoann Padioleau, Julia Lawall, Rene Rydhof Hansen, Henrik Stuart, Gilles Muller, Nicolas Palix
4 * This file is part of Coccinelle.
6 * Coccinelle is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, according to version 2 of the License.
10 * Coccinelle is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with Coccinelle. If not, see <http://www.gnu.org/licenses/>.
18 * The authors reserve the right to distribute this or future versions of
19 * Coccinelle under other licenses.
24 * Copyright 2005-2010, Ecole des Mines de Nantes, University of Copenhagen
25 * Yoann Padioleau, Julia Lawall, Rene Rydhof Hansen, Henrik Stuart, Gilles Muller, Nicolas Palix
26 * This file is part of Coccinelle.
28 * Coccinelle is free software: you can redistribute it and/or modify
29 * it under the terms of the GNU General Public License as published by
30 * the Free Software Foundation, according to version 2 of the License.
32 * Coccinelle is distributed in the hope that it will be useful,
33 * but WITHOUT ANY WARRANTY; without even the implied warranty of
34 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
35 * GNU General Public License for more details.
37 * You should have received a copy of the GNU General Public License
38 * along with Coccinelle. If not, see <http://www.gnu.org/licenses/>.
40 * The authors reserve the right to distribute this or future versions of
41 * Coccinelle under other licenses.
45 (* split patch per file *)
47 let git_tree = "/var/linuxes/linux-next"
49 let maintainer_command file
=
51 "cd %s; scripts/get_maintainer.pl --separator , --nomultiline --nogit -f %s"
54 let subsystem_command file
=
56 "cd %s; scripts/get_maintainer.pl --nogit --subsystem -f %s | grep -v @"
59 let checkpatch_command file
=
60 Printf.sprintf
"cd %s; scripts/checkpatch.pl %s" git_tree file
62 let default_string = "THE REST" (* split by file *)
64 let extra_cc = Some
"kernel-janitors@vger.kernel.org" (* comma separated *)
66 let prefix_before = Some
"/var/linuxes/linux-next"
67 let prefix_after = Some
"/var/julia/linuxcopy"
69 (* ------------------------------------------------------------------------ *)
71 let spaces = Str.regexp
"[ \t]"
73 let fix_before_after l prefix
= function
75 (match Str.split
spaces l
with
76 ("diff"|"+++"|"---")::_
->
77 (match Str.split
(Str.regexp old_prefix
) l
with
79 (match Str.split_delim
(Str.regexp
("[ \t]"^prefix
)) a
with
80 [_
;""] -> a^b
(* prefix is alwaredy there *)
87 match Str.split
spaces l
with
88 (("+++"|"---") as a
)::path
::rest
-> Printf.sprintf
"%s %s" a path
91 (* ------------------------------------------------------------------------ *)
93 let is_diff = Str.regexp
"diff "
95 let patches = ref [] in
98 match Str.split_delim
(Str.regexp
",") l
with
99 [_
;size
] -> int_of_string size
100 | _
-> failwith
("bad size: "^l
) in
101 let rec read_diff_or_atat _
=
102 let l = input_line i
in
103 let l = fix_date(fix_before_after l "a" prefix_before) in
104 let l = fix_date(fix_before_after l "b" prefix_after) in
105 match Str.split
spaces l with
107 (if List.length
!cur > 0
108 then patches := List.rev
!cur :: !patches);
111 | "@@"::min
::pl
::"@@"::rest
->
112 let msize = get_size min
in
113 let psize = get_size pl
in
115 read_hunk
msize psize
116 | "\\"::_
-> cur := l :: !cur; read_diff_or_atat()
119 "expected diff or @@ (diffstat information should not be present)"
121 let l = input_line i
in
122 let l = fix_date(fix_before_after l "a" prefix_before) in
123 let l = fix_date(fix_before_after l "b" prefix_after) in
125 match Str.split
spaces l with
126 "+++"::_
-> read_diff_or_atat()
128 and read_hunk
msize psize =
129 if msize = 0 && psize = 0
130 then read_diff_or_atat()
132 let l = input_line i
in
134 match Str.split
spaces l with
135 "-"::_
-> read_hunk
(msize - 1) psize
136 | "+"::_
-> read_hunk
msize (psize - 1)
137 | _
-> read_hunk
(msize - 1) (psize - 1) in
138 try read_diff_or_atat()
139 with End_of_file
-> List.rev
((List.rev
!cur)::!patches)
141 (* ------------------------------------------------------------------------ *)
143 (* can get_maintainers take a file as an argument, or only a patch? *)
144 let resolve_maintainers patches =
145 let maintainer_table = Hashtbl.create
(List.length
patches) in
149 (match Str.split
(Str.regexp
" a/") diff_line
with
151 (match Str.split
spaces after
with
154 List.hd
(Common.cmd_to_list
(maintainer_command file
)) in
158 | Some
extra_cc -> maintainers ^
"," ^
extra_cc in
160 Common.cmd_to_list
(subsystem_command file
) in
161 let info = (subsystems,maintainers) in
163 try Hashtbl.find
maintainer_table info
166 Hashtbl.add
maintainer_table info cell;
168 cell := (file
,(diff_line
:: rest
)) :: !cell
169 | _
-> failwith
"filename not found")
171 failwith
(Printf.sprintf
"prefix a/ not found in %s" diff_line
))
172 | _
-> failwith
"bad diff line")
176 (* ------------------------------------------------------------------------ *)
178 let common_prefix l1 l2
=
179 let rec loop = function
180 ([],_
) | (_
,[]) -> []
181 | (x
::xs
,y
::ys
) when x
= y
-> x
:: (loop (xs
,ys
))
183 match loop (l1
,l2
) with
186 (Printf.sprintf
"found nothing in common for %s and %s"
187 (String.concat
"/" l1
) (String.concat
"/" l2
))
190 let merge_files the_rest files
=
191 let butlast l = if the_rest
then l else List.rev
(List.tl
(List.rev
l)) in
192 match List.map
(function s
-> Str.split
(Str.regexp
"/") s
) files
with
194 let rec loop res
= function
195 [] -> String.concat
"/" res
196 | x
::rest
-> loop (common_prefix res x
) rest
in
197 loop (butlast first
) rest
198 | _
-> failwith
"not possible"
200 (* ------------------------------------------------------------------------ *)
203 List.iter
(function x
-> Printf.fprintf o
"%s\n" x
) l
205 let make_output_files template
maintainer_table patch
=
209 (function (services
,maintainers) ->
212 if services
=[default_string]
214 (* if no maintainer, then one file per diff *)
216 (function (file
,diff
) ->
218 (!ctr,true,maintainers,[file
],[diff
]))
224 let (files
,diffs
) = List.split
(List.rev
!diffs
) in
225 (!ctr,false,maintainers,files
,diffs
)::rest
227 maintainer_table [] in
228 let number = List.length
elements in
230 (function (ctr,the_rest
,maintainers,files
,diffs
) ->
231 let output_file = Printf.sprintf
"%s%d" patch
ctr in
232 let o = open_out
output_file in
233 Printf.fprintf
o "To: %s\n\n" maintainers;
234 Printf.fprintf
o "Subject: [PATCH %d/%d] %s: "
235 ctr number (merge_files the_rest files
);
236 print_all o template
;
237 let (nm
,o1
) = Filename.open_temp_file
"patch" "patch" in
238 List.iter
(print_all o1
) (List.rev diffs
);
242 (Printf.sprintf
"diffstat -p1 < %s ; /bin/rm %s" nm nm
) in
243 List.iter
(print_all o) [diffstat];
244 Printf.fprintf
o "\n";
245 List.iter
(print_all o) diffs
;
248 Common.cmd_to_list_and_status
249 (checkpatch_command ((Sys.getcwd
())^
"/"^
output_file)) in
250 if not
(stat
= Unix.WEXITED
0)
251 then (print_all stderr
info; Printf.fprintf stderr
"\n"))
253 let later = Printf.sprintf
"%s%d" patch
(number + 1) in
254 if Sys.file_exists
later
255 then Printf.fprintf stderr
"Warning: %s and other files may be left over from a previous run\n" later
257 (* ------------------------------------------------------------------------ *)
265 let anonymous x
= file := x
268 Arg.parse
(Arg.align
options) (fun x
-> file := x
) usage;
269 let i = open_in
!file in
270 let patches = split_patch i in
272 let maintainer_table = resolve_maintainers patches in
273 let template = Common.cmd_to_list
(Printf.sprintf
"cat %s.tmp" !file) in
274 make_output_files template maintainer_table !file