home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Usenet 1994 October
/
usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso
/
unix
/
volume27
/
ytalk-3.0
/
part01
/
comm.c
next >
Wrap
C/C++ Source or Header
|
1993-08-20
|
28KB
|
1,313 lines
/* 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;
u_long 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)) == (u_long)-1)
{
errno = 0;
sprintf(errstr, "unknown host: '%s'\n", 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, (u_long)-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, (u_long)-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;
u_long 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)) == (u_long)-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);
if(user->remote.vmajor <= 2)
spew_term(me, fd, parm.rows, parm.cols);
/* else 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->user_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;
move_term(user, user->y, x - i);
clreol_term(user);
newline_term(user);
for(i--; i >= 1; i--)
addch_term(user, temp[i]);
}
/* 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(send_dgram(user, ANNOUNCE) != 0)
return -1;
return 0;
}
/* ---- 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 = 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 = time(NULL);
if(send_announce && announce(user) < 0)
{
(void)send_dgram(user, DELETE_INVITE);
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;
long t;
static char estr[80];
static u_long last_auto = 0;
int answer;
t = 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 = time(NULL);
if(!(def_flags & FL_RING))
{
if(input_flag)
continue;
sprintf(estr, "Rering %s?", u->full_name);
answer = yes_no(estr);
t = time(NULL);
if(answer == 'n')
continue;
}
if(announce(u) < 0)
{
(void)send_dgram(u, DELETE_INVITE);
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);
}
void
send_users(buf, len)
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;
}
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]);
user->got_esc = 0;
break;
default:
user->got_esc = 0;
}
if(user->got_esc == 0)
{
len--, buf++;
break;
}
}
}
for(; len > 0; len--, buf++)
{
if(*buf >= ' ' && *buf <= '~')
{
if((user->flags & FL_WRAP) && user->x + 1 >= user->cols)
{
if(*buf == ' ')
newline_term(user);
else
{
word_wrap(user);
addch_term(user, *buf);
}
}
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(buf, len)
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 */
c = buf;
for(; len > 0; buf++, len--)
{
if(*buf == me->old_rub)
*buf = me->RUB;
else if(*buf == '\r')
*buf = '\n';
else if(*buf == 3) /* Ctrl-C */
bail(0);
else if(*buf == 27) /* Esc */
break;
}
if((i = buf - c) > 0)
{
show_input(me, c, i);
send_users(c, i);
}
}
/* start a menu if necessary */
if(len > 0)
{
buf++;
len--;
show_main_menu();
if(len <= 0)
update_menu();
}
}
}
void
lock_flags(flags)
u_long 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);
}