#!/usr/bin/perl
# $Author: thomas $ $Id: note,v 1.19 2000/04/30 16:07:23 thomas Exp thomas $ $Revision: 1.19 $
# 
# $Log: note,v $
# Revision 1.19  2000/04/30 16:07:23  thomas
# *** empty log message ***
#
# Revision 1.18  2000/04/30 14:58:21  thomas
# updated the usage and help subs
#
# Revision 1.17  2000/04/30 14:44:38  thomas
# added colors to the tree functions
#
# Revision 1.16  2000/04/30 14:28:38  thomas
# added the t command, which displays a topic-tree.
# and enhanced the list command in interactive mode
#
# Revision 1.15  2000/03/19 23:41:04  thomas
# changed set_del, now no extra TEMP file is required!
# instead I get it from $this->get_all() !
#
# Revision 1.14  2000/03/19 22:51:49  thomas
# Bug in NOTEDB::binary fixed, recount of nubers was
# incorrect.
#
# Revision 1.13  2000/03/19 11:53:32  thomas
# edit bug fixed (ude => uen)
#
# Revision 1.12  2000/03/19 03:06:51  thomas
# backend support completed.
# mysql and binary backends now excluded in separate files
#
# Revision 1.11  2000/03/18 00:16:47  thomas
# added NOTEDB::mysql and changed note to work with that.
# thus, from now on there is only one script to maintain and
# it is possible to provide more bacjends as well as making
# additional scripts upon them, i.e. cgi script...
#
# Revision 1.8  2000/03/13 22:48:43  thomas
# small width bug fixed
#
# Revision 1.7  2000/03/08 23:11:19  tom
# added cd
#
# Revision 1.6  2000/03/08 22:50:41  tom
# Added the $KEEP_TIMESTAMP option and fixed a bug regarding topic names
# and invalid resolution of them in case it started with "1 name".
#
# Revision 1.5  2000/02/25 20:59:30  tom
# corrected small timestamp problem in &edit and &new
#
# Revision 1.4  2000/02/25 13:24:11  tom
# fixed a small bug, that caused to use the last line for a note title instead the 2nd.
#
# Revision 1.3  2000/02/25 11:28:53  tom
# all changes from bin version applied to sql version
#
# Revision 1.2  2000/02/25 10:30:06  tom
# *** empty log message ***
#
#
# this is the small console program "note" (MYSQL version)
# It works similar to some well known GUI note programs,
# but instead of using X11 it uses the UN*X console.
# You can edit existing notes, delete them, create new
# ones and, of course display them.
# The notes will be stored in a mysql database. Refer to
# the README of the desitribution for details about 
# installation.
# It requires a configfile named .noterc in the users home.
# If it does not exist, note will create one for you, which
# you will have to edit.
#
# If you find it usefull or find a bug, please let me know:
# Thomas Linden <tom@daemon.de>
#
# note is GPL software.

use strict;
use Data::Dumper;

sub usage;
sub find_editor;
sub output;
sub C;
sub uen;
sub ude;
sub num_bereich;
sub getdate;

sub new;
sub edit;
sub del;
sub display;
sub list;
sub help;
sub import;
sub display_tree;
sub tree;

my (
        $maxlen, $timelen, $TOPIC, $TYPE, $mode, $NOTEDB, $NoteKey, 
        $version, $number, $CurTopic, $CurDepth, $PATH, $CONF, $WantTopic,
        $sizeof, $MAX_TIME, $PreferredEditor, %TP, $TopicSep,
        $TreeType, $ListType, $searchstring, $dump_file, $ALWAYS_INT, $KEEP_TIMESTAMP,
        $BORDERC, $BORDER_COLOR, $_BORDERC, $NOTEC, $NOTE_COLOR,
        $NUMC, $NUM_COLOR, $_NUMC, $_NOTEC, $TIMEC, $TIME_COLOR,
        $_TIMEC, $TOPICC, $TOPIC_COLOR, $_TOPICC, $SetTitle, $COLOR,
        $typedef, $MAX_NOTE, $MAX_TIME, @NumBlock, $ALWAYS_EDIT, $HOME,
	$db, $dbname, $dbhost, $DEFAULTDBNAME, $dbuser, $USER, $dbpasswd,
	$table, $fnum, $fnote, $fdate, $date, $dbdriver, $libpath, $db,
	$USE_CRYPT, $CRYPT_METHOD, $key
   );

####################################################################
# DEFAULTS, allows one to use note without a config ################
# don't change them, instead use the config file!   ################
####################################################################
$maxlen = 30;
$timelen = 22;
$date = &getdate;
$USER = getlogin || getpwuid($<);
chomp $USER;
$HOME = $ENV{'HOME'};
$CONF = $HOME . "/.noterc";
$dbdriver = "binary";
$libpath = "/usr/local/lib";
$NOTEDB = $HOME . "/.notedb";
$MAX_NOTE = 4096;
$MAX_TIME = 64;
$COLOR = "YES";
$BORDER_COLOR = "BLACK";
$NUM_COLOR    = "blue";
$NOTE_COLOR   = "green";
$TIME_COLOR   = "black";
$TOPIC_COLOR = "BLACK";
$TOPIC = 1;
$TopicSep = '/';
$version = "1.0.2";
if($TOPIC)
{
    $CurDepth = 1; # the current depth inside the topic "directory" structure...
}
$USE_CRYPT = "NO";
####################################################################

# process command line args
if($ARGV[0] eq "")
{
        $mode = "new";
}
else
{
  while($ARGV[0] ne "" )
  {
        if($ARGV[0] =~ /^\d/)
        {
                # first arg is a digit!
                $number = $ARGV[0];
                if($mode eq "")
                {
                        # change mode only, if started with an option 
                        # ($mode will be already set)
                        $mode = "display";
                }
                $ARGV[0] = "";
        }
	elsif($ARGV[0] eq "-i" || $ARGV[0] eq "--interactive")
	{
		$mode = "interactive";
		$ARGV[0] = "";
	}
	elsif($ARGV[0] eq "-t" || $ARGV[0] eq "--tree")
	{
		$mode = "tree";
		$ARGV[0] = "";
	}
        elsif($ARGV[0] eq "-T" || $ARGV[0] eq "--longtree")
        {
                $mode = "tree";
		$TreeType = "LONG";
                $ARGV[0] = "";
        }
        elsif($ARGV[0] eq "-l" || $ARGV[0] eq "--list")
        {
                $mode = "list";
                my @ArgTopics = split /$TopicSep/,$ARGV[1];
                $CurDepth += $#ArgTopics + 1 if $ARGV[1];
                $CurTopic = $ArgTopics[$#ArgTopics]; # use the last element everytime...
                $ARGV[0] = "";
        }
	elsif($ARGV[0] eq "-L" || $ARGV[0] eq "--longlist")
	{
		$mode = "list";
		$ListType = "LONG";
		$CurTopic = $ARGV[1];
       		$ARGV[0] = "";
	}
        elsif($ARGV[0] eq "-s" || $ARGV[0] eq "--search")
        {
                # searching
                $mode = "search";
                $searchstring = $ARGV[1];
                $ARGV[0] = "";
        }
        elsif($ARGV[0] eq "-e" || $ARGV[0] eq "--edit")
        {
                if($mode eq "edit")
                {
                        # note -e -e !
                        &usage;
                        exit(1);
                }
                else
                {
                        $mode = "edit";
                        shift;
                }
        }
        elsif($ARGV[0] eq "-d" || $ARGV[0] eq "--delete")
        {
                if($mode eq "delete")
                {
                        &usage;
                        exit(1);
                }
                else
                {
                        $mode = "delete";
                        shift;
                }
        }
	elsif($ARGV[0] eq "-D" || $ARGV[0] eq "--Dump" || $ARGV[0] eq "--dump")
	{
		$mode = "dump";
		$dump_file = $ARGV[1];
		$ARGV[0] = "";
		if($dump_file eq "")
		{
			$dump_file = "note.dump.$$";
			print "no dumpfile specified, using $dump_file.\n";
		}
	}
	elsif($ARGV[0] eq "-I" || $ARGV[0] eq "--Import" || $ARGV[0] eq "--import")
        {
                $mode = "import";
                $dump_file = $ARGV[1];
                $ARGV[0] = "";
                if($dump_file eq "")
                {
                        print "No dumpfile specified.\n";
                        exit(1);
                }
        }
        elsif($ARGV[0] eq "-v" || $ARGV[0] eq "--version")
        {
                print "This is note $version by Thomas Linden <tom\@daemon.de>.\n";
                exit(0);
        }
        elsif($ARGV[0] eq "-h" || $ARGV[0] eq "--help")
        {
                &usage;
                exit(0);
        }
        else
        {
                &usage;
        		exit(0);
        }
  }
}

# open the configfile.



if(-e $CONF)
{
	eval `cat $CONF`;
}


# Always interactive?
if($ALWAYS_INT eq "YES" && $mode ne "dump" && $mode ne "import")
{
	$mode = "interactive";
}


# *if* loading of the config was successful, try to load the
# configured database backend. Currently supported: mysql and binary.
push @INC, $libpath;
if($dbdriver eq "mysql") {
	eval {
		require NOTEDB::mysql;
		$db = new NOTEDB($dbdriver, $dbname, $dbhost, $dbuser, $dbpasswd, $table, $fnum, $fnote, $fdate);
	}
}
elsif($dbdriver eq "binary") {
	eval {
		require NOTEDB::binary;
		$db = new NOTEDB($dbdriver, $NOTEDB, $MAX_NOTE, $MAX_TIME, $dbdriver);
	}
}
else {
	print "Unsupported database backend: NOTEDB::$dbdriver!\n";
	exit 1;
}
if($@) {
        print "backend-error: " . $@;
        exit 1;
}

# add the backend version to the note version:
$version .= " " . $db->version();

# calculate some constants...
$BORDERC  = "<$BORDER_COLOR>";
$_BORDERC = "</$BORDER_COLOR>";
$NUMC     = "<$NUM_COLOR>";
$_NUMC    = "</$NUM_COLOR>";
$NOTEC    = "<$NOTE_COLOR>";
$_NOTEC   = "</$NOTE_COLOR>";
$TIMEC    = "<$TIME_COLOR>";
$_TIMEC   = "</$TIME_COLOR>";
$TOPICC   = "<$TOPIC_COLOR>";
$_TOPICC  = "</$TOPIC_COLOR>";

$NoteKey  = $TopicSep . "notes" . $TopicSep;


if($ListType ne "LONG" && $mode ne "interactive")
{
     $maxlen += $timelen; # no time will be displayed!
}


# check if the user wants to use encryption:
if($USE_CRYPT eq "YES" && $NOTEDB::crypt_supported == 1) {
	if($CRYPT_METHOD eq "") {
		$CRYPT_METHOD = "Crypt::IDEA";
	}
	print "password: ";
	eval {
	    local($|) = 1;
	    local(*TTY);
	    open(TTY,"/dev/tty");
	    system ("stty -echo </dev/tty");
	    chomp($key = <TTY>);
	    print STDERR "\r\n";
	    system ("stty echo </dev/tty");
	    close(TTY);
	};
	if($@) {
		$key = <>;
	}
	chomp $key;
	$db->use_crypt($key,$CRYPT_METHOD);
	undef $key;
	# verify correctness of passwd
	my ($note, $date) = $db->get_single(1);	
	if($date ne "") {
		if($date !~ /^\d+\.\d+?/) {
			print "access denied.\n"; # decrypted $date is not a number!
			exit(1);
		}
	} #else empty!
}
else {
	$db->no_crypt;
	# does: NOTEDB::crypt_supported = 0;
}

# main loop: ###############
if($mode eq "display")
{
	&display;
}
elsif($mode eq "search")
{
	&search;
}
elsif($mode eq "list")
{
	&list;
}
elsif($mode eq "tree")
{
        &display_tree;
}
elsif($mode eq "new")
{
	&new;
}
elsif($mode eq "delete")
{
	del;
}
elsif($mode eq "edit")
{
	&edit;
}
elsif($mode eq "dump")
{
	&dump;
}
elsif($mode eq "import")
{
	&import;
}
elsif($mode eq "interactive")
{
	&interactive;
}
else
{
        #undefined :-(
}


exit(0);
################## EOP ################



############################### DISPLAY ##################################
sub display
{
    my($N,$match,$note,$date,$num);
	# display a certain note
	print "\n";
	&num_bereich; # get @NumBlock from $numer
	foreach $N (@NumBlock)
	{
		($note, $date) = $db->get_single($N);
		if($note)
		{
			output($N, $note, $date, "SINGLE");
			print "\n";		
			$match = 1;
		}
	}
	if(!$match)
	{
		print "no note with that number found!\n";
	}
}
############################### SEARCH ##################################
sub search
{
    my($n,$match,$note,$date,$num,%res);
	$maxlen += $timelen;
	if($searchstring eq "")
	{
		&usage;
		exit(1);
	}
	print "searching the database $dbname for \"$searchstring\"...\n\n";

	%res = $db->get_search($searchstring);	

    	foreach $num (sort { $a <=> $b } keys %res)
    	{
		output($num, $res{$num}->{'note'}, $res{$num}->{'date'});
    		$match = 1;
    	}
	
	
	if(!$match)
    	{
     	   print "no matching note found!\n";
     	}	
	print "\n";
}


############################### LIST ##################################
sub list
{
	my(@topic,@RealTopic, $i,$t,$n,$num,@CurItem,$top,$in,%res);
	if($mode ne "interactive")
        {
                print "List of all existing notes:\n\n";
        }

	# list all available notes (number and firstline)
	%res = $db->get_all();

	if($TOPIC)
        {
                undef %TP;
        }
    	
	foreach $num (sort { $a <=> $b } keys %res)
    	{
		$n = $res{$num}->{'note'}; 
		$t = $res{$num}->{'date'};
		if($TOPIC)
                {
                        # this allows us to have multiple topics (subtopics!)
                        my ($firstline,$dummy) = split /\n/, $n, 2;
                        if($firstline =~ /^($TopicSep)/)
                        {
                                @topic = split(/$TopicSep/,$firstline);
                        }
                        else
                        {
                                @topic = ();
                        }
                        # looks like: "\topic\"
                        # collect a list of topics under the current topic
                        if($topic[$CurDepth-1] eq $CurTopic && $topic[$CurDepth] ne "")
                        {
                                if(exists $TP{$topic[$CurDepth]})
                                {
                                        $TP{$topic[$CurDepth]}++;
                                }
                                else
                                {
                                        # only if the next item *is* a topic!
                                        $TP{$topic[$CurDepth]} = 1 if(($CurDepth) <= $#topic);
                                }
                        }
                        elsif($topic[$CurDepth-1] eq $CurTopic || ($topic[$CurDepth] eq "" && $CurDepth ==1))
                        {
                                # cut the topic off the note-text
                                if($n =~ /^($TopicSep)/)
                                {
                                        $CurItem[$i]->{'note'} = $dummy;
                                }
				else
				{
				        $CurItem[$i]->{'note'} = $n;
				}
                                # save for later output() call
                                $CurItem[$i]->{'num'}  = $num;
                                $CurItem[$i]->{'time'} = $t;
                                $i++;
                                # use this note for building the $PATH!
                                if($RealTopic[0] eq "")
                                {
                                    @RealTopic = @topic;
                                }
                        }
               	}
                else
                {
                        output($num, $n, $t);
                }
        }	
        if($TOPIC)
        {
                if($CurTopic ne "")
                {
                    undef $PATH;
                    foreach (@RealTopic)
                    {
                        $PATH .= $_ . $TopicSep;
                        last if($_ eq $CurTopic);
                    }
                }
                else
                {
                    $PATH = $TopicSep;
                }

                # we are at top level, print a list of topics...
                foreach $top (sort(keys %TP))
                {
                        output("-", " => ". $top . "$TopicSep ($TP{$top} notes)",
                                " Sub Topic         ");
                }
		#print Dumper(@CurItem);
                for($in=0;$in<$i;$in++)
                {
                        output( $CurItem[$in]->{'num'},
                                $CurItem[$in]->{'note'},
                                $CurItem[$in]->{'time'} );
                }
        }
	
	print "\n";
}

############################### NEW ##################################
sub new
{
    my($TEMP,$editor, $date, $note, $WARN, $c, $line, $num, @topic);
	$date = &getdate;
	if($ALWAYS_EDIT eq "YES")
	{
		$TEMP = "/tmp/note.$$";
		# let the user edit it...
	    	$editor = &find_editor;
        	if($editor)
        	{
        	        system $editor, $TEMP;
        	}
        	else
        	{
        	        print "Could not find an editor to use!\n";
        	        exit(0);
        	}
    		# read it in ($note)
    		$note = "";
		open E, "<$TEMP" or $WARN = 1;
		if($WARN)
		{
			print "...edit process interupted! No note has been saved.\n"; 
			undef $WARN;
			return;
		}
    		$c = 0;
    		while(<E>)
    		{
    		    	$note = $note . $_;
    		}
    		chomp $note;
    		close E;
   		# privacy!
		unlink $TEMP;
	}
	else
	{
		$note = "";
		$line = "";
		# create a new note
    		print "enter the text of the note, end with .\n";
		do
    		{
    		      $line = <STDIN>;
    		      $note = $note . $line;
    		} until $line eq ".\n";
		# remove the . !
    		chop $note;
    		chop $note;
	}
	
	# since we have not number, look for the next available:
	$number = $db->get_nextnum();
	if($TOPIC && $CurTopic ne "")
        {
                @topic = split(/$TopicSep/,$note);
                if($topic[1] eq "")
                {
                        $note = $PATH . "\n$note";
                }
        }


	$db->set_new($number,$note,$date);

	# everything ok until here!
	print "note stored. it has been assigned the number $number.\n\n";
}


############################### DELETE ##################################
sub del
{
    	my($i,@count, $setnum, $pos, $ERR);
	# delete a note
	&num_bereich; # get @NumBlock from $number
	foreach $_ (@NumBlock)
	{
		$ERR = $db->set_del($_);
		if($ERR)
		{
			print "no note with number $_ found!\n";
		}
		else
		{
			print "note number $_ has been deleted.\n";
		}
	}
	# recount the notenumbers:
	$db->set_recountnums();

	@NumBlock = ();
}

############################### EDIT ##################################
sub edit
{
    my($keeptime, $date, $editor, $TEMP, $note, $t, $num, $match);
	# edit a note
	$date = &getdate;
	($note, $keeptime) = $db->get_single($number);
	if($keeptime eq "")
    	{
		print "no note with that number found!\n\n";
		exit(0) if($mode ne "interactive");
    	}	
	$TEMP = "/tmp/note.$USER.$$";
	open NOTE,">$TEMP" or die "Could not open $TEMP\n";
	select NOTE;

	print $note;
	close NOTE;
	select STDOUT;
	$editor = &find_editor;
	if($editor)
	{
		system $editor, $TEMP;
	}
	else
	{
		print "Could not find an editor to use!\n";
		exit(0);
	}
	$note = "";
	open NOTE,"<$TEMP" or die "Could not open $TEMP\n";
	
	while(<NOTE>)
	{
		$note = $note . $_;
	}
	chomp $note;
	close NOTE;

	unlink $TEMP;

	if($KEEP_TIMESTAMP eq "YES")
	{
	    $t = $keeptime;
	}
	else
	{
	    $t = $date;
	}
	
	# we got it, now save to db
	$db->set_edit($number, $note, $t);

	print "note number $number has been changed.\n";
}


sub dump
{
    my(%res, $num);
	# $dump_file
	open (DUMP, ">$dump_file") or die "could not open $dump_file\n";
	select DUMP;
    	%res = $db->get_all();	
    	foreach $num (sort { $a <=> $b } keys %res)
    	{
		print STDOUT "dumping note number $num to $dump_file\n";
                print "Number: $num\n"
		      ."Timestamp: $res{$num}->{'date'}\n"
		      ."$res{$num}->{'note'}\n";
    	}
        print "\n";
	close(DUMP);
	select STDOUT;
}

sub import
{
    my($num, $start, $complete, $dummi, $note, $date, $time, $number);
	# open $dump_file and import it into the notedb
	open (DUMP, "<$dump_file") or die "could not open $dump_file\n";
	$complete=0;
	$start = 0;
	while(<DUMP>)
	{
		chomp $_;
		if($_ =~ /^Number:\s\d+/)
		{
			if($start == 0)
			{
				# we have no previous record
				($dummi,$number) = split(/\s/,$_);
				$start = 1;
			}
			else
			{
				# we got a complete record, save it!
				$number = $db->get_nextnum();
				$db->set_new($number,$note, $date);
				print "note number $number from $dump_file inserted into notedb.\n";
				$complete = 0;  
				$note = "";    
				$date = "";   
				($dummi,$number) = split(/\s/,$_);
			}	
		}
		elsif($_ =~ /^Timestamp:\s\d+/ && $complete == 0)
		{
			($dummi,$date,$time) = split(/\s/,$_);
			$date = "$date $time";
			$complete = 1;
		}
		else
		{
			$note .= $_ . "\n";
		}
	}
	if($note ne "" && $date ne "")
        {
		# the last record, if existent
		$number = $db->get_nextnum();
		$db->set_new($number,$note, $date);
                print "note number $number from $dump_file inserted into notedb.\n";
        }
}



sub interactive
{
    	#$|=1;
	#my $term = new Term::ReadLine('');
	#my $OUT = $term->OUT || *STDOUT;
	#my $term->MinLine(undef);
	#my $attribs = $term->Attribs;
	#$term->bind_key(ord "\cc", 'abort');
        my($maxlen_save, $B, $BB, $menu, $char, @LastTopic);
	$maxlen_save = $maxlen;
	# create menu:
	$B = "<blackI>";
	$BB = "</blackI>";
	$menu = 	"[" .  $B . "L" . $BB . " List ";
	if($TOPIC) {
		$menu .= $B . "T" . $BB . " Topics ";
	}
	$menu	.= $B . "N" . $BB . " New "
		. $B . "D" . $BB . " Delete "
		. $B . "S" . $BB . " Search "
		. $B . "E" . $BB . " Edit "
		. $B . "?" . $BB . " Help "
		. $B . "Q" . $BB . " Quit] "; # $CurTopic will be empty if $TOPIC is off!
	# per default let's list all the stuff:
	# Initially do a list command!
	$maxlen += $timelen;
	print "\n";
	&list;
	undef $SetTitle;
	for(;;)
	{
		$ListType = "";
		$maxlen = $maxlen_save;
		if($CurDepth > 2)
                {
                    print C $menu . $TOPICC . "../" . $CurTopic . $_TOPICC . ">";
                }
                else
                {
                    print C $menu . $TOPICC . $CurTopic . $_TOPICC . ">";
                }
	
		# endless until user press "Q" or "q"!
		$char = <STDIN>;
		#$char = $term->readline('');
		chomp $char;
		if($char =~ /^\d+\s*[\di*?,*?\-*?]*$/)
		{
			# display notes
			$maxlen += $timelen;
			$number = $char;
			&display;
			undef $SetTitle;
		}
		elsif($char =~ /^n$/i)
		{
			# create a new one
			&new;
		}
		elsif($char =~ /^l$/ || $char =~ /^$/)
		{
			# list
			print "\n";
			$ListType = "";
			$maxlen += $timelen;
			&list;
			undef $SetTitle;
		}
		elsif($char =~ /^L$/)
		{
			$ListType = "LONG";
			print "\n";
			&list;
			undef $SetTitle;
		}
		elsif($char =~ /^h$/i || $char =~ /^\?/)
		{
			# zu dumm der Mensch ;-)
			&help;
		}
		elsif($char =~ /^d\s+([\d*?,*?\-*?]*)$/i)
		{
			# delete one!
			$number = $1;
			&del;
		}
		elsif($char =~ /^d$/i)
		{
			# we have to ask her:
			print "enter number(s) of note(s) you want to delete: ";
			$char = <STDIN>;
			chomp $char;
			$number = $char;
			&del;
		}
		elsif($char =~ /^e\s+(\d+\-*\,*\d*)/i)
		{
			# edit one!
			$number = $1;
			&edit;
		}
		elsif($char =~ /^e$/i)
		{
			# we have to ask her:
			print "enter number of the note you want to edit: ";
			$char = <STDIN>;
			chomp $char;
			$number = $char;
			&edit;
		}
		elsif($char =~ /^s\s+/i)
		{
			# she want's to search
			$searchstring = $';
			chomp $searchstring;
			&search;
		}
		elsif($char =~ /^s$/i)
		{
			# we have to ask her:
			print "enter the string you want to search for: ";
			$char = <STDIN>;
			chomp $char;
			$char =~ s/^\n//;
			$searchstring = $char;
			&search;
		}
		elsif($char =~ /^q$/i)
		{
			# schade!!!
			print "\n\ngood bye\n";
			exit(0);
		}
		elsif($char =~ /^t$/)
		{
			&display_tree;
		}
		elsif($char =~ /^T$/)
		{
			$TreeType = "LONG";
			&display_tree;
			$TreeType = "";
		}
                elsif($char =~ /^\.\.$/ || $char =~ /^cd\s*\.\.$/)
                {
			$CurDepth-- if ($CurDepth > 1);
                        $CurTopic = $LastTopic[$CurDepth];
                        $maxlen += $timelen;
                        print "\n";
                        &list;
                        undef $SetTitle;
                }
		elsif($char =~ /^l\s+(\w+)$/)
                {
                        # list
			$WantTopic = $1;
			if(exists $TP{$WantTopic}) 
			{
				my %SaveTP = %TP;
				$LastTopic[$CurDepth] = $CurTopic;
				$CurTopic = $1;
				$CurDepth++;
                        	print "\n";
                        	$ListType = "";
                        	$maxlen += $timelen;
                        	&list;
                        	undef $SetTitle;
				$CurTopic = $LastTopic[$CurDepth];
				$CurDepth--;
				%TP = %SaveTP;
			}
			else
                        {
                                print "\nunknown command!\n";
                        }
                }
		else
		{
			# unknown
			my $unchar = $char;
			$unchar =~ s/^cd //; # you may use cd <topic> now!
			if(exists $TP{$char} || exists $TP{$unchar})
                        {
			        $char = $unchar if(exists $TP{$unchar});
                                $LastTopic[$CurDepth] = $CurTopic;
                                $CurTopic = $char;
                                $maxlen += $timelen;
                                $CurDepth++;
                                print "\n";
                                &list;
                                undef $SetTitle;
                        }
                        else
                        {
                                print "\nunknown command!\n";
                        }
			undef $unchar;
		}
	}
}



sub usage
{
print qq~This is the program note $version by Thomas Linden (c) 1999-2000.
It comes with absolutely NO WARRANTY. It is distributed under the
terms of the GNU General Public License. Use it at your own risk :-)
Usage: 		note [-i | --interactive] | [ options ] [ number [,number...]]
Options:
	-h	--help			displays this help screen
	-v	--version		displays the version number
	-l	--list [<topic>]	lists all existing notes If no topic were specified,
					it will display a list of all existing topics.
	-L	--longlist [<topic>]	the same as -l but prints also the timestamp
	-t	--topic			prints a list of all topics as a tree.
	-T	--longtopc		prints the topic-tree with the notes under each topic
	-s	--search <string>	searches for <string> trough the notes database
	-e	--edit <number>		edit note with <number>
	-d	--delete <number>	delete note with <number>
	-D	--Dump [<file>]		dumps the notes to the textfile <file>
	-I	--Import <file>		imports a previously dumped textfile into the
					note-database. Dumps from the mysql and the binary
					version are in the same format.
	-i	--interactive		interactive mode

 o if you specify only a number (i.e. "note 4"), then the note with that
   number will be displayed.
 o you can specify more then one number for delete and display, for example:
   "note -d 3,4" deletes #3 and #4. "note 5-7" displays #5, #6 and #7.
 o if you run note without any parameter and if \$ALWAYS_INT in the config is
   not set, then note will create a new note and prompt you for new text.
 o If it finds \~/.noterc, it will process it. Refer to the manpage for more
   informations about the configuration.
 o In interactive mode you can get help at any time by typing "?" or "h" at
   the prompt.
~;
  exit 1;
}
sub find_editor {
   return $PreferredEditor || $ENV{"VISUAL"} || $ENV{"EDITOR"} || "vim" || "vi" || "pico";
}
#/
sub output
{
	my($SSS, $LINE, $num, $note, $time, $TYPE, $L, $LONGSPC, $R, $PathLen, $SP, $title, $CUTSPACE,
	       $len, $diff, $Space, $nlen);
	($num, $note, $time, $TYPE) = @_;
	if($ListType ne "LONG")
	{
		$SSS = "-" x ($maxlen + 31 - $timelen);
	}
	else
	{
		$SSS = "-" x ($maxlen + 31);
	}
	$nlen = length("$num");
	$LINE = "$BORDERC $SSS $_BORDERC\n";
	$L = $BORDERC . "[" . $_BORDERC;
	$LONGSPC = " " x (26 - $nlen);
        $R = $BORDERC . "]" . $_BORDERC;
	$PathLen = length($PATH); # will be ZERO, if not in TOPIC mode!
	if($TYPE ne "SINGLE")
	{
		if(!$SetTitle) 
		{
			$SP = "";
			# print only if it is the first line!
			if($ListType ne "LONG")
			{
				$SP = " " x ($maxlen-2 - $timelen - $PathLen);
                	}
			else
			{
				$SP = " " x ($maxlen-2 - $PathLen);
			}
			print C $LINE;
		
			print C "$L $NUMC#$_NUMC  ";
			if($ListType eq "LONG")
			{
				print C " $TIMEC" . "creation date$_TIMEC           ";
			}
			else
			{
				print $LONGSPC;
			}
                        if($TOPIC)
                        {
                            print C $TOPICC . "$PATH    $_TOPICC$SP$R\n";
                        }
                        else
                        {
                            print C $NOTEC . "note$_NOTEC$SP$R\n";
                        }

                	print C $LINE;
			$SetTitle = 1;
		}
		$title = "";
		$CUTSPACE = " " x $maxlen;
		$note =~ s/\n/$CUTSPACE/g;
        	$len   = length($note);
        	if($len < ($maxlen - 2 - $nlen))
        	{
        	    $diff = $maxlen - $len;
		    $Space = " " x $diff;	
                    if($num eq "-")
                    {
                        $title = $BORDERC . $TOPICC . "\"" . $note . "\"" . $_TOPICC . $Space . "$_BORDERC";
                    }
                    else
                    {
                        $title = $BORDERC . $NOTEC . "\"" . $note . "\"" . $_NOTEC . $Space . "$_BORDERC";
                    }

        	}
		else
        	{
        	        $title = substr($note,0,($maxlen - 2 - $nlen));
        	        $title = $BORDERC . $NOTEC . "\"" . $title . "...\"$_NOTEC$_BORDERC";
        	}
		# $title should now look as: "A sample note                       "
       		print C "$L $NUMC$num$_NUMC $R";
		if($ListType eq "LONG")
		{
			print C "$L$TIMEC" . $time . " $_TIMEC$R"; 
		}
		print C "$L $NOTEC" . $title . "$_NOTEC $R\n";


		print C $LINE;
	}
	else
	{
		chomp $note;
		$Space = " " x ($maxlen - 16);
		$SP = " " x ($maxlen + 13);
		#print C $LINE;
		#print C "$L $NUMC#$_NUMC   " . $TIMEC . "creation date$_TIMEC$SP$R\n";
		print C $LINE;
		print C "$L $NUMC$num$_NUMC $R$L$TIMEC$time$_TIMEC $Space$R\n";
		print C $LINE;
		print C $NOTEC . $note . $_NOTEC . "\n";
		print C $LINE;
	}
	
}



sub C
{
    my(%Color, $default, $S, $Col, $NC, $T);
        # \033[1m%30s\033[0m    
        %Color = (        'black'         => '0;30',
                                'red'           => '0;31',
                                'green'         => '0;32',
                                'yellow'        => '0;33',
                                'blue'          => '0;34',
                                'magenta'       => '0;35',
                                'cyan'          => '0;36',
                                'white'         => '0;37',
				'B'		=> '1;30',
                                'BLACK'         => '1;30',
                                'RED'           => '1;31',
                                'GREEN'         => '1;32',
                                'YELLOW'        => '1;33',
                                'BLUE'          => '1;34',
                                'MAGENTA'       => '1;35',
                                'CYAN'          => '1;36',
                                'WHITE'         => '1;37',
                                'black_'        => '4;30',
                                'red_'          => '4;31',
                                'green_'        => '4;32',
                                'yellow_'       => '4;33',
                                'blue_'         => '4;34',
                                'magenta_'      => '4;35',
                                'cyan_'         => '4;36',
                                'white_'        => '4;37',
                                'blackI'        => '7;30',
                                'redI'          => '7;31',
                                'greenI'        => '7;32',
                                'yellowI'       => '7;33',
                                'blueI'         => '7;34',
                                'magentaI'      => '7;35',
                                'cyanI'         => '7;36',
                                'whiteI'        => '7;37'
                        ); 
        $default = "\033[0m";
        $S = $_[0];
        foreach $Col (%Color)
        {
                if ($S =~ /<$Col>/g)
                {
			if($COLOR ne "NO")
			{
                        	$NC = "\033[" . $Color{$Col} . "m";     
                        	$S =~ s/<$Col>/$NC/g;
                        	$S =~ s/<\/$Col>/$default/g;
                	}
			else
			{
				$S =~ s/<$Col>//g;
                                $S =~ s/<\/$Col>//g;
			}
		}
        }
        return $S;
}


sub uen
{
    my($T);
	$T = pack("u", $_[0]);
	chomp $T;
	return $T;
}

sub ude
{
    my($T);
        $T = unpack("u", $_[0]);
        return $T;
}

sub num_bereich
{
    my($m,@LR,@Sorted_LR,$i);
    # $number is the one we want to delete!
    # But does it contain kommas?
    @NumBlock = (); #reset
    $m = 0;
    if($number =~ /\,/)
    {
        # accept -d 3,4,7
        @NumBlock = split(/\,/,$number);
    }
    elsif($number =~ /^\d+\-\d+$/)
    {
        # accept -d 3-9
        @LR = split(/\-/,$number);
        @Sorted_LR = ();

	if($LR[0] > $LR[1])
	{
		@Sorted_LR = ($LR[1], $LR[0]);
	}
	elsif($LR[0] == $LR[1])
	{	
		# 0 and 1 are the same
		@Sorted_LR = ($LR[0], $LR[1]);
	}
	else
	{
		@Sorted_LR = ($LR[0], $LR[1]);
	}

        for($i=$Sorted_LR[0]; $i<=$Sorted_LR[1]; $i++)
        {
            # from 3-6 create @NumBlock (3,4,5,6)
            $NumBlock[$m] = $i;
            $m++;
        }
    }
    else
    {
        @NumBlock = ($number);
    }

}

sub getdate
{
    my($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
    $year += 1900;
    $mon += 1;
    $mon =~ s/^(\d)$/0$1/;
    $hour =~ s/^(\d)$/0$1/;
    $min =~ s/^(\d)$/0$1/;
    $sec =~ s/^(\d)$/0$1/;
    $mday =~ s/^(\d)$/0$1/;
    return "$mday.$mon.$year $hour:$min:$sec";
}


sub help
{
print qq~
--------------------------------------------------------------
HELP for interactive note       $version

The following commands are available:
L/l     List notes. L=long, with timestamp and l=short without timestamp.
        You can also just hit <enter> for short list.
	If you specify a subtopic, then list will display it's contents, i.e.
	"l mytopic" will dislpay notes under mytopic.
N       Create a new note.
D       Delete a note. You can either hit "d 1" or "d 1-4" or just hit "d".
        If you don't specify a number, you will be asked for.
S       Search trough the notes database. Usage is similar to Delete, use
        a string instead of a number to search for.
E       Edit a note. Usage is similar to Delete but you can only edit note
        a time.~;
if($TOPIC)
{
print qq~
T/t     print a list of all existing topics as a tree. T prints the tree with 
	all notes under each topic.
	=====>
	  You can change the actual topic by simply typing it's name or by
	  using the command "cd", i.e. "cd mytopic".
          You can create a new topic by creating a new note, the first line
          must be the topic borderd by slashes, i.e. "/newtopic/". The slash
	  is the default topic-sepearator, but you can override thie in the
	  config!
          If you type just ".." instead of a topic, you will go one step back
	  in your topic-structure.
	=====>~;
}
print qq~
?/H     This help screen.
Q       Exit the program.

All commands except the List command are case insensitive.
---------------------------------------------------------------
~;
}


sub display_tree {
	# displays a tree of all topics
	my(%TREE, %res, $n, $t, $num, @nodes, $firstline, $text, $untext);
        %res = $db->get_all();
        foreach $num (keys %res)
        {
                $n = $res{$num}->{'note'};
                $t = $res{$num}->{'date'};
                # this allows us to have multiple topics (subtopics!)
                my ($firstline,$text,$untext) = split /\n/, $n, 3;
                if($firstline =~ /^($TopicSep)/)
                {
			$firstline =~ s/($TopicSep)*$//; #remove TopicSepatator
                	@nodes = split(/$TopicSep/,$firstline);
                }
                else
                {
                	@nodes = ();("$TopicSep");
			$text = $firstline;
                }
	    	&tree($text, \%TREE, @nodes);
	}

	# now that we have build our tree (in %TREE) go on t display it:
	print C $BORDERC . "\n[" . $TopicSep . $BORDERC . "]\n";
	&print_tree(\%{$TREE{''}},"");
	print C $BORDERC . $_BORDERC . "\n";
}


sub tree {
    my($text, $LocalTree, $node, @nodes) = @_;
    if(@nodes) {
	if(! exists $LocalTree->{$node}->{$NoteKey}) {
	    $LocalTree->{$node}->{$NoteKey} = [];
	}
	&tree($text, $LocalTree->{$node}, @nodes);
    }
    else {
	push @{$LocalTree->{$node}->{$NoteKey}}, $text;
    }
}


sub print_tree {
  # thanks to Jens for his hints and this sub!
  my $hashref=shift;
  my $prefix=shift;
  my @notes=@{$hashref->{$NoteKey}};
  my @subnotes=sort grep { ! /^$NoteKey$/ } keys %$hashref;
  if($TreeType eq "LONG") {
  	for my $note (@notes) {
	  if($note ne "") {
  	    print C $BORDERC ;# . $prefix. "|\n";
  	    print C "$prefix+---<" . $NOTEC . $note . $BORDERC . ">" . $_NOTEC . "\n";
	  }
  	}
  }
  for my $index (0..$#subnotes) {
    print C $BORDERC . $prefix. "|\n";
    print C "$prefix+---[" . $TOPICC . $subnotes[$index] . $BORDERC . "]\n";
    &print_tree($hashref->{$subnotes[$index]},($index == $#subnotes?"$prefix    ":"$prefix|   "));
  }
}
