home *** CD-ROM | disk | FTP | other *** search
- Newsgroups: comp.lang.perl
- Path: sparky!uunet!stanford.edu!eos!data.nas.nasa.gov!irving.nas.nasa.gov!pomeranz
- From: pomeranz@irving.nas.nasa.gov (Hal R. Pomeranz)
- Subject: PLOD v1.1 (whoops!)
- Sender: news@nas.nasa.gov (News Administrator)
- Organization: NAS, NASA Ames Research Center, Moffett Field, California
- Date: Sat, 23 Jan 93 00:52:51 GMT
- Message-ID: <1993Jan23.005251.14057@nas.nasa.gov>
- Lines: 525
-
-
- [If that last v1.1 I posted managed to get out past my cancel somehow,
- please ignore it-- a small buglet crept in due to lazy editing on my
- part. Here's a good version. --Hal]
-
- This is v1.1 of PLOD, the Personal LOgging Device.
-
- This version is essentially a bugfix release. Useful fixes and
- suggestions were contributed by Erik E. Rantapaa
- (rantapaa@math.umn.edu) and John Ellis (ellis@rtsg.mot.com).
-
- ** &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)
-
- Thanks to everybody for the comments (and to Paul Foley (paul@ascent.com)
- for the Emacs mode). Keep those bug reports and comments coming in.
-
- ===============================================================================
- Hal Pomeranz Senior Member of Technical Staff NASA Ames Research Center
- pomeranz@nas.nasa.gov pomeranz@cs.swarthmore.edu
- "I sit and stare with hardened eyes through a time when life's so hard." --L&O
- ===============================================================================
-
- #!/usr/nas/bin/perl
- #
- # PLOD-- Personal LOgging Device, v1.1
- # Copyright (C), 1993, Hal Pomeranz (pomeranz@nas.nasa.gov)
- # 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: Erik E. Rantapaa (rantapaa@math.umn.edu)
- # John Ellis (ellis@rtsg.mot.com)
- #
- # 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 exhorbitant salary (any
- # amount of money they may be paying you being classified as "exhorbitant"),
- # 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 supercedes 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 function declarations section below.
-
- ######################### 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
- # 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
- # HOME user's home directory
- # DEADLOG place to drop dead.log file on abort or ~q, also used by ~d
-
- # Some variable values use date/time information
- #
- ($ss, $mm, $hh, $DD, $MM, $YY) = localtime($^T); $MM++;
-
- $KEYVAL = $ENV{'KEYVAL'} || sprintf("pl%d%dod", $YY, $MM);
- $CRYPTCMD = defined($ENV{'CRYPTCMD'})? $ENV{'CRYPTCMD'} : "/bin/crypt $KEYVAL";
- $TMPFILE = $ENV{'TMPFILE'} || "/tmp/plodtmp$$";
- $HOME = $ENV{'HOME'} || (getpwuid($<))[7];
- $EDITOR = $ENV{'EDITOR'} || "/usr/local/bin/emacs";
- $VISUAL = $ENV{'VISUAL'} || "/usr/local/bin/emacs";
- $PAGER = $ENV{'PAGER'} || "/usr/local/bin/less";
- $LINES = $ENV{'LINES'} || 24;
- $LOGDIR = $ENV{'LOGDIR'} || "$HOME/.logdir";
- $LOGFILE = $ENV{'LOGFILE'} || sprintf("%02d%02d", $YY, $MM);
- $DEADLOG = $ENV{'DEADLOG'} || "dead.log";
-
-
- ########################## 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) = @_;
- local($count);
- 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; # 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 = <INP>; # contents of tmp file
- close(INP);
- unlink "$TMPFILE";
- unshift(@lines, $header);
- print "(continue composing note)\n";
- }
- $pipetocmd = "cmdline\tPipe contents of buffer through cmdline";
- $funcs{'|'} = *pipetocmd;
-
-
- # Perlit (~P): 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{'P'} = *perlit;
-
-
- # 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($file) = @_;
- 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 $home/dead.log.\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;
-
-
- # 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 {
- $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);
- system("/bin/cat $TMPFILE | $PAGER");
- 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) = @_;
- return(&mistake) if ($bogus);
- if (@lines < $LINES-1 || !open(TMP, ">$TMPFILE")) {
- print "-----\n";
- print @lines;
- }
- else {
- print TMP @lines;
- close(TMP);
- system("/bin/cat $TMPFILE | $PAGER");
- unlink "$TMPFILE";
- }
- print "(continue composing note)\n";
- }
- $printout = "\t\tView contents of buffer, one page at a time";
- $funcs{'p'} = *printout;
-
-
- # 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);
- open(DEAD, ">> $DEADLOG") || die "Can't open $DEADLOG\n";
- print DEAD @lines;
- close(DEAD);
- exit;
- }
- $quitit = "\t\tQuit, attempts to save buffer in \$DEADLOG";
- $funcs{'q'} = *quitit;
-
-
- # Readfile (~r): Append contents of file into buffer.
- #
- sub readfile {
- local($file) = @_;
- 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($file) = @_;
- 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);
- exit;
- }
- $exitnow = "\t\tExit without saving buffer";
- $funcs{'x'} = *exitnow;
-
-
- ########################## End Function Declarations ##########################
- ############################# Begin Main Program ##############################
-
-
- # Check for ~/.plodrc and eval() contents. Exit with an error message if
- # eval() complains for any reason.
- #
- if (-e "$HOME/.plodrc") {
- eval { do "$HOME/.plodrc"; };
- die "*** Error in $HOME/.plodrc:\n$@" if $@;
- }
-
- # Prepend parent directories unless we have explicit pathnames
- #
- $LOGFILE = "$LOGDIR/$LOGFILE" unless ($LOGFILE =~ /^\//);
- $DEADLOG = "$HOME/$DEADLOG" unless ($DEADLOG =~ /^\//);
-
- # Extract dirname from $LOGFILE and make sure it exists
- #
- ($dirname = $LOGFILE) =~ s,/[^/]*$,,;
- if (!(-d $dirname)) {
- warn "Attempting to create logging directory, $dirname\n";
- die "Attempt failed!\n" unless (mkdir($dirname, 0700));
- }
-
- # Trap SIGQUIT and SIGINT. &trapit() is really just a wrapper for &quitit().
- #
- $SIG{'QUIT'} = trapit;
- $SIG{'INT'} = trapit;
-
- # Create time/date stamp and make it the first line of the buffer.
- #
- ($ss, $mm, $hh, $DD, $MM, $YY) = localtime($^T); $MM++;
- $stamp = sprintf("%02d/%02d/%02d, %02d:%02d --", $MM, $DD, $YY, $hh, $mm);
- push(@lines, "$stamp\n");
-
- # Log entry can appear on the command line, otherwise loop until end of
- # STDIN or '.' recognized on a line by itself.
- #
- if (@ARGV) { push(@lines, "@ARGV\n"); }
- else {
- print "$stamp\n" if (-t STDIN);
- while (<STDIN>) {
- 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
- }
- }
- }
-
- # 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") {
- system "$CRYPTCMD <$LOGFILE >$TMPFILE";
- }
- if (!open(LOGFILE, ">> $TMPFILE")) {
- unlink "$TMPFILE";
- warn "*** Unable to append new log entry\n";
- &quitit();
- }
- }
- else { # not encyrpting
- if (!open(LOGFILE, ">> $LOGFILE")) {
- warn "*** Unable to append new log entry\n";
- &quitit();
- }
- }
-
- # Dump contents of buffer into plain text file.
- #
- print LOGFILE "-----\n";
- print LOGFILE @lines;
- close(LOGFILE);
-
- # If encrypting, replace old log file with new version. Unlink plain
- # text temporary file when done.
- #
- if ($CRYPTCMD) {
- unlink "$LOGFILE";
- system "$CRYPTCMD <$TMPFILE >$LOGFILE";
- chmod 0600, "$LOGFILE";
- unlink "$TMPFILE";
- }
-
-
- ############################## 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) = @_;
- if (!open(EDIT, ">$TMPFILE")) {
- warn "*** Unable to create file for editing\n";
- return;
- }
- print EDIT @lines;
- close(EDIT);
- chmod 0600, "$TMPFILE";
- system "$edit $TMPFILE";
- if (!open(EDIT, "<$TMPFILE")) {
- warn "*** Unable to read changes, returning to previous state.\n";
- system "/bin/rm -f $TMPFILE*";
- return;
- }
- undef @lines;
- @lines = <EDIT>;
- close(EDIT);
- system "/bin/rm -f $TMPFILE*";
- print "(continue composing note)\n";
- }
-
-
- # 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 &quitit()-- called on SIGINT and SIGQUIT. Wrapper required
- # because signal handlers get the signal as an argument and &quitit() does
- # not appreciate arguments.
- #
- sub trapit {
- &quitit();
- }
-