Article View: gnu.epoch.misc
Article #2075Re: [spr3043] epoch and lisp/emacs interface
From: Jarl Nilsson
Date: Wed, 17 Apr 1991 23:47
Date: Wed, 17 Apr 1991 23:47
3888 lines
126920 bytes
126920 bytes
Hi Bruce,
I spoke to David Margolies a few minutes
ago. He had apparently talked to you over the
phone. If I understand it correctly your problem
is that you want to mark regions with your mouse.
This is possible with emacs too although you will
not get the characters in the region inverted or
otherwise marked. If you used x11fns.c when
building your emacs you should have mouse
capability in your emacs. You can easily check
this by trying to describe for instance
x-mouse-set-mark.
I have included our process.c and spec.out.
============================================================
/* Asynchronous subprocess control for GNU Emacs.
Copyright (C) 1985, 1986, 1987, 1988 Free Software Foundation, Inc.
This file is part of GNU Emacs.
GNU Emacs is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY. No author or distributor
accepts responsibility to anyone for the consequences of using it
or for whether it serves any particular purpose or works at all,
unless he says so in writing. Refer to the GNU Emacs General Public
License for full details.
Everyone is granted permission to copy, modify and redistribute
GNU Emacs, but only under the conditions described in the
GNU Emacs General Public License. A copy of this license is
supposed to have been given to you along with GNU Emacs so you
can know your rights and responsibilities. It should be in a
file named COPYING. Among other things, the copyright notice
and this notice must be preserved on all copies. */
#include <signal.h>
#include "config.h"
#ifdef subprocesses
/* The entire file is within this conditional */
#include <stdio.h>
#include <errno.h>
#include <setjmp.h>
#include <sys/types.h> /* some typedefs are used in sys/file.h */
#include <sys/file.h>
#include <sys/stat.h>
#ifdef HAVE_SOCKETS /* TCP connection support, if kernel can do it */
#include <sys/socket.h>
#include <netdb.h>
#include <netinet/in.h>
#endif /* HAVE_SOCKETS */
#if defined(BSD) || defined(STRIDE)
#include <sys/ioctl.h>
#if !defined (O_NDELAY) && defined (HAVE_PTYS)
#include <fcntl.h>
#endif /* HAVE_PTYS and no O_NDELAY */
#endif /* BSD or STRIDE */
#ifdef USG
#include <termio.h>
#include <fcntl.h>
#endif /* USG */
#ifdef NEED_BSDTTY
#include <sys/bsdtty.h>
#endif
#ifdef IRIS
#include <sys/sysmacros.h> /* for "minor" */
#include <sys/time.h>
#else
#ifdef UNIPLUS
#include <sys/time.h>
#else /* not IRIS, not UNIPLUS */
#ifdef HAVE_TIMEVAL
#if defined(USG) && !defined(IBMRTAIX)
#include <time.h>
#else /* IBMRTAIX or not USG */
#include <sys/time.h>
#endif /* IBMRTAIX or not USG */
#endif /* HAVE_TIMEVAL */
#endif /* not UNIPLUS */
#endif /* not IRIS */
#if defined (HPUX) && defined (HAVE_PTYS)
#include <sys/ptyio.h>
#endif
#ifdef SYSV_PTYS
#include <sys/tty.h>
#include <sys/pty.h>
#endif
#undef NULL
#include "lisp.h"
#include "window.h"
#include "buffer.h"
#include "process.h"
#include "termhooks.h"
#include "termopts.h"
#include "commands.h"
/* a process object is a network connection when its childp field is neither
Qt nor Qnil but is instead a string (name of foreign host we
are connected to + name of port we are connected to) */
#ifdef HAVE_SOCKETS
#define NETCONN_P(p) (XGCTYPE (XPROCESS (p)->childp) == Lisp_String)
#else
#define NETCONN_P(p) 0
#endif /* HAVE_SOCKETS */
/* Define SIGCHLD as an alias for SIGCLD. There are many conditionals
testing SIGCHLD. */
#if !defined (SIGCHLD) && defined (SIGCLD)
#define SIGCHLD SIGCLD
#endif /* SIGCLD */
/* Define the structure that the wait system call stores.
On many systems, there is a structure defined for this.
But on vanilla-ish USG systems there is not. */
#ifndef WAITTYPE
#if !defined (BSD) && !defined (UNIPLUS) && !defined (STRIDE) && !(defined (HPUX) && !defined (NOMULTIPLEJOBS))
#define WAITTYPE int
#define WIFSTOPPED(w) ((w&0377) == 0177)
#define WIFSIGNALED(w) ((w&0377) != 0177 && (w&~0377) == 0)
#define WIFEXITED(w) ((w&0377) == 0)
#define WRETCODE(w) (w >> 8)
#define WSTOPSIG(w) (w >> 8)
#define WCOREDUMP(w) ((w&0200) != 0)
#define WTERMSIG(w) (w & 0377)
#else
#ifdef BSD4_1
#include <wait.h>
#else
#include <sys/wait.h>
#endif /* not BSD 4.1 */
#define WAITTYPE union wait
#define WRETCODE(w) w.w_retcode
#define WCOREDUMP(w) w.w_coredump
#ifndef WTERMSIG
#define WTERMSIG(w) w.w_termsig
#endif
#ifndef WSTOPSIG
#define WSTOPSIG(w) w.w_stopsig
#endif
#endif /* BSD or UNIPLUS or STRIDE */
#endif /* no WAITTYPE */
extern errno;
extern sys_nerr;
extern char *sys_errlist[];
#ifndef BSD4_1
extern char *sys_siglist[];
#else
char *sys_siglist[] =
{
"bum signal!!",
"hangup",
"interrupt",
"quit",
"illegal instruction",
"trace trap",
"iot instruction",
"emt instruction",
"floating point exception",
"kill",
"bus error",
"segmentation violation",
"bad argument to system call",
"write on a pipe with no one to read it",
"alarm clock",
"software termination signal from kill",
"status signal",
"sendable stop signal not from tty",
"stop signal from tty",
"continue a stopped process",
"child status has changed",
"background read attempted from control tty",
"background write attempted from control tty",
"input record available at control tty",
"exceeded CPU time limit",
"exceeded file size limit"
};
#endif
#ifdef vipc
#include "vipc.h"
extern int comm_server;
extern int net_listen_address;
#endif /* vipc */
/* t means use pty, nil means use a pipe,
maybe other values to come. */
Lisp_Object Vprocess_connection_type;
#ifdef SKTPAIR
#ifndef HAVE_SOCKETS
#include <sys/socket.h>
#endif
#endif /* SKTPAIR */
int child_changed; /* Flag when a child process has ceased
to be */
/* Mask of bits indicating the descriptors that we wait for input on */
int input_wait_mask;
int delete_exited_processes;
#define MAXDESC 32
/* Indexed by descriptor, gives the process (if any) for that descriptor */
Lisp_Object chan_process[MAXDESC];
/* Alist of elements (NAME . PROCESS) */
Lisp_Object Vprocess_alist;
Lisp_Object Qprocessp;
Lisp_Object get_process ();
/* Buffered-ahead input char from process, indexed by channel.
-1 means empty (no char is buffered).
Used on sys V where the only way to tell if there is any
output from the process is to read at least one char.
Always -1 on systems that support FIONREAD. */
int proc_buffered_char[MAXDESC];
#ifdef HAVE_PTYS
/* Open an available pty, putting descriptor in *ptyv,
and return the file name of the pty. Return 0 if none available. */
char ptyname[24];
char *
pty (ptyv)
int *ptyv;
{
struct stat stb;
register c, i;
#ifdef PTY_ITERATION
PTY_ITERATION
#else
for (c = FIRST_PTY_LETTER; c <= 'z'; c++)
for (i = 0; i < 16; i++)
#endif
{
#ifdef PTY_NAME_SPRINTF
PTY_NAME_SPRINTF
#else
#ifdef HPUX
sprintf (ptyname, "/dev/ptym/pty%c%x", c, i);
#else
#ifdef RTU
sprintf (ptyname, "/dev/pty%x", i);
#else
sprintf (ptyname, "/dev/pty%c%x", c, i);
#endif /* not RTU */
#endif /* not HPUX */
#endif /* no PTY_NAME_SPRINTF */
#ifndef IRIS
if (stat (ptyname, &stb) < 0)
return 0;
*ptyv = open (ptyname, O_RDWR | O_NDELAY, 0);
#else /* Unusual IRIS code */
*ptyv = open ("/dev/ptc", O_RDWR | O_NDELAY, 0);
if (*ptyv < 0)
return 0;
if (fstat (*ptyv, &stb) < 0)
return 0;
#endif /* IRIS */
if (*ptyv >= 0)
{
/* check to make certain that both sides are available
this avoids a nasty yet stupid bug in rlogins */
#ifdef PTY_TTY_NAME_SPRINTF
PTY_TTY_NAME_SPRINTF
#else
/* In version 19, make these special cases use the macro above. */
#ifdef HPUX
sprintf (ptyname, "/dev/pty/tty%c%x", c, i);
#else
#ifdef RTU
sprintf (ptyname, "/dev/ttyp%x", i);
#else
#ifdef IRIS
sprintf (ptyname, "/dev/ttyq%d", minor (stb.st_rdev));
#else
sprintf (ptyname, "/dev/tty%c%x", c, i);
#endif /* not IRIS */
#endif /* not RTU */
#endif /* not HPUX */
#endif /* no PTY_TTY_NAME_SPRINTF */
#ifndef UNIPLUS
if (access (ptyname, 6) != 0)
{
close (*ptyv);
#ifndef IRIS
continue;
#else
return (0);
#endif /* IRIS */
}
#endif /* not UNIPLUS */
/*
* If the following statement is included,
* then a 0 length record is EOT, but no other
* control characters can be sent down the pty
* (e.g., ^S/^Q, ^O, etc.). If it is not
* included, then sending ^D down the pty-pipe
* makes a pretty good EOF.
*/
/* I'm told that TOICREMOTE does not mean control chars
"can't be sent" but rather that they don't have
input-editing or signaling effects.
That should be good, because we have other ways
to do those things in Emacs.
However, telnet mode seems not to work on 4.2.
So TIOCREMOTE is turned off now. */
/* Under hp-ux, if TIOCREMOTE is turned on, some calls
will hang. In particular, the "timeout" feature (which
causes a read to return if there is no data available)
does this. Also it is known that telnet mode will hang
in such a way that Emacs must be stopped (perhaps this
is the same problem).
If TIOCREMOTE is turned off, then there is a bug in
hp-ux which sometimes loses data. Apparently the
code which blocks the master process when the internal
buffer fills up does not work. Other than this,
though, everything else seems to work fine.
Since the latter lossage is more benign, we may as well
lose that way. -- cph */
#ifdef HPUX
#if 0
#define DID_REMOTE
ioctl (*ptyv, TIOCREMOTE, 1);
/* Yes, HPUX has an incompatible interface for this.
Also, using it makes telnet.el fail (Emacs hangs sending text to
it). */
#endif
#else /* not HPUX */
#if 0
#ifdef TIOCREMOTE
{
int on = 1;
ioctl (*ptyv, TIOCREMOTE, &on);
}
#endif
#endif
#endif /* not HPUX */
/* this is said to be unecessary, and to be harmful in 4.3. */
/* ioctl (*ptyv, FIONBIO, &on); */
#ifdef FIONBIO
#ifdef SYSV_PTYS
{
int on = 1;
ioctl (*ptyv, FIONBIO, &on);
}
#endif
#endif
#ifdef IBMRTAIX
/* On AIX, the parent gets SIGHUP when a pty attached child dies. So, we */
/* ignore SIGHUP once we've started a child on a pty. Note that this may */
/* cause EMACS not to die when it should, i.e., when its own controlling */
/* tty goes away. I've complained to the AIX developers, and they may */
/* change this behavior, but I'm not going to hold my breath. */
signal (SIGHUP, SIG_IGN);
#endif
return ptyname;
}
}
return 0;
}
#endif /* HAVE_PTYS */
Lisp_Object
make_process (name)
Lisp_Object name;
{
register Lisp_Object val, tem, name1;
register struct Lisp_Process *p;
char suffix[10];
register int i;
/* size of process structure includes the vector header,
so deduct for that. But struct Lisp_Vector includes the first
element, thus deducts too much, so add it back. */
val = Fmake_vector (make_number ((sizeof (struct Lisp_Process)
- sizeof (struct Lisp_Vector)
+ sizeof (Lisp_Object))
/ sizeof (Lisp_Object)),
Qnil);
XSETTYPE (val, Lisp_Process);
p = XPROCESS (val);
XFASTINT (p->infd) = 0;
XFASTINT (p->outfd) = 0;
XFASTINT (p->pid) = 0;
XFASTINT (p->flags) = 0;
XFASTINT (p->reason) = 0;
p->mark = Fmake_marker ();
/* If name is already in use, modify it until it is unused. */
name1 = name;
for (i = 1; ; i++)
{
tem = Fget_process (name1);
if (NULL (tem)) break;
sprintf (suffix, "<%d>", i);
name1 = concat2 (name, build_string (suffix));
}
name = name1;
p->name = name;
Vprocess_alist = Fcons (Fcons (name, val), Vprocess_alist);
return val;
}
remove_process (proc)
register Lisp_Object proc;
{
register Lisp_Object pair;
pair = Frassq (proc, Vprocess_alist);
Vprocess_alist = Fdelq (pair, Vprocess_alist);
Fset_marker (XPROCESS (proc)->mark, Qnil, Qnil);
deactivate_process (proc);
}
DEFUN ("processp", Fprocessp, Sprocessp, 1, 1, 0,
"Return t if OBJECT is a process.")
(obj)
Lisp_Object obj;
{
return XTYPE (obj) == Lisp_Process ? Qt : Qnil;
}
DEFUN ("get-process", Fget_process, Sget_process, 1, 1, 0,
"Return the process named NAME, or nil if there is none.")
(name)
register Lisp_Object name;
{
if (XTYPE (name) == Lisp_Process)
return name;
CHECK_STRING (name, 0);
return Fcdr (Fassoc (name, Vprocess_alist));
}
DEFUN ("get-buffer-process", Fget_buffer_process, Sget_buffer_process, 1, 1, 0,
"Return the (or, a) process associated with BUFFER.\n\
BUFFER may be a buffer or the name of one.")
(name)
register Lisp_Object name;
{
register Lisp_Object buf, tail, proc;
if (NULL (name)) return Qnil;
buf = Fget_buffer (name);
if (NULL (buf)) return Qnil;
for (tail = Vprocess_alist; !NULL (tail); tail = Fcdr (tail))
{
proc = Fcdr (Fcar (tail));
if (XTYPE (proc) == Lisp_Process && EQ (XPROCESS (proc)->buffer, buf))
return proc;
}
return Qnil;
}
/* This is how commands for the user decode process arguments */
Lisp_Object
get_process (name)
register Lisp_Object name;
{
register Lisp_Object proc;
if (NULL (name))
proc = Fget_buffer_process (Fcurrent_buffer ());
else
{
proc = Fget_process (name);
if (NULL (proc))
proc = Fget_buffer_process (Fget_buffer (name));
}
if (!NULL (proc))
return proc;
if (NULL (name))
error ("Current buffer has no process");
else
error ("Process %s does not exist", XSTRING (name)->data);
/* NOTREACHED */
}
DEFUN ("delete-process", Fdelete_process, Sdelete_process, 1, 1, 0,
"Delete PROCESS: kill it and forget about it immediately.\n\
PROCESS may be a process or the name of one, or a buffer name.")
(proc)
register Lisp_Object proc;
{
proc = get_process (proc);
if (NETCONN_P (proc))
XFASTINT (XPROCESS (proc)->flags) = EXITED | CHANGED;
else if (XFASTINT (XPROCESS (proc)->infd))
{
Fkill_process (proc, Qnil);
/* Do this now, since remove_process will make child_sig do nothing. */
XFASTINT (XPROCESS (proc)->flags) = SIGNALED | CHANGED;
change_msgs ();
}
remove_process (proc);
return Qnil;
}
DEFUN ("process-status", Fprocess_status, Sprocess_status, 1, 1, 0,
"Return the status of PROCESS: a symbol, one of these:\n\
run -- for a process that is running.\n\
stop -- for a process stopped but continuable.\n\
exit -- for a process that has exited.\n\
signal -- for a process that has got a fatal signal.\n\
open -- for a network stream connection that is open.\n\
closed -- for a network stream connection that is closed.\n\
nil -- if arg is a process name and no such process exists.")
/* command -- for a command channel opened to Emacs by another process.\n\
external -- for an i/o channel opened to Emacs by another process.\n\ */
(proc)
register Lisp_Object proc;
{
register struct Lisp_Process *p;
proc = Fget_process (proc);
if (NULL (proc))
return proc;
p = XPROCESS (proc);
switch (XFASTINT (p->flags) & PROC_STATUS)
{
case RUNNING:
if (NETCONN_P (proc))
return intern ("open");
else if (!NULL (p->childp))
return intern ("run");
else if (!NULL (p->command_channel_p))
return intern ("command");
return intern ("external");
case EXITED:
if (NETCONN_P (proc))
return intern ("closed");
return intern ("exit");
case SIGNALED:
return intern ("signal");
case STOPPED:
return intern ("stop");
}
/* NOTREACHED */
}
DEFUN ("process-exit-status", Fprocess_exit_status, Sprocess_exit_status,
1, 1, 0,
"Return the exit status of PROCESS or the signal number that killed it.\n\
If PROCESS has not yet exited or died, return 0.")
(proc)
register Lisp_Object proc;
{
CHECK_PROCESS (proc, 0);
return XPROCESS (proc)->reason;
}
DEFUN ("process-id", Fprocess_id, Sprocess_id, 1, 1, 0,
"Return the process id of PROCESS.\n\
This is the pid of the Unix process which PROCESS uses or talks to.\n\
For a network connection, this value is nil.")
(proc)
register Lisp_Object proc;
{
CHECK_PROCESS (proc, 0);
return XPROCESS (proc)->pid;
}
DEFUN ("process-name", Fprocess_name, Sprocess_name, 1, 1, 0,
"Return the name of PROCESS, as a string.\n\
This is the name of the program invoked in PROCESS,\n\
possibly modified to make it unique among process names.")
(proc)
register Lisp_Object proc;
{
CHECK_PROCESS (proc, 0);
return XPROCESS (proc)->name;
}
DEFUN ("process-command", Fprocess_command, Sprocess_command, 1, 1, 0,
"Return the command that was executed to start PROCESS.\n\
This is a list of strings, the first string being the program executed\n\
and the rest of the strings being the arguments given to it.\n\
For a non-child channel, this is nil.")
(proc)
register Lisp_Object proc;
{
CHECK_PROCESS (proc, 0);
return XPROCESS (proc)->command;
}
DEFUN ("set-process-buffer", Fset_process_buffer, Sset_process_buffer,
2, 2, 0,
"Set buffer associated with PROCESS to BUFFER (a buffer, or nil).")
(proc, buffer)
register Lisp_Object proc, buffer;
{
CHECK_PROCESS (proc, 0);
if (!NULL (buffer))
CHECK_BUFFER (buffer, 1);
XPROCESS (proc)->buffer = buffer;
return buffer;
}
DEFUN ("process-buffer", Fprocess_buffer, Sprocess_buffer,
1, 1, 0,
"Return the buffer PROCESS is associated with.\n\
Output from PROCESS is inserted in this buffer\n\
unless PROCESS has a filter.")
(proc)
register Lisp_Object proc;
{
CHECK_PROCESS (proc, 0);
return XPROCESS (proc)->buffer;
}
DEFUN ("process-mark", Fprocess_mark, Sprocess_mark,
1, 1, 0,
"Return the marker for the end of the last output from PROCESS.")
(proc)
register Lisp_Object proc;
{
CHECK_PROCESS (proc, 0);
return XPROCESS (proc)->mark;
}
DEFUN ("set-process-filter", Fset_process_filter, Sset_process_filter,
2, 2, 0,
"Give PROCESS the filter function FILTER; nil means no filter.\n\
When a process has a filter, each time it does output\n\
the entire string of output is passed to the filter.\n\
The filter gets two arguments: the process and the string of output.\n\
If the process has a filter, its buffer is not used for output.")
(proc, filter)
register Lisp_Object proc, filter;
{
CHECK_PROCESS (proc, 0);
XPROCESS (proc)->filter = filter;
return filter;
}
DEFUN ("process-filter", Fprocess_filter, Sprocess_filter,
1, 1, 0,
"Returns the filter function of PROCESS; nil if none.\n\
See set-process-filter for more info on filter functions.")
(proc)
register Lisp_Object proc;
{
CHECK_PROCESS (proc, 0);
return XPROCESS (proc)->filter;
}
DEFUN ("set-process-sentinel", Fset_process_sentinel, Sset_process_sentinel,
2, 2, 0,
"Give PROCESS the sentinel SENTINEL; nil for none.\n\
The sentinel is called as a function when the process changes state.\n\
It gets two arguments: the process, and a string describing the change.")
(proc, sentinel)
register Lisp_Object proc, sentinel;
{
CHECK_PROCESS (proc, 0);
XPROCESS (proc)->sentinel = sentinel;
return sentinel;
}
DEFUN ("process-sentinel", Fprocess_sentinel, Sprocess_sentinel,
1, 1, 0,
"Return the sentinel of PROCESS; nil if none.\n\
See set-process-sentinel for more info on sentinels.")
(proc)
register Lisp_Object proc;
{
CHECK_PROCESS (proc, 0);
return XPROCESS (proc)->sentinel;
}
DEFUN ("process-kill-without-query", Fprocess_kill_without_query,
Sprocess_kill_without_query, 1, 1, 0,
"Say no query needed if this process is running when Emacs is exited.")
(proc)
register Lisp_Object proc;
{
CHECK_PROCESS (proc, 0);
XPROCESS (proc)->kill_without_query = Qt;
return Qt;
}
DEFUN ("process-kill-without-query-p", Fprocess_kill_without_query_p,
Sprocess_kill_without_query_p, 1, 1, 0,
"Return t or nil, depending on whether or not PROCESS will be killed\n\
without query.")
(proc)
register Lisp_Object proc;
{
CHECK_PROCESS (proc, 0);
return XPROCESS (proc)->kill_without_query;
}
Lisp_Object
list_processes_1 ()
{
register Lisp_Object tail, tem;
Lisp_Object proc, minspace, tem1;
register struct buffer *old = bf_cur;
register struct Lisp_Process *p;
register int state;
char tembuf[80];
XFASTINT (minspace) = 1;
SetBfp (XBUFFER (Vstandard_output));
Fbuffer_flush_undo (Vstandard_output);
bf_cur->truncate_lines = Qt;
write_string ("\
Proc Status Buffer Command\n\
---- ------ ------ -------\n", -1);
for (tail = Vprocess_alist; !NULL (tail); tail = Fcdr (tail))
{
proc = Fcdr (Fcar (tail));
p = XPROCESS (proc);
if (NULL (p->childp))
continue;
Finsert (1, &p->name);
Findent_to (make_number (13), minspace);
state = XFASTINT (p->flags) & PROC_STATUS;
switch (state)
{
case RUNNING:
if (NETCONN_P (proc))
write_string ("Open", -1);
else
write_string ("Run", -1);
break;
case STOPPED:
write_string ("Stop", -1);
break;
case EXITED:
if (NETCONN_P (proc))
write_string ("Closed", -1);
else
write_string ("Exit", -1);
if (XFASTINT (p->reason))
{
sprintf (tembuf, " %d", XFASTINT (p->reason));
write_string (tembuf, -1);
}
remove_process (proc);
break;
case SIGNALED:
if (XFASTINT (p->reason) < NSIG)
write_string (sys_siglist [XFASTINT (p->reason)], -1);
else
write_string ("Signal", -1);
remove_process (proc);
}
Findent_to (make_number (22), minspace);
if (NULL (p->buffer))
InsStr ("(none)");
else if (NULL (XBUFFER (p->buffer)->name))
InsStr ("(Killed)");
else
Finsert (1, &XBUFFER (p->buffer)->name);
Findent_to (make_number (37), minspace);
if (NETCONN_P (proc))
{
sprintf (tembuf, "(network stream connection to %s)\n",
XSTRING (p->childp)->data);
InsStr (tembuf);
}
else
{
tem = p->command;
while (1)
{
tem1 = Fcar (tem);
Finsert (1, &tem1);
tem = Fcdr (tem);
if (NULL (tem))
break;
InsStr (" ");
}
InsStr ("\n");
}
}
SetBfp (old);
return Qnil;
}
DEFUN ("list-processes", Flist_processes, Slist_processes, 0, 0, "",
"Display a list of all processes.\n\
\(Any processes listed as Exited or Signaled are actually eliminated\n\
after the listing is made.)")
()
{
internal_with_output_to_temp_buffer ("*Process List*",
list_processes_1, Qnil);
return Qnil;
}
DEFUN ("process-list", Fprocess_list, Sprocess_list, 0, 0, 0,
"Return a list of all processes.")
()
{
return Fmapcar (Qcdr, Vprocess_alist);
}
DEFUN ("start-process", Fstart_process, Sstart_process, 3, MANY, 0,
"Start a program in a subprocess. Return the process object for it.\n\
Args are NAME BUFFER PROGRAM &rest PROGRAM-ARGS\n\
NAME is name for process. It is modified if necessary to make it unique.\n\
BUFFER is the buffer or (buffer-name) to associate with the process.\n\
Process output goes at end of that buffer, unless you specify\n\
an output stream or filter function to handle the output.\n\
BUFFER may be also nil, meaning that this process is not associated\n\
with any buffer\n\
Third arg is program file name. It is searched for as in the shell.\n\
Remaining arguments are strings to give program as arguments.")
(nargs, args)
int nargs;
register Lisp_Object *args;
{
Lisp_Object buffer, name, program, proc, tem;
register unsigned char **new_argv;
register int i;
name = args[0];
CHECK_STRING (name, 0);
buffer = args[1];
program = args[2];
CHECK_STRING (program, 2);
new_argv = (unsigned char **) alloca ((nargs - 1) * sizeof (char *));
for (i = 3; i < nargs; i++)
{
tem = args[i];
CHECK_STRING (tem, i);
new_argv[i - 2] = XSTRING (tem)->data;
}
new_argv[i - 2] = 0;
new_argv[0] = XSTRING (program)->data;
/* If program file name is not absolute, search our path for it */
if (new_argv[0][0] != '/')
{
tem = Qnil;
openp (Vexec_path, program, "", &tem, 1);
if (NULL (tem))
report_file_error ("Searching for program", Fcons (program, Qnil));
new_argv[0] = XSTRING (tem)->data;
}
if (!NULL (buffer))
buffer = Fget_buffer_create (buffer);
proc = make_process (name);
XPROCESS (proc)->childp = Qt;
XPROCESS (proc)->command_channel_p = Qnil;
XPROCESS (proc)->buffer = buffer;
XPROCESS (proc)->sentinel = Qnil;
XPROCESS (proc)->filter = Qnil;
XPROCESS (proc)->command = Flist (nargs - 2, args + 2);
create_process (proc, new_argv);
return proc;
}
create_process_1 (signo)
int signo;
{
#ifdef USG
/* USG systems forget handlers when they are used;
must reestablish each time */
signal (signo, create_process_1);
#endif /* USG */
}
create_process (process, new_argv)
Lisp_Object process;
char **new_argv;
{
int pid, inchannel, outchannel, forkin, forkout;
int sv[2];
#ifdef SIGCHLD
int (*sigchld)();
#endif
register char *ptyname = 0;
char **env;
extern char **environ;
#ifdef MAINTAIN_ENVIRONMENT
env = (char **) alloca (size_of_current_environ ());
get_current_environ (env);
#else
env = environ;
#endif /* MAINTAIN_ENVIRONMENT */
#ifdef HAVE_PTYS
if (EQ (Vprocess_connection_type, Qt))
ptyname = pty (&inchannel);
outchannel = inchannel;
if (ptyname)
{
#if !defined(USG) && !defined(sun)
/* On USG systems it does not work to open
the pty's tty here and then close and reopen it in the child. */
forkout = forkin = open (ptyname, O_RDWR, 0);
if (forkin < 0)
report_file_error ("Opening pty", Qnil);
#else
forkin = forkout = -1;
#endif
}
else
#endif /* HAVE_PTYS */
#ifdef SKTPAIR
{
if (socketpair (AF_UNIX, SOCK_STREAM, 0, sv) < 0)
report_file_error ("Opening socketpair", Qnil);
outchannel = inchannel = sv[0];
forkout = forkin = sv[1];
}
#else /* not SKTPAIR */
{
pipe (sv);
inchannel = sv[0];
forkout = sv[1];
pipe (sv);
outchannel = sv[1];
forkin = sv[0];
}
#endif /* not SKTPAIR */
#if 0
/* Replaced by close_process_descs */
set_exclusive_use (inchannel);
set_exclusive_use (outchannel);
#endif
/* Stride people say it's a mystery why this is needed
as well as the O_NDELAY, but that it fails without this. */
#ifdef STRIDE
{
int one = 1;
ioctl (inchannel, FIONBIO, &one);
}
#endif
#ifdef O_NDELAY
fcntl (inchannel, F_SETFL, O_NDELAY);
#endif
/* Record this as an active process, with its channels.
As a result, child_setup will close Emacs's side of the pipes. */
chan_process[inchannel] = process;
XFASTINT (XPROCESS (process)->infd) = inchannel;
XFASTINT (XPROCESS (process)->outfd) = outchannel;
XFASTINT (XPROCESS (process)->flags) = RUNNING;
/* Delay interrupts until we have a chance to store
the new fork's pid in its process structure */
#ifdef SIGCHLD
#ifdef BSD4_1
sighold (SIGCHLD);
#else /* not BSD4_1 */
#ifdef HPUX
sigsetmask (1 << (SIGCHLD - 1));
#endif /* HPUX */
#if defined (BSD) || defined (UNIPLUS)
sigsetmask (1 << (SIGCHLD - 1));
#else /* ordinary USG */
sigchld = (int (*)()) signal (SIGCHLD, SIG_DFL);
#endif /* ordinary USG */
#endif /* not BSD4_1 */
#endif /* SIGCHLD */
{
/* child_setup must clobber environ on systems with true vfork.
Protect it from permanent change. */
char **save_environ = environ;
pid = vfork ();
if (pid == 0)
{
int xforkin = forkin;
int xforkout = forkout;
/* Make the pty be the controlling terminal of the process. */
#ifdef HAVE_PTYS
/* First, disconnect its current controlling terminal. */
#ifdef USG
/* It's very important to call setpgrp() here and no time
afterwards. Otherwise, we lose our controlling tty which
is set when we open the pty. */
setpgrp ();
#else
#ifdef sun
setpgrp(0, 0);
#endif /* sun */
#endif /* USG */
#ifdef TIOCNOTTY
/* In 4.3BSD, the TIOCSPGRP bug has been fixed, and now you
can do TIOCSPGRP only to the process's controlling tty. */
if (ptyname)
{
/* I wonder: would just ioctl (0, TIOCNOTTY, 0) work here?
I can't test it since I don't have 4.3. */
int j = open ("/dev/tty", O_RDWR, 0);
ioctl (j, TIOCNOTTY, 0);
close (j);
}
#endif /* TIOCNOTTY */
#if !defined (RTU) && !defined (UNIPLUS)
/*** There is a suggestion that this ought to be a
conditional on TIOCSPGRP. */
/* Now close the pty (if we had it open) and reopen it.
This makes the pty the controlling terminal of the subprocess. */
if (ptyname)
{
/* I wonder if close (open (ptyname, ...)) would work? */
if (xforkin >= 0)
close (xforkin);
xforkout = xforkin = open (ptyname, O_RDWR, 0);
if (xforkin < 0)
abort ();
}
#endif /* not UNIPLUS and not RTU */
#ifdef IBMRTAIX
/* On AIX, we've disabled SIGHUP above once we start a child on a pty.
Now reenable it in the child, so it will die when we want it to. */
if (ptyname)
signal (SIGHUP, SIG_DFL);
#endif
#endif /* HAVE_PTYS */
child_setup_tty (xforkout);
child_setup (xforkin, xforkout, xforkout, new_argv, env);
}
environ = save_environ;
}
input_wait_mask |= ChannelMask (inchannel);
/* If the subfork execv fails, and it exits,
this close hangs. I don't know why.
So have an interrupt jar it loose. */
signal (SIGALRM, create_process_1);
alarm (1);
if (forkin >= 0)
close (forkin);
alarm (0);
if (forkin != forkout && forkout >= 0)
close (forkout);
if (pid < 0)
{
remove_process (process);
report_file_error ("Doing vfork", Qnil);
}
XFASTINT (XPROCESS (process)->pid) = pid;
#ifdef SIGCHLD
#ifdef BSD4_1
sigrelse (SIGCHLD);
#else /* not BSD4_1 */
#ifdef HPUX
sigsetmask (0);
#endif /* HPUX */
#if defined (BSD) || defined (UNIPLUS)
sigsetmask (0);
#else /* ordinary USG */
signal (SIGCHLD, sigchld);
#endif /* ordinary USG */
#endif /* not BSD4_1 */
#endif /* SIGCHLD */
}
#ifdef HAVE_SOCKETS
/* open a TCP network connection to a given HOST/SERVICE. Treated
exactly like a normal process when reading and writing. Only
differences are in status display and process deletion. A network
connection has no PID; you cannot signal it. All you can do is
deactivate and close it via delete-process */
#ifdef HAVE_UNIX_DOMAIN
#include <sys/un.h>
#endif HAVE_UNIX_DOMAIN
DEFUN ("open-network-stream", Fopen_network_stream, Sopen_network_stream,
4, 4, 0,
#ifdef HAVE_UNIX_DOMAIN
"Open a TCP connection for a service to a host.\n\
Returns a subprocess-object to represent the connection.\n\
Input and output work as for subprocesses; `delete-process' closes it.\n\
Args are NAME BUFFER HOST SERVICE.\n\
If SERVICE is 0, then HOST is taken to be the name of a socket file, and a\n\
Unix domain socket is opened.\n\
NAME is name for process. It is modified if necessary to make it unique.\n\
BUFFER is the buffer (or buffer-name) to associate with the process.\n\
Process output goes at end of that buffer, unless you specify\n\
an output stream or filter function to handle the output.\n\
BUFFER may be also nil, meaning that this process is not associated\n\
with any buffer\n\
Third arg is name of the host to connect to.\n\
Fourth arg SERVICE is name of the service desired, or an integer\n\
specifying a port number to connect to."
#else
"Open a TCP connection for a service to a host.\n\
Returns a subprocess-object to represent the connection.\n\
Input and output work as for subprocesses; `delete-process' closes it.\n\
Args are NAME BUFFER HOST SERVICE.\n\
NAME is name for process. It is modified if necessary to make it unique.\n\
BUFFER is the buffer (or buffer-name) to associate with the process.\n\
Process output goes at end of that buffer, unless you specify\n\
an output stream or filter function to handle the output.\n\
BUFFER may be also nil, meaning that this process is not associated\n\
with any buffer\n\
Third arg is name of the host to connect to.\n\
Fourth arg SERVICE is name of the service desired, or an integer\n\
specifying a port number to connect to."
#endif HAVE_UNIX_DOMAIN
)
(name, buffer, host, service)
Lisp_Object name, buffer, host, service;
{
Lisp_Object proc;
register int i;
struct sockaddr_in address;
#ifdef HAVE_UNIX_DOMAIN
struct sockaddr_un server;
int unix_domain = 0;
#endif HAVE_UNIX_DOMAIN
struct servent *svc_info;
struct hostent *host_info;
int s, outch, inch;
char errstring[80];
int port;
CHECK_STRING (name, 0);
CHECK_STRING (host, 0);
if (XTYPE(service) == Lisp_Int)
{
#ifdef HAVE_UNIX_DOMAIN
if (XINT (service) == 0)
unix_domain = 1;
else
#endif HAVE_UNIX_DOMAIN
port = htons ((unsigned short) XINT (service));
}
else
{
CHECK_STRING (service, 0);
svc_info = getservbyname (XSTRING (service)->data, "tcp");
if (svc_info == 0)
error ("Unknown service \"%s\"", XSTRING (service)->data);
port = svc_info->s_port;
}
#ifdef HAVE_UNIX_DOMAIN
if (unix_domain)
{
server.sun_family = AF_UNIX;
strcpy (server.sun_path, XSTRING (host)->data);
}
else
#endif HAVE_UNIX_DOMAIN
{
host_info = gethostbyname (XSTRING (host)->data);
if (host_info == 0)
error ("Unknown host \"%s\"", XSTRING(host)->data);
bzero (&address, sizeof address);
bcopy (host_info->h_addr, (char *) &address.sin_addr, host_info->h_length);
address.sin_family = host_info->h_addrtype;
address.sin_port = port;
}
#ifdef HAVE_UNIX_DOMAIN
if (unix_domain)
s = socket (AF_UNIX, SOCK_STREAM, 0);
else
#endif HAVE_UNIX_DOMAIN
s = socket (host_info->h_addrtype, SOCK_STREAM, 0);
if (s < 0)
report_file_error ("error creating socket", Fcons (name, Qnil));
#ifdef HAVE_UNIX_DOMAIN
if (unix_domain)
{
if (connect (s, &server, strlen (server.sun_path) + 2) < 0)
error ("connect failed for socket: \"%s\"", XSTRING (host)->data);
}
else
#endif HAVE_UNIX_DOMAIN
{
if (connect (s, &address, sizeof address) == -1)
error ("Host \"%s\" not responding", XSTRING (host)->data);
}
inch = s;
outch = dup (s);
if (outch < 0)
report_file_error ("error duplicating socket", Fcons (name, Qnil));
if (!NULL (buffer))
buffer = Fget_buffer_create (buffer);
proc = make_process (name);
chan_process[inch] = proc;
#ifdef O_NDELAY
fcntl (inch, F_SETFL, O_NDELAY);
#endif
XPROCESS (proc)->childp = host;
XPROCESS (proc)->command_channel_p = Qnil;
XPROCESS (proc)->buffer = buffer;
XPROCESS (proc)->sentinel = Qnil;
XPROCESS (proc)->filter = Qnil;
XPROCESS (proc)->command = Qnil;
XPROCESS (proc)->pid = Qnil;
XPROCESS (proc)->kill_without_query = Qt;
XFASTINT (XPROCESS (proc)->infd) = s;
XFASTINT (XPROCESS (proc)->outfd) = outch;
XFASTINT (XPROCESS (proc)->flags) = RUNNING;
input_wait_mask |= ChannelMask (inch);
return proc;
}
#endif /* HAVE_SOCKETS */
deactivate_process (proc)
Lisp_Object proc;
{
register int inchannel, outchannel;
register struct Lisp_Process *p = XPROCESS (proc);
inchannel = XFASTINT (p->infd);
outchannel = XFASTINT (p->outfd);
if (inchannel)
{
/* Beware SIGCHLD hereabouts. */
flush_pending_output (inchannel);
close (inchannel);
if (outchannel && outchannel != inchannel)
close (outchannel);
XFASTINT (p->infd) = 0;
XFASTINT (p->outfd) = 0;
chan_process[inchannel] = Qnil;
input_wait_mask &= ~ChannelMask (inchannel);
}
}
/* Close all descriptors currently in use for communication
with subprocess. This is used in a newly-forked subprocess
to get rid of irrelevant descriptors. */
close_process_descs ()
{
int i;
for (i = 0; i < MAXDESC; i++)
{
Lisp_Object process;
process = chan_process[i];
if (!NULL (process))
{
int in = XFASTINT (XPROCESS (process)->infd);
int out = XFASTINT (XPROCESS (process)->outfd);
close (in);
if (in != out)
close (out);
}
}
}
DEFUN ("accept-process-output", Faccept_process_output, Saccept_process_output,
0, 2, 0,
"Allow any pending output from subprocesses to be read by Emacs.\n\
It is read into the process' buffers or given to their filter functions.\n\
Non-nil arg PROCESS means do not return until some output has been received\n\
from PROCESS. Non-nil arg TIMEOUT means wait for that many seconds, -1\n\
return immediately.")
(proc, timeout)
register Lisp_Object proc, timeout;
{
if (NULL (proc)) {
if (XTYPE(timeout) == Lisp_Int)
timeout = XINT(timeout);
else
timeout = -1;
return (wait_reading_process_input (timeout, 0, 0));
}
else
{
if (XTYPE(timeout) == Lisp_Int)
timeout = XINT(timeout);
else
timeout = 0;
proc = get_process (proc);
return (wait_reading_process_input (timeout, XPROCESS (proc), 0));
}
}
/* This variable is different from waiting_for_input in keyboard.c.
It is used to communicate to a lisp process-filter/sentinel (via the
function Fwaiting_for_user_input_p below) whether emacs was waiting
for user-input when that process-filter was called.
waiting_for_input cannot be used as that is by definition 0 when
lisp code is being evalled */
static int waiting_for_user_input_p;
/* Read and dispose of subprocess output
while waiting for timeout to elapse and/or keyboard input to be available.
time_limit is the timeout in seconds, or zero for no limit.
-1 means gobble data available immediately but don't wait for any.
read_kbd is 1 to return when input is available.
-1 means caller will actually read the input.
A pointer to a struct Lisp_Process means wait until
something arrives from that process.
do_display means redisplay should be done to show
subprocess output that arrives. */
Lisp_Object
wait_reading_process_input (time_limit, read_kbd, do_display)
int time_limit, read_kbd, do_display;
{
int found = 0;
register int channel, nfds, m;
int Available = 0;
int Exception;
int xerrno;
Lisp_Object proc;
#ifdef HAVE_TIMEVAL
struct timeval timeout, end_time, garbage;
#else
long timeout, end_time, temp;
#endif /* not HAVE_TIMEVAL */
int Atemp;
int wait_channel = 0;
struct Lisp_Process *wait_proc = 0;
extern kbd_count;
/* Detect when read_kbd is really the address of a Lisp_Process. */
if (read_kbd > 10 || read_kbd < -1)
{
wait_proc = (struct Lisp_Process *) read_kbd;
wait_channel = XFASTINT (wait_proc->infd);
read_kbd = 0;
}
waiting_for_user_input_p = read_kbd;
/* Since we may need to wait several times,
compute the absolute time to return at. */
if (time_limit)
{
#ifdef HAVE_TIMEVAL
gettimeofday (&end_time, &garbage);
end_time.tv_sec += time_limit;
#else /* not HAVE_TIMEVAL */
time (&end_time);
end_time += time_limit;
#endif /* not HAVE_TIMEVAL */
}
while (1)
{
/* If calling from keyboard input, do not quit
since we want to return C-g as an input character.
Otherwise, do pending quit if requested. */
if (read_kbd >= 0)
QUIT;
/* If status of something has changed, and no input is available,
notify the user of the change right away */
if (child_changed && do_display)
{
Atemp = input_wait_mask;
#ifdef HAVE_TIMEVAL
timeout.tv_sec=0; timeout.tv_usec=0;
#else /* not HAVE_TIMEVAL */
timeout = 0;
#endif /* not HAVE_TIMEVAL */
if (select (MAXDESC, &Atemp, 0, 0, &timeout) <= 0)
change_msgs();
}
/* Don't wait for output from a non-running process. */
if (wait_proc != 0
&& (XFASTINT (wait_proc->flags) & PROC_STATUS) != RUNNING)
break;
if (fix_screen_hook)
(*fix_screen_hook) ();
/* Compute time from now till when time limit is up */
/* Exit if already run out */
if (time_limit == -1)
{
/* -1 specified for timeout means
gobble output available now
but don't wait at all. */
#ifdef HAVE_TIMEVAL
timeout.tv_sec = 0;
timeout.tv_usec = 0;
#else
timeout = 0;
#endif /* not HAVE_TIMEVAL */
}
else if (time_limit)
{
#ifdef HAVE_TIMEVAL
gettimeofday (&timeout, &garbage);
timeout.tv_sec = end_time.tv_sec - timeout.tv_sec;
timeout.tv_usec = end_time.tv_usec - timeout.tv_usec;
if (timeout.tv_usec < 0)
timeout.tv_usec += 1000000,
timeout.tv_sec--;
if (timeout.tv_sec < 0)
break;
#else /* not HAVE_TIMEVAL */
time (&temp);
timeout = end_time - temp;
if (timeout < 0)
break;
#endif /* not HAVE_TIMEVAL */
}
else
{
#ifdef HAVE_TIMEVAL
/* If no real timeout, loop sleeping with a big timeout
so that input interrupt can wake us up by zeroing it */
timeout.tv_sec = 100;
timeout.tv_usec = 0;
#else /* not HAVE_TIMEVAL */
timeout = 100000; /* 100000 recognized by the select emulator */
#endif /* not HAVE_TIMEVAL */
}
/* Cause C-g and alarm signals to take immediate action,
and cause input available signals to zero out timeout */
if (read_kbd < 0)
set_waiting_for_input (&timeout);
/* Wait till there is something to do */
Available = Exception = input_wait_mask;
if (!read_kbd)
Available &= ~1;
if (read_kbd && kbd_count)
nfds = 0;
else
#ifdef IBMRTAIX
nfds = select (MAXDESC, &Available, 0, 0, &timeout);
#else
#ifdef HPUX
nfds = select (MAXDESC, &Available, 0, 0, &timeout);
#else
nfds = select (MAXDESC, &Available, 0, &Exception, &timeout);
#endif
#endif
xerrno = errno;
found = nfds > 0;
if (fix_screen_hook)
(*fix_screen_hook) ();
/* Make C-g and alarm signals set flags again */
clear_waiting_for_input ();
if (time_limit && nfds == 0) /* timeout elapsed */
break;
if (nfds < 0)
{
if (xerrno == EINTR)
Available = 0;
#ifdef ALLIANT
/* This happens for no known reason on ALLIANT.
I am guessing that this is the right response. -- RMS. */
else if (xerrno == EFAULT)
Available = 0;
#endif
else if (xerrno == EBADF)
abort ();
else
error("select error: %s", sys_errlist[xerrno]);
}
#ifdef sun
else if (nfds > 0 && (Available & 1) && interrupt_input)
/* System sometimes fails to deliver SIGIO. */
kill (getpid (), SIGIO);
#endif
/* Check for keyboard input */
/* If there is any, return immediately
to give it higher priority than subprocesses */
if (read_kbd && detect_input_pending ())
break;
#ifdef vipc
/* Check for connection from other process */
if (Available & ChannelMask (comm_server))
{
Available &= ~(ChannelMask (comm_server));
create_commchan ();
}
#endif vipc
/* Check for data from a process or a command channel */
for (channel = 3; Available && channel < MAXDESC; channel++)
{
m = ChannelMask (channel);
if (m & Available)
{
Available &= ~m;
/* If waiting for this channel,
arrange to return as soon as no more input
to be processed. No more waiting. */
if (wait_channel == channel)
{
wait_channel = 0;
time_limit = -1;
}
proc = chan_process[channel];
if (NULL (proc))
continue;
#ifdef vipc
/* It's a command channel */
if (!NULL (XPROCESS (proc)->command_channel_p))
{
ProcessCommChan (channel, proc);
if (NULL (XPROCESS (proc)->command_channel_p))
{
/* It has ceased to be a command channel! */
int bytes_available;
if (ioctl (channel, FIONREAD, &bytes_available) < 0)
bytes_available = 0;
if (bytes_available)
Available |= m;
}
continue;
}
#endif vipc
/* Read data from the process, starting with our
buffered-ahead character if we have one. */
if (read_process_output (proc, channel) > 0)
{
if (do_display)
DoDsp (1);
}
else
{
/* Preserve status of processes already terminated. */
child_changed++;
deactivate_process (proc);
/*
* With ptys: when the parent process of a pty exits we are notified,
* just as we would be with any of our other children. After the process
* exits, select() will indicate that we can read the channel. When we
* do this, read() returns 0. Upon receiving this, we close the channel.
*
* For external channels, when the peer closes the connection, select()
* will indicate that we can read the channel. When we do this, read()
* returns -1 with errno = ECONNRESET. Since we never get notified of
* this via wait3(), we must explictly mark the process as having exited.
*/
if ((XFASTINT (XPROCESS (proc)->flags) & PROC_STATUS)
== RUNNING)
{
XFASTINT (XPROCESS (proc)->flags) = EXITED | CHANGED;
XFASTINT (XPROCESS (proc)->reason) = 0;
}
}
}
} /* end for */
} /* end while */
return found ? Qt : Qnil;
}
/* Read pending output from the process channel,
starting with our buffered-ahead character if we have one.
Yield number of characters read.
This function reads at most 1024 characters.
If you want to read all available subprocess output,
you must call it repeatedly until it returns zero. */
read_process_output (proc, channel)
Lisp_Object proc;
register int channel;
{
register int nchars;
char chars[1024];
register Lisp_Object outstream;
register struct buffer *old = bf_cur;
register struct Lisp_Process *p = XPROCESS (proc);
register int opoint;
if (proc_buffered_char[channel] < 0)
nchars = read (channel, chars, sizeof chars);
else
{
chars[0] = proc_buffered_char[channel];
proc_buffered_char[channel] = -1;
nchars = read (channel, chars + 1, sizeof chars - 1);
if (nchars < 0)
nchars = 1;
else
nchars = nchars + 1;
}
if (nchars <= 0) return 0;
outstream = p->filter;
if (!NULL (outstream))
{
call2 (outstream, proc, make_string (chars, nchars));
return nchars;
}
/* If no filter, write into buffer if it isn't dead. */
if (!NULL (p->buffer) && !NULL (XBUFFER (p->buffer)->name))
{
Lisp_Object tem;
Fset_buffer (p->buffer);
opoint = point;
/* Insert new output into buffer
at the current end-of-output marker,
thus preserving logical ordering of input and output. */
if (XMARKER (p->mark)->buffer)
SetPoint (marker_position (p->mark));
else
SetPoint (NumCharacters + 1);
if (point <= opoint)
opoint += nchars;
tem = bf_cur->read_only;
bf_cur->read_only = Qnil;
InsCStr (chars, nchars);
bf_cur->read_only = tem;
Fset_marker (p->mark, make_number (point), p->buffer);
RedoModes++;
SetPoint (opoint);
SetBfp (old);
}
return nchars;
}
DEFUN ("waiting-for-user-input-p", Fwaiting_for_user_input_p, Swaiting_for_user_input_p,
0, 0, 0,
"Returns non-NIL if emacs is waiting for input from the user.\n\
This is intended for use by asynchronous process output filters and sentinels.")
()
{
return ((waiting_for_user_input_p) ? Qt : Qnil);
}
/* Sending data to subprocess */
jmp_buf send_process_frame;
send_process_trap ()
{
#ifdef BSD4_1
sigrelse (SIGPIPE);
sigrelse (SIGALRM);
#endif /* BSD4_1 */
longjmp (send_process_frame, 1);
}
send_process_1 (proc, buf, len)
Lisp_Object proc;
char *buf;
int len;
{
/* Don't use register vars; longjmp can lose them. */
int rv;
unsigned char *procname = XSTRING (XPROCESS (proc)->name)->data;
if ((XFASTINT (XPROCESS (proc)->flags) & PROC_STATUS) != RUNNING)
error ("Process %s not running", procname);
if (!setjmp (send_process_frame))
while (len > 0)
{
signal (SIGPIPE, send_process_trap);
rv = write (XFASTINT (XPROCESS (proc)->outfd), buf, len);
signal (SIGPIPE, SIG_DFL);
if (rv < 0)
{
#ifdef EWOULDBLOCK
if (errno == EWOULDBLOCK)
{
/* It would be nice to accept process output here,
but that is difficult. For example, it could
garbage what we are sending if that is from a buffer. */
immediate_quit = 1;
QUIT;
sleep (1);
immediate_quit = 0;
continue;
}
#endif
report_file_error ("writing to process", Fcons (proc, Qnil));
}
buf += rv;
len -= rv;
}
else
{
XFASTINT (XPROCESS (proc)->flags) = EXITED | CHANGED;
deactivate_process (proc);
error ("SIGPIPE raised on process %s; closed it", procname);
}
}
/*** Is it really safe for this to get an error ? */
send_process (proc, buf, count)
Lisp_Object proc;
char *buf;
int count;
{
#ifdef vipc
struct { int checkword, type, datalen; } header;
if (!NULL (XPROCESS (proc)->command_channel_p))
{
checkword = UNIQUE_FROB;
type = VIPC_MESG;
datalen = count;
send_process_1 (proc, &header, sizeof header);
}
#endif vipc
send_process_1 (proc, buf, count);
}
DEFUN ("process-send-region", Fprocess_send_region, Sprocess_send_region,
3, 3, 0,
"Send current contents of region as input to PROCESS.\n\
PROCESS may be a process name.\n\
Called from program, takes three arguments, PROCESS, START and END.")
(process, start, end)
Lisp_Object process, start, end;
{
Lisp_Object proc;
int start1;
proc = get_process (process);
validate_region (&start, &end);
if (XINT (start) < bf_s1 && XINT (end) >= bf_s1)
move_gap (start);
start1 = XINT (start);
send_process (proc, &CharAt (start1), XINT (end) - XINT (start));
return Qnil;
}
DEFUN ("process-send-string", Fprocess_send_string, Sprocess_send_string,
2, 2, 0,
"Send PROCESS the contents of STRING as input.\n\
PROCESS may be a process name.")
(process, string)
Lisp_Object process, string;
{
Lisp_Object proc;
CHECK_STRING (string, 1);
proc = get_process (process);
send_process (proc, XSTRING (string)->data, XSTRING (string)->size);
return Qnil;
}
/* send a signal number SIGNO to PROCESS.
CURRENT_GROUP means send to the process group that currently owns
the terminal being used to communicate with PROCESS.
This is used for various commands in shell mode.
If NOMSG is zero, insert signal-announcements into process's buffers
right away. */
sig_process (process, signo, current_group, nomsg)
Lisp_Object process;
int signo;
Lisp_Object current_group;
int nomsg;
{
Lisp_Object proc;
register struct Lisp_Process *p;
int gid;
proc = get_process (process);
p = XPROCESS (proc);
if (!EQ (p->childp, Qt))
error ("Process %s is not a subprocess",
XSTRING (p->name)->data);
if (!XFASTINT (p->infd))
error ("Process %s is not active",
XSTRING (p->name)->data);
#ifdef TIOCGPGRP /* Not sure about this! (fnf) */
/* If we are using pgrps, get a pgrp number and make it negative. */
if (!NULL (current_group))
{
ioctl (XFASTINT (p->infd), TIOCGPGRP, &gid);
gid = - gid;
}
else
gid = - XFASTINT (p->pid);
#else /* not using pgrps */
/* Can't select pgrps on this system, so we know that
the child itself heads the pgrp. */
gid = - XFASTINT (p->pid);
#endif /* not using pgrps */
switch (signo)
{
#ifdef SIGCONT
case SIGCONT:
XFASTINT (p->flags) = RUNNING | CHANGED;
child_changed++;
break;
#endif
case SIGINT:
case SIGQUIT:
case SIGKILL:
flush_pending_output (XFASTINT (p->infd));
break;
}
/* gid may be a pid, or minus a pgrp's number */
#ifdef BSD
/* On bsd, [man says] kill does not accept a negative number to kill a pgrp.
Must do that differently. */
killpg (-gid, signo);
#else /* Not BSD. */
kill (gid, signo);
#endif /* Not BSD. */
/* Put notices in buffers now, since it is safe now.
Because of this, we know that a process we have just killed
will never need to use its buffer again. */
if (!nomsg)
change_msgs ();
}
DEFUN ("interrupt-process", Finterrupt_process, Sinterrupt_process, 0, 2, 0,
"Interrupt process PROCESS. May be process or name of one.\n\
Nil or no arg means current buffer's process.\n\
Second arg CURRENT-GROUP non-nil means send signal to\n\
the current process-group of the process's controlling terminal\n\
rather than to the process's own process group.\n\
If the process is a shell, this means interrupt current subjob\n\
rather than the shell.")
(process, current_group)
Lisp_Object process, current_group;
{
sig_process (process, SIGINT, current_group, 0);
return process;
}
DEFUN ("kill-process", Fkill_process, Skill_process, 0, 2, 0,
"Kill process PROCESS. May be process or name of one.\n\
See function interrupt-process for more details on usage.")
(process, current_group)
Lisp_Object process, current_group;
{
sig_process (process, SIGKILL, current_group, 0);
return process;
}
DEFUN ("quit-process", Fquit_process, Squit_process, 0, 2, 0,
"Send QUIT signal to process PROCESS. May be process or name of one.\n\
See function interrupt-process for more details on usage.")
(process, current_group)
Lisp_Object process, current_group;
{
sig_process (process, SIGQUIT, current_group, 0);
return process;
}
DEFUN ("stop-process", Fstop_process, Sstop_process, 0, 2, 0,
"Stop process PROCESS. May be process or name of one.\n\
See function interrupt-process for more details on usage.")
(process, current_group)
Lisp_Object process, current_group;
{
#ifndef SIGTSTP
error ("no SIGTSTP support");
#else
sig_process (process, SIGTSTP, current_group, 0);
#endif
return process;
}
DEFUN ("continue-process", Fcontinue_process, Scontinue_process, 0, 2, 0,
"Continue process PROCESS. May be process or name of one.\n\
See function interrupt-process for more details on usage.")
(process, current_group)
Lisp_Object process, current_group;
{
#ifdef SIGCONT
sig_process (process, SIGCONT, current_group, 0);
#else
error ("no SIGCONT support");
#endif
return process;
}
DEFUN ("process-send-eof", Fprocess_send_eof, Sprocess_send_eof, 0, 1, 0,
"Make PROCESS see end-of-file in its input.\n\
Eof comes after any text already sent to it.\n\
nil or no arg means current buffer's process.")
(process)
Lisp_Object process;
{
Lisp_Object proc;
proc = get_process (process);
/* Sending a zero-length record is supposed to mean eof
when TIOCREMOTE is turned on. */
#ifdef DID_REMOTE
{
char buf[1];
write (XFASTINT (XPROCESS (proc)->outfd), buf, 0);
}
#else /* did not do TOICREMOTE */
send_process (proc, "\004", 1);
#endif /* did not do TOICREMOTE */
return process;
}
/* Kill all processes associated with `buffer'.
If `buffer' is nil, kill all processes */
kill_buffer_processes (buffer)
Lisp_Object buffer;
{
Lisp_Object tail, proc;
for (tail = Vprocess_alist; XGCTYPE (tail) == Lisp_Cons;
tail = XCONS (tail)->cdr)
{
proc = XCONS (XCONS (tail)->car)->cdr;
if (XGCTYPE (proc) == Lisp_Process
&& (NULL (buffer) || EQ (XPROCESS (proc)->buffer, buffer)))
{
if (NETCONN_P (proc))
deactivate_process (proc);
else if (XFASTINT (XPROCESS (proc)->infd))
sig_process (proc, SIGHUP, Qnil, 1);
}
}
}
count_active_processes ()
{
register Lisp_Object tail, proc;
register int count = 0;
for (tail = Vprocess_alist; !NULL (tail); tail = Fcdr (tail))
{
proc = Fcdr (Fcar (tail));
if ((1 << (XFASTINT (XPROCESS (proc)->flags) & PROC_STATUS)
& ((1 << RUNNING) | (1 << STOPPED)))
&& NULL (XPROCESS (proc)->kill_without_query))
count++;
}
return count;
}
/* On receipt of a signal that a child status has changed,
loop asking about children with changed statuses until
the system says there are no more.
All we do is change the flags components;
we do not run sentinels or print notifications.
That is saved for the next time keyboard input is done,
in order to avoid timing errors. */
/** WARNING: this can be called during garbage collection.
Therefore, it must not be fooled by the presence of mark bits in
Lisp objects. */
/** USG WARNING: Although it is not obvious from the documentation
in signal(2), on a USG system the SIGCLD handler MUST NOT call
signal() before executing at least one wait(), otherwise the handler
will be called again, resulting in an infinite loop. The relevant
portion of the documentation reads "SIGCLD signals will be queued
and the signal-catching function will be continually reentered until
the queue is empty". Invoking signal() causes the kernel to reexamine
the SIGCLD queue. Fred Fish, UniSoft Systems Inc. */
child_sig (signo)
int signo;
{
register int pid;
WAITTYPE w;
Lisp_Object tail, proc;
register struct Lisp_Process *p;
int old_errno = errno;
#ifdef BSD4_1
extern int synch_process_pid;
extern int sigheld;
sigheld |= sigbit (SIGCHLD);
#endif
loop:
#ifdef WNOHANG
#ifndef WUNTRACED
#define WUNTRACED 0
#endif /* no WUNTRACED */
pid = wait3 (&w, WNOHANG | WUNTRACED, 0);
if (pid <= 0)
{
if (errno == EINTR)
{
errno = 0;
goto loop;
}
/* USG systems forget handlers when they are used;
must reestablish each time */
#ifdef USG
signal (signo, child_sig); /* WARNING - must come after wait3() */
#endif
#ifdef BSD4_1
sigheld &= ~sigbit (SIGCHLD);
sigrelse (SIGCHLD);
#endif
errno = old_errno;
return;
}
#else
pid = wait (&w);
#endif /* no WNOHANG */
#ifdef BSD4_1
if (synch_process_pid == pid)
synch_process_pid = 0; /* Zero it to show process has died. */
#endif
for (tail = Vprocess_alist; XSYMBOL (tail) != XSYMBOL (Qnil); tail = XCONS (tail)->cdr)
{
proc = XCONS (XCONS (tail)->car)->cdr;
p = XPROCESS (proc);
if (EQ (p->childp, Qt) && XFASTINT (p->pid) == pid)
break;
}
if (XSYMBOL (tail) == XSYMBOL (Qnil))
#if defined (USG) && ! (defined (HPUX) && defined (WNOHANG))
goto ignore;
#else
goto loop; /* We don't know who this is */
#endif
child_changed++;
if (WIFSTOPPED (w))
{
XFASTINT (p->flags) = STOPPED | CHANGED;
XFASTINT (p->reason) = WSTOPSIG (w);
}
else if (WIFEXITED (w))
{
XFASTINT (p->flags) = EXITED | CHANGED;
if (WCOREDUMP (w))
XFASTINT (p->flags) |= COREDUMPED;
XFASTINT (p->reason) = WRETCODE (w);
}
else if (WIFSIGNALED (w))
{
XFASTINT (p->flags) = SIGNALED | CHANGED;
if (WCOREDUMP (w))
XFASTINT (p->flags) |= COREDUMPED;
XFASTINT (p->reason) = WTERMSIG (w);
}
#if !defined (USG) || (defined (HPUX) && defined (WNOHANG))
goto loop;
#else
ignore:
#ifdef USG
signal (signo, child_sig);
#endif
errno = old_errno;
#endif /* not USG, or HPUX with WNOHANG */
}
/* Find all process marked as "changed"
and notify the user in a suitable fashion
(either run the sentinel or output a message).
This is done while Emacs is waiting for keyboard input */
change_msgs()
{
register Lisp_Object tail, proc, buffer;
register struct Lisp_Process *p;
char line[50];
child_changed = 0;
for (tail = Vprocess_alist; !NULL (tail); tail = Fcdr (tail))
{
proc = Fcdr (Fcar (tail));
p = XPROCESS (proc);
if (!(XFASTINT (p->flags) & CHANGED))
continue;
/* If process is still active, read any output that remains. */
if (XFASTINT (p->infd))
while (read_process_output (proc, XFASTINT (p->infd)) > 0);
XFASTINT (p->flags) &= ~CHANGED;
line[0] = 0;
buffer = p->buffer;
if ((XFASTINT (p->flags) & PROC_STATUS) == SIGNALED
|| (XFASTINT (p->flags) & PROC_STATUS) == STOPPED)
{
sprintf (line, "%s%s\n",
XFASTINT (p->reason) < NSIG
? sys_siglist[XFASTINT (p->reason)] : "unknown",
XFASTINT (p->flags) & COREDUMPED ? " (core dumped)" : "");
line[0] = DOWNCASE (line[0]);
if ((XFASTINT (p->flags) & PROC_STATUS) == SIGNALED)
if (delete_exited_processes)
remove_process (proc);
else
deactivate_process (proc);
}
else if ((XFASTINT (p->flags) & PROC_STATUS) == EXITED)
{
if (XFASTINT (p->reason))
sprintf (line, "exited abnormally with code %d\n",
XFASTINT (p->reason));
else
sprintf (line, "finished\n");
if (delete_exited_processes)
remove_process (proc);
else
deactivate_process (proc);
}
if (!NULL (p->sentinel))
exec_sentinel (proc, build_string (line));
else if (line[0] && !NULL (buffer))
{
Lisp_Object ro = XBUFFER (buffer)->read_only;
struct buffer *old = bf_cur;
int opoint;
/* Avoid error if buffer is deleted
(probably that's why the process is dead, too) */
if (NULL (XBUFFER (buffer)->name))
continue;
Fset_buffer (buffer);
opoint = point;
SetPoint (NumCharacters + 1);
if (point == opoint)
opoint = -1;
bf_cur->read_only = Qnil;
InsStr ("\nProcess ");
Finsert (1, &p->name);
InsStr (" ");
InsStr (line);
bf_cur->read_only = ro;
if (opoint > 0)
SetPoint (opoint);
SetBfp (old);
}
} /* end for */
RedoModes++; /* in case buffers use %s in mode-line-format */
DoDsp (1);
}
exec_sentinel (proc, reason)
Lisp_Object proc, reason;
{
Lisp_Object sentinel;
register struct Lisp_Process *p = XPROCESS (proc);
sentinel = p->sentinel;
if (NULL (sentinel))
return;
p->sentinel = Qnil;
call2 (sentinel, proc, reason);
p->sentinel = sentinel;
}
init_process ()
{
register int i;
#ifdef SIGCHLD
#ifndef CANNOT_DUMP
if (! noninteractive || initialized)
#endif
signal (SIGCHLD, child_sig);
#endif
input_wait_mask = ChannelMask(0);
Vprocess_alist = Qnil;
for (i = 0; i < MAXDESC; i++)
{
chan_process[i] = Qnil;
proc_buffered_char[i] = -1;
}
}
syms_of_process ()
{
Qprocessp = intern ("processp");
staticpro (&Qprocessp);
staticpro (&Vprocess_alist);
DEFVAR_BOOL ("delete-exited-processes", &delete_exited_processes,
"*Non-nil means delete processes immediately when they exit.\n\
nil means don't delete them until `list-processes' is run.");
delete_exited_processes = 1;
DEFVAR_LISP ("process-connection-type", &Vprocess_connection_type,
"Control type of device used to communicate with subprocesses.\n\
Values are nil to use a pipe, t for a pty (or pipe if ptys not supported).\n\
Value takes effect when `start-process' is called.");
Vprocess_connection_type = Qt;
defsubr (&Sprocessp);
defsubr (&Sget_process);
defsubr (&Sget_buffer_process);
defsubr (&Sdelete_process);
defsubr (&Sprocess_status);
defsubr (&Sprocess_exit_status);
defsubr (&Sprocess_id);
defsubr (&Sprocess_name);
defsubr (&Sprocess_command);
defsubr (&Sset_process_buffer);
defsubr (&Sprocess_buffer);
defsubr (&Sprocess_mark);
defsubr (&Sset_process_filter);
defsubr (&Sprocess_filter);
defsubr (&Sset_process_sentinel);
defsubr (&Sprocess_sentinel);
defsubr (&Sprocess_kill_without_query);
defsubr (&Sprocess_kill_without_query_p);
defsubr (&Slist_processes);
defsubr (&Sprocess_list);
defsubr (&Sstart_process);
#ifdef HAVE_SOCKETS
defsubr (&Sopen_network_stream);
#endif /* HAVE_SOCKETS */
defsubr (&Saccept_process_output);
defsubr (&Sprocess_send_region);
defsubr (&Sprocess_send_string);
defsubr (&Sinterrupt_process);
defsubr (&Skill_process);
defsubr (&Squit_process);
defsubr (&Sstop_process);
defsubr (&Scontinue_process);
defsubr (&Sprocess_send_eof);
defsubr (&Swaiting_for_user_input_p);
}
#endif subprocesses
============================================================
This document is a specification of the Franz Inc. GNU Emacs interface
to Common Lisp, which is written entirely in GNU Emacs Lisp (for GNU
Emacs version 18).
0. Concepts and Terminology
In Emacs, each buffer has a `mode.' A mode is a collection of key
and variable `bindings' which are specialized to a certain task. When
the cursor is in a `buffer' and you depress a key, its action is
determined by the binding found in the `keymap' associated with that
buffer (the buffer-local keymap). If there is no binding in the
buffer-local keymap, then the binding is sought on the `global' keymap.
Thus, a mode is usually implemented by creating a buffer-local keymap
and adding bindings to do specific tasks. The result is that certain
keys act differently in the buffer but most keys act just as they do
in all the other buffers. Usually the mode of a buffer is displayed
in parentheses at the bottom of the screen (in what is called the
`mode line').
Two terms used interchangeably in this document are `subprocess' and
`inferior process'. They both refer to the interaction between Emacs
and running program, and their names come from the fact that UNIX
processes are part of a hierarchical structure--each process has a
parent and some processes have children. Here, Lisp is run as a child
of Emacs, or a subordinate process under Emacs.
Most machines available today have a `network' interface. There
are several `protocols' in use today, of which `TCP/IP' (Transmission
Control Protocol) is one. TCP/IP is most commonly used for
communication between machines using `ethernet'--ethernet is the
hardware and TCP/IP is the software. TCP/IP is available on Berkeley
UNIX and some other UNIX systems, and is a powerful tool for making
resources from a large number of machines easily available to all
users on the network. The X window system, for example, uses TCP/IP
to communicate between clients and server.
In order for Emacs and Lisp to communicate, Lisp must accept
messages sent to a certain address and Emacs must know that address.
There are two types of addressing available: internet and UNIX domain.
An internet address is a machine address and a port number. A UNIX
domain address is a file name. The advantage of an internet address
is that it can be accessed from any machine on the network because the
address contains the machine number. The disadvantage is that only
one process (a Lisp process in our case) on a given machine can listen
for messages on a given internet address. Thus, if you wanted to use
the Emacs to Lisp interface on a machine with many users, only one
user at a time could use it. A UNIX domain address can only be used
from programs running on the same machine. However the flexibility of
using file names in UNIX domain addresses allows the Emacs to Lisp
interface to choose unique addresses for each user running on a
machine. The file name convention we use is this: when user `smith'
starts up Lisp, the UNIX domain address `/tmp/GnutoACLxxx' (xxx is the
UID for user smith) is used by Lisp. When `smith' directs Emacs to
connect to Lisp, it knows it was started by smith and thus talks to
address `/tmp/GnutoACLxxx'. Thus multiple users on a given machine
can run the Emacs to Lisp interface.
The Emacs/Lisp interface in this document can use both of these
methods of communication, but the preferred method uses UNIX domain
sockets, because more than one person on a machine may use the
Lisp/Emacs interface.
1. Introduction
The purpose of this package is to enhance the Lisp environment. To
this end, GNU Emacs and Lisp have been tightly coupled, and in the
case of Allegro CL 3.0 the interface uses TCP/IP UNIX or internet
domain sockets. This interface can be broken down into three modes:
* Common Lisp,
* Inferior Common Lisp, and
* TCP Common Lisp.
The first is for editing Lisp programs, the second for starting up a
Lisp subprocess under Emacs, and the third for TCP/IP communication
between Emacs and Lisp.
While editing Lisp source code, there are commands that make it
easy to send expressions to, and receive information from, a Lisp
process. For example: a function can be sent to Lisp from a source
buffer, a form in a source buffer can be macroexpanded, and
source for functions defined by loading files into Lisp may be located
automatically.
For many of the bindings set up in the editing modes to be useful,
there must be a Lisp to which Emacs can communicate. In this area the
interface can be broken down into three parts: an enhanced mode for
running an inferior Lisp process in an Emacs buffer, a new mode that
creates an Emacs buffer onto an existing Lisp process via a TCP/IP
socket, and mechanisms to query the Lisp environment for information
which can be used in the Emacs environment (also using TCP/IP
sockets).
Running an inferior Lisp in an Emacs buffer is not new--Emacs
supports this `off the shelf.' This feature allows users to interact
directly with Lisp while editing Lisp source code. Although the
existing inferior Lisp mode has been enhanced and a new mode added to
support socket connections to Lisp, the most significant contribution of
this package is in offering tighter coupling of the Lisp and Emacs
environments and in providing better subprocess management tools.
By taking advantage of Lisp multiprocessing, an Emacs buffer can be
opened onto a Lisp not started as a subprocess of Emacs. This
connection uses TCP/IP sockets (UNIX or internet domain), which in turn
means that Emacs can talk to a Lisp running on any machine on the
network. Sockets and multiprocessing also mean that one can establish
more than one connection between Lisp and Emacs.
These socket connections to Lisp are the key to the integration of
the two environments. Emacs uses a `back door' connection to exchange
information with a Lisp process. Users can obtain information about
Lisp function argument lists and symbol documentation, macroexpand Lisp
forms, and locate source definitions of Lisp symbols (which are recorded
by Lisp when a symbol is defined, usually by loading a file). All these
features are accessible with a few key-strokes.
Aside from the Lisp interface proper, this package provides useful
features for subprocess manipulation, including an input ring for
handy retrieval of previously-typed input, filename completion,
special handling of certain subprocess-specific keys (e.g., ^C or ^D),
and continuously showing output from a subprocess in a visible buffer.
2. Editing Lisp
This package modifies `auto-mode-alist' so that the major modes
defined in this package are invoked when certain types of source files
are edited. The list of filename extensions given by the value of
`fi:common-lisp-file-types' cause `fi:common-lisp-mode' to be invoked
whenever a file with one of these types is visited. The important
actions done by Common Lisp mode are (in order):
1. set up local variables specific to the mode;
2. check for package information;
3. set up keymaps; and
4. call hooks.
The above functions are enhancements to the standard available GNU
Emacs Lisp mode.
The major mode function for Common Lisp is called
`fi:common-lisp-mode', and this function can be interactively called
to put a buffer in Common Lisp mode.
fi:common-lisp-file-types...........................................[variable]
Value: (".cl" ".lisp" ".lsp")
*A list of the files which are automatically put in fi:common-lisp-mode.
This variable should be set before this package is loaded.
2.1 Bindings
The key bindings for Common Lisp mode are set up only once, the
first time the mode is entered, and are taken from the variable
`fi:common-lisp-mode-map'. Users wanting to change the bindings could
explicitly set this variable or augment the map from within the hooks
for the mode.
The initial local bindings for Common Lisp mode are:
fi:common-lisp-mode-map...............................................[keymap]
Major mode map used when editing Common Lisp source.
RET fi:lisp-mode-newline
DEL backward-delete-char-untabify
TAB fi:lisp-indent-line
C-c Prefix Command
C-x Prefix Command
ESC Prefix Command
C-c C-r fi:lisp-eval-region
C-c C-s fi:lisp-eval-last-sexp
C-c C-b fi:lisp-eval-current-buffer
C-c - fi:log-functional-change
ESC C-x fi:lisp-eval-defun
ESC W fi:lisp-walk
ESC M fi:lisp-macroexpand
ESC F fi:lisp-function-documentation
ESC D fi:lisp-describe
ESC C fi:lisp-who-calls
ESC A fi:lisp-arglist
ESC TAB fi:lisp-complete-symbol
ESC , fi:lisp-tags-loop-continue
ESC . fi:lisp-find-tag
ESC C-q fi:indent-sexp
2.2 Variables
All local variables are killed when Common Lisp mode is
entered--this is a convention to insure that there are no name
conflicts with user variables.
To correctly deal with the Common Lisp package system, Emacs must
know the package for each file, so that it may use this when sending
expressions from a buffer to Lisp. There is a buffer-local variable
which contains the name of the package:
fi:package..........................................................[variable]
Value: "user"
A buffer-local variable whose value should either be nil or a string
which names a package in the Lisp world (ie, in a Lisp subprocess running
as an inferior of Emacs in some buffer). It is used when expressions are
sent from an Emacs buffer to a Lisp process so that the symbols are read
into the correct Lisp package.
There are three ways to automatically set this variable (the first and
third items here are standard GNU Emacs features):
1. If the first line in the file contains text surrounded by -*-,
the `package' field of this line is parsed for the package name.
For example,
;; -*- mode: common-lisp; package: excl -*-
would put the file in package `excl'.
2. If the above attempt fails, then the buffer is searched for an
`in-package' form, which is parsed for the package name.
3. Lastly, the values obtained in one of the first two locations can
be over-written by manually setting fi:package in the local
variables list. For example, the following at the end of the
file will override any value given previously given to
fi:package:
^L
;; Local Variables:
;; eval: (setq fi:package "excl")
;; End:
Note that the normal method for setting a variable will not work,
because `fi:package' contains a `:' and this confuses GNU
Emacs, hence the use of `eval'.
If Emacs cannot determine the package, then it is assumed to be
"user" (the initial Common Lisp package).
2.3 Functions
The following functions are either bound to keys in Common Lisp
mode or are available as extended commands:
fi:lisp-mode-newline................................................[function]
Invoke with: RET in Common Lisp mode.
Function bound to C-m. The default version just inserts a newline.
fi:lisp-eval-region.................................................[function]
Invoke with: C-c C-r in Common Lisp mode.
Send the text in the region to the Lisp subprocess associated with this
buffer, one expression at a time if there is more than one complete
expression. If a Lisp subprocess has not been started, then one is
started. With a prefix argument, the source sent to the subprocess is
compiled.
fi:lisp-eval-last-sexp..............................................[function]
Invoke with: C-c C-s in Common Lisp mode.
Send the sexp before the point to the Lisp subprocess associated with
this buffer. If a Lisp subprocess has not been started, then one is
started. With a prefix argument, the source sent to the subprocess is
compiled.
fi:lisp-eval-current-buffer.........................................[function]
Invoke with: C-c C-b in Common Lisp mode.
Send the entire buffer to the Lisp subprocess associated with this
buffer. If a Lisp subprocess has not been started, then one is started.
With a prefix argument, the source sent to the subprocess is compiled.
fi:lisp-eval-defun..................................................[function]
Invoke with: ESC C-x in Common Lisp mode.
Send the current top-level (or nearest previous) form to the Lisp
subprocess associated with this buffer. A `top-level' form is one that
starts in column 1. If a Lisp subprocess has not been started, then one is
started. With a prefix argument, the source sent to the subprocess is
compiled.
fi:remove-all-temporary-lisp-transaction-files......................[function]
This function will clean up all the files created for Lisp/Emacs
communication. See the variable fi:emacs-to-lisp-transaction-directory for
the location of the files.
The following functions are bound to keys in Common Lisp mode, and
require the TCP/IP communication channel to Lisp:
fi:lisp-find-tag....................................................[function]
Invoke with: ESC . in Common Lisp mode.
Find the Common Lisp source for a symbol, using the characters around
the point as the default tag.
fi:lisp-tags-loop-continue..........................................[function]
Invoke with: ESC , in Common Lisp mode.
Find the next occurrence of the tag last used by fi:lisp-find-tag.
fi:lisp-arglist.....................................................[function]
Invoke with: ESC A in Common Lisp mode.
Print the arglist (using excl:arglist) for a symbol, which is read from
the minibuffer. The word around the point is used as the default.
fi:lisp-describe....................................................[function]
Invoke with: ESC D in Common Lisp mode.
Describe a symbol, which is read from the minibuffer. The word around
the point is used as the default.
fi:lisp-function-documentation......................................[function]
Invoke with: ESC F in Common Lisp mode.
Print the function documentation for a symbol, which is read from the
minibuffer. The word around the point is used as the default.
fi:lisp-macroexpand.................................................[function]
Invoke with: ESC M in Common Lisp mode.
Print the macroexpansion of the form at the point.
fi:lisp-walk........................................................[function]
Invoke with: ESC W in Common Lisp mode.
Print the full macroexpansion the form at the point.
With a prefix argument, macroexpand the code as the compiler would.
fi:lisp-who-calls...................................................[function]
Invoke with: ESC C in Common Lisp mode.
Print all the callers of a function. The default symbol name is taken
from the sexp around the point.
fi:lisp-complete-symbol.............................................[function]
Invoke with: ESC TAB in Common Lisp mode.
Perform completion on the Common Lisp symbol preceding the point. That
symbol is compared to symbols that exist in the Common Lisp, to which there
is a TCP/IP connection (see fi:eval-in-lisp). If the symbol starts just
after an open-parenthesis, then only symbols (in the Common Lisp) with
function defintions are considered. Otherwise all symbols are considered.
fi:set-associated-sublisp...........................................[function]
When evaluated in a Lisp source buffer causes further `eval'
commands (those which send expressions from Emacs to Lisp) to use
BUFFER-NAME as the buffer which contains a Lisp subprocess. If evaluated
when not in a Lisp source buffer, then the process type is read from the
minibuffer ("common-lisp" or "franz-lisp"). The buffer name is
interactively read and must be the name of an existing buffer. New buffers
with the same mode as the current buffer will also use BUFFER-NAME for
future `eval' commands.
2.3 Hooks
The hooks run for Common Lisp mode are fi:lisp-mode-hook and
fi:common-lisp-mode-hook. The form of the hook function is determined
by `run-hooks', which is used to process the hooks. Hooks are run
last during mode initialization. For example, the following hook
enables auto-fill in Common Lisp mode:
(setq fi:lisp-mode-hook
'(lambda ()
(define-key (current-local-map) "\t" 'lisp-indent-line)
(setq fill-column 75)
(auto-fill-mode 1)))
3. Running Lisp
What follows is a discussion of the bindings, variables and
functions allowing the user to interact with and manipulate a Lisp run
as an inferior to Emacs. A discussion on how to start up and use the
interface is discussed in the next section.
3.1 Bindings
The initial local bindings for Inferior Common Lisp mode are:
fi:inferior-common-lisp-mode-map......................................[keymap]
The inferior-common-lisp major-mode keymap.
DEL backward-delete-char-untabify
TAB fi:lisp-indent-line
C-x Prefix Command
ESC Prefix Command
C-c Prefix Command
RET fi:inferior-lisp-newline
C-c C-\ fi:subprocess-quit
C-c C-d fi:subprocess-send-eof
C-c C-c fi:subprocess-interrupt
C-c . fi:lisp-sync-current-working-directory
C-c C-w fi:subprocess-backward-kill-word
C-c C-v fi:subprocess-show-output
C-c C-u fi:subprocess-kill-input
C-c C-s fi:re-search-forward-input
C-c C-r fi:re-search-backward-input
C-c C-p fi:pop-input
C-c C-o fi:subprocess-send-flush
C-c C-n fi:push-input
C-c RET fi:subprocess-input-region
C-c C-l fi:list-input-ring
C-c C-k fi:subprocess-kill-output
C-c C-a fi:subprocess-beginning-of-line
C-x RET fi:inferior-lisp-input-list
ESC W fi:lisp-walk
ESC M fi:lisp-macroexpand
ESC F fi:lisp-function-documentation
ESC D fi:lisp-describe
ESC C fi:lisp-who-calls
ESC A fi:lisp-arglist
ESC TAB fi:lisp-complete-symbol
ESC , fi:lisp-tags-loop-continue
ESC . fi:lisp-find-tag
ESC RET fi:inferior-lisp-input-sexp
ESC C-q fi:indent-sexp
and for the TCP Common Lisp mode:
fi:tcp-common-lisp-mode-map...........................................[keymap]
The tcp-lisp major-mode keymap.
DEL backward-delete-char-untabify
TAB fi:lisp-indent-line
C-x Prefix Command
ESC Prefix Command
C-c Prefix Command
RET fi:inferior-lisp-newline
C-c C-\ fi:tcp-lisp-kill-process
C-c C-d fi:tcp-lisp-send-eof
C-c C-c fi:tcp-lisp-interrupt-process
C-c . fi:lisp-sync-current-working-directory
C-c C-w fi:subprocess-backward-kill-word
C-c C-v fi:subprocess-show-output
C-c C-u fi:subprocess-kill-input
C-c C-s fi:re-search-forward-input
C-c C-r fi:re-search-backward-input
C-c C-p fi:pop-input
C-c C-o fi:subprocess-send-flush
C-c C-n fi:push-input
C-c RET fi:subprocess-input-region
C-c C-l fi:list-input-ring
C-c C-k fi:subprocess-kill-output
C-c C-a fi:subprocess-beginning-of-line
C-x RET fi:inferior-lisp-input-list
ESC W fi:lisp-walk
ESC M fi:lisp-macroexpand
ESC F fi:lisp-function-documentation
ESC D fi:lisp-describe
ESC C fi:lisp-who-calls
ESC A fi:lisp-arglist
ESC TAB fi:lisp-complete-symbol
ESC , fi:lisp-tags-loop-continue
ESC . fi:lisp-find-tag
ESC RET fi:inferior-lisp-input-sexp
ESC C-q fi:indent-sexp
Superkeys are keys that are handled specially at the end of a
buffer. At the end of a buffer it has a buffer-specific meaning, but
anywhere else in the buffer it has the normal global meaning (obtained
by looking the key up in the global keymap). This way, ^D can send an
EOF to the Lisp process at the end of a buffer but invoke delete-char
("delete character") anywhere else.
The values of `fi:inferior-common-lisp-mode-super-key-map' and
`fi:tcp-common-lisp-mode-super-key-map' are used as superkey maps for
Inferior Common Lisp and TCP Common Lisp modes.
Superkeys are turned off by default.
3.2 Variables
The name of the Common Lisp image to activate for an inferior Lisp
is taken from the following variables:
fi:common-lisp-image-name...........................................[variable]
Value: "cl"
*Default Common Lisp image to invoke from `fi:common-lisp'. If the
value is a string then it names the image file or image path that
`fi:common-lisp' invokes. Otherwise, the value of this variable is given
to funcall, the result of which should yield a string which is the image
name or path.
After the image name as been determined and the process has been
started, a file in the user's home directory called .emacs_<IMAGE>,
where <IMAGE> is the image name, is fed to the newly created process.
This could be used to customize a initialization file on the name of
the Lisp binary.
The command-line arguments passed to the activated image are taken
from the following variables:
fi:common-lisp-image-arguments......................................[variable]
Value: nil
*Default Common Lisp image arguments when invoked from `fi:common-lisp',
which must be a list of strings.
The prompt pattern for the inferior Lisp is taken from a variable
with the name `fi:<IMAGE>-prompt-pattern' or from the following:
fi:common-lisp-prompt-pattern.......................................[variable]
Value: "^\\(\\[[0-9]+c?\\] \\|\\[step\\] \\)?<[-A-Za-z]* ?[0-9]*?> "
*The regular expression which matches the Common Lisp prompt, used in
Inferior Common Lisp mode. Anything from beginning of line up to the end
of what this pattern matches is deemed to be a prompt.
fi:common-lisp-package-regexp.......................................[variable]
Value: "(in-package\\>\\|:\\<pa\\>\\|:\\<pac\\>\\|:\\<pack\\>\\|:\\<packa\\>\\|:\\<packag\\>\\|:\\<package\\>"
*The regular expression matching the Common Lisp expression(s) to change
packages. If nil, no automatic package tracking will be done.
The following variables are used in TCP Common Lisp mode:
fi:unix-domain......................................................[variable]
Value: nil
*If non-nil, use the UNIX domain for communicating with Common Lisp,
otherwise use the Internet domain.
fi:unix-domain-socket...............................................[variable]
Value: nil
*THIS VARIABLE IS READ-ONLY and is the name of the socket file through
which Emacs communicates with Common Lisp if fi:unix-domain is non-nil.
fi:source-info-not-found-hook.......................................[variable]
Value: find-tag
*The value of this variable is funcalled when source information is not
present in Lisp for a symbol. The function is given one argument, the name
for which source is desired (a string). The null string means use the word
at the point as the search word. This allows the GNU Emacs tags facility
to be used when the information is not present in Lisp.
fi:source-info-not-found-hook.......................................[variable]
Value: find-tag
*The value of this variable is funcalled when source information is not
present in Lisp for a symbol. The function is given one argument, the name
for which source is desired (a string). The null string means use the word
at the point as the search word. This allows the GNU Emacs tags facility
to be used when the information is not present in Lisp.
fi:filename-frobber-hook............................................[variable]
Value: fi::discombobulate-automounter-lint
*If non-nil, then name of a function which transforms filenames received
from Lisp. This exists solely for the purpose of removing /tmp_mnt/net
from the beginning of filenames that are on automounted filesystems.
plus the following miscellaneous variables:
fi:remote-lisp-track-image-name-directory...........................[variable]
Value: nil
*If non-nil, then fi:remote-common-lisp and
fi:explicit-remote-common-lisp will cause both Emacs and Lisp to cd into
the directory where the Common Lisp image resides. Otherwise, the remote
Common Lisp will have $HOME as its current working directory and Emacs will
have whatever directory you were in when you evaluated either function.
fi:default-explicit-common-lisp-image-name..........................[variable]
Value: nil
*If non-nil, then the value of this variable is used as the name of the
image fi:explicit-common-lisp and fi:explicit-remote-common-lisp uses to
run Lisp, instead of reading the name from the minibuffer.
fi:default-explicit-common-lisp-image-arguments.....................[variable]
Value: nil
*If non-nil, then the value of this variable (a string) contains the
arguments given to Lisp by fi:explicit-common-lisp and
fi:explicit-remote-common-lisp when Lisp is invoked, instead of reading the
name from the minibuffer.
fi:default-remote-common-lisp-host..................................[variable]
Value: nil
*If non-nil, then the value of this variable (a string) is names the
host on which fi:remote-common-lisp and fi:explicit-remote-common-lisp
executes Lisp, instead of reading the name from the minibuffer.
fi:default-remote-common-lisp-directory.............................[variable]
Value: nil
*If non-nil, used by fi:remote-common-lisp and
fi:explicit-remote-common-lisp as the directory in which the Lisp is
started. Note that the pathname of the Lisp image must be give relative to
this directory, or the home directory if this variable is nil. See
fi:fi:default-explicit-common-lisp-image-name
fi:lisp-case-sensitive..............................................[variable]
Value: t
*If non-NIL, the code that is being edited is for a case-sensitive dialect
of Lisp. This variable is buffer-local. If a Lisp is case-insensitive,
indentation specifications should be placed on the Emacs Lisp symbol that
corresponds to the lowercase name of the function, macro, or special
form.
fi:echo-evals-from-buffer-in-listener-p.............................[variable]
Value: nil
*If non-NIL, forms evalutated directly from a lisp buffer by the
fi:lisp-eval-* functions will be echoed by the lisp listener.
fi:emacs-to-lisp-transaction-directory..............................[variable]
Value: "/tmp"
*The directory in which files for Emacs/Lisp communication are stored.
When using Lisp and Emacs on different machines, this directory should be
accessible on both machine with the same pathname (via the wonders of NFS).
fi:subprocess-enable-superkeys......................................[variable]
Value: nil
*If t, certain keys become `superkeys' in subprocess buffers--this
should be set before starting any subprocesses. The superkeys are C-a,
C-d, C-o,C-u, C-w, C-z, and C-\, which will behave as they would in the
current local keymap when typed at the end of a subprocess buffer. If
typed elsewhere, these keys have their normal global binding. This is a
buffer-local symbol. Use setq-default to set the default value for this
symbol.
fi:pop-to-sublisp-buffer-after-lisp-eval............................[variable]
Value: t
*If non-nil, then after sending expressions to a Lisp process do pop to
the buffer which contains the Lisp.
fi:display-buffer-function..........................................[variable]
Value: switch-to-buffer
*A function of one argument, a buffer, which is used to display a
buffer when a subprocess is created.
fi:lisp-evalserver-timeout..........................................[variable]
Value: 5
The time which fi:eval-in-lisp will wait before timing out and
signalling an error. Without a timeout Emacs would potentially be locked
out if Lisp did not `return' a result.
fi:lisp-evalserver-number-reads.....................................[variable]
Value: 20
The number of times the Lisp eval server tries to read from the
lisp-evalserver process before giving up. Without this feature Emacs would
hang if Lisp got into an infinite loop while printing. If the size of the
values returned to Emacs is large, then the value of this variable should
be increased.
The following are miscellaneous subprocess variables:
fi:default-input-ring-max...........................................[variable]
Value: 50
*The default maximum length to which an input ring is allowed to grow.
fi:shell-token-pattern..............................................[variable]
Value: "[
()<>&|;=]"
*The regular expression used by file name completion to mark path name
boundaries.
fi:subprocess-continuously-show-output-in-visible-buffer............[variable]
Value: t
*If t, output from a subprocess to a visible buffer is continuously
shown. If a subprocess buffer is visible and the window point is beyond
the process output marker, output to that buffer from its associated
process will be continuously visible. If the window point is before the
process output marker, the window is not updated. This is a buffer-local
symbol.
The following three variables are used to track the shell commands
which change the current working directory. For Common Lisp, the
aliases in Appendix A together with these variables will allow Common
Lisp to emulate the shell commands and for Emacs to track them:
fi:shell-cd-regexp..................................................[variable]
Value: ":?cd"
*The regular expression matching the C shell `cd' command. If nil,
no automatic directory changes will be made.
fi:shell-popd-regexp................................................[variable]
Value: ":?popd"
*The regular expression matching the C shell `popd' command. If nil, no
automatic directory changes will be made.
fi:shell-pushd-regexp...............................................[variable]
Value: ":?pushd"
*The regular expression matching the C shell `pushd' command. If nil,
no automatic directory changes will be made.
3.3 Functions
fi:common-lisp......................................................[function]
Start a Common Lisp subprocess in a buffer whose name is determined
from the optional prefix argument BUFFER-NUMBER. Common Lisp buffer names
start with `*common-lisp' and end with `*', with an optional `-N' in
between. If BUFFER-NUMBER is not given it defaults to 1. If BUFFER-NUMBER
is >= 0, then the buffer is named `*common-lisp*<BUFFER-NUMBER>'. If
BUFFER-NUMBER is < 0, then the first available buffer name is chosen.
The image file and image arguments are taken from the variables
`fi:common-lisp-image-name' and `fi:common-lisp-image-arguments'.
See fi:explicit-common-lisp.
fi:explicit-common-lisp.............................................[function]
The same as fi:common-lisp, except that the image and image arguments
are read from the minibuffer.
fi:remote-common-lisp...............................................[function]
Start a Common Lisp subprocess in a buffer whose name is determined
from the optional prefix argument BUFFER-NUMBER, where the Common Lisp
image is run on another machine. Common Lisp buffer names start with
`*common-lisp' and end with `*', with an optional `-N' in between. If
BUFFER-NUMBER is not given it defaults to 1. If BUFFER-NUMBER is >= 0,
then the buffer is named `*common-lisp*<BUFFER-NUMBER>'. If BUFFER-NUMBER
is < 0, then the first available buffer name is chosen.
The host on which the image is run is read from the minibuffer.
The image file and image arguments are taken from the variables
`fi:common-lisp-image-name' and `fi:common-lisp-image-arguments'.
See fi:explicit-remote-common-lisp.
fi:explicit-remote-common-lisp......................................[function]
The same as fi:remote-common-lisp, except that the image and image
arguments are read from the minibuffer.
fi:tcp-common-lisp..................................................[function]
In a buffer whose name is determined from the optional prefix argument
BUFFER-NAME, connect to a Common Lisp using either a UNIX domain socket
file or internet port number. Common Lisp buffer names start with
`*common-lisp' and end with `*', with an optional `-N' in between. If
BUFFER-NUMBER is not given it defaults to 1. If BUFFER-NUMBER is >= 0,then
the buffer is named `*common-lisp*<BUFFER-NUMBER>'. If BUFFER-NUMBER is <
0, then the first available buffer name is chosen.
See `fi:unix-domain' and `fi:explicit-tcp-common-lisp'.
fi:explicit-tcp-common-lisp.........................................[function]
The same as fi:tcp-common-lisp, except the buffer name of the Lisp onto
which the listener is disired is read from the minibuffer. Use port 0 if
fi:unix-domain is non-nil.
fi:inferior-lisp-newline............................................[function]
Invoke with: RET in Inferior Common Lisp mode.
Bound to RET in an inferior Lisp buffer. At the end of the buffer it
inserts a newline and performs automatic indentation. Whole expressions
are sent to Lisp (not each piece after each newline is typed). This allows
previously typed lines to be edited before Lisp is sent the input. Typed
anywhere else in the buffer, this functions causes the input previously
typed (around the point) to be copied to the end of the subprocess buffer
and send to Lisp.
fi:inferior-lisp-input-sexp.........................................[function]
Invoke with: ESC RET in Inferior Common Lisp mode.
Send the sexp on which the point resides to the Lisp subprocess. With a
numeric prefix argument, send that many sexps.
fi:inferior-lisp-input-list.........................................[function]
Invoke with: C-x RET in Inferior Common Lisp mode.
Send the list before the point to the Lisp subprocess. With a numeric
prefix argument, send that many lists.
fi:subprocess-input-region..........................................[function]
Invoke with: C-c RET in Inferior Common Lisp mode.
Send the region defined by the point and mark to the Lisp subprocess.
fi:subprocess-kill-input............................................[function]
Invoke with: C-c C-u in Inferior Common Lisp mode.
Kill all input since the last output by the subprocess.
fi:subprocess-beginning-of-line.....................................[function]
Invoke with: C-c C-a in Inferior Common Lisp mode.
Moves point to beginning of line, just like (beginning-of-line),
except that if the pattern at the beginning of the line matches the
current subprocess prompt pattern, this function skips over it.
fi:subprocess-backward-kill-word....................................[function]
Invoke with: C-c C-w in Inferior Common Lisp mode.
Kill previous word in current subprocess input line. This function
takes care not to delete past most recent subprocess output.
fi:subprocess-kill-output...........................................[function]
Invoke with: C-c C-k in Inferior Common Lisp mode.
Kill all output from the subprocess since the last input.
fi:subprocess-quit..................................................[function]
Invoke with: C-c C-\ in Inferior Common Lisp mode.
Send a quit signal to the subprocess.
fi:subprocess-send-flush............................................[function]
Invoke with: C-c C-o in Inferior Common Lisp mode.
Send the `flush output' character (^O) to subprocess.
fi:subprocess-show-output...........................................[function]
Invoke with: C-c C-v in Inferior Common Lisp mode.
Display the start of this batch of shell output at top of window.
Also move the point there.
fi:subprocess-suspend...............................................[function]
Suspend, with a SIGSTOP, the current subprocess.
The following three functions are used in fi:inferior-lisp-mode to
interrupt, kill or input an end-of-file to the Lisp process:
fi:subprocess-interrupt.............................................[function]
Invoke with: C-c C-c in Inferior Common Lisp mode.
Interrupt the current subprocess.
fi:subprocess-kill..................................................[function]
Send a `kill' (SIGKILL) signal to the current subprocess.
fi:subprocess-send-eof..............................................[function]
Invoke with: C-c C-d in Inferior Common Lisp mode.
Send an end of file to the subprocess.
The following three functions are used in fi:tcp-common-lisp-mode to
interrupt, kill and input an end-of-file to the Lisp process at the
other end of the TCP/IP socket:
fi:tcp-lisp-interrupt-process.......................................[function]
Invoke with: C-c C-c in TCP Common Lisp mode.
Interrupt the tcp-lisp process via a mp:process-interrupt spoken to the
backdoor Common Lisp listener.
fi:tcp-lisp-kill-process............................................[function]
Invoke with: C-c C-\ in TCP Common Lisp mode.
Kill a tcp-lisp process via a mp:process-kill spoken to the backdoor
Common Lisp listener.
fi:tcp-lisp-send-eof................................................[function]
Invoke with: C-c C-d in TCP Common Lisp mode.
Simulate an EOF on the tcp-lisp process via a db:debug-pop spoken to the
backdoor Common Lisp listener.
All input typed to a subprocess buffer is saved in a ring. The
following functions retrieve input and manipulate the ring:
fi:pop-input........................................................[function]
Invoke with: C-c C-p in Inferior Common Lisp mode.
Yank previous text from input ring. Cycle through input ring with each
successive invocation.
fi:push-input.......................................................[function]
Invoke with: C-c C-n in Inferior Common Lisp mode.
Yank next text from input ring. Cycle through input ring in reverse
order with each successive invocation.
fi:list-input-ring..................................................[function]
Invoke with: C-c C-l in Inferior Common Lisp mode.
Display contents of input ring, starting at arg. The list is displayed
in reverse order if called from a program and the optional second parameter
is non-nil.
fi:re-search-backward-input.........................................[function]
Invoke with: C-c C-r in Inferior Common Lisp mode.
Search in input ring for text that contains regexp and yank.
fi:re-search-forward-input..........................................[function]
Invoke with: C-c C-s in Inferior Common Lisp mode.
Search in input ring for text that contains regexp and yank.
plus the following miscellaneous function:
fi:eval-in-lisp.....................................................[function]
Apply format (in Emacs) to STRING and ARGS and evaluate the result
in the Common Lisp to which we are connected. If a lisp-eval-server has
not been started, then this function starts it.
3.4 Hooks
The hooks run at the end of Inferior Common Lisp mode
initialization are:
fi:lisp-mode-hook
fi:subprocess-mode-hook
fi:inferior-common-lisp-mode-hook
and for TCP Common Lisp mode are:
fi:lisp-mode-hook
fi:subprocess-mode-hook
fi:tcp-common-lisp-mode-hook
4. Starting the Interface
The two parts of the (full) interface which require user setup are:
* running Lisp as an inferior of Emacs, with a "Lisp
listener daemon" listening for connections from Emacs
* a set of Emacs bindings that query Lisp for information
In this sections the setup of these features will be discussed.
4.1 Setup
(After the installation procedure has been followed (see the file
INSTALL in the main distribution directory) the interface will be in a
subdirectory of the `lisp' directory. Typically the location is
/usr/local/lib/emacs, which is the pathname used throughout the rest
of this document.)
The Lisp part of the interface consists of a Lisp process called
the "Lisp listener daemon" which lies dormant until Emacs tries to
communicate with it. When Emacs "connects" an interactive top level
is started (called a "read-eval-print" loop) to which Emacs can send
expressions for evaluation and from which Emacs receives the results
of evaluation (see fi:eval-in-lisp).
Unlike previous versions of the interface, nothing at all need be
added to your .clinit.cl file. You do, however, need to add the
following line to the .emacs file in your home directory:
(load "fi/site-init.el")
Now, consider the following typed to Emacs:
M-x fi:common-lisp RET
This will cause a Common Lisp (the default image name is `cl') to be
started in a buffer called `*common-lisp*', as an inferior process of
Emacs. The connection between Emacs and Lisp will automatically be
started, in the Internet domain. All of the bindings and functions
described above are now available.
4.2 Examples
After fi:common-lisp has started a Common Lisp and all the above
modifications to .emacs and .clinit.cl have been made, the following
Emacs command will open a second buffer onto the same Lisp. This
listener is a separate Common Lisp process (as opposed to a separate
Operating System process) and the entire Common Lisp environment
available in the first listener is available in this one:
M-x fi:tcp-common-lisp RET
To run a second Common Lisp and get a completely fresh Common Lisp:
C-u 2 M-x fi:common-lisp RET
The former uses one Common Lisp for multiple purposes (compiling a
program in one buffer while typing forms to another), while the latter
starts two separate Common Lisp processes, and thus uses twice the
memory and swap space.
There is also a function for running Lisp on a remote machine. For
example, to run Emacs on a machine called `snooze' and Common Lisp on
one called `akbar', where both machines are accessible on the network,
you just need to execute the following sequence in Emacs:
M-x fi:explicit-remote-common-lisp RET
akbar RET
/usr/local/cl RET
x y z RET
which will run the program Lisp image /usr/local/cl (on akbar), giving
Lisp the three arguments "x", "y" and "z".
5. Using the Online Manual
The online manual is automatically loaded with the rest of the
interface. The two entry points to the online manual are the
functions fi:clman and fi:clman-apropos. The first is used to look up
a symbol in the online manual, and the second is used to do an apropos
search on a string.
The features of this package include:
* Command completion: begin typing in a symbol and hit the completion
key. Note: You cannot use command completion when you precede a symbol
with a package qualifier, but see the item below.
* Package-specific search. To confine the search to a particular
package, type the nickname of the package to the prompt and hit the
carriage return. You can then type the name of a symbol in this package
or use command completion to see a list of all the symbols in the
package. The following is the complete list of package nicknames.
(One of the nicknames, "about", while not properly the name
of a package, refers to a group of broad conceptual entries such as
"about-reading" and "about-process-locks".) :
about
cl
compiler
excl
foreign
matrix
mp
sys
tpl
xcw
* Default Lookup: the program assumes you want to lookup the
symbol that surrounds, or is immediately to the left of,
your cursor. Thus you can easily look up a function in
the midst of using it in your code, or quickly pick a symbol
to lookup in the See Also section of the manual page.
* All instances of a symbol in a particular package are found. Use
fi:clman-next-entry to go to the next documented entry for a particular
symbol. (To narrow the search for a symbol down to a particular
package, use fi:clman-apropos.)
Known limitations:
* The Default Lookup mechanism doesn't understand symbols whose
names are broken over a line (e.g. make- window-stream).
Symbols in the See Also section are often broken in that way and
the Default Lookup will not work in those cases.
* There are still some rare cases when the Default Lookup routine
generates an emacs error (usually at the very beginning of a
file). If this happens, just move the cursor to a different
place and try again.
* Only one instance of a symbol that is multiply defined (like
"function" or "+") is currrently found.
5.1 Bindings
The key bindings for CLMAN mode are set up only once, the first
time the mode is entered, and are taken from the variable
`clman:mode-map'. Users wanting to change the bindings could
explicitly set this variable or augment the map from within the hooks
for the mode.
fi:clman-mode.......................................................[function]
Major mode for viewing Allegro manual pages. text-mode-syntax-table and
text-mode-abbrev-table are `used' in this mode.
fi:clman-mode-map.....................................................[keymap]
*Major mode key definitions for viewing a clman page.
n fi:clman-next-entry
s fi:clman-search-forward-see-alsos
p fi:clman-package-help
m fi:clman
a fi:clman-apropos
C-c Prefix Command
C-c C-c fi:clman-flush-doc
5.2 Variables
fi:clman-displaying-function........................................[variable]
Value: fi:clman-find-file
*This function will be funcalled with two arguments, the .doc file to be
displayed, and the buffer which is the value of fi:clman-displaying-buffer.
If you wish, you can set this variable to your own displaying function.
fi:clman-displaying-buffer..........................................[variable]
Value: "*CLMan*"
*Either nil or a string naming the buffer that the system will use for
displaying documentation pages. If nil, then the system will not try to
reuse the same buffer.
5.3 Functions
fi:clman............................................................[function]
Invoke with: m in CLMAN mode.
Look up SYMBOL in the online manual, with completion. The optional
argument SYMBOL is prompted for in the minibuffer, if not supplied. As a
guess, the system uses the symbol immediately to the left of the text
cursor. To get completion for a symbol in a package other than the :lisp
package, use the nickname of that package, followed by a colon (e.g. cw: or
math:). The buffer that is displayed will be in CLMAN mode.
fi:clman-apropos....................................................[function]
Invoke with: a in CLMAN mode.
Prompts for a string on which an apropos search is done. Displays a
buffer which lists all documented symbols which match the string. The
buffer will be in CLMAN mode.
fi:clman-search-forward-see-alsos...................................[function]
Invoke with: s in CLMAN mode.
Move text cursor directly to the beginnig of the SEE ALSO section of a
clman buffer, from anywhere in the buffer.
fi:clman-flush-doc..................................................[function]
Invoke with: C-c C-c in CLMAN mode.
Flush the current clman buffer.
Appendix A
Sample .emacs
;; -[Fri Sep 14 00:17:21 1990 by layer]-
;; Sample .emacs file
;;
;; $Header: dot.emacs,v 1.4 90/10/09 10:48:25 layer Exp $
(load "fi/site-init.el")
;; the following causes fi:common-lisp to give the inferior Common
;; Lisp, by default, a command line argument of `+ipc-unix' or `+ipc-internet':
;;
(setq fi:common-lisp-image-arguments
(if fi:unix-domain '("+ipc-unix") '("+ipc-internet")))
;; turn on superkeys in subprocess modes
;;
(setq-default fi:subprocess-enable-superkeys t)
;; the following causes fi:common-lisp to invoke the image `acl', but
;; to ask for an image name when given a prefix argument:
;;
(setq fi:common-lisp-image-name
'(lambda ()
(let ((image "acl"))
(if current-prefix-arg
(setq image
(read-file-name (format "cl image (default: %s): " image)
default-directory image nil)))
(setq mode-line-buffer-identification
(format "%s (%s)" (buffer-name) (file-name-nondirectory image)))
image)))
;; This redefines `kill-emacs' so that transaction files in /tmp are
;; removed emacs is killed:
;;
(fset 'old-kill-emacs (symbol-function 'kill-emacs))
(defun kill-emacs (&optional arg)
(interactive "P")
(fi:remove-all-temporary-lisp-transaction-files)
(old-kill-emacs arg))
;; Some fancy things you can do in your subprocess mode hook
;;
(setq fi:subprocess-mode-hook
'(lambda ()
(interactive)
(setq mode-line-process '(": %s"))
(define-key (current-local-map) "\C-i" 'fi:shell-do-completion)
(define-key (current-local-map) "\C-z" nil)
(setq mode-line-buffer-identification
(list (buffer-name)
(let* ((proc (get-buffer-process (current-buffer)))
(name (and proc (calculate-process-name proc))))
(if name
(format " (%s)" (file-name-nondirectory name))))
'package-mode-info))
(setq fi:shell-popd-regexp (default-value 'fi:shell-popd-regexp))
(setq fi:shell-pushd-regexp (default-value 'fi:shell-pushd-regexp))
(setq fi:shell-cd-regexp (default-value 'fi:shell-cd-regexp))))
(defun calculate-process-name (process)
(let ((arglist (process-command process))
(res nil)
el)
(cond ((or (string= "env" (car arglist))
(string= (concat exec-directory "env") (car arglist)))
(while (and (not res) (setq arglist (cdr arglist)))
(setq el (car arglist))
(cond
((or (string= "-s" el) (string= "-set" el))
(setq arglist (cdr (cdr arglist))))
((or (string= "-u" el) (string= "-unset" el))
(setq arglist (cdr arglist)))
((string-match "=" el))
((or (string= "-" el) (string= "--" el)))
(t
(if (not
(or (string= "env" el)
(string= (concat exec-directory "env") el)))
(if (string= "rsh" el)
(setq res (car (cdr arglist)))
(setq res el))))))
res)
(t (car arglist)))))
Appendix B
Sample .clinit.cl
;; -[Sun Sep 2 13:33:40 1990 by layer]-
;; Sample .clinit.cl file
;;
;; $Header: dot.clinit,v 1.4 90/09/02 18:33:27 layer Exp $
(format t "; Loading home clinit...~%")
(cond
((find "+ipc-unix" (system:command-line-arguments) :test #'string=)
(require :ipc)
(require :emacs)
(let ((ipc-package (find-package :ipc)))
(set (find-symbol (symbol-name :*unix-domain*) ipc-package) t)
(funcall (find-symbol (symbol-name :start-lisp-listener-daemon)
ipc-package))))
((find "+ipc-internet" (system:command-line-arguments) :test #'string=)
(require :ipc)
(require :emacs)
(funcall (find-symbol (symbol-name :start-lisp-listener-daemon)
(find-package :ipc)))))
;; The following emulates the C shell cd, pushd, popd, pwd, and dirs,
;; and allows Emacs to track directory changes:
(defparameter *directory-stack*
(list (namestring
(setq *default-pathname-defaults* (current-directory)))))
(tpl:alias ("pushd" :string) (&optional dir)
(if* (string= "" dir)
then (let ((old-top (pop *directory-stack*))
(new-top (pop *directory-stack*)))
(push old-top *directory-stack*)
(push (chdir new-top) *directory-stack*))
else (push (chdir dir) *directory-stack*))
(format t "~a~%" *directory-stack*))
(tpl:alias "popd" ()
(if (> (length *directory-stack*) 1)
(pop *directory-stack*)
(format t "nothing to pop into~%"))
(chdir (car *directory-stack*))
(format t "~a~%" *directory-stack*))
(tpl:alias "dirs" ()
(format t "~a~%" *directory-stack*))
(tpl:alias ("cd" :string) (dir)
(setf (car *directory-stack*)
(apply #'chdir
(if (string= "" dir) nil (list dir))))
(format t "~a~%" *directory-stack*))
(tpl:alias "pwd" ()
(format t "process cwd = ~a~%*default-pathname-defaults* = ~a~%"
(namestring (current-directory))
(namestring (truename *default-pathname-defaults*))))
Appendix C
Sample X11 interaction
;; -[Tue Nov 20 08:04:42 1990 by layer]-
;; Sample X11 bindings for .emacs file
;;
;; $Header: dot.emacsX11,v 1.2 90/11/29 14:12:52 layer Exp $
(require 'x-mouse)
(defun x-lisp-find-tag (arg)
(x-mouse-set-point arg)
(cond ((eq major-mode 'fi:common-lisp-mode)
(fi:lisp-find-tag (find-tag-default)))
(t (find-tag-other-window (find-tag-default)))))
(defun x-lisp-eval-defun (arg)
(x-mouse-set-point arg)
(cond ((memq major-mode '(fi:common-lisp-mode fi:franz-lisp-mode
fi:lisp-mode))
(fi:lisp-eval-defun nil))
((eq major-mode 'fi:emacs-lisp-mode) (eval-defun))))
(defun x-lisp-arglist (arg)
(x-mouse-set-point arg)
(cond ((eq major-mode 'fi:common-lisp-mode) (fi:lisp-arglist))
(t (describe-function (intern (find-tag-default))))))
(define-key mouse-map x-button-left-up 'x-lisp-find-tag)
(define-key mouse-map x-button-middle-up 'x-lisp-eval-defun)
(define-key mouse-map x-button-right-up 'x-lisp-arglist)
;; we ignore mouse button-down events, because if we put something
;; on it the `up' event will cause the minibuffer output to disappear
(define-key mouse-map x-button-left 'x-mouse-ignore)
(define-key mouse-map x-button-middle 'x-mouse-ignore)
(define-key mouse-map x-button-right 'x-mouse-ignore)
(define-key mouse-map x-button-c-left 'x-mouse-ignore)
Appendix D
Franz Lisp Support
D.1 Introduction
Franz Lisp is handled very similar to Common Lisp, the only
difference being that Franz Lisp does not support TCP/IP socket
communication. In all other ways, the features, function and variable
names mirror those of the corresponding Common Lisp functions and
variables.
D.2 Editing Franz Lisp
As with Common Lisp, editing a Franz Lisp source file (.l
extension) causes fi:franz-lisp-mode to be invoked. The order of
setup in the mode follows that of fi:common-lisp-mode.
Package system set-up also occurs in the same way as in
fi:common-lisp-mode.
Franz Lisp does not support the commands depending on TCP/IP.
D.2.1 Bindings
fi:franz-lisp-mode-map................................................[keymap]
Major mode map used when editing Franz Lisp source.
RET fi:lisp-mode-newline
DEL backward-delete-char-untabify
TAB fi:lisp-indent-line
C-c Prefix Command
C-x Prefix Command
ESC Prefix Command
C-c C-r fi:lisp-eval-region
C-c C-s fi:lisp-eval-last-sexp
C-c C-b fi:lisp-eval-current-buffer
C-c - fi:log-functional-change
ESC C-x fi:lisp-eval-defun
ESC C-q fi:indent-sexp
D.2.2 Variables
The buffer-local variable fi:package is set up as in
fi:common-lisp-mode.
D.2.3 Functions
The functions in the keymaps are a subset of those in Common Lisp
mode--refer to the main document for a description of those commands.
D.2.4 Hooks
The hooks run are fi:lisp-mode-hook and fi:franz-lisp-mode-hook,
and are done last in the mode initialization.
D.3 Running Franz Lisp
As with Common Lisp, there are two entry points for running an
inferior Franz Lisp. `M-x fi:franz-lisp' and `M-x fi:explicit-franz-lisp'
create Franz Lisp processes. The default name of the Franz Lisp image
is `lisp'.
D.3.1 Bindings
fi:inferior-franz-lisp-mode-map.......................................[keymap]
The inferior-franz-lisp major-mode keymap.
DEL backward-delete-char-untabify
TAB fi:lisp-indent-line
C-x Prefix Command
ESC Prefix Command
C-c Prefix Command
RET fi:inferior-lisp-newline
C-c C-\ fi:subprocess-quit
C-c C-d fi:subprocess-send-eof
C-c C-c fi:subprocess-interrupt
C-c . fi:lisp-sync-current-working-directory
C-c C-w fi:subprocess-backward-kill-word
C-c C-v fi:subprocess-show-output
C-c C-u fi:subprocess-kill-input
C-c C-s fi:re-search-forward-input
C-c C-r fi:re-search-backward-input
C-c C-p fi:pop-input
C-c C-o fi:subprocess-send-flush
C-c C-n fi:push-input
C-c RET fi:subprocess-input-region
C-c C-l fi:list-input-ring
C-c C-k fi:subprocess-kill-output
C-c C-a fi:subprocess-beginning-of-line
C-x RET fi:inferior-lisp-input-list
ESC RET fi:inferior-lisp-input-sexp
ESC C-q fi:indent-sexp
D.3.2 Variables
fi:franz-lisp-image-name............................................[variable]
Value: "lisp"
*Default Franz Lisp image to invoke from `fi:franz-lisp'. If the value
is a string then it names the image file or image path that
`fi:common-lisp' invokes. Otherwise, the value of this variable is given
to funcall, the result of which should yield a string which is the image
name or path.
fi:franz-lisp-image-arguments.......................................[variable]
Value: nil
*Default Franz Lisp image arguments when invoked from `fi:franz-lisp'.
fi:franz-lisp-prompt-pattern........................................[variable]
Value: "^[-=]> +\\|^c{[0-9]+} +"
*The regular expression which matches the Franz Lisp prompt, used in
Inferior Franz Lisp mode. Anything from beginning of line up to the end
of what this pattern matches is deemed to be a prompt.
D.3.3 Functions
fi:franz-lisp.......................................................[function]
Start a Franz Lisp subprocess in a buffer whose name is determined
from the optional prefix argument BUFFER-NUMBER. Franz Lisp buffer names
start with `*franz-lisp' and end with `*', with an optional `-N' in
between. If BUFFER-NUMBER is not given it defaults to 1. If BUFFER-NUMBER
is >= 0, then the buffer is named `*franz-lisp*<BUFFER-NUMBER>'. If
BUFFER-NUMBER is < 0, then the first available buffer name is chosen.
The image file and image arguments are taken from the variables
`fi:franz-lisp-image-name' and `fi:franz-lisp-image-arguments'.
See fi:explicit-franz-lisp.
fi:explicit-franz-lisp..............................................[function]
The same as fi:franz-lisp, except that the image and image arguments
are read from the minibuffer.
D.3.3 Hooks
The following hooks are run when starting up an inferior Franz
Lisp:
fi:lisp-mode-hook
fi:subprocess-mode-hook
fi:inferior-franz-lisp-mode-hook
============================================================
If you have any further questions, please don't
hesitate to contact us again.
/jarl
___________________________________________________
Jarl Atle Nilsson (home) (415) 524-8110
Franz inc. (work) (415) 548-3600
1995 University ave INTERNET: jarl@franz.com
Berkeley, CA 94704 UUCP: uunet!franz!jarl
Technical Details
Message-ID:
<9104172347.AA01268@ox.Franz.COM>Path:
arc1-ca.pugleaf.net!gmdzi!unido!unidui!math.fu-berlin.de!fauern!ira.uka.de!sol.ctr.columbia.edu!caen!sdd.hp.com!decwrl!ucbvax!tut.cis.ohio-state.edu!unreplyable!garbageShow Full Headers
Complete Article Headers
X-Google-Language: ENGLISH,ASCII-7-bit X-Google-Thread: fc7ee,dc9b54c6f21c8885,start X-Google-Attributes: gidfc7ee,public X-Google-ArrivalTime: 1991-04-18 01:35:00 PST Path: arc1-ca.pugleaf.net!gmdzi!unido!unidui!math.fu-berlin.de!fauern!ira.uka.de!sol.ctr.columbia.edu!caen!sdd.hp.com!decwrl!ucbvax!tut.cis.ohio-state.edu!unreplyable!garbage From: jarl@franz.com (Jarl Nilsson) Newsgroups: gnu.epoch.misc Subject: Re: [spr3043] epoch and lisp/emacs interface Message-ID: <9104172347.AA01268@ox.Franz.COM> Date: 17 Apr 91 23:47:07 GMT Sender: daemon@tut.cis.ohio-state.edu Distribution: gnu Organization: GNUs Not Usenet Lines: 3888