Release coccinelle-0.2.3rc1
[bpt/coccinelle.git] / tools / gitgrep.ml
CommitLineData
5636bb2c
C
1(*
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.
5 *
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.
9 *
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.
14 *
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/>.
17 *
18 * The authors reserve the right to distribute this or future versions of
19 * Coccinelle under other licenses.
20 *)
21
22
9f8e26f4 23(*
ae4735db 24 * Copyright 2005-2010, Ecole des Mines de Nantes, University of Copenhagen
9f8e26f4
C
25 * Yoann Padioleau, Julia Lawall, Rene Rydhof Hansen, Henrik Stuart, Gilles Muller, Nicolas Palix
26 * This file is part of Coccinelle.
27 *
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.
31 *
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.
36 *
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/>.
39 *
40 * The authors reserve the right to distribute this or future versions of
41 * Coccinelle under other licenses.
42 *)
43
44
34e49164
C
45(* adjust as convenient *)
46let prefix = "/tmp/"
47let prefix = ""
48
49(* The -grouped option means that all - and + code must appear in a
50single contiguous block of - + code. This option has no effect on the
51other kinds of patterns, ie Changelog (C) or Context (@) *)
52
53(* example: gitgrep -grouped -maxlen 25 - "[A-Z][A-Z]+" + "[A-Z][A-Z]+"
54usb_21_22 *)
55
56type dir = Minus | Plus | Context | ChangeLog
57
58type res = Git of string | Block of int * string
59
60let grouped = ref false
61let maxlen = ref None
62
63let space = Str.regexp " "
64
65let matches pattern line =
66 try let _ = Str.search_forward pattern line 0 in true
67 with Not_found -> false
68
69let res = ref []
70
71let scan dir pattern i =
72 let rec loop skipping cl git =
73 let line = input_line i in
74 match Str.split space line with
75 ["commit";git] -> loop false true git
76 | "diff"::_ -> loop skipping false git
77 | _ ->
78 if String.length line > 0 && not skipping &&
79 ((String.get line 0 = '-' && dir = Minus) or
80 (String.get line 0 = '+' && dir = Plus) or
81 (cl && dir = ChangeLog) or
82 (not (String.get line 0 = '-') && not (String.get line 0 = '+') &&
83 dir = Context)) &&
84 matches pattern line
85 then (res := Git(git)::!res; loop true cl git)
86 else loop skipping cl git in
87 loop false false ""
88
89(* for Minus and Plus directions only *)
90let scan_grouped dir pattern i =
91 let block = ref 0 in
92 (* mp = true in minus-plus region *)
93 let rec loop mp git =
94 let line = input_line i in
95 match Str.split space line with
96 ["commit";git] -> loop false git
97 | "diff"::_ -> loop false git
98 | _ ->
99 if String.length line > 0
100 then
101 let first_char = String.get line 0 in
102 let new_mp =
103 match first_char with
104 '-' | '+' -> (if not mp then block := !block + 1; true)
105 | _ -> false in
106 match (first_char,dir) with
107 ('-',Minus) | ('+',Plus) ->
108 let info = Block(!block,git) in
109 (if matches pattern line && not (List.mem info !res)
110 then res := info::!res);
111 loop new_mp git
112 | _ -> loop new_mp git
113 else loop mp git in
114 loop false ""
115
116let scan_line max i =
117 let rec loop skipping num git =
118 let line = input_line i in
119 match Str.split space line with
120 ["commit";git1] ->
121 loop false (-1) git1
122 | "diff"::_ ->
123 if num > max && not skipping
124 then (res:=Git(git)::!res;loop true (num+1) git)
125 else loop skipping (if num = (-1) then 1 else num+1) git
126 | _ ->
127 if num > max && not skipping
128 then (res:=Git(git)::!res;loop true (num+1) git)
129 else loop skipping (if num = (-1) then num else num+1) git in
130 loop false (-1) ""
131
132let dot = Str.regexp "\\."
133
134let open_git file =
135 let tmp = prefix^file in
136 if Sys.file_exists tmp
137 then open_in tmp
138 else
139 match List.rev (Str.split dot file) with
140 last::rest ->
141 let last_int = int_of_string last in
142 if last_int = 0
143 then
144 failwith
145 "can't go back one version from 0; make the log file by hand";
146 let prev =
147 String.concat "." (List.rev ((string_of_int (last_int-1))::rest)) in
148 let _ =
149 Sys.command
150 (Printf.sprintf "git log -p v%s..v%s > %s" prev file tmp) in
151 open_in tmp
152 | _ -> open_in file
153
154let rec split_args = function
155 [] -> []
156 | "-grouped"::rest -> grouped := true; split_args rest
157 | "-maxlen"::len::rest -> maxlen := Some (int_of_string len); split_args rest
158 | "-"::pattern::rest -> (Minus,Str.regexp pattern) :: split_args rest
159 | "+"::pattern::rest -> (Plus,Str.regexp pattern) :: split_args rest
160 | "@"::pattern::rest -> (Context,Str.regexp pattern) :: split_args rest
161 | "C"::pattern::rest -> (ChangeLog,Str.regexp pattern) :: split_args rest
162 | _ -> failwith "bad argument list"
163
164let process_one (dir,pattern) version =
165 res := [];
166 let i = open_git version in
167 try
168 if !grouped && (dir = Minus or dir = Plus)
169 then scan_grouped dir pattern i
170 else scan dir pattern i
171 with End_of_file -> (close_in i; List.rev !res)
172
173let process_len max version =
174 res := [];
175 let i = open_git version in
176 try scan_line max i
177 with End_of_file -> (close_in i; List.rev !res)
178
179let inter l1 l2 =
180 List.rev
181 (List.fold_left
182 (function prev ->
183 function
184 (Git(git)) as x ->
185 let rec loop = function
186 [] -> prev
187 | Git(git1)::rest when git = git1 -> x::prev
188 | Block(b1,git1)::rest when git = git1 -> Block(b1,git1)::prev
189 | _::rest -> loop rest in
190 loop l2
191 | (Block(block,git)) as x ->
192 let rec loop = function
193 [] -> prev
194 | Git(git1)::rest when git = git1 -> x::prev
195 | Block(b1,git1)::rest when block = b1 && git = git1 ->
196 Block(b1,git1)::prev
197 | _::rest -> loop rest in
198 loop l2)
199 [] l1)
200
201let _ =
202 if Array.length Sys.argv < 4
203 then failwith "arguments: -/+/@/C pattern -/+/@/C pattern ... version";
204 let args = List.tl(Array.to_list Sys.argv) in
205 let version = List.hd(List.rev args) in
206 let pairs = List.rev(List.tl(List.rev args)) in
207 let requirements = split_args pairs in
208 let res =
209 List.map (function Git x -> x | Block (_,x) -> x)
210 (List.fold_left
211 (function all ->
212 function pattern ->
213 inter (process_one pattern version) all)
214 (process_one (List.hd requirements) version)
215 (List.tl requirements)) in
216 let res =
217 if !grouped
218 then
219 List.rev
220 (List.fold_left
221 (function prev ->
222 function x -> if List.mem x prev then prev else x::prev)
223 [] res)
224 else res in
225 let res =
226 match !maxlen with
227 None -> res
228 | Some max ->
229 let badgits = process_len max version in
230 List.filter (function x -> not(List.mem (Git(x)) badgits)) res in
231 List.iter (function name -> Printf.printf "%s\n" name) res