#!/bin/bash
#
# Function to roll files in the way that logrotate rolls log files
# Keeps up to the specified amount of files
#
#
# (C) 2022, AG
#
set -eu
# Globals, optional
declare -r SAVE_COUNT=10
declare COMPRESS=xz
declare EXT=".xz"

#
# roll_file()
# Description
# -----------
#   Roll a file up to a maximum keep count in the way that logrotate
#   rolls and keeps log file
#
# Arguments
# ---------
#   basefile: The filename that should be rolled
#   compressor: Optional; The name of the compression program (e.g. xz)
#   ext: Optional; The extension added by the compression program, including the dot (e.g. .xz)
#   maxkeep: Optional; The total number of rolled files to keep
#
# Caveats
# -------
# This will not clean up files that are above the keep count. For example, if
# your keep count is 5 and you basefile.8 exists, it will not be cleaned up. It
# will properly clean up basefile.5. If you're consistent in how you use this it
# should not be an issue
#
# Notes
# -----
# There must be a simpler and more elegant way to do this, but this works
# reliably so it's good enough :>
#
roll_file() {
  local ext="${EXT:-}"
  local -r basefile="$1"
  local -r compressor="${2:-${COMPRESS:-}}"
  local -r ext="${3:-${EXT:-}}"
  local -r maxkeep="${4:-$SAVE_COUNT}"

  if [ -e "$basefile" ]; then
    for i in $(seq $maxkeep -1 1); do
      nextfile="$basefile.$i$ext"
      next_keep_file="$basefile.$((i + 1))$ext"
      [[ ! -e "$nextfile" ]] && continue
      if [ $i -eq $maxkeep ]; then
        # Reached maximum keep count, remove it; it will be replaced
        rm -f "$nextfile"
        continue
      fi
      mv "$nextfile" "$next_keep_file"
    done
    # Finally, roll the base file and compress it if necessary
    local -r roll_to="$basefile.1"
    mv "$basefile" "$roll_to"
    if [ "$compressor" != "" ]; then
      # compress the rolled file if compressor is set and it exists
      if [ ! $(command -v "$compressor") ]; then
        echo "$compressor does not exist; file $basefile was rolled to file $roll_to but *not* compressed!"
        echo "Exiting ..."
        exit
      fi
      # Remove if it already exists
      rm -f "$roll_to$ext"
      # Compress it
      "$compressor" "$roll_to"
    fi
  fi
}

# Roll the file "somefile", using the global defaults
roll_file somefile
# Roll the file "somefile" using bzip2 to compress
roll_file somefile bzip2 .bz2
# Roll the file "somefile" using bzip2 to compress; keep only the latest 5 copies
roll_file somefile bzip2 .bz2 5