Erlangでwho

30分プログラム、その351。前回(id:mzp:20080818:who)の続きで、Erlangでwhoを実装してみる。
やっぱりバイナリ構文は便利ですね。ただ、定数が実際にどう宣言されているかを全部見にいかないといけないのが面倒くさい。

使い方

1> who:load("/var/run/utmpx").
[{"utmpx-1.00",[],[]},
 {[],[],[]},
 {"mzp","/","console"},
 {"mzp","s000","ttys000"},
 {"mzp","s001","ttys001"},
 {"mzp","s002","ttys002"},
 {"mzp","s003","ttys003"},
 {"mzp","s004","ttys004"}]
ok

ソースコード

-module(who).
-compile([export_all]).
%% struct utmpx {
%%         char ut_user[_UTX_USERSIZE];
%% 	     /* login name */
%%         char ut_id[_UTX_IDSIZE];        /* id */
%%         char ut_line[_UTX_LINESIZE];    /* tty name */
%%         pid_t ut_pid;                   /* process id creating the entry */
%%         short ut_type;                  /* type of this entry */
%%         struct timeval ut_tv;           /* time entry was created */
%%         char ut_host[_UTX_HOSTSIZE];    /* host name */
%%         __uint32_t ut_pad[16];          /* reserved for future use */
%% };

-define(Size,628).
-define(USERSIZE,256).
-define(IDSIZE,4).
-define(LINESIZE,32).

string(Bin)->
    lists:takewhile(fun(N)-> N > 0 end,binary_to_list(Bin)).

utmpx(<<User:?USERSIZE/binary,
       Id:?IDSIZE/binary,
       Line:?LINESIZE/binary,
       _/binary>>) ->
    {string(User),string(Id),string(Line)}.

who(Bin)->
    {Utmpx,Rest} = split_binary(Bin,?Size),
    case size(Rest) > 0 of
	true ->
	    [utmpx(Utmpx)|who(Rest)];
	_ ->
	    []
    end.

load(Path) ->
    {ok,Bin} = file:read_file(Path),
    io:format("~p~n",[who(Bin)]).