+
+my $MUSIC = $ENV{MUSIC} // "/srv/music";
+my $host = $ENV{MPD_HOST} // "localhost";
+my $port = $ENV{MPD_PORT} // "6600";
+my $sock;
+
+my ($albumid, $trackid);
+my ($topmenu, $menu);
+my $mode = "top";
+my %artistids;
+
+# Quotes the argument so that it is presented as a single argument to MPD
+# at the protocol level. This also works OK for most FVWM arguments.
+sub escape {
+ my $s = @_[0] // $_;
+
+ # No way to encode literal newlines in the protocol, so we
+ # convert any newlines in the arguments into a space, which
+ # can help with quoting.
+ $s =~ s/\n/ /g;
+
+ if (/[ \t\\"]/) {
+ $s =~ s/[\\"]/\\$&/g;
+ return "\"$s\"";
+ }
+
+ $s =~ s/^\s*$/"$&"/;
+ return $s;
+}
+
+# Submit a command to the MPD server; each argument to this function
+# is quoted and sent as a single argument to MPD.
+sub mpd_exec {
+ my $cmd = join(' ', map { escape } @_);
+
+ print $sock "$cmd\n";
+}
+
+sub fvwm_cmd_unquoted {
+ print join(' ', @_), "\n";
+}
+
+sub fvwm_cmd {
+ fvwm_cmd_unquoted(map { escape } @_);
+}
+
+# Quotes the argument in such a way that it is passed unadulterated by
+# both FVWM and the shell to a command as a single argument (for use as
+# an # argument for e.g., the Exec or PipeRead FVWM commands).
+#
+# The result must be used with fvwm_cmd_unquoted;
+sub fvwm_shell_literal {
+ my $s = @_[0] // $_;
+
+ $s =~ s/\$/\$\$/g;
+ if ($s =~ /[' \t]/) {
+ $s =~ s/'/'\\''/g;
+ return "'$s'";
+ }
+ $s =~ s/^\s*$/'$&'/;
+ return "$s";
+}
+
+# Escapes metacharacters in the argument used in FVWM menu labels. The
+# string must still be quoted (e.g., by using fvwm_cmd).
+sub fvwm_label_escape {
+ my @tokens = split /\t/, $_[0];
+ @tokens[0] =~ s/&/&&/g;
+ my $ret = join "\t", @tokens;
+ $ret =~ s/[\$@%^*]/$&$&/g;
+ return $ret;
+}
+
+# make_submenu(name, [args ...])
+#
+# Creates a submenu (with the specified name) constructed by invoking this
+# script with the given arguments. Returns a list that can be passed to
+# fvwm_cmd to display the menu.
+sub make_submenu {
+ my $name = shift;
+ $name =~ s/-/_/g;
+ unshift @_, ("exec", $SELF, "--topmenu=$topmenu", "--menu=$name");
+
+ fvwm_cmd("DestroyFunc", "Make$name");
+ fvwm_cmd("AddToFunc", "Make$name");
+ fvwm_cmd("+", "I", "DestroyMenu", $name);
+
+ fvwm_cmd("DestroyMenu", $name);
+ fvwm_cmd("AddToMenu", $name, "DynamicPopupAction", "Make$name");
+ fvwm_cmd("AddToFunc", "Kill$topmenu", "I", "DestroyMenu", $name);
+
+ fvwm_cmd("DestroyFunc", "Make$name");
+ fvwm_cmd("AddToFunc", "Make$name");
+ fvwm_cmd("+", "I", "DestroyMenu", $name);
+ fvwm_cmd("+", "I", "-PipeRead",
+ join(' ', map { fvwm_shell_literal } @_));
+ fvwm_cmd("AddToFunc", "Kill$topmenu", "I", "DestroyFunc", "Make$name");
+
+ return ("Popup", $name);
+}