source to "phone" system (part 2 of 4)
Jonathan C. Broome
broome at ucbvax.BERKELEY.EDU
Sun Dec 29 18:40:59 AEST 1985
#-----cut here-----cut here-----cut here-----cut here-----
#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create the files:
# Makefile
# defs.h
# alias.c
# calls.c
# check_invite.c
# connect_daemon.c
# cmd.c
# getdaemon.c
# kb.c
# main.c
# message.c
# misc.c
# names.c
# parse.c
# readctl.c
# readrc.c
# readstream.c
# runprog.c
# sendit.c
# set.c
# sig.c
# stop.c
# strsave.c
# tilde.c
# timer.c
# who.c
# windows.c
# This archive created: Sat Dec 28 01:11:00 1985
export PATH; PATH=/bin:$PATH
mkdir client
cd client
echo shar: extracting "'Makefile'" '(4691 characters)'
if test -f 'Makefile'
then
echo shar: will not over-write existing file "'Makefile'"
else
cat << \!Funky!Stuff! > 'Makefile'
#
# Makefile for phone client 20 December 1985
#
# What all this stuff means::
# SERVICES - define this is you have an entry for phone in /etc/services
# PORT - if SERVICES is not defined, set this to the port number that
# phone should use (this overrides the default)
# LOCAL_ECHO - if this is defined, what the user types will be echoed
# locally, and the remote echo will be ignored. The option
# is really an old relic, and should always be on.
#
# Also:
# o Add "-lresolv" to the library if you want to use the network name
# server and have the library routines.
# o Add "-Dvoid=int" if your compiler doesn't know about the void type.
CC = cc
CFLAGS = -O -DSERVICES -DLOCAL_ECHO
LIBS = -lcurses -ltermlib -lresolv
LPR = lpr -Pvax
RDEST = /usr/local/phone
HDRS = defs.h
SRCS = alias.c calls.c check_invite.c connect_daemon.c cmd.c \
getdaemon.c kb.c main.c message.c misc.c names.c \
parse.c readctl.c readrc.c readstream.c runprog.c \
sendit.c set.c sig.c stop.c strsave.c tilde.c timer.c \
who.c windows.c
OBJS = alias.o calls.o check_invite.o connect_daemon.o cmd.o \
getdaemon.o kb.o main.o message.o misc.o names.o \
parse.o readctl.o readrc.o readstream.o runprog.o \
sendit.o set.o sig.o stop.o strsave.o tilde.o timer.o \
who.o windows.o
DEST = phone
.DEFAULT:
co $<
all: ${DEST}
${DEST}: ${OBJS}
/bin/rm -f ${DEST}
${CC} ${CFLAGS} -o ${DEST} ${OBJS} ${LIBS}
${OBJS}: ${HDRS}
install: ${DEST}
/bin/rm -f ${RDEST}
cp ${DEST} ${RDEST}
print: ${HDRS} ${SRCS}
pr -f ${HDRS} ${SRCS} | expand -4 | ${LPR}
tags: /dev/null
ctags -w ${HDRS} ${SRCS}
tar: ${HDRS} ${SRCS}
tar cf client.tar ${HDRS} ${SRCS} Makefile
shar: Makefile ${HDRS} ${SRCS}
shar -v Makefile ${HDRS} ${SRCS} > ../shar.client
clean:
/bin/rm -f ${OBJS} ${DEST} core
lint: ${HDRS} ${SRCS}
lint ${CFLAGS} ${SRCS} > lint.out
dist: ${DEST}
-rcp ${DEST} buddy:${RDEST}
-rcp ${DEST} franny:${RDEST}
-rcp ${DEST} holden:${RDEST}
-rcp ${DEST} seymour:${RDEST}
-rcp ${DEST} zooey:${RDEST}
-rcp ${DEST} miro:${RDEST}
-rcp ${DEST} cory:${RDEST}
depend: ${SRCS}
mv Makefile makefile.old
sed '/^# Dependencies follow/,$$d' makefile.old > Makefile
echo '# Dependencies follow' >> Makefile
includes -so ${SRCS} >> Makefile
echo ' ' >> Makefile
echo '# IF YOU PUT STUFF HERE IT WILL GO AWAY' >> Makefile
echo '# see depend: above' >> Makefile
# DO NOT DELETE THE FOLLOWING LINE
# Dependencies follow
who.o: /usr/include/sys/file.h
who.o: /usr/include/utmp.h
runprog.o: /usr/include/fcntl.h
runprog.o: /usr/include/sys/resource.h
runprog.o: /usr/include/sys/wait.h
tilde.o names.o: /usr/include/pwd.h
windows.o stop.o runprog.o readstream.o misc.o kb.o: /usr/include/sgtty.h
windows.o stop.o runprog.o readstream.o misc.o kb.o: /usr/include/curses.h
windows.o stop.o sig.o runprog.o readstream.o misc.o kb.o: \
/usr/include/sys/ttydev.h
windows.o stop.o sig.o runprog.o readstream.o misc.o kb.o: \
/usr/include/sys/ttychars.h
windows.o windows.o stop.o sig.o runprog.o readstream.o misc.o kb.o: \
/usr/include/sys/ioctl.h
runprog.o main.o getdaemon.o: /usr/include/errno.h
windows.o strsave.o stop.o sig.o message.o main.o getdaemon.o \
connect_daemon.o: /usr/include/signal.h
sendit.o runprog.o getdaemon.o calls.o: /usr/include/time.h
sendit.o runprog.o getdaemon.o calls.o: /usr/include/sys/time.h
who.o names.o connect_daemon.o calls.o: /usr/include/netdb.h
who.o runprog.o readstream.o readctl.o names.o main.o getdaemon.o \
connect_daemon.o check_invite.o calls.o: ./../common.h
windows.o strsave.o stop.o runprog.o readstream.o readrc.o names.o misc.o \
message.o main.o kb.o connect_daemon.o calls.o: /usr/include/stdio.h
windows.o who.o stop.o set.o sendit.o runprog.o readstream.o readrc.o \
readctl.o names.o misc.o message.o main.o kb.o getdaemon.o connect_daemon.o \
check_invite.o calls.o alias.o: /usr/include/netinet/in.h
windows.o who.o stop.o set.o sendit.o runprog.o readstream.o readrc.o \
readctl.o names.o misc.o message.o main.o kb.o getdaemon.o connect_daemon.o \
check_invite.o calls.o alias.o: /usr/include/sys/socket.h
windows.o who.o stop.o set.o sendit.o runprog.o runprog.o readstream.o \
readrc.o readctl.o names.o misc.o message.o main.o kb.o getdaemon.o \
connect_daemon.o check_invite.o calls.o alias.o: /usr/include/sys/types.h
windows.o who.o stop.o set.o sendit.o runprog.o readstream.o readrc.o \
readctl.o names.o misc.o message.o main.o kb.o getdaemon.o connect_daemon.o \
check_invite.o calls.o alias.o: ./defs.h
# IF YOU PUT STUFF HERE IT WILL GO AWAY
# see depend: above
!Funky!Stuff!
fi # end of overwriting check
echo shar: extracting "'defs.h'" '(1720 characters)'
if test -f 'defs.h'
then
echo shar: will not over-write existing file "'defs.h'"
else
cat << \!Funky!Stuff! > 'defs.h'
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#define BUFFER 512 /* internal character buffer size */
char *sprintf();
char *index();
char *strsave();
char *malloc();
char *basename();
char *expalias();
char *login; /* user's login name */
char *tty; /* tty he's on */
char *home; /* home directory */
char *shell; /* preferred shell */
extern char host[]; /* local host name */
extern char realname[]; /* from password file */
int maxx; /* max legal x coordinate */
int maxy; /* max legal y coordinate */
int stream; /* stream socket filedes */
int ctl; /* control socket fd */
int reading; /* currently reading kb? */
extern char buf[]; /* general-use buffer */
extern int pending; /* number of pending calls */
int connected; /* number of connected people */
int touched25; /* message changed line 25 */
struct sockaddr_in locaddr; /* localhost address */
extern char convaddr[]; /* conversation address */
int port; /* phoned socket port */
#ifndef ESC
#define ESC '\033'
#endif
#define PROMPT "Command> "
#define ctrl(C) ('C'-'@')
#define isdigit(c) ('0' <= c && c <= '9')
#define equal(a,b) (strcmp (a,b) == 0)
/*
* Various oft-used flags.
*/
int Interval; /* seconds between messages */
int Inverse; /* allow use of inverse video */
int Stop; /* print "Stopped" to socket on ^Z */
int Debug; /* verbose tracing stuff */
int Bells; /* let ^G come through as bell */
int Hold; /* send input to child process */
int Echo; /* send input to socket when running prog */
!Funky!Stuff!
fi # end of overwriting check
echo shar: extracting "'alias.c'" '(1316 characters)'
if test -f 'alias.c'
then
echo shar: will not over-write existing file "'alias.c'"
else
cat << \!Funky!Stuff! > 'alias.c'
#include "defs.h"
/*
* Routines to handle phone aliases, much like mail aliases.
*/
struct alias {
char *name; /* name of alias */
char *real; /* and what it expands to */
struct alias *next;
};
static struct alias *aliases = (struct alias *) 0;
/*
* Set up an alias.
*/
alias (argc, argv)
int argc;
char *argv[];
{
struct alias *ap;
if (argc == 0) { /* just show aliases */
message ("Showing all aliases:");
for (ap = aliases; ap; ap = ap->next) {
sprintf (buf, "%s --> %s", ap->name, ap->real);
message (buf);
}
message ("That's all!");
return;
}
if (argc == 1) {
putmessage ("alias: arg count must be 0 or 2!");
return;
}
ap = (struct alias *) malloc (sizeof (struct alias));
if (ap == (struct alias *) 0) {
error (0, "alias: cannot allocate memory for alias");
return;
}
ap->name = strsave (argv[0]);
ap->real = strsave (argv[1]);
ap->next = aliases;
aliases = ap;
}
/*
* Expand an alias. It copies the expansion into a buffer so
* that the calling routines can munge it to their heart's content.
*/
char *
expalias (name)
char *name;
{
register struct alias *ap;
static char buf[128];
for (ap = aliases; ap; ap = ap->next)
if (strcmp (name, ap->name) == 0) {
strcpy (buf, ap->real);
return (buf);
}
return ((char *) 0);
}
!Funky!Stuff!
fi # end of overwriting check
echo shar: extracting "'calls.c'" '(7403 characters)'
if test -f 'calls.c'
then
echo shar: will not over-write existing file "'calls.c'"
else
cat << \!Funky!Stuff! > 'calls.c'
#include <stdio.h>
#include "defs.h"
#include "../common.h"
#include <netdb.h>
#include <sys/time.h>
/* client pending call structure */
struct call {
char *user; /* user's name */
char *host; /* his host */
char *tty; /* his tty */
struct sockaddr_in addr; /* socket address */
char id[11]; /* used to talk to daemon */
int rings; /* ticks since last ring */
int status; /* has daemon acted on it? */
struct call *next; /* next one in the list */
};
static struct call *calls = (struct call *) 0; /* list of pending calls */
int pending = 0; /* number of pending calls */
/*
** Place a call.
**
** Parameters:
** user - the login name of the person to call
** host - his hostname, in either normal form ('ucbarpa') or
** internet dotted quad form ('128.32.149.5')
** tty - a specified tty, if any, other the null string ("")
**
** What we do:
** 1. Send a request packet to the phone daemon at his address,
** packet looks like "<esc> P callno : user : tty : myname : convaddr".
**
** Callno is a unique request identifier that the daemon uses
** to guard against that duplicate messages occurring due to
** the time delay between placing the call and getting the response
** causing the client to retransmit the request. [Got that?]
**
** If we are using the master/slave protocol, the convaddr is
** preceded by either 'y' or 'n' to indicate whether or not we
** will be willing to give up our convd and go to his. (This
** is used for joining an existing conversation.)
** [Not used yet ...]
**
** 2. We expect to get a response back from his phoned of the form
** "<ESC> P y id" where `id' is a small 5-character request id which
** is to be used when 'renewing' a page request.
**
** 3. Then save all of the info (user, host, tty, message id) in
** a 'call' structure which is added to the linked list of pending
** calls.
**
** 4. As a side effect, the global variable 'pending' is incremented.
*/
placecall (user, host, tty)
char *user;
char *host;
char *tty;
{
struct call *new; /* new call structure */
struct hostent *hp; /* host info */
struct hostent *gethostbyname();
struct sockaddr_in sin; /* network address */
static int callno = 0;
extern int users;
int rval;
if (isdigit (*host)) /* numeric address ??? */
sin.sin_addr.s_addr = inet_addr (host);
else {
if ((hp = gethostbyname (host)) == (struct hostent *) 0) {
sprintf (buf, "Unknown host: %s", host);
putmessage (buf);
return;
}
bcopy ((char *)hp->h_addr, (char *)&sin.sin_addr, hp->h_length);
}
sin.sin_family = AF_INET;
sin.sin_port = port;
sprintf (buf, "Calling %s@%s", user, host);
message (buf);
#ifdef SLAVE
sprintf (buf, "%c%c%03d:%s:%s:%s:%c%s", ESC, PAGE, callno++, user,
tty ? tty : "", login, users > 1 ? NAK : ACK, convaddr);
#else !SLAVE
sprintf (buf, "%c%c%03d:%s:%s:%s:%s", ESC, PAGE, callno++,
user, tty ? tty : "", login, convaddr);
#endif SLAVE
if ((rval = sendit (buf, strlen (buf), sin)) < 0) {
message ("No response to call request.");
return;
}
/* got a response - text is in buf. */
buf[rval] = '\0';
new = (struct call *) malloc (sizeof (struct call));
new->user = strsave (user);
new->host = strsave (host);
new->tty = strsave (tty);
new->rings = 1;
strncpy (new->id, buf+3, 10); /* save the mesg id # */
bcopy ((char *)&sin, (char *)&new->addr, sizeof (sin));
new->next = calls; /* put list together */
calls = new;
pending++; /* increment the number of pending calls */
return;
}
/*
** Go through the list of pending calls and reinvite
** all the calls that have expired. Returns 1 if the timer
** should be reset, 0 if the timer should expire.
**
** If SLAVE is defined, we check every call to see they have
** tried to call us. If they indicate that they are *not* willing
** to submit (they are in an existing conversation), then we close
** our existing convd connection and connect to their convd; else
** we go over only if our convd address is lower than theirs.
** (Yes, this looks like a hack, but it seems to work best.)
*/
reinvite ()
{
register struct call *ptr;
extern int users;
if (pending == 0) /* nothing to do */
return (0);
for (ptr = calls; ptr; ptr = ptr->next) {
/*
* See if we've connected to anyone,
* if not then first call our local host and see if the
* intended recipient is trying to call us...
*/
#ifdef SLAVE
if (users < 2 && pending < 2) {
int r;
if (Debug)
putmessage ("Checking for reverse calls...");
sprintf (buf, "%c%c%s:%s", ESC, INQUIRE, login, ptr->user);
if ((r = sendit (buf, strlen (buf), locaddr)) > 0) {
buf[r] = '\0';
if (Debug)
printf ("Received \"%s\"\r\n", buf+1);
if (buf[2] == ACK &&
(buf[3] == NAK || strcmp (convaddr, buf+4) < 0) &&
connect_daemon (buf+4) == 0) {
putmessage ("Switching to conversation already in progress.");
delete (ptr->id);
/* need to reset status so windows work */
users = 0;
return;
}
}
putmessage ("");
}
#endif SLAVE
if (ptr->rings == 0) { /* time to reinvite */
buf[0] = ESC;
buf[1] = REINVITE;
strcpy (buf+2, ptr->id);
if (sendit (buf, strlen (buf), ptr->addr) < 0)
error (0, "reinvite: sendto");
/* should check response value!!! */
}
ptr->rings = (ptr->rings + 1) % 20; /* reinvite every 20 secs. */
}
return (1);
}
/*
** Delete the call `id' from the pending list.
*/
delete (id)
char *id;
{
register struct call *curr;
register struct call *prev = (struct call *) 0;
register struct call *next;
for (curr = calls; curr; curr = next) {
next = curr->next;
if (strncmp (id, curr->id, 5) == 0) { /* this is the one */
if (prev)
prev->next = next;
else
calls = next;
free (curr->user);
free (curr->host);
free (curr->tty);
free (curr);
pending--;
return;
}
}
}
/*
** Front end routine --- Page a user to join in the conversation.
*/
page (argc, argv)
int argc;
char *argv[];
{
char *hishost;
char *tty;
char *user;
char *index();
if ((user = expalias (argv[0])) == (char *) 0)
user = argv[0];
if (hishost = index (user, '@'))
*hishost++ = '\0';
else
hishost = host;
tty = (argc == 2) ? argv[1] : (char *) 0;
placecall (user, hishost, tty);
}
/*
** Cancel a call sent out by simply not bothering to "renew" it.
*/
cancel (argc, argv)
int argc;
char *argv[];
{
char *user;
char *hishost;
char *tty;
struct call *curr, *prev, *next;
if ((user = expalias (argv[0])) == (char *) 0)
user = argv[0];
tty = (argc == 2) ? argv[1] : (char *) 0;
if (hishost = index (user, '@'))
*hishost++ = '\0';
else
hishost = host;
prev = (struct call *) 0;
for (curr = calls; curr; curr = next) {
next = curr->next;
if (equal (user, curr->user) && equal (hishost, curr->host)) {
if (prev)
prev->next = next;
else
calls = next;
free (curr->user);
free (curr->host);
free (curr->tty);
free (curr);
pending--;
sprintf (buf, "Cancelling call to %s@%s",
user, hishost);
if (tty) {
strcat (buf, " on ");
strcat (buf, tty);
}
message (buf);
return;
}
}
sprintf (buf, "No pending calls to %s@%s", user, hishost);
message (buf);
}
!Funky!Stuff!
fi # end of overwriting check
echo shar: extracting "'check_invite.c'" '(1106 characters)'
if test -f 'check_invite.c'
then
echo shar: will not over-write existing file "'check_invite.c'"
else
cat << \!Funky!Stuff! > 'check_invite.c'
#include "../common.h"
#include "defs.h"
/*
* Check to see if we have any pending invitations from the named user.
* Returns the string containing our given address (or NULL)
*/
/*ARGSUSED*/
char *
check_invite (user, host)
char *user;
char *host;
{
struct sockaddr_in sin; /* address of daemon socket */
char mbuf[80]; /* one line of incoming message */
int r, tries;
/* socket address "locaddr" is already initialised to the local host */
bcopy ((char *)&locaddr, (char *)&sin, sizeof (sin));
sin.sin_port = port;
sprintf (mbuf, "%c%c%s:%s", ESC, INQUIRE, login, user);
for (tries = 0; tries < 5; tries++) {
if ((r = sendit (mbuf, strlen (mbuf), sin)) <= 0) {
if (Debug)
printf ("\r\ncheck_invite: sendit returned %d.\r\n", r);
continue;
}
buf[r] = '\0';
if (Debug)
printf ("check_invite: returned string is \"%s\"\r\n", buf+1);
if (buf[2] == NAK)
return ((char *) 0);
return (strsave (buf+3)); /* skip over ESC I y */
printf ("Trying to contact phone daemon.\r\n");
}
error (1, "Cannot contact local phone daemon.");
return ((char *) 0);
}
!Funky!Stuff!
fi # end of overwriting check
echo shar: extracting "'connect_daemon.c'" '(2397 characters)'
if test -f 'connect_daemon.c'
then
echo shar: will not over-write existing file "'connect_daemon.c'"
else
cat << \!Funky!Stuff! > 'connect_daemon.c'
#include "../common.h"
#include "defs.h"
#include <signal.h>
#include <stdio.h>
#include <netdb.h>
/*
* Connect to the conversation daemon.
* Addrstr is a string containing the intended address
* in the form "128.32.149.2/9535".
*/
connect_daemon (addrstr)
char *addrstr;
{
struct sockaddr_in addr;
char *inet_ntoa();
char *p, *index();
char str[30];
int oldval;
int (*oldfunc)();
int timeout();
if (addrstr == (char *) 0 || *addrstr == '\0')
error (1, "Failed to connect to conversation daemon");
oldval = alarm (0); /* turn off alarms and save old value */
#ifdef SLAVE
if (*addrstr == 'y' || *addrstr == 'n')
addrstr++;
#endif SLAVE
strcpy (convaddr, addrstr); /* save the current conversation address */
strcpy (str, addrstr); /* and use 'str' for our working copy */
if (p = index (str, '/')) /* find slash before port number */
*p++ = '\0';
if ((stream = socket (AF_INET, SOCK_STREAM, 0)) < 0)
error (1, "Cannot open stream socket");
bzero ((char *)&addr, sizeof (addr)); /* connect trashes the args */
addr.sin_addr.s_addr = inet_addr (str);
addr.sin_port = htons (atoi (p));
addr.sin_family = AF_INET;
oldfunc = signal (SIGALRM, timeout); /* save old handler */
alarm (15); /* timeout in five seconds */
if (Debug) {
printf ("Trying to connect to conversation daemon.\r\n");
fflush (stdout);
}
if (connect (stream, (char *)&addr, sizeof (struct sockaddr_in)) < 0) {
alarm (0);
close (stream);
connected = 0;
if (Debug)
printf ("\r\n\r\nNot connected.\r\n");
} else { /* say hello */
if (Debug) {
printf ("Connected to conversation daemon @ \"%s\"\r\n", convaddr);
fflush (stdout);
}
sprintf (buf, "%s:%s:%s:%s", login, host, tty, realname);
if (write (stream, buf, strlen (buf)) < 0)
error (1, "getdaemon: cannot write stream socket");
connected = 1;
}
/*
* Tell the local phoned to delete our invitaton now
* that we've answered it. Not used yet.
*/
#ifdef notdef
sprintf (buf, "%c%c%s:%s", ESC, ANSWER, login, convaddr);
(void) sendto (ctl, buf, strlen (buf), 0, &locaddr, sizeof (locaddr));
#endif notdef
(void) alarm (oldval); /* restore old alarm value */
(void) signal (SIGALRM, oldfunc); /* and handler routine */
return (!connected);
}
timeout ()
{
putmessage ("cannot connect to conversation daemon: connection timed out.");
}
!Funky!Stuff!
fi # end of overwriting check
echo shar: extracting "'cmd.c'" '(3466 characters)'
if test -f 'cmd.c'
then
echo shar: will not over-write existing file "'cmd.c'"
else
cat << \!Funky!Stuff! > 'cmd.c'
/*
* All the (external) command functions.
*/
extern int
alias(), cancel(), cd(), dump(), help(), page(), quit(),
run(), set(), show(), longhelp(), who(), zip();
static char aliashelp[] = "Assign an alias to a user name";
static char cancelhelp[] = "Cancel a pending call to user";
static char cdhelp[] = "Change the working directory";
static char dumphelp[] = "Save a copy of the screen image into a file";
static char helphelp[] = "See help messages for any command";
static char pagehelp[] = "Invite user to join the conversation";
static char quithelp[] = "Leave the current conversation";
static char runhelp[] = "Run a program within the window";
static char sethelp[] = "Set the value of a variable";
static char showhelp[] = "See the value of a variable";
static char whohelp[] = "See who is currently logged on";
static struct cmd {
char *name; /* name this command goes by */
int (*func)(); /* function to handle it */
int minargs; /* minimum number of args */
int maxargs; /* maximum number of args */
char *usage; /* usage message text */
char *help; /* help message text */
} cmds [] = {
{ "call", page, 2, 3, "user at host [tty]", pagehelp },
{ "page", page, 2, 3, "user at host [tty]", pagehelp },
{ "phone", page, 2, 3, "user at host [tty]", pagehelp },
{ "cancel", cancel, 2, 3, "user at host [tty]", cancelhelp },
{ "quit", quit, 1, 1, "", quithelp },
{ "who", who, 1, 2, "[user]", whohelp },
{ "?", help, 1, 2, "[command]", helphelp },
{ "help", help, 1, 2, "[command]", helphelp },
{ "set", set, 2, 3, "variable [value]", sethelp },
{ "show", show, 1, 98, "[var [var]]", showhelp },
{ "!", run, 2, 98, "prog [args]", runhelp },
{ "run", run, 2, 98, "prog [args]", runhelp },
{ "dump", dump, 1, 2, "[file]", dumphelp },
{ "cd", cd, 1, 2, "[directory]", cdhelp },
{ "alias", alias, 1, 3, "[alias user]", aliashelp },
{ "accept", zip, 1, 99, "", "" },
{ "forward",zip, 1, 99, "", "" },
{ 0, 0, 0, 0, 0, 0 }
};
/*
* Given a buffer, parse it and execute the command.
*/
execute (buf)
char *buf;
{
int len;
int argc;
char *argv[99];
char line[80];
struct cmd *cmd;
if ((argc = parse (buf, argv)) == 0) /* null command */
return;
len = strlen (argv[0]);
for (cmd = cmds; cmd->name; cmd++) {
if (strncmp (argv[0], cmd->name, len)) /* is it a prefix? */
continue;
if (argc < cmd->minargs || argc > cmd->maxargs) {
sprintf (line, "%s: bad number of arguments - usage: %s %s",
cmd->name, cmd->name, cmd->usage);
putmessage (line);
} else
(*cmd->func)(argc-1, argv+1);
return;
}
sprintf (line, "Unknown command: \"%s\" - try \"?\" for list of commands",
argv[0]);
putmessage (line);
}
/*
* Print a help message for the given command, or all commands.
*/
help (argc, argv)
int argc;
char *argv[];
{
struct cmd *cmd;
char buf[80];
int all = (argc == 0);
int len;
if (all)
message ("Showing help for all commands. Commands may be abbreviated.");
else
len = strlen (argv[0]);
for (cmd = cmds; cmd->name; cmd++)
if (all || strncmp (argv[0], cmd->name, len) == 0) {
if (!all || strcmp (cmd->help, (cmd+1)->help)) {
sprintf (buf, "help: %s: %s.", cmd->name, cmd->help);
message (buf);
}
}
}
/*
* Do nothing. Used for commands in .phonerc that have
* no meaning to the client process.
*/
zip ()
{
;
}
!Funky!Stuff!
fi # end of overwriting check
echo shar: extracting "'getdaemon.c'" '(972 characters)'
if test -f 'getdaemon.c'
then
echo shar: will not over-write existing file "'getdaemon.c'"
else
cat << \!Funky!Stuff! > 'getdaemon.c'
#include "../common.h"
#include "defs.h"
#include <sys/time.h>
#include <signal.h>
#include <errno.h>
/*
* Ask the master daemon to give us its first-born child ...
* Returns the string that it gave us ...
*/
char *
getdaemon ()
{
int r; /* number of chars read */
char mbuf[80];
if (connected) {
error (0, "getdaemon(): already connected!");
return;
}
connected = 0;
if (Debug)
message ("Asking for a conversation daemon ...");
mbuf[0] = ESC;
mbuf[1] = DAEMON;
for ( ;; ) {
if ((r = sendit (mbuf, 2, locaddr)) < 0) {
error (0, "could not get conversation daemon from local daemon");
return ((char *) 0);
}
buf[r] = '\0';
if (buf[2] == NAK) {
sprintf (mbuf, "Daemon failed to fork: %s", buf+3);
putmessage (mbuf);
} else
break;
putmessage ("Trying to connect to conversation daemon.");
}
if (Debug)
message (sprintf (mbuf, "Daemon address is %s", buf+3));
return (strsave (buf+3)); /* skip ESC D y */
}
!Funky!Stuff!
fi # end of overwriting check
echo shar: extracting "'kb.c'" '(4489 characters)'
if test -f 'kb.c'
then
echo shar: will not over-write existing file "'kb.c'"
else
cat << \!Funky!Stuff! > 'kb.c'
#include <sys/ioctl.h>
#include <curses.h>
#undef ctrl()
#include "defs.h"
/*
* Handle keyboard input.
*
* Need to handle edit character mapping --- see if char is one
* of his edit characters, swap to standard character.
*/
static struct key {
int his; /* his key */
int std; /* what we'll map it to */
} keys [] = {
{ ctrl(M), ctrl(J) }, /* <cr> -> newline */
{ 0, ctrl(H) }, /* backspace */
{ 0, ctrl(W) }, /* word erase */
{ 0, ctrl(X) }, /* line kill */
{ 0, 0 }
};
keyboard ()
{
static char c; /* character read in */
register int k; /* for doing key maps */
static int command = 0; /* on bottom line? */
static int cpos = 0; /* position in buffer */
static char cbuf[80]; /* and command buffer */
extern int childpid, tochild;
static int y, x;
if (read (0, &c, 1) != 1)
return;
/*
* We remap the basic editing characters here to make life easier later.
*/
for (k = 0; k < 4; k++) {
if (keys[k].his == c) {
c = keys[k].std;
break;
}
}
if (c == ctrl(L) || c == ctrl(R)) {
wrefresh (curscr);
return;
}
if (c == ESC) { /* toggle command mode */
getyx (stdscr, y, x); /* save the current cursor position */
move (maxy, 0); /* jump to bottom line */
clrtoeol ();
command = !command; /* toggle mode */
if (command) /* entering command mode */
addstr (PROMPT);
cpos = 0; /* and reset buffer index */
move (y, x); /* restore cursor position */
refresh ();
return;
}
if (command == 0) { /* not in command mode */
if ((c == ctrl(D)) && childpid) /* send eof to child */
close (tochild);
if (childpid == 0 || Echo) { /* send char to the socket */
#ifdef LOCAL_ECHO
int old = selwin (0);
showch (c);
selwin (old);
refresh ();
#endif LOCAL_ECHO
(void) write (stream, &c, 1);
}
if (childpid && !Hold) /* and to child process */
(void) write (tochild, &c, 1);
return;
} else {
/*
* The rest is command-mode processing where
* we fiddle with the command buffer. This
* should move to a separate routine somewhere.
*/
getyx (stdscr, y, x); /* save the current cursor position */
if (c == ctrl(J)) { /* execute the buffer */
cbuf[cpos] = '\0'; /* null-terminate it */
move (maxy, 0); /* go to beginning of line */
clrtoeol (); /* clear line to end */
refresh (); /* update the screen */
if (cpos)
execute (cbuf); /* and execute it !!! */
command = cpos = 0; /* turn off command mode */
move (y, x); /* restore cursor position */
refresh ();
return; /* and we're done */
}
/*
* The message() routine modified the bottom line,
* so reprint whatever should be down there.
*/
if (touched25) {
move (maxy, 0); /* go to beginning of line */
addstr (PROMPT); /* print prompt */
cbuf[cpos] = '\0';
addstr (cbuf); /* and print buffer */
clrtoeol ();
touched25 = 0;
}
move (maxy, strlen (PROMPT) + cpos);
/*
* If we're here, then we want to add the character to
* the pending command buffer.
*/
switch (c) {
case ctrl(H): /* backspace */
if (cpos) {
cpos--;
addstr ("\b \b");
}
break;
case ctrl(W): /* word erase */
while (--cpos >= 0) {
if (cbuf[cpos] != ' ') {
cpos++;
break;
}
addstr ("\b \b");
}
while (--cpos >= 0) {
if (cbuf[cpos] == ' ') {
cpos++;
break;
}
addstr ("\b \b");
}
break;
case ctrl(U): /* line kill */
case ctrl(X): move (maxy, strlen (PROMPT));
clrtoeol ();
cpos = 0;
break;
case ctrl(I): /* tab */
c = ' '; /* fall through ... */
default: cbuf[cpos++] = c;
addch (c);
break;
}
cbuf[cpos] = '\0';
move (y, x); /* restore cursor position */
refresh ();
return;
}
}
/*
* Get our editing characters. Called by setup().
*/
geteditchars ()
{
struct sgttyb chars; /* standard char- and line-erase */
#ifdef TIOCGLTC
struct ltchars lchars; /* local - word-erase */
#endif
if (ioctl (0, TIOCGETP, &chars) < 0) {
error (0, "ioctl (TIOCGETP) failed");
return;
}
keys[1].his = chars.sg_erase;
keys[3].his = chars.sg_kill;
#ifdef TIOCGLTC
if (ioctl (0, TIOCGLTC, &lchars) < 0) {
error (0, "ioctl (TIOCGLTC) failed");
return;
}
keys[2].his = lchars.t_werasc;
#endif
}
!Funky!Stuff!
fi # end of overwriting check
echo shar: extracting "'main.c'" '(4570 characters)'
if test -f 'main.c'
then
echo shar: will not over-write existing file "'main.c'"
else
cat << \!Funky!Stuff! > 'main.c'
#include "../common.h"
#include "defs.h"
#include <stdio.h>
#include <signal.h>
#include <errno.h>
#define toggle(var) var = !var
struct sockaddr_in locaddr; /* our own host address */
char buf[BUFFER]; /* general purpose character buffer */
char convaddr[25]; /* address of our conversation in ascii */
char host[32]; /* local host name */
char realname[100]; /* user's real name from password file */
extern int users; /* number of users connected - from windows.c */
int pending; /* number of pending calls */
int changed_size; /* caught SIGWINCH, need to resize windows */
main (argc, argv)
int argc;
char *argv[];
{
int answer = 0;
int smask; /* saved socket select mask */
extern int errno;
extern int timer();
extern int quit();
extern int badsig();
extern int sigint();
extern int sigquit();
extern int sigstop();
extern int sigwinch();
extern int sigchld();
char *check_invite();
char *getdaemon();
char *index();
char *user; /* name of user we are trying to call */
char *hishost; /* and his hostname */
char *addr; /* address of conversation from getdaemon () */
struct sockaddr_in sin;
argv++, argc--;
while (*argv && **argv == '-') {
while (*++*argv) {
switch (**argv) {
case 'a': if (argc > 1) { /* given an address */
addr = *++argv;
argc--;
printf ("Using address \"%s\"\n", addr);
}
break;
case 'b': toggle (Bells); break;
case 'd': toggle(Debug); break;
case 'i': toggle(Inverse); break;
}
}
argv++, argc--;
}
if (argc == 0) /* just answer incoming calls */
answer = 1;
else if (argc > 2) {
fprintf (stderr, "Usage: %s [-{bdi}] user at host [ tty ]\n", *argv);
exit (1);
}
if (names ()) /* get all the various info */
exit (1);
readrc (); /* read in and act on the .phonerc */
if (!answer) { /* expand any user aliases and see who we're calling */
if ((user = expalias (argv[0])) == (char *) 0)
user = argv[0];
if (hishost = index (user, '@'))
*hishost++ = '\0';
else
hishost = host;
}
connected = 0;
pending = 0;
/* set up all the signals */
signal (SIGPIPE, SIG_IGN);
if (!Debug) {
signal (SIGHUP, badsig);
signal (SIGBUS, badsig);
signal (SIGSEGV, badsig);
}
signal (SIGALRM, timer);
signal (SIGQUIT, sigquit);
signal (SIGINT, sigint);
#ifdef SIGWINCH
signal (SIGWINCH, sigwinch);
#endif
signal (SIGCHLD, sigchld);
/*
* open datagram control socket.
*/
if ((ctl = socket (AF_INET, SOCK_DGRAM, 0)) < 0)
error (1, "Can't get control socket");
sin.sin_addr.s_addr = INADDR_ANY;
sin.sin_port = 0;
sin.sin_family = AF_INET;
if (bind (ctl, &sin, sizeof (sin)) < 0)
error (1, "Can't bind control socket");
setup (); /* initialize all the screen stuff */
/* Have we been invited ??? */
if (addr || (addr = check_invite (answer ? "*" : user, hishost))) {
if (Debug)
printf ("\r\nCheck_invite returned \"%s\"\r\n", addr);
connect_daemon (addr);
if (!connected) {
if (Debug)
printf ("Connection failed.\r\n");
goto again; /* caller has hung up !!! */
}
} else {
again:
if (Debug)
printf ("\r\nCheck_invite returned no address.\r\n");
if (answer) { /* don't want to place a call */
fprintf (stderr, "\r\nNo pending calls for %s\r\n", login);
cleanup ();
exit (1);
}
do {
putmessage ("");
if ((addr = getdaemon()) == (char *) 0)
error (1, "Cannot get conversation daemon address");
if (Debug)
printf ("\rGet_daemon returned address of \"%s\"\r\n", addr);
connect_daemon (addr);
if (!connected) {
putmessage ("Trying to connect to conversation daemon.");
printf ("Trying to connect to conversation daemon.");
fflush (stdout);
}
} while (!connected);
placecall (user, hishost, argv[1] ? argv[1] : "");
}
smask = (1 << 0) | (1 << ctl) | (1 << stream);
alarm (1); /* get things rolling */
/*
* Main loop - select on sockets and handle appropriately.
*/
for ( ;; ) {
static int mask;
extern int childpid, fromchild;
#ifdef SIGWINCH
if (changed_size) { /* caught SIGWINCH - have to resize windows */
stretch (1);
changed_size = 0;
}
#endif
mask = smask;
if (childpid)
mask |= 1 << fromchild;
if (select (32, &mask, 0, 0, 0) <= 0) {
if (errno == EINTR)
continue;
error (1, "select");
}
errno = 0;
if (mask & (1 << 0))
keyboard ();
if (mask & (1 << stream))
readstream ();
if (mask & (1 << ctl))
readctl ();
if (childpid && (mask & (1 << fromchild)))
readchild ();
}
/*NOTREACHED*/
}
!Funky!Stuff!
fi # end of overwriting check
echo shar: extracting "'message.c'" '(2503 characters)'
if test -f 'message.c'
then
echo shar: will not over-write existing file "'message.c'"
else
cat << \!Funky!Stuff! > 'message.c'
#include "defs.h"
#include <stdio.h>
#include <signal.h>
/*
* Routines for dealing with the messages printed
* down on the bottom line of the screen.
*/
struct mesg {
char *text; /* text of this message */
struct mesg *next; /* link to next message */
};
static struct mesg *mesghead = (struct mesg *) 0; /* top of queue */
static struct mesg *last = (struct mesg *) 0; /* bottom of queue */
static struct mesg *freelist = (struct mesg *) 0; /* stuff to free up */
int msgpending = 0; /* number of pending messages */
/*
* Add a message to the pending message queue.
*/
message (str)
char *str;
{
struct mesg *new;
int sigs;
sigs = sigblock (1 << SIGALRM);
if (!freelist) { /* need to malloc some up */
new = (struct mesg *) malloc (sizeof (struct mesg));
if (!new)
error (1, "cannot malloc message buffer!");
} else { /* take off the top of the list */
new = freelist;
freelist = freelist->next;
free (new->text);
}
new->text = strsave (str);
new->next = (struct mesg *) 0;
if (last) /* add to end of currently pending list */
last->next = new;
if (!mesghead) /* empty queue - add to top */
mesghead = new;
last = new; /* update bottom pointer */
if (msgpending <= 0) { /* need to restart alarms */
alarm (1);
msgpending = 1;
} else
msgpending++;
(void) sigsetmask (sigs);
}
/*
* Called by the alarm interrupt routine - print the
* next pending message and remove it from the queue.
*/
showmessage ()
{
static int ticks = 0;
struct mesg *m;
if ((ticks = (ticks + 1) % Interval) != 0)
return;
if (msgpending == 0) { /* just clean up after last message */
putmessage ("");
msgpending = -1;
return;
}
m = mesghead; /* save pointer to this message */
putmessage (m->text);
mesghead = m->next; /* move down top of queue */
if (last == m) /* was the last item, too */
last = (struct mesg *) 0;
m->next = freelist; /* add this item to the free list */
freelist = m; /* this way we don't free in an interrupt !!! */
msgpending--; /* and decrement counter */
}
/*
* Flush the pending message buffer. Not actually used yet.
*/
flush ()
{
last->next = freelist; /* add curr freelist to end of list */
last = (struct mesg *) 0;
freelist = mesghead; /* move pending list to free list */
mesghead = (struct mesg *) 0; /* zap the head */
msgpending = 0; /* and reset the counter */
}
!Funky!Stuff!
fi # end of overwriting check
echo shar: extracting "'misc.c'" '(2640 characters)'
if test -f 'misc.c'
then
echo shar: will not over-write existing file "'misc.c'"
else
cat << \!Funky!Stuff! > 'misc.c'
#include "defs.h"
#include <curses.h>
#undef echo()
#undef noecho()
/*
* See if char `c' is contained in string `s'. Returns 1 if true, 0 if not.
*/
any (c, s)
char c;
char *s;
{
while (*s)
if (c == *s++)
return (1);
return (0);
}
/*
* Send an error message to the user. Much like perror().
* If fatal is non-zero, we will terminate with that status.
*/
error (fatal, mesg)
int fatal;
char *mesg;
{
extern int errno;
extern char *sys_errlist[];
extern int sys_nerr;
char buf[80];
if (errno) {
if (errno < sys_nerr)
sprintf (buf, "%s: %s", mesg, sys_errlist[errno]);
else
sprintf (buf, "%s: Error %d", mesg, errno);
} else
strcpy (buf, mesg);
if (connected) /* and have set up curses */
putmessage (buf);
else
fprintf (stderr, "%s\r\n", buf);
if (fatal) {
cleanup ();
printf ("\n\n");
exit (fatal);
}
}
/*
* User interface to the screen dump routine.
*/
dump (argc, argv)
int argc;
char *argv[];
{
dodump (argc ? *argv : ".dump");
}
/*
* Do a screen dump to the named file.
*/
dodump (name)
char *name;
{
FILE *fp;
int y, x;
char buf[80];
extern int connected;
if (!connected) /* take it as a normal interrupt */
quit ();
if ((fp = fopen (name, "w")) == (FILE *) 0) {
(void) sprintf (buf, "Cannot create \"%s\" file", name);
error (0, buf);
return;
}
for (y = 0; y <= maxy; y++) {
for (x = 0; x < maxx; x++)
(void) putc (stdscr->_y[y][x] & 0177, fp);
(void) putc ('\n', fp);
}
(void) fclose (fp);
(void) sprintf (buf, "Screen dump to \"%s\" complete.", name);
putmessage (buf);
}
/*
* Move to the bottom line and exit.
*/
quit ()
{
extern int did_screen;
extern int childpid;
if (did_screen)
cleanup ();
if (connected)
printf ("\nConnection closed. Exiting ...");
printf ("\n");
if (childpid) /* clean up rather forcefully */
kill (childpid, 9);
exit (0);
}
/*
* Catch SIGWINCH and come here - just set a flag and return.
*/
sigwinch ()
{
extern int changed_size;
changed_size = 1;
}
/*
* Chdir to the named directory, to $HOME if no args.
*/
cd (argc, argv)
int argc;
char *argv[];
{
char *getenv();
char *dir;
if (argc == 0) { /* try to go home */
if ((dir = getenv ("HOME")) == (char *) 0) {
putmessage ("No HOME set!");
return;
}
} else
dir = argv[0];
if (chdir (dir)) {
sprintf (buf, "Cannot chdir to \"%s\"", dir);
error (0, buf);
}
}
/*
* Given a full pathname, return a pointer to the last part of the path.
*/
char *
basename (path)
char *path;
{
char *rindex();
char *p;
if (p = rindex (path, '/'))
return (p+1);
return (path);
}
!Funky!Stuff!
fi # end of overwriting check
echo shar: extracting "'names.c'" '(2359 characters)'
if test -f 'names.c'
then
echo shar: will not over-write existing file "'names.c'"
else
cat << \!Funky!Stuff! > 'names.c'
#include "../common.h"
#include "defs.h"
#include <netdb.h>
#include <stdio.h>
#include <pwd.h>
#undef NULL
#define NULL ((char *) 0)
/*
* Initialize all the various names and such that we all want to know.
*/
names ()
{
struct passwd *pw, *getpwnam();
struct hostent *hp, *gethostbyname();
#ifdef SERVICES
struct servent *sp, *getservbyname();
#endif
extern char *getlogin();
extern char *ttyname();
extern char *getenv();
extern char realname[];
char *r, *p, *n;
if ((pw = getpwuid (getuid ())) == (struct passwd *) 0) {
fprintf (stderr, "Who are you?\n");
return (1);
}
if ((n = getlogin ()) == NULL) /* in a window? */
login = strsave (pw->pw_name); /* use our acct name */
else
login = strsave (n);
if ((home = getenv ("HOME")) == NULL)
home = pw->pw_dir;
if ((shell = getenv ("SHELL")) == NULL)
shell = "/bin/sh";
if (p = getenv ("NAME"))
strcpy (realname, p);
else {
for (p=pw->pw_gecos, r=realname; p && *p && *p!=',' && *p!=';'; p++) {
if (*p == '&') { /* copy in from login name */
n = pw->pw_name; /* and grab the login name */
if ('a' <= *n && *n <= 'z') /* capitalize the first character */
*n += 'A' - 'a';
while (*n)
*r++ = *n++;
} else
*r++ = *p;
}
*r = '\0';
}
if ((tty = ttyname (0)) == NULL || !isatty (1)) {
fprintf (stderr, "Input must be a terminal, not a file or pipe.\n");
return (1);
}
tty = strsave (tty);
if (strncmp (tty, "/dev/", 5) == 0)
tty += 5; /* skip over "/dev/" */
/* find out about our host */
gethostname (host, 32);
if ((hp = gethostbyname ("localhost")) == (struct hostent *) 0)
if ((hp = gethostbyname (host)) == (struct hostent *) 0) {
fprintf (stderr, "Cannot find network entry for %s\n", host);
return (1);
}
bzero ((char *)&locaddr, sizeof (locaddr));
bcopy ((char *)hp->h_addr, (char *)&locaddr.sin_addr, hp->h_length);
locaddr.sin_family = hp->h_addrtype;
#ifdef SERVICES
if ((sp = getservbyname ("phone", "udp")) == (struct servent *) 0) {
fprintf (stderr, "This machine doesn't support \"phone\"\r\n");
return (1);
}
locaddr.sin_port = port = sp->s_port;
#else SERVICES
#ifdef PORT
locaddr.sin_port = port = htons (PORT);
#else /* PORT */
fprintf (stderr,
"Your site administrator screwed up installing this program!!!\r\n");
return (1);
#endif PORT
#endif SERVICES
return (0);
}
!Funky!Stuff!
fi # end of overwriting check
echo shar: extracting "'parse.c'" '(1159 characters)'
if test -f 'parse.c'
then
echo shar: will not over-write existing file "'parse.c'"
else
cat << \!Funky!Stuff! > 'parse.c'
/*
* Take a line buffer and turn into an array of words,
* doing the right thing with quotes.
*/
parse (line, argv)
char *line;
char **argv;
{
char *lp;
char *wp;
char **ap;
char word[128];
char quotec = '\0';
char *exptilde ();
ap = argv;
lp = line;
while (*lp) {
while (any (*lp, " \t")) /* skip over leading space */
lp++;
quotec = '\0';
wp = word;
while (*lp) { /* get one word */
if (*lp == '!') { /* hack to allow !cmd */
*wp++ = *lp++;
break;
}
if (*lp == '\\') { /* literal next character */
*wp++ = *++lp;
lp++;
continue;
}
if (any (*lp, " \t") && !quotec) /* wsp not in a quoted string */
break;
if (any (*lp, "\"'")) { /* quote characters */
if (!quotec) { /* this must be open quote */
quotec = *lp++;
continue;
} /* else */
if (*lp == quotec) { /* matches opening -> close quote */
quotec = '\0';
lp++;
continue;
}
}
*wp++ = *lp++;
}
*wp = '\0';
if (wp > word)
*ap++ = exptilde (word);
}
*ap = (char *) 0;
if (quotec)
putmessage ("Unmatched quote.");
return (ap - argv);
}
!Funky!Stuff!
fi # end of overwriting check
echo shar: extracting "'readctl.c'" '(1238 characters)'
if test -f 'readctl.c'
then
echo shar: will not over-write existing file "'readctl.c'"
else
cat << \!Funky!Stuff! > 'readctl.c'
#include "defs.h"
#include "../common.h"
/*
* Read a message on the control socket and act upon it.
*/
readctl ()
{
int ack; /* = 1 if this is a good message */
int r; /* number of chars read */
char buf[512]; /* input buffer */
int len; /* length of return address */
struct sockaddr_in from;
len = sizeof (from);
if ((r = recvfrom (ctl, buf, 512, 0, &from, &len)) == 0) {
error (0, "read: control socket");
return;
}
if (*buf != ESC) { /* bad control packet */
error (0, "malformed control packet");
return;
}
buf[r] = '\0';
ack = buf[2] == ACK;
switch (buf[1]) { /* command character */
case CALLING: if (!ack) /* can't call a user */
delete (buf+3);
putmessage (buf+8);
break;
case PAGE: if (!ack) { /* couldn't place call */
pending--;
putmessage (buf+3);
}
break;
case INQUIRE: break; /* don't do anything */
case DAEMON: break; /* and again */
case MESSAGE: message (buf+3); /* show message */
break;
case ANSWER: delete (buf+3); /* someone answered a call */
if (Debug) {
char mbuf[80];
sprintf (mbuf, "Answer on id >>%s<<", buf+3);
putmessage (mbuf);
}
break;
}
}
!Funky!Stuff!
fi # end of overwriting check
echo shar: extracting "'readrc.c'" '(519 characters)'
if test -f 'readrc.c'
then
echo shar: will not over-write existing file "'readrc.c'"
else
cat << \!Funky!Stuff! > 'readrc.c'
#include "defs.h"
#include <stdio.h>
#ifndef PHONERC
#define PHONERC "/.phonerc"
#endif
/*
* Open the .phonerc and do the stuff in it.
*/
readrc ()
{
char line[BUFSIZ];
char *i, *index();
FILE *rc;
strcpy (buf, home);
strcat (buf, PHONERC);
if ((rc = fopen (buf, "r")) == (FILE *) 0)
return;
while (fgets (line, BUFSIZ, rc)) {
if (Debug)
printf ("Rc: %s", line);
if (*line == '#' || *line == '\n')
continue;
if (i = index (line, '\n'))
*i = '\0';
execute (line);
}
fclose (rc);
}
!Funky!Stuff!
fi # end of overwriting check
echo shar: extracting "'readstream.c'" '(2629 characters)'
if test -f 'readstream.c'
then
echo shar: will not over-write existing file "'readstream.c'"
else
cat << \!Funky!Stuff! > 'readstream.c'
#include "../common.h"
#include "defs.h"
#include <curses.h>
/*
* Read and process some characters from the stream socket.
*
* Commands are passed in one byte by setting the high bit
* with info encoded in the following manner:
*
* +---+-------+-------------------+
* Bit: | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
* +---+-------+-------------------+
* | 1 | cmd | window number |
* +---+-------+-------------------+
*
* For the "set update" command, only the low bit of the window
* number is used, to indicate whether updating should be on or off.
*/
#define WINMASK ((1<<4)|(1<<3)|(1<<2)|(1<<1)|(1<<0)) /*037*/ /* lower 5 bits (0-4) set */
#define CMDMASK ((1<<6)|(1<<5)) /* only bits 5 and 6 set */
readstream ()
{
static int adding = 0; /* reading an "adduser" string? */
static char combuf[512]; /* command mode buffer */
char buf[512]; /* normal input buffer */
register char *bufptr; /* pointer into normal buffer */
static char *comptr; /* pointer into command mode buffer */
register int i, r;
register int c;
static int addslot = 0; /* slot for adduser() */
static int dorefresh = 0;
static int noupdate = 0; /* don't refresh screen until told to */
if ((r = read (stream, buf, 512)) == 0) { /* daemon hung up */
quit ();
connected = 0;
return;
}
bufptr = buf;
for (i = 0; i < r; i++) { /* go through each character */
c = *bufptr++;
if (c & META) { /* a command byte */
switch (c & CMDMASK) {
case ADDUSER: /* add a new user */
comptr = combuf;
adding = 1;
addslot = c & WINMASK;
break;
case DELUSER: /* delete an existing user */
deluser (c & WINMASK);
break;
case UPDATE: /* set screen updating */
if (c & 01) /* turn updating on */
noupdate = 0;
else /* turn it off */
noupdate = 1;
break;
default:
if (adding) { /* end of adduser cmd */
*comptr = '\0';
adduser (addslot, combuf);
adding = 0;
dorefresh = 1;
} else /* select current window */
selwin (c & WINMASK);
break;
}
} else { /* normal character */
if (adding) /* add to adduser buffer */
*comptr++ = c;
else { /* add to display */
#ifdef LOCAL_ECHO
extern int current; /* defined in windows.c */
if (current != 0 || !Echo){ /* only show if it's not ours */
#endif
showch (c);
dorefresh = 1;
#ifdef LOCAL_ECHO
}
#endif
}
}
}
if (dorefresh && noupdate == 0) {
refresh ();
dorefresh = 0;
}
return;
}
!Funky!Stuff!
fi # end of overwriting check
echo shar: extracting "'runprog.c'" '(3260 characters)'
if test -f 'runprog.c'
then
echo shar: will not over-write existing file "'runprog.c'"
else
cat << \!Funky!Stuff! > 'runprog.c'
#include "../common.h"
#include "defs.h"
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/time.h> /* these two are for wait3() */
#include <sys/resource.h>
#include <fcntl.h>
#include <errno.h>
#include <curses.h>
/*
* Routines to handle subprocesses inside windows.
*
* Future plans include putting children on ptys,
* allowing multiple processes, some kind of simple
* process control, setting window size (ioctl) and
* termcap to properly reflect the window size, and
* being able to run screen-oriented programs within
* the phone window. Maybe sometime ...
*/
int childpid = 0; /* current child process's pid */
int tochild; /* fd to write to child */
int fromchild; /* fd to read from child */
int killedchild; /* flag set on intr - flush output */
/*
* Run a program. We use the user's shell to allow piping and wildcards.
*/
run (argc, argv)
int argc;
char *argv[];
{
char args[512];
int in[2], out[2];
int i;
if (childpid) {
putmessage ("You are already running a program!!!");
return;
}
/* concatenate all the arguments to pass to a shell */
for (*args = '\0', i = 0; i < argc; ) {
strcat (args, argv[i]);
if (++i < argc)
strcat (args, " ");
}
if (pipe (in) < 0 || pipe (out) < 0) {
error (0, "Cannot pipe to child process");
return;
}
killedchild = 0;
switch (childpid = fork ()) {
case -1: error (0, "fork"); break;
case 0: (void) dup2 (in[0], 0); /* child's stdin */
(void) dup2 (out[1], 1); /* stdout */
(void) dup2 (out[1], 2); /* stderr */
(void) close (in[0]); (void) close (out[1]);
execl (shell, basename (shell), "-c", args, (char *) 0);
perror (argv[0]);
exit (1);
default: (void) close (in[0]);
(void) close (out[1]);
(void) fcntl (in[1], F_SETFL, FNDELAY | FASYNC);
(void) fcntl (out[0], F_SETFL, FNDELAY | FASYNC);
tochild = in[1]; /* write to child here */
fromchild = out[0]; /* read from child here */
return;
}
}
/*
* Read some output from the process. We make sure that there
* aren't any magic characters there to hang anyone, and do
* local echoing if need be.
*/
readchild ()
{
int r, i;
char buf[1024];
register char *c;
extern int errno, stream;
if ((r = read (fromchild, buf, 1024)) == -1) {
if (errno != EWOULDBLOCK)
perror ("read from child");
return;
} else if (r == 0) { /* child is finished */
if (Debug)
putmessage ("Saw EOF on child process.");
childpid = 0;
(void) close (tochild);
(void) close (fromchild);
} else { /* send to everyone */
if (!killedchild) {
#ifdef LOCAL_ECHO
int oldwin = selwin (0);
#endif LOCAL_ECHO
for (c = buf, i = 0; i < r; c++, i++) {
if (*c & META)
*c &= 0177;
#ifdef LOCAL_ECHO
showch (*c);
#endif LOCAL_ECHO
}
(void) write (stream, buf, r);
#ifdef LOCAL_ECHO
refresh ();
(void) selwin (oldwin);
#endif LOCAL_ECHO
}
}
}
/*
* SIGCHLD handler.
* We don't reset the pid and fd states because we'll get those when
* we see the eof on the kid's descriptors.
*/
sigchld ()
{
union wait status;
if (childpid == 0)
return;
while (wait3 (&status, WNOHANG, (struct rusage *) 0) > 0)
;
if (Debug)
putmessage ("Caught sigchld.");
}
!Funky!Stuff!
fi # end of overwriting check
echo shar: extracting "'sendit.c'" '(1506 characters)'
if test -f 'sendit.c'
then
echo shar: will not over-write existing file "'sendit.c'"
else
cat << \!Funky!Stuff! > 'sendit.c'
#include "defs.h"
#include <sys/time.h>
#define WAIT 2 /* wait this long before re-sending */
/*
* Send the given buffer to the given address, with
* multiple retries. Stores the received string
* in the global `buf', and returns the number of chars
* read, or -1 on error or no response.
*/
sendit (str, len, sin)
char *str;
int len;
struct sockaddr_in sin;
{
struct timeval timeout;
int rval; /* number of bytes read */
int mask; /* used for select */
int tries; /* number of tries made */
#ifdef DEBUG
char mbuf[100]; /* message buffer */
#endif
timeout.tv_sec = WAIT;
timeout.tv_usec = 0;
for (tries = 0; tries < 5; tries++) {
if (sendto (ctl, str, len, 0, &sin, sizeof (sin)) < 0) {
error (0, "sendit: sendto");
return (-1);
}
if (Debug)
printf ("\r\nsendit: trying #%d.\r\n", tries);
#ifdef DEBUG
sprintf (mbuf, "sendit: trying #%d ...", tries);
putmessage (mbuf);
#endif DEBUG
mask = 1 << ctl;
if (select (32, &mask, 0, 0, &timeout) <= 0) { /* nothing to read on */
if (Debug)
printf ("\r\nsendit: no response on try #%d.\r\n", tries);
continue;
}
rval = read (ctl, buf, BUFFER); /* read the response */
if (rval < 0) {
error (0, "sendit: read response");
continue;
}
if (Debug)
printf ("sendit: read %d chars.\r\n", rval);
return (rval);
}
if (Debug)
printf ("\r\nsendit: response timed out.\r\n");
#ifdef DEBUG
putmessage ("sendit: response timed out.");
#endif DEBUG
return (-1); /* timed out */
}
!Funky!Stuff!
fi # end of overwriting check
echo shar: extracting "'set.c'" '(3287 characters)'
if test -f 'set.c'
then
echo shar: will not over-write existing file "'set.c'"
else
cat << \!Funky!Stuff! > 'set.c'
#include "defs.h"
/*
* Routines to manage the various user-modifiable variables
* that influence the program's behaviour.
*/
int
Interval = 2, /* number of seconds between messages */
Inverse = 1, /* use inverse video in header lines */
Stop = 1, /* print out the "[Stopped]" mesg on ^Z */
Debug = 0, /* print out gobs of debuuging info */
Bells = 0, /* allow bells to actually beep */
Echo = 1, /* echo kb input while running program */
Hold = 0; /* hold kb input from child program */
#define VALUE (1<<1) /* variable must have a _value_ */
#define BOOLEAN (1<<2) /* is just a toggle */
static struct variable {
char *name; /* name of variable as user sees it */
int *value; /* currently - stored value */
int type; /* boolean or integer-type variable */
} vars[] = {
{ "interval", &Interval, VALUE },
{ "inverse", &Inverse, BOOLEAN },
{ "stop", &Stop, BOOLEAN },
{ "debug", &Debug, BOOLEAN },
{ "bells", &Bells, BOOLEAN },
{ "echo", &Echo, BOOLEAN },
{ "hold", &Hold, BOOLEAN },
{ 0, 0, 0 },
};
/*
* Assign a value to a given variable ...
*/
set (argc, argv)
int argc;
char **argv;
{
struct variable *vp;
char *name;
int vlen;
int toggle = 0;
/* see if it's a toggle */
if (strncmp ("no", *argv, 2) == 0) {
toggle = 1;
*argv += 2;
}
/* search for variable */
vlen = strlen (*argv);
for (vp = vars; vp && (name = vp->name); vp++)
if (strncmp (*argv, name, vlen) == 0)
break;
if (!vp || !name) { /* not found */
sprintf (buf, "%s: No such variable - %s", *argv,
"use 'show' to list all variable names");
putmessage (buf);
return;
}
if (argc == 2) {
if (vp->type == BOOLEAN) {
sprintf (buf,
"%s is a boolean toggle - use 'set %s' or 'set no%s'.",
name, name, name);
putmessage (buf);
} else {
if (!isdigit (argv[1][0]) || toggle) {
sprintf (buf, "%s requires a numeric argument", name);
putmessage (buf);
} else
*vp->value = atoi (argv[1]);
}
} else { /* argc == 1 */
if (vp->type != BOOLEAN) {
sprintf (buf, "%s requires a numeric argument", name);
putmessage (buf);
} else {
if (toggle) /* name was preceeded by 'no' */
*vp->value = 0;
else
*vp->value = 1;
}
}
}
/*
* Show the value of a variable.
*/
show (argc, argv)
int argc;
char *argv[];
{
struct variable *vp;
char *name;
int vlen;
if (argc == 0) { /* list all variables known */
message ("Listing all variable names:");
for (vp = vars; vp && vp->name; vp++)
showvar (vp);
return;
}
while (*argv) {
/* search for variable */
vlen = strlen (*argv);
for (vp = vars; vp && (name = vp->name); vp++)
if (strncmp (*argv, name, vlen) == 0)
break;
if (!vp || !name) { /* not found */
sprintf (buf, "%s: No such variable - %s", *argv,
"use 'show' to list all variable names");
putmessage (buf);
return;
}
/* print out the variable */
showvar (vp);
argv++;
}
}
/*
* Print out one variable nicely...
*/
showvar (vp)
struct variable *vp;
{
if (vp->type == BOOLEAN)
(void) sprintf (buf, "%s is %s", vp->name, *vp->value ? "on" : "off");
else
(void) sprintf (buf, "%s = %d", vp->name, *vp->value);
message (buf);
}
!Funky!Stuff!
fi # end of overwriting check
echo shar: extracting "'sig.c'" '(2331 characters)'
if test -f 'sig.c'
then
echo shar: will not over-write existing file "'sig.c'"
else
cat << \!Funky!Stuff! > 'sig.c'
#include <signal.h>
#include <sys/ioctl.h>
/*
* Interrupt handler - if we have a child, kill it off, else exit.
*/
sigint ()
{
extern int tochild, fromchild;
extern int childpid;
extern int killedchild;
extern int connected;
extern int did_screen;
unsigned char c;
if (childpid > 0) {
kill (childpid, SIGHUP); /* it's sure to die sooner or later!!! */
kill (childpid, SIGINT);
kill (childpid, SIGKILL);
killedchild = 1;
putmessage ("Child has been killed!!!!");
} else {
if (connected && did_screen) {
putmessage ("Really quit? ");
read (0, &c, 1);
c &= 0177;
if (c == 'y' || c == 'Y')
quit ();
putmessage (" ");
} else {
quit ();
}
}
}
/*
* Sigquit handler - save a copy of the screen into a ".dump" file.
*/
sigquit ()
{
dodump (".dump");
}
#ifdef vax
/*
* Generic bad signal handler. Very non-portable,
* at least according to the documentation.
*/
badsig (sig)
int sig;
{
char *s;
cleanup ();
switch (sig) {
case SIGHUP: s = "SIGHUP"; break;
case SIGINT: s = "SIGINT"; break;
case SIGQUIT: s = "SIGQUIT"; break;
case SIGILL: s = "SIGILL"; break;
case SIGTRAP: s = "SIGTRAP"; break;
case SIGIOT: s = "SIGIOT"; break;
case SIGEMT: s = "SIGEMT"; break;
case SIGFPE: s = "SIGFPE"; break;
case SIGKILL: s = "SIGKILL"; break;
case SIGBUS: s = "SIGBUS"; break;
case SIGSEGV: s = "SIGSEGV"; break;
case SIGSYS: s = "SIGSYS"; break;
case SIGPIPE: s = "SIGPIPE"; break;
case SIGALRM: s = "SIGALRM"; break;
case SIGTERM: s = "SIGTERM"; break;
case SIGURG: s = "SIGURG"; break;
case SIGSTOP: s = "SIGSTOP"; break;
case SIGTSTP: s = "SIGTSTP"; break;
case SIGCONT: s = "SIGCONT"; break;
case SIGCHLD: s = "SIGCHLD"; break;
case SIGTTIN: s = "SIGTTIN"; break;
case SIGTTOU: s = "SIGTTOU"; break;
case SIGIO: s = "SIGIO"; break;
case SIGXCPU: s = "SIGXCPU"; break;
case SIGXFSZ: s = "SIGXFSZ"; break;
case SIGVTALRM: s = "SIGVTALRM"; break;
case SIGPROF: s = "SIGPROF"; break;
case SIGWINCH: s = "SIGWINCH"; break;
case SIGUSR1: s = "SIGUSR1"; break;
case SIGUSR2: s = "SIGUSR2"; break;
}
printf ("\r\n\r\nCaught %s signal!!!! Exiting...\r\n\r\n", s);
exit (1);
}
#else
badsig ()
{
cleanup ();
printf ("\r\n\r\nCaught an unknown bad signal! Exiting ...\r\n\r\n");
exit (1);
}
#endif vax
!Funky!Stuff!
fi # end of overwriting check
echo shar: extracting "'stop.c'" '(501 characters)'
if test -f 'stop.c'
then
echo shar: will not over-write existing file "'stop.c'"
else
cat << \!Funky!Stuff! > 'stop.c'
#include <signal.h>
#include <curses.h>
#include "defs.h"
/*
* Routine to handle SIGTSTP and the like.
*/
sigstop ()
{
extern int stream;
extern int maxy;
int (*func)();
func = signal (SIGTSTP, SIG_DFL); /* save old SIGTSTP routine */
if (Stop)
(void) write (stream, "\n[ Stopped ]", 12);
tstp (); /* curses routine to stop and restart */
if (Stop)
(void) write (stream, "\r\f", 2); /* clear this line */
signal (SIGTSTP, func); /* and restore SIGTSTP routine */
}
!Funky!Stuff!
fi # end of overwriting check
echo shar: extracting "'strsave.c'" '(324 characters)'
if test -f 'strsave.c'
then
echo shar: will not over-write existing file "'strsave.c'"
else
cat << \!Funky!Stuff! > 'strsave.c'
#include <signal.h>
#include <stdio.h>
/*
* Allocate enough space for the given string and copy it over.
*/
char *
strsave (s)
char *s;
{
register char *new;
char *malloc();
if (new = malloc ((unsigned) (strlen (s) + 1)))
strcpy (new, s);
else
error (1, "strsave: cannot malloc memory");
return (new);
}
!Funky!Stuff!
fi # end of overwriting check
echo shar: extracting "'tilde.c'" '(866 characters)'
if test -f 'tilde.c'
then
echo shar: will not over-write existing file "'tilde.c'"
else
cat << \!Funky!Stuff! > 'tilde.c'
#include <pwd.h>
/*
* Expand a name containing a tilde to the full pathname.
*/
char *
exptilde (str)
char *str;
{
char buf[512];
char name[20];
char *i, *index();
char *getenv(), *strsave();
struct passwd *pw, *getpwnam();
if (*str != '~') /* doesn't contain a tilde */
return (strsave (str));
if (i = index (str, '/')) /* find slash, if any */
*i = '\0';
strcpy (name, str+1); /* copy name after tilde */
if (*name == '\0') /* was "~/" */
strcpy (buf, getenv ("HOME")); /* so use our home */
else {
if ((pw = getpwnam (name)) == (struct passwd *) 0) {
sprintf (buf, "Unknown user: %s", name);
putmessage (buf);
return ((char *) 0);
}
strcpy (buf, pw->pw_dir); /* fill in that user's home */
}
if (i) {
*i = '/'; /* and put back trailing pathname */
strcat (buf, i);
}
return (strsave (buf));
}
!Funky!Stuff!
fi # end of overwriting check
echo shar: extracting "'timer.c'" '(373 characters)'
if test -f 'timer.c'
then
echo shar: will not over-write existing file "'timer.c'"
else
cat << \!Funky!Stuff! > 'timer.c'
/*
* Timer routine.
* When we come here, see if we have any pending calls or messages,
* call the appropriate routines, then reset the timer.
*/
timer ()
{
extern int pending, msgpending;
if (pending) /* have pending calls */
reinvite ();
if (msgpending >= 0)
showmessage ();
if (pending || msgpending >= 0) /* set the next alarm */
alarm (1);
}
!Funky!Stuff!
fi # end of overwriting check
echo shar: extracting "'who.c'" '(2104 characters)'
if test -f 'who.c'
then
echo shar: will not over-write existing file "'who.c'"
else
cat << \!Funky!Stuff! > 'who.c'
#include "../common.h"
#include "defs.h"
#include <netdb.h>
#include <utmp.h>
#include <sys/file.h>
/*
* Print out who is on the machine.
* If an argument is specified, only show occurrences of that user.
*/
who (argc, argv)
int argc;
char *argv[];
{
extern int maxx; /* max display line length */
int found = 0; /* found who we're looking for */
int utfd; /* /etc/utmp file descriptor */
int len = 256; /* length of current line */
int ulen; /* length of buf this user */
struct utmp utmp; /* one entry in /etc/utmp */
char line[256]; /* line buffer */
char buf[20]; /* and one entry buffer */
char *host;
struct hostent *hp, *gethostbyname();
struct sockaddr_in sin;
if (argc == 1 && **argv == '@') {
host = &argv[0][1];
if (isdigit (*host))
sin.sin_addr.s_addr = inet_addr (host);
else if (hp = gethostbyname (host))
bcopy ((char *)hp->h_addr, (char *)&sin.sin_addr, hp->h_length);
else {
sprintf (buf, "who: unknown host: %s", host);
message (buf);
}
sin.sin_port = port;
sin.sin_family = AF_INET;
sprintf (buf, "%c%c", ESC, WHO);
if (sendto (ctl, buf, 2, 0, &sin, sizeof (sin)) < 0)
error (0, "who: sendto");
return;
}
if ((utfd = open ("/etc/utmp", O_RDONLY)) < 0) {
message ("Can't open /etc/utmp!!!");
return;
}
line[0] = '\0';
while (read (utfd, (char *)&utmp, sizeof (struct utmp))) {
if (utmp.ut_name[0] == '\0')
continue;
if (argc && strncmp (argv[0], utmp.ut_name, 8))
continue;
found++;
sprintf (buf, " %.8s(%.5s)", utmp.ut_name, utmp.ut_line);
ulen = strlen (buf);
if (len + ulen + 2 < maxx) { /* just add to current message */
strcat (line, buf);
} else { /* buf is too long - print line */
message (line);
len = Inverse ? 2 : 0; /* kludge for inverse glitch */
sprintf (line, "who:%s", buf);
}
len += ulen; /* add to total line length */
}
if (len)
message (line);
close (utfd);
if (argc && !found)
message ("<not logged on>");
}
!Funky!Stuff!
fi # end of overwriting check
echo shar: extracting "'windows.c'" '(12693 characters)'
if test -f 'windows.c'
then
echo shar: will not over-write existing file "'windows.c'"
else
cat << \!Funky!Stuff! > 'windows.c'
/*
* Routines to manage the windows. SOme of this is pretty ugly
* stuff, especially as most of the time is spent here.
*
* We manage to resize windows by buffering all parts of a conversation,
* so we just (re)print the buffered part. The buffering also accounts
* for most of the lack of speed. A future enhancement will probably
* be to replace the constant buffering with a smarter routine that
* will just grab each window from the curses screen buffer when we call
* stretch() ...
*/
#include <curses.h>
#include <sys/ioctl.h>
#include <signal.h>
#undef ctrl() /* defined in curses.h */
#include "defs.h"
#ifndef cbreak() /* these are defined in *some* versions of curses.h */
#define cbreak() (_tty.sg_flags |= CBREAK, _rawmode = TRUE, stty(_tty_ch,&_tty))
#define nocbreak() (_tty.sg_flags &= ~CBREAK,_rawmode=FALSE,stty(_tty_ch,&_tty))
#endif !cbreak()
#ifndef WINDOWS
#define WINDOWS 32 /* we can only fit 23 on a sun !!! */
#endif
struct win {
int inuse; /* 1 if in use, 0 if not */
char *login; /* login name of this person */
char *host; /* their host name */
char *tty; /* tty */
char *realname; /* finger name, if any */
int upper; /* top line of this window */
int lower; /* bottom line of this window */
int y; /* y coord rel to top of wind */
int x; /* last known x coordinate */
int old; /* index of oldest ch in buf */
int new; /* where next char goes */
char *buf; /* text buffer */
};
static struct win windows[WINDOWS];
static struct win *currwin; /* pointer to current window */
int current = 0; /* currently active window */
int users; /* number of users/windows */
int upper_y; /* boundaries of current window */
int lower_y;
int stretching = 0;
int did_screen = 0; /* have set up curses */
static int y , x; /* current screen coordinates */
extern int sigstop(); /* ^Z handling routine */
static int ourwin; /* the window # that is _ours_ */
/*
* Mark all of the windows as not being in use.
*/
setup ()
{
int i;
for (i = 0; i < WINDOWS; i++) {
windows[i].inuse = 0;
windows[i].old = 0;
windows[i].new = 0;
}
initscr();
did_screen = 1;
clear ();
cbreak ();
noecho ();
nonl ();
refresh ();
signal (SIGTSTP, sigstop);
geteditchars (); /* get and save his editing characters */
maxx = stdscr->_maxx - 1; /* save max screen coordinates */
maxy = stdscr->_maxy - 1;
windows[0].upper = upper_y = 0; /* initial screen bounds */
windows[0].lower = lower_y = maxy - 1;
windows[0].y = y = 1; /* initial window position */
windows[0].x = x = 0;
currwin = &windows[0];
move (1, 0);
users = 0; /* not talking to anyone yet */
}
/*
* Restore everything to how it was before we came...
*/
cleanup ()
{
move (maxy, 0);
refresh ();
nocbreak ();
echo ();
endwin ();
did_screen = 0;
}
/*
* Select the new current window to use, set the window bounds,
* and set the new cursor position ... Returns the old window number.
*/
selwin (win)
char win;
{
int old = current;
if (win == current && !stretching) /* no change */
return (win);
if (win == ourwin) /* map our window to window 0 */
win = 0;
if (windows[win].inuse == 0)
return (old);
if (win >= 0 && win < WINDOWS) {
currwin->y = y; /* save last coordinates */
currwin->x = x;
currwin = &windows[win]; /* set pointer to new window */
y = currwin->y; /* and retrieve from new win */
x = currwin->x;
upper_y = currwin->upper; /* get bounds */
lower_y = currwin->lower;
move (y + upper_y, x); /* restore cursor pos */
current = win;
}
return (old);
}
/*
* Add a new window to the screen.
* Buf looks like "user:host:tty:realname"
*/
adduser (slot, buf)
int slot;
char *buf;
{
char *i;
/*
* We rely on the fact that the first window given to us is
* our own, so we remember which one that is and remap it
* to the top window. This makes people happier.
*/
if (users == 0) {
ourwin = slot;
slot = 0;
}
if (windows[slot].inuse) /* that window is already in use */
return;
windows[slot].inuse = 1; /* mark as being in use */
windows[slot].x = 0; /* and current coordinates */
windows[slot].y = 1;
windows[slot].buf = malloc (BUFFER); /* and grab a buffer for it */
windows[slot].old = 0; /* nothing in the buffer yet */
windows[slot].new = 0;
/* get the info out of the buffer line */
if (i = index (buf, ':')) /* name */
*i++ = '\0';
else
return;
windows[slot].login = strsave (buf);
buf = i;
i = index (buf, ':'); /* host */
*i++ = '\0';
windows[slot].host = strsave (buf);
buf = i;
i = index (buf, ':'); /* tty */
*i++ = '\0';
windows[slot].tty = strsave (buf);
buf = i;
windows[slot].realname = strsave (buf); /* real name */
if (users++ == 1) /* add another user to the count */
(void) write (1, "\07\07", 2); /* initial connection - beep the user */
stretch (0); /* and recompute all the windows */
}
/*
* Delete a user and his window.
*/
deluser (win)
char win;
{
if (win < 0 || win >= WINDOWS)
return;
if (windows[win].inuse == 0)
return;
windows[win].inuse = 0; /* this window no longer in use */
free (windows[win].login); /* so free up any space allocated for it */
free (windows[win].host);
free (windows[win].tty);
free (windows[win].realname);
free (windows[win].buf);
if (--users > 1) { /* decrement the count */
stretch (0); /* and redraw */
refresh ();
} else /* if only one user left .. */
quit (); /* then we're done */
}
/*
* Show the given character, performing word and line erase.
*/
showch (ch)
char ch;
{
if (users == 0)
return;
if (!stretching) {
/* save char into buffer */
if (currwin->new >= BUFFER)
error (1, "New >= BUFFER in showch()");
currwin->buf[currwin->new++] = ch;
if (currwin->new >= BUFFER) /* wrap around */
currwin->new = 0;
if (currwin->new == currwin->old) /* tail caught up */
currwin->old++;
if (currwin->old >= BUFFER) /* wrap old around */
currwin->old = 0;
}
switch (ch) {
case '\0177': /* delete */
case ctrl(H): /* backspace */
if (x > 0) {
addstr ("\b \b");
x--;
}
break;
case ctrl(I): /* tab */
addch (' ');
x++;
while (x++ % 8)
addch (' ');
break;
case ctrl(J): /* newline */
x = 0;
if ((++y + upper_y) > lower_y)
y = 1;
move (y + upper_y, 0);
clrtoeol ();
break;
case ctrl(L): /* clear to end of current line */
clrtoeol ();
break;
case ctrl(M): /* carriage return */
x = 0;
move (y + upper_y, 0);
break;
case ctrl(N): /* clear window */
for (y = 1; (y+upper_y) <= lower_y; y++) {
move (y + upper_y, 0);
clrtoeol ();
}
y = 1; x = 0;
move (y + upper_y, 0);
break;
case ctrl(U): /* line kill */
case ctrl(X): x = 0;
move (y + upper_y, 0);
clrtoeol ();
break;
case ctrl(W): /* word erase */
while (x >= 0) { /* nuke the spaces */
if (inch () != ' ')
break;
addstr (" \b\b");
if (x > 0)
x--;
else
break;
}
while (x >= 0) { /* and the non-spaces */
if ((ch = inch ()) == ' ') {
addch (ch);
x++;
break;
}
addstr (" \b\b");
if (x > 0)
x--;
else
break;
}
break;
case ctrl(^): /* home cursor */
y = 1; x = 0;
move (y + upper_y, 0);
break;
case ctrl(G): /* bell - switchable */
if (Bells) {
(void) write (1, "\007", 1);
break;
}
/* fall through ... */
default: if (ch < ' ') {
addch ('^');
ch += '@';
x++;
}
addch (ch);
x++;
break;
}
if (x >= maxx) { /* have wrapped around to next line */
y++;
x %= maxx;
if (y + upper_y > lower_y) /* wrapped back to top of window */
y = 1;
move (upper_y + y, 0);
clrtoeol ();
}
}
/*
* Print a message to the bottom line.
*/
putmessage (str)
char *str;
{
int y, x;
extern int Inverse;
extern int did_screen;
if (!did_screen) {
printf ("%s\r\n", str);
fflush (stdout);
return;
}
touched25 = 1; /* has been changed */
getyx (stdscr, y, x); /* save current pos */
move (maxy, 0); /* go to bottom line */
clrtoeol (); /* clear it */
refresh (); /* they see it clear */
if (Inverse)
standout ();
while (*str) { /* print out the message, removing ctrl chars */
if (*str < ' ') {
addch ('^');
addch (*str + '@');
} else
addch (*str);
str++;
}
if (Inverse)
standend ();
move (y, x); /* restore old pos */
refresh (); /* etc ... */
}
/*
* Print out a header for the given slot.
*/
header (num)
int num;
{
register int i;
extern int Inverse;
if (windows[num].inuse == 0) {
error (0, "header(): slot not in use");
return;
}
sprintf (buf, "---- %s@%s on %s (%s) ", windows[num].login,
windows[num].host, windows[num].tty, windows[num].realname);
for (i = strlen (buf); i < maxx; i++)
buf[i] = '-';
buf[maxx-2] = '\0'; /* two (four?) for magic-cookie terminals */
move (windows[num].upper, 0);
if (Inverse)
standout ();
addstr (buf);
if (Inverse)
standend ();
}
/*
* Take the current screen and contort it to handle the new windows.
*
* Handles both the screen changing size and just the
* number of windows changing.
*
* We set caught_sig to be non-zero if coming here on SIGWINCH, 0 otherwise.
* We also only refresh the screen if we caught the signal....
*/
stretch (caught_sig)
int caught_sig;
{
register int i, w;
register int extra;
register int lines;
int oldy, oldx;
int newy, newx;
int lines_win;
int oldcurr = current;
if (users == 0) /* nothing to recompute */
return;
stretching = 1;
oldy = maxy + 1;
oldx = maxx + 1;
if (caught_sig) { /* got SIGWINCH, have to redo screen */
#if defined(TIOCGWINSZ) || defined(TIOCGSIZE)
#ifdef TIOCGWINSZ
struct winsize winsize;
#else TIOCGSIZE /* (older?) suns */
struct ttysize ttysize;
#endif TIOGSIZE
/* get new screen size */
#ifdef TIOCGWINSZ
if (ioctl (0, TIOCGWINSZ, &winsize)) {
error (0, "ioctl: TIOCGWINSZ");
return;
}
newy = winsize.ws_row;
newx = winsize.ws_col;
#else TIOCGSIZE
if (ioctl (0, TIOCGSIZE, &ttysize)) {
error (0, "ioctl: TIOCGSIZE");
return;
}
newy = ttysize.ts_lines;
newx = ttysize.ts_cols;
#endif TIOCGSIZE
#else /* don't have window size ioctls */
/*
* Can't get new window size, so presumably sigwinch
* doesn't exist and this code will never happen !!!
*/
newy = oldy;
newx = oldx;
#endif TIOCGWINSZ
maxy = newy - 1; /* max screen coordinate - zero-based */
maxx = newx - 1;
} else { /* screen size hasn't changed */
newy = oldy;
newx = oldx;
}
if (newx != oldx || newy != oldy) { /* have to shut down curses */
echo ();
nocbreak ();
endwin ();
did_screen = 0;
initscr (); /* and restart it */
did_screen = 1;
cbreak ();
noecho ();
nonl ();
signal (SIGTSTP, sigstop);
} else { /* just have to clear all the lines */
for (i = 0; i < newy; i++) {
move (i, 0);
clrtoeol ();
}
}
lines_win = (newy - 1) / users; /* recompute window boundaries */
extra = (newy - 1) - (users * lines_win); /* total extra lines */
lines = 0;
/*
* Now calculate the new boundaries and
* copy the buffer back to the new window.
*/
x = 0; y = 1; /* go back to the beginning of the window */
for (w = 0; w < WINDOWS; w++) { /* now go copy boundaries */
if (windows[w].inuse == 0)
continue;
windows[w].upper = lines; /* new top boundary */
if (extra-- > 0) /* extra lines for this window */
lines++;
lines += lines_win; /* add on lines */
windows[w].lower = lines-1; /* new bottom boundary */
header (w); /* redraw the window header */
windows[w].x = 0; /* reset position to top corner */
windows[w].y = 1;
selwin (w); /* switch to this window */
/* and dump the buffer back into the window */
for (i = windows[w].old; i != windows[w].new; i = (i+1) % BUFFER)
showch (windows[w].buf[i]);
}
move (y, x);
selwin (oldcurr); /* reselect the right window */
if (caught_sig)
refresh (); /* and make it all visible */
stretching = 0;
}
!Funky!Stuff!
fi # end of overwriting check
# End of shell archive
exit 0
More information about the Comp.sources.unix
mailing list