From fd6f22e512115753817163a4f4f39e29bfdbc805 Mon Sep 17 00:00:00 2001 From: librelad Date: Tue, 26 May 2026 22:45:36 +0100 Subject: [PATCH] fix(install): stop initUpdateConfigOption choking on '|' chars in comments MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit initUpdateConfigOption (init.sh) and commandUpdateConfigOption (the CLI wrapper heredoc) both rewrite CFG_= lines with a sed s-command using `|` as the delimiter. The escaping covered only `/` and `&` in $escaped_value, and $comment_part wasn't escaped at all — so any line whose comment contains a literal `|` blew up the substitution: sed: -e expression #1, char 167: unknown option to `s' The trigger in the install log is the CFG_INSTALL_MODE comment: # Installation Mode - ... [release:Release tarball (recommended)|git:Git clone (dev)|local:Local folder (dev)] Two sed errors in install-20260526-223006.log, both same line — once from initUpdateConfigOption during the initial-values pass, once from the CFG_INSTALL_MODE re-set later. The substitution silently failed (line not rewritten) and the install continued. Switch the delimiter to SOH (\x01). Text-based config values + comments never contain that byte, so the delimiter never needs to be escaped. Only `&` (whole-match insertion in the replacement) and `\` (escape char) remain hazardous in the replacement field, and BOTH are now neutralised in $escaped_value AND in $comment_part. Verified against the actual offending line: the old form reproduces `sed: unknown option to 's'` at char 165; the new form rewrites cleanly with every `|` in the comment preserved. Same fix applied to both functions — initUpdateConfigOption lives at install-time, commandUpdateConfigOption is baked into the CLI wrapper at /usr/local/lib/libreportal/libreportal; new installs pick up both from this commit. Co-Authored-By: Claude Opus 4.7 Signed-off-by: librelad --- init.sh | 33 ++++++++++++++++++++++++--------- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/init.sh b/init.sh index 0c630bd..d12ce28 100755 --- a/init.sh +++ b/init.sh @@ -338,14 +338,23 @@ initUpdateConfigOption() { for config_file in "$category_dir"/*; do if [ -f "$config_file" ] && [[ ! "$config_file" =~ \.category$ ]]; then if grep -q "^$config_option=" "$config_file"; then - local escaped_value=$(printf '%s\n' "$config_value" | sed -e 's/[\/&]/\\&/g') + # Use SOH (\x01) as the s-command delimiter — it can't appear + # in text-based config values or comments, so no field needs + # to escape the delimiter. The replacement field DOES need + # `&` (whole-match insertion) and `\` (escape char) neutralised + # in BOTH the value AND the comment — comments like + # `# Installation Mode - ...[release|git|local]` previously + # broke the `|`-delimited form with "unknown option to `s'". + local DELIM=$'\x01' + local escaped_value=$(printf '%s' "$config_value" | sed -e 's/[\\&]/\\&/g') local original_line=$(grep "^$config_option=" "$config_file") local comment_part=$(echo "$original_line" | sed -n "s|^$config_option=[^#]*\(#.*\)|\1|p") + local escaped_comment=$(printf '%s' "$comment_part" | sed -e 's/[\\&]/\\&/g') if [[ -n "$comment_part" ]]; then - sed -i "s|^$config_option=.*|$config_option=$escaped_value $comment_part|" "$config_file" + sed -i "s${DELIM}^$config_option=.*${DELIM}$config_option=$escaped_value $escaped_comment${DELIM}" "$config_file" else - sed -i "s|^$config_option=.*|$config_option=$escaped_value|" "$config_file" + sed -i "s${DELIM}^$config_option=.*${DELIM}$config_option=$escaped_value${DELIM}" "$config_file" fi source "$config_file" return 0 @@ -1249,18 +1258,24 @@ commandUpdateConfigOption() { for config_file in "$category_dir"/*; do if [ -f "$config_file" ] && [[ ! "$config_file" =~ \.category$ ]]; then if grep -q "^$config_option=" "$config_file"; then - # Escape special characters in the config value to prevent sed issues - local escaped_value=$(printf '%s\n' "$config_value" | sed -e 's/[\/&]/\\&/g') + # Use SOH (\x01) as the s-command delimiter — it can't appear + # in text-based config values or comments, so no field needs + # to escape the delimiter. The replacement field DOES need + # `&` (whole-match insertion) and `\` (escape char) neutralised + # in BOTH the value AND the comment — comments like + # `# Installation Mode - ...[release|git|local]` previously + # broke the `|`-delimited form with "unknown option to `s'". + local DELIM=$'\x01' + local escaped_value=$(printf '%s' "$config_value" | sed -e 's/[\\&]/\\&/g') # Extract the comment part first (everything after the first #) local original_line=$(grep "^$config_option=" "$config_file") local comment_part=$(echo "$original_line" | sed -n "s|^$config_option=[^#]*\(#.*\)|\1|p") - - # Replace the value, preserving comment if it existed + local escaped_comment=$(printf '%s' "$comment_part" | sed -e 's/[\\&]/\\&/g') if [[ -n "$comment_part" ]]; then - sed -i "s|^$config_option=.*|$config_option=$escaped_value $comment_part|" "$config_file" + sed -i "s${DELIM}^$config_option=.*${DELIM}$config_option=$escaped_value $escaped_comment${DELIM}" "$config_file" else - sed -i "s|^$config_option=.*|$config_option=$escaped_value|" "$config_file" + sed -i "s${DELIM}^$config_option=.*${DELIM}$config_option=$escaped_value${DELIM}" "$config_file" fi source "$config_file" return 0