#!/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); }