#! /bin/sh
# biffjr --- beep and notify when you have new mail

# Copyright (C) 1993, 1995 Noah S. Friedman

# Author: Noah Friedman <friedman@prep.ai.mit.edu>
# Created: 1993-09-21

# $Id: biffjr,v 1.8 1996/03/03 22:45:28 friedman Exp $

# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2, or (at your option)
# any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, you can either send email to this
# program's maintainer or write to: The Free Software Foundation,
# Inc.; 59 Temple Place, Suite 330; Boston, MA 02111-1307, USA.

# Commentary:

# This script works without the `comsat' daemon, which is not always
# enabled or is buggy (some versions don't work if your username is 8
# characters long).
#
# This program will background itself automatically; you don't need to use
# `&' in the shell.  It terminate itself when you log out.
#
# Because of the means biffjr uses to determine when you've logged out, you
# must be connected to a pty which is owned by you.  Sometimes this is not
# the case in emacs shell buffers.
#
# The --foreground and --ignore-tty-owner options frob this behavior.

# Code:

progname=`echo "$0" | sed -e 's/[^\/]*\///g'`

bq='`'
eq="'"

usage="Usage: $progname {options}

Options are:
-D, --debug                  Turn on shell debugging ($bq${bq}set -x$eq$eq).
-f, --from           FPROG   Display envelopes from new messages.  Optional
                             argument FPROG is the name of the ${bq}from$eq
                             program to execute.
                             This option should either be listed last or
                             the token $bq--from=$eq should be used if the
                             optional argument is not to be supplied.
-F, --foreground             Don't background this program.
                             This option implies $bq--ignore-tty-owner$eq.
-h, --help                   You're looking at it.
-i, --ignore-tty-owner       Do not examine ownership of tty to decide
                             whether to terminate.
                             Use of this option alone is discouraged
                             because failure to kill this program at logout
                             will annoy the next user to log in.
                             Run this program in the shell foreground
                             instead, using $bq--foreground$eq.
-m, --mail-file      MFILE   Path of mail spool file.
-N, --no-incremental-from    Always summarize entire mail spool contents,
                             not just messages which have arrive since the
                             last report.
                             This option implies ${bq}--from$eq, but if you
                             wish to specify a particular summary program to
                             use, you must supply it with that option anyway.
-n, --no-bell                Do not beep when displaying messages.
                             Primarily intended for emacs shell buffers.
-s, --sleep-interval SEC     Interval (in seconds) between mail checks.
                             Default is 60 seconds.
"

# Clever way to save arguments whilst preserving quoting.
# Note that only one set of args can be saved at a time.
# Usage: eval "$save_current_args"
#        eval "$restore_saved_args"
save_current_args='
  {
    _saved_args=
    while : ; do
      case $# in 0 ) break ;; esac
      eval _saved_args$#=\$1
      _saved_args="$_saved_args \"\$_saved_args$#\""
      shift
    done
    eval "$restore_saved_args"
  }'
restore_saved_args='
  {
    eval '\''{ eval set fnord $_saved_args ; }'\''
    shift
  }'

# Usage: eval "$getopt"; value=$optarg
# or     optarg_optional=t; eval "$getopt"; value=$optarg
#
# This function automatically shifts the positional args as appropriate.
# The argument to an option is optional if the variable `optarg_optional'
# is non-empty.  Otherwise, the argument is required and getopt will cause
# the program to exit on an error.  optarg_optional is reset to be empty
# after every call to getopt.  The argument (if any) is stored in the
# variable `optarg'.
#
# Long option syntax is `--foo=bar' or `--foo bar'.  2nd argument
# won't get used if first long option syntax was used.
#
# Note: because of broken bourne shells, using --foo=bar syntax can
# actually screw the quoting of args that end with trailing newlines.
# Specifically, most shells strip trailing newlines from substituted
# output, regardless of quoting.
getopt='
  {
    optarg=
    case "$1" in
      --*=* )
        optarg=`echo "$1" | sed -e "1s/^[^=]*=//"`
        shift
       ;;
      * )
        case ${2+set} in
          set )
            optarg="$2"
            shift
            shift
           ;;
          * )
            case "$optarg_optional" in
              "" )
                case "$1" in
                  --*=* ) option=`echo "$1" | sed -e "1s/=.*//;q"` ;;
                  * ) option="$1" ;;
                esac
                exec 1>&2
                echo "$progname: option $bq$option$eq requires argument."
                echo "$progname: use $bq--help$eq to list option syntax."
                exit 1
               ;;
           esac
         ;;
        esac
     ;;
    esac
    optarg_optional=
  }'

# Initialize variables.
# Don't use `unset' since old bourne shells don't have this command.
# Instead, assign them an empty value.
bells=t
debug=
daemonp=f
ignoretty=
mail_file=${MAIL-$MAILPATH}
new_messages_only_p=t
show_from=
sleep_interval=60

eval "$save_current_args"

# Parse command line arguments.
# Make sure that all wildcarded options are long enough to be unambiguous.
# It's a good idea to document the full long option name in each case.
# Long options which take arguments will need a `*' appended to the
# canonical name to match the value appended after the `=' character.
while test $# != 0 ; do
  case "$1" in
    # The --daemon option is used by this program when it recursively calls
    # itself in the background, but is not advertised to the user.
    # The --foreground option has mostly the same effect, but it also turns
    # off tty checking since it's not important in that case.
    --daemon )
      daemonp=t
      shift
     ;;
    -D | --debug | --de* )
      debug=t
      shift
     ;;
    -f | --from* | --fr* )
      eval "$getopt"
      show_from=$optarg

      case "$show_from" in
        '' ) : ;;
        * )  FROM=$show_from ;;
      esac
      show_from=t
     ;;
    -F | --foreground | --fo* )
      daemonp=t
      ignoretty=t
      shift
     ;;
    -h | --help | --h )
      echo "$usage" 1>&2
      exit 1
     ;;
    -i | --ignore-tty-owner | --i* )
      ignoretty=t
      shift
     ;;
    -m | --mail-file* | --m* )
      eval "$getopt"
      mail_file=$optarg
     ;;
    -N | --no-incremental-from | --no-i )
      new_messages_only_p=f
      show_from=t
      shift
     ;;
    -n | --no-bell | --no-b* )
      bells=
      shift
     ;;
    -s | --sleep-interval* | --s* )
      eval "$getopt"
      sleep_interval=$optarg
     ;;
    -- )     # Stop option processing
      shift
      break
     ;;
    -? | --* )
      case "$1" in
        --*=* ) arg=`echo "$1" | sed -e 's/=.*//'` ;;
        * )     arg="$1" ;;
      esac
      exec 1>&2
      echo "$progname: unknown or ambiguous option $bq$arg$eq"
      echo "$progname: Use $bq--help$eq for a list of options."
      exit 1
     ;;
    -??* )
      # Split grouped single options into separate args and try again
      optarg="$1"
      shift
      set fnord `echo "x$optarg" | sed -e 's/^x-//;s/\(.\)/-\1 /g'` ${1+"$@"}
      shift
     ;;
    * )
      break
     ;;
  esac
done

case "$debug" in t ) set -x ;; esac

case "$ignoretty" in
  t ) : ;;
  * )
    TTY=${TTY-`tty 2> /dev/null`}
    case "$TTY" in
      /dev/* ) : ;;
      * )
        echo "$progname: Can't run without a tty." 1>&2
        exit 1
       ;;
    esac

    USER=${USER-${LOGNAME-`{ id | sed -ne 's/.*uid=[0-9]*(//
                                           s/).*//
                                           p'
                           } \
                           || { (whoami) 2> /dev/null; }`}}
    export TTY USER

    tty_sed='s/	/ /g
             s/^[^ ]* *[0-9]* *//
             s/ .*//
             p'
    tty_owner=`ls -ld "$TTY" | sed -ne "$tty_sed"`
    case "$tty_owner" in
      "$USER" ) : ;;
      * )
        exec 1>&2
        echo "$progname: You must own $TTY to run this script, or use $bq--foreground$eq"
        ls -ld "$TTY"
        exit 1
       ;;
    esac
   ;;
esac

case "$daemonp" in f )
  eval "$restore_saved_args"
  $0 --daemon ${1+"$@"} &
  echo "$progname backgrounded."
  exit 0
 ;;
esac

case "$mail_file" in '' )
  USER=${USER-${LOGNAME-`{ id | sed -ne 's/.*uid=[0-9]*(//
                                         s/).*//
                                         p'
                         } \
                         || { (whoami) 2> /dev/null; }`}}


  for dir in /usr/spool/mail /var/mail /var/spool/mail /usr/mail ; do
    if test -d "$dir" ; then
      mail_file="$dir/$USER"
      break
    fi
  done
 ;;
esac
case "$bells" in t )
  bells=`echo ..... | tr '.' '\007'` ;;
esac

msgcount=0
prevmsgcount=1000000

msgprefix="$bells*** $progname"

oldstate=

while : ; do
  case "$ignoretty" in
    t ) : ;;
    * )
      tty_owner=`ls -ld "$TTY" | sed -ne "$tty_sed"`
      case "$tty_owner" in
        "$USER" ) : ;;
        * )
          exit 0
         ;;
      esac
     ;;
  esac

  if test -s "$mail_file"; then
    state=`ls -ld "$mail_file" 2> /dev/null`
    case "$state" in
      "$oldstate" ) : ;;
      * )
        oldstate="$state"

        # The echo "you have new mail" bit is repeated here a lot, but is
        # done so along with all the rest of the output, to minimize the
        # delay between printing that and finishing other computations.
        case "$show_from" in
          t )
            case "$new_messages_only_p" in
              f )
                echo "$msgprefix: You have new mail."
                echo "$msgprefix: Spooled messages as of `date`:"
                ${FROM-from}
               ;;
              t )
                set fnord `grep '^From ' "$mail_file" 2> /dev/null | wc -l`
                shift
                msgcount=${1-0}

                if test $msgcount -lt $prevmsgcount ; then
                  echo "$msgprefix: You have new mail ($msgcount total)."
                  echo "$msgprefix: All spooled messages as of `date`:"
                  ${FROM-from}
                else
                  echo "$msgprefix: You have new mail ($msgcount total)."
                  echo "$msgprefix: Newly-arrived messages as of `date`:"
                  ${FROM-from} | sed -e "1,${prevmsgcount}d"
                fi

                prevmsgcount=$msgcount
              ;;
            esac
           ;;
          * )
            set fnord `grep '^From ' "$mail_file" 2> /dev/null | wc -l`
            shift
            msgcount=${1-new}

            echo "$msgprefix: You have $msgcount messages as of `date`"
        esac
       ;;
    esac
  else
    msgcount=0
    prevmsgcount=1000000
  fi

  # If sleep exits abnormally, it's probably because of an invalid
  # argument to sleep, in which case no sleep is happening and this script
  # will chew cycles.  So exit instead.
  sleep "$sleep_interval" || exit 1
done

# biffjr ends here