Last active
August 29, 2015 14:19
-
-
Save SHyx0rmZ/fbafc684f2e2aaa31521 to your computer and use it in GitHub Desktop.
Trying to build ls -l in Erlang
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| -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