#!/bin/bash
# numbfile - renames the specified files in a sequential pattern.
# Chris X Edwards - 99.09.29 - Version 1
# -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= 
# Chris X Edwards - 00.07.17 - Version 2
# Fixed problems with bash 2+ incompatibilities. The main problem
# seemed to be the new way that { } pairs are closed. Now, one needs
# a ; like this { ;}  for organizing braces (not braces that are
# part of other constructions.
# -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= 
# Wondering what this program does and how it works?  
# use "numbfile -h" or read on...
# -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= 

# Wow! I used 5 of bash's interpretations of the `#' symbol. How apt!
# Search for `HERE' to find out more. For example:
# HERE # is to denote a comment (1)

function explainyourself
{
echo -n 'numbfile [-rcvh] [-n NUM] '
echo '[[-p PRE] [-z DIG] [-s SUF] | [-e EXPR ]] files...'
echo 'This program renames a group of randomly named files so that they'
echo 'have a consistent naming scheme.'
echo '   -n	= the number to start the count at (default=1)'
echo '   -p	= the prefix to prepend to the number (default=<none>)'
echo '   -z	= the minimum number of digits (default=<no padding>)'
echo '   -s	= the suffix to append to the number (default=<none>)'
echo '   -e     = example expression method of defining output filenames'
echo '            EXPR must have the format: [prefix]^[^...][suffix]'
echo '            where "^" represents one required digit'
echo '   -r	= reverse, count backwards from number'
echo '   -c   = check, don'\'t' actually rename anything, just preview'
echo '   -v	= verbose, show file renamings (if any occur)'
echo '   -h	= help, print this message'
echo 'Examples:'
echo 'To reorganize a bunch of badly named .gif files into a neater group:'
echo '   $ numbfile -p pound -s .gif *.gif'
echo '   Creates: pound1.gif, pound2.gif, pound3.gif...'
echo 'Often when dealing with a large collection of arbitrarily named files,'
echo 'naming them in reverse order is helpful so that the last used number'
echo 'is readily apparent in a file operation dialog box.'
echo '   $ numbfile -r -phash -s.dwg -n100 -z3 *'
echo '   Creates: hash098.dwg, hash099.dwg, hash100.dwg...'
echo 'To specify the output filename using an example expression:'
echo '   $ numbfile -re tictac^^^^^.toe -n2000 *'
echo '   Creates: tictac01998.toe, tictac01999.toe, tictac02000.toe...'
echo 'Or if you don'\''t want leading zeros or a suffix:'
echo '   $ numbfile -e octothorpe^ -n9 *'
echo '   Creates: octothorpe10, octothorpe11, octothorpe9...'
	exit
} # version2 - ; not needed here because of newline

# First thing, if there are no options at all, bail...
if [ $# = "0" ]; then	# HERE the # is for the argument count (2)
  	echo 'Unfortunate syntax: use "numbfile -h", for help.'
	exit
fi

# Set up some default variables...
num=1			# this is the default number
v="false"		# this is the default verbose condition
pre=""			# this is the default prefix (ie none)
suf=""			# this is the default suffix (also none)
z=0			# number of digits (0 is flag for no padding)

# Set up some default functions...
function action		# this is what happens when all conditions are good
 { mv $1 $filepath$pre$num$suf ;} #version2 - added ;
function nextnum	# this is how we index the counter
 { echo $((10#$1+1)) ;}	# HERE the # controls base so 000? is base 10 (3)
			# (actually necessary here with leading 000s!)
			# version2 - added;

# Handle the options using the getopts facility...
while getopts ":e:n:p:s:z:crvh" opt; do	# 1st : means don't bail on wrong arg
case $opt in				# other :'s mean arg is expected too
  e) pre=${OPTARG%%^*}	# get beginning off of example exp, get end next 
     suf=${OPTARG##*^}	# HERE the # is an "end of pattern" match funct (4)
     z=$(( ${#OPTARG} - ${#pre} - ${#suf} ))  # z=lengths of (exp -p -s )
     if [ $z -lt 1 ]; then		# sanity check - was exp ok?
       echo "unfortunate syntax:" 
       echo 'the format for -e arguments is "abc^^^def" where "^" are digits'
       exit
     fi ;; 
  n) num=$OPTARG ;;
  p) pre=$OPTARG ;;
  s) suf=$OPTARG ;;
  z) z=$OPTARG ;;			# set padding value
  c) function action			# redefine to use alternate action
      { echo $1" >>>====> "$filepath$pre$num$suf ;};; # version2 - ;
  r) function nextnum			# redefine to count backwards
      { echo $((10#$1-1)) ;};;		# version2 - added ;
  v) v="true";;
  h) explainyourself;; 
  *) echo 'Unfortunate syntax: use "numbfile -h", for help.'
     exit;;		# catchall bail out if a bad option is called
esac
done

shift $(($OPTIND - 1))	# reset arg numbers to reflect real args now

while [ -n "$1" ]	# keep doing this till we run out of args (files)
do

# pad the variable n with zeros here if needed 
# this format 00123 gets reset to 123 when interpreted for the indexing
if [ $z -gt 0 ]; then
while [ ${#num} -lt $z ]	# HERE the # is for string length (5)
 do
  num=0$num
 done
fi

# This strips out the path name. We're going to ditch the filename, but 
# we want the new files to wind up in their own directory.
# Thanks to Dr. Seth for pointing this out.
if [ ${1%/*} = $1 ]; then
 filepath='./'
else
 filepath=${1%/*}/
fi

action $1		# do the action, move or preview the move

# If verbose is turned on, print out some extra stuff.
if [ $v = "true" ]; then
echo "mv "$1" "$filepath$pre$num$suf
fi

num=$(nextnum $num)		# index the counter by calling our function

shift 			# ok, onto the next argument (file)
done
