summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.shellcheckrc5
-rwxr-xr-xscripts/shellcheck-xdg-util.sh154
-rwxr-xr-xscripts/test-common-function13
-rw-r--r--scripts/xdg-utils-common.in64
4 files changed, 227 insertions, 9 deletions
diff --git a/.shellcheckrc b/.shellcheckrc
index d16c2b0..2beac27 100644
--- a/.shellcheckrc
+++ b/.shellcheckrc
@@ -7,3 +7,8 @@ disable=SC2181
# x prefixes aren't pretty anymore,
# but they don't hurt either.
disable=SC2268
+
+# While local isn't exactly great
+# this warning produces unceccessary noise
+# distracting from more urgent issues
+disable=3043
diff --git a/scripts/shellcheck-xdg-util.sh b/scripts/shellcheck-xdg-util.sh
new file mode 100755
index 0000000..50e4668
--- /dev/null
+++ b/scripts/shellcheck-xdg-util.sh
@@ -0,0 +1,154 @@
+#!/bin/sh
+#---------------------------------------------
+#
+# shellcheck-xdg-util.sh
+#
+# Shell script for shellchecking the xdg-utils scripts as a whole
+# translating line numbers back to the source files.
+#
+# Copyright 2024, Slatian <baschdel@disroot.org>
+#
+# LICENSE:
+#
+# Permission is hereby granted, free of charge, to any person obtaining a
+# copy of this software and associated documentation files (the "Software"),
+# to deal in the Software without restriction, including without limitation
+# the rights to use, copy, modify, merge, publish, distribute, sublicense,
+# and/or sell copies of the Software, and to permit persons to whom the
+# Software is furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+# OTHER DEALINGS IN THE SOFTWARE.
+#
+#---------------------------------------------
+
+set -e
+
+if [ -z "$1" ] || [ "$1" = "--help" ] ; then
+ echo "Usage: $(basename "$0") <script-name> [<shellcheck-option> ...]"
+ exit
+fi
+
+if ! command -v shellcheck >/dev/null 2>/dev/null ; then
+ echo "Please install shellcheck for this script to work!"
+ exit 2
+fi
+
+SCRIPT="$(printf "%s" "$1" | sed 's/.in$//')"
+shift 1
+
+if ! [ -e "$SCRIPT.in" ] ; then
+ echo "$SCRIPT.in source file was not found."
+ exit 1
+fi
+
+SCRIPT_DIR="$(printf "%s" "$SCRIPT" | sed 's|[^/]*$||' )"
+SCRIPT="$(printf "%s" "$SCRIPT" | grep -o '[^/]*$' )"
+
+cd "$SCRIPT_DIR"
+
+make "$SCRIPT"
+
+# (pattern, nth_result=1)
+find_line() {
+ line="$(grep -Pn "^$1$" "$SCRIPT" | cut -d: -f1 | head -n "${2:-1}" | tail -n 1)"
+ echo "$line"
+ printf 'Found \33[32m"%s"\33[00m at line %s\n' "$1" "$line" >&2
+}
+
+XDG_UTILS_COMMON_INCLUDE_LINE="$(find_line "#@xdg-utils-common@")"
+XDG_UTILS_COMMON_LINES="$(wc -l ./xdg-utils-common.in | cut -d' ' -f1)"
+
+#TODO: Remove the questionmarks here when the quoted sections are merged
+
+XDG_UTILS_MANUALPAGE_BLOCK_START="$(find_line 'cat <<'" '?_MANUALPAGE'?")"
+XDG_UTILS_MANUALPAGE_BLOCK_END="$(find_line '_MANUALPAGE')"
+
+XDG_UTILS_USAGE_BLOCK_START="$(find_line 'cat <<'" '?_USAGE'?")"
+XDG_UTILS_USAGE_BLOCK_END="$(find_line '_USAGE')"
+
+XDG_UTILS_LICENSE_LINES="$(wc -l ../LICENSE)"
+
+if [ -z "$XDG_UTILS_COMMON_INCLUDE_LINE" ] ; then
+ echo "No '#@xdg-utils-common@' include found …"
+ exit 1
+fi
+
+echo ""
+echo "Running Shellcheck ..."
+
+# Note on shellchecks ran diectly against the .in file:
+# SC1111 is for Unicode quotes wich appear in the manual page sections
+# SC2317 is unreachable code wich is caused by functions in the
+# xdg-utils-common.in file not being called.
+# This one should be updated once shellcheck gets a
+# "function never gets called" check with its own id.
+
+shellcheck --color=always "./$SCRIPT.in" \
+ -i SC2317
+
+shellcheck --color=always "./$SCRIPT" \
+ -e SC2317 \
+ "$@" |
+ awk \
+ -v"source_name=$SCRIPT.in" \
+ -v"include_start_line=$XDG_UTILS_COMMON_INCLUDE_LINE" \
+ -v"manualpage_block_start=$XDG_UTILS_MANUALPAGE_BLOCK_START" \
+ -v"manualpage_block_end=$XDG_UTILS_MANUALPAGE_BLOCK_END" \
+ -v"usage_block_start=$XDG_UTILS_USAGE_BLOCK_START" \
+ -v"usage_block_end=$XDG_UTILS_USAGE_BLOCK_END" \
+ -v"include_lines=$XDG_UTILS_COMMON_LINES" \
+ -v"license_lines=$XDG_UTILS_LICENSE_LINES" \
+ '
+
+BEGIN {
+ after_include_offset = include_start_line + include_lines
+}
+/In [^ ]+ line [0-9]+:/ {
+ line = $4+0
+ in_file = source_name
+
+ if ( line > manualpage_block_start+0 && line < manualpage_block_end+0) {
+ line = line-manualpage_block_start
+ in_file = "[_MANUALPAGE]"
+ } else if (line > usage_block_start+0 && line < usage_block_end+0) {
+ line = line-usage_block_start
+ in_file = "[_USAGE]"
+ } else if (line > include_start_line+0 && line < after_include_offset+0) {
+ in_file="xdg-utils-common.in"
+ line = line-include_start_line
+ }
+
+ if (in_file == source_name) {
+ orig_line = line
+ if (orig_line > manualpage_block_end+0) {
+ line = line - (manualpage_block_end - manualpage_block_start)+1
+ }
+ if (orig_line > usage_block_end+0) {
+ line = line - (usage_block_end - usage_block_start)+1
+ }
+ if (orig_line > after_include_offset+0) {
+ line = line - include_lines
+ }
+ line = line - license_lines
+ }
+ print "\x1b[01;34mIn " in_file " line " line ":\x1b[00m\x1b[09m"
+}
+
+{
+ // Pass everything from shellcheck through
+ print
+}
+ '
+
+
+
+echo "Shellcheck done."
diff --git a/scripts/test-common-function b/scripts/test-common-function
new file mode 100755
index 0000000..c8af98d
--- /dev/null
+++ b/scripts/test-common-function
@@ -0,0 +1,13 @@
+#!/bin/sh
+
+# This script is for testing internal functions of the xdg-utils-common.in file
+#
+# Example ./test-common-function xdg_which echo
+
+XDG_UTILS_DEBUG_LEVEL="${XDG_UTILS_DEBUG_LEVEL:-99}"
+
+. ./xdg-utils-common.in
+
+"$@"
+
+exit $?
diff --git a/scripts/xdg-utils-common.in b/scripts/xdg-utils-common.in
index f0a1aac..adab368 100644
--- a/scripts/xdg-utils-common.in
+++ b/scripts/xdg-utils-common.in
@@ -51,19 +51,24 @@ binary_to_desktop_file()
}
#-------------------------------------------------------------
-# map a .desktop file to a binary
+# map a .desktop file name to its Exec binary
+# Returns the realpath resolved path to the binary or noting.
+
+# desktop_file_to_binary <desktop-file-name>
desktop_file_to_binary()
{
+ DEBUG 1 "desktop_file_to_binary '$1'"
search="${XDG_DATA_HOME:-$HOME/.local/share}:${XDG_DATA_DIRS:-/usr/local/share:/usr/share}"
desktop="$(basename "$1")"
IFS=:
for dir in $search; do
+ DEBUG 2 "Searching in '$dir/{applications,applnk}'"
unset IFS
- [ "$dir" ] && [ -d "$dir/applications" ] || [ -d "$dir/applnk" ] || continue
+ [ -n "$dir" ] && [ -d "$dir/applications" ] || [ -d "$dir/applnk" ] || continue
# Check if desktop file contains -
if [ "${desktop#*-}" != "$desktop" ]; then
- vendor=${desktop%-*}
- app=${desktop#*-}
+ vendor="${desktop%-*}"
+ app="${desktop#*-}"
if [ -r "$dir/applications/$vendor/$app" ]; then
file_path="$dir/applications/$vendor/$app"
elif [ -r "$dir/applnk/$vendor/$app" ]; then
@@ -72,18 +77,31 @@ desktop_file_to_binary()
fi
if test -z "$file_path" ; then
for indir in "$dir"/applications/ "$dir"/applications/*/ "$dir"/applnk/ "$dir"/applnk/*/; do
+ DEBUG 4 "Does file exist? '$indir/$desktop'"
file="$indir/$desktop"
if [ -r "$file" ]; then
- file_path=$file
+ file_path="$file"
break
fi
done
fi
if [ -r "$file_path" ]; then
- # Remove any arguments (%F, %f, %U, %u, etc.).
- command="$(grep -E "^Exec(\[[^]=]*])?=" "$file_path" | cut -d= -f 2- | first_word)"
- command="$(command -v "$command")"
- xdg_realpath "$command"
+ DEBUG 2 "Checking desktop file '$file_path'"
+ # Get the command name from the correct Exec
+ # Note: Ignoring quoting and escape sequences here, see #253
+ binary="$(awk -F '=' '
+ /^\[/{ in_entry=0 }
+ $0 == "[Desktop Entry]"{ in_entry=1 }
+ in_entry && /^Exec\s*=/ {
+ sub(/^\s+/,"",$2);
+ match($2,/^[^ ]+/);
+ print substr($2,RSTART,RLENGTH)
+ }' \
+ < "$file_path" )"
+ DEBUG 2 "Found command: $binary"
+ binary="$(xdg_which "$binary")"
+ DEBUG 2 "Resolved to command to file: '$binary'"
+ [ -z "$binary" ] || xdg_realpath "$binary"
return
fi
done
@@ -461,3 +479,31 @@ xdg_realpath()
;;
esac
}
+
+#----------------------------------------------------------------------------
+# The `which` command but as a shell implementation.
+# Returns either the path of the resolved binary or nothing
+# because command -v does not always return the path of a command
+# (builtins, aliases, functions, etc.)
+
+# xdg_which <command>
+xdg_which()
+{
+ if [ -z "$1" ] ; then
+ return 1
+ elif [ -x "$1" ] ; then
+ printf "%s\n" "$1"
+ else
+ # this should be faster than the real thing because of shell builtins
+ old_ifs="$IFS"
+ IFS=:
+ for p in $PATH ; do
+ IFS="$old_ifs"
+ if [ -x "$p/$1" ] ; then
+ printf "%s\n" "$p/$1"
+ return
+ fi
+ done
+ return 1
+ fi
+}