PYENV prompt.
[profile.git] / .profile.d / PATH.bashrc
index 66cd709..38bb825 100644 (file)
-# $Id$
-#
 # Path information is stored on separate lines in XXXdirs.
-# We extract each directory exists and add it to the appropriate PATH.
+# We check each directory exists and add it to the appropriate PATH.
+# Prepend paths with XXXdirs.pre.  They are guaranteed to precede other paths.
+# Append paths with XXXdirs.post.  They are not guaranteed to follow others.
+# Set per-OS files in $SYSTEM[/$ARCHITECTURE] subdirectories of $DIR.
 #
 
 # Location of the XXXdirs files.
-DIR=$HOME/.profile.d
+DIR=${PROFILE_HOME:-~}/.PATH
+
+# Paths to set and the file to get them from @variable to copy from.
+PATHS="
+PATH:bindirs
+C_INCLUDE_PATH:incdirs
+CPLUS_INCLUDE_PATH:@C_INCLUDE_PATH
+LD_LIBRARY_PATH:libdirs
+MANPATH:mandirs
+PKG_CONFIG_PATH:pkgdirs
+"
+
+function sanitisepath() {
+  local paths="${@//:/ }"
+  local sane
+
+  for path in $paths; do
+    paths="${paths## }"
+    paths="${paths%% }"
+    paths="${paths#$path} "
+    if [ "${sane/:$path:/}" = "$sane" ]; then
+      sane="$sane:$path:"
+    fi
+  done
+
+  sane="${sane//::/:}"
+  sane="${sane#:}"
+  sane="${sane%:}"
+
+  echo "$sane"
+}
 
 # Set one path to be the same as another.
 function copypath() {
-  newpath="$1"; shift
-  oldpath="$1"; shift
+  local newpath="$1"; shift
+  local oldpath="$1"; shift
 
   # Sanitise and export.
-  path="$(eval echo \$$oldpath)"
-  [ -z "$path" ] || eval export $newpath="$path"
+  local path="$(eval echo \$$oldpath)"
+  [ -z "$path" ] || eval "export $newpath='$path'"
+}
 
-  unset path newpath oldpath
+# Try to expand variables in a path definition, discarding it if expansion fails.
+function expandpath() {
+  local path="$1"; shift
+
+  while [ -n "$path" ]; do
+    [ "${path/\$/}" = "$path" ] && return 0
+    path="${path#*\$}"
+
+    local var="${path%%/*}"
+    eval "[ -n \"\$$var\" ] || return 1"
+  done
+
+  return 0
+}
+
+# Helper.
+function addpath() {
+  local path="$1"; shift
+  local dir="$1"; shift
+
+  if ! expandpath "$dir"; then
+    echo "$path"
+  else
+    dir=$(eval echo "$dir")
+    if [ -d "$dir" ]; then
+      echo "$path:$dir"
+    else
+      echo "$path"
+    fi
+  fi
 }
 
 # Set a path from directories.
 function makepath() {
-  newpath="$1"; shift
-  dirs="$1"; shift
-
-  # Check the file exists.
-  [ -e "$DIR/$dirs" ] || return
+  local newpath="$1"; shift
+  local dirs="$1"; shift
 
   # Set IFS to newline only so that we can read $(embedded shell commands).
-  JGD=$IFS
+  local JGD=$IFS
   IFS='
 '
   # Read them.
-  path=
+  local path=
   while read dir; do
-    dir=$(eval echo "$dir")
-    [ -d "$dir" ] || continue
-
-    path="$path:$dir"
-  done < "$DIR/$dirs"
-  unset dir
+    path=$(addpath "$path" "$dir")
+    if [ -n "$PROFILE_HOME" -a ! "${dir#\$HOME}" = "$dir" ]; then
+      dir="$PROFILE_HOME${dir#\$HOME}"
+      path=$(addpath "$path" "$dir")
+    fi
+  done < "$dirs"
 
   # Restore IFS.
   IFS=$JGD
-  unset JGD
+
+  # Are we setting, prepending or appending?
+  local existing="$(eval echo \$$newpath)"
+  dirs="${dirs##*/}"
+  local where="${dirs##*.}"
+  case "$where" in
+    "$dirs") ;;
+    pre) path="$path:$existing";;
+    post) path="$existing:$path";;
+    *) return;;
+  esac
 
   # Sanitise path.
-  path=${path#:}
+  path=$(sanitisepath "$path")
   [ -z "$path" ] && return
 
   # Export.
-  eval export $newpath="$path"
+  eval "export $newpath='$path'"
+}
+
+# Remove entries which are symlinks to other existing entries.
+function canonicalisepath() {
+  local path="$1"; shift
+  local newpath=
 
-  unset path newpath dirs
+  local JGD=$IFS
+  IFS='
+'
+
+  local dirs=$(eval echo "\$$path")
+  local check=":$dirs:"
+  for dir in ${dirs//:/
+}; do
+    if [ -L "$dir" ]; then
+      # Is this a symlink to another entry?
+      local canon=$(readlink -f "$dir" 2>/dev/null)
+      if [ -n "$canon" ]; then
+        [ "${check/:$canon:/}" = "$check" ] || continue
+      fi
+    fi
+
+    newpath="$newpath:$dir"
+  done
+
+  IFS=$JGD
+
+  eval "export $path='${newpath##:}'"
 }
 
-makepath PATH bindirs
-makepath C_INCLUDE_PATH incdirs
-copypath CPLUS_INCLUDE_PATH C_INCLUDE_PATH
-makepath LD_LIBRARY_PATH libdirs
-copypath LD_RUN_PATH PATH
-makepath MANPATH mandirs
-makepath PKG_CONFIG_PATH pkgdirs
+# Construct directory list, omitting nonexistent and undefined ones.
+dirs=
+for dir in "${SYSTEM:-@}/${ARCHITECTURE:-@}" "${SYSTEM:-@}" ""; do
+  [ "${dir/@/}" = "$dir" ] || continue
+  [ -d "$DIR/$dir" ] || continue
+  dirs="$dirs,$DIR/$dir"
+  dirs="${dirs%%/}"
+done
+dirs="${dirs##,}"
+[ "${dirs/,/}" = "$dirs" ] || dirs="{$dirs}"
+
+for path in $PATHS; do
+  var="${path%:*}"
+  source="${path#*:}"
+
+  if [ "${source#@}" = "$source" ]; then
+    for dir in $(eval echo $dirs/$source{,.pre,.post}); do
+      [ -e "$dir" ] || continue
+      makepath "$var" "$dir"
+    done
+    canonicalisepath "$var"
+  else
+    copypath "$var" "${source#@}"
+  fi
+done
 
-unset dirs copypath makepath newpath
+unset DIR PATHS dir dirs path var source expandpath sanitisepath copypath makepath newpath addpath canonicalisepath