On 23/03/10 12:22, Pádraig Brady wrote:
> On 23/03/10 06:15, Jim Meyering wrote:
>> Pádraig Brady wrote:
>>>
>>> However `replace` is used by mysql-server as previously mentioned.
>>> And `repl` is used by nmh. How about keeping with the terse unix
>>> tradition while alluding to the relationship with cp/mv/rm by using...
>>>
>>> rp
>>
>> I wouldn't object.
>>
>> A possible bastardized compromise: re-place
>> It's sort of analogous to in-place.
> 
> None of rp, inplace, re-place are great TBH.
> Anyway attached is the latest version.
> It's getting a bit complicated, but I don't
> think much more needs to go in (maybe translations?)

Thinking more about it, atomicity would be required in many cases
so therefore we would need a --atomic option to stop the fall back
to the non atomic update.

Also I noticed that `mv -b ./file.tmp ./file` is not atomic
because the renames are done like: rename(file,bak); rename(tmp,file);

I've updated the attached script accordingly.
It's starting to get a bit complex and hacky though.

cheers,
Pádraig.

#!/bin/sh

me=$(basename $0)

usage() {
  err=$1; out=2
  [ $err -eq 0 ] && out=1
  printf "\
$me [OPTION] 'FILTER' FILE...
Replace the contents of each FILE after processing with FILTER

      --atomic            at no point leave a FILE unavailable or inconsistent
      --backup[=CONTROL]  make a backup of each existing destination file
  -b                      like --backup but does not accept an argument
  -C, --compare           compare each pair of source and destination files, and
                            in some cases, do not modify the destination at all.
  -p, --preserve-timestamps  apply access/modification times of SOURCE files
                             to corresponding destination files.

  -S, --suffix=SUFFIX     override the usual backup suffix

The backup suffix is \`~', unless set with --suffix or SIMPLE_BACKUP_SUFFIX.
The version control method may be selected via the --backup option or through
the VERSION_CONTROL environment variable.  Here are the values:

  none            never make backups (even if --backup is given)
  numbered        make numbered backups
  existing        numbered if numbered backups exist, simple otherwise
  simple          always make simple backups

Report "$me" bugs to [email protected]
GNU coreutils home page: <http://www.gnu.org/software/coreutils/>
General help using GNU software: <http://www.gnu.org/gethelp/>
Report "$me" translation bugs to <http://translationproject.org/team/>
" >&$out
  exit $err
}

version() {
  Cmd=$1; Date=2010; Version=8.5 #TODO: auto update
  #TODO: translation
  printf "\
$Cmd (GNU coreutils) $Version
Copyright (C) $Date Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

Written by Pádraig Brady.
"
  exit 0
}

if getopt -T >/dev/null 2>&1; test $? -ne 4; then
  # Enhanced `getopt` not available and POSIX `getopts`
  # only supports short options, so revert to simple manual parsing
  if test $# -lt 2; then
    test "$1" = "--help" && usage 0
    test "$1" = "--version" && version "$me"
    usage 1
  fi
else
  OPT=$(
    getopt -o bCpS: \
           --long atomic,backup::,compare,help,\
preserve-timestamps,suffix:,version \
           -n"$me" -- "$@" ||
    usage 1
  )
  eval set -- "$OPT"

  while true; do
    case "$1" in
      --atomic)
        atomic=1; shift 1;;
      -b)
        test "$VERSION_CONTROL" || VERSION_CONTROL="existing"
        backup="$VERSION_CONTROL"; shift;;
      --backup)
        case "$2" in
          "") backup=existing; shift 2;;
          *) backup="$2"; shift 2;;
        esac;;
      -C|--compare) compare=1; shift;;
      -p|--preserve-timestamps) preserve_times=1; shift;;
      -S|--suffix) suffix="$2"; shift 2;;
      --help) usage 0; shift;;
      --version) version "$me"; shift;;
      --) shift; break ;;
      *) echo "Option processing error"; exit 1;;
    esac
  done

  if test "$backup"; then
    backup="--backup=$backup"
    test "$suffix" && backup="$backup --suffix=$suffix"
  fi

  if test $# -lt 1; then
    printf "\
$me: missing file operand
Try \`$me --help' for more information.
"
  fi
fi

filter="$1"; shift

cleanup() { rm -f "$tf"; }
trap "cleanup" EXIT

filter_file() {
  ret=0

  if $filter < "$file" > "$tf"; then
    if test "$preserve_times"; then
      touch "$tf" --reference="$file" || { ret=1; fail=1; }
    fi
  else
    ret=1; fail=1
  fi

  if test $ret = 0; then
    if test "$compare"; then
      cmp -s -- "$tf" "$file"
      status=$?
      test $status -eq 0 && { ret=1; } # Don't process further
      test $status -gt 1 && { ret=1; fail=1; }
    fi
  fi

  return $ret
}

fail=0
for file in "$@"; do
  dir=$(dirname -- "$file")
  tf=$(mktemp -q --tmpdir="$dir")
  cp_err=0
  # XXX: We need to preserve timestamps on this also.
  if test -e "$tf" && cp --attr -- "$file" "$tf" 2>/dev/null; then
    # Modify file atomically.

    # Note if we passed $backup to `mv` then the data will be
    # atomically consistent but the file may be missing for a short period.
    # Therefore we make an explicit backup first (hence increasing the
    # free space requirement to 200% of the original file).
    # Note `sed -i.bak` uses the quick rename() method, thus having the issue.
    # XXX: We leave this backup file around on subsequent failure.
    if test "$backup" && test "$atomic"; then
      quick_backup=""
      cp $backup --force "$file" "$file" || { cp_err=1; fail=1; }
    else
      quick_backup="$backup"
    fi

    # We could (prompt to) `chmod u+rw` here to allow updating non rw files?
    test $cp_err=0 && filter_file &&
    { mv $quick_backup -- "$tf" "$file" || fail=1; } # rename
  elif test "$atomic"; then # repeat to output errors
    cleanup
    tf=$(mktemp --tmpdir="$dir") &&
    cp --attr -- "$file" "$tf"
  else
    # cp doesn't support --attributes-only or
    # $dir is not writeable.  In this case we
    # use $TMPDIR, but don't use mv to unlink/copy
    # as $TMPDIR might not support all attrs of $dir.
    # Also we can't unlink in unwriteable dir.
    # We could (prompt to) `chmod u+rw` here to allow updating non rw files?
    cleanup # dir/file.tmp
    tf=$(mktemp)
    filter_file &&
    { cp $backup -- "$tf" "$file" || cp_err=1; fail=1; } # truncate and copy
    test $cp_err = 0 && test "$preserve_times" &&
      { touch "$file" --reference="$tf" || fail=1; }
    cleanup # tmp/file.tmp
  fi
done

exit $fail

Reply via email to