summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSlatian <baschdel@disroot.org>2024-01-23 06:14:06 +0000
committerSimon Lees <simon@simotek.net>2024-01-23 06:14:06 +0000
commit9b7d253ca07b80fbc201c5cf5b7f14a49bd09b3f (patch)
tree889a7f74f6f08da9d73df68e09d8781df0b19c24
parent8f5bae73b5a3401525e9a7831da11a8bda0be170 (diff)
Add xdg-realpath to better handle Canonicalizing filenames
This replaces calls to `realpath -f` which isn't available on all shells. (fix #66)
-rw-r--r--README.md3
-rw-r--r--scripts/.gitignore1
-rw-r--r--scripts/desc/xdg-realpath.xml208
-rw-r--r--scripts/html/.gitignore1
-rw-r--r--scripts/man/.gitignore2
-rw-r--r--scripts/xdg-desktop-icon.in4
-rw-r--r--scripts/xdg-desktop-menu.in2
-rw-r--r--scripts/xdg-email.in2
-rw-r--r--scripts/xdg-icon-resource.in4
-rw-r--r--scripts/xdg-mime.in4
-rw-r--r--scripts/xdg-realpath.in60
-rw-r--r--scripts/xdg-utils-common.in66
12 files changed, 345 insertions, 12 deletions
diff --git a/README.md b/README.md
index b514cb7..e5b44de 100644
--- a/README.md
+++ b/README.md
@@ -32,7 +32,7 @@ Documentation is mostly in the maual pages and on the [freedesktop.org wiki](htt
## Overview
-The following tools are included in xdg-utils 1.0:
+The following tools are included in xdg-utils 1.2:
* `xdg-desktop-menu` - Install desktop menu items
* `xdg-desktop-icon` - Install icons to the desktop
@@ -40,6 +40,7 @@ The following tools are included in xdg-utils 1.0:
* `xdg-mime` - Query information about file type handling and install descriptions for new file types
* `xdg-open` - Open a file or URL in the user's preferred application
* `xdg-email` - Send mail using the user's preferred e-mail composer
+* `xdg-realpath` - Canonicalize filenames (new in 1.2)
* `xdg-screensaver` - Control the screensaver
diff --git a/scripts/.gitignore b/scripts/.gitignore
index 261d5ce..3b51bac 100644
--- a/scripts/.gitignore
+++ b/scripts/.gitignore
@@ -8,3 +8,4 @@ xdg-mime
xdg-open
xdg-screensaver
xdg-settings
+xdg-realpath
diff --git a/scripts/desc/xdg-realpath.xml b/scripts/desc/xdg-realpath.xml
new file mode 100644
index 0000000..60a5b22
--- /dev/null
+++ b/scripts/desc/xdg-realpath.xml
@@ -0,0 +1,208 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/xsl"
+ href="http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl"?>
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.3//EN" "http://www.oasis-open.org/docbook/xml/4.3/docbookx.dtd">
+<refentry id="xdg-open">
+ <refentryinfo>
+ <title>xdg-realpath Manual</title>
+ <copyright>
+ <year>2023</year>
+ </copyright>
+ <author>
+ <firstname>Slatian</firstname>
+ <address>
+ <email>baschdel+xdg-utils@disroot.org</email>
+ </address>
+ </author>
+ <author>
+ <firstname>Kevin</firstname>
+ <surname>Krammer</surname>
+ <address>
+ <!--email>kevin.krammer@gmx.at</email-->
+ </address>
+ </author>
+ <author>
+ <firstname>Jeremy</firstname>
+ <surname>White</surname>
+ <address>
+ <!--email>jwhite@codeweavers.com</email-->
+ </address>
+ </author>
+ <releaseinfo>xdg-utils 1.2</releaseinfo>
+ </refentryinfo>
+ <refmeta>
+ <refentrytitle>xdg-realpath</refentrytitle>
+ <manvolnum>1</manvolnum>
+ </refmeta>
+ <refnamediv>
+ <refname>xdg-realpath</refname>
+ <refpurpose>Canonicalizes filepaths in a consistent way.</refpurpose>
+ </refnamediv>
+ <refsynopsisdiv>
+ <cmdsynopsis>
+ <command>xdg-realpath</command>
+ <arg choice="opt"><option>--</option></arg>
+ <arg choice="plain" rep="repeat">
+ <option><replaceable>file</replaceable></option>
+ </arg>
+ </cmdsynopsis>
+ <cmdsynopsis>
+ <command>xdg-realpath</command>
+ <arg choice="plain"><option>--get-backend</option></arg>
+ </cmdsynopsis>
+ <cmdsynopsis>
+ <command>xdg-realpath</command>
+ <group choice="req">
+ <arg choice="plain"><option>--help</option></arg>
+ <arg choice="plain"><option>--manual</option></arg>
+ <arg choice="plain"><option>--version</option></arg>
+ </group>
+ </cmdsynopsis>
+ </refsynopsisdiv>
+ <refsect1 id="description">
+ <title>Description</title>
+ <para>
+ xdg-realpath canonicalizes filepaths using the installed <command>realpath</command> or <command>readlink -f</command> implementation, automatically choosing the right calling conventions so you don't have to worry about it.
+ </para>
+ <para>
+ Canonicalization happens by resolving relative paths and symbolic links resulting in an absolute path to a file. It fails if the path does not lead to any kind file or directory.
+ </para>
+ <para>
+ It is strongly recommended to call xdg-realpath using the <option>--</option> option.
+ </para>
+ </refsect1>
+ <refsect1 id="options">
+ <title>Options</title>
+ <variablelist>
+ <varlistentry>
+ <term>
+ <option>--help</option>
+ </term>
+ <listitem>
+ <simpara>
+ Show command synopsis.
+ </simpara>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ <option>--manual</option>
+ </term>
+ <listitem>
+ <simpara>
+ Show this manual page.
+ </simpara>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ <option>--version</option>
+ </term>
+ <listitem>
+ <simpara>
+ Show the xdg-utils version information.
+ </simpara>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </refsect1>
+ <refsect1 id="exitcodes">
+ <title>Exit Codes</title>
+ <para>
+ An exit code of 0 indicates success while a non-zero exit code
+ indicates failure. The following failure codes can be returned:
+ </para>
+ <variablelist>
+ <varlistentry>
+ <term>
+ <option>1</option>
+ </term>
+ <listitem>
+ <simpara>
+ Error in command line syntax.
+ </simpara>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ <option>2</option>
+ </term>
+ <listitem>
+ <simpara>
+ One of the files passed on the command line did not exist.
+ </simpara>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ <option>3</option>
+ </term>
+ <listitem>
+ <simpara>
+ A required tool could not be found.
+ </simpara>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ <option>4</option>
+ </term>
+ <listitem>
+ <simpara>
+ The action failed.
+ </simpara>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </refsect1>
+ <refsect1 id="env_vars">
+ <title>Environment Variables</title>
+ <para>
+ xdg-realpath honours the following environment variables:
+ </para>
+ <variablelist>
+ <varlistentry>
+ <term>XDG_UTILS_REALPATH_BACKEND</term>
+ <listitem>
+ <simpara>
+ When left empty (recommended) xdg-realpath automatically chooses an appropriate backend. Possible values are <code>realpath</code>, <code>busybox-realpath</code> and <code>readlink</code>.
+ This is also used by other scripts from the xdg-utils family.
+ </simpara>
+ <simpara>
+ Setting this variable has the possibility to break a lot of things. It is primarily intended for testing and quick-fixing. <emphasis>If you are not the local system administrator or a distribution maintainer leave it alone!</emphasis>
+ </simpara>
+ <simpara>
+ The <option>--get-backend</option> option will print the used backend.
+ </simpara>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </refsect1>
+ <refsect1 id="seealso">
+ <title>See Also</title>
+ <para><citerefentry><refentrytitle>realpath</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>readlink</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+ </para>
+ </refsect1>
+ <refsect1 id="examples">
+ <title>Examples</title>
+ <para>
+<programlisting>
+xdg-realpath -- /./bin
+</programlisting>
+ Canonicalizes the path to <code>/./bin</code>, on most systems the output will be <code>/bin</code> or <code>/usr/bin</code>.
+ </para>
+ <para>
+<programlisting>
+xdg-realpath -- foo.txt
+</programlisting>
+ Will print the absolute path of the file foo.txt or inform you that foo.txt doesn't exist.
+ </para>
+ <para>
+<programlisting>
+xdg-realpath -- .
+</programlisting>
+ Will print the present working directory path with symbolic links resolved.
+ </para>
+ </refsect1>
+</refentry>
diff --git a/scripts/html/.gitignore b/scripts/html/.gitignore
index 0be5f9a..b373b67 100644
--- a/scripts/html/.gitignore
+++ b/scripts/html/.gitignore
@@ -5,5 +5,6 @@ xdg-email.html
xdg-icon-resource.html
xdg-mime.html
xdg-open.html
+xdg-realpath.html
xdg-screensaver.html
xdg-settings.html
diff --git a/scripts/man/.gitignore b/scripts/man/.gitignore
index 43c7914..82d9456 100644
--- a/scripts/man/.gitignore
+++ b/scripts/man/.gitignore
@@ -4,5 +4,7 @@ xdg-email.1
xdg-icon-resource.1
xdg-mime.1
xdg-open.1
+xdg-realpath.1
xdg-screensaver.1
xdg-settings.1
+
diff --git a/scripts/xdg-desktop-icon.in b/scripts/xdg-desktop-icon.in
index 6417181..21dc64b 100644
--- a/scripts/xdg-desktop-icon.in
+++ b/scripts/xdg-desktop-icon.in
@@ -108,7 +108,7 @@ if gconftool-2 -g /apps/nautilus/preferences/desktop_is_home_dir 2> /dev/null |
desktop_dir_gnome="$HOME"
# Don't create $HOME/Desktop if it doesn't exist
[ -w "$desktop_dir" ] || desktop_dir=
-fi
+fi
if [ -n "$desktop_dir_kde" ]; then
if [ ! -d "$desktop_dir_kde" ]; then
save_umask=`umask`
@@ -117,7 +117,7 @@ if [ -n "$desktop_dir_kde" ]; then
umask $save_umask
fi
# Is the KDE desktop dir != $HOME/Desktop ?
- if [ "x`readlink -f "$desktop_dir"`" != "x`readlink -f "$desktop_dir_kde"`" ]; then
+ if [ "x$(xdg_realpath "$desktop_dir")" != "x$(xdg_realpath "$desktop_dir_kde")" ]; then
# If so, don't create $HOME/Desktop if it doesn't exist
[ -w "$desktop_dir" ] || desktop_dir=
else
diff --git a/scripts/xdg-desktop-menu.in b/scripts/xdg-desktop-menu.in
index 8025676..08fe2a6 100644
--- a/scripts/xdg-desktop-menu.in
+++ b/scripts/xdg-desktop-menu.in
@@ -88,7 +88,7 @@ make_lazy_default()
# App already listed as default
continue;
fi
- default_file="$(readlink -f "$1/defaults.list")"
+ default_file="$(xdg_realpath "$1/defaults.list")"
if [ -f "$default_file" ] ; then
DEBUG 1 "Updating $default_file"
grep -v "$MIME=" $default_file > ${default_file}.new 2> /dev/null
diff --git a/scripts/xdg-email.in b/scripts/xdg-email.in
index fc24886..ca9323a 100644
--- a/scripts/xdg-email.in
+++ b/scripts/xdg-email.in
@@ -377,7 +377,7 @@ while [ $# -gt 0 ] ; do
exit_failure_syntax "file argument missing for --attach option"
fi
check_input_file "$1"
- file="$(readlink -f "$1")" # Normalize path
+ file="$(xdg_realpath "$1")" # Normalize path
if [ -z "$file" ] || [ ! -f "$file" ] ; then
exit_failure_file_missing "file '$1' does not exist"
fi
diff --git a/scripts/xdg-icon-resource.in b/scripts/xdg-icon-resource.in
index ac17ae6..0bfa26b 100644
--- a/scripts/xdg-icon-resource.in
+++ b/scripts/xdg-icon-resource.in
@@ -306,7 +306,7 @@ fi
need_kde_icon_path()
{
local path
- path=`readlink -f "$1" 2> /dev/null` # Normalize path
+ path="$(xdg_realpath "$1")" 2> /dev/null` # Normalize path
DEBUG 2 "need_kde_icon_path $path"
if [ -z "$path" ] ; then
DEBUG 2 "need_kde_icon_path RETURN 1 (not needed, no xdg icon dir)"
@@ -323,7 +323,7 @@ need_kde_icon_path()
fi
needed=0 # Needed
for y in $kde_icon_dirs ; do
- x=`readlink -f "$y"` # Normalize path
+ x="$(xdg_realpath "$y")" # Normalize path
DEBUG 3 "Normalize $y --> $x"
if [ -n "$x" ] ; then
if [ "$x" = "$path" ] ; then
diff --git a/scripts/xdg-mime.in b/scripts/xdg-mime.in
index d1b4226..bfb088b 100644
--- a/scripts/xdg-mime.in
+++ b/scripts/xdg-mime.in
@@ -299,7 +299,7 @@ make_default_generic()
[ -n "$xdg_config_home" ] || xdg_config_home="$HOME/.config"
default_file="$xdg_config_home/mimeapps.list"
if [ -L "$default_file" ]; then
- out_file=$(readlink -f "$default_file")
+ out_file="$(xdg_realpath "$default_file")"
else
out_file="$default_file"
fi
@@ -587,7 +587,7 @@ case $1 in
;;
esac
check_input_file "$filename"
- filename="$(readlink -f -- "$filename")"
+ filename="$(xdg_realpath "$filename")"
;;
default)
diff --git a/scripts/xdg-realpath.in b/scripts/xdg-realpath.in
new file mode 100644
index 0000000..87733da
--- /dev/null
+++ b/scripts/xdg-realpath.in
@@ -0,0 +1,60 @@
+#!/bin/sh
+#---------------------------------------------
+# xdg-realpath
+#
+# Utility script to canonicalize filepaths.
+#
+# Copyright 2023, Slatian <baschdel@disroot.org>
+#
+# LICENSE:
+#
+#---------------------------------------------
+
+manualpage()
+{
+cat << _MANUALPAGE
+_MANUALPAGE
+}
+
+usage()
+{
+cat << _USAGE
+_USAGE
+}
+
+XDG_UTILS_ENABLE_DOUBLE_HYPEN="y"
+
+#@xdg-utils-common@
+
+set -e
+
+[ -n "$1" ] || exit_failure_syntax
+
+past_double_hyphen=""
+exit_with_missing_status=""
+
+while [ $# -gt 0 ] ; do
+ if [ -n "$past_double_hyphen" ] ; then
+ [ -e "$1" ] || echo "@NAME@: $1: No such file or directory" >&2
+ xdg_realpath "$1"
+ else
+ case "$1" in
+ --)
+ past_double_hyphen="y"
+ ;;
+ --get-backend)
+ xdg_realpath || true
+ echo "$XDG_UTILS_REALPATH_BACKEND"
+ ;;
+ -*)
+ exit_failure_syntax "Unknown option: '$1'"
+ ;;
+ *)
+ xdg_realpath "$1"
+ ;;
+ esac
+ fi
+ shift
+done
+
+[ -z "$exit_with_missing_status" ] || exit_failure_file_missing
diff --git a/scripts/xdg-utils-common.in b/scripts/xdg-utils-common.in
index 5135c86..bba3d8a 100644
--- a/scripts/xdg-utils-common.in
+++ b/scripts/xdg-utils-common.in
@@ -27,7 +27,7 @@ binary_to_desktop_file()
{
search="${XDG_DATA_HOME:-$HOME/.local/share}:${XDG_DATA_DIRS:-/usr/local/share:/usr/share}"
binary="$(command -v "$1")"
- binary="$(readlink -f "$binary")"
+ binary="$(xdg_realpath "$binary")"
base="$(basename "$binary")"
IFS=:
for dir in $search; do
@@ -42,7 +42,7 @@ binary_to_desktop_file()
grep -Eq "^(NoDisplay|Hidden)=true" "$file" && continue
command="$(grep -E "^Exec(\[[^]=]*])?=" "$file" | cut -d= -f 2- | first_word)"
command="$(command -v "$command")"
- if [ x"$(readlink -f "$command")" = x"$binary" ]; then
+ if [ x"$(xdg_realpath "$command")" = x"$binary" ]; then
# Fix any double slashes that got added path composition
echo "$file" | tr -s /
return
@@ -84,7 +84,7 @@ desktop_file_to_binary()
# Remove any arguments (%F, %f, %U, %u, etc.).
command="$(grep -E "^Exec(\[[^]=]*])?=" "$file_path" | cut -d= -f 2- | first_word)"
command="$(command -v "$command")"
- readlink -f "$command"
+ xdg_realpath "$command"
return
fi
done
@@ -252,6 +252,10 @@ check_common_commands()
echo "@NAME@ 1.2.0-beta1"
exit_success
;;
+
+ --)
+ [ -z "$XDG_UTILS_ENABLE_DOUBLE_HYPEN" ] || break
+ ;;
esac
done
}
@@ -402,3 +406,59 @@ has_display()
return 1
fi
}
+
+#----------------------------------------------------------------------------
+# Prefixes a path with a "./" if it starts with a "-".
+# This is useful for programs to not confuse paths with options.
+
+unoption_path()
+{
+ case "$1" in
+ -*)
+ printf "./%s" "$1" ;;
+ *)
+ printf "%s" "$1" ;;
+ esac
+}
+
+#----------------------------------------------------------------------------
+# Performs a symlink and relative path resolving for a single argument.
+# This will always fail if the given file does not exist!
+
+xdg_realpath()
+{
+ # allow caching and external configuration
+ if [ -z "$XDG_UTILS_REALPATH_BACKEND" ] ; then
+ if command -v realpath >/dev/null 2>/dev/null ; then
+ lines="$(realpath -- / 2>&1)"
+ if [ $? = 0 ] && [ "$lines" = "/" ] ; then
+ XDG_UTILS_REALPATH_BACKEND="realpath"
+ else
+ # The realpath took the -- literally, probably the busybox implementation
+ XDG_UTILS_REALPATH_BACKEND="busybox-realpath"
+ fi
+ unset lines
+ elif command -v readlink >/dev/null 2>/dev/null ; then
+ XDG_UTILS_REALPATH_BACKEND="readlink"
+ else
+ exit_failure_operation_failed "No usable realpath backend found. Have a realpath binary or a readlink -f that canonicalizes paths."
+ fi
+ fi
+ # Always fail if the file doesn't exist (busybox realpath does that for example)
+ [ -e "$1" ] || return 1
+ case "$XDG_UTILS_REALPATH_BACKEND" in
+ realpath)
+ realpath -- "$1"
+ ;;
+ busybox-realpath)
+ # busybox style realpath implementations have options too
+ realpath "$(unoption_path "$1")"
+ ;;
+ readlink)
+ readlink -f "$(unoption_path "$1")"
+ ;;
+ *)
+ exit_failure_operation_impossible "Realpath backend '$XDG_UTILS_REALPATH_BACKEND' not recognized."
+ ;;
+ esac
+}