#!/bin/sh
COMMAND="$(basename "$0")"
ZYPPERLOG="/var/log/zypper.log"

ZL_usage() {
cat <<-EOF
	Usage: $COMMAND [-h] [-l FILE] [-r N] [-d YYYY[-MM[-DD]]] [PID]

	This tool helps you to access the zypper logfile '$ZYPPERLOG'.
	Run this command without any arguments to get a list of your
	zypper runs.
	Provide the PID of a zypper run as an argument to query the
	log for this run.

	Positional arguments:
	  PID            Get log for this PID

	Optional arguments:
	  -h, --help         show this help message and exit
	  -l FILE            Read only this file
	  -r N               Read N rotated logfiles
	  -d YYYY[-MM[-DD]]  Get runs for this date
EOF
}

ZL_msg()
{ echo "$*" >&2; }

ZL_printf()
{ printf "$@" >&2; }

ZL_exit() {
  local RET="${1:-0}"
  shift
  [ $# -ne 0 ] && ZL_msg "$COMMAND: $*"
  exit $RET
}

ZL_exit_parseargs() {
  ZL_exit 2 "$*"
}

# ######################################################################
# Parse command line arguments
options=$(getopt -n "$COMMAND" -o hl:r:d: --long help -- "$@")
[ $? -ne 0 ] && ZL_exit_parseargs

ROTATED=0
FILEARG=""
DATEARG_GREP=""
PIDARG_PATFILE=""
eval set -- "$options"
while [ $# -ne 0 ]; do
  case "$1" in
    -h|--help)
      ZL_usage
      exit 0
      ;;
    -l)
      shift	# get argument
      FILEARG="$FILEARG $1"
      ;;
    -r)
      shift	# get argument
      echo "$1" | grep -qE '^[[:digit:]]+$' || {
	ZL_exit_parseargs "-r: Invalid int value: '$1'"
      }
      ROTATED="$1"
      ;;
    -d)
      shift	# get argument
      echo "$1" | grep -qE '^[[:digit:]]{4}(-[[:digit:]]{2}(-[[:digit:]]{2})?)?$' || {
	ZL_exit_parseargs "-d: Time stamp '$1' does not match format 'YYYY[-MM[-DD]]'"
      }
      DATEARG_GREP="$DATEARG_GREP -e ^$1"
      ;;
    --)
      shift
      break
      ;;
  esac
  shift
done

# Default to zypper.log
if [ -z "$FILEARG" ]; then
  FILEARG=" $ZYPPERLOG"
fi

for PID in "$@"; do
  echo "$PID" | grep -qE '^[[:digit:]]+$' || {
    ZL_exit_parseargs "PID: Invalid int value: '$PID'"
  }
  if [ -z "$PIDARG_PATFILE" ]; then
    PIDARG_PATFILE=$(mktemp)
    trap "rm -f \"\$PIDARG_PATFILE\"" EXIT
  fi
  printf '^[^(]*(%s)\n' "$PID" >> "$PIDARG_PATFILE"
done

# ######################################################################
# main

may_collect()
{
  local FILE="$1"
  local FORMAT="Collect from %s ... %s\n"

  [ -f "$FILE" ] || {
    ZL_printf "$FORMAT" "$FILE" "No such file"
    return 1
  }
  [ -r "$FILE" ] || {
    ZL_printf "$FORMAT" "$FILE" "Permission denied"
    return 1
  }
  ZL_printf "$FORMAT" "$FILE"
  return 0
}


# Global variable for get_rotated results
_GET_ROTATED_RESULT=""

get_rotated()
{
  local STEM="$1"
  local ROTATED="$2"
  _GET_ROTATED_RESULT=""

  if [ "$ROTATED" -gt 0 ]; then
    local FILES=""
    local COUNT=0
    for F in "$STEM"-????????.*; do
      [ -e "$F" ] || continue
      FILES="$FILES $F"
      COUNT=$((COUNT + 1))
    done
    # Keep only the last $ROTATED files
    if [ "$COUNT" -gt "$ROTATED" ]; then
      local SKIP=$((COUNT - ROTATED))
      local I=0
      local TRIMMED=""
      for F in $FILES; do
	I=$((I + 1))
	if [ $I -gt $SKIP ]; then
	  TRIMMED="$TRIMMED $F"
	fi
      done
      FILES="$TRIMMED"
    fi
    for F in $FILES; do
      if may_collect "$F"; then
	_GET_ROTATED_RESULT="$_GET_ROTATED_RESULT $F"
      fi
    done
  fi
  if may_collect "$STEM"; then
    _GET_ROTATED_RESULT="$_GET_ROTATED_RESULT $STEM"
  fi
}

cat_files()
{
  local FILELIST="$1"
  for F in $FILELIST; do
    case "${F##*.}" in
      xz)
	xzcat "$F"
	;;
      bz2)
	bzcat "$F"
	;;
      gz)
	zcat "$F"
	;;
      *)
	cat "$F"
	;;
    esac
  done
}

date_filter()
{
  if [ -n "$DATEARG_GREP" ]; then
    eval grep $DATEARG_GREP
  else
    cat
  fi
}

pid_filter()
{
  if [ -n "$PIDARG_PATFILE" ]; then
    grep -f "$PIDARG_PATFILE"
  else
    grep -a '\] \(main\.cc(\|bin/y2start:\|genericfrontend\.cc(\)' |
    awk '
      function outh() {
	F="%-16s %6s %8s  %s\n"
	printf F,"TIME","PID","VER","CMD"
      }
      function outl( TD,TH,RP,V,C ) {
	if ( !F ) outh()
	sub ( ":..$", "", TH )
	gsub( ".*\\(|\\)", "", RP )
	printf F,TD" "TH,RP,V,C
      }
      ( $7 == "=====" && $8 == "Hi," ) { V = $11; next; }
      ( $7 == "=====" && substr( $8, 0, 1 ) == "\047"  ) {
        C = $0
	gsub( ".*===== \047|\047 =====.*$", "", C )
	gsub( "\047 \047", " ", C )
        outl( $1,$2,$4,V,C )
	next
      }
      /y2base called with/ {
	C = $0
	gsub( ".*y2base called with \\[\"|\"\\]$", "", C )
	gsub( "\", \"", " ", C )
	outl( $1,$2,$4,"----","y2base "C )
	next
      }
      /Launched YaST2 component/ {
	C = $0
	gsub( ".*Launched YaST2 component \047|\047$", "", C )
	gsub( "\047 \047", " ", C )
	outl( $1,$2,$4,"----",C )
	next
      }
      END { if ( !F ) { outh(); print " n/a"; } }
    '
  fi
}

parse_file()
{
  local FILELIST="$1"

  if [ -n "$DATEARG_GREP" ]; then
    cat_files "$FILELIST" | date_filter | pid_filter
  else
    cat_files "$FILELIST" | pid_filter
  fi
}

parse_stem()
{
  local STEM="$1"

  ZL_msg "================================================================================"
  get_rotated "$STEM" "$ROTATED"
  local STEM_FILES="$_GET_ROTATED_RESULT"
  if [ -z "$STEM_FILES" ]; then
    ZL_msg "No files to collect!"
    return 1
  fi
  ZL_msg
  parse_file "$STEM_FILES"
}

for F in $FILEARG; do
  parse_stem "$F"
done
