[alt.sources] new write

leres@ace.ee.lbl.gov (Craig Leres) (12/23/89)

About two months ago, Jef Poskanzer and I decided write should use idle
time to break ties when the target user is logged in more than once.
Since the BSD write was AT&T derived, we decided to write a new one
from scratch. It's appended.

A similar version will go out with the 4.4 BSD distribution. This one
has been back-fitted for 4.3 BSD (and has been tested on an early 4.3
release).

Note: This isn't for sysV. It probably won't compile under sysV. It
probably won't run under sysV. We have no interest in sysV.

		Craig
------
#! /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:
#	Makefile
#	write.1
#	write.c
#	strerror.c
# This archive created: Fri Dec 22 01:33:33 1989
# By:	Jef Poskanzer
export PATH; PATH=/bin:$PATH
echo shar: extracting "'Makefile'" '(1390 characters)'
if test -f 'Makefile'
then
	echo shar: will not over-write existing file "'Makefile'"
else
sed 's/^X//' << \SHAR_EOF > 'Makefile'
X# @(#) $Header: Makefile,v 1.8dist 89/12/18 21:04:42 jef Exp $ (LBL)
X#
X# Copyright (c) 1988 Regents of the University of California.
X# All rights reserved.
X#
X# Redistribution and use in source and binary forms are permitted
X# provided that the above copyright notice and this paragraph are
X# duplicated in all such forms and that any documentation, advertising
X# materials, and other materials related to such redistribution and
X# use acknowledge that the software was developed by the University
X# of California, Berkeley.  The name of the University may not be
X# used to endorse or promote products derived from this software
X# without specific prior written permission.  THIS SOFTWARE IS PROVIDED
X# ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
X# WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND
X# FITNESS FOR A PARTICULAR PURPOSE.
X#
X# @(#)Makefile	5.2 (Berkeley) 5/11/89
X#
X
XCFLAGS=	-O
X#LIBC=	/lib/libc.a
XSRCS=	write.c strerror.c
XOBJS=	write strerror.o
XMAN=	write.0
X
Xall: write
X
Xwrite: write.c strerror.o
X	${CC} -o $@ ${CFLAGS} write.c strerror.o
X
Xclean:
X	rm -f ${OBJS} *.o core
X
Xcleandir: clean
X	rm -f ${MAN} tags .depend
X
Xdepend: ${SRCS}
X	mkdep -p ${CFLAGS} ${SRCS}
X
Xinstall: write
X	install write ${DESTDIR}/bin
X#	install -c -o bin -g bin -m 444 ${MAN} ${DESTDIR}/usr/man/cat1
X
Xlint: ${SRCS}
X	lint ${CFLAGS} ${SRCS}
X
Xtags: ${SRCS}
X	ctags ${SRCS}
SHAR_EOF
if test 1390 -ne "`wc -c < 'Makefile'`"
then
	echo shar: error transmitting "'Makefile'" '(should have been 1390 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'write.1'" '(2664 characters)'
if test -f 'write.1'
then
	echo shar: will not over-write existing file "'write.1'"
else
sed 's/^X//' << \SHAR_EOF > 'write.1'
X.\" Copyright (c) 1989 The Regents of the University of California.
X.\" All rights reserved.
X.\"
X.\" This code is derived from software contributed to Berkeley by
X.\" Jef Poskanzer and Craig Leres.
X.\"
X.\" Redistribution and use in source and binary forms are permitted
X.\" provided that the above copyright notice and this paragraph are
X.\" duplicated in all such forms and that any documentation,
X.\" advertising materials, and other materials related to such
X.\" distribution and use acknowledge that the software was developed
X.\" by the University of California, Berkeley.  The name of the
X.\" University may not be used to endorse or promote products derived
X.\" from this software without specific prior written permission.
X.\" THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
X.\" IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
X.\" WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
X.\"
X.\"	@(#)write.1	6.3 (Berkeley) 11/21/89
X.\"
X.TH WRITE 1 "November 21, 1989"
X.UC 4
X.SH NAME
Xwrite - send a message to another user
X.SH SYNOPSIS
X.B write user
X[
X.B ttyname
X]
X.ft R
X.SH DESCRIPTION
X.I Write
Xallows you to communicate with other users, by copying lines from
Xyour terminal to theirs.
X.PP
XWhen you run the
X.I write
Xcommand, the user you are writing to gets a message of the form:
X.sp
X.nf
X.ti +5
XMessage from yourname@yourhost on yourtty at hh:mm ...
X.fi
X.sp
XAny further lines you enter will be copied to the specified user's
Xterminal.
XIf the other user wants to reply, they must run
X.I write
Xas well.
X.PP
XWhen you are done, type an end-of-file or interrupt character.
XThe other user will see the message ``EOF'' indicating that the
Xconversation is over.
X.PP
XYou can prevent people (other than the super-user) from writing to you
Xwith the
X.IR mesg (1)
Xcommand.
XSome commands, for example
X.I nroff
Xand
X.IR pr ,
Xdisallow writing automatically, so that your output isn't overwritten.
X.PP
XIf the user you want to write to is logged in on more than one terminal,
Xyou can specify which terminal to write to by specifying the terminal
Xname as the second operand to the
X.I write
Xcommand.
XAlternatively, you can let
X.I write
Xselect one of the terminals \- it will pick the one with the shortest
Xidle time.
XThis is so that if the user is logged in at work and also dialed up from
Xhome, the message will go to the right place.
X.PP
XThe traditional protocol for writing to someone is that the string ``-o'',
Xeither at the end of a line or on a line by itself, means that it's the
Xother person's turn to talk.
XThe string ``o-o'' means that the person believes the conversation to be
Xover.
X.SH "SEE ALSO"
Xmesg(1), talk(1), who(1)
SHAR_EOF
if test 2664 -ne "`wc -c < 'write.1'`"
then
	echo shar: error transmitting "'write.1'" '(should have been 2664 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'write.c'" '(7792 characters)'
if test -f 'write.c'
then
	echo shar: will not over-write existing file "'write.c'"
else
sed 's/^X//' << \SHAR_EOF > 'write.c'
X#ifndef lint
Xstatic char rcsid[] =
X    "@(#) $Header: write.c,v 1.24dist 89/12/18 21:04:42 jef Exp $ (LBL)";
X#endif
X/*
X * Copyright (c) 1989 The Regents of the University of California.
X * All rights reserved.
X *
X * This code is derived from software contributed to Berkeley by
X * Jef Poskanzer and Craig Leres.
X *
X * Redistribution and use in source and binary forms are permitted
X * provided that the above copyright notice and this paragraph are
X * duplicated in all such forms and that any documentation,
X * advertising materials, and other materials related to such
X * distribution and use acknowledge that the software was developed
X * by the University of California, Berkeley.  The name of the
X * University may not be used to endorse or promote products derived
X * from this software without specific prior written permission.
X * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
X * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
X * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
X */
X
X#ifndef lint
Xchar copyright[] =
X"@(#) Copyright (c) 1989 The Regents of the University of California.\n\
X All rights reserved.\n";
X#endif /* not lint */
X
X#ifndef lint
Xstatic char sccsid[] = "@(#)write.c	4.20 (Berkeley) 11/25/89";
X#endif /* not lint */
X
X#include <sys/param.h>
X#include <sys/signal.h>
X#include <sys/stat.h>
X#include <sys/file.h>
X#include <sys/time.h>
X#include <utmp.h>
X#include <ctype.h>
X#include <pwd.h>
X#include <stdio.h>
X#include <strings.h>
X
X#ifndef _PATH_UTMP
X#define _PATH_UTMP "/etc/utmp"
X#endif
X#ifndef UT_LINESIZE
X#define UT_LINESIZE sizeof(((struct utmp *)0)->ut_line)
X#endif
X
Xextern int errno;
X
Xmain(argc, argv)
X	int argc;
X	char **argv;
X{
X	register char *cp;
X	time_t atime;
X	uid_t myuid;
X	int msgsok, myttyfd;
X	char tty[MAXPATHLEN], *mytty, *ttyname();
X	void done();
X
X	/* check that sender has write enabled */
X	if (isatty(fileno(stdin)))
X		myttyfd = fileno(stdin);
X	else if (isatty(fileno(stdout)))
X		myttyfd = fileno(stdout);
X	else if (isatty(fileno(stderr)))
X		myttyfd = fileno(stderr);
X	else {
X		(void)fprintf(stderr, "write: can't find your tty\n");
X		exit(1);
X	}
X	if (!(mytty = ttyname(myttyfd))) {
X		(void)fprintf(stderr, "write: can't find your tty's name\n");
X		exit(1);
X	}
X	if (cp = rindex(mytty, '/'))
X		mytty = cp + 1;
X	if (term_chk(mytty, &msgsok, &atime, 1))
X		exit(1);
X	if (!msgsok) {
X		(void)fprintf(stderr,
X		    "write: you have write permission turned off.\n");
X		exit(1);
X	}
X
X	myuid = getuid();
X
X	/* check args */
X	switch (argc) {
X	case 2:
X		search_utmp(argv[1], tty, mytty, myuid);
X		do_write(tty, mytty, myuid);
X		break;
X	case 3:
X		if (!strncmp(argv[2], "/dev/", 5))
X			argv[2] += 5;
X		if (utmp_chk(argv[1], argv[2])) {
X			(void)fprintf(stderr,
X			    "write: %s is not logged in on %s.\n",
X			    argv[1], argv[2]);
X			exit(1);
X		}
X		if (term_chk(argv[2], &msgsok, &atime, 1))
X			exit(1);
X		if (myuid && !msgsok) {
X			(void)fprintf(stderr,
X			    "write: %s has messages disabled on %s\n",
X			    argv[1], argv[2]);
X			exit(1);
X		}
X		do_write(argv[2], mytty, myuid);
X		break;
X	default:
X		(void)fprintf(stderr, "usage: write user [tty]\n");
X		exit(1);
X	}
X	done();
X	/* NOTREACHED */
X}
X
X/*
X * utmp_chk - checks that the given user is actually logged in on
X *     the given tty
X */
Xutmp_chk(user, tty)
X	char *user, *tty;
X{
X	struct utmp u;
X	int ufd;
X
X	if ((ufd = open(_PATH_UTMP, O_RDONLY)) < 0)
X		return(0);	/* ignore error, shouldn't happen anyway */
X
X	while (read(ufd, (char *) &u, sizeof(u)) == sizeof(u))
X		if (strncmp(user, u.ut_name, sizeof(u.ut_name)) == 0 &&
X		    strncmp(tty, u.ut_line, sizeof(u.ut_line)) == 0) {
X			(void)close(ufd);
X			return(0);
X		}
X
X	(void)close(ufd);
X	return(1);
X}
X
X/*
X * search_utmp - search utmp for the "best" terminal to write to
X *
X * Ignores terminals with messages disabled, and of the rest, returns
X * the one with the most recent access time.  Returns as value the number
X * of the user's terminals with messages enabled, or -1 if the user is
X * not logged in at all.
X *
X * Special case for writing to yourself - ignore the terminal you're
X * writing from, unless that's the only terminal with messages enabled.
X */
Xsearch_utmp(user, tty, mytty, myuid)
X	char *user, *tty, *mytty;
X	uid_t myuid;
X{
X	struct utmp u;
X	time_t bestatime, atime;
X	int ufd, nloggedttys, nttys, msgsok, user_is_me;
X	char atty[UT_LINESIZE + 1];
X
X	if ((ufd = open(_PATH_UTMP, O_RDONLY)) < 0) {
X		perror("utmp");
X		exit(1);
X	}
X
X	nloggedttys = nttys = 0;
X	bestatime = 0;
X	user_is_me = 0;
X	while (read(ufd, (char *) &u, sizeof(u)) == sizeof(u))
X		if (strncmp(user, u.ut_name, sizeof(u.ut_name)) == 0) {
X			++nloggedttys;
X#ifdef sun
X			if (nonuser(u))
X				continue;	/* skip pseudo users */
X#endif
X			(void)strncpy(atty, u.ut_line, UT_LINESIZE);
X			atty[UT_LINESIZE] = '\0';
X			if (term_chk(atty, &msgsok, &atime, 0))
X				continue;	/* bad term? skip */
X			if (myuid && !msgsok)
X				continue;	/* skip ttys with msgs off */
X			if (strcmp(atty, mytty) == 0) {
X				user_is_me = 1;
X				continue;	/* don't write to yourself */
X			}
X			++nttys;
X			if (atime > bestatime) {
X				bestatime = atime;
X				(void)strcpy(tty, atty);
X			}
X		}
X
X	(void)close(ufd);
X	if (nloggedttys == 0) {
X		(void)fprintf(stderr, "write: %s is not logged in\n", user);
X		exit(1);
X	}
X	if (nttys == 0) {
X		if (user_is_me) {		/* ok, so write to yourself! */
X			(void)strcpy(tty, mytty);
X			return;
X		}
X		(void)fprintf(stderr,
X		    "write: %s has messages disabled\n", user);
X		exit(1);
X	} else if (nttys > 1) {
X		(void)fprintf(stderr,
X		    "write: %s is logged in more than once; writing to %s\n",
X		    user, tty);
X	}
X}
X
X/*
X * term_chk - check that a terminal exists, and get the message bit
X *     and the access time
X */
Xterm_chk(tty, msgsokP, atimeP, showerror)
X	char *tty;
X	int *msgsokP, showerror;
X	time_t *atimeP;
X{
X	struct stat s;
X	char path[MAXPATHLEN];
X
X	(void)sprintf(path, "/dev/%s", tty);
X	if (stat(path, &s) < 0) {
X		if (showerror)
X			(void)fprintf(stderr,
X			    "write: %s: %s\n", path, strerror(errno));
X		return(1);
X	}
X	*msgsokP = (s.st_mode & (S_IWRITE >> 3)) != 0;	/* group write bit */
X	*atimeP = s.st_atime;
X	return(0);
X}
X
X/*
X * do_write - actually make the connection
X */
Xdo_write(tty, mytty, myuid)
X	char *tty, *mytty;
X	uid_t myuid;
X{
X	register char *login, *nows;
X	register struct passwd *pwd;
X	time_t now, time();
X	char *getlogin(), path[MAXPATHLEN], host[MAXHOSTNAMELEN], line[512];
X	void done();
X
X	/* Determine our login name before we reopen() stdout */
X	if ((login = getlogin()) == NULL)
X		if (pwd = getpwuid(myuid))
X			login = pwd->pw_name;
X		else
X			login = "???";
X
X	(void)sprintf(path, "/dev/%s", tty);
X	if ((freopen(path, "w", stdout)) == NULL) {
X		(void)fprintf(stderr, "write: %s: %s\n", path, strerror(errno));
X		exit(1);
X	}
X
X	(void)signal(SIGINT, done);
X	(void)signal(SIGHUP, done);
X
X	/* print greeting */
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	(void)printf("\r\n\007\007\007Message from %s@%s on %s at %s ...\r\n",
X	    login, host, mytty, nows + 11);
X
X	while (fgets(line, sizeof(line), stdin) != NULL)
X		wr_puts(line);
X}
X
X/*
X * done - cleanup and exit
X */
Xvoid
Xdone()
X{
X	(void)printf("EOF\r\n");
X	exit(0);
X}
X
X/*
X * wr_puts - like puts(), but makes control characters visible and
X *     turns \n into \r\n (and doesn't add the obnoxious gratuitous \n)
X */
Xwr_puts(s)
X	register char *s;
X{
X	register char c;
X
X#define	PUTC(c)	if (putchar(c) == EOF) goto err;
X
X	for (; *s != '\0'; ++s) {
X		c = toascii(*s);
X		if (c == '\n') {
X			PUTC('\r');
X			PUTC('\n');
X		} else if (!isprint(c) && !isspace(c) && c != '\007') {
X			PUTC('^');
X			PUTC(c^0x40);	/* DEL to ?, others to alpha */
X		} else
X			PUTC(c);
X	}
X	return;
X
Xerr:	(void)fprintf(stderr, "write: %s\n", strerror(errno));
X	exit(1);
X#undef PUTC
X}
SHAR_EOF
if test 7792 -ne "`wc -c < 'write.c'`"
then
	echo shar: error transmitting "'write.c'" '(should have been 7792 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'strerror.c'" '(1214 characters)'
if test -f 'strerror.c'
then
	echo shar: will not over-write existing file "'strerror.c'"
else
sed 's/^X//' << \SHAR_EOF > 'strerror.c'
X/*
X * Copyright (c) 1988 Regents of the University of California.
X * All rights reserved.
X *
X * Redistribution and use in source and binary forms are permitted
X * provided that the above copyright notice and this paragraph are
X * duplicated in all such forms and that any documentation,
X * advertising materials, and other materials related to such
X * distribution and use acknowledge that the software was developed
X * by the University of California, Berkeley.  The name of the
X * University may not be used to endorse or promote products derived
X * from this software without specific prior written permission.
X * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
X * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
X * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
X */
X
X#if defined(LIBC_SCCS) && !defined(lint)
Xstatic char sccsid[] = "@(#)strerror.c	5.1 (Berkeley) 4/9/89";
X#endif /* LIBC_SCCS and not lint */
X
Xchar *
Xstrerror(errnum)
X	int errnum;
X{
X	extern int sys_nerr;
X	extern char *sys_errlist[];
X	static char ebuf[20];
X
X	if ((unsigned int)errnum < sys_nerr)
X		return(sys_errlist[errnum]);
X	(void)sprintf(ebuf, "Unknown error: %d", errnum);
X	return(ebuf);
X}
SHAR_EOF
if test 1214 -ne "`wc -c < 'strerror.c'`"
then
	echo shar: error transmitting "'strerror.c'" '(should have been 1214 characters)'
fi
fi # end of overwriting check
#	End of shell archive
exit 0