[comp.sources.misc] v08i016: hint, an unobtrusive messager, part 1 of 2

allbery@uunet.UU.NET (Brandon S. Allbery - comp.sources.misc) (08/25/89)

Posting-number: Volume 8, Issue 16
Submitted-by: edf@ROCKY2.ROCKEFELLER.EDU (David MacKenzie)
Archive-name: hint/part01

hint is an alternative to write(1) that sends a one-line message to one
or more other users.  The message appears along with the sender's user
name and a beep (or, if possible, a ``visual bell'' such as flashing
the screen) on the status line of the recipient's terminal, and
disappears after 10 seconds.  If the recipient's terminal does not have
a status line, hints appear wherever the cursor happens to be when they
are received.


#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of archive 1 (of 2)."
# Contents:  README hint.1 clearhint.1 hall.1 MANIFEST main.c hint.h
#   hinttab.c hinttab.h stov.c stov.h str.c user.c hall.sh
# Wrapped by dave@zedfdc on Thu Aug 24 19:49:21 1989
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'README' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'README'\"
else
echo shar: Extracting \"'README'\" \(1576 characters\)
sed "s/^X//" >'README' <<'END_OF_FILE'
XInstallation notes for hint
X
XTo allow people to disable write and talk messages but still allow
Xhints, hint does not use the mesg bit to determine write permission.
XInstead, it uses the group-execute bit of the tty. However, because
Xsetting this bit does not actually give write permission, hint must run
Xsetuid to root.  This ensures that running the hint program is the only
Xinterface which can activate write permission for hints; that is
Ximportant, because not only must the permission bit be set, but an
Xentry in the file /usr/local/lib/hinttab must also be updated.
X
XA companion program, clearhint, is provided for systems where
X"tput dsl" is not available to clear the status line.
X
XFor people with Wyse 50 terminals, the standard termcap files often don't
Xhave the status line capabilities defined, and the Quick Reference Guide
Xthat comes with the terminal isn't of much help either.  For a status
Xline on the bottom, use:
X	:hs:es:ds=\Ef\r:fs=\r:ts=\Ef:ws#78:
X
XFor a short status line on the top, use:
X	:hs:es:ds=\EF \r:fs=\r:ts=\EF:ws#46:
X
XFor a flash instead of a beep (this works for vi also):
X	:vb=\E`8\E`9:
X
XTerminfo users will need to adapt these.
X
XA brief history of hint is approximately as follows:
X
X-Originally written by Paul Borman, for the Heath/Zenith 29 terminal only.
X-Rewritten by Mike Haertel, still for the H29 only.
X-Simple terminal independence added by Walter Poxon and Jon Westbrock.
X-Rewritten from scratch by David MacKenzie with an improved terminal
X independence system and other enhancements.
X
XDavid MacKenzie <edf@rocky2.rockefeller.edu>
END_OF_FILE
if test 1576 -ne `wc -c <'README'`; then
    echo shar: \"'README'\" unpacked with wrong size!
fi
# end of 'README'
fi
if test -f 'hint.1' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'hint.1'\"
else
echo shar: Extracting \"'hint.1'\" \(4553 characters\)
sed "s/^X//" >'hint.1' <<'END_OF_FILE'
X.\" Copyright (C) 1989 David MacKenzie
X.\" 
X.\" Permission is granted to make and distribute verbatim copies of this
X.\" manual provided the copyright notice and this permission notice are
X.\" preserved on all copies.
X.\" 
X.\" Permission is granted to process this file through nroff or troff
X.\" and print the results.
X.\"
X.\" Permission is granted to copy and distribute modified versions of
X.\" this manual under the conditions for verbatim copying, provided that
X.\" the entire resulting derived work is distributed under the terms of
X.\" a permission notice identical to this one.
X.\" 
X.\" Permission is granted to copy and distribute translations of this
X.\" manual into another language, under the above conditions for
X.\" modified versions.
X.TH HINT 1L
X.SH NAME
Xhint \- send a one-line message to other users
X.SH SYNOPSIS
X\fBhint\fR [ \fB\-fp\fR ] [ \fB\-d sec\fR ]
X\fBuser\fR|\fBtty\fR[\fB,user\fR|\fBtty\fR...] [ \fBmessage\fR ]
X.br
X\fBhint \-y\fR [ \fB\-bs\fR ] [ \fB\-T termtype\fR ]
X.br
X\fBhint \-n\fR
X.br
X\fBhint \-V\fR
X.br
X\fBhint\fR
X.SH DESCRIPTION
X.I hint
Xsends a one-line message to one or more other users.  The message
Xappears along with the sender's user name and a beep (or, if possible,
Xa ``visual bell'' such as flashing the screen) on the status line of
Xthe recipient's terminal, and disappears after 10 seconds.  If the
Xrecipient's terminal does not have a status line, hints appear wherever
Xthe cursor happens to be when they are received.
X.PP
XIf
X.I hint
Xis run with no arguments, it displays the user's current hint status,
Xwhich is either
X.B y
Xor
X.BR n ,
Xmeaning the user is allowing or refusing hints, respectively.  The hint
Xstatus is independent of the
X.BR mesg (1)
Xstatus.
X.PP
XIf one or more user names (e.g., \fIdave\fR)
Xor terminal names (e.g., \fItty10\fR)
Xare given (separated by commas but no spaces), any
Xarguments after them are taken to be the message to send.  If
Xthe message is not given on the command line,
X.I hint
Xprompts for the message from the keyboard.
X.PP
X.I hint
Xsends the message to all terminals where each given user
Xis logged on and all terminals given directly.
X.I hint
Xskips users who are not logged in and hint
X.B y
Xand terminals that are not hint
X.BR y ,
Xand sends the message to whatever users and terminals remain.
X.SS OPTIONS
X.PP
XThe following options are used when setting the hint status:
X.TP
X.I \-y
XAllow receipt of hints; no hint is sent.  This option saves the codes
Xfor accessing your terminal's status line, as read from the
X.BR termcap (5)
Xor
X.BR terminfo (4)
Xdatabase (whichever is available),
Xinto an entry in the file /usr/local/lib/hinttab.
X.TP
X.I \-b
XForce hints that you receive to beep even if a ``visual bell'' is available.
XHas no effect if the
X.I \-y
Xoption is not also given.
X.TP
X.I \-s
XForce hints that you receive to be silent; a ``visual bell'' will be used
Xif available.  If both
X.I \-b
Xand
X.I \-s
Xare given, neither a beep nor a ``visual bell'' will be used.
XHas no effect if the
X.I \-y
Xoption is not also given.
X.TP
X.I \-T termtype
XUse
X.I termtype
Xas the terminal type.
XHas no effect if the
X.I \-y
Xoption is not also given.
X.TP
X.I \-n
XRefuse receipt of hints; no hint is sent.
X.PP
XThe following options are used when sending a hint:
X.TP
X.I \-f
XMake the hint last forever (until overwritten or cleared).  This option
Xoverrides any
X.I \-d
Xoption given.
X.TP
X.I \-p
XMake the sender's name appear preceding the hint, enclosed in parentheses,
Xrather than after the hint, following a dash.
X.TP
X.I \-d sec
XChange the hint duration from 10 seconds to
X.I sec
Xseconds.
X.I sec
Xis not allowed to be less than 5 or greater than 60.
X.PP
XAdditional options:
X.TP
X.I \-V
XDisplay the version number of the
X.I hint
Xprogram; no other action is taken.
X.SS ENVIRONMENT
X.PP
XAll options except
X.IR \-y ,
X.IR \-n ,
Xand
X.I -V
Xcan be given defaults by setting the
X.B HINT
Xenvironment variable.
XFor example, to change the default duration
Xof hints that you send from 10 seconds to 15 seconds, and always
Xreceive silent hints, you could use:
X.sp
X.RS
XFor the C shell:
X.br
Xsetenv HINT "-d15 -s"
X.sp
XFor the Bourne and Korn shells:
X.br
XHINT="-d15 -s"; export HINT
X.RE
X.SH FILES
X/usr/local/lib/hinttab
X.SH "SEE ALSO"
X.BR clearhint (1L),
X.BR hall (1L),
X.BR mesg (1),
X.BR write (1),
X.BR terminfo (4),
X.BR termcap (5)
X.SH BUGS
XIf a hint is sent to several terminals which have different length
Xstatus lines, the message will be truncated on all of the terminals to
Xthe length of the shortest status line, so that everyone gets the same hint.
X.PP
XThere is no network support.
X.SH AUTHOR
XDavid MacKenzie
END_OF_FILE
if test 4553 -ne `wc -c <'hint.1'`; then
    echo shar: \"'hint.1'\" unpacked with wrong size!
fi
# end of 'hint.1'
fi
if test -f 'clearhint.1' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'clearhint.1'\"
else
echo shar: Extracting \"'clearhint.1'\" \(1612 characters\)
sed "s/^X//" >'clearhint.1' <<'END_OF_FILE'
X.\" Copyright (C) 1989 David MacKenzie
X.\" 
X.\" Permission is granted to make and distribute verbatim copies of this
X.\" manual provided the copyright notice and this permission notice are
X.\" preserved on all copies.
X.\" 
X.\" Permission is granted to process this file through nroff or troff
X.\" and print the results.
X.\"
X.\" Permission is granted to copy and distribute modified versions of
X.\" this manual under the conditions for verbatim copying, provided that
X.\" the entire resulting derived work is distributed under the terms of
X.\" a permission notice identical to this one.
X.\" 
X.\" Permission is granted to copy and distribute translations of this
X.\" manual into another language, under the above conditions for
X.\" modified versions.
X.TH CLEARHINT 1L
X.SH NAME
Xclearhint \- erase terminal's status line
X.SH SYNOPSIS
X.B clearhint
X[
X.B \-T terminal
X]
X.SH DESCRIPTION
X.I clearhint
Xerases the status
Xline (usually a ``25th line'') of the user's terminal.  If the terminal
Xdoes not have a status line,
X.I clearhint
Xdoes nothing.  The program
Xis named for its main use, which is erasing messages sent by the
X.BR hint (1L)
Xprogram with the
X.I \-f
X(make the hint last forever) option given.
X.PP
X.I clearhint
Xuses the
X.BR termcap (5)
Xor
X.BR terminfo (4)
Xdatabase (whichever is available) to determine how to move the cursor to the
Xstatus line of each user's terminal.
X.SS OPTIONS
X.TP
X.I \-T terminal
XUse
X.I terminal
Xas the terminal type instead of the value given by the
X.B TERM
Xenvironment variable.
X.SH "SEE ALSO"
X.BR hall (1L),
X.BR hint (1L),
X.BR terminfo (4),
X.BR termcap (5)
X.SH AUTHOR
XDavid MacKenzie
END_OF_FILE
if test 1612 -ne `wc -c <'clearhint.1'`; then
    echo shar: \"'clearhint.1'\" unpacked with wrong size!
fi
# end of 'clearhint.1'
fi
if test -f 'hall.1' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'hall.1'\"
else
echo shar: Extracting \"'hall.1'\" \(1161 characters\)
sed "s/^X//" >'hall.1' <<'END_OF_FILE'
X.\" Copyright (C) 1989 David MacKenzie
X.\" 
X.\" Permission is granted to make and distribute verbatim copies of this
X.\" manual provided the copyright notice and this permission notice are
X.\" preserved on all copies.
X.\" 
X.\" Permission is granted to process this file through nroff or troff
X.\" and print the results.
X.\"
X.\" Permission is granted to copy and distribute modified versions of
X.\" this manual under the conditions for verbatim copying, provided that
X.\" the entire resulting derived work is distributed under the terms of
X.\" a permission notice identical to this one.
X.\" 
X.\" Permission is granted to copy and distribute translations of this
X.\" manual into another language, under the above conditions for
X.\" modified versions.
X.TH HALL 1L
X.SH NAME
Xhall - hint all users logged on
X.SH SYNOPSIS
X.B hall
X[
X.B message
X]
X.SH DESCRIPTION
X.I hall
Xsends a hint message to everyone who is logged on to the
Xmachine where it is run and who is hint
X.BR y .
XIf no message is given on the command line,
X.I hall
Xprompts for one.
X.SH FILES
X.TP
X/etc/utmp
Xlist of who is logged on
X.SH "SEE ALSO"
X.BR clearhint (1L),
X.BR hint (1L)
X.SH AUTHOR
XDavid MacKenzie
END_OF_FILE
if test 1161 -ne `wc -c <'hall.1'`; then
    echo shar: \"'hall.1'\" unpacked with wrong size!
fi
# end of 'hall.1'
fi
if test -f 'MANIFEST' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'MANIFEST'\"
else
echo shar: Extracting \"'MANIFEST'\" \(672 characters\)
sed "s/^X//" >'MANIFEST' <<'END_OF_FILE'
X   File Name		Archive #	Description
X-----------------------------------------------------------
X README                     1	
X hint.1                     1	
X clearhint.1                1	
X hall.1                     1	
X MANIFEST                   1	This shipping list
X main.c                     1	
X hint.h                     1	
X hinttab.c                  1	
X hinttab.h                  1	
X stov.c                     1	
X stov.h                     1	
X str.c                      1	
X tty.c                      2	
X user.c                     1	
X clearhint.c                2	
X hall.sh                    1	
X Makefile                   2	
X COPYING                    2	
END_OF_FILE
if test 672 -ne `wc -c <'MANIFEST'`; then
    echo shar: \"'MANIFEST'\" unpacked with wrong size!
fi
# end of 'MANIFEST'
fi
if test -f 'main.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'main.c'\"
else
echo shar: Extracting \"'main.c'\" \(10862 characters\)
sed "s/^X//" >'main.c' <<'END_OF_FILE'
X/* main.c -- argument parser and control loops
X   Copyright (C) 1989 David MacKenzie
X
X   This program is free software; you can redistribute it and/or modify
X   it under the terms of the GNU General Public License as published by
X   the Free Software Foundation; either version 1, or (at your option)
X   any later version.
X
X   This program is distributed in the hope that it will be useful,
X   but WITHOUT ANY WARRANTY; without even the implied warranty of
X   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
X   GNU General Public License for more details.
X
X   You should have received a copy of the GNU General Public License
X   along with this program; if not, write to the Free Software
X   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
X
X/* hint - send a one-line message to other users
X
X   Usage: hint [-fp] [-d sec] user|tty[,user|tty...] [message]
X	  hint -y [-bs] [-T terminal]
X	  hint -n
X	  hint -V
X	  hint
X
X   Options:
X   -f	make hint last forever, until overwritten
X   -p	put sender's name at beginning of hint instead of end
X   -d	duration in seconds (5 <= sec <= 60) before hint is erased (default 10)
X
X   -y	enable receipt of hints
X   -b	don't use a visual bell (can still use audible bell)
X   -s	don't use an audible bell (can still use visual bell)
X   -T	specify terminal type, overriding $TERM
X
X   -n	disable receipt of hints
X
X   -V	display program version number
X
X   With no arguments, displays the current hint status.
X
X   David MacKenzie <edf@rocky2.rockefeller.edu>
X   Latest revision: 08/24/89 */
X
X#define ALLOCATE		/* Allocate global variables in hint.h. */
X
X#include "hint.h"
X
X/* Hint status to set if 'y' or 'n'. */
Xstatic int new_stats;
X
X/* The hint message can be no longer than this. */
Xstatic int length_limit;
X
X/* Head of linked list of ttys to hint. */
Xstatic struct ttylist *tty_list;
X
Xint
Xmain (argc, argv)
X     int argc;
X     char **argv;
X{
X  extern int optind;
X  struct ttylist *tp;		/* A ttylist entry. */
X  char temp_message[MAX_MESSAGE];	/* Hint read from tty. */
X  int i;			/* To reset `optind' between uses. */
X
X  program_name = argv[0] = basename (argv[0]);
X  new_stats = 0;
X  length_limit = MAX_MESSAGE - 1;
X
X  if (argc == 1)
X    {
X      show_status ();
X      exit (0);
X    }
X
X  if (chdir ("/dev") == -1)
X    pfatal ("Cannot change directory to /dev");
X
X  term = getenv ("TERM");
X  /* Some implementations of getopt seem to start out with `optind' == 0,
X     while others start out with `optind' == 1.  Whatever it starts out as,
X     we save it and then restore it.  */
X  i = optind;
X  parse_environment ();
X  optind = i;
X  parse_options (argc, argv, 1);
X
X  if (new_stats)
X    {
X      if (argc < optind)
X	usage ();
X      set_status (new_stats);
X      exit (0);
X    }
X
X  /* Check for user|tty[,user|tty...]. */
X  if (optind < argc)
X      add_recipients (argv[optind++]);
X  if (tty_list == NULL)
X    {
X      fprintf (stderr, "%s: No ttys to hint\n", program_name);
X      exit (1);
X    }
X  endhtent ();
X
X  length_limit -= 11;		/* 8 for username + 3 for punctuation. */
X  if (length_limit < 11)
X    length_limit = 11;		/* Let's be reasonable. */
X
X  /* Check for message. */
X  if (optind < argc)
X    for (; optind < argc; ++optind)
X      {
X	append_word (argv[optind]);
X	if (optind < argc - 1)
X	  append_word (" ");
X      }
X  else
X    {
X      prompt ();
X      fgets (temp_message, MAX_MESSAGE, stdin);
X      append_word (temp_message);
X    }
X
X  set_signals ();
X  /* Send the hint to all relevant ttys. */
X  while (tp = next_tty ())
X    sendhint (tp);
X
X  if (forever)
X    exit (0);
X  switch (fork ())
X    {
X    case -1:
X      pfatal ("Cannot fork");
X    case 0:			/* Child. */
X      sleep (duration);
X      while (tp = next_tty ())
X	erasehint (tp);
X    }
X  exit (0);
X  /* NOTREACHED */
X}
X
Xvoid
Xparse_environment ()
X{
X  char *enval;
X  struct args *argsp;
X  char *s;			/* program_name + enval. */
X
X  enval = getenv (ENVAR);
X  if (enval && *enval)
X    {
X      s = xmalloc ((unsigned) (strlen (enval) + strlen (program_name) + 2));
X      strcpy (s, program_name);
X      strcat (s, " ");
X      strcat (s, enval);
X      argsp = stov (s);
X      parse_options (argsp->argc, argsp->argv, 0);
X      free (s);
X    }
X}
X
X/* If `cmdline' is nonzero, this function was called with command line
X   arguments; otherwise they were taken from the environment. */
X
Xvoid
Xparse_options (argc, argv, cmdline)
X     int argc;
X     char **argv;
X     int cmdline;
X{
X  extern int optind;
X  extern char *optarg;
X  int c;
X
X  while ((c = getopt (argc, argv, "ynbfpsd:T:V")) != EOF)
X    {
X      switch (c)
X	{
X	case 'y':
X	case 'n':
X	  if (cmdline)
X	    new_stats = c;
X	  else
X	    ignored (c);
X	  break;
X	case 'b':
X	  use_vbell = 0;
X	  break;
X	case 'f':
X	  forever = 1;
X	  break;
X	case 'p':
X	  name_at_end = 0;
X	  break;
X	case 's':
X	  use_bell = 0;
X	  break;
X	case 'd':
X	  duration = atoi (optarg);
X	  if (duration < MIN_SEC || duration > MAX_SEC)
X	    {
X	      fprintf (stderr,
X		       "%s: %d: Duration out of range (%d-%d seconds)\n",
X		       program_name, duration, MIN_SEC, MAX_SEC);
X	      exit (1);
X	    }
X	  break;
X	case 'T':
X	  term = optarg;
X	  break;
X	case 'V':
X	  if (cmdline)
X	    {
X	      fprintf (stderr, "hint, version 3.5\n");
X	      exit (0);
X	    }
X	  else
X	    ignored (c);
X	  break;
X	default:
X	  if (cmdline)
X	    usage ();
X	  else
X	    fprintf (stderr, "%s: Invalid option in environment ignored\n",
X		     program_name);
X	  break;
X	}
X    }
X}
X
X/* Display a prompt that attempts to be as long as `length_limit'. */
X
Xvoid
Xprompt ()
X{
X  static char *prompt_string = "[ Enter message (max. %d characters):";
X  int prompt_length;
X
X  printf (prompt_string, length_limit);
X  /* Set prompt_length to length of the prompt except for trailing spaces. */
X  prompt_length = strlen (prompt_string) + 1;	/* The 1 is the ']'. */
X  if (length_limit > 99)
X    ++prompt_length;		/* Adjust for the extra digit. */
X  if (prompt_length > length_limit)
X    prompt_length = length_limit;
X  printf ("%*s]\n", length_limit - prompt_length, " ");
X  fflush (stdout);
X}
X
X/* Print the program name, `s', and a system error message, and die.  */
X
Xvoid
Xpfatal (s)
X     char *s;
X{
X  fprintf (stderr, "%s: ", program_name);
X  perror (s);
X  exit (1);
X}
X
X/* Same but stay alive. */
X
Xvoid
Xpnonfatal (s)
X     char *s;
X{
X  fprintf (stderr, "%s: ", program_name);
X  perror (s);
X}
X
Xvoid
Xshow_status ()
X{
X  char *tty;
X
X  tty = ttyname (2);		/* Stderr is least likely to be redirected. */
X  if (tty == NULL)
X    pfatal ("Cannot get ttyname");
X  printf ("is %c\n", hintable (tty) ? 'y' : 'n');
X}
X
X/* Set hint status.
X   `newstat' can be 'y' or 'n'.  */
X
Xvoid
Xset_status (newstat)
X     char newstat;
X{
X  struct stat tty_stats;
X  char *tty;
X
X  tty = ttyname (2);
X  if (tty == NULL)
X    pfatal ("Cannot get ttyname");
X  if (stat (tty, &tty_stats) < 0)
X    pfatal (tty);
X
X  if (newstat == 'y')
X    {
X      tty_stats.st_mode |= HINTBIT;
X      updatetab (tty);
X    }
X  else
X    tty_stats.st_mode &= ~HINTBIT;
X  if (chmod (tty, tty_stats.st_mode) < 0)
X    pfatal (tty);
X}
X
X/* Return true if `tty' is hint y, false otherwise.  */
X
Xint
Xhintable (tty)
X     char *tty;
X{
X  struct stat tty_stats;
X
X  if (stat (tty, &tty_stats) < 0)
X    {
X      pnonfatal (tty);
X      return 0;			/* Cannot stat; assume they're hint n. */
X    }
X  return tty_stats.st_mode & HINTBIT;
X}
X
X/* Return true if `name' is a tty device in /dev. */
X
Xint
Xistty (name)
X     char *name;
X{
X  struct stat stats;
X  char *path;
X  int i;
X
X  path = xmalloc (strlen (name) + 3);
X  strcpy (path, "./");
X  strcat (path, name);
X  i = stat (path, &stats);
X  free (path);
X  if (i == -1 || (stats.st_mode & S_IFMT) != S_IFCHR)
X    return 0;
X  else
X    return 1;
X}
X
Xvoid
Xadd_recipients (names)
X     char *names;
X{
X  char *name;
X
X  for (name = strtok (names, ","); name; name = strtok ((char *) NULL, ","))
X    if (istty (name))
X      {
X	addtty ("user", name);
X      }
X    else
X      {
X	/* Add any ttys where user `name' is logged on to `tty_list'. */
X	if (adduser (name) == 0)
X	  fprintf (stderr, "%s: %s is not logged on\n", program_name, name);
X      }
X}
X
X/* Add `user' at `tty' (which should have no leading path) to the list of
X   ttys to send the hint to.  */
X
Xvoid
Xaddtty (user, tty)
X     char *user;
X     char *tty;
X{
X  struct ttylist *tp;
X  struct hinttab *ht;
X  char *ttypath;
X
X  /* Make sure they haven't already been added. */
X  for (tp = tty_list; tp; tp = tp->tt_next)
X    if (!strcmp (tp->tt_tty, tty))
X      return;
X
X  if (!hintable (tty))
X    {
X      /* They're hint n. */
X      fprintf (stderr, "%s: %s on %s is refusing hints\n",
X	       program_name, user, tty);
X      return;
X    }
X
X  sethtent ();			/* Rewind hinttab file. */
X
X  ttypath = xmalloc ((unsigned) (strlen (tty) + 6));
X  strcpy (ttypath, "/dev/");
X  strcat (ttypath, tty);
X  ht = gethttty (ttypath);
X  free (ttypath);
X
X  if (ht == NULL)
X    {
X      fprintf (stderr, "%s: No hinttab entry for %s\n", program_name, tty);
X      return;
X    }
X
X  tp = (struct ttylist *) xmalloc (sizeof (struct ttylist));
X  tp->tt_user = strdup (user);
X  tp->tt_tty = strdup (tty);
X  tp->tt_ht = *ht;
X
X  tp->tt_next = tty_list;
X  tty_list = tp;
X
X  /* Keep track of the shortest status line we'll be hinting. */
X  if (length_limit > tp->tt_ht.h_ws)
X    length_limit = tp->tt_ht.h_ws;
X}
X
X/* Return the next tty in the list.
X   Return NULL if the list is empty.  */
X
Xstruct ttylist *
Xnext_tty ()
X{
X  static struct ttylist *tp = NULL;
X
X  tp = tp ? tp->tt_next : tty_list;
X
X  return tp;
X}
X
Xvoid
Xset_signals ()
X{
X#ifdef SIGTTOU
X  signal (SIGTTOU, SIG_IGN);
X#endif
X  /* If the sender logs out right after hinting, stick around to erase the
X     hint. */
X  signal (SIGHUP, SIG_IGN);
X  signal (SIGINT, SIG_IGN);
X}
X
X/* Concatenate `word' onto the end of the hint message, silently truncating
X   if it would overflow `length_limit', and removing any control
X   characters.  */
X
Xvoid
Xappend_word (word)
X     char *word;
X{
X  static int message_length = 0;/* Length of message. */
X
X  for (; *word && message_length < length_limit; ++word)
X    if (isascii (*word) && isprint (*word))
X      message[message_length++] = *word;
X  message[message_length] = 0;
X}
X
X/* Return a malloc'd duplicate of string `s'. */
X
Xchar *
Xstrdup (s)
X     char *s;
X{
X  return strcpy (xmalloc ((unsigned) (strlen (s) + 1)), s);
X}
X
X/* Return `name' with any leading path stripped off.  */
X
Xchar *
Xbasename (name)
X     char *name;
X{
X  char *base;
X
X  base = strrchr (name, '/');
X  return base ? base + 1 : name;
X}
X
Xvoid
Xignored (c)
X     char c;
X{
X  fprintf (stderr, "%s: Option ignored in environment: -%c\n", program_name, c);
X}
X
Xvoid
Xusage ()
X{
X  fprintf (stderr,
X	   "Usage: %s [-fp] [-d sec] user|tty[,user|tty...] [message]\n",
X	   program_name);
X  fprintf (stderr, "       %s -y [-bs] [-T termtype]\n", program_name);
X  fprintf (stderr, "       %s -n\n", program_name);
X  fprintf (stderr, "       %s -V\n", program_name);
X  fprintf (stderr, "       %s\n", program_name);
X  exit (1);
X}
END_OF_FILE
if test 10862 -ne `wc -c <'main.c'`; then
    echo shar: \"'main.c'\" unpacked with wrong size!
fi
# end of 'main.c'
fi
if test -f 'hint.h' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'hint.h'\"
else
echo shar: Extracting \"'hint.h'\" \(3447 characters\)
sed "s/^X//" >'hint.h' <<'END_OF_FILE'
X/* hint.h -- constant, variable and function declarations
X   Copyright (C) 1989 David MacKenzie
X
X   This program is free software; you can redistribute it and/or modify
X   it under the terms of the GNU General Public License as published by
X   the Free Software Foundation; either version 1, or (at your option)
X   any later version.
X
X   This program is distributed in the hope that it will be useful,
X   but WITHOUT ANY WARRANTY; without even the implied warranty of
X   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
X   GNU General Public License for more details.
X
X   You should have received a copy of the GNU General Public License
X   along with this program; if not, write to the Free Software
X   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
X
X#ifndef USG
X#define strrchr rindex
X#endif
X
X/* Enforced limits on hint duration. */
X#define MIN_SEC 5
X#define MAX_SEC 60
X
X/* Maximum length of message (who has terminals wider than this?). */
X#define MAX_MESSAGE 400
X
X/* Default status line width if not defined in termcap. */
X#define DEF_WS 78
X
X/* The bit that determines hint y or n. */
X#define HINTBIT 00010
X
X/* Environment variable to check for options. */
X#define ENVAR "HINT"
X
X#include <stdio.h>
X#include <sys/types.h>
X#include <sys/stat.h>
X#include <ctype.h>
X#include <pwd.h>
X#include <fcntl.h>
X#include <signal.h>
X#ifdef USG
X#include <termio.h>
X#else
X#include <sgtty.h>
X#endif
X
X#include "stov.h"
X#include "hinttab.h"
X
X/* List of ttys to send hint to. */
Xstruct ttylist
X{
X  char *tt_user;
X  char *tt_tty;			/* Contains no leading path. */
X  struct hinttab tt_ht;
X  struct ttylist *tt_next;
X};
X
X#ifdef ALLOCATE
X#define EXTERN
X#define INIT(x) = {x}
X#else
X#define EXTERN extern
X#define INIT(x)
X#endif
X
X/* Base of the name this program was run with. */
XEXTERN char *program_name;
X
X/* Terminal type. */
XEXTERN char *term;
X
X/* The hint to send. */
XEXTERN char message[MAX_MESSAGE];
X
X/* If nonzero, put the name at the start of the hint. */
XEXTERN int name_at_end INIT (1);
X
X/* If nonzero, erase the hint. */
XEXTERN int forever INIT (0);
X
X/* If nonzero, try to use bell capability. */
XEXTERN int use_bell INIT (1);
X
X/* If nonzero, try to use visual bell capability. */
XEXTERN int use_vbell INIT (1);
X
X/* Seconds between sending the hint and erasing it. */
XEXTERN int duration INIT (10);
X
Xchar *getenv ();
Xchar *getlogin ();
Xchar *malloc ();
Xchar *realloc ();
Xchar *strcat ();
Xchar *strcpy ();
Xchar *strncat ();
Xchar *strncpy ();
Xchar *strrchr ();
Xchar *strtok ();
Xchar *tgetstr ();
Xchar *tgoto ();
Xchar *ttyname ();
Xint strcspn ();
Xint strspn ();
Xoff_t lseek ();
Xstruct passwd *getpwuid ();
Xunsigned sleep ();
Xvoid perror ();
Xvoid exit ();
Xvoid free ();
X
Xchar *basename ();
Xchar *pwname ();
Xchar *strdup ();
Xchar *whoami ();
Xchar *xmalloc ();
Xchar *xrealloc ();
Xint addmatch ();
Xint adduser ();
Xint buffer_char ();
Xint hintable ();
Xint istty ();
Xstruct args *stov ();
Xstruct hinttab *gethttty ();
Xstruct ttylist *next_tty ();
Xvoid add_recipients ();
Xvoid addtty ();
Xvoid append_word ();
Xvoid buffer_string ();
Xvoid endhtent ();
Xvoid erasehint ();
Xvoid flush_buffer ();
Xvoid get_speed ();
Xvoid ignored ();
Xvoid init_buffer ();
Xvoid parse_environment ();
Xvoid parse_options ();
Xvoid pfatal ();
Xvoid pnonfatal ();
Xvoid prompt ();
Xvoid sendhint ();
Xvoid set_signals ();
Xvoid set_status ();
Xvoid sethtapp ();
Xvoid sethtent ();
Xvoid setup_termcap ();
Xvoid show_status ();
Xvoid updatetab ();
Xvoid usage ();
Xvoid writehtent ();
END_OF_FILE
if test 3447 -ne `wc -c <'hint.h'`; then
    echo shar: \"'hint.h'\" unpacked with wrong size!
fi
# end of 'hint.h'
fi
if test -f 'hinttab.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'hinttab.c'\"
else
echo shar: Extracting \"'hinttab.c'\" \(2073 characters\)
sed "s/^X//" >'hinttab.c' <<'END_OF_FILE'
X/* hinttab.c -- functions for dealing with hinttab
X   Copyright (C) 1989 David MacKenzie
X
X   This program is free software; you can redistribute it and/or modify
X   it under the terms of the GNU General Public License as published by
X   the Free Software Foundation; either version 1, or (at your option)
X   any later version.
X
X   This program is distributed in the hope that it will be useful,
X   but WITHOUT ANY WARRANTY; without even the implied warranty of
X   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
X   GNU General Public License for more details.
X
X   You should have received a copy of the GNU General Public License
X   along with this program; if not, write to the Free Software
X   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
X
X#include "hint.h"
X
Xstatic int fd = -1;
Xstatic off_t offset;
X
X/* Open (rewind if already open) the hinttab for reading and writing.  */
X
Xvoid
Xsethtent ()
X{
X  if (fd == -1)
X    {
X      fd = open (HINTTAB, O_RDWR);
X      if (fd == -1)
X	pfatal (HINTTAB);
X    }
X  else
X    {
X      offset = 0;
X      if (lseek (fd, offset, 0) == -1)
X	pfatal (HINTTAB);
X    }
X}
X
X/* Open the hinttab for appending.  */
X
Xvoid
Xsethtapp ()
X{
X  if (fd != -1)
X    close (fd);
X  fd = open (HINTTAB, O_WRONLY | O_APPEND);
X  if (fd == -1)
X    pfatal (HINTTAB);
X  offset = 0;
X}
X
X/* Return a pointer to the hinttab entry matching `tty', or
X   NULL if none do.
X   The offset into hinttab will be left pointing to the start of the
X   matching entry. */
X
Xstruct hinttab *
Xgethttty (tty)
X     char *tty;
X{
X  static struct hinttab h;
X
X  while (offset = lseek (fd, (off_t) 0, 1),
X	 read (fd, &h, sizeof (h)) == sizeof (h))
X    {
X      if (!strcmp (tty, h.h_tty))
X	{
X	  /* Seek back to the start of the record. */
X	  lseek (fd, offset, 0);
X	  return &h;
X	}
X    }
X  return NULL;
X}
X
X/* Update the current entry (the last one matched by gethttty).  */
X
Xvoid
Xwritehtent (h)
X     struct hinttab *h;
X{
X  if (write (fd, h, sizeof (*h)) != sizeof (*h))
X    pfatal (HINTTAB);
X}
X
X/* Close the hinttab.  */
X
Xvoid
Xendhtent ()
X{
X  close (fd);
X  fd = -1;
X}
END_OF_FILE
if test 2073 -ne `wc -c <'hinttab.c'`; then
    echo shar: \"'hinttab.c'\" unpacked with wrong size!
fi
# end of 'hinttab.c'
fi
if test -f 'hinttab.h' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'hinttab.h'\"
else
echo shar: Extracting \"'hinttab.h'\" \(1411 characters\)
sed "s/^X//" >'hinttab.h' <<'END_OF_FILE'
X/* hinttab.h -- declarations for accessing the hinttab file
X   Copyright (C) 1989 David MacKenzie
X
X   This program is free software; you can redistribute it and/or modify
X   it under the terms of the GNU General Public License as published by
X   the Free Software Foundation; either version 1, or (at your option)
X   any later version.
X
X   This program is distributed in the hope that it will be useful,
X   but WITHOUT ANY WARRANTY; without even the implied warranty of
X   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
X   GNU General Public License for more details.
X
X   You should have received a copy of the GNU General Public License
X   along with this program; if not, write to the Free Software
X   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
X
X/* Maximum length of termcap strings. */
X#define TCSLEN 20
X
X/* Structure of record in hinttab.
X   These are the strings returned by tgetstr.  They will be run through
X   tputs when the hint is actually sent.
X
X   Records are assigned in hinttab in first come, first served order.  */
Xstruct hinttab
X{
X  char h_tty[15];		/* Full pathname. */
X  char h_ts[TCSLEN];		/* Go to status line. */
X  char h_fs[TCSLEN];		/* Go from status line. */
X  char h_ds[TCSLEN];		/* Disable (clear) status line. */
X  char h_bl[TCSLEN];		/* Bell or visible bell. */
X  short h_ws;			/* Width (columns) of status line. */
X};
X
Xstruct hinttab *gethttty ();
END_OF_FILE
if test 1411 -ne `wc -c <'hinttab.h'`; then
    echo shar: \"'hinttab.h'\" unpacked with wrong size!
fi
# end of 'hinttab.h'
fi
if test -f 'stov.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'stov.c'\"
else
echo shar: Extracting \"'stov.c'\" \(2409 characters\)
sed "s/^X//" >'stov.c' <<'END_OF_FILE'
X/* stov.c -- convert string to vector
X   Copyright (C) 1989 David MacKenzie
X
X   This program is free software; you can redistribute it and/or modify
X   it under the terms of the GNU General Public License as published by
X   the Free Software Foundation; either version 1, or (at your option)
X   any later version.
X
X   This program is distributed in the hope that it will be useful,
X   but WITHOUT ANY WARRANTY; without even the implied warranty of
X   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
X   GNU General Public License for more details.
X
X   You should have received a copy of the GNU General Public License
X   along with this program; if not, write to the Free Software
X   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
X
X#include "hint.h"
X
X/* Number of arguments between realloc's. */
X#define GRANULARITY 10
X
X/* Make an argv-like vector (string array) from string `s' (such as an
X   environment variable).  The last element of the vector is null.
X   Return a static structure with a malloc'd array of pointers into `s'.
X   Scatters nulls throughout `s'.  */
X
Xstruct args *
Xstov (s)
X     char *s;
X{
X  static struct args args;
X  int argc;
X  char **argv;
X
X  argc = 0;
X  argv = (char **) xmalloc ((unsigned) (sizeof (char *) * GRANULARITY));
X
X  for (s = strtok (s, " \t\n\r"); s; s = strtok ((char *) NULL, " \t\n\r"))
X    {
X      if (argc > 0 && argc % GRANULARITY == 0)
X	argv = (char **) xrealloc ((char *) argv,
X		       (unsigned) (sizeof (char *) * (argc + GRANULARITY)));
X      argv[argc++] = s;
X    }
X  argv = (char **) xrealloc ((char *) argv,
X			     (unsigned) (sizeof (char *) * (argc + 1)));
X  argv[argc] = NULL;
X  args.argc = argc;
X  args.argv = argv;
X  return &args;
X}
X
Xstatic void
Xmemory_out ()
X{
X  fprintf (stderr, "%s: Virtual memory exhausted\n", program_name);
X  exit (1);
X}
X
X/* Allocate `n' bytes of memory dynamically, with error checking.  */
X
Xchar *
Xxmalloc (n)
X     unsigned n;
X{
X  char *p;
X
X  p = malloc (n);
X  if (p == 0)
X    memory_out ();
X  return p;
X}
X
X/* Change the size of an allocated block of memory `p' to `n' bytes,
X   with error checking.
X   If `p' is NULL, run xmalloc.
X   If `n' is 0, run free and return NULL.  */
X
Xchar *
Xxrealloc (p, n)
X     char *p;
X     unsigned n;
X{
X  if (p == 0)
X    return xmalloc (n);
X  if (n == 0)
X    {
X      free (p);
X      return 0;
X    }
X  p = realloc (p, n);
X  if (p == 0)
X    memory_out ();
X  return p;
X}
END_OF_FILE
if test 2409 -ne `wc -c <'stov.c'`; then
    echo shar: \"'stov.c'\" unpacked with wrong size!
fi
# end of 'stov.c'
fi
if test -f 'stov.h' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'stov.h'\"
else
echo shar: Extracting \"'stov.h'\" \(880 characters\)
sed "s/^X//" >'stov.h' <<'END_OF_FILE'
X/* stov.h -- declarations for using stov function
X   Copyright (C) 1989 David MacKenzie
X
X   This program is free software; you can redistribute it and/or modify
X   it under the terms of the GNU General Public License as published by
X   the Free Software Foundation; either version 1, or (at your option)
X   any later version.
X
X   This program is distributed in the hope that it will be useful,
X   but WITHOUT ANY WARRANTY; without even the implied warranty of
X   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
X   GNU General Public License for more details.
X
X   You should have received a copy of the GNU General Public License
X   along with this program; if not, write to the Free Software
X   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
X
X/* Structure for return value of stov. */
Xstruct args
X{
X  int argc;
X  char **argv;
X};
X
Xstruct args *stov ();
END_OF_FILE
if test 880 -ne `wc -c <'stov.h'`; then
    echo shar: \"'stov.h'\" unpacked with wrong size!
fi
# end of 'stov.h'
fi
if test -f 'str.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'str.c'\"
else
echo shar: Extracting \"'str.c'\" \(2222 characters\)
sed "s/^X//" >'str.c' <<'END_OF_FILE'
X/* str.c -- string functions missing from BSD
X   Copyright (C) 1989 David MacKenzie
X
X   This program is free software; you can redistribute it and/or modify
X   it under the terms of the GNU General Public License as published by
X   the Free Software Foundation; either version 1, or (at your option)
X   any later version.
X
X   This program is distributed in the hope that it will be useful,
X   but WITHOUT ANY WARRANTY; without even the implied warranty of
X   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
X   GNU General Public License for more details.
X
X   You should have received a copy of the GNU General Public License
X   along with this program; if not, write to the Free Software
X   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
X
X#define NULL 0
X
Xchar *index ();
X
X/* Return the length of the span of characters at the start of `string'
X   that are members of `class'.  */
X
Xint
Xstrspn (string, class)
X     char *string;
X     char *class;
X{
X  int count;
X
X  for (count = 0; string[count]; ++count)
X    if (!index (class, string[count]))
X      break;
X  return count;
X}
X
X/* Return the length of the span of characters at the start of `string'
X   that are non-members of `class'.  */
X
Xint
Xstrcspn (string, class)
X     char *string;
X     char *class;
X{
X  int count;
X
X  for (count = 0; string[count]; ++count)
X    if (index (class, string[count]))
X      break;
X  return count;
X}
X
X/* Return the next token in `string', delimited by one or more members of
X   the set `separators'.  If `string' is NULL, use the same string as in the
X   last call.  */
X
Xchar *
Xstrtok (string, separators)
X     char *string;
X     char *separators;
X{
X  static char *pos = NULL;	/* Current location in the string. */
X  int token_length;
X
X  if (string)
X    pos = string;
X  pos += strspn (pos, separators);	/* Skip initial separators. */
X  token_length = strcspn (pos, separators);	/* Find token length. */
X  if (token_length == 0)
X    return NULL;		/* No more tokens; pos is on a 0. */
X  separators = pos;		/* Re-use separators to save start of token. */
X  pos += token_length;		/* Move onto the 0. */
X  if (*pos)			/* If not the last token, */
X    *pos++ = 0;			/* null terminate the token. */
X  return separators;
X}
END_OF_FILE
if test 2222 -ne `wc -c <'str.c'`; then
    echo shar: \"'str.c'\" unpacked with wrong size!
fi
# end of 'str.c'
fi
if test -f 'user.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'user.c'\"
else
echo shar: Extracting \"'user.c'\" \(1906 characters\)
sed "s/^X//" >'user.c' <<'END_OF_FILE'
X/* user.c -- functions dealing with logins
X   Copyright (C) 1989 David MacKenzie
X
X   This program is free software; you can redistribute it and/or modify
X   it under the terms of the GNU General Public License as published by
X   the Free Software Foundation; either version 1, or (at your option)
X   any later version.
X
X   This program is distributed in the hope that it will be useful,
X   but WITHOUT ANY WARRANTY; without even the implied warranty of
X   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
X   GNU General Public License for more details.
X
X   You should have received a copy of the GNU General Public License
X   along with this program; if not, write to the Free Software
X   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
X
X#include "hint.h"
X#include <utmp.h>
X
X/* Add all ttys where `user' is logged on to the list of ttys to hint.
X   Return the number of times `user' is logged on.  */
X
Xint
Xadduser (user)
X     char *user;
X{
X  struct utmp ut;
X  int nfound;			/* Number of times `user' is logged on. */
X  int fd;
X
X  fd = open ("/etc/utmp", O_RDONLY);
X  if (fd == -1)
X    pfatal ("/etc/utmp");
X
X  nfound = 0;
X  while (read (fd, &ut, sizeof (struct utmp)) > 0)
X    nfound += addmatch (user, &ut);
X
X  close (fd);
X  return nfound;
X}
X
X/* If the name field in `utp' matches `user' and the tty is hint y,
X     add the `utp' tty field to the list of ttys to hint and return 1;
X   else,
X     return 0.  */
X
Xint
Xaddmatch (user, utp)
X     char *user;
X     struct utmp *utp;
X{
X  char temp_line[30];		/* 30 is random number > max. ut_line size. */
X
X  /* Make sure it's a valid entry. */
X  if (*utp->ut_name == 0)
X    return 0;
X
X  if (!strncmp (user, utp->ut_name, sizeof (utp->ut_name)))
X    {
X      strncpy (temp_line, utp->ut_line, sizeof (utp->ut_line));
X      temp_line[sizeof (utp->ut_line)] = 0;
X      addtty (user, temp_line);
X      return 1;
X    }
X  else
X    return 0;
X}
END_OF_FILE
if test 1906 -ne `wc -c <'user.c'`; then
    echo shar: \"'user.c'\" unpacked with wrong size!
fi
# end of 'user.c'
fi
if test -f 'hall.sh' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'hall.sh'\"
else
echo shar: Extracting \"'hall.sh'\" \(1009 characters\)
sed "s/^X//" >'hall.sh' <<'END_OF_FILE'
X#!/bin/sh
X#  hall -- hint all users logged in
X#  Copyright (C) 1989 David MacKenzie
X#
X#  This program is free software; you can redistribute it and/or modify
X#  it under the terms of the GNU General Public License as published by
X#  the Free Software Foundation; either version 1, or (at your option)
X#  any later version.
X#
X#  This program is distributed in the hope that it will be useful,
X#  but WITHOUT ANY WARRANTY; without even the implied warranty of
X#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
X#  GNU General Public License for more details.
X#
X#  You should have received a copy of the GNU General Public License
X#  along with this program; if not, write to the Free Software
X#  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
X
X# Usage: hall [message]
X# David MacKenzie
X# Latest revision: 08/23/89
X
Xecho 'Making list of everyone logged on . . .'
Xusers=`who|awk '{ if (NR > 1) printf ","; printf "%s", $1 }'`
Xif [ $# -eq 0 ]
Xthen
X  hint $users
Xelse
X  hint $users "$@"
Xfi
END_OF_FILE
if test 1009 -ne `wc -c <'hall.sh'`; then
    echo shar: \"'hall.sh'\" unpacked with wrong size!
fi
chmod +x 'hall.sh'
# end of 'hall.sh'
fi
echo shar: End of archive 1 \(of 2\).
cp /dev/null ark1isdone
MISSING=""
for I in 1 2 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked both archives.
    rm -f ark[1-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit 0