datatracker/bin/update
Robert Sparks ddcd80766e Merged in [19071] from mark@painless-security.com:
Update the mkdevbranch script for new Trac location, don't clobber existing paths
The script has been cleaned up, adjusted to avoid clobbering an SVN path that already exists when running it for a single sprinter login, and run with the new locations for the Trac system.
This is a decently huge rewrite of the script.  It's pulled a lot of logic into functions, excised the python code into its own file, etc.
 - Legacy-Id: 19076
Note: SVN reference [19071] has been migrated to Git commit 537b76eb76170d69036999f8a8ef20fd81caca93
2021-06-04 18:42:01 +00:00

230 lines
7 KiB
Bash
Executable file

#!/bin/bash
version="0.34"
program=$(basename $0)
NEW="" # If there are more than $NEW % new lines, skip update
OLD="" # If there are more than $OLD % deleted lines, skip update
FILE=""
verbose=""
silent=""
# ----------------------------------------------------------------------
function usage() {
cat <<EOF
NAME
$program - conditionally update target file.
SYNOPSIS
$program [OPTIONS] FILE
DESCRIPTION
$program reads input from a pipe or file and saves it to a target
(FILE) if there are changes. If the new content is the same as the
old, the target is left untouched. By default, the target is also
left untouched if the new content is empty. There are options to
also abstain from applying an update if the changes are too large,
and to back up the previous version.
The purpose is to handle files with dynamically generated content in
such a manner that timestamps don't change if the content doesn't change,
and mistakes in content generation doesn't unnecessarily propagate to
the target.
OPTIONS
EOF
if [ "$(uname)" = "Linux" ]; then
egrep "^[ ]+[-][A-Za-z| -]+\*?\)[ ]+[A-Za-z].+#" $0 | tr -s "\t|" "\t," | sed -r -e 's/\)[ \t]+([A-Z]+)="\$2"[^#]*#/=\1\t/' -e 's/\)[^#]*#/\t/'
else
egrep "^[ ]+[-][A-Za-z| -]+\*?\)[ ]+[A-Za-z].+#" $0 | sed 's/\|.*"\$2"[^#]*#/ /'| sed -E 's/\|.*\)[^#]*#/ /'
fi
cat <<EOF
AUTHOR
Henrik Levkowetz <henrik@levkowetz.com>
EOF
exit
}
# ----------------------------------------------------------------------
function note() {
if [ -n "$verbose" ]; then
echo -e "$program: $*"
fi
}
# ----------------------------------------------------------------------
function warn() {
[ "$QUIET" ] || echo -e "$program: $*"
}
# ----------------------------------------------------------------------
function err() {
echo -e "$program: $*" > /dev/stderr
}
# -----------------------------------------------------------------------------
function leave() {
errcode=$1; shift
if [ "$errcode" -ge "2" ]; then warn "$*"; else note "$*"; fi
if [ -f "$tempfile" ]; then rm $tempfile; fi
if [ -f "$difffile" ]; then rm $difffile; fi
if [ "$errcode" = "1" -a "$RESULT" = "0" ]; then exit 0; else exit $errcode; fi
}
# ----------------------------------------------------------------------
# Set up error trap
trap 'leave 127 "$program($LINENO): Command failed with error code $? while processing '$origfile'."' ERR
# exit with a message if a command fails
set -e
# ----------------------------------------------------------------------
# Get any options
#
# Default values
PAT="\$path\$base.%Y-%m-%d_%H%M"
RESULT="0"
QUIET=""
# Based on the sample code in /usr/share/doc/util-linux/examples/parse.bash.gz
if [ "$(uname)" = "Linux" ]; then
GETOPT_RESULT=$(getopt -o bc:ef:hn:o:p:qrvV --long backup,maxchg:,empty,file:,help,maxnew:,maxold:,prefix:,report,quiet,verbose,version -n "$program" -- "$@")
else
GETOPT_RESULT=$(getopt bc:ef:hn:o:p:qrvV "$@")
fi
if [ $? != 0 ] ; then echo "Terminating..." >&2 ; exit 1 ; fi
note "GETOPT_RESULT: $GETOPT_RESULT"
eval set -- "$GETOPT_RESULT"
while true ; do
case "$1" in
-b|--backup) backup=1; shift ;; # Back up earlier versions by creating a backup file
-c|--maxchg) CHG="$2"; shift 2 ;; # Limit on percentage of changed lines
-e|--empty) empty=1; shift ;; # Permit the update to be empty (default: discard)
-f|--file) FILE="$2"; shift 2 ;; # Read input from FILE instead of standard input
-h|--help) usage; shift ;; # Show this text and exit
-n|--maxnew) NEW="$2"; shift 2 ;; # Limit on percentage of new (added) lines
-o|--maxold) OLD="$2"; shift 2 ;; # Limit on percentage of old (deleted) lines
-p|--pat*) PAT="$2"; shift 2 ;; # Backup name base ('$path$base.%Y%m%d_%H%M')
-q|--quiet) QUIET=1; shift;; # Be less verbose
-r|--result) RESULT=1; shift ;; # Return 1 if update not done
-v|--verbose) verbose=1; shift ;; # Be more verbose about what's happening
-V|--version) echo -e "$program\t$version"; exit;; # Show version and exit
--) shift ; break ;;
*) echo "$program: Internal error, inconsistent option specification." ; exit 1 ;;
esac
done
if [ $CHG ]; then OLD=$CHG; NEW=$CHG; fi
if [ $# -lt 1 ]; then echo -e "$program: Missing output filename\n"; usage; fi
origfile=$1
tempfile=$(mktemp)
difffile=$(mktemp)
if [ -e "$origfile" ]; then
cp -p $origfile $tempfile # For ownership and permissions
cat $FILE > $tempfile
[ "$FILE" ] && touch -r $FILE $tempfile
# This won't work if we don't have sufficient privileges:
#chown --reference=$origfile $tempfile
#chmod --reference=$origfile $tempfile
else
cat $FILE > $origfile
[ "$FILE" ] && touch -r $FILE $tempfile
leave 0 "Created file '$origfile'"
fi
origlen=$(wc -c < $origfile)
newlen=$(wc -c < $tempfile)
if [ $origlen = 0 -a $newlen = 0 ]; then
rm $tempfile
leave 1 "New content is identical (and void) - not updating '$origfile'."
fi
if [ $newlen = 0 -a -z "$empty" ]; then
leave 1 "New content is void - not updating '$origfile'."
fi
diff $origfile $tempfile > $difffile || [ $? -le 1 ] && true # suppress the '1' error code on differences
difflen=$(wc -l < $difffile)
if [ $difflen = 0 ]; then
leave 1 "New content is identical - not updating '$origfile'."
fi
if [ "$OLD" -o "$NEW" ]; then
if [ "$NEW" ]; then maxnew=$(( $origlen * $NEW / 100 )); fi
if [ "$OLD" ]; then maxdel=$(( $origlen * $OLD / 100 )); fi
newcount=$(grep "^> " $difffile | wc -c)
outcount=$(grep "^< " $difffile | wc -c)
delcount=$(grep "^! " $difffile | wc -c)
delcount=$(( $outcount + $delcount ))
rm $difffile
if [ "$OLD" ]; then
if [ "$delcount" -ge "$maxdel" ]; then
cp $tempfile $origfile.update
leave 2 "New content has too many removed lines ($delcount/$origlen)\n - not updating '$origfile'.\nNew content placed in '$origfile.update' instead"
fi
fi
if [ "$NEW" ]; then
if [ "$newcount" -ge "$maxnew" ]; then
cp $tempfile $origfile.update
leave 2 "New content has too many added lines ($newcount/$origlen)\n - not updating '$origfile'.\nNew content placed in '$origfile.update' instead"
fi
fi
fi
if [ "$backup" ]; then
path=${origfile%/*}
name=${origfile##*/}
base=${name%.*}
ext=${origfile##*.}
if [ "$ext" = "$origfile" ]; then
ext=""
elif [ ! "${ext%/*}" = "$ext" ]; then
ext=""
else
ext=".$ext"
fi
if [ "$path" = "$origfile" ]; then
path=""
else
path="$path/"
fi
ver=1
backfile=$(eval date +"$PAT")
backpath="${backfile%/*}"
if [ "$backpath" = "$backfile" ]; then
backpath="."
fi
if [ ! -d $backpath ]; then
if [ -e $backpath ]; then
leave 3 "The backup path '$backpath' exists but isn't a directory"
else
mkdir -p $backpath
fi
fi
while [ -e "$backfile,$ver$ext" ]; do
ver=$(( $ver+1 ))
done
note "Saving backup: $backfile,$ver$ext"
cp -p "$origfile" "$backfile,$ver$ext"
chmod -w "$backfile,$ver$ext" || true
fi
if ! mv $tempfile $origfile; then cp -p $tempfile $origfile; fi
leave 0 "Updated file '$origfile'"