aboutsummaryrefslogtreecommitdiffstats
path: root/lib/kernel/src/packages.erl
diff options
context:
space:
mode:
Diffstat (limited to 'lib/kernel/src/packages.erl')
-rw-r--r--lib/kernel/src/packages.erl158
1 files changed, 158 insertions, 0 deletions
diff --git a/lib/kernel/src/packages.erl b/lib/kernel/src/packages.erl
new file mode 100644
index 0000000000..e0b1f36b85
--- /dev/null
+++ b/lib/kernel/src/packages.erl
@@ -0,0 +1,158 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2002-2009. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+-module(packages).
+
+-export([to_string/1, concat/1, concat/2, is_valid/1, is_segmented/1,
+ split/1, last/1, first/1, strip_last/1, find_modules/1,
+ find_modules/2]).
+
+%% A package name (or a package-qualified module name) may be an atom or
+%% a string (list of nonnegative integers) - not a deep list, and not a
+%% list containing atoms. A name may be empty, but may not contain two
+%% consecutive period (`.') characters or end with a period character.
+
+-type package_name() :: atom() | string().
+
+-spec to_string(package_name()) -> string().
+to_string(Name) when is_atom(Name) ->
+ atom_to_list(Name);
+to_string(Name) ->
+ Name.
+
+%% `concat' does not insert a leading period if the first segment is
+%% empty. However, the result may contain leading, consecutive or
+%% dangling period characters, if any of the segments after the first
+%% are empty. Use 'is_valid' to check the result if necessary.
+
+-spec concat(package_name(), package_name()) -> string().
+concat(A, B) ->
+ concat([A, B]).
+
+-spec concat([package_name()]) -> string().
+concat([H | T]) when is_atom(H) ->
+ concat([atom_to_list(H) | T]);
+concat(["" | T]) ->
+ concat_1(T);
+concat(L) ->
+ concat_1(L).
+
+concat_1([H | T]) when is_atom(H) ->
+ concat_1([atom_to_list(H) | T]);
+concat_1([H]) ->
+ H;
+concat_1([H | T]) ->
+ H ++ "." ++ concat_1(T);
+concat_1([]) ->
+ "";
+concat_1(Name) ->
+ erlang:error({badarg, Name}).
+
+-spec is_valid(package_name()) -> boolean().
+is_valid(Name) when is_atom(Name) ->
+ is_valid_1(atom_to_list(Name));
+is_valid([$. | _]) ->
+ false;
+is_valid(Name) ->
+ is_valid_1(Name).
+
+is_valid_1([$.]) -> false;
+is_valid_1([$., $. | _]) -> false;
+is_valid_1([H | T]) when is_integer(H), H >= 0 ->
+ is_valid_1(T);
+is_valid_1([]) -> true;
+is_valid_1(_) -> false.
+
+-spec split(package_name()) -> [string()].
+split(Name) when is_atom(Name) ->
+ split_1(atom_to_list(Name), []);
+split(Name) ->
+ split_1(Name, []).
+
+split_1([$. | T], Cs) ->
+ [lists:reverse(Cs) | split_1(T, [])];
+split_1([H | T], Cs) when is_integer(H), H >= 0 ->
+ split_1(T, [H | Cs]);
+split_1([], Cs) ->
+ [lists:reverse(Cs)];
+split_1(_, _) ->
+ erlang:error(badarg).
+
+%% This is equivalent to testing if `split(Name)' yields a list of
+%% length larger than one (i.e., if the name can be split into two or
+%% more segments), but is cheaper.
+
+-spec is_segmented(package_name()) -> boolean().
+is_segmented(Name) when is_atom(Name) ->
+ is_segmented_1(atom_to_list(Name));
+is_segmented(Name) ->
+ is_segmented_1(Name).
+
+is_segmented_1([$. | _]) -> true;
+is_segmented_1([H | T]) when is_integer(H), H >= 0 ->
+ is_segmented_1(T);
+is_segmented_1([]) -> false;
+is_segmented_1(_) ->
+ erlang:error(badarg).
+
+-spec last(package_name()) -> string().
+last(Name) ->
+ last_1(split(Name)).
+
+last_1([H]) -> H;
+last_1([_ | T]) -> last_1(T).
+
+-spec first(package_name()) -> [string()].
+first(Name) ->
+ first_1(split(Name)).
+
+first_1([H | T]) when T =/= [] -> [H | first_1(T)];
+first_1(_) -> [].
+
+-spec strip_last(package_name()) -> string().
+strip_last(Name) ->
+ concat(first(Name)).
+
+%% This finds all modules available for a given package, using the
+%% current code server search path. (There is no guarantee that the
+%% modules are loadable; only that the object files exist.)
+
+-spec find_modules(package_name()) -> [string()].
+find_modules(P) ->
+ find_modules(P, code:get_path()).
+
+-spec find_modules(package_name(), [string()]) -> [string()].
+find_modules(P, Paths) ->
+ P1 = filename:join(packages:split(P)),
+ find_modules(P1, Paths, code:objfile_extension(), sets:new()).
+
+find_modules(P, [Path | Paths], Ext, S0) ->
+ case file:list_dir(filename:join(Path, P)) of
+ {ok, Fs} ->
+ Fs1 = [F || F <- Fs, filename:extension(F) =:= Ext],
+ S1 = lists:foldl(fun (F, S) ->
+ F1 = filename:rootname(F, Ext),
+ sets:add_element(F1, S)
+ end,
+ S0, Fs1),
+ find_modules(P, Paths, Ext, S1);
+ _ ->
+ find_modules(P, Paths, Ext, S0)
+ end;
+find_modules(_P, [], _Ext, S) ->
+ sets:to_list(S).