Article 6045 of comp.lang.perl: Xref: feenix.metronet.com comp.lang.perl:6045 Newsgroups: comp.lang.perl Path: feenix.metronet.com!news.utdallas.edu!convex!convex!convex!cs.utexas.edu!uunet!imagen!pomeranz From: pomeranz@imagen.com (Hal Pomeranz) Subject: PLOD (v1.6) Message-ID: <1993Sep22.162240.9080@imagen.com> Followup-To: poster Sender: usenet@imagen.com Nntp-Posting-Host: cockroach Organization: QMS Inc., Imagen Division Date: Wed, 22 Sep 1993 16:22:40 GMT Lines: 1215 Greetings, PLOD enthusiasts! Your humble author is releasing this latest version in preparation for LISA VII (the paper I just submitted reads in part, "The latest version of PLOD was recently submitted to comp.lang.perl"). Not a whole lot has changed since the last public release. I've added support for a system-wide /etc/plodrc file, and a customization mechanism for the time/date stamp since most of the world uses DD/MM/YY instead of MM/DD/YY. The log file format is still the same, so no worries on that score. I'd also like to take some of your experiences with me to LISA. Please email any PLOD success stories, humorous anecdotes, or death threats to me, and I'll include a few of the choicer ones in my presentation. Please let me know if you specifically do not want your name or your organization's name mentioned-- otherwise everybody gets full attribution. I'll also be setting up a PLOD archive in the near future on gatekeeper.imagen.com (161.33.3.1) in the directory /pub/plod. I plan to keep a copy of the very latest version in this directory at all times. I'll also keep things like my LISA presentation and Paul Foley's Emacs mode for PLOD in here as well. ============================================================================== Hal Pomeranz pomeranz@imagen.com pomeranz@cs.swarthmore.edu System/Network Manager "We are islands to each other, building QMS Imagen Division hopeful bridges on a troubled sea." --Rush ============================================================================== #!/bin/sh # This is a shell archive (produced by shar 3.50) # To extract the files from this archive, save it to a file, remove # everything above the "!/bin/sh" line above, and type "sh file_name". # # made 09/12/1993 16:40 UTC by pomeranz@imagen # Source directory /tmp_mnt/home/cockroach/pomeranz/programming.dir/plod # # existing files will NOT be overwritten unless -c is specified # # This shar contains: # length mode name # ------ ---------- ------------------------------------------ # 4069 -rw-r--r-- README # 16947 -rwxr-xr-x plod # 9043 -rw-r--r-- plod.man # 3523 -rw-r--r-- plod.el # # ============= README ============== if test -f 'README' -a X"$1" != X"-c"; then echo 'x - skipping README (File already exists)' else echo 'x - extracting README (Text)' sed 's/^X//' << 'SHAR_EOF' > 'README' && 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. X 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. X 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. X 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. Share and enjoy! X Hal Pomeranz, pomeranz@aqm.com X =============================================================================== X History: X v1.0 (Original release to comp.lang.perl) X X v1.1 (Second release to comp.lang.perl) ** &pipetocmd() unlinks all temporary files it creates (Pomeranz) X ** Variable assignment idiom cleaned up (Rantapaa) X ** LOGDIR and HOME are only prepended to LOGFILE and DEADLOG after X .plodrc has been eval-ed and if LOGFILE and DEADLOG are relative X paths. This means that you can change LOGDIR in your .plodrc and X really affect where the log files go. (Rantapaa) X ** eval of .plodrc is done with "do" rather than "cat" (Rantapaa) X ** You can now do quick one-liner entries on the command line, e.g. X "plod Completed modifications to PLOD" (Rantapaa) X ** Time/date stamp only printed if user is entering info directly from X a tty (Rantapaa) X ** PLOD attempts to create logging directory if it does not exist (Ellis) X X v1.2 (not publicly released) ** Page/Edit/Visual log files from interactive mode or from the shell X (Tizard) X X v1.3 (comp.lang.perl again, and also comp.sources.misc) ** Multi-line Perl input function, ~M (Crabb) X ** Manual page generated (Pomeranz) X X v1.4 (not publicly released) ** Trap for empty log entries and don't add them to log file (Pomeranz) X ** -C flag to cat logfile to STDOUT (Prestemon) X ** /etc/plodrc (Lachowski) X ** $STAMP customization variable (Lachowski/Pomeranz) X X v1.5 (not publicly released) ** Trap for non-existant/zero size/non-executable CRYPTCMD X X v1.6 (current) ** Minor fix to above trap. Would have been part of v1.5 but I had already X distributed v1.5 to a few people. SHAR_EOF chmod 0644 README || echo 'restore of README failed' Wc_c="`wc -c < 'README'`" test 4069 -eq "$Wc_c" || echo 'README: original size 4069, current size' "$Wc_c" fi # ============= plod ============== if test -f 'plod' -a X"$1" != X"-c"; then echo 'x - skipping plod (File already exists)' else echo 'x - extracting plod (Text)' sed 's/^X//' << 'SHAR_EOF' > 'plod' && #!/usr/local/bin/perl # # PLOD-- Personal LOgging Device, v1.6 # Copyright (C), 1993, Hal Pomeranz (pomeranz@aqm.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: # David W Crabb (crabb@phoenix.Princeton.EDU) # John Ellis (ellis@rtsg.mot.com) # Mike Lachowski (mlachow@erenj.com) # Eric Prestemon (ecprest@pocorvares.er.usgs.GOV) # Erik E. Rantapaa (rantapaa@math.umn.edu) # James Tizard (james@ringo.ssn.flinders.edu.au) # ######################### 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 # STAMP time/date stamp printed at the top of each entry X # Some variable values use date/time information # ($ss, $mm, $hh, $DD, $MM, $YY) = localtime($^T); $MM++; X $KEYVAL = sprintf("pl%d%dod", $YY, $MM); $CRYPTCMD = "/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("%02d%02d", $YY, $MM); $DEADLOG = "dead.log"; $STAMP = sprintf("%02d/%02d/%02d, %02d:%02d --", $MM, $DD, $YY, $hh, $mm); X X ########################## End Variable Declarations ########################## ######################### Begin Function Declarations ######################### X X # Printvar (~=): Output the value of one or more variables. # sub printvar { X local($vars) = @_; X $, = ','; print eval "($vars)"; $, = ''; X print "\n"; X print "(continue composing note)\n"; } $printvar = "\$var[, ...]\tOutput value of variables"; $funcs{'='} = *printvar; X X # Bang (~!): Execute a command in the shell and then return to plod. # sub bang { X local($cmdline) = @_; X system "$cmdline"; X print "(continue composing note)\n"; } $bang = "cmdline\tExecute system command and return"; $funcs{'!'} = *bang; X X # Redirect (~>): Pipe the output of a command into the current buffer. # sub redirect { X local($cmdline) = @_; X local($count); X if (!open(CMD, "$cmdline |")) { X warn "*** Unable to execute: $cmdline\n"; X return; X } X &readit(CMD); } $redirect = "cmdline\tAdd output of given command to buffer"; $funcs{'>'} = *redirect; X X # Pipetocmd (~|): Pipe the contents of the current buffer through a UNIX # command line and replace the buffer with the result. # sub pipetocmd { X local($cmdline) = @_; X local($header); X if (!open(PIPELN, "| $cmdline >$TMPFILE 2>&1")) { # output to tmp file X warn "*** Unable to execute: $cmdline\n"; X return; X } X $header = shift @lines if ($STAMP); # don't include stamp X print PIPELN @lines; X close(PIPELN); X if (!open(INP, "<$TMPFILE")) { X warn "*** Unable to get command output\n"; X unshift(@lines, $header); X unlink "$TMPFILE"; X return; X } X undef @lines; # replace buffer with X @lines = ; # contents of tmp file X close(INP); X unlink "$TMPFILE"; X unshift(@lines, $header) if ($STAMP); X print "(continue composing note)\n"; } $pipetocmd = "cmdline\tPipe contents of buffer through cmdline"; $funcs{'|'} = *pipetocmd; X X # Perlit (~X): Execute Perl code. # sub perlit { X local($code) = @_; X eval "$code"; X warn $@ if $@; X print "(continue composing note)\n"; } $perlit = "code\t\tExecute a line of Perl code"; $funcs{'X'} = *perlit; X X # Longperl (~M): Edit then eval a multi-line Perl fragment # sub longperl { X local($bogus) = @_; X return(&mistake) if ($bogus); X if (@code) { X if (!open(TMP, "> $TMPFILE")) { X warn "*** Unable to create temporary file\n"; X return; X } X print TMP @code; X close(TMP); X } X system("$EDITOR $TMPFILE"); X if (!open(TMP, "< $TMPFILE")) { X warn "*** Unable to read buffer\n"; X return; X } X undef @code; X @code = ; X close(TMP); X system("/bin/rm -f $TMPFILE*"); X eval "@code"; X warn $@ if $@; X print "(continue composing note)\n"; } $longperl = "\t\tInvoke \$EDITOR on command buffer, then execute as Perl code"; $funcs{'M'} = *longperl; X X # 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 { X local($file) = @_; X if (!open(OUTP, ">> $file")) { X warn "*** Could not append to file $file\n"; X return; X } X print OUTP @lines; X close(OUTP); X print "Wrote ", scalar(@lines), " lines to file $file\n"; X print "(continue composing note)\n"; } $appendfl = "file\t\tAppend contents of buffer to file"; $funcs{'a'} = *appendfl; X X # Getdead (~d): Suck contents of DEADLOG file into buffer. # sub getdead { X local($bogus) = @_; X return(&mistake) if ($bogus); X if (!open(DEAD, "<$DEADLOG")) { X warn "*** Unable to open $home/dead.log.\n"; X return; X } X &readit(DEAD, "$DEADLOG"); } $getdead = "\t\tIncorporate contents of \$DEADLOG into buffer"; $funcs{'d'} = *getdead; X X # Editbuf (~e) and Visualbuf (~v): Call appropriate editor on buffer. # sub editbuf { X local($bogus) = @_; X return(&mistake) if ($bogus); X &calledit($EDITOR); } sub visualbuf { X local($bogus) = @_; X return(&mistake) if ($bogus); X &calledit($VISUAL); } $editbuf = "\t\tEdit buffer with \$EDITOR"; $visualbuf = "\t\tEdit buffer with \$VISUAL"; $funcs{'e'} = *editbuf; $funcs{'v'} = *visualbuf; X X # Editlog (~E) and Visuallog (~V): Call appropriate editor on LOGFILE. # sub editlog { X local($args) = @_; X &logedit($EDITOR, $args); X print "(continue composing note)\n"; } sub visuallog { X local($args) = @_; X &logedit($VISUAL, $args); X 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; X X # 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 { X $long = (scalar(keys %funcs) >= $LINES) && open(TMP, ">$TMPFILE"); X for (sort keys %funcs) { X *info = $funcs{$_}; X if ($long) { X print TMP "~$_ $info\n"; X } X else { print "~$_ $info\n"; } X } X if ($long) { X close(TMP); X system("/bin/cat $TMPFILE | $PAGER"); X unlink "$TMPFILE"; X } } $helpuser = "\t\tPrint this message"; $funcs{'h'} = *helpuser; $funcs{'?'} = *helpuser; X X # Printout (~p): cat back the current buffer for review. Use PAGER if # the buffer is longer than LINES. # sub printout { X local($bogus) = @_; X return(&mistake) if ($bogus); X if (@lines < $LINES-1 || !open(TMP, ">$TMPFILE")) { X print "-----\n"; X print @lines; X } X else { X print TMP @lines; X close(TMP); X system("/bin/cat $TMPFILE | $PAGER"); X unlink "$TMPFILE"; X } X print "(continue composing note)\n"; } $printout = "\t\tView contents of buffer, one page at a time"; $funcs{'p'} = *printout; X X # Pagelog (~P): Page contents of LOGFILE. # sub pagelog { X local($args) = @_; X &pageit($args); X print "(continue composing note)\n"; } $pagelog = "[file [key]]\tView contents of LOGFILE [or older log] with PAGER"; $funcs{'P'} = *pagelog; $funcs{'L'} = *pagelog; X X # Quitit (~q): Quit plod and attempt to save buffer in DEADLOG. Also # called on SIGINT and SIGQUIT via &trapit(). # sub quitit { X local($bogus) = @_; X return(&mistake) if ($bogus); X open(DEAD, ">> $DEADLOG") || die "Can't open $DEADLOG\n"; X print DEAD @lines; X close(DEAD); X exit; } $quitit = "\t\tQuit, attempts to save buffer in \$DEADLOG"; $funcs{'q'} = *quitit; X X # Readfile (~r): Append contents of file into buffer. # sub readfile { X local($file) = @_; X if (!open(INPT, "<$file")) { X warn "*** Unable to open $file.\n"; X return; X } X &readit(INPT, $file); } $readfile = "file\t\tRead contents of file into buffer"; $funcs{'r'} = *readfile; X X # Writefl (~w): Overwrite file with contents of buffer. To append to a # given file, see &appendfl(). # sub writefl { X local($file) = @_; X if (!open(OUTP, "> $file")) { X warn "*** Could not write to file $file\n"; X return; X } X print OUTP @lines; X close(OUTP); X print "Wrote ", scalar(@lines), " lines to file $file\n"; X print "(continue composing note)\n"; } $writefl = "file\t\tOverwrite file with contents of buffer"; $funcs{'w'} = *writefl; X X # Exitnow (~x): Exit plod without writing to DEADLOG or LOGFILE. # sub exitnow { X local($bogus) = @_; X return(&mistake) if ($bogus); X exit; } $exitnow = "\t\tExit without saving buffer"; $funcs{'x'} = *exitnow; X X ########################## End Function Declarations ########################## ############################# Begin Main Program ############################## X X # 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") { X eval { do "/etc/plodrc"; }; X die "*** Error in /etc/plodrc:\n$@" if $@; } X $CRYPTCMD = $ENV{'CRYPTCMD'} if (defined($ENV{'CRYPTCMD'})); $DEADLOG = $ENV{'DEADLOG'} if ($ENV{'DEADLOG'}); $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'}); $STAMP = $ENV{'STAMP'} if (defined($ENV{'STAMP'})); $TMPFILE = $ENV{'TMPFILE'}if ($ENV{'TMPFILE'}); $VISUAL = $ENV{'VISUAL'} if ($ENV{'VISUAL'}); X if (-e "$HOME/.plodrc") { X eval { do "$HOME/.plodrc"; }; X die "*** Error in $HOME/.plodrc:\n$@" if $@; } X # You can lose your log file if CRYPTCMD is set, but the executable # doesn't actually exist. # die "There's something wrong with $CRYPTCMD-- I can't deal!\n" X if ($CRYPTCMD && !(-e "$CRYPTCMD" && -x _ && -f _ && -s _)); X # Prepend parent directories unless we have explicit pathnames # $LOGFILE = "$LOGDIR/$LOGFILE" unless ($LOGFILE =~ /^\//); $DEADLOG = "$HOME/$DEADLOG" unless ($DEADLOG =~ /^\//); X # Extract dirname from $LOGFILE and make sure it exists # ($dirname = $LOGFILE) =~ s,/[^/]*$,,; if (!(-d $dirname)) { X warn "Attempting to create logging directory, $dirname\n"; X die "Attempt failed!\n" unless (mkdir($dirname, 0700)); } X # Jam time/date stamp into buffer... # push(@lines, "$STAMP\n") if ($STAMP); X # Log entry can appear on the command line, otherwise loop until end of # STDIN or '.' recognized on a line by itself. # if ($ARGV[0] eq "-P") { shift @ARGV; &pageit("@ARGV"); exit; } elsif ($ARGV[0] eq "-E") { shift @ARGV; &logedit($EDITOR, "@ARGV"); exit; } elsif ($ARGV[0] eq "-V") { shift @ARGV; &logedit($VISUAL, "@ARGV"); exit; } elsif ($ARGV[0] eq "-C") { X shift @ARGV; X $file = (shift @ARGV) || $LOGFILE; X $file = "$LOGDIR/$file" unless ($file =~ /^\//); X $key = (shift @ARGV) || $KEYVAL; X $cmd = $CRYPTCMD ? "$CRYPTCMD $key < $file" : "/bin/cat $file"; X system("$cmd"); X exit; } elsif (@ARGV) { push(@lines, "@ARGV\n"); } else { X if (-t STDIN) { X print "$STAMP\n" if ($STAMP); X $SIG{'QUIT'} = trapit; X $SIG{'INT'} = trapit; X } X while () { X if (/^~/) { # escape sequence: X ($esc, $args) = /^~(\S)\s*(.*)$/; # 1) parse line X *glob = $funcs{$esc}; # 2) unpack type glob X if (!defined(&glob)) { # 3) check defined() X warn "Unrecognized escape sequence: ~$esc\n"; X next; X } X &glob($args); # 4) call func w/ args X } X elsif (/^\.\s*$/) { # lone dot means end X print "(eot)\n"; # of log entry X last; X } X else { # else append line to X push(@lines, $_); # log buffer X } X } } X # Drop out if buffer is empty. Append a final newline if one isn't there. # if (!@lines || (@lines == 1 && $STAMP)) { X warn "*** Empty log entry not added to log file\n"; X exit; } $lines[$#lines] = "$lines[$#lines]\n" unless ($lines[$#lines] =~ /\n$/); X # 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 X if (-e "$LOGFILE") { X system "$CRYPTCMD $KEYVAL <$LOGFILE >$TMPFILE"; X } X if (!open(LOGFILE, ">> $TMPFILE")) { X unlink "$TMPFILE"; X warn "*** Unable to append new log entry\n"; X &quitit(); X } } else { # not encyrpting X if (!open(LOGFILE, ">> $LOGFILE")) { X warn "*** Unable to append new log entry\n"; X &quitit(); X } } X # Dump contents of buffer into plain text file. # print LOGFILE "-----\n"; print LOGFILE @lines; close(LOGFILE); X # If encrypting, replace old log file with new version. Unlink plain # text temporary file when done. # if ($CRYPTCMD) { X unlink "$LOGFILE"; X system "$CRYPTCMD $KEYVAL <$TMPFILE >$LOGFILE"; X chmod 0600, "$LOGFILE"; X unlink "$TMPFILE"; } X X ############################## End Main Program ############################### ########################### Miscellaneous Functions ########################### X X # 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 { X local($fh, $fname) = @_; X push(@lines, <$fh>); X print STDOUT ($fname) ? "$fname: " : "Added "; X print STDOUT "$. lines"; X print STDOUT ($fname) ? "\n" : " to buffer.\n"; X print STDOUT "(continue composing note)\n"; X close($fh); } X X # Call the editor $_[0] on the contents of the buffer. Used by &editbuf() # and &visualbuf(). # sub calledit { X local($edit) = @_; X if (!open(EDIT, ">$TMPFILE")) { X warn "*** Unable to create file for editing\n"; X return; X } X print EDIT @lines; X close(EDIT); X chmod 0600, "$TMPFILE"; X system "$edit $TMPFILE"; X if (!open(EDIT, "<$TMPFILE")) { X warn "*** Unable to read changes, returning to previous state.\n"; X system "/bin/rm -f $TMPFILE*"; X return; X } X undef @lines; X @lines = ; X close(EDIT); X system "/bin/rm -f $TMPFILE*"; X print "(continue composing note)\n"; } X X # Call the appropriate editor on a log file. Used by &editlog and &visualedit. # sub logedit { X local($edit, $args) = @_; X local($file, $key) = split(/\s+/, $args); X $key = $key || $KEYVAL; X $file = $file || $LOGFILE; X $file = "$LOGDIR/$file" unless ($file =~ /^\//); X if ($CRYPTCMD) { X if (!(-e "$file")) { X warn "*** $file does not exist\n"; X return; X } X system("$CRYPTCMD $key <$file >$TMPFILE"); X chmod 0600, "$TMPFILE"; X system("$edit $TMPFILE"); X if (!(-e "$TMPFILE") || -z _) { X warn "*** Modified file is empty-- restoring old version\n"; X unlink "$TMPFILE"; X return; X } X unlink "$file"; X system "$CRYPTCMD $key <$TMPFILE >$file"; X chmod 0600, "$file"; X unlink "$TMPFILE"; X } X else { system("$edit $file"); } } X X # Page a log file. # sub pageit { X local($args) = @_; X local($file, $key) = split(/\s+/, $args); X local($cmd); X $key = $key || $KEYVAL; X $file = $file || $LOGFILE; X $file = "$LOGDIR/$file" unless ($file =~ /^\//); X $cmd = $CRYPTCMD ? "$CRYPTCMD $key < $file" : "/bin/cat $file"; X system("$cmd | $PAGER"); } X X # Generic warning message called by all escapes that do not expect arguments # when @_ is not empty. # sub mistake { X warn "*** Arguments are not expected for this escape.\n"; } X X # 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 { X &quitit(); } SHAR_EOF chmod 0755 plod || echo 'restore of plod failed' Wc_c="`wc -c < 'plod'`" test 16947 -eq "$Wc_c" || echo 'plod: original size 16947, current size' "$Wc_c" fi # ============= plod.man ============== if test -f 'plod.man' -a X"$1" != X"-c"; then echo 'x - skipping plod.man (File already exists)' else echo 'x - extracting plod.man (Text)' sed 's/^X//' << 'SHAR_EOF' > 'plod.man' && .nh .de Sp .if t .sp .5v .if n .sp .. .de Ip .br .ie \\n.$>=3 .ne \\$3 .el .ne 3 .IP "\\$1" \\$2 .. .TH PLOD 1 "2 February 1993" "PLOD" .SH NAME PLOD \- keep a log of your work .SH SYNOPSIS .B plod \ [\ \fIone line message\fR\ ] .br .B plod \ \fB-E\fR|\fB-P\fR|\fB-V\fR\ [\ \fIlogfile\fR\ [\ \fIkey\fR\ ]] .SH DESCRIPTION \fBPLOD\fR is a tool developed to help System/Network Administrators (and others) keep track of the work that they do. Good logs are useful both as a personal reference and to show to your management on a regular basis or around performance review time. By default, logs will be stored in an encrypted format in the directory \fI$HOME/.logdir\fR, but this behavior is completely customizable (see \fBENVIRONMENT\fR and \fBCUSTOMIZATION\fR below). X The first form of the command will enter a short message on the command line into your log file. If no message is present on the command line, a date/time stamp will be printed and \fBPLOD\fR will go into an interactive mode reminiscent of BSD mail. Many tilde escape sequences are supported (see \fBCOMMANDS\fR below or type \fI~h\fR or \fI~?\fR within interactive mode). Enter a period on a line by itself to end your log entry. X The second mode allows you to review or edit your old log files. The \fB-P\fR option invokes the default \fBPAGER\fR defined in the \fBPLOD\fR source, or as defined in your environment, on the current log file. The \fB-E\fR and \fB-V\fR flags invoke \fBEDITOR\fR and \fBVISUAL\fR respectively. Older log files may be accessed by specifying a file name and optional encryption key on the command line. X .SH ENVIRONMENT \fBPLOD\fR supports a number of variables which can be modified to customize its behavior. The values of these variables may be changed by editing \fBPLOD\fR directly, by assignment in a system-wide startup file, by creating an environment variable, or by assignment in the user's startup file (see \fBCUSTOMIZATION\fR below). \fBPLOD\fR recognizes the following environment variables: .Ip "\fBSTAMP\fR" 4 The time/date stamp entered into every log entry. Set this to null if you do not wish to datestamp your logs. Default is \fIMM/DD/YY, HH:MM --\fR. .Sp .Ip "\fBEDITOR\fR" 4 The user's preferred editor (used by the \fB-E\fR command line flag and the \fB~e\fR, \fB~E\fR, and \fB~M\fR escape sequences). Default is \fI/usr/local/bin/emacs\fR. .Sp .Ip "\fBVISUAL\fR" 4 The user's preferred visual editor (used by \fB-V\fR, \fB~v\fR, and \fB~V\fR). Default is \fI/usr/local/bin/emacs\fR. .Sp .Ip "\fBPAGER\fR" 4 The user's preferred pager (used by \fB-P\fR, \fB~p\fR, and \fB~P\fR). Default is \fI/usr/local/bin/less\fR. .Sp .Ip "\fBLINES\fR" 4 The number of lines on the current display. Used to determine when the \fBPAGER\fR needs to be invoked. Default is \fI24\fR. .Sp .Ip "\fBCRYPTCMD\fR" 4 The encryption command to be used. If you do not wish to encrypt your log files, set this to null. The standard UNIX \fBcrypt\fR command is not in the least secure, but does provide protection from casual browsing. Default is \fI/bin/crypt\fR. .Sp .Ip "\fBKEYVAL\fR" 4 The key to be used with \fBCRYPTCMD\fR. Default is \fIplod\fR. .Sp .Ip "\fBLOGDIR\fR" 4 Where log files are placed. Default is \fI$HOME/.logdir\fR. .Sp .Ip "\fBLOGFILE\fR" 4 The name of the current log file. \fBLOGDIR\fR will be prepended to this value if \fBLOGFILE\fR does not begin with a \fI/\fR. Default is \fI\fR. .Sp .Ip "\fBHOME\fR" 4 The user's home directory. Default taken from user's password entry. .Sp .Ip "\fBDEADLOG\fR" 4 Where interrupted log entries are placed. \fBHOME\fR will be prepended to this value if \fBDEADLOG\fR does not begin with a \fI/\fR. Default is \fIdead.log\fR. .Sp .Ip "\fBTMPFILE\fR" 4 Scratch file used throughout execution of program. Default is \fI/tmp/plodtmp\fR. .Sp X .SH COMMANDS Many tilde escape sequences are supported under \fBPLOD\fR's interactive mode. Users may also define their own escape sequences in \fBPLOD\fR's initialization file (see \fBCUSTOMIZATION\fR below). Currently defined sequences are: .Ip "~h, ~?" 8 Show a list of all escape sequences with a short usage message. .Sp .Ip "~= var[, ...]" 8 Display the current value of one or more variables. .Ip ~e 8 Edit the current buffer with \fB$EDITOR\fR. .Sp .Ip ~v 8 Edit the current buffer with \fB$VISUAL\fR. .Sp .Ip ~p 8 Display the contents of the current buffer (using \fB$PAGER\fR if necessary). .Sp .Ip "~V [\ logfile\ [\ key\ ]]" 8 Call \fB$VISUAL\fR on the current log file, or on some other log file as specified. An additional encryption key may also be supplied. .Sp .Ip "~E, ~l [\ logfile\ [\ key\ ]]" 8 Similar to ~E except that \fB$EDITOR\fR is used. .Sp .Ip "~P, ~L [\ logfile\ [\ key\ ]]" 8 Same as ~E and ~V except that \fB$PAGER\fR is invoked. .Sp .Ip ~q 8 Quit \fBPLOD\fR, saving contents of buffer into \fB$DEADLOG\fR. .Sp .Ip ~x 8 Quit without attempting to save buffer. .Sp .Ip ~d 8 Append contents of \fB$DEADLOG\fR to current buffer. .Sp .Ip "~r somefile" 8 Append contents of file to current buffer. .Sp .Ip "~a somefile" 8 Append contents of current buffer to file. .Sp .Ip "~w somefile" 8 Overwrite file with contents of current buffer. .Sp .Ip "~X Perl-code" 8 Execute a line of Perl code. .Sp .Ip ~M 8 Invoke \fB$EDITOR\fR and execute resulting file as Perl code. Each successive invocation of this escape will edit the previously executed Perl code so as to make it easier to go back and correct small errors. .Sp .Ip "~! command" 8 Execute a command in the shell and return. .Sp .Ip "~> command" 8 Append the output of a command to the current buffer. .Sp .Ip "~| command" 8 Pipe the current buffer through a command and replace the buffer with the resulting output. .Sp X .SH CUSTOMIZATION Like most UNIX utilities, \fBPLOD\fR has an initialization file, the \fI\.plodrc\fR, which is read at startup. Unlike most UNIX utilities, this file is interpreted as Perl code. Thus, if you wished to assign a new value to a customization variable you would use the syntax .RS .PP $LOGDIR = "$HOME/mylogs"; .RE .PP \fBPLOD\fR also looks for a system-wide customization file, \fI/etc/plodrc\fR. The order of evaluation is: hard coded defaults in \fBPLOD\fR, then the \fI/etc/plodrc\fR file, then the user's environment, and finally the user's \fI\.plodrc\fR file. X It is also possible for the user to create their own tilde escapes. First, create a subroutine which performs the desired function. Then assign the type glob which references that function to global array \fI%funcs\fR indexed by the character of the escape sequence. Any arguments that the user enters after the tilde escape will be passed into the function as a single string in \fI@_\fR. The list \fI@lines\fR contains the current buffer. X As an example, here is the append to file function (~a) from the \fBPLOD\fR source: .RS .PP .nf sub appendfl { X local($file) = @_; X if (!open(OUTP, ">> $file")) { X warn "*** Could not append to file $file\\n"; X return; X } X print OUTP @lines; X close(OUTP); X print "Wrote ", scalar(@lines), " lines to file $file\\n"; X print "(continue composing note)\\n"; } $appendfl = "file\\t\\tAppend contents of buffer to file"; $funcs{'a'} = *appendfl; .fi .RE .PP The scalar variable \fI$appendfl\fR is used by \fBPLOD\fR's help function (~h or ~?) to provide a descriptive message about the escape sequence. As a further example, here is \fBPLOD\fR's help function .RS .PP .nf sub helpuser { X $long = (scalar(keys %funcs) >= $LINES) X && open(TMP, ">$TMPFILE"); X for (sort keys %funcs) { X *info = $funcs{$_}; X if ($long) { X print TMP "~$_ $info\\n"; X } X else { print "~$_ $info\\n"; } X } X if ($long) { X close(TMP); X system("/bin/cat $TMPFILE | $PAGER"); X unlink "$TMPFILE"; X } } $helpuser = "\\t\\tPrint this message"; $funcs{'h'} = *helpuser; $funcs{'?'} = *helpuser; .fi .RE .PP Note the use of various customization variables as well as the assignment of the type glob to two different indices of the \fI%funcs\fR array. X .SH FILES .PP .Ip $HOME/.plodrc 24 Users' personal initialization files .Sp .Ip /etc/plodrc 24 System-wide initialization file .Sp .PP Various other customizable file locations. X .SH SEE ALSO .BR perl (1) X .SH AUTHORS The original idea for \fBPLOD\fR comes from Bill Mendyka (\fBmendyka@dg-rtp.dg.com\fR). X The current Perl implementation was developed by Hal Pomeranz (\fBpomeranz@aqm.com\fR). X An Emacs mode for \fBPLOD\fR was developed by Paul Foley (\fBpaul@ascent.com\fR). X Additional improvements have been suggested/developed by: David W Crabb (\fBcrabb@phoenix.Princeton.EDU\fR), John Ellis (\fBellis@rtsg.mot.com\fR), Mike Lachowski (\fBmlachow@erenj.com\fR), Eric Prestemon (\fBecprest@pocorvares.er.usgs.GOV\fR), Erik E. Rantapaa (\fBrantapaa@math.umn.edu\fR), and James Tizard (\fBjames@ringo.ssn.flinders.edu.au\fR). X .SH BUGS Any bug reports or suggestions for improvement should be submitted to Hal Pomeranz via email at \fBpomeranz@aqm.com\fR. SHAR_EOF chmod 0644 plod.man || echo 'restore of plod.man failed' Wc_c="`wc -c < 'plod.man'`" test 9043 -eq "$Wc_c" || echo 'plod.man: original size 9043, current size' "$Wc_c" fi # ============= plod.el ============== if test -f 'plod.el' -a X"$1" != X"-c"; then echo 'x - skipping plod.el (File already exists)' else echo 'x - extracting plod.el (Text)' sed 's/^X//' << 'SHAR_EOF' > 'plod.el' && Newsgroups: comp.lang.perl XFrom: paul@ascent.com (Paul Foley) Subject: Emacs interface to PLOD Message-ID: Date: 21 Jan 93 12:57:01 Organization: Ascent Technology, Inc., Cambridge Massachusetts Lines: 119 X Here is an emacs-lisp interface to PLOD --- the Personal LOgging Device posted to comp.lang.perl a few days ago. X Simplest way to use is M-x plod. X There is also an "alarm" interface that will switch you to a PLOD buffer every so often, in case you forget to invoke it yourself. X Enjoy. X ------------------------------------------------------------------ X ;;;; ;;;; plod.el ;;;; ;;;; Emacs interface to PLOD --- A (Perl) tool to keep track of the work you do ;;;; PLOD was written by pomeranz@aqm.com (Hal R. Pomeranz). ;;;; ;;;; This software is FREE to all and may be used for any purpose as long as this ;;;; notice remains intact. The author does not assume responsibility for anything. ;;;; ;;;; Suggested addition to .emacs: ;;;; (load-library "plod") ;;;; (plod-alarm-on 60) ; once an hour ;;;; ;;;; When you are tired of PLODding use "M-x plod-alarm-off" ;;;; ;;;; Alternately, use "M-x plod" whenever you want to log something. ;;;; ;;;; paul@ascent.com (Paul Foley) Wednesday January 20, 1993 X (require 'shell) X ;;; ;;; Variables ;;; X ;; Name of executable --- should be in your $PATH (defvar plod-program-name "plod") (defvar plod-buffer-name "*PLOD*") X ;;; ;;; Manual Interface ;;; X (defvar plod-buffer-process nil) X ;; Interactive command to invoke PLOD in a shell-mode buffer. ;; X (defun plod () X "Invoke PLOD." X (interactive) X ; restart PLOD if necessary X (if (not (get-buffer-process plod-buffer-name)) X (setq plod-buffer-process (start-process "plod-process" plod-buffer-name plod-program-name))) X (switch-to-buffer plod-buffer-name t) X (if (not (eq major-mode 'shell-mode)) (shell-mode))) X X ;;; ;;; Alarm interface ;;; X (defvar plod-alarm-on-p nil) ; t if alarm is on (defvar plod-alarm-process nil) X ;; run when plod-alarm-process is killed (defun plod-alarm-sentinel (proc reason) X (or (eq (process-status proc) 'run) X (setq plod-alarm-on-p nil) X (ding) X (message "PLOD alarm off"))) X ;; run every interval & at initial call to plod-alarm-on (defun plod-alarm-filter (proc string) X (if plod-alarm-on-p X (plod) X (setq plod-alarm-on-p t))) X ;; Set alarm to call PLOD every so often ;; (defun plod-alarm-on (interval) X "Turn the Emacs PLOD alarm on. The alarm goes off every INTERVAL minutes and you will be switched to the PLOD buffer automatically. Use plod-alarm-off to stop this behaviour." X (interactive "nEnter PLOD alarm interval (in minutes): ") X (let ((live (and plod-alarm-process X (eq (process-status plod-alarm-process) 'run)))) X (if (not live) X (progn X (setq plod-alarm-on-p nil) X (if plod-alarm-process X (delete-process plod-alarm-process)) X (let ((process-connection-type nil)) X (setq plod-alarm-process X (start-process "plod-alarm" nil X (concat exec-directory "wakeup") X ; convert minutes -> seconds for wakeup X (int-to-string (* 60 interval))))) X (process-kill-without-query plod-alarm-process) X (set-process-sentinel plod-alarm-process 'plod-alarm-sentinel) X (set-process-filter plod-alarm-process 'plod-alarm-filter))))) X ;; Turn PLOD alarm off ;; (defun plod-alarm-off () X "Turn the Emacs PLOD alarm off." X (interactive) X (if plod-alarm-on-p (kill-process plod-alarm-process))) X ;;; End -- paul@ascent.com ...!uunet!ascent!paul X X X X X X SHAR_EOF chmod 0644 plod.el || echo 'restore of plod.el failed' Wc_c="`wc -c < 'plod.el'`" test 3523 -eq "$Wc_c" || echo 'plod.el: original size 3523, current size' "$Wc_c" fi exit 0