#!/usr/bin/perl -w
#
#    rip -- A command-line based CD ripper (supports MP3, Ogg Vorbis, and WAV)
#
#    rips audio CD tracks to either Motion Picture Experts Group Layer 3 (MP3)
#    files, to Ogg Vorbis files, or to WAV files with no user intervention
#    between steps of ripping and encoding.
#
#    Copyright (C) 2001 Gregory J. Smethells
#
#
#    License:
#
#    This program 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 2 of the License, or
#    (at your option) any later version.
#
#    This program 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 this program; if not, write to the Free Software Foundation,
#    Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
#
#    Disclaimer:
#
#    YOU SHOULD ONLY CREATE MP3/OGG FILES FROM AUDIO CDs THAT YOU OWN
#    LEGALLY. ANY OTHER USE OF THIS SCRIPT IS NOT LEGAL, IS NOT CONDONED
#    BY THIS AUTHOR, NOR IS SUCH ACTION IN THE SPIRIT OF THIS SCRIPT. THE
#    AUTHOR HEREBY REMOVES HIMSELF FROM TAKING ANY RESPONSIBILITY FOR
#    MISUSE OF THIS SCRIPT OR ANY PIECE OF SOFTWARE USED BY THIS SCRIPT.
#
#
#    Author's Notes:
#
#    This script requires a CD ripper such as cdparanoia and either an MP3
#    application that can encode WAVs to MP3s such as gogo or an Ogg Vorbis
#    application that can encode WAVs to Ogg Vorbis files. These tools need
#    to be on your $PATH, since this script is essentially only a wrapper.
#    All major functionality is found in those programs. However, I believe
#    that the abstract functionality provided by this script is quite useful
#    and makes these tools all the more powerful.
#
#    In the future, I hope to get more CD rippers and, mostly, more encoders
#    to work with this script. The essential changes would be to add more
#    $PATH searches for different tools of these types and then set the flags
#    available for that tool. Also flags to rip for any new tool would need
#    to be added and handled properly.
#
#    Booleans in this script use "" for FALSE and "true" for TRUE
#



##########################################################################
#                                                                        #
# Defined early so it is always parsed first by perl interpreter before  #
# any other code can have a chance to run.                               #
#                                                                        #
# SUB: signalHandler                                                     #
#                                                                        #
#   Handlers signals returned from the main system() call that rips the  #
#   requested CD tracks straight to MP3 or Ogg Vorbis.                   #
#                                                                        #
##########################################################################

sub signalHandler {
   if( $inSubEncode ) {
     print OUT "\n\n\nError occurred or kill signal received.\nAborting.\n\n";
   }
   else {
     print( "\n\n\nKill signal received.\nAborting.\n\n" );
   }

   # Close $tempSTDERR output file
   close( STDERR );

   # Do not leave the temp MP3/Ogg output file laying around
   system( "rm -f $out" );

   exit(1);
}



##########################################################################
#                                                                        #
# DECLARATIONS                                                           #
#                                                                        #
##########################################################################

use Cwd;
use Time::localtime;
use sigtrap 'handler', \&signalHandler, 'normal-signals';

$time        = localtime(time());
$now         = $time->mon . $time->mday . $time->hour . $time->min . $time->sec;

system( "stty erase ^H" );                # Make sure backspace is set to '^H'

$debug       = "true";                    # Default is to have debugging output on
$keepDebug   = "";                        # Default is to remove $tempSTDERR

$tempSTDERR  = "/tmp/rip-$now-stderr";    # DEBUG file (stderr redirected here)
$tempCDDB    = "/tmp/rip-$now-cddb";      # Temp file for CDDB output

$date        = "2001-03-14";              # Date of last modification
$version     = "0.90";                    # Version number for this script

$DEFAULT_DIR = getcwd;                    # Default "default directory"
$pwd         = $DEFAULT_DIR;              # For convience's sake
$outputDir   = $DEFAULT_DIR;              # Default output directory

$out         = "rip_temp_file";           # Temp file's filename
$extension   = "mp3";                     # Filename extension: "mp3", "ogg", or "wav"

$wavONLY     = "";                        # Default is to encode CD tracks to MP3

@PATH        = split( ":", $ENV{PATH});   # Valid @PATH for current user
$HOME        = $ENV{HOME};                # Valid $HOME for current user


# Redirect stderr output to a temp file $tempSTDERR
close( STDERR );
open(STDERR, ">$tempSTDERR") or print( "rip:  cannot open $tempSTDERR: $!\n" ) and exit(2);


# The LOWER the integer value the more preferable that tool is,
# i.e., more likely it will be set to $ripper or $encoder.

# Rippers
$cdparanoia                    = 0;

$supportedRippers[$cdparanoia] = "cdparanoia";

$existantRipper[$cdparanoia]   = "";

# Encoders
$gogo                          = 0;
$lame                          = 1;
$bladeenc                      = 2;
$BladeEnc                      = 3;
$oggenc                        = 4;

$supportedEncoders[$gogo]      = "gogo";
$supportedEncoders[$lame]      = "lame";
$supportedEncoders[$bladeenc]  = "bladeenc";
$supportedEncoders[$BladeEnc]  = "BladeEnc";
$supportedEncoders[$oggenc]    = "oggenc";

$existantEncoder[$gogo]        = "";
$existantEncoder[$lame]        = "";
$existantEncoder[$bladeenc]    = "";
$existantEncoder[$BladeEnc]    = "";
$existantEncoder[$oggenc]      = "";

# CDDB Tools
$cddbpl                        = 0;

$supportedCDDBs[$cddbpl]       = "cddb.pl";

$existantCDDB[$cddbpl]         = "";



##########################################################################
#                                                                        #
# TOOLS AVAILABLE                                                        #
#                                                                        #
##########################################################################

# Make sure we have our tools available on the $PATH first...

# Assume there is nothing available initially
$ripper  = "";
$encoder = "";
$cddb    = "";


# Find valid CD ripper tools
for( $i = 0 ; $i < @supportedRippers ; $i++ ) {
  for( $j = 0 ; $j < @PATH ; $j++ ) {
    if( -e ("$PATH[$j]/$supportedRippers[$i]") ) {
      $existantRipper[$i] = "$PATH[$j]/$supportedRippers[$i]";

      if( not($ripper) ) {
        $ripper = $existantRipper[$i];
        print STDERR "DEBUG: using ripper $ripper\n" if $debug;
      }
    }
  }
}


# Find valid encoder tools
for( $i = 0 ; $i < @supportedEncoders ; $i++ ) {
  for( $j = 0 ; $j < @PATH ; $j++ ) {
    if( -e ("$PATH[$j]/$supportedEncoders[$i]") ) {
      $existantEncoder[$i] = "$PATH[$j]/$supportedEncoders[$i]";

      if( not($encoder) ) {
        $encoder = $existantEncoder[$i];
        print STDERR "DEBUG: using encoder $encoder\n" if $debug;
      }
    }
  }
}


# Find valid CDDB tools
for( $i = 0 ; $i < @supportedCDDBs ; $i++ ) {
  for( $j = 0 ; $j < @PATH ; $j++ ) {
    if( -e ("$PATH[$j]/$supportedCDDBs[$i]") ) {
      $existantCDDB[$i] = "$PATH[$j]/$supportedCDDBs[$i]";

      if( not($cddb) ) {
        $cddb = $existantCDDB[$i];
        print STDERR "DEBUG: using CDDB tool $cddb\n" if $debug;
      }
    }
  }
}


# Determine which encoder user likes better based on flags set in @ARGV.
# This MUST be done NOW because the next step is to set the ripper and
# encoder tool's flag variables to their proper values.

for( $i = 0 ; $i < @ARGV ; $i++ ) {
  # Test for BladeEnc flag
  # The check for $ARGV[0] not having a "=" in it prevents accidental s/B//g
  # on flags like "-m=/home/greg/mp3/BBKing" which as a "B" in it.
  if( $ARGV[0] =~ /B/ and not($ARGV[0] =~ /=/) or $ARGV[0] =~ /--bladeenc/ ) {
    if( $existantEncoder[$bladeenc] ) {
      $encoder = $existantEncoder[$bladeenc];
      print STDERR "DEBUG: bladeenc flagged: using $encoder now\n" if $debug;
    }
    elsif( $existantEncoder[$BladeEnc] ) {
      $encoder = $existantEncoder[$BladeEnc];
      print STDERR "DEBUG: BladeEnc flagged: using $encoder now\n" if $debug;
    }
    else {
      print( "rip:  No \"bladeenc\" and no \"BladeEnc\" on your \$PATH\n" );
      print( "rip:  though a -B or a --bladeenc flag was specified. Aborting.\n" );
      &abort;
    }

    if( $ARGV[0] =~ /^--bladeenc$/ or $ARGV[0] =~ /^-B$/ ) {
      shift( @ARGV );
    }
    else {
      $ARGV[0] =~ s/B//g;
    }
  }

  # Test for LAME flag
  # The check for $ARGV[0] not having a "=" in it prevents accidental s/L//g
  # on flags like "-m=/home/greg/mp3/LynyrdSkynyrd" which as an "L" in it.
  if( $ARGV[0] =~ /L/ and not($ARGV[0] =~ /=/) or $ARGV[0] =~ /--lame/ ) {
    if( $existantEncoder[$lame] ) {
      $encoder = $existantEncoder[$lame];
      print STDERR "DEBUG: LAME flagged: using $encoder now\n" if $debug;
    }
    else {
      print( "rip:  No \"lame\" on your \$PATH though a -L or a\n" );
      print( "rip:  --lame flag was specified. Aborting.\n" );
      &abort;
    }

    if( $ARGV[0] =~ /^--lame$/ or $ARGV[0] =~ /^-L$/ ) {
      shift( @ARGV );
    }
    else {
      $ARGV[0] =~ s/L//g;
    }
  }

  # Test for GOGO flag
  # The check for $ARGV[0] not having a "=" in it prevents accidental s/G//g
  # on flags like "-m=/home/greg/mp3/GratefulDead" which as a "G" in it.
  if( $ARGV[0] =~ /G/ and not($ARGV[0] =~ /=/) or $ARGV[0] =~ /--gogo/ ) {
    if( $existantEncoder[$gogo] ) {
      $encoder = $existantEncoder[$gogo];
      print STDERR "DEBUG: GOGO flagged: using $encoder now\n" if $debug;
    }
    else {
      print( "rip:  No \"gogo\" on your \$PATH though a -G or a\n" );
      print( "rip:  --gogo flag was specified. Aborting.\n" );
      &abort;
    }

    if( $ARGV[0] =~ /^--gogo$/ or $ARGV[0] =~ /^-G$/ ) {
      shift( @ARGV );
    }
    else {
      $ARGV[0] =~ s/G//g;
    }
  }


  # Test for OggEnc flag
  # The check for $ARGV[0] not having a "=" in it prevents accidental s/O//g
  # on flags like "-m=/home/greg/mp3/OtisRedding" which as a "O" in it.
  if( $ARGV[0] =~ /O/ and not($ARGV[0] =~ /=/) or $ARGV[0] =~ /--oggenc/ ) {
    if( $existantEncoder[$oggenc] ) {
      $encoder   = $existantEncoder[$oggenc];
      $extension = "ogg";
      print STDERR "DEBUG: Ogg Vorbis flagged: using $encoder now\n" if $debug;
    }
    else {
      print( "rip:  No \"oggenc\" on your \$PATH though a -O or a\n" );
      print( "rip:  --oggenc flag was specified. Aborting.\n" );
      &abort;
    }

    if( $ARGV[0] =~ /^--oggenc$/ or $ARGV[0] =~ /^-O$/ ) {
      shift( @ARGV );
    }
    else {
      $ARGV[0] =~ s/O//g;
    }
  }


  # Cycle through @ARGV flags
  $temp = shift( @ARGV );
  @ARGV = ( @ARGV, $temp );
}



##########################################################################
#                                                                        #
# FLAGS AVAILABLE                                                        #
#                                                                        #
##########################################################################

# The flags should be set dependent on which tool was found
# on the $PATH for either the purpose of being a CD ripper
# or being a MP3 or Ogg encoder.


# Set the ripper's flags
if( $ripper =~ /cdparanoia/ ) {
  # Used by parseFlags() when appropriate
  $ripperVERBOSE    = " -v ";
  $ripperQUIET      = " --quiet ";
  $ripperSPEED      = " --force-read-speed ";
  $ripperDEVICE     = " --force-cdrom-device ";
  $ripperPARANOIA   = " --never-skip ";

  $ripperFlags      = " ";            # Default cdparanoia flags
}


# Set the encoder's flags
if( $encoder =~ /bladeenc/ or $encoder =~ /BladeEnc/ ) {
  # Used by parseFlags() when appropriate
  $encoderQUIET     = " -quiet ";
  $encoderBITRATE   = " -br ";
  $encoderPARANOIA  = " -br 160 ";

  $encoderFlags     = " ";            # Default BladeEnc flags

  # How to denote standard input
  $in                  = " STDIN ";
}
elsif( $encoder =~ /gogo/ ) {
  # Used by parseFlags() when appropriate
  $encoderQUIET     = " -silent ";
  $encoderBITRATE   = " -b ";
  $encoderPARANOIA  = " -b 160 ";

  $encoderFlags     = " ";            # Default GOGO flags

  # How to denote standard input
  $in                  = " stdin ";
}
elsif( $encoder =~ /lame/ ) {
  # Used by parseFlags() when appropriate
  $encoderQUIET     = " -S ";
  $encoderBITRATE   = " -b ";
  $encoderPARANOIA  = " -b 160 ";

  $encoderFlags     = " ";            # Default LAME flags

  # How to denote standard input
  $in                  = " - ";
}
elsif( $encoder =~ /oggenc/ ) {
  # Used by parseFlags() when appropriate
  $encoderQUIET     = " --quiet ";
  $encoderBITRATE   = " --bitrate ";
  $encoderPARANOIA  = " --bitrate 160 ";

  # How to denote standard input
  $encoderFlags     = " - ";          # Default OGGENC flags

  # Bit of a kludge because we need "-" before "-o"
  $in               = " -o ";
}



##########################################################################
#                                                                        #
# MAIN SCRIPT                                                            #
#                                                                        #
##########################################################################

# Main control structure from here on out. When this if is
# finished, then the script is done and will exit.
if( not($ripper) or not($encoder) ) {
  # Prints an error message and exits the script NOW
  &abort;
}
else {
  # All cases must remain here, even "encode" so that an @ARGV
  # of only digits to rip does not cause usage() to be called.
  $case =    ( $ARGV[0] =~ /^\-/ )  &&  "flag"     # Either there're flags
          || ( $ARGV[0] =~ /\d/  )  &&  "encode"   # or just digits
          ||                            "default"; # or no args at all

  # Prints a usage message and EXITS the script if there are NO args
  if( $case eq "default" ) {
    &usage;
  }

  # Parse any flags if they exist in the argument list (sets GLOBAL vars)
  if( $case eq "flag" ) {
    &parseFlags;
    &handleFlags;
  }

  &parseTracks;
  &setNames;
  &encode;
}

&terminate;



##########################################################################
#                                                                        #
# SUBROUTINES                                                            #
#                                                                        #
##########################################################################


##########################################################################
#                                                                        #
# SUB: parseFlags                                                        #
#                                                                        #
#   Parses arguments (flags) given to rip. Once it is determined what    #
#   flags have been given, either GLOBAL variables that the subroutine   #
#   "encode" looks at are set or functions such as a CD tray close       #
#   are run.                                                             #
#                                                                        #
##########################################################################

sub parseFlags {
  print STDERR "DEBUG: \@ARGV: @ARGV\n" if $debug;

  # Exhaustively check args in @ARGV until the array is empty.
  # We shift() out each arg after we are completely finished with it.

  while( @ARGV > 0 and $ARGV[0] =~ /^\-/ ) {

    # Parse flags of the form:  --eject

    $item =   ( $ARGV[0] =~ /^--all/       )  &&  "all"
           || ( $ARGV[0] =~ /^-b=/         )  &&  "bitrate"
           || ( $ARGV[0] =~ /^--bitrate=/  )  &&  "bitrate"
           || ( $ARGV[0] =~ /^--bladeenc/  )  &&  "bladeenc"
           || ( $ARGV[0] =~ /^--cddb/      )  &&  "cddb"
           || ( $ARGV[0] =~ /^-d=/         )  &&  "device"
           || ( $ARGV[0] =~ /^--dev=/      )  &&  "device"
           || ( $ARGV[0] =~ /^--debug/     )  &&  "debug"
           || ( $ARGV[0] =~ /^--eject/     )  &&  "eject"
           || ( $ARGV[0] =~ /^--gogo/      )  &&  "gogo"
           || ( $ARGV[0] =~ /^--help/      )  &&  "help"
           || ( $ARGV[0] =~ /^--lazy/      )  &&  "lazy"
           || ( $ARGV[0] =~ /^--lame/      )  &&  "lame"
           || ( $ARGV[0] =~ /^-m=/         )  &&  "move"
           || ( $ARGV[0] =~ /^--move=/     )  &&  "move"
           || ( $ARGV[0] =~ /^--number/    )  &&  "number"
           || ( $ARGV[0] =~ /^--oggenc/    )  &&  "oggenc"
           || ( $ARGV[0] =~ /^--paranoia/  )  &&  "paranoia"
           || ( $ARGV[0] =~ /^--play/      )  &&  "play"
           || ( $ARGV[0] =~ /^--quiet/     )  &&  "quiet"
           || ( $ARGV[0] =~ /^--rename/    )  &&  "rename"
           || ( $ARGV[0] =~ /^-s=/         )  &&  "cdspeed"
           || ( $ARGV[0] =~ /^--speed=/    )  &&  "cdspeed"
           || ( $ARGV[0] =~ /^--superlazy/ )  &&  "superlazy"
           || ( $ARGV[0] =~ /^--trayclose/ )  &&  "trayclose"
           || ( $ARGV[0] =~ /^--version/   )  &&  "version"
           || ( $ARGV[0] =~ /^--verbose/   )  &&  "verbose"
           || ( $ARGV[0] =~ /^--wav/       )  &&  "wav"
           ||                                     "unknown";

    print STDERR "DEBUG: handling \"\-\-\?\?\" flag types: flag is $item\n" if $debug;

    # Parse out value given to bitrate=, move=, speed=,
    # or dev= when argument is in one of those formats.

    $revArg = reverse( $ARGV[0] );

    if( $ARGV[0] =~ /^-b=/ ) {
      for( $i = 0 ; $i < 3 ; $i++ ) { chop( $revArg ); }
      $kbps = reverse( $revArg );
      print STDERR "DEBUG: \$kbps: $kbps\n" if $debug;
    }
    elsif( $ARGV[0] =~ /^--bitrate=/ ) {
      for( $i = 0 ; $i < 10 ; $i++ ) { chop( $revArg ); }
      $kbps = reverse( $revArg );
      print STDERR "DEBUG: \$kbps: $kbps\n" if $debug;
    }
    elsif( $ARGV[0] =~ /^-d=/ ) {
      for( $i = 0 ; $i < 3 ; $i++ ) { chop( $revArg ); }
      $dev = reverse( $revArg );
      print STDERR "DEBUG: \$dev: $dev\n" if $debug;
    }
    elsif( $ARGV[0] =~ /^--dev=/ ) {
      for( $i = 0 ; $i < 6 ; $i++ ) { chop( $revArg ); }
      $dev = reverse( $revArg );
      print STDERR "DEBUG: \$dev: $dev\n" if $debug;
    }
    elsif( $ARGV[0] =~ /^-m=/ ) {
      for( $i = 0 ; $i < 3 ; $i++ ) { chop( $revArg ); }
      $outputDir = reverse( $revArg ) . "\/";
      $outputDir  =~ s/\/\//\//g;
      $outputDir  =~ s/\/\//\//g;
      print STDERR "DEBUG: \$outputDir: $outputDir\n" if $debug;
    }
    elsif( $ARGV[0] =~ /^--move=/ ) {
      for( $i = 0 ; $i < 7 ; $i++ ) { chop( $revArg ); }
      $outputDir = reverse( $revArg ) . "\/";
      $outputDir  =~ s/\/\//\//g;
      $outputDir  =~ s/\/\//\//g;
      print STDERR "DEBUG: \$outputDir: $outputDir\n" if $debug;
    }
    elsif( $ARGV[0] =~ /^-s=/ ) {
      for( $i = 0 ; $i < 3 ; $i++ ) { chop( $revArg ); }
      $speed = reverse( $revArg );
      print STDERR "DEBUG: \$speed: $speed\n" if $debug;
    }
    elsif( $ARGV[0] =~ /^--speed=/ ) {
      for( $i = 0 ; $i < 8 ; $i++ ) { chop( $revArg ); }
      $speed = reverse( $revArg );
      print STDERR "DEBUG: \$speed: $speed\n" if $debug;
    }


    if( $item ne "unknown" ) {
      # Accumulate the $item to be dealt with later on or...

      @flagsToCheck = ( @flagsToCheck, $item );
    }
    else {
      # Parse flags of the form:  -SPn

      # Make sure we get every last char in $ARGV[0]
      while( $ARGV[0] =~ /^\-\w/ ) {
        $check = chop( $ARGV[0] );

        $item =   ( $check =~ /a/  )  &&  "all"
               || ( $check =~ /B/  )  &&  "bladeenc"
               || ( $check =~ /c/  )  &&  "cddb"
               || ( $check =~ /D/  )  &&  "debug"
               || ( $check =~ /e/  )  &&  "eject"
               || ( $check =~ /G/  )  &&  "gogo"
               || ( $check =~ /h/  )  &&  "help"
               || ( $check =~ /l/  )  &&  "lazy"
               || ( $check =~ /L/  )  &&  "lame"
               || ( $check =~ /n/  )  &&  "number"
               || ( $check =~ /O/  )  &&  "oggenc"
               || ( $check =~ /p/  )  &&  "paranoia"
               || ( $check =~ /P/  )  &&  "play"
               || ( $check =~ /Q/  )  &&  "quiet"
               || ( $check =~ /r/  )  &&  "rename"
               || ( $check =~ /S/  )  &&  "superlazy"
               || ( $check =~ /t/  )  &&  "trayclose"
               || ( $check =~ /V/  )  &&  "version"
               || ( $check =~ /v/  )  &&  "verbose"
               || ( $check =~ /w/  )  &&  "wav"
               ||                         "default";

        print STDERR "DEBUG: handling only \"\-\?\" flag types: flag is $item\n" if $debug;

        @flagsToCheck = ( @flagsToCheck, $item );
      }
    }

    print STDERR "DEBUG: \@flagsToCheck: @flagsToCheck\n" if $debug;

    shift( @ARGV );

    print STDERR "DEBUG: \@ARGV: @ARGV\n" if $debug;
  }
}



##########################################################################
#                                                                        #
# SUB: handleFlags                                                       #
#                                                                        #
#   Once parseFlags sets up the @flagsToCheck array, handleFlags may     #
#   run through the array, taking appropriate action. These actions can  #
#   include, and are not limited to: setting ripperFlags or setting      #
#   encoderFlags, calling usage/exiting, and printing out version info.  #
#   Many switches only set flags and do not exit the script.             #
#                                                                        #
##########################################################################

sub handleFlags {
  # Finished parsing the @ARGV array. Now determine how to flag
  # $ripper and $encoder; possibly reacting quickly, calling
  # subroutines abort() or usage(), even exit().

  $numFlags = @flagsToCheck;

  foreach $flag ( @flagsToCheck ) {

    if( $flag eq "all" ) {
        $ripEntireCD = "true";
        print STDERR "DEBUG: set to rip entire CD\n" if $debug;
    }
    elsif( $flag eq "bitrate" ) {
        if( not($bitrateSet) ) {
          if( not($kbps =~ /\d{1,3}/) ) {
            print( "rip:  Invalid bitrate ${kbps}.\n" ) and &terminate;
          }

          $encoderFlags = $encoderFlags . $encoderBITRATE . $kbps;
          $bitrateSet   = "true";

          print STDERR "DEBUG: set bitrate to $kbps\n" if $debug;
        }
    }
    elsif( $flag eq "bladeenc" ) {
        if( $existantEncoder[$bladeenc] ) {
          $encoder = $existantEncoder[$bladeenc];
          print STDERR "DEBUG: set encoder to $encoder\n" if $debug;
        }
        elsif( $existantEncoder[$BladeEnc] ) {
          $encoder = $existantEncoder[$BladeEnc];
          print STDERR "DEBUG: set encoder to $encoder\n" if $debug;
        }
        else {
          print( "rip:  No \"bladeenc\" and no \"BladeEnc\" on your \$PATH\n" );
          print( "rip:  though a -B or a --bladeenc flag was specified. Aborting.\n" );
          &abort;
        }
    }
    elsif( $flag eq "cddb" ) {
        $checkDatabase = "true";
        $mayNotEncode  = "true";
        print STDERR "DEBUG: setup checking database\n" if $debug;
    }
    elsif( $flag eq "device" ) {
        if( $dev =~ /^\/dev\/\w+/ and ( -e $dev ) ) {
          $ripperFlags = $ripperFlags . $ripperDEVICE . $dev;
          print STDERR "DEBUG: set device to $dev\n" if $debug;
        }
        else {
          print( "rip:  Invalid device: ${dev}. Aborting.\n" ) and &terminate;
        }
    }
    elsif( $flag eq "debug" ) {
        $keepDebug = "true";
        print STDERR "DEBUG: set to leave debug file\n" if $debug;
    }
    elsif( $flag eq "eject" ) {
        $eject        = "true";
        $mayNotEncode = "true";
        print STDERR "DEBUG: set to eject\n" if $debug;
    }
    elsif( $flag eq "gogo" ) {
        if( $existantEncoder[$gogo] ) {
          $encoder = $existantEncoder[$gogo];
          print STDERR "DEBUG: set encoder to $encoder\n" if $debug;
        }
        else {
          print( "rip:  No \"gogo\" on your \$PATH though a -G or a\n" );
          print( "rip:  --gogo flag was specified. Aborting.\n" );
          &abort;
        }
    }
    elsif( $flag eq "help" or $flag eq "default" ) {
        print STDERR "DEBUG: set to display usage\n" if $debug;
        &usage;
    }
    elsif( $flag eq "lazy" ) {
        $kbps         = 160;
        @flagsToCheck = (@flagsToCheck, "trayclose");
        @flagsToCheck = (@flagsToCheck, "bitrate"  );
        @flagsToCheck = (@flagsToCheck, "cddb"     );
        @flagsToCheck = (@flagsToCheck, "all"      );
        @flagsToCheck = (@flagsToCheck, "eject"    );
        $lazy         = "true";
        $superlazy    = "";

        print STDERR "DEBUG: set to do lazy rip\n" if $debug;
    }
    elsif( $flag eq "lame" ) {
        if( $existantEncoder[$lame] ) {
          $encoder = $existantEncoder[$lame];
          print STDERR "DEBUG: set encoder to $encoder\n" if $debug;
        }
        else {
          print( "rip:  No \"lame\" on your \$PATH though a -L or a\n" );
          print( "rip:  --lame flag was specified. Aborting.\n" );
          &abort;
        }
    }
    elsif( $flag eq "move" ) {
        if( $outputDir =~ /~/ ) {
          $outputDir =~ s/~/$HOME/g;
        }

        if(  not( -e $outputDir )  ) {
          system( "mkdir -p $outputDir" );
          print STDERR "DEBUG: tried to create dir $outputDir\n" if $debug;

          if(  not( -e $outputDir ) or not( -w $outputDir )  ) {
            print( "Either the output directory $outputDir cannot be created\n" );
            $outputDir = $DEFAULT_DIR;
            print( "or it is not writable. Output will be to $outputDir instead.\n" );
          }
          else {
            $moveIt = "true";
            print STDERR "DEBUG: set \$outputDir to $outputDir\n" if $debug;
          }
        }
    }
    elsif( $flag eq "number" ) {
        $numberThem = "true";
        print STDERR "DEBUG: set to number output files\n" if $debug;
    }
    elsif( $flag eq "oggenc" ) {
        if( $existantEncoder[$oggenc] ) {
          $encoder   = $existantEncoder[$oggenc];
          $extension = "ogg";
          print STDERR "DEBUG: set encoder to $encoder\n" if $debug;
        }
        else {
          print( "rip:  No \"oggenc\" on your \$PATH though a -O or a\n" );
          print( "rip:  --oggenc flag was specified. Aborting.\n" );
          &abort;
        }
    }
    elsif( $flag eq "paranoia" ) {
        $ripperFlags  = $ripperFlags  . $ripperPARANOIA;

        if( not($bitrateSet) ) {
          $encoderFlags = $encoderFlags . $encoderPARANOIA;
          $bitrateSet   = "true";
        }

        print STDERR "DEBUG: set paranoia\n" if $debug;
    }
    elsif( $flag eq "play" ) {
        $play = "true";
        print STDERR "DEBUG: set to play output files\n" if $debug;
    }
    elsif( $flag eq "quiet" ) {
        $quiet        = "true";
        $verbose      = "";
        $ripperFlags  = $ripperFlags  . $ripperQUIET;
        $encoderFlags = $encoderFlags . $encoderQUIET;

        print STDERR "DEBUG: set to be quiet\n" if $debug;
    }
    elsif( $flag eq "rename" ) {
        $renameTracks = "true";
        print STDERR "DEBUG: set to prompt for output file names\n" if $debug;
    }
    elsif( $flag eq "cdspeed" ) {
        $okaySpeed = "true";

        if(  not( $speed =~ /^\d+$/ )  ) {
          print( "rip:  Invalid CD read speed: ${speed}. Aborting.\n" ) and &terminate;
        }
        else {
          if( $speed < 0 or $speed > 80 ) {
            $okaySpeed = "";

            print( "\nCD read speed should be ${speed}?\n" );
            print( "Are you sure (y/N)? " );
            chop( $answer = <STDIN> );

            if( $answer =~ /^[Yy]/ ) {
              $okaySpeed = "true";
            }
          }
        }

        if( $okaySpeed ) {
          $ripperFlags = $ripperFlags . $ripperSPEED . $speed;

          print STDERR "DEBUG: set CD speed to $speed\n" if $debug;
        }
    }
    elsif( $flag eq "superlazy" ) {
        $kbps         = 160;
        @flagsToCheck = (@flagsToCheck, "trayclose");
        @flagsToCheck = (@flagsToCheck, "bitrate"  );
        @flagsToCheck = (@flagsToCheck, "cddb"     );
        @flagsToCheck = (@flagsToCheck, "all"      );
        @flagsToCheck = (@flagsToCheck, "eject"    );
        $lazy         = "true";
        $superlazy    = "true";

        print STDERR "DEBUG: set to be super lazy\n" if $debug;
    }
    elsif( $flag eq "trayclose" ) {
        $trayclose    = "true";
        $mayNotEncode = "true";
        print STDERR "DEBUG: set to close CD tray\n" if $debug;
    }
    elsif( $flag eq "verbose" ) {
        $verbose      = "true";
        $quiet        = "";
        $mayNotEncode = "true";
        $ripperFlags = $ripperFlags . $ripperVERBOSE;

        print STDERR "DEBUG: set to be verbose\n" if $debug;
    }
    elsif( $flag eq "version" ) {
        $mayNotEncode = "true";

        print( "\n########################################################\n"  );
        print( "#     rip  --  Created by Greg Smethells (c) 2001      #\n"  );
        print( "########################################################\n"  );
        print( "#      Version $version was last modified on $date    #\n"  );
        print( "# Please report bugs and/or email your suggestions to  #\n"  );
        print( "#             gsmethells\@linuxfreak.com                #\n" );
        print( "#    http://www.linuxfreak.com/~gsmethells/rip/        #\n"  );
        print( "########################################################\n\n"  );

        &terminate;
    }
    elsif( $flag eq "wav" ) {
        $wavONLY   = "true";
        $extension = "wav";
        print STDERR "DEBUG: set to encode to WAV only\n" if $debug;
    }
    else {
        print( "rip:  Unknown flag \"${flag}\". It will be ignored.\n" );
        print STDERR "DEBUG: unknown flag $flag.\n" if $debug;
    }
  }


  # Only trayclose if requested and after all flags such
  # as --quiet have been fully parsed.
  if( $trayclose ) {
    print "Loading CD tray... " if not($quiet);
    system( "eject --trayclose" );
    print( "Done.\n" ) if not($quiet);
  }
}



##########################################################################
#                                                                        #
# SUB: parseTracks                                                       #
#                                                                        #
#   Parses the track list argument to rip. Then set @trackList to show   #
#   what tracks to handle.                                               #
#                                                                        #
##########################################################################

sub parseTracks {
  # Figure out how many track there are on the CD ($cdLength)
  # just in case the user used the "-a/--all" flag
  if($ripper =~ /$existantRipper[$cdparanoia]/ and not(($numFlags == 1) and $mayNotEncode)) {
    system( "cdparanoia -sQ" );
    print STDERR "DEBUG: querying CD for track info\n" if $debug;
  }

  $cdLength = 0;

  open(TEMP, "<$tempSTDERR") or print( "rip: cannot open temp file: $!\n" ) and exit(3);

  $_ = " ";

  while( not(eof TEMP) and not($_ =~ /=====/) ) {
    $_ = <TEMP>;
  }

  while( not(eof TEMP) and not($_ =~ /TOTAL/) ) {
    $_ = <TEMP>;

    if( not($_ =~ /TOTAL/ ) ) {
      $cdLength++;
    }
  }

  close( TEMP );


  # Parse the tracks list given after the flags
  if( $ripEntireCD ) {
     @trackList = (1..$cdLength);
  }
  else {
    print STDERR "DEBUG: \@ARGV: @ARGV\n" if $debug;

    while( @ARGV > 0 and $ARGV[0] =~ /\d/ ) {

      # Determine if the user used a dash or spaced number format

      if( $ARGV[0] =~ /^\d{1,2}\-\d{1,2}$/ ) {      # USED DASHED
        $char  = chop( $ARGV[0] );
        $start = "";
        $end   = "";

        while( $char ne '-' ) {
          $end  = $char . $end;
          $char = chop( $ARGV[0] );
        }

        $start = $ARGV[0];

        @trackList = ( @trackList, ${start}..${end} );
      }
      elsif( $ARGV[0] =~ /^\d{1,2}$/ ) {           # USED SPACED
        @trackList = ( @trackList, $ARGV[0] );
      }
      else {
        print( "rip:  Sorry, cannot rip \"$ARGV[0]\"\n" );
        &usage;
      }

      shift( @ARGV );

      print STDERR "DEBUG: updating track list now: @trackList\n" if $debug;
      print STDERR "DEBUG: \@ARGV: @ARGV\n" if $debug;
    }
  }

  if( @trackList != 0 and not($quiet) ) {
    print( "\n\nRipping these track(s):  @trackList\n\n" );
  }

  # If there *still* are more args, go back and see if they are flags
  if( @ARGV > 0 ) {
    &parseFlags;
    &handleFlags;
  }
}



##########################################################################
#                                                                        #
# SUB: setNames                                                          #
#                                                                        #
#   If the track are to be renamed after encoding, then request or set   #
#   the names to be used for each track now.                             #
#                                                                        #
##########################################################################

sub setNames {
  if( $checkDatabase and $renameTracks ) {
    print( "\nOnly one of -c/--cddb and -r/--rename can be used at a time\n\n" );
    &abort;
  }

  if( $checkDatabase ) {
    &cddbRename;
  }

  if( $renameTracks ) {
    &manualRename;
  }

  if( $lazy ) {
    &beLazy;
  }
}



##########################################################################
#                                                                        #
# SUB: renameIfDuplicate                                                 #
#                                                                        #
#   Takes input in $_ and if the string already exists in @renameList    #
#   a new name is created to replace the name found in $_. The return    #
#   value is left in $_.                                                 #
#                                                                        #
##########################################################################

sub renameIfDuplicate {
  $tempName = $_;
  $newNum   = "2";
  $oldNum   = "1";

  print STDERR "DEBUG: \$tempName originally: $tempName\n" if $debug;

  # Do not allow tracks to have the same name
  do {
    $changed  = "";

    foreach $item (@renameList) {
      if( $tempName eq $item ) {
        $tempName =~ s/\.$extension$//;
        $tempName =~ s/_$oldNum$//;
        $tempName = $tempName . "_" . $newNum . "." . $extension;
        $oldNum   = $newNum;
        $newNum++;
        $changed  = "true";
      }
    }

    if( $changed ) {
      print STDERR "DEBUG: \$newNum is $newNum and \$oldNum is $oldNum\n" if $debug;
      print STDERR "DEBUG: \$tempName now: $tempName\n" if $debug;
    }
  } while( $changed );

  $_ = $tempName;
}



##########################################################################
#                                                                        #
# SUB: cddbRename                                                        #
#                                                                        #
#   Setup the renameList variable by way of CDDB. If there are no track  #
#   arguments to rip, just print CDDB info to the screen.                #
#                                                                        #
##########################################################################

sub cddbRename {
  # Get the names to rename MP3s/Oggs to, via CDDB, if so requested (flagged)

  if( not($cddb) ) {
    print( "The -c/--cddb flag you used requires CDDB/CDDB_get's \"cddb.pl\" script.\n" );
    print( "You can get this perl module from http://armin.emx.at/cddb/\n" );
  }
  elsif(  0 == @trackList and not($lazy)  ) {
    print( "Connecting to freedb.freedb.org on port 888...\n\n" );
    $result = system( "$cddb > /dev/tty" );

    if( $result != 0 ) {
      print( "rip:  No info received from CDDB.\n" );
      print( "rip:  Connection may have been refused.\n" );
      print( "rip:  CDDB lookup has been aborted.\n" );
    }
  }
  else {
    print STDERR "DEBUG: dumping CDDB info to $tempCDDB\n" if $debug;

    print( "Connecting to freedb.freedb.org on port 888... " ) if not($quiet);
    system( "echo 1 | $cddb > $tempCDDB" );

    @renameList = ();

    open(INFILE, "<$tempCDDB") or print( "rip:  cannot open cddb file: $!\n") and exit(4);
    &parseCDDBFile;
    close( INFILE );

    system( "rm -f $tempCDDB" );
  }
}



##########################################################################
#                                                                        #
# SUB: parseCDDBFile                                                     #
#                                                                        #
#   Parses the information found in the $tempCDDB file and creates the   #
#   correct information in the global vars $artist, $title, $trackno,    #
#   and @renameList.                                                     #
#                                                                        #
##########################################################################

sub parseCDDBFile {
  # Check for an empty file (CDDB lookup failed if empty)
  if( eof INFILE ) {
    print( "\n\nrip:  No info received from CDDB.\n" );
    print( "rip:  Connection may have been refused.\n" );
    print( "rip:  CDDB lookup has been aborted.\n" );

    if( $lazy ) {
      print( "rip:  Cannot complete lazy rip. Exiting script.\n" );
      &terminate;
    }
    else {
      print( "\nrip:  Abort the rip or attempt to recover (a/R)? " );
      $answer = <STDIN>;
      &abort if $answer =~ /^[Aa]/;

      print( "rip:  Use GENERIC names or setup names MANUALLY (M/g)? " );
      $answer = <STDIN>;

      if( $answer =~ /^[Gg]/ ) {
        print( "rip:  Recovering by using generic naming convention.\n" );
        $checkDatabase = "";
        $renameTracks  = "";
      }
      else {
        $checkDatabase = "";
        $renameTracks  = "true";
      }
    }
  }
  else {
    # Read the artist name (used by beLazy() if it's called)
    while(  not($_ =~ /artist/)  ) {
      $_ = <INFILE>;
    }

    s/\n//g;
    s/ //g;
    s/\&/And/g;
    s/\#/Number/g;
    s/[\(\)\[\]\{\}\'\"\`\.\?\/\\,]//g;

    # May get back "Choose: artist: NAME" instead of "artist: NAME"
    # if forced to choose between CDDB entries: prompt the user to choose
    if( $_ =~ /Choose/ ) {
      print( "\nThere is more than one entry in the CDDB\n\n" );

      $doItAgain = "true";

      while( $doItAgain ) {
        system( "rm -f $tempCDDB" );
        system( "$cddb tee $tempCDDB" );
        print( "\n\nWould you like to use this entry (Y/n)?: ");

        $answer = <STDIN>;

        if( $answer =~ /^[Nn]/ ) {
          $doItAgain = "true";
        }
        else {
          $doItAgain = "";

          # Read the artist name (used by beLazy() if it's called)
          while(  not($_ =~ /artist/)  ) {
            $_ = <INFILE>;
          }

          s/\n//g;
          s/ //g;
          s/\&/And/g;
          s/\#/Number/g;
          s/[\(\)\[\]\{\}\'\"\`\.\?\/\\,]//g;
        }
      }
    }
    else {
      print( "Done.\n" ) if not($quiet);
    }

    /.*artist:(.*)$/m;
    $artist = $1;

    print STDERR "DEBUG: \$artist name set to $artist\n" if $debug;


    # Read the album name (used by beLazy() if it's called)
    while(  not($_ =~ /title/)  ) {
      $_ = <INFILE>;
    }

    chop( $_ );
    s/ //g;
    s/\&/And/g;
    s/\#/Number/g;
    s/[\(\)\[\]\{\}\'\"\`\.\?\/\\,]//g;

    /.*title:(.*)$/m;
    $title = $1;

    print STDERR "DEBUG: album \$title set to $title\n" if $debug;


    # Read the number of tracks (used by beLazy() to rip entire CD)
    while(  not($_ =~ /trackno/)  ) {
      $_ = <INFILE>;
    }

    chop( $_ );
    s/ //g;
    /.*trackno:(.*)$/m;
    $trackno = $1;

    print STDERR "DEBUG: \$trackno set to $trackno\n" if $debug;


    # Create the @renameList
    for( $i = 0 ; $i < $trackno ; $i++ ) {
      $_ = <INFILE>;
      chop( $_ );
      s/^track \d{1,2}: //g;
      s/ /_/g;
      s/\&/And/g;
      s/\#/Number_/g;
      s/[\(\)\[\]\{\}\'\"\`\.\?\/\\,:]//g;
      s/__/_/g;
      s/\.$extension$//;

      $_ = $_ . "." . $extension; # Put argument in $_
      &renameIfDuplicate;
      $newName = $_;              # Grab return value from $_

      # Make sure name is less than an arbitrary number of chars long
      if( length( $newName ) > 50 ) {
        while( length($newName) > 50 ) {
          $char = chop( $newName );
        }

        while( $char ne "_" ) {
          $char = chop( $newName );
        }

        $newName = $newName . "." . $extension;
      }

      # Finally we can add the new name to the rename list
      @renameList = ( @renameList, $newName );
    }

    print STDERR "DEBUG: CDDB rename list: @renameList\n" if $debug;
  }
}



##########################################################################
#                                                                        #
# SUB: manualRename                                                      #
#                                                                        #
#   Prompt the user, manually, to give the proper name for each track    #
#   which is to be ripped to MP3 or to Ogg Vorbis.                       #
#                                                                        #
##########################################################################

sub manualRename {
  if( @trackList > 0 ) {
    print( "\n*****************************************************\n"      );
    print( "Now taking the proper names for the output $extension files.\n" );
    print( "AN EXAMPLE:    Riders On The Storm\n"                           );
    print( "TURNS INTO:    Riders_On_The_Storm.$extension\n\n"              );

    print( "Do you want your CD back for a moment (Y/n)? " );
    chop( $answer = <STDIN> );

    if( not($answer =~ /^[Nn]/) ) {
      system( "eject --cdrom" );
    }

    print( "\n" );
    $doItAgain = "true";

    # Prompt the user to complete the rename list
    while( $doItAgain ) {
      @renameList = ();

      foreach $num (@trackList) {
        if( $num < 10 ) {
          print( "<Rename track $num >  " );
        }
        else {
          print( "<Rename track $num>  " );
        }

        chop( $newName = <STDIN> );

        $newName =~ s/\.$extension$//;
        $newName =~ s/ /_/g;
        $newName =~ s/\&/And/g;
        $newName =~ s/\#/Number_/g;
        $newName =~ s/\W//g;
        $newName = $newName . "." . $extension;

        $_ = $newName;      # Put argument in $_
        &renameIfDuplicate;
        $newName = $_;      # Grab return value from $_

        @renameList = ( @renameList, $newName );
      }

      print( "\nDone.\n\n" );

      $num = 0;

      foreach $item (@renameList) {
        print( "track $trackList[$num]: $item\n" );
        $num++;
      }

      print( "\nAre these the right names (Y/n)?  " );
      chop( $answer = <STDIN> );
      print( "\n" );

      if( $answer =~ /^[Nn]/ ) {
        $doItAgain = "true";
      }
      else {
        $doItAgain = "";
      }
    }

    print STDERR "DEBUG: manual rename list: @renameList\n" if $debug;

    print( "Make sure the CD is in the tray and hit <ENTER>." );
    <STDIN>;  # Block until user hits <enter>

    system( "eject --trayclose" );

    print( "*****************************************************\n\n" );
  }
}



##########################################################################
#                                                                        #
# SUB: beLazy                                                            #
#                                                                        #
#   Using cddb.pl, create a dir in the current working dir with the name #
#   given in the artist field, setup the trackList to rip the entire CD, #
#   and create a playlist that XMMS could use in the "artist's" dir.     #
#   This entire section of code assumes you have already called the      #
#   subroutine "cddbRename" prior to this point.                         #
#                                                                        #
##########################################################################

sub beLazy {
  # Warn that users of "lazy" rips cannot do a "move" also (its implicit)
  if( $lazy and $moveIt ) {
    print( "\nrip:  Cannot use -m/--move and -l/--lazy or -S/--superlazy together\n" );
    print( "rip:  since -m/--move is implicit in a -l/--lazy or a -S/--superlazy\n\n" );
  }

  # Setup the output dir based on the artist's name
  $outputDir = "$pwd/$artist/";
  $outputDir =~ s/\/\//\//g;
  $outputDir =~ s/\/\//\//g;

  if(  not( -e $outputDir ) ) {
    system( "mkdir -p $outputDir" );

    print STDERR "DEBUG: tried to create dir $outputDir\n" if $debug;

    if(  not( -e $outputDir ) or not( -w $outputDir )  ) {
      print( "Either the output directory $outputDir cannot be created\n" );
      $outputDir = $DEFAULT_DIR;
      print( "or it is not writable. Output will be to $outputDir instead.\n" );
      print( "Likely there is a file with the name $artist already in $pwd.\n" );
    }
  }

  # Setup the output dir based on the artist's name
  # and the album's title if we are being super lazy
  if( $superlazy ) {
    $outputDir = "$pwd/$artist/$title/";
    $outputDir =~ s/\/\//\//g;
    $outputDir =~ s/\/\//\//g;

    if(  not( -e $outputDir ) ) {
      system( "mkdir -p $outputDir" );

      print STDERR "DEBUG: tried to create dir $outputDir\n" if $debug;

      if(  not( -e $outputDir ) or not( -w $outputDir )  ) {
        print( "Either the output directory $outputDir cannot be created\n" );
        $outputDir = $DEFAULT_DIR;
        print( "or it is not writable. Output will be to $outputDir instead.\n" );
        print( "Likely there is a file with the name $title already in $pwd/$artist.\n" );
      }
    }
  }

  print STDERR "DEBUG: output dir set to $outputDir\n" if $debug;

  # Rip the *entire* CD
  @trackList = (1..$trackno);

  print STDERR "DEBUG: trackList set to @trackList\n" if $debug;

  system( "rm -f $pwd/$title" );
  open(OUTFILE, ">$pwd/$title.playlist") or print( "rip:  can't open playlist: $!\n" ) and exit(5);

  # Create each playlist file entry using a format XMMS likes
  for( $i = 0 ; $i < $trackno ; $i++ ) {
    $nextTrack = $renameList[$i];
    $value     = $i + 1;

    if( $numberThem and $value < 10 ) {
      print OUTFILE ( "${outputDir}0${value}_${nextTrack}\n" );
      print STDERR "DEBUG: added ${outputDir}0${value}_${nextTrack}\n" if $debug;
    }
    elsif( $numberThem ) {
      print OUTFILE ( "${outputDir}${value}_${nextTrack}\n" );
      print STDERR "DEBUG: added ${outputDir}${value}_${nextTrack}\n" if $debug;
    }
    else {
      print OUTFILE ( "${outputDir}${nextTrack}\n" );
      print STDERR "DEBUG: added ${outputDir}${nextTrack}\n" if $debug;
    }
  }

  close( OUTFILE );
}



##########################################################################
#                                                                        #
# SUB: encode                                                            #
#                                                                        #
#   Based upon what GLOBAL variables have been set, encode the given     #
#   tracks to MP3, Ogg Vorbis, or WAV one at a time. This saves hard     #
#   drive space compared to ripping all tracks to WAV first and then     #
#   encoding.                                                            #
#                                                                        #
##########################################################################

sub encode {
  # Setup rip's temp file name
  $out = $out . "." . $extension;

  # Copy (do not alias!) the STDOUT file descriptor
  open(OUT, ">&STDOUT") or print( "rip:  cannot alias stdout: $!\n" ) and exit(6);

  # Temp file for outputting debug info
  open(DF, ">>$tempSTDERR") or print( "rip: cannot open debug file: $!\n" ) and exit(7);

  if( $debug ) {
    print DF   "DEBUG: tracklist is: @trackList\n";
    print DF "\nDEBUG: Ripper:        $ripper\n";
    print DF   "DEBUG: Ripper flags:  $ripperFlags\n";
    print DF   "DEBUG: Encoder:       $encoder\n";
    print DF   "DEBUG: Encoder flags: $encoderFlags\n";
    print DF   "DEBUG: CDDB tool:     $cddb\n";
    print DF   "DEBUG: Output dir:    $outputDir\n\n";
  }

  # If in verbose mode, output flags in use by cdparanoia and bladeenc
  if( $verbose ) {
    print( "\nRipper:        $ripper\n"       );
    print(   "Ripper flags:  $ripperFlags\n"  );
    print(   "Encoder:       $encoder\n"      );
    print(   "Encoder flags: $encoderFlags\n" );
    print(   "CDDB tool:     $cddb\n"         );
    print(   "Output dir:    $outputDir\n\n"  );
  }

  # Make child processes (ripper and encoder) quiet if not in verbose mode
  if( not($verbose) ) {
    # Make sure $ripper is quiet
    if( not($ripperFlags =~ /$ripperQUIET/) ) {
      $ripperFlags = $ripperFlags  . $ripperQUIET;
    }

    # Make sure $encoder is quiet
    if( not($encoderFlags =~ /$encoderQUIET/) ) {
      $encoderFlags = $encoderFlags . $encoderQUIET;
    }

    close( STDOUT );
    close( STDERR );
  }

  print OUT "\n" if not($quiet) and (@trackList > 0);


  # Rip CD tracks to MP3, Ogg Vorbis, or WAV depending on flags set
  foreach $track ( @trackList ) {
    if( $track =~ /^\d{1,2}$/ ) {
      if( $track < 10 ) {
        print OUT "Now ripping CD track $track to $extension...  " if not($quiet);
        print OUT "\n" if $verbose and not($encoder =~ /$existantEncoder[$lame]/);
      }
      else {
        print OUT "Now ripping CD track $track to $extension... "  if not($quiet);
        print OUT "\n" if $verbose and not($encoder =~ /$existantEncoder[$lame]/);
      }

      # Get the track's proper name (output file name)
      if( $renameTracks ) {
        $properName = shift( @renameList );
      }
      elsif( $checkDatabase ) {
        $properName = $renameList[ $track - 1 ];
      }
      else {
        $properName = "track" . $track . "." . $extension;
      }

      # Possibly add numbering to proper name (output file name)
      if( $numberThem and $track < 10 ) {
        $properName = "0" . $track . "_" . $properName;
      }
      elsif( $numberThem ) {
        $properName = $track . "_" . $properName;
      }

      # Strip out non-word characters still existant in the filename $properName
      $properName =~ s/\.$extension$//;
      $properName =~ s/ /_/g;
      $properName =~ s/\&/And/g;
      $properName =~ s/\#/Number_/g;
      $properName =~ s/\W//g;
      $properName .= ".$extension";

      # Do the actual CD rip to...
      if( $wavONLY ) {
        # ...WAV format (blocking system call)
        print DF "\nDEBUG: $ripper $ripperFlags $track $out &&
                           mv $out ${outputDir}/${properName}\n" if $debug;
        $result = system( "$ripper $ripperFlags $track $out &&
                           mv $out ${outputDir}/${properName}" );
      }
      else {
        # ...MP3 or Ogg Vorbis (blocking system call)
        print DF "\nDEBUG: $ripper $ripperFlags $track - |
                           $encoder $encoderFlags $in $out &&
                           mv $out ${outputDir}/${properName}\n" if $debug;
        $result = system( "$ripper $ripperFlags $track - |
                           $encoder $encoderFlags $in $out &&
                           mv $out ${outputDir}/${properName}" );
      }

      # Call signal handler if system call did not finish error free
      if( $result != 0 ) {
        $inSubEncode = "true";
        &signalHandler;
      }

      # Play the ripped file if so flagged
      if( $play and $track == $trackList[0] ) {
        print OUT "Playing   $properName\n" if not($quiet);
        system( "xmms --play $outputDir/$properName &" );
        print DF "DEBUG: doing:  xmms --play $outputDir/$properName & \n" if $debug;
      }
      elsif( $play ) {
        print OUT "Enqueuing $properName\n" if not($quiet);
        system( "xmms --enqueue $outputDir/$properName &" );
        print DF "DEBUG: doing:  xmms --enqueue $outputDir/$properName & \n" if $debug;
      }
      else {
        # Do not play anything
      }

      print OUT "Done.\n" if( not($quiet) and not($play) );
    }
    else {
       print OUT "Track to rip argument was syntactically incorrect: $track\n\n";
       print OUT "An example of legal syntax is:   rip 2-5 7 8 10-13 15\n";
       &terminate;
    }
  }

  close( DF );

  if( @trackList > 0 ) {
    print OUT "\n" if not($quiet);
  }

  # Finished with CD. It is safe to now eject the CD tray, if so flagged
  if( $eject ) {
    print OUT "Ejecting CD tray... " if not($quiet);
    system( "eject --cdrom" );
    print OUT "Done.\n" if not($quiet);
  }

  close( STDERR );
}



##########################################################################
#                                                                        #
# SUB: usage                                                             #
#                                                                        #
#   Prints a usage message for rip and exits.                            #
#                                                                        #
##########################################################################

sub usage {
  print("\nUSAGE: rip [option(s)] <track(s)>\n"                                           );
  print("\nOPTIONS:\n\n"                                                                  );
  print("  -a   --all                 rip and encode all the tracks on the CD\n"          );
  print("  -b=  --bitrate=NUM         set bitrate for encoding to NUM kbps\n"             );
  print("  -B   --bladeenc            use BladeEnc for MP3 encoding\n"                    );
  print("  -c   --cddb                use CDDB to rename output files (must be online)\n" );
  print("  -d=  --dev=DEV             force input to come from CD-ROM device DEV\n"       );
  print("  -D   --debug               leave debug info in /tmp/rip-MMDDHHMMSS-stderr\n"   );
  print("  -e   --eject               eject CD tray after doing everything else\n"        );
  print("  -G   --gogo                use GOGO for MP3 encoding\n"                        );
  print("  -h   --help                print this help to the screen and exit\n"           );
  print("  -l   --lazy                uses -t, -a, -c, -b=160, -m=, and -e implicitly,\n" );
  print("                             create an XMMS compatible playlist in \$PWD,\n"     );
  print("                             move all output files to \$PWD/ArtistName/\n"       );
  print("  -L   --lame                use LAME for MP3 \"encoding\"\n"                    );
  print("  -m=  --move=DIR            move all output files to DIR (absolute pathname)\n" );
  print("  -n   --number              append 01_, 02_, 03_, etc to start of filename\n"   );
  print("  -O   --oggenc              use oggenc and encode to Ogg Vorbis files\n"        );
  print("  -p   --paranoia            use 160 kbps and do not accept skips on rip\n"      );
  print("  -P   --play                play finished files in XMMS during rip\n"           );
  print("  -Q   --quiet               rip and encode tracks without any visible output\n" );
  print("  -r   --rename              ask for proper name of all tracks before ripping\n" );
  print("  -s=  --speed=NUM           force CD-ROM device to read at speed NUM\n"         );
  print("  -S   --superlazy           same functionality as -l/--lazy except that all\n"  );
  print("                             output is to \$PWD/ArtistName/AlbumTitle/ instead\n");
  print("  -t   --trayclose           close CD tray before doing anything else\n"         );
  print("  -v   --verbose             print verbose info about what script is doing\n"    );
  print("  -V   --version             print rip's version information\n"                  );
  print("  -w   --wav                 rip CD tracks to WAV files and no further\n"        );
  print("\nEXAMPLES:\n\n"                                                                 );
  print("  rip 1 3-5 7 9-11 12 14     rip tracks 1 3 4 5 7 9 10 11 12 and 14 to MP3\n"    );
  print("  rip 6                      rip track 6 to MP3 using default encoder\n"         );
  print("  rip -c 6                   rip track 6 and rename its MP3 file via CDDB\n"     );
  print("  rip -cp 6                  simliar: also uses 160 kbps and accepts no skips\n" );
  print("  rip -cp -m=~/mp3 6         simliar: also moves output MP3 files to ~/mp3\n"    );
  print("  rip -et -s=32 6            close tray, rip track 6, speed at 32x, eject\n"     );
  print("  rip -vL -b=160 6           verbose rip, using LAME and 160 kbps encoding\n"    );
  print("  rip -d=/dev/hdd 6          rip using /dev/hdd as the input CDROM drive\n"      );
  print("\nNORMAL USAGE:\n\n"                                                             );
  print("  rip -c                     print CDDB info rip would use during rip\n\n"       );
  print("  rip -Par                   rip entire CD to MP3, rename, and play\n"           );
  print("  rip -Pater                 similar: also closes and ejects CD tray\n"          );
  print("  rip -Patec -m=~/mp3        similar: renames via CDDB and does a move\n"        );
  print("  rip -LPatec -m=~/mp3       similar: makes sure LAME is used\n\n"               );
  print("  rip -SP                    laziness at its best IMHO (must be online)\n"       );
  print("  rip -SPO                   similar: rip CD to Ogg Vorbis instead\n\n\n"        );
  print("  The default output directory is the present working directory (\$PWD).\n"      );
  print("  This goes for -l/--lazy and -S/--superlazy in that they output to a\n"         );
  print("  subdir \$PWD/ArtistName/ and \$PWD/ArtistName/AlbumTitle/ respectively.\n\n"   );
  print("  The default output format is MP3. To rip to Ogg Vorbis, use the -O/--oggenc\n" );
  print("  flag. To rip to WAV, use the -w/--wav flag.\n\n"                               );
  print("  If no specific encoder is flagged, rip uses the first encoder it finds.\n\n"   );
  print("             http://www.linuxfreak.com/~gsmethells/rip/\n\n"                     );

  &terminate;
}



##########################################################################
#                                                                        #
# SUB: abort                                                             #
#                                                                        #
#   Prints error messages if either a CD Ripper or an Encoder is not     #
#   present on the $PATH, then exits.                                    #
#                                                                        #
##########################################################################

sub abort {
  if( not($ripper) ) {
    print( "\nrip:  WARNING: A CD Ripper was *not* found on your \$PATH\n"    );
    print("rip:  you can download one from http://www.xiph.org/paranoia/\n\n" );
  }

  if( not($encoder) ) {
    print( "\nrip:  WARNING: An MP3 Encoder was *not* found on your \$PATH\n" );
    print("rip:  you can download one from http://bladeenc.mp3.no\n\n"        );
  }

  exit(8);
}



##########################################################################
#                                                                        #
# SUB: terminate                                                         #
#                                                                        #
#   Exits script normally. Removes debug file if it is not to be kept.   #
#                                                                        #
##########################################################################

sub terminate {
  if( not($keepDebug) ) {
    system( "rm -f $tempSTDERR" );
  }

  exit(0);
}
