#!/bin/bash

###########################################################
## Copyright (c) 2025 Alexey Kuryakin daqgroup@mail.ru
###########################################################

###########################################################
## Script to roll files in the way that logrotate rolls   #
## log files. Keeps up to the specified amount of files.  #
###########################################################

###########################################################
source $(crwkit which crwlib_base.sh); # Use base library #
source $(crwkit which crwlib_file.sh); # Use file library #
###########################################################

####################################################################
## Based on roll.sh from site: #####################################
## https://gist.github.com/mzpqnxow/88761eaaecc393ea3e75ab8040465eca
####################################################################

let fatal_notify_uses=1;      # uses notify-send for fatal?
let fatal_notify_colorized=1; # fatal message is colorized?

declare -i opt_verb=0;        # option --verb
declare -i opt_touch=0;       # option --touch
readonly defnmaxkeep="9";     # default value for nmaxkeep

function note(){
 if [[ $opt_verb -gt 0 ]]; then echo "$1"; fi;
};

function print_version(){
 echo "$scriptname version 1.0";
};

function print_copyright(){
 echo "Copyright (c) 2025 Alexey Kuryakin daqgroup@mail.ru";
};

function print_help(){
 print_version;
 print_copyright;
 echo "Utility to roll (rotate) logfile(s) (like logrotate do it).";
 echo "Usage:";
 echo "------";
 echo " $scriptname [-options] [basefile [nmaxkeep [compress]]]";
 echo "";
 echo "Description:";
 echo "------------";
 echo " Roll a file (basefile) up to a maximum keep count (nmaxkeep)";
 echo " in the way that logrotate rolls and keeps log files.";
 echo "Options:";
 echo "--------";
 echo " --version   - print version";
 echo " -h,--help   - print help screen";
 echo " -v,--verb   - verbose execution with delails and notes";
 echo " -m,--mute   - mute (silent) execution, no tootips on error";
 echo " -t,--touch  - touch basefile, i.e. create zero file after roll";
 echo "Arguments:";
 echo "----------";
 echo " \$1 basefile: The filename that should be rolled.";
 echo " \$2 nmaxkeep: Optional number of rolled files to keep. Default: 9.";
 echo " \$3 compress: Optional compressor name:gz,xz,bz,lz,no. Default: gz.";
 echo "Notes:";
 echo "------";
 echo "This will not clean up files that are above the keep count. For example, if";
 echo "your keep count is 5 and you basefile.8 exists, it will not be cleaned up. It";
 echo "will properly clean up basefile.5.";
};

function log_roll(){
 note "Fetch args:";
 local basefile="$1";
 local nmaxkeep="$2";
 local ext="$3";
 note "Prepare local vars:";
 local rolltemp="$basefile.0";
 local rolltarg="$basefile.1";
 local -i nblogroll=0;  # num succeed log rool  done
 local -i ncompress=0;  # num succeed compress  done
 local -i nbremoved=0;  # num files that was removed
 local -i nbtouched=0;  # num files that was touched
 local -i nbsucceed=0;  # num succeed operation done
 if [[ -z "$nmaxkeep" ]]; then nmaxkeep="$defnmaxkeep"; fi;
 if [[ -z "$ext" ]]; then ext="gz"; fi;
 note "Prepare compressor:";
 local compressor="";
 local copt="";
 case $ext in
  gz) ext=".gz";  compressor="gzip";  copt="-n";  ;;
  xz) ext=".xz";  compressor="xz";    copt="  ";  ;;
  bz) ext=".bz2"; compressor="bzip2"; copt="  ";  ;;
  lz) ext=".lz";  compressor="lzip";  copt="  ";  ;;
  no) ext="";     compressor="";      copt="  ";  ;;
  *)  fatal 1 "Error: bad compressor name: $ext"; ;;
 esac;
 note "Start rolling:";
 if [[ -e "$basefile" ]]; then
  colorize_head echo "Rolling file: $basefile";
  note "Check compresor";
  if [[ -n "$compressor" ]]; then
   if command -v "$compressor" > /dev/null 2>&1; then
    echo "Compressor found: $compressor";
   else
    fatal 1 "Error: Compressor not found $compressor";
   fi;
  fi;
  note "Remove temporary file:";
  if [[ -e "$rolltemp" ]]; then
   if rm -fv "$rolltemp"; then let nbremoved++; fi;
  fi;
  note "Try roll basefile to temporary:";
  if mv -fv "$basefile" "$rolltemp"; then
   note "Touch to create zero file:";
   if [[ $opt_touch -eq 1 ]]; then
    if touch "$basefile"; then let nbtouched++; fi;
   fi;
   note "Compress 1-st backup:";
   if [[ -e "$rolltarg" ]]; then
    if [[ -n "$compressor" ]]; then
     if command -v "$compressor" > /dev/null 2>&1; then
      if rm -fv "$rolltarg$ext";  then let nbremoved++; fi;
      if $compressor $copt "$rolltarg"; then let ncompress++; fi;
     else
      fatal 1 "Error: Compressor not found $compressor";
     fi;
    fi;
   fi;
   if mv -fv "$rolltemp" "$rolltarg"; then
    let nblogroll++;
    let nbsucceed++;
   fi;
   note "Roll compressed file:";
   for i in $(seq $nmaxkeep -1 1); do
    nextfile="$basefile.$i$ext";
    nextkeep="$basefile.$((i+1))$ext";
    if [[ -e "$nextfile" ]]; then
     if [[ $i -ge $nmaxkeep ]]; then
      if rm -fv "$nextfile"; then let nbremoved++; fi;
     fi;
     if [[ $i -lt $nmaxkeep ]]; then
      if mv -fv "$nextfile" "$nextkeep"; then let nblogroll++; fi;
     fi;
    fi;
   done;
  else
   fatal 1 "Error: file locked $basefile";
  fi;
 else
  fatal 1 "Error: file missed $basefile";
 fi;
 note "Report result:";
 local msg="Done $scriptbase:";
 msg+=" $nblogroll file(s) rolled,";
 msg+=" $ncompress compressed,";
 msg+=" $nbtouched touched,";
 msg+=" $nbremoved removed.";
 if [[ $nbsucceed -gt 0 ]]; then
  colorize_bold echo "$msg";
  return 0;
 else
  colorize_bold echo_to_stderr "$msg";
  return 1;
 fi;
};

######
# Main
######
function main(){
 if [[ -z "$1" ]]; then
  fatal 1 "Error: empty argiments. Call $scriptname -h for help.";
 fi;
 while is_option "$1"; do
  case $1 in
   --version)          print_version; return 0; ;;
   -h|-help|--help)    print_help;    return 0; ;;
   -v|-verb|--verb)    let opt_verb=1;          ;;
   -verbose|--verbose) let opt_verb=1;          ;;
   -m|-mute|--mute)    let fatal_notify_uses=0; ;;
   -t|-touch|--touch)  let opt_touch=1;         ;;
   *)                  fatal 1 "Error: unknown option $1"; ;;
  esac;
  shift;
 done;
 log_roll "$@";
};

main "$@";

##############
## END OF FILE
##############
