#!/bin/sh # Copyright (c) 2022–2026 Thomas Voss # # Permission to use, copy, modify, and distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL # WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED # WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE # AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL # DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA # OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER # TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR # PERFORMANCE OF THIS SOFTWARE. # Copy an existing text file to a temporary location. Then edit the file. # Attempt to then transfer the temporary file back to the original location if # the temprary file has been altered. Conclude with a little clean-up. Try to # avoid deleting any changes. warn() { echo "$PROG: $1" >&2; } err() { warn "$1" exit 1 } get_intr() { stty -a | sed -En ' /^(.* )?intr = / { s/// s/;.*$// p }' } get_abspath() ( cd "$(dirname "$1")" echo "$(pwd)/$(basename "$1")" ) PROG="${0##*/}" if [ $# -ne 1 ] then echo "Usage: $PROG file" exit 1 fi [ ! -f "$1" ] && err "$1: file does not exist or is a special file/link" [ -L "$1" ] && err "$1: file is a symbolic link; refusing to edit" [ ! -r "$1" ] && err "$1: file cannot be read by the current user" tmp="$(mktemp -t doasedit.XXXXXXXX)" || err 4 "could not create temporary file" trap "rm -f \"$tmp\"" EXIT HUP INT TERM cp "$1" $tmp || err "$1: unable to copy file" abspath="$(get_abspath "$1")" DOASEDIT_EDITING="$abspath" "${VISUAL:-${EDITOR:-vi}}" "$tmp" || exit $? if cmp -s "$1" "$tmp" then warn "file unchanged; not writing back to the original location" exit 0 fi # At this point the file has been changed. Make sure it still exists. if [ -f "$tmp" ] then doas cp "$tmp" "$1" until cmp -s "$tmp" "$1" do warn "$1: copying failed; press enter to try again, or $(get_intr) to cancel" read _ doas cp "$tmp" "$1" done fi