[comp.sources.misc] v03i086: VAX-like Phone Utility for SysV

jack@swlabs.UUCP (Jack Bonn) (07/17/88)

Posting-number: Volume 3, Issue 86
Submitted-by: "Jack Bonn" <jack@swlabs.UUCP>
Archive-name: sys5-phone

[Another "talk" clone.  ++bsa]

#!/bin/sh
# shar:	Shell Archiver  (v1.22)
#
#	Run the following text with /bin/sh to create:
#	  phone.1
#	  phone.c
#	  Makefile
#
sed 's/^X//' << 'SHAR_EOF' > phone.1 &&
X.TH PHONE 1 "July 17, 1988"
X.UC 4
X.SH NAME
Xphone \- phone another user, typing screen to screen.
X.SH SYNOPSIS
Xphone user
X.SH DESCRIPTION
X.I Phone 
Xcauses a message to appear on the terminal being used by
X.I user
Xthat indicates that someone is trying to phone him.  
XIf the other user also executes phone, then both screens are cleared
Xand the users are set up in a two way conversation.
X.PP
XEach user's input is displayed at the bottom of their own
Xscreen and at the top of the other user's screen.  The
Xfollowing control characters are handled:
X.TP 20
X.BI "ERASE\ (Control\ H)"
XErase the character before the cursor.
X.TP
X.BI "KILL\ (Control\ U)"
XErase the line the cursor is on and move the cursor to the beginning 
Xof the (now blank) line.
X.TP
X.BI "Control\ L"
XRefresh your own screen.
X.TP
X.BI "Control\ G"
XEither flash (preferably) or ring the bell on other user's terminal.
X.TP
X.BI "EOF\ (Control\ D)"
XDiscontinue execution.  The other user is informed.
X.PP
X(The characters in parenthesis above are typical assignments
Xfor these control characters.  Those assigned by the user
Xwill be used.  See STTY(1)).
X.SH BUGS
X.I Phone
Xhandles only two users.  If a third user attempts entry into
Xa conversation, strange things happen.
X.PP
XWorks only when each user is logged in once.  An error
Xmessage is indicated otherwise (although a "-d tty" option
Xwould be an obvious extension).
X.PP
XRequires System V, since it uses Sys V IPC.
X.SH AUTHOR
XJack Bonn
X.br
XSoftware Labs, Ltd.
X.br
XBox 451
X.br
XEaston, CT  06612
X.PP
Xjack@swlabs.UUCP
X.br
Xuunet!swlabs!jack
SHAR_EOF
chmod 0640 phone.1 || echo "restore of phone.1 fails"
sed 's/^X//' << 'SHAR_EOF' > phone.c &&
X#include <sys/param.h>
X#include <sys/types.h>
X#include <sys/ipc.h>
X#include <sys/msg.h>
X#include <utmp.h>
X#include <signal.h>
X#include <curses.h>
X#include <stdio.h>
X#include <string.h>
X
X#define SAME		0	/* For calls to strcmp */
X#define CTRL_G		('G'-'A'+1)
X#define CTRL_L		('L'-'A'+1)
X
X/*
X	packet.type is one of these --
X*/
X#define	ERASE		1	/* Erase previous character */
X#define	KILL		2	/* Erase line cursor is on */
X#define	DISPLAY		3	/* Display character on screen */
X#define	ADIOS		4	/* Hang up/disolve connection */
X#define	REFRESH		5	/* Redraw screen on CTL-L */
X#define	FLASH		6	/* Ring bell/flash screen */
X
X/*
X	packet.origin is one of these --
X*/
X#define	LOCAL		0
X#define	REMOTE		1
X
X#define	IPC_ID		230	/* Upper part of key (for uniqueness) */
X#define RING_TIME	10	/* Ring cadence in seconds */
X
Xint pid = 0;			/* pid for offspring, if non-zero */
X
Xint loc_q, rem_q;		/* message queue id's */
Xkey_t my_key, other_key;	/* keys for the two queues */
Xkey_t ftok();			/* convert a file to a key */
X
Xchar *my_name, *other_name;	/* Both user names */
X
Xchar *my_tty;			/* Path of my device */
Xchar other_tty[24];		/* Path of other users device */
X
Xint erase_ch, kill_ch, eof_ch;	/* Special control chars for user */
X
XWINDOW *topwin, *botwin;	/* curses windows */
XWINDOW *topleg, *botleg;	/* windows for legends */
Xstruct utmp *u_elem, *getutent();
Xstruct termio new_ioargs, old_ioargs;
X
Xstruct packet			/* This is the packet that we ipc */
X{
X    long type;
X    int origin;
X    char keypress;
X} s_pkt, r_pkt;
X
Xvoid wrapup();			/* Forward reference */
Xvoid wrapup_child();		/* Forward reference */
Xvoid exit();
X
Xmain(argc,argv)
X    int argc;
X    char *argv[];
X    {
X
X    if (argc != 2) {
X	printf ("usage: phone user\n");
X	exit(1);
X    }
X
X    signal (SIGINT, wrapup);
X
X    get_names(argv[1]);		/* Get our name and other user's */
X
X    if (!get_ttys())		/* Get our tty and other user's */
X	wrapup();
X
X    get_keys();
X
X    if (!open_queues())
X	wrapup();
X
X    init_screen();
X
X    if (pid=fork()) {
X	scrn_proc();	/* Returns when ADIOS packet received */
X	wrapup();
X    } else {
X	signal (SIGQUIT, wrapup_child);
X	key_proc();	/* Never returns, must be killed */
X    }
X    /* NOTREACHED */
X}
X
Xvoid wrapup () {
X    int ret_code;			/* Wait insists on an argument */
X    struct msqid_ds buf;
X
X    if (pid) {				/* Kill sibbling if present */
X	kill (pid, SIGQUIT);
X	wait (&ret_code);
X    }
X
X    endwin();				/* Clean up curses windows */
X    msgctl (loc_q, IPC_RMID, &buf);	/* Remove our message queue */
X    send_remote (ADIOS, 0);		/* In case other side is still up */
X    exit(0);
X}
X
Xkey_proc() {
X    int ch;
X
X    /* Keyboard process */
X
X    init_kbd();
X
X    for (;;) {
X	ch=getchar();
X
X        if (ch == erase_ch)
X	    send_both (ERASE, 0);
X
X        else if (ch == kill_ch)
X	    send_both (KILL, 0);
X
X        else if (ch == CTRL_L)
X	    send_local (REFRESH, 0);
X
X        else if (ch == CTRL_G)
X	    send_both (FLASH, 0);
X
X	else if (ch == eof_ch)
X	    send_local (ADIOS, 0);
X
X        else {
X            if (ch == '\r')
X		ch = '\n';
X	    send_both (DISPLAY, ch);
X
X        }
X    }
X}
X
Xvoid wrapup_child () {
X    reset_kbd();			/* Put ioctl stuff where it was */
X    exit(0);
X}
X
Xscrn_proc() {
X    int y, x;
X    int ch;
X    WINDOW *window;			/* temp curses windows */
X
X    /* Screen process */
X
X    scrn_legend (topleg, other_name, other_tty);
X    scrn_legend (botleg, my_name, my_tty);
X
X    wrefresh (topwin);
X    wrefresh (botwin);
X
X    do {
X	msgrcv (loc_q, &r_pkt, sizeof(r_pkt) - sizeof(r_pkt.type), 0L, 0);
X
X	if (r_pkt.origin == LOCAL)
X	    window = botwin;
X        else
X	    window = topwin;
X
X        ch = r_pkt.keypress;
X
X	switch ((int)r_pkt.type) {
X
X	case ERASE:
X	    waddch(window, '\b');
X	    wdelch(window);
X	    break;
X
X	case KILL:
X	    wdeleteln(window);
X	    getyx(window, y, x);
X	    wmove(window, y, 0);
X	    break;
X
X	case DISPLAY:
X	    waddch (window, (char)ch);
X	    break;
X
X	case ADIOS:
X	    if (r_pkt.origin == REMOTE)
X	        wprintw(botwin, "\n*** %s has hung up. ***\n", other_name);
X	    break;
X
X	case REFRESH:
X	    clearok(curscr, TRUE);
X	    break;
X
X	case FLASH:
X	    flash();
X	    break;
X
X	}
X
X	if (r_pkt.origin == REMOTE)
X	    wrefresh (topwin);
X
X	wrefresh (botwin);
X
X    } while (r_pkt.type != ADIOS);
X}
X
Xint open_queues()
X    {
X    int seconds;
X
X    loc_q = msgget (my_key, 0666 | IPC_CREAT);
X
X    if (loc_q == -1) {
X	printf ("Unable to create my queue\n");
X	return (FALSE);
X    }
X
X    /*
X	Stand on our head to test creation of rem_q more often 
X	than when we ring the other user.
X    */
X    for (seconds=RING_TIME; ; seconds++) {
X	rem_q = msgget (other_key, 0);
X
X	if (rem_q != -1)
X	    break;
X
X	if (seconds == RING_TIME) {
X	    seconds = 0;
X	    if (!ring (other_tty)) {
X		printf ("%s's phone is off the hook (mesg -n).\n\n", other_name);
X		return (FALSE);
X	    }
X	    printf ("Ringing %s on %.14s\n\n", other_name, other_tty);
X	}
X	if (sleep (1))	/* != 0 if interrupted by other signal */
X	    return (FALSE);
X    }
X    return (TRUE);
X}
X
Xinit_screen() {
X    initscr();
X    topleg = newwin(1,0,0,0);
X    topwin = newwin(10,0,1,0);
X    botleg = newwin(1,0,12,0);
X    botwin = newwin(10,0,13,0);
X    idlok(topwin,TRUE);
X    idlok(botwin,TRUE);
X    scrollok(topwin,TRUE);
X    scrollok(botwin,TRUE);
X}
X
Xinit_kbd() {
X    ioctl (0, TCGETA, &old_ioargs);
X    erase_ch = old_ioargs.c_cc[VERASE];
X    kill_ch = old_ioargs.c_cc[VKILL];
X    eof_ch = old_ioargs.c_cc[VEOF];
X    new_ioargs = old_ioargs;
X    new_ioargs.c_lflag &= ~(ICANON | ECHO);
X    new_ioargs.c_cc[VMIN] = (char)1;
X    ioctl (0, TCSETA, &new_ioargs);
X}
X
Xreset_kbd() {
X    ioctl (0, TCSETA, &old_ioargs);
X}
X
Xget_names(cmd_arg)
X    char *cmd_arg;
X    {
X    char *getenv();
X
X    my_name = getenv("LOGNAME");
X    other_name = cmd_arg;
X    }
X
Xint get_ttys()
X{
X    int found;
X    char *ttyname();
X
X    my_tty = ttyname(0);
X    found=0;
X    while(u_elem=getutent()) {
X
X	if (strcmp(u_elem->ut_user, other_name) == SAME) {
X	    found++;
X	    strcpy(other_tty, "/dev/");
X	    strncpy(&other_tty[5], u_elem->ut_line, sizeof(u_elem->ut_line));
X	}
X    }
X    if (found != 1) {
X	if (found == 0)
X	    printf("User %s is not logged in.\n", other_name);
X	else
X	    printf("User %s is logged in more than once.\n", other_name);
X	return (FALSE);
X    }
X    return (TRUE);
X}
X
Xget_keys()	/* Get unique keys for naming message queues */
X{
X    my_key = ftok (my_tty, IPC_ID);
X    other_key = ftok (other_tty, IPC_ID);
X}
X
Xint ring (device)
X    char *device;
X    {
X    FILE *out_s;
X
X    if (!(out_s = fopen (device, "w")))
X	return (FALSE);
X
X    fprintf (out_s, "\n%s is phoning you.\n", my_name);
X    fprintf (out_s, "Type 'phone %s' to answer.\007\n\n", my_name);
X    fclose (out_s);
X    return (TRUE);
X}
X
Xscrn_legend (window, user, terminal)
X    WINDOW *window;
X    char *user;
X    char *terminal;
X    {
X    char buffer[40];
X
X    wattron (window, A_REVERSE | A_BOLD);
X    sprintf (buffer, "%s (%s):", user, terminal);
X    wprintw (window, "%-*s", COLS, buffer);
X    wrefresh (window);
X    }
X
Xsend_local (type, ch)
X    int type;
X    int ch;
X    {
X    s_pkt.type = type;
X    s_pkt.keypress = ch;
X    s_pkt.origin = LOCAL;
X    msgsnd (loc_q, &s_pkt, sizeof(s_pkt) - sizeof(s_pkt.type), 0);
X    }
X
Xsend_remote (type, ch)
X    int type;
X    int ch;
X    {
X    s_pkt.type = type;
X    s_pkt.keypress = ch;
X    s_pkt.origin = REMOTE;
X    msgsnd (rem_q, &s_pkt, sizeof(s_pkt) - sizeof(s_pkt.type), 0);
X    }
X
Xsend_both (type, ch)
X    int type;
X    int ch;
X    {
X    send_local (type, ch);
X    send_remote (type, ch);
X    }
SHAR_EOF
chmod 0644 phone.c || echo "restore of phone.c fails"
sed 's/^X//' << 'SHAR_EOF' > Makefile &&
Xphone:	phone.c
X	cc phone.c -o phone -lcurses
X
Xlint:
X	lint phone.c -lcurses
X
Xinstall:
X	cp phone /usr/lbin
X
Xshar:
X	shar phone.1 phone.c Makefile >phone.sh
SHAR_EOF
chmod 0640 Makefile || echo "restore of Makefile fails"
exit 0
-- 
Jack Bonn, <> Software Labs, Ltd, Box 451, Easton CT  06612
uunet!swlabs!jack