From 0234e81811640360af82ddc2999595d2dae24518 Mon Sep 17 00:00:00 2001 From: Felitendo Date: Tue, 29 Sep 2026 08:15:38 +0200 Subject: [PATCH] fix: patch symlinked entries instead of writing through them --- doc/middleclick-autoscroll.1.scd | 4 +++- src/lib/common.sh | 8 ++++++- src/lib/patch.sh | 39 +++++++++++++++++++++++++++----- 3 files changed, 43 insertions(+), 8 deletions(-) diff --git a/doc/middleclick-autoscroll.1.scd b/doc/middleclick-autoscroll.1.scd index 89e6f57..e0a860d 100644 --- a/doc/middleclick-autoscroll.1.scd +++ b/doc/middleclick-autoscroll.1.scd @@ -116,7 +116,9 @@ Two ways, chosen per application. Programs that start themselves at login write their own entry into _~/.config/autostart_ pointing straight at their binary, bypassing the menu entry entirely. Those are patched in place as well, unless the application is -turned off in the applications list. +turned off in the applications list. An entry that is a symlink, often to an +application's entry in _/usr/share/applications_, is replaced by a patched +file rather than written through, and undoing puts the link back. Shortcuts on the desktop itself are patched in place too. Nothing in the XDG search path looks at that folder, so a shortcut that lives only there would diff --git a/src/lib/common.sh b/src/lib/common.sh index 487e688..db9aa5a 100644 --- a/src/lib/common.sh +++ b/src/lib/common.sh @@ -343,12 +343,18 @@ mca_backup_name() { # copy was stored under. Existing backups are never overwritten: the first copy # is the pristine one, and a second apply must not replace it with an already # patched version. +# +# A symlink is kept as a symlink, so undo can put the link back. It is taken +# again every time: this program never writes a link, so one is always pristine. mca_backup() { local file="$1" name name="$(mca_backup_name "$file")" mkdir -p "$MCA_BACKUPDIR" 2>/dev/null || return 1 - if [[ ! -e "$MCA_BACKUPDIR/$name" ]]; then + if [[ -L $file ]]; then + rm -f -- "$MCA_BACKUPDIR/$name" + cp -P -p -- "$file" "$MCA_BACKUPDIR/$name" 2>/dev/null || return 1 + elif [[ ! -e "$MCA_BACKUPDIR/$name" && ! -L "$MCA_BACKUPDIR/$name" ]]; then cp -p -- "$file" "$MCA_BACKUPDIR/$name" 2>/dev/null || return 1 fi printf '%s\n' "$name" diff --git a/src/lib/patch.sh b/src/lib/patch.sh index b2cbf7a..ca583d9 100644 --- a/src/lib/patch.sh +++ b/src/lib/patch.sh @@ -375,7 +375,7 @@ mca_desktop_apply() { [[ "$content" == "$(< "$src")" ]] && return 0 backup="$(mca_backup "$src")" || return 1 - if mca_write_if_changed "$src" "$content"$'\n'; then + if _mca_entry_write "$src" "$content"$'\n'; then MCA_CHANGES=$(( MCA_CHANGES + 1 )) fi mca_ledger_add inplace "$src" "$backup" @@ -418,6 +418,30 @@ mca_desktop_apply() { # Neither can be shadowed from anywhere, so both are edited where they stand, # with the original kept. +# _mca_entry_write +# Writes an entry edited in place, like mca_write_if_changed. A symlink is +# replaced by the edited file instead of written through: it usually points at +# a package's entry in /usr, which is not ours to change. Undo puts the link +# back from the backup. +_mca_entry_write() { + local file="$1" content="$2" tmp + + [[ -L $file ]] || { mca_write_if_changed "$file" "$content"; return; } + + tmp="$(mktemp "$file.XXXXXX")" || return 2 + chmod --reference="$file" -- "$tmp" 2>/dev/null || chmod 644 -- "$tmp" + + # A link into /usr/share/applications is trusted for where it points. A + # copy on the desktop is only trusted by KDE when it can be run. + chmod u+x -- "$tmp" 2>/dev/null || true + + if printf '%s' "$content" > "$tmp" && mv -f -- "$tmp" "$file"; then + return 0 + fi + rm -f -- "$tmp" + return 2 +} + # _mca_entry_patch_inplace # One desktop entry that lives outside the XDG search path, edited where it is # because there is nowhere to shadow it from. It follows the applications list @@ -482,7 +506,7 @@ _mca_entry_patch_inplace() { [[ "$content" == "$(< "$file")" ]] && return 0 backup="$(mca_backup "$file")" || return 0 - if mca_write_if_changed "$file" "$content"$'\n'; then + if _mca_entry_write "$file" "$content"$'\n'; then MCA_CHANGES=$(( MCA_CHANGES + 1 )) fi mca_ledger_add inplace "$file" "$backup" @@ -607,7 +631,7 @@ mca_prune_orphans() { # --------------------------------------------------------------------------- mca_revert_all() { - local kind path detail + local kind path detail backup [[ -f $MCA_LEDGER ]] || return 0 @@ -623,12 +647,15 @@ mca_revert_all() { fi ;; inplace) - if [[ -n $detail && -f "$MCA_BACKUPDIR/$detail" ]]; then + backup="$MCA_BACKUPDIR/$detail" + if [[ -n $detail ]] && [[ -f $backup || -L $backup ]]; then if [[ -e $path ]] || [[ -d "$(dirname -- "$path")" ]]; then - cp -p -- "$MCA_BACKUPDIR/$detail" "$path" 2>/dev/null \ + # A link goes back as a link, not written through. + [[ -L $backup ]] && rm -f -- "$path" + cp -P -p -- "$backup" "$path" 2>/dev/null \ && MCA_CHANGES=$(( MCA_CHANGES + 1 )) fi - rm -f -- "$MCA_BACKUPDIR/$detail" + rm -f -- "$backup" elif [[ -f $path ]]; then # No backup: the file did not exist before we wrote it. rm -f -- "$path"