Wicked Cool Shell Scripts
Wicked Cool Shell Scripts :: shell script 012-library.sh

Shell Script 012-library.sh

#!/bin/sh

# inpath - verify that a specified program is either valid as-is,
#   or can be found in the PATH directory list.

in_path()
{
  # given a command and the PATH, try to find the command. Returns
  # 0 if found and executable, 1 if not. Note that this temporarily modifies 
  # the the IFS (input field seperator), but restores it upon completion.
  # return variable 'directory' contains the directory where the 
  # command was found.

  cmd=$1        path=$2         retval=1
  oldIFS=$IFS   IFS=":"

  for directory in $path
  do
    if [ -x $directory/$cmd ] ; then
      retval=0      # if we're here, we found $cmd in $directory
    fi
  done
  IFS=$oldIFS
  return $retval
}

checkForCmdInPath()
{
  var=$1
  
  # The variable slicing notation in the following conditional 
  # needs some explanation: ${var#expr} returns everything after
  # the match for 'expr' in the variable value (if any), and
  # ${var%expr} returns everything that doesn't match (in this
  # case just the very first character. You can also do this in
  # Bash with ${var:0:1} and you could use cut too: cut -c1

  if [ "$var" != "" ] ; then
    if [ "${var%${var#?}}" = "/" ] ; then
      if [ ! -x $var ] ; then
        return 1
      fi
    elif ! in_path $var $PATH ; then
      return 2
    fi 
  fi
  return 0
}

# cnvalidate - Ensures that input only consists of alphabetical
#              and numeric characters.

cnvalidate()
{
  # validate arg: returns 0 if all upper+lower+digits, 1 otherwise

  # Remove all unacceptable chars
  compressed="$(echo $1 | sed -e 's/[^[:alnum:]]//g')"

  if [ "$compressed" != "$input" ] ; then
    return 1
  else
    return 0
  fi
}

monthnoToName()
{
  # sets the variable 'month' to the appropriate value
  case $1 in
    1 ) month="Jan"    ;;  2 ) month="Feb"    ;;
    3 ) month="Mar"    ;;  4 ) month="Apr"    ;;
    5 ) month="May"    ;;  6 ) month="Jun"    ;;
    7 ) month="Jul"    ;;  8 ) month="Aug"    ;;
    9 ) month="Sep"    ;;  10) month="Oct"    ;;
    11) month="Nov"    ;;  12) month="Dec"    ;;
    * ) echo "$0: Unknown numeric month value $1" >&2; exit 1
   esac
   return 0
}

# nicenumber - given a number, show it with comma separated values
#    expects DD and TD to be instantiated. instantiates nicenum
#    if arg2 is specified, this function echoes output, rather than
#    sending it back as a variable

nicenumber()
{
  # Note that we use the '.' as the decimal separator for parsing
  # the INPUT value to this script. The output value is as specified
  # by the user with the -d flag, if different from a '.'

  integer=$(echo $1 | cut -d. -f1)		# left of the decimal
  decimal=$(echo $1 | cut -d. -f2)		# right of the decimal

  if [ $decimal != $1 ]; then
    # there's a fractional part, let's include it.
    result="${DD:="."}$decimal"
  fi

  thousands=$integer

  while [ $thousands -gt 999 ]; do
    remainder=$(($thousands % 1000))	# three least significant digits
  
    while [ ${#remainder} -lt 3 ] ; do	# force leading zeroes as needed
      remainder="0$remainder"
    done
  
    thousands=$(($thousands / 1000))	# to left of remainder, if any
    result="${TD:=","}${remainder}${result}"	# builds right-to-left
  done

  nicenum="${thousands}${result}"

  if [ ! -z $2 ] ; then
     echo $nicenum
  fi
}

# validint - validate integer input, allow negative ints too

validint()
{
  # validate first field. Optionally test against min value $2 and/or 
  # max value $3: if you'd rather skip these tests, send "" as values.
  # returns 1 for error, 0 for success

  number="$1";	    min="$2";	   max="$3"

  if [ -z "$number" ] ; then
    echo "You didn't enter anything. Unacceptable." >&2 ; return 1
  fi
  
  if [ "${number%${number#?}}" = "-" ] ; then	# first char '-' ?
    testvalue="${number#?}" 	# all but first character
  else
    testvalue="$number"
  fi
  
  nodigits="$(echo $testvalue | sed 's/[[:digit:]]//g')"
  
  if [ ! -z "$nodigits" ] ; then
    echo "Invalid number format! Only digits, no commas, spaces, etc." >&2
    return 1
  fi
  
  if [ ! -z "$min" ] ; then
    if [ "$number" -lt "$min" ] ; then
       echo "Your value is too small: smallest acceptable value is $min" >&2
       return 1
    fi
  fi
  if [ ! -z "$max" ] ; then
     if [ "$number" -gt "$max" ] ; then
       echo "Your value is too big: largest acceptable value is $max" >&2
       return 1
     fi
  fi
  return 0
}

# validfloat - test whether a number is a valid floating point value.
#    Note that this cannot accept scientific (1.304e5) notation.

# To test whether an entered value is a valid floating point number, we
# need to split the value at the decimal point, then test the first part
# to see if it's a valid integer, then the second part to see if it's a 
# valid >=0 integer, so -30.5 is valid, but -30.-8 isn't.  Returns 0 on
# success, 1 on failure.

validfloat()
{
  fvalue="$1"

  if [ ! -z "$(echo $fvalue | sed 's/[^.]//g')" ] ; then

    decimalPart="$(echo $fvalue | cut -d. -f1)"
    fractionalPart="$(echo $fvalue | cut -d. -f2)"

    if [ ! -z "$decimalPart" ] ; then
      if ! validint "$decimalPart" "" "" ; then
        return 1
      fi 
    fi

    if [ "${fractionalPart%${fractionalPart#?}}" = "-" ] ; then
      echo "Invalid floating point number: '-' not allowed \
        after decimal point" >&2 
      return 1
    fi 
    if [ "$fractionalPart" != "" ] ; then 
      if ! validint "$fractionalPart" "0" "" ; then
        return 1
      fi
    fi

    if [ "$decimalPart" = "-" -o -z "$decimalPart" ] ; then
      if [ -z "$fractionalPart" ] ; then
        echo "Invalid floating point format." >&2 ; return 1
      fi 
    fi

  else
    if [ "$fvalue" = "-" ] ; then
      echo "Invalid floating point format." >&2 ; return 1
    fi

    if ! validint "$fvalue" "" "" ; then
      return 1
    fi
  fi

  return 0
}

exceedsDaysInMonth()
{
  # given a month name, return 0 if the specified day value is
  # less than or equal to the max days in the month, 1 otherwise

  case $(echo $1|tr '[:upper:]' '[:lower:]') in
    jan* ) days=31    ;;  feb* ) days=28    ;;
    mar* ) days=31    ;;  apr* ) days=30    ;;
    may* ) days=31    ;;  jun* ) days=30    ;;
    jul* ) days=31    ;;  aug* ) days=31    ;;
    sep* ) days=30    ;;  oct* ) days=31    ;;
    nov* ) days=30    ;;  dec* ) days=31    ;;
    * ) echo "$0: Unknown month name $1" >&2; exit 1
   esac
   
   if [ $2 -lt 1 -o $2 -gt $days ] ; then
     return 1
   else
     return 0	# all is well
   fi 
}

isLeapYear()
{    
  # this function returns 0 if a leap year, 1 otherwise
  # The formula for checking whether a year is a leap year is: 
  # 1. years divisible by four are leap years, unless..
  # 2. years also divisible by 100 are not leap years, except...
  # 3. years divisible by 400 are leap years

  year=$1
  if [ "$((year % 4))" -ne 0 ] ; then
    return 1 # nope, not a leap year
  elif [ "$((year % 400))" -eq 0 ] ; then
    return 0 # yes, it's a leap year
  elif [ "$((year % 100))" -eq 0 ] ; then
    return 1
  else
    return 0
  fi 
}

validdate()
{
  # expects three values, month, day and year. Returns 0 if success.

  newdate="$(normdate "$@")"

  if [ $? -eq 1 ] ; then
    exit 1	# error condition already reported by normdate
  fi

  month="$(echo $newdate | cut -d\  -f1)"
    day="$(echo $newdate | cut -d\  -f2)"
   year="$(echo $newdate | cut -d\  -f3)"

  # Now that we have a normalized date, let's check to see if the
  # day value is logical 

  if ! exceedsDaysInMonth $month "$2" ; then
    if [ "$month" = "Feb" -a $2 -eq 29 ] ; then
      if ! isLeapYear $3 ; then
        echo "$0: $3 is not a leap year, so Feb doesn't have 29 days" >&2
        exit 1
      fi
    else 
      echo "$0: bad day value: $month doesn't have $2 days" >&2
      exit 1
    fi
  fi
  return 0
}

echon()
{
  echo "$*" | tr -d '\n'
}

initializeANSI()
{
  esc=""

  blackf="${esc}[30m";   redf="${esc}[31m";    greenf="${esc}[32m"
  yellowf="${esc}[33m"   bluef="${esc}[34m";   purplef="${esc}[35m"
  cyanf="${esc}[36m";    whitef="${esc}[37m"
  
  blackb="${esc}[40m";   redb="${esc}[41m";    greenb="${esc}[42m"
  yellowb="${esc}[43m"   blueb="${esc}[44m";   purpleb="${esc}[45m"
  cyanb="${esc}[46m";    whiteb="${esc}[47m"

  boldon="${esc}[1m";    boldoff="${esc}[22m"
  italicson="${esc}[3m"; italicsoff="${esc}[23m"
  ulon="${esc}[4m";      uloff="${esc}[24m"
  invon="${esc}[7m";     invoff="${esc}[27m"

  reset="${esc}[0m"
}

Explore The Book!
[book cover]
Table of Contents
Read Some Scripts!
Shell Script Library
Book Errata
All The Links
Read the Reviews
Talk About It
Author Bio
Buy The Book!



Other books by author Dave Taylor
Learning Unix for Mac OS X (O'Reilly & Associates)
Solaris 9 for Dummies (Wiley)
Teach Yourself Unix in 24 Hours (Sams/Macmillan)
Teach Yourself Unix System Administration in 24 Hours (Sams/Macmillan)
Creating Cool HTML 4 Web Pages (Wiley)
Ten Quick Steps to Learning Mac OS X Unix (ebook!)