#!/usr/bin/perl
#   
# @program cdcat
# @version 0.3
# @author LittleDragon <littledragon@altern.org>
#
# @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.
#

$program_name='cdcat';
$version_major=0;
$version_minor=3;

# Default settings

$device="/dev/cdrom"; # The default device name
$mount_point="/mnt/cdrom"; # The default device's mount point
$directory="/var/lib/cdcat"; # The directory where the text files will reside

$ls="/bin/ls";
$sed="/bin/sed";
$grep="/bin/grep";
$find="/usr/bin/find";
$isoinfo="/usr/bin/isoinfo";

$fstab="/etc/fstab";

$colors=1;           # Use colors
$show_related=1;     # Show related entries for the -d option

$err="Error";
$cat="Catalogued";
$del="Erased";
$ce="\033[m";
$cer="\033[1;31m";   # Error color
$cok="\033[1;32m";   # OK color
$cn="\033[1;37m";    # Neutral color
$cfind="\033[0;36m"; # Find: match color
$ccd="\033[0;32m";   # Find: CD name color
$csep="\033[1;30m";  # Find: separator color
$cerr="${cer}${err}:${ce}";
$ccat="${cok}${cat}:${ce}";
$cdel="${cok}${del}:${ce}";

$tsep=":";           # Find: separator

my @qq;
@qq[1]="a";

# Read settings from config files.

for("/etc/cdcat.conf", "$ENV{HOME}/.cdcatrc") {
	open(CONF, "<$_");
	for(<CONF>) { ${$1}=$2 if(/^[\s\t]*([^#]+?)[\s\t]*=[\s\t]*(.+?)$/) }
	close(CONF);
}

# Program

sub error { print STDERR "$cerr $_[0]\n" }

sub check_mount {
	if($<>0) {
		$mtab=qx{cat /etc/mtab|$grep $mount_point|wc -l};
		if($mtab=~/^\s*0/) {
			error("You must be root or $device must be mounted.");
			exit;
		}
	}
}

$def_dev=$device;

sub color_switch {
	if(!$colors) {
		$cok=$cfind=$ccd=$csep=$ce="";
		$cerr="$err:";
		$ccat="$cat:";
		$cdel="$del:";
	}
}

if(@ARGV!="") {
	for(@ARGV) { $argv.=$_." " };

	if($argv=~/(-V|--version)/) {
		print("$program_name $version_major.$version_minor\n");
		exit(0);
	}
	
	if($argv=~/(-c|--no-colors)/) { $colors=0; color_switch }

	if($argv=~/(-q|--quiet)/) { $quiet=1 }

	if($argv=~/(-d|--device) *(.+?) /) {
		$device=$mount_point="";
		$device=$2;
		@mp=qx{cat $fstab};
		for(@mp) {
			if(/${device}[ \t]/) {
			s/${device}[ \t]+(.+?)[ \t].*$/$1/;
			if(length($_)>0) { $mount_point=$_ }
			}
		}
	}
	
	if($argv=~/(-m|--mount-point) *(.+?) /) { $mount_point=$2 }

	chomp($device); chomp($mount_point);
	if(!length($device)>0 || !length($mount_point)>0) {
		error("Device or mount point not specified.");
		exit;
	}

	if($argv=~/(-l|--list)/) {
		@list_files=qx{ls -1 $directory 2>/dev/null|sort};
		$list_counter=0;
		if(@list_files) {
			for(@list_files) {
				chomp; $list_counter++;
				s/^(.*)?:.*$/$1/;
				print("$list_counter) $_\n");
			}
		} else {
			error("Catalog empty.");
		}
		exit;
	}
	
	if($argv=~/(-b|--browse) *(.+?) /) {
		$browse_number=$2-1;
		@list_files=qx{ls -1 $directory 2>/dev/null|sort};
		if(@list_files) {
			@list_cat=qx{cat $directory/$list_files[$browse_number]};
			for(@list_cat) { chomp; s/^\///; print("$_\n") }
		} else {
			error("Catalog empty.");
		}
		exit;
	}
	
	if($argv=~/-a/ || $argv=~/--append/) { # Append
		check_mount;
		$append=1;
		$append_label=$argv;
		$append_label=~s/^.*(-a|--append) *(.*?) .*$/$2/;
	}
	
	elsif($argv=~/-r/ || $argv=~/--rename/) { # Rename
		check_mount;
		$rename=1;
		$rename_label=$argv;
		$rename_label=~s/^.*(-r|--rename) *(.*?) .*$/$2/;
	}
	
	elsif($argv=~/-e/ || $argv=~/--erase/) { # Delete
		$delete=1;
		$delete_match=$argv;
		$delete_match=~s/^.*(-e|--erase) *(.*?) .*$/$2/;
	}
	
	elsif($argv=~/-f/ || $argv=~/--find/) { # Find
		$grep_string=$argv;
		$grep_string=~s/^.*(-f|--find) *(.*$)/$2/;
		if($grep_string=~/^(\s*-[^ ]+\s*)+ ([^-].+?) -(.*) $/g) {
			# (-f <-options <regex>>) (-cdcat_options)
			# Separate arguments from grep arguments and from grep quote.
			# Magic formula: ^(\s*-[^ ]+\s*)+ ([^-].+?) -(.*) $
			$grep_quote=$2;
			$grep_args=$grep_string; $grep_args=~s/^(.*)$grep_quote.*/$1/;
			$grep_string="$grep_args \"$grep_quote\"";
			$argv=~s/$grep_args//g; $argv=~s/$grep_quote//g; $argv=~s/-f//g;
			$grep_string=~s/ $//g;
		} else {
			$grep_string=~s/ $//g;
		}
		@find_strings=qx{$grep $grep_string $directory/*};
		$grep_pattern=$grep_string;
		$grep_pattern=~s/^ *//g; $grep_pattern=~s/-[a-zA-Z0-9]+//g;
		$grep_pattern=~s/^ *//g; $grep_pattern=~s/\"//g;
		for(@find_strings) {
			chomp;
			s/$directory\///g; s/^(.+?):.+?:\/(.*)$/$ccd$1$csep$tsep$ce$2/g;
			s/($grep_pattern)/$cfind$1$ce/gi;
			print("$_\n");
		}
		exit;
	}
	
	elsif($argv=~/-(C|-compress) *(.+?)\b/) {
		my $tmp_compression = $1;
		if($tmp_compression =~ /^gzip$/i || $tmp_compression =~ /^bzip2$/) {
			$compress = $tmp_compression;
		} else {
			$compress = "none";
		}
	}

	elsif($argv=~/(-h|--help)/) {
		$prog_name=$0;
		$prog_name=~s/^.*\/(.+)$/$1/g;
		print("Usage: $prog_name [OPTION]\n");
		print("Example: $prog_name -f -i source\n");
		print("\n");
		print("[OPTION] can be:\n");
		print("
  -a, --append [text]             Append \"_[text]\" at the end of the catalog
                                  entry name, when creating a new entry.
  -b, --browse <number>           Browse CD <number>.
  -c, --no-colors                 Disable colors.
  -d, --device <device>           Specify a different device. The default is
                                  $def_dev.
  -e, --erase [number|a]          Delete the current CD entry from the
                                  catalog, or the [number] similar to current
                                  entry. If \"a\" is specified instead of a
                                  number, then all the similar entries are
                                  deleted.
  -f, --find [options] <regexp>   Search all the catalog entries and return
                                  the file names matching <regexp>. Also, most
                                  grep options can be specified as [options].
  -h, --help                      Show this help screen.
  -l, --list                      Show the list of all catalogued CDs.
  -m, --mount-point <directory>   Specify a different mount point for the
                                  current device.
  -q, --quiet                     Only show errors.
  -r, --rename <text>             Rename the catalog entry to <text>.
  -V, --version                   Outputs version information and exits.

");
		exit;
	}
}

$escaped_mp=$mount_point;
$escaped_mp=~s/\//\\\//gi;

@iso=qx{$isoinfo -d -i $device 2>&1};
for(@iso) {
	if(/volume id: (.*)$/i) { $label=$1 }
	if(/volume size is: (.*)$/i) { $volume=$1 }
	if(/isoinfo: (.*)$/i) { error("$1"); exit }
}
$label=~s/\s/_/gi; $label=~s/[\/\\]/_/gi;
if($append) { $label.="_".$append_label }
elsif($rename) { $label=$rename_label }

if($delete) {
    if($delete_match && $delete_match=~/^a$/) {
			@delete_strings=qx{find $directory -type f|grep -i "$label"};
			if(@delete_strings) {
	  	  $delete_count=-1;
	    	for(@delete_strings) {
					$delete_count++;
					$delete_string=$delete_strings[$delete_count];
					chomp($delete_string);
					if(qx{[ -e "$delete_string" ] && echo 1 || echo 0}==1) {
				    qx{rm -fr $delete_string};
				    $delete_current=$delete_string;
				    $delete_current=~s/$directory\///;
		    		$delete_current=~s/^(.*):.*$/$1/;
				    if(!$quiet) { print("$cdel $delete_current\n") }
					}
		    }
			} else {
	  	  error("No entries matching $label found.");
			}
			exit;
    }
    if($delete_match) {
			@delete_strings=qx{find $directory -type f|grep -i "$label"};
			if(@delete_strings) {
	  	  $delete_match--;
	    	$delete_string=$delete_strings[$delete_match];
		    chomp($delete_string);
		    if(qx{[ -e "$delete_string" ] && echo 1 || echo 0}==1) {
					qx{rm -fr $delete_string};
					$delete_current=$delete_string;
					$delete_current=~s/$directory\///;
					$delete_current=~s/^(.*):.*$/$1/;
					if(!$quiet) { print("$cdel $delete_current\n") }
	    	}
			} else {
	  	  error("No such similar entry number: $delete_match.");
			}
			exit;
    }
    if(qx{[ -e "$directory/$label:$volume" ] && echo 1 || echo 0}==1) {
			qx{rm -fr "$directory/$label:$volume"};
			if(!$quiet) { print("$cdel $label\n") }
    } else {
			error("Entry $label not found.");
    }
    if(!$quiet && $show_related) {
			@delete_strings=qx{find $directory -type f|grep -i "$label"};
			if(@delete_strings) {
	    $delete_count=0;
	    print("Related entries found:\n");
	    for(@delete_strings) {
				chomp;
				$delete_current=$_;
				$delete_current=~s/$directory\///;
				$delete_current=~s/^(.*):.*$/$1/;
				$delete_count++;
				print("$delete_count) $delete_current\n");
	    }
		}
	}
	exit;
}

if(qx{[ -e "$directory/$label:$volume" ] && echo 1 || echo 0}==1) {
	error("The CD was most probably already catalogued.");
	exit;
} else {
	$fname="$directory/$label:$volume"
}

@files=qx{
  mount $device $mount_point 2>&1|
  $grep -vi "already mounted"|
  $grep -vi "write-protected"|
  $sed 's/mount: \\(.*\\)\$/$cerr \\1/gi';
  $find $mount_point|
  $sed 's/$escaped_mp//gi';
};
check_mount;
$counter=0; undef($first_line);
for(@files) {
  chomp;
  if(!defined($first_line)) { $first_line=1 }
  else {
    $files[$counter]=$_;
    $counter++;
  }
}

open(F, ">$fname");
for(@files) { print F "$_\n" }
close(F);

if(!$quiet) { print("$ccat $label\n") }
