#!/bin/bash -
# cdd script
# Antonio Maschio 2006-2007 <tbin at libero dot it>
#  
# cd.db is my private cd database in text format, with fields separated 
# by $separator (see custom.data); cdd searches through this database.
#
# Type cdd -h to get the command list
# Type cdd -v to get cdd version
# Type cdd -? to get cdd usage


umask 077


# Constants
PLAIN=0         # plain view: chaotic but complete listing
FORMATTED=1     # framed view: very schematic and complete listing
ALIGNED=2       # aligned view: suitable for long listings but trims long lines
TRUE=1
FALSE=0
RELEASE=1
MAJOR=5
MINOR=0

#####################################
#      FUNCTIONS 
#####################################

# clean-up: save temporary search file (if not void) to a "standard" 
# file, if a (user or system) interrupt prevents a regular process ending
function cleanup
{
        if [ -s $tempfile ]; then
                mv $tempfile $deadsearch
        fi
        exit 1
}


# printout: get a line from standard input and print it 
# (only for framed or aligned mode)
function printout()
{
	o=$separator 	# separator is a too long name, after all!
        while read line; do
                artist=${line%%$o*}; line=${line#*$o}
                album=${line%%$o*}; line=${line#*$o}
                year=${line%%$o*}; line=${line#*$o}
                issue=${line%%$o*}
                notes=${line#*$o}
		# check issue code ] or > and print a * if things ain't good,
		# that is the record is not original in its tracking list
		issued=${issue:0:4}	# issue date
		issuec=${issue:4:1}	# issue code
		if [ "$issuec" == "" ]; then
			if [ $issued != $year ] && [ $year != "v.v." ] && [ $issued != "^^^^" ]; then
				issued="$issued*"
			fi
		fi
                # print in aligned mode 
                if [ $view -eq $ALIGNED ]; then
                        printf '%-20.20s %-25.25s %-4.4s %-5.5s %-18.18s\n' "$artist" "$album" "$year" "$issued" "$notes"
                # table mode
                elif [ $view -eq $FORMATTED ]; then
                        echo
                        echo "Artist    :" $artist
                        echo "Album     :" $album
                        echo "Issued in :" $year"          CD date:  "$issued
                        echo "Notes     :" $notes
                        echo "------------------------------------------------------------------------------"
                fi       
            	# Note: plain mode (-N option) is executed ahead
        done
}

function versionf {
	printf "cdd - compact disc database maintainer, bash version %d.%d.%d\n"\
		$RELEASE $MAJOR $MINOR
	printf "Antonio Maschio 2006, <tbin at libero dot it>\n\n"
}

# fast help (usage) function
function fusagef {
        versionf
        echo "Usage: $(basename $0) -[aAcCfFhnorsvxy] [pattern]"
        echo "-o, -s and -x options require a|r|y|i|n as parameter (type $(basename $0) -h for help)"
        echo
}

# help function
function helpf {
        #versionf
        echo "Usage: $(basename $0) [OPTIONS] [pattern]
Options:
  -a            print all voices in the database
  -A            align output in fixed-size colunms (may trim fields)
  -c            report total number of records in the database and exit
  -C            add number of records found to search report 
  -f <file>     extract info from <file> rather than from cd.db 
  -F            frame output (may end in a long listing)
  -h --help     print this help and exit
  -n            print record number for each output line (unformatted outputs)
  -N            print output in plain format (may give uneasy listing)
  -o a|r|y|i|n  output only selected field (unifying multiple identical lines)
  -r            reverse sorting
  -s a|r|y|i|n  sort records only according to selected field
  -v --version  print version and exit
  -x a|r|y|i|n  limit search only to selected field
  -y            print a note about issue years rules and exit
Notes:
1. -s, -x, -o options must be followed by a selector char for field:
     a   artist name,
     r   record title,
     y   year of original record issue,
     i   CD manufacture year, if found (type $(basename $0) -y for info),
     n   notes about the recordings (if any);
2. -o selected output has precedence over -A aligned or -F framed modes;
3. if set together, the last among -A, -F and -N determines the output format.
4. if no pattern is given, cdd will silently quit."
}

# notes function
function notef 
{
	echo "
THE SPECIAL MARKERS AND THE SYMBOLS OF THE DATABASE
	
Usually, cd date may be the same of the cd first issue (which means the record 
is rather recent) or they differ if it's a reprint, maybe from an LP. 
CD manufacture date is so followed by a special marker:

> means no reprint date is found, but track listing is complete; e.g.: 1972>
] means a reprint date is found, and track listing is complete;  e.g.: 2005]

Bootlegs, live and original collections should report the > or ] marker.

If the issue and manufacture years differ and no special marker is present, 
the cd __does not__ feature the same track listing of the original record 
(excluding any additional bonus track). 
The output is influenced by the ']' and '>' markers; if cdd suspects that
the record hasn't got an original tracking list, or that you've typed a wrong 
year, an asterisk (*) is printed after the issue year. 

Other conventions:

v.v. means 'various years' (the record is typically a collection);
^^^^ means 'unknown data' (unavailable)" 
}

#####################################
#      MAIN 
#####################################

# trap signals using cleanup()
trap cleanup INT TERM HUP QUIT


# set defaults
sorting=a       # if no sorting is specified, the output is ordered by artist
sorttype=0      # artists numeric sort type
count=0         # number of items (both for c and C options)
view=$PLAIN     # if no view is selected, plain view is active
rev=""          # reversion of sorting is disabled by default
numbers=""      # record numbers output is disabled by default
xsearch=$FALSE  # search in a specific field is disabled by default
osearch=$FALSE  # output of a specific field is disabled by default
viewall=$FALSE  # print all records is disabled by default


# read custom data, to overwrite defaults
source custom.data


# parse double-dash options
if [ "$1" = "--help" ]; then
	helpf
	exit 0
elif [ "$1" = "--version" ]; then
	versionf
	exit 0
# takes care of null pattern.
elif [ "$1" = "" ]; then
	exit 0
fi

# parse options
while getopts ":AacCf:FhnNo:rs:x:vy" opt; do
        case $opt in
                a  ) viewall=$TRUE ;;
                A  ) view=$ALIGNED ;;
                c  ) count=$(grep $separator -c $filename)
                     echo "There are $count records in the database"
                     exit 0 ;;
                C  ) count=1 ;;
                f  ) filename=$OPTARG ;;
                F  ) view=$FORMATTED ;;
                h  ) helpf
                     exit 0 ;;
                n  ) numbers="-n"
                     if [ -z $1 ]; then
                             exit 0
                     fi ;;
		N  ) view=$PLAIN ;;
                o  ) osearch=$TRUE
                     ofield=$OPTARG ;;
                r  ) rev="-r" ;;
                s  ) sorting=$OPTARG ;;
                v  ) versionf
                     exit 0 ;;
                x  ) xsearch=$TRUE
                     xfield=$OPTARG ;;
                y  ) notef
                     exit 0 ;;
                *  ) echo && echo "$(basename $0): unrecognized option"
                     fusagef
                     exit 1 ;;
        esac
done
shift $(( $OPTIND - 1 ))

# set sorting type
case $sorting in
        a  ) sorttype=0 ;;
        r  ) sorttype=1 ;;
        y  ) sorttype=2 ;;
        i  ) sorttype=3 ;;
        n  ) sorttype=4 ;;
esac

# set pattern (with -a option, all lines will be printed)
# note: only the first pattern ($1) is kept
if [ $viewall -eq $TRUE ]; then
        pattern=$separator
else
        pattern=$(echo "$@" | tr '[A-Z]' '[a-z]')
fi

# generate tempfile with grep
if [ $view -gt $PLAIN ]; then
		# grep with no numbering (formatted views)
	    	grep "$pattern" $filename | sort $rev -f \
			-t$separator +$sorttype > $tempfile
		
else
        # increment sort column reference if numbering has been chosen
		if [ -n "$numbers" ]; then
                sorttype=$((sorttype+1))
        fi
		# grep with numbering (plain view)
		grep "$pattern" $numbers $filename | sort $rev -f -t$separator +$sorttype | tr "[:digit:]:" "[:digit:]|" > $tempfile
fi

# perform a search into a field only (-x option),
if [ $xsearch -eq $TRUE ]; then
		# determine which column for awk
		case $xfield in
				a  ) xcol=1 ;;
				r  ) xcol=2 ;;
				y  ) xcol=3 ;;
				i  ) xcol=4 ;;
				n  ) xcol=5 ;;
		esac
		# if numbering was active into grep plain view, 
		# increment awk columns
		if [ $view -eq $PLAIN ] && [ -n "$numbers" ]; then
				xcol=$((xcol+1))
		fi
        
        # search with awk for correct lines and save them into another tempfile
        # NOTE: awk treats lower and capital letters differently!
        awk -F$separator "\$$xcol ~ /$pattern/ { print \$0 }" $tempfile > ${tempfile}x

	# rewriting tempfile
        rm $tempfile
        mv ${tempfile}x $tempfile
        
fi

# show lines
if [ $osearch -eq $TRUE ]; then
        # determine which field for cut
        case $ofield in
			a  ) ocol=1 ;;
			r  ) ocol=2 ;;
			y  ) ocol=3 ;;
			i  ) ocol=4 ;;
			n  ) ocol=5 ;;
	esac
	# output of a single column (this overrides plain/formatted views)
        cat $tempfile | cut -d$separator -f$ocol | uniq
elif [ $view -eq $PLAIN ]; then
	# plain view
        tr $separator '\t' < $tempfile
else
	# formatted views (framed or aligned)
	printout < $tempfile
fi

# print count of search records
if [ $count -eq 1 ]; then
        # operate with wc upon tempfile to set count
        dummy=$(wc -l $tempfile)
        count=${dummy#/* /}
        c=$(echo $count | awk -F' ' "{print \$1}")
        if [ $c -eq 1 ]; then
                echo "$c record found."
        else
                echo "$c records found."
        fi
fi
        

# clean
rm $tempfile

# wait processes to end and exit
wait
exit 0
