Files
note/bin/_note

1682 lines
43 KiB
Plaintext
Raw Normal View History

CHANGED: does no more use the external touch command to create a new file, use perls open() instead. CHANGED: excluded some of the help texts from the usage message and the interactive help command to a manpage. ADDED: new commandline flag "--encrypt" which one can use to encrypt the mysql database password. This will be decrypted before connecting to the db. There is also a new config file option "encrypt_passwd" which indicates an encrypted db-password. ADDED: another new config option "ShortCd", which can be set to "yes" or 1 and if set, then a command like "cd 13" would jump directly to the topic of the note with the number 13. ADDED: now you can at any time cd back to the "root" of the topic-structure using the command "cd /". CHANGED: mysql.pm does now only do a table-lock on single write accesses, no more on the whole session. This allows one to access the same db twice or more. FIXED: Changed README and Changelog for readability on 80 by 25 displays. And changed indentation of the note script itself. ADDED: NOTEDB.pm - a generic module, which holds some methods, which are used by binary.pm, mysql.pm and dbm.pm. ADDED: NOTEDB.pm generate_search(), which allows one to use AND, OR and various combinations of them using ( and ). ADDED: a search does now return the 2nd line of a note if a matching note's first line is a topic. CHANGED: use "unshift" instead of push to add $libpath to @INC. ADDED: a new feature, Caching of notes. supported by binary.pm and mysql.pm. To turn it on, one need to set "Cache" in the config to a true value.
2012-02-10 20:22:49 +01:00
#!/usr/bin/perl
# $Id: note,v 1.2 2000/07/09 22:10:03 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_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,
#
# db specifics from .noterc
#
$db, $dbname, $dbhost, $dbuser, $dbpasswd,
$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,
$version, $number, $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 = 64;
$COLOR = "YES";
$BORDER_COLOR = "BLACK";
$NUM_COLOR = "blue";
$NOTE_COLOR = "green";
$TIME_COLOR = "black";
$TOPIC_COLOR = "BLACK";
$TOPIC = 1;
$TopicSep = '/';
$version = "1.0.8";
if($TOPIC)
{
$CurDepth = 1; # the current depth inside the topic "directory" structure...
}
$USE_CRYPT = "NO";
$TempDir = "/tmp";
#
# 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
);
$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(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);
}
# 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 and binary.
push @INC, $libpath;
if($dbdriver eq "binary") {
eval {
require NOTEDB::binary;
$db = new NOTEDB($dbdriver, $NOTEDB, $MAX_NOTE, $MAX_TIME, $dbdriver);
}
}
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;
}
# 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;
# 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;
$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!
}
else {
$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);
}
}
}
# 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)
{
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'});
$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 "")
{
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 = &gettemp;
# let the user edit it...
$editor = &find_editor;
if($editor)
{
system "touch", $TEMP and die $!;
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(DUMP);
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, @LastTopic, $Channel);
$Channel = $|;
# 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!
$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];
&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(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...]]
Options:
-h --help displays this help screen
-v --version displays the version number
-c --config <file> use another config file than the default \~/.noterc
-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>. if <file> is simply
a "-" it will printed out to standard output.
-I --Import <file> | - imports a previously dumped textfile into the
note-database. Data will be appended by default.
You can also specify a dash "note -I -" instead of a <file>,
which causes note, silently to read in a dump from STDIN.
-o --overwrite only suitable for use with --Import. Overwrites an
existing notedb.
-r --raw raw mode, output will not be formatted. Works not in interactive
mode, only on cmd-line for list and display.
-i --interactive interactive mode
- if you run note only with one dash: "note -", then it will
read in a new note from STDIN until EOF, this makes it
possible to pipe text into a new note.
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 "AlwaysInteractive" in the config
set off, 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.
o If encryption support is turned on, note will ask you for a passphrase every
time it runs. You can avoid this behavior by setting the environment-variable
\$NOTE_PASSWD. You will need this for example, if you call note from a script.
~;
#my ($package, $filename, $line) = caller;
#print "called from line $line\n";
exit 1;
}
sub find_editor {
return $PreferredEditor || $ENV{"VISUAL"} || $ENV{"EDITOR"} || "vim" || "vi" || "pico";
}
#/
sub format {
# make text bold/underlined/inverse using current $NOTEC
# s/\[([^]]*)\]/$param{$1}/g;
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$2$_BN /g;
#$note =~ s/ (_)(.+)(_) / $UN$2$_UN /g;
#$note =~ s/ (\/)(.+)(\/) / $IN$2$_IN /g;
$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;
$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(%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 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 = "<blackI>";
my $BB = "</blackI>";
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.
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 over-
ride this in the config! If you type just ".." instead of a topic, you will
go one step back in your topic-structure.
~;
}
print C qq~
$NOTEC
All commands except the List and Topic commands are case insensitive. $_NOTEC $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/);
$dbuser = $value if (/^DbUser/);
$dbpasswd = $value if (/^DbPasswd/);
$dbname = $value if (/^DbName/);
$table = $value if (/^DbTable/);
$fnum = $value if (/^FieldNumber/);
$fnote = $value if (/^FieldNote/);
$fdate = $value if (/^FieldDate/);
$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/);
}
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.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