pkg://ytalk-3.0.3-4.src.rpm:59297/ytalk-3.0.3-8bit.tar.gz
info downloads
ytalk-3.0.3-8bit/ 40755 7527 230 0 6241653071 12424 5 ustar espel i_math93 ytalk-3.0.3-8bit/Imakefile 100644 7527 230 6262 6241653057 14344 0 ustar espel i_math93 #### Imakefile for YTalk version 3.0 ####
#
# NOTICE
#
# Copyright (c) 1990,1992,1993 Britt Yenne. All rights reserved.
#
# This software is provided AS-IS. The author gives no warranty,
# real or assumed, and takes no responsibility whatsoever for any
# use or misuse of this software, or any damage created by its use
# or misuse.
#
# This software may be freely copied and distributed provided that
# no part of this NOTICE is deleted or edited in any manner.
#
###################################
## CONFIGURATION (The Fun Part) ##
###################################
#
# If you are on a System V machine (such as Solaris 2.x), uncomment
# the following line:
# VDEFS = -DSYSV
# If you're on Solaris you NEED this one for ytalk shells; it might
# work on other Sys V machines too
# PDEFS = -DPTM
#
# If your machine does not support TERMIOS (example: any NeXT running
# NeXTStep up to and including version 3.1), then uncomment the following
# line.
#TDEFS = -DUSE_SGTTY
#
# If you are running an older Sun OS using YP (now known as NIS), you might
# need to uncomment the next line if ytalk asks you "Who are you?"
#SLIBS = -lsun
#
# If you are on a sun running solaris 2.* you might need to uncomment the
# following line.
#SLIBS = -lnsl -lsocket
#
# If your machine has a 64-bit architecture or uses 64-bit 'long's, then you
# will need to uncomment the following line.
#BDEFS = -DY64BIT
#
# If you want ytalk to be 8-bit clean, you need to define this. Note that
# this requires linking with an 8-bit clean (n)curses. Under SunOS 4.x,
# set CC to /usr/5bin/cc for this to work.
EBCDEFS = -DEIGHT_BIT_CLEAN
#
# If you have (or want) a system-wide .ytalkrc file, uncomment the
# next line and set it to the correct pathname. The backslashes must
# remain before each double-quote.
#RCDEF = -DSYSTEM_YTALKRC=\"/usr/local/etc/ytalkrc\"
#
# If you plan to install ytalk on your system, you may want to modify
# the following lines. Y_BINDIR is where the binary will be placed.
# Y_MANDIR is where the manpage will be placed.
Y_BINDIR = /usr/local/bin
Y_MANDIR = /usr/local/man/man1
# Uncomment if you have gcc - recommended
CC = gcc
############################################################
## Past this point, you shouldn't need to modify anything ##
############################################################
LIB = -lcurses -ltermcap $(SLIBS) $(XLIB)
DEFINES = -DUSE_X11 -I/usr/local/include $(TDEFS) $(BDEFS) $(RCDEF) $(VDEFS) \
$(PDEFS) $(EBCDEFS) -O
LDFLAGS = $(LDOPTIONS)
OBJ = main.o term.o user.o fd.o comm.o menu.o socket.o rc.o exec.o cwin.o \
xwin.o
PRG = ytalk
all: $(PRG) ytalk.cat
$(PRG): $(OBJ)
$(CC) $(LDFLAGS) -o $(PRG) $(OBJ) $(LIB)
ytalk.cat: ytalk.1
nroff -man ytalk.1 > ytalk.cat
start: Imakefile
sed 's/^DEFINES.*X11/CFLAGS =/' < Imakefile > Makefile
shar:
make start
shar -i Manifest -o ytalk-3.0.shar -t "== Now read the README file =="
clean::
-rm -f $(OBJ) $(PRG)
install:: $(PRG)
/bin/cp ytalk $(Y_BINDIR)
/bin/cp ytalk.1 $(Y_MANDIR)
@echo "Ytalk installation complete."
$(OBJ): header.h
main.o: menu.h
term.o: cwin.h xwin.h menu.h
fd.o: menu.h
comm.o: socket.h menu.h
menu.o: menu.h
socket.o: socket.h
cwin.o: cwin.h
xwin.o: xwin.h
ytalk-3.0.3-8bit/Makefile 100644 7527 230 6256 6241653065 14175 0 ustar espel i_math93 #### Imakefile for YTalk version 3.0 ####
#
# NOTICE
#
# Copyright (c) 1990,1992,1993 Britt Yenne. All rights reserved.
#
# This software is provided AS-IS. The author gives no warranty,
# real or assumed, and takes no responsibility whatsoever for any
# use or misuse of this software, or any damage created by its use
# or misuse.
#
# This software may be freely copied and distributed provided that
# no part of this NOTICE is deleted or edited in any manner.
#
###################################
## CONFIGURATION (The Fun Part) ##
###################################
#
# If you are on a System V machine (such as Solaris 2.x), uncomment
# the following line:
# VDEFS = -DSYSV
# If you're on Solaris you NEED this one for ytalk shells; it might
# work on other Sys V machines too
# PDEFS = -DPTM
#
# If your machine does not support TERMIOS (example: any NeXT running
# NeXTStep up to and including version 3.1), then uncomment the following
# line.
#TDEFS = -DUSE_SGTTY
#
# If you are running an older Sun OS using YP (now known as NIS), you might
# need to uncomment the next line if ytalk asks you "Who are you?"
#SLIBS = -lsun
#
# If you are on a sun running solaris 2.* you might need to uncomment the
# following line.
#SLIBS = -lnsl -lsocket
#
# If your machine has a 64-bit architecture or uses 64-bit 'long's, then you
# will need to uncomment the following line.
#BDEFS = -DY64BIT
#
# If you want ytalk to be 8-bit clean, you need to define this. Note that
# this requires linking with an 8-bit clean (n)curses. Under SunOS 4.x,
# set CC to /usr/5bin/cc for this to work.
EBCDEFS = -DEIGHT_BIT_CLEAN
#
# If you have (or want) a system-wide .ytalkrc file, uncomment the next
# line and set it to the correct pathname. The backslashes must remain
# before each double-quote.
#RCDEF = -DSYSTEM_YTALKRC=\"/usr/local/etc/ytalkrc\"
#
# If you plan to install ytalk on your system, you may want to modify
# the following lines. Y_BINDIR is where the binary will be placed.
# Y_MANDIR is where the manpage will be placed.
Y_BINDIR = /usr/local/bin
Y_MANDIR = /usr/local/man/man1
# Uncomment if you have gcc - recommended
CC = gcc
############################################################
## Past this point, you shouldn't need to modify anything ##
############################################################
LIB = -lcurses -ltermcap $(SLIBS) $(XLIB)
CFLAGS = -I/usr/local/include $(TDEFS) $(BDEFS) $(RCDEF) $(VDEFS) $(PDEFS) \
$(EBCDEFS) -O
LDFLAGS = $(LDOPTIONS)
OBJ = main.o term.o user.o fd.o comm.o menu.o socket.o rc.o exec.o cwin.o \
xwin.o
PRG = ytalk
all: $(PRG)
$(PRG): $(OBJ)
$(CC) $(LDFLAGS) -o $(PRG) $(OBJ) $(LIB)
strip $(PRG)
ytalk.cat: ytalk.1
nroff -man ytalk.1 > ytalk.cat
start: Imakefile
sed 's/^DEFINES.*X11/CFLAGS =/' < Imakefile > Makefile
shar:
make start
shar -i Manifest -o ytalk-3.0.shar -t "== Now read the README file =="
clean::
-rm -f $(OBJ) $(PRG)
install:: $(PRG)
/bin/cp ytalk $(Y_BINDIR)
/bin/cp ytalk.1 $(Y_MANDIR)
@echo "Ytalk installation complete."
$(OBJ): header.h
main.o: menu.h
term.o: cwin.h xwin.h menu.h
fd.o: menu.h
comm.o: socket.h menu.h
menu.o: menu.h
socket.o: socket.h
cwin.o: cwin.h
xwin.o: xwin.h
ytalk-3.0.3-8bit/Manifest 100644 7527 230 252 5435465461 14201 0 ustar espel i_math93 README
Manifest
ytalk.1
Imakefile
Makefile
term.doc
header.h
menu.h
socket.h
curses.h
xwin.h
main.c
comm.c
fd.c
menu.c
exec.c
rc.c
socket.c
user.c
term.c
curses.c
xwin.c
ytalk-3.0.3-8bit/README 100644 7527 230 13177 6241652201 13424 0 ustar espel i_math93 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This version of ytalk has been modified (patched) by orabidoo
<roger.espel.llima@pobox.com>, to fix a few bugs and add a few options.
Use at your own risk.
One bug fix makes the ytalk shell wrap its lines properly, instead of
making a column to the right of your screen. This is local, so if you
are patched and whoever you're talking to isn't, you will always see it
right and they will see it wrong.
The other bug fix makes ytalk process the "set scrolling-zone" escape
sequence (ESC[r) properly, which lets programs such as IRC work
within a ytalk shell.
Also, I've added an option in the makefile to use Solaris's ptys, since
they seem to handle them differently from everyone else.
The new options are -Y, to require CAPS on all y/n answers (so you don't
answer without wanting to, when it asks you to add someone..), and -i,
which won't attempt to catch new incoming talk requests with a "talk to
blah@blah?", but will just let the daemon beep you. In my opinion, you
always want -Y, and you want -i only if you have a separate window or
screen or console for talk requests.
You can specify these by adding the lines "turn caps on",
"turn noinvite on", respectively, to your ~/.ytalkrc.
Added the option "kill all unconnected" to the main menu, which makes
ytalk forget about any pending connections (i.e. people we are ringing
but haven't answered yet). This is useful when you are trying to get a
bunch of people on a ytalk and have them there finally, but then you
find that you're still trying to rering some of them.
Made it so that when ytalk is about to exit, it doesn't clear the window
for the last user who is disconnecting, so you still get to see the last
few lines the person typed if they ^C when you're not watching that
screen.
Some of these patches have been mailed to the ytalk maintainers, so they
*might* make it into some future release.
30 Oct 96:
Yet another patch... now you can define EIGHT_BIT_CLEAN in the Makefile,
and if your curses is 8-bit-clean you'll have all those wonderful
accented characters (as long as the other end has them too, of course).
Also, ^H and Delete (ASCII 8 and 127 respectively) are *always*
understood as delete characters now, in addition to whatever is set by
your stty (it's not clean, but it works better this way in practice).
11 Nov 96 :
Removed the whole TCP NODELAY mode, since all that does is disable the
Nagle algorithm, which at most delays packets by .2 sec (right?).
The original README follows:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
YTalk Make Procedure:
To restore the Makefile to its original state, run:
make start
If wish to support the X11 interface, edit the configuration at
the top of the 'Imakefile' file then run:
xmkmf
make
If you do not have "xmkmf" on your system, then you cannot support
X11 through ytalk at this time. Ask your system administrator to
install "xmkmf" (it stands for "X Make Makefile").
If you do not have X11 installed on your system, or you do not
wish to support the X11 interface, then edit the configuration at
the top of the 'Makefile' file then run:
make
The resulting executable program will be named "ytalk".
If you ever go back and change some configuration parameters at the
top of Imakefile or Makefile, you should run 'make clean' before running
'xmkmf' and/or 'make' again. This will ensure that everything recompiles.
A manpage will automatically be built. It will be named "ytalk.cat".
You can read the manual page by running:
more ytalk.cat
If you make changes to this source code or if you have any ideas for
neat new features, _PLEASE_ mail me and let me know about it. Chances
are I'll incorporate your ideas/changes into the next version and put a
great big "thank you" note to you in the documentation and source
code. If you do change something or add features, and you intend to
give a copy to a friend, please make sure _your_ name appears somewhere
in the documentation so that _I_ don't get reams of mail or bug reports
about features I didn't know existed... :-)
If you have questions or suggestions, I'd be happy to hear from you
at this email address:
ytalk@austin.eds.com
---- PATCH LEVEL 02
1. Remove #elif directive, patch by Pete Wenzel.
2. Recognize if party is refusing messages, thanks to J. Adam Hawkes.
3. Linux support, thanks to Thilo Wunderlich.
4. Port to AIX 3.1+.
5. Add -s option to start in a shell, thanks to Mark Musone.
6. Fix various error messages.
7. Fix possible obscure bug in socket.c.
---- PATCH LEVEL 01
1. Forcibly reset the scrolling region after a shell exits.
2. Handle a local X resize while in a shell.
3. Repair the password entry lookup stuff.
4. Character-wrap to next line if word-wrap is disabled.
5. Solaris 2.* support, patch by Magnus Hammerin.
6. Aside messages in X, patch by Jonas Yngvesson.
7. Fix X support, patch by Carl Edman.
8. Option -x to disable X from the command line, thanks to John Vanderpool.
9. Ctrl-L or ctrl-R to redraw screen, thanks to Shih-Chen Huang.
10. Fix bizarre WINCH bug in exec.c.
11. Handle 64-bit machines, thanks to Andrew Myers.
12. Implement raw/cooked curses -- fixes 8-bit/parity problems with some
terminals and allows ytalk to be suspended. Thanks to Duncan Sinclair.
13. System ytalkrc file, thanks to Evan McLean.
14. Place user's full name in title bar, thanks to Evan McLean.
15. Better imake support, thanks to Carl Edman.
16. Installation features for make.
17. Fix X resource database calls, patch by Andreas Stolcke.
18. Fix cross-platform problems, thanks to Larry Schwimmer.
ytalk-3.0.3-8bit/comm.c 100644 7527 230 70375 6241652261 13654 0 ustar espel i_math93 /* comm.c -- firewall between socket and terminal I/O */
/* NOTICE
*
* Copyright (c) 1990,1992,1993 Britt Yenne. All rights reserved.
*
* This software is provided AS-IS. The author gives no warranty,
* real or assumed, and takes no responsibility whatsoever for any
* use or misuse of this software, or any damage created by its use
* or misuse.
*
* This software may be freely copied and distributed provided that
* no part of this NOTICE is deleted or edited in any manner.
*
*/
/* Mail comments or questions to ytalk@austin.eds.com */
#include "header.h"
#include "socket.h"
#include "menu.h"
#include <sys/uio.h>
ychar *io_ptr; /* user input pointer */
int io_len = 0; /* user input count */
extern int input_flag; /* see fd.c */
/* ---- local functions ---- */
static y_parm parm;
static v2_pack v2p;
static v3_pack v3p;
static v3_flags v3f;
static v3_winch v3w;
/* Set up a drain of out-of-band data.
*/
static void
drain_user(user, len, func)
yuser *user;
int len;
void (*func)();
{
if(len > user->dbuf_size)
{
user->dbuf_size = len + 64;
user->dbuf = (ychar *)realloc_mem(user->dbuf, user->dbuf_size);
}
user->drain = len;
user->dptr = user->dbuf;
user->dfunc = func;
}
/* Send out-of-band data.
*/
static void
send_oob(fd, ptr, len)
int fd;
yaddr ptr;
int len;
{
ychar oob, size;
static struct iovec iov[3];
if(len <= 0 || len > V3_MAXPACK)
{
errno = 0;
show_error("send_oob: packet too large");
return;
}
oob = V3_OOB;
iov[0].iov_base = (yaddr)(&oob);
iov[0].iov_len = 1;
size = len;
iov[1].iov_base = (yaddr)(&size);
iov[1].iov_len = 1;
iov[2].iov_base = ptr;
iov[2].iov_len = len;
if(writev(fd, iov, 3) != len + 2)
show_error("send_oob: write failed");
}
/* Ask another ytalk connection if he wants to import a user I've
* just now connected to.
*/
static void
send_import(to, from)
yuser *to, *from;
{
if(to->remote.vmajor > 2)
{
v3p.code = V3_IMPORT;
v3p.host_addr = htonl(from->host_addr);
v3p.pid = htonl(from->remote.pid);
strncpy(v3p.name, from->user_name, V3_NAMELEN);
strncpy(v3p.host, from->host_name, V3_HOSTLEN);
send_oob(to->fd, &v3p, V3_PACKLEN);
}
else if(to->remote.vmajor == 2)
{
v2p.code = V2_IMPORT;
strncpy(v2p.name, from->user_name, V2_NAMELEN);
strncpy(v2p.host, from->host_name, V2_HOSTLEN);
(void)write(to->fd, &v2p, V2_PACKLEN);
}
}
/* Tell another ytalk connection to connect to a user.
*/
static void
send_accept(to, from)
yuser *to, *from;
{
if(to->remote.vmajor > 2)
{
v3p.code = V3_ACCEPT;
v3p.host_addr = htonl(from->host_addr);
v3p.pid = htonl(from->remote.pid);
strncpy(v3p.name, from->user_name, V3_NAMELEN);
strncpy(v3p.host, from->host_name, V3_HOSTLEN);
send_oob(to->fd, &v3p, V3_PACKLEN);
}
else if(to->remote.vmajor == 2)
{
v2p.code = V2_ACCEPT;
strncpy(v2p.name, from->user_name, V2_NAMELEN);
strncpy(v2p.host, from->host_name, V2_HOSTLEN);
(void)write(to->fd, &v2p, V2_PACKLEN);
}
}
/* Process a Ytalk version 2.? data packet.
*/
static void
v2_process(user, pack)
yuser *user;
v2_pack *pack;
{
register yuser *u;
ylong host_addr;
static char name[V2_NAMELEN + 1];
static char host[V2_HOSTLEN + 1];
static char estr[V2_NAMELEN + V2_HOSTLEN + 20];
/* Ytalk version 2.* didn't have very clever import/export
* capabilities. We'll just go with the flow.
*/
strncpy(name, pack->name, V2_NAMELEN);
strncpy(host, pack->host, V2_HOSTLEN);
name[V2_NAMELEN] = '\0';
host[V2_HOSTLEN] = '\0';
if((host_addr = get_host_addr(host)) == (ylong)-1)
{
errno = 0;
sprintf(errstr, "unknown host: '%s'", host);
show_error(errstr);
show_error("port from ytalk V2.? failed");
return;
}
switch(pack->code)
{
case V2_IMPORT:
/* Don't import a user with the same name of an existing
* user at this end. yukk.
*/
if(find_user(name, host_addr, (ylong)-1) != NULL)
break;
if(!(def_flags & FL_IMPORT))
{
sprintf(estr, "Import %s@%s?", name, host);
if(yes_no(estr) == 'n')
break;
}
/* invite him but don't ring him */
sprintf(estr, "%s@%s", name, host);
invite(estr, 0);
/* now tell him to connect to us */
pack->code = V2_EXPORT;
(void)write(user->fd, pack, V2_PACKLEN);
break;
case V2_EXPORT:
/* We don't need to check if he's not connected, since
* send_accept() will think his version number is zero
* and won't send anything.
*/
if((u = find_user(name, host_addr, (ylong)-1)) == NULL)
break;
send_accept(u, user);
break;
case V2_ACCEPT:
sprintf(estr, "%s@%s", name, host);
invite(estr, 1); /* we should be expected */
break;
}
}
/* Process a Ytalk version 3.? data packet.
*/
static void
v3_process_pack(user, pack)
yuser *user;
v3_pack *pack;
{
register yuser *u;
ylong host_addr, pid;
static char name[V3_NAMELEN + 1];
static char host[V3_HOSTLEN + 1];
static char estr[V3_NAMELEN + V3_HOSTLEN + 20];
strncpy(name, pack->name, V3_NAMELEN);
strncpy(host, pack->host, V3_HOSTLEN);
name[V3_NAMELEN] = '\0';
host[V3_HOSTLEN] = '\0';
if((host_addr = get_host_addr(host)) == (ylong)-1)
host_addr = ntohl(pack->host_addr);
pid = ntohl(pack->pid);
switch(pack->code)
{
case V3_IMPORT:
/* Don't import a user which is already in this
* session. This is defined as a user with a matching
* name, host address, and process id.
*/
if(find_user(name, host_addr, pid) != NULL)
break;
if(!(def_flags & FL_IMPORT))
{
sprintf(estr, "Import %s@%s?", name, host);
if(yes_no(estr) == 'n')
break;
}
/* invite him but don't ring him */
sprintf(estr, "%s@%s", name, host);
invite(estr, 0);
/* now tell him to connect to us */
pack->code = V3_EXPORT;
send_oob(user->fd, pack, V3_PACKLEN);
break;
case V3_EXPORT:
/* We don't need to check if he's not connected, since
* send_accept() will think his version number is zero
* and won't send anything.
*/
if((u = find_user(name, host_addr, pid)) == NULL)
break;
send_accept(u, user);
break;
case V3_ACCEPT:
sprintf(estr, "%s@%s", name, host);
invite(estr, 1); /* we should be expected */
break;
}
}
/* Process a Ytalk version 3.? flags packet. Other users can request
* that their flags be locked to a particular value until they unlock
* them later.
*/
static void
v3_process_flags(user, pack)
yuser *user;
v3_flags *pack;
{
switch(pack->code)
{
case V3_LOCKF:
user->flags = ntohl(pack->flags) | FL_LOCKED;
break;
case V3_UNLOCKF:
user->flags = def_flags;
break;
}
}
/* Process a Ytalk version 3.? winch packet.
*/
static void
v3_process_winch(user, pack)
yuser *user;
v3_winch *pack;
{
switch(pack->code)
{
case V3_YOURWIN:
user->remote.my_rows = ntohs(pack->rows);
user->remote.my_cols = ntohs(pack->cols);
winch_exec();
break;
case V3_MYWIN:
user->remote.rows = ntohs(pack->rows);
user->remote.cols = ntohs(pack->cols);
break;
case V3_REGION:
pack->rows = ntohs(pack->rows);
pack->cols = ntohs(pack->cols);
if(pack->rows > 0)
set_win_region(user, (int)(pack->rows), (int)(pack->cols));
else
end_win_region(user);
break;
}
user_winch = 1;
}
/* Process a Ytalk version 3.? out-of-band packet. Call the appropriate
* function based on the type of packet.
*/
static void
v3_process(user, ptr)
yuser *user;
yaddr ptr;
{
ychar *str;
/* ignore anything we don't understand */
str = (ychar *)ptr;
switch(*str)
{
case V3_IMPORT:
case V3_EXPORT:
case V3_ACCEPT:
v3_process_pack(user, (v3_pack *)ptr);
break;
case V3_LOCKF:
case V3_UNLOCKF:
v3_process_flags(user, (v3_flags *)ptr);
break;
case V3_YOURWIN:
case V3_MYWIN:
case V3_REGION:
v3_process_winch(user, (v3_winch *)ptr);
break;
}
}
/* Take input from a connected user. If necessary, drain out-of-band
* data from the canonical input stream.
*/
static void
read_user(fd)
int fd;
{
register ychar *c, *p;
register int rc;
register yuser *user;
static ychar buf[512];
if(input_flag)
{
/* tell input_loop() to ignore this function for now */
input_flag = 0;
return;
}
if((user = fd_to_user[fd]) == NULL)
{
remove_fd(fd);
show_error("read_user: unknown contact");
return;
}
if((rc = read(fd, buf, 512)) <= 0)
{
if(rc < 0)
show_error("read_user: read() failed");
free_user(user);
return;
}
c = buf;
while(rc > 0)
{
if(user->drain > 0) /* there is still some OOB data to drain */
{
if(rc < user->drain)
{
(void)memcpy(user->dptr, c, rc);
user->dptr += rc;
user->drain -= rc;
rc = 0;
}
else
{
(void)memcpy(user->dptr, c, user->drain);
rc -= user->drain;
c += user->drain;
user->drain = 0;
user->dfunc(user, user->dbuf);
}
}
else
{
/* Ytalk version 3.0 Out-Of-Band data protocol:
*
* If I receive a V3_OOB character, I look at the next
* character. If the next character is V3_OOB, then I
* send one V3_OOB through transparently. Else, the
* next character is a packet length to be drained.
* The packet length can never be V3_OOB because the
* maximum out-of-band packet length is (V3_OOB - 1) bytes.
* If any packet requires more information, then it can
* always kick off another drain_user() inside v3_process().
*/
p = buf;
if(user->got_oob)
{
user->got_oob = 0;
if(*c <= V3_MAXPACK)
{
drain_user(user, *c, v3_process);
c++, rc--;
continue;
}
*(p++) = *c;
c++, rc--;
}
for(; rc > 0; c++, rc--)
{
if(*c > 127) /* could be inline data */
{
if(user->remote.vmajor > 2) /* ytalk 3.0+ */
{
if(*c == V3_OOB)
{
c++, rc--;
if(rc > 0)
{
if(*c <= V3_MAXPACK)
{
drain_user(user, *c, v3_process);
c++, rc--;
break;
}
}
else
{
user->got_oob = 1;
break;
}
}
}
else if(user->remote.vmajor == 2) /* ytalk 2.0+ */
{
/* Version 2.* didn't support data transparency */
if(*c == V2_IMPORT || *c == V2_EXPORT
|| *c == V2_ACCEPT || *c == V2_AUTO)
{
drain_user(user, V2_PACKLEN, v2_process);
/* don't increment c or decrement rc -- they're
* part of the drain. :-)
*/
break;
}
}
}
*(p++) = *c;
}
if(p > buf)
{
if(user->output_fd > 0)
if(write(user->output_fd, buf, p - buf) <= 0)
{
show_error("write to user output file failed");
close(user->output_fd);
user->output_fd = 0;
}
show_input(user, buf, p - buf);
}
}
}
}
/* Initial Handshaking: read the parameter pack from another ytalk user.
*/
static void
ytalk_user(fd)
int fd;
{
register yuser *user, *u;
u_short cols;
if((user = fd_to_user[fd]) == NULL)
{
remove_fd(fd);
show_error("ytalk_user: unknown contact");
return;
}
if(full_read(user->fd, &parm, sizeof(y_parm)) < 0)
{
free_user(user);
show_error("ytalk_user: bad ytalk contact");
return;
}
switch(parm.protocol)
{
case YTP_OLD:
cols = parm.w_cols;
(void)memset(&parm, 0, sizeof(y_parm));
parm.vmajor = 2;
parm.cols = cols;
parm.my_cols = cols;
spew_term(me, fd, me->t_rows, parm.cols);
break;
case YTP_NEW:
parm.vmajor = ntohs(parm.vmajor);
parm.vminor = ntohs(parm.vminor);
parm.rows = ntohs(parm.rows);
parm.cols = ntohs(parm.cols);
parm.my_rows = ntohs(parm.my_rows);
parm.my_cols = ntohs(parm.my_cols);
parm.pid = ntohl(parm.pid);
/* we spew_term later */
break;
default:
free_user(user);
show_error("ytalk_user: unsupported ytalk protocol");
return;
}
user->remote = parm;
user_winch = 1;
add_fd(fd, read_user);
/* update the lists */
if(user == wait_list)
wait_list = user->next;
else
for(u = wait_list; u; u = u->next)
if(u->next == user)
{
u->next = user->next;
break;
}
user->next = connect_list;
connect_list = user;
/* send him my status */
if(user->remote.vmajor > 2)
{
if(me->region_set)
{
v3w.code = V3_REGION;
v3w.rows = htons(me->rows);
v3w.cols = htons(me->cols);
send_oob(fd, &v3w, V3_WINCHLEN);
winch_exec();
spew_term(me, fd, me->rows, me->cols);
}
else
spew_term(me, fd, parm.rows, parm.cols);
if(me->flags & FL_LOCKED)
{
v3f.code = V3_LOCKF;
v3f.flags = htonl(me->flags);
send_oob(fd, &v3f, V3_FLAGSLEN);
}
}
/* tell everybody else he's here! */
for(u = connect_list; u; u = u->next)
if(u != user)
send_import(u, user);
}
/* Initial Handshaking: read the edit keys and determine whether or not
* this is another ytalk user.
*/
static void
connect_user(fd)
int fd;
{
register yuser *user, *u;
if((user = fd_to_user[fd]) == NULL)
{
remove_fd(fd);
show_error("connect_user: unknown contact");
return;
}
if(full_read(fd, user->edit, 3) < 0)
{
free_user(user);
show_error("connect_user: bad read");
return;
}
if(open_term(user, user->full_name) < 0)
{
free_user(user);
show_error("connect_user: open_term() failed");
return;
}
/* check for ytalk connection */
if(user->RUB == RUBDEF)
{
(void)memset(&parm, 0, sizeof(y_parm));
parm.protocol = YTP_NEW;
parm.vmajor = htons(VMAJOR);
parm.vminor = htons(VMINOR);
parm.rows = htons(me->t_rows);
parm.cols = htons(me->t_cols);
parm.my_rows = htons(user->t_rows);
parm.my_cols = htons(user->t_cols);
parm.w_rows = parm.rows;
parm.w_cols = parm.cols;
parm.pid = htonl(me->remote.pid);
(void)write(user->fd, &parm, sizeof(y_parm));
add_fd(fd, ytalk_user);
}
else
{
/* update the lists */
if(user == wait_list)
wait_list = user->next;
else
for(u = wait_list; u; u = u->next)
if(u->next == user)
{
u->next = user->next;
break;
}
user->next = connect_list;
connect_list = user;
spew_term(me, fd, me->t_rows, me->t_cols);
user_winch = 1;
add_fd(fd, read_user);
}
}
/* Initial Handshaking: delete his invitation (if it exists) and send
* my edit keys.
*/
static void
contact_user(fd)
int fd;
{
register yuser *user;
register int n;
int socklen;
remove_fd(fd);
if((user = fd_to_user[fd]) == NULL)
{
show_error("contact_user: unknown contact");
return;
}
(void)send_dgram(user, DELETE_INVITE);
socklen = sizeof(struct sockaddr_in);
if((n = accept(fd, (struct sockaddr *) &(user->sock), &socklen)) < 0)
{
free_user(user);
show_error("connect_user: accept() failed");
return;
}
close(fd);
fd_to_user[fd] = NULL;
user->fd = n;
fd_to_user[user->fd] = user;
add_fd(user->fd, connect_user);
(void)write(user->fd, me->edit, 3); /* send the edit keys */
}
/* Do a word wrap.
*/
static int
word_wrap(user)
register yuser *user;
{
register int i, x, bound;
static ychar temp[20];
x = user->x;
if((bound = (x >> 1)) > 20)
bound = 20;
for(i = 1; i < bound && user->scr[user->y][x-i] != ' '; i++)
temp[i] = user->scr[user->y][x-i];
if(i >= bound)
return -1;
move_term(user, user->y, x - i);
clreol_term(user);
newline_term(user);
for(i--; i >= 1; i--)
addch_term(user, temp[i]);
return 0;
}
/* Ring a user. If he has an auto-invitation port established then talk
* to that instead of messing up his screen.
*/
static int
announce(user)
yuser *user;
{
register int rc, fd;
errno = 0;
while((rc = send_dgram(user, AUTO_LOOK_UP)) == 0)
{
/* he has an auto-invite port established */
if((fd = connect_to(NULL)) < 0)
{
if(fd == -3) /* it's one of my sockets... *sigh* */
break;
if(fd == -2) /* connection refused -- they hung up! */
{
(void)send_dgram(user, AUTO_DELETE);
errno = 0;
continue;
}
return -1;
}
/* Go ahead and use the Ytalk version 2.? auto-announce
* packet.
*/
v2p.code = V2_AUTO;
strncpy(v2p.name, me->user_name, V2_NAMELEN);
strncpy(v2p.host, me->host_name, V2_HOSTLEN);
v2p.name[V2_NAMELEN-1] = '\0';
v2p.host[V2_HOSTLEN-1] = '\0';
(void)write(fd, &v2p, V2_PACKLEN);
close(fd);
return 0;
}
if(rc == -1)
return -1;
errno = 0;
if((rc = send_dgram(user, ANNOUNCE)) == 0)
return 0;
if(rc == 4) /* mesg n (refusing messages) */
return 1;
return -1;
}
/* ---- global functions ---- */
/* Invite a user into the conversation.
*/
void
invite(name, send_announce)
register char *name;
int send_announce;
{
register int rc;
char *hisname, *hishost, *histty;
yuser *user;
/* First break down the username into login name and login host,
* assuming our host as a default.
*/
hisname = str_copy(name);
hishost = NULL;
histty = NULL;
for(name = hisname; *name; name++)
{
if(*name == '@')
{
*name = '\0';
hishost = name+1;
}
if(*name == '#')
{
*name = '\0';
histty = name+1;
}
}
user = new_user(hisname, hishost, histty);
free(hisname);
if(user == NULL)
return;
/* Now send off the invitation */
user->next = wait_list;
wait_list = user;
user_winch = 1;
while((rc = send_dgram(user, LOOK_UP)) == 0)
{
/* We are expected... */
if((rc = connect_to(user)) < 0)
{
if(rc == -3) /* it's one of my sockets... *sigh* */
break;
if(rc == -2) /* connection refused -- they hung up! */
{
(void)send_dgram(user, DELETE);
continue;
}
free_user(user);
return;
}
user->last_invite = (ylong)time(NULL);
add_fd(user->fd, connect_user);
(void)write(user->fd, me->edit, 3); /* send the edit keys */
return;
}
if(rc == -1)
return;
/* Leave an invitation for him, and announce ourselves. */
if(send_announce)
{
sprintf(errstr, "Ringing %s...", user->user_name);
msg_term(me, errstr);
}
if(newsock(user) != 0)
{
free_user(user);
return;
}
(void)send_dgram(user, LEAVE_INVITE);
user->last_invite = (ylong)time(NULL);
if(send_announce && (rc = announce(user)) != 0)
{
(void)send_dgram(user, DELETE_INVITE);
if(rc > 0)
sprintf(errstr, "%s refusing messages", user->full_name);
else
sprintf(errstr, "%s not logged in", user->full_name);
show_error(errstr);
free_user(user);
return;
}
add_fd(user->fd, contact_user);
}
/* Periodic housecleaning.
*/
void
house_clean()
{
register yuser *u, *next;
ylong t;
static char estr[80];
static ylong last_auto = 0;
int answer, rc;
t = (ylong)time(NULL);
if(t - last_auto >= 30)
{
last_auto = t;
if(send_auto(LEAVE_INVITE) != 0)
{
show_error("house_clean: send_auto() failed");
kill_auto();
}
}
for(u = wait_list; u; u = next)
{
next = u->next;
if(t - u->last_invite >= 30)
{
(void)send_dgram(u, LEAVE_INVITE);
u->last_invite = t = (ylong)time(NULL);
if(!(def_flags & FL_RING))
{
if(input_flag)
continue;
sprintf(estr, "Rering %s?", u->full_name);
answer = yes_no(estr);
t = (ylong)time(NULL);
if(answer == 'n')
continue;
}
if((rc = announce(u)) != 0)
{
(void)send_dgram(u, DELETE_INVITE);
if(rc > 0)
sprintf(errstr, "%s refusing messages", u->full_name);
else
sprintf(errstr, "%s not logged in", u->full_name);
show_error(errstr);
free_user(u);
}
}
}
}
void
send_winch(user)
yuser *user;
{
register yuser *u;
v3w.rows = htons(user->t_rows);
v3w.cols = htons(user->t_cols);
if(user == me)
{
v3w.code = V3_MYWIN;
for(u = connect_list; u; u = u->next)
if(u->remote.vmajor > 2)
send_oob(u->fd, &v3w, V3_WINCHLEN);
winch_exec();
}
else if(user->remote.vmajor > 2)
{
v3w.code = V3_YOURWIN;
send_oob(user->fd, &v3w, V3_WINCHLEN);
}
}
void
send_region()
{
register yuser *u;
v3w.code = V3_REGION;
v3w.rows = htons(me->rows);
v3w.cols = htons(me->cols);
for(u = connect_list; u; u = u->next)
if(u->remote.vmajor > 2)
send_oob(u->fd, &v3w, V3_WINCHLEN);
}
void
send_end_region()
{
register yuser *u;
v3w.code = V3_REGION;
v3w.rows = htons(0);
v3w.cols = htons(0);
for(u = connect_list; u; u = u->next)
if(u->remote.vmajor > 2)
send_oob(u->fd, &v3w, V3_WINCHLEN);
}
/* Send some output to a given user. Sends the output to all connected
* users if the given user is either "me" or NULL.
*/
void
send_users(user, buf, len)
yuser *user;
ychar *buf;
register int len;
{
register ychar *o, *b;
register yuser *u;
static ychar *o_buf = NULL;
static int o_len = 0;
/* data transparency */
if((len << 1) > o_len)
{
o_len = (len << 1) + 512;
o_buf = (ychar *)realloc_mem(o_buf, o_len);
}
for(b = buf, o = o_buf; len > 0; b++, len--)
{
*(o++) = *b;
if(*b == V3_OOB)
*(o++) = V3_OOB;
}
if(user && user != me)
{
if(user->fd > 0) /* just to be sure... */
{
if(user->remote.vmajor > 2)
(void)write(user->fd, o_buf, o - o_buf);
else
(void)write(user->fd, buf, b - buf);
}
}
else
for(u = connect_list; u; u = u->next)
if(u->remote.vmajor > 2)
(void)write(u->fd, o_buf, o - o_buf);
else
(void)write(u->fd, buf, b - buf);
}
/* Display user input. Emulate ANSI.
*/
void
show_input(user, buf, len)
yuser *user;
register ychar *buf;
register int len;
{
if(user->got_esc)
{
process_esc:
for(; len > 0; len--, buf++)
{
if(*buf >= '0' && *buf <= '9' && user->got_esc > 1)
{
user->av[user->ac] = (user->av[user->ac] * 10) + (*buf - '0');
continue;
}
switch(*buf)
{
case ';': /* arg separator */
if(user->ac < MAXARG-1)
user->av[++(user->ac)] = 0;
break;
case '[':
user->got_esc = 2;
break;
case '?':
if(user->got_esc == 2)
user->got_esc = 3;
else
user->got_esc = 0;
break;
case '7': /* save cursor */
user->sy = user->y;
user->sx = user->x;
user->got_esc = 0;
break;
case '8': /* restore cursor */
move_term(user, user->sy, user->sx);
user->got_esc = 0;
break;
case '@':
if(user->got_esc == 2) /* add char */
{
if(user->av[0] == 0)
add_char_term(user, 1);
else
add_char_term(user, user->av[0]);
}
user->got_esc = 0;
break;
case 'A': /* move up */
if(user->av[0] == 0)
move_term(user, user->y - 1, user->x);
else if(user->av[0] > user->y)
move_term(user, 0, user->x);
else
move_term(user, user->y - user->av[0], user->x);
user->got_esc = 0;
break;
case 'B': /* move down */
if(user->av[0] == 0)
move_term(user, user->y + 1, user->x);
else
move_term(user, user->y + user->av[0], user->x);
user->got_esc = 0;
break;
case 'C': /* move right */
if(user->av[0] == 0)
move_term(user, user->y, user->x + 1);
else
move_term(user, user->y, user->x + user->av[0]);
user->got_esc = 0;
break;
case 'D': /* move left */
if(user->av[0] == 0)
move_term(user, user->y, user->x - 1);
else if(user->av[0] > user->x)
move_term(user, user->y, 0);
else
move_term(user, user->y, user->x - user->av[0]);
user->got_esc = 0;
break;
case 'H': /* move */
if(user->av[0] > 0)
user->av[0]--;
if(user->av[1] > 0)
user->av[1]--;
move_term(user, user->av[0], user->av[1]);
user->got_esc = 0;
break;
case 'J': /* clear to end of screen */
clreos_term(user);
user->got_esc = 0;
break;
case 'K': /* clear to end of line */
clreol_term(user);
user->got_esc = 0;
break;
case 'L':
if(user->got_esc == 2) /* add line */
{
if(user->av[0] == 0)
add_line_term(user, 1);
else
add_line_term(user, user->av[0]);
}
user->got_esc = 0;
break;
case 'M':
if(user->got_esc == 2) /* delete line */
{
if(user->av[0] == 0)
del_line_term(user, 1);
else
del_line_term(user, user->av[0]);
}
else /* reverse scroll */
rev_scroll_term(user);
user->got_esc = 0;
break;
case 'P':
if(user->got_esc == 2) /* del char */
{
if(user->av[0] == 0)
del_char_term(user, 1);
else
del_char_term(user, user->av[0]);
}
user->got_esc = 0;
break;
case 'S': /* forward scroll */
scroll_term(user);
user->got_esc = 0;
break;
case 'r': /* set scroll region */
if(user->av[0] > 0)
user->av[0]--;
if(user->av[1] > 0)
user->av[1]--;
set_scroll_region(user, user->av[0], user->av[1]);
move_term(user, 0, 0);
user->got_esc = 0;
break;
default:
user->got_esc = 0;
}
if(user->got_esc == 0)
{
len--, buf++;
break;
}
}
}
for(; len > 0; len--, buf++)
{
if (is_printable(*buf))
{
if(user->x + 1 >= user->cols)
{
if(user->flags & FL_WRAP)
{
if(*buf == ' ')
newline_term(user);
else if(word_wrap(user) >= 0)
addch_term(user, *buf);
else
{
addch_term(user, *buf);
newline_term(user);
}
}
else
{
addch_term(user, *buf);
newline_term(user);
}
}
else
addch_term(user, *buf);
}
else if(*buf == user->RUB && !(user->flags & FL_RAW))
rub_term(user);
else if(*buf == user->WORD && !(user->flags & FL_RAW))
(void)word_term(user);
else if(*buf == user->KILL && !(user->flags & FL_RAW))
kill_term(user);
else
{
switch(*buf)
{
case 7: /* Bell */
putc(7, stderr);
break;
case 8: /* Backspace */
if(user->x > 0)
move_term(user, user->y, user->x - 1);
break;
case 9: /* Tab */
tab_term(user);
break;
case 10: /* Newline */
newline_term(user);
break;
case 13: /* Return */
if(user->flags & FL_RAW)
move_term(user, user->y, 0);
else
newline_term(user);
break;
case 27: /* Escape */
user->got_esc = 1;
user->ac = 0;
user->av[0] = 0;
user->av[1] = 0;
len--, buf++;
goto process_esc; /* ugly but _fast_ */
default:
if(*buf < ' ')
{
/* show a control char */
}
}
}
}
flush_term(user);
}
/* Process keyboard input.
*/
void
my_input(user, buf, len)
yuser *user;
register ychar *buf;
int len;
{
register ychar *c;
register int i;
/* If someone's waiting for input, give it to them! */
if(input_flag)
{
io_ptr = buf;
io_len = len;
return;
}
/* Process input normally */
while(len > 0)
{
/* check for a menu in process */
if(menu_ptr)
{
io_ptr = buf;
io_len = len;
update_menu();
buf = io_ptr;
len = io_len;
io_len = 0;
}
/* check for a running process */
if(running_process)
{
io_ptr = buf;
io_len = len;
update_exec();
buf = io_ptr;
len = io_len;
io_len = 0;
}
else
{
/* do normal input */
while(len > 0)
{
c = buf;
for(; len > 0; buf++, len--)
{
if(*buf == me->old_rub || *buf == 8 || *buf == 0x7f)
*buf = me->RUB;
else if(*buf == '\r')
*buf = '\n';
else if(*buf == 3) /* Ctrl-C */
bail(0);
else if(*buf == 27) /* Esc */
break;
else if(*buf == 12 || *buf == 18) /* ^L or ^R */
break;
}
if((i = buf - c) > 0)
{
if(user != NULL && user != me && !(def_flags & FL_ASIDE))
putc(7, stderr);
else
{
show_input(me, c, i);
send_users(user, c, i);
}
}
if(len > 0) /* we broke for a special char */
{
if(*buf == 27) /* ESC */
break;
if(*buf == 12 || *buf == 18) /* ^L or ^R */
{
redraw_all_terms();
buf++, len--;
}
}
}
}
/* start a menu if necessary */
if(len > 0)
{
buf++, len--;
show_main_menu();
if(len <= 0)
update_menu();
}
}
}
void
lock_flags(flags)
ylong flags;
{
register yuser *u;
me->flags = flags | FL_LOCKED;
/* send to connected users... */
v3f.code = V3_LOCKF;
v3f.flags = htonl(me->flags);
for(u = connect_list; u; u = u->next)
if(u->remote.vmajor > 2)
send_oob(u->fd, &v3f, V3_FLAGSLEN);
}
void
unlock_flags()
{
register yuser *u;
me->flags = def_flags;
/* send to connected users... */
v3f.code = V3_UNLOCKF;
v3f.flags = htonl(me->flags);
for(u = connect_list; u; u = u->next)
if(u->remote.vmajor > 2)
send_oob(u->fd, &v3f, V3_FLAGSLEN);
}
ytalk-3.0.3-8bit/cwin.c 100644 7527 230 16334 5473663545 13671 0 ustar espel i_math93 /* cwin.c -- curses interface */
/* NOTICE
*
* Copyright (c) 1990,1992,1993 Britt Yenne. All rights reserved.
*
* This software is provided AS-IS. The author gives no warranty,
* real or assumed, and takes no responsibility whatsoever for any
* use or misuse of this software, or any damage created by its use
* or misuse.
*
* This software may be freely copied and distributed provided that
* no part of this NOTICE is deleted or edited in any manner.
*
*/
/* Mail comments or questions to ytalk@austin.eds.com */
#include "header.h"
#include <curses.h>
#include <sys/signal.h>
#include "cwin.h"
typedef struct _ywin {
struct _ywin *next; /* next ywin in linked list */
yuser *user; /* user pointer */
WINDOW *win; /* window pointer */
int height, width; /* height and width in characters */
int row, col; /* row and column position on screen */
char *title; /* window title string */
} ywin;
static ywin *head; /* head of linked list */
/* ---- local functions ---- */
/* Take input from the user.
*/
static void
curses_input(fd)
int fd;
{
register int rc;
static ychar buf[MAXBUF];
if((rc = read(fd, buf, MAXBUF)) <= 0)
{
if(rc == 0)
bail(YTE_SUCCESS);
bail(YTE_ERROR);
}
my_input(me, buf, rc);
}
static ywin *
new_ywin(user, title)
yuser *user;
char *title;
{
register ywin *out;
register int len;
len = strlen(title);
out = (ywin *)get_mem(sizeof(ywin) + len + 1);
(void)memset(out, 0, sizeof(ywin));
out->user = user;
out->title = ((char *)out) + sizeof(ywin);
strcpy(out->title, title);
return out;
}
static void
make_win(w, height, width, row, col)
ywin *w;
int height, width, row, col;
{
if((w->win = newwin(height, width, row, col)) == NULL)
{
register ywin *w;
w = (ywin *)(me->term);
if(w->win != NULL)
show_error("make_win: newwin() failed");
bail(YTE_ERROR);
}
w->height = height;
w->width = width;
w->row = row;
w->col = col;
scrollok(w->win, FALSE);
wmove(w->win, 0, 0);
}
static void
draw_title(w)
ywin *w;
{
register int pad, x;
register char *t;
pad = (w->width - strlen(w->title)) / 2;
move(w->row - 1, w->col);
x = 0;
for(; x < pad - 2; x++)
addch('-');
if(pad >= 2)
{
addch('=');
addch(' ');
x += 2;
}
for(t = w->title; *t && x < w->width; x++, t++)
addch(*t);
if(pad >= 2)
{
addch(' ');
addch('=');
x += 2;
}
for(; x < w->width; x++)
addch('-');
}
/* Return number of lines per window, given "wins" windows.
*/
static int
win_size(wins)
int wins;
{
return (LINES - 1) / wins;
}
/* Break down and redraw all user windows.
*/
static void
curses_redraw()
{
register ywin *w;
register int row, wins, wsize;
/* kill old windows */
wins = 0;
for(w = head; w; w = w->next)
{
if(w->win)
{
delwin(w->win);
w->win = NULL;
}
wins++;
}
if((wsize = win_size(wins)) < 3)
{
end_term();
errno = 0;
show_error("curses_redraw: window size too small");
bail(YTE_ERROR);
}
/* make new windows */
clear();
refresh();
row = 0;
for(w = head; w; w = w->next)
{
if(w->next)
{
make_win(w, wsize-1, COLS, row+1, 0);
resize_win(w->user, wsize-1, COLS);
}
else
{
make_win(w, LINES-row-2, COLS, row+1, 0);
resize_win(w->user, LINES-row-2, COLS);
}
draw_title(w);
row += wsize;
refresh();
wrefresh(w->win);
}
}
/* Start curses and set all options.
*/
static void
curses_start()
{
LINES = COLS = 0; /* so resizes will work */
initscr();
noraw();
crmode();
noecho();
clear();
}
/* Restart curses... window size has changed.
*/
static void
curses_restart()
{
register ywin *w;
/* kill old windows */
for(w = head; w; w = w->next)
if(w->win)
{
delwin(w->win);
w->win = NULL;
}
/* restart curses */
endwin();
curses_start();
curses_redraw();
refresh();
/* some systems require we do this again */
#ifdef SIGWINCH
signal(SIGWINCH, curses_restart);
#endif
}
/* ---- global functions ---- */
void
init_curses()
{
curses_start();
refresh();
head = NULL;
add_fd(0, curses_input); /* set up user's input function */
/* set up SIGWINCH signal handler */
#ifdef SIGWINCH
signal(SIGWINCH, curses_restart);
#endif
}
void
end_curses()
{
move(LINES-1, 0);
clrtoeol();
refresh();
endwin();
}
/* Open a new window.
*/
int
open_curses(user, title)
yuser *user;
char *title;
{
register ywin *w;
register int wins;
/* count the open windows. We want to ensure at least
* three lines per window.
*/
wins = 0;
for(w = head; w; w = w->next)
wins++;
if(win_size(wins+1) < 3)
return -1;
/* add the new user */
if(head == NULL)
w = head = new_ywin(user, title);
else
for(w = head; w; w = w->next)
if(w->next == NULL)
{
w->next = new_ywin(user, title);
w = w->next;
break;
}
user->term = w;
/* redraw */
curses_redraw();
return 0;
}
/* Close a window.
*/
void
close_curses(user)
yuser *user;
{
register ywin *w, *p;
/* zap the old user */
w = (ywin *)(user->term);
if(w == head)
head = w->next;
else
{
for(p = head; p; p = p->next)
if(w == p->next)
{
p->next = w->next;
break;
}
if(p == NULL)
{
show_error("close_curses: user not found");
return;
}
}
delwin(w->win);
free(w);
curses_redraw();
}
void
addch_curses(user, c)
yuser *user;
register ychar c;
{
register ywin *w;
register int x, y;
w = (ywin *)(user->term);
getyx(w->win, y, x);
waddch(w->win, c);
if(x >= COLS-1)
wmove(w->win, y, x);
}
void
move_curses(user, y, x)
yuser *user;
register int y, x;
{
register ywin *w;
w = (ywin *)(user->term);
wmove(w->win, y, x);
}
void
clreol_curses(user)
register yuser *user;
{
register ywin *w;
w = (ywin *)(user->term);
wclrtoeol(w->win);
}
void
clreos_curses(user)
register yuser *user;
{
register ywin *w;
w = (ywin *)(user->term);
wclrtobot(w->win);
}
void
scroll_curses(user)
register yuser *user;
{
register ywin *w;
/* Curses has uses busted scrolling. In order to call scroll()
* effectively, scrollok() must be TRUE. However, if scrollok()
* is TRUE, then placing a character in the lower right corner
* will cause an auto-scroll. *sigh*
*/
w = (ywin *)(user->term);
scrollok(w->win, TRUE);
scroll(w->win);
scrollok(w->win, FALSE);
/* Some curses won't leave the cursor in the same place, and some
* curses programs won't erase the bottom line properly.
*/
wmove(w->win, user->t_rows - 1, 0);
wclrtoeol(w->win);
wmove(w->win, user->y, user->x);
}
void
flush_curses(user)
register yuser *user;
{
register ywin *w;
w = (ywin *)(user->term);
wrefresh(w->win);
}
/* Clear and redisplay.
*/
void
redisplay_curses()
{
register ywin *w;
clear();
refresh();
for(w = head; w; w = w->next)
{
redraw_term(w->user, 0);
draw_title(w);
refresh();
wrefresh(w->win);
}
}
/* Set raw mode.
*/
void
set_raw_curses()
{
raw();
}
/* Set cooked mode.
*/
void
set_cooked_curses()
{
noraw();
crmode();
noecho();
}
ytalk-3.0.3-8bit/cwin.h 100644 7527 230 2161 5436644653 13645 0 ustar espel i_math93 /* cwin.h -- curses interface (cwin.c) */
/* NOTICE
*
* Copyright (c) 1990,1992,1993 Britt Yenne. All rights reserved.
*
* This software is provided AS-IS. The author gives no warranty,
* real or assumed, and takes no responsibility whatsoever for any
* use or misuse of this software, or any damage created by its use
* or misuse.
*
* This software may be freely copied and distributed provided that
* no part of this NOTICE is deleted or edited in any manner.
*
*/
/* Mail comments or questions to ytalk@austin.eds.com */
extern void init_curses ();
extern void end_curses ();
extern int open_curses ( /* yuser, title */ );
extern void close_curses ( /* yuser */ );
extern void addch_curses ( /* yuser, char */ );
extern void move_curses ( /* yuser, y, x */ );
extern void clreol_curses ( /* yuser */ );
extern void clreos_curses ( /* yuser */ );
extern void scroll_curses ( /* yuser */ );
extern void flush_curses ( /* yuser */ );
extern void redisplay_curses();
extern void set_raw_curses();
extern void set_cooked_curses();
#ifndef getyx
# define getyx(w,y,x) y = w->_cury, x = w->_curx
#endif
/* EOF */
ytalk-3.0.3-8bit/exec.c 100644 7527 230 12401 6063707606 13635 0 ustar espel i_math93 /* exec.c -- run a command inside a window */
/* NOTICE
*
* Copyright (c) 1990,1992,1993 Britt Yenne. All rights reserved.
*
* This software is provided AS-IS. The author gives no warranty,
* real or assumed, and takes no responsibility whatsoever for any
* use or misuse of this software, or any damage created by its use
* or misuse.
*
* This software may be freely copied and distributed provided that
* no part of this NOTICE is deleted or edited in any manner.
*
*/
/* Mail comments or questions to ytalk@austin.eds.com */
#include "header.h"
#include <fcntl.h>
#include <signal.h>
#include <sys/wait.h>
#ifdef PTM
#include <stropts.h>
#include <sys/conf.h>
#endif
#ifdef USE_SGTTY
# include <sys/ioctl.h>
# ifdef hpux
# include <sys/bsdtty.h>
# include <sgtty.h>
# endif
#endif
int running_process = 0; /* flag: is process running? */
static int pid; /* currently executing process id */
static int pfd; /* currently executing process fd */
static int prows, pcols; /* saved rows, cols */
/* ---- local functions ---- */
#ifdef USE_SGTTY
static int
setsid()
{
register int fd;
if((fd = open("/dev/tty", O_RDWR)) >= 0)
{
ioctl(fd, TIOCNOTTY);
close(fd);
}
return fd;
}
#endif
#ifdef PTM
extern char *ptsname();
int needtopush=0;
#endif
static int
getpty(name)
char *name;
{
register int pty, tty;
char *pty_dev = "/dev/ptc", *tt;
extern char *ttyname();
#ifdef PTM
if ((pty=open("/dev/ptmx", O_RDWR)) >= 0)
{
grantpt(pty);
unlockpt(pty);
if ((tt=ptsname(pty)) != NULL)
{
strcpy(name, tt);
needtopush=1;
return pty;
}
}
#endif
/* first look for a SYSV-type pseudo device */
if((pty = open(pty_dev, O_RDWR)) >= 0)
{
if((tt = ttyname(pty)) != NULL)
{
strcpy(name, tt);
return pty;
}
close(pty);
}
/* scan Berkeley-style */
strcpy(name, "/dev/ptyp0");
while(access(name, 0) == 0)
{
if((pty = open(name, O_RDWR)) >= 0)
{
name[5] = 't';
if((tty = open(name, O_RDWR)) >= 0)
{
close(tty);
return pty;
}
name[5] = 'p';
close(pty);
}
/* get next pty name */
if(name[9] == 'f')
{
name[8]++;
name[9] = '0';
}
else if(name[9] == '9')
name[9] = 'a';
else
name[9]++;
}
errno = ENOENT;
return -1;
}
static void
exec_input(fd)
int fd;
{
register int rc;
static ychar buf[MAXBUF];
if((rc = read(fd, buf, MAXBUF)) <= 0)
{
kill_exec();
errno = 0;
show_error("command shell terminated");
return;
}
show_input(me, buf, rc);
send_users(me, buf, rc);
}
static void
calculate_size(rows, cols)
int *rows, *cols;
{
register yuser *u;
*rows = me->t_rows;
*cols = me->t_cols;
for(u = connect_list; u; u = u->next)
if(u->remote.vmajor > 2)
{
if(u->remote.my_rows > 1 && u->remote.my_rows < *rows)
*rows = u->remote.my_rows;
if(u->remote.my_cols > 1 && u->remote.my_cols < *cols)
*cols = u->remote.my_cols;
}
}
/* ---- global functions ---- */
/* Execute a command inside my window. If command is NULL, then execute
* a shell.
*/
void
execute(command)
char *command;
{
int fd;
char name[20], *shell;
if(me->flags & FL_LOCKED)
{
errno = 0;
show_error("alternate mode already running");
return;
}
if((fd = getpty(name)) < 0)
{
msg_term(me, "cannot get pseudo terminal");
return;
}
if((shell = (char *)getenv("SHELL")) == NULL)
shell = "/bin/sh";
calculate_size(&prows, &pcols);
if((pid = fork()) == 0)
{
close(fd);
close_all();
if(setsid() < 0)
exit(-1);
if((fd = open(name, O_RDWR)) < 0)
exit(-1);
#ifdef PTM
if (needtopush)
{
ioctl(fd, I_PUSH, "ptem");
ioctl(fd, I_PUSH, "ldterm");
}
#endif
dup2(fd, 0);
dup2(fd, 1);
dup2(fd, 2);
/* tricky bit -- ignore WINCH */
#ifdef SIGWINCH
signal(SIGWINCH, SIG_IGN);
#endif
/* set terminal characteristics */
set_terminal_flags(fd);
set_terminal_size(fd, prows, pcols);
#ifndef NeXT
putenv("TERM=vt100");
#endif
/* execute the command */
if(command)
execl(shell, shell, "-c", command, NULL);
else
execl(shell, shell, NULL);
perror("execl");
(void)exit(-1);
}
if(pid < 0)
{
show_error("fork() failed");
return;
}
set_win_region(me, prows, pcols);
sleep(1);
pfd = fd;
running_process = 1;
lock_flags(FL_RAW | FL_SCROLL);
set_raw_term();
add_fd(fd, exec_input);
}
/* Send input to the command shell.
*/
void
update_exec()
{
(void)write(pfd, io_ptr, io_len);
io_len = 0;
}
/* Kill the command shell.
*/
void
kill_exec()
{
if(!running_process)
return;
remove_fd(pfd);
close(pfd);
running_process = 0;
unlock_flags();
set_cooked_term();
end_win_region(me);
}
/* Send a SIGWINCH to the process.
*/
void
winch_exec()
{
int rows, cols;
if(!running_process)
return;
/* if the winch has no effect, return now */
calculate_size(&rows, &cols);
if(rows == prows && cols == pcols)
{
if(prows != me->rows || pcols != me->cols)
set_win_region(me, prows, pcols);
return;
}
/* oh well -- redo everything */
prows = rows;
pcols = cols;
set_terminal_size(pfd, prows, pcols);
set_win_region(me, prows, pcols);
#ifdef SIGWINCH
kill(pid, SIGWINCH);
#endif
}
ytalk-3.0.3-8bit/fd.c 100644 7527 230 13633 5473663543 13317 0 ustar espel i_math93 /* fd.c */
/* NOTICE
*
* Copyright (c) 1990,1992,1993 Britt Yenne. All rights reserved.
*
* This software is provided AS-IS. The author gives no warranty,
* real or assumed, and takes no responsibility whatsoever for any
* use or misuse of this software, or any damage created by its use
* or misuse.
*
* This software may be freely copied and distributed provided that
* no part of this NOTICE is deleted or edited in any manner.
*
*/
/* Mail comments or questions to ytalk@austin.eds.com */
#include "header.h"
#include "menu.h"
#include <sys/time.h>
#include <signal.h>
#ifdef _AIX
# include <sys/select.h>
#endif
static fd_set fdset; /* descriptors to select on */
static fd_set fdtmp; /* descriptors to select on (input_loop) */
static fd_set sel; /* currently readable descriptors */
static int high_fd = 0; /* highest fd so far */
int input_flag = 0; /* flag: waiting for user input */
int user_winch = 0; /* flag: user window/status changed */
struct fd_func {
void (*func)(); /* user function */
};
static struct fd_func tag[MAX_FILES]; /* one function per file descriptor */
/* Initialize fdset data.
*/
void
init_fd()
{
FD_ZERO(&fdset);
}
/* Add a file descriptor to the current checklist. The supplied
* function will be called whenever the file descriptor has input
* waiting.
*/
void
add_fd(fd, user_func)
int fd;
void (*user_func)();
{
if(fd < 0 || fd >= MAX_FILES)
{
show_error("add_fd: descriptor out of range");
return;
}
FD_SET(fd, &fdset);
tag[fd].func = user_func;
if(fd >= high_fd)
high_fd = fd + 1;
}
/* Remove a file descriptor from the checklist.
*/
void
remove_fd(fd)
int fd;
{
if(fd < 0 || fd >= MAX_FILES)
{
show_error("remove_fd: descriptor out of range");
return;
}
FD_CLR(fd, &fdset);
FD_CLR(fd, &fdtmp);
FD_CLR(fd, &sel);
}
/* Read an entire length of data.
* Returns 0 on success, -1 on error.
*/
int
full_read(fd, buf, len)
int fd;
register char *buf;
register int len;
{
register int rc;
while(len > 0)
{
if((rc = read(fd, buf, len)) <= 0)
return -1;
buf += rc;
len -= rc;
}
return 0;
}
/* -- MAIN LOOPS -- */
static ylong lastping, curtime;
void
main_loop()
{
register int fd, rc;
struct timeval tv;
#ifndef Y_USE_SIGHOLD
int mask, old_mask;
#endif
/* Some signals need to be blocked while doing internal
* processing, else some craziness might occur.
*/
#ifndef Y_USE_SIGHOLD
mask = 0;
# ifdef SIGWINCH
mask |= sigmask(SIGWINCH);
# endif
#endif
#if defined(SIGCHLD)
signal(SIGCHLD, SIG_IGN);
#else
# if defined(SIGCLD)
signal(SIGCLD, SIG_IGN);
# endif
#endif
/* For housecleaning to occur every CLEAN_INTERVAL seconds, we make
* our own little timer system. SIGALRM is nice; in fact it's so
* useful that we'll be using it in other parts of YTalk. Since
* we therefore can't use it here, we affect the timer manually.
*/
house_clean();
curtime = lastping = (ylong)time(NULL);
for(;;)
{
/* check if we're done */
if(connect_list == NULL
&& wait_list == NULL
&& menu_ptr == NULL
&& running_process == 0)
bail(0);
/* select */
sel = fdset;
if(curtime > lastping + CLEAN_INTERVAL)
tv.tv_sec = 0;
else
tv.tv_sec = (lastping + CLEAN_INTERVAL) - curtime;
tv.tv_usec = 0;
if((rc = select(high_fd, &sel, 0, 0, &tv)) < 0)
if(errno != EINTR)
show_error("main_loop: select failed");
/* block signals while doing internal processing */
#ifdef Y_USE_SIGHOLD
# ifdef SIGWINCH
sighold(SIGWINCH);
# endif
#else
old_mask = sigblock(mask);
#endif
/* process file descriptors with input waiting */
if(rc > 0)
for(fd = 0; fd < high_fd; fd++)
if(FD_ISSET(fd, &sel))
{
errno = 0;
tag[fd].func(fd);
if(--rc <= 0)
break;
}
/* check timer */
curtime = (ylong)time(NULL);
if(curtime - lastping >= CLEAN_INTERVAL)
{
house_clean();
lastping = (ylong)time(NULL);
}
/* re-allow signals */
#ifdef Y_USE_SIGHOLD
# ifdef SIGWINCH
sigrelse(SIGWINCH);
# endif
#else
sigsetmask(old_mask);
#endif
if(user_winch)
{
/* This is a cute hack that updates a user menu
* dynamically as information changes. So I had
* some free time. there.
*/
user_winch = 0;
update_user_menu();
}
}
}
/* Input loop. This loop keeps everything except user input going until
* input is received from <me>. This is necessary for answering pressing
* questions without needing to add a getch_term() function to the terminal
* definition library. Hack? maybe. Fun, tho.
*/
void
input_loop()
{
register int fd, rc;
struct timeval tv;
static int left_loop;
left_loop = 0;
fdtmp = fdset;
while(io_len <= 0)
{
/* select */
sel = fdtmp;
if(curtime > lastping + CLEAN_INTERVAL)
tv.tv_sec = 0;
else
tv.tv_sec = (lastping + CLEAN_INTERVAL) - curtime;
tv.tv_usec = 0;
if((rc = select(high_fd, &sel, 0, 0, &tv)) < 0)
if(errno != EINTR)
show_error("input_loop: select failed");
/* process file descriptors with input waiting */
if(rc > 0)
for(fd = 0; fd < high_fd; fd++)
if(FD_ISSET(fd, &sel))
{
/* Here the hack begins. Any function that takes user
* input should clear "input_flag" and return. This
* tells us to ignore this function for now. Any
* function which receives input from <me> should leave
* my input in io_ptr/io_len.
*/
errno = 0;
input_flag = 1;
tag[fd].func(fd);
if(left_loop) /* recursive input_loop()s. argh! */
return; /* let my parent function re-call me */
if(input_flag == 0)
{
/* don't check this descriptor anymore */
FD_CLR(fd, &fdtmp);
}
if(--rc <= 0)
break;
}
/* check timer */
curtime = (ylong)time(NULL);
if(curtime - lastping >= CLEAN_INTERVAL)
{
input_flag = 1;
house_clean();
lastping = (ylong)time(NULL);
}
}
input_flag = 0;
left_loop = 1;
}
ytalk-3.0.3-8bit/header.h 100644 7527 230 31223 6241652555 14151 0 ustar espel i_math93 /* header.h */
/* NOTICE
*
* Copyright (c) 1990,1992,1993 Britt Yenne. All rights reserved.
*
* This software is provided AS-IS. The author gives no warranty,
* real or assumed, and takes no responsibility whatsoever for any
* use or misuse of this software, or any damage created by its use
* or misuse.
*
* This software may be freely copied and distributed provided that
* no part of this NOTICE is deleted or edited in any manner.
*
*/
/* Mail comments or questions to ytalk@austin.eds.com */
#include <sys/types.h>
#ifdef LINUX
# include <linux/param.h>
#else
# include <sys/param.h>
#endif
#include <sys/socket.h>
#include <netinet/in.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#ifdef USE_X11
# include <X11/X.h>
#endif
#define VMAJOR 3 /* major version number */
#define VMINOR 0 /* minor version number */
#define VPATCH 3 /* patch level */
#ifdef EIGHT_BIT_CLEAN
#define is_printable(x) ( (((x) >= ' ' && (x) <= '~') || \
(unsigned char)(x) >= 0xa0) \
&& (x) != RUBDEF )
#else
#define is_printable(x) ( (x) >= ' ' && (x) <= '~' )
#endif
/* ---- YTalk protocols ---- */
/* These protocol numbers are a MAJOR HACK designed to get around the
* fact that old versions of ytalk didn't send any version information
* during handshaking. Nor did they bzero() the unused portions of the
* handshaking structures. Argh! These two protocol numbers were very
* carefully picked... do not add any others and expect them to work.
* Instead, use the "vmajor" and "vminor" fields of the y_parm structure.
*/
#define YTP_OLD 20 /* YTalk versions before 3.0 */
#define YTP_NEW 27 /* YTalk versions 3.0 and up */
/* ---- types ---- */
typedef void * yaddr; /* any 32-bit address */
typedef yaddr yterm; /* terminal cookie */
typedef u_char ychar; /* we use unsigned chars */
#ifdef Y64BIT
typedef u_int ylong; /* unsigned 32-bit */
#else
typedef u_long ylong; /* unsigned 32-bit */
#endif
typedef struct {
u_char w_rows, w_cols; /* window size FOR PROTOCOL YTP_OLD */
char protocol; /* ytalk protocol -- see above */
char pad1; /* zeroed out */
short vmajor, vminor; /* version numbers */
u_short rows, cols; /* his window size over there */
u_short my_rows, my_cols; /* my window size over there */
ylong pid; /* my process id */
char pad[44]; /* zeroed out */
} y_parm;
#define MAXARG 4 /* max ESC sequence arg count */
typedef struct _yuser {
struct _yuser *next; /* next user in group lists */
struct _yuser *unext; /* next user in full user list */
int fd; /* file descriptor */
int output_fd; /* non-zero if output is going to a file */
ylong flags; /* active FL_* flags below */
ychar edit[4]; /* edit characters */
u_short t_rows, t_cols; /* his rows and cols on window over here */
u_short rows, cols; /* his active region rows and cols over here */
y_parm remote; /* remote parms */
ychar **scr; /* screen data */
char bump; /* set if at far right */
ychar old_rub; /* my actual rub character */
char got_esc; /* received an ESC */
ychar key; /* this user's ident letter for menus */
int y, x; /* current cursor position */
int sy, sx; /* saved cursor position */
int sc_top, sc_bot; /* scrolling region */
int region_set; /* set if using a screen region */
char *full_name; /* full name (up to 50 chars) */
char *user_name; /* user name */
char *host_name; /* host name */
char *tty_name; /* tty name */
ylong host_addr; /* host inet address */
int daemon; /* daemon type to use */
ylong l_id, r_id; /* local and remote talkd invite list index */
ylong d_id; /* talk daemon process id -- see socket.c */
ylong last_invite; /* timestamp of last invitation sent */
struct sockaddr_in sock; /* communication socket */
struct sockaddr_in orig_sock; /* original socket -- another sick hack */
u_int av[MAXARG]; /* ESC sequence arguments */
u_int ac; /* ESC sequence arg count */
/* out-of-band data */
int dbuf_size; /* current buffer size */
ychar *dbuf, *dptr; /* buffer base and current pointer */
int drain; /* remaining bytes to drain */
void (*dfunc)(); /* function to call with drained data */
int got_oob; /* got OOB flag */
/* anything below this is available for the terminal interface */
yterm term; /* terminal cookie */
int ty, tx; /* terminal's idea of Y,X (optional) */
#ifdef USE_X11
Window win; /* user's window */
#endif
} yuser;
#define FL_RAW 0x00000001L /* raw input enabled */
#define FL_SCROLL 0x00000002L /* scrolling enabled */
#define FL_WRAP 0x00000004L /* word-wrap enabled */
#define FL_IMPORT 0x00000008L /* auto-import enabled */
#define FL_INVITE 0x00000010L /* auto-invite enabled */
#define FL_RING 0x00000020L /* auto-rering enabled */
#define FL_XWIN 0x00000040L /* X Windows enabled (startup opt) */
#define FL_ASIDE 0x00000080L /* multiple window input/asides */
#define FL_CAPS 0x00000100L /* want caps as answers */
#define FL_NOAUTO 0x00000200L /* no auto-invite port */
#define FL_LOCKED 0x40000000L /* flags locked by other end */
/* ---- defines and short-cuts ---- */
#ifdef NOFILE
# define MAX_FILES NOFILE /* max open file descriptors */
#else
# define MAX_FILES 256 /* better to be too high than too low */
#endif
#define CLEAN_INTERVAL 16 /* seconds between calls to house_clean() */
#define MAXBUF 4096 /* buffer size for I/O operations */
#define MAXERR 132 /* error text buffer size */
#define MAXTEXT 50 /* text entry buffer */
#define RUB edit[0]
#define KILL edit[1]
#define WORD edit[2]
#define CLR edit[3]
#define RUBDEF 0xfe
/* ---- Ytalk version 3.* out-of-band data ---- */
/* see comm.c for a description of Ytalk 3.* OOB protocol */
#define V3_OOB 0xfd /* out-of-band marker -- see comm.c */
#define V3_MAXPACK 0xfc /* max OOB packet size -- see comm.c */
#define V3_NAMELEN 16 /* max username length */
#define V3_HOSTLEN 64 /* max hostname length */
typedef struct {
ychar code; /* V3_EXPORT, V3_IMPORT, or V3_ACCEPT */
char filler[3];
ylong host_addr; /* host address */
ylong pid; /* process id */
char name[V3_NAMELEN]; /* user name */
char host[V3_HOSTLEN]; /* host name */
} v3_pack;
#define V3_PACKLEN sizeof(v3_pack)
#define V3_EXPORT 101 /* export a user */
#define V3_IMPORT 102 /* import a user */
#define V3_ACCEPT 103 /* accept a connection from a user */
typedef struct {
ychar code; /* V3_LOCKF or V3_UNLOCKF */
char filler[3];
ylong flags; /* flags */
} v3_flags;
#define V3_FLAGSLEN sizeof(v3_flags)
#define V3_LOCKF 111 /* lock my flags */
#define V3_UNLOCKF 112 /* unlock my flags */
typedef struct {
ychar code; /* V3_YOURWIN, V3_MYWIN, or V3_REGION */
char filler[3];
u_short rows, cols; /* window size */
} v3_winch;
#define V3_WINCHLEN sizeof(v3_winch)
#define V3_YOURWIN 121 /* your window size changed over here */
#define V3_MYWIN 122 /* my window size changed over here */
#define V3_REGION 123 /* my window region changed over here */
/* ---- Ytalk version 2.* out-of-band data ---- */
#define V2_NAMELEN 12
#define V2_HOSTLEN 64
typedef struct {
ychar code; /* one of the V2_?? codes below */
char filler;
char name[V2_NAMELEN]; /* user name */
char host[V2_HOSTLEN]; /* user host */
} v2_pack;
#define V2_PACKLEN sizeof(v2_pack)
#define V2_EXPORT 130 /* export a user */
#define V2_IMPORT 131 /* import a user */
#define V2_ACCEPT 132 /* accept a connection from a user */
#define V2_AUTO 133 /* accept auto invitation */
/* ---- exit codes ---- */
#define YTE_SUCCESS 0 /* successful completion */
#define YTE_INIT 1 /* initialization error */
#define YTE_NO_MEM 2 /* out of memory */
#define YTE_SIGNAL 3 /* fatal signal received */
#define YTE_ERROR 4 /* unrecoverable error */
/* ---- global variables ---- */
extern char *sys_errlist[]; /* system errors */
extern yuser *me; /* just lil' ol' me */
extern yuser *user_list; /* full list of invited/connected users */
extern yuser *connect_list; /* list of connected users */
extern yuser *wait_list; /* list of invited users */
extern yuser *fd_to_user[MAX_FILES]; /* convert file descriptors to users */
extern yuser *key_to_user[128]; /* convert menu ident chars to users */
extern char errstr[MAXERR]; /* temporary string for errors */
extern ylong def_flags; /* default FL_* flags */
extern int user_winch; /* user window/status changed flag */
extern ychar *io_ptr; /* user input pointer */
extern int io_len; /* user input count */
extern int running_process; /* flag: is process running? */
/* ---- some machine compatibility definitions ---- */
#if defined(SYSV)
# define Y_USE_SIGHOLD 1
#endif
extern int errno;
/* ---- global functions ---- */
extern void bail ( /* int */ ); /* main.c */
extern yaddr get_mem ( /* int */ ); /* main.c */
extern char *str_copy ( /* string */ ); /* main.c */
extern yaddr realloc_mem ( /* pointer, int */ ); /* main.c */
extern void show_error ( /* str */ ); /* main.c */
extern void init_term (); /* term.c */
extern void set_terminal_size ( /* fd, rows, cols */ ); /* term.c */
extern void set_terminal_flags ( /* fd */ ); /* term.c */
extern int what_term (); /* term.c */
extern void end_term (); /* term.c */
extern int open_term ( /* yuser, title */ ); /* term.c */
extern void close_term ( /* yuser */ ); /* term.c */
extern void addch_term ( /* yuser, ch */ ); /* term.c */
extern void move_term ( /* yuser, y, x */ ); /* term.c */
extern void clreol_term ( /* yuser */ ); /* term.c */
extern void clreos_term ( /* yuser */ ); /* term.c */
extern void scroll_term ( /* yuser */ ); /* term.c */
extern void rev_scroll_term ( /* yuser */ ); /* term.c */
extern void flush_term ( /* yuser */ ); /* term.c */
extern void rub_term ( /* yuser */ ); /* term.c */
extern int word_term ( /* yuser */ ); /* term.c */
extern void kill_term ( /* yuser */ ); /* term.c */
extern void tab_term ( /* yuser */ ); /* term.c */
extern void newline_term ( /* yuser */ ); /* term.c */
extern void add_line_term ( /* yuser, num */ ); /* term.c */
extern void del_line_term ( /* yuser, num */ ); /* term.c */
extern void add_char_term ( /* yuser, num */ ); /* term.c */
extern void del_char_term ( /* yuser, num */ ); /* term.c */
extern void redraw_term ( /* yuser, start_row */ ); /* term.c */
extern void resize_win ( /* yuser, h, w */ ); /* term.c */
extern void set_win_region ( /* yuser, h, w */ ); /* term.c */
extern void end_win_region ( /* yuser */ ); /* term.c */
extern void set_scroll_region( /* yuser, top, bottom */ ); /* term.c */
extern void msg_term ( /* yuser, str */ ); /* term.c */
extern void spew_term ( /* yuser, fd, rows, cols */ ); /* term.c */
extern int center ( /* width, n */ ); /* term.c */
extern void redraw_all_terms(); /* term.c */
extern void set_raw_term (); /* term.c */
extern void set_cooked_term (); /* term.c */
extern int term_does_asides(); /* term.c */
extern void init_user (); /* user.c */
extern yuser *new_user ( /* name, host, tty */ ); /* user.c */
extern void free_user ( /* yuser */ ); /* user.c */
extern yuser *find_user ( /* name, host_addr, pid */ ); /* user.c */
extern void init_fd (); /* fd.c */
extern void add_fd ( /* fd, func */ ); /* fd.c */
extern void remove_fd ( /* fd */ ); /* fd.c */
extern int full_read ( /* fd, buf, len */ ); /* fd.c */
extern void main_loop (); /* fd.c */
extern void input_loop (); /* fd.c */
extern void invite ( /* username, announce */ ); /* comm.c */
extern void house_clean (); /* comm.c */
extern void send_winch ( /* yuser */ ); /* comm.c */
extern void send_region (); /* comm.c */
extern void send_end_region (); /* comm.c */
extern void send_users ( /* buf, len */ ); /* comm.c */
extern void show_input ( /* user, buf, len */ ); /* comm.c */
extern void my_input ( /* buf, len */ ); /* comm.c */
extern void lock_flags ( /* flags */ ); /* comm.c */
extern void unlock_flags (); /* comm.c */
extern void init_socket (); /* socket.c */
extern void close_all (); /* socket.c */
extern int send_dgram ( /* user, type */ ); /* socket.c */
extern int send_auto ( /* type */ ); /* socket.c */
extern void kill_auto (); /* socket.c */
extern int newsock ( /* yuser */ ); /* socket.c */
extern int connect_to ( /* yuser */ ); /* socket.c */
extern ylong get_host_addr ( /* hostname */ ); /* socket.c */
extern char *host_name ( /* addr */ ); /* socket.c */
extern void readdress_host ( /* from, to, on */ ); /* socket.c */
extern void read_ytalkrc (); /* rc.c */
extern void execute ( /* command */ ); /* exec.c */
extern void update_exec (); /* exec.c */
extern void kill_exec (); /* exec.c */
extern void winch_exec (); /* exec.c */
/* EOF */
ytalk-3.0.3-8bit/main.c 100644 7527 230 10667 6241651534 13645 0 ustar espel i_math93 /* main.c */
/* NOTICE
*
* Copyright (c) 1990,1992,1993 Britt Yenne. All rights reserved.
*
* This software is provided AS-IS. The author gives no warranty,
* real or assumed, and takes no responsibility whatsoever for any
* use or misuse of this software, or any damage created by its use
* or misuse.
*
* This software may be freely copied and distributed provided that
* no part of this NOTICE is deleted or edited in any manner.
*
*/
/* Mail comments or questions to ytalk@austin.eds.com */
#include "header.h"
#include <signal.h>
#include "menu.h"
char errstr[132]; /* temporary string for errors */
/* Clean up and exit.
*/
void
bail(n)
int n;
{
end_term();
kill_auto();
(void)exit(n);
}
/* Display an error.
*/
void
show_error(str)
register char *str;
{
register char *syserr;
static int in_error = 0;
if(errno == 0)
syserr = "(no system error)";
else
syserr = sys_errlist[errno];
putc(7, stderr);
if(in_error == 0 && what_term() != 0)
{
in_error = 1;
if(show_error_menu(str, syserr) < 0)
{
show_error("show_error: show_error_menu() failed");
show_error(str);
}
else
update_menu();
in_error = 0;
}
else
{
fprintf(stderr, "%s: %s\n", str, syserr);
sleep(2);
}
}
/* Allocate memory.
*/
yaddr
get_mem(n)
int n;
{
register yaddr out;
if((out = (yaddr)malloc(n)) == NULL)
{
show_error("malloc() failed");
bail(YTE_NO_MEM);
}
return out;
}
/* Copy a string.
*/
char *
str_copy(str)
register char *str;
{
register char *out;
register int len;
if(str == NULL)
return NULL;
len = strlen(str) + 1;
out = get_mem(len);
(void)memcpy(out, str, len);
return out;
}
/* Reallocate memory.
*/
yaddr
realloc_mem(p, n)
char *p;
int n;
{
register yaddr out;
if(p == NULL)
return get_mem(n);
if((out = (yaddr)realloc(p, n)) == NULL)
{
show_error("realloc() failed");
bail(YTE_NO_MEM);
}
return out;
}
/* Process signals.
*/
static void
got_sig(n)
int n;
{
if(n == SIGINT)
bail(0);
bail(YTE_SIGNAL);
}
/* MAIN */
int
main(argc, argv)
int argc;
char **argv;
{
int xflg = 0, sflg = 0, yflg = 0, iflg = 0;
char *prog;
/* check for a 64-bit mis-compile */
if(sizeof(ylong) != 4)
{
if(sizeof(ylong) > 4)
{
fprintf(stderr,
"You should have compiled ytalk with the -DY64BIT option.\n");
}
else
{
#ifdef Y64BIT
fprintf(stderr,
"You should NOT have compiled ytalk with the -DY64BIT option.\n");
#else
fprintf(stderr,
"Your machine doesn't support 32-bit longs. Please mail\n");
fprintf(stderr,
"ytalk@austin.eds.com your machine type and OS version.\n");
(void)exit(YTE_INIT);
#endif
}
fprintf(stderr,
"See the README file on how to update the appropriate\n");
fprintf(stderr,
"makefile, then type 'make clean', 'make'.\n");
(void)exit(YTE_INIT);
}
/* search for options */
prog = *argv;
argv++, argc--;
while(argc > 0 && **argv == '-')
{
if(strcmp(*argv, "-x") == 0
|| strcmp(*argv, "-nw") == 0)
{
xflg++; /* disable X from the command line */
argv++, argc--;
}
else if(strcmp(*argv, "-Y") == 0)
{
yflg++;
argv++, argc--;
}
else if(strcmp(*argv, "-i") == 0)
{
iflg++;
argv++, argc--;
}
else if(strcmp(*argv, "-s") == 0)
{
sflg++; /* immediately start a shell */
argv++, argc--;
}
else
argc = 0; /* force a Usage error */
}
/* check for users */
if(argc <= 0)
{
fprintf(stderr,
"Usage: %s [options] user[@host][#tty]...\n\
Options: -i -- no auto-invite port\n\
-x -- do not use the X interface\n\
-Y -- require caps on all y/n answers\n\
-s -- start a shell\n", prog);
(void)exit(YTE_INIT);
}
/* set up signals */
signal(SIGINT, got_sig);
signal(SIGHUP, got_sig);
signal(SIGQUIT, got_sig);
signal(SIGABRT, got_sig);
/* set default options */
def_flags = FL_XWIN;
/* go for it! */
errno = 0;
init_fd();
init_user();
read_ytalkrc();
if(xflg)
def_flags &= ~FL_XWIN;
if(yflg)
def_flags |= FL_CAPS;
if(iflg)
def_flags |= FL_NOAUTO;
init_term();
init_socket();
for(; argc > 0; argc--, argv++)
invite(*argv, 1);
if(sflg)
execute(NULL);
else
msg_term(me, "Waiting for connection...");
main_loop();
bail(YTE_SUCCESS);
return 0; /* make lint happy */
}
ytalk-3.0.3-8bit/menu.c 100644 7527 230 45232 6033543072 13655 0 ustar espel i_math93 /* menu.c */
/* NOTICE
*
* Copyright (c) 1990,1992,1993 Britt Yenne. All rights reserved.
*
* This software is provided AS-IS. The author gives no warranty,
* real or assumed, and takes no responsibility whatsoever for any
* use or misuse of this software, or any damage created by its use
* or misuse.
*
* This software may be freely copied and distributed provided that
* no part of this NOTICE is deleted or edited in any manner.
*
*/
/* Mail comments or questions to ytalk@austin.eds.com */
#include "header.h"
#include <fcntl.h>
#include "menu.h"
/* This particular file was written real early one night (morning?)
* while trying to stay awake long enough to do laundry. I hereby take
* extra-special pains to absolve myself of any and all responsibility
* for this source.
*/
static void main_menu_sel();
menu_item *menu_ptr = NULL; /* current menu in processing */
static int menu_len; /* number of items in current menu */
static int menu_long; /* longest item of current menu */
static int menu_line; /* current line number of menu */
static int text_pos = -1; /* text offset if non-negative */
static int text_ypos = -1, text_xpos = -1; /* text coord if non-negative */
extern void raw_term(); /* our raw interface to the terminal */
/* some menus... */
static menu_item main_menu[] = {
{ "Main Menu", NULL, ' ' },
{ "", NULL, ' ' },
{ "add a user", main_menu_sel, 'a' },
{ "delete a user", main_menu_sel, 'd' },
{ "kill all unconnected", main_menu_sel, 'k' },
{ "options", main_menu_sel, 'o' },
{ "shell", main_menu_sel, 's' },
{ "user list", main_menu_sel, 'u' },
{ "output user to file", main_menu_sel, 'w' },
{ "quit", main_menu_sel, 'q' },
{ "", NULL, '\0'} /* MUST BE LAST */
};
#define MAXUMENU 52
static menu_item user_menu[MAXUMENU]; /* this one changes each time */
static menu_item option_menu[20]; /* options menu buffer */
static menu_item yes_no_menu[1]; /* yes/no entry menu */
static menu_item mesg_menu[1]; /* message menu */
static char text_str[MAXTEXT+1]; /* string entry buffer */
static menu_item text_menu[2]; /* string entry menu */
static char user_buf[MAXUMENU][80]; /* user list buffers */
/* major hack below... [maniacal laughter] */
static int got_error = 0;
static char err_str[8][MAXERR];
static menu_item error_menu[] = {
{ "Ytalk Error", NULL, ' ' },
{ "", NULL, ' ' },
{ NULL, show_error, ' ' },
{ NULL, show_error, ' ' },
{ "", NULL, ' ' },
{ NULL, show_error, ' ' },
{ NULL, show_error, ' ' },
{ "", NULL, ' ' },
{ NULL, show_error, ' ' },
{ NULL, show_error, ' ' },
{ "", NULL, ' ' },
{ NULL, show_error, ' ' },
{ NULL, show_error, ' ' },
{ "", NULL, '\0'} /* MUST BE LAST */
};
/* ---- local functions ---- */
static yuser *output_user = NULL;
static void
do_output(filename)
char *filename;
{
int fd;
if(output_user == NULL)
return;
if((fd = open(filename, O_RDWR | O_TRUNC | O_CREAT, 0600)) < 0)
{
show_error(filename);
return;
}
output_user->output_fd = fd;
spew_term(output_user, fd, output_user->rows, output_user->cols);
output_user = NULL;
}
static void
do_output_user(user)
yuser *user;
{
/* if he has an open descriptor, close it */
if(user->output_fd > 0)
{
close(user->output_fd);
user->output_fd = 0;
if(show_mesg("Output Terminated", NULL) >= 0)
update_menu();
return;
}
/* else open one */
output_user = user;
if(show_text("Output filename?", do_output) >= 0)
update_menu();
else
output_user = NULL;
}
static void
do_invite(name)
char *name;
{
invite(name, 1);
}
static void
kill_all_unconnected()
{
while(wait_list) free_user(wait_list);
}
static void
main_menu_sel(key)
ychar key;
{
switch(key)
{
case 'a': /* add a user */
if(show_text("Add Which User?", do_invite) >= 0)
update_menu();
break;
case 'd': /* delete a user */
if(show_user_menu("Delete Which User?", free_user) >= 0)
update_menu();
break;
case 'k': /* kill all unconnected users */
kill_all_unconnected();
kill_menu();
break;
case 'o': /* show options */
if(show_option_menu() >= 0)
update_menu();
break;
case 's': /* invoke a shell */
kill_menu();
execute(NULL);
break;
case 'u': /* show a user list */
if(show_user_list() >= 0)
update_menu();
break;
case 'w': /* output user to file */
if(show_user_menu("Output Which User?", do_output_user) >= 0)
update_menu();
break;
case 'q': /* quit */
bail(0);
}
}
static void
option_menu_sel(key)
ychar key;
{
register yuser *u;
ylong old_flags;
old_flags = def_flags;
switch(key)
{
case 'a': /* toggle asides */
def_flags ^= FL_ASIDE;
break;
case 's': /* toggle scrolling */
def_flags ^= FL_SCROLL;
break;
case 'w': /* toggle word wrap */
def_flags ^= FL_WRAP;
break;
case 'i': /* toggle automatic imports */
def_flags ^= FL_IMPORT;
break;
case 'v': /* toggle automatic invitations */
def_flags ^= FL_INVITE;
break;
case 'r': /* toggle automatic re-rings */
def_flags ^= FL_RING;
break;
}
if(old_flags != def_flags)
{
for(u = user_list; u != NULL; u = u->unext)
if(!(u->flags & FL_LOCKED))
u->flags = def_flags;
}
if(show_option_menu() >= 0)
update_menu();
else
kill_menu();
}
static void
user_menu_sel(key)
ychar key;
{
register int i;
register yuser *u;
/* Remember... the user list could have changed between the time
* I created the user menu and the time I just now selected one
* of the users from it.
*/
for(i = 0; i < menu_len; i++)
if(user_menu[i].key == key)
{
for(u = user_list; u; u = u->unext)
if(u->key == key
&& strcmp(u->full_name, user_menu[i].item) == 0)
{
user_menu[0].func(u);
break;
}
break;
}
if(menu_ptr == user_menu)
kill_menu();
}
#define MENU_EXTRA 7 /* number of extra characters per menu screen item */
static void
generate_text_length()
{
menu_long = me->t_cols - MENU_EXTRA - 2;
if(menu_long < 5 || menu_long > MAXTEXT)
menu_long = MAXTEXT;
}
static void
generate_yes_no_length()
{
menu_long = strlen(yes_no_menu[0].item) - 2;
}
static void
pad_str(from, len, to)
char *from, *to;
int len;
{
for(; len > 0 && *from; len--, from++)
*(to++) = *from;
for(; len > 0; len--)
*(to++) = ' ';
*to = '\0';
}
/* ---- global functions ---- */
/* End any menu processing.
*/
void
kill_menu()
{
register int i;
if(menu_ptr != NULL)
{
menu_ptr = NULL;
redraw_term(me, 0);
flush_term(me);
text_pos = -1;
text_ypos = -1;
text_xpos = -1;
}
if(got_error)
{
got_error = 0;
for(i = 0; error_menu[i].key != '\0'; i++)
if(error_menu[i].func != NULL)
error_menu[i].item = NULL;
}
}
/* Update menu information.
*/
void
update_menu()
{
register ychar *c;
register char *d;
register int j, i, y, x;
static ychar *buf = NULL;
static int buflen = 0;
if(menu_ptr == NULL)
return;
/* process any input */
if(io_len > 0)
{
ychar ic;
if(menu_ptr == text_menu)
{
for(; io_len > 0; io_len--)
{
ic = *(io_ptr++);
if(ic > ' ' && ic <= '~')
{
if(text_pos >= menu_long)
putc(7, stderr);
else
{
text_str[text_pos] = (char)ic;
if(text_ypos >= 0)
raw_term(me, text_ypos, text_xpos + text_pos,
text_str + text_pos, 1);
text_str[++text_pos] = '\0';
}
}
else if(ic == me->old_rub)
{
if(text_pos > 0)
{
text_str[--text_pos] = '\0';
if(text_ypos >= 0)
raw_term(me, text_ypos, text_xpos + text_pos,
" ", 1);
}
}
else if(ic == me->KILL || ic == me->WORD)
{
if(text_pos > 0)
{
text_str[0] = '\0';
text_pos = 0;
if(text_ypos > 0)
raw_term(me, text_ypos, text_xpos,
" ", menu_long);
}
}
else if(ic == '\n' || ic == '\r')
{
if(text_pos > 0)
{
text_str[text_pos] = '\0'; /* just to be sure */
kill_menu();
text_menu[0].func(text_str);
}
else
kill_menu();
return;
}
else if(ic == 27 || ic == 4)
{
kill_menu();
return;
}
}
if(text_ypos >= 0)
{
raw_term(me, text_ypos, text_xpos + text_pos, NULL, 0);
flush_term(me);
return;
}
}
else if(menu_ptr == yes_no_menu)
{
/* don't handle yes/no input here */
}
else if(menu_ptr == mesg_menu)
{
ic = *(io_ptr++);
io_len--;
kill_menu();
if(mesg_menu[0].func)
mesg_menu[0].func(ic);
return;
}
else
{
ic = *(io_ptr++);
io_len--;
if(ic == ' ' || ic == '\n' || ic == '\r')
{
/* scroll the menu */
menu_line += me->t_rows - 1;
if(menu_line >= menu_len)
{
kill_menu();
return;
}
i = menu_len - (me->t_rows - 1); /* last full screen */
if(i < menu_line)
menu_line = i;
}
else if(ic > ' ' && ic <= '~')
{
for(i = 0; i < menu_len; i++)
if(menu_ptr[i].key == ic && menu_ptr[i].func != NULL)
{
menu_ptr[i].func(ic);
/*
* THE WHOLE WORLD COULD BE DIFFERENT NOW.
*/
i = -1;
break;
}
if(i >= 0)
kill_menu();
return;
}
else
{
kill_menu();
return;
}
}
}
/* Check the buffer. Keep in mind that we could be here because
* the window size has changed.
*/
if(menu_ptr == text_menu)
{
generate_text_length();
text_ypos = -1; /* assume it's not displayed */
text_xpos = -1;
}
else if(menu_ptr == yes_no_menu)
{
menu_len = 1;
menu_line = 0;
generate_yes_no_length();
}
if(menu_long > buflen)
{
buflen = menu_long + 64;
buf = (ychar *)realloc_mem(buf, buflen + MENU_EXTRA);
}
/* get starting X and Y coord */
x = center(me->t_cols, menu_long + MENU_EXTRA);
if(menu_line == 0)
{
if(menu_len + 2 <= me->t_rows)
{
y = center(me->t_rows, menu_len + 2);
raw_term(me, y++, x, "#####", menu_long + MENU_EXTRA);
}
else
y = 0;
}
else
y = 0;
/* show as many menu lines as we can */
for(i = menu_line; y+1 < me->t_rows && i < menu_len; i++, y++)
{
c = buf;
*(c++) = '#';
*(c++) = ' ';
if(menu_ptr[i].key == ' ')
{
j = 0;
if(menu_ptr == text_menu)
{
if(i > 0)
{
*(c++) = '>';
*(c++) = ' ';
j += 2;
text_ypos = y;
text_xpos = x + j + 2;
}
}
else if(menu_ptr != yes_no_menu)
{
int temp;
temp = center(menu_long + 3, strlen(menu_ptr[i].item));
for(; j < temp; j++)
*(c++) = ' ';
}
for(d = menu_ptr[i].item; *d; d++, j++)
*(c++) = (ychar)*d;
for(; j < menu_long + 3; j++)
*(c++) = ' ';
}
else
{
*(c++) = menu_ptr[i].key;
*(c++) = ':';
*(c++) = ' ';
for(d = menu_ptr[i].item, j = 0; *d; d++, j++)
*(c++) = (ychar)*d;
for(; j < menu_long; j++)
*(c++) = ' ';
}
*(c++) = ' ';
*(c++) = '#';
raw_term(me, y, x, buf, c - buf);
}
if(y < me->t_rows)
{
if(i < menu_len)
{
c = buf;
*(c++) = '#';
*(c++) = ' ';
*(c++) = ' ';
*(c++) = ' ';
*(c++) = ' ';
for(d = "(more)", j = 0; *d; d++, j++)
*(c++) = (ychar)*d;
for(; j < menu_long; j++)
*(c++) = ' ';
*(c++) = ' ';
*(c++) = '#';
raw_term(me, y, x, buf, c - buf);
raw_term(me, y, x + 12, NULL, 0);
}
else
{
raw_term(me, y, x, "#####", menu_long + MENU_EXTRA);
if(menu_ptr == text_menu)
raw_term(me, text_ypos, text_xpos + text_pos, NULL, 0);
else if(menu_ptr == yes_no_menu)
raw_term(me, y-1, x + menu_long + MENU_EXTRA - 2, NULL, 0);
else
raw_term(me, y, me->t_cols / 2, NULL, 0);
}
}
flush_term(me);
}
/* Show a menu, overriding any existing menu.
*/
int
show_menu(menu, len)
menu_item *menu;
int len;
{
register int i, j;
if(me->t_rows < 2)
{
show_error("show_menu: window too small");
return -1;
}
/* scan the menu for problems */
menu_long = 0;
for(i = 0; i < len; i++)
{
if((j = strlen(menu[i].item)) > menu_long)
menu_long = j;
if(menu[i].key < ' ' || menu[i].key >= '~')
{
show_error("show_menu: invalid key");
return -1;
}
}
if(menu_long <= 0)
{
show_error("show_menu: menu too small");
return -1;
}
if(menu_long < 10)
menu_long = 10;
/* set up the menu for display */
menu_ptr = menu;
menu_len = len;
menu_line = 0;
return 0;
}
/* Show a text entry menu, overriding any existing menu.
*/
int
show_text(prompt, func)
char *prompt;
void (*func)();
{
if(me->t_rows < 3)
{
show_error("show_text: window too small");
return -1;
}
/* set up the menu for display */
text_menu[0].item = prompt;
text_menu[0].func = func;
text_menu[0].key = ' ';
text_str[0] = '\0';
text_menu[1].item = text_str;
text_menu[1].func = NULL;
text_menu[1].key = ' ';
menu_ptr = text_menu;
menu_len = 2;
menu_line = 0;
text_ypos = -1;
text_xpos = -1;
text_pos = 0;
generate_text_length();
return 0;
}
/* Show a message in a menu.
*/
int
show_mesg(mesg, func)
char *mesg;
void (*func)();
{
/* set up the menu for display */
mesg_menu[0].item = mesg;
mesg_menu[0].func = func;
mesg_menu[0].key = ' ';
return show_menu(mesg_menu, 1);
}
int
show_main_menu()
{
static int main_items = 0;
if(main_items == 0)
{
while(main_menu[main_items].key != '\0')
main_items++;
}
return show_menu(main_menu, main_items);
}
int
show_option_menu()
{
register int i = 0;
option_menu[i].item = "Options Menu";
option_menu[i].func = NULL;
option_menu[i].key = ' ';
i++;
option_menu[i].item = "";
option_menu[i].func = NULL;
option_menu[i].key = ' ';
i++;
if(def_flags & FL_SCROLL)
option_menu[i].item = "turn scrolling off";
else
option_menu[i].item = "turn scrolling on";
option_menu[i].func = option_menu_sel;
option_menu[i].key = 's';
i++;
if(def_flags & FL_WRAP)
option_menu[i].item = "turn word-wrap off";
else
option_menu[i].item = "turn word-wrap on";
option_menu[i].func = option_menu_sel;
option_menu[i].key = 'w';
i++;
if(def_flags & FL_IMPORT)
option_menu[i].item = "turn auto-import off";
else
option_menu[i].item = "turn auto-import on";
option_menu[i].func = option_menu_sel;
option_menu[i].key = 'i';
i++;
if(def_flags & FL_INVITE)
option_menu[i].item = "turn auto-invite off";
else
option_menu[i].item = "turn auto-invite on";
option_menu[i].func = option_menu_sel;
option_menu[i].key = 'v';
i++;
if(def_flags & FL_RING)
option_menu[i].item = "turn auto-rering off";
else
option_menu[i].item = "turn auto-rering on";
option_menu[i].func = option_menu_sel;
option_menu[i].key = 'r';
i++;
if(term_does_asides())
{
if(def_flags & FL_ASIDE)
option_menu[i].item = "turn asides off";
else
option_menu[i].item = "turn asides on";
option_menu[i].func = option_menu_sel;
option_menu[i].key = 'a';
i++;
}
return show_menu(option_menu, i);
}
int
show_user_menu(title, func)
char *title;
void (*func)();
{
register int i;
register yuser *u;
user_menu[0].item = title;
user_menu[0].func = func;
user_menu[0].key = ' ';
user_menu[1].item = "";
user_menu[1].func = NULL;
user_menu[1].key = ' ';
for(i = 2, u = user_list; u != NULL && i < MAXUMENU; u = u->unext)
if(u != me)
{
if(u->key != '\0')
{
strcpy(user_buf[i], u->full_name);
user_menu[i].item = user_buf[i];
user_menu[i].func = user_menu_sel;
user_menu[i].key = u->key;
i++;
}
}
if(i > 2)
return show_menu(user_menu, i);
kill_menu();
return -1;
}
int
show_user_list()
{
register int i;
register yuser *u;
static char name_buf[25], stat_buf[25];
i = 0;
user_menu[i].item = "User List";
user_menu[i].func = NULL;
user_menu[i].key = ' ';
i++;
user_menu[i].item = "Name Winsize [My_Size] Software ";
user_menu[i].func = NULL;
user_menu[i].key = ' ';
i++;
user_menu[i].item = "";
user_menu[i].func = NULL;
user_menu[i].key = ' ';
i++;
for(u = connect_list; u && i < MAXUMENU; u = u->next)
if(u != me)
{
if(u->remote.vmajor > 2)
sprintf(stat_buf, "YTalk V%d.%d",
u->remote.vmajor, u->remote.vminor);
else if(u->remote.vmajor == 2)
sprintf(stat_buf, "YTalk V2.?");
else
sprintf(stat_buf, "UNIX Talk");
pad_str(u->full_name, 15, name_buf);
pad_str(stat_buf, 15, stat_buf);
sprintf(user_buf[i], "%s %3.3dx%3.3d [%3.3dx%3.3d] %s",
name_buf,
u->remote.cols, u->remote.rows,
u->remote.my_cols, u->remote.my_rows,
stat_buf);
user_menu[i].item = user_buf[i];
user_menu[i].func = NULL;
user_menu[i].key = ' ';
i++;
}
for(u = wait_list; u && i < MAXUMENU; u = u->next)
{
pad_str(u->full_name, 15, name_buf);
pad_str("<unconnected>", 15, stat_buf);
sprintf(user_buf[i], "%s %s",
name_buf,
stat_buf);
user_menu[i].item = user_buf[i];
user_menu[i].func = NULL;
user_menu[i].key = ' ';
i++;
}
return show_menu(user_menu, i);
}
int
show_error_menu(str1, str2)
char *str1, *str2;
{
register int i;
for(i = 0; error_menu[i].key != '\0'; i++)
if(error_menu[i].item == NULL)
{
strncpy(err_str[got_error], str1, MAXERR);
err_str[got_error][MAXERR-1] = '\0';
error_menu[i++].item = err_str[got_error++];
strncpy(err_str[got_error], str2, MAXERR);
err_str[got_error][MAXERR-1] = '\0';
error_menu[i++].item = err_str[got_error++];
return show_menu(error_menu, i);
}
return 0;
}
/* Prompt user for yes/no response. Return the response. It is
* necessary for this function to hang until an answer is received.
*/
int
yes_no(prompt)
char *prompt;
{
int out = 0;
yes_no_menu[0].func = NULL;
yes_no_menu[0].key = ' ';
/* show the menu and call input_loop() */
do {
yes_no_menu[0].item = prompt;
menu_ptr = yes_no_menu;
update_menu();
input_loop();
if(menu_ptr != yes_no_menu || yes_no_menu[0].item != prompt)
{
/* somebody pre-empted us */
kill_menu();
io_len = 0;
}
for(; io_len > 0; io_len--, io_ptr++)
{
if(*io_ptr == 'Y' || (*io_ptr == 'y' && !(def_flags&FL_CAPS)))
{
out = 'y';
break;
}
if(*io_ptr == 'N' || (*io_ptr == 'n' && !(def_flags&FL_CAPS)) || *io_ptr == 27)
{
out = 'n';
break;
}
}
} while(out == 0);
kill_menu();
io_len = 0;
return out;
}
void
update_user_menu()
{
if(menu_ptr == user_menu)
{
redraw_term(me, 0);
if(user_menu[0].func) /* it's a user menu */
(void)show_user_menu(user_menu[0].item, user_menu[0].func);
else /* it's a user status list */
(void)show_user_list();
update_menu();
}
}
ytalk-3.0.3-8bit/menu.h 100644 7527 230 3111 5435465461 13642 0 ustar espel i_math93 /* menu.h */
/* NOTICE
*
* Copyright (c) 1990,1992,1993 Britt Yenne. All rights reserved.
*
* This software is provided AS-IS. The author gives no warranty,
* real or assumed, and takes no responsibility whatsoever for any
* use or misuse of this software, or any damage created by its use
* or misuse.
*
* This software may be freely copied and distributed provided that
* no part of this NOTICE is deleted or edited in any manner.
*
*/
/* Mail comments or questions to ytalk@austin.eds.com */
/* The following structure defines a menu item. It will be displayed
* to the user as the _key_ followed by the _item_. If an item's _key_
* is pressed, the _func_ for that item is called with one argument:
* the _key_ pressed.
*/
typedef struct {
char *item; /* item string, ie: "add a user" */
void (*func)(); /* function to call */
ychar key; /* activating keypress, ie: "a" */
} menu_item;
extern menu_item *menu_ptr; /* if non-NULL, current menu in processing */
/* global functions */
extern void kill_menu (); /* menu.c */
extern void update_menu (); /* menu.c */
extern int show_menu ( /* menuptr, len */ ); /* menu.c */
extern int show_text ( /* prompt, func */ ); /* menu.c */
extern int show_mesg (); /* menu.c */
extern int show_main_menu (); /* menu.c */
extern int show_option_menu(); /* menu.c */
extern int show_user_menu ( /* title, func */ ); /* menu.c */
extern int show_error_menu ( /* str1, str2 */ ); /* menu.c */
extern int yes_no ( /* prompt */ ); /* menu.c */
extern void update_user_menu(); /* menu.c */
/* EOF */
ytalk-3.0.3-8bit/poster 100644 7527 230 2712 5435465462 13773 0 ustar espel i_math93 Subject: v27i024: ytalk-3.0 - multi-user replacement for the UNIX "talk" program, Part01/01
Submitted-By: yenne@austin.eds.com (Britt Yenne)
Posting-Number: Volume 27, Issue 24
Archive-Name: ytalk-3.0/part01
Okay... believe it or not, here it finally is. Several people who have been
mailing me for months to ask about the status of 3.0 graciously
"volunteered" to beta-test, and all appears to work.
Ytalk is in essence a multi-user replacement for the UNIX "talk" program.
Not only will ytalk allow any number of users to connect with one another,
but it also will communicate with _both_ UNIX talk daemons, as well as
daemons compiled incorrectly and/or with byte-order or address boundary
problems.
There is a very large ytalk following spanning every continent, and older
versions of ytalk are available on more anonymous FTP sites than I can even
keep track of, but this is the first time I have attempted to post the
sources to a newsgroup. Somehow, this seems more organized (or maybe I'm
frightfully naive... :-)
Version 3.0 is also the first ytalk source I am actually proud of. It has
been completely rewritten since 2.3 to remove all of the desperate hacks
placed in response to incoming bug reports and comments.
My development platform is a Sun SPARC. Although I have tested on every
platform I have access to, I am always looking for cross-platform compile
problems should anyone wish to mail me some.
As always, have fun.
-britt
yenne@austin.eds.com
ytalk-3.0.3-8bit/poster.p1 100644 7527 230 3030 5436645132 14277 0 ustar espel i_math93 This patch HAS been applied
Patch01 for ytalk-3.0, Volume 27
This patch will update Ytalk 3.0 to Patch Level 1. This patch requires
that you have a directory of unmodified source code for YTalk version 3.0,
with no patches currently applied.
To determine which version of Ytalk you have, run it and look at the top
of your window. The version identifier for the original ytalk release
source will look something like this:
YTalk version 3.0
The version above is what this patch will work on. If there is also
a number in parentheses, ie:
YTalk version 3.0 (1)
then that is the current Patch Level, meaning that all patches up
to and including the displayed number have been applied. This patch
will not work if there is a number in parentheses.
If this is not the patch you require, check the ftp.uu.net USENET archive
for comp.sources.unix via anonymous FTP (ytalk-3.0 is under volume27), or
mail to ytalk@austin.eds.com.
To apply this patch, make a copy of this file under your ytalk source
directory and follow the instructions for the "shell archive" below.
This patch comes with a new Imakefile and Makefile. You will need to
rename or remove your current Imakefile and Makefile before you unpack
the shell archive below. After you unpack the archive you will need
to configure the new Imakefile or Makefile -- see the README file.
For a list of changes contained in this patch, see the README file after
you apply the patch.
Questions or comments to: ytalk@austin.eds.com (Britt Yenne)
ytalk-3.0.3-8bit/rc.c 100644 7527 230 10721 6241651560 13313 0 ustar espel i_math93 /* rc.c -- read the .ytalkrc file */
/* NOTICE
*
* Copyright (c) 1990,1992,1993 Britt Yenne. All rights reserved.
*
* This software is provided AS-IS. The author gives no warranty,
* real or assumed, and takes no responsibility whatsoever for any
* use or misuse of this software, or any damage created by its use
* or misuse.
*
* This software may be freely copied and distributed provided that
* no part of this NOTICE is deleted or edited in any manner.
*
*/
/* Mail comments or questions to ytalk@austin.eds.com */
#include "header.h"
#define IS_WHITE(c) ((c)==' ' || (c)=='\t' || (c)=='\n')
/* ---- local functions ---- */
static char *
get_word(p)
char **p;
{
register char *c, *out;
c = *p;
while(IS_WHITE(*c))
c++;
if(*c == '\0')
return NULL;
out = c;
while(*c && !IS_WHITE(*c))
c++;
if(*c)
*(c++) = '\0';
*p = c;
return out;
}
static int
set_option(opt, value)
char *opt, *value;
{
ylong mask = 0;
int set_it;
if(strcmp(value, "true") == 0 || strcmp(value, "on") == 0)
set_it = 1;
else if(strcmp(value, "false") == 0 || strcmp(value, "off") == 0)
set_it = 0;
else
return -1;
if(strcmp(opt, "scroll") == 0
|| strcmp(opt, "scrolling") == 0
|| strcmp(opt, "sc") == 0)
mask |= FL_SCROLL;
if(strcmp(opt, "wrap") == 0
|| strcmp(opt, "word-wrap") == 0
|| strcmp(opt, "wordwrap") == 0
|| strcmp(opt, "wrapping") == 0
|| strcmp(opt, "ww") == 0)
mask |= FL_WRAP;
if(strcmp(opt, "import") == 0
|| strcmp(opt, "auto-import") == 0
|| strcmp(opt, "autoimport") == 0
|| strcmp(opt, "importing") == 0
|| strcmp(opt, "aip") == 0
|| strcmp(opt, "ai") == 0)
mask |= FL_IMPORT;
if(strcmp(opt, "invite") == 0
|| strcmp(opt, "auto-invite") == 0
|| strcmp(opt, "autoinvite") == 0
|| strcmp(opt, "aiv") == 0
|| strcmp(opt, "av") == 0)
mask |= FL_INVITE;
if(strcmp(opt, "ring") == 0
|| strcmp(opt, "auto-ring") == 0
|| strcmp(opt, "auto-rering") == 0
|| strcmp(opt, "autoring") == 0
|| strcmp(opt, "autorering") == 0
|| strcmp(opt, "ar") == 0)
mask |= FL_RING;
if(strcmp(opt, "xwin") == 0
|| strcmp(opt, "xwindows") == 0
|| strcmp(opt, "XWindows") == 0
|| strcmp(opt, "Xwin") == 0
|| strcmp(opt, "x") == 0
|| strcmp(opt, "X") == 0)
mask |= FL_XWIN;
if(strcmp(opt, "asides") == 0
|| strcmp(opt, "aside") == 0
|| strcmp(opt, "as") == 0)
mask |= FL_ASIDE;
if(strcmp(opt, "caps") == 0
|| strcmp(opt, "CAPS") == 0
|| strcmp(opt, "ca") == 0
|| strcmp(opt, "CA") == 0)
mask |= FL_CAPS;
if(strcmp(opt, "noinvite") == 0
|| strcmp(opt, "no-invite") == 0
|| strcmp(opt, "noinv") == 0
|| strcmp(opt, "ni") == 0)
mask |= FL_NOAUTO;
if(!mask)
return -1;
if(set_it)
def_flags |= mask;
else
def_flags &= ~mask;
return 0;
}
static void
read_rcfile(fname)
char *fname;
{
FILE *fp;
char *buf, *ptr;
char *w, *arg1, *arg2, *arg3;
int line_no, errline;
if((fp = fopen(fname, "r")) == NULL)
{
if(errno != ENOENT)
show_error(fname);
return;
}
buf = get_mem(BUFSIZ);
line_no = errline = 0;
while(fgets(buf, BUFSIZ, fp) != NULL)
{
line_no++;
ptr = buf;
w = get_word(&ptr);
if(w == NULL || *w == '#')
continue;
if(strcmp(w, "readdress") == 0)
{
arg1 = get_word(&ptr);
arg2 = get_word(&ptr);
arg3 = get_word(&ptr);
if(arg3 == NULL)
{
errline = line_no;
break;
}
readdress_host(arg1, arg2, arg3);
}
else if(strcmp(w, "set") == 0 || strcmp(w, "turn") == 0)
{
arg1 = get_word(&ptr);
arg2 = get_word(&ptr);
if(arg2 == NULL)
{
errline = line_no;
break;
}
if(set_option(arg1, arg2) < 0)
{
errline = line_no;
break;
}
}
else
{
errline = line_no;
break;
}
}
if(errline)
{
sprintf(errstr, "%s: syntax error at line %d", fname, errline);
errno = 0;
show_error(errstr);
}
free(buf);
fclose(fp);
}
/* ---- global functions ---- */
void
read_ytalkrc()
{
char *w;
yuser *u;
char fname[256];
/* read the system ytalkrc file */
#ifdef SYSTEM_YTALKRC
read_rcfile(SYSTEM_YTALKRC);
#endif
/* read the user's ytalkrc file */
if((w = (char *)getenv("HOME")) != NULL)
{
sprintf(fname, "%s/.ytalkrc", w);
read_rcfile(fname);
}
/* set all default flags */
for(u = user_list; u != NULL; u = u->unext)
if(!(u->flags & FL_LOCKED))
u->flags = def_flags;
}
ytalk-3.0.3-8bit/socket.c 100644 7527 230 53115 6241651570 14204 0 ustar espel i_math93 /* socket.c - socket functions */
/* NOTICE
*
* Copyright (c) 1990,1992,1993 Britt Yenne. All rights reserved.
*
* This software is provided AS-IS. The author gives no warranty,
* real or assumed, and takes no responsibility whatsoever for any
* use or misuse of this software, or any damage created by its use
* or misuse.
*
* This software may be freely copied and distributed provided that
* no part of this NOTICE is deleted or edited in any manner.
*
*/
/* Mail comments or questions to ytalk@austin.eds.com */
#include "header.h"
#include "menu.h"
#include "socket.h"
#include <sys/time.h>
#ifdef _AIX
# include <sys/select.h>
#endif
struct _talkd talkd[MAXDAEMON+1];
int daemons = 0;
static int otalk, ntalk; /* daemon numbers */
static CTL_MSG omsg; /* old talk message */
static CTL_RESPONSE orsp; /* old talk response */
static CTL_MSG42 nmsg; /* new talk message */
static CTL_RESPONSE42 nrsp; /* new talk response */
static int autofd = -1; /* auto invite socket fd */
static struct sockaddr_in autosock; /* auto invite socket */
static ylong autoid[MAXDAEMON+1]; /* auto invite seq numbers */
static ylong announce_id = 0; /* announce sequence id */
static readdr *readdr_list = NULL; /* list of re-addresses */
#define IN_ADDR(s) ((s).sin_addr.s_addr)
#define IN_PORT(s) ((s).sin_port)
#define SOCK_EQUAL(s,c) (IN_PORT(s) == IN_PORT(c) && IN_ADDR(s) == IN_ADDR(c))
/* ---- local functions ---- */
/* Create a datagram socket.
*/
static int
init_dgram(sock)
struct sockaddr_in *sock;
{
int fd, socklen;
sock->sin_family = AF_INET;
IN_ADDR(*sock) = INADDR_ANY;
IN_PORT(*sock) = 0;
if((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
{
show_error("init_dgram: socket() failed");
bail(YTE_ERROR);
}
if(bind(fd, (struct sockaddr *)sock, sizeof(struct sockaddr_in)) != 0)
{
close(fd);
show_error("init_dgram: bind() failed");
bail(YTE_ERROR);
}
socklen = sizeof(struct sockaddr_in);
if(getsockname(fd, (struct sockaddr *)sock, &socklen) < 0)
{
close(fd);
show_error("init_dgram: getsockname() failed");
bail(YTE_ERROR);
}
IN_ADDR(*sock) = me->host_addr;
return fd;
}
/* Initialize a new daemon structure.
*/
static int
init_daemon(name, port, mptr, mlen, rptr, rlen)
char *name;
short port;
yaddr mptr, rptr;
int mlen, rlen;
{
struct servent *serv;
int d;
if(daemons >= MAXDAEMON)
{
show_error("init_daemon: too many daemons");
bail(YTE_ERROR);
}
d = ++daemons; /* daemon number zero is not defined */
if((serv = getservbyname(name, "udp")) != NULL)
talkd[d].port = serv->s_port;
else
talkd[d].port = port;
talkd[d].fd = init_dgram(&(talkd[d].sock));
talkd[d].mptr = mptr;
talkd[d].mlen = mlen;
talkd[d].rptr = rptr;
talkd[d].rlen = rlen;
return d;
}
static void
read_autoport(fd)
int fd;
{
int socklen;
static v2_pack pack;
static char estr[V2_NAMELEN + V2_HOSTLEN + 20];
static struct sockaddr_in temp;
/* accept the connection */
socklen = sizeof(struct sockaddr_in);
if((fd = accept(autofd, (struct sockaddr *) &temp, &socklen)) == -1)
{
show_error("read_autoport: accept() failed");
return;
}
/* The autoport socket just uses the old Ytalk version 2.?
* packet.
*/
errno = 0;
if(full_read(fd, &pack, V2_PACKLEN) < 0 || pack.code != V2_AUTO)
{
show_error("read_autoport: unknown auto-invite connection");
close(fd);
return;
}
close(fd);
if(!(def_flags & FL_INVITE))
{
sprintf(estr, "Talk to %s@%s?", pack.name, pack.host);
if(yes_no(estr) == 'n')
return;
}
sprintf(estr, "%s@%s", pack.name, pack.host);
invite(estr, 1); /* we should be expected */
}
/* Create and initialize the auto-invitation socket.
*/
static void
init_autoport()
{
int socklen;
autosock.sin_family = AF_INET;
IN_ADDR(autosock) = INADDR_ANY;
IN_PORT(autosock) = 0;
if((autofd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
show_error("init_autoport: socket() failed");
return;
}
if(bind(autofd, (struct sockaddr *)&autosock,
sizeof(struct sockaddr_in)) < 0)
{
close(autofd);
autofd = -1;
show_error("init_autoport: bind() failed");
return;
}
socklen = sizeof(struct sockaddr_in);
if(getsockname(autofd, (struct sockaddr *)&autosock, &socklen) < 0)
{
close(autofd);
autofd = -1;
show_error("init_autoport: getsockname() failed");
return;
}
IN_ADDR(autosock) = me->host_addr;
if(listen(autofd, 5) < 0)
{
close(autofd);
autofd = -1;
show_error("init_autoport: listen() failed");
return;
}
(void)memset(autoid, 0, (MAXDAEMON + 1) * sizeof(ylong));
add_fd(autofd, read_autoport);
}
/* Fill the socket address field with the appropriate return address for
* the host I'm sending to.
*/
static void
place_my_address(sock, addr)
struct sockaddr_in *sock;
register ylong addr;
{
register readdr *r;
for(r = readdr_list; r != NULL; r = r->next)
if((addr & r->mask) == r->addr)
{
addr = (r->id_addr & r->id_mask) |
(me->host_addr & (~(r->id_mask)));
IN_ADDR(*sock) = addr;
break;
}
if(r == NULL)
IN_ADDR(*sock) = me->host_addr;
sock->sin_family = htons(AF_INET);
}
/* sendit() sends the completed message to the talk daemon at the given
* hostname, then reads a response packet.
*/
static int
sendit(addr, d)
ylong addr; /* host internet address */
int d; /* daemon number */
{
int n;
struct sockaddr_in daemon;
struct timeval tv;
char *rtype, *mtype;
fd_set sel;
/* set up the appropriate message structure */
if(d == ntalk)
{
nmsg.vers = TALK_VERSION;
place_my_address(&(nmsg.ctl_addr), addr);
mtype = &(nmsg.type);
rtype = &(nrsp.type);
}
else if(d == otalk)
{
omsg.type = nmsg.type;
omsg.addr = nmsg.addr;
omsg.id_num = nmsg.id_num;
omsg.pid = nmsg.pid;
strncpy(omsg.l_name, nmsg.l_name, NAME_SIZE);
strncpy(omsg.r_name, nmsg.r_name, NAME_SIZE);
strncpy(omsg.r_tty, nmsg.r_tty, TTY_SIZE);
place_my_address(&(omsg.ctl_addr), addr);
mtype = &(omsg.type);
rtype = &(orsp.type);
}
else
{
sprintf(errstr, "Unkown daemon type: %d", d);
show_error(errstr);
return -1;
}
/* set up a sockaddr_in for the daemon we're sending to */
daemon.sin_family = AF_INET;
IN_ADDR(daemon) = addr;
IN_PORT(daemon) = talkd[d].port;
/* flush any lingering input */
FD_ZERO(&sel);
for(;;)
{
tv.tv_sec = 0L;
tv.tv_usec = 0L;
FD_SET(talkd[d].fd, &sel);
if((n = select(talkd[d].fd + 1, &sel, 0, 0, &tv)) < 0)
{
show_error("sendit: flush select() failed");
return -1;
}
if(n <= 0)
break;
if(recv(talkd[d].fd, talkd[d].rptr, talkd[d].rlen, 0) < 0)
{
show_error("sendit: flush recv() failed");
return -1;
}
}
/* Now we need to send the actual packet. Due to unreliability of
* DGRAM sockets, we must resend the packet until we get a response
* from the server. Geez... two different daemons, both on unreliable
* sockets, and maybe even different daemons on different machines.
* Is *nothing* reliable anymore???
*/
do
{
do
{
n = sendto(talkd[d].fd, talkd[d].mptr, talkd[d].mlen,
0, (struct sockaddr *) &daemon, sizeof(daemon));
if(n != talkd[d].mlen)
{
show_error("sendit: sendto() failed");
return -1;
}
tv.tv_sec = 5L;
tv.tv_usec = 0L;
FD_SET(talkd[d].fd, &sel);
if((n = select(talkd[d].fd + 1, &sel, 0, 0, &tv)) < 0)
{
show_error("sendit: first select() failed");
return -1;
}
} while (n <= 0); /* ie: until we receive a reply */
do
{
n = recv(talkd[d].fd, talkd[d].rptr, talkd[d].rlen, 0);
if(n < 0)
{
show_error("sendit: recv() failed");
return -1;
}
if(*rtype != *mtype)
tv.tv_sec = 5L;
else
tv.tv_sec = 0L;
tv.tv_usec = 0L;
FD_SET(talkd[d].fd, &sel);
if((n = select(talkd[d].fd + 1, &sel, 0, 0, &tv)) < 0)
{
show_error("sendit: second select() failed");
return -1;
}
} while(n > 0 && *rtype != *mtype);
} while(*rtype != *mtype);
/* WHEW */
/* Just because a person is a SYSADMIN doesn't necessarily mean he/she
* knows everything about installing software. In fact, many have been
* known to install the talk daemon without setting the option required
* to pad out the structures so that "long"s are on four-byte boundaries
* on machines where "long"s can be on two-byte boundaries. This "bug"
* cost me about four hours of debugging to discover, so I'm not happy
* right now. Anyway, here's a quick hack to fix this problem.
*/
if(d == otalk && nrsp.type == LOOK_UP && nrsp.answer == 0)
{
u_short t;
(void)memcpy((char *)&t, ((char *)&orsp.addr.sin_family)-2, sizeof(t));
if(ntohs(t) == AF_INET && ntohs(orsp.addr.sin_family) != AF_INET)
{
char *c;
c = ((char *)&orsp) + sizeof(orsp) - 1;
for(; c >= (char *)&orsp.id_num; c--)
*c = *(c-2);
}
}
/* Fill in the new talk response structure if we just read an
* old one.
*/
if(d == otalk)
{
nrsp.type = orsp.type;
nrsp.answer = orsp.answer;
nrsp.id_num = orsp.id_num;
nrsp.addr = orsp.addr;
}
return 0;
}
/* find_daemon() locates the talk daemon(s) on a machine and determines
* what version(s) of the daemon are running.
*/
static int
find_daemon(addr)
ylong addr;
{
register hostinfo *h;
register int n, i, d;
CTL_MSG m1;
CTL_MSG42 m2;
struct sockaddr_in daemon;
struct timeval tv;
int sel, out;
static hostinfo *host_head = NULL;
/* If we've already used this host, look it up instead of blitting to
* the daemons again...
*/
for(h = host_head; h; h = h->next)
if(h->host_addr == addr)
return h->dtype;
daemon.sin_family = AF_INET;
IN_ADDR(daemon) = addr;
m1 = omsg;
m2 = nmsg;
m1.ctl_addr = talkd[otalk].sock;
place_my_address(&(m1.ctl_addr), addr);
m2.ctl_addr = talkd[ntalk].sock;
place_my_address(&(m2.ctl_addr), addr);
m1.type = m2.type = LOOK_UP;
m1.id_num = m2.id_num = htonl(0);
m1.r_tty[0] = m2.r_tty[0] = '\0';
strcpy(m1.r_name, "ytalk");
strcpy(m2.r_name, "ytalk");
m1.addr.sin_family = m2.addr.sin_family = htons(AF_INET);
out = 0;
for(i = 0; i < 5; i++)
{
IN_PORT(daemon) = talkd[ntalk].port;
n = sendto(talkd[ntalk].fd, &m2, sizeof(m2),
0, (struct sockaddr *) &daemon, sizeof(daemon));
if(n != sizeof(m2))
show_error("Warning: cannot write to new talk daemon");
IN_PORT(daemon) = talkd[otalk].port;
n = sendto(talkd[otalk].fd, &m1, sizeof(m1),
0, (struct sockaddr *) &daemon, sizeof(daemon));
if(n != sizeof(m1))
show_error("Warning: cannot write to old talk daemon");
tv.tv_sec = 4L;
tv.tv_usec = 0L;
sel = (1 << talkd[ntalk].fd) | (1 << talkd[otalk].fd);
if((n = select(32, &sel, 0, 0, &tv)) < 0)
{
show_error("find_daemon: first select() failed");
continue;
}
if(n == 0)
continue;
do
{
for(d = 1; d <= daemons; d++)
if(sel & (1 << talkd[d].fd))
{
out |= (1 << d);
if(recv(talkd[d].fd, errstr, talkd[d].rlen, 0) < 0)
show_error("find_daemon: recv() failed");
}
tv.tv_sec = 0L;
tv.tv_usec = 500000L; /* give the other daemon a chance */
sel = (1 << talkd[ntalk].fd) | (1 << talkd[otalk].fd);
if((n = select(32, &sel, 0, 0, &tv)) < 0)
show_error("find_daemon: second select() failed");
} while(n > 0);
h = (hostinfo *)get_mem(sizeof(hostinfo));
h->next = host_head;
host_head = h;
h->host_addr = addr;
h->dtype = out;
return out;
}
sprintf(errstr, "No talk daemon on %s", host_name(addr));
show_error(errstr);
return 0;
}
static ylong
make_net_mask(addr)
ylong addr;
{
if(addr & (ylong)0xff)
return (ylong)0xffffffff;
if(addr & (ylong)0xffff)
return (ylong)0xffffff00;
if(addr & (ylong)0xffffff)
return (ylong)0xffff0000;
if(addr)
return (ylong)0xff000000;
return (ylong)0;
}
/* ---- global functions ---- */
/* Initialize sockets and message parameters.
*/
void
init_socket()
{
/* init daemons in order of preference */
ntalk = init_daemon("ntalk", 518, &nmsg, sizeof(nmsg),
&nrsp, sizeof(nrsp));
otalk = init_daemon("talk", 517, &omsg, sizeof(omsg),
&orsp, sizeof(orsp));
strncpy(nmsg.l_name, me->user_name, NAME_SIZE);
omsg.ctl_addr = talkd[otalk].sock;
nmsg.ctl_addr = talkd[ntalk].sock;
nmsg.vers = TALK_VERSION;
(void)find_daemon(me->host_addr);
if (!(def_flags & FL_NOAUTO))
init_autoport();
}
/* Close every open descriptor. This should only be used for a quick
* exit... it does not gracefully shut systems down.
*/
void
close_all()
{
register yuser *u;
register int d;
for(u = user_list; u; u = u->unext)
{
if(u->fd > 0)
close(u->fd);
if(u->output_fd > 0)
close(u->output_fd);
}
if(autofd > 0)
close(autofd);
for(d = 1; d <= daemons; d++)
close(talkd[d].fd);
}
/* The following routines send a request across the DGRAM socket to the
* talk daemons.
*/
/* First, a quick and easy interface for the user sockets.
*/
int
send_dgram(user, type)
yuser *user;
u_char type;
{
ylong addr;
int d;
/* set up the message type and where to send it */
switch(type)
{
case LEAVE_INVITE: /* leave an invite on my machine */
addr = me->host_addr;
nmsg.type = LEAVE_INVITE;
nmsg.id_num = htonl(user->l_id);
break;
case DELETE_INVITE: /* delete my invite on my machine */
addr = me->host_addr;
nmsg.type = DELETE;
nmsg.id_num = htonl(user->l_id);
break;
case ANNOUNCE: /* ring a user */
addr = user->host_addr;
nmsg.type = ANNOUNCE;
announce_id += 5; /* no guesswork here */
nmsg.id_num = htonl(announce_id);
break;
case LOOK_UP: /* look up remote invitation */
addr = user->host_addr;
nmsg.type = LOOK_UP;
nmsg.id_num = htonl(user->r_id);
break;
case DELETE: /* delete erroneous remote invitation */
addr = user->host_addr;
nmsg.type = DELETE;
nmsg.id_num = htonl(user->r_id);
break;
case AUTO_LOOK_UP: /* look up remote auto-invitation */
addr = user->host_addr;
nmsg.type = LOOK_UP;
nmsg.id_num = htonl(user->r_id);
break;
case AUTO_DELETE: /* delete erroneous remote auto-invitation */
addr = user->host_addr;
nmsg.type = DELETE;
nmsg.id_num = htonl(user->r_id);
break;
default:
errno = 0;
show_error("send_dgram: unknown type");
return -1;
}
/* find a common daemon, if possible */
if(user->daemon != 0)
d = user->daemon;
else
{
int dtype, d1, d2;
/* Find the daemon(s) their host supports. If our two machines
* support a daemon in common, use that one. Else, normal UNIX
* "talk" is already screwed to the wall, but YTalk will at least
* work.
*/
d1 = find_daemon(user->host_addr);
d2 = find_daemon(me->host_addr);
dtype = d1 & d2;
if(d1 == 0 || d2 == 0)
return -1;
if(dtype == 0)
{
dtype = find_daemon(addr);
for(d = 1; d <= daemons; d++)
if(dtype & (1<<d))
break;
if(d > daemons)
return -1;
}
else
{
for(d = 1; d <= daemons; d++)
if(dtype & (1<<d))
{
user->daemon = d;
break;
}
if(d > daemons)
return -1;
}
}
/* Each user has his own unique daemon id. Why? Tsch. Why.
* Well, the talk daemons consider two users equivalent if their
* usernames and machine names match. Hence, the daemons will not
* allow ytalk to talk with two different users with the same name
* on some machine. By assigning unique process id's, we trick
* the daemons into thinking we're several different users trying
* to talk to the same person. Sick? Don't blame me.
*/
nmsg.pid = htonl(user->d_id);
if(type == AUTO_LOOK_UP || type == AUTO_DELETE)
{
strcpy(nmsg.l_name, "+AUTO"); /* put on my mask... */
strncpy(nmsg.r_name, user->user_name, NAME_SIZE);
nmsg.r_tty[0] = '\0';
}
else
{
strncpy(nmsg.r_name, user->user_name, NAME_SIZE);
strncpy(nmsg.r_tty, user->tty_name, TTY_SIZE);
}
nmsg.addr = user->sock;
nmsg.addr.sin_family = htons(AF_INET);
if(sendit(addr, d) != 0)
{
if(type == AUTO_LOOK_UP || type == AUTO_DELETE)
strncpy(nmsg.l_name, me->user_name, NAME_SIZE);
return -2;
}
switch(type)
{
case LEAVE_INVITE:
user->l_id = ntohl(nrsp.id_num);
break;
case LOOK_UP:
user->r_id = ntohl(nrsp.id_num);
break;
case AUTO_LOOK_UP:
strncpy(nmsg.l_name, me->user_name, NAME_SIZE);
user->r_id = ntohl(nrsp.id_num);
break;
case AUTO_DELETE:
strncpy(nmsg.l_name, me->user_name, NAME_SIZE);
break;
}
return nrsp.answer;
}
/* Next, an interface for the auto-invite socket. The auto-invite socket
* always sends to the caller's host, and always does just an invite.
*/
int
send_auto(type)
u_char type;
{
int dtype, d, rc;
if(autofd < 0)
return 0;
nmsg.type = type;
strcpy(nmsg.r_name, "+AUTO");
nmsg.r_tty[0] = '\0';
nmsg.addr = autosock;
nmsg.addr.sin_family = htons(AF_INET);
rc = 0;
dtype = find_daemon(me->host_addr);
for(d = daemons; d >= 1; d--)
if(dtype & (1<<d))
{
nmsg.id_num = htonl(autoid[d]);
nmsg.pid = htonl(1);
if(sendit(me->host_addr, d) < 0)
rc = -1;
else
autoid[d] = ntohl(nrsp.id_num);
}
if(rc)
return rc;
if(type == LEAVE_INVITE)
return 0;
return nrsp.answer;
}
/* Shut down the auto-invitation system.
*/
void
kill_auto()
{
if(autofd < 0)
return;
(void)send_auto(DELETE);
remove_fd(autofd);
close(autofd);
autofd = -1;
}
/* Create a TCP socket for communication with other talk users.
*/
int
newsock(user)
yuser *user;
{
int socklen, fd;
user->sock.sin_family = AF_INET;
IN_ADDR(user->sock) = INADDR_ANY;
IN_PORT(user->sock) = 0;
if((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
show_error("newsock: socket() failed");
return -1;
}
if(bind(fd, (struct sockaddr *)&user->sock, sizeof(struct sockaddr_in)) < 0)
{
close(fd);
show_error("newsock: bind() failed");
return -1;
}
socklen = sizeof(struct sockaddr_in);
if(getsockname(fd, (struct sockaddr *)&user->sock, &socklen) < 0)
{
close(fd);
show_error("newsock: getsockname() failed");
return -1;
}
place_my_address(&(user->sock), user->host_addr);
if(listen(fd, 5) < 0)
{
close(fd);
show_error("newsock: listen() failed");
return -1;
}
user->fd = fd;
fd_to_user[user->fd] = user;
user->orig_sock = user->sock;
return 0;
}
/* Connect to another user's communication socket.
*/
int
connect_to(user)
yuser *user;
{
register yuser *u;
int socklen, fd;
struct sockaddr_in sock, orig_sock;
orig_sock = *(struct sockaddr_in *)&nrsp.addr;
orig_sock.sin_family = AF_INET;
/* it could be one of mine... */
for(u = user_list; u; u = u->unext)
if(SOCK_EQUAL(orig_sock, u->orig_sock))
return -3;
if(SOCK_EQUAL(orig_sock, autosock))
return -3;
sock = orig_sock;
if((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
show_error("connect_to: socket() failed");
return -1;
}
if(connect(fd, (struct sockaddr *)&sock, sizeof(struct sockaddr_in)) < 0)
{
close(fd);
if(errno == ECONNREFUSED)
{
errno = 0;
return -2;
}
show_error("connect_to: connect() failed");
return -1;
}
socklen = sizeof(struct sockaddr_in);
if(getsockname(fd, (struct sockaddr *)&sock, &socklen) < 0)
{
close(fd);
show_error("connect_to: getsockname() failed");
return -1;
}
if(user)
{
user->sock = sock;
user->orig_sock = orig_sock;
user->fd = fd;
fd_to_user[user->fd] = user;
}
return fd;
}
/* Find a host's address.
*/
ylong
get_host_addr(hostname)
char *hostname;
{
struct hostent *host;
ylong addr;
ylong inet_addr();
errno = 0;
if((host = (struct hostent *) gethostbyname(hostname)) != NULL)
{
if(host->h_length != sizeof(addr))
{
sprintf(errstr, "Bad IN addr: %s", hostname);
show_error(errstr);
return (ylong)-1;
}
(void)memcpy(&addr, host->h_addr, sizeof(addr));
}
else if((addr = (ylong)inet_addr(hostname)) == (ylong)-1)
return (ylong)-1;
return addr;
}
/* Find a host name by host address.
*/
char *
host_name(addr)
ylong addr;
{
struct hostent *host;
char *inet_ntoa();
if((host = gethostbyaddr((char *) &addr, sizeof(addr), AF_INET)) == NULL)
{
struct in_addr tmp;
tmp.s_addr = addr;
return inet_ntoa(tmp);
}
return host->h_name;
}
/* Re-address a given host ("from_id") to the given address or host
* id ("to_id") when communicating with some other host id ("on_id").
* This is useful especially over routers where "foo.com" is known
* as the differently-addressed "bar.com" to host "xyzzy.com".
*/
void
readdress_host(from_id, to_id, on_id)
char *from_id, *to_id, *on_id;
{
register readdr *new;
ylong from_addr, to_addr, on_addr;
ylong from_mask, to_mask, on_mask;
if((from_addr = get_host_addr(from_id)) == (ylong)-1)
{
sprintf(errstr, "Unknown host: '%s'", from_id);
show_error(errstr);
return;
}
if((to_addr = get_host_addr(to_id)) == (ylong)-1)
{
sprintf(errstr, "Unknown host: '%s'", to_id);
show_error(errstr);
return;
}
if((on_addr = get_host_addr(on_id)) == (ylong)-1)
{
sprintf(errstr, "Unknown host: '%s'", on_id);
show_error(errstr);
return;
}
from_mask = make_net_mask(from_addr);
to_mask = make_net_mask(to_addr);
on_mask = make_net_mask(on_addr);
if((from_addr & from_mask) != (me->host_addr & from_mask))
return;
if(from_addr == to_addr)
return;
new = (readdr *)get_mem(sizeof(readdr));
new->addr = on_addr;
new->mask = on_mask;
new->id_addr = to_addr;
new->id_mask = to_mask;
new->next = readdr_list;
readdr_list = new;
}
ytalk-3.0.3-8bit/socket.h 100644 7527 230 5030 5436644653 14173 0 ustar espel i_math93 /* socket.h */
#include <netdb.h>
/* ---- talk daemon information structure ---- */
#define MAXDAEMON 5
struct _talkd {
struct sockaddr_in sock; /* socket */
int fd; /* socket file descriptor */
short port; /* port number */
yaddr mptr; /* message pointer */
int mlen; /* message length */
yaddr rptr; /* response pointer */
int rlen; /* response length */
};
typedef struct _hostinfo {
struct _hostinfo *next; /* next in linked list */
ylong host_addr; /* host address */
int dtype; /* active daemon types bitmask */
} hostinfo;
typedef struct _readdr {
struct _readdr *next; /* next in linked list */
ylong addr; /* this net address [group?], */
ylong mask; /* with this mask, */
ylong id_addr; /* thinks I'm at this net address, */
ylong id_mask; /* with this mask. */
} readdr;
extern struct _talkd talkd[MAXDAEMON+1];
extern int daemons;
/* ---- talk daemon I/O structures ---- */
#define NAME_SIZE 9
#define TTY_SIZE 16
/* Control Message structure for earlier than BSD4.2
*/
typedef struct {
char type;
char l_name[NAME_SIZE];
char r_name[NAME_SIZE];
char filler;
ylong id_num;
ylong pid;
char r_tty[TTY_SIZE];
struct sockaddr_in addr;
struct sockaddr_in ctl_addr;
} CTL_MSG;
/* Control Response structure for earlier than BSD4.2
*/
typedef struct {
char type;
char answer;
u_short filler;
ylong id_num;
struct sockaddr_in addr;
} CTL_RESPONSE;
/* Control Message structure for BSD4.2
*/
typedef struct {
u_char vers;
char type;
u_short filler;
ylong id_num;
struct sockaddr_in addr;
struct sockaddr_in ctl_addr;
ylong pid;
char l_name[NAME_SIZE];
char l_name_filler[3];
char r_name[NAME_SIZE];
char r_name_filler[3];
char r_tty[TTY_SIZE];
} CTL_MSG42;
/* Control Response structure for BSD4.2
*/
typedef struct {
u_char vers;
char type;
char answer;
char filler;
ylong id_num;
struct sockaddr_in addr;
} CTL_RESPONSE42;
#define TALK_VERSION 1 /* protocol version */
/* Dgram Types.
*
* These are the "type" arguments to feed to send_dgram(). Each acts
* either on the remote daemon or the local daemon, as marked.
*/
#define LEAVE_INVITE 0 /* leave an invitation (local) */
#define LOOK_UP 1 /* look up an invitation (remote) */
#define DELETE 2 /* delete erroneous invitation (remote) */
#define ANNOUNCE 3 /* ring a user (remote) */
#define DELETE_INVITE 4 /* delete my invitation (local) */
#define AUTO_LOOK_UP 5 /* look up auto-invitation (remote) */
#define AUTO_DELETE 6 /* delete erroneous auto-invitation (remote) */
/* EOF */
ytalk-3.0.3-8bit/term.c 100644 7527 230 54465 6241652674 13702 0 ustar espel i_math93 /* term.c */
/* NOTICE
*
* Copyright (c) 1990,1992,1993 Britt Yenne. All rights reserved.
*
* This software is provided AS-IS. The author gives no warranty,
* real or assumed, and takes no responsibility whatsoever for any
* use or misuse of this software, or any damage created by its use
* or misuse.
*
* This software may be freely copied and distributed provided that
* no part of this NOTICE is deleted or edited in any manner.
*
*/
/* Mail comments or questions to ytalk@austin.eds.com */
#include "header.h"
#include <sys/ioctl.h>
#ifdef USE_SGTTY
# ifdef hpux
# include <sys/bsdtty.h>
# include <sgtty.h>
# endif
#else
# include <termios.h>
#endif
#include "cwin.h"
#include "xwin.h"
#include "menu.h"
static int (*_open_term)(); /* open a new terminal */
static void (*_close_term)(); /* close a terminal */
static void (*_addch_term)(); /* write a char to a terminal */
static void (*_move_term)(); /* move cursor to Y,X position */
static void (*_clreol_term)(); /* clear to end of line */
static void (*_clreos_term)(); /* clear to end of screen */
static void (*_scroll_term)(); /* scroll up one line */
static void (*_rev_scroll_term)(); /* scroll down one line */
static void (*_flush_term)(); /* flush pending output */
static int term_type = 0;
#ifdef USE_SGTTY
static int line_discipline;
static int local_mode;
static struct sgttyb sgttyb;
static struct tchars tchars;
static struct ltchars ltchars;
#else
static struct termios tio;
#endif
#ifdef USE_SGTTY
static void
init_sgtty()
{
if(ioctl(0, TIOCGETD, &line_discipline) < 0)
{
show_error("TIOCGETD");
bail(YTE_INIT);
}
if(ioctl(0, TIOCLGET, &local_mode) < 0)
{
show_error("TIOCGETP");
bail(YTE_INIT);
}
if(ioctl(0, TIOCGETP, &sgttyb) < 0)
{
show_error("TIOCGETP");
bail(YTE_INIT);
}
if(ioctl(0, TIOCGETC, &tchars) < 0)
{
show_error("TIOCGETC");
bail(YTE_INIT);
}
if(ioctl(0, TIOCGLTC, <chars) < 0)
{
show_error("TIOCGLTC");
bail(YTE_INIT);
}
me->old_rub = sgttyb.sg_erase;
me->RUB = RUBDEF;
me->KILL = sgttyb.sg_kill;
me->WORD = ltchars.t_werasc;
me->CLR = '\024'; /* ^T */
}
#else
static void
init_termios()
{
/* get edit chars */
if(tcgetattr(0, &tio) < 0)
{
show_error("tcgetattr failed");
bail(YTE_INIT);
}
me->old_rub = tio.c_cc[VERASE];
me->RUB = RUBDEF;
#ifdef VKILL
me->KILL = tio.c_cc[VKILL];
#else
me->KILL = '\025'; /* ^U */
#endif
#ifdef VWERASE
me->WORD = tio.c_cc[VWERASE];
if(me->WORD == 0xff)
me->WORD = '\027'; /* ^W */
#else
me->WORD = '\027'; /* ^W */
#endif
me->CLR = '\024'; /* ^T */
}
#endif
/* Initialize terminal and input characteristics.
*/
void
init_term()
{
char tmpstr[64];
#ifdef USE_SGTTY
init_sgtty();
#else
init_termios();
#endif
/* Decide on a terminal (window) system and set up the
* function pointers.
*/
term_type = 0; /* nothing selected yet */
#ifdef USE_X11
if(term_type == 0 && (def_flags & FL_XWIN) && getenv("DISPLAY"))
{
_open_term = open_xwin;
_close_term = close_xwin;
_addch_term = addch_xwin;
_move_term = move_xwin;
_clreol_term = clreol_xwin;
_clreos_term = clreos_xwin;
_scroll_term = scroll_xwin;
_rev_scroll_term = rev_scroll_xwin;
_flush_term = flush_xwin;
init_xwin();
term_type = 2; /* using xwin */
}
#endif
/* if no window system found, default to curses */
if(term_type == 0)
{
_open_term = open_curses;
_close_term = close_curses;
_addch_term = addch_curses;
_move_term = move_curses;
_clreol_term = clreol_curses;
_clreos_term = clreos_curses;
_scroll_term = scroll_curses;
_rev_scroll_term = NULL;
_flush_term = flush_curses;
init_curses();
term_type = 1; /* using curses */
}
/* set me up a terminal */
sprintf(tmpstr, "YTalk version %d.%d (%d)", VMAJOR, VMINOR, VPATCH);
if(open_term(me, tmpstr) < 0)
{
end_term();
show_error("init_term: open_term() failed");
bail(0);
}
}
/* Set terminal size.
*/
void
set_terminal_size(fd, rows, cols)
int fd, rows, cols;
{
#ifdef TIOCSWINSZ
struct winsize winsize;
winsize.ws_row = rows;
winsize.ws_col = cols;
ioctl(fd, TIOCSWINSZ, &winsize);
#endif
}
/* Set terminal and input characteristics for slave terminals.
*/
void
set_terminal_flags(fd)
int fd;
{
#ifdef USE_SGTTY
(void)ioctl(fd, TIOCSETD, &line_discipline);
(void)ioctl(fd, TIOCLSET, &local_mode);
(void)ioctl(fd, TIOCSETP, &sgttyb);
(void)ioctl(fd, TIOCSETC, &tchars);
(void)ioctl(fd, TIOCSLTC, <chars);
#else
if(tcsetattr(fd, TCSANOW, &tio) < 0)
show_error("tcsetattr failed");
#endif
}
int
what_term()
{
return term_type;
}
/* Abort all terminal processing.
*/
void
end_term()
{
switch(term_type)
{
case 1: /* curses */
end_curses();
break;
#ifdef USE_X11
case 2: /* xwin */
end_xwin();
break;
#endif
}
term_type = 0;
}
/* Open a new user window.
*/
int
open_term(user, title)
register yuser *user;
register char *title;
{
if(_open_term(user, title) != 0)
return -1;
user->x = user->y = 0;
if(user->scr == NULL)
resize_win(user, 24, 80);
return 0;
}
/* Close a user window.
*/
void
close_term(user)
register yuser *user;
{
register int i;
if(user->scr)
{
_close_term(user);
for(i = 0; i < user->t_rows; i++)
free(user->scr[i]);
free(user->scr);
user->scr = NULL;
user->t_rows = user->rows = 0;
user->t_cols = user->cols = 0;
}
}
/* Place a character.
*/
void
addch_term(user, c)
register yuser *user;
register ychar c;
{
if (is_printable(c))
{
_addch_term(user, c);
user->scr[user->y][user->x] = c;
if(++(user->x) >= user->cols)
{
user->bump = 1;
user->x = user->cols - 1;
if(user->cols < user->t_cols)
_move_term(user, user->y, user->x);
}
}
}
/* Move the cursor.
*/
void
move_term(user, y, x)
register yuser *user;
register int y, x;
{
if(y < 0 || y > user->sc_bot)
y = user->sc_bot;
if(x < 0 || x >= user->cols)
{
user->bump = 1;
x = user->cols - 1;
}
else
user->bump = 0;
_move_term(user, y, x);
user->y = y;
user->x = x;
}
/* Clear to EOL.
*/
void
clreol_term(user)
register yuser *user;
{
register int j;
register ychar *c;
if(user->cols < user->t_cols)
{
c = user->scr[user->y] + user->x;
for(j = user->x; j < user->cols; j++)
{
*(c++) = ' ';
_addch_term(user, ' ');
}
move_term(user, user->y, user->x);
}
else
{
_clreol_term(user);
c = user->scr[user->y] + user->x;
for(j = user->x; j < user->cols; j++)
*(c++) = ' ';
}
}
/* Clear to EOS.
*/
void
clreos_term(user)
register yuser *user;
{
register int j, i;
register ychar *c;
int x, y;
if(user->cols < user->t_cols || user->rows < user->t_rows)
{
x = user->x;
y = user->y;
clreol_term(user);
for(i = user->y + 1; i < user->rows; i++)
{
move_term(user, i, 0);
clreol_term(user);
}
move_term(user, y, x);
}
else
{
_clreos_term(user);
j = user->x;
for(i = user->y; i < user->rows; i++)
{
c = user->scr[i] + j;
for(; j < user->cols; j++)
*(c++) = ' ';
j = 0;
}
}
}
/* Scroll window.
*/
void
scroll_term(user)
register yuser *user;
{
register int i;
register ychar *c;
int sy, sx;
if(user->sc_bot > user->sc_top)
{
c = user->scr[user->sc_top];
for(i = user->sc_top; i < user->sc_bot; i++)
user->scr[i] = user->scr[i+1];
user->scr[user->sc_bot] = c;
for(i = 0; i < user->cols; i++)
*(c++) = ' ';
if(_scroll_term
&& user->rows == user->t_rows
&& user->cols == user->t_cols
&& user->sc_top == 0
&& user->sc_bot == user->rows - 1)
_scroll_term(user);
else
redraw_term(user, 0);
}
else
{
sy = user->y;
sx = user->x;
move_term(user, user->sc_top, 0);
clreol_term(user);
move_term(user, sy, sx);
}
}
/* Reverse-scroll window.
*/
void
rev_scroll_term(user)
register yuser *user;
{
register int i;
register ychar *c;
int sy, sx;
if(user->sc_bot > user->sc_top)
{
c = user->scr[user->sc_bot];
for(i = user->sc_bot; i > user->sc_top; i--)
user->scr[i] = user->scr[i-1];
user->scr[user->sc_top] = c;
for(i = 0; i < user->cols; i++)
*(c++) = ' ';
if(_rev_scroll_term
&& user->rows == user->t_rows
&& user->cols == user->t_cols
&& user->sc_top == 0
&& user->sc_bot == user->rows - 1)
_rev_scroll_term(user);
else
redraw_term(user, 0);
}
else
{
sy = user->y;
sx = user->x;
move_term(user, user->sc_top, 0);
clreol_term(user);
move_term(user, sy, sx);
}
}
/* Flush window output.
*/
void
flush_term(user)
register yuser *user;
{
_flush_term(user);
}
/* Rub one character.
*/
void
rub_term(user)
register yuser *user;
{
if(user->x > 0)
{
if(user->bump)
{
addch_term(user, ' ');
user->bump = 0;
}
else
{
move_term(user, user->y, user->x - 1);
addch_term(user, ' ');
move_term(user, user->y, user->x - 1);
}
}
}
/* Rub one word.
*/
int
word_term(user)
register yuser *user;
{
register int x, out;
for(x = user->x - 1; x >= 0 && user->scr[user->y][x] == ' '; x--)
continue;
for(; x >= 0 && user->scr[user->y][x] != ' '; x--)
continue;
out = user->x - (++x);
if(out <= 0)
return 0;
move_term(user, user->y, x);
clreol_term(user);
return out;
}
/* Kill current line.
*/
void
kill_term(user)
register yuser *user;
{
if(user->x > 0)
{
move_term(user, user->y, 0);
clreol_term(user);
}
}
/* Expand a tab. We use non-destructive tabs.
*/
void
tab_term(user)
register yuser *user;
{
move_term(user, user->y, (user->x + 8) & 0xfff8);
}
/* Process a newline.
*/
void
newline_term(user)
register yuser *user;
{
register int new_y, next_y;
new_y = user->y + 1;
if(user->flags & FL_RAW)
{
if(new_y > user->sc_bot)
{
if(user->flags & FL_SCROLL)
scroll_term(user);
}
move_term(user, new_y, 0);
}
else
{
if(new_y > user->sc_bot)
{
if(user->flags & FL_SCROLL)
{
scroll_term(user);
move_term(user, user->y, 0);
return;
}
new_y = 0;
}
next_y = new_y + 1;
if(next_y >= user->rows)
next_y = 0;
if(next_y > 0 || !(user->flags & FL_SCROLL))
{
move_term(user, next_y, 0);
clreol_term(user);
}
move_term(user, new_y, 0);
clreol_term(user);
}
}
/* Insert lines.
*/
void
add_line_term(user, num)
register yuser *user;
int num;
{
register ychar *c;
register int i;
if(num == 1 && user->y == 0)
rev_scroll_term(user);
else
{
/* find number of remaining lines */
i = user->rows - user->y - num;
if(i <= 0)
{
i = user->x;
move_term(user, user->y, 0);
clreos_term(user);
move_term(user, user->y, i);
return;
}
/* swap the remaining lines to bottom */
for(i--; i >= 0; i--)
{
c = user->scr[user->y + i];
user->scr[user->y + i] = user->scr[user->y + i + num];
user->scr[user->y + i + num] = c;
}
/* clear the added lines */
for(num--; num >= 0; num--)
{
c = user->scr[user->y + num];
for(i = 0; i < user->cols; i++)
*(c++) = ' ';
}
redraw_term(user, user->y);
}
}
/* Delete lines.
*/
void
del_line_term(user, num)
register yuser *user;
int num;
{
register ychar *c;
register int i;
if(num == 1 && user->y == 0)
scroll_term(user);
else
{
/* find number of remaining lines */
i = user->rows - user->y - num;
if(i <= 0)
{
i = user->x;
move_term(user, user->y, 0);
clreos_term(user);
move_term(user, user->y, i);
return;
}
/* swap the remaining lines to top */
for(; i > 0; i--)
{
c = user->scr[user->rows - i];
user->scr[user->rows - i] = user->scr[user->rows - i - num];
user->scr[user->rows - i - num] = c;
}
/* clear the remaining bottom lines */
for(; num > 0; num--)
{
c = user->scr[user->rows - num];
for(i = 0; i < user->cols; i++)
*(c++) = ' ';
}
redraw_term(user, user->y);
}
}
static void
copy_text(fr, to, count)
register ychar *fr, *to;
register int count;
{
if(to < fr)
{
for(; count > 0; count--)
*(to++) = *(fr++);
}
else
{
fr += count;
to += count;
for(; count > 0; count--)
*(--to) = *(--fr);
}
}
/* Add chars.
*/
void
add_char_term(user, num)
register yuser *user;
int num;
{
register ychar *c;
register int i;
/* find number of remaining non-blank chars */
i = user->cols - user->x - num;
c = user->scr[user->y] + user->cols - num - 1;
while(i > 0 && *c == ' ')
c--, i--;
if(i <= 0)
{
clreol_term(user);
return;
}
/* transfer the chars and clear the remaining */
c++;
copy_text(c - i, c - i + num, i);
for(c -= i; num > 0; num--)
{
*(c++) = ' ';
_addch_term(user, ' ');
}
for(; i > 0; i--)
_addch_term(user, *(c++));
_move_term(user, user->y, user->x);
}
/* Delete chars.
*/
void
del_char_term(user, num)
register yuser *user;
int num;
{
register ychar *c;
register int i;
/* find number of remaining non-blank chars */
i = user->cols - user->x - num;
c = user->scr[user->y] + user->cols - 1;
while(i > 0 && *c == ' ')
c--, i--;
if(i <= 0)
{
clreol_term(user);
return;
}
/* transfer the chars and clear the remaining */
c++;
copy_text(c - i, c - i - num, i);
for(c -= (i + num); i > 0; i--)
_addch_term(user, *(c++));
for(; num > 0; num--)
{
*(c++) = ' ';
_addch_term(user, ' ');
}
_move_term(user, user->y, user->x);
}
/* Redraw a user's window.
*/
void
redraw_term(user, y)
register yuser *user;
register int y;
{
register int x, spaces;
register ychar *c;
for(; y < user->t_rows; y++)
{
_move_term(user, y, 0);
_clreol_term(user);
spaces = 0;
c = user->scr[y];
for(x = 0; x < user->t_cols; x++, c++)
{
if(*c == ' ')
spaces++;
else
{
if(spaces)
{
if(spaces <= 3) /* arbitrary */
{
for(; spaces > 0; spaces--)
_addch_term(user, ' ');
}
else
{
_move_term(user, y, x);
spaces = 0;
}
}
_addch_term(user, *c);
}
}
}
/* redisplay any active menu */
if(menu_ptr != NULL)
update_menu();
else
_move_term(user, user->y, user->x);
}
/* Return the first interesting row for a user with a window of
* the given height and width.
*/
static int
first_interesting_row(user, height, width)
yuser *user;
int height, width;
{
register int j, i;
register ychar *c;
if(height < user->t_rows)
{
j = (user->y + 1) - height;
if(j < 0)
j += user->t_rows;
}
else
{
j = user->y + 1;
if(j >= user->t_rows)
j = 0;
}
while(j != user->y)
{
i = (width > user->t_cols) ? user->t_cols : width;
for(c = user->scr[j]; i > 0; i--, c++)
if(*c != ' ')
break;
if(i > 0)
break;
if(++j >= user->t_rows)
j = 0;
}
return j;
}
/* Called when a user's window has been resized.
*/
void
resize_win(user, height, width)
yuser *user;
int height, width;
{
register int j, i;
register ychar *c, **newscr;
int new_y, y_pos;
if(height == user->t_rows && width == user->t_cols)
return;
/* resize the user terminal buffer */
new_y = -1;
y_pos = 0;
newscr = (ychar **)get_mem(height * sizeof(ychar *));
if(user->scr == NULL)
{
user->t_rows = user->rows = 0;
user->t_cols = user->cols = 0;
}
else if(user->region_set)
{
/* save as many top lines as possible */
for(j = 0; j < height && j < user->t_rows; j++)
newscr[j] = user->scr[j];
new_y = j - 1;
y_pos = user->y;
for(; j < user->t_rows; j++)
free(user->scr[j]);
free(user->scr);
}
else
{
/* shift all recent lines to top of screen */
j = first_interesting_row(user, height, width);
for(i = 0; i < height; i++)
{
newscr[++new_y] = user->scr[j];
if(j == user->y)
break;
if(++j >= user->t_rows)
j = 0;
}
for(i++; i < user->t_rows; i++)
{
if(++j >= user->t_rows)
j = 0;
free(user->scr[j]);
}
y_pos = new_y;
free(user->scr);
}
user->scr = newscr;
/* fill in the missing portions */
if(width > user->t_cols)
for(i = 0; i <= new_y; i++)
{
user->scr[i] = (ychar *)realloc_mem(user->scr[i], width);
for(j = user->t_cols; j < width; j++)
user->scr[i][j] = ' ';
}
for(i = new_y + 1; i < height; i++)
{
c = user->scr[i] = (ychar *)get_mem(width);
for(j = 0; j < width; j++)
*(c++) = ' ';
}
/* reset window values */
user->t_rows = user->rows = height;
user->t_cols = user->cols = width;
user->sc_top = 0;
user->sc_bot = height - 1;
move_term(user, y_pos, user->x);
send_winch(user);
redraw_term(user, 0);
flush_term(user);
}
/* Draw a nice box.
*/
static void
draw_box(user, height, width, c)
yuser *user;
int height, width;
char c;
{
register int i;
if(width < user->t_cols)
{
for(i = 0; i < height; i++)
{
move_term(user, i, width);
addch_term(user, c);
if(width + 1 < user->t_cols)
clreol_term(user);
}
}
if(height < user->t_rows)
{
move_term(user, height, 0);
for(i = 0; i < width; i++)
addch_term(user, c);
if(width < user->t_cols)
addch_term(user, c);
if(width + 1 < user->t_cols)
clreol_term(user);
if(height + 1 < user->t_rows)
{
move_term(user, height + 1, 0);
clreos_term(user);
}
}
}
/* Set the virtual terminal size, ie: the display region.
*/
void
set_win_region(user, height, width)
yuser *user;
int height, width;
{
register int x, y;
int old_height, old_width;
if(height < 2 || height > user->t_rows)
height = user->t_rows;
if(width < 2 || width > user->t_cols)
width = user->t_cols;
/* Don't check if they're already equal; always perform processing.
* Just because it's equal over here doesn't mean it's equal for all
* ytalk connections. We still need to clear the screen.
*/
old_height = user->rows;
old_width = user->cols;
user->rows = user->t_rows;
user->cols = user->t_cols;
if(user->region_set)
{
x = user->x;
y = user->y;
if(width > old_width || height > old_height)
draw_box(user, old_height, old_width, ' ');
}
else
{
x = y = 0;
move_term(user, 0, 0);
clreos_term(user);
user->region_set = 1;
}
draw_box(user, height, width, '%');
/* set the display region */
user->rows = height;
user->cols = width;
user->sc_top = 0;
user->sc_bot = height - 1;
move_term(user, y, x);
flush_term(user);
if(user == me)
send_region();
}
/* Set the virtual terminal size, ie: the display region.
*/
void
end_win_region(user)
yuser *user;
{
int old_height, old_width;
old_height = user->rows;
old_width = user->cols;
user->rows = user->t_rows;
user->cols = user->t_cols;
user->sc_top = 0;
user->sc_bot = user->rows - 1;
if(old_height < user->t_rows || old_width < user->t_cols)
draw_box(user, old_height, old_width, ' ');
user->region_set = 0;
if(user == me)
send_end_region();
}
/* Set the scrolling region.
*/
void
set_scroll_region(user, top, bottom)
yuser *user;
int top, bottom;
{
if(top < 0 || top >= user->rows || bottom >= user->rows || bottom < top
|| (bottom <= 0 && top <= 0))
{
user->sc_top = 0;
user->sc_bot = user->rows - 1;
} else
{
user->sc_top = top;
user->sc_bot = bottom;
}
}
/* Send a message to the terminal.
*/
void
msg_term(user, str)
yuser *user;
char *str;
{
int y;
if((y = user->y + 1) >= user->rows)
y = 0;
_move_term(user, y, 0);
_addch_term(user, '[');
while(*str)
_addch_term(user, *(str++));
_addch_term(user, ']');
_clreol_term(user);
_move_term(user, user->y, user->x);
_flush_term(user);
}
/* Spew terminal contents to a file descriptor.
*/
void
spew_term(user, fd, rows, cols)
yuser *user;
int fd, rows, cols;
{
register ychar *c, *e;
register int len;
int y;
static char tmp[20];
if(user->region_set)
{
y = 0;
if(cols > user->cols)
cols = user->cols;
if(rows > user->rows)
rows = user->rows;
for(;;)
{
for(c = e = user->scr[y], len = cols; len > 0; len--, c++)
if(*c != ' ')
e = c + 1;
if(e != user->scr[y])
(void)write(fd, user->scr[y], e - user->scr[y]);
if(++y >= rows)
break;
(void)write(fd, "\n", 1);
}
/* move the cursor to the correct place */
sprintf(tmp, "%c[%d;%dH", 27, user->y + 1, user->x + 1);
(void)write(fd, tmp, strlen(tmp));
}
else
{
y = first_interesting_row(user, rows, cols);
for(;;)
{
if(y == user->y)
{
if(user->x > 0)
(void)write(fd, user->scr[y], user->x);
break;
}
for(c = e = user->scr[y], len = user->t_cols; len > 0; len--, c++)
if(*c != ' ')
e = c + 1;
if(e != user->scr[y])
(void)write(fd, user->scr[y], e - user->scr[y]);
(void)write(fd, "\n", 1);
if(++y >= user->t_rows)
y = 0;
}
}
}
/* Draw some raw characters to the screen without updating any buffers.
* Whoever uses this should know what they're doing. It should always
* be followed by a redraw_term() before calling any of the normal
* term functions again.
*
* If the given string is not as long as the given length, then the
* string is repeated to fill the given length.
*
* This is an unadvertised function.
*/
void
raw_term(user, y, x, str, len)
yuser *user;
int y, x;
ychar *str;
int len;
{
register ychar *c;
if(y < 0 || y >= user->t_rows)
return;
if(x < 0 || x >= user->t_cols)
return;
_move_term(user, y, x);
for(c = str; len > 0; len--, c++)
{
if(*c == '\0')
c = str;
if (!is_printable(*c))
return;
_addch_term(user, *c);
}
}
int
center(width, n)
int width, n;
{
if(n >= width)
return 0;
return (width - n) >> 1;
}
void
redraw_all_terms()
{
register yuser *u;
switch(term_type)
{
case 1: /* curses */
redisplay_curses();
break;
default:
redraw_term(me, 0);
flush_term(me);
for(u = connect_list; u; u = u->next)
{
redraw_term(u, 0);
flush_term(u);
}
}
}
void
set_raw_term()
{
/* only some terminal systems need to do this */
switch(term_type)
{
case 1: /* curses */
set_raw_curses();
break;
}
}
void
set_cooked_term()
{
/* only some terminal systems need to do this */
switch(term_type)
{
case 1: /* curses */
set_cooked_curses();
break;
}
}
int
term_does_asides()
{
/* only some terminal systems can do this */
switch(term_type)
{
case 2: /* X11 */
return 1;
}
return 0;
}
ytalk-3.0.3-8bit/term.doc 100644 7527 230 20246 5436644652 14215 0 ustar espel i_math93 Terminal I/O requirements:
Every time a user joins a YTalk connection, he is given a window in
which his output will appear. This terminal I/O is modularized in such
a way that YTalk should be able to drive any windowing system or terminal,
as long as someone programs a set of primitive functions.
When init_term() [in term.c] is called from main(), it will select the
appropriate window system, initialize pointers to the appropriate
primitives, and call the init function for that window system. After
this initialization, YTalk will transparently communicate with the
window system by using these primitives. It is therefore important
that each primitive should be implemented exactly the same for each
windowing system. The purpose of this document is to define the
expected behavior of each of the terminal I/O functions.
A valid YTalk 3.0 terminal interface provides an input interface. Each
time the user sends keyboard input, the input should be given to YTalk
by calling this function in comm.c:
void
my_input(user, buf, len) [in comm.c]
yuser *user;
ychar *buf;
int len;
The "user" parameter should be set to the user pointer whose window
the given input was taken from (ie: I had my mouse cursor in the
window assigned to user X and typed some info). The my_input routine
will send the given info to the given user as an aside, ie: no other
users get sent this info. If a terminal interface cannot distinguish
input from various windows, or if you do not wish to bother with this,
then just send "me" as the user. Any input given the "me" user will
get sent to all connected users.
Note that it is much more optimal to call this function once with a
batch of input characters rather than calling this function once for
each character.
A valid YTalk 3.0 terminal interface provides these output functions:
void
init_???()
This is called when the terminal interface has been selected for
use. It should initialize any static variables and start any necessary
connections. It should not open or create any user windows.
Input processing (ie: calls to my_input() in comm.c) should begin after
this initialization function is called.
----------------------
void
end_???()
This is called before YTalk exits. All open windows should be shut
down, any memory should be freed, and any connections should be
terminated. Consider your terminal interface worthy if it can survive
this test indefinitely:
for(;;)
{
init_???();
end_???();
}
----------------------
int
open_???(user, title)
yuser *user;
char *title;
Zero should be returned on success; any other value will be interpreted
as an error.
This function should open a new window with the given title and assigned
to the given user. All future calls which affect this window will be
passed the same user pointer. Since the yuser structure is not passed
between clients, you may add any variables you wish to the structure
as long as you comment them as part of your terminal interface.
The terminal interface should never modify any of the other fields in
the yuser structure, especially the window height and width fields. These
should only be set by calling the resize_win() [term.c] function.
The cursor position should be preset to 0,0.
The window size is assumed to be 80 columns by 24 rows. If this is
not the case, you are required to call the function resize_win() [term.c]
with the appropriate height and width values. I suggest you always call
resize_win() from within open_???().
void
resize_win(user, height, width) [in term.c]
yuser *user;
int height, width;
----------------------
void
close_???(user)
yuser *user;
This will close the window assigned to the given user and free any
attached memory. Again, imagine the test:
for(;;)
{
open_???(user, "test");
close_???(user);
}
----------------------
void
addch_???(user, char)
yuser *user;
ychar char;
This will add the given character to the window, following the terminal
I/O rules listed below.
----------------------
void
move_???(user, y, x)
yuser *user;
int y, x;
This will move the cursor (the next output location) to the given Y,X
coordinates, following the terminal I/O rules listed below.
----------------------
void
clreol_???(user)
yuser *user;
This will clear all characters from (and including) the current cursor
position to the end of the line. The cursor position does not change.
----------------------
void
clreos_???(user)
yuser *user;
This will clear all characters from (and including) the current cursor
position to the end of the screen. The cursor position does not change.
----------------------
void
scroll_???(user)
yuser *user;
This will scroll the window up one line, losing the line at the top
of the window and creating a BLANK line at the bottom of the window.
The cursor's X and Y positions do not change.
This function can be implemented using the other primitives, so it
is therefore optional. I strongly recommend that it be included, as
it will no doubt be faster than the version implemented through the
primitives. If it is not available, then _scroll_term should be
set to NULL in term.c.
----------------------
void
rev_scroll_???(user)
yuser *user;
This will revserse-scroll the window up one line, losing the line at
the bottom of the window and creating a BLANK line at the top of the
window. The cursor's X and Y positions do not change.
This function can be implemented using the other primitives, so it
is therefore optional. I strongly recommend that it be included, as
it will no doubt be faster than the version implemented through the
primitives. If it is not available, then _rev_scroll_term should be
set to NULL in term.c.
----------------------
void
flush_???(user)
yuser *user;
If your window driver optimizes I/O by queuing updates and sending
batches of changes at a time, this function should flush any pending
output. If your window driver does not require flushes, then this
function should do nothing.
----------------------
Terminal I/O Rules:
[ For simplicity, I'll use "maxrows" to mean the maximum number of ]
[ rows and "maxcols" to mean the maximum number of columns in a window. ]
When a window is initially opened, the cursor position should start
at the upper left-hand corner. This position is Y=0,X=0, or (0,0).
The Y position is always given first and corresponds to the row
number, starting at zero and ending at (maxrows-1). The X position
is always given second and corresponds to the column number, starting
at zero and ending at (maxcols-1).
Every window is required to have at least two rows, and each row should
have at least 20 columns.
Every time a character is added to the window, it should be placed
at the cursor's current Y,X position, clearing and overwriting any
character which may already be there. Then, the cursor's X position
should be incremented by one. If the X position is now greater than
or equal to maxcols, then the X position should be set back to
(maxcols-1). THERE IS NO DEFINITION FOR WRAPPING. The cursor's
Y position is never incremented as a result of X being too large.
Instead, X is maintained at (maxcols-1) until move_???() is called
to move the cursor.
Since there is no definition for wrapping, it follows that there is
no definition for automatic scrolling. A window should only scroll
when scroll_???() is called explicitly. Note that some terminals
will scroll automatically when a character is placed in the lower
right-hand corner. If this is the case with your system, I suggest
you tell YTalk that your terminal is actually one row shorter. You
could tell YTalk it is one column skinnier, but this effect can
be visually displeasing.
The terminal interface will only be asked to display printable
characters. These are the characters in the decimal range from
32 [space] to 126 [tilde] inclusive. Therefore, the addch_???()
procedure need not consider how to display control characters or
high-bit characters, because these will never be sent.
Similarly, the move_???() procedure will never be called with
Y or X values outside the range of the current window.
-- EOF --
ytalk-3.0.3-8bit/user.c 100644 7527 230 15503 6104257355 13672 0 ustar espel i_math93 /* user.c -- user database */
/* NOTICE
*
* Copyright (c) 1990,1992,1993 Britt Yenne. All rights reserved.
*
* This software is provided AS-IS. The author gives no warranty,
* real or assumed, and takes no responsibility whatsoever for any
* use or misuse of this software, or any damage created by its use
* or misuse.
*
* This software may be freely copied and distributed provided that
* no part of this NOTICE is deleted or edited in any manner.
*
*/
/* Mail comments or questions to ytalk@austin.eds.com */
#include "header.h"
#include "menu.h"
#include <pwd.h>
extern char *getlogin();
yuser *me; /* my user information */
yuser *user_list; /* list of invited/connected users */
yuser *connect_list; /* list of connected users */
yuser *wait_list; /* list of connected users */
yuser *fd_to_user[MAX_FILES]; /* convert file descriptors to users */
yuser *key_to_user[128]; /* convert menu ident chars to users */
ylong def_flags = 0L; /* default FL_* flags */
static ylong daemon_id; /* running daemon ID counter */
/* ---- local functions ----- */
static int passwd_opened = 0;
static int
user_id(name)
char *name;
{
register struct passwd *pw;
passwd_opened = 1;
if((pw = getpwnam(name)) == NULL)
return -60000; /* for most archs, an impossible user ID */
return pw->pw_uid;
}
static char *
user_name(uid)
int uid;
{
register struct passwd *pw;
passwd_opened = 1;
if((pw = getpwuid(uid)) == NULL)
return NULL;
return pw->pw_name;
}
static void
close_passwd()
{
if(passwd_opened)
{
(void)endpwent();
passwd_opened = 0;
}
}
static void
generate_full_name(user)
yuser *user;
{
register char *c, *d, *ce;
if(user->full_name == NULL)
user->full_name = get_mem(50);
c = user->full_name, ce = user->full_name + 49;
for(d = user->user_name; *d && c < ce; d++)
*(c++) = *d;
if(c < ce)
*(c++) = '@';
for(d = user->host_name; *d && c < ce; d++)
*(c++) = *d;
if(user->tty_name[0])
{
if(c < ce)
*(c++) = '#';
for(d = user->tty_name; *d && c < ce; d++)
*(c++) = *d;
}
*c = '\0';
}
static void
assign_key(user)
yuser *user;
{
register ychar old;
static ychar key = 'a';
if(user->key != '\0' || user == me || user_list == NULL)
return;
old = key;
do {
if(key_to_user[key] == NULL)
{
key_to_user[key] = user;
user->key = key;
return;
}
if(key == 'z')
key = 'A';
else if(key == 'Z')
key = 'a';
else
key++;
} while(old != key);
user->key = '\0';
}
/* ---- global functions ----- */
/* Initialize user data structures.
*/
void
init_user()
{
int my_uid;
char *my_name;
char my_host[100];
user_list = NULL;
connect_list = NULL;
wait_list = NULL;
daemon_id = getpid() << 10;
(void)memset(fd_to_user, 0, MAX_FILES * sizeof(yuser *));
(void)memset(key_to_user, 0, 128 * sizeof(yuser *));
my_uid = getuid();
/* get my username */
if((my_name = getlogin()) != NULL)
if(user_id(my_name) != my_uid)
my_name = NULL;
if(my_name == NULL)
my_name = user_name(my_uid);
if(my_name == NULL)
my_name = getlogin();
if(my_name == NULL || my_name[0] == '\0')
{
show_error("Who are you?");
bail(YTE_ERROR);
}
/* get my hostname */
if(gethostname(my_host, 100) < 0)
{
show_error("init_user: gethostname() failed");
bail(YTE_ERROR);
}
/* get my user record */
if((me = new_user(my_name, my_host, NULL)) == NULL)
bail(YTE_ERROR);
me->remote.protocol = YTP_NEW;
me->remote.vmajor = VMAJOR;
me->remote.vminor = VMINOR;
me->remote.pid = getpid();
close_passwd();
}
/* Create a new user record.
*/
yuser *
new_user(name, hostname, tty)
char *name, *hostname, *tty;
{
register yuser *out, *u;
ylong addr;
/* find the host address */
if(hostname == NULL || *hostname == '\0')
{
hostname = me->host_name;
addr = me->host_addr;
}
else if((addr = get_host_addr(hostname)) == (ylong)-1)
{
sprintf(errstr, "new_user: bad host: '%s'", hostname);
show_error(errstr);
return NULL;
}
/* create the user record */
out = (yuser *)get_mem(sizeof(yuser));
(void)memset(out, 0, sizeof(yuser));
out->user_name = str_copy(name);
out->host_name = str_copy(hostname);
out->host_addr = addr;
if(tty)
out->tty_name = str_copy(tty);
else
out->tty_name = str_copy("");
out->d_id = daemon_id++;
generate_full_name(out);
assign_key(out);
out->flags = def_flags;
/* Actually make an effort to keep the user list in order */
if(user_list == NULL || out->key <= user_list->key)
{
out->unext = user_list;
user_list = out;
}
else
{
for(u = user_list; u->unext != NULL; u = u->unext)
if(out->key <= u->unext->key)
break;
out->unext = u->unext;
u->unext = out;
}
return out;
}
void
free_user(user)
yuser *user;
{
register yuser *u;
/* remove him from the various blacklists */
if(user == user_list)
user_list = user->unext;
else
for(u = user_list; u; u = u->unext)
if(u->unext == user)
{
u->unext = user->unext;
break;
}
if(user == connect_list)
connect_list = user->next;
else
for(u = connect_list; u; u = u->next)
if(u->next == user)
{
u->next = user->next;
break;
}
if(user == wait_list)
wait_list = user->next;
else
for(u = wait_list; u; u = u->next)
if(u->next == user)
{
u->next = user->next;
break;
}
/* close him down */
if(connect_list == NULL
&& wait_list == NULL
&& menu_ptr == NULL
&& running_process == 0)
bail(0);
close_term(user);
free(user->full_name);
free(user->user_name);
free(user->host_name);
free(user->tty_name);
if(user->dbuf)
free(user->dbuf);
if(user->output_fd > 0)
close(user->output_fd);
if(user->fd)
{
remove_fd(user->fd);
fd_to_user[user->fd] = NULL;
close(user->fd);
}
if(user->key != '\0')
key_to_user[user->key] = NULL;
free(user);
if(connect_list == NULL && wait_list != NULL)
msg_term(me, "Waiting for connection...");
user_winch = 1;
}
/* Find a user by name/host/pid. If name is NULL, then it is not checked.
* If host_addr is (ylong)-1 then it is not checked. If pid is (ylong)-1
* then it is not checked.
*/
yuser *
find_user(name, host_addr, pid)
char *name;
ylong host_addr, pid;
{
register yuser *u;
for(u = user_list; u; u = u->unext)
if(name == NULL || strcmp(u->user_name, name) == 0)
if(host_addr == (ylong)-1 || u->host_addr == host_addr)
if(pid == (ylong)-1 || u->remote.pid == pid)
return u;
/* it could be _me_! */
if(name == NULL || strcmp(me->user_name, name) == 0)
if(host_addr == (ylong)-1 || me->host_addr == host_addr)
if(pid == (ylong)-1 || me->remote.pid == pid)
return me;
/* nobody I know */
return NULL;
}
ytalk-3.0.3-8bit/xwin.c 100644 7527 230 22305 5436644661 13706 0 ustar espel i_math93 /* xwin.c -- X Window Terminal Interface */
/* NOTICE
*
* Copyright (c) 1990,1992,1993 Britt Yenne. All rights reserved.
*
* This software is provided AS-IS. The author gives no warranty,
* real or assumed, and takes no responsibility whatsoever for any
* use or misuse of this software, or any damage created by its use
* or misuse.
*
* This software may be freely copied and distributed provided that
* no part of this NOTICE is deleted or edited in any manner.
*
*/
/* Mail comments or questions to ytalk@austin.eds.com */
#ifdef USE_X11
#include "header.h"
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xresource.h>
static Display *display; /* display */
static Window rootwin; /* root window */
static int screen_num; /* screen number */
static XrmDatabase db; /* resource database */
static XFontStruct *text_font; /* font */
static GC textGC, /* text graphic context */
invertGC; /* graphic context for inverts */
static ylong whitepix, /* white pixel */
blackpix; /* black pixel */
static int font_width, /* font width */
font_height, /* font height */
font_ascent; /* font ascent */
#define YPOS(p) ((p) * font_height)
#define XPOS(p) ((p) * font_width)
/* ----- local functions ----- */
static XTextProperty *
strToTP(s)
char *s;
{
XTextProperty *tp = (XTextProperty *)get_mem(sizeof(XTextProperty));
XStringListToTextProperty(&s, 1, tp);
return tp;
}
static char *
getOption(o)
char *o;
{
XrmValue value;
char *type;
if(XrmGetResource(db, o, o, &type, &value))
{
if(value.addr == NULL)
return NULL;
if(strcmp(value.addr, "false") == 0)
return NULL;
if(strcmp(value.addr, "False") == 0)
return NULL;
return value.addr;
}
else
return (char *)NULL;
}
static void
load_font(name, font)
char *name;
XFontStruct **font;
{
if((*font = XLoadQueryFont(display, name)) == NULL)
{
sprintf(errstr, "Cannot load font %s", name);
show_error(errstr);
bail(YTE_ERROR);
}
}
static void
make_GC(gc, font, fgpixel, bgpixel, l_width, l_style, l_cap, l_join, gcfunc)
GC *gc;
XFontStruct *font;
ylong fgpixel, bgpixel;
int l_width, l_style, l_cap, l_join, gcfunc;
{
ylong mask = 0;
XGCValues values;
if(font != NULL)
{
values.font = font->fid;
mask |= GCFont;
}
values.foreground = fgpixel;
values.background = bgpixel;
values.line_width = l_width;
values.line_style = l_style;
values.cap_style = l_cap;
values.join_style = l_join;
mask |= GCForeground | GCBackground | GCLineWidth | GCLineStyle |
GCCapStyle | GCJoinStyle;
if(gcfunc != -1)
{
values.function = gcfunc;
mask |= GCFunction;
}
*gc = XCreateGC(display, rootwin, mask, &values);
}
/* Find the user who owns a given Window.
*/
static yuser *
win_user(win)
Window win;
{
register yuser *u;
for(u = user_list; u; u = u->unext)
if(u->win == win)
break;
return u;
}
#define TWIN report.xany.window
static void
process_event()
{
register int n;
register yuser *user;
static XEvent report;
static char buf[512];
while(XPending(display))
{
XNextEvent(display, &report);
switch(report.type)
{
case Expose:
if(report.xexpose.count)
break;
if((user = win_user(TWIN)) != NULL)
redraw_term(user, 0);
break;
case ConfigureNotify: /* RESIZED (or moved) */
if((user = win_user(TWIN)) != NULL)
{
int rows, cols;
rows = report.xconfigure.height / font_height;
cols = report.xconfigure.width / font_width;
resize_win(user, rows, cols);
}
break;
case KeyPress:
n = XLookupString((XKeyEvent *) &report, buf, 512, NULL, NULL);
my_input(win_user(report.xkeymap.window), buf, n);
break;
}
}
}
static void
place_cursor(win, y, x)
Window win;
int y, x;
{
XFillRectangle(display, win, invertGC,
XPOS(x), YPOS(y),
font_width, font_height);
}
/* ----- global functions ----- */
/* Initialize X Windows.
*/
void
init_xwin()
{
char *xrmstr;
char *displayName;
char *rfn, str[256];
int xfd;
XGCValues values;
/* get and open the display */
displayName = getOption("YTalk.display");
if((display = XOpenDisplay(displayName)) == NULL)
{
show_error("Cannot open X display");
bail(YTE_ERROR);
}
rootwin = DefaultRootWindow(display);
screen_num = DefaultScreen(display);
/* read all options */
db = NULL;
XrmInitialize();
if((xrmstr = XResourceManagerString(display)) != NULL)
db = XrmGetStringDatabase(xrmstr);
else if((rfn = (char *)getenv("XENVIRONMENT")) != NULL
&& access(rfn, 0) == 0)
db = XrmGetFileDatabase(rfn);
else if((rfn = (char *)getenv("HOME")) != NULL)
{
sprintf(str, "%s/.Xdefaults", rfn);
if(access(str, 0) == 0)
db = XrmGetFileDatabase(str);
}
if(db == NULL)
db = XrmGetStringDatabase("");
if(getOption("YTalk.reverse"))
{
whitepix = BlackPixel(display, screen_num);
blackpix = WhitePixel(display, screen_num);
}
else
{
blackpix = BlackPixel(display, screen_num);
whitepix = WhitePixel(display, screen_num);
}
/* load font and graphic context */
if((rfn = getOption("YTalk.font")) == NULL)
rfn = "9x15";
load_font(rfn, &text_font);
font_width = text_font->max_bounds.rbearing;
font_height = text_font->max_bounds.ascent + text_font->max_bounds.descent;
font_ascent = text_font->max_bounds.ascent;
make_GC(&textGC, text_font, blackpix, whitepix,
2, LineSolid, CapRound, JoinRound, -1);
make_GC(&invertGC, text_font, blackpix, whitepix,
2, LineSolid, CapRound, JoinRound, GXinvert);
values.plane_mask = blackpix ^ whitepix;
XChangeGC(display, invertGC, GCPlaneMask, &values);
/* set up event processing */
xfd = ConnectionNumber(display);
add_fd(xfd, process_event);
}
/* End X Windows.
*/
void
end_xwin()
{
XCloseDisplay(display);
}
/* Open a new window.
*/
int
open_xwin(user, title)
yuser *user;
char *title;
{
XWMHints WMhints;
XClassHint ClassHints;
XSizeHints size;
XTextProperty *name;
Window win;
int rows, cols;
size.x = 0;
size.y = 0;
size.width = 80;
size.height = 24;
size.min_width = 20;
size.min_height = 2;
size.width_inc = font_width;
size.height_inc = font_height;
size.flags = PSize | PMinSize | PResizeInc;
if(getOption("YTalk.geometry"))
{
XParseGeometry(getOption("YTalk.geometry"),
&size.x, &size.y, (u_int *)&size.width, (u_int *)&size.height);
/* don't set USPosition -- it confuses tvtwm */
}
rows = size.height;
cols = size.width;
size.width *= font_width;
size.height *= font_height;
size.min_width *= font_width;
size.min_height *= font_height;
win = XCreateSimpleWindow(display, rootwin, size.x, size.y,
size.width, size.height, 4, blackpix, whitepix);
if(win == (Window)0)
return -1;
WMhints.flags = InputHint;
WMhints.input = 1;
ClassHints.res_name = "ytalk";
ClassHints.res_class = "YTalk";
name = strToTP(title);
XSetWMProperties(display, win, name, name,
0, 0, &size, &WMhints, &ClassHints);
XSelectInput(display, win, ExposureMask | KeyPressMask
| StructureNotifyMask);
XMapRaised(display, win);
user->win = win;
user->ty = user->tx = 0;
place_cursor(win, 0, 0);
resize_win(user, rows, cols);
return 0;
}
void
close_xwin(user)
yuser *user;
{
XDestroyWindow(display, user->win);
user->win = (Window)0;
}
void
addch_xwin(user, ch)
yuser *user;
ychar ch;
{
XClearArea(display, user->win,
XPOS(user->tx), YPOS(user->ty),
font_width, font_height,
False);
XDrawString(display, user->win, textGC,
XPOS(user->tx), YPOS(user->ty) + font_ascent,
&ch, 1);
user->tx++;
if(user->tx >= user->t_cols)
user->tx--;
place_cursor(user->win, user->ty, user->tx);
}
void
move_xwin(user, y, x)
yuser *user;
int y, x;
{
place_cursor(user->win, user->ty, user->tx);
user->ty = y;
user->tx = x;
place_cursor(user->win, user->ty, user->tx);
}
void
clreol_xwin(user)
yuser *user;
{
XClearArea(display, user->win,
XPOS(user->tx), YPOS(user->ty),
0, font_height,
False);
place_cursor(user->win, user->ty, user->tx);
}
void
clreos_xwin(user)
yuser *user;
{
XClearArea(display, user->win,
XPOS(user->tx), YPOS(user->ty),
0, font_height,
False);
XClearArea(display, user->win,
0, YPOS(user->ty + 1),
0, 0,
False);
place_cursor(user->win, user->ty, user->tx);
}
void
scroll_xwin(user)
yuser *user;
{
place_cursor(user->win, user->ty, user->tx);
XCopyArea(display, user->win, user->win, textGC,
XPOS(0), YPOS(1),
XPOS(user->t_cols), YPOS(user->t_rows - 1),
XPOS(0), YPOS(0));
XClearArea(display, user->win,
0, YPOS(user->t_rows - 1),
0, font_height,
False);
place_cursor(user->win, user->ty, user->tx);
}
void
rev_scroll_xwin(user)
yuser *user;
{
place_cursor(user->win, user->ty, user->tx);
XCopyArea(display, user->win, user->win, textGC,
XPOS(0), YPOS(0),
XPOS(user->t_cols), YPOS(user->t_rows - 1),
XPOS(0), YPOS(1));
XClearArea(display, user->win,
XPOS(0), YPOS(0),
0, font_height,
False);
place_cursor(user->win, user->ty, user->tx);
}
void
flush_xwin(user)
yuser *user;
{
/* "user" is unused -- sorry, lint :-) */
XFlush(display);
}
#endif
ytalk-3.0.3-8bit/xwin.h 100644 7527 230 1777 5435465462 13704 0 ustar espel i_math93 /* xwin.h -- X Windows interface (xwin.c) */
/* NOTICE
*
* Copyright (c) 1990,1992,1993 Britt Yenne. All rights reserved.
*
* This software is provided AS-IS. The author gives no warranty,
* real or assumed, and takes no responsibility whatsoever for any
* use or misuse of this software, or any damage created by its use
* or misuse.
*
* This software may be freely copied and distributed provided that
* no part of this NOTICE is deleted or edited in any manner.
*
*/
/* Mail comments or questions to ytalk@austin.eds.com */
#ifdef USE_X11
extern void init_xwin ();
extern void end_xwin ();
extern int open_xwin ( /* yuser, title */ );
extern void close_xwin ( /* yuser */ );
extern void addch_xwin ( /* yuser, char */ );
extern void move_xwin ( /* yuser, y, x */ );
extern void clreol_xwin ( /* yuser */ );
extern void clreos_xwin ( /* yuser */ );
extern void scroll_xwin ( /* yuser */ );
extern void rev_scroll_xwin ( /* yuser */ );
extern void flush_xwin ( /* yuser */ );
#endif
/* EOF */
ytalk-3.0.3-8bit/ytalk.1 100644 7527 230 31735 6241652056 13762 0 ustar espel i_math93 '''
''' Ytalk Version 3
'''
.de Sh
.br
.ne 5
.PP
\fB\\$1\fR
.PP
..
.TH YTalk 1 "20 Nov 1993"
.SH NAME
ytalk - A multi-user chat program.
.SH SYNOPSIS
ytalk [-x] [-s] [-Y] [-i] username...
.SH DESCRIPTION
.I YTalk V3.0 Patch Level 2
.PP
YTalk is in essence a multi-user chat program. It works almost exactly like
the UNIX talk program and even communicates with the same talk daemon(s), but
YTalk allows for multiple connections.
The
.I username
field may be formatted in several different ways:
.br
name - some user on your machine
.br
name@host - some user on a different machine
.br
name#tty - some user on a particular terminal
.br
name#tty@host - some user on a particular tty on a
.br
different machine
.br
name@host#tty - same as "name#tty@host"
.PP
You can specify multiple user names on the command line, ie:
.sp
ytalk george fred@hissun.edu marc@grumpy.cc
.PP
The -x option disables the X11 interface (described below).
.PP
The -s option starts your YTalk window in a shell.
.PP
The -i option disables the auto-invite port (meaning you won't see
"talk to blah@blah.com", but your talk daemon will beep you instead).
.PP
The -Y option requires a capital Y or N as an answer to any yes/no
question.
.PP
For each user on the command line, YTalk will attempt to connect to the talk
daemon on the specified user's host and determine if that user has left an
invitation for you to call. If not, YTalk leaves an invitation for him
and tells his talk daemon to send an announcement to his screen.
There is not yet a dedicated YTalk daemon, but there will be. Right now,
YTalk is able to communicate with BOTH existing versions of UNIX talk
daemons. For any particular host, YTalk will attempt to communicate with a
talk daemon the caller's host also supports. If the two hosts have no daemon
in common, then UNIX talk will not function at all, but a connection is
possible through (and only through) YTalk.
.PP
Once a connection has been established between two users, they can chat back
and forth to their hearts' content. The connection is terminated when one
of them hits control-C or selects quit off the main menu.
.PP
YTalk is perfectly compatible with UNIX talk and they can even converse
with each other without any problems. However, many of the features of YTalk
can only operate when you are connected to a user who is also using YTalk.
For the rest of this document, it will be assumed that all connected users
are using YTalk, unless otherwise stated.
.PP
If you specified more than one user on the YTalk command line, then YTalk
will process and add each user to the conversation as they respond to your
invitation. As each new user enters the conversation, the screen is further
subdivided into smaller and smaller windows, one for each connected user.
Right now, the number of connected users is limited by the number of lines
on your terminal (or window), for each connected user needs at least three
lines.
.PP
YTalk does implement primitive support of the X11 Windowing System. If the
environment variable DISPLAY is set, then YTalk attempts to connect to that
X server. Further details about the X11 interface (and how to turn it off)
are given below.
.PP
As each new user is added to the conversation, YTalk will transmit information
about that user to all other connected YTalk users so that their screens will
also subdivide and incorporate the new user. If the new user is using UNIX
talk, then information about him will NOT be transmitted, for his screen
would be unable to accept multiple connections. I have given brief thought
to allowing at least the output of UNIX talk users to be transmitted to
all connected YTalk users, but I have not written any code to do so. Note
that even though UNIX talk cannot handle multiple connections, it is still
possible for YTalk to handle multiple UNIX "talk" connections. For example,
george (using YTalk) could communicate with fred and joe (both using UNIX
talk), but fred and joe would be unaware of each other. The best way to
understand the limitations that UNIX "talk" places on YTalk is to test
various connections between the two and see how things work.
.PP
.SH ESCAPE MENU
Whenever you are using YTalk, you can hit the
.I ESCAPE
key to bring up a menu which at this
moment has these options:
.sp
a: add a user
.br
d: delete a user
.br
k: kill all unconnected
.br
o: options
.br
s: shell
.br
u: user list
.br
w: output user to file
.br
q: quit
.PP
By choosing option "a", you are given the opportunity to type the name