#! /bin/sh
# savelog - save a log file
#    Copyright (C) 1987, 1988 Ronald S. Karr and Landon Curt Noll
#    Copyright (C) 1992  Ronald S. Karr
# Slight modifications by Ian A. Murdock <imurdock@gnu.ai.mit.edu>:
#	* uses `gzip' rather than `compress'
#	* doesn't use $savedir; keeps saved log files in the same directory
#	* reports successful rotation of log files
#	* for the sake of consistency, files are rotated even if they are
#	  empty
# More modifications by Guy Maor <maor@debian.org>:
#       * cleanup.
#       * -p (preserve) option
# 
# usage: savelog [-m mode] [-u user] [-g group] [-t] [-p] [-c cycle] [-l]
#                file...
#	-m mode	  - chmod log files to mode
#	-u user	  - chown log files to user
#	-g group  - chgrp log files to group
#	-c cycle  - save cycle versions of the logfile	(default: 7)
#	-t	  - touch file
#	-l	  - don't compress any log files	(default: compress)
#       -p        - preserve mode/user/group of original file
#	file 	  - log file names
#
# The savelog command saves and optionally compresses old copies of files.
# Older version of 'file' are named:
#
#		'file'.<number><compress_suffix>
#
# where <number> is the version number, 0 being the newest.  By default,
# version numbers > 0 are compressed (unless -l prevents it). The
# version number 0 is never compressed on the off chance that a process
# still has 'file' opened for I/O.
#
# If the 'file' does not exist and -t was given, it will be created.
#
# For files that do exist and have lengths greater than zero, the following 
# actions are performed.
#
#	1) Version numered files are cycled.  That is version 6 is moved to
#	   version 7, version is moved to becomes version 6, ... and finally
#	   version 0 is moved to version 1.  Both compressed names and
#	   uncompressed names are cycled, regardless of -t.  Missing version 
#	   files are ignored.
#
#	2) The new file.1 is compressed and is changed subject to 
#	   the -m, -u and -g flags.  This step is skipped if the -t flag 
#	   was given.
#
#	3) The main file is moved to file.0.
#
#	4) If the -m, -u, -g, -t, or -p flags are given, then the file is
#          touched into existence subject to the given flags.  The -p flag
#          will preserve the original owner, group, and permissions.
#
#	5) The new file.0 is changed subject to the -m, -u and -g flags.
#
# Note: If no -m, -u, -g, -t, or -p is given, then the primary log file is 
#	not created.
#
# Note: Since the version numbers start with 0, version number <cycle>
#       is never formed.  The <cycle> count must be at least 2.
#
# Bugs: If a process is still writing to the file.0 and savelog
#	moved it to file.1 and compresses it, data could be lost.
#	Smail does not have this problem in general because it
#	restats files often.

# common location
export PATH=$PATH:/sbin:/bin:/usr/sbin:/usr/bin
COMPRESS="gzip -9f"
DOT_Z=".gz"

# parse args
exitcode=0	# no problems to far
prog=`basename $0`
mode=
user=
group=
touch=
preserve=
count=7

usage()
{
    echo "Usage: $prog [-m mode][-u user][-g group][-t][-c cycle][-l][-p] file ..."
}


fixfile()
{
    if [ -n "$user" ]; then
	chown "$user" "$1"
    fi
    if [ -n "$group" ]; then 
	chgrp "$group" "$1"
    fi
    if [ -n "$mode" ]; then 
	chmod "$mode" "$1"
    fi
}


while getopts m:u:g:c:ltph opt ; do
	case "$opt" in
	m) mode="$OPTARG" ;;
	u) user="$OPTARG" ;;
	g) group="$OPTARG" ;;
	c) count="$OPTARG" ;;
	t) touch=1 ;;
	l) COMPRESS="" ;;
	p) preserve=1 ;;
	h) usage; exit 0 ;;
	*) usage; exit 1 ;;
	esac
done

shift $(($OPTIND - 1))

if [ "$count" -lt 2 ]; then
	echo "$prog: count must be at least 2" 1>&2
	exit 2
fi

# cycle thru filenames
while [ $# -gt 0 ]; do

	# get the filename
	filename="$1"
	shift

	# catch bogus files
	if [ -e "$filename" -a ! -f "$filename" ]; then
		echo "$prog: $filename is not a regular file" 1>&2
		exitcode=3
		continue
	fi

	# if not a file or empty, do nothing major
	# (in the Debian version, we rotate even if empty)
	#if [ ! -s $filename ]; then
		# if -t was given and it does not exist, create it
		if [ -n "$touch" -a ! -f "$filename" ]; then 
			touch "$filename"
			if [ "$?" -ne 0 ]; then
				echo "$prog: could not touch $filename" 1>&2
				exitcode=4
				continue
			fi
			fixfile "$filename"
		fi
	#	continue
	#fi

 	# be sure that the savedir exists and is writable
	# (in the Debian version, $savedir is . and not ./OLD)
 	savedir=`dirname "$filename"`
 	if [ -z "$savedir" ]; then
 		savedir=.
 	fi
 	if [ ! -d "$savedir" ]; then
 		mkdir "$savedir"
 		if [ "$?" -ne 0 ]; then
 			echo "$prog: could not mkdir $savedir" 1>&2
 			exitcode=5
 			continue
 		fi
 		chmod 0755 "$savedir"
 	fi
 	if [ ! -w "$savedir" ]; then
 		echo "$prog: directory $savedir is not writable" 1>&2
 		exitcode=7
 		continue
 	fi
 
	# determine our uncompressed file names
	newname=`basename "$filename"`
	newname="$savedir/$newname"

	# cycle the old compressed log files
	cycle=$(( $count - 1))
	rm -f "$newname.$cycle" "$newname.$cycle$DOT_Z"
	while [ $cycle -gt 1 ]; do
		# --cycle
		oldcycle=$cycle
		cycle=$(( $cycle - 1 ))
		# cycle log
		if [ -f "$newname.$cycle$DOT_Z" ]; then
			mv -f "$newname.$cycle$DOT_Z" \
			    "$newname.$oldcycle$DOT_Z"
		fi
		if [ -f "$newname.$cycle" ]; then
			# file was not compressed. move it anyway
			mv -f "$newname.$cycle" "$newname.$oldcycle"
		fi
	done

	# compress the old uncompressed log if needed
	if [ -f "$newname.0" ]; then
		if [ -z "$COMPRESS" ]; then
			newfile="$newname.1"
			mv "$newname.0" "$newfile"
		else
			newfile="$newname.1$DOT_Z"
#			$COMPRESS < $newname.0 > $newfile
#			rm -f $newname.0
			$COMPRESS "$newname.0"
			mv "$newname.0$DOT_Z" "$newfile"
		fi
		fixfile "$newfile"
	fi

	# create new file if needed
	if [ -n "$preserve" ]; then
		cp -p "$filename" "$filename.new"
		echo -n > "$filename.new"
		filenew=1
	elif [ -n "$touch$user$group$mode" ]; then
		touch "$filename.new"
		fixfile "$filename.new"
		filenew=1
	fi

	# link the file into the file.0 holding place
	if [ -f "$filename" ]; then
		if [ -n "$filenew" ]; then
			ln -f "$filename" "$newname.0"
			mv "$filename.new" "$filename"
		else
			mv "$filename" "$newname.0"
		fi
	fi
	[ ! -f "$newname.0" ] && touch "$newname.0"
	fixfile "$newname.0"

	# report successful rotation
	echo "Rotated \`$filename' at `date`."
done
exit $exitcode
