#! /bin/sh # biffjr --- beep and notify when you have new mail # Copyright (C) 1993, 1995 Noah S. Friedman # Author: Noah Friedman # 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