Filewatcher File Search
FTP Search
  
Directory 
  
Content Search 
   
pkg://ytalk-3.0.3-2.src.rpm:57232/ytalk-3.0.3-8bit.tar.gz  info  downloads

ytalk-3.0.3-8bit/ 40755   7527    230           0  6241653071  12424 5ustar  espeli_math93ytalk-3.0.3-8bit/Imakefile100644   7527    230        6262  6241653057  14344 0ustar  espeli_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/Makefile100644   7527    230        6256  6241653065  14175 0ustar  espeli_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/Manifest100644   7527    230         252  5435465461  14201 0ustar  espeli_math93README
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/README100644   7527    230       13177  6241652201  13424 0ustar  espeli_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.c100644   7527    230       70375  6241652261  13654 0ustar  espeli_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.c100644   7527    230       16334  5473663545  13671 0ustar  espeli_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.h100644   7527    230        2161  5436644653  13645 0ustar  espeli_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.c100644   7527    230       12401  6063707606  13635 0ustar  espeli_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.c100644   7527    230       13633  5473663543  13317 0ustar  espeli_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.h100644   7527    230       31223  6241652555  14151 0ustar  espeli_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.c100644   7527    230       10667  6241651534  13645 0ustar  espeli_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.c100644   7527    230       45232  6033543072  13655 0ustar  espeli_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.h100644   7527    230        3111  5435465461  13642 0ustar  espeli_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/poster100644   7527    230        2712  5435465462  13773 0ustar  espeli_math93Subject: 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.p1100644   7527    230        3030  5436645132  14277 0ustar  espeli_math93This 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.c100644   7527    230       10721  6241651560  13313 0ustar  espeli_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.c100644   7527    230       53115  6241651570  14204 0ustar  espeli_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.h100644   7527    230        5030  5436644653  14173 0ustar  espeli_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.c100644   7527    230       54465  6241652674  13702 0ustar  espeli_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, &ltchars) < 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, &ltchars);
#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.doc100644   7527    230       20246  5436644652  14215 0ustar  espeli_math93Terminal 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.c100644   7527    230       15503  6104257355  13672 0ustar  espeli_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.c100644   7527    230       22305  5436644661  13706 0ustar  espeli_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.h100644   7527    230        1777  5435465462  13704 0ustar  espeli_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.1100644   7527    230       31735  6241652056  13762 0ustar  espeli_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 
Results 1 - 1
Help - FTP Sites List - Software Dir.
Searching half a billion files worldwide
© 1997-2009 MARUHN Internet Solutions