]> git.sev.monster Git - dotfiles.git/commitdiff
zsh: path logic fixes, better duplicate detection
authorsev <git@sev.monster>
Sun, 5 Jan 2025 08:36:30 +0000 (02:36 -0600)
committersev <git@sev.monster>
Sun, 5 Jan 2025 08:36:30 +0000 (02:36 -0600)
PATH, FPATH, and XDG vars will now properly remove duplicate paths whose only
difference is that they end in a slash. also removed some cruft and an old
comment that no longer applies.

etc/zsh/.zshenv

index f97424c91b310a40c3f45917dd2f19241206af31..07312658844ef3bb85929150f1e102e1ffa79ec9 100644 (file)
@@ -60,29 +60,37 @@ function _sev_setpath {
     #       old PATH that we did not anticipate and shift them to the front,
     #       since they are probably important to the system
     typeset -gU path fpath
-    local -a syspath=("$path[@]")
+    local -a syspath=("$path[@]%%/")
     # NOTE: /usr/{pkg,games} are unix/bsdisms
     # NOTE: some systems (esp. research machines) may have multiple versions of
     #       packages installed in /opt/[pkg]/[ver]/bin or other dirs, managed
     #       with something like Environment Modules. this code does not account
-    #       for this type of usage and will add all valid paths. any undesired
-    #       paths can be removed using .zshenv.local.
-    # NOTE: fun non-conformant systems like Android may have fun dirs that
-    #       contain binaries intended solely for the base system, and their use
-    #       by other users/subsystems may cause problems. for example,
-    #       /system/bin/bc on Android may be a different version of or
-    #       completely different codebase compared to the Termux-installed bc.
-    #       modern Termux does not add these such systems to PATH by default,
-    #       but older versions/configurations might.
-    # XXX: PREFIX not validated, non-POSIX but Termux uses it, maybe others
+    #       for this type of usage and may end up adding multiple versions of
+    #       the same program to PATH. any undesired paths can be removed using
+    #       .zshenv.local. (if you're crazy enough to be using this code on a
+    #       research machine and are debugging your PATH, do send me an email,
+    #       I'd love to hear about your situation!)
+    # NOTE: on systems using unexpected paths for binaries (we call them
+    #       "system paths" here), such paths will be shunted to the beginning
+    #       of the list before the expected paths. this can cause problems if a
+    #       system path contains programs that elide programs from an expected
+    #       path that do not run properly. for example, under Termux, there is
+    #       a wrapper for /bin/pm under $PREFIX/bin/pm that allows pm to be
+    #       used by the Termux unprivileged user, where pm can normally only be
+    #       run by a privileged user such as shell. (N.B.: modern Termux does
+    #       not add /system/bin to PATH by default, this is just an example—
+    #       and we add $PREFIX/bin before /bin (link to /system/bin) anyway).
+    # XXX: PREFIX not validated—it's non-POSIX but Termux uses it, maybe others
     # XXX: XDG specifies ~/.local/bin as the only user-writable dir for
     #      executables, but we specify more; technically this is against spec
-    path=({{${_sev_home:-~},~}{/.local,},{$PREFIX,}{,/usr{,/local,/pkg},/opt{,/*{/*,}}}}/{s,}bin(N)
-          {$PREFIX,}/usr/{X11R{7,6}/bin,games}
-          # emulate Arch Linux flatpak-bindir.sh for use on other systems
-          {${XDG_DATA_HOME:-~/.local/share},{$PREFIX,}/var/lib}/flatpak/exports/bin)
+    path=(
+        {{${_sev_home:-~},~}{/.local,},{${PREFIX%%/},}{,/usr{,/local,/pkg},/opt{,/*{/*,}}}}/{s,}bin(/N^M)
+        {${PREFIX%%/},}/usr/{X11R{7,6}/bin,games}(/N^M)
+        # emulate Arch Linux flatpak-bindir.sh for use on other systems
+        {${${XDG_DATA_HOME:-~/.local/share}%%/},{${PREFIX%%/},}/var/lib}/flatpak/exports/bin(/N^M)
+    )
     local -i i len=$#path
-    path+=("$syspath[@]")
+    path+=("${syspath[@]%%/}")
     # remove bad paths... after having combined the arrays to remove duplicates
     for (( i = 1; i <= $#path; i++ )) {
         if [[ ! -d $path[$i] ]] {
@@ -94,8 +102,12 @@ function _sev_setpath {
     }
     # shift valid system paths to the front if there are any left
     ((len > 0 && len < $#path)) && path=("${(@)path[len + 1, -1]}" "${(@)path[1, len]}")
+
     # include our zsh dir in fpath. unlike above, we always prefer our paths
-    fpath=({${ZDOTDIR:-{${_sev_home:-~},~}/.zsh},{${_sev_home:-~},~}/.zsh}/functions/**/*(/N) "$fpath[@]")
+    fpath=(
+        {${ZDOTDIR:-{${_sev_home:-~},~}/.zsh},{${_sev_home:-~},~}/.zsh}/functions/**/*(/N^M)
+        "${fpath[@]%%/}"
+    )
     # remove bad paths
     for (( i = 1; i <= $#fpath; i++ )) {
         if [[ ! -d $fpath[$i] ]] {
@@ -106,7 +118,8 @@ function _sev_setpath {
     }
     # FPATH is not exported by default
     export FPATH
-    # un-unique system arrays for consistency
+
+    # un-unique system arrays as they are by default
     typeset +U path fpath
 }
 
@@ -223,8 +236,6 @@ if [[ ! -v _sev_setup_xdg ]] {
     ## merge with any existing dirs and remove duplicates using unique arrays
     # NOTE: we are accepting whatever value might be set for CONFIG and DATA;
     #       if it wasn't set, we just use default and leave it unset
-    # NOTE: include and then remove CONFIG_HOME and DATA_HOME to ensure they
-    #       are not present in the array if it was added before we got to it
 
     # source user dirs before other vars; technically it is against spec to
     # include any of the below dirs there, but you never know what crazy shit
@@ -233,25 +244,27 @@ if [[ ! -v _sev_setup_xdg ]] {
     [[ -f $XDG_CONFIG_HOME/user-dirs.dirs ]] &&
       emulate sh -c "source $XDG_CONFIG_HOME/user-dirs.dirs"
 
-    typeset -UT XDG_DATA_DIRS xdg_data_dirs
     [[ -v XDG_DATA_HOME ]] && export XDG_DATA_HOME
     [[ -e ${XDG_DATA_HOME:-~/.local/share} ]] ||
       mkdir -m760 ${XDG_DATA_HOME:-~/.local/share}
-    xdg_data_dirs=($XDG_DATA_HOME ${XDG_DATA_DIRS:+${xdg_data_dirs%%/}}
-      /{usr{,/local,/pkg},opt{,/*{/*,}}}/share(N))
+
+    typeset -xUT XDG_DATA_DIRS xdg_data_dirs
+    xdg_data_dirs=(${XDG_DATA_DIRS:+${xdg_data_dirs%%/}}
+      {${PREFIX%%/},}/{usr{,/local,/pkg},opt{,/*{/*,}}}/share(N^M))
     xdg_data_dirs=($xdg_data_dirs(/N))
-    export XDG_DATA_DIRS
+    ((${#xdg_data_dirs} == 0)) && unset XDG_DATA_DIRS
 
-    typeset -UT XDG_CONFIG_DIRS xdg_config_dirs
     [[ -v XDG_CONFIG_HOME ]] && export XDG_CONFIG_HOME
     [[ -e ${XDG_CONFIG_HOME:-~/.config} ]] ||
       mkdir -m760 ${XDG_CONFIG_HOME:-~/.config}
     # I am of the belief .local should follow FHS /usr/local...
     [[ -e ~/.local/etc ]] || ln -s ${XDG_CONFIG_HOME:-~/.config} ~/.local/etc
-    xdg_config_dirs=($XDG_CONFIG_HOME ${XDG_CONFIG_DIRS:+${xdg_config_dirs%%/}}
-      {,/usr{,/local,/pkg},opt{,/*{/*,}}}/etc/xdg(N))
+
+    typeset -xUT XDG_CONFIG_DIRS xdg_config_dirs
+    xdg_config_dirs=(${XDG_CONFIG_DIRS:+${xdg_config_dirs%%/}}
+      {${PREFIX%%/},}{,/usr{,/local,/pkg},opt{,/*{/*,}}}/etc/xdg(N^M))
     xdg_config_dirs=($xdg_config_dirs(/N))
-    export XDG_CONFIG_DIRS
+    ((${#xdg_config_dirs} == 0)) && unset XDG_CONFIG_DIRS
 
     [[ -v XDG_STATE_HOME ]] && export XDG_STATE_HOME
     [[ -e ${XDG_STATE_HOME:-~/.local/state} ]] ||
@@ -262,8 +275,8 @@ if [[ ! -v _sev_setup_xdg ]] {
     } else {
         export XDG_CACHE_HOME=$_sev_tmp/.xdg.cache
     }
-    [[ -e ${XDG_CACHE_HOME:-~/.cache} ]] ||
-      mkdir -m700 ${XDG_CACHE_HOME:-~/.cache}
+    [[ -e $XDG_CACHE_HOME ]] ||
+      mkdir -m700 $XDG_CACHE_HOME
 
     # NOTE: this can be set by systemd or other pre-shell supervisor, and if
     #       any services were started such as pipewire, we need to use the
@@ -273,14 +286,14 @@ if [[ ! -v _sev_setup_xdg ]] {
     } else {
         # make runtime dir in our session-specific tmpdir
         export XDG_RUNTIME_DIR=$TMPDIR/.xdg.runtime
-        # same as in tmpdir creation, ensure dir doesn't exist
+        # should be unique—same as in tmpdir creation, ensure dir doesn't exist
         if [[ -h $XDG_RUNTIME_DIR ]] {
             unlink $XDG_RUNTIME_DIR 2>/dev/null
         } elif [[ -e $XDG_RUNTIME_DIR ]] {
             rm -rf $XDG_RUNTIME_DIR 2>/dev/null
         }
     }
-    [[ ! -e $XDG_RUNTIME_DIR ]] &&
+    [[ -e $XDG_RUNTIME_DIR ]] ||
       mkdir -m700 $XDG_RUNTIME_DIR 2>/dev/null
 
     export _sev_setup_xdg=
This page took 0.046778 seconds and 4 git commands to generate.