Permalink
Fetching contributors…
Cannot retrieve contributors at this time
executable file 990 lines (946 sloc) 40.9 KB
#!/usr/bin/env escript
%%!
%-*-Mode:erlang;coding:utf-8;tab-width:4;c-basic-offset:4;indent-tabs-mode:()-*-
% ex: set ft=erlang fenc=utf-8 sts=4 ts=4 sw=4 et:
%%%
%%%------------------------------------------------------------------------
%%% BSD LICENSE
%%%
%%% Copyright (c) 2013-2016, Michael Truog <mjtruog at gmail dot com>
%%% All rights reserved.
%%%
%%% Redistribution and use in source and binary forms, with or without
%%% modification, are permitted provided that the following conditions are met:
%%%
%%% * Redistributions of source code must retain the above copyright
%%% notice, this list of conditions and the following disclaimer.
%%% * Redistributions in binary form must reproduce the above copyright
%%% notice, this list of conditions and the following disclaimer in
%%% the documentation and/or other materials provided with the
%%% distribution.
%%% * All advertising materials mentioning features or use of this
%%% software must display the following acknowledgment:
%%% This product includes software developed by Michael Truog
%%% * The name of the author may not be used to endorse or promote
%%% products derived from this software without specific prior
%%% written permission
%%%
%%% THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
%%% CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
%%% INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
%%% OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
%%% DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
%%% CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
%%% SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
%%% BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
%%% SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
%%% INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
%%% WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
%%% NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
%%% OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
%%% DAMAGE.
%%%------------------------------------------------------------------------
-author('mjtruog [at] gmail (dot) com').
-vsn("1.5.5").
-mode(compile).
-record(config,
{
directories_in = [],
prefix_ignore = undefined,
prefix_scope = undefined,
directory_suffix_old = "_original",
include_directories = [],
undo = false,
replace_usage = false,
help = false,
version = false
}).
-record(state,
{
files_source = [],
files_include = [],
files_applications = [],
files_protobuffs = [],
include_directories = [],
name_conversions = []
}).
-record(progress_bar,
{
percentage = true,
width_bar = $+,
width_space = $ ,
width_end = $|,
width = 40,
percentage_current = 0,
refresh = true,
stdout,
count_i = 0,
count_x
}).
-include_lib("kernel/include/file.hrl").
%-define(USE_IGOR, undefined).
main(Arguments) ->
handle_operation(parse_command_line(Arguments)).
handle_operation(#config{version = true,
help = Help}) ->
io:format("~s~n",[version_info()]),
if
Help ->
help(),
exit_code(1);
true ->
exit_code(0)
end;
handle_operation(#config{help = true}) ->
help(),
exit_code(1);
handle_operation(#config{directories_in = DirectoriesIn,
prefix_ignore = PrefixIgnore,
prefix_scope = PrefixScope,
directory_suffix_old = DirectorySuffixOld,
undo = true})
when DirectoriesIn /= [], is_list(PrefixIgnore), is_list(PrefixScope),
is_list(DirectorySuffixOld) ->
ok = undo_mv_old(DirectoriesIn, DirectorySuffixOld),
ok = undo_rm_scope(DirectoriesIn, PrefixScope),
exit_code(0);
handle_operation(#config{directories_in = DirectoriesIn,
prefix_ignore = PrefixIgnore,
prefix_scope = PrefixScope,
replace_usage = true})
when DirectoriesIn /= [], is_list(PrefixIgnore), is_list(PrefixScope) ->
#state{files_source = FilesSource,
files_include = FilesInclude,
files_applications = FilesApplications,
name_conversions = NameConversions} = files_find(DirectoriesIn,
PrefixIgnore,
PrefixScope,
true),
Regexes = create_regexes(NameConversions),
ProgressBar = progress_bar_pid(erlang:length(FilesSource) +
erlang:length(FilesInclude)),
Fconversion = fun(FilePath) ->
{ok, FileContents} = file:read_file(FilePath),
NewFileContents = reduce(Regexes, erlang:binary_to_list(FileContents)),
ok = file:write_file(FilePath, NewFileContents),
progress_bar_pid_increment(ProgressBar, 1)
end,
lists:foreach(Fconversion, FilesSource),
lists:foreach(Fconversion, FilesInclude),
files_update_apps(FilesApplications, orddict:from_list(NameConversions)),
io:format("~ncheck modified files!~n", []),
exit_code(0);
handle_operation(#config{directories_in = DirectoriesIn,
prefix_ignore = PrefixIgnore,
prefix_scope = PrefixScope,
directory_suffix_old = DirectorySuffixOld,
include_directories = IncludeDirectories0})
when DirectoriesIn /= [], is_list(PrefixIgnore), is_list(PrefixScope),
is_list(DirectorySuffixOld) ->
#state{files_source = FilesSource,
files_include = FilesInclude,
files_applications = FilesApplications,
files_protobuffs = FilesProtobuffs,
include_directories = IncludeDirectories1,
name_conversions = NameConversions} = files_find(DirectoriesIn,
PrefixIgnore,
PrefixScope,
false),
if
NameConversions == [] ->
io:format("no files to process!~n", []),
exit_code(0);
true ->
ok
end,
% n.b., since only a prefix is added it is always true that no
% resulting names in the NameConversions list are also source names
% (so no cycles within the lookup)
NameLookup = orddict:from_list(NameConversions),
rename(FilesSource, FilesInclude,
IncludeDirectories0 ++ IncludeDirectories1,
NameConversions, NameLookup, DirectorySuffixOld),
ok = files_rename_apps(FilesApplications, NameLookup),
ok = files_move_old(FilesApplications, DirectorySuffixOld),
ok = files_rename_protobuffs(FilesProtobuffs, PrefixScope),
ok = files_move_old(FilesProtobuffs, DirectorySuffixOld),
exit_code(0).
undo_mv_old([], _) ->
ok;
undo_mv_old([Directory | DirectoriesIn], DirectorySuffixOld) ->
true = filelib:is_dir(Directory),
case lists:suffix("/src", Directory) of
true ->
[$c, $r, $s, $/ | DirectoryPrefix] = lists:reverse(Directory),
IncludeDirectory = filename:join(lists:reverse(DirectoryPrefix),
"include"),
OldIncludeDirectory = IncludeDirectory ++ DirectorySuffixOld,
case filelib:is_dir(OldIncludeDirectory) of
true ->
{ok, IncludeFilePaths} = file:list_dir(OldIncludeDirectory),
ok = undo_mv_old_dir(IncludeFilePaths,
OldIncludeDirectory,
IncludeDirectory),
ok = file:del_dir(OldIncludeDirectory);
false ->
ok
end;
false ->
ok
end,
OldDirectory = Directory ++ DirectorySuffixOld,
case filelib:is_dir(OldDirectory) of
true ->
{ok, FilePaths} = file:list_dir(OldDirectory),
ok = undo_mv_old_dir(FilePaths, OldDirectory, Directory),
ok = file:del_dir(OldDirectory),
ok;
false ->
ok
end,
undo_mv_old(DirectoriesIn, DirectorySuffixOld).
undo_mv_old_dir([], _, _) ->
ok;
undo_mv_old_dir([FilePath | FilePaths], OldDirectory, Directory) ->
OldFilePath = filename:join(OldDirectory, FilePath),
NewFilePath = filename:join(Directory, FilePath),
case file:read_file_info(NewFilePath, [mtime]) of
{ok, #file_info{mtime = NewFileMtime}} ->
{ok, #file_info{mtime = OldFileMtime}} =
file:read_file_info(OldFilePath, [mtime]),
if
NewFileMtime > OldFileMtime ->
ok = file:delete(OldFilePath);
true ->
ok = file:rename(OldFilePath, NewFilePath)
end;
{error, _} ->
ok = file:rename(OldFilePath, NewFilePath)
end,
undo_mv_old_dir(FilePaths, OldDirectory, Directory).
undo_rm_scope([], _) ->
ok;
undo_rm_scope([Directory | DirectoriesIn], PrefixScope) ->
case lists:suffix("/src", Directory) of
true ->
[$c, $r, $s, $/ | DirectoryPrefix] = lists:reverse(Directory),
IncludeDirectory = filename:join(lists:reverse(DirectoryPrefix),
"include"),
case filelib:is_dir(IncludeDirectory) of
true ->
{ok, IncludeFilePaths} = file:list_dir(IncludeDirectory),
ok = undo_rm_scope_dir(IncludeFilePaths,
IncludeDirectory, PrefixScope);
false ->
ok
end;
false ->
ok
end,
true = filelib:is_dir(Directory),
{ok, FilePaths} = file:list_dir(Directory),
ok = undo_rm_scope_dir(FilePaths, Directory, PrefixScope),
undo_rm_scope(DirectoriesIn, PrefixScope).
undo_rm_scope_dir([], _, _) ->
ok;
undo_rm_scope_dir([FilePath | FilePaths], Directory, PrefixScope) ->
case lists:prefix(PrefixScope, FilePath) of
true ->
FullFilePath = filename:join(Directory, FilePath),
ok = file:delete(FullFilePath);
false ->
ok
end,
undo_rm_scope_dir(FilePaths, Directory, PrefixScope).
files_find(DirectoriesIn,
PrefixIgnore, PrefixScope, ReplaceUsage) ->
files_find(DirectoriesIn,
PrefixIgnore, PrefixScope, ReplaceUsage, #state{}).
files_find([], _, _, _,
#state{files_source = FilesSource,
files_include = FilesInclude,
files_applications = FilesApplications,
files_protobuffs = FilesProtobuffs,
include_directories = IncludeDirectories,
name_conversions = NameConversions} = State) ->
State#state{files_source = lists:reverse(FilesSource),
files_include = lists:reverse(FilesInclude),
files_applications = lists:reverse(FilesApplications),
files_protobuffs = lists:reverse(FilesProtobuffs),
include_directories = lists:reverse(IncludeDirectories),
name_conversions = lists:reverse(NameConversions)};
files_find([Directory | DirectoriesIn],
PrefixIgnore, PrefixScope, ReplaceUsage,
#state{include_directories = IncludeDirectories} = State0) ->
true = filelib:is_dir(Directory),
NextState = case lists:suffix("/src", Directory) of
true ->
[$c, $r, $s, $/ | DirectoryPrefix] = lists:reverse(Directory),
IncludeDirectory = filename:join(lists:reverse(DirectoryPrefix),
"include"),
case filelib:is_dir(IncludeDirectory) of
true ->
{ok, IncludeFilePaths} =
file:list_dir(IncludeDirectory),
State1 = case lists:member(IncludeDirectory,
IncludeDirectories) of
true ->
State0;
false ->
State0#state{include_directories =
[IncludeDirectory |
IncludeDirectories]}
end,
files_find_dir_include(IncludeFilePaths, IncludeDirectory,
PrefixIgnore, PrefixScope,
ReplaceUsage, State1);
false ->
State0
end;
false ->
State0
end,
{ok, FilePaths} = file:list_dir(Directory),
files_find(DirectoriesIn, PrefixIgnore, PrefixScope, ReplaceUsage,
files_find_dir_src(FilePaths, Directory,
PrefixIgnore, PrefixScope,
ReplaceUsage, NextState)).
files_find_dir_include([], _, _, _, _, State) ->
State;
files_find_dir_include([FilePath | FilePaths], Directory,
PrefixIgnore, PrefixScope, ReplaceUsage,
#state{files_include = FilesInclude,
name_conversions = NameConversions} = State) ->
FileExtension = filename:extension(FilePath),
FileName = filename:basename(FilePath, FileExtension),
NewState = if
FileExtension == ".hrl" ->
case lists:prefix(PrefixIgnore, FileName) of
true ->
NewFilesInclude = if
ReplaceUsage ->
[filename:join(Directory, FilePath) |
FilesInclude];
true ->
FilesInclude
end,
State#state{files_include = NewFilesInclude};
false ->
case lists:prefix(PrefixScope, FileName) of
true ->
State;
false ->
NewFilesInclude = if
ReplaceUsage ->
FilesInclude;
true ->
[filename:join(Directory, FilePath) |
FilesInclude]
end,
Convert = {erlang:list_to_atom(FileName),
erlang:list_to_atom(PrefixScope ++
FileName)},
State#state{
files_include = NewFilesInclude,
name_conversions =
lists:umerge(NameConversions,
[Convert])}
end
end;
true ->
State
end,
files_find_dir_include(FilePaths, Directory,
PrefixIgnore, PrefixScope, ReplaceUsage,
NewState).
files_find_dir_src([], _, _, _, _, State) ->
State;
files_find_dir_src([FilePath | FilePaths], Directory,
PrefixIgnore, PrefixScope, ReplaceUsage,
#state{files_source = FilesSource,
files_include = FilesInclude,
files_applications = FilesApplications,
files_protobuffs = FilesProtobuffs,
name_conversions = NameConversions} = State) ->
FileExtension = filename:extension(FilePath),
FileName = filename:basename(FilePath, FileExtension),
ApplicationFile = if
FileExtension == ".app" ->
true;
FileExtension == ".src" ->
filename:extension(FileName) == ".app";
true ->
false
end,
NextState = if
ApplicationFile ->
case lists:prefix(PrefixIgnore, FileName) of
true ->
NewFilesApplications = if
ReplaceUsage ->
[filename:join(Directory, FilePath) |
FilesApplications];
true ->
FilesApplications
end,
State#state{files_applications = NewFilesApplications};
false ->
case lists:prefix(PrefixScope, FileName) of
true ->
State;
false ->
BaseFileName = if
FileExtension == ".src" ->
filename:basename(FileName, ".app");
true ->
FileName
end,
NewFilesApplications = if
ReplaceUsage ->
FilesApplications;
true ->
[filename:join(Directory, FilePath) |
FilesApplications]
end,
Convert = {erlang:list_to_atom(BaseFileName),
erlang:list_to_atom(PrefixScope ++
BaseFileName)},
State#state{
files_applications = NewFilesApplications,
name_conversions =
lists:umerge(NameConversions, [Convert])}
end
end;
FileExtension == ".erl" ->
FileNameContents = case lists:prefix("Elixir.", FileName) of
true ->
string:to_lower(string:sub_string(FileName, 8));
false ->
FileName
end,
case lists:prefix(PrefixIgnore, FileNameContents) of
true ->
NewFilesSource = if
ReplaceUsage ->
[filename:join(Directory, FilePath) |
FilesSource];
true ->
FilesSource
end,
State#state{files_source = NewFilesSource};
false ->
case lists:prefix(PrefixScope, FileName) of
true ->
State;
false ->
Convert = {erlang:list_to_atom(FileName),
erlang:list_to_atom(PrefixScope ++
FileName)},
NewFilesSource = if
ReplaceUsage ->
FilesSource;
true ->
[filename:join(Directory, FilePath) |
FilesSource]
end,
State#state{
files_source = NewFilesSource,
name_conversions =
lists:umerge(NameConversions, [Convert])}
end
end;
FileExtension == ".hrl" ->
case lists:prefix(PrefixIgnore, FileName) of
true ->
NewFilesInclude = if
ReplaceUsage ->
[filename:join(Directory, FilePath) |
FilesInclude];
true ->
FilesInclude
end,
State#state{files_include = NewFilesInclude};
false ->
case lists:prefix(PrefixScope, FileName) of
true ->
State;
false ->
Convert = {erlang:list_to_atom(FileName),
erlang:list_to_atom(PrefixScope ++
FileName)},
NewFilesInclude = if
ReplaceUsage ->
FilesInclude;
true ->
[filename:join(Directory, FilePath) |
FilesInclude]
end,
State#state{
files_include = NewFilesInclude,
name_conversions =
lists:umerge(NameConversions, [Convert])}
end
end;
FileExtension == ".proto" ->
case lists:prefix(PrefixIgnore, FileName) of
true ->
State;
false ->
case lists:prefix(PrefixScope, FileName) of
true ->
State;
false ->
OutputFileName = FileName ++ "_pb",
Convert = {erlang:list_to_atom(OutputFileName),
erlang:list_to_atom(PrefixScope ++
OutputFileName)},
NewFilesProtobuffs = if
ReplaceUsage ->
FilesProtobuffs;
true ->
[filename:join(Directory, FilePath) |
FilesProtobuffs]
end,
State#state{
files_protobuffs = NewFilesProtobuffs,
name_conversions =
lists:umerge(NameConversions, [Convert])}
end
end;
true ->
State
end,
files_find_dir_src(FilePaths, Directory,
PrefixIgnore, PrefixScope, ReplaceUsage,
NextState).
files_rename_apps([], _) ->
ok;
files_rename_apps([FilePath | FilesApplications], NameLookup) ->
FileExtension = filename:extension(FilePath),
FileName = filename:basename(FilePath, FileExtension),
ApplicationName = if
FileExtension == ".app" ->
erlang:list_to_atom(FileName);
FileExtension == ".src" ->
erlang:list_to_atom(filename:basename(FileName, ".app"))
end,
NewApplicationName = orddict:fetch(ApplicationName, NameLookup),
NewFileName = if
FileExtension == ".app" ->
erlang:atom_to_list(NewApplicationName) ++ ".app";
FileExtension == ".src" ->
erlang:atom_to_list(NewApplicationName) ++ ".app.src"
end,
NewFilePath = filename:join(filename:dirname(FilePath), NewFileName),
% process the application file for renamed modules and applications
true = filelib:is_regular(FilePath),
{ok, [{application,
ApplicationNameFile,
ApplicationEnv0}]} = file:consult(FilePath),
true = (ApplicationNameFile =:= ApplicationName),
ApplicationEnv2 = case lists:keytake(modules, 1, ApplicationEnv0) of
{value,
{modules, ModulesList},
ApplicationEnv1} ->
NewModulesList = lists:map(fun(M) ->
% all source file directories must have been provided
orddict:fetch(M, NameLookup)
end, ModulesList),
[{modules, NewModulesList} | ApplicationEnv1];
false ->
ApplicationEnv0
end,
ApplicationEnv4 = case lists:keytake(mod, 1, ApplicationEnv2) of
false ->
ApplicationEnv2;
{value,
{mod, {ApplicationModule, ApplicationModuleList}}, ApplicationEnv3} ->
NewApplicationModule = orddict:fetch(ApplicationModule, NameLookup),
[{mod, {NewApplicationModule,
ApplicationModuleList}} | ApplicationEnv3]
end,
ApplicationEnv6 = case lists:keytake(registered, 1, ApplicationEnv4) of
false ->
ApplicationEnv4;
{value, {registered, RegisteredNames}, ApplicationEnv5} ->
NewRegisteredNames = lists:map(fun(M) ->
case orddict:find(M, NameLookup) of
{ok, NewM} ->
NewM;
error ->
M
end
end, RegisteredNames),
[{registered, NewRegisteredNames} | ApplicationEnv5]
end,
ApplicationEnv8 = case lists:keytake(applications, 1, ApplicationEnv6) of
false ->
ApplicationEnv6;
{value, {applications, ApplicationNames}, ApplicationEnv7} ->
NewApplicationNames = lists:map(fun(M) ->
case orddict:find(M, NameLookup) of
{ok, NewM} ->
NewM;
error ->
M
end
end, ApplicationNames),
[{applications, NewApplicationNames} | ApplicationEnv7]
end,
ApplicationEnv10 = case lists:keytake(included_applications, 1,
ApplicationEnv8) of
false ->
ApplicationEnv8;
{value, {included_applications, IncludedApplicationNames},
ApplicationEnv9} ->
NewIncludedApplicationNames = lists:map(fun(M) ->
case orddict:find(M, NameLookup) of
{ok, NewM} ->
NewM;
error ->
M
end
end, IncludedApplicationNames),
[{included_applications, NewIncludedApplicationNames} |
ApplicationEnv9]
end,
ApplicationEnvN = ApplicationEnv10,
ok = file:write_file(NewFilePath,
io_lib:format("~p.~n",
[{application,
NewApplicationName,
ApplicationEnvN}])),
files_rename_apps(FilesApplications, NameLookup).
files_update_apps([], _) ->
ok;
files_update_apps([FilePath | FilesApplications], NameLookup) ->
FileExtension = filename:extension(FilePath),
FileName = filename:basename(FilePath, FileExtension),
ApplicationName = if
FileExtension == ".app" ->
erlang:list_to_atom(FileName);
FileExtension == ".src" ->
erlang:list_to_atom(filename:basename(FileName, ".app"))
end,
% process the application file for renamed applications
true = filelib:is_regular(FilePath),
{ok, [{application,
ApplicationNameFile,
ApplicationEnv0}]} = file:consult(FilePath),
true = (ApplicationNameFile =:= ApplicationName),
ApplicationEnv2 = case lists:keytake(applications, 1, ApplicationEnv0) of
false ->
ApplicationEnv0;
{value, {applications, ApplicationNames}, ApplicationEnv1} ->
NewApplicationNames = lists:map(fun(M) ->
case orddict:find(M, NameLookup) of
{ok, NewM} ->
NewM;
error ->
M
end
end, ApplicationNames),
[{applications, NewApplicationNames} | ApplicationEnv1]
end,
ApplicationEnvN = ApplicationEnv2,
ok = file:write_file(FilePath,
io_lib:format("~p.~n",
[{application,
ApplicationName,
ApplicationEnvN}])),
files_update_apps(FilesApplications, NameLookup).
files_rename_protobuffs([], _) ->
ok;
files_rename_protobuffs(FilesProtobuffs, PrefixScope) ->
Regexes = lists:map(fun(FilePath) ->
FileName = filename:basename(FilePath),
{ok, R} = re:compile("import \"" ++ FileName ++ "\";"),
fun (S) ->
re:replace(S, R,
"import \"" ++ PrefixScope ++ FileName ++ "\";",
[global, {return, list}])
end
end, FilesProtobuffs),
pforeach(fun(FilePath) ->
{ok, FileContents} = file:read_file(FilePath),
NewFileContents = reduce(Regexes, erlang:binary_to_list(FileContents)),
FileName = filename:basename(FilePath),
NewFilePath = filename:join(filename:dirname(FilePath),
PrefixScope ++ FileName),
ok = file:write_file(NewFilePath, NewFileContents)
end, FilesProtobuffs).
files_move_old([], _) ->
ok;
files_move_old([FilePath | FilePaths], DirectorySuffixOld) ->
Directory = filename:dirname(FilePath),
NewDirectory = Directory ++ DirectorySuffixOld,
case filelib:is_dir(NewDirectory) of
true ->
ok;
false ->
ok = file:make_dir(NewDirectory)
end,
NewFilePath = filename:join(NewDirectory, filename:basename(FilePath)),
ok = file:rename(FilePath, NewFilePath),
true = filelib:is_regular(NewFilePath),
files_move_old(FilePaths, DirectorySuffixOld).
create_regexes(NameConversions) ->
OrderedNameConversions = lists:sort(lists:map(fun({Name1, Name2}) ->
{erlang:length(erlang:atom_to_list(Name1)), Name1, Name2}
end, NameConversions)),
% make sure regexes will be processed with the longest names first
% to avoid erroneous replacements
% (not necessary, just a way of making bugs simpler)
lists:map(fun({_, Name1, Name2}) ->
{ok, R} = re:compile("([^a-z0-9_])" ++
erlang:atom_to_list(Name1) ++
"([^a-z0-9_])"),
fun (S) ->
re:replace(S, R,
"\\1" ++ erlang:atom_to_list(Name2) ++ "\\2",
[global, {return, list}])
end
end, lists:reverse(OrderedNameConversions)).
reduce([], V) ->
V;
reduce([H | T], V) ->
reduce(T, H(V)).
-ifdef(USE_IGOR).
rename(FilesSource, FilesInclude, IncludeDirectories,
NameConversions, NameLookup, DirectorySuffixOld) ->
rename_igor(FilesSource, FilesInclude, IncludeDirectories,
NameConversions, NameLookup, DirectorySuffixOld).
rename_igor(FilesSource, _FilesInclude, IncludeDirectories,
NameConversions, _NameLookup, DirectorySuffixOld) ->
_Output = igor:rename(FilesSource, NameConversions,
[{stubs, false},
{backups, false},
{preprocess, true},
{includes, IncludeDirectories}]),
ok = files_move_old(FilesSource, DirectorySuffixOld),
ok.
-else.
rename(FilesSource, FilesInclude, IncludeDirectories,
NameConversions, NameLookup, DirectorySuffixOld) ->
rename_regex(FilesSource, FilesInclude, IncludeDirectories,
NameConversions, NameLookup, DirectorySuffixOld).
rename_regex(FilesSource, FilesInclude, _IncludeDirectories,
NameConversions, NameLookup, DirectorySuffixOld) ->
Regexes = create_regexes(NameConversions),
ProgressBar = progress_bar_pid(erlang:length(FilesSource) +
erlang:length(FilesInclude)),
Fconversion = fun(FileExtension) -> fun(FilePath) ->
{ok, FileContents} = file:read_file(FilePath),
NewFileContents = reduce(Regexes, erlang:binary_to_list(FileContents)),
Name1 = filename:basename(FilePath, FileExtension),
Name2 = erlang:atom_to_list(orddict:fetch(erlang:list_to_atom(Name1),
NameLookup)),
NewFilePath = filename:join(filename:dirname(FilePath),
Name2 ++ FileExtension),
ok = file:write_file(NewFilePath, NewFileContents),
progress_bar_pid_increment(ProgressBar, 1)
end end,
pforeach(Fconversion(".erl"), FilesSource),
pforeach(Fconversion(".hrl"), FilesInclude),
ok = files_move_old(FilesSource, DirectorySuffixOld),
ok = files_move_old(FilesInclude, DirectorySuffixOld),
ok.
pforeach(F, L) when length(L) =< 4 ->
lists:foreach(F, L);
pforeach(F, L) ->
N = erlang:system_info(schedulers),
Pids = lists:map(fun(_) ->
erlang:spawn_link(fun pforeach_pid/0)
end, lists:seq(1, N)),
pforeach_run([], Pids, L, F).
pforeach_pid() ->
receive
{execute, F, Arg1} ->
F(Arg1),
pforeach_pid();
{exit, Pid} ->
Pid ! exit,
ok
end.
pforeach_run(OldPids, NewPids, [], _F) ->
Self = self(),
Pids = lists:reverse(OldPids) ++ NewPids,
lists:foreach(fun(Pid) ->
Pid ! {exit, Self}
end, Pids),
lists:foreach(fun(_) ->
receive exit -> ok end
end, Pids),
ok;
pforeach_run(OldPids, [], L, F) ->
pforeach_run([], lists:reverse(OldPids), L, F);
pforeach_run(OldPids, [Pid | NewPids], [Arg1 | L], F) ->
Pid ! {execute, F, Arg1},
pforeach_run([Pid | OldPids], NewPids, L, F).
-endif.
parse_command_line(Arguments) ->
Config0 = parse_command_line(Arguments, #config{}),
Config1 = if
Config0#config.prefix_ignore =:= undefined ->
Config0#config{prefix_ignore = Config0#config.prefix_scope};
Config0#config.prefix_scope =:= undefined ->
Config0#config{prefix_scope = Config0#config.prefix_ignore};
true ->
Config0
end,
Config2 = Config1#config{
directories_in = lists:reverse(Config1#config.directories_in)},
ConfigN = Config2,
true = (((if ConfigN#config.help -> 1; true -> 0 end) +
(if ConfigN#config.undo -> 1; true -> 0 end) +
(if ConfigN#config.replace_usage -> 1; true -> 0 end)) =< 1),
ConfigN.
parse_command_line([], Config) ->
Config;
parse_command_line(["-d", Directory | Arguments],
#config{directories_in = Directories} = Config) ->
false = lists:member(Directory, Directories),
NewConfig = Config#config{directories_in = [Directory | Directories]},
parse_command_line(Arguments, NewConfig);
parse_command_line(["-p", PrefixIgnore | Arguments],
#config{prefix_ignore = undefined} = Config) ->
NewConfig = Config#config{prefix_ignore = PrefixIgnore},
parse_command_line(Arguments, NewConfig);
parse_command_line(["-s", PrefixScope | Arguments],
#config{prefix_scope = undefined} = Config) ->
NewConfig = Config#config{prefix_scope = PrefixScope},
parse_command_line(Arguments, NewConfig);
parse_command_line(["-b", DirectorySuffixOld | Arguments],
Config) ->
NewConfig = Config#config{directory_suffix_old = DirectorySuffixOld},
parse_command_line(Arguments, NewConfig);
parse_command_line(["-c", Directory | Arguments],
#config{include_directories = Directories} = Config) ->
false = lists:member(Directory, Directories),
NewConfig = Config#config{include_directories = [Directory | Directories]},
parse_command_line(Arguments, NewConfig);
parse_command_line(["-r" | Arguments], Config) ->
NewConfig = Config#config{replace_usage = true},
parse_command_line(Arguments, NewConfig);
parse_command_line(["-u" | Arguments], Config) ->
NewConfig = Config#config{undo = true},
parse_command_line(Arguments, NewConfig);
parse_command_line(["-h" | Arguments], Config) ->
NewConfig = Config#config{help = true},
parse_command_line(Arguments, NewConfig);
parse_command_line(["-V" | Arguments], Config) ->
NewConfig = Config#config{version = true},
parse_command_line(Arguments, NewConfig);
parse_command_line([Unknown | _], _) ->
erlang:exit({badarg, {command_line, Unknown}}).
version_info() ->
{ok, Data} = file:read_file(?FILE),
ByteSize = erlang:byte_size(Data),
<<B01:32, B02:32, B03:32, B04:32, B05:32, B06:32, B07:32, B08:32,
B09:32, B10:32, B11:32, B12:32, B13:32, B14:32, B15:32, B16:32>> =
crypto:hash(sha512, Data),
Info = ?MODULE:module_info(),
{attributes, Attributes} = lists:keyfind(attributes, 1, Info),
{vsn, Version} = lists:keyfind(vsn, 1, Attributes),
io_lib:format("scope v~s~n"
"~8.16.0b~8.16.0b~8.16.0b~8.16.0b~8.16.0b~8.16.0b~8.16.0b"
"~8.16.0b~8.16.0b~8.16.0b~8.16.0b~8.16.0b~8.16.0b~8.16.0b"
"~8.16.0b~8.16.0b sha512sum, ~6w bytes -------",
[Version,
B01, B02, B03, B04, B05, B06, B07, B08,
B09, B10, B11, B12, B13, B14, B15, B16,
ByteSize]).
progress_bar_new(CountX)
when is_integer(CountX), CountX >= 0 ->
#progress_bar{stdout = erlang:open_port({fd, 0, 1}, [out, {line, 256}]),
count_x = CountX}.
progress_bar_increment(I, #progress_bar{percentage_current = PercentageCurrent,
refresh = Refresh,
count_i = CountI,
count_x = CountX} = ProgressBar)
when is_integer(I), I > 0 ->
NewCountI = CountI + I,
NewPercentageCurrent = erlang:min(erlang:round((NewCountI /
CountX) * 100.0), 100),
NewRefresh = Refresh orelse (PercentageCurrent /= NewPercentageCurrent),
ProgressBar#progress_bar{percentage_current = NewPercentageCurrent,
refresh = NewRefresh,
count_i = NewCountI}.
progress_bar_display(#progress_bar{refresh = false} = ProgressBar) ->
ProgressBar;
progress_bar_display(#progress_bar{percentage = Percentage,
width_bar = WidthBar,
width_space = WidthSpace,
width_end = WidthEnd,
width = Width,
percentage_current = PercentageCurrent,
refresh = true,
stdout = STDOUT} = ProgressBar) ->
WidthCurrent = erlang:min(erlang:round(Width *
(PercentageCurrent / 100.0)), Width),
Line = ["\r", (if
WidthCurrent == Width ->
lists:duplicate(WidthCurrent, WidthBar);
WidthCurrent < Width ->
[lists:duplicate(WidthCurrent, WidthBar),
lists:duplicate(Width - (WidthCurrent + 1), WidthSpace),
[WidthEnd]]
end), (if
Percentage =:= true ->
io_lib:format(" ~3.. w%", [PercentageCurrent]);
Percentage =:= false ->
[]
end)],
erlang:port_command(STDOUT, Line),
ProgressBar#progress_bar{refresh = false}.
progress_bar_destroy(#progress_bar{stdout = STDOUT}) ->
erlang:port_command(STDOUT, "\n"),
erlang:port_close(STDOUT),
ok.
progress_bar_pid(CountX) ->
erlang:spawn_link(fun() ->
progress_bar(progress_bar_new(CountX))
end).
progress_bar_pid_increment(Pid, I)
when is_pid(Pid), is_integer(I), I > 0 ->
Pid ! {progress_bar_increment, I},
ok.
progress_bar(#progress_bar{refresh = true} = ProgressBar) ->
progress_bar(progress_bar_display(ProgressBar));
progress_bar(#progress_bar{count_i = CountI,
count_x = CountX} = ProgressBar)
when CountI >= CountX ->
progress_bar_destroy(ProgressBar);
progress_bar(#progress_bar{} = ProgressBar) ->
receive
{progress_bar_increment, I} ->
progress_bar(progress_bar_increment(I, ProgressBar))
end.
exit_code(ExitCode) ->
erlang:halt(ExitCode, [{flush, true}]).
help() ->
Config = #config{},
io:format(
"Usage: ~s [OPTION]~n"
"~n"
" -d DIR Source directory of an application for input files~n"
" -s SCOPE Scope to apply~n"
" -p SCOPE Scope to ignore~n"
" -b SUFFIX Set the subdirectory name suffix for file backups~n"
" (default=\"~s\")~n"
" -c DIR Add a directory to the include path~n"
" -u Undo the scope operation mode~n"
" -r Replace ignored scope usage of apply scope mode~n"
" (done before the apply scope is first created)~n"
" -V Display the version information~n"
" -h Display command line options (as shown here)~n"
"~n"
" Note1: When processed files are moved to the application source directory~n"
" with the old suffix (-b option), it is possible an older file will~n"
" be silently clobbered~n"
" Note2: When using the -r mode, files are modified in-place (i.e., all files~n"
" matching the ignored scope) and the result may require manual~n"
" modification so that data structure names do not look obtuse~n"
,
[escript:script_name(),
Config#config.directory_suffix_old]).