X-Git-Url: http://git.iain.cx/?p=profile.git;a=blobdiff_plain;f=.profile.d%2FPATH.bashrc;h=38bb82558f7b760c423768c39e9415f08ad7a5c5;hp=3113f75a54ddec55d0fff56c08de953c377c5ee8;hb=88bb919af5a85d1d7f791408058f9eee96140907;hpb=328f2f42404cb5de71e06be1e44ddc1957a89800 diff --git a/.profile.d/PATH.bashrc b/.profile.d/PATH.bashrc index 3113f75..38bb825 100644 --- a/.profile.d/PATH.bashrc +++ b/.profile.d/PATH.bashrc @@ -1,64 +1,179 @@ # 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)" + local path="$(eval echo \$$oldpath)" [ -z "$path" ] || eval "export $newpath='$path'" +} + +# Try to expand variables in a path definition, discarding it if expansion fails. +function expandpath() { + local path="$1"; shift - unset path newpath oldpath + 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'" +} + +# Remove entries which are symlinks to other existing entries. +function canonicalisepath() { + local path="$1"; shift + local newpath= + + 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 - unset path newpath dirs + 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