#!/usr/bin/perl
# $Id: note,v 1.10 2000/08/19 13:38:33 zarahg Exp $
#
#
# note - console notes management with database and encryption support.
# Copyright (C) 1999-2000 Thomas Linden   (see README for details!)
#
# 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.
#
#                               - Thomas Linden <tom@daemon.de>
#
# latest version on:
# http://www.daemon.de/software.html
# ftp://www.0x49.org/pub/scip/note/
#

use strict;
use Data::Dumper;
use Getopt::Long;

#
# prototypes
#
sub usage;			# print usage message for us thumb userz :-)
sub find_editor;		# returns an external editor for use
sub output;			# used by &list and &display
sub C;				# print colourized 
sub num_bereich;		# returns array from "1-4" (1,2,3,4)
sub getdate;			# return pretty formatted day
sub new;			# crate new note
sub edit;			# edit a note
sub del;			# delete a note
sub display;			# display one or more notes
sub list;			# note-listing
sub help;			# interactive help screen
sub import;			# import from notedb-dump
sub display_tree;		# show nice tree-view
sub tree;			# build the tree
sub print_tree;			# print the tree, contributed by Jens Heunemann <Jens.Heunemann@consol.de>. THX!


#
# globals
#
my (
    #
    # commandline options
    #
    $opt_, $opt_i, $opt_r, $opt_e, $opt_d, $opt_enc,
    $opt_s, $opt_t, $opt_T, $opt_l, $opt_L, $opt_c,
    $opt_D, $opt_I, $opt_o, $opt_h, $opt_n, $opt_v,

    #
    # set from commandline (or interactive)
    #
    $number, $searchstring, $dump_file, $ImportType, $NewType, $Raw,

    #
    # options from config file .noterc
    #
    $maxlen, $timelen, $TOPIC, $NOTEDB, $MAX_TIME, $PreferredEditor,
    $ALWAYS_INT, $KEEP_TIMESTAMP, $COLOR, $ALWAYS_EDIT, $HOME, $FormatText,
    $BORDER_COLOR, $NOTE_COLOR, $NUM_COLOR, $TOPIC_COLOR, $MAX_NOTE,
    $USE_CRYPT, $CRYPT_METHOD, $TopicSep, $DEFAULT_LIST, $TIME_COLOR, $SHORT_CD,
    $USE_CACHE,

    #
    # db specifics from .noterc
    #
    $db, $dbname, $dbhost, $dbport, $dbuser, $dbpasswd, $encrypt_passwd, $clearstring,
    $table, $fnum, $fnote, $fdate, $date, $dbdriver, $libpath,

    #
    # processed colors
    #
    $BORDERC,  $_BORDERC, $NOTEC, $NUMC,  $_NUMC, $_NOTEC, $TIMEC,
    $_TIMEC, $TOPICC,  $_TOPICC,

    #
    # config presets
    #
    $DEFAULTDBNAME, $USER, $PATH, $CONF,

    #
    # internals
    #
    $TYPE, $mode, $NoteKey, $TempDir, %Color, @LastTopic,
    $version, $CurTopic, $CurDepth, $WantTopic,
    $sizeof, %TP, $TreeType, $ListType, $SetTitle,
    @ArgTopics, $key, $typedef, @NumBlock, $has_nothing,
   );


#
# 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 	= 84;
$COLOR 		= "YES";
$BORDER_COLOR 	= "bold";
$NUM_COLOR    	= "blue";
$NOTE_COLOR   	= "green";
$TIME_COLOR   	= "blue";
$TOPIC_COLOR 	= "bold";
$TOPIC 		= 1;
$TopicSep 	= '/';
$version 	= "1.1.1";
if ($TOPIC) {
  $CurDepth 	= 1;		# the current depth inside the topic "directory" structure...
}
$USE_CRYPT 	= "NO";
$TempDir        = "/tmp";
# mysql stuff
$table          = "note";
$fnote          = "note";
$fdate          = "date";
$fnum           = "number";

# colors available
# \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',
                      'white_black'   => '40;37;01',
                      'bold'          => ';01',
             );

#
# process command line args
#
if ($ARGV[0] eq "") {
  $mode = "new";
}
elsif ($#ARGV == 0 && $ARGV[0] eq "-") {
  $mode = "new";
  $NewType = 1;			# read from STDIN until EOF
  shift;
  undef $has_nothing;
}
else {
  Getopt::Long::Configure( qw(no_ignore_case));
  GetOptions (
	      "interactive|i!"       => \$opt_i, # no arg
	      "config|c=s"	     => \$opt_c, # string,  required
	      "raw|r!"	             => \$opt_r, # no arg
	      "edit|e=i"	     => \$opt_e, # integer, required
	      "delete|d=s"	     => \$opt_d, # integer, required
	      "search|s=s"	     => \$opt_s, # string,  required
	      "tree|topic|t!"	     => \$opt_t, # no arg
	      "longtopic|T!"	     => \$opt_T, # no arg
	      "list|l:s"	     => \$opt_l, # string,  optional
	      "longlist|L:s"	     => \$opt_L, # string,  optional
	      "dump||Dump|D:s"       => \$opt_D, # string,  optional
	      "import|Import|I:s"    => \$opt_I, # string,  optional
	      "overwrite|o!"	     => \$opt_o, # no arg
	      "help|h|?!"	     => \$opt_h, # no arg
	      "version|v!"	     => \$opt_v, # no arg
	      "encrypt=s"            => \$opt_enc, # string, required
	     );
  $opt_n = shift;		# after that @ARGV contains eventually
				# a note-number
				# $opt_ is a single dash, in case of existence!
  #
  # determine mode
  #
  if ($opt_i) {
    $mode = "interactive";
  }
  elsif (defined $opt_l || defined $opt_L) {
    $mode = "list";
    if (defined $opt_l) {
      @ArgTopics = split /$TopicSep/, $opt_l;
    }
    else {
      $ListType = "LONG";
      @ArgTopics = split /$TopicSep/, $opt_L;
    }
    $CurDepth += $#ArgTopics + 1 if($opt_l || $opt_L);
    $CurTopic = $ArgTopics[$#ArgTopics];  # use the last element everytime...
  }
  elsif ($opt_t || $opt_T) {
    $mode = "tree";
    $TreeType = "LONG" if($opt_T);
  }
  elsif (defined $opt_s) {
    $mode = "search";
    $searchstring = $opt_s;
  }
  elsif ($opt_e) {
    $mode = "edit";
    $number = $opt_e;
  }
  elsif ($opt_d) {
    $mode = "delete";
    $number = $opt_d;
  }
  elsif ($opt_enc) {
    $mode = "encrypt_passwd";
    $clearstring = $opt_enc;
  }
  elsif (defined $opt_D) {
    $mode = "dump";
    if (!$opt_) {
      if ($opt_D ne "") {
	$dump_file = $opt_D;
      }
      else {
	$dump_file = "note.dump.$$";
	print "no dumpfile specified, using $dump_file.\n";
      }
    }
    else {
      $dump_file = "-";		# use STDIN
    }
  }
  elsif (defined $opt_I) {
    $mode = "import";
    if (!$opt_) {
      if ($opt_I ne "") {
	$dump_file = $opt_I;
      }
      else {
	print "Import-error! No dump_file specified!\n";
	exit(1);
      }
    }
    else {
      $dump_file = "-";
    }
  }
  elsif ($opt_v) {
    print "This is note $version by Thomas Linden <tom\@daemon.de>.\n";
    exit(0);
  }
  elsif ($opt_h) {
    &usage;
  }
  else {
    if ($opt_c && $mode eq "" && !$opt_n) {
      $mode = "new";
    }
    elsif ($opt_c && $mode eq "") {
      $mode = "";		# huh?!
    }
    else {
      $has_nothing = 1;
    }
  }
  ### determine generic options
  if ($opt_n =~ /^[\d+\-?\,*]+$/) {
    # first arg is a digit!
    if ($mode eq "") {
      $number = $opt_n;
      $mode = "display";
      undef $has_nothing;
    }
    else {
      print "mode <$mode> does not take a numerical argument!\n";
      exit(1);
    }
  }
  elsif ($opt_n ne "") {
    print "Unknown option: $opt_n\n";
    &usage;
  }
  if ($opt_r) {
    $Raw = 1;
  }
  if ($opt_o) {
    $ImportType = "overwrite";
    if (!$opt_I) {
      print "--overwrite is only suitable for use with --import!\n";
      exit(1);
    }
  }
  #####
}
if ($has_nothing && $mode eq "") {
  &usage;
}


# read the configfile.
$CONF = $opt_c if($opt_c);	# if given by commandline, use this.
if (-e $CONF) {
  &getconfig($CONF);
}
elsif ($opt_c) {
  # only wrong, if specified by commandline! else use default values!
  print STDERR "Could not open \"$CONF\": file does not exist or permission denied!\n";
  exit(1);
}

# directly jump to encrypt, 'cause this sub does
# not require a database connection
if ($mode eq "encrypt_passwd") {
  &encrypt_passwd;
  exit;
}

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

# OK ... Long-Listing shall be default ... You wanted it!!!
if ($DEFAULT_LIST eq "LONG") {
  # takes only precedence in commandline mode
  $ListType="LONG";
}



# *if* loading of the config was successful, try to load the
# configured database backend. Currently supported: 
# mysql, dbm and binary.
unshift @INC, $libpath;

if ($dbdriver eq "binary") {
  eval {
    require NOTEDB::binary;
    $db = new NOTEDB($dbdriver, $NOTEDB, $MAX_NOTE, $MAX_TIME, $dbdriver);
  }
}
elsif ($dbdriver eq "mysql") {
  # do the new() later because of the encrypted password!
  eval {
    require "NOTEDB/mysql.pm";
  };
  die $@ if($@);
}
else {
  eval {
    require "NOTEDB/$dbdriver.pm";
    $db = new NOTEDB($dbdriver, $dbname, $dbhost, $dbuser, $dbpasswd, $table, $fnum, $fnote, $fdate);
  };
}
if ($@) {
  print "Unsupported database backend: NOTEDB::$dbdriver!\n";
  print "The following error has occured:\n------------------------\n" . $@ . "\n------------------------\n";
  exit 1;
}


# 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;

# default permissions on new files (tmp)
umask 077;

# 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";
  }
  if (!exists $ENV{'NOTE_PASSWD'}) {
    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 = <>;
    }
  }
  else {
    $key = $ENV{'NOTE_PASSWD'};
  }
  chomp $key;
  if ($dbdriver eq "mysql") {
    eval {
      require Crypt::CBC;
      my $cipher = new Crypt::CBC($key, $CRYPT_METHOD);
      # decrypt the dbpasswd, if it's encrypted!
      my $dbpasswd  = $cipher->decrypt(unpack("u",$dbpasswd)) if($encrypt_passwd);
      $db = new NOTEDB($dbdriver, $dbname, $dbhost, $dbuser,
		       $dbpasswd, $table, $fnum, $fnote, $fdate, $dbport);
    };
    die "Could not connect do db: $@!\n" if($@);
  }
  $db->use_crypt($key,$CRYPT_METHOD);
  undef $key;
  # verify correctness of passwd
  my ($cnote, $cdate) = $db->get_single(1);
  if ($cdate ne "") {
    if ($cdate !~ /^\d+\.\d+?/) {
      print "access denied.\n"; # decrypted $date is not a number!
      exit(1);
    }
  }				#else empty database!
}
else {
    if ($dbdriver eq "mysql") {
	$db = new NOTEDB($dbdriver, $dbname, $dbhost, $dbuser,
			 $dbpasswd, $table, $fnum, $fnote, $fdate, $dbport);
    }
    $db->no_crypt;
    # does: NOTEDB::crypt_supported = 0;
    my ($cnote, $cdate) = $db->get_single(1);
    if ($cdate ne "") {
	if ($cdate !~ /^\d+\.\d+?/) {
	    print "$NOTEDB seems to be encrypted!\n";
	    exit(1);
	}
    }
}


# do we use the db cache?
if ($USE_CACHE) {
    $db->use_cache();
}


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


# 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 ################


############ encrypt a given password ##############
sub encrypt_passwd {
  my($key, $crypt_string);
  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;
  eval {
    require Crypt::CBC;
    my $cipher = new Crypt::CBC($key, $CRYPT_METHOD);
    $crypt_string  = pack("u", $cipher->encrypt($clearstring));
  };
  if ($@) {
    print "Something went wrong: $@\n";
    exit 1;
  } else {
    print "Encrypted password:\n$crypt_string\n";
  }
}


############################### 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) {
	if ($Raw) {
	  print "$N\n$date\n$note\n\n";
	}
	else {
	  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);
    if ($searchstring eq "") {
      print "No searchstring specified!\n";
    }
    else {
      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'}, "search");
	$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" && !$Raw) {
      print "\nList of all existing notes:\n\n";
    }
    else {
      print "\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 "") {
	if ($i) {
	  # only if there were notes under current topic
	  undef $PATH;
	  foreach (@RealTopic) {
	    $PATH .= $_ . $TopicSep;
	    last if($_ eq $CurTopic);
	  }
	}
	else {
	  # it is an empty topic, no notes here
	  # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
	  $PATH = join $TopicSep, @LastTopic;
	  $PATH .= $TopicSep . $CurTopic . $TopicSep;
	  $PATH =~ s/^\Q$TopicSep$TopicSep\E/$TopicSep/;
	}
      }
      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 = &gettemp;
      # let the user edit it...
      $editor = &find_editor;
      if ($editor) {
	# create the temp file
	open NEW, "> $TEMP" or die $!;
	close NEW;
	system "chattr", "+s", $TEMP; # ignore errors, since only on ext2 supported!
	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
      if ($NewType) {
	# be silent! read from STDIN until EOF.
	while (<STDIN>) {
	  $note .= $_;
	}
      }
      else {
	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 = &gettemp;
    open NOTE,">$TEMP" or die "Could not open $TEMP\n";
    select NOTE;

    system "chattr", "+s", $TEMP; # ignore errors, like in new()

    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 || die $!;

    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);
    # $dump_file
    if ($dump_file eq "-") {
      $DUMP = *STDOUT;
    }
    else {
      open (DUMPFILE, ">$dump_file") or die "could not open $dump_file\n";
      $DUMP = *DUMPFILE;
    }
    select $DUMP;
    %res = $db->get_all();
    foreach $num (sort { $a <=> $b } keys %res) {
      print STDOUT "dumping note number $num to $dump_file\n" if($dump_file ne "-");
      print "Number: $num\n"
	   ."Timestamp: $res{$num}->{'date'}\n"
	   ."$res{$num}->{'note'}\n";
    }
    print "\n";
    close(DUMPFILE);
    select STDOUT;
  }

sub import
  {
    my($num, $start, $complete, $dummi, $note, $date, $time, $number, $stdin, $DUMP);
    # open $dump_file and import it into the notedb
    $stdin = 1 if($dump_file eq "-");
    if ($stdin) {
      $DUMP = *STDIN;
    }
    else {
      open (DUMPFILE, "<$dump_file") or die "could not open $dump_file\n";
      $DUMP = *DUMPFILE;
    }
    $db->set_del_all() if($ImportType ne "");
    $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" if(!$stdin);
	  $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" if(!$stdin);
    }
  }



sub interactive
  {
    my($B, $BB, $menu, $char, $Channel);
    $Channel = $|;
    # create menu:
    $B = "<white_black>";
    $BB = "</white_black>";
    $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!
    $ListType = ($DEFAULT_LIST eq "LONG") ? "LONG" : "";
    &list;

    for (;;) {
      $ListType = ($DEFAULT_LIST eq "LONG") ? "LONG" : "";
      undef $SetTitle;
      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*?,*?\-*?]*$/) {
	$ListType = "";		#overrun
				# display notes
	$number = $char;
	&display;
      }
      elsif ($char =~ /^n$/i) {
	# create a new one
	&new;
      }
      elsif ($char =~ /^$/) {
	&list;
      }
      elsif ($char =~ /^l$/) {
	$ListType = "";
	&list;
      }
      elsif ($char =~ /^L$/) {
	$ListType = "LONG";
	&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!!!
	$| = $Channel;
	print "\n\ngood bye!\n";
	exit(0);
      }
      elsif ($char =~ /^t$/) {
	$TreeType = "";
	&display_tree;
      }
      elsif ($char =~ /^T$/) {
	$TreeType = "LONG";
	&display_tree;
	$TreeType = "";
      }
      elsif ($char =~ /^\.\.$/ || $char =~ /^cd\s*\.\.$/) {
	$CurDepth-- if ($CurDepth > 1);
	$CurTopic = $LastTopic[$CurDepth];
	pop @LastTopic; # remove last element
	&list;
      }
      elsif ($char =~ /^l\s+(\w+)$/) {
	# list
	$WantTopic = $1;
	if (exists $TP{$WantTopic}) {
	  my %SaveTP = %TP;
	  $LastTopic[$CurDepth] = $CurTopic;
	  $CurTopic = $1;
	  $CurDepth++;
	  &list;
	  $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 ($unchar =~ /^\d+?$/ && $SHORT_CD) {
	  # just a number!
	  my @topic;
	  my ($cnote, $cdate) = $db->get_single($unchar);
	  my ($firstline,$dummy) = split /\n/, $cnote, 2;
	  if ($firstline =~ /^($TopicSep)/) {
	    @topic = split(/$TopicSep/,$firstline);
	  }
	  else {
	    @topic = ();
	  }
	  if (@topic) {
	    # only jump, if, and only if there were at least one topic!
	    $CurDepth = $#topic + 1;
	    $CurTopic = pop @topic;
	    @LastTopic = ("");
	    push @LastTopic, @topic;
	  }
	  &list;
	}
	elsif ($unchar eq $TopicSep) {
	  # cd /
	  $CurDepth = 1;
	  $CurTopic = "";
	  &list;
	}
	elsif (exists $TP{$char} || exists $TP{$unchar}) {
	  $char = $unchar if(exists $TP{$unchar});
	  $LastTopic[$CurDepth] = $CurTopic;
	  $CurTopic = $char;
	  $CurDepth++;
	  &list;
	}
	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 [ options ] [ number [,number...]]
Read the note(1) manpage for more details.
~;
    exit 1;
  }

sub find_editor {
  return $PreferredEditor || $ENV{"VISUAL"} || $ENV{"EDITOR"} || "vim" || "vi" || "pico";
}

#/

sub format {
  # make text bold/underlined/inverse using current $NOTEC
  my($note) = @_;
  if ($FormatText) {
    my $BN = uc($NOTEC);
    my $_BN = uc($_NOTEC);
    my $UN  = $NOTEC;
    $UN =~ s/<(.*)>/<$1_>/;
    my $_UN = $UN;
    $_UN =~ s/<(.*)>/<\/$1>/;
    my $IN = $NOTEC; my $_IN = $_NOTEC;
    $IN =~ s/<(.*)>/<$1I>/;
    $_IN =~ s/<(.*)>/<$1I>/;
    $note =~ s/\*\*([^\*^\*]*)\*\*/$BN$1$_BN/g;
    $note =~ s/__([^_^_]*)__/$UN$1$_UN/g;
    $note =~ s/{{([^}^}]*)}}/$IN$1$_IN/g;
  }
  $note =~ s/(<\/.*>)/$1$NOTEC/g;
  $note;
}

sub output
  {
    my($SSS, $LINE, $num, $note, $time, $TYPE, $L, $LONGSPC, $R, $PathLen, $SP, $title, $CUTSPACE,
       $len, $diff, $Space, $nlen, $txtlen);
    ($num, $note, $time, $TYPE) = @_;
    $txtlen = ($ListType eq "LONG") ? $maxlen : $timelen + $maxlen;
    $note = &format($note);

    $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!
	$SP = " " x ($maxlen - 2 - $PathLen);
	if (!$Raw) { 
				# no title in raw-mode!
	  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 $txtlen;
      if ($TYPE eq "search") {
	$note =~ s/^\Q$TopicSep\E.+?\Q$TopicSep\E\n//;
      }
      $note =~ s/\n/$CUTSPACE/g;
      $len   = length($note);
      if ($len < ($txtlen - 2 - $nlen)) {
	$diff = $txtlen - $len;
	$Space = " " x $diff;
	if (!$Raw) {
	  if ($num eq "-") {
	    $title = $BORDERC . $TOPICC . "\"" . $note . "\"" . $_TOPICC . $Space . "$_BORDERC";
	  }
	  else {
	    $title = $BORDERC . $NOTEC . "\"" . $note . "\"" . $_NOTEC . $Space . "$_BORDERC";
	  }
	}
	else {
	  $title = $note;
	}
      }
      else {
	$title = substr($note,0,($txtlen - 2 - $nlen));
	if (!$Raw) {
	  $title = $BORDERC . $NOTEC . "\"" . $title . "...\"$_NOTEC$_BORDERC";
	}
      }
      if ($Raw) {
	print "$num  ";
	print "$time  " if($ListType eq "LONG");
	if ($title =~ /^ => (.*)$TopicSep (.*)$/) {
	  $title = "$1$TopicSep $2"; # seems to be a topic!
	}
	print "$title\n";
      }
      else {
	# $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 {
      # we will not reach this in raw-mode, therefore no decision here!
      chomp $note;
      $Space = " " x (($maxlen + $timelen) - 16);
      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($default, $S, $Col, $NC, $T);
    $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 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 gettemp
  {
    my($random, @range);
    @range=('0'..'9','a'..'z','A'..'Z');
    srand(time||$$);
    for (0..10) {
      $random .= $range[rand(int($#range)+1)];
    }
    my $tempfile = $TempDir . "/" . $USER  . "." . $random;
    if (-e $tempfile) {
      # avoid race conditions!
      unlink $tempfile;
    }
    return $tempfile;
  }



sub help
  {
    my $B = "<white_black>";
    my $BB = "</white_black>";
    my($S, $L, $T, $Q, $H, $N, $D, $E);
    $L = $B . "L" . $BB . $NOTEC;
    $T = $B . "T" . $BB . $NOTEC;
    $Q = $B . "Q" . $BB . $NOTEC;
    $H = $B . "?" . $BB . $NOTEC;
    $N = $B . "N" . $BB . $NOTEC;
    $D = $B . "D" . $BB . $NOTEC;
    $E = $B . "E" . $BB . $NOTEC;
    $S = $B . "S" . $BB . $NOTEC;

    print C qq~$BORDERC
----------------------------------------------------------------------$_BORDERC $TOPICC
HELP for interactive note       $version
$_TOPICC $NOTEC
The following commands are available:
$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.
$H       This help screen.
$Q       Exit the program.~;
    if ($TOPIC) {
      print C qq~
$T       print a list of all existing topics as a tree. T prints the tree
	with all notes under each topic.~;
    }
    print C qq~

All commands except the List and Topic commands are case insensitive.
Read the note(1) manpage for more details.$BORDERC
----------------------------------------------------------------------$_BORDERC
~;
  }


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($num, $text, \%TREE, @nodes);
  }
  #return if ($num == 0);
  # now that we have build our tree (in %TREE) go on t display it:
  print C $BORDERC . "\n[" . $TopicSep . $BORDERC . "]\n";
  &print_tree(\%{$TREE{''}},"") if(%TREE);
  print C $BORDERC . $_BORDERC . "\n";
}


sub tree {
  my($num, $text, $LocalTree, $node, @nodes) = @_;
  if (@nodes) {
    if (! exists $LocalTree->{$node}->{$NoteKey}) {
      $LocalTree->{$node}->{$NoteKey} = [];
    }
    &tree($num, $text, $LocalTree->{$node}, @nodes);
  } else {
    if (length($text) > ($maxlen - 5)) {
      $text = substr($text, 0, ($maxlen -5));
    }
    $text = $text . " (" . $NUMC . "#" . $num . $_NUMC . $NOTEC . ")" . $_NOTEC if($text ne "");
    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|   "));
  }
}


sub getconfig
  {
    my($configfile) = @_;
    my ($home, $value, $option);
    # checks are already done, so trust myself and just open it!
    open CONFIG, "<$configfile" || die $!;
    while (<CONFIG>) {
      chomp;
      next if(/^\s*$/ || /^\s*#/);
      my ($option,$value) = split /\s\s*=?\s*/, $_, 2;
      $value 		=~ s/\s*$//;
      $home 		= $value if (/^Home/);
      $libpath 	        = $value if (/^LibPath/);
      $dbdriver 	= $value if (/^DbDriver/);
      $dbhost 	        = $value if (/^DbHost/);
      $dbport           = $value if (/^DbPort/);
      $dbuser 	        = $value if (/^DbUser/);
      $dbpasswd 	= $value if (/^DbPasswd/);
      $encrypt_passwd   = $value if (/^encrypt_passwd/);
      $dbname 	        = $value if (/^DbName/);
      $table 		= $value if (/^DbTable/);
      $fnum 		= $value if (/^FieldNumber/);
      $fnote 		= $value if (/^FieldNote/);
      $fdate 		= $value if (/^FieldDate/);
      $SHORT_CD         = $value if (/^ShortCd/);
      $NOTEDB 	        = $value if (/^NoteDb/);
      $MAX_NOTE 	= $value if (/^MaxNoteByte/);
      $MAX_TIME 	= $value if (/^MaxTimeByte/);
      $CRYPT_METHOD     = $value if (/^CryptMethod/);
      $USE_CRYPT 	= "YES"  if (/^UseEncryption/ && $value == 1);
      $USE_CRYPT 	= undef  if (/^UseEncryption/ && $value == 0);
      $ALWAYS_INT 	= "YES"  if (/^AlwaysInteractive/ && $value == 1);
      $ALWAYS_INT 	= undef  if (/^AlwaysInteractive/ && $value == 0);
      $DEFAULT_LIST 	= "LONG" if (/^DefaultLong/ && $value == 1);
      $DEFAULT_LIST 	= undef  if (/^DefaultLong/ && $value == 0);
      $ALWAYS_EDIT 	= "YES"  if (/^AlwaysEditor/ && $value == 1);
      $ALWAYS_EDIT 	= undef  if (/^AlwaysEditor/ && $value == 0);
      $KEEP_TIMESTAMP   = "YES"  if (/^KeepTimeStamp/ && $value == 1);
      $KEEP_TIMESTAMP   = undef  if (/^KeepTimeStamp/ && $value == 0);
      $COLOR            = "YES"  if (/^UseColors/ && $value == 1);
      $COLOR    	= "NO"   if (/^UseColors/ && $value == 0);
      $TopicSep 	= $value if (/^TopicSeparator/);
      $maxlen 	        = $value if (/^MaxLen/);
      $BORDER_COLOR 	= $value if (/^BorderColor/);
      $NUM_COLOR 	= $value if (/^NumberColor/);
      $NOTE_COLOR 	= $value if (/^NoteColor/);
      $TIME_COLOR 	= $value if (/^TimeColor/);
      $TOPIC_COLOR	= $value if (/^TopicColor/);
      $PreferredEditor  = $value if (/^PreferredEditor/);
      $FormatText       = $value if (/^FormatText/);
      $TempDir          = $value if (/^TempDirectory/);
      $USE_CACHE        = $value if (/^Cache/);
    }
    chomp $home;
    $home =~ s/\/*$//;		# cut eventually / at the end
    $HOME = eval($home);
    if ($NOTEDB =~ /^(~\/)(.*)$/) {
      $NOTEDB = "/home/" . $USER . "/" . $2;
    }
    $libpath =~ s/\/*$//;

    close CONFIG;
  }




__END__
#
# $Log: note,v $
# Revision 1.10  2000/08/19 13:38:33  zarahg
# .
#
# Revision 1.9  2000/08/14 14:14:20  zarahg
# changed bug in cd .. after cd <number>, $PATH was empty.
#
# Revision 1.8  2000/08/14 11:21:34  zarahg
# hmmm... the default size for time was too small :-(
#
# Revision 1.7  2000/08/14 10:59:26  zarahg
# moved %Color hash from ::C() to ::main, displaying is now faster.
#
# Revision 1.6  2000/08/14 10:27:26  zarahg
# use now better color defaults and added 2 new color values, bold an white_black
#
# Revision 1.5  2000/08/11 00:05:58  zarahg
# 1.1.0 beta2 ready for testing
#
# Revision 1.4  2000/08/10 09:21:56  zarahg
# ready for 1.1.0 shipping, lots of changes/additions, see Changelog
#
# Revision 1.3  2000/07/21 06:41:25  zarahg
# 638: precedence bug fixed
#
# Revision 1.2  2000/07/09 22:10:03  zarahg
# tempfile management more secure now. new option TempDirectory. thx to Donald.
#
# Revision 1.30  2000/07/09 21:59:48  scip
# secure temp files
#
# Revision 1.29  2000/06/25 20:13:23  scip
# *** empty log message ***
#
# Revision 1.28  2000/06/25 19:51:51  scip
# changed pattern matching of seraching(\@ ... \E)
# added --config option
#
# Revision 1.27  2000/05/16 23:51:35  thomas
# fixed many option-parsing related bugd!
#
# Revision 1.26  2000/05/13 01:05:17  thomas
# changed config format and fixed some bugs
# as well as some other additions...
#
# Revision 1.25  2000/05/11 23:42:43  thomas
# --tree changed to --topic
#
# Revision 1.24  2000/05/10 22:59:44  thomas
# updated usage to reflect --raw and build it into output
# and display subs.
#
# Revision 1.23  2000/05/10 22:19:04  thomas
# changed to Getopt::Long, added --raw
#
# Revision 1.22  2000/05/01 18:51:40  thomas
# added "-" to sub dump
#
# Revision 1.21  2000/05/01 00:17:27  thomas
# *** empty log message ***
#
# Revision 1.20  2000/04/30 23:31:38  thomas
# added -o and coloured sub help.
#
# 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
