open Unix
let title = ref "CVS log"
let link = ref ""
let desc = ref ""
let exec = ref None
let max_items = ref None
let options = [
"-t", Arg.Set_string title, "<title> set <title> as title of the channel" ;
"-l", Arg.Set_string link, "<url> set <url> as link of the channel" ;
"-d", Arg.Set_string desc, "<s> set <s> as description of the channel" ;
"-e", Arg.String (fun s -> exec := Some s),
"<options> execute \"cvs log <options>\" to get the log rather than read it from stdin" ;
"-maxitems", Arg.Int (fun n -> max_items := Some n), "<n> keep only <n> items maximum";
]
let string_of_in_channel ic =
let len = 1024 in
let s = String.create len in
let buf = Buffer.create len in
let rec iter () =
try
let n = input ic s 0 len in
if n = 0 then
()
else
(
Buffer.add_substring buf s 0 n;
iter ()
)
with
End_of_file -> ()
in
iter ();
Buffer.contents buf
let get_cvslog params =
let inch = Unix.open_process_in (Printf.sprintf "cvs log %s" params) in
let s = string_of_in_channel inch in
ignore(Unix.close_process_in inch);
s
let date_of_string s =
let f ye mo da ho mi se =
let t =
{
tm_year = ye - 1900 ;
tm_mon = mo - 1;
tm_mday = da ;
tm_wday = 0 ;
tm_hour = ho ;
tm_min = mi ;
tm_sec = 0 ;
tm_yday = 0;
tm_isdst = false ;
}
in
fst (Unix.mktime t)
in
Scanf.sscanf s "%d-%d-%d %d:%d:%d" f
let get_field s pos field =
let f = field^": " in
let len = String.length f in
let re = Str.regexp_string f in
let p = Str.search_forward re s pos in
let p2 = String.index_from s p ';' in
let res = (p2, String.sub s (p+len) (p2 - p - len)) in
res
let analyze_log s =
let s_date = "\ndate" in
let s_end = "----------------------------" in
let re_end = Str.regexp_string s_end in
let t = Hashtbl.create 37 in
let rec next_date s pos =
try
let (p, field) = get_field s pos s_date in
let date = date_of_string field in
try
let (p2, author) = get_field s p "author" in
let (p2, state) = get_field s p2 "state" in
let p3 = String.index_from s p2 '\n' in
if state = "dead" then
next_date s p3
else
let p4 = Str.search_forward re_end s p3 in
let comment = String.sub s (p3 + 1) (p4 - p3 - 1) in
let comment =
try
let (p,_) = get_field comment 0 "branches" in
let p2 = String.index_from comment p '\n' in
String.sub comment (p2+1) ((String.length comment) - p2 - 1)
with _ -> comment
in
let authors_comments =
try Hashtbl.find t date
with Not_found -> []
in
let new_l =
match List.partition (fun (s,_) -> s = author) authors_comments with
([], l) -> (author, [comment]) :: l
| (((a,c)::_), l) ->
if List.mem comment c then
(a, c) :: l
else
(a, c @ [comment]) :: l
in
Hashtbl.replace t date new_l;
next_date s p4
with
_ ->
next_date s (p+1)
with
Not_found ->
()
in
let re_sep =
"\n============================================================================="
in
let l = Str.split (Str.regexp_string re_sep) s in
List.iter (fun s -> next_date s 0) l;
let l = Hashtbl.fold (fun date l acc -> (date, l) :: acc) t [] in
List.sort
(fun(d1,_) (d2,_) -> compare d1 d2)
l
let replace_cr s =
let len = String.length s in
let buf = Buffer.create len in
for i = 0 to len - 1 do
match s.[i] with
'\n' -> Buffer.add_string buf "<br/>\n"
| c -> Buffer.add_char buf c
done;
Buffer.contents buf
let rss_items_of_log s =
let entries = analyze_log s in
let f (date, l) =
List.map
(fun (author, comments) ->
Rss.item
~pubdate: (Rss.float_to_date date)
~title: author
~comments: (String.concat "\n" comments)
()
)
l
in
List.flatten (List.map f entries)
let rec list_n_first n = function
[] -> []
| h :: q ->
if n > 0 then
h :: (list_n_first (n-1) q)
else
[]
let main () =
Arg.parse options (fun _ -> ())
(Printf.sprintf "Usage %s [options]\nwhere options are:" Sys.argv.(0));
let log =
match !exec with
Some params -> get_cvslog params
| None -> string_of_in_channel Pervasives.stdin
in
let items = rss_items_of_log log in
let items =
match !max_items with
None -> items
| Some n -> List.rev (list_n_first n (List.rev items))
in
let channel = Rss.channel
~title: !title
~link: !link
~desc: !desc
~pubdate: (Rss.float_to_date (Unix.time()))
items
in
Rss.print_channel Format.std_formatter channel
let _ = main ()