Skip to content

Instantly share code, notes, and snippets.

@SHyx0rmZ
Last active August 29, 2015 14:19
Show Gist options
  • Select an option

  • Save SHyx0rmZ/fbafc684f2e2aaa31521 to your computer and use it in GitHub Desktop.

Select an option

Save SHyx0rmZ/fbafc684f2e2aaa31521 to your computer and use it in GitHub Desktop.
Trying to build ls -l in Erlang
-module(ls).
-export([main/1,name_server/3,entry_builder/2]).
-include_lib("kernel/include/file.hrl").
-record(list_entry, { name, permissions, links, user, group, size, modified }).
main([ Arguments ]) ->
Entries = list(atom_to_list(Arguments)),
Lengths = determine_lengths(Entries, [ #list_entry.links, #list_entry.user, #list_entry.group, #list_entry.size ]),
lists:map(fun(Entry) -> print_list_entry(Entry#list_entry{ name = build_name(Entry) }, Lengths) end, lists:sort(fun sort_by_names/2, Entries)),
init:stop().
print_list_entry(Entry, Lengths) when is_record(Entry, list_entry) ->
io:format("~s ~*.B ~*s ~*s ~*.B ~s ~ts~n", [ Entry#list_entry.permissions, lists:nth(1, Lengths), Entry#list_entry.links, lists:nth(2, Lengths), Entry#list_entry.user, lists:nth(3, Lengths), Entry#list_entry.group, lists:nth(4, Lengths), Entry#list_entry.size, Entry#list_entry.modified, Entry#list_entry.name ]).
determine_lengths(Entries, List) ->
lists:reverse(determine_lengths(Entries, List, [])).
determine_lengths(_, [], Lengths) ->
Lengths;
determine_lengths(Entries, [ Index | List ], Lengths) ->
Max = lists:max(lists:map(fun(Entry) -> element(Index, Entry) end, Entries)),
Length = determine_length(Max),
determine_lengths(Entries, List, [ Length | Lengths ]).
determine_length(Element) when is_integer(Element) ->
length(lists:nth(1, io_lib:format("~B", [ Element ])));
determine_length(Element) when is_list(Element) ->
length(lists:nth(1, io_lib:format("~ts", [ Element ]))).
sort_by_names(Entry1, Entry2) ->
string:to_lower(Entry1#list_entry.name) < string:to_lower(Entry2#list_entry.name).
list(Dir) ->
{ ok, Entries } = file:list_dir(Dir),
FullEntries = lists:map(fun(Entry) -> filename:join(Dir, Entry) end, Entries),
spawn(?MODULE, name_server, [ user_server, self(), "/etc/passwd" ]),
spawn(?MODULE, name_server, [ group_server, self(), "/etc/group" ]),
wait_for_servers([ user_server, group_server ]),
lists:map(fun(Entry) -> spawn(?MODULE, entry_builder, [ self(), Entry ]) end, FullEntries),
list_builder(length(FullEntries), []).
list_builder(0, Entries) ->
Entries;
list_builder(N, Entries) ->
receive
{ _, entry, Entry } ->
list_builder(N - 1, [ Entry | Entries ])
end.
entry_builder(Pid, Entry) ->
{ ok, Info } = file:read_link_info(Entry),
CompleteEntry = #list_entry{
name = Entry,
permissions = build_permission(Info),
links = Info#file_info.links,
user = get_resolved_from(Info#file_info.uid, user_server),
group = get_resolved_from(Info#file_info.gid, group_server),
size = Info#file_info.size,
modified = build_modified(Info)
},
Pid ! { self(), entry, CompleteEntry }.
build_name(Entry) when is_record(Entry, list_entry) ->
Name = filename:basename(Entry#list_entry.name),
[ Type, _, _, X1, _, _, X2, _, _, X3 ] = Entry#list_entry.permissions,
if
Type =:= "d" ->
io_lib:format("\033[1;34m~ts\033[0m", [ Name ]);
true ->
if
X1 =:= "x" orelse X2 =:= "x" orelse X3 =:= "x" ->
io_lib:format("\033[1;32m~ts\033[0m", [ Name ]);
true ->
Extension = case filename:extension(Name) of
[ $. | Ext ] ->
Ext;
[] ->
[]
end,
IsArchive = lists:member(Extension, [ "tar", "zip", "xz", "gz", "bz2" ]),
IsMedia = lists:member(Extension, [ "bmp", "png", "jpg", "jpeg", "gif", "mp4" ]),
if
IsArchive ->
io_lib:format("\033[1;31m~ts\033[0m", [ Name ]);
IsMedia ->
io_lib:format("\033[1;35m~ts\033[0m", [ Name ]);
true ->
Name
end
end
end.
build_permission(Info) when is_record(Info, file_info) ->
Mode = Info#file_info.mode,
<<_:7, UR:1, UW:1, UX:1, GR:1, GW:1, GX:1, OR:1, OW:1, OX:1>> = <<Mode:16/integer>>,
build_permission([ { build_type(Info), 1 }, { r, UR }, { w, UW }, { x, UX }, { r, GR }, { w, GW}, { x, GX }, { r, OR }, { w, OW }, { x, OX } ]);
build_permission(List) when is_list(List) ->
lists:map(fun build_permission/1, List);
build_permission({ _, 0 }) ->
$-;
build_permission({ Atom, _ }) ->
atom_to_list(Atom).
build_type(Info) ->
Type = Info#file_info.type,
case Type of
directory ->
d;
symlink ->
l;
_ ->
'-'
end.
get_resolved_from(Id, Server) ->
Server ! { self(), resolve, Id },
receive
{ Server, resolved, Resolved } ->
Resolved
end.
build_modified(Info) when is_record(Info, file_info) ->
{ { Year, Month, Day }, { Hour, Minute, _ } } = Info#file_info.mtime,
{ Days, _ } = calendar:time_difference(Info#file_info.mtime, calendar:universal_time()),
if
Days =< 180 ->
io_lib:format("~3s ~2B ~2..0B:~2..0B", [ build_modified(Month), Day, Hour, Minute ]);
true ->
io_lib:format("~3s ~2B ~4B", [ build_modified(Month), Day, Year ])
end;
build_modified(Month) ->
lists:nth(Month, [ "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" ]).
wait_for_servers([]) -> ok;
wait_for_servers([ Server | Servers ]) ->
receive
{ Server, initialized } ->
wait_for_servers(Servers)
end.
parse_file(Fields, Binary) ->
parse_file(Fields, Binary, []).
parse_file(_, <<>>, Acc) ->
maps:from_list(Acc);
parse_file(Fields, Binary, Acc) ->
{ Line, Rest } = parse_line(Binary),
{ Id, Name } = parse_fields(Fields, Line),
parse_file(Fields, Rest, [ { list_to_integer(Id), Name } | Acc ]).
parse_line(Binary) ->
parse_line(Binary, <<>>).
parse_line(<<$\n, Rest/binary>>, Line) ->
{ Line, Rest };
parse_line(<<Char:1/binary, Rest/binary>>, Line) ->
parse_line(Rest, <<Line/binary, Char/binary>>).
parse_fields(Fields, Line) ->
List = parse_fields(Line, <<>>, []),
list_to_tuple(lists:map(fun(Field) -> lists:nth(Field, List) end, Fields)).
parse_fields(<<>>, _, List) -> lists:reverse(List);
parse_fields(<<$:, Rest/binary>>, Field, List) ->
parse_fields(Rest, <<>>, [ binary_to_list(Field) | List ]);
parse_fields(<<Char:1/binary, Rest/binary>>, Field, List) ->
parse_fields(Rest, <<Field/binary, Char/binary>>, List).
name_server(Name, Parent, File) ->
{ ok, Content } = file:read_file(File),
Map = parse_file([ 3, 1 ], Content),
register(Name, self()),
Parent ! { Name, initialized },
name_server(Name, Map).
name_server(Name, Map) ->
receive
{ Process, resolve, Id } ->
Process ! { Name, resolved, maps:get(Id, Map) },
name_server(Name, Map)
end.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment