#!/bin/bash
#
# mrename2 0.4.2 - GPL
# (c) Guimi
# http://guimi.net
#
# Masive rename of files
#
# Last mod: Guimi 2006-02
#

####################################
#### ERRORES
E_NOPARAM=64        # Bad number of params
E_PARAMINVAL=65     # Invalid param
E_SALINVAL=66       # The result of params is not valid (see 'Invalid exit error' below)


####################################
#### HELP

# Functions to show help
show_help ()
{
  echo "Use `basename $0` --help for help" >&2
}

show_long_help ()
{
  more << EOF

mrename2
(c) 2005 Guimi (http://guimi.net)
This software is under the GNU Public License (GPL) v.2

Use:
----
mrename2 'filter' [options]
  Rename the files filtered with the invoked options.
  See examples below

mrename 'filter' prefix -c | -m
  mrename is DEPRECATED, please use mrename2
  The use of mrename implies -cn 1 -d 1 -v -i


Options:
---------
  -c
  --copy
     Copy files, instead of moving
  -cn [n]
  --counter [n]
     Uses a counter initialized on 'n' or '0'
  -cs
  --counter_suffix
     Puts counter as suffix
     Implies --counter
  -d n
  --digits n
     Set the minimum number of digits of the counter
     By default it's 3
     Implies --counter
  -e
  --extensions
     Don't modify extensions -string after the last point
  -f
  --force
     Don't ask confirmation if destination files exists
  -h
  --help
     Show this help
  -i
  --interactive
     Ask confirmation if destination files exists
  -m
  --move
     Move (rename) the files, instead of copy
     This is the default option
  -n
  --no_original
     Don't use original name
     Destination name will be composed only with the strings specified on params (counter, prefix, suffix)
  [-p] string
  [--prefix] string
     Puts 'string' as prefix
     For old mrename backward compatibility -p is not mandatory, you can use only 'preffix' but it's not recommended
  -s string
  --suffix string
     Puts 'string' as suffix
  -sed 'expresion1' 'expresion2'
     Substitute 'expresion1' with 'expresion2' using sed
     (man sed)
  -t
  --test
     The execution doesn't do real actions
     Implies -v
  -v
  --verbose
     Be verbose


NOTE on 'Invalid exit error':
-----------------------------
  Some combinations of params will result on an empty string as the destination name
  This will cause all the files will be lost except the last one, that will be named ''
  mrename2 prevents this situation by the 'Invalid exit error'
  Example:
    mrename2 '*' -n -f

Examples:
---------

mrename2 '*' -cn  -t
  Shows (-t) the new names of all the files setting a counter as preffix

mrename2 '*' -p changed_
mrename2 '*' changed_
  (Equals, first recommended) Moves all the files putting 'changed_' as preffix

mrename2 '*.jpg' -s _changed -e
  Moves all the *.jpg files putting '_changed' as suffix but respecting extensions
  This way myself.jpg would be renamed as myself_changed.jpg

mrename2 '*.mp3' -p Serrat_ -e -d 4 -cn 108 -cs -n -i
  Moves all the *.mp3 files calling them 'Serrat_xxxx.mp3'
  xxxx is a four digits (-d 4) counter starting on 108 (-cn 108)
  It asks confirmation (-i) if destination file exists

mrename2 '*' -sed ' ' ''
mrename2 '*' -sed ' '
  (Equals, first recommended) Delete all spaces of all the files

mrename2 '*.mpg' -sed 'pg$' 'peg'
  Moves all the *.mpg files replacing only finals 'pg' with 'peg'
  (man sed)

Guimi 2006/02
http://guimi.net

EOF
}

show_mrename_help ()
{
  echo "" >&2
  echo "mrename is DEPRECATED, please use mrename2 --help" >&2
  echo "  to know the new features" >&2
  echo "" >&2
}

####################################
####################################
#### CHECKING PARAMS

# Default params
CMD_OPTIONS=""
CMD="mv"
DIGITS=3

# More old mrename default params
if [ `basename $0` = "mrename" ]
then
  counter="1"
  DIGITS="1"
  CMD_OPTIONS="-i"
  VERBOSE="1"
  show_mrename_help
fi

# Checking params
while [ $# -gt 0 ]; do    # While we have params
  case "$1" in
    -c|--copy) # Copy files instead of moving
      CMD="cp"
      ;;
    -cn|--counter) # Set counter
      # Checking if there is another param after this
      if [ -n "$2" ]
      then
        case $2 in
          -*)  # If next param is an option, set counter at '0'
            counter='0'
            ;;
          *[!0-9]*) # If some character is not a digit, error
            echo "Error: Invalid counter $2" >&2
            show_help
            exit $E_PARAMINVAL
            ;;
          *)  # Set counter
            counter="$2"
            shift
            ;;
        esac
      else # If there isn't another param, set '0'
        counter='0'
      fi
      ;;
    -cs|--counter_suffix) # Set counter as suffix
      SUFFIX_COUNTER="1"
      # If counter is not defined, set '0'
      if [ -z "$counter" ]; then
        counter="0"
      fi
      ;;
    -d|--digits) # Set number of digits of the counter
      # Checking if there is another param after this
      if [ -z "$2" ]; then
        echo "Error: Invalid number of digits $2" >&2
        show_help
        exit $E_NOPARAM
      else
        case $2 in
          -*)  # If next param is an option, error
            echo "Error: Missing number of digits" >&2
            show_help
            exit $E_NOPARAM
            ;;
          *[!0-9]*) # If some character is not a digit, error
            echo "Error: Invalid number of digits $2" >&2
            show_help
            exit $E_PARAMINVAL
            ;;
          *)  # Set number of digits of the counter
            DIGITS="$2"
            # If counter is not defined, set '0'
            if [ -z "$counter" ]; then
              counter="0"
            fi
            ;;
        esac
        shift
      fi
      ;;
    -e|--extensions) # Don't modify extensions
      EXTENSIONS="1"
      ;;
    -f|--force) # Param for command
      CMD_OPTIONS="-f"
      ;;
    -h|--help) # Show long help
      show_long_help
      exit 0
      ;;
    -i|--interactive) # Param for command
      CMD_OPTIONS="-i"
      ;;
    -m|--move) # Move (rename) files
      CMD="mv"
      ;;
    -n|--no_original) # Don't use original name
      NO_ORIGINAL="1"
      ;;
    -p|--prefix) # Set prefix
      # Checking if there is another param after this
      if [ -z "$2" ]
      then
        echo "Error: Missing prefix" >&2
        show_help
        exit $E_NOPARAM
      else
        string_prefix="$2"
        shift
      fi
      ;;
    -s|--suffix) # Set suffix
      # Checking if there is another param after this
      if [ -z "$2" ]
      then
        echo "Error: Missing suffix" >&2
        show_help
        exit $E_NOPARAM
      else
        string_suffix="$2"
        shift
      fi
      ;;
    -sed) # Substitute string1 with string2
      # Checking if there is another param after this
      #  string_tr_2 can be null if -sed param is the last one
      #  (changing string_tr_1 by nothing, thus is deleting it)
      if [ -z "$2" ]; then
        echo "Error: Missing string(s) of -sed param" >&2
        show_help
        exit $E_NOPARAM
      else
        string_tr_1=$2
        string_tr_2=$3
        shift
        shift
      fi
      ;;
    -t|--test) # Dont' do any action
      TEST="1"
      VERBOSE="1"
      ;;
    -v|--verbose) # Be verbose
      VERBOSE="1"
      ;;
    -*) # Invalid param
      echo "Error: Invalid param $1" >&2
      show_help
      exit $E_PARAMINVAL
      ;;
    *) # Setting filter
      if [ -z "$filter" ]; then
        filter=$1
      elif [ -z "$string_prefix" ]; then # Setting prefix (old mrename way)
        string_prefix="$1"
      else
	# If we have filter and prefix, error
        echo "Error: Invalid number of params" >&2
        show_help
        exit $E_NOPARAM
      fi
      ;;
  esac
  shift # Go to next param
done


# If we are old mrename, we need a prefix
if [ `basename $0` = "mrename" ] && [ -z "$string_prefix" ]
then
  echo "Error: Invalid params" >&2
  show_help
  exit 0
fi

# If we don't have filter string, error
if [ -z "$filter" ]
then
  echo "Error: Missing filter" >&2
  show_help
  exit $E_NOPARAM
fi


####################################
####################################
#### DOING IT

# Cheking if there files to move or copy
ls $filter >/dev/null 2> /dev/null
if [ "$?" -gt 0 ]
then
  if [ "$VERBOSE" = "1" ]; then
    echo "There aren't files to rename or copy"
  fi
  exit 0
fi

# Set specified counter format (digits)
FORMAT='%0'$DIGITS'd\n'


#### RENAME LOOP
for this_file in $filter
do

#
# These alternatives doesn't work well with names with spaces
#
#> files_list=`ls $filter`
#> for this_file in $files_list
#
#> for this_file in `ls $filter`
#

  # Setting counter with specified digits
  if [ -n "$counter" ]
  then
    new_counter=`printf ${FORMAT} $counter`
    counter=$(($counter+1))
  else
    new_counter=""
  fi

  # Cheking if we must respect extensions
  if [ "$EXTENSIONS" = "1" ]
  then
    # Take point character position
    point_position=`expr index "$this_file" \.`
    # If there is a point
    if [ $point_position -gt 0 ]
    then
      # Looping to take LAST point character position
      extension_string=$this_file
      while [ $point_position -gt 0 ]
      do
        extension_string=${extension_string:point_position}
        point_position=`expr index "$extension_string" \.`
      done

      # Take the name
      file_lenght=${#this_file}
      extension_length=${#extension_string}
      name_length=$(($file_lenght-$extension_length-1))
      file_name=${this_file:0:$name_length}
    else # If there is NOT a point
      # Take name AND unset extension
      file_name=$this_file
      unset extension_string
    fi
  else # We don't care about extensions
    file_name=$this_file
    unset extension_string
  fi

  # Don't use original name?
  if [ -n "$NO_ORIGINAL" ]
  then
    file_name=""
  elif [ -n "$string_tr_1" ]
  # We do sed substitution only if we use original name (otherway is dumb)
  then
    file_name=$(echo $file_name | sed s/"${string_tr_1}"/"${string_tr_2}"/g)
  fi

  # Creating new name looking at counter position
  if [ -n "$SUFFIX_COUNTER" ]; then
    final_string=$string_prefix$file_name$new_counter$string_suffix
  else
    final_string=$string_prefix$new_counter$file_name$string_suffix
  fi

  # Append extension to the name
  if [ -n "$extension_string" ]; then
    final_string=$final_string'.'$extension_string
  fi

  # Some combinations of params will result on an empty string as the destination name
  # This will cause all the files will be lost except the last one, that will be named ''
  # Prevent this situation by the 'Invalid exit error'
  if [ "$final_string" = "" ]
  then
    echo "Error: Invalid exit error"
    show_help
    exit $E_SALINVAL
  fi

  # Finally we do it -if concern-
  if [ "$this_file" != "$final_string" ]; then
    # Be verbose
    if [ "$VERBOSE" = "1" ]; then
      echo "$CMD $CMD_OPTIONS '$this_file' '$final_string'"
    fi

    if [ -z "$TEST" ]; then
      $CMD $CMD_OPTIONS "$this_file" "$final_string"
      SALIDA=$?

      if [ "$SALIDA" -gt 0 ]
      then
        echo "$? - Error on $CMD $CMD_OPTIONS '$this_file' '$final_string'" >&2
      fi
    fi
  fi

done


####################################
# Finish
exit 0

