From 5ff1fbe175af17fbb808c5ea942f465a932da3af Mon Sep 17 00:00:00 2001 From: "Christopher R. Nelson" Date: Tue, 21 Jul 2026 11:44:41 -0400 Subject: Rename app from er_xdg_pipe_menu to xdg_pipe_menu Drop the "er-" prefix throughout: module/atom names, filenames, socket paths, and docs. It was an implementation detail, not part of the app's identity. Co-Authored-By: Claude Sonnet 5 --- src/er_xdg_pipe_menu_desktop_entry.erl | 141 --------------------------------- 1 file changed, 141 deletions(-) delete mode 100644 src/er_xdg_pipe_menu_desktop_entry.erl (limited to 'src/er_xdg_pipe_menu_desktop_entry.erl') diff --git a/src/er_xdg_pipe_menu_desktop_entry.erl b/src/er_xdg_pipe_menu_desktop_entry.erl deleted file mode 100644 index 9a71013..0000000 --- a/src/er_xdg_pipe_menu_desktop_entry.erl +++ /dev/null @@ -1,141 +0,0 @@ -%%%------------------------------------------------------------------- -%% @doc Parser for XDG .desktop files. -%% -%% Reads the `[Desktop Entry]' group of a desktop entry file and -%% extracts the fields needed to build a menu item: `Name', `Exec' -%% and (optionally) `Icon' and `Categories'. Other groups (e.g. -%% `[Desktop Action ...]') and localized keys (e.g. `Name[fr]') are -%% ignored. -%% @end -%%%------------------------------------------------------------------- - --module(er_xdg_pipe_menu_desktop_entry). - --include("er_xdg_pipe_menu.hrl"). - --export([parse_file/1, parse/1]). - --define(GROUP, <<"Desktop Entry">>). --define(WANTED_KEYS, [ - <<"Name">>, <<"Exec">>, <<"Icon">>, <<"Categories">>, <<"NoDisplay">>, <<"Hidden">>, <<"Type">> -]). --define(FIELD_CODES, "fFuUdDnNickvm"). - --spec parse_file(file:filename_all()) -> {ok, #desktop_entry{}} | {error, term()}. -parse_file(Path) -> - case file:read_file(Path) of - {ok, Bin} -> parse(Bin); - {error, Reason} -> {error, Reason} - end. - --spec parse(binary()) -> {ok, #desktop_entry{}} | {error, missing_name | missing_exec}. -parse(Bin) -> - to_entry(scan(Bin)). - -%% Fold every line into a map of the fields we care about, tracking -%% which `[Group]' we're currently inside. -scan(Bin) -> - Lines = binary:split(Bin, [<<"\r\n">>, <<"\n">>], [global]), - {_Group, Fields} = lists:foldl(fun scan_line/2, {undefined, #{}}, Lines), - Fields. - -scan_line(Line, {Group, Fields}) -> - case classify(string:trim(Line)) of - skip -> - {Group, Fields}; - {section, Name} -> - {Name, Fields}; - {kv, Key, Value} -> - case Group =:= ?GROUP andalso lists:member(Key, ?WANTED_KEYS) of - true -> {Group, maps:put(Key, unescape(Value), Fields)}; - false -> {Group, Fields} - end - end. - -classify(<<>>) -> - skip; -classify(<<"#", _/binary>>) -> - skip; -classify(<<"[", Rest/binary>>) -> - Size = byte_size(Rest) - 1, - case Rest of - <> -> {section, string:trim(Name)}; - _ -> skip - end; -classify(Line) -> - case binary:split(Line, <<"=">>) of - [Key, Value] -> {kv, string:trim(Key), string:trim(Value)}; - [_] -> skip - end. - -%% Desktop entry escape sequences: \\ \s \n \t \r -unescape(Bin) -> - unescape(Bin, <<>>). - -unescape(<<>>, Acc) -> - Acc; -unescape(<<$\\, $\\, Rest/binary>>, Acc) -> - unescape(Rest, <>); -unescape(<<$\\, $s, Rest/binary>>, Acc) -> - unescape(Rest, <>); -unescape(<<$\\, $n, Rest/binary>>, Acc) -> - unescape(Rest, <>); -unescape(<<$\\, $t, Rest/binary>>, Acc) -> - unescape(Rest, <>); -unescape(<<$\\, $r, Rest/binary>>, Acc) -> - unescape(Rest, <>); -unescape(<>, Acc) -> - unescape(Rest, <>). - -to_entry(Fields) -> - case maps:find(<<"Name">>, Fields) of - error -> - {error, missing_name}; - {ok, Name} -> - case maps:find(<<"Exec">>, Fields) of - error -> - {error, missing_exec}; - {ok, Exec} -> - {ok, #desktop_entry{ - name = Name, - exec = strip_field_codes(Exec), - icon = maps:get(<<"Icon">>, Fields, undefined), - categories = parse_categories(maps:get(<<"Categories">>, Fields, undefined)), - hidden = is_hidden(Fields) - }} - end - end. - -%% `Categories' is a `;'-separated list, conventionally with a -%% trailing separator (e.g. `Utility;Development;'); empty segments -%% from that trailing separator (or repeated ones) are dropped. -parse_categories(undefined) -> - []; -parse_categories(Bin) -> - [C || C <- binary:split(Bin, <<";">>, [global]), C =/= <<>>]. - -%% Niche variations (e.g. a Type-less override dropped into -%% ~/.local/share/applications) are left to be special-cased there -%% rather than accounted for here. -is_hidden(Fields) -> - maps:get(<<"NoDisplay">>, Fields, <<"false">>) =:= <<"true">> orelse - maps:get(<<"Hidden">>, Fields, <<"false">>) =:= <<"true">> orelse - case maps:find(<<"Type">>, Fields) of - {ok, Type} -> Type =/= <<"Application">>; - error -> false - end. - -%% Field codes (%f, %u, %i, ...) are expanded by launchers that pass -%% files/URIs on the command line; a pipemenu has none to pass, so -%% each code is dropped along with the whitespace around it. `%%' is -%% then unescaped to a literal `%'. -strip_field_codes(Exec) -> - Tokens = binary:split(Exec, <<" ">>, [global]), - Kept = [T || T <- Tokens, T =/= <<>>, not is_field_code(T)], - Joined = iolist_to_binary(lists:join(<<" ">>, Kept)), - binary:replace(Joined, <<"%%">>, <<"%">>, [global]). - -is_field_code(<<"%", C>>) -> - lists:member(C, ?FIELD_CODES); -is_field_code(_) -> - false. -- cgit v1.2.3