# HG changeset patch # User Jim Blandy # Date 675040733 0 # Node ID 03e467a3d92a4a325d94f6ca398cce6d1c801516 # Parent 78fbe3043a5da6ae097829c98f69f20a7c50958f Initial revision diff -r 78fbe3043a5d -r 03e467a3d92a src/emacs.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/emacs.c Thu May 23 23:18:53 1991 +0000 @@ -0,0 +1,751 @@ +/* Fully extensible Emacs, running on Unix, intended for GNU. + Copyright (C) 1985, 1986, 1987 Free Software Foundation, Inc. + +This file is part of GNU Emacs. + +GNU Emacs is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 1, or (at your option) +any later version. + +GNU Emacs is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Emacs; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + + +#include +#include + +#include "config.h" +#include + +#include +#include + +#ifdef VMS +#include +#endif + +#ifdef USG5 +#include +#endif + +#ifdef BSD +#include +#endif + +#ifdef APOLLO +#ifndef APOLLO_SR10 +#include +#endif +#endif + +#undef NULL +#include "lisp.h" +#include "commands.h" + +#ifndef O_RDWR +#define O_RDWR 2 +#endif + +#define PRIO_PROCESS 0 + +/* Command line args from shell, as list of strings */ +Lisp_Object Vcommand_line_args; + +/* Set nonzero after Emacs has started up the first time. + Prevents reinitialization of the Lisp world and keymaps + on subsequent starts. */ +int initialized; + +/* Variable whose value is symbol giving operating system type */ +Lisp_Object Vsystem_type; + +/* If non-zero, emacs should not attempt to use an window-specific code, + but instead should use the virtual terminal under which it was started */ +int inhibit_window_system; + +#ifdef HAVE_X_WINDOWS +/* If non-zero, -d was specified, meaning we're using some window system. */ +int display_arg; +#endif + +/* An address near the bottom of the stack. + Tells GC how to save a copy of the stack. */ +char *stack_bottom; + +#ifdef HAVE_X_WINDOWS +extern Lisp_Object Vwindow_system; +#endif /* HAVE_X_WINDOWS */ + +#ifdef USG_SHARED_LIBRARIES +/* If nonzero, this is the place to put the end of the writable segment + at startup. */ + +unsigned int bss_end = 0; +#endif + +/* Nonzero means running Emacs without interactive terminal. */ + +int noninteractive; + +/* Value of Lisp variable `noninteractive'. + Normally same as C variable `noninteractive' + but nothing terrible happens if user sets this one. */ + +int noninteractive1; + +/* Signal code for the fatal signal that was received */ +int fatal_error_code; + +/* Nonzero if handling a fatal error already */ +int fatal_error_in_progress; + +/* Handle bus errors, illegal instruction, etc. */ +fatal_error_signal (sig) + int sig; +{ +#ifdef BSD + int tpgrp; +#endif /* BSD */ + + fatal_error_code = sig; + signal (sig, SIG_DFL); + + /* If fatal error occurs in code below, avoid infinite recursion. */ + if (fatal_error_in_progress) + kill (getpid (), fatal_error_code); + + fatal_error_in_progress = 1; + + /* If we are controlling the terminal, reset terminal modes */ +#ifdef BSD + if (ioctl(0, TIOCGPGRP, &tpgrp) == 0 + && tpgrp == getpgrp (0)) +#endif /* BSD */ + { + reset_sys_modes (); + if (sig != SIGTERM) + fprintf (stderr, "Fatal error (%d).", sig); + } + + /* Clean up */ +#ifdef subprocesses + kill_buffer_processes (Qnil); +#endif + Fdo_auto_save (Qt, Qnil); + +#ifdef CLASH_DETECTION + unlock_all_files (); +#endif /* CLASH_DETECTION */ + +#ifdef VMS + kill_vms_processes (); + LIB$STOP (SS$_ABORT); +#else + /* Signal the same code; this time it will really be fatal. */ + kill (getpid (), fatal_error_code); +#endif /* not VMS */ +} + +/* Code for dealing with Lisp access to the Unix command line */ + +static +init_cmdargs (argc, argv, skip_args) + int argc; + char **argv; + int skip_args; +{ + register int i; + + Vcommand_line_args = Qnil; + + for (i = argc - 1; i >= 0; i--) + { + if (i == 0 || i > skip_args) + Vcommand_line_args + = Fcons (build_string (argv[i]), Vcommand_line_args); + } +} + +#ifdef VMS +#ifdef LINK_CRTL_SHARE +#ifdef SHAREABLE_LIB_BUG +extern noshare char **environ; +#endif /* SHAREABLE_LIB_BUG */ +#endif /* LINK_CRTL_SHARE */ +#endif /* VMS */ + +/* ARGSUSED */ +main (argc, argv, envp) + int argc; + char **argv; + char **envp; +{ + char stack_bottom_variable; + int skip_args = 0; + extern int errno; + extern sys_nerr; + extern char *sys_errlist[]; + extern void malloc_warning (); + +/* Map in shared memory, if we are using that. */ +#ifdef HAVE_SHM + if (argc > 1 && !strcmp (argv[1], "-nl")) + { + map_in_data (0); + /* The shared memory was just restored, which clobbered this. */ + skip_args = 1; + } + else + { + map_in_data (1); + /* The shared memory was just restored, which clobbered this. */ + skip_args = 0; + } +#endif + +#ifdef HAVE_X_WINDOWS + /* Stupid kludge to catch command-line display spec. ask jla */ + { + int i; + + for (i = 1; (i < argc && ! display_arg); i++) + if (!strcmp (argv[i], "-d")) + display_arg = 1; + } +#endif + +#ifdef VMS + /* If -map specified, map the data file in */ + if (argc > 2 && ! strcmp (argv[1], "-map")) + { + skip_args = 2; + mapin_data (argv[2]); + } + +#ifdef LINK_CRTL_SHARE +#ifdef SHAREABLE_LIB_BUG + /* Bletcherous shared libraries! */ + if (!stdin) + stdin = fdopen (0, "r"); + if (!stdout) + stdout = fdopen (1, "w"); + if (!stderr) + stderr = fdopen (2, "w"); + if (!environ) + environ = envp; +#endif /* SHAREABLE_LIB_BUG */ +#endif /* LINK_CRTL_SHARE */ +#endif /* VMS */ + + /* Record (approximately) where the stack begins. */ + stack_bottom = &stack_bottom_variable; + +#ifdef RUN_TIME_REMAP + if (initialized) + run_time_remap (argv[0]); +#endif + +#ifdef USG_SHARED_LIBRARIES + if (bss_end) + brk (bss_end); +#endif + + clearerr (stdin); +#ifdef BSD + setpgrp (0, getpid ()); +#endif + +#ifdef APOLLO +#ifndef APOLLO_SR10 + /* If USE_DOMAIN_ACLS environment variable exists, + use ACLs rather than UNIX modes. */ + if (egetenv ("USE_DOMAIN_ACLS")) + default_acl (USE_DEFACL); +#endif +#endif /* APOLLO */ + +#ifndef SYSTEM_MALLOC + if (! initialized) + malloc_init (0, malloc_warning); +#endif /* not SYSTEM_MALLOC */ + +#ifdef HIGHPRI + setpriority (PRIO_PROCESS, getpid (), HIGHPRI); + setuid (getuid ()); +#endif /* HIGHPRI */ + +#ifdef BSD + /* interrupt_input has trouble if we aren't in a separate process group. */ + setpgrp (getpid (), getpid ()); +#endif + + inhibit_window_system = 0; + +/* Handle the -t switch, which specifies filename to use as terminal */ + if (skip_args + 2 < argc && !strcmp (argv[skip_args + 1], "-t")) + { + int result; + skip_args += 2; + close (0); + close (1); + result = open (argv[skip_args], O_RDWR, 2 ); + if (result < 0) + { + char *errstring; + + if (errno >= 0 && errno < sys_nerr) + errstring = sys_errlist[errno]; + else + errstring = "undocumented error code"; + fprintf (stderr, "emacs: %s: %s\n", argv[skip_args], errstring); + exit (1); + } + dup (0); + if (! isatty (0)) + { + fprintf (stderr, "emacs: %s: not a tty\n", argv[skip_args]); + exit (1); + } + fprintf (stderr, "Using %s\n", argv[skip_args]); +#ifdef HAVE_X_WINDOWS + inhibit_window_system = 1; /* -t => -nw */ +#endif + } + + if (skip_args + 1 < argc + && (!strcmp (argv[skip_args + 1], "-nw"))) + { + skip_args += 1; + inhibit_window_system = 1; + } + +/* Handle the -batch switch, which means don't do interactive display. */ + noninteractive = 0; + if (skip_args + 1 < argc && !strcmp (argv[skip_args + 1], "-batch")) + { + skip_args += 1; + noninteractive = 1; + } + + if ( +#ifndef CANNOT_DUMP + ! noninteractive || initialized +#else + 1 +#endif + ) + { + /* Don't catch these signals in batch mode if not initialized. + On some machines, this sets static data that would make + signal fail to work right when the dumped Emacs is run. */ + signal (SIGHUP, fatal_error_signal); + signal (SIGQUIT, fatal_error_signal); + signal (SIGILL, fatal_error_signal); + signal (SIGTRAP, fatal_error_signal); + signal (SIGIOT, fatal_error_signal); +#ifdef SIGEMT + signal (SIGEMT, fatal_error_signal); +#endif + signal (SIGFPE, fatal_error_signal); + signal (SIGBUS, fatal_error_signal); + signal (SIGSEGV, fatal_error_signal); + signal (SIGSYS, fatal_error_signal); + signal (SIGTERM, fatal_error_signal); +#ifdef SIGXCPU + signal (SIGXCPU, fatal_error_signal); +#endif +#ifdef SIGXFSZ + signal (SIGXFSZ, fatal_error_signal); +#endif /* SIGXFSZ */ + +#ifdef AIX + signal (SIGDANGER, fatal_error_signal); + signal (20, fatal_error_signal); + signal (21, fatal_error_signal); + signal (22, fatal_error_signal); + signal (23, fatal_error_signal); + signal (24, fatal_error_signal); + signal (SIGAIO, fatal_error_signal); + signal (SIGPTY, fatal_error_signal); + signal (SIGIOINT, fatal_error_signal); + signal (SIGGRANT, fatal_error_signal); + signal (SIGRETRACT, fatal_error_signal); + signal (SIGSOUND, fatal_error_signal); + signal (SIGMSG, fatal_error_signal); +#endif /* AIX */ + } + + noninteractive1 = noninteractive; + +/* Perform basic initializations (not merely interning symbols) */ + + if (!initialized) + { + init_alloc_once (); + init_obarray (); + init_eval_once (); + init_syntax_once (); /* Create standard syntax table. */ + /* Must be done before init_buffer */ + init_casetab_once (); + init_buffer_once (); /* Create buffer table and some buffers */ + init_minibuf_once (); /* Create list of minibuffers */ + /* Must precede init_window_once */ + init_window_once (); /* Init the window system */ + } + + init_alloc (); +#ifdef MAINTAIN_ENVIRONMENT + init_environ (); +#endif + init_eval (); + init_data (); + init_read (); + + init_cmdargs (argc, argv, skip_args); /* Create list Vcommand_line_args */ + init_buffer (); /* Init default directory of main buffer */ + if (!noninteractive) + { +#ifdef VMS + init_vms_input ();/* init_display calls get_screen_size, that needs this */ +#endif /* VMS */ + init_display (); /* Determine terminal type. init_sys_modes uses results */ + } + init_keyboard (); /* This too must precede init_sys_modes */ + init_callproc (); /* And this too. */ +#ifdef VMS + init_vmsproc (); /* And this too. */ +#endif /* VMS */ + init_sys_modes (); /* Init system terminal modes (RAW or CBREAK, etc.) */ + init_xdisp (); + init_macros (); + init_editfns (); +#ifdef LISP_FLOAT_TYPE + init_floatfns (); +#endif +#ifdef VMS + init_vmsfns (); +#endif /* VMS */ +#ifdef subprocesses + init_process (); +#endif /* subprocesses */ + +/* Intern the names of all standard functions and variables; define standard keys */ + + if (!initialized) + { + /* The basic levels of Lisp must come first */ + /* And data must come first of all + for the sake of symbols like error-message */ + syms_of_data (); + syms_of_alloc (); +#ifdef MAINTAIN_ENVIRONMENT + syms_of_environ (); +#endif /* MAINTAIN_ENVIRONMENT */ + syms_of_read (); + syms_of_print (); + syms_of_eval (); + syms_of_fns (); +#ifdef LISP_FLOAT_TYPE + syms_of_floatfns (); +#endif + + syms_of_abbrev (); + syms_of_buffer (); + syms_of_bytecode (); + syms_of_callint (); + syms_of_casefiddle (); + syms_of_casetab (); + syms_of_callproc (); + syms_of_cmds (); +#ifndef NO_DIR_LIBRARY + syms_of_dired (); +#endif /* not NO_DIR_LIBRARY */ + syms_of_display (); + syms_of_doc (); + syms_of_editfns (); + syms_of_emacs (); + syms_of_fileio (); +#ifdef CLASH_DETECTION + syms_of_filelock (); +#endif /* CLASH_DETECTION */ + syms_of_indent (); + syms_of_keyboard (); + syms_of_keymap (); + syms_of_macros (); + syms_of_marker (); + syms_of_minibuf (); + syms_of_mocklisp (); +#ifdef subprocesses + syms_of_process (); +#endif /* subprocesses */ + syms_of_search (); +#ifdef MULTI_SCREEN + syms_of_screen (); +#endif + syms_of_syntax (); + syms_of_undo (); +#ifdef VMS + syms_of_vmsproc (); +#endif /* VMS */ + syms_of_window (); + syms_of_xdisp (); +#ifdef HAVE_X_WINDOWS + syms_of_xfns (); +#ifdef HAVE_X_MENU + syms_of_xmenu (); +#endif /* HAVE_X_MENU */ +#endif /* HAVE_X_WINDOWS */ + +#ifdef SYMS_SYSTEM + SYMS_SYSTEM; +#endif + +#ifdef SYMS_MACHINE + SYMS_MACHINE; +#endif + + keys_of_casefiddle (); + keys_of_cmds (); + keys_of_buffer (); + keys_of_keyboard (); + keys_of_keymap (); + keys_of_macros (); + keys_of_minibuf (); + keys_of_window (); + } + + if (!initialized) + { + /* Handle -l loadup-and-dump, args passed by Makefile. */ + if (argc > 2 + skip_args && !strcmp (argv[1 + skip_args], "-l")) + Vtop_level = Fcons (intern ("load"), + Fcons (build_string (argv[2 + skip_args]), Qnil)); +#ifdef CANNOT_DUMP + /* Unless next switch is -nl, load "loadup.el" first thing. */ + if (!(argc > 1 + skip_args && !strcmp (argv[1 + skip_args], "-nl"))) + Vtop_level = Fcons (intern ("load"), + Fcons (build_string ("loadup.el"), Qnil)); +#endif /* CANNOT_DUMP */ + } + + initialized = 1; + + /* Enter editor command loop. This never returns. */ + Frecursive_edit (); + /* NOTREACHED */ +} + +DEFUN ("kill-emacs", Fkill_emacs, Skill_emacs, 0, 1, "P", + "Exit the Emacs job and kill it. Ask for confirmation, without argument.\n\ +If ARG is an integer, return ARG as the exit program code.\n\ +If ARG is a string, stuff it as keyboard input.\n\n\ +The value of `kill-emacs-hook', if not void,\n\ +is a list of functions (of no args),\n\ +all of which are called before Emacs is actually killed.") + (arg) + Lisp_Object arg; +{ + Lisp_Object hook, hook1; + int i; + struct gcpro gcpro1; + + GCPRO1 (arg); + + if (feof (stdin)) + arg = Qt; + + if (!NULL (Vrun_hooks) && !noninteractive) + call1 (Vrun_hooks, intern ("kill-emacs-hook")); + +#ifdef subprocesses + kill_buffer_processes (Qnil); +#endif /* subprocesses */ + +#ifdef VMS + kill_vms_processes (); +#endif /* VMS */ + + Fdo_auto_save (Qt, Qnil); + +#ifdef CLASH_DETECTION + unlock_all_files (); +#endif /* CLASH_DETECTION */ + + fflush (stdout); + reset_sys_modes (); + +#ifdef HAVE_X_WINDOWS + if (!noninteractive && EQ (Vwindow_system, intern ("x"))) + Fx_close_current_connection (); +#endif /* HAVE_X_WINDOWS */ + + UNGCPRO; + +/* Is it really necessary to do this deassign + when we are going to exit anyway? */ +/* #ifdef VMS + stop_vms_input (); + #endif */ + stuff_buffered_input (arg); +#ifdef SIGIO + /* There is a tendency for a SIGIO signal to arrive within exit, + and cause a SIGHUP because the input descriptor is already closed. */ + unrequest_sigio (); + signal (SIGIO, SIG_IGN); +#endif + exit ((XTYPE (arg) == Lisp_Int) ? XINT (arg) +#ifdef VMS + : 1 +#else + : 0 +#endif + ); + /* NOTREACHED */ +} + +#ifndef CANNOT_DUMP +/* Nothing like this can be implemented on an Apollo. + What a loss! */ + +#ifdef HAVE_SHM + +DEFUN ("dump-emacs-data", Fdump_emacs_data, Sdump_emacs_data, 1, 1, 0, + "Dump current state of Emacs into data file FILENAME.\n\ +This function exists on systems that use HAVE_SHM.") + (intoname) + Lisp_Object intoname; +{ + extern int my_edata; + Lisp_Object tem; + extern void malloc_warning (); + + CHECK_STRING (intoname, 0); + intoname = Fexpand_file_name (intoname, Qnil); + + tem = Vpurify_flag; + Vpurify_flag = Qnil; + + fflush (stdout); + /* Tell malloc where start of impure now is */ + /* Also arrange for warnings when nearly out of space. */ +#ifndef SYSTEM_MALLOC + malloc_init (&my_edata, malloc_warning); +#endif + map_out_data (XSTRING (intoname)->data); + + Vpurify_flag = tem; + + return Qnil; +} + +#else /* not HAVE_SHM */ + +DEFUN ("dump-emacs", Fdump_emacs, Sdump_emacs, 2, 2, 0, + "Dump current state of Emacs into executable file FILENAME.\n\ +Take symbols from SYMFILE (presumably the file you executed to run Emacs).\n\ +This is used in the file `loadup.el' when building Emacs.\n\ +\n\ +Bind `command-line-processed' to nil before dumping,\n\ +if you want the dumped Emacs to process its command line\n\ +and announce itself normally when it is run.") + (intoname, symname) + Lisp_Object intoname, symname; +{ + extern int my_edata; + Lisp_Object tem; + extern void malloc_warning (); + + CHECK_STRING (intoname, 0); + intoname = Fexpand_file_name (intoname, Qnil); + if (!NULL (symname)) + { + CHECK_STRING (symname, 0); + if (XSTRING (symname)->size) + symname = Fexpand_file_name (symname, Qnil); + } + + tem = Vpurify_flag; + Vpurify_flag = Qnil; + + fflush (stdout); +#ifdef VMS + mapout_data (XSTRING (intoname)->data); +#else + /* Tell malloc where start of impure now is */ + /* Also arrange for warnings when nearly out of space. */ +#ifndef SYSTEM_MALLOC + malloc_init (&my_edata, malloc_warning); +#endif + unexec (XSTRING (intoname)->data, + !NULL (symname) ? XSTRING (symname)->data : 0, &my_edata, 0, 0); +#endif /* not VMS */ + + Vpurify_flag = tem; + + return Qnil; +} + +#endif /* not HAVE_SHM */ + +#endif /* not CANNOT_DUMP */ + +#ifdef VMS +#define SEPCHAR ',' +#else +#define SEPCHAR ':' +#endif + +Lisp_Object +decode_env_path (evarname, defalt) + char *evarname, *defalt; +{ + register char *path, *p; + extern char *index (); + + Lisp_Object lpath; + + path = (char *) egetenv (evarname); + if (!path) + path = defalt; + lpath = Qnil; + while (1) + { + p = index (path, SEPCHAR); + if (!p) p = path + strlen (path); + lpath = Fcons (p - path ? make_string (path, p - path) : Qnil, + lpath); + if (*p) + path = p + 1; + else + break; + } + return Fnreverse (lpath); +} + +syms_of_emacs () +{ +#ifdef HAVE_SHM + defsubr (&Sdump_emacs_data); +#else + defsubr (&Sdump_emacs); +#endif + + defsubr (&Skill_emacs); + + DEFVAR_LISP ("command-line-args", &Vcommand_line_args, + "Args passed by shell to Emacs, as a list of strings."); + + DEFVAR_LISP ("system-type", &Vsystem_type, + "Value is symbol indicating type of operating system you are using."); + Vsystem_type = intern (SYSTEM_TYPE); + + DEFVAR_BOOL ("noninteractive", &noninteractive1, + "Non-nil means Emacs is running without interactive terminal."); +} diff -r 78fbe3043a5d -r 03e467a3d92a src/minibuf.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/minibuf.c Thu May 23 23:18:53 1991 +0000 @@ -0,0 +1,1261 @@ +/* Minibuffer input and completion. + Copyright (C) 1985, 1986 Free Software Foundation, Inc. + +This file is part of GNU Emacs. + +GNU Emacs is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 1, or (at your option) +any later version. + +GNU Emacs is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Emacs; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + + +#include "config.h" +#include "lisp.h" +#include "commands.h" +#include "buffer.h" +#include "dispextern.h" +#include "screen.h" +#include "window.h" +#include "syntax.h" + +#define min(a, b) ((a) < (b) ? (a) : (b)) + +/* List of buffers for use as minibuffers. + The first element of the list is used for the outermost minibuffer invocation, + the next element is used for a recursive minibuffer invocation, etc. + The list is extended at the end as deeped minibuffer recursions are encountered. */ +Lisp_Object Vminibuffer_list; + +struct minibuf_save_data + { + char *prompt; + int prompt_width; + Lisp_Object help_form; + Lisp_Object current_prefix_arg; + }; + +int minibuf_save_vector_size; +struct minibuf_save_data *minibuf_save_vector; + +/* Depth in minibuffer invocations. */ +int minibuf_level; + +/* Nonzero means display completion help for invalid input */ +int auto_help; + +/* Fread_minibuffer leaves the input, as a string, here */ +Lisp_Object last_minibuf_string; + +/* Nonzero means let functions called when within a minibuffer + invoke recursive minibuffers (to read arguments, or whatever) */ +int enable_recursive_minibuffers; + +/* help-form is bound to this while in the minibuffer. */ + +Lisp_Object Vminibuffer_help_form; + +/* Nonzero means completion ignores case. */ + +int completion_ignore_case; + +/* If last completion attempt reported "Complete but not unique" + then this is the string completed then; otherwise this is nil. */ + +static Lisp_Object last_exact_completion; + +Lisp_Object Quser_variable_p; + +/* Width in columns of current minibuffer prompt. */ + +extern int minibuf_prompt_width; + +#ifdef MULTI_SCREEN + +/* When the global-minibuffer-screen is not used, this is the screen + where the minbuffer is active, and thus where certain windows + (completions, etc.) should appear. */ +struct screen *active_screen; + +extern Lisp_Object Vglobal_minibuffer_screen; +#endif + +/* Actual minibuffer invocation. */ + +void read_minibuf_unwind (); +Lisp_Object get_minibuffer (); +Lisp_Object read_minibuf (); + +Lisp_Object +read_minibuf (map, initial, prompt, backup_n, expflag) + Lisp_Object map; + Lisp_Object initial; + Lisp_Object prompt; + Lisp_Object backup_n; + int expflag; +{ + register Lisp_Object val; + int count = specpdl_ptr - specpdl; + struct gcpro gcpro1, gcpro2; + Lisp_Object prev_screen = Qnil; + + if (XTYPE (prompt) != Lisp_String) + prompt = build_string (""); + + /* Emacs in -batch mode calls minibuffer: print the prompt. */ + if (noninteractive && XTYPE (prompt) == Lisp_String) + printf ("%s", XSTRING (prompt)->data); + + if (!enable_recursive_minibuffers + && minibuf_level > 0 + && (EQ (selected_window, minibuf_window))) +#if 0 + || selected_screen != XSCREEN (WINDOW_SCREEN (XWINDOW (minibuf_window))) +#endif + error ("Command attempted to use minibuffer while in minibuffer"); + + if (minibuf_level == minibuf_save_vector_size) + minibuf_save_vector = + (struct minibuf_save_data *) + xrealloc (minibuf_save_vector, + (minibuf_save_vector_size *= 2) + * sizeof (struct minibuf_save_data)); + minibuf_save_vector[minibuf_level].prompt = minibuf_prompt; + minibuf_save_vector[minibuf_level].prompt_width = minibuf_prompt_width; + minibuf_prompt_width = 0; + /* >> Why is this done this way rather than binding these variables? */ + minibuf_save_vector[minibuf_level].help_form = Vhelp_form; + minibuf_save_vector[minibuf_level].current_prefix_arg = Vcurrent_prefix_arg; + GCPRO2 (minibuf_save_vector[minibuf_level].help_form, + minibuf_save_vector[minibuf_level].current_prefix_arg); + + record_unwind_protect (Fset_window_configuration, + Fcurrent_window_configuration ()); + + val = current_buffer->directory; + Fset_buffer (get_minibuffer (minibuf_level)); + current_buffer->directory = val; + Fmake_local_variable (Qprint_escape_newlines); + print_escape_newlines = 1; + + Vminibuf_scroll_window = selected_window; + Fset_window_buffer (minibuf_window, Fcurrent_buffer ()); +#ifdef MULTI_SCREEN + if (SCREENP (Vglobal_minibuffer_screen)) + active_screen = selected_screen; +#endif + Fselect_window (minibuf_window); + XFASTINT (XWINDOW (minibuf_window)->hscroll) = 0; + + Ferase_buffer (); + minibuf_level++; + record_unwind_protect (read_minibuf_unwind, Qnil); + + if (!NULL (initial)) + { + Finsert (1, &initial); + if (!NULL (backup_n) && XTYPE (backup_n) == Lisp_Int) + Fforward_char (backup_n); + } + + minibuf_prompt = (char *) alloca (XSTRING (prompt)->size + 1); + bcopy (XSTRING (prompt)->data, minibuf_prompt, XSTRING (prompt)->size + 1); + echo_area_glyphs = 0; + + Vhelp_form = Vminibuffer_help_form; + current_buffer->keymap = map; + +/* ??? MCC did redraw_screen here if switching screens. */ + recursive_edit_1 (); + + /* If cursor is on the minibuffer line, + show the user we have exited by putting it in column 0. */ + if ((SCREEN_CURSOR_Y (selected_screen) + >= XFASTINT (XWINDOW (minibuf_window)->top)) + && !noninteractive) + { + SCREEN_CURSOR_X (selected_screen) = 0; + update_screen (selected_screen, 1, 1); + } + + /* Make minibuffer contents into a string */ + val = make_string (BEG_ADDR, Z - BEG); + bcopy (GAP_END_ADDR, XSTRING (val)->data + GPT - BEG, Z - GPT); + unbind_to (count, Qnil); /* The appropriate screen will get selected + from set-window-configuration. */ + + UNGCPRO; + + /* VAL is the string of minibuffer text. */ + + last_minibuf_string = val; + + /* If Lisp form desired instead of string, parse it */ + if (expflag) + val = Fread (val); + +#ifdef MULTI_SCREEN + if (active_screen) + active_screen = (struct screen *) 0; +#endif + + return val; +} + +/* Return a buffer to be used as the minibuffer at depth `depth'. + depth = 0 is the lowest allowed argument, and that is the value + used for nonrecursive minibuffer invocations */ + +Lisp_Object +get_minibuffer (depth) + int depth; +{ + Lisp_Object tail, num, buf; + char name[14]; + extern Lisp_Object nconc2 (); + + XFASTINT (num) = depth; + tail = Fnthcdr (num, Vminibuffer_list); + if (NULL (tail)) + { + tail = Fcons (Qnil, Qnil); + Vminibuffer_list = nconc2 (Vminibuffer_list, tail); + } + buf = Fcar (tail); + if (NULL (buf) || NULL (XBUFFER (buf)->name)) + { + sprintf (name, " *Minibuf-%d*", depth); + buf = Fget_buffer_create (build_string (name)); + XCONS (tail)->car = buf; + } + else + reset_buffer (XBUFFER (buf)); + return buf; +} + +/* This function is called on exiting minibuffer, whether normally or not, + and it restores the current window, buffer, etc. */ + +void +read_minibuf_unwind () +{ + /* Erase the minibuffer we were using at this level. */ + Fset_buffer (XWINDOW (minibuf_window)->buffer); + + /* Prevent error in erase-buffer. */ + current_buffer->read_only = Qnil; + Ferase_buffer (); + + /* If this was a recursive minibuffer, + tie the minibuffer window back to the outer level minibuffer buffer */ + minibuf_level--; + /* Make sure minibuffer window is erased, not ignored */ + windows_or_buffers_changed++; + XFASTINT (XWINDOW (minibuf_window)->last_modified) = 0; + + /* Restore prompt from outer minibuffer */ + minibuf_prompt = minibuf_save_vector[minibuf_level].prompt; + minibuf_prompt_width = minibuf_save_vector[minibuf_level].prompt_width; + Vhelp_form = minibuf_save_vector[minibuf_level].help_form; + Vcurrent_prefix_arg = minibuf_save_vector[minibuf_level].current_prefix_arg; +} + +DEFUN ("read-from-minibuffer", Fread_from_minibuffer, Sread_from_minibuffer, 1, 5, 0, + "Read a string from the minibuffer, prompting with string PROMPT.\n\ +If optional second arg INITIAL-CONTENTS is non-nil, it is a string\n\ + to be inserted into the minibuffer before reading input.\n\ +Third arg KEYMAP is a keymap to use whilst reading;\n\ + if omitted or nil, the default is `minibuffer-local-map'.\n\ +If fourth arg READ is non-nil, then interpret the result as a lisp object\n\ + and return that object:\n\ + in other words, do `(car (read-from-string INPUT-STRING))'\n\ +Fifth arg POSITION, if non-nil, is where to put point\n\ + in the minibuffer after inserting INITIAL-CONTENTS.") + (prompt, initial_input, keymap, read, position) + Lisp_Object prompt, initial_input, keymap, read, position; +{ + int pos = 0; + + CHECK_STRING (prompt, 0); + if (!NULL (initial_input)) + { + CHECK_STRING (initial_input, 1); + if (!NULL (position)) + { + CHECK_NUMBER (position, 0); + /* Convert to distance from end of input. */ + pos = XINT (position) - 1 - XSTRING (initial_input)->size; + } + } + + if (NULL (keymap)) + keymap = Vminibuffer_local_map; + else + keymap = get_keymap (keymap,2); + return read_minibuf (keymap, initial_input, prompt, + pos, !NULL (read)); +} + +DEFUN ("read-minibuffer", Fread_minibuffer, Sread_minibuffer, 1, 2, 0, + "Return a Lisp object read using the minibuffer.\n\ +Prompt with PROMPT. If non-nil, optional second arg INITIAL-CONTENTS\n\ +is a string to insert in the minibuffer before reading.") + (prompt, initial_contents) + Lisp_Object prompt, initial_contents; +{ + CHECK_STRING (prompt, 0); + if (!NULL (initial_contents)) + CHECK_STRING (initial_contents, 1) + return read_minibuf (Vminibuffer_local_map, initial_contents, prompt, Qnil, 1); +} + +DEFUN ("eval-minibuffer", Feval_minibuffer, Seval_minibuffer, 1, 2, 0, + "Return value of Lisp expression read using the minibuffer.\n\ +Prompt with PROMPT. If non-nil, optional second arg INITIAL-CONTENTS\n\ +is a string to insert in the minibuffer before reading.") + (prompt, initial_contents) + Lisp_Object prompt, initial_contents; +{ + return Feval (Fread_minibuffer (prompt, initial_contents)); +} + +/* Functions that use the minibuffer to read various things. */ + +DEFUN ("read-string", Fread_string, Sread_string, 1, 2, 0, + "Read a string from the minibuffer, prompting with string PROMPT.\n\ +If non-nil second arg INITIAL-INPUT is a string to insert before reading.") + (prompt, initial_input) + Lisp_Object prompt, initial_input; +{ + return Fread_from_minibuffer (prompt, initial_input, Qnil, Qnil, Qnil); +} + +DEFUN ("read-no-blanks-input", Fread_no_blanks_input, Sread_no_blanks_input, 2, 1, 0, + "Args PROMPT and INIT, strings. Read a string from the terminal, not allowing blanks.\n\ +Prompt with PROMPT, and provide INIT as an initial value of the input string.") + (prompt, init) + Lisp_Object prompt, init; +{ + CHECK_STRING (prompt, 0); + if (! NULL (init)) + CHECK_STRING (init, 1); + + return read_minibuf (Vminibuffer_local_ns_map, init, prompt, Qnil, 0); +} + +DEFUN ("read-command", Fread_command, Sread_command, 1, 1, 0, + "One arg PROMPT, a string. Read the name of a command and return as a symbol.\n\ +Prompts with PROMPT.") + (prompt) + Lisp_Object prompt; +{ + return Fintern (Fcompleting_read (prompt, Vobarray, Qcommandp, Qt, Qnil, Qnil), + Qnil); +} + +#ifdef NOTDEF +DEFUN ("read-function", Fread_function, Sread_function, 1, 1, 0, + "One arg PROMPT, a string. Read the name of a function and return as a symbol.\n\ +Prompts with PROMPT.") + (prompt) + Lisp_Object prompt; +{ + return Fintern (Fcompleting_read (prompt, Vobarray, Qfboundp, Qt, Qnil, Qnil), + Qnil); +} +#endif /* NOTDEF */ + +DEFUN ("read-variable", Fread_variable, Sread_variable, 1, 1, 0, + "One arg PROMPT, a string. Read the name of a user variable and return\n\ +it as a symbol. Prompts with PROMPT.\n\ +A user variable is one whose documentation starts with a `*' character.") + (prompt) + Lisp_Object prompt; +{ + return Fintern (Fcompleting_read (prompt, Vobarray, + Quser_variable_p, Qt, Qnil, Qnil), + Qnil); +} + +DEFUN ("read-buffer", Fread_buffer, Sread_buffer, 1, 3, 0, + "One arg PROMPT, a string. Read the name of a buffer and return as a string.\n\ +Prompts with PROMPT.\n\ +Optional second arg is value to return if user enters an empty line.\n\ +If optional third arg REQUIRE-MATCH is non-nil, only existing buffer names are allowed.") + (prompt, def, require_match) + Lisp_Object prompt, def, require_match; +{ + Lisp_Object tem; + Lisp_Object args[3]; + struct gcpro gcpro1; + + if (XTYPE (def) == Lisp_Buffer) + def = XBUFFER (def)->name; + if (!NULL (def)) + { + args[0] = build_string ("%s(default %s) "); + args[1] = prompt; + args[2] = def; + prompt = Fformat (3, args); + } + GCPRO1 (def); + tem = Fcompleting_read (prompt, Vbuffer_alist, Qnil, require_match, Qnil, Qnil); + UNGCPRO; + if (XSTRING (tem)->size) + return tem; + return def; +} + +DEFUN ("try-completion", Ftry_completion, Stry_completion, 2, 3, 0, + "Return common substring of all completions of STRING in ALIST.\n\ +Each car of each element of ALIST is tested to see if it begins with STRING.\n\ +All that match are compared together; the longest initial sequence\n\ +common to all matches is returned as a string.\n\ +If there is no match at all, nil is returned.\n\ +For an exact match, t is returned.\n\ +\n\ +ALIST can be an obarray instead of an alist.\n\ +Then the print names of all symbols in the obarray are the possible matches.\n\ +\n\ +ALIST can also be a function to do the completion itself.\n\ +It receives three arguments: the values STRING, PREDICATE and nil.\n\ +Whatever it returns becomes the value of `try-completion'.\n\ +\n\ +If optional third argument PREDICATE is non-nil,\n\ +it is used to test each possible match.\n\ +The match is a candidate only if PREDICATE returns non-nil.\n\ +The argument given to PREDICATE is the alist element or the symbol from the obarray.") + (string, alist, pred) + Lisp_Object string, alist, pred; +{ + Lisp_Object bestmatch, tail, elt, eltstring; + int bestmatchsize; + int compare, matchsize; + int list = CONSP (alist) || NULL (alist); + int index, obsize; + int matchcount = 0; + Lisp_Object bucket, zero, end, tem; + struct gcpro gcpro1, gcpro2, gcpro3, gcpro4; + + CHECK_STRING (string, 0); + if (!list && XTYPE (alist) != Lisp_Vector) + return call3 (alist, string, pred, Qnil); + + bestmatch = Qnil; + + /* If ALIST is not a list, set TAIL just for gc pro. */ + tail = alist; + if (! list) + { + index = 0; + obsize = XVECTOR (alist)->size; + bucket = XVECTOR (alist)->contents[index]; + } + + while (1) + { + /* Get the next element of the alist or obarray. */ + /* Exit the loop if the elements are all used up. */ + /* elt gets the alist element or symbol. + eltstring gets the name to check as a completion. */ + + if (list) + { + if (NULL (tail)) + break; + elt = Fcar (tail); + eltstring = Fcar (elt); + tail = Fcdr (tail); + } + else + { + if (XFASTINT (bucket) != 0) + { + elt = bucket; + eltstring = Fsymbol_name (elt); + if (XSYMBOL (bucket)->next) + XSETSYMBOL (bucket, XSYMBOL (bucket)->next); + else + XFASTINT (bucket) = 0; + } + else if (++index >= obsize) + break; + else + { + bucket = XVECTOR (alist)->contents[index]; + continue; + } + } + + /* Is this element a possible completion? */ + + if (XTYPE (eltstring) == Lisp_String && + XSTRING (string)->size <= XSTRING (eltstring)->size && + 0 > scmp (XSTRING (eltstring)->data, XSTRING (string)->data, + XSTRING (string)->size)) + { + /* Yes. */ + /* Ignore this element if there is a predicate + and the predicate doesn't like it. */ + + if (!NULL (pred)) + { + if (EQ (pred, Qcommandp)) + tem = Fcommandp (elt); + else + { + GCPRO4 (tail, string, eltstring, bestmatch); + tem = call1 (pred, elt); + UNGCPRO; + } + if (NULL (tem)) continue; + } + + /* Update computation of how much all possible completions match */ + + matchcount++; + if (NULL (bestmatch)) + bestmatch = eltstring, bestmatchsize = XSTRING (eltstring)->size; + else + { + compare = min (bestmatchsize, XSTRING (eltstring)->size); + matchsize = scmp (XSTRING (bestmatch)->data, + XSTRING (eltstring)->data, + compare); + bestmatchsize = (matchsize >= 0) ? matchsize : compare; + } + } + } + + if (NULL (bestmatch)) + return Qnil; /* No completions found */ + if (matchcount == 1 && bestmatchsize == XSTRING (string)->size) + return Qt; + + XFASTINT (zero) = 0; /* Else extract the part in which */ + XFASTINT (end) = bestmatchsize; /* all completions agree */ + return Fsubstring (bestmatch, zero, end); +} + +/* Compare exactly LEN chars of strings at S1 and S2, + ignoring case if appropriate. + Return -1 if strings match, + else number of chars that match at the beginning. */ + +scmp (s1, s2, len) + register char *s1, *s2; + int len; +{ + register int l = len; + + if (completion_ignore_case) + { + while (l && DOWNCASE (*s1++) == DOWNCASE (*s2++)) + l--; + } + else + { + while (l && *s1++ == *s2++) + l--; + } + if (l == 0) + return -1; + else return len - l; +} + +DEFUN ("all-completions", Fall_completions, Sall_completions, 2, 3, 0, + "Search for partial matches to STRING in ALIST.\n\ +Each car of each element of ALIST is tested to see if it begins with STRING.\n\ +The value is a list of all the strings from ALIST that match.\n\ +ALIST can be an obarray instead of an alist.\n\ +Then the print names of all symbols in the obarray are the possible matches.\n\ +\n\ +ALIST can also be a function to do the completion itself.\n\ +It receives three arguments: the values STRING, PREDICATE and t.\n\ +Whatever it returns becomes the value of `all-completion'.\n\ +\n\ +If optional third argument PREDICATE is non-nil,\n\ +it is used to test each possible match.\n\ +The match is a candidate only if PREDICATE returns non-nil.\n\ +The argument given to PREDICATE is the alist element or the symbol from the obarray.") + (string, alist, pred) + Lisp_Object string, alist, pred; +{ + Lisp_Object tail, elt, eltstring; + Lisp_Object allmatches; + int list = CONSP (alist) || NULL (alist); + int index, obsize; + Lisp_Object bucket, tem; + struct gcpro gcpro1, gcpro2, gcpro3, gcpro4; + + CHECK_STRING (string, 0); + if (!list && XTYPE (alist) != Lisp_Vector) + { + return call3 (alist, string, pred, Qt); + } + allmatches = Qnil; + + /* If ALIST is not a list, set TAIL just for gc pro. */ + tail = alist; + if (! list) + { + index = 0; + obsize = XVECTOR (alist)->size; + bucket = XVECTOR (alist)->contents[index]; + } + + while (1) + { + /* Get the next element of the alist or obarray. */ + /* Exit the loop if the elements are all used up. */ + /* elt gets the alist element or symbol. + eltstring gets the name to check as a completion. */ + + if (list) + { + if (NULL (tail)) + break; + elt = Fcar (tail); + eltstring = Fcar (elt); + tail = Fcdr (tail); + } + else + { + if (XFASTINT (bucket) != 0) + { + elt = bucket; + eltstring = Fsymbol_name (elt); + if (XSYMBOL (bucket)->next) + XSETSYMBOL (bucket, XSYMBOL (bucket)->next); + else + XFASTINT (bucket) = 0; + } + else if (++index >= obsize) + break; + else + { + bucket = XVECTOR (alist)->contents[index]; + continue; + } + } + + /* Is this element a possible completion? */ + + if (XTYPE (eltstring) == Lisp_String && + XSTRING (string)->size <= XSTRING (eltstring)->size && + XSTRING (eltstring)->data[0] != ' ' && + 0 > scmp (XSTRING (eltstring)->data, XSTRING (string)->data, + XSTRING (string)->size)) + { + /* Yes. */ + /* Ignore this element if there is a predicate + and the predicate doesn't like it. */ + + if (!NULL (pred)) + { + if (EQ (pred, Qcommandp)) + tem = Fcommandp (elt); + else + { + GCPRO4 (tail, eltstring, allmatches, string); + tem = call1 (pred, elt); + UNGCPRO; + } + if (NULL (tem)) continue; + } + /* Ok => put it on the list. */ + allmatches = Fcons (eltstring, allmatches); + } + } + + return Fnreverse (allmatches); +} + +Lisp_Object Vminibuffer_completion_table, Qminibuffer_completion_table; +Lisp_Object Vminibuffer_completion_predicate, Qminibuffer_completion_predicate; +Lisp_Object Vminibuffer_completion_confirm, Qminibuffer_completion_confirm; + +DEFUN ("completing-read", Fcompleting_read, Scompleting_read, 2, 6, 0, + "Read a string in the minibuffer, with completion.\n\ +Args are PROMPT, TABLE, PREDICATE, REQUIRE-MATCH and INITIAL-INPUT.\n\ +PROMPT is a string to prompt with; normally it ends in a colon and a space.\n\ +TABLE is an alist whose elements' cars are strings, or an obarray.\n\ +PREDICATE limits completion to a subset of TABLE.\n\ +See `try-completion' for more details on completion, TABLE, and PREDICATE.\n\ +If REQUIRE-MATCH is non-nil, the user is not allowed to exit unless\n\ + the input is (or completes to) an element of TABLE.\n\ + If it is also not t, Return does not exit if it does non-null completion.\n\ +If INITIAL-INPUT is non-nil, insert it in the minibuffer initially.\n\ +Case is ignored if ambient value of `completion-ignore-case' is non-nil.\n\ +If BACKUP-N is specified, point should be placed that many spaces from\n\ +the end of the buffer. This is useful when providing default values,\n\ +because you can put point before the last component of a filename or any\n\ +other component that is likely to be deleted.") + (prompt, table, pred, require_match, init, backup_n) + Lisp_Object prompt, table, pred, require_match, init, backup_n; +{ + Lisp_Object val; + int count = specpdl_ptr - specpdl; + specbind (Qminibuffer_completion_table, table); + specbind (Qminibuffer_completion_predicate, pred); + specbind (Qminibuffer_completion_confirm, + EQ (require_match, Qt) ? Qnil : Qt); + last_exact_completion = Qnil; + val = read_minibuf (NULL (require_match) + ? Vminibuffer_local_completion_map + : Vminibuffer_local_must_match_map, + init, prompt, backup_n, 0); + return unbind_to (count, val); +} + +/* Temporarily display the string M at the end of the current + minibuffer contents. This is used to display things like + "[No Match]" when the user requests a completion for a prefix + that has no possible completions, and other quick, unobtrusive + messages. */ + +temp_echo_area_glyphs (m) + char *m; +{ + /* It's not very modular to do things this way, but then it seems + to me that the whole echo_area_glyphs thing is a hack anyway. */ + extern char *previous_echo_glyphs; + + int osize = ZV; + Lisp_Object oinhibit; + oinhibit = Vinhibit_quit; + + /* Clear out any old echo-area message to make way for our new + thing. */ + echo_area_glyphs = previous_echo_glyphs = 0; + + SET_PT (osize); + insert_string (m); + SET_PT (osize); + Vinhibit_quit = Qt; + Fsit_for (make_number (2), Qnil, Qnil); + del_range (point, ZV); + if (!NULL (Vquit_flag)) + { + Vquit_flag = Qnil; + unread_command_char = Ctl ('g'); + } + Vinhibit_quit = oinhibit; +} + +Lisp_Object Fminibuffer_completion_help (); + +/* returns: + * 0 no possible completion + * 1 was already an exact and unique completion + * 3 was already an exact completion + * 4 completed to an exact completion + * 5 some completion happened + * 6 no completion happened + */ +int +do_completion () +{ + Lisp_Object completion, tem; + int completedp; + Lisp_Object last; + + completion = Ftry_completion (Fbuffer_string (), Vminibuffer_completion_table, + Vminibuffer_completion_predicate); + last = last_exact_completion; + last_exact_completion = Qnil; + + if (NULL (completion)) + { + bitch_at_user (); + temp_echo_area_glyphs (" [No match]"); + return 0; + } + + if (EQ (completion, Qt)) /* exact and unique match */ + return 1; + + /* compiler bug */ + tem = Fstring_equal (completion, Fbuffer_string()); + if (completedp = NULL (tem)) + { + Ferase_buffer (); /* Some completion happened */ + Finsert (1, &completion); + } + + /* It did find a match. Do we match some possibility exactly now? */ + if (CONSP (Vminibuffer_completion_table) + || NULL (Vminibuffer_completion_table)) + tem = Fassoc (Fbuffer_string (), Vminibuffer_completion_table); + else if (XTYPE (Vminibuffer_completion_table) == Lisp_Vector) + { + /* the primitive used by Fintern_soft */ + extern Lisp_Object oblookup (); + + tem = Fbuffer_string (); + /* Bypass intern-soft as that loses for nil */ + tem = oblookup (Vminibuffer_completion_table, + XSTRING (tem)->data, XSTRING (tem)->size); + if (XTYPE (tem) != Lisp_Symbol) + tem = Qnil; + else if (!NULL (Vminibuffer_completion_predicate)) + tem = call1 (Vminibuffer_completion_predicate, tem); + else + tem = Qt; + } + else + tem = call3 (Vminibuffer_completion_table, + Fbuffer_string (), + Vminibuffer_completion_predicate, + Qlambda); + + if (NULL (tem)) + { /* not an exact match */ + if (completedp) + return 5; + else if (auto_help) + Fminibuffer_completion_help (); + else + temp_echo_area_glyphs (" [Next char not unique]"); + return 6; + } + else if (completedp) + return 4; + /* If the last exact completion and this one were the same, + it means we've already given a "Complete but not unique" + message and the user's hit TAB again, so no we give him help. */ + last_exact_completion = completion; + if (!NULL (last)) + { + tem = Fbuffer_string (); + if (!NULL (Fequal (tem, last))) + Fminibuffer_completion_help (); + } + return 3; + +} + + +DEFUN ("minibuffer-complete", Fminibuffer_complete, Sminibuffer_complete, 0, 0, "", + "Complete the minibuffer contents as far as possible.") + () +{ + register int i = do_completion (); + switch (i) + { + case 0: + return Qnil; + + case 1: + temp_echo_area_glyphs (" [Sole completion]"); + break; + + case 3: + temp_echo_area_glyphs (" [Complete, but not unique]"); + break; + } + + return Qt; +} + +DEFUN ("minibuffer-complete-and-exit", Fminibuffer_complete_and_exit, + Sminibuffer_complete_and_exit, 0, 0, "", + "Complete the minibuffer contents, and maybe exit.\n\ +Exit if the name is valid with no completion needed.\n\ +If name was completed to a valid match,\n\ +a repetition of this command will exit.") + () +{ + register int i; + + /* Allow user to specify null string */ + if (BEGV == ZV) + goto exit; + + i = do_completion (); + switch (i) + { + case 1: + case 3: + goto exit; + + case 4: + if (!NULL (Vminibuffer_completion_confirm)) + { + temp_echo_area_glyphs (" [Confirm]"); + return Qnil; + } + else + goto exit; + + default: + return Qnil; + } + exit: + Fthrow (Qexit, Qnil); + /* NOTREACHED */ +} + +DEFUN ("minibuffer-complete-word", Fminibuffer_complete_word, Sminibuffer_complete_word, + 0, 0, "", + "Complete the minibuffer contents at most a single word.\n\ +After one word is completed as much as possible, a space or hyphen\n\ +is added, provided that matches some possible completion.") + () +{ + Lisp_Object completion, tem; + register int i; + register unsigned char *completion_string; + /* We keep calling Fbuffer_string + rather than arrange for GC to hold onto a pointer to + one of the strings thus made. */ + + completion = Ftry_completion (Fbuffer_string (), + Vminibuffer_completion_table, + Vminibuffer_completion_predicate); + if (NULL (completion)) + { + bitch_at_user (); + temp_echo_area_glyphs (" [No match]"); + return Qnil; + } + if (EQ (completion, Qt)) + return Qnil; + +#if 0 /* How the below code used to look, for reference */ + tem = Fbuffer_string (); + b = XSTRING (tem)->data; + i = ZV - 1 - XSTRING (completion)->size; + p = XSTRING (completion)->data; + if (i > 0 || + 0 <= scmp (b, p, ZV - 1)) + { + i = 1; + /* Set buffer to longest match of buffer tail and completion head. */ + while (0 <= scmp (b + i, p, ZV - 1 - i)) + i++; + del_range (1, i + 1); + SET_PT (ZV); + } +#else /* Rewritten code */ + { + register unsigned char *buffer_string; + int buffer_length, completion_length; + + tem = Fbuffer_string (); + buffer_string = XSTRING (tem)->data; + completion_string = XSTRING (completion)->data; + buffer_length = XSTRING (tem)->size; /* ie ZV - BEGV */ + completion_length = XSTRING (completion)->size; + i = buffer_length - completion_length; + /* Mly: I don't understand what this is supposed to do AT ALL */ + if (i > 0 || + 0 <= scmp (buffer_string, completion_string, buffer_length)) + { + /* Set buffer to longest match of buffer tail and completion head. */ + if (i <= 0) i = 1; + buffer_string += i; + buffer_length -= i; + while (0 <= scmp (buffer_string++, completion_string, buffer_length--)) + i++; + del_range (1, i + 1); + SET_PT (ZV); + } + } +#endif /* Rewritten code */ + i = ZV - BEGV; + + /* If completion finds next char not unique, + consider adding a space or a hyphen */ + if (i == XSTRING (completion)->size) + { + tem = Ftry_completion (concat2 (Fbuffer_string (), build_string (" ")), + Vminibuffer_completion_table, + Vminibuffer_completion_predicate); + if (XTYPE (tem) == Lisp_String) + completion = tem; + else + { + tem = Ftry_completion (concat2 (Fbuffer_string (), build_string ("-")), + Vminibuffer_completion_table, + Vminibuffer_completion_predicate); + if (XTYPE (tem) == Lisp_String) + completion = tem; + } + } + + /* Now find first word-break in the stuff found by completion. + i gets index in string of where to stop completing. */ + completion_string = XSTRING (completion)->data; + + for (; i < XSTRING (completion)->size; i++) + if (SYNTAX (completion_string[i]) != Sword) break; + if (i < XSTRING (completion)->size) + i = i + 1; + + /* If got no characters, print help for user. */ + + if (i == ZV - BEGV) + { + if (auto_help) + Fminibuffer_completion_help (); + return Qnil; + } + + /* Otherwise insert in minibuffer the chars we got */ + + Ferase_buffer (); + insert_from_string (completion, 0, i); + return Qt; +} + +DEFUN ("display-completion-list", Fdisplay_completion_list, Sdisplay_completion_list, + 1, 1, 0, + "Display in a buffer the list of completions, COMPLETIONS.\n\ +Each element may be just a symbol or string\n\ +or may be a list of two strings to be printed as if concatenated.") + (completions) + Lisp_Object completions; +{ + register Lisp_Object tail, elt; + register int i; + struct buffer *old = current_buffer; + /* No GCPRO needed, since (when it matters) every variable + points to a non-string that is pointed to by COMPLETIONS. */ + + set_buffer_internal (XBUFFER (Vstandard_output)); + + if (NULL (completions)) + insert_string ("There are no possible completions of what you have typed."); + else + { + insert_string ("Possible completions are:"); + for (tail = completions, i = 0; !NULL (tail); tail = Fcdr (tail), i++) + { + /* this needs fixing for the case of long completions + and/or narrow windows */ + /* Sadly, the window it will appear in is not known + until after the text has been made. */ + if (i & 1) + Findent_to (make_number (35), make_number (1)); + else + Fterpri (Qnil); + elt = Fcar (tail); + if (CONSP (elt)) + { + Fprinc (Fcar (elt), Qnil); + Fprinc (Fcar (Fcdr (elt)), Qnil); + } + else + Fprinc (elt, Qnil); + } + } + set_buffer_internal (old); + return Qnil; +} + +DEFUN ("minibuffer-completion-help", Fminibuffer_completion_help, Sminibuffer_completion_help, + 0, 0, "", + "Display a list of possible completions of the current minibuffer contents.") + () +{ + Lisp_Object completions; + + message ("Making completion list..."); + completions = Fall_completions (Fbuffer_string (), + Vminibuffer_completion_table, + Vminibuffer_completion_predicate); + echo_area_glyphs = 0; + + if (NULL (completions)) + { + bitch_at_user (); + temp_echo_area_glyphs (" [No completions]"); + } + else + internal_with_output_to_temp_buffer ("*Completions*", + Fdisplay_completion_list, + Fsort (completions, Qstring_lessp)); + return Qnil; +} + +DEFUN ("self-insert-and-exit", Fself_insert_and_exit, Sself_insert_and_exit, 0, 0, "", + "Terminate minibuffer input.") + () +{ + if (XTYPE (last_command_char) == Lisp_Int) + internal_self_insert (last_command_char, 0); + else + bitch_at_user (); + + Fthrow (Qexit, Qnil); +} + +DEFUN ("exit-minibuffer", Fexit_minibuffer, Sexit_minibuffer, 0, 0, "", + "Terminate this minibuffer argument.") + () +{ + Fthrow (Qexit, Qnil); +} + +DEFUN ("minibuffer-depth", Fminibuffer_depth, Sminibuffer_depth, 0, 0, 0, + "Return current depth of activations of minibuffer, a nonnegative integer.") + () +{ + return make_number (minibuf_level); +} + + +init_minibuf_once () +{ + Vminibuffer_list = Qnil; + staticpro (&Vminibuffer_list); +} + +syms_of_minibuf () +{ + minibuf_level = 0; + minibuf_prompt = 0; + minibuf_save_vector_size = 5; + minibuf_save_vector = (struct minibuf_save_data *) malloc (5 * sizeof (struct minibuf_save_data)); + + Qminibuffer_completion_table = intern ("minibuffer-completion-table"); + staticpro (&Qminibuffer_completion_table); + + Qminibuffer_completion_confirm = intern ("minibuffer-completion-confirm"); + staticpro (&Qminibuffer_completion_confirm); + + Qminibuffer_completion_predicate = intern ("minibuffer-completion-predicate"); + staticpro (&Qminibuffer_completion_predicate); + + staticpro (&last_minibuf_string); + last_minibuf_string = Qnil; + + Quser_variable_p = intern ("user-variable-p"); + staticpro (&Quser_variable_p); + + + + DEFVAR_BOOL ("completion-auto-help", &auto_help, + "*Non-nil means automatically provide help for invalid completion input."); + auto_help = 1; + + DEFVAR_BOOL ("completion-ignore-case", &completion_ignore_case, + "Non-nil means don't consider case significant in completion."); + completion_ignore_case = 0; + + DEFVAR_BOOL ("enable-recursive-minibuffers", &enable_recursive_minibuffers, + "*Non-nil means to allow minibuffer commands while in the minibuffer.\n\ +More precisely, this variable makes a difference when the minibuffer window\n\ +is the selected window. If you are in some other window, minibuffer commands\n\ +are allowed even if a minibuffer is active."); + enable_recursive_minibuffers = 0; + + DEFVAR_LISP ("minibuffer-completion-table", &Vminibuffer_completion_table, + "Alist or obarray used for completion in the minibuffer.\n\ +This becomes the ALIST argument to `try-completion' and `all-completion'.\n\ +\n\ +The value may alternatively be a function, which is given three arguments:\n\ + STRING, the current buffer contents;\n\ + PREDICATE, the predicate for filtering possible matches;\n\ + CODE, which says what kind of things to do.\n\ +CODE can be nil, t or `lambda'.\n\ +nil means to return the best completion of STRING, or nil if there is none.\n\ +t means to return a list of all possible completions of STRING.\n\ +`lambda' means to return t if STRING is a valid completion as it stands."); + Vminibuffer_completion_table = Qnil; + + DEFVAR_LISP ("minibuffer-completion-predicate", &Vminibuffer_completion_predicate, + "Within call to `completing-read', this holds the PREDICATE argument."); + Vminibuffer_completion_predicate = Qnil; + + DEFVAR_LISP ("minibuffer-completion-confirm", &Vminibuffer_completion_confirm, + "Non-nil => demand confirmation of completion before exiting minibuffer."); + Vminibuffer_completion_confirm = Qnil; + + DEFVAR_LISP ("minibuffer-help-form", &Vminibuffer_help_form, + "Value that `help-form' takes on inside the minibuffer."); + Vminibuffer_help_form = Qnil; + + defsubr (&Sread_from_minibuffer); + defsubr (&Seval_minibuffer); + defsubr (&Sread_minibuffer); + defsubr (&Sread_string); + defsubr (&Sread_command); + defsubr (&Sread_variable); + defsubr (&Sread_buffer); + defsubr (&Sread_no_blanks_input); + defsubr (&Sminibuffer_depth); + + defsubr (&Stry_completion); + defsubr (&Sall_completions); + defsubr (&Scompleting_read); + defsubr (&Sminibuffer_complete); + defsubr (&Sminibuffer_complete_word); + defsubr (&Sminibuffer_complete_and_exit); + defsubr (&Sdisplay_completion_list); + defsubr (&Sminibuffer_completion_help); + + defsubr (&Sself_insert_and_exit); + defsubr (&Sexit_minibuffer); + +} + +keys_of_minibuf () +{ + initial_define_key (Vminibuffer_local_map, Ctl ('g'), + "abort-recursive-edit"); + initial_define_key (Vminibuffer_local_map, Ctl ('m'), + "exit-minibuffer"); + initial_define_key (Vminibuffer_local_map, Ctl ('j'), + "exit-minibuffer"); + + initial_define_key (Vminibuffer_local_ns_map, Ctl ('g'), + "abort-recursive-edit"); + initial_define_key (Vminibuffer_local_ns_map, Ctl ('m'), + "exit-minibuffer"); + initial_define_key (Vminibuffer_local_ns_map, Ctl ('j'), + "exit-minibuffer"); + + initial_define_key (Vminibuffer_local_ns_map, ' ', + "exit-minibuffer"); + initial_define_key (Vminibuffer_local_ns_map, '\t', + "exit-minibuffer"); + initial_define_key (Vminibuffer_local_ns_map, '?', + "self-insert-and-exit"); + + initial_define_key (Vminibuffer_local_completion_map, Ctl ('g'), + "abort-recursive-edit"); + initial_define_key (Vminibuffer_local_completion_map, Ctl ('m'), + "exit-minibuffer"); + initial_define_key (Vminibuffer_local_completion_map, Ctl ('j'), + "exit-minibuffer"); + + initial_define_key (Vminibuffer_local_completion_map, '\t', + "minibuffer-complete"); + initial_define_key (Vminibuffer_local_completion_map, ' ', + "minibuffer-complete-word"); + initial_define_key (Vminibuffer_local_completion_map, '?', + "minibuffer-completion-help"); + + initial_define_key (Vminibuffer_local_must_match_map, Ctl ('g'), + "abort-recursive-edit"); + initial_define_key (Vminibuffer_local_must_match_map, Ctl ('m'), + "minibuffer-complete-and-exit"); + initial_define_key (Vminibuffer_local_must_match_map, Ctl ('j'), + "minibuffer-complete-and-exit"); + initial_define_key (Vminibuffer_local_must_match_map, '\t', + "minibuffer-complete"); + initial_define_key (Vminibuffer_local_must_match_map, ' ', + "minibuffer-complete-word"); + initial_define_key (Vminibuffer_local_must_match_map, '?', + "minibuffer-completion-help"); +}