diff options
-rw-r--r-- | .shellcheckrc | 5 | ||||
-rwxr-xr-x | scripts/shellcheck-xdg-util.sh | 154 | ||||
-rwxr-xr-x | scripts/test-common-function | 13 | ||||
-rw-r--r-- | scripts/xdg-utils-common.in | 64 |
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 +} |