@@ -140,13 +140,6 @@ case "$USER_JAVA_OPTS" in
140140 ;;
141141esac
142142
143- # Escape replacement-special chars once; these values are loop-invariant.
144- # USER_JAVA_OPTS is injected via string-split below (not ${//}) — bash 4.x and 5.x
145- # treat '\\' in replacement strings differently, corrupting backslashes.
146- _escaped_deps_dir="${DEPS_DIR//\\/\\\\}"
147- _escaped_deps_dir="${_escaped_deps_dir//&/\\&}"
148- _escaped_home="${HOME//\\/\\\\}"
149- _escaped_home="${_escaped_home//&/\\&}"
150143_user_java_opts_placeholder='__JAVA_OPTS_BUILDPACK_PLACEHOLDER__'
151144
152145if [ -d "$DEPS_DIR/%s/java_opts" ]; then
@@ -159,11 +152,22 @@ if [ -d "$DEPS_DIR/%s/java_opts" ]; then
159152 # a literal $VAR to the JVM (Ruby buildpack parity).
160153 opts_content="${opts_content//\\\$/$_escaped_dollar_placeholder}"
161154
162- # Expand $DEPS_DIR and $HOME using bash parameter expansion.
163- # In ${var//pattern/repl}, '&' and '\' are special in replacement strings,
164- # so escape them first to preserve literal path contents.
165- opts_content="${opts_content//\$DEPS_DIR/$_escaped_deps_dir}"
166- opts_content="${opts_content//\$HOME/$_escaped_home}"
155+ # Replace $DEPS_DIR and $HOME using string-split, not ${//}: bash 4.x, 5.1.x
156+ # and 5.2.x differ in how '&' and '\' in replacement variables are handled,
157+ # causing literal \& and \\ in output on bash 5.1 (cflinuxfs4).
158+ _rest="$opts_content"; opts_content=""
159+ while [[ "$_rest" == *'$DEPS_DIR'* ]]; do
160+ opts_content+="${_rest%%%%'$DEPS_DIR'*}${DEPS_DIR}"
161+ _rest="${_rest#*'$DEPS_DIR'}"
162+ done
163+ opts_content+="$_rest"
164+
165+ _rest="$opts_content"; opts_content=""
166+ while [[ "$_rest" == *'$HOME'* ]]; do
167+ opts_content+="${_rest%%%%'$HOME'*}${HOME}"
168+ _rest="${_rest#*'$HOME'}"
169+ done
170+ opts_content+="$_rest"
167171
168172 # Resolve the trusted $(nproc) token to the computed processor count.
169173 opts_content="${opts_content//\$\(nproc\)/$_nproc_count}"
0 commit comments