#include "sys/types.h"
#include "sys/stat.h"
#include "utmp.h"

/* error codes:
 -7: cant stat invoking tty
 -6: cant change tty mode
 -5: target tty missing
 -3: no permission file
 -2: no /etc/utmp
 -1: invalid usage
 0: ok
 1: not logged in
 3: not at specified tty
 4: permission denied
 5: not receiving
 6: talking to someone else
*/
#define	HANGUP 1
#define INTRPT 2
#define QUIT 3
#define TIME 14
#define TERMINATE 15
int	ctty;
int	ttytab[58][9];
int fd;
struct	utmp	utmp;
struct	stat	statb;
char buftty[128];
char	ttydes[16];
char	mytty[16];
char line[512];
char	lognm[10];
char    *toname;
int henry3 = 0;
char	multiple = 0;
main(argc,argv)
char **argv;
int argc;
{
	register char *p, *ttyletter;
	register int ntty;
	int i, tv[2], flag3, garbage, out(), byebye(), go();
	char *flagb, *henry, *ttyname(), subuf[10];
	flag3 = 0;
	flagb = 0;
	subuf[8] = subuf[9] = 0;
	if (**argv == '-') /* silent mode */
		henry3++;
	switch(argc) {
		case 1: break;
		case 2: goto ok0;
		case 3: if (argv[2][0] == '-') break;
			flagb = argv[2];
			goto ok0;
		case 4: if (argv[2][0] != '-') break;
			flag3 = 1;
			goto ok0;
		default: if (argv[2][0] == '-') {
				flag3 = 1;
				goto ok0;
			} else if (argv[3][0] == '-') {
				flagb = argv[2];
				flag3 = 2;
				goto ok0;
			}
			break;
	}
	xwrite("Usage: write person [tty] [- note text]\n");
	exit(-1);

ok0:
	strcpy(lognm, logname());
	toname = argv[1];
	if((fd = open(UTMP_FILE,0))<0) {
		xwrite("utmp file has gone away\n");
		exit(-2);
	}
	p = ttyletter = logtty();
	garbage = 0;
	ntty = 0;
	while (read(fd, &utmp, sizeof (utmp)) == sizeof (utmp)) {
		if(utmp.ut_line[0] != '\0' && utmp.ut_name[0] != 0) {
			if(strncmp(ttyletter, utmp.ut_line, 8) == 0){
				strncpy(subuf, utmp.ut_name, 8);
				garbage = 1;
			}
			if (eq(utmp.ut_name,toname)) {
				strncpy(ttytab[ntty], utmp.ut_line, 8);
				ttytab[ntty][8] = 0;
				ntty++;
			}
		}
	}
	ctty = 0;
	multiple = ((flagb == 0) && ntty > 1);
	if(multiple){
		xwrite("\"");
		xwrite(argv[1]);
		xwrite("\" is logged in at more than one terminal\n");
		goto ok;
	}
	if(ntty > 0) goto ok;
	xwrite("\"");
	xwrite(argv[1]);
	xwrite("\" is not logged in\n");
	exit(1);
ok:
	if(flagb){
		maktty(ttydes, flagb);
		goto okk;
	}
	else if (ntty == 1) {
		maktty(ttydes, ttytab[0]);
		goto ok2;
	}
	else {
okk:
		while(ctty < ntty){
			maktty(ttydes, ttytab[ctty]);
			if (multiple)goto ok2;
			if(strcmp(flagb, ttytab[ctty])){
				ctty++;
				continue;
			}
			goto ok2;
		}
	}
	if(multiple){
			exit(12);
	}
	xwrite("\"");
	xwrite(argv[1]);
	xwrite("\" is not logged in at that terminal\n");
	exit(3);
retry:
	ctty++;
	goto okk;
ok2:
	if(multiple){
		xwrite("(attempting write to ");
		xwrite(ttydes+5);
		xwrite(")\n");
	}
	close(fd);
	if (stat(ttydes, &statb)) {
		xwrite("destination tty is missing\n");
		if(multiple)goto retry;
		exit(-5);
	}
	if ((statb.st_mode & S_IFMT) != S_IFCHR) {
		xwrite( "destination tty is not a character special device\n");
		if(multiple)goto retry;
		exit(-5);
	}
	if ((statb.st_mode & 01) == 0) {
		xwrite("\"");
		xwrite(argv[1]);
		xwrite("\" is not receiving (mesg n)\n");
		if(multiple)goto retry;
		exit(5);
	}
	if ((statb.st_mode & 010) == 0) {
		xwrite("\"");
		xwrite(argv[1]);
		xwrite("\" is talking to someone else\n");
		if(multiple)goto retry;
		exit(6);
	}
	if((fd = open(ttydes,1)) <0) {
		xwrite("cannot open destination tty\n");
		if(multiple)goto retry;
		exit(-5);
	}
	signal (2, out);
	signal (3, out);
	if (chmod(ttydes,0601) < 0) {
		xwrite("tty wont change mode, cant write.\n");
		if(multiple)goto retry;
		exit(-6);
	}
	maktty(mytty, ttyletter);
	if(stat(mytty,buftty)) {
		xwrite("cannot stat invoking tty\n");
		if (henry3 == 0){
			if(multiple)goto retry;
			exit(-7);
		}
	}
	if(((buftty[4] & 01) == 0) && (henry3 == 0))
		xwrite("Be advised that your write permission is off (mesg n)\n");
	if (garbage == 0) {
		if (henry3 == 0) {
			xwrite("Utmp is dead\n");
			chmod(ttydes,0611);
			exit(-2);
		}
		garbage = -2;
		goto notry;
	}
	p = subuf;
	while(*p)
		if(*p == ' '){
			*p = 0;
			break;
		}
		else	p++;
	garbage = (strcmp(lognm, subuf) == 0);

notry:
	if (flag3) write(fd,"\nNote from ",12);
	else write(fd,"\nMessage from ",15);
	if (garbage >= -1)
		write(fd, subuf, strlen(subuf));
	if ((garbage == 0) || (garbage == -2)) {
		write(fd," [",2);
		write(fd,lognm, strlen(lognm));
		write(fd,"]",1);
		}
	if(!henry3) {
		write(fd," (",2);
		write(fd, ttyletter, strlen(ttyletter));
		write(fd,")",1);
	}
	time(tv);
	p = ctime(tv);
	write(fd, p+10, 6);
	if (flag3) write(fd," :",2);
	else write(fd," ...\n",5);
	if (flag3) goto ok99;
		signal(HANGUP,go);
		signal(INTRPT,go);
		signal(QUIT,go);
		signal(TIME,byebye);
		signal(TERMINATE,go);
	xwrite("*\n");
	for(;;) {
		alarm(300);
		i = read(0,line,512);
		if(i <= 0)
			go();
		if(line[0] == '!') {
			line[i] = 0;
			shell();
			continue;
		}
		write(fd,line,i);
	}
	go();
ok99:
	argv++;
	argc--;
	argv += flag3;
	argc -= flag3;
	while (--argc) { argv++;
		write(fd," ",1);
		write(fd, *argv, strlen(*argv));
	}
	write(fd,"\n",1);
	if (chmod(ttydes,0611) < 0) {
		xwrite("chmod failed  --  forever writing\n");
		exit(-6);
	}
	exit(0);
	}
go() {
	write(fd,"EOT\n",4);
	if (chmod(ttydes,0611) < 0) {
		xwrite("cant change mode back...you are forever talking\n");
		exit(-6);
	}
	exit(0);
	}
shell() {
	register i;
	alarm(0);
	sigs(1);
	i = fork();
	if(i < 0) {
		write(1,"try again\n",10);
		goto out;
		}
	if(i == 0) {
		sigs(0);
		setgid(getgid());
		execl("/bin/sh", "sh", "-c", line+1, 0);
		exit(0);
		}
	while(wait(line) != i)
		;
	write(1,"!\n",2);
out:    sigs(go);
	}
sigs(sig) {
	register i;
	for(i=1;i<4;i++)
		signal(i, sig);
	}
byebye() {
	write(1,"forced EOT (due to TIMEOUT)\n",29);
	go();
	}

eq(a,b)
register char *a, *b;
{
/* b is null terminated */
	register int n;
	n = 0;
	while (*b) {
		if (*a++ != *b++)
			return(0);
		n++;
	}
	if (n > 7)
		return(1);
	return(*a == ' ' || *a == 0);
}
xwrite(text)
register char *text;
{
	if (henry3 == 0)
		write(1, text, strlen(text));
}
out()
{
	chmod(ttydes, 0611);
	exit(0);
}

maktty(d,s)
register char *d, *s;
{
	register char *p;

	p = "/dev/";
	while(*d = *p++)
		d++;
	while(*d++ = *s++);
}

