home *** CD-ROM | disk | FTP | other *** search
/ NetNews Usenet Archive 1993 #3 / NN_1993_3.iso / spool / comp / lang / perl / 7958 < prev    next >
Encoding:
Text File  |  1993-01-23  |  16.7 KB  |  536 lines

  1. Newsgroups: comp.lang.perl
  2. Path: sparky!uunet!stanford.edu!eos!data.nas.nasa.gov!irving.nas.nasa.gov!pomeranz
  3. From: pomeranz@irving.nas.nasa.gov (Hal R. Pomeranz)
  4. Subject: PLOD v1.1 (whoops!)
  5. Sender: news@nas.nasa.gov (News Administrator)
  6. Organization: NAS, NASA Ames Research Center, Moffett Field, California
  7. Date: Sat, 23 Jan 93 00:52:51 GMT
  8. Message-ID: <1993Jan23.005251.14057@nas.nasa.gov>
  9. Lines: 525
  10.  
  11.  
  12. [If that last v1.1 I posted managed to get out past my cancel somehow,
  13.  please ignore it-- a small buglet crept in due to lazy editing on my
  14.  part.  Here's a good version.                    --Hal]
  15.  
  16. This is v1.1 of PLOD, the Personal LOgging Device.
  17.  
  18. This version is essentially a bugfix release.  Useful fixes and
  19. suggestions were contributed by Erik E. Rantapaa 
  20. (rantapaa@math.umn.edu) and John Ellis (ellis@rtsg.mot.com).
  21.  
  22. ** &pipetocmd() unlinks all temporary files it creates (Pomeranz)
  23.  
  24. ** Variable assignment idiom cleaned up (Rantapaa)
  25.  
  26. ** LOGDIR and HOME are only prepended to LOGFILE and DEADLOG after
  27.    .plodrc has been eval-ed and if LOGFILE and DEADLOG are relative
  28.    paths.  This means that you can change LOGDIR in your .plodrc and
  29.    really affect where the log files go. (Rantapaa)
  30.  
  31. ** eval of .plodrc is done with "do" rather than "cat" (Rantapaa)
  32.  
  33. ** You can now do quick one-liner entries on the command line, e.g.
  34.    "plod Completed modifications to plod" (Rantapaa)
  35.  
  36. ** Time/date stamp only printed if user is entering info directly from
  37.    a tty (Rantapaa)
  38.  
  39. ** plod attempts to create logging directory if it does not exist (Ellis)
  40.  
  41. Thanks to everybody for the comments (and to Paul Foley (paul@ascent.com)
  42. for the Emacs mode).  Keep those bug reports and comments coming in.
  43.  
  44. ===============================================================================
  45. Hal Pomeranz     Senior Member of Technical Staff     NASA Ames Research Center
  46.              pomeranz@nas.nasa.gov     pomeranz@cs.swarthmore.edu
  47. "I sit and stare with hardened eyes through a time when life's so hard."  --L&O
  48. ===============================================================================
  49.  
  50. #!/usr/nas/bin/perl
  51. #
  52. #    PLOD-- Personal LOgging Device, v1.1
  53. #    Copyright (C), 1993, Hal Pomeranz (pomeranz@nas.nasa.gov)
  54. #    All rights reserved.  No warranty expressed or implied.
  55. #    PLOD is freely distributable under the same terms as Perl.
  56. #    Inspired by Bill Mendyka (mendyka@dg-rtp.dg.com)
  57. #    Suggestions/Bugfixes: Erik E. Rantapaa (rantapaa@math.umn.edu)
  58. #                          John Ellis (ellis@rtsg.mot.com)
  59. #
  60. # plod is a tool designed to help administrators (and others) keep track
  61. # of their daily activities.  Since your management will typically have
  62. # no idea what you are doing to justify such an exhorbitant salary (any
  63. # amount of money they may be paying you being classified as "exhorbitant"),
  64. # and since most people forget what they do themselves, it's good to keep a
  65. # record.  Trot your logs out around performance review time, and show them
  66. # to your management (after suitable sanitization) on a regular basis.
  67. #
  68. # The interface is designed to make it quick to dash off a simple note
  69. # to yourself.  Since most folks who are going to use plod also use email,
  70. # I've based the interface on Berkeley mail-- tilde escapes and all (for
  71. # a list of escapes, try ~h or ~?).  By default, your logs will be encrypted
  72. # using the /bin/crypt command-- not secure in the least, but marginally
  73. # safe from casual browsing (I tend to vent into my logs sometimes rather
  74. # than at those who might be offended and fire me).  You can turn off
  75. # the encryption if you find it more a hassle than a comfort.
  76. #
  77. # Which brings us to the subject of customization.  Many escape sequences
  78. # and variables have already been defined, but you can make your own
  79. # changes using your ~/.plodrc file.  This file is interpreted using
  80. # eval(), so any valid Perl syntax is acceptable.  Variables may be
  81. # customized by editing this script directly, setting an environment
  82. # variable with the same name as the plod variable, or by doing an assignment
  83. # in your .plodrc (.plodrc value supercedes environment value which beats
  84. # default value in script).  New tilde escapes may be defined by assigning
  85. # a type glob, say *foo, to the global array %funcs-- the index of the type
  86. # glob in the array being the character (single chars only!) of the escape
  87. # sequence.  &foo should be a function which performs the escape (any
  88. # arguments after the escape are passed in as a single string in @_),
  89. # and $foo can be a descriptive help string (see &helpuser()).  Your functions
  90. # may reference any of the plod customization variables as well as the
  91. # list @lines, which contains all information in the current log entry
  92. # (including the date/time stamp plod adds to the beginning of each entry).
  93. # For examples, consult the function declarations section below.
  94.  
  95. ######################### Begin Variable Declarations #########################
  96.  
  97. # All variables have default values which will be superceded by environment
  98. # variables of the same name.  The user's .plodrc is read after all other
  99. # variable assignments, so any assignments there take precedence.
  100. #
  101. # Note that $LOGFILE and $DEADLOG are used as absolute pathnames.  After
  102. # the .plodrc has been evaluated, $LOGDIR or $HOME will be prepended to
  103. # $LOGFILE and $DEADLOG respectively if either of these variables does not
  104. # begin with a '/'.
  105. #
  106. # Set $CRYPTCMD to null if you don't want encryption to be performed.
  107. #
  108. # KEYVAL    key value used by CRYPTCMD
  109. # CRYPTCMD    encryption command, set this to null for no encryption
  110. # TMPFILE    file name to use for temporary holding
  111. # EDITOR    editor called by ~e
  112. # VISUAL    editor called by ~v
  113. # PAGER         used by ~p and ~h when output longer than one page (see LINES)
  114. # LINES        number of lines on the screen
  115. # LOGDIR    directory containing log files
  116. # LOGFILE    absolute path of log file
  117. # HOME        user's home directory
  118. # DEADLOG    place to drop dead.log file on abort or ~q, also used by ~d
  119.  
  120. # Some variable values use date/time information
  121. #
  122. ($ss, $mm, $hh, $DD, $MM, $YY) = localtime($^T); $MM++; 
  123.  
  124. $KEYVAL = $ENV{'KEYVAL'} || sprintf("pl%d%dod", $YY, $MM);
  125. $CRYPTCMD = defined($ENV{'CRYPTCMD'})? $ENV{'CRYPTCMD'} : "/bin/crypt $KEYVAL";
  126. $TMPFILE = $ENV{'TMPFILE'} || "/tmp/plodtmp$$";
  127. $HOME = $ENV{'HOME'} || (getpwuid($<))[7];
  128. $EDITOR = $ENV{'EDITOR'} || "/usr/local/bin/emacs";
  129. $VISUAL = $ENV{'VISUAL'} || "/usr/local/bin/emacs";
  130. $PAGER = $ENV{'PAGER'} || "/usr/local/bin/less";
  131. $LINES = $ENV{'LINES'} || 24;
  132. $LOGDIR = $ENV{'LOGDIR'} || "$HOME/.logdir";
  133. $LOGFILE = $ENV{'LOGFILE'} || sprintf("%02d%02d", $YY, $MM);
  134. $DEADLOG = $ENV{'DEADLOG'} || "dead.log";
  135.  
  136.  
  137. ########################## End Variable Declarations ##########################
  138. ######################### Begin Function Declarations #########################
  139.  
  140.  
  141. # Printvar (~=): Output the value of one or more variables.
  142. #
  143. sub printvar {
  144.    local($vars) = @_;
  145.    $, = ','; print eval "($vars)"; $, = '';
  146.    print "\n";
  147.    print "(continue composing note)\n";
  148. }
  149. $printvar = "\$var[, ...]\tOutput value of variables";
  150. $funcs{'='} = *printvar;
  151.  
  152.  
  153. # Bang (~!): Execute a command in the shell and then return to plod.
  154. #
  155. sub bang {
  156.    local($cmdline) = @_;
  157.    system "$cmdline";
  158.    print "(continue composing note)\n";
  159. }
  160. $bang = "cmdline\tExecute system command and return";
  161. $funcs{'!'} = *bang;
  162.  
  163.  
  164. # Redirect (~>): Pipe the output of a command into the current buffer.
  165. #
  166. sub redirect {
  167.    local($cmdline) = @_;
  168.    local($count);
  169.    if (!open(CMD, "$cmdline |")) {
  170.       warn "*** Unable to execute: $cmdline\n";
  171.       return;
  172.    }
  173.    &readit(CMD);
  174. }
  175. $redirect = "cmdline\tAdd output of given command to buffer";
  176. $funcs{'>'} = *redirect;
  177.  
  178.  
  179. # Pipetocmd (~|): Pipe the contents of the current buffer through a UNIX
  180. # command line and replace the buffer with the result.
  181. #
  182. sub pipetocmd {
  183.    local($cmdline) = @_;
  184.    local($header);
  185.    if (!open(PIPELN, "| $cmdline >$TMPFILE 2>&1")) {    # output to tmp file
  186.       warn "*** Unable to execute: $cmdline\n";
  187.       return;
  188.    }
  189.    $header = shift @lines;                # don't include stamp
  190.    print PIPELN @lines;
  191.    close(PIPELN);
  192.    if (!open(INP, "<$TMPFILE")) {
  193.       warn "*** Unable to get command output\n";
  194.       unshift(@lines, $header);
  195.       unlink "$TMPFILE";
  196.       return;
  197.    }
  198.    undef @lines;                    # replace buffer with
  199.    @lines = <INP>;                    # contents of tmp file
  200.    close(INP);
  201.    unlink "$TMPFILE";
  202.    unshift(@lines, $header);
  203.    print "(continue composing note)\n";   
  204. }
  205. $pipetocmd = "cmdline\tPipe contents of buffer through cmdline";
  206. $funcs{'|'} = *pipetocmd;
  207.  
  208.  
  209. # Perlit (~P): Execute Perl code.
  210. #
  211. sub perlit {
  212.    local($code) = @_;
  213.    eval "$code";
  214.    warn $@ if $@;
  215.    print "(continue composing note)\n";   
  216. }
  217. $perlit = "code\t\tExecute a line of Perl code";
  218. $funcs{'P'} = *perlit;
  219.  
  220.  
  221. # Appendfl (~a): Append contents of buffer to a file and return to plod.
  222. # To overwrite a file with the contents of the buffer, see &writefl().
  223. #
  224. sub appendfl {
  225.    local($file) = @_;
  226.    if (!open(OUTP, ">> $file")) {
  227.       warn "*** Could not append to file $file\n";
  228.       return;
  229.    }
  230.    print OUTP @lines;
  231.    close(OUTP);
  232.    print "Wrote ", scalar(@lines), " lines to file $file\n";
  233.    print "(continue composing note)\n";
  234. }
  235. $appendfl = "file\t\tAppend contents of buffer to file";
  236. $funcs{'a'} = *appendfl;
  237.  
  238.  
  239. # Getdead (~d): Suck contents of DEADLOG file into buffer.
  240. #
  241. sub getdead {
  242.    local($bogus) = @_;
  243.    return(&mistake) if ($bogus);
  244.    if (!open(DEAD, "<$DEADLOG")) {
  245.       warn "*** Unable to open $home/dead.log.\n";
  246.       return;
  247.    }
  248.    &readit(DEAD, "$DEADLOG");
  249. }
  250. $getdead = "\t\tIncorporate contents of \$DEADLOG into buffer";
  251. $funcs{'d'} = *getdead;
  252.  
  253.  
  254. # Editbuf (~e) and Visualbuf (~v): Call appropriate editor on buffer.
  255. #
  256. sub editbuf {
  257.    local($bogus) = @_;
  258.    return(&mistake) if ($bogus);
  259.    &calledit($EDITOR);
  260. }
  261. sub visualbuf {
  262.    local($bogus) = @_;
  263.    return(&mistake) if ($bogus);
  264.    &calledit($VISUAL);
  265. }
  266. $editbuf = "\t\tEdit buffer with \$EDITOR";
  267. $visualbuf = "\t\tEdit buffer with \$VISUAL";
  268. $funcs{'e'} = *editbuf;
  269. $funcs{'v'} = *visualbuf;
  270.  
  271.  
  272. # Helpuser (~h or ~?): Output a list of tilde escapes with associated
  273. # help messages (found in the scalar values of the type globs in %funcs).
  274. # Use the defined PAGER if the output would be more than LINES long.
  275. #
  276. sub helpuser {
  277.    $long = (scalar(keys %funcs) >= $LINES) && open(TMP, ">$TMPFILE");
  278.    for (sort keys %funcs) {
  279.       *info = $funcs{$_};
  280.       if ($long) {
  281.          print TMP "~$_ $info\n";
  282.       }
  283.       else { print "~$_ $info\n"; }
  284.    }
  285.    if ($long) {
  286.       close(TMP);
  287.       system("/bin/cat $TMPFILE | $PAGER");
  288.       unlink "$TMPFILE";
  289.    }
  290. }
  291. $helpuser = "\t\tPrint this message";
  292. $funcs{'h'} = *helpuser;
  293. $funcs{'?'} = *helpuser;
  294.  
  295.  
  296. # Printout (~p):  cat back the current buffer for review.  Use PAGER if
  297. # the buffer is longer than LINES.
  298. #
  299. sub printout {
  300.    local($bogus) = @_;
  301.    return(&mistake) if ($bogus);
  302.    if (@lines < $LINES-1 || !open(TMP, ">$TMPFILE")) {
  303.       print "-----\n";
  304.       print @lines;
  305.    }
  306.    else {
  307.       print TMP @lines;
  308.       close(TMP);
  309.       system("/bin/cat $TMPFILE | $PAGER");
  310.       unlink "$TMPFILE";
  311.    }
  312.    print "(continue composing note)\n";
  313. }
  314. $printout = "\t\tView contents of buffer, one page at a time";
  315. $funcs{'p'} = *printout;
  316.  
  317.  
  318. # Quitit (~q): Quit plod and attempt to save buffer in DEADLOG.  Also
  319. # called on SIGINT and SIGQUIT via &trapit().
  320. #
  321. sub quitit {
  322.    local($bogus) = @_;
  323.    return(&mistake) if ($bogus);
  324.    open(DEAD, ">> $DEADLOG") || die "Can't open $DEADLOG\n";
  325.    print DEAD @lines;
  326.    close(DEAD);
  327.    exit;
  328. }
  329. $quitit = "\t\tQuit, attempts to save buffer in \$DEADLOG";
  330. $funcs{'q'} = *quitit;
  331.  
  332.  
  333. # Readfile (~r): Append contents of file into buffer.
  334. #
  335. sub readfile {
  336.    local($file) = @_;
  337.    if (!open(INPT, "<$file")) {
  338.       warn "*** Unable to open $file.\n";
  339.       return;
  340.    }
  341.    &readit(INPT, $file);
  342. }
  343. $readfile = "file\t\tRead contents of file into buffer";
  344. $funcs{'r'} = *readfile;
  345.  
  346.  
  347. # Writefl (~w): Overwrite file with contents of buffer.  To append to a
  348. # given file, see &appendfl().
  349. #
  350. sub writefl {
  351.    local($file) = @_;
  352.    if (!open(OUTP, "> $file")) {
  353.       warn "*** Could not write to file $file\n";
  354.       return;
  355.    }
  356.    print OUTP @lines;
  357.    close(OUTP);
  358.    print "Wrote ", scalar(@lines), " lines to file $file\n";
  359.    print "(continue composing note)\n";
  360. }
  361. $writefl = "file\t\tOverwrite file with contents of buffer";
  362. $funcs{'w'} = *writefl;
  363.  
  364.  
  365. # Exitnow (~x): Exit plod without writing to DEADLOG or LOGFILE.
  366. #
  367. sub exitnow {
  368.    local($bogus) = @_;
  369.    return(&mistake) if ($bogus);
  370.    exit;
  371. }
  372. $exitnow = "\t\tExit without saving buffer";
  373. $funcs{'x'} = *exitnow;
  374.  
  375.  
  376. ########################## End Function Declarations ##########################
  377. ############################# Begin Main Program ##############################
  378.  
  379.  
  380. # Check for ~/.plodrc and eval() contents.  Exit with an error message if
  381. # eval() complains for any reason.
  382. #
  383. if (-e "$HOME/.plodrc") {
  384.    eval { do "$HOME/.plodrc"; };
  385.    die "*** Error in $HOME/.plodrc:\n$@" if $@;
  386. }
  387.  
  388. # Prepend parent directories unless we have explicit pathnames
  389. #
  390. $LOGFILE = "$LOGDIR/$LOGFILE" unless ($LOGFILE =~ /^\//);
  391. $DEADLOG = "$HOME/$DEADLOG" unless ($DEADLOG =~ /^\//);
  392.  
  393. # Extract dirname from $LOGFILE and make sure it exists
  394. #
  395. ($dirname = $LOGFILE) =~ s,/[^/]*$,,;
  396. if (!(-d $dirname)) {
  397.    warn "Attempting to create logging directory, $dirname\n";
  398.    die "Attempt failed!\n" unless (mkdir($dirname, 0700));
  399. }
  400.  
  401. # Trap SIGQUIT and SIGINT.  &trapit() is really just a wrapper for &quitit().
  402. #
  403. $SIG{'QUIT'} = trapit;
  404. $SIG{'INT'} = trapit;
  405.  
  406. # Create time/date stamp and make it the first line of the buffer.
  407. #
  408. ($ss, $mm, $hh, $DD, $MM, $YY) = localtime($^T); $MM++; 
  409. $stamp = sprintf("%02d/%02d/%02d, %02d:%02d --", $MM, $DD, $YY, $hh, $mm);
  410. push(@lines, "$stamp\n");
  411.  
  412. # Log entry can appear on the command line, otherwise loop until end of
  413. # STDIN or '.' recognized on a line by itself.
  414. #
  415. if (@ARGV) { push(@lines, "@ARGV\n"); }
  416. else {
  417.    print "$stamp\n" if (-t STDIN);
  418.    while (<STDIN>) {
  419.       if (/^~/) {                    # escape sequence:
  420.          ($esc, $args) = /^~(\S)\s*(.*)$/;        # 1) parse line
  421.          *glob = $funcs{$esc};                # 2) unpack type glob
  422.          if (!defined(&glob)) {                # 3) check defined()
  423.         warn "Unrecognized escape sequence: ~$esc\n";
  424.             next;
  425.          }
  426.          &glob($args);                    # 4) call func w/ args
  427.       }
  428.       elsif (/^\.\s*$/) {                # lone dot means end 
  429.          print "(eot)\n";                # of log entry
  430.          last;
  431.       }
  432.       else {                        # else append line to
  433.          push(@lines, $_);                # log buffer
  434.       }
  435.    }
  436. }
  437.  
  438. # Completed log entry now in @lines.  If using encryption, call encryption
  439. # command to decrypt previous log entries (if present).  If not encrypting,
  440. # simply open log file to append.
  441. #
  442. if ($CRYPTCMD) {                    # encrypting
  443.    if (-e "$LOGFILE") {
  444.       system "$CRYPTCMD <$LOGFILE >$TMPFILE";
  445.    }
  446.    if (!open(LOGFILE, ">> $TMPFILE")) {
  447.       unlink "$TMPFILE";
  448.       warn "*** Unable to append new log entry\n";
  449.       &quitit();
  450.    }
  451. }
  452. else {                             # not encyrpting
  453.    if (!open(LOGFILE, ">> $LOGFILE")) {
  454.       warn "*** Unable to append new log entry\n";
  455.       &quitit();
  456.    }
  457. }
  458.  
  459. # Dump contents of buffer into plain text file.
  460. #
  461. print LOGFILE "-----\n";
  462. print LOGFILE @lines;
  463. close(LOGFILE);
  464.  
  465. # If encrypting, replace old log file with new version.  Unlink plain
  466. # text temporary file when done.
  467. #
  468. if ($CRYPTCMD) {
  469.    unlink "$LOGFILE";
  470.    system "$CRYPTCMD <$TMPFILE >$LOGFILE";
  471.    chmod 0600, "$LOGFILE";
  472.    unlink "$TMPFILE";
  473. }
  474.  
  475.  
  476. ############################## End Main Program ###############################
  477. ########################### Miscellaneous Functions ###########################
  478.  
  479.  
  480. # Append contents of file $fname (associated with file handle $fh) to buffer.
  481. # Assume $fh is a pipe if $fname is null.  This function called by many tilde
  482. # escapes.
  483. #
  484. sub readit {
  485.    local($fh, $fname) = @_;
  486.    push(@lines, <$fh>);
  487.    print STDOUT ($fname) ? "$fname: " : "Added ";
  488.    print STDOUT "$. lines";
  489.    print STDOUT ($fname) ? "\n" : " to buffer.\n";
  490.    print STDOUT "(continue composing note)\n";
  491.    close($fh);
  492. }
  493.  
  494.  
  495. # Call the editor $_[0] on the contents of the buffer.  Used by &editbuf()
  496. # and &visualbuf().
  497. #
  498. sub calledit {
  499.    local($edit) = @_;
  500.    if (!open(EDIT, ">$TMPFILE")) {
  501.       warn "*** Unable to create file for editing\n";
  502.       return;
  503.    }
  504.    print EDIT @lines;
  505.    close(EDIT);
  506.    chmod 0600, "$TMPFILE";
  507.    system "$edit $TMPFILE";
  508.    if (!open(EDIT, "<$TMPFILE")) {
  509.       warn "*** Unable to read changes, returning to previous state.\n";
  510.       system "/bin/rm -f $TMPFILE*";
  511.       return;
  512.    }
  513.    undef @lines;
  514.    @lines = <EDIT>;
  515.    close(EDIT);
  516.    system "/bin/rm -f $TMPFILE*";
  517.    print "(continue composing note)\n";
  518. }
  519.  
  520.  
  521. # Generic warning message called by all escapes that do not expect arguments
  522. # when @_ is not empty.
  523. #
  524. sub mistake {
  525.    warn "*** Arguments are not expected for this escape.\n";
  526. }
  527.  
  528.  
  529. # Wrapper for &quitit()-- called on SIGINT and SIGQUIT.  Wrapper required
  530. # because signal handlers get the signal as an argument and &quitit() does
  531. # not appreciate arguments.
  532. #
  533. sub trapit {
  534.    &quitit();
  535. }
  536.