[alt.sources] saywha - front-end for write

jef@well.sf.ca.us (Jef Poskanzer) (07/27/90)

You use say(1) just like you would use write(1).  To the recipient,
it looks just like you used write(1).  The two differences are that
your message gets sent all at once when you are done entering it,
instead of a line at a time; and the recipient can use wha(1) to
re-display the message you just sent, in case it got messed up the
first time by a screen-editor redraw or something.
---
Jef

  Jef Poskanzer  jef@well.sf.ca.us  {ucbvax, apple, hplabs}!well!jef
 "YOU ARE STANDING AT THE END OF A ROAD BEFORE A SMALL BRICK BUILDING.
 AROUND YOU IS A FOREST.  A SMALL STREAM FLOWS OUT OF THE BUILDING AND
                            DOWN A GULLY."

#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create the files:
#	README
#	Makefile
#	say.c
#	say.1
#	wha.c
#	wha.1
#	libsaywha.c
#	libsaywha.h
# This archive created: Thu Jul 26 10:18:01 1990
# By:	Jef Poskanzer (Paratheo-Anametamystikhood Of Eris Esoteric, Ada Lovelace Cabal)
export PATH; PATH=/bin:$PATH
echo shar: extracting "'README'" '(1007 characters)'
if test -f 'README'
then
	echo shar: will not over-write existing file "'README'"
else
sed 's/^X//' << \SHAR_EOF > 'README'
X         saywha - front-end for write(1) that records messages
X                        Version 1.0 of 26jul90
X
XYou use say(1) just like you would use write(1).  To the recipient,
Xit looks just like you used write(1).  The two differences are that
Xyour message gets sent all at once when you are done entering it,
Xinstead of a line at a time; and the recipient can use wha(1) to
Xre-display the message you just sent, in case it got messed up the
Xfirst time by a screen-editor redraw or something.
X
XSee the manual entries for more details.
X
XFiles in this distribution:
X    README		this
X    Makefile		guess
X    say.c		front-end for write(1) that records messages
X    wha.c		re-displays messages
X    libsaywha.c		common routines
X    libsaywha.h		header file for libsaywha.c
X    *.1			manual entries
X
XTo install:
X    Unpack the files.
X    Edit the Makefile to change the configuration options if necessary.
X    Make.
X
XComments to:
X    jef@well.sf.ca.us
X    {ucbvax, lll-crg, sun!pacbell, apple, hplabs}!well!jef
SHAR_EOF
if test 1007 -ne "`wc -c < 'README'`"
then
	echo shar: error transmitting "'README'" '(should have been 1007 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'Makefile'" '(2449 characters)'
if test -f 'Makefile'
then
	echo shar: will not over-write existing file "'Makefile'"
else
sed 's/^X//' << \SHAR_EOF > 'Makefile'
X# Makefile for saywha
X#
X# Copyright (C) 1990 by Jef Poskanzer.
X#
X# Permission to use, copy, modify, and distribute this software and its
X# documentation for any purpose and without fee is hereby granted, provided
X# that the above copyright notice appear in all copies and that both that
X# copyright notice and this permission notice appear in supporting
X# documentation.  This software is provided "as is" without express or
X# implied warranty.
X
X# CONFIGURE: Define this if you want to enable the mail_editor option.
X# If you don't know what mail_editor is, don't worry about it.
X#ME_DEF =	-DMAIL_EDITOR=\"/usr/local/bin/mail_editor\"
X
X# CONFIGURE: As far as I know, all implementations of NFS choke on
X# write-only files.  Normally, say and wha use write-only files to record
X# the messages, but if your home directories are NFS-ized, this won't work.
X# In this case you have two options.  One, set the WORLD_READABLE flag
X# below; this will make your .sayfiles world-readable, which is undesireable,
X# but it works.  Two, don't set WORLD_READABLE flag, but after installation
X# manually make the say and wha executables set-group-id to tty, and
X# manually chgrp everyone's .sayfiles to tty, and keep on doing this
X# every time someone new starts using say/wha.  Neither alternative is
X# very nice, but that's life.  If you can think of a better solution,
X# please let me know.
X#NFS_DEF =
XNFS_DEF =	-DWORLD_READABLE
X
X# CONFIGURE: Change these if you want to use something besides write(1).
XWR_DEF =	-DWRITE=\"write\"
XWR_HD_DEF =	-DWRITE_HEAD=\"Message\ from\ %s@%s\ on\ %s\ at\ %s\ ...\\n\"
XWR_TL_DEF =	-DWRITE_TAIL=\"EOF\\n\"
X
XDEFINES =	$(ME_DEF) $(NFS_DEF) $(WR_DEF) $(WR_HD_DEF) $(WR_TL_DEF)
X
X# CONFIGURE: Set compiler flags here if you like.
XCFLAGS =	-O $(DEFINES)
X# CFLAGS =	-g $(DEFINES)
X
X# CONFIGURE: Set linker flags here if you like.
XLDFLAGS =	-s
X# LDFLAGS =
X
XLFLAGS =	-hxz $(DEFINES)
X
Xall:		say wha
X
Xsay:		say.c libsaywha.h libsaywha.o
X	cc $(CFLAGS) say.c libsaywha.o $(LDFLAGS) -o say
X
Xwha:		wha.c libsaywha.h libsaywha.o
X	cc $(CFLAGS) wha.c libsaywha.o $(LDFLAGS) -o wha
X
Xlibsaywha.o:	libsaywha.c libsaywha.h
X	cc $(CFLAGS) -c libsaywha.c
X
Xlint:
X	lint $(LFLAGS) say.c libsaywha.c
X	lint $(LFLAGS) wha.c libsaywha.c
X
Xclean:
X	-rm -f *.o core say wha *.shar
X
Xshar:		saywha.shar
Xsaywha.shar:	README Makefile say.c say.1 wha.c wha.1 libsaywha.c libsaywha.h
X	shar -v -c -p X README Makefile say.c say.1 wha.c wha.1 libsaywha.c libsaywha.h > $@
SHAR_EOF
if test 2449 -ne "`wc -c < 'Makefile'`"
then
	echo shar: error transmitting "'Makefile'" '(should have been 2449 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'say.c'" '(8683 characters)'
if test -f 'say.c'
then
	echo shar: will not over-write existing file "'say.c'"
else
sed 's/^X//' << \SHAR_EOF > 'say.c'
X/*
X** say - front-end for write(1) that records messages
X**
X** Copyright (C) 1990 by Jef Poskanzer.
X**
X** Permission to use, copy, modify, and distribute this software and its
X** documentation for any purpose and without fee is hereby granted, provided
X** that the above copyright notice appear in all copies and that both that
X** copyright notice and this permission notice appear in supporting
X** documentation.  This software is provided "as is" without express or
X** implied warranty.
X*/
X
X#ifndef lint
Xstatic char rcsid[] = "@(#) $Header: say.c,v 1.7 90/07/24 16:39:48 jef Exp $";
X#endif
X
X#include <stdio.h>
X#include <sys/file.h>
X#include <sys/types.h>
X#include <sys/signal.h>
X#include <utmp.h>
X#include <sys/stat.h>
X#include <sys/wait.h>
X#include <pwd.h>
X#include <ctype.h>
X#include <errno.h>
X#include "libsaywha.h"
X
X#ifndef _PATH_UTMP
X#define _PATH_UTMP "/etc/utmp"
X#endif
X
X/* External routines. */
Xextern int dup2(), fprintf(), printf(), strlen(), strncmp(), system(), vfork();
Xextern time_t time();
Xextern char *ctime(), *getenv(), *getlogin(), *mktemp(), *sprintf(), *strcpy(),
X    *ttyname();
Xextern int errno;	/* in case errno.h fails to declare it */
X
X/* Forward routines. */
Xint cleanup();
Xint search_utmp();
X
X/* Private globals. */
Xstatic char *tmpfilename = "/tmp/say.XXXXXX";
Xstatic int unlinktmpfile = 0;
X
Xmain( argc, argv )
Xint argc;
Xchar *argv[];
X    {
X    struct stat termstat;
X    char filename[200], buf[1000];
X    FILE *fp;
X    int fd, child, wval;
X    union wait waitstatus;
X
X    sw_argv0 = argv[0];
X
X    /* Initialize. */
X    (void) signal( SIGINT, cleanup );
X    (void) signal( SIGHUP, cleanup );
X    if ( sw_check_sayrc( ) < 0 )
X	(void) cleanup( 1 );
X    if ( sw_check_my_sayfile( ) < 0 )
X	(void) cleanup( 1 );
X
X    /* Cursory check of args - let write(1) do the rigorous check. */
X    if ( argc != 2 && argc != 3 )
X	{
X	(void) fprintf( stderr, "usage:  %s <user> [ <tty> ]\n", sw_argv0 );
X	(void) cleanup( 1 );
X	}
X    if ( search_utmp( argv[1] ) < 0 )
X	{
X	(void) fprintf( stderr,
X	    "%s: there is no user \"%s\" logged in right now\n",
X	    sw_argv0, argv[1] );
X	(void) cleanup( 1 );
X	}
X    if ( stat( ttyname( fileno( stderr ) ), &termstat ) >= 0 )
X	{
X	if ( ( termstat.st_mode & (S_IWRITE>>3) ) == 0 )  /* group write bit */
X	    {
X	    (void) fprintf( stderr,
X		"%s: you have write permission turned off\n", sw_argv0 );
X	    (void) cleanup( 1 );
X	    }
X	}
X
X    /* Read in the message and save it to a temp file. */
X    (void) mktemp( tmpfilename );
X    if ( ( fp = fopen( tmpfilename, "w" ) ) == NULL )
X	{
X	(void) fprintf( stderr, "%s: couldn't open temp file ", sw_argv0 );
X	perror( tmpfilename );
X	(void) cleanup( 1 );
X	}
X    unlinktmpfile = 1;
X    (void) fchmod( fileno( fp ), 0600 );
X#ifdef MAIL_EDITOR
X    if ( sw_mail_editor )
X	{
X	(void) sprintf( buf, "%s %s", MAIL_EDITOR, tmpfilename );
X	(void) system( buf );
X	}
X    else
X	{
X#endif
X    if ( ! sw_terse )
X	(void) printf( "Enter message, '^D' or '.' to end.\n" );
X    while ( fgets( buf, sizeof(buf), stdin ) != NULL )
X	{
X	int i;
X
X	if ( buf[0] == '.' && buf[1] == '\n' && buf[2] == '\0' )
X	    break;
X	/* Write the line with control characters made visible. */
X	for ( i = 0; buf[i] != '\0'; ++i )
X	    {
X	    char c;
X
X	    c = toascii( buf[i] );
X	    if ( ! isprint( c ) && ! isspace( c ) && c != '\n' && c != '\007' )
X		{
X		(void) putc( '^', fp );
X		(void) putc( c ^ 0x40, fp );	/* DEL to ?, others to alpha */
X		}
X	    else
X		(void) putc( c, fp );
X	    }
X	}
X    if ( fclose( fp ) == EOF )
X	{
X	(void) fprintf( stderr, "%s: error closing temp file ", sw_argv0 );
X	perror( tmpfilename );
X	(void) cleanup( 1 );
X	}
X#ifdef MAIL_EDITOR
X	}
X#endif
X
X    /* Try sending it. */
X    if ( ( child = vfork() ) == 0 )
X	{
X	if ( ( fd = open( tmpfilename, O_RDONLY ) ) < 0 )
X	    {
X	    (void) fputs( sw_argv0, stderr );
X	    perror( ": couldn't re-open temp file" );
X	    exit( 1 );
X	    }
X	(void) dup2( fd, fileno( stdin ) );
X	if ( argc == 2 )
X	    (void) execlp( WRITE, WRITE, argv[1], NULL );
X	else
X	    (void) execlp( WRITE, WRITE, argv[1], argv[2], NULL );
X	(void) fputs( sw_argv0, stderr );
X	perror( ": exec failed" );
X	exit( 1 );
X	}
X    while ( ( wval = wait( &waitstatus ) ) != child && wval != -1 )
X	;
X    if ( wval == child && waitstatus.w_termsig == 0 &&
X	 waitstatus.w_retcode == 0 )
X	{
X	/* Success.  Now save a copy in the recipient's .sayfile, and the
X	** sender's too if sw_savemine is set. */
X	struct passwd *pw;
X	char *home, *login, *nows;
X	int myfd;
X	char myfilename[200], host[100];
X	time_t now;
X
X	if ( ( pw = getpwnam( argv[1] ) ) == NULL )
X	    {
X	    (void) fprintf( stderr, "%s: couldn't find home directory for %s\n",
X		sw_argv0, argv[1] );
X	    (void) cleanup( 1 );
X	    }
X	(void) sprintf( filename, "%s/.sayfile", pw->pw_dir );
X	if ( ( fd = open( filename, O_WRONLY|O_APPEND ) ) < 0 )
X	    {
X	    if ( errno != ENOENT ) /* don't bother reporting lack of .sayfile */
X		{
X		(void) fprintf(
X		    stderr, "%s: warning, couldn't open ", sw_argv0 );
X		perror( filename );
X		}
X	    }
X
X	if ( ( login = getlogin() ) == NULL )
X	    if ( pw = getpwuid( getuid() ) )
X		login = pw->pw_name;
X	    else
X		login = "???";
X	if ( sw_savemine && strcmp( argv[1], login ) != 0 )
X	    {
X	    if ( ( home = getenv( "HOME" ) ) == NULL )
X		{
X		(void) fprintf(
X		    stderr, "%s: can't find home directory\n", sw_argv0 );
X		(void) cleanup( 1 );
X		}
X	    (void) sprintf( myfilename, "%s/.sayfile", home );
X	    if ( ( myfd = open( myfilename, O_WRONLY|O_APPEND ) ) < 0 )
X		{
X		if ( errno != ENOENT ) /* don't bother reporting lack of .sayfile */
X		    {
X		    (void) fprintf(
X			stderr, "%s: warning, couldn't open ", sw_argv0 );
X		    perror( myfilename );
X		    }
X		}
X	    }
X	else
X	    myfd = -1;
X
X	if ( fd >= 0 || myfd >= 0 )
X	    {
X	    if ( ( fp = fopen( tmpfilename, "r" ) ) == NULL )
X		{
X		(void) fputs( sw_argv0, stderr );
X		perror( ": couldn't re-open temp file" );
X		(void) cleanup( 1 );
X		}
X	    /* Greeting code extracted from BSD write(1). */
X	    if ( gethostname( host, sizeof(host) ) < 0 )
X		(void) strcpy( host, "???" );
X	    now = time( (time_t *) NULL );
X	    nows = ctime( &now );
X	    nows[16] = '\0';
X	    /* Message separator - a control-A, a timestamp, and the sender. */
X	    (void) sprintf( buf, "%c%d,%s\n", MSGSEP, (int) now, login );
X	    if ( fd >= 0 )
X		if ( write( fd, buf, strlen(buf) ) < 0 )
X		    {
X		    (void) fprintf( stderr, "%s: error writing on ", sw_argv0 );
X		    perror( filename );
X		    (void) cleanup( 1 );
X		    }
X	    if ( myfd >= 0 )
X		if ( write( myfd, buf, strlen(buf) ) < 0 )
X		    {
X		    (void) fprintf( stderr, "%s: error writing on ", sw_argv0 );
X		    perror( myfilename );
X		    (void) cleanup( 1 );
X		    }
X	    (void) sprintf( buf, WRITE_HEAD, login, host,
X		ttyname( fileno( stderr ) ) + 5, nows + 11 );
X	    if ( fd >= 0 )
X		(void) write( fd, buf, strlen(buf) );
X	    if ( myfd >= 0 )
X		(void) write( myfd, buf, strlen(buf) );
X	    /* And copy in the message. */
X	    while ( fgets( buf, sizeof(buf), fp ) != NULL )
X		{
X		if ( fd >= 0 )
X		    (void) write( fd, buf, strlen(buf) );
X		if ( myfd >= 0 )
X		    (void) write( myfd, buf, strlen(buf) );
X		}
X	    if ( fd >= 0 )
X		(void) write( fd, WRITE_TAIL, strlen(WRITE_TAIL) );
X	    if ( myfd >= 0 )
X		(void) write( myfd, WRITE_TAIL, strlen(WRITE_TAIL) );
X	    if ( fclose( fp ) == EOF )
X		{
X		(void) fprintf(
X		    stderr, "%s: error closing temp file ", sw_argv0 );
X		perror( tmpfilename );
X		(void) cleanup( 1 );
X		}
X	    if ( fd >= 0 )
X		if ( close( fd ) < 0 )
X		    {
X		    (void) fprintf( stderr, "%s: error closing ", sw_argv0 );
X		    perror( filename );
X		    (void) cleanup( 1 );
X		    }
X	    if ( myfd >= 0 )
X		if ( close( myfd ) < 0 )
X		    {
X		    (void) fprintf( stderr, "%s: error closing ", sw_argv0 );
X		    perror( myfilename );
X		    (void) cleanup( 1 );
X		    }
X	    }
X	}
X    else
X	{
X	/* Write failed. */
X	(void) printf(
X	    "Your message could not be delivered for some reason.\n" );
X	(void) printf( "I will mail it instead.\n" );
X	(void) sprintf( buf, "mail -s 'this was a failing say' %s < %s",
X	    argv[1], tmpfilename );
X	(void) system( buf );
X	}
X
X    /* All done. */
X    (void) cleanup( 0 );
X    }
X
Xint
Xsearch_utmp( user )
Xchar *user;
X    {
X    struct utmp u;
X    int ufd;
X
X    if ( ( ufd = open( _PATH_UTMP, O_RDONLY ) ) < 0 )
X	{
X	perror( _PATH_UTMP );
X	(void) cleanup( 1 );
X	}
X
X    while ( read( ufd, (char *) &u, sizeof(u) ) == sizeof(u) )
X	{
X	if ( strncmp( user, u.ut_name, sizeof(u.ut_name) ) == 0 )
X	    {
X	    (void) close( ufd );
X	    return 0;
X	    }
X	}
X    (void) close( ufd );
X    return -1;
X    }
X
Xint
Xcleanup( status )
Xint status;
X    {
X    sw_cleanup( );
X    if ( unlinktmpfile )
X	(void) unlink( tmpfilename );
X    unlinktmpfile = 0;
X    exit( status );
X    }
X
SHAR_EOF
if test 8683 -ne "`wc -c < 'say.c'`"
then
	echo shar: error transmitting "'say.c'" '(should have been 8683 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'say.1'" '(1551 characters)'
if test -f 'say.1'
then
	echo shar: will not over-write existing file "'say.1'"
else
sed 's/^X//' << \SHAR_EOF > 'say.1'
X.TH say 1 "26 July 1990"
X.SH NAME
Xsay - front-end for write(1) that records messages
X.SH SYNOPSIS
Xsay <user> [ <tty> ]
X.SH DESCRIPTION
XYou use this program just like you would use write(1).
XTo the recipient, it looks just like you used write(1).
XThe two differences are that your message gets sent all at once when
Xyou are done entering it, instead of a line at a time; and the recipient
Xcan use wha(1) to re-display the message you just sent, in case it got
Xmessed up the first time by a screen-editor redraw or something.
X.SH FILES
XSay(1) looks for a file called ".sayrc" in your home directory.
XThis file can contain commands to change some of the defaults.
XCurrently available commands:
X.PP
Xset keepdays=<n>, to change the number of days that your messages
Xare kept for.  The default value for keepdays is 3.
X.PP
Xset terse, to turn off some of the verboseness.
X.PP
Xset mail_editor, to use the mail_editor for entering your messages.
X.PP
Xset savemine, to keep a copy of outgoing says as well as incoming.
X.SH "SEE ALSO"
Xwha(1), write(1)
X.SH "BUGS / DEFICIENCIES"
XDoes not work very well with NFS home directories.
X.SH AUTHOR
XCopyright (C) 1990 by Jef Poskanzer.
X.. Permission to use, copy, modify, and distribute this software and its
X.. documentation for any purpose and without fee is hereby granted, provided
X.. that the above copyright notice appear in all copies and that both that
X.. copyright notice and this permission notice appear in supporting
X.. documentation.  This software is provided "as is" without express or
X.. implied warranty.
SHAR_EOF
if test 1551 -ne "`wc -c < 'say.1'`"
then
	echo shar: error transmitting "'say.1'" '(should have been 1551 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'wha.c'" '(4174 characters)'
if test -f 'wha.c'
then
	echo shar: will not over-write existing file "'wha.c'"
else
sed 's/^X//' << \SHAR_EOF > 'wha.c'
X/*
X** wha - re-display messages from say(1)
X**
X** Copyright (C) 1990 by Jef Poskanzer.
X**
X** Permission to use, copy, modify, and distribute this software and its
X** documentation for any purpose and without fee is hereby granted, provided
X** that the above copyright notice appear in all copies and that both that
X** copyright notice and this permission notice appear in supporting
X** documentation.  This software is provided "as is" without express or
X** implied warranty.
X*/
X
X#ifndef lint
Xstatic char rcsid[] = "@(#) $Header: wha.c,v 1.10 90/07/26 10:11:00 jef Exp $";
X#endif
X
X#include <stdio.h>
X#include <sys/signal.h>
X#include <errno.h>
X#include "libsaywha.h"
X
X/* External routines. */
Xextern int fprintf(), printf(), strcmp(), strlen();
Xextern char *ctime(), *getenv(), *sprintf();
Xextern int errno;	/* in case errno.h fails to declare it */
X
X/* Forward routines. */
Xint cleanup();
X
Xmain( argc, argv )
Xint argc;
Xchar *argv[];
X    {
X    int argn, n, t, messages, message, writing, written;
X    long lt;
X    char *user, *home, filename[200], buf[2000];
X    char tuser[200];
X    FILE *fp;
X    char *usage = "usage:  %s [ <n> | <user> | <n> <user> | <user> <n> ]\n";
X
X    /* Initialize. */
X    sw_argv0 = argv[0];
X    (void) signal( SIGINT, cleanup );
X    (void) signal( SIGHUP, cleanup );
X    if ( sw_check_sayrc( ) < 0 )
X	(void) cleanup( 1 );
X    if ( sw_check_my_sayfile( ) < 0 )
X	(void) cleanup( 1 );
X
X    /* Check args. */
X    n = -1;
X    user = NULL;
X    for ( argn = 1; argn < argc; ++argn )
X	{
X	if ( sscanf( argv[argn], "%d", &t ) == 1 )
X	    {
X	    if ( n != -1 )
X		{
X		(void) fprintf( stderr, usage, sw_argv0 );
X		(void) cleanup( 1 );
X		}
X	    n = t;
X	    }
X	else
X	    {
X	    if ( user != NULL )
X		{
X		(void) fprintf( stderr, usage, sw_argv0 );
X		(void) cleanup( 1 );
X		}
X	    user = argv[argn];
X	    }
X	}
X    if ( n == -1 )
X	n = 1;
X
X    /* Open up the sayfile. */
X    if ( ( home = getenv( "HOME" ) ) == NULL )
X	{
X	(void) fprintf( stderr, "%s: can't find home directory\n", sw_argv0 );
X	(void) cleanup( 1 );
X	}
X    (void) sprintf( filename, "%s/.sayfile", home );
X    if ( ( fp = fopen( filename, "r" ) ) == NULL )
X	{
X	(void) fputs( sw_argv0, stderr );
X	perror( ": can't open .sayfile" );
X	(void) cleanup( 1 );
X	}
X    else
X	{
X	/* Read through once just to count up the messages that match. */
X	messages = 0;
X	while ( errno = 0, fgets( buf, sizeof(buf), fp ) != NULL )
X	    {
X	    if ( buf[0] == MSGSEP )
X		{
X		if ( sscanf( buf + 1, "%d,%s\n", &t, tuser ) != 2 )
X		    {
X		    (void) fprintf( stderr, "%s: garbled .sayfile!\n",
X			sw_argv0 );
X		    (void) cleanup( 1 );
X		    }
X		if ( user == NULL )
X		    ++messages;
X		else if ( strcmp( user, tuser ) == 0 )
X		    ++messages;
X		}
X	    }
X	if ( errno != 0 )
X	    {
X	    (void) fputs( sw_argv0, stderr );
X	    perror( ": error reading .sayfile" );
X	    (void) cleanup( 1 );
X	    }
X	if ( messages > 0 )
X	    {
X	    /* Now read through again to print. */
X	    (void) rewind( fp );
X	    message = 0;
X	    writing = 0;
X	    written = 0;
X	    while ( errno = 0, fgets( buf, sizeof(buf), fp ) != NULL )
X		{
X		if ( buf[0] == MSGSEP )
X		    {
X		    if ( sscanf( buf + 1, "%d,%s\n", &t, tuser ) != 2 )
X			{
X			(void) fprintf( stderr, "%s: garbled .sayfile!\n",
X			    sw_argv0 );
X			(void) cleanup( 1 );
X			}
X		    if ( user == NULL || strcmp( user, tuser ) == 0 )
X			{
X			++message;
X			if ( message > messages - n )
X			    {
X			    char *cp;
X
X			    writing = 1;
X			    ++written;
X			    if ( written > 1 )
X				(void) putchar( '\n' );
X			    lt = t;
X			    cp = ctime( &lt );
X			    cp[strlen(cp) - 1] = '\0';
X			    (void) printf( "[%s]\n", cp );
X			    }
X			else
X			    writing = 0;
X			}
X		    else
X			writing = 0;
X		    }
X		else if ( writing )
X		    (void) fputs( buf, stdout );
X		}
X	    if ( errno != 0 )
X		{
X		(void) fputs( sw_argv0, stderr );
X		perror( ": error reading .sayfile" );
X		(void) cleanup( 1 );
X		}
X	    }
X
X	if ( fclose( fp ) == EOF )
X	    {
X	    (void) fprintf( stderr, "%s: error closing ", sw_argv0 );
X	    perror( filename );
X	    (void) cleanup( 1 );
X	    }
X	}
X
X    /* All done. */
X    (void) cleanup( 0 );
X    }
X
Xint
Xcleanup( status )
Xint status;
X    {
X    sw_cleanup( );
X    exit( status );
X    }
SHAR_EOF
if test 4174 -ne "`wc -c < 'wha.c'`"
then
	echo shar: error transmitting "'wha.c'" '(should have been 4174 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'wha.1'" '(1271 characters)'
if test -f 'wha.1'
then
	echo shar: will not over-write existing file "'wha.1'"
else
sed 's/^X//' << \SHAR_EOF > 'wha.1'
X.TH wha 1 "26 July 1990"
X.SH NAME
Xwha - re-display messages from say(1)
X.SH SYNOPSIS
Xwha [ <n> | <user> | <n> <user> | <user> <n> ]
X.SH DESCRIPTION
XWha(1) re-displays messages that were sent to you via say(1).
XWith no arguments, it just shows you the most recent message.
XYou can also ask it for the last <n> messages, the last message
Xfrom <user>, or the last <n> messages from <user>.
X.SH FILES
XWha(1) looks for a file called ".sayrc" in your home directory.
XThis file can contain commands to change some of the defaults.
XCurrently available commands:
X.PP
Xset keepdays=<n>, to change the number of days that your messages
Xare kept for.  The default value for keepdays is 3.
X.PP
Xset terse, to turn off some of the verboseness.
X.SH "SEE ALSO"
Xsay(1), write(1)
X.SH "BUGS / DEFICIENCIES"
XDoes not work very well with NFS home directories.
X.SH AUTHOR
XCopyright (C) 1990 by Jef Poskanzer.
X.. Permission to use, copy, modify, and distribute this software and its
X.. documentation for any purpose and without fee is hereby granted, provided
X.. that the above copyright notice appear in all copies and that both that
X.. copyright notice and this permission notice appear in supporting
X.. documentation.  This software is provided "as is" without express or
X.. implied warranty.
SHAR_EOF
if test 1271 -ne "`wc -c < 'wha.1'`"
then
	echo shar: error transmitting "'wha.1'" '(should have been 1271 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'libsaywha.c'" '(6690 characters)'
if test -f 'libsaywha.c'
then
	echo shar: will not over-write existing file "'libsaywha.c'"
else
sed 's/^X//' << \SHAR_EOF > 'libsaywha.c'
X/*
X** libsaywha.c - common routines for say(1) and wha(1)
X**
X** Copyright (C) 1990 by Jef Poskanzer.
X**
X** Permission to use, copy, modify, and distribute this software and its
X** documentation for any purpose and without fee is hereby granted, provided
X** that the above copyright notice appear in all copies and that both that
X** copyright notice and this permission notice appear in supporting
X** documentation.  This software is provided "as is" without express or
X** implied warranty.
X*/
X
X#ifndef lint
Xstatic char rcsid[] = "@(#) $Header: libsaywha.c,v 1.6 90/07/24 16:39:42 jef Exp $";
X#endif
X
X#include <stdio.h>
X#include <sys/file.h>
X#include <sys/types.h>
X#include "libsaywha.h"
X
X/* External routines. */
Xextern int fprintf(), strcmp(), strlen(), strncmp();
Xextern char *getenv(), *index(), *mktemp(), *sprintf();
Xextern time_t time();
X
X/* Public globals. */
Xchar *sw_argv0;
Xint sw_keepdays = 3;
Xint sw_terse = 0;
X#ifdef MAIL_EDITOR
Xint sw_mail_editor = 0;
X#endif
Xint sw_savemine = 0;
X
X/* Private globals. */
Xstatic char *tmpfilename = "/tmp/sw.XXXXXX";
Xstatic int unlinktmpfile = 0;
X
Xint
Xsw_check_sayrc( )
X    {
X    char *home, buf[1000], *cp, *name, *value;
X    FILE *fp;
X
X    if ( ( home = getenv( "HOME" ) ) == NULL )
X	{
X	(void) fprintf( stderr, "%s: can't find home directory\n", sw_argv0 );
X	return -1;
X	}
X    (void) sprintf( buf, "%s/.sayrc", home );
X    if ( ( fp = fopen( buf, "r" ) ) != NULL )
X	{
X	while ( fgets( buf, sizeof(buf), fp ) != NULL )
X	    {
X	    /* Trim comments. */
X	    if ( ( cp = index( buf, '#' ) ) != NULL )
X		*cp = '\0';
X	    /* Trim trailing whitespace. */
X	    for ( cp = &buf[strlen(buf) - 1];
X		  *cp == ' ' || *cp == '\t' || *cp == '\n';
X		  --cp )
X		*cp = '\0';
X	    /* Skip leading whitespace. */
X	    for ( cp = buf;
X		  *cp == ' ' || *cp == '\t' || *cp == '\n';
X		  ++cp )
X		;
X
X	    /* Now, see what we've got. */
X	    if ( *cp == '\0' )
X		{
X		/* Null command. */
X		}
X	    else if ( strncmp( cp, "set", 3 ) == 0 )
X		{
X		/* A set command. */
X		cp += 3;
X		while ( *cp != ' ' && *cp != '\t' && *cp != '\0' )
X		    ++cp;
X		while ( *cp != '\0' )
X		    {
X		    while ( *cp == ' ' || *cp == '\t' )
X			++cp;
X		    name = cp;
X		    while ( *cp != '=' && *cp != ' ' && *cp != '\t' &&
X			    *cp != '\0' )
X			++cp;
X		    if ( *cp == '=' )
X			{
X			/* set name=value */
X			*cp++ = '\0';
X			value = cp;
X			while ( *cp != ' ' && *cp != '\t' && *cp != '\0' )
X			    ++cp;
X			*cp++ = '\0';
X			}
X		    else
X			{
X			/* set name */
X			*cp++ = '\0';
X			value = NULL;
X			}
X		    /* Ok, we have name and value.  Now take a look. */
X		    if ( strcmp( name, "keepdays" ) == 0 )
X			{
X			if ( value == NULL )
X			    (void) fprintf( stderr,
X			   "%s: missing value in .sayrc set keepdays command\n",
X				sw_argv0 );
X			else if ( sscanf( value, "%d", &sw_keepdays ) != 1 )
X			    (void) fprintf( stderr,
X	       "%s: non-numeric value in .sayrc set keepdays command: \"%s\"\n",
X				sw_argv0, value );
X			}
X#ifdef MAIL_EDITOR
X		    else if ( strcmp( name, "mail_editor" ) == 0 )
X			{
X			if ( value != NULL )
X			    (void) fprintf( stderr,
X	     "%s: extraneous value in .sayrc set mail_editor command: \"%s\"\n",
X				sw_argv0, value );
X			else
X			    sw_mail_editor = 1;
X			}
X#endif
X		    else if ( strcmp( name, "terse" ) == 0 )
X			{
X			if ( value != NULL )
X			    (void) fprintf( stderr,
X		   "%s: extraneous value in .sayrc set terse command: \"%s\"\n",
X				sw_argv0, value );
X			else
X			    sw_terse = 1;
X			}
X		    else if ( strcmp( name, "savemine" ) == 0 )
X			{
X			if ( value != NULL )
X			    (void) fprintf( stderr,
X		"%s: extraneous value in .sayrc set savemine command: \"%s\"\n",
X				sw_argv0, value );
X			else
X			    sw_savemine = 1;
X			}
X		    else
X			(void) fprintf( stderr,
X		    "%s: unrecognized variable in .sayrc set command: \"%s\"\n",
X			    sw_argv0, name );
X		    }
X		}
X	    else
X		(void) fprintf( stderr,
X		    "%s: unrecognized command in .sayrc: \"%s\"\n",
X		    sw_argv0, cp );
X	    }
X	(void) fclose( fp );
X#ifdef MAIL_EDITOR
X	if ( sw_mail_editor && sw_terse )
X	    (void) fprintf( stderr,
X		"%s: set mail_editor and set terse don't make sense together\n",
X		sw_argv0 );
X#endif
X	}
X    return 0;
X    }
X
Xint
Xsw_check_my_sayfile( )
X    {
X    char *home, filename[200], buf[1000];
X    FILE *fp1, *fp2;
X    int fd;
X
X    if ( ( home = getenv( "HOME" ) ) == NULL )
X	{
X	(void) fprintf( stderr, "%s: can't find home directory\n", sw_argv0 );
X	return -1;
X	}
X    (void) sprintf( filename, "%s/.sayfile", home );
X    if ( ( fp1 = fopen( filename, "r+" ) ) == NULL )
X	{
X	/* No .sayfile found - create one, with the proper protection. */
X	if ( ( fd = open( filename, O_CREAT | O_EXCL, 0 ) ) < 0 )
X	    {
X	    (void) sprintf( buf, "%s: error creating .sayfile", sw_argv0 );
X	    perror( buf );
X	    return -1;
X	    }
X	(void) close( fd );
X#ifdef WORLD_READABLE
X	(void) chmod( filename, 0666 );
X#else /*WORLD_READABLE*/
X	(void) chmod( filename, 0622 );
X#endif /*WORLD_READABLE*/
X	}
X    else
X	{
X	/* Expire old messages. */
X	int expired, saving, newsize;
X	time_t now;
X	int then;
X	char tuser[100];
X
X	(void) mktemp( tmpfilename );
X	if ( ( fp2 = fopen( tmpfilename, "w+" ) ) == NULL )
X	    {
X	    (void) sprintf( buf, "%s: couldn't open temp file", sw_argv0 );
X	    perror( buf );
X	    return -1;
X	    }
X	unlinktmpfile = 1;
X	(void) fchmod( fileno( fp2 ), 0600 );
X	expired = 0;
X	saving = 1;
X	now = time( (time_t *) NULL );
X	while ( fgets( buf, sizeof(buf), fp1 ) != NULL )
X	    {
X	    if ( buf[0] == MSGSEP )
X		{
X		if ( sscanf( buf + 1, "%d,%s\n", &then, tuser ) == 2 )
X		    {
X		    if ( then / 86400 + sw_keepdays > now / 86400 )
X			{
X			if ( ! expired )
X			    break;	/* don't bother reading further */
X			saving = 1;
X			}
X		    else
X			{
X			saving = 0;
X			expired = 1;
X			}
X		    }
X		else
X		    {
X		    saving = 0;
X		    }
X		}
X	    if ( saving )
X		(void) fputs( buf, fp2 );
X	    }
X	/* If any messages were expired, copy back the temp file. */
X	if ( expired )
X	    {
X	    rewind( fp1 );
X	    rewind( fp2 );
X	    while ( fgets( buf, sizeof(buf), fp2 ) != NULL )
X		(void) fputs( buf, fp1 );
X	    newsize = ftell( fp1 );
X	    }
X	if ( fclose( fp1 ) == EOF )
X	    {
X	    (void) fprintf( stderr, "%s: error closing ", sw_argv0 );
X	    perror( filename );
X	    return -1;
X	    }
X	if ( fclose( fp2 ) == EOF )
X	    {
X	    (void) fprintf( stderr, "%s: error closing temp file ", sw_argv0 );
X	    perror( tmpfilename );
X	    return -1;
X	    }
X	(void) unlink( tmpfilename );
X	if ( expired )
X	    if ( truncate( filename, newsize ) < 0 )
X		{
X		(void) fprintf( stderr, "%s: error truncating ", sw_argv0 );
X		perror( filename );
X		return -1;
X		}
X	}
X    return 0;
X    }
X
Xvoid
Xsw_cleanup( )
X    {
X    if ( unlinktmpfile )
X	(void) unlink( tmpfilename );
X    unlinktmpfile = 0;
X    }
SHAR_EOF
if test 6690 -ne "`wc -c < 'libsaywha.c'`"
then
	echo shar: error transmitting "'libsaywha.c'" '(should have been 6690 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'libsaywha.h'" '(812 characters)'
if test -f 'libsaywha.h'
then
	echo shar: will not over-write existing file "'libsaywha.h'"
else
sed 's/^X//' << \SHAR_EOF > 'libsaywha.h'
X/*
X** libsaywha.h - header file for libsaywha
X**
X** Copyright (C) 1990 by Jef Poskanzer.
X**
X** Permission to use, copy, modify, and distribute this software and its
X** documentation for any purpose and without fee is hereby granted, provided
X** that the above copyright notice appear in all copies and that both that
X** copyright notice and this permission notice appear in supporting
X** documentation.  This software is provided "as is" without express or
X** implied warranty.
X**
X** @(#) $Header: libsaywha.h,v 1.5 90/07/24 16:39:45 jef Exp $
X*/
X
X#define MSGSEP '\001'
X
Xextern char *sw_argv0;
X
Xextern int sw_keepdays;
Xextern int sw_terse;
X#ifdef MAIL_EDITOR
Xextern int sw_mail_editor;
X#endif
Xextern int sw_savemine;
X
Xextern int sw_check_sayrc( );
X
Xextern int sw_check_my_sayfile( );
X
Xextern void sw_cleanup( );
SHAR_EOF
if test 812 -ne "`wc -c < 'libsaywha.h'`"
then
	echo shar: error transmitting "'libsaywha.h'" '(should have been 812 characters)'
fi
fi # end of overwriting check
#	End of shell archive
exit 0