#! /bin/sh
# sayto --- front end for `write' to send messages atomically

# Copyright (C) 1995 Noah S. Friedman

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

# $Id: sayto,v 1.7 1996/06/13 22:23:01 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:
# Code:

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

bq='`'
eq="'"

# Ed is the standard text editor.
stdeditor=ed

usage="Usage: $progname {options} [user1{:tty1}] {user2{:tty2}} {...}

Options are:
-D, --debug                  Turn on shell debugging ($bq${bq}set -x$eq$eq).
-e, --edit                   Invoke editor to edit message before sending.
                             The editor is specified by the environment
                             variable ${bq}EDITOR$eq, otherwise the
                             program ${bq}$stdeditor$eq is used.
-E, --empty-ok               Send message even if empty.
-f, --file        FILE       Read message text from file FILE.
-h, --help                   You're looking at it.
-q, --quiet                  Do not print confirmation about messages sent.
"

debug=
edit=
empty_ok=
infile=
verbose=t

# 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=
  }'

while : ; do
  case $# in 0) break ;; esac
  case "$1" in
    -D | --debug | --d* )
      debug=t
      shift
     ;;
    -e | --edit | -ed* )
      edit=t
      shift
     ;;
    -E | --empty-ok | -em* )
      empty_ok=t
      shift
     ;;
    -f | --file* | -f* )
      eval "$getopt"
      infile="$optarg"
     ;;
    -h | --help | --h )
      echo "$usage" 1>&2
      exit 0
     ;;
    -q | --quiet | --q* )
      verbose=
      shift
     ;;
    -- )     # 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 $# in
  0 ) echo "$usage" 1>&2; exit 1 ;;
esac

case "$infile" in
  '' ) : ;;
  * )  exec 0< "$infile" ;;
esac

case "$verbose" in
  t ) exec 3>&1 ;;
  * ) exec 3> /dev/null ;;
esac

write=${SAYTOWRITE-write}
nodelete=t
file="$infile"
exitstat=0

if test -t 0 ; then
  umask 077
  file="${TMPDIR-/tmp}/$progname$$"

  test "$debug" = t || nodelete=

  # The || pair of commands used to be a case statement, but bash 1.14 had
  # parsing bugs.
  trap 'test "$nodelete" = t || rm -f "$file"
        trap "" 0 1 3 15
        exit ${exitstat-0}
       ' 0 1 3 15

  trap 'echo "[Interrupted.  No message sent.]" 1>&3
        exitstat=130
        exit $exitstat
       ' 2

  if (> "$file") 2> /dev/null; then
    case "$edit" in
      t )
        echo "[Editing message with $bq${EDITOR-$stdeditor}$eq...]" 1>&3
        ${EDITOR-$stdeditor} $file
        exitstat=$?
       ;;
      * )
        echo "[Enter message, ending with C-d on a new line when finished, or C-c to abort.]" 1>&3
        cat > "$file"
        exitstat=$?
       ;;
    esac

    case "$exitstat" in
      0 ) : ;;
      * )
        exec 1>&2
        echo "*** $progname: Editor exited unsuccessfully."
        echo "*** $progname: No message sent.  Sorry."
        exit $exitstat
       ;;
    esac
  else
    exitstat=$?
    exec 1>&2
    echo "*** $progname: $file: Cannot create temporary file; aborting."
    exit $exitstat
  fi
fi

if test -s "$file"; then
  :
else
  case "$empty_ok" in
    t ) echo "[Sending empty message.]" 1>&3 ;;
    * )
      echo "[Message is empty.  Not sending.]" 1>&3
      exit 1
     ;;
  esac
fi

# If only args were in format "user tty", rewrite them to "user:tty"
# for parsing by the mechanism below equipped to handle multiple
# user:tty pairs.
case "$#" in
  2 )
    case "$2" in
      /dev/* | tty* | pts/* )
        set fnord "$1:$2"
        shift
       ;;
    esac
   ;;
esac

exitstat=0
for user in ${1+"$@"}; do
  tty=
  case "$user" in
    *:* )
      oIFS="$IFS"
      IFS=':'
      set fnord $user
      IFS="$oIFS"
      user=$2
      tty="$3"

      # The `write' program implicitly adds "/dev", so strip it out here
      case "$tty" in
        /dev/* ) tty=`echo "$tty" | sed -e 's/\/dev\///'` ;;
      esac
     ;;
  esac

  case "$tty" in
    '' ) s="$user" ;;
    *  ) s="$user on /dev/$tty" ;;
  esac

  if $write $user $tty < "$file" ; then
    echo "[Message sent to $s using $bq$write$eq.]" 1>&3
  else
    echo "*** $progname: $bq$write$eq to $s was unsuccessful." 1>&2
    nodelete=t
    exitstat=1
  fi
done

case "$exitstat:$nodelete" in
  1:t )
    if test -t 0; then
      echo "*** $progname: message retained in $bq$file$eq." 1>&2
    fi
    exit $exitstat
   ;;
esac

# sayto ends here
