Fixes #9044, close #9667. Thanks to @taku0 for suggesting this solution. Now we have no modes starting with `/` or `+`. Rewrite the `-perm` parameters of find: - completely safe: rewrite `/0100` and `+100` to `-0100`, - slightly semantics-changing: rewrite `+111` to `-0100`. I cross-verified the `find` manual pages for Linux, Darwin, FreeBSD.
		
			
				
	
	
		
			63 lines
		
	
	
		
			2.4 KiB
		
	
	
	
		
			Bash
		
	
	
	
	
	
			
		
		
	
	
			63 lines
		
	
	
		
			2.4 KiB
		
	
	
	
		
			Bash
		
	
	
	
	
	
# This setup hook causes the fixup phase to rewrite all script
 | 
						|
# interpreter file names (`#!  /path') to paths found in $PATH.  E.g.,
 | 
						|
# /bin/sh will be rewritten to /nix/store/<hash>-some-bash/bin/sh.
 | 
						|
# /usr/bin/env gets special treatment so that ".../bin/env python" is
 | 
						|
# rewritten to /nix/store/<hash>/bin/python.  Interpreters that are
 | 
						|
# already in the store are left untouched.
 | 
						|
 | 
						|
fixupOutputHooks+=('if [ -z "$dontPatchShebangs" ]; then patchShebangs "$prefix"; fi')
 | 
						|
 | 
						|
patchShebangs() {
 | 
						|
    local dir="$1"
 | 
						|
    header "patching script interpreter paths in $dir"
 | 
						|
    local f
 | 
						|
    local oldPath
 | 
						|
    local newPath
 | 
						|
    local arg0
 | 
						|
    local args
 | 
						|
    local oldInterpreterLine
 | 
						|
    local newInterpreterLine
 | 
						|
 | 
						|
    find "$dir" -type f -perm -0100 | while read f; do
 | 
						|
        if [ "$(head -1 "$f" | head -c +2)" != '#!' ]; then
 | 
						|
            # missing shebang => not a script
 | 
						|
            continue
 | 
						|
        fi
 | 
						|
 | 
						|
        oldInterpreterLine=$(head -1 "$f" | tail -c +3)
 | 
						|
        read -r oldPath arg0 args <<< "$oldInterpreterLine"
 | 
						|
 | 
						|
        if $(echo "$oldPath" | grep -q "/bin/env$"); then
 | 
						|
            # Check for unsupported 'env' functionality:
 | 
						|
            # - options: something starting with a '-'
 | 
						|
            # - environment variables: foo=bar
 | 
						|
            if $(echo "$arg0" | grep -q -- "^-.*\|.*=.*"); then
 | 
						|
                echo "unsupported interpreter directive \"$oldInterpreterLine\" (set dontPatchShebangs=1 and handle shebang patching yourself)"
 | 
						|
                exit 1
 | 
						|
            fi
 | 
						|
            newPath="$(command -v "$arg0" || true)"
 | 
						|
        else
 | 
						|
            if [ "$oldPath" = "" ]; then
 | 
						|
                # If no interpreter is specified linux will use /bin/sh. Set
 | 
						|
                # oldpath="/bin/sh" so that we get /nix/store/.../sh.
 | 
						|
                oldPath="/bin/sh"
 | 
						|
            fi
 | 
						|
            newPath="$(command -v "$(basename "$oldPath")" || true)"
 | 
						|
            args="$arg0 $args"
 | 
						|
        fi
 | 
						|
 | 
						|
        newInterpreterLine="$newPath $args"
 | 
						|
 | 
						|
        if [ -n "$oldPath" -a "${oldPath:0:${#NIX_STORE}}" != "$NIX_STORE" ]; then
 | 
						|
            if [ -n "$newPath" -a "$newPath" != "$oldPath" ]; then
 | 
						|
                echo "$f: interpreter directive changed from \"$oldInterpreterLine\" to \"$newInterpreterLine\""
 | 
						|
                # escape the escape chars so that sed doesn't interpret them
 | 
						|
                escapedInterpreterLine=$(echo "$newInterpreterLine" | sed 's|\\|\\\\|g')
 | 
						|
                sed -i -e "1 s|.*|#\!$escapedInterpreterLine|" "$f"
 | 
						|
            fi
 | 
						|
        fi
 | 
						|
    done
 | 
						|
 | 
						|
    stopNest
 | 
						|
}
 |