plod-1.9/01250804100000644 000000 000000 00000000000 10261252102 013444 0ustar00rootwheel000000 000000 plod-1.9/01250806100000644 000000 000000 00000000000 10261252102 013446 0ustar00rootwheel000000 000000 plod-1.9/Makefile000644 000000 000000 00000000373 10261264410 014057 0ustar00rootwheel000000 000000 #Nothing to be done, file is a perl script all: plod plod: plod.1.gz plod.el install: all cp ./plod /usr/local/bin cp ./plod.1.gz /usr/local/man/man1 cp ./plod.el /usr/local/share/emacs/site-lisp clean: rm ./plod rm ./plod.1.gz rm ./plod.el plod-1.9/README000644 000000 000000 00000012052 10261252102 013267 0ustar00rootwheel000000 000000 PLOD is a tool designed to help administrators (and others) keep track of their daily activities. Since your management will typically have no idea what you are doing to justify such an exorbitant salary (any amount of money they may be paying you being classified as "exorbitant"), and since most people forget what they do themselves, it's good to keep a record. Trot your logs out around performance review time, and show them to your management (after suitable sanitization) on a regular basis. The interface is designed to make it quick to dash off a simple note to yourself. Since most folks who are going to use PLOD also use email, I've based the interface on Berkeley mail-- tilde escapes and all (for a list of escapes, try ~h or ~?). By default, your logs will be encrypted using the /bin/crypt command-- not secure in the least, but marginally safe from casual browsing (I tend to vent into my logs sometimes rather than at those who might be offended and fire me). You can turn off the encryption if you find it more a hassle than a comfort. Which brings us to the subject of customization. Many escape sequences and variables have already been defined, but you can make your own changes using your ~/.plodrc file. This file is interpreted using eval(), so any valid Perl syntax is acceptable. Variables may be customized by editing this script directly, setting an environment variable with the same name as the PLOD variable, or by doing an assignment in your .plodrc (.plodrc value supersedes environment value which beats default value in script). New tilde escapes may be defined by assigning a type glob, say *foo, to the global array %funcs-- the index of the type glob in the array being the character (single chars only!) of the escape sequence. &foo should be a function which performs the escape (any arguments after the escape are passed in as a single string in @_), and $foo can be a descriptive help string (see &helpuser()). Your functions may reference any of the PLOD customization variables as well as the list @lines, which contains all information in the current log entry (including the date/time stamp PLOD adds to the beginning of each entry). For examples, consult the PLOD source code or the manual page. PLOD is a living, growing entity. If you have suggestions for improvements or new features, or find any bugs, please send them to me via email. The latest version of PLOD is always available at http://www.deer-run.com/~hal/plod/plod.shar Share and enjoy! Hal Pomeranz, hal@deer-run.com =============================================================================== History: v1.0 (Original release to comp.lang.perl) v1.1 (Second release to comp.lang.perl) ** &pipetocmd() unlinks all temporary files it creates (Pomeranz) ** Variable assignment idiom cleaned up (Rantapaa) ** LOGDIR and HOME are only prepended to LOGFILE and DEADLOG after .plodrc has been eval-ed and if LOGFILE and DEADLOG are relative paths. This means that you can change LOGDIR in your .plodrc and really affect where the log files go. (Rantapaa) ** eval of .plodrc is done with "do" rather than "cat" (Rantapaa) ** You can now do quick one-liner entries on the command line, e.g. "plod Completed modifications to PLOD" (Rantapaa) ** Time/date stamp only printed if user is entering info directly from a tty (Rantapaa) ** PLOD attempts to create logging directory if it does not exist (Ellis) v1.2 (not publicly released) ** Page/Edit/Visual log files from interactive mode or from the shell (Tizard) v1.3 (comp.lang.perl again, and also comp.sources.misc) ** Multi-line Perl input function, ~M (Crabb) ** Manual page generated (Pomeranz) v1.4 (not publicly released) ** Trap for empty log entries and don't add them to log file (Pomeranz) ** -C flag to cat logfile to STDOUT (Prestemon) ** /etc/plodrc (Lachowski) ** $STAMP customization variable (Lachowski/Pomeranz) v1.5 (not publicly released) ** Trap for non-existant/zero size/non-executable CRYPTCMD (Pomeranz) v1.6 (comp.sources.misc, comp.lang.perl, LISA VII) ** Minor fix to above trap. Would have been part of v1.5 but I had already distributed v1.5 to a few people. v1.7 (comp.lang.perl) ** Minor code cleanup (finally ran perl -w ;-)) (Pomeranz) ** -f flag to change user startup file (Dignard) ** Modularized &encrypt and &decrypt to allow customization of encryption strategy (Schneebeli) ** Manual page updated (Pomeranz) ** Relative directory pattern matches corrected (Pomeranz) v1.8 ** $PREFIX and $SUFFIX variables added (Billingsley) ** Error checking on &encrypt and &decrypt (Ziemba) ** "plod -[g|G]" switch (Gibson) ** $SEPARATOR variable (Pomeranz) ** Changed argument passing to logedit(), and pageit(), and improved argument parsing on some tilde escapes (Pomeranz) ** Protect filenames that get passed to the shell (Pomeranz) ** -s option and $PROMPT to prompt for keys (Schneebeli/Pomeranz) ** Argument processing cleaned up (Pomeranz) ** $PLODRC variable added (Pomeranz) ** $BACKUP file mechanism (Pomeranz) ** post-execution routines (Pomeranz) v1.9 ** Y2K fixes (Evans) plod-1.9/TODO000644 000000 000000 00000000216 05655775732 013134 0ustar00rootwheel000000 000000 * redo argument processing to handle multiple log files (Bob Gibson ) * delayed loading (ala plum) of tilde escape functions plod-1.9/plod000755 000000 000000 00000066144 10261264622 013320 0ustar00rootwheel000000 000000 #!/usr/local/bin/perl # # PLOD-- Personal LOgging Device, v1.9 # Copyright (C), 1993-1999, Hal Pomeranz (hal@deer-run.com) # All rights reserved. No warranty expressed or implied. # PLOD is freely distributable under the same terms as Perl. # Inspired by Bill Mendyka (mendyka@dg-rtp.dg.com) # Suggestions/Bugfixes: # Bobby Billingsley (bobby@dc.dk) # David W Crabb (crabb@phoenix.Princeton.EDU) # Michel Dignard (dignard@ERE.UMontreal.CA) # John Ellis (ellis@rtsg.mot.com) # Tim Evans (Tim.Evans@Citicorp.com) # Bob Gibson (rjg@sco.COM) # Mike Lachowski (mlachow@erenj.com) # Eric Prestemon (ecprest@pocorvares.er.usgs.GOV) # Erik E. Rantapaa (rantapaa@math.umn.edu) # Scot Schneebeli (sls@tct.com) # James Tizard (james@ringo.ssn.flinders.edu.au) # G. Paul Ziemba (paul@alantec.com) # ######################### Begin Variable Declarations ######################### # # All variables have default values which will be superceded by environment # variables of the same name. The user's .plodrc is read after all other # variable assignments, so any assignments there take precedence. # # Note that $LOGFILE and $DEADLOG are used as absolute pathnames. After # the .plodrc has been evaluated, $LOGDIR or $HOME will be prepended to # $LOGFILE and $DEADLOG respectively if either of these variables does not # begin with a '/'. # # Set $CRYPTCMD to null if you don't want encryption to be performed. # # KEYVAL key value used by CRYPTCMD # PROMPT true if user wants to be prompted for KEYVAL # CRYPTCMD encryption command, set this to null for no encryption # TMPFILE file name to use for temporary holding # EDITOR editor called by ~e # VISUAL editor called by ~v # PAGER used by ~p and ~h when output longer than one page (see LINES) # LINES number of lines on the screen # LOGDIR directory containing log files # LOGFILE absolute path of log file # BACKUP absolute path for backup of current LOGFILE # HOME user's home directory # PLODRC default user startup file # DEADLOG place to drop dead.log file on abort or ~q, also used by ~d # STAMP time/date stamp printed at the top of each entry # PREFIX string prepended to each line of log entry # SUFFIX string appended to each line of log entry # SEPARATOR string which separates log entries (should be static) # Some variable values use date/time information # ($ss, $mm, $hh, $DD, $MM, $YY) = localtime($^T); $MM++; $KEYVAL = sprintf("pl%d%dod", $YY, $MM); $PROMPT = 0; $CRYPTCMD = "/usr/bin/crypt"; $TMPFILE = "/tmp/plodtmp$$"; $HOME = (getpwuid($<))[7]; $EDITOR = "/usr/local/bin/emacs"; $VISUAL = "/usr/local/bin/emacs"; $PAGER = "/usr/local/bin/less"; $LINES = 24; $LOGDIR = "$HOME/.logdir"; $LOGFILE = sprintf("%04d%02d", $YY+1900, $MM); $BACKUP = ".plod$$.bak"; $PLODRC = "$HOME/.plodrc"; $DEADLOG = "dead.log"; $STAMP = sprintf("%02d/%02d/%04d, %02d:%02d --", $MM, $DD, $YY+1900, $hh, $mm); $PREFIX = ''; $SUFFIX = ''; $SEPARATOR = '-----'; ########################## End Variable Declarations ########################## ######################### Begin Function Declarations ######################### # Printvar (~=): Output the value of one or more variables. # sub printvar { local($vars) = @_; $, = ','; print eval "($vars)"; $, = ''; print "\n"; print "(continue composing note)\n"; } $printvar = "\$var[, ...]\tOutput value of variables"; $funcs{'='} = *printvar; # Bang (~!): Execute a command in the shell and then return to plod. # sub bang { local($cmdline) = @_; system("$cmdline"); print "(continue composing note)\n"; } $bang = "cmdline\tExecute system command and return"; $funcs{'!'} = *bang; # Redirect (~>): Pipe the output of a command into the current buffer. # sub redirect { local($cmdline) = @_; if (!open(CMD, "$cmdline |")) { warn "*** Unable to execute: $cmdline\n"; return; } &readit(CMD); } $redirect = "cmdline\tAdd output of given command to buffer"; $funcs{'>'} = *redirect; # Pipetocmd (~|): Pipe the contents of the current buffer through a UNIX # command line and replace the buffer with the result. # sub pipetocmd { local($cmdline) = @_; local($header); if (!open(PIPELN, "| $cmdline >$TMPFILE 2>&1")) { # output to tmp file warn "*** Unable to execute: $cmdline\n"; return; } $header = shift @lines if ($STAMP); # don't include stamp print PIPELN @lines; close(PIPELN); if (!open(INP, "<$TMPFILE")) { warn "*** Unable to get command output\n"; unshift(@lines, $header); unlink $TMPFILE; return; } undef @lines; # replace buffer with @lines = ; # contents of tmp file close(INP); unlink $TMPFILE; unshift(@lines, $header) if ($STAMP); print "(continue composing note)\n"; } $pipetocmd = "cmdline\tPipe contents of buffer through cmdline"; $funcs{'|'} = *pipetocmd; # Perlit (~X): Execute Perl code. # sub perlit { local($code) = @_; eval "$code"; warn $@ if $@; print "(continue composing note)\n"; } $perlit = "code\t\tExecute a line of Perl code"; $funcs{'X'} = *perlit; # Longperl (~M): Edit then eval a multi-line Perl fragment # sub longperl { local($bogus) = @_; local($safename); return(&mistake) if ($bogus); if (@code) { if (!open(TMP, "> $TMPFILE")) { warn "*** Unable to create temporary file\n"; return; } print TMP @code; close(TMP); } ($safename = $TMPFILE) =~ s/(\W)/\\$1/g; system("$EDITOR $safename"); if (!open(TMP, "< $TMPFILE")) { warn "*** Unable to read buffer\n"; return; } undef @code; @code = ; close(TMP); system("/bin/rm -f $safename*"); eval "@code"; warn $@ if $@; print "(continue composing note)\n"; } $longperl = "\t\tInvoke \$EDITOR on command buffer, then execute as Perl code"; $funcs{'M'} = *longperl; # Appendfl (~a): Append contents of buffer to a file and return to plod. # To overwrite a file with the contents of the buffer, see &writefl(). # sub appendfl { local($args) = @_; local(@args, $file); if ($args =~ /\S/) { @args = &shellwords($args); if (@args != 1) { warn "*** Only one argument allowed.\n"; return; } $file = $args[0]; } else { warn "*** No file name specified.\n"; return; } if (!open(OUTP, ">> $file")) { warn "*** Could not append to file $file\n"; return; } print OUTP @lines; close(OUTP); print "Wrote ", scalar(@lines), " lines to file $file\n"; print "(continue composing note)\n"; } $appendfl = "file\t\tAppend contents of buffer to file"; $funcs{'a'} = *appendfl; # Getdead (~d): Suck contents of DEADLOG file into buffer. # sub getdead { local($bogus) = @_; return(&mistake) if ($bogus); if (!open(DEAD, "<$DEADLOG")) { warn "*** Unable to open $DEADLOG.\n"; return; } &readit(DEAD, $DEADLOG); } $getdead = "\t\tIncorporate contents of \$DEADLOG into buffer"; $funcs{'d'} = *getdead; # Editbuf (~e) and Visualbuf (~v): Call appropriate editor on buffer. # sub editbuf { local($bogus) = @_; return(&mistake) if ($bogus); &calledit($EDITOR); } sub visualbuf { local($bogus) = @_; return(&mistake) if ($bogus); &calledit($VISUAL); } $editbuf = "\t\tEdit buffer with \$EDITOR"; $visualbuf = "\t\tEdit buffer with \$VISUAL"; $funcs{'e'} = *editbuf; $funcs{'v'} = *visualbuf; # Editlog (~E) and Visuallog (~V): Call appropriate editor on LOGFILE. # sub editlog { local($args) = @_; local($file, $key); if ($args =~ /\S/) { ($file, $key) = &shellwords($args); return unless ($file || $key); } &logedit($EDITOR, $file, $key); print "(continue composing note)\n"; } sub visuallog { local($args) = @_; local($file, $key); if ($args =~ /\S/) { ($file, $key) = &shellwords($args); return unless ($file || $key); } &logedit($VISUAL, $args); print "(continue composing note)\n"; } $editlog = "[file [key]]\tEdit LOGFILE [or older log] with \$EDITOR"; $visuallog = "[file [key]]\tEdit LOGFILE [or older log] with \$VISUAL"; $funcs{'E'} = *editlog; $funcs{'l'} = *editlog; $funcs{'V'} = *visuallog; # Helpuser (~h or ~?): Output a list of tilde escapes with associated # help messages (found in the scalar values of the type globs in %funcs). # Use the defined PAGER if the output would be more than LINES long. # sub helpuser { local($safename); $long = (scalar(keys %funcs) >= $LINES) && open(TMP, ">$TMPFILE"); for (sort keys %funcs) { *info = $funcs{$_}; if ($long) { print TMP "~$_ $info\n"; } else { print "~$_ $info\n"; } } if ($long) { close(TMP); ($safename = $TMPFILE) =~ s/(\W)/\\$1/g; system("$PAGER $safename"); unlink $TMPFILE; } } $helpuser = "\t\tPrint this message"; $funcs{'h'} = *helpuser; $funcs{'?'} = *helpuser; # Printout (~p): cat back the current buffer for review. Use PAGER if # the buffer is longer than LINES. # sub printout { local($bogus) = @_; local($safename); return(&mistake) if ($bogus); if (@lines < $LINES-1 || !open(TMP, ">$TMPFILE")) { print "$SEPARATOR\n"; print @lines; } else { print TMP @lines; close(TMP); ($safename = $TMPFILE) =~ s/(\W)/\\$1/g; system("$PAGER $safename"); unlink $TMPFILE; } print "(continue composing note)\n"; } $printout = "\t\tView contents of buffer, one page at a time"; $funcs{'p'} = *printout; # Pagelog (~P): Page contents of LOGFILE. # sub pagelog { local($args) = @_; local($file, $key); if ($args =~ /\S/) { ($file, $key) = &shellwords($args); return unless ($file || $key); } &pageit($file, $key); print "(continue composing note)\n"; } $pagelog = "[file [key]]\tView contents of LOGFILE [or older log] with PAGER"; $funcs{'P'} = *pagelog; $funcs{'L'} = *pagelog; # Quitit (~q): Quit plod and attempt to save buffer in DEADLOG. Also # called on SIGINT and SIGQUIT via &trapit(). # sub quitit { local($bogus) = @_; return(&mistake) if ($bogus); &PLODBadExit(); } $quitit = "\t\tQuit, attempts to save buffer in \$DEADLOG"; $funcs{'q'} = *quitit; # Readfile (~r): Append contents of file into buffer. # sub readfile { local($args) = @_; local(@args, $file); if ($args =~ /\S/) { @args = &shellwords($args); if (@args != 1) { warn "*** Only one argument allowed.\n"; return; } $file = $args[0]; } else { warn "*** No file name specified.\n"; return; } if (!open(INPT, "<$file")) { warn "*** Unable to open $file.\n"; return; } &readit(INPT, $file); } $readfile = "file\t\tRead contents of file into buffer"; $funcs{'r'} = *readfile; # Writefl (~w): Overwrite file with contents of buffer. To append to a # given file, see &appendfl(). # sub writefl { local($args) = @_; local(@args, $file); if ($args =~ /\S/) { @args = &shellwords($args); if (@args != 1) { warn "*** Only one argument allowed.\n"; return; } $file = $args[0]; } else { warn "*** No file name specified.\n"; return; } if (!open(OUTP, "> $file")) { warn "*** Could not write to file $file\n"; return; } print OUTP @lines; close(OUTP); print "Wrote ", scalar(@lines), " lines to file $file\n"; print "(continue composing note)\n"; } $writefl = "file\t\tOverwrite file with contents of buffer"; $funcs{'w'} = *writefl; # Exitnow (~x): Exit plod without writing to DEADLOG or LOGFILE. # sub exitnow { local($bogus) = @_; return(&mistake) if ($bogus); &PLODNormExit(); } $exitnow = "\t\tExit without saving buffer"; $funcs{'x'} = *exitnow; ########################## End Function Declarations ########################## ############################# Begin Main Program ############################## # Check for /etc/plodrc and ~/.plodrc and eval() contents. Exit with an # error message if eval() complains for any reason. Environment supercedes # /etc/plodrc but is overridden by ~/.plodrc. # if (-e "/etc/plodrc") { eval { do "/etc/plodrc"; }; die "*** Error in /etc/plodrc:\n$@" if $@; } $BACKUP = $ENV{'BACKUP'} if (defined($ENV{'BACKUP'})); $CRYPTCMD = $ENV{'CRYPTCMD'} if (defined($ENV{'CRYPTCMD'})); $DEADLOG = $ENV{'DEADLOG'} if ($ENV{'DEADLOG'}); $PLODRC = $ENV{'PLODRC'} if ($ENV{'PLODRC'}); $EDITOR = $ENV{'EDITOR'} if ($ENV{'EDITOR'}); $HOME = $ENV{'HOME'} if ($ENV{'HOME'}); $KEYVAL = $ENV{'KEYVAL'} if ($ENV{'KEYVAL'}); $LINES = $ENV{'LINES'} if ($ENV{'LINES'}); $LOGDIR = $ENV{'LOGDIR'} if ($ENV{'LOGDIR'}); $LOGFILE = $ENV{'LOGFILE'} if ($ENV{'LOGFILE'}); $PAGER = $ENV{'PAGER'} if ($ENV{'PAGER'}); $PREFIX = $ENV{'PREFIX'} if (defined($ENV{'PREFIX'})); $PROMPT = $ENV{'PROMPT'} if (defined($ENV{'PROMPT'})); $STAMP = $ENV{'STAMP'} if (defined($ENV{'STAMP'})); $TMPFILE = $ENV{'TMPFILE'}if ($ENV{'TMPFILE'}); $VISUAL = $ENV{'VISUAL'} if ($ENV{'VISUAL'}); $SEPARATOR = $ENV{'SEPARATOR'} if ($ENV{'SEPARATOR'}); $SUFFIX = $ENV{'SUFFIX'} if (defined($ENV{'SUFFIX'})); # Set some defaults # $grep = $cat = $edit = $page = ''; $pattern = $file = $key = ''; # Now process arguments as long as the first thing in @ARGV looks like # it might be one. After that, we assume the user is making a one line # log entry. # while ($ARGV[0] =~ /^-\w$/) { $curr = shift @ARGV; if ($curr eq '-f') { &usage unless (@ARGV); $PLODRC = shift @ARGV; } elsif ($curr eq '-s') { $PROMPT = 1; } elsif ($curr eq '-g' || $curr eq '-G') { &usage() if ($grep || $cat || $edit || $page); &usage unless (@ARGV); $grep = $curr; $pattern = shift @ARGV; $file = shift @ARGV unless ($ARGV[0] =~ /^-\w$/); $key = shift @ARGV unless ($ARGV[0] =~ /^-\w$/); } elsif ($curr eq '-E' || $curr eq '-V') { &usage() if ($grep || $cat || $edit || $page); $edit = $curr; $file = shift @ARGV unless ($ARGV[0] =~ /^-\w$/); $key = shift @ARGV unless ($ARGV[0] =~ /^-\w$/); } elsif ($curr eq '-P') { &usage() if ($grep || $cat || $edit || $page); $page = $curr; $file = shift @ARGV unless ($ARGV[0] =~ /^-\w$/); $key = shift @ARGV unless ($ARGV[0] =~ /^-\w$/); } elsif ($curr eq '-C') { &usage() if ($grep || $cat || $edit || $page); $cat = $curr; $file = shift @ARGV unless ($ARGV[0] =~ /^-\w$/); $key = shift @ARGV unless ($ARGV[0] =~ /^-\w$/); } else { &usage(); } } &usage() if (@ARGV && ($grep || $cat || $edit || $page)); # Now do the .plodrc. We have to do this here so the user can # change the location/name of the file with the -f option. # $PLODRC = "$HOME/$PLODRC" unless ($PLODRC =~ /^\.?\.?\//); if (-e $PLODRC) { die "*** $PLODRC is not a regular file.\n" unless (-f $PLODRC); eval { do "$PLODRC"; }; die "*** Error in $PLODRC:\n$@" if $@; } # Prepend parent directories unless we have explicit pathnames # $LOGFILE = "$LOGDIR/$LOGFILE" unless ($LOGFILE =~ /^\.?\.?\//); $DEADLOG = "$HOME/$DEADLOG" unless ($DEADLOG =~ /^\.?\.?\//); $BACKUP = "$HOME/$BACKUP" if ($BACKUP && !($BACKUP =~ /^\.?\.?\//)); # Do a $BACKUP file if $LOGFILE is non-empty. Failure to create a # backup is not a fatal error. # if ($BACKUP && -s $LOGFILE) { ($safelog = $LOGFILE) =~ s/(\W)/\\$1/g; ($safeback = $BACKUP) =~ s/(\W)/\\$1/g; if (system("cp $safelog $safeback") >> 8) { warn "*** Couldn't create backup file $BACKUP.\n"; $BACKUP = ''; } else { chmod(0600, $BACKUP); } } # You can lose your log file if CRYPTCMD is set, but the executable # doesn't actually exist. # &PLODBadExit("There's something wrong with $CRYPTCMD-- I can't deal!\n") if ($CRYPTCMD && !(-e $CRYPTCMD && -x _ && -f _ && -s _)); # Extract dirname from $LOGFILE and make sure it exists # ($dirname = $LOGFILE) =~ s,/[^/]*$,,; if (!(-d $dirname)) { warn "Attempting to create logging directory, $dirname\n"; &PLODBadExit("Attempt failed!\n") unless (mkdir($dirname, 0700)); } # Jam time/date stamp into buffer... # push(@lines, "$STAMP\n") if ($STAMP); # Log entry can appear on the command line, otherwise loop until end of # STDIN or '.' recognized on a line by itself. # if ($page) { &pageit($file, $key); &PLODNormExit(); } elsif ($edit) { &logedit(($edit eq '-E') ? $EDITOR : $VISUAL, $file, $key); &PLODNormExit(); } elsif ($grep) { $insensitive = ($grep eq "-g"); &loggrep($insensitive, $pattern, $file, $key); &PLODNormExit(); } elsif ($cat) { $file = $file || $LOGFILE; $file = "$LOGDIR/$file" unless ($file =~ /^\.?\.?\//); &PLODBadExit("$file does not exist.\n") unless (-f $file); $key = $key || $KEYVAL; if ($CRYPTCMD) { unless (&decrypt($key, $file, $TMPFILE)) { unlink($TMPFILE); &PLODBadExit("*** Can't decrypt $file.\n"); } $file = $TMPFILE; } open(INP, "< $file") || &PLODBadExit("Can't read $file\n"); while () { print; } close(INP); unlink($TMPFILE) if ($CRYPTCMD); &PLODNormExit(); } elsif (@ARGV) { push(@lines, "@ARGV\n"); } else { if (-t STDIN) { print "$STAMP\n" if ($STAMP); $SIG{'QUIT'} = 'trapit'; $SIG{'INT'} = 'trapit'; } while () { if (/^~/) { # escape sequence: ($esc, $args) = /^~(\S)\s*(.*)$/; # 1) parse line *glob = $funcs{$esc}; # 2) unpack type glob if (!defined(&glob)) { # 3) check defined() warn "Unrecognized escape sequence: ~$esc\n"; next; } &glob($args); # 4) call func w/ args } elsif (/^\.\s*$/) { # lone dot means end print "(eot)\n"; # of log entry last; } else { # else append line to push(@lines, $_); # log buffer } } } # Drop out if buffer is empty. Append a final newline if one isn't there. # if (!@lines || (@lines == 1 && $STAMP)) { warn "*** Empty log entry not added to log file\n"; &PLODNormExit(); } $lines[$#lines] = "$lines[$#lines]\n" unless ($lines[$#lines] =~ /\n$/); # Completed log entry now in @lines. If using encryption, call encryption # command to decrypt previous log entries (if present). If not encrypting, # simply open log file to append. # if ($CRYPTCMD) { # encrypting if (-e $LOGFILE) { unless (&decrypt($KEYVAL, $LOGFILE, $TMPFILE)) { unlink($TMPFILE); &PLODBadExit("*** Unable to decrypt logfile.\n"); } } if (!open(LOGFILE, ">> $TMPFILE")) { unlink $TMPFILE; &PLODBadExit("*** Unable to append new log entry\n"); } } else { # not encyrpting if (!open(LOGFILE, ">> $LOGFILE")) { &PLODBadExit("*** Unable to append new log entry\n"); } } # Dump contents of buffer into plain text file. # print LOGFILE "$SEPARATOR\n"; if ($PREFIX || $SUFFIX) { print LOGFILE $lines[0]; chop(@trunc = @lines[1 .. $#lines]); for (@trunc) { print LOGFILE $PREFIX, $_, $SUFFIX, "\n"; } } else { print LOGFILE @lines; } close(LOGFILE); # If encrypting, replace old log file with new version. Unlink plain # text temporary file when done. # if ($CRYPTCMD) { unless (&encrypt($KEYVAL, $TMPFILE, "$LOGFILE.$$")) { unlink("$LOGFILE.$$", $TMPFILE); &PLODBadExit("*** Unable to encrypt log. Previous version restored.\n"); } unless (rename("$LOGFILE.$$", $LOGFILE)) { unlink("$LOGFILE.$$", $TMPFILE); &PLODBadExit("*** rename($LOGFILE.$$, $LOGFILE) failed.\n"); } chmod 0600, $LOGFILE; unlink $TMPFILE; } &PLODNormExit(); ############################## End Main Program ############################### ########################### Miscellaneous Functions ########################### # Append contents of file $fname (associated with file handle $fh) to buffer. # Assume $fh is a pipe if $fname is null. This function called by many tilde # escapes. # sub readit { local($fh, $fname) = @_; push(@lines, <$fh>); print STDOUT ($fname) ? "$fname: " : "Added "; print STDOUT "$. lines"; print STDOUT ($fname) ? "\n" : " to buffer.\n"; print STDOUT "(continue composing note)\n"; close($fh); } # Call the editor $_[0] on the contents of the buffer. Used by &editbuf() # and &visualbuf(). # sub calledit { local($edit) = @_; local($safename); if (!open(EDIT, ">$TMPFILE")) { warn "*** Unable to create file for editing\n"; return; } print EDIT @lines; close(EDIT); chmod 0600, $TMPFILE; ($safename = $TMPFILE) =~ s/(\W)/\\$1/g; system("$edit $safename"); if (!open(EDIT, "<$TMPFILE")) { warn "*** Unable to read changes, returning to previous state.\n"; system("/bin/rm -f $safename*"); return; } undef @lines; @lines = ; close(EDIT); system("/bin/rm -f $safename*"); print "(continue composing note)\n"; } # Call the appropriate editor on a log file. Used by &editlog and &visuallog. # sub logedit { local($edit, $file, $key) = @_; local($safename); $key = $key || $KEYVAL; $file = $file || $LOGFILE; $file = "$LOGDIR/$file" unless ($file =~ /^\.?\.?\//); if ($CRYPTCMD) { if (!(-e $file)) { warn "*** $file does not exist\n"; return; } unless (&decrypt($key, $file, $TMPFILE)) { unlink($TMPFILE); warn "*** Unable to decrypt $file.\n"; return; } chmod 0600, $TMPFILE; ($safename = $TMPFILE) =~ s/(\W)/\\$1/g; system("$edit $safename"); if (!(-e $TMPFILE) || -z _) { warn "*** Modified file is empty-- restoring old version\n"; unlink $TMPFILE; return; } unless (&encrypt($key, $TMPFILE, "$file.$$")) { warn "*** Unable to re-encrypt log file. Changes lost.\n"; unlink("$file.$$", $TMPFILE); return; } unless (rename("$file.$$", $file)) { warn "*** rename($file.$$, $file) failed. Changes lost.\n"; unlink("$file.$$", $TMPFILE); return; } chmod 0600, $file; unlink $TMPFILE; } else { ($safename = $file) =~ s/(\W)/\\$1/g; system("$edit $safename"); } } # Print all entries from a log file which contain a line matching a # given pattern. # sub loggrep { local($insensitive, $pattern, $file, $key) = @_; local(@entry, $print); $pattern =~ tr/[A-Z]/[a-z]/ if ($insensitive); $key = $key || $KEYVAL; $file = $file || $LOGFILE; $file = "$LOGDIR/$file" unless ($file =~ /^\.?\.?\//); if ($CRYPTCMD) { unless (&decrypt($key, $file, $TMPFILE)) { unlink($TMPFILE); warn "*** Unable to decrypt $file.\n"; return; } $file = $TMPFILE; } unless (open(INP, "< $file")) { warn "*** Can't open $file for reading.\n"; return; } $print = 0; undef @entry; while () { if (/^$SEPARATOR$/o) { print @entry if ($print); $print = 0; undef @entry; } push(@entry, $_); tr/[A-Z]/[a-z]/ if ($insensitive); $print = ($print || /$pattern/o); } print @entry if ($print); close(INP); unlink($TMPFILE) if ($CRYPTCMD); } # Page a log file. # sub pageit { local($file, $key) = @_; local($safename); $key = $key || $KEYVAL; $file = $file || $LOGFILE; $file = "$LOGDIR/$file" unless ($file =~ /^\.?\.?\//); if ($CRYPTCMD) { unless (&decrypt($key, $file, $TMPFILE)) { unlink($TMPFILE); warn "*** Unable to decrypt $file.\n"; return; } $file = $TMPFILE; } ($safename = $file) =~ s/(\W)/\\$1/g; system("$PAGER $safename"); unlink($TMPFILE) if ($CRYPTCMD); } # Taken directly from shellwords.pl as provided with the Perl4.036 # distribution. Minor tweaks to exit gracefully. # sub shellwords { package shellwords; local($_) = join('', @_) if @_; local(@words,$snippet,$field); s/^\s+//; while ($_ ne '') { $field = ''; for (;;) { if (s/^"(([^"\\]|\\[\\"])*)"//) { ($snippet = $1) =~ s#\\(.)#$1#g; } elsif (/^"/) { warn "*** Unmatched double quote in arguments.\n"; return (); } elsif (s/^'(([^'\\]|\\[\\'])*)'//) { ($snippet = $1) =~ s#\\(.)#$1#g; } elsif (/^'/) { warn "*** Unmatched single quote in arguments.\n"; return (); } elsif (s/^\\(.)//) { $snippet = $1; } elsif (s/^([^\s\\'"]+)//) { $snippet = $1; } else { s/^\s+//; last; } $field .= $snippet; } push(@words, $field); } @words; } # Generic warning message called by all escapes that do not expect arguments # when @_ is not empty. # sub mistake { warn "*** Arguments are not expected for this escape.\n"; } # Wrapper for &PLODBadExit()-- called on SIGINT and SIGQUIT. Wrapper # required because signal handlers get the signal as an argument, and # this does not correspond with arguments to &PLODBadExit(). # sub trapit { &PLODBadExit(); } # A mundane usage message, and a dumb comment. # sub usage { local($prog); ($prog = $0) =~ s,.*/,,; warn "Usage: $prog [one line log entry]\tor\n"; warn " $prog -C|-E|-V|-P [file [key]]\tor\n"; die " $prog -g|-G pattern [file [key]]\n"; } # Routine called when PLOD exits abnormally. Attempts recover log # from backup if it looks like its been blown away, and attempts to # dump current log buffer into $DEADLOG. Will call user-defined error # routine if present. # sub PLODBadExit { local($msg) = @_; local($stat, $safeback, $safelog); if ($BACKUP && -s $BACKUP && !(-s $LOGFILE)) { ($safeback = $BACKUP) =~ s/(\W)/\\$1/g; ($safelog = $LOGFILE) =~ s/(\W)/\\$1/g; $stat = (system("mv -f $safeback $safelog")>>8) ? "failed!":"succeeded!"; warn "*** $LOGFILE truncated or missing. Restore $stat\n"; } open(DEAD, ">> $DEADLOG") || die "Can't open $DEADLOG\n"; print DEAD @lines; close(DEAD); &on_error() if (defined(&on_error)); die $msg if ($msg); exit(255); } # Routine called when PLOD exits normally. Missing logfile triggers # abnormal exit. Will call user-defined routine if present. # sub PLODNormExit { local($stat); &PLODBadExit("*** Normal exit aborted!\n") if ($BACKUP && -s $BACKUP && !(-s $LOGFILE)); unlink($BACKUP) if ($BACKUP); &on_exit() if (defined(&on_exit)); exit(0); } # encrypt() and decrypt() are provided to allow users to customize their # encryption strategy. UNIX crypt is bidirectional so the functions are # identical, but other encryption mechanisms might not have this property. # sub encrypt { local($key, $inputfl, $outputfl) = @_; local($safekey, $safeinp, $safeout); unlink($outputfl); if ($PROMPT) { # Prompt for $KEYVAL if $PROMPT has been set print "File is $file.\n"; print "Please enter encryption key: "; system 'stty', '-echo'; chop($key = ); system 'stty', 'echo'; print "\n"; } ($safekey = $key) =~ s/(\W)/\\$1/g; ($safeinp = $inputfl) =~ s/(\W)/\\$1/g; ($safeout = $outputfl) =~ s/(\W)/\\$1/g; !(system("$CRYPTCMD $safekey < $safeinp >$safeout") >> 8); } sub decrypt { local($key, $inputfl, $outputfl) = @_; local($safekey, $safeinp, $safeout); unlink($outputfl); if ($PROMPT) { # Prompt for $KEYVAL if $PROMPT has been set print "File is $file.\n"; print "Please enter encryption key: "; system 'stty', '-echo'; chop($key = ); system 'stty', 'echo'; print "\n"; } ($safekey = $key) =~ s/(\W)/\\$1/g; ($safeinp = $inputfl) =~ s/(\W)/\\$1/g; ($safeout = $outputfl) =~ s/(\W)/\\$1/g; !(system("$CRYPTCMD $safekey < $safeinp >$safeout") >> 8); } plod-1.9/plod.1.gz000644 000000 000000 00000013005 10261261762 014061 0ustar00rootwheel000000 000000 BTBplod.man[ms6|+P_o#udyMoJֵuĚ"Yv~s$/fF"h-0rZa2B瞿eM E@i]H%|!\u)NLS1<4^s+v>'_x4Vpo>x] 7;d5~ UU>yAl4=OǗD$"1t穌NB*:-tm*:I_ǫ$KLU*/,yԥ Dw1: C9i`d>;qTW:]˨*75K '.ލ./G »&%L/8&ы4 q FIi*fˬh.)]Yy'#{DyђǑAWc=6NZDa UIZL!D^vmH|Ǥ1˓\ekh2&RVԿd.֮L]2=H2X?#"yD7e;na7+6*AWiˉ.S3~O@|g8.tEB+%F ?TTW< qP8IgwFN&Y.`q2W!U]8$^kA2~# HCIMhUQ,KRʮ]KAw֘XallӺ/\"TQ@c.o6wB*A2 dY i^ eRL0f;Y Qc͟FA4e@t@ གE-8`REetĜMԪbfam8ܡ*\'u=3m{10|A"9Y#LGc<46VF#2>uB6" m-y]k ,e|TcAQ^ZQpNQcb6\'Ny 8ð({  @ʩĕ`)2%"^;R;??<;;ǁ|d(sԊ(M+<z@" (vX?'kg]67`B sTpA:#ejŽFq*N,y ӈ?(xNS7aj6am4Tz8KCRRg}bj͍xPf9pӆ'1(64\.&6oiDHQf:ܸ.+W+ϙֱqjl~r$ľpX YfX蔶6uZtA̝b𞋚pjmRKPBmQꈈ"O,.6AS[q2[N;MrvѮNO~'o>[64\T+@({ʖs 8 S5Jw!ּUpEa|wxrf -`>,휍ϰ q>Tuzpl\Niӵ霣YjQjM/3uÖ6p[l?;z'^ULlIŦQI}[Kꥭ5k": تUqH¿,fI.b}Q-ѭ3j+ ;z :bv.="!MT(cUlZuBن+5ɿ)u[E M8u}fot-%p8(Y>/X7F_-5&Μli~S$,<Mvs3ftz o'i+rwtO}3ȟXD$SOupˀwң-қfBeZuZ |D{YNiNv~iJ308F{S*p;' cj.hn|+l@VIx෺'U uJQ%#`C-FMսvI1&sYmu d xvI?"a ƀ# !qyˇ2\K=iz!{!"lv赤ׁ1rܜFw##QjOp.rD SsB2K|傜=1r~ A\+@Q+T[VҬ8e^P(yISem`" ) )8{a<,]#XH_!IR]e݀1O,歂Nr%Q*Gr =E"{K:w38p}|bفMfƃ VS^+Vt *q9]^v`!m LR=my*YދT 'o3ܳjM ƽ1ܥ Mە3} jq-mT[^?Ep? ]yFHMWaPP&&y y(cFY6 qtI%+_}zsJ~ 3$nLG 6lb>lLwm!j9`hkm\՘9EΠ;¡ISgIי^\^{\W{Ew\I .X<~oO>({ ǫ >(|y]+G ?V\fs5,*5isGMDKr"ѠE˘LtI = 4-_Oh`4׎s,Dd=+X۔wUNN#}:v2CX AՄ*5< nJ#[' KmCxܺjV&4065ilbc4WE7'%|&O)$(=DCv׉6mit+iMẚOStxʷ>7`ͱYh]Hɱ?߁gmS|r}_#k;>aҰ!޾F__-'V$}J1n'왆_Cf4{>GlE݊lt"Z?gKfz}6xjfgs х`9~?{0H9u|t(Uvkg=too߷e7*KOs/_?6gv1'p4^Qߔ)r\7+]j[ UQHNiv.>;UO&3|j>5o˺o~dPZ ^uV63& 12y[щ, $鎽׹=ry]A!"Oʤ#ml5kDm[ToG4)z>FƦkUtmum|ї.Y̞Bg3[I6)/YubX{?S灱~pywoGv{ kǠ$V,ĦٳHv但E= )Y0F'0fq5atڭӾnŎ_kDP|hmE4A!oROe7/p@%~> Xѧ}{v2lm=ͥ.Jp I lvo.g^mcswEW{ ;#o'فb>S/ݞ]ЅȞ?.f^}H\,7[M;9F0+sx Όٙ!m^`YƅW_}ZjHl~?kVsЃ#Ӡ!cn9öMίr8xt4obxrEe߾:~si"acx3=@*?!,ra~>eU M sKn< LXߘRGAYga]?.vTfGI e87gyWMNTl+釂|$[Xh(7> z.NV" '3zzG9g U~/OK5>\g!HW;V !4'FW9t:<=/!Q0bxOGeeU 8!a/#sosU_(rRIIJrtD?n seconds for wakeup (int-to-string (* 60 interval))))) (process-kill-without-query plod-alarm-process) (set-process-sentinel plod-alarm-process 'plod-alarm-sentinel) (set-process-filter plod-alarm-process 'plod-alarm-filter))))) ;; Turn PLOD alarm off ;; (defun plod-alarm-off () "Turn the Emacs PLOD alarm off." (interactive) (if plod-alarm-on-p (kill-process plod-alarm-process))) ;;; End