macOS ships /bin/bash 3.2.57 which has no `${var,,}` parameter expansion.
Replace with a portable case statement matching y/Y. Behaviorally
identical (read -n 1 already constrains input to a single char, and the
original code only accepted 'y' after lowercasing, so 'y' and 'Y' are
the only accepting inputs).
Repo-wide audit for other bash 4+ features (case modification, mapfile,
nameref locals, ${var@op} transforms, coproc) returned clean.
Repro before fix:
$ ./install.sh
Enable daily scheduled sync (launchd)? [y/N]: y
lib/common.sh: line 52: ${reply,,}: bad substitution
Co-authored-by: Cursor <cursoragent@cursor.com>
In bash, strings cannot contain NUL bytes, so `$'\0'` expands to the empty
string. The pattern `*$'\0'*` collapsed to `**`, which matches every
non-empty value, so `validate_path` aborted on every real path with
"Path contains control chars".
- Drop the NUL check entirely (impossible to violate by construction).
- Use a single case statement for the newline and traversal checks; more
predictable than chained [[ ... && ... ]] glob comparisons on bash 3.2.
Verified: paths with spaces, dots in basenames (~/.config, ~/.local), and
the iCloud bundle directory all pass; empty paths, "..", and embedded
newlines still reject.
Repro before fix:
$ ./install.sh
[ERR] Path contains control chars
Co-authored-by: Cursor <cursoragent@cursor.com>
- bin/ssr-update: fast-forward `git pull` on the clone with safety guards
(dirty tree, detached HEAD, divergent history all abort).
- Re-renders & reloads the launchd plist when launchd/ or bin/ssr* changed
AND the schedule is currently active, so the cron picks up any plist
template updates automatically.
- Reports VERSION delta, commit log, and new config keys introduced in
config/ssr.conf.example so users can opt-in via `ssr config edit`.
- `--dry-run` shows would-be changes without pulling or touching launchd.
- Wired into bin/ssr dispatcher + README updated (Updating section, layout
entry, command help line).
Co-authored-by: Cursor <cursoragent@cursor.com>