changeset 82988:f82e3a6f5ccb

A few more bugfixes and new features. (Sigh.) I obviously need to remember to separate individual changes to multiple commits. src/emacsclient.c: Improved error handling. (decode_options): Changed frame option (again) from -f to -t. (print_help_and_exit): Ditto. (copy_from_to): Check EINTR after write, not EAGAIN. Removed SIGIO hack. (pty_conversation): Handle errors transmitted through the socket. Handle pty errors by not reading from it anymore. (main): Restore correct errno after socket_status failed. Send -tty on -t, not -pty. lisp/server.el (server-process-filter): Watch -tty, not -pty. Use make-frame-on-tty instead of make-terminal-frame. Don't set newframe to t if make-frame-on-tty failed. Don't delete frames here. Print correct message when there are no files to edit, but a new frame was requested. (server-sentinel): Delete the frame after the process. (server-handle-delete-frame): New function for delete-frame-functions. (server-start): Add server-handle-delete-frame to delete-frame-functions. (server-buffer-done): Don't delete frames here. src/alloc.c (mark_ttys): Add prototype. (Fgarbage_collect): Call mark_ttys. src/emacs.c: (shut_down_emacs): Don't flush stdout before reset_sys_modes(). src/process.c (add_keyboard_wait_descriptor_called_flag): Removed. (add_keyboard_wait_descriptor): Removed stdin hack. src/sysdep.c: Unconditionally include sysselect.h. (old_fcntl_flags): Changed to an array. (init_sigio, reset_sigio): Use it. (narrow_foreground_group, widen_foreground_group): Use setpgid, not setpgrp. (old_fcntl_owner): Changed to an array. (init_sys_modes, reset_sys_modes): Use it. Fix fsync() and reset_sigio() calls. src/term.c (Qframe_tty_name, Qframe_tty_type): New variables. (syms_of_term): Initialize them. (Fframe_tty_name, Fframe_tty_type): New functions. (term_init): Call add_keyboard_wait_descriptor(). (Fdelete_tty): New function. (delete_tty): Call delete_keyboard_wait_descriptor(). (get_current_tty): Removed. (mark_ttys): New function. git-archimport-id: lorentey@elte.hu--2004/emacs--multi-tty--0--patch-28
author Karoly Lorentey <lorentey@elte.hu>
date Wed, 31 Dec 2003 05:09:29 +0000
parents 1682917e56b4
children f3845715a5f6
files README.multi-tty lib-src/emacsclient.c lisp/server.el src/alloc.c src/emacs.c src/process.c src/sysdep.c src/term.c
diffstat 8 files changed, 371 insertions(+), 176 deletions(-) [+]
line wrap: on
line diff
--- a/README.multi-tty	Tue Dec 30 19:27:57 2003 +0000
+++ b/README.multi-tty	Wed Dec 31 05:09:29 2003 +0000
@@ -45,15 +45,13 @@
 separate terminals.  (This seems to be very useful, emacsclient starts
 up even faster than vi!) :-) You can close the newly opened frame and
 return to the shell without exiting Emacs by pressing C-x 5 0, i.e.,
-delete-frame.  (Note that this does not seem to work yet on the
-original terminal.)  Creating new frames on the same tty with C-x 5 2
+delete-frame.  Creating new frames on the same tty with C-x 5 2
 works exactly as before.  Suspending Emacs is disabled at the moment.
 If you exit emacs, all terminals should be restored to their previous
 states.
 
 X, Mac, Windows and DOS support is broken, probably doesn't even
-compile -- this will be solved later.  Please see the attached README
-for other issues, implementation notes and sub-TODO items.
+compile -- this will be solved later.
 
 Only tested on my GNU/Linux box.
 
@@ -75,6 +73,10 @@
 ** A make-frame-on-tty function has been added to make it easier to
    create frames on new terminals.
 
+** New functions: frame-tty-name, frame-tty-type for accessing
+   terminal parameters, and delete-tty for closing the terminal
+   device.
+
 ** Emacsclient has been extended to support opening a new terminal
    frame.
 
@@ -108,6 +110,8 @@
    succeeds.  MULTIKBOARD is not used.  Secondary terminals don't send
    SIGIO!)
 
+   (Update: They do, now.)
+
 -- other-frame should cycle through the frames on the `current'
    terminal only.  
 
@@ -159,6 +163,10 @@
    (Update: This is bullshit.  There is a read_input_waiting function,
    extend that somehow.)
 
+   (Update of update: The first update was not right either, extending
+   read_input_waiting was not necessary.  Secondary ttys do seem to
+   send signals on input.)
+
 -- Make make-terminal-frame look up the `tty' and `tty-type' frame
    parameters from the currently selected terminal before the global
    default.
@@ -231,56 +239,67 @@
    free()ing memory.  Utterly trivial matter.  I love the C's memory
    management, it puts hair on your chest.)
 
-THINGS TO DO
-------------
+-- Support raw secondary terminals.  (Note that SIGIO works only on
+   the controlling terminal.) Hint: extend read_input_waiting() for
+   multiple ttys and hopefully this will be fixed.
 
-** Understand Emacs's low-level input system (it seems complicated) :-)
-   and maybe rewrite multi-tty input in terms of MULTIKBOARD.
+   (Done, it seems to have been working already for some time.  It
+   seems F_SETOWN does work, after all.  Not sure what made it fail
+   earlier, but it seems to be fixed (there were several changes
+   around request_sigio, maybe one of them did it).
+   read_input_waiting() is only used in sys_select(), don't change
+   it.)
 
-** Find out why does Emacs abort when it wants to close its
+-- Find out why does Emacs abort when it wants to close its
    controlling tty.  Hint: chan_process[] array.  Hey, maybe
    noninterrupt-IO would work, too?  Update: no, there is no process
    for stdin/out.
 
-** Support raw secondary terminals.  (Note that SIGIO works only on
-   the controlling terminal.) Hint: extend read_input_waiting() for
-   multiple ttys and hopefully this will be fixed.
+   (Done.  Added add/delete_keyboard_wait_descriptor to
+   term_init/delete_tty.  The hint was right, in a way.)
 
-** What does interrupt_input do?  I tried to disable it for raw
-   secondary tty support, but it does not seem to do anything useful.
-
-** Issue with SIGIO: it needs to be disabled during redisplay.  See if
+-- Issue with SIGIO: it needs to be disabled during redisplay.  See if
    fcntl() kernel behaviour could be emulated by emacsclient.
 
-** Get rid of the accessor macros in termchar.h, or define macros for
-   all members.
+   (Done.  Simply disabled the SIGIO emulation hack in emacsclient.)
 
-** Make parts of struct tty_output accessible from Lisp.  The device
-   name and the type is sufficient.
-
-** server.el: There are issues with saving files in buffers of closed
+-- server.el: There are issues with saving files in buffers of closed
    clients.  Try editing a file with emacsclient -f, and (without
    saving it) do a delete-frame.  The frame is closed without
    question, and a surprising confirmation prompt appears in another
    frame.
 
-** emacsclient.el, server.el: Handle eval or file open errors when
+   (Done.  delete-frame now asks for confirmation if it still has
+   pending buffers, and modified buffers don't seem to be deleted.)
+
+-- emacsclient.el, server.el: Handle eval or file open errors when
    doing -f.
 
-** Export delete_tty to the Lisp environment, for emacsclient.
+   (Done.)
 
-** Make sure C-g goes to the right frame.  This is hard, as SIGINT
-   doesn't have a tty parameter. :-(
+-- Make parts of struct tty_output accessible from Lisp.  The device
+   name and the type is sufficient.
+
+   (Done, see frame-tty-name and frame-tty-type.)
 
-** Implement support for starting an interactive Emacs session without
-   an initial frame.  (The user would connect to it and open frames
-   later, with emacsclient.)  Not necessarily a good idea.
+-- Export delete_tty to the Lisp environment, for emacsclient.
+
+   (Done, see delete-tty.)
 
-** Move optimalization parameters (costs) from union output_data to
-   a backend-neutral per-device structure.
+
+THINGS TO DO
+------------
 
 ** Find out the best way to support suspending Emacs with multiple
-   ttys.
+   ttys.  My guess: disable it on the controlling tty, but other ttys
+   should pass it on to emacsclient somehow.  (It is (I hope) trivial
+   to extend emacsclient to handle suspend/resume.  A `kill -STOP'
+   almost works right now.)
+
+** Move baud_rate to tty_output.
+
+** Move device-specific parameters (like costs) commonly used by
+   device backends to a common, device-dependent structure.
 
 ** Do tty output through term_hooks, like graphical display backends.
 
@@ -289,6 +308,10 @@
 ** Allow simultaneous X and tty frames.  (Handling input could be
    tricky.)
 
+** Implement support for starting an interactive Emacs session without
+   an initial frame.  (The user would connect to it and open frames
+   later, with emacsclient.)  Not necessarily a good idea.
+
 ** Fix Mac support (I can't do this myself).
 
 ** Fix W32 support (I can't do this myself).
@@ -297,4 +320,35 @@
 
 ** Do a grep on XXX and ?? for more issues.
 
+** Get rid of the accessor macros in termchar.h, or define macros for
+   all members.
+
+** Understand Emacs's low-level input system (it seems complicated) :-)
+   and maybe rewrite multi-tty input in terms of MULTIKBOARD.
+
+** What does interrupt_input do?  I tried to disable it for raw
+   secondary tty support, but it does not seem to do anything useful.
+
+** Make sure C-g goes to the right frame.  This is hard, as SIGINT
+   doesn't have a tty parameter. :-(
+
+** I have seen a case when Emacs with multiple ttys went ate 100% of
+   CPU time.  Strace showed this loop:
+
+
+	getpid()                                = 30284
+	kill(30284, SIGIO)                      = 0
+	--- SIGIO (I/O possible) @ 0 (0) ---
+	ioctl(6, FIONREAD, [0])                 = -1 EIO (Input/output error)
+	ioctl(5, FIONREAD, [0])                 = -1 EIO (Input/output error)
+	ioctl(0, FIONREAD, [0])                 = 0
+	sigreturn()                             = ? (mask now [])
+	gettimeofday({1072842297, 747760}, NULL) = 0
+	gettimeofday({1072842297, 747806}, NULL) = 0
+	select(9, [0 3 5 6], NULL, NULL, {0, 0}) = 2 (in [5 6], left {0, 0})
+	select(9, [0 3 5 6], NULL, NULL, {0, 0}) = 2 (in [5 6], left {0, 0})
+	gettimeofday({1072842297, 748245}, NULL) = 0
+
+   I have not been able to reproduce this.
+
 ;;; arch-tag: 8da1619e-2e79-41a8-9ac9-a0485daad17d
--- a/lib-src/emacsclient.c	Tue Dec 30 19:27:57 2003 +0000
+++ b/lib-src/emacsclient.c	Wed Dec 31 05:09:29 2003 +0000
@@ -153,7 +153,7 @@
   while (1)
     {
       int opt = getopt_long (argc, argv,
-			     "VHnea:s:d:f", longopts, 0);
+			     "VHnea:s:d:t", longopts, 0);
 
       if (opt == EOF)
 	break;
@@ -192,7 +192,7 @@
 	  exit (0);
 	  break;
 
-        case 'f':
+        case 't':
           frame = 1;
           break;
           
@@ -225,7 +225,7 @@
 The following OPTIONS are accepted:\n\
 -V, --version           Just print a version info and return\n\
 -H, --help              Print this usage information message\n\
--f, --frame             Open a new Emacs frame on the current terminal\n\
+-t, --tty               Open a new Emacs frame on the current terminal\n\
 -n, --no-wait           Don't wait for the server to return\n\
 -e, --eval              Evaluate the FILE arguments as ELisp expressions\n\
 -d, --display=DISPLAY   Visit the file in the given display\n\
@@ -300,7 +300,7 @@
   defined-- exit with an errorcode.
 */
 void
-fail ()
+fail (void)
 {
   if (alternate_editor)
     {
@@ -720,14 +720,14 @@
 }
 
 int
-copy_from_to (int in, int out, int sigio)
+copy_from_to (int in, int out)
 {
   static char buf[BUFSIZ];
   int nread = read (in, &buf, BUFSIZ);
   if (nread == 0)
     return 1;                   /* EOF */
   else if (nread < 0 && errno != EAGAIN)
-    return 0;                   /* Error */
+    return 0;
   else if (nread > 0)
     {
       int r = 0;
@@ -735,16 +735,11 @@
 
       do {
         r = write (out, &buf, nread);
-      } while ((r < 0 && errno == EAGAIN)
+      } while ((r < 0 && errno == EINTR)
                || (r > 0 && (written += r) && written != nread));
       
       if (r < 0)
-        return 0;               /* Error */
-
-      if (emacs_pid && sigio)
-        {
-          kill (emacs_pid, SIGIO);
-        }
+        return 0;
     }
   return 1;
 }
@@ -754,53 +749,85 @@
 {
   char *str;
   char string[BUFSIZ];              
-  fd_set set;
+  fd_set set, rset;
+  int res;
+
+  FD_ZERO (&set);
+  FD_SET (master, &set);
+  FD_SET (1, &set);
+  FD_SET (fileno (in), &set);
 
   in_conversation = 1;
   
   while (! quit_conversation) {
-    int res;
-    
-    FD_ZERO (&set);
-    FD_SET (master, &set);
-    FD_SET (1, &set);
-    FD_SET (fileno (in), &set);
-    res = select (FD_SETSIZE, &set, NULL, NULL, NULL);
-    if (res < 0)
+    rset = set;
+    res = select (FD_SETSIZE, &rset, NULL, NULL, NULL);
+    if (res < 0 && errno != EINTR)
       {
-        if (errno != EINTR)
-          return 0;
+          reset_tty ();
+          fprintf (stderr, "%s: ", progname);
+          perror ("select");
+          return 0;             /* Error */
       }
     else if (res > 0)
       {
-        if (FD_ISSET (master, &set))
+        if (FD_ISSET (master, &rset))
           {
             /* Copy Emacs output to stdout. */
-            if (! copy_from_to (master, 0, 0))
-              return 1;
+            if (! copy_from_to (master, 0))
+              {
+                FD_CLR (master, &set);
+              }
           }
-        if (FD_ISSET (1, &set))
+        if (FD_ISSET (1, &rset))
           {
             /* Forward user input to Emacs. */
-            if (! copy_from_to (1, master, 1))
-              return 1;
+            if (! copy_from_to (1, master))
+              {
+                FD_CLR (master, &set);
+              }
           }
-        if (FD_ISSET (fileno (in), &set))
+        if (FD_ISSET (fileno (in), &rset))
           {
+            do {
+              res = read (fileno (in), string, BUFSIZ-1);
+            } while (res == EINTR);
+            if (res < 0)
+              {
+                reset_tty ();
+                fprintf (stderr, "%s: ", progname);
+                perror ("read");
+                return 0;
+              }
+            if (!res)
+              {
+                return 1;
+              }
+            
+            string[res] = 0;
+            if (string[res-1] == '\n')
+              string[res-1] = 0;
+            
             if (! emacs_pid)
               {
                 /* Get the pid of the Emacs process.
-                   XXX Is there is some nifty libc/kernel feature for doing this?
+                   XXX Is there some nifty libc/kernel feature for doing this?
                 */
-                str = fgets (string, BUFSIZ, in);
-                if (! str)
+                if (! string[0])
                   {
                     reset_tty ();
-                    fprintf (stderr, "%s: %s\n", progname, str);
-                    fail ();
+                    fprintf (stderr, "%s: could not get Emacs process id\n"
+                             "Maybe this Emacs does not support multiple terminals.\n", progname);
+                    return 0;
                   }
-                
-                emacs_pid = atoi (str);
+                emacs_pid = strtol (string, NULL, 10);
+              }
+            
+            if (! emacs_pid)    /* emacs_pid should be set above */
+              {
+                reset_tty ();
+                fprintf (stderr, "%s: %s\n", progname, string);
+                return 0;
               }
           }
       }
@@ -822,7 +849,7 @@
 	   argv[0]);
   fprintf (stderr, "on systems with Berkeley sockets.\n");
 
-  fail (argc, argv);
+  fail ();
 }
 
 #else /* HAVE_SOCKETS */
@@ -891,7 +918,7 @@
     {
       fprintf (stderr, "%s: ", argv[0]);
       perror ("socket");
-      fail (argc, argv);
+      fail ();
     }
 
   server.sun_family = AF_UNIX;
@@ -922,7 +949,8 @@
 
   {
     int sock_status = 0;
-
+    int oerrno = 0;
+    
     if (! socket_name)
       {
 	socket_name = alloca (system_name_length + 100);
@@ -933,11 +961,15 @@
     if (strlen (socket_name) < sizeof (server.sun_path))
       strcpy (server.sun_path, socket_name);
     else
-      fprintf (stderr, "%s: socket-name %s too long",
-	       argv[0], socket_name);
+      {
+        fprintf (stderr, "%s: socket-name %s too long",
+                 argv[0], socket_name);
+        fail ();
+      }
 
     /* See if the socket exists, and if it's owned by us. */
     sock_status = socket_status (server.sun_path);
+    oerrno = errno;
     if (sock_status)
       {
 	/* Failing that, see if LOGNAME or USER exist and differ from
@@ -958,6 +990,7 @@
 		sprintf (server.sun_path, "/tmp/esrv%d-%s",
 			 (int) pw->pw_uid, system_name);
 		sock_status = socket_status (server.sun_path);
+                oerrno = errno;
 	      }
 	  }
       }
@@ -970,7 +1003,7 @@
 	 if (0 != geteuid ())
 	   {
 	     fprintf (stderr, "%s: Invalid socket owner\n", argv[0]);
-	     fail (argc, argv);
+	     fail ();
 	   }
 	 break;
 
@@ -978,13 +1011,13 @@
 	 /* `stat' failed */
 	 if (errno == ENOENT)
 	   fprintf (stderr,
-		    "%s: can't find socket; have you started the server?\n\
+		    "%s: Can't find socket; have you started the server?\n\
 To start the server in Emacs, type \"M-x server-start\".\n",
 		    argv[0]);
 	 else
-	   fprintf (stderr, "%s: can't stat %s: %s\n",
-		    argv[0], server.sun_path, strerror (errno));
-	 fail (argc, argv);
+	   fprintf (stderr, "%s: Can't stat %s: %s\n",
+		    argv[0], server.sun_path, strerror (oerrno));
+	 fail ();
 	 break;
        }
   }
@@ -994,7 +1027,7 @@
     {
       fprintf (stderr, "%s: ", argv[0]);
       perror ("connect");
-      fail (argc, argv);
+      fail ();
     }
 
   /* We use the stream OUT to send our command to the server.  */
@@ -1002,7 +1035,7 @@
     {
       fprintf (stderr, "%s: ", argv[0]);
       perror ("fdopen");
-      fail (argc, argv);
+      fail ();
     }
 
   /* We use the stream IN to read the response.
@@ -1014,7 +1047,7 @@
     {
       fprintf (stderr, "%s: ", argv[0]);
       perror ("fdopen");
-      fail (argc, argv);
+      fail ();
     }
 
 #ifdef HAVE_GETCWD
@@ -1032,7 +1065,7 @@
 #else
       fprintf (stderr, "%s: %s (%s)\n", argv[0], string, strerror (errno));
 #endif
-      fail (argc, argv);
+      fail ();
     }
 
   if (nowait)
@@ -1054,7 +1087,7 @@
         {
           fprintf (stderr, "%s: ", argv[0]);
           perror ("fdopen");
-          fail (argc, argv);
+          fail ();
         }
         
       if (! init_tty ())
@@ -1062,7 +1095,7 @@
           reset_tty ();
           fprintf (stderr, "%s: ", argv[0]);
           perror ("fdopen");
-          fail (argc, argv);
+          fail ();
         }
       
       if (! init_pty ())
@@ -1070,10 +1103,10 @@
           reset_tty ();
           fprintf (stderr, "%s: ", argv[0]);
           perror ("fdopen");
-          fail (argc, argv);
+          fail ();
         }
       
-      fprintf (out, "-pty ");
+      fprintf (out, "-tty ");
       quote_file_name (pty_name, out);
       fprintf (out, " ");
       quote_file_name (getenv("TERM"), out);
@@ -1133,11 +1166,8 @@
       if (! pty_conversation (out))
         {
           reset_tty ();
-          fprintf (stderr, "%s: ", argv[0]);
-          perror ("fdopen");
-          fail (argc, argv);
+          fail ();
         }
-      close (master);
       reset_tty ();
       return 0;
     }
--- a/lisp/server.el	Tue Dec 30 19:27:57 2003 +0000
+++ b/lisp/server.el	Wed Dec 31 05:09:29 2003 +0000
@@ -185,9 +185,6 @@
     ;; Remove PROC from the list of clients.
     (when client
       (setq server-clients (delq client server-clients))
-      (let ((frame (assq (car client) server-frames)))
-	(setq server-frames (delq frame server-frames))
-	(when (frame-live-p (cadr frame)) (delete-frame (cadr frame) 'force)))
       (dolist (buf (cdr client))
 	(with-current-buffer buf
 	  ;; Remove PROC from the clients of each buffer.
@@ -197,9 +194,24 @@
 		     (or (and server-kill-new-buffers
 			      (not server-existing-buffer))
 			 (server-temp-file-p)))
-	    (kill-buffer (current-buffer)))))))
+	    (kill-buffer (current-buffer)))))
+      (let ((frame (assq (car client) server-frames)))
+	(when frame
+	  (setq server-frames (delq frame server-frames))
+	  (when (frame-live-p (cadr frame)) (delete-frame (cadr frame) 'force))))))
   (server-log (format "Status changed to %s" (process-status proc)) proc))
 
+(defun server-handle-delete-frame (frame)
+  (dolist (entry server-frames)
+    (let ((proc (nth 0 entry))
+	  (f (nth 1 entry)))
+      (when (eq f frame)
+	(let ((client (assq proc server-clients)))
+	  (if (and (cdr client) (not (yes-or-no-p "Frame has pending buffers; close anyway? ")))
+	      (error "Frame deletion cancelled")
+	    (setq server-frames (delq entry server-frames))
+	    (delete-process (car client))))))))
+
 (defun server-select-display (display)
   ;; If the current frame is on `display' we're all set.
   (unless (equal (frame-parameter (selected-frame) 'display) display)
@@ -276,6 +288,7 @@
     (if server-process
 	(server-log (message "Restarting server")))
     (letf (((default-file-modes) ?\700))
+      (add-to-list 'delete-frame-functions 'server-handle-delete-frame)
       (setq server-process
 	    (make-network-process
 	     :name "server" :family 'local :server t :noquery t
@@ -335,18 +348,18 @@
 		(error (process-send-string proc (nth 1 err))
 		       (setq request "")))))
 	   ;; Open a new frame at the client.  ARG is the name of the pseudo tty.
-	   ((and (equal "-pty" arg) (string-match "\\([^ ]*\\) \\([^ ]*\\) " request))
-	    (setq newframe t)
-	    (let ((pty (server-unquote-arg (match-string 1 request)))
+	   ((and (equal "-tty" arg) (string-match "\\([^ ]*\\) \\([^ ]*\\) " request))
+	    (let ((tty (server-unquote-arg (match-string 1 request)))
 		  (type (server-unquote-arg (match-string 2 request))))
 	      (setq request (substring request (match-end 0)))
 	      (condition-case err
-		  (let ((frame (make-terminal-frame `((tty . ,pty) (tty-type . ,type)))))
+		  (let ((frame (make-frame-on-tty tty type)))
 		    (setq server-frames (cons (list (car client) frame) server-frames))
 		    (sit-for 0)
 		    (process-send-string proc (concat (number-to-string (emacs-pid)) "\n"))
-		    (select-frame frame))
-		(error (process-send-string proc (nth 1 err))
+		    (select-frame frame)
+		    (setq newframe t))
+		(error (ignore-errors (process-send-string proc (concat (nth 1 err) "\n")))
 		       (setq request "")))))
 	   ;; ARG is a line number option.
 	   ((string-match "\\`\\+[0-9]+\\'" arg)
@@ -386,19 +399,19 @@
       (if (and (not newframe) (null (cdr client)))
 	  ;; This client is empty; get rid of it immediately.
 	  (progn
-	    (let ((frame (assq (car client) server-frames)))
-	      (setq server-frames (delq frame server-frames))
-	      (when (frame-live-p (cadr frame)) (delete-frame (cadr frame) 'force)))
 	    (delete-process proc)
 	    (server-log "Close empty client" proc))
 	;; We visited some buffer for this client.
 	(or nowait (push client server-clients))
 	(unless (or isearch-mode (minibufferp))
-	  (server-switch-buffer (nth 1 client))
-	  (run-hooks 'server-switch-hook)
-	  (unless nowait
-	    (message (substitute-command-keys
-		      "When done with a buffer, type \\[server-edit]")))))))
+	  (if (and newframe (null (cdr client)))
+	      (message (substitute-command-keys
+			"When done with this frame, type \\[delete-frame]"))
+	    (server-switch-buffer (nth 1 client))
+	    (run-hooks 'server-switch-hook)
+	    (unless nowait
+	      (message (substitute-command-keys
+			"When done with a buffer, type \\[server-edit]"))))))))
   ;; Save for later any partial line that remains.
   (when (> (length string) 0)
     (process-put proc 'previous-string string)))
@@ -475,9 +488,6 @@
 	;; If client now has no pending buffers,
 	;; tell it that it is done, and forget it entirely.
 	(unless (cdr client)
-	  (let ((frame (assq (car client) server-frames)))
-	    (setq server-frames (delq frame server-frames))
-	    (when (frame-live-p (cadr frame)) (delete-frame (cadr frame) 'force)))
 	  (delete-process (car client))
 	  (server-log "Close" (car client))
 	  (setq server-clients (delq client server-clients))))
--- a/src/alloc.c	Tue Dec 30 19:27:57 2003 +0000
+++ b/src/alloc.c	Wed Dec 31 05:09:29 2003 +0000
@@ -256,6 +256,7 @@
 
 static void mark_buffer P_ ((Lisp_Object));
 extern void mark_kboards P_ ((void));
+extern void mark_ttys P_ ((void));
 static void gc_sweep P_ ((void));
 static void mark_glyph_matrix P_ ((struct glyph_matrix *));
 static void mark_face_cache P_ ((struct face_cache *));
@@ -4441,6 +4442,7 @@
 	mark_object (backlist->args[i]);
     }
   mark_kboards ();
+  mark_ttys ();
 
   /* Look thru every buffer's undo list
      for elements that update markers that were not marked,
--- a/src/emacs.c	Tue Dec 30 19:27:57 2003 +0000
+++ b/src/emacs.c	Wed Dec 31 05:09:29 2003 +0000
@@ -1990,7 +1990,6 @@
     if (EMACS_GET_TTY_PGRP (0, &tpgrp) != -1
 	&& tpgrp == pgrp)
       {
-	fflush (stdout);
 	reset_all_sys_modes ();
 	if (sig && sig != SIGTERM)
 	  fprintf (stderr, "Fatal error (%d)", sig);
--- a/src/process.c	Tue Dec 30 19:27:57 2003 +0000
+++ b/src/process.c	Wed Dec 31 05:09:29 2003 +0000
@@ -275,11 +275,11 @@
 
 static SELECT_TYPE input_wait_mask;
 
-/* Mask that excludes keyboard input descriptor (s).  */
+/* Mask that excludes keyboard input descriptor(s).  */
 
 static SELECT_TYPE non_keyboard_wait_mask;
 
-/* Mask that excludes process input descriptor (s).  */
+/* Mask that excludes process input descriptor(s).  */
 
 static SELECT_TYPE non_process_wait_mask;
 
@@ -6430,20 +6430,10 @@
 
 
 
-/* The first time this is called, assume keyboard input comes from DESC
-   instead of from where we used to expect it.
-   Subsequent calls mean assume input keyboard can come from DESC
-   in addition to other places.  */
-
-static int add_keyboard_wait_descriptor_called_flag;
-
 void
 add_keyboard_wait_descriptor (desc)
      int desc;
 {
-  if (! add_keyboard_wait_descriptor_called_flag)
-    FD_CLR (0, &input_wait_mask);
-  add_keyboard_wait_descriptor_called_flag = 1;
   FD_SET (desc, &input_wait_mask);
   FD_SET (desc, &non_process_wait_mask);
   if (desc > max_keyboard_desc)
--- a/src/sysdep.c	Tue Dec 30 19:27:57 2003 +0000
+++ b/src/sysdep.c	Wed Dec 31 05:09:29 2003 +0000
@@ -45,6 +45,8 @@
 #endif
 #endif
 
+#include "sysselect.h"
+
 #include "blockinput.h"
 #undef NULL
 
@@ -913,16 +915,15 @@
 
 #ifdef F_SETFL
 
-int old_fcntl_flags;
+int old_fcntl_flags[MAXDESC];
 
 void
 init_sigio (fd)
      int fd;
 {
 #ifdef FASYNC
-  /* XXX What if we get called with more than one fds? */
-  old_fcntl_flags = fcntl (fd, F_GETFL, 0) & ~FASYNC;
-  fcntl (fd, F_SETFL, old_fcntl_flags | FASYNC);
+  old_fcntl_flags[fd] = fcntl (fd, F_GETFL, 0) & ~FASYNC;
+  fcntl (fd, F_SETFL, old_fcntl_flags[fd] | FASYNC);
 #endif
   interrupts_deferred = 0;
 }
@@ -931,7 +932,7 @@
 reset_sigio (fd)
      int fd;
 {
-  fcntl (fd, F_SETFL, old_fcntl_flags);
+  fcntl (fd, F_SETFL, old_fcntl_flags[fd]);
 }
 
 #ifdef FASYNC		/* F_SETFL does not imply existence of FASYNC */
@@ -1086,11 +1087,12 @@
 {
   int me = getpid ();
 
-  setpgrp (0, inherited_pgroup);
+  if (! inherited_pgroup)
+    inherited_pgroup = getpgid (0);
   /* XXX This only works on the controlling tty. */
   if (inherited_pgroup != me)
     EMACS_SET_TTY_PGRP (fd, &me);
-  setpgrp (0, me);
+  setpgid (0, me);
 }
 
 /* Set the tty to our original foreground group.  */
@@ -1099,7 +1101,7 @@
 {
   if (inherited_pgroup != getpid ())
     EMACS_SET_TTY_PGRP (fd, &inherited_pgroup);
-  setpgrp (0, inherited_pgroup);
+  setpgid (0, inherited_pgroup);
 }
 
 #endif /* BSD_PGRPS */
@@ -1265,7 +1267,7 @@
 
 #ifndef F_SETOWN_BUG
 #ifdef F_SETOWN
-int old_fcntl_owner;
+int old_fcntl_owner[MAXDESC];
 #endif /* F_SETOWN */
 #endif /* F_SETOWN_BUG */
 
@@ -1614,7 +1616,8 @@
   if (interrupt_input
       && ! read_socket_hook && EQ (Vwindow_system, Qnil))
     {
-      old_fcntl_owner = fcntl (fileno (TTY_INPUT (tty_out)), F_GETOWN, 0);
+      old_fcntl_owner[fileno (TTY_INPUT (tty_out))] =
+        fcntl (fileno (TTY_INPUT (tty_out)), F_GETOWN, 0);
       fcntl (fileno (TTY_INPUT (tty_out)), F_SETOWN, getpid ());
       init_sigio (fileno (TTY_INPUT (tty_out)));
     }
@@ -1823,9 +1826,12 @@
       )
     return;
 #endif
+
   cmgoto (tty_out, FrameRows (tty_out) - 1, 0);
   tty_clear_end_of_line (tty_out, FrameCols (tty_out));
   cmgoto (tty_out, FrameRows (tty_out) - 1, 0);
+  fflush (tty_out->output);
+  
 #if defined (IBMR2AIX) && defined (AIXHFT)
   {
     /* HFT devices normally use ^J as a LF/CR.  We forced it to
@@ -1842,7 +1848,7 @@
 #ifdef BSD_SYSTEM
 #ifndef BSD4_1
   /* Avoid possible loss of output when changing terminal modes.  */
-  fsync (TTY_OUTPUT (tty_out));
+  fsync (fileno (TTY_OUTPUT (tty_out)));
 #endif
 #endif
 
@@ -1851,8 +1857,9 @@
 #ifdef F_SETOWN		/* F_SETFL does not imply existence of F_SETOWN */
   if (interrupt_input)
     {
-      reset_sigio (tty_out);
-      fcntl (fileno (TTY_INPUT (tty_out)), F_SETOWN, old_fcntl_owner);
+      reset_sigio (fileno (TTY_INPUT (tty_out)));
+      fcntl (fileno (TTY_INPUT (tty_out)), F_SETOWN,
+             old_fcntl_owner[fileno (TTY_INPUT (tty_out))]);
     }
 #endif /* F_SETOWN */
 #endif /* F_SETOWN_BUG */
@@ -1863,7 +1870,7 @@
 #endif /* F_SETFL */
 #ifdef BSD4_1
   if (interrupt_input)
-    reset_sigio (tty_out);
+    reset_sigio (fileno (TTY_INPUT (tty_out)));
 #endif /* BSD4_1 */
 
   if (tty_out->old_tty)
@@ -2704,6 +2711,10 @@
     }
 }
 
+#if !defined (HAVE_SELECT) || defined (BROKEN_SELECT_NON_X)
+#define select sys_select
+#endif
+
 #endif /* not HAVE_SELECT */
 #endif /* not VMS */
 #endif /* not MSDOS */
--- a/src/term.c	Tue Dec 30 19:27:57 2003 +0000
+++ b/src/term.c	Wed Dec 31 05:09:29 2003 +0000
@@ -109,7 +109,9 @@
    pages, where one page is used for Emacs and another for all
    else. */
 int no_redraw_on_reenter;
-  
+
+Lisp_Object Qframe_tty_name, Qframe_tty_type;
+
 /* Hook functions that you can set to snap out the functions in this file.
    These are all extern'd in termhooks.h  */
 
@@ -2107,6 +2109,60 @@
 }
 
 
+
+DEFUN ("frame-tty-name", Fframe_tty_name, Sframe_tty_name, 0, 1, 0,
+       doc: /* Return the name of the TTY device that FRAME is displayed on. */)
+  (frame)
+     Lisp_Object frame;
+{
+  struct frame *f;
+
+  if (NILP (frame))
+    {
+      f = XFRAME (selected_frame);
+    }
+  else
+    {
+      CHECK_LIVE_FRAME (frame);
+      f = XFRAME (frame);
+    }
+
+  if (f->output_method != output_termcap)
+    wrong_type_argument (Qframe_tty_name, frame);
+
+  if (f->output_data.tty->name)
+    return build_string (f->output_data.tty->name);
+  else
+    return Qnil;
+}
+
+DEFUN ("frame-tty-type", Fframe_tty_type, Sframe_tty_type, 0, 1, 0,
+       doc: /* Return the type of the TTY device that FRAME is displayed on. */)
+  (frame)
+     Lisp_Object frame;
+{
+  struct frame *f;
+  
+  if (NILP (frame))
+    {
+      f = XFRAME (selected_frame);
+    }
+  else
+    {
+      CHECK_LIVE_FRAME (frame);
+      f = XFRAME (frame);
+    }
+
+  if (f->output_method != output_termcap)
+    wrong_type_argument (Qframe_tty_type, frame);
+
+  if (f->output_data.tty->type)
+    return build_string (f->output_data.tty->type);
+  else
+    return Qnil;
+}
+
+
 /***********************************************************************
 			    Initialization
  ***********************************************************************/
@@ -2186,6 +2242,8 @@
 
   TTY_TYPE (tty) = xstrdup (terminal_type);
 
+  add_keyboard_wait_descriptor (fileno (tty->input));
+  
 #ifdef WINDOWSNT
   initialize_w32_display ();
 
@@ -2665,27 +2723,31 @@
   exit (1);
 }
 
-void
-syms_of_term ()
+
+
+DEFUN ("delete-tty", Fdelete_tty, Sdelete_tty, 0, 1, 0,
+       doc: /* Delete all frames on the terminal named TTY, and close the device. */)
+  (tty)
+     Lisp_Object tty;
 {
-  DEFVAR_BOOL ("system-uses-terminfo", &system_uses_terminfo,
-    doc: /* Non-nil means the system uses terminfo rather than termcap.
-This variable can be used by terminal emulator packages.  */);
-#ifdef TERMINFO
-  system_uses_terminfo = 1;
-#else
-  system_uses_terminfo = 0;
-#endif
-
-  DEFVAR_LISP ("ring-bell-function", &Vring_bell_function,
-    doc: /* Non-nil means call this function to ring the bell.
-The function should accept no arguments.  */);
-  Vring_bell_function = Qnil;
-
-  defsubr (&Stty_display_color_p);
-  defsubr (&Stty_display_color_cells);
-
-  Fprovide (intern ("multi-tty"), Qnil);
+  struct tty_output *t;
+  char *name = 0;
+
+  CHECK_STRING (tty);
+  
+  if (SBYTES (tty) > 0)
+    {
+      name = (char *) alloca (SBYTES (tty) + 1);
+      strncpy (name, SDATA (tty), SBYTES (tty));
+      name[SBYTES (tty)] = 0;
+    }
+
+  t = get_named_tty (name);
+
+  if (! t)
+    error ("No such tty device: %s", name);
+  
+  delete_tty (t);
 }
 
 static int deleting_tty = 0;
@@ -2734,10 +2796,14 @@
     xfree (tty->name);
   if (tty->type)
     xfree (tty->type);
-
+  
   if (tty->input)
-    fclose (tty->input);
-  if (tty->output && tty->output != tty->input)
+    {
+      delete_keyboard_wait_descriptor (fileno (tty->input));
+      if (tty->input != stdin)
+        fclose (tty->input);
+    }
+  if (tty->output && tty->output != stdout && tty->output != tty->input)
     fclose (tty->output);
   if (tty->termscript)
     fclose (tty->termscript);
@@ -2754,24 +2820,57 @@
 }
 
 
-struct tty_output *
-get_current_tty ()
-{
-  return CURTTY();
-}
-
+
+
+/* Mark the pointers in the tty_output objects.
+   Called by the Fgarbage_collector.  */
 void
-print_all_frames ()
+mark_ttys ()
 {
-  /* XXX Debug function. */
-  Lisp_Object frame, tail;
-  FOR_EACH_FRAME (tail, frame)
+  struct tty_output *tty;
+  Lisp_Object *p;
+  for (tty = tty_list; tty; tty = tty->next)
     {
-      fprintf (stderr, "Frame: %x\n", XFRAME (frame));
-      fflush (stderr);
+      if (tty->top_frame)
+        mark_object (tty->top_frame);
     }
 }
 
 
+
+void
+syms_of_term ()
+{
+  DEFVAR_BOOL ("system-uses-terminfo", &system_uses_terminfo,
+    doc: /* Non-nil means the system uses terminfo rather than termcap.
+This variable can be used by terminal emulator packages.  */);
+#ifdef TERMINFO
+  system_uses_terminfo = 1;
+#else
+  system_uses_terminfo = 0;
+#endif
+
+  DEFVAR_LISP ("ring-bell-function", &Vring_bell_function,
+    doc: /* Non-nil means call this function to ring the bell.
+The function should accept no arguments.  */);
+  Vring_bell_function = Qnil;
+
+  Qframe_tty_name = intern ("frame-tty-name");
+  staticpro (&Qframe_tty_name);
+
+  Qframe_tty_type = intern ("frame-tty-type");
+  staticpro (&Qframe_tty_type);
+
+  defsubr (&Stty_display_color_p);
+  defsubr (&Stty_display_color_cells);
+  defsubr (&Sframe_tty_name);
+  defsubr (&Sframe_tty_type);
+  defsubr (&Sdelete_tty);
+  
+  Fprovide (intern ("multi-tty"), Qnil);
+}
+
+
+
 /* arch-tag: 498e7449-6f2e-45e2-91dd-b7d4ca488193
    (do not change this comment) */