#! /bin/bash
# vim: set filetype=bash:

# to-lzip: Converts .Z, .lzo, .zip, .rar, .gz, .bz2, .zst, and .lzma files to
# lz format.

# Copyright (C) 2009-2020 by Brian Lindholm.  This file is part of the
# littleutils utility set.
#
# The to-lzip utility is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 3, or (at your option) any later version.
#
# The to-lzip utility is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
# more details.
#
# You should have received a copy of the GNU General Public License along with
# the littleutils.  If not, see <https://www.gnu.org/licenses/>.

# set up traps
trap 'rm -f ${TMPFILE} ; exit 1' 1 2 3 13 15
trap 'rm -f ${TMPFILE} ; exit 0' 0

# get command-line options
DELORIG='no'
LZMAUTIL='lzma'
MAXCOMP='no'
OVERWRITE='yes'
PREFER=''
RETAINTIME='no'
THREADS='1'
while getopts 46dhkmnp:t opts
do
  case $opts in
    4) PREFER='--prefer-family=IPv4' ;;
    6) PREFER='--prefer-family=IPv6' ;;
    d) DELORIG='yes' ;;
    h) echo 'to-lzip 1.2.3'
       echo 'usage: to-lzip [-(IPv)4] [-(IPv)6] [-d(el_orig)] [-k(eep_smallest)]'
       echo '               [-m(ax_compression)] [-n(o_overwrite)] [-p threads]'
       echo '               [-t(imestamp retain)] [-h(elp)] filename ...'
       exit 0 ;;
    k) DELORIG='small' ;;
    m) MAXCOMP='yes' ;;
    n) OVERWRITE='no' ;;
    p) THREADS=${OPTARG} ;;
    t) RETAINTIME='yes' ;;
    *) echo 'to-lzip 1.2.3'
       echo 'usage: to-lzip [-(IPv)4] [-(IPv)6] [-d(el_orig)] [-k(eep_smallest)]'
       echo '               [-m(ax_compression)] [-n(o_overwrite)] [-p threads]'
       echo '               [-t(imestamp retain)] [-h(elp)] filename ...'
       exit 1 ;;
  esac
done
shift `expr ${OPTIND} - 1`
if [ "$MAXCOMP" = 'yes' ]; then
  LZIP_OPTS='-9'
else
  LZIP_OPTS='-6'
fi
if [ "$THREADS" = '1' ]; then
  ENGINE='lzip'
else
  command -v plzip > /dev/null
  if [ "$?" = '0' ]; then
    if [ "$THREADS" = '0' ]; then
      if [ -r /proc/cpuinfo ]; then
        NCORE=`grep -c '^processor' /proc/cpuinfo`
      else
        NCORE=`nproc`
      fi
      ENGINE="plzip -n ${NCORE}"
    else
      ENGINE="plzip -n ${THREADS}"
    fi
  else
    echo 'to-lzip warning: plzip not found; reverting to lzip'
    ENGINE='lzip'
  fi
fi

# run through files
while [ $# -gt 0 ]; do

  # if file is actually a URL, attempt to download, otherwise verify that it already exists
  NAME="$1"
  if [ "${NAME:0:8}" = 'https://' -o "${NAME:0:7}" = 'http://' -o "${NAME:0:6}" = 'ftp://' ]; then
    wget ${PREFER} -q ${NAME}
    NAME=${NAME##*/}
    if [ ! -f "$NAME" -o ! -r "$NAME" ]; then
      echo "${NAME} failed to download!"
      shift; continue
    fi
  elif [ ! -f "$NAME" -o ! -r "$NAME" ]; then
    echo "${NAME} is not a readable file!"
    shift; continue
  fi
  echo -n "${NAME}... "

  # determine filepath and check for writeability
  FILENAME="${NAME##*/}"
  FILEPATH="${NAME%${FILENAME}}"
  FILEPATH="${FILEPATH%/}"
  if [ "X$FILEPATH" = 'X' ]; then
    FILEPATH='.'
  fi
  if [ ! -w "$FILEPATH" ]; then
    echo 'failed because target directory is non-writeable'
    shift; continue
  fi

  # determine new name and check for availability
  if  [ "${NAME#${NAME%.bz2}}" = '.bz2' ]; then
    NEWNAME="${NAME%.bz2}.lz"
  elif [ "${NAME#${NAME%.tbz}}" = '.tbz' ]; then
    NEWNAME="${NAME%.tbz}.tlz"
  elif  [ "${NAME#${NAME%.gz}}" = '.gz' ]; then
    NEWNAME="${NAME%.gz}.lz"
  elif [ "${NAME#${NAME%.tgz}}" = '.tgz' ]; then
    NEWNAME="${NAME%.tgz}.tlz"
  elif [ "${NAME#${NAME%.lzma}}" = '.lzma' ]; then
    NEWNAME="${NAME%.lzma}.lz"
  elif  [ "${NAME#${NAME%.lzo}}" = '.lzo' ]; then
    NEWNAME="${NAME%.lzo}.lz"
  elif  [ "${NAME#${NAME%.rar}}" = '.rar' ]; then
    NEWNAME="${NAME%.rar}.tar.lz"
  elif  [ "${NAME#${NAME%.RAR}}" = '.RAR' ]; then
    NEWNAME="${NAME%.RAR}.tar.lz"
  elif  [ "${NAME#${NAME%.Z}}" = '.Z' ]; then
    NEWNAME="${NAME%.Z}.lz"
  elif  [ "${NAME#${NAME%.zip}}" = '.zip' ]; then
    NEWNAME="${NAME%.zip}.tar.lz"
  elif  [ "${NAME#${NAME%.ZIP}}" = '.ZIP' ]; then
    NEWNAME="${NAME%.ZIP}.tar.lz"
  elif  [ "${NAME#${NAME%.zst}}" = '.zst' ]; then
    NEWNAME="${NAME%.zst}.lz"
  else
    NEWNAME="${NAME}.lz"
  fi
  if [ "$OVERWRITE" = 'no' -a -e "$NEWNAME" ]; then
    echo 'skipped because target already exists'
    shift; continue
  fi

  # recompress based on filetype
  FILETYPE=`file -b "$NAME" | cut -b1-4 | tr '[:upper:]' '[:lower:]'`
  # file versions before 5.03 cannot detect lzma
  if [ "$FILETYPE" = 'data' -a "${NAME#${NAME%.lzma}}" = '.lzma' ]; then
    FILETYPE='lzma'
  fi
  TMPFILE=`tempname -d "$FILEPATH" .to-lzip_$$` || exit 99
  if [ "X$FILETYPE" = 'Xlzip' ]; then
    echo 'already converted to lz'
    rm -f "$TMPFILE"
    shift; continue
  elif [ "X$FILETYPE" = 'Xbzip' ]; then
    bzip2 -c -d -q "$NAME" | ${ENGINE} -q $LZIP_OPTS > "$TMPFILE"
    OUT="$?"
    if [ "$OUT" -ne 0 ]; then
      echo 'failed on bzip2-to-lzip conversion'
      rm -f "$TMPFILE"
      shift; continue
    fi
  elif [ "X$FILETYPE" = 'Xgzip' ]; then
    gzip -c -d -q "$NAME" | ${ENGINE} -q $LZIP_OPTS > "$TMPFILE"
    OUT="$?"
    if [ "$OUT" -ne 0 ]; then
      echo 'failed on gzip-to-lzip conversion'
      rm -f "$TMPFILE"
      shift; continue
    fi
  elif [ "X$FILETYPE" = 'Xlzma' ]; then
    if [ "X$LZMAUTIL" = 'Xlzma_alone' ]; then
      lzma_alone d "$NAME" -so | ${ENGINE} -q $LZIP_OPTS > "$TMPFILE"
    else
      lzma -c -d -q "$NAME" | ${ENGINE} -q $LZIP_OPTS > "$TMPFILE"
    fi
    OUT="$?"
    if [ "$OUT" -ne 0 ]; then
      echo 'failed on lzma-to-lzip conversion'
      rm -f "$TMPFILE"
    else
      CONVERTED='yes'
    fi
  elif [ "X$FILETYPE" = 'Xlzop' ]; then
    lzop -c -d -q "$NAME" | ${ENGINE} -q $LZIP_OPTS > "$TMPFILE"
    OUT="$?"
    if [ "$OUT" -ne 0 ]; then
      echo 'failed on lzop-to-lzip conversion'
      rm -f "$TMPFILE"
      shift; continue
    fi
  elif [ "X$FILETYPE" = 'Xcomp' ]; then
    gzip -c -d -q "$NAME" | ${ENGINE} -q $LZIP_OPTS > "$TMPFILE"
    OUT="$?"
    if [ "$OUT" -ne 0 ]; then
      echo 'failed on compress-to-lzip conversion'
      rm -f "$TMPFILE"
      shift; continue
    fi
  elif [ "X$FILETYPE" = 'Xrar ' ]; then
    rar2tarcat "$NAME" | ${ENGINE} -q $LZIP_OPTS > "$TMPFILE"
    OUT="$?"
    if [ "$OUT" -ne 0 ]; then
      echo 'failed on rar-to-lzip conversion'
      rm -f "$TMPFILE"
      shift; continue
    fi
  elif [ "X$FILETYPE" = 'Xzip ' ]; then
    zip2tarcat "$NAME" | ${ENGINE} -q $LZIP_OPTS > "$TMPFILE"
    OUT="$?"
    if [ "$OUT" -ne 0 ]; then
      echo 'failed on zip-to-lzip conversion'
      rm -f "$TMPFILE"
      shift; continue
    fi
  elif [ "X$FILETYPE" = 'Xzsta' ]; then
    zstd -c -d -q "$NAME" | ${ENGINE} -q $LZIP_OPTS > "$TMPFILE"
    OUT="$?"
    if [ "$OUT" -ne 0 ]; then
      echo 'failed on zst-to-lzip conversion'
      rm -f "$TMPFILE"
      shift; continue
    fi
  else
    echo 'skipped on unrecognized filetype'
    rm -f "$TMPFILE"
    shift; continue
  fi

  # perform the final move, deleting old or new if necessary
  chmod --reference="$NAME" "$TMPFILE"
  mv "$TMPFILE" "$NEWNAME"
  if [ "$RETAINTIME" = 'yes' ]; then
    touch --reference="$NAME" "$NEWNAME"
  fi
  S0=`filesize "$NAME"`
  S1=`filesize "$NEWNAME"`
  if [ "$S1" -lt "$S0" ]; then
    if [ "$DELORIG" != 'no' ]; then
      rm -f "$NAME"
      echo "${S0} vs. ${S1} (deleting original)"
    else
      echo "${S0} vs. ${S1}"
    fi
  else
    if [ "$DELORIG" = 'no' ]; then
      echo "${S0} vs. ${S1} (warning: new file is not smaller)"
    elif [ "$DELORIG" = 'yes' ]; then
      rm -f "$NAME"
      echo "${S0} vs. ${S1} (deleting original: new file is not smaller)"
    else
      rm -f "$NEWNAME"
      echo 'new file deleted (not smaller than old)'
    fi
  fi

  shift
done
