Relay-Version: version B 2.10 5/3/83; site utzoo.UUCP Posting-Version: version B 2.10.3 4.3bsd-beta 6/6/85; site ucbvax.BERKELEY.EDU Path: utzoo!linus!decvax!ucbvax!broome From: broome@ucbvax.BERKELEY.EDU (Jonathan C. Broome) Newsgroups: net.sources Subject: source to "phone" system (part 2 of 4) Message-ID: <11312@ucbvax.BERKELEY.EDU> Date: Sun, 29-Dec-85 03:40:59 EST Article-I.D.: ucbvax.11312 Posted: Sun Dec 29 03:40:59 1985 Date-Received: Sun, 29-Dec-85 20:16:09 EST References: <11311@ucbvax.BERKELEY.EDU> Organization: University of California at Berkeley Lines: 3327 #-----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 #include #include #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 #include "defs.h" #include "../common.h" #include #include /* 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 " 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 ** " 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 #include #include /* * 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@host [tty]", pagehelp }, { "page", page, 2, 3, "user@host [tty]", pagehelp }, { "phone", page, 2, 3, "user@host [tty]", pagehelp }, { "cancel", cancel, 2, 3, "user@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 #include #include /* * 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 #include #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) }, /* -> 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 #include #include #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@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 #include /* * 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 #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 #include #include #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 #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 /* * 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 #include #include /* these two are for wait3() */ #include #include #include #include /* * 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 #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 #include /* * 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 #include #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 #include /* * 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 /* * 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 #include #include /* * 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 (""); } !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 #include #include #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