%%%---------------------------------------------------------------------- %%% File : cvs.erl %%% Author : %%% Purpose : %%% Created : 17 Jul 2001 by %%%---------------------------------------------------------------------- -module(cvs). -author('sean@evasdev'). -vsn('$Id: cvs.erl,v 1.2 2001/07/24 14:14:40 sean Exp $ '). %%-compile(export_all). -export([check/0, check/1]). -export([get_versions/1, check/2]). -export([latest_version/0, latest_version/1]). %% Check an application module in CVS to make sure that the Vsn given %% is the latest version and that there are no files later than those %% tagged with this Vsn. Also check that all fields have been tagged to %% the latest release. %% ----------- Check entire repository -------- check() -> case os:cmd("/opt/rcs/bin/cvsp1 -d /opt/rcs/cvsroot rlog -R .") of "cvsp1 rlog: Logging" ++ Result -> Apps = extract_apps(Result), lists:foreach(fun(X) -> io:format(" Checking: ~s. ",[X]), check(X) end, Apps); Else -> {error, Else} end. check(Application) -> case get_versions(Application) of {ok, Vsns} -> Tag = latest_tag(Vsns), io:format("Latest tagged Version: \t ~s~n",[Tag]), is_vsn_latest_rel(Application, Tag, Vsns), are_there_new_untagged_checkins(Application, Vsns), are_all_files_tagged_with_this_tag(Application, Vsns, Tag), ok; {error, Reason} -> io:format("~s~n",[Reason]) end. check(Application, Vsn) -> case get_versions(Application, Vsn) of {ok, Vsns} -> are_all_files_tagged_with_this_tag(Application, Vsns, Vsn), ok; {error, Reason} -> io:format("~s~n",[Reason]) end. latest_version() -> case os:cmd("/opt/rcs/bin/cvsp1 -d /opt/rcs/cvsroot rlog -R .") of "cvsp1 rlog: Logging" ++ Result -> Apps = extract_apps(Result), lists:foreach(fun(X) -> latest_version(X) end, Apps); Else -> {error, Else} end. latest_version(Application) -> io:format("~-20s - ",[Application]), case get_versions(Application) of {ok, Vsns} -> Tag = latest_tag(Vsns), io:format("~s~n",[Tag]); {error, Reason} -> io:format("~s~n",[Reason]) end. get_versions(Application) -> get_versions1(Application, ""). get_versions(Application, Vsn) -> Dash_vsn = replace_dot_with_dash(Vsn), get_versions1(Application, "-rrel-" ++ Dash_vsn ++ " "). get_versions1(Application, Vsn) -> case os:cmd("/opt/rcs/bin/cvsp1 -d /opt/rcs/cvsroot rlog " ++ Vsn ++ Application) of "cvsp1 rlog: Logging" ++ Result -> case get_vsns(Result) of error -> {error, "Error, can't check versions of:" ++ Application}; Vsns -> {ok, Vsns} end; Else -> {error, "Error, can't check versions of:" ++ Application ++ "\n" ++ Else} end. replace_dot_with_dash("." ++ T) -> [$-|replace_dot_with_dash(T)]; replace_dot_with_dash([H|T]) -> [H|replace_dot_with_dash(T)]; replace_dot_with_dash([]) -> []. get_vsns([]) -> []; get_vsns(Rlog) -> case get_RCS_file(Rlog) of {error, T} -> get_vsns(T); {Path, Tail} -> Filename = filename:basename(Path), case get_head(Tail) of {error, T1} -> get_vsns(T1); {Head_vsn, Tail1} -> case get_sym_names(Tail1) of {error, T2} -> get_vsns(T2); {Names, Tail2} -> [{Filename, Head_vsn, Names}|get_vsns(Tail)] end end end. get_RCS_file(Rlog) -> case get_line(Rlog) of {"RCS file: " ++ File, T} -> case in_attic(File) of yes -> {error, T}; no -> {File, T} end; eof -> {error, []}; {_, T} -> get_RCS_file(T) end. in_attic(File) -> case string:str(File, "Attic") of 0 -> no; _ -> yes end. get_head(Rlog) -> case get_line(Rlog) of {"head: " ++ Head, T} -> {Head, T}; eof -> {error, []}; {_, T} -> get_head(T) end. get_sym_names(Rlog) -> case get_line(Rlog) of {"symbolic names:" ++ _, T} -> get_names(T); eof -> {error, []}; {_, T} -> get_sym_names(T) end. get_names(Names) -> get_names(Names, []). get_names(T, Acc) -> case get_line(T) of {"keyword" ++ _, T1} -> {lists:reverse(Acc), T1}; eof -> {[], []}; {"\trel-" ++ Name, T2} -> get_names(T2, [fmt_vsn(Name)|Acc]); {_, T3} -> get_names(T3, Acc) end. get_line(Lines) -> get_line(Lines, []). get_line("\n" ++ T, Acc) -> {lists:reverse(Acc), T}; get_line([H|T], Acc) -> get_line(T, [H|Acc]); get_line([], Acc) -> eof. fmt_vsn(Vsn) -> case string:tokens(Vsn, "- :") of [Maj, Min, Mod_ver] -> {Maj ++ "." ++ Min, Mod_ver}; Else -> io:format("duff vsn string: rel-~s~n",[Vsn]), {[],[]} end. %% -------- check a few things -------------- %% Check that there are no greater tagged versions than Vsn is_vsn_latest_rel(App, Vsn, [{Filename, Head, Tags}|T]) -> {Tag, Mod} = largest_tag(Tags), if Tag > Vsn -> io:format("Warning. ~s. Later Release of ~s exists in CVS: rel-~s~n",[App, Filename, Tag]); true -> ok end, is_vsn_latest_rel(App, Vsn, T); is_vsn_latest_rel(App, Vsn, []) -> ok. %% Check that there are no module versions greater than the latest %% one tagged for that version. are_there_new_untagged_checkins(App, [{Filename, Head, Tags}|T]) -> {Tag, Mod} = largest_tag(Tags), if Head > Mod -> io:format("Warning. ~s. Later Version of file ~s exists in CVS: ~s~n",[App,Filename, Head]), {ok, Tag}; true -> {ok, Tag} end, are_there_new_untagged_checkins(App, T); are_there_new_untagged_checkins(App, []) -> ok. % ensure_tagged are_all_files_tagged_with_this_tag(App, [{Filename, Head, Tags}|T], Tag) -> case lists:keysearch(Tag, 1, Tags) of {value, {Tag, Mod}} -> are_all_files_tagged_with_this_tag(App, T, Tag); _ -> io:format("Warning. ~s File ~s not tagged with tag ~s~n", [App, Filename, Tag]), are_all_files_tagged_with_this_tag(App, T, Tag) end; are_all_files_tagged_with_this_tag(App, [], Tag) -> ok. latest_tag(Files) -> latest_tag(Files, "0.0 = Not Tagged"). latest_tag([{Filename, Head, Tags}|T], Max) -> {Tag, Mod} = largest_tag(Tags), if Tag > Max -> latest_tag(T, Tag); true -> latest_tag(T, Max) end; latest_tag([], Max) -> Max. largest_tag(Tags) -> largest_tag(Tags, {"0.0", ""}). largest_tag([{Tag, Mod}|T], {Max, Mod1}) when Tag > Max -> largest_tag(T, {Tag, Mod}); largest_tag([{Tag, Mod}|T], Max) -> largest_tag(T, Max); largest_tag([], Max) -> Max. extract_apps(Rlog) -> case get_line(Rlog) of {"cvsp1 rlog: Logging " ++ App, T} -> case string:tokens(App, "/") of ["."] -> extract_apps(T); ["CVSROOT"] -> extract_apps(T); [App_name] -> [App_name|extract_apps(T)]; Else -> extract_apps(T) end; {Else, T} -> extract_apps(T); eof -> [] end.