#!/bin/bash

### Copyright notice at the bottom of this file ###

# $Id: note,v 1.43 2011/01/22 15:41:12 adamm Exp $
#
# $Source: /home/adamm/src/note/RCS/note,v $


export BASE=$HOME/data/note
export DATEPROG=edate
export PROG=$(basename $0)


main() {
    # GNU-style help
    if [ "x$1" = "x--help" ] || [ "x$1" = "x--man" ] ; then
	usage
	exit 0
    fi

    # Parse the command line (a la POSIX)
    optcnt=0
    while getopts ":deLlpsVv" opt ; do
	case $opt in
	\?)	printf "%s: unrecognized option: %s\n\n"\
		    $PROG $OPTARG 1>&2
		usage 1>&2
		exit 1
		;;

	e)	function=editFile
		optcnt=$(( $optcnt + 1 ))
		;;

	d)	function=deleteFiles
		optcnt=$(( $optcnt + 1 ))
		;;

	L)	function=listFiles
		optcnt=$(( $optcnt + 1 ))
		;;

	l)	function=displayFiles
		optcnt=$(( $optcnt + 1 ))
		;;

	p)	function=printFiles
		optcnt=$(( $optcnt + 1 ))
		;;

	s)	function=searchFiles
		optcnt=$(( $optcnt + 1 ))
		;;

	V)	showVarsVers
		exit 0
		;;
	esac
    done

    # Sanity check
    if [ $optcnt -gt 1 ] ; then
	printf "%s: options are mutually exclusive\n\n" $PROG 1>&2
	usage 1>&2
	exit 1
    fi

    # Get rid of the switches (and their arguments)
    shift $(($OPTIND - 1))

    # Many things are easier if we guarantee that $BASE and the
    # expected subdirectories exist, so test for them and create them
    # if they're not there
    if ! [ -d $BASE ] ; then
	initBase
    fi

    # Decide what to do based on how much is left on the command line
    # and whether we defined $function

    # No $function means start a new file -- but how?
    if ! [ "$function" ] ; then
	# Empty command line means start an editor
	if [ $# -eq 0 ] ; then
	    function=editNewFile
	else
	    # Words on the command line get stuffed into a new file
	    # (and don't bother starting an editor) -- but a single
	    # dash ("-") means read stdin and write it to a file
	    if [ "x$1" == "x-" ] ; then
		function=writeStdinToFile
	    else
		function=writeFile
	    fi
	fi
    fi

    # Call the appropriate function
    $function "$@"

    # Exit with whatever we got back
    exit $?
} # main


#
# Usage: checkMultiple string
#
checkMultiple() {
    if expr "$1" : '.* .*' > /dev/null ; then
	printf \
	    "%s: file '%s' found in multiple directories:\n" \
	    $PROG $dfArg 1>&2
	echo "$1" |
	tr '\040' '\012' |
	sed 's/^/    /' 1>&2
	return 1
    else
	return 0
    fi
} # checkMultiple


#
# Usage: deleteFiles file [...]
#
deleteFiles() {
    dfErr=0

    for dfArg in "$@" ; do
	dfFile=$(getPath "$dfArg")
	if ! checkMultiple "$dfFile" ; then
	    dfErr=1
	    continue
	fi

	if [ "$dfFile" == "" ] ; then
	    printf "%s: file not found: '%s'\n" $PROG "$dfArg" 1>&2
	    dfErr=1
	    continue
	fi

	rm $dfFile
	if [ $? -ne 0 ] ; then
	    dfErr=1
	fi
    done

    return $dfErr
} # deleteFiles


#
# Usage: displayFiles
#
displayFiles() {
    listFiles |
    xargs awk '
	FNR == 1 {
	    f = FILENAME
	    sub(/.*\//, "", f)
	    printf("%s:%s\n", f, $0)
	}' |
    sort
} # displayFiles


#
# Usage: editFile file [...]
#
editFile() {
    efErr=0

    efList=""
    for efArg in "$@" ; do
	efFile=$(getPath "$efArg")
	if ! checkMultiple "$efFile" ; then
	    efErr=1
	    continue
	fi

	if [ "$efFile" == "" ] ; then
	    printf "%s: file not found: '%s'\n" $PROG "$efArg" 1>&2
	    efErr=1
	    continue
	fi

	efList="$efList $efFile"
    done

    if [ "$efList" ] ; then
	${EDITOR:-vi} $efList
    fi

    return $efErr
} # editFile


#
# Usage: editNewFile
#
editNewFile() {
    while true ; do
	enfFile=$(newNoteFile)
	if ! [ -f $enfFile ] ; then
	    break
	else
	    printf "%s: sleeping . . .\n" $PROG
	    sleep 1
	fi
    done

    ${EDITOR:-vi} $enfFile
    enfErr=$?
    if [ -f $enfFile ] ; then
	printf "wrote %s\n" $(basename $enfFile)
    fi

    return $enfErr
} # editNewFile


#
# Usage: getPath file
#
getPath() {
    # Find files, convert \n to space, and zap the trailing space
    find $BASE -name "$1" -print |
    tr '\012' '\040' |
    sed 's/ $//'
} # getPath


#
# Usage: initBase
#
initBase() {
    printf "%s: initializing . . .\n" $PROG
    mkdir -p $BASE
    for ibDay in $(seq 0 6) ; do
	for ibSec in $(seq -w 0 59) ; do
	    mkdir -p $BASE/$ibDay/$ibSec
	done
    done
} # initBase


#
# Usage: listFiles
#
listFiles() {
    printf "finding . . .\n" 1>&2
    find $BASE -type f -name 'n.??????' -print |
    sort
} # listFiles


#
# Usage: newNoteFile
#
newNoteFile() {
    nnfFile=$(
	printf \
	    "%s/%s/%s/n.%s"  \
	    "$BASE" $($DATEPROG '+%w %S') \
	    $(baseconv -i 10 -o 62 $($DATEPROG '+%s'))
    )

    echo $nnfFile
} # newNoteFile


#
# Usage: printFiles file [...]
#
printFiles() {
    pfErr=0

    for pfArg in "$@" ; do
	pfFile=$(getPath "$pfArg")
	if ! checkMultiple "$pfFile" ; then
	    pfErr=1
	    continue
	fi

	if [ "$pfFile" == "" ] ; then
	    printf "%s: file not found: '%s'\n" $PROG "$pfArg" 1>&2
	    pfErr=1
	    continue
	fi

	cat $pfFile
	if [ $? -ne 0 ] ; then
	    pfErr=1
	fi
    done

    return $pfErr
} # printFiles


#
# Usage: searchFiles pattern [...]
#
searchFiles() {
    sfPattern=$1
    shift
    for arg in "$@" ; do
	sfPattern="$sfPattern|$arg"
    done

    sfTmpFile=$(mkstemp $(pwd)/note.XXXXXX)
    printf "xargs egrep -i '%s' /dev/null\n" "$sfPattern" > $sfTmpFile

    listFiles |
    sh $sfTmpFile |
    sed 's,^/.*/\(n\.......\):,\1:,' |
    sort
    ret=$?

    rm -f $sfTmpFile
    return $ret
} # searchFiles


#
# Usage: showVarsVers
#
showVarsVers() {
    printf "BASE = $BASE\n"

    echo '$Id: note,v 1.43 2011/01/22 15:41:12 adamm Exp $' |
    awk '{
	sub(/,v/, "", $2)
	printf("\n%s v%s, %s %s UTC\n", $2, $3, $4, $5)
    }'
} # showVarsVers


#
# Usage: writeFile word [...]
#
writeFile() {
    while true ; do
	wfFile=$(newNoteFile)
	if ! [ -f $wfFile ] ; then
	    break
	else
	    printf "%s: sleeping . . .\n" $PROG
	    sleep 1
	fi
    done

    echo "$@" > $wfFile
    wfErr=$?

    printf "wrote %s\n" $(basename $wfFile)
    return $wfErr
} # writeFile


#
# Usage: writeStdinToFile
#
writeStdinToFile() {
    while true ; do
	wstfFile=$(newNoteFile)
	if ! [ -f $wstfFile ] ; then
	    break
	else
	    printf "%s: sleeping . . .\n" $PROG
	    sleep 1
	fi
    done

    cat - > $wstfFile
    wstfErr=$?

    printf "wrote %s\n" $(basename $wstfFile)
    return $wstfErr
} # writeStdinToFile


#
# Usage: usage
#
usage() {
    cat - <<__EOF
Usage:
    $PROG
        interactive entry

    $PROG -
	write stdin to a new file

    $PROG [word ...]
        write word ... to a new file

    $PROG --help | --man
        show this text

    $PROG -d file [...]
        delete one or more files

    $PROG -e file [...]
        edit one or more existing files

    $PROG -l
        list files and first lines

    $PROG -L
        list file names and paths

    $PROG -p file [...]
        print one or more files

    $PROG -s pattern [...]
        search for pattern(s)

    $PROG -V
        show internal variables and version info
__EOF
} # usage


main "$@"


###
### Copyright, 2009, Adam Moskowitz. All rights reserved.
###
### Any redistribution of this software must retain the above copyright
### notice, this list of conditions, and the following disclaimer.
###
### Without specific prior written permission from the copyright holder,
### you may not charge a fee for the redistribution of this software.
###
### All other redistribution and use is hereby permitted.
###
### This software is provided "as is" and without any express or implied
### warranties, including, without limitation, the implied warranties of
### merchantability and fitness for a particular purpose.
###
