diff --git a/src/ejabberd_oauth.erl b/src/ejabberd_oauth.erl
index 3e3fc3082..def4b225a 100644
--- a/src/ejabberd_oauth.erl
+++ b/src/ejabberd_oauth.erl
@@ -436,7 +436,7 @@ process(_Handlers,
?INPUT(<<"hidden">>, <<"scope">>, Scope),
?INPUT(<<"hidden">>, <<"state">>, State),
?BR,
- ?LABEL(<<"ttl">>, [?CT(<<"Token TTL">>), ?CT(<<": ">>)]),
+ ?LABEL(<<"ttl">>, [?CT(<<"Token TTL">>), ?C(<<": ">>)]),
?XAE(<<"select">>, [{<<"name">>, <<"ttl">>}],
[
?XAC(<<"option">>, [{<<"value">>, <<"3600">>}],<<"1 Hour">>),
diff --git a/src/mod_muc_log.erl b/src/mod_muc_log.erl
index 61101d1c2..91203530d 100644
--- a/src/mod_muc_log.erl
+++ b/src/mod_muc_log.erl
@@ -974,10 +974,9 @@ roomconfig_to_string(Options, Lang, FileFormat) ->
Os2 = lists:sort(Os1),
Options2 = Title ++ Os2,
lists:foldl(fun ({Opt, Val}, R) ->
- case get_roomconfig_text(Opt) of
+ case get_roomconfig_text(Opt, Lang) of
undefined -> R;
- OptT ->
- OptText = (?T(OptT)),
+ OptText ->
R2 = case Val of
false ->
<<"
",
@@ -1025,49 +1024,49 @@ roomconfig_to_string(Options, Lang, FileFormat) ->
end,
<<"">>, Options2).
-get_roomconfig_text(title) -> <<"Room title">>;
-get_roomconfig_text(persistent) ->
- <<"Make room persistent">>;
-get_roomconfig_text(public) ->
- <<"Make room public searchable">>;
-get_roomconfig_text(public_list) ->
- <<"Make participants list public">>;
-get_roomconfig_text(password_protected) ->
- <<"Make room password protected">>;
-get_roomconfig_text(password) -> <<"Password">>;
-get_roomconfig_text(anonymous) ->
- <<"This room is not anonymous">>;
-get_roomconfig_text(members_only) ->
- <<"Make room members-only">>;
-get_roomconfig_text(moderated) ->
- <<"Make room moderated">>;
-get_roomconfig_text(members_by_default) ->
- <<"Default users as participants">>;
-get_roomconfig_text(allow_change_subj) ->
- <<"Allow users to change the subject">>;
-get_roomconfig_text(allow_private_messages) ->
- <<"Allow users to send private messages">>;
-get_roomconfig_text(allow_private_messages_from_visitors) ->
- <<"Allow visitors to send private messages to">>;
-get_roomconfig_text(allow_query_users) ->
- <<"Allow users to query other users">>;
-get_roomconfig_text(allow_user_invites) ->
- <<"Allow users to send invites">>;
-get_roomconfig_text(logging) -> <<"Enable logging">>;
-get_roomconfig_text(allow_visitor_nickchange) ->
- <<"Allow visitors to change nickname">>;
-get_roomconfig_text(allow_visitor_status) ->
- <<"Allow visitors to send status text in "
- "presence updates">>;
-get_roomconfig_text(captcha_protected) ->
- <<"Make room captcha protected">>;
-get_roomconfig_text(description) ->
- <<"Room description">>;
-%% get_roomconfig_text(subject) -> "Subject";
-%% get_roomconfig_text(subject_author) -> "Subject author";
-get_roomconfig_text(max_users) ->
- <<"Maximum Number of Occupants">>;
-get_roomconfig_text(_) -> undefined.
+get_roomconfig_text(title, Lang) -> ?T(<<"Room title">>);
+get_roomconfig_text(persistent, Lang) ->
+ ?T(<<"Make room persistent">>);
+get_roomconfig_text(public, Lang) ->
+ ?T(<<"Make room public searchable">>);
+get_roomconfig_text(public_list, Lang) ->
+ ?T(<<"Make participants list public">>);
+get_roomconfig_text(password_protected, Lang) ->
+ ?T(<<"Make room password protected">>);
+get_roomconfig_text(password, Lang) -> ?T(<<"Password">>);
+get_roomconfig_text(anonymous, Lang) ->
+ ?T(<<"This room is not anonymous">>);
+get_roomconfig_text(members_only, Lang) ->
+ ?T(<<"Make room members-only">>);
+get_roomconfig_text(moderated, Lang) ->
+ ?T(<<"Make room moderated">>);
+get_roomconfig_text(members_by_default, Lang) ->
+ ?T(<<"Default users as participants">>);
+get_roomconfig_text(allow_change_subj, Lang) ->
+ ?T(<<"Allow users to change the subject">>);
+get_roomconfig_text(allow_private_messages, Lang) ->
+ ?T(<<"Allow users to send private messages">>);
+get_roomconfig_text(allow_private_messages_from_visitors, Lang) ->
+ ?T(<<"Allow visitors to send private messages to">>);
+get_roomconfig_text(allow_query_users, Lang) ->
+ ?T(<<"Allow users to query other users">>);
+get_roomconfig_text(allow_user_invites, Lang) ->
+ ?T(<<"Allow users to send invites">>);
+get_roomconfig_text(logging, Lang) -> ?T(<<"Enable logging">>);
+get_roomconfig_text(allow_visitor_nickchange, Lang) ->
+ ?T(<<"Allow visitors to change nickname">>);
+get_roomconfig_text(allow_visitor_status, Lang) ->
+ ?T(<<"Allow visitors to send status text in "
+ "presence updates">>);
+get_roomconfig_text(captcha_protected, Lang) ->
+ ?T(<<"Make room captcha protected">>);
+get_roomconfig_text(description, Lang) ->
+ ?T(<<"Room description">>);
+%% get_roomconfig_text(subject, Lang) -> "Subject";
+%% get_roomconfig_text(subject_author, Lang) -> "Subject author";
+get_roomconfig_text(max_users, Lang) ->
+ ?T(<<"Maximum Number of Occupants">>);
+get_roomconfig_text(_, _) -> undefined.
%% Users = [{JID, Nick, Role}]
roomoccupants_to_string(Users, _FileFormat) ->
diff --git a/src/mod_muc_room.erl b/src/mod_muc_room.erl
index fde43694c..41e776339 100644
--- a/src/mod_muc_room.erl
+++ b/src/mod_muc_room.erl
@@ -2697,8 +2697,8 @@ find_changed_items(UJID, UAffiliation, URole,
Nick /= <<"">> ->
case find_jids_by_nick(Nick, StateData) of
[] ->
- ErrText = str:format(<<"Nickname ~s does not exist in the room">>,
- [Nick]),
+ ErrText = {<<"Nickname ~s does not exist in the room">>,
+ [Nick]},
throw({error, xmpp:err_not_acceptable(ErrText, Lang)});
JIDList ->
JIDList
diff --git a/src/mod_vcard_ldap.erl b/src/mod_vcard_ldap.erl
index 38c4747e6..869f9348b 100644
--- a/src/mod_vcard_ldap.erl
+++ b/src/mod_vcard_ldap.erl
@@ -324,31 +324,31 @@ default_vcard_map() ->
{<<"PHOTO">>, <<"%s">>, [<<"jpegPhoto">>]}].
default_search_fields() ->
- [{<<"User">>, <<"%u">>},
- {<<"Full Name">>, <<"displayName">>},
- {<<"Given Name">>, <<"givenName">>},
- {<<"Middle Name">>, <<"initials">>},
- {<<"Family Name">>, <<"sn">>},
- {<<"Nickname">>, <<"%u">>},
- {<<"Birthday">>, <<"birthDay">>},
- {<<"Country">>, <<"c">>},
- {<<"City">>, <<"l">>},
- {<<"Email">>, <<"mail">>},
- {<<"Organization Name">>, <<"o">>},
- {<<"Organization Unit">>, <<"ou">>}].
+ [{translate:mark(<<"User">>), <<"%u">>},
+ {translate:mark(<<"Full Name">>), <<"displayName">>},
+ {translate:mark(<<"Given Name">>), <<"givenName">>},
+ {translate:mark(<<"Middle Name">>), <<"initials">>},
+ {translate:mark(<<"Family Name">>), <<"sn">>},
+ {translate:mark(<<"Nickname">>), <<"%u">>},
+ {translate:mark(<<"Birthday">>), <<"birthDay">>},
+ {translate:mark(<<"Country">>), <<"c">>},
+ {translate:mark(<<"City">>), <<"l">>},
+ {translate:mark(<<"Email">>), <<"mail">>},
+ {translate:mark(<<"Organization Name">>), <<"o">>},
+ {translate:mark(<<"Organization Unit">>), <<"ou">>}].
default_search_reported() ->
- [{<<"Full Name">>, <<"FN">>},
- {<<"Given Name">>, <<"FIRST">>},
- {<<"Middle Name">>, <<"MIDDLE">>},
- {<<"Family Name">>, <<"LAST">>},
- {<<"Nickname">>, <<"NICK">>},
- {<<"Birthday">>, <<"BDAY">>},
- {<<"Country">>, <<"CTRY">>},
- {<<"City">>, <<"LOCALITY">>},
- {<<"Email">>, <<"EMAIL">>},
- {<<"Organization Name">>, <<"ORGNAME">>},
- {<<"Organization Unit">>, <<"ORGUNIT">>}].
+ [{translate:mark(<<"Full Name">>), <<"FN">>},
+ {translate:mark(<<"Given Name">>), <<"FIRST">>},
+ {translate:mark(<<"Middle Name">>), <<"MIDDLE">>},
+ {translate:mark(<<"Family Name">>), <<"LAST">>},
+ {translate:mark(<<"Nickname">>), <<"NICK">>},
+ {translate:mark(<<"Birthday">>), <<"BDAY">>},
+ {translate:mark(<<"Country">>), <<"CTRY">>},
+ {translate:mark(<<"City">>), <<"LOCALITY">>},
+ {translate:mark(<<"Email">>), <<"EMAIL">>},
+ {translate:mark(<<"Organization Name">>), <<"ORGNAME">>},
+ {translate:mark(<<"Organization Unit">>), <<"ORGUNIT">>}].
parse_options(Host, Opts) ->
MyHosts = gen_mod:get_opt_hosts(Host, Opts, <<"vjud.@HOST@">>),
diff --git a/src/mod_vcard_mnesia.erl b/src/mod_vcard_mnesia.erl
index d2f4ef52d..08dd4f940 100644
--- a/src/mod_vcard_mnesia.erl
+++ b/src/mod_vcard_mnesia.erl
@@ -95,32 +95,32 @@ search(LServer, Data, AllowReturnAll, MaxMatch) ->
end.
search_fields(_LServer) ->
- [{<<"User">>, <<"user">>},
- {<<"Full Name">>, <<"fn">>},
- {<<"Name">>, <<"first">>},
- {<<"Middle Name">>, <<"middle">>},
- {<<"Family Name">>, <<"last">>},
- {<<"Nickname">>, <<"nick">>},
- {<<"Birthday">>, <<"bday">>},
- {<<"Country">>, <<"ctry">>},
- {<<"City">>, <<"locality">>},
- {<<"Email">>, <<"email">>},
- {<<"Organization Name">>, <<"orgname">>},
- {<<"Organization Unit">>, <<"orgunit">>}].
+ [{translate:mark(<<"User">>), <<"user">>},
+ {translate:mark(<<"Full Name">>), <<"fn">>},
+ {translate:mark(<<"Name">>), <<"first">>},
+ {translate:mark(<<"Middle Name">>), <<"middle">>},
+ {translate:mark(<<"Family Name">>), <<"last">>},
+ {translate:mark(<<"Nickname">>), <<"nick">>},
+ {translate:mark(<<"Birthday">>), <<"bday">>},
+ {translate:mark(<<"Country">>), <<"ctry">>},
+ {translate:mark(<<"City">>), <<"locality">>},
+ {translate:mark(<<"Email">>), <<"email">>},
+ {translate:mark(<<"Organization Name">>), <<"orgname">>},
+ {translate:mark(<<"Organization Unit">>), <<"orgunit">>}].
search_reported(_LServer) ->
- [{<<"Jabber ID">>, <<"jid">>},
- {<<"Full Name">>, <<"fn">>},
- {<<"Name">>, <<"first">>},
- {<<"Middle Name">>, <<"middle">>},
- {<<"Family Name">>, <<"last">>},
- {<<"Nickname">>, <<"nick">>},
- {<<"Birthday">>, <<"bday">>},
- {<<"Country">>, <<"ctry">>},
- {<<"City">>, <<"locality">>},
- {<<"Email">>, <<"email">>},
- {<<"Organization Name">>, <<"orgname">>},
- {<<"Organization Unit">>, <<"orgunit">>}].
+ [{translate:mark(<<"Jabber ID">>), <<"jid">>},
+ {translate:mark(<<"Full Name">>), <<"fn">>},
+ {translate:mark(<<"Name">>), <<"first">>},
+ {translate:mark(<<"Middle Name">>), <<"middle">>},
+ {translate:mark(<<"Family Name">>), <<"last">>},
+ {translate:mark(<<"Nickname">>), <<"nick">>},
+ {translate:mark(<<"Birthday">>), <<"bday">>},
+ {translate:mark(<<"Country">>), <<"ctry">>},
+ {translate:mark(<<"City">>), <<"locality">>},
+ {translate:mark(<<"Email">>), <<"email">>},
+ {translate:mark(<<"Organization Name">>), <<"orgname">>},
+ {translate:mark(<<"Organization Unit">>), <<"orgunit">>}].
remove_user(LUser, LServer) ->
US = {LUser, LServer},
diff --git a/src/mod_vcard_sql.erl b/src/mod_vcard_sql.erl
index fd1d05478..28a6f2ce5 100644
--- a/src/mod_vcard_sql.erl
+++ b/src/mod_vcard_sql.erl
@@ -150,32 +150,32 @@ search(LServer, Data, AllowReturnAll, MaxMatch) ->
end.
search_fields(_LServer) ->
- [{<<"User">>, <<"user">>},
- {<<"Full Name">>, <<"fn">>},
- {<<"Name">>, <<"first">>},
- {<<"Middle Name">>, <<"middle">>},
- {<<"Family Name">>, <<"last">>},
- {<<"Nickname">>, <<"nick">>},
- {<<"Birthday">>, <<"bday">>},
- {<<"Country">>, <<"ctry">>},
- {<<"City">>, <<"locality">>},
- {<<"Email">>, <<"email">>},
- {<<"Organization Name">>, <<"orgname">>},
- {<<"Organization Unit">>, <<"orgunit">>}].
+ [{translate:mark(<<"User">>), <<"user">>},
+ {translate:mark(<<"Full Name">>), <<"fn">>},
+ {translate:mark(<<"Name">>), <<"first">>},
+ {translate:mark(<<"Middle Name">>), <<"middle">>},
+ {translate:mark(<<"Family Name">>), <<"last">>},
+ {translate:mark(<<"Nickname">>), <<"nick">>},
+ {translate:mark(<<"Birthday">>), <<"bday">>},
+ {translate:mark(<<"Country">>), <<"ctry">>},
+ {translate:mark(<<"City">>), <<"locality">>},
+ {translate:mark(<<"Email">>), <<"email">>},
+ {translate:mark(<<"Organization Name">>), <<"orgname">>},
+ {translate:mark(<<"Organization Unit">>), <<"orgunit">>}].
search_reported(_LServer) ->
- [{<<"Jabber ID">>, <<"jid">>},
- {<<"Full Name">>, <<"fn">>},
- {<<"Name">>, <<"first">>},
- {<<"Middle Name">>, <<"middle">>},
- {<<"Family Name">>, <<"last">>},
- {<<"Nickname">>, <<"nick">>},
- {<<"Birthday">>, <<"bday">>},
- {<<"Country">>, <<"ctry">>},
- {<<"City">>, <<"locality">>},
- {<<"Email">>, <<"email">>},
- {<<"Organization Name">>, <<"orgname">>},
- {<<"Organization Unit">>, <<"orgunit">>}].
+ [{translate:mark(<<"Jabber ID">>), <<"jid">>},
+ {translate:mark(<<"Full Name">>), <<"fn">>},
+ {translate:mark(<<"Name">>), <<"first">>},
+ {translate:mark(<<"Middle Name">>), <<"middle">>},
+ {translate:mark(<<"Family Name">>), <<"last">>},
+ {translate:mark(<<"Nickname">>), <<"nick">>},
+ {translate:mark(<<"Birthday">>), <<"bday">>},
+ {translate:mark(<<"Country">>), <<"ctry">>},
+ {translate:mark(<<"City">>), <<"locality">>},
+ {translate:mark(<<"Email">>), <<"email">>},
+ {translate:mark(<<"Organization Name">>), <<"orgname">>},
+ {translate:mark(<<"Organization Unit">>), <<"orgunit">>}].
remove_user(LUser, LServer) ->
ejabberd_sql:sql_transaction(
diff --git a/src/translate.erl b/src/translate.erl
index 240a423d6..aaadcd967 100644
--- a/src/translate.erl
+++ b/src/translate.erl
@@ -29,7 +29,7 @@
-behaviour(gen_server).
--export([start_link/0, reload/0, translate/2]).
+-export([start_link/0, reload/0, translate/2, mark/1]).
%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
terminate/2, code_change/3]).
@@ -219,6 +219,10 @@ translate(Msg) ->
end
end.
+-spec mark(binary()) -> binary().
+mark(Msg) ->
+ Msg.
+
ascii_tolower(B) ->
iolist_to_binary(ascii_tolower_s(binary_to_list(B))).
diff --git a/tools/extract-tr.sh b/tools/extract-tr.sh
new file mode 100755
index 000000000..1a864f6a8
--- /dev/null
+++ b/tools/extract-tr.sh
@@ -0,0 +1,232 @@
+#!/usr/bin/env escript
+%% -*- erlang -*-
+%%! -pa ebin
+
+main([Dir]) ->
+ Txts =
+ filelib:fold_files(
+ Dir, ".+\.beam\$", false,
+ fun(FileIn, Res) ->
+ case get_forms(FileIn) of
+ {ok, Forms} ->
+ Tree = erl_syntax:form_list(Forms),
+ Mod = mod(FileIn),
+ erl_syntax_lib:fold_subtrees(
+ fun(Form, Acc) ->
+ case erl_syntax:type(Form) of
+ function ->
+ case map(Form, Mod) of
+ [] ->
+ Acc;
+ Vars ->
+ Vars ++ Acc
+ end;
+ _ ->
+ Acc
+ end
+ end, [], Tree) ++ Res;
+ _Err ->
+ Res
+ end
+ end, []),
+ Dict = lists:foldl(
+ fun({B, Meta}, Acc) ->
+ dict:update(
+ binary_to_list(B),
+ fun(OldMeta) ->
+ lists:usort([Meta|OldMeta])
+ end,
+ [Meta], Acc)
+ end, dict:new(), Txts),
+ generate_pot(Dict).
+
+map(Tree, Mod) ->
+ Vars = erl_syntax_lib:fold(
+ fun(Form, Acc) ->
+ case erl_syntax:type(Form) of
+ application ->
+ analyze_app(Form, Mod) ++ Acc;
+ _ ->
+ Acc
+ end
+ end, [], Tree),
+ Bins = lists:flatmap(
+ fun({Var, Pos}) when is_atom(Var) ->
+ Res = erl_syntax_lib:fold(
+ fun(Form, Acc) ->
+ case process_match_expr(
+ Form, Var, Mod) of
+ {ok, Binary, NewPos} ->
+ [{Binary, NewPos}|Acc];
+ error ->
+ Acc
+ end
+ end, [], Tree),
+ case Res of
+ [] ->
+ log("~s:~p: unresolved variable: ~s~n",
+ [Mod, Pos, Var]);
+ _ ->
+ ok
+ end,
+ Res;
+ ({Var, Pos}) when is_binary(Var) ->
+ [{Var, Pos}]
+ end, lists:usort(Vars)),
+ [{B, {Mod, Pos}} || {B, Pos} <- Bins, B /= <<"">>].
+
+process_match_expr(Form, Var, Mod) ->
+ case erl_syntax:type(Form) of
+ match_expr ->
+ Pattern = erl_syntax:match_expr_pattern(Form),
+ Body = erl_syntax:match_expr_body(Form),
+ {V, Expr} =
+ case {erl_syntax:type(Pattern), erl_syntax:type(Body)} of
+ {variable, _} ->
+ {erl_syntax:variable_name(Pattern), Body};
+ {_, variable} ->
+ {erl_syntax:variable_name(Body), Pattern};
+ _ ->
+ {'', none}
+ end,
+ Text = maybe_extract_tuple(Expr),
+ if V == Var ->
+ Pos = erl_syntax:get_pos(Text),
+ try {ok, erl_syntax:concrete(Text), Pos}
+ catch _:_ ->
+ case catch erl_syntax_lib:analyze_application(Text) of
+ {_M, {Fn, 1}} when Fn == format_error;
+ Fn == io_format_error ->
+ error;
+ _ ->
+ log("~s:~p: not a binary: ~s~n",
+ [Mod, Pos, erl_prettypr:format(Text)]),
+ {ok, <<>>, Pos}
+ end
+ end;
+ true ->
+ error
+ end;
+ _ ->
+ error
+ end.
+
+maybe_extract_tuple(none) ->
+ none;
+maybe_extract_tuple(Form) ->
+ try
+ tuple = erl_syntax:type(Form),
+ [Text, _] = erl_syntax:tuple_elements(Form),
+ Text
+ catch _:{badmatch, _} ->
+ Form
+ end.
+
+analyze_app(Form, Mod) ->
+ try
+ {M, {F, A}} = erl_syntax_lib:analyze_application(Form),
+ Args = erl_syntax:application_arguments(Form),
+ Txt = case {M, atom_to_list(F), A, Args} of
+ {xmpp, "err_" ++ _, 2, [T|_]} -> T;
+ {xmpp, "serr_" ++ _, 2, [T|_]} -> T;
+ {xmpp, "mk_text", 2, [T|_]} -> T;
+ {translate, "translate", 2, [_,T|_]} -> T;
+ {translate, "mark", 1, [T]} -> T
+ end,
+ Pos = erl_syntax:get_pos(Txt),
+ case erl_syntax:type(Txt) of
+ binary ->
+ try [{erl_syntax:concrete(Txt), Pos}]
+ catch _:_ ->
+ Pos = erl_syntax:get_pos(Txt),
+ log("~s:~p: not a binary: ~s~n",
+ [Mod, Pos, erl_prettypr:format(Txt)]),
+ []
+ end;
+ variable ->
+ [{erl_syntax:variable_name(Txt), Pos}];
+ application ->
+ Vars = sets:to_list(erl_syntax_lib:variables(Txt)),
+ case Vars of
+ [Var] ->
+ [{Var, Pos}];
+ [_|_] ->
+ log("Too many variables: ~p~n", [Vars]),
+ [];
+ [] ->
+ []
+ end;
+ _ ->
+ []
+ end
+ catch _:{badmatch, _} ->
+ [];
+ _:{case_clause, _} ->
+ []
+ end.
+
+generate_pot(Dict) ->
+ io:format("~s~n~n", [pot_header()]),
+ lists:foreach(
+ fun({Msg, Location}) ->
+ S1 = format_location(Location),
+ S2 = format_msg(Msg),
+ io:format("~smsgstr \"\"~n~n", [S1 ++ S2])
+ end, lists:keysort(1, dict:to_list(Dict))).
+
+format_location([A, B, C|T]) ->
+ format_location_list([A,B,C]) ++ format_location(T);
+format_location([A, B|T]) ->
+ format_location_list([A,B]) ++ format_location(T);
+format_location([A|T]) ->
+ format_location_list([A]) ++ format_location(T);
+format_location([]) ->
+ "".
+
+format_location_list(L) ->
+ "#: " ++ string:join(
+ lists:map(
+ fun({File, Pos}) ->
+ io_lib:format("~s:~B", [File, Pos])
+ end, L),
+ " ") ++ io_lib:nl().
+
+format_msg(Bin) ->
+ io_lib:format("msgid \"~s\"~n", [escape(Bin)]).
+
+escape(Bin) ->
+ lists:map(
+ fun($") -> "\\\"";
+ (C) -> C
+ end, binary_to_list(iolist_to_binary(Bin))).
+
+pot_header() ->
+ string:join(
+ ["msgid \"\"",
+ "msgstr \"\"",
+ "\"Project-Id-Version: 15.11.127\\n\"",
+ "\"X-Language: Language Name\\n\"",
+ "\"Last-Translator: Translator name and contact method\\n\"",
+ "\"MIME-Version: 1.0\\n\"",
+ "\"Content-Type: text/plain; charset=UTF-8\\n\"",
+ "\"Content-Transfer-Encoding: 8bit\\n\""],
+ io_lib:nl()).
+
+mod(Path) ->
+ filename:rootname(filename:basename(Path)) ++ ".erl".
+
+log(Format, Args) ->
+ io:format(standard_error, Format, Args).
+
+get_forms(File) ->
+ case beam_lib:chunks(File, [abstract_code]) of
+ {ok, {_, List}} ->
+ case lists:keyfind(abstract_code, 1, List) of
+ {abstract_code, {raw_abstract_v1, Abstr}} ->
+ {ok, Abstr};
+ _ ->
+ error
+ end;
+ _ ->
+ error
+ end.