[net.sources] talk: an EMACS inter-user communication package

majka@ubc-vision.CDN (Marc Majka) (10/03/84)

Here is a simple EMACS "talk" package.  It allows two people using EMACS to
talk to each other.  Each user gets a window for their input and a window
for the other user's output.  Complete lines (not single characters) are
written to the user's input window and to the other user's output window. 

Included is a C program called utty, which tells you which terminal a user
is on.

The shar file below contains 5 things:
    1: The file talk.1 which is a man page for the MLisp program talk.
    2: The file talk.ml
    3: The file utty.1 which is a man page for the C program utty
    4: The file utty.c
    5: A call to "cc" to compile utty

Bug reports and fixes gratefully accepted.

----
Marc Majka - UBC Laboratory for Computational Vision

#------------------------  C U T    H E R E  -----------------------------
#
# talk: an EMACS inter-user communication package
# 
# Author: Marc Majka - UBC Laboratory for Computational Vision
#
# shell archive - extract files with /bin/sh
#
set CSHOUT1 = 'echo Extract with sh not csh'
set CSHOUT2 = 'exit 1'
$CSHOUT1
$CSHOUT2
echo extracting file talk.1
sed 's/^X//' > talk.1 <<'!FUNKY!STUFF!'
X.TH TALK 1-EMACS "2 Oct 84"
X.SH NAME
X.B talk
X\- talk to another user using EMACS.
X.SH SYNOPSIS
X.B talk
X.SH DESCRIPTION
X.I Talk 
Xallows two EMACS users to communicate. It sets up a window for each
Xuser on the screen.  Lines written by that user are kept in that window. 
X.I Talk 
Xsets up a file called /tmp/talk.[me], which is used for the output window.
XThe file is updated whenever the user inserts a newline.  The input window
Xin connected to a process which reads the other user's file,
X/tmp/talk.[them]. 
X.I Talk 
Xfinishes when either user sends a ^C. Load
X.I talk
Xinto EMACS with (load "talk.ml"), and invoke it with ESC-X talk. You will be
Xprompted for a user name, and an optional tty. (e.g. majka tty01)
X.SH LIMITATIONS
XOnly two-way talking is supported, although it would not be hard to add
X"conference calls".  
X.I Talk
Xdoes not alow you to talk to yourself, because the /tmp file names would not 
Xbe the same.
X.SH KLUDGES
X.I Talk
Xuses a combination of append-region-to-buffer and append-to-file to update
Xthe output file. This is the only way I could find to get around EMACS
Xasking about overwriting modified files. 
X.SH "SEE ALSO"
XThe program 
X.I utty 
Xis used to get the other user's terminal. The read process is done with
X.I tail
X\-f
X.SH AUTHOR
XMarc Majka - UBC Laboratory for Computational Vision
!FUNKY!STUFF!
echo extracting file talk.ml
sed 's/^X//' > talk.ml <<'!FUNKY!STUFF!'
X; 				
X; talk: an inter-user communication package for EMACS
X; Author: Marc Majka - UBC Laboratory for Computational Vision
X; 
X(defun 
X    (talk user their-buffer my-buffer their-file my-file waiting ring tty
X	  (save-window-excursion
X	      ; 
X	      ; get the user name and tty.
X	      ; 
X	      (setq user (get-tty-string "talk to user [tty]: "))
X	      (setq tty "")
X	      (save-window-excursion
X		  (pop-to-buffer "talk scratch")
X		  (setq needs-checkpointing 0)
X		  (erase-buffer)
X		  (insert-string (concat user " "))
X		  (beginning-of-line)
X		  (set-mark)
X		  (search-forward " ")
X		  (delete-previous-character)
X		  (setq user (region-to-string))
X		  (if (!(eolp))
X		      (progn
X			    (set-mark)
X			    (end-of-line)
X			    (delete-previous-character)
X			    (setq tty (concat "/dev/" (region-to-string))))))
X	      (if (= user (users-login-name))
X		  (error-message "You don't need me to talk to yourself!"))
X	      (if (| (= user "godot") (= user "Godot"))
X		  (error-message "No use waiting for " user))
X	      (if (= "" tty)
X		  (progn
X			(pop-to-buffer "talk scratch")
X			(setq needs-checkpointing 0)
X			(set-mark)
X			(insert-string user)
X			;  
X			;  This is where utty gets called
X			;  
X			(fast-filter-region "utty")
X			(setq tty (region-to-string))
X			(erase-buffer)))
X	      ; 
X	      ; quit if the user is not logged on
X	      ; 
X	      (if (= tty "not logged on")
X		  (progn
X			(delete-buffer "talk scratch")
X			(error-message user " is not logged on"))); 
X	      ;
X	      ; set up the buffer and file names
X	      ;
X	      
X	      (message "setting up ...")
X	      (sit-for 0)
X	      (setq their-buffer (concat "TALK " user))
X	      (setq their-file (concat "/tmp/talk." user))
X	      (setq my-buffer (concat "TALK " (users-login-name)))
X	      (setq my-file (concat "/tmp/talk." (users-login-name)))
X	      ; 
X	      ; if initiating the talk, ring their terminal
X	      ; 
X	      (if (! (file-exists their-file))
X		  (progn
X			(pop-to-buffer "talk scratch")
X			(setq needs-checkpointing 0)
X			(erase-buffer)
X			(newline)
X			(insert-string "
")
X			(insert-string "[][][][][][][][][]")
X			(newline)
X			(insert-string "
")
X			(newline)
X			(insert-string 
X			    (concat "
EMACS talk request from " 
X				    (users-login-name)))
X			(newline)
X			(insert-string "
")
X			(insert-string 
X			    (concat "Reply with EMACS:  talk " 
X				    (users-login-name)))
X			(newline)
X			(insert-string "
")
X			(newline)
X			(insert-string "
")
X			(insert-string "[][][][][][][][][]")
X			(newline)
X			(insert-string "
")
X			(if (error-occurred (append-to-file tty))
X			    (error-message "can't talk to " user " at " tty))))
X	      ; 
X	      ; set up the two buffers and write the /tmp/talk.[me] file
X	      ; 
X	      (pop-to-buffer my-buffer)
X	      (setq needs-checkpointing 0)
X	      (local-bind-to-key "tk-nl-write" "
")
X	      (insert-string (concat (users-login-name) "'s talk:"))
X	      (newline)
X	      (write-named-file (concat "/tmp/talk." (users-login-name)))
X	      (set-mark)
X	      (delete-other-windows)
X	      (pop-to-buffer their-buffer)
X	      (setq needs-checkpointing 0)
X	      ; 
X	      ; wait for them to set up /tmp/talk.[them]
X	      ; ring them again if desired
X	      ; 
X	      (setq waiting (! (file-exists their-file)))
X	      (setq ring "y")
X	      (while (& waiting (| (= ring "") (= ring "y")))
X		     (message "Waiting for " user)
X		     (sit-for 150)
X		     (setq waiting (! (file-exists their-file)))
X		     (if waiting
X			 (progn
X			       (setq ring 
X				     (get-tty-string "Ring again [y/n]? "))
X			       (if (| (= ring "") (= ring "y"))
X				   (save-window-excursion
X				       (pop-to-buffer "talk scratch")
X				       (append-to-file tty))))))
X	      ; 
X	      ; quit if there was no answer
X	      ; 
X	      (if waiting
X		  (progn
X			(message "Cleaning up - please wait ...")
X			(sit-for 0)
X			(delete-buffer their-buffer)
X			(delete-buffer my-buffer)
X			(delete-buffer "talk scratch")
X			(execute-monitor-command "rm /tmp/talk.*")
X			(error-message "Gave up waiting for " user)))
X	      ; 
X	      ; they have set up - start talking
X	      ; 
X	      (save-window-excursion
X		  (pop-to-buffer "talk scratch")
X		  (erase-buffer))
X	      ; 
X	      ; this is where the read process gets started
X	      ;
X	      (start-process 
X		  (concat "tail -f /tmp/talk." user) their-buffer)
X	      (pop-to-buffer my-buffer)
X	      (redraw-display)
X	      (message "Lines are sent when you type return. Type ^C to exit")
X	      (sit-for 0)
X	      (recursive-edit)
X	      ; 
X	      ; finished talking - tidy up 
X	      ; (await-process) can be replaced with (sit-for 10)
X	      ; 
X	      (error-occurred (kill-process their-buffer))
X	      (message "Finished Talking - please wait ...")
X	      (await-process)
X	      (delete-buffer their-buffer)
X	      (delete-buffer my-buffer)
X	      (delete-buffer "talk scratch")
X	      (execute-monitor-command "rm /tmp/talk.*")
X	      (message ""))))
X
X(defun
X    (tk-nl-write
X; 
X; add a newline, then update /tmp/talk.[me]
X; 
X; uses append-region-to-buffer and append-to-file as a kludge 
X; to get around EMACS asking about overwriting the file.
X; 
X	(newline)
X	(save-window-excursion
X	    (append-region-to-buffer "talk scratch")
X	    (pop-to-buffer "talk scratch")
X	    (append-to-file my-file)
X	    (erase-buffer))
X; 
X; quit if they have stopped talking
X; 
X	(if (! (file-exists their-file))
X	    (progn
X		  (message user " has finished talking to you")
X		  (sit-for 10)
X		  (exit-emacs)))
X	(set-mark)))
!FUNKY!STUFF!
echo extracting file utty.1
sed 's/^X//' > utty.1 <<'!FUNKY!STUFF!'
X.TH UTTY 1 "2 Oct 84"
X.SH NAME
X.B utty
X\- find out which terminal a user is on.
X.SH SYNOPSIS
X.B utty 
X[user]
X.SH DESCRIPTION
X.I Utty
Xtakes a user's login name, and determines which terminal that user is logged
Xin on, if at all. If no argument is given, 
X.I utty
Xreads a name from stdin.  If this is the case, utty does not print a newline 
Xafter the terminal name.  This is useful for processes (like EMACS) that call
X.I utty.
X.SH AUTHOR
XMarc Majka - UBC Laboratory for Computational Vision
!FUNKY!STUFF!
echo extracting file utty.c
sed 's/^X//' > utty.c <<'!FUNKY!STUFF!'
X
X/* utty: reads a user name and prints the terminal they are logged in on. 
X   Called from emacs by talk.ml  If called with a name as an argument,
X   prints a newline after the tty 
X
X   Author: Marc Majka - UBC Laboratory for Computational Vision
X
X   compile with: cc utty.c
X*/
X
X#include <stdio.h>
X#include <utmp.h>
X
Xmain (argc,argv)
Xint argc;
Xchar   *argv[];
X{
X	char them[256];
X	struct utmp user;
X	int uf, usize = sizeof user;
X
X	/* get the user name */
X	if (argc > 1) strcpy(them,argv[1]);
X	else scanf("%s",them);
X
X	/* scan utmp for the user */	
X	uf = open("/etc/utmp",0);
X
X	while (usize == read(uf,(char *) &user, usize)) {
X		if (0 == strcmp(user.ut_name, them)) {
X			printf("/dev/%s",user.ut_line);
X			fflush(stdout);
X			if (argc > 1) printf("\n");
X			exit(0);
X		}
X	}
X	
X	printf("not logged on");
X	fflush(stdout);
X	if (argc > 1) printf("\n");
X}
!FUNKY!STUFF!
echo compiling utty
cc -o utty utty.c
echo Done\!
exit 0