www
aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYestin L. Harrison <yestin@ylh.io>2022-02-16 02:43:27 -0800
committerYestin L. Harrison <yestin@ylh.io>2022-02-16 02:43:27 -0800
commitf05250406522e58dcedd0109b3582265857a9cc5 (patch)
tree67dde740a1ec4ddc3e32a5a2bf2a6370d49feed7
parentf5561fbbcbc43fbe88cc879ffb5e425877368597 (diff)
downloaddemoheader-f05250406522e58dcedd0109b3582265857a9cc5.tar.gz
demoheader-f05250406522e58dcedd0109b3582265857a9cc5.tar.xz
demoheader-f05250406522e58dcedd0109b3582265857a9cc5.zip
cleanliness
-rw-r--r--src/demoheader.erl100
1 files changed, 41 insertions, 59 deletions
diff --git a/src/demoheader.erl b/src/demoheader.erl
index 821dd43..b9ec948 100644
--- a/src/demoheader.erl
+++ b/src/demoheader.erl
@@ -3,7 +3,7 @@
-export([main/1]).
% bless epp
--define(HEADER,
+-define(FIELDS,
?X('demo-protocol', $d, "(version number)", 32, signed-little),
?X('network-protocol', $n, "(version number)", 32, signed-little),
?X('server-name', $s, "(IP:port string)", 260, bytes),
@@ -19,110 +19,92 @@
-define(X(ATOM, CHAR, DESC, _W, _T), F(ATOM, CHAR, DESC)).
main(Args) ->
F = fun(A, C, D) -> {A, C, atom_to_list(A), undefined, D} end,
- Opts = [
+ args(Args, [
F(json, $j, "Emit JSON"),
- ?HEADER,
+ ?FIELDS,
F(help, $h, "Show this help")
- ],
- case Args of
- [_|_] ->
- args(Opts, Args);
- _ ->
- usage(Opts),
- halt(1)
- end.
+ ]).
-undef(X).
-define(X(ATOM, _C, _D, _W, _T), ATOM).
-is_header(Atom) -> lists:member(Atom, [?HEADER]).
-args(Opts, Args) ->
- DU = fun(F, A) ->
- err("error: " ++ F, A),
- usage(Opts),
- halt(1)
+args(Args, Opts) ->
+ DU = fun(S) ->
+ err("error: ~s", [S]),
+ usage(Opts, 1)
end,
- {Options, NonOptionArgs} = case getopt:parse(Opts, Args) of
- {error, E} -> DU("~s", [getopt:format_error(Opts, E)]);
+ Args =:= [] andalso DU("no arguments given"),
+ {Options, NonOptionArgs} = case getopt:parse(Opts, Args) of
+ {error, E} -> DU(getopt:format_error(Opts, E));
{ok, Res} -> Res
end,
- lists:member(help, Options) andalso begin usage(Opts), halt(0) end,
- Headers = lists:filter(fun is_header/1, Options),
- Retrieval = case lists:member(json, Options) of
- true -> json_of(case Headers of
- [] -> [?HEADER];
- Else -> Else
- end);
+ lists:member(help, Options) andalso usage(Opts, 0),
+ Headers = lists:filter(fun(A) -> lists:member(A, [?FIELDS]) end, Options),
+ Show = case lists:member(json, Options) of
+ true -> json_of(case Headers of [] -> [?FIELDS]; Else -> Else end);
_ -> case Headers of
[One] -> fun(#{One := V}) ->
- Fmt = case is_list(V) of true -> "~s"; _ -> "~p" end,
- io_lib:format(Fmt, [V])
+ io_lib:format(case V of [_|_] -> "~s\n"; _ -> "~w\n" end, [V])
end;
- _ -> DU("without -j, exactly one field must be requested", [])
- end
+ _ -> DU("without -j, exactly one field must be requested")
+ end
end,
Parsed = case NonOptionArgs of
[Single] -> parse(Single);
- _ -> DU("exactly one file must be specified", [])
+ _ -> DU("exactly one file must be specified")
end,
- io:format("~s\n", [Retrieval(Parsed)]).
+ io:put_chars(Show(Parsed)).
-undef(X).
json_of(Keys) ->
fun(Map) ->
F = fun(Key, Acc) ->
#{Key := Value} = Map,
- [io_lib:format("\"~s\": ~p", [atom_to_list(Key), Value])|Acc]
+ [io_lib:format("\"~s\": ~0p", [atom_to_list(Key), Value])|Acc]
end,
- "{\n\t" ++
+ "{\n\t" ++
lists:join(",\n\t", lists:foldl(F, [], lists:reverse(Keys))) ++
- "\n}"
+ "\n}\n"
end.
-define(X(ATOM, _C, _D, WIDTH, TYPE), fun
- ({<<Var:WIDTH/TYPE,Rest/binary>>, Map}) -> {Rest, Map#{ATOM => Var}}
+ ({<<Var:WIDTH/TYPE, Rem/binary>>, Map}) -> {Rem, Map#{ATOM => Var}}
end).
parse(Arg) ->
In = case file:open(Arg, [read, binary]) of
{ok, I} -> I;
- {error, EE} -> die("~s: ~s", [Arg, file:format_error(EE)])
+ {error, E} -> die("~s: ~s", [Arg, file:format_error(E)])
end,
Bin = case file:read(In, 1072) of
- {ok, B} when byte_size(B) =:= 1072 -> B;
- {ok, _} -> die("could not read 1072 bytes (is ~s a demo?)", [Arg]);
- {error, E} -> die("error reading ~s: ~s", [Arg, file:format_error(E)])
- end,
- file:close(In),
- Rem = case Bin of
- <<"HL2DEMO\0", R/binary>> -> R;
- _ -> die("did not see \"HL2DEMO\" at start (is ~s a demo?)", [Arg])
+ {ok, <<"HL2DEMO\0", Rem:1064/bytes>>} -> Rem;
+ {ok, <<_:1072/bytes>>} -> die("no \"HL2DEMO\" at start of file", []);
+ {ok, _} -> die("could not read 1072 bytes", []);
+ {error, EE} -> die("while reading ~s: ~s", [Arg, file:format_error(EE)])
end,
- {<<>>, Map} = lists:foldl(fun(F, A) -> F(A) end, {Rem, #{}}, [?HEADER]),
+ {<<>>, Map} = lists:foldl(fun(F, A) -> F(A) end, {Bin, #{}}, [?FIELDS]),
maps:map(fun
- (_, S) when is_binary(S) ->
- % emitting nulls breaks a lot of things, it's kind of funny
- unicode:characters_to_list(case binary:match(S, <<0>>) of
- {Pos, _} -> binary:part(S, 0, Pos);
- _ -> S
- end);
+ (_, <<S/binary>>) ->
+ [Head|_] = binary:split(S, <<0>>),
+ unicode:characters_to_list(Head);
(_, V) ->
V
end, Map).
-undef(X).
-define(X(_A, CHAR, _D, _W, _T), CHAR).
-usage(Opts) ->
- err("Usage: ~w -j [-~s] demo.dem", [?MODULE, [?HEADER]]),
- err(" ~w -~s demo.dem\n", [?MODULE, [?HEADER]]),
- err("~ts", [unicode:characters_to_list(getopt:usage_options(Opts))]),
+usage(Opts, Ret) ->
+ err("Usage: ~w -j [-~s] demo.dem", [?MODULE, [?FIELDS]]),
+ err(" ~w -~s demo.dem\n", [?MODULE, [?FIELDS]]),
+ err(getopt:usage_options(Opts)),
err("If -j is not given, exactly one header element must be selected "
-"(-" ++ [?HEADER] ++ "),\n"
+"(-" ++ [?FIELDS] ++ "),\n"
"and it will be printed on standard output. If -j is given, any number of\n"
"header elements may be selected; specifying none is equivalent to specifying\n"
-"all of them. Keys are the long forms of their corresponding flags.").
+"all of them. Keys are the long forms of their corresponding flags."),
+ halt(Ret).
-undef(X).
die(Fmt, Args) ->
- err(Fmt, Args),
+ err("error: " ++ Fmt, Args),
halt(1).
err(Str) ->
err("~s", [Str]).