jack@swlabs.UUCP (Jack Bonn) (06/13/88)
#!/bin/sh # shar: Shell Archiver (v1.22) # # Run the following text with /bin/sh to create: # phone.doc # phone.c # Makefile # sed 's/^X//' << 'SHAR_EOF' > phone.doc && X phone(1) phone(1) X X NAME X phone - phone another user, typing screen to screen. X X SYNOPSIS X phone user X X DESCRIPTION X phone causes text to appear on user's terminal indicating X that someone is trying to phone him. If he also executes X phone, the two users are set up in a two way conversation. X X Each user's input is displayed at the bottom of their own X screen and at the top of the other user's screen. The X following control characters are handled: X X ERASE (Control H) Erase character before the cursor. X X KILL (Control U) Erase line cursor is on and move the X cursor to the beginning of the line. X X Control L Refresh your own screen. X X Control G Either flash (preferably) or ring bell X on other user's terminal. X X EOF (Control D) Discontinue execution. Other user is X informed. X X (The characters in parenthesis above are typical assignments X for these control characters. Those assigned by the user X will be used. See STTY(1)). X X BUGS X Handles only two users. If a third user attempts entry into X a conversation, strange things happen. X X Only works when each user is only logged in once. An error X message is indicated otherwise (although a "-d tty" option X would handle things better). X X Requires System V, since it uses Sys V IPC. X X X Jack Bonn, <> Software Labs, Ltd, Box 451, Easton CT 06612 X uunet!swlabs!jack SHAR_EOF chmod 0666 phone.doc || echo "restore of phone.doc fails" sed 's/^X//' << 'SHAR_EOF' > phone.c && 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 <termio.h> X#include <curses.h> X#include <stdio.h> X X#define SAME 0 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 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 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, *window; /* curses windows */ Xstruct utmp *getutent(),*u_elem; 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 */ 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 if (!get_keys(argv[1])) X wrapup(); X X if (!open_queues(argv[1])) X wrapup(); X X init_screen(); X X if (pid=fork()) { X screen_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} X Xvoid wrapup () { X int ret_code; /* Wait insists on an argument */ 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, &s_pkt); /* Remove our message queue */ 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_loc_rem (ERASE, 0); X X else if (ch == kill_ch) X send_loc_rem (KILL, 0); X X else if (ch == CTRL_L) X send_local (REFRESH, 0); X X else if (ch == CTRL_G) X send_loc_rem (FLASH, 0); X X else if (ch == eof_ch) X send_loc_rem (ADIOS, 0); X X else { X if (ch == '\r') X ch = '\n'; X send_loc_rem (DISPLAY, ch); X X } X } X} X Xvoid wrapup_child () { X reset_kbd(); /* Put ioctl stuff where it was */ X exit(0); X} X Xscreen_proc() { X int y, x; X int ch; X X /* Screen process */ X X wrefresh (topwin); X wrefresh (botwin); X X do { X msgrcv (loc_q, &r_pkt, sizeof(r_pkt) - sizeof(r_pkt.type), 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 (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, ch); X break; X X case ADIOS: X if (r_pkt.origin == REMOTE) X waddstr(botwin, "\n*** Other user has hung up. ***\n"); 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(user) X char *user; X { 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 for (;;) { X rem_q = msgget (other_key, 0); X X if (rem_q != -1) X break; X X if (!ring (other_tty)) { X printf ("%s's phone is off the hook (mesg -n).\n\n", user); X return (FALSE); X } X X printf ("Ringing %s on %.14s\n\n", user, other_tty); X sleep (5); X } X return (TRUE); X} X Xinit_screen() { X initscr(); X topwin = newwin(12,COLS,0,0); X botwin = newwin(12,COLS,12,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 Xint get_keys(user) /* Get unique keys for naming message queues */ X char *user; X { X int found; X char *my_tty, *ttyname(); X X my_tty = ttyname(0); X my_key = ftok (my_tty, IPC_ID); X X found=0; X while(u_elem=getutent()) { X X if (strcmp(u_elem->ut_user, user) == 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 } X X if (found != 1) { X X if (found == 0) X printf("User %s is not logged in.\n", user); X else X printf("User %s is logged in more than once.\n", user); X return (FALSE); X } X X other_key = ftok (other_tty, IPC_ID); X X return (TRUE); X} X Xint ring (device) X char *device; X { X char *getenv(), *my_name; X FILE *out_s; X X my_name = getenv("LOGNAME"); 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 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_loc_rem (type, ch) X int type; X int ch; X { X send_local (type, ch); X send_remote (type, ch); X } SHAR_EOF chmod 0666 phone.c || echo "restore of phone.c fails" sed 's/^X//' << 'SHAR_EOF' > Makefile && Xphone: phone.c X cc -g phone.c -o phone -lcurses X Xinstall: X cp phone /usr/lbin X Xshar: X shar phone.doc phone.c Makefile >phone.sh SHAR_EOF chmod 0666 Makefile || echo "restore of Makefile fails" exit 0 -- Jack Bonn, <> Software Labs, Ltd, Box 451, Easton CT 06612 uunet!swlabs!jack