summaryrefslogtreecommitdiff
path: root/usr.bin/vim/unix.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr.bin/vim/unix.c')
-rw-r--r--usr.bin/vim/unix.c2883
1 files changed, 2883 insertions, 0 deletions
diff --git a/usr.bin/vim/unix.c b/usr.bin/vim/unix.c
new file mode 100644
index 00000000000..bdb7bf55124
--- /dev/null
+++ b/usr.bin/vim/unix.c
@@ -0,0 +1,2883 @@
+/* $OpenBSD: unix.c,v 1.1 1996/09/07 21:40:24 downsj Exp $ */
+/* vi:set ts=4 sw=4:
+ *
+ * VIM - Vi IMproved by Bram Moolenaar
+ * OS/2 port by Paul Slootman
+ *
+ * Do ":help uganda" in Vim to read copying and usage conditions.
+ * Do ":help credits" in Vim to see a list of people who contributed.
+ */
+
+/*
+ * unix.c -- code for all flavors of Unix (BSD, SYSV, SVR4, POSIX, ...)
+ * Also for OS/2, using the excellent EMX package!!!
+ *
+ * A lot of this file was originally written by Juergen Weigert and later
+ * changed beyond recognition.
+ */
+
+/*
+ * Some systems have a prototype for select() that has (int *) instead of
+ * (fd_set *), which is wrong. This define removes that prototype. We include
+ * our own prototype in osdef.h.
+ */
+#define select select_declared_wrong
+
+#include "vim.h"
+#include "globals.h"
+#include "option.h"
+#include "proto.h"
+
+#ifdef HAVE_FCNTL_H
+# include <fcntl.h>
+#endif
+
+#include "unixunix.h" /* unix includes for unix.c only */
+
+/*
+ * Use this prototype for select, some include files have a wrong prototype
+ */
+#undef select
+
+#if defined(HAVE_SELECT)
+extern int select __ARGS((int, fd_set *, fd_set *, fd_set *, struct timeval *));
+#endif
+
+/*
+ * end of autoconf section. To be extended...
+ */
+
+/* Are the following #ifdefs still required? And why? Is that for X11? */
+
+#if defined(ESIX) || defined(M_UNIX) && !defined(SCO)
+# ifdef SIGWINCH
+# undef SIGWINCH
+# endif
+# ifdef TIOCGWINSZ
+# undef TIOCGWINSZ
+# endif
+#endif
+
+#if defined(SIGWINDOW) && !defined(SIGWINCH) /* hpux 9.01 has it */
+# define SIGWINCH SIGWINDOW
+#endif
+
+#if defined(HAVE_X11) && defined(WANT_X11)
+# include <X11/Xlib.h>
+# include <X11/Xutil.h>
+# include <X11/Xatom.h>
+
+Window x11_window = 0;
+Display *x11_display = NULL;
+int got_x_error = FALSE;
+
+static int get_x11_windis __ARGS((void));
+static void set_x11_title __ARGS((char_u *));
+static void set_x11_icon __ARGS((char_u *));
+#endif
+
+static int get_x11_title __ARGS((int));
+static int get_x11_icon __ARGS((int));
+
+static void may_core_dump __ARGS((void));
+
+static int Read __ARGS((char_u *, long));
+static int WaitForChar __ARGS((long));
+static int RealWaitForChar __ARGS((int, long));
+static void fill_inbuf __ARGS((int));
+
+#if defined(SIGWINCH)
+static RETSIGTYPE sig_winch __ARGS(SIGPROTOARG);
+#endif
+#if defined(SIGALRM) && defined(HAVE_X11) && defined(WANT_X11)
+static RETSIGTYPE sig_alarm __ARGS(SIGPROTOARG);
+#endif
+static RETSIGTYPE deathtrap __ARGS(SIGPROTOARG);
+
+static void catch_signals __ARGS((RETSIGTYPE (*func)()));
+#ifndef __EMX__
+static int have_wildcard __ARGS((int, char_u **));
+static int have_dollars __ARGS((int, char_u **));
+#endif
+
+static int do_resize = FALSE;
+static char_u *oldtitle = NULL;
+static char_u *fixedtitle = (char_u *)"Thanks for flying Vim";
+static char_u *oldicon = NULL;
+#ifndef __EMX__
+static char_u *extra_shell_arg = NULL;
+static int show_shell_mess = TRUE;
+#endif
+static int core_dump = FALSE; /* core dump in mch_windexit() */
+
+#ifdef SYS_SIGLIST_DECLARED
+/*
+ * I have seen
+ * extern char *_sys_siglist[NSIG];
+ * on Irix, Linux, NetBSD and Solaris. It contains a nice list of strings
+ * that describe the signals. That is nearly what we want here. But
+ * autoconf does only check for sys_siglist (without the underscore), I
+ * do not want to change everything today.... jw.
+ * This is why AC_DECL_SYS_SIGLIST is commented out in configure.in
+ */
+#endif
+
+static struct
+{
+ int sig; /* Signal number, eg. SIGSEGV etc */
+ char *name; /* Signal name (not char_u!). */
+ int dump; /* Should this signal cause a core dump? */
+} signal_info[] =
+{
+#ifdef SIGHUP
+ {SIGHUP, "HUP", FALSE},
+#endif
+#ifdef SIGINT
+ {SIGINT, "INT", FALSE},
+#endif
+#ifdef SIGQUIT
+ {SIGQUIT, "QUIT", TRUE},
+#endif
+#ifdef SIGILL
+ {SIGILL, "ILL", TRUE},
+#endif
+#ifdef SIGTRAP
+ {SIGTRAP, "TRAP", TRUE},
+#endif
+#ifdef SIGABRT
+ {SIGABRT, "ABRT", TRUE},
+#endif
+#ifdef SIGEMT
+ {SIGEMT, "EMT", TRUE},
+#endif
+#ifdef SIGFPE
+ {SIGFPE, "FPE", TRUE},
+#endif
+#ifdef SIGBUS
+ {SIGBUS, "BUS", TRUE},
+#endif
+#ifdef SIGSEGV
+ {SIGSEGV, "SEGV", TRUE},
+#endif
+#ifdef SIGSYS
+ {SIGSYS, "SYS", TRUE},
+#endif
+#ifdef SIGALRM
+ {SIGALRM, "ALRM", FALSE},
+#endif
+#ifdef SIGTERM
+ {SIGTERM, "TERM", FALSE},
+#endif
+#ifdef SIGVTALRM
+ {SIGVTALRM, "VTALRM", FALSE},
+#endif
+#ifdef SIGPROF
+ {SIGPROF, "PROF", FALSE},
+#endif
+#ifdef SIGXCPU
+ {SIGXCPU, "XCPU", TRUE},
+#endif
+#ifdef SIGXFSZ
+ {SIGXFSZ, "XFSZ", TRUE},
+#endif
+#ifdef SIGUSR1
+ {SIGUSR1, "USR1", FALSE},
+#endif
+#ifdef SIGUSR2
+ {SIGUSR2, "USR2", FALSE},
+#endif
+ {-1, "Unknown!", -1}
+};
+
+ void
+mch_write(s, len)
+ char_u *s;
+ int len;
+{
+#ifdef USE_GUI
+ if (gui.in_use && !gui.dying)
+ {
+ gui_write(s, len);
+ if (p_wd)
+ gui_mch_wait_for_chars(p_wd);
+ }
+ else
+#endif
+ {
+ write(1, (char *)s, len);
+ if (p_wd) /* Unix is too fast, slow down a bit more */
+ RealWaitForChar(0, p_wd);
+ }
+}
+
+/*
+ * mch_inchar(): low level input funcion.
+ * Get a characters from the keyboard.
+ * Return the number of characters that are available.
+ * If wtime == 0 do not wait for characters.
+ * If wtime == n wait a short time for characters.
+ * If wtime == -1 wait forever for characters.
+ */
+ int
+mch_inchar(buf, maxlen, wtime)
+ char_u *buf;
+ int maxlen;
+ long wtime; /* don't use "time", MIPS cannot handle it */
+{
+ int len;
+
+#ifdef USE_GUI
+ if (gui.in_use)
+ {
+ if (!gui_mch_wait_for_chars(wtime))
+ return 0;
+ return Read(buf, (long)maxlen);
+ }
+#endif
+
+ if (wtime >= 0)
+ {
+ while (WaitForChar(wtime) == 0) /* no character available */
+ {
+ if (!do_resize) /* return if not interrupted by resize */
+ return 0;
+ set_winsize(0, 0, FALSE);
+ do_resize = FALSE;
+ }
+ }
+ else /* wtime == -1 */
+ {
+ /*
+ * If there is no character available within 'updatetime' seconds
+ * flush all the swap files to disk
+ * Also done when interrupted by SIGWINCH.
+ */
+ if (WaitForChar(p_ut) == 0)
+ updatescript(0);
+ }
+
+ for (;;) /* repeat until we got a character */
+ {
+ if (do_resize) /* window changed size */
+ {
+ set_winsize(0, 0, FALSE);
+ do_resize = FALSE;
+ }
+ /*
+ * we want to be interrupted by the winch signal
+ */
+ WaitForChar(-1L);
+ if (do_resize) /* interrupted by SIGWINCHsignal */
+ continue;
+
+ /*
+ * For some terminals we only get one character at a time.
+ * We want the get all available characters, so we could keep on
+ * trying until none is available
+ * For some other terminals this is quite slow, that's why we don't do
+ * it.
+ */
+ len = Read(buf, (long)maxlen);
+ if (len > 0)
+ {
+#ifdef OS2
+ int i;
+
+ for (i = 0; i < len; i++)
+ if (buf[i] == 0)
+ buf[i] = K_NUL;
+#endif
+ return len;
+ }
+ }
+}
+
+/*
+ * return non-zero if a character is available
+ */
+ int
+mch_char_avail()
+{
+#ifdef USE_GUI
+ if (gui.in_use)
+ {
+ gui_mch_update();
+ return !is_input_buf_empty();
+ }
+#endif
+ return WaitForChar(0L);
+}
+
+ long
+mch_avail_mem(special)
+ int special;
+{
+#ifdef __EMX__
+ return ulimit(3, 0L); /* always 32MB? */
+#else
+ return 0x7fffffff; /* virtual memory eh */
+#endif
+}
+
+ void
+mch_delay(msec, ignoreinput)
+ long msec;
+ int ignoreinput;
+{
+ if (ignoreinput)
+#ifndef HAVE_SELECT
+ poll(NULL, 0, (int)msec);
+#else
+# ifdef __EMX__
+ _sleep2(msec);
+# else
+ {
+ struct timeval tv;
+
+ tv.tv_sec = msec / 1000;
+ tv.tv_usec = (msec % 1000) * 1000;
+ select(0, NULL, NULL, NULL, &tv);
+ }
+# endif /* __EMX__ */
+#endif /* HAVE_SELECT */
+ else
+#ifdef USE_GUI
+ if (gui.in_use)
+ gui_mch_wait_for_chars(msec);
+ else
+#endif
+ WaitForChar(msec);
+}
+
+#if defined(SIGWINCH)
+/*
+ * We need correct potatotypes, otherwise mean compilers will barf when the
+ * second argument to signal() is ``wrong''.
+ * Let me try it with a few tricky defines from my own osdef.h (jw).
+ */
+ static RETSIGTYPE
+sig_winch SIGDEFARG(sigarg)
+{
+ /* this is not required on all systems, but it doesn't hurt anybody */
+ signal(SIGWINCH, (RETSIGTYPE (*)())sig_winch);
+ do_resize = TRUE;
+ SIGRETURN;
+}
+#endif
+
+#if defined(SIGALRM) && defined(HAVE_X11) && defined(WANT_X11)
+/*
+ * signal function for alarm().
+ */
+ static RETSIGTYPE
+sig_alarm SIGDEFARG(sigarg)
+{
+ /* doesn't do anything, just to break a system call */
+ SIGRETURN;
+}
+#endif
+
+ void
+mch_resize()
+{
+ do_resize = TRUE;
+}
+
+/*
+ * This function handles deadly signals.
+ * It tries to preserve any swap file and exit properly.
+ * (partly from Elvis).
+ */
+ static RETSIGTYPE
+deathtrap SIGDEFARG(sigarg)
+{
+ static int entered = 0;
+#ifdef SIGHASARG
+ int i;
+
+ for (i = 0; signal_info[i].dump != -1; i++)
+ {
+ if (sigarg == signal_info[i].sig)
+ {
+ if (signal_info[i].dump)
+ core_dump = TRUE;
+ break;
+ }
+ }
+#endif
+
+ /*
+ * If something goes wrong after entering here, we may get here again.
+ * When this happens, give a message and try to exit nicely (resetting the
+ * terminal mode, etc.)
+ * When this happens twice, just exit, don't even try to give a message,
+ * stack may be corrupt or something weird.
+ */
+ if (entered == 2)
+ {
+ may_core_dump();
+ exit(7);
+ }
+ if (entered)
+ {
+ OUTSTR("Vim: Double signal, exiting\n");
+ flushbuf();
+ getout(1);
+ }
+ ++entered;
+
+ sprintf((char *)IObuff, "Vim: Caught %s %s\n",
+#ifdef SIGHASARG
+ "deadly signal", signal_info[i].name);
+#else
+ "some", "deadly signal");
+#endif
+
+ preserve_exit(); /* preserve files and exit */
+
+ SIGRETURN;
+}
+
+/*
+ * If the machine has job control, use it to suspend the program,
+ * otherwise fake it by starting a new shell.
+ * When running the GUI iconify the window.
+ */
+ void
+mch_suspend()
+{
+#ifdef USE_GUI
+ if (gui.in_use)
+ {
+ gui_mch_iconify();
+ return;
+ }
+#endif
+#ifdef SIGTSTP
+ flushbuf(); /* needed to make cursor visible on some systems */
+ settmode(0);
+ flushbuf(); /* needed to disable mouse on some systems */
+ kill(0, SIGTSTP); /* send ourselves a STOP signal */
+
+ /*
+ * Set oldtitle to NULL, so the current title is obtained again.
+ */
+ if (oldtitle != fixedtitle)
+ {
+ vim_free(oldtitle);
+ oldtitle = NULL;
+ }
+ settmode(1);
+#else
+ MSG_OUTSTR("new shell started\n");
+ (void)call_shell(NULL, SHELL_COOKED);
+#endif
+ need_check_timestamps = TRUE;
+}
+
+ void
+mch_windinit()
+{
+ Columns = 80;
+ Rows = 24;
+
+ flushbuf();
+
+ (void)mch_get_winsize();
+
+#if defined(SIGWINCH)
+ /*
+ * WINDOW CHANGE signal is handled with sig_winch().
+ */
+ signal(SIGWINCH, (RETSIGTYPE (*)())sig_winch);
+#endif
+
+ /*
+ * We want the STOP signal to work, to make mch_suspend() work
+ */
+#ifdef SIGTSTP
+ signal(SIGTSTP, SIG_DFL);
+#endif
+
+ /*
+ * We want to ignore breaking of PIPEs.
+ */
+#ifdef SIGPIPE
+ signal(SIGPIPE, SIG_IGN);
+#endif
+
+ /*
+ * Arrange for other signals to gracefully shutdown Vim.
+ */
+ catch_signals(deathtrap);
+}
+
+ static void
+catch_signals(func)
+ RETSIGTYPE (*func)();
+{
+ int i;
+
+ for (i = 0; signal_info[i].dump != -1; i++)
+ signal(signal_info[i].sig, func);
+}
+
+ void
+reset_signals()
+{
+ catch_signals(SIG_DFL);
+}
+
+/*
+ * Check_win checks whether we have an interactive window.
+ */
+ int
+mch_check_win(argc, argv)
+ int argc;
+ char **argv;
+{
+ if (isatty(1))
+ return OK;
+ return FAIL;
+}
+
+ int
+mch_check_input()
+{
+ if (isatty(0))
+ return OK;
+ return FAIL;
+}
+
+#if defined(HAVE_X11) && defined(WANT_X11)
+/*
+ * X Error handler, otherwise X just exits! (very rude) -- webb
+ */
+ static int
+x_error_handler(dpy, error_event)
+ Display *dpy;
+ XErrorEvent *error_event;
+{
+ XGetErrorText(dpy, error_event->error_code, (char *)IObuff, IOSIZE);
+ STRCAT(IObuff, "\nVim: Got X error\n");
+
+#if 1
+ preserve_exit(); /* preserve files and exit */
+#else
+ printf(IObuff); /* print error message and continue */
+ /* Makes my system hang */
+#endif
+
+ return 0; /* NOTREACHED */
+}
+
+/*
+ * Another X Error handler, just used to check for errors.
+ */
+ static int
+x_error_check(dpy, error_event)
+ Display *dpy;
+ XErrorEvent *error_event;
+{
+ got_x_error = TRUE;
+ return 0;
+}
+
+/*
+ * try to get x11 window and display
+ *
+ * return FAIL for failure, OK otherwise
+ */
+ static int
+get_x11_windis()
+{
+ char *winid;
+ XTextProperty text_prop;
+ int (*old_handler)();
+ static int result = -1;
+ static int x11_display_opened_here = FALSE;
+
+ /* X just exits if it finds an error otherwise! */
+ XSetErrorHandler(x_error_handler);
+
+#ifdef USE_GUI_X11
+ if (gui.in_use)
+ {
+ /*
+ * If the X11 display was opened here before, for the window where Vim
+ * was started, close that one now to avoid a memory leak.
+ */
+ if (x11_display_opened_here && x11_display != NULL)
+ {
+ XCloseDisplay(x11_display);
+ x11_display = NULL;
+ x11_display_opened_here = FALSE;
+ }
+ return gui_get_x11_windis(&x11_window, &x11_display);
+ }
+#endif
+
+ if (result != -1) /* Have already been here and set this */
+ return result; /* Don't do all these X calls again */
+
+ /*
+ * If WINDOWID not set, should try another method to find out
+ * what the current window number is. The only code I know for
+ * this is very complicated.
+ * We assume that zero is invalid for WINDOWID.
+ */
+ if (x11_window == 0 && (winid = getenv("WINDOWID")) != NULL)
+ x11_window = (Window)atol(winid);
+ if (x11_window != 0 && x11_display == NULL)
+ {
+#ifdef SIGALRM
+ RETSIGTYPE (*sig_save)();
+
+ /*
+ * Opening the Display may hang if the DISPLAY setting is wrong, or
+ * the network connection is bad. Set an alarm timer to get out.
+ */
+ sig_save = (RETSIGTYPE (*)())signal(SIGALRM,
+ (RETSIGTYPE (*)())sig_alarm);
+ alarm(2);
+#endif
+ x11_display = XOpenDisplay(NULL);
+#ifdef SIGALRM
+ alarm(0);
+ signal(SIGALRM, (RETSIGTYPE (*)())sig_save);
+#endif
+ if (x11_display != NULL)
+ {
+ /*
+ * Try to get the window title. I don't actually want it yet, so
+ * there may be a simpler call to use, but this will cause the
+ * error handler x_error_check() to be called if anything is wrong,
+ * such as the window pointer being invalid (as can happen when the
+ * user changes his DISPLAY, but not his WINDOWID) -- webb
+ */
+ old_handler = XSetErrorHandler(x_error_check);
+ got_x_error = FALSE;
+ if (XGetWMName(x11_display, x11_window, &text_prop))
+ XFree((void *)text_prop.value);
+ XSync(x11_display, False);
+ if (got_x_error)
+ {
+ /* Maybe window id is bad */
+ x11_window = 0;
+ XCloseDisplay(x11_display);
+ x11_display = NULL;
+ }
+ else
+ x11_display_opened_here = TRUE;
+ XSetErrorHandler(old_handler);
+ }
+ }
+ if (x11_window == 0 || x11_display == NULL)
+ return (result = FAIL);
+ return (result = OK);
+}
+
+/*
+ * Determine original x11 Window Title
+ */
+ static int
+get_x11_title(test_only)
+ int test_only;
+{
+ XTextProperty text_prop;
+ int retval = FALSE;
+
+ if (get_x11_windis() == OK)
+ {
+ /* Get window name if any */
+ if (XGetWMName(x11_display, x11_window, &text_prop))
+ {
+ if (text_prop.value != NULL)
+ {
+ retval = TRUE;
+ if (!test_only)
+ oldtitle = strsave((char_u *)text_prop.value);
+ }
+ XFree((void *)text_prop.value);
+ }
+ }
+ if (oldtitle == NULL && !test_only) /* could not get old title */
+ oldtitle = fixedtitle;
+
+ return retval;
+}
+
+/*
+ * Determine original x11 Window icon
+ */
+
+ static int
+get_x11_icon(test_only)
+ int test_only;
+{
+ XTextProperty text_prop;
+ int retval = FALSE;
+
+ if (get_x11_windis() == OK)
+ {
+ /* Get icon name if any */
+ if (XGetWMIconName(x11_display, x11_window, &text_prop))
+ {
+ if (text_prop.value != NULL)
+ {
+ retval = TRUE;
+ if (!test_only)
+ oldicon = strsave((char_u *)text_prop.value);
+ }
+ XFree((void *)text_prop.value);
+ }
+ }
+
+ /* could not get old icon, use terminal name */
+ if (oldicon == NULL && !test_only)
+ {
+ if (STRNCMP(term_strings[KS_NAME], "builtin_", 8) == 0)
+ oldicon = term_strings[KS_NAME] + 8;
+ else
+ oldicon = term_strings[KS_NAME];
+ }
+
+ return retval;
+}
+
+/*
+ * Set x11 Window Title
+ *
+ * get_x11_windis() must be called before this and have returned OK
+ */
+ static void
+set_x11_title(title)
+ char_u *title;
+{
+#if XtSpecificationRelease >= 4
+ XTextProperty text_prop;
+
+ text_prop.value = title;
+ text_prop.nitems = STRLEN(title);
+ text_prop.encoding = XA_STRING;
+ text_prop.format = 8;
+ XSetWMName(x11_display, x11_window, &text_prop);
+#else
+ XStoreName(x11_display, x11_window, (char *)title);
+#endif
+ XFlush(x11_display);
+}
+
+/*
+ * Set x11 Window icon
+ *
+ * get_x11_windis() must be called before this and have returned OK
+ */
+ static void
+set_x11_icon(icon)
+ char_u *icon;
+{
+#if XtSpecificationRelease >= 4
+ XTextProperty text_prop;
+
+ text_prop.value = icon;
+ text_prop.nitems = STRLEN(icon);
+ text_prop.encoding = XA_STRING;
+ text_prop.format = 8;
+ XSetWMIconName(x11_display, x11_window, &text_prop);
+#else
+ XSetIconName(x11_display, x11_window, (char *)icon);
+#endif
+ XFlush(x11_display);
+}
+
+#else /* HAVE_X11 && WANT_X11 */
+
+ static int
+get_x11_title(test_only)
+ int test_only;
+{
+ if (!test_only)
+ oldtitle = fixedtitle;
+ return FALSE;
+}
+
+ static int
+get_x11_icon(test_only)
+ int test_only;
+{
+ if (!test_only)
+ {
+ if (STRNCMP(term_strings[KS_NAME], "builtin_", 8) == 0)
+ oldicon = term_strings[KS_NAME] + 8;
+ else
+ oldicon = term_strings[KS_NAME];
+ }
+ return FALSE;
+}
+
+#endif /* HAVE_X11 && WANT_X11 */
+
+ int
+mch_can_restore_title()
+{
+#ifdef USE_GUI
+ /*
+ * If GUI is (going to be) used, we can always set the window title.
+ * Saves a bit of time, because the X11 display server does not need to be
+ * contacted.
+ */
+ if (gui.starting || gui.in_use)
+ return TRUE;
+#endif
+ return get_x11_title(TRUE);
+}
+
+ int
+mch_can_restore_icon()
+{
+#ifdef USE_GUI
+ /*
+ * If GUI is (going to be) used, we can always set the icon name.
+ * Saves a bit of time, because the X11 display server does not need to be
+ * contacted.
+ */
+ if (gui.starting || gui.in_use)
+ return TRUE;
+#endif
+ return get_x11_icon(TRUE);
+}
+
+/*
+ * Set the window title and icon.
+ * Currently only works for x11.
+ */
+ void
+mch_settitle(title, icon)
+ char_u *title;
+ char_u *icon;
+{
+ int type = 0;
+
+ if (term_strings[KS_NAME] == NULL) /* no terminal name (yet) */
+ return;
+ if (title == NULL && icon == NULL) /* nothing to do */
+ return;
+
+/*
+ * if the window ID and the display is known, we may use X11 calls
+ */
+#if defined(HAVE_X11) && defined(WANT_X11)
+ if (get_x11_windis() == OK)
+ type = 1;
+#endif
+
+ /*
+ * Note: if terminal is xterm, title is set with escape sequence rather
+ * than x11 calls, because the x11 calls don't always work
+ * Check only if the start of the terminal name is "xterm", also catch
+ * "xterms".
+ */
+ if (is_xterm(term_strings[KS_NAME]))
+ type = 2;
+
+ if (is_iris_ansi(term_strings[KS_NAME]))
+ type = 3;
+
+ if (type)
+ {
+ if (title != NULL)
+ {
+ if (oldtitle == NULL) /* first call, save title */
+ (void)get_x11_title(FALSE);
+
+ switch(type)
+ {
+#if defined(HAVE_X11) && defined(WANT_X11)
+ case 1: set_x11_title(title); /* x11 */
+ break;
+#endif
+ case 2: outstrn((char_u *)"\033]2;"); /* xterm */
+ outstrn(title);
+ outchar(Ctrl('G'));
+ flushbuf();
+ break;
+
+ case 3: outstrn((char_u *)"\033P1.y"); /* iris-ansi */
+ outstrn(title);
+ outstrn((char_u *)"\234");
+ flushbuf();
+ break;
+ }
+ }
+
+ if (icon != NULL)
+ {
+ if (oldicon == NULL) /* first call, save icon */
+ get_x11_icon(FALSE);
+
+ switch(type)
+ {
+#if defined(HAVE_X11) && defined(WANT_X11)
+ case 1: set_x11_icon(icon); /* x11 */
+ break;
+#endif
+ case 2: outstrn((char_u *)"\033]1;"); /* xterm */
+ outstrn(icon);
+ outchar(Ctrl('G'));
+ flushbuf();
+ break;
+
+ case 3: outstrn((char_u *)"\033P3.y"); /* iris-ansi */
+ outstrn(icon);
+ outstrn((char_u *)"\234");
+ flushbuf();
+ break;
+ }
+ }
+ }
+}
+
+ int
+is_xterm(name)
+ char_u *name;
+{
+ if (name == NULL)
+ return FALSE;
+ return (vim_strnicmp(name, (char_u *)"xterm", (size_t)5) == 0 ||
+ STRCMP(name, "builtin_xterm") == 0);
+}
+
+ int
+is_iris_ansi(name)
+ char_u *name;
+{
+ if (name == NULL)
+ return FALSE;
+ return (vim_strnicmp(name, (char_u *)"iris-ansi", (size_t)9) == 0 ||
+ STRCMP(name, "builtin_iris-ansi") == 0);
+}
+
+/*
+ * Return TRUE if "name" is a terminal for which 'ttyfast' should be set.
+ * This should include all windowed terminal emulators.
+ */
+ int
+is_fastterm(name)
+ char_u *name;
+{
+ if (name == NULL)
+ return FALSE;
+ if (is_xterm(name) || is_iris_ansi(name))
+ return TRUE;
+ return (vim_strnicmp(name, (char_u *)"hpterm", (size_t)6) == 0 ||
+ vim_strnicmp(name, (char_u *)"sun-cmd", (size_t)7) == 0 ||
+ vim_strnicmp(name, (char_u *)"screen", (size_t)6) == 0 ||
+ vim_strnicmp(name, (char_u *)"dtterm", (size_t)6) == 0);
+}
+
+/*
+ * Restore the window/icon title.
+ * which is one of:
+ * 1 Just restore title
+ * 2 Just restore icon
+ * 3 Restore title and icon
+ */
+ void
+mch_restore_title(which)
+ int which;
+{
+ mch_settitle((which & 1) ? oldtitle : NULL, (which & 2) ? oldicon : NULL);
+}
+
+/*
+ * Insert user name in s[len].
+ * Return OK if a name found.
+ */
+ int
+mch_get_user_name(s, len)
+ char_u *s;
+ int len;
+{
+#if defined(HAVE_PWD_H) && defined(HAVE_GETPWUID)
+ struct passwd *pw;
+#endif
+ uid_t uid;
+
+ uid = getuid();
+#if defined(HAVE_PWD_H) && defined(HAVE_GETPWUID)
+ if ((pw = getpwuid(uid)) != NULL &&
+ pw->pw_name != NULL && *pw->pw_name != NUL)
+ {
+ STRNCPY(s, pw->pw_name, len);
+ return OK;
+ }
+#endif
+ sprintf((char *)s, "%d", (int)uid); /* assumes s is long enough */
+ return FAIL; /* a number is not a name */
+}
+
+/*
+ * Insert host name is s[len].
+ */
+
+#ifdef HAVE_SYS_UTSNAME_H
+ void
+mch_get_host_name(s, len)
+ char_u *s;
+ int len;
+{
+ struct utsname vutsname;
+
+ uname(&vutsname);
+ STRNCPY(s, vutsname.nodename, len);
+}
+#else /* HAVE_SYS_UTSNAME_H */
+
+# ifdef HAVE_SYS_SYSTEMINFO_H
+# define gethostname(nam, len) sysinfo(SI_HOSTNAME, nam, len)
+# endif
+
+ void
+mch_get_host_name(s, len)
+ char_u *s;
+ int len;
+{
+ gethostname((char *)s, len);
+}
+#endif /* HAVE_SYS_UTSNAME_H */
+
+/*
+ * return process ID
+ */
+ long
+mch_get_pid()
+{
+ return (long)getpid();
+}
+
+#if !defined(HAVE_STRERROR) && defined(USE_GETCWD)
+static char *strerror __ARGS((int));
+
+ static char *
+strerror(err)
+ int err;
+{
+ extern int sys_nerr;
+ extern char *sys_errlist[];
+ static char er[20];
+
+ if (err > 0 && err < sys_nerr)
+ return (sys_errlist[err]);
+ sprintf(er, "Error %d", err);
+ return er;
+}
+#endif
+
+/*
+ * Get name of current directory into buffer 'buf' of length 'len' bytes.
+ * Return OK for success, FAIL for failure.
+ */
+ int
+mch_dirname(buf, len)
+ char_u *buf;
+ int len;
+{
+#if defined(USE_GETCWD)
+ if (getcwd((char *)buf, len) == NULL)
+ {
+ STRCPY(buf, strerror(errno));
+ return FAIL;
+ }
+ return OK;
+#else
+ return (getwd((char *)buf) != NULL ? OK : FAIL);
+#endif
+}
+
+#ifdef __EMX__
+/*
+ * Replace all slashes by backslashes.
+ */
+ static void
+slash_adjust(p)
+ char_u *p;
+{
+ while (*p)
+ {
+ if (*p == '/')
+ *p = '\\';
+ ++p;
+ }
+}
+#endif
+
+/*
+ * Get absolute filename into buffer 'buf' of length 'len' bytes.
+ *
+ * return FAIL for failure, OK for success
+ */
+ int
+FullName(fname, buf, len, force)
+ char_u *fname, *buf;
+ int len;
+ int force; /* also expand when already absolute path name */
+{
+ int l;
+#ifdef OS2
+ int only_drive; /* only a drive letter is specified in file name */
+#endif
+#ifdef HAVE_FCHDIR
+ int fd = -1;
+ static int dont_fchdir = FALSE; /* TRUE when fchdir() doesn't work */
+#endif
+ char_u olddir[MAXPATHL];
+ char_u *p;
+ char_u c;
+ int retval = OK;
+
+ if (fname == NULL) /* always fail */
+ {
+ *buf = NUL;
+ return FAIL;
+ }
+
+ *buf = 0;
+ if (force || !isFullName(fname)) /* if forced or not an absolute path */
+ {
+ /*
+ * If the file name has a path, change to that directory for a moment,
+ * and then do the getwd() (and get back to where we were).
+ * This will get the correct path name with "../" things.
+ */
+#ifdef OS2
+ only_drive = 0;
+ if (((p = vim_strrchr(fname, '/')) != NULL) ||
+ ((p = vim_strrchr(fname, '\\')) != NULL) ||
+ (((p = vim_strchr(fname, ':')) != NULL) && ++only_drive))
+#else
+ if ((p = vim_strrchr(fname, '/')) != NULL)
+#endif
+ {
+#ifdef HAVE_FCHDIR
+ /*
+ * Use fchdir() if possible, it's said to be faster and more
+ * reliable. But on SunOS 4 it might not work. Check this by
+ * doing a fchdir() right now.
+ */
+ if (!dont_fchdir)
+ {
+ fd = open(".", O_RDONLY | O_EXTRA);
+ if (fd >= 0 && fchdir(fd) < 0)
+ {
+ close(fd);
+ fd = -1;
+ dont_fchdir = TRUE; /* don't try again */
+ }
+ }
+#endif
+ if (
+#ifdef HAVE_FCHDIR
+ fd < 0 &&
+#endif
+ mch_dirname(olddir, MAXPATHL) == FAIL)
+ {
+ p = NULL; /* can't get current dir: don't chdir */
+ retval = FAIL;
+ }
+ else
+ {
+#ifdef OS2
+ /*
+ * compensate for case where ':' from "D:" was the only
+ * path separator detected in the file name; the _next_
+ * character has to be removed, and then restored later.
+ */
+ if (only_drive)
+ p++;
+#endif
+ c = *p;
+ *p = NUL;
+ if (vim_chdir((char *)fname))
+ retval = FAIL;
+ else
+ fname = p + 1;
+ *p = c;
+#ifdef OS2
+ if (only_drive)
+ {
+ p--;
+ if (retval != FAIL)
+ fname--;
+ }
+#endif
+ }
+ }
+ if (mch_dirname(buf, len) == FAIL)
+ {
+ retval = FAIL;
+ *buf = NUL;
+ }
+ l = STRLEN(buf);
+ if (l && buf[l - 1] != '/')
+ STRCAT(buf, "/");
+ if (p != NULL)
+ {
+#ifdef HAVE_FCHDIR
+ if (fd >= 0)
+ {
+ fchdir(fd);
+ close(fd);
+ }
+ else
+#endif
+ vim_chdir((char *)olddir);
+ }
+ }
+ STRCAT(buf, fname);
+#ifdef OS2
+ slash_adjust(buf);
+#endif
+ return retval;
+}
+
+/*
+ * return TRUE is fname is an absolute path name
+ */
+ int
+isFullName(fname)
+ char_u *fname;
+{
+#ifdef __EMX__
+ return _fnisabs(fname);
+#else
+ return (*fname == '/' || *fname == '~');
+#endif
+}
+
+/*
+ * get file permissions for 'name'
+ */
+ long
+getperm(name)
+ char_u *name;
+{
+ struct stat statb;
+
+ if (stat((char *)name, &statb))
+ return -1;
+ return statb.st_mode;
+}
+
+/*
+ * set file permission for 'name' to 'perm'
+ *
+ * return FAIL for failure, OK otherwise
+ */
+ int
+setperm(name, perm)
+ char_u *name;
+ int perm;
+{
+ return (chmod((char *)name, (mode_t)perm) == 0 ? OK : FAIL);
+}
+
+/*
+ * return TRUE if "name" is a directory
+ * return FALSE if "name" is not a directory
+ * return FALSE for error
+ */
+ int
+mch_isdir(name)
+ char_u *name;
+{
+ struct stat statb;
+
+ if (stat((char *)name, &statb))
+ return FALSE;
+#ifdef _POSIX_SOURCE
+ return (S_ISDIR(statb.st_mode) ? TRUE : FALSE);
+#else
+ return ((statb.st_mode & S_IFMT) == S_IFDIR ? TRUE : FALSE);
+#endif
+}
+
+ void
+mch_windexit(r)
+ int r;
+{
+ settmode(0);
+ exiting = TRUE;
+ mch_settitle(oldtitle, oldicon); /* restore xterm title */
+ stoptermcap();
+ flushbuf();
+ ml_close_all(TRUE); /* remove all memfiles */
+ may_core_dump();
+ exit(r);
+}
+
+ static void
+may_core_dump()
+{
+#ifdef SIGQUIT
+ signal(SIGQUIT, SIG_DFL);
+ if (core_dump)
+ kill(getpid(), SIGQUIT); /* force a core dump */
+#endif
+}
+
+static int curr_tmode = 0; /* contains current raw/cooked mode (0 = cooked) */
+
+ void
+mch_settmode(raw)
+ int raw;
+{
+ static int first = TRUE;
+
+ /* Why is NeXT excluded here (and not in unixunix.h)? */
+#if defined(ECHOE) && defined(ICANON) && (defined(HAVE_TERMIO_H) || defined(HAVE_TERMIOS_H)) && !defined(__NeXT__)
+ /* for "new" tty systems */
+# ifdef HAVE_TERMIOS_H
+ static struct termios told;
+ struct termios tnew;
+# else
+ static struct termio told;
+ struct termio tnew;
+# endif
+
+# ifdef TIOCLGET
+ static unsigned long tty_local;
+# endif
+
+ if (raw)
+ {
+ if (first)
+ {
+ first = FALSE;
+# ifdef TIOCLGET
+ ioctl(0, TIOCLGET, &tty_local);
+# endif
+# if defined(HAVE_TERMIOS_H)
+ tcgetattr(0, &told);
+# else
+ ioctl(0, TCGETA, &told);
+# endif
+ }
+ tnew = told;
+ /*
+ * ICRNL enables typing ^V^M
+ */
+ tnew.c_iflag &= ~ICRNL;
+ tnew.c_lflag &= ~(ICANON | ECHO | ISIG | ECHOE
+# if defined(IEXTEN) && !defined(MINT)
+ | IEXTEN /* IEXTEN enables typing ^V on SOLARIS */
+ /* but it breaks function keys on MINT */
+# endif
+ );
+ tnew.c_cc[VMIN] = 1; /* return after 1 char */
+ tnew.c_cc[VTIME] = 0; /* don't wait */
+# if defined(HAVE_TERMIOS_H)
+ tcsetattr(0, TCSANOW, &tnew);
+# else
+ ioctl(0, TCSETA, &tnew);
+# endif
+ }
+ else
+ {
+# if defined(HAVE_TERMIOS_H)
+ tcsetattr(0, TCSANOW, &told);
+# else
+ ioctl(0, TCSETA, &told);
+# endif
+# ifdef TIOCLGET
+ ioctl(0, TIOCLSET, &tty_local);
+# endif
+ }
+#else
+# ifndef TIOCSETN
+# define TIOCSETN TIOCSETP /* for hpux 9.0 */
+# endif
+ /* for "old" tty systems */
+ static struct sgttyb ttybold;
+ struct sgttyb ttybnew;
+
+ if (raw)
+ {
+ if (first)
+ {
+ first = FALSE;
+ ioctl(0, TIOCGETP, &ttybold);
+ }
+ ttybnew = ttybold;
+ ttybnew.sg_flags &= ~(CRMOD | ECHO);
+ ttybnew.sg_flags |= RAW;
+ ioctl(0, TIOCSETN, &ttybnew);
+ }
+ else
+ ioctl(0, TIOCSETN, &ttybold);
+#endif
+ curr_tmode = raw;
+}
+
+/*
+ * Try to get the code for "t_kb" from the stty setting
+ *
+ * Even if termcap claims a backspace key, the user's setting *should*
+ * prevail. stty knows more about reality than termcap does, and if
+ * somebody's usual erase key is DEL (which, for most BSD users, it will
+ * be), they're going to get really annoyed if their erase key starts
+ * doing forward deletes for no reason. (Eric Fischer)
+ */
+ void
+get_stty()
+{
+ char_u buf[2];
+ char_u *p;
+
+ /* Why is NeXT excluded here (and not in unixunix.h)? */
+#if defined(ECHOE) && defined(ICANON) && (defined(HAVE_TERMIO_H) || defined(HAVE_TERMIOS_H)) && !defined(__NeXT__)
+ /* for "new" tty systems */
+# ifdef HAVE_TERMIOS_H
+ struct termios keys;
+# else
+ struct termio keys;
+# endif
+
+# if defined(HAVE_TERMIOS_H)
+ if (tcgetattr(0, &keys) != -1)
+# else
+ if (ioctl(0, TCGETA, &keys) != -1)
+# endif
+ {
+ buf[0] = keys.c_cc[VERASE];
+#else
+ /* for "old" tty systems */
+ struct sgttyb keys;
+
+ if (ioctl(0, TIOCGETP, &keys) != -1)
+ {
+ buf[0] = keys.sg_erase;
+#endif
+ buf[1] = NUL;
+ add_termcode((char_u *)"kb", buf);
+
+ /*
+ * If <BS> and <DEL> are now the same, redefine <DEL>.
+ */
+ p = find_termcode((char_u *)"kD");
+ if (p != NULL && p[0] == buf[0] && p[1] == buf[1])
+ do_fixdel();
+ }
+#if 0
+ } /* to keep cindent happy */
+#endif
+}
+
+#ifdef USE_MOUSE
+/*
+ * set mouse clicks on or off (only works for xterms)
+ */
+ void
+mch_setmouse(on)
+ int on;
+{
+ static int ison = FALSE;
+
+ if (on == ison) /* return quickly if nothing to do */
+ return;
+
+ if (is_xterm(term_strings[KS_NAME]))
+ {
+ if (on)
+ outstrn((char_u *)"\033[?1000h"); /* xterm: enable mouse events */
+ else
+ outstrn((char_u *)"\033[?1000l"); /* xterm: disable mouse events */
+ }
+ ison = on;
+}
+#endif
+
+/*
+ * set screen mode, always fails.
+ */
+ int
+mch_screenmode(arg)
+ char_u *arg;
+{
+ EMSG("Screen mode setting not supported");
+ return FAIL;
+}
+
+/*
+ * Try to get the current window size:
+ * 1. with an ioctl(), most accurate method
+ * 2. from the environment variables LINES and COLUMNS
+ * 3. from the termcap
+ * 4. keep using the old values
+ */
+ int
+mch_get_winsize()
+{
+ int old_Rows = Rows;
+ int old_Columns = Columns;
+ char_u *p;
+
+#ifdef USE_GUI
+ if (gui.in_use)
+ return gui_mch_get_winsize();
+#endif
+
+ Columns = 0;
+ Rows = 0;
+
+/*
+ * For OS/2 use _scrsize().
+ */
+# ifdef __EMX__
+ {
+ int s[2];
+ _scrsize(s);
+ Columns = s[0];
+ Rows = s[1];
+ }
+# endif
+
+/*
+ * 1. try using an ioctl. It is the most accurate method.
+ *
+ * Try using TIOCGWINSZ first, some systems that have it also define TIOCGSIZE
+ * but don't have a struct ttysize.
+ */
+# ifdef TIOCGWINSZ
+ {
+ struct winsize ws;
+
+ if (ioctl(0, TIOCGWINSZ, &ws) == 0)
+ {
+ Columns = ws.ws_col;
+ Rows = ws.ws_row;
+ }
+ }
+# else /* TIOCGWINSZ */
+# ifdef TIOCGSIZE
+ {
+ struct ttysize ts;
+
+ if (ioctl(0, TIOCGSIZE, &ts) == 0)
+ {
+ Columns = ts.ts_cols;
+ Rows = ts.ts_lines;
+ }
+ }
+# endif /* TIOCGSIZE */
+# endif /* TIOCGWINSZ */
+
+/*
+ * 2. get size from environment
+ */
+ if (Columns == 0 || Rows == 0)
+ {
+ if ((p = (char_u *)getenv("LINES")))
+ Rows = atoi((char *)p);
+ if ((p = (char_u *)getenv("COLUMNS")))
+ Columns = atoi((char *)p);
+ }
+
+#ifdef HAVE_TGETENT
+/*
+ * 3. try reading the termcap
+ */
+ if (Columns == 0 || Rows == 0)
+ getlinecol(); /* get "co" and "li" entries from termcap */
+#endif
+
+/*
+ * 4. If everything fails, use the old values
+ */
+ if (Columns <= 0 || Rows <= 0)
+ {
+ Columns = old_Columns;
+ Rows = old_Rows;
+ return FAIL;
+ }
+
+ check_winsize();
+
+/* if size changed: screenalloc will allocate new screen buffers */
+ return OK;
+}
+
+ void
+mch_set_winsize()
+{
+ char_u string[10];
+
+#ifdef USE_GUI
+ if (gui.in_use)
+ {
+ gui_mch_set_winsize();
+ return;
+ }
+#endif
+
+ /* try to set the window size to Rows and Columns */
+ if (is_iris_ansi(term_strings[KS_NAME]))
+ {
+ sprintf((char *)string, "\033[203;%ld;%ld/y", Rows, Columns);
+ outstrn(string);
+ flushbuf();
+ screen_start(); /* don't know where cursor is now */
+ }
+}
+
+ int
+call_shell(cmd, options)
+ char_u *cmd;
+ int options; /* SHELL_FILTER if called by do_filter() */
+ /* SHELL_COOKED if term needs cooked mode */
+ /* SHELL_EXPAND if called by ExpandWildCards() */
+{
+#ifdef USE_SYSTEM /* use system() to start the shell: simple but slow */
+
+ int x;
+#ifndef __EMX__
+ char_u newcmd[1024]; /* only needed for unix */
+#else /* __EMX__ */
+ /*
+ * Set the preferred shell in the EMXSHELL environment variable (but
+ * only if it is different from what is already in the environment).
+ * Emx then takes care of whether to use "/c" or "-c" in an
+ * intelligent way. Simply pass the whole thing to emx's system() call.
+ * Emx also starts an interactive shell if system() is passed an empty
+ * string.
+ */
+ char_u *p, *old;
+
+ if (((old = getenv("EMXSHELL")) == NULL) || strcmp(old, p_sh))
+ {
+ /* should check HAVE_SETENV, but I know we don't have it. */
+ p = alloc(10 + strlen(p_sh));
+ if (p)
+ {
+ sprintf(p, "EMXSHELL=%s", p_sh);
+ putenv(p); /* don't free the pointer! */
+ }
+ }
+#endif
+
+ flushbuf();
+
+ if (options & SHELL_COOKED)
+ settmode(0); /* set to cooked mode */
+
+#ifdef __EMX__
+ if (cmd == NULL)
+ x = system(""); /* this starts an interactive shell in emx */
+ else
+ x = system(cmd);
+ if (x == -1) /* system() returns -1 when error occurs in starting shell */
+ {
+ MSG_OUTSTR("\nCannot execute shell ");
+ msg_outstr(p_sh);
+ msg_outchar('\n');
+ }
+#else /* not __EMX__ */
+ if (cmd == NULL)
+ x = system(p_sh);
+ else
+ {
+ sprintf(newcmd, "%s %s -c \"%s\"", p_sh,
+ extra_shell_arg == NULL ? "" : (char *)extra_shell_arg,
+ (char *)cmd);
+ x = system(newcmd);
+ }
+ if (x == 127)
+ {
+ MSG_OUTSTR("\nCannot execute shell sh\n");
+ }
+#endif /* __EMX__ */
+ else if (x && !expand_interactively)
+ {
+ msg_outchar('\n');
+ msg_outnum((long)x);
+ MSG_OUTSTR(" returned\n");
+ }
+
+ settmode(1); /* set to raw mode */
+#ifdef OS2
+ /* external command may change the window size in OS/2, so check it */
+ mch_get_winsize();
+#endif
+ resettitle();
+ return (x ? FAIL : OK);
+
+#else /* USE_SYSTEM */ /* don't use system(), use fork()/exec() */
+
+#define EXEC_FAILED 122 /* Exit code when shell didn't execute. Don't use
+ 127, some shell use that already */
+
+ char_u newcmd[1024];
+ int pid;
+#ifdef HAVE_UNION_WAIT
+ union wait status;
+#else
+ int status = -1;
+#endif
+ int retval = FAIL;
+ char **argv = NULL;
+ int argc;
+ int i;
+ char_u *p;
+ int inquote;
+#ifdef USE_GUI
+ int pty_master_fd = -1; /* for pty's */
+ int pty_slave_fd = -1;
+ char *tty_name;
+ int fd_toshell[2]; /* for pipes */
+ int fd_fromshell[2];
+ int pipe_error = FALSE;
+# ifdef HAVE_SETENV
+ char envbuf[50];
+# else
+ static char envbuf_Rows[20];
+ static char envbuf_Columns[20];
+# endif
+#endif
+ int did_settmode = FALSE; /* TRUE when settmode(1) called */
+
+ flushbuf();
+ if (options & SHELL_COOKED)
+ settmode(0); /* set to cooked mode */
+
+ /*
+ * 1: find number of arguments
+ * 2: separate them and built argv[]
+ */
+ STRCPY(newcmd, p_sh);
+ for (i = 0; i < 2; ++i)
+ {
+ p = newcmd;
+ inquote = FALSE;
+ argc = 0;
+ for (;;)
+ {
+ if (i == 1)
+ argv[argc] = (char *)p;
+ ++argc;
+ while (*p && (inquote || (*p != ' ' && *p != TAB)))
+ {
+ if (*p == '"')
+ inquote = !inquote;
+ ++p;
+ }
+ if (*p == NUL)
+ break;
+ if (i == 1)
+ *p++ = NUL;
+ p = skipwhite(p);
+ }
+ if (i == 0)
+ {
+ argv = (char **)alloc((unsigned)((argc + 4) * sizeof(char *)));
+ if (argv == NULL) /* out of memory */
+ goto error;
+ }
+ }
+ if (cmd != NULL)
+ {
+ if (extra_shell_arg != NULL)
+ argv[argc++] = (char *)extra_shell_arg;
+ argv[argc++] = "-c";
+ argv[argc++] = (char *)cmd;
+ }
+ argv[argc] = NULL;
+
+#ifdef tower32
+ /*
+ * reap lost children (seems necessary on NCR Tower,
+ * although I don't have a clue why...) (Slootman)
+ */
+ while (wait(&status) != 0 && errno != ECHILD)
+ ; /* do it again, if necessary */
+#endif
+
+#ifdef USE_GUI
+/*
+ * First try at using a pseudo-tty to get the stdin/stdout of the executed
+ * command into the current window for the GUI.
+ */
+
+ if (gui.in_use && show_shell_mess)
+ {
+ /*
+ * Try to open a master pty.
+ * If this works, open the slave pty.
+ * If the slave can't be opened, close the master pty.
+ */
+ if (p_guipty)
+ {
+ pty_master_fd = OpenPTY(&tty_name); /* open pty */
+ if (pty_master_fd >= 0 && ((pty_slave_fd =
+ open(tty_name, O_RDWR | O_EXTRA)) < 0))
+ {
+ close(pty_master_fd);
+ pty_master_fd = -1;
+ }
+ }
+ /*
+ * If opening a pty didn't work, try using pipes.
+ */
+ if (pty_master_fd < 0)
+ {
+ pipe_error = (pipe(fd_toshell) < 0);
+ if (!pipe_error) /* pipe create OK */
+ {
+ pipe_error = (pipe(fd_fromshell) < 0);
+ if (pipe_error) /* pipe create failed */
+ {
+ close(fd_toshell[0]);
+ close(fd_toshell[1]);
+ }
+ }
+ if (pipe_error)
+ {
+ MSG_OUTSTR("\nCannot create pipes\n");
+ flushbuf();
+ }
+ }
+ }
+
+ if (!pipe_error) /* pty or pipe opened or not used */
+#endif
+
+ {
+ if ((pid = fork()) == -1) /* maybe we should use vfork() */
+ {
+ MSG_OUTSTR("\nCannot fork\n");
+#ifdef USE_GUI
+ if (gui.in_use && show_shell_mess)
+ {
+ if (pty_master_fd >= 0) /* close the pseudo tty */
+ {
+ close(pty_master_fd);
+ close(pty_slave_fd);
+ }
+ else /* close the pipes */
+ {
+ close(fd_toshell[0]);
+ close(fd_toshell[1]);
+ close(fd_fromshell[0]);
+ close(fd_fromshell[1]);
+ }
+ }
+#endif
+ }
+ else if (pid == 0) /* child */
+ {
+ reset_signals(); /* handle signals normally */
+ if (!show_shell_mess)
+ {
+ int fd;
+
+ /*
+ * Don't want to show any message from the shell. Can't just
+ * close stdout and stderr though, because some systems will
+ * break if you try to write to them after that, so we must
+ * use dup() to replace them with something else -- webb
+ */
+ fd = open("/dev/null", O_WRONLY | O_EXTRA);
+ fclose(stdout);
+ fclose(stderr);
+
+ /*
+ * If any of these open()'s and dup()'s fail, we just continue
+ * anyway. It's not fatal, and on most systems it will make
+ * no difference at all. On a few it will cause the execvp()
+ * to exit with a non-zero status even when the completion
+ * could be done, which is nothing too serious. If the open()
+ * or dup() failed we'd just do the same thing ourselves
+ * anyway -- webb
+ */
+ if (fd >= 0)
+ {
+ /* To replace stdout (file descriptor 1) */
+ dup(fd);
+
+ /* To replace stderr (file descriptor 2) */
+ dup(fd);
+
+ /* Don't need this now that we've duplicated it */
+ close(fd);
+ }
+ }
+#ifdef USE_GUI
+ else if (gui.in_use)
+ {
+
+#ifdef HAVE_SETSID
+ (void)setsid();
+#endif
+#ifdef TIOCSCTTY
+ /* try to become controlling tty (probably doesn't work,
+ * unless run by root) */
+ ioctl(pty_slave_fd, TIOCSCTTY, (char *)NULL);
+#endif
+ /* Simulate to have a dumb terminal (for now) */
+#ifdef HAVE_SETENV
+ setenv("TERM", "dumb", 1);
+ sprintf((char *)envbuf, "%ld", Rows);
+ setenv("ROWS", (char *)envbuf, 1);
+ sprintf((char *)envbuf, "%ld", Columns);
+ setenv("COLUMNS", (char *)envbuf, 1);
+#else
+ /*
+ * Putenv does not copy the string, it has to remain valid.
+ * Use a static array to avoid loosing allocated memory.
+ */
+ putenv("TERM=dumb");
+ sprintf(envbuf_Rows, "ROWS=%ld", Rows);
+ putenv(envbuf_Rows);
+ sprintf(envbuf_Columns, "COLUMNS=%ld", Columns);
+ putenv(envbuf_Columns);
+#endif
+
+ if (pty_master_fd >= 0)
+ {
+ close(pty_master_fd); /* close master side of pty */
+
+ /* set up stdin/stdout/stderr for the child */
+ close(0);
+ dup(pty_slave_fd);
+ close(1);
+ dup(pty_slave_fd);
+ close(2);
+ dup(pty_slave_fd);
+
+ close(pty_slave_fd); /* has been dupped, close it now */
+ }
+ else
+ {
+ /* set up stdin for the child */
+ close(fd_toshell[1]);
+ close(0);
+ dup(fd_toshell[0]);
+ close(fd_toshell[0]);
+
+ /* set up stdout for the child */
+ close(fd_fromshell[0]);
+ close(1);
+ dup(fd_fromshell[1]);
+ close(fd_fromshell[1]);
+
+ /* set up stderr for the child */
+ close(2);
+ dup(1);
+ }
+ }
+#endif
+ /*
+ * There is no type cast for the argv, because the type may be
+ * different on different machines. This may cause a warning
+ * message with strict compilers, don't worry about it.
+ */
+ execvp(argv[0], argv);
+ exit(EXEC_FAILED); /* exec failed, return failure code */
+ }
+ else /* parent */
+ {
+ /*
+ * While child is running, ignore terminating signals.
+ */
+ catch_signals(SIG_IGN);
+
+#ifdef USE_GUI
+
+ /*
+ * For the GUI we redirect stdin, stdout and stderr to our window.
+ */
+ if (gui.in_use && show_shell_mess)
+ {
+#define BUFLEN 100 /* length for buffer, pseudo tty limit is 128 */
+ char_u buffer[BUFLEN];
+ int len;
+ int p_more_save;
+ int old_State;
+ int read_count;
+ int c;
+ int toshell_fd;
+ int fromshell_fd;
+
+ if (pty_master_fd >= 0)
+ {
+ close(pty_slave_fd); /* close slave side of pty */
+ fromshell_fd = pty_master_fd;
+ toshell_fd = dup(pty_master_fd);
+ }
+ else
+ {
+ close(fd_toshell[0]);
+ close(fd_fromshell[1]);
+ toshell_fd = fd_toshell[1];
+ fromshell_fd = fd_fromshell[0];
+ }
+
+ /*
+ * Write to the child if there are typed characters.
+ * Read from the child if there are characters available.
+ * Repeat the reading a few times if more characters are
+ * available. Need to check for typed keys now and then, but
+ * not too often (delays when no chars are available).
+ * This loop is quit if no characters can be read from the pty
+ * (WaitForChar detected special condition), or there are no
+ * characters available and the child has exited.
+ * Only check if the child has exited when there is no more
+ * output. The child may exit before all the output has
+ * been printed.
+ *
+ * Currently this busy loops!
+ * This can probably dead-lock when the write blocks!
+ */
+ p_more_save = p_more;
+ p_more = FALSE;
+ old_State = State;
+ State = EXTERNCMD; /* don't redraw at window resize */
+
+ for (;;)
+ {
+ /*
+ * Check if keys have been typed, write them to the child
+ * if there are any. Don't do this if we are expanding
+ * wild cards (would eat typeahead).
+ */
+ if (!(options & SHELL_EXPAND) &&
+ (len = mch_inchar(buffer, BUFLEN - 1, 10)) != 0)
+ {
+ /*
+ * For pipes:
+ * Check for CTRL-C: sent interrupt signal to child.
+ * Check for CTRL-D: EOF, close pipe to child.
+ */
+ if (len == 1 && (pty_master_fd < 0 || cmd != NULL))
+ {
+#ifdef SIGINT
+ if (buffer[0] == Ctrl('C'))
+ {
+ /* Use both kill() and killpg(), in case one
+ * of the two fails */
+ kill(pid, SIGINT);
+# ifdef HAVE_KILLPG
+ killpg(0, SIGINT);
+# endif
+ }
+#endif
+ if (pty_master_fd < 0 && toshell_fd >= 0 &&
+ buffer[0] == Ctrl('D'))
+ {
+ close(toshell_fd);
+ toshell_fd = -1;
+ }
+ }
+
+ /* replace K_BS by <BS> and K_DEL by <DEL> */
+ for (i = 0; i < len; ++i)
+ {
+ if (buffer[i] == CSI && len - i > 2)
+ {
+ c = TERMCAP2KEY(buffer[i + 1], buffer[i + 2]);
+ if (c == K_DEL || c == K_BS)
+ {
+ vim_memmove(buffer + i + 1, buffer + i + 3,
+ (size_t)(len - i - 2));
+ if (c == K_DEL)
+ buffer[i] = DEL;
+ else
+ buffer[i] = Ctrl('H');
+ len -= 2;
+ }
+ }
+ else if (buffer[i] == '\r')
+ buffer[i] = '\n';
+ }
+
+ /*
+ * For pipes: echo the typed characters.
+ * For a pty this does not seem to work.
+ */
+ if (pty_master_fd < 0)
+ {
+ for (i = 0; i < len; ++i)
+ if (buffer[i] == '\n' || buffer[i] == '\b')
+ msg_outchar(buffer[i]);
+ else
+ msg_outtrans_len(buffer + i, 1);
+ windgoto(msg_row, msg_col);
+ flushbuf();
+ }
+
+ /*
+ * Write the characters to the child, unless EOF has
+ * been typed for pipes. Ignore errors.
+ */
+ if (toshell_fd >= 0)
+ write(toshell_fd, (char *)buffer, (size_t)len);
+ }
+
+ /*
+ * Check if the child has any characters to be printed.
+ * Read them and write them to our window.
+ * Repeat this a few times as long as there is something
+ * to do, avoid the 10ms wait for mch_inchar().
+ * TODO: This should handle escape sequences.
+ */
+ for (read_count = 0; read_count < 10 &&
+ RealWaitForChar(fromshell_fd, 10); ++read_count)
+ {
+ len = read(fromshell_fd, (char *)buffer,
+ (size_t)BUFLEN);
+ if (len == 0) /* end of file */
+ goto finished;
+ buffer[len] = NUL;
+ msg_outstr(buffer);
+ windgoto(msg_row, msg_col);
+ cursor_on();
+ flushbuf();
+ }
+
+ /*
+ * Check if the child still exists when we finished
+ * outputting all characters.
+ */
+ if (read_count == 0 &&
+#ifdef __NeXT__
+ wait4(pid, &status, WNOHANG, (struct rusage *) 0) &&
+#else
+ waitpid(pid, &status, WNOHANG) &&
+#endif
+ WIFEXITED(status))
+ break;
+ }
+finished:
+ p_more = p_more_save;
+ State = old_State;
+ if (toshell_fd >= 0)
+ close(toshell_fd);
+ close(fromshell_fd);
+ }
+#endif /* USE_GUI */
+
+ /*
+ * Wait until child has exited.
+ */
+#ifdef ECHILD
+ /* Don't stop waiting when a signal (e.g. SIGWINCH) is received. */
+ while (wait(&status) == -1 && errno != ECHILD)
+ ;
+#else
+ wait(&status);
+#endif
+ /*
+ * Set to raw mode right now, otherwise a CTRL-C after
+ * catch_signals will kill Vim.
+ */
+ settmode(1);
+ did_settmode = TRUE;
+ catch_signals(deathtrap);
+
+ /*
+ * Check the window size, in case it changed while executing the
+ * external command.
+ */
+ mch_get_winsize();
+
+ if (WIFEXITED(status))
+ {
+ i = WEXITSTATUS(status);
+ if (i)
+ {
+ if (i == EXEC_FAILED)
+ {
+ MSG_OUTSTR("\nCannot execute shell ");
+ msg_outtrans(p_sh);
+ msg_outchar('\n');
+ }
+ else if (!expand_interactively)
+ {
+ msg_outchar('\n');
+ msg_outnum((long)i);
+ MSG_OUTSTR(" returned\n");
+ }
+ }
+ else
+ retval = OK;
+ }
+ else
+ MSG_OUTSTR("\nCommand terminated\n");
+ }
+ }
+ vim_free(argv);
+
+error:
+ if (!did_settmode)
+ settmode(1); /* always set to raw mode */
+ resettitle();
+
+ return retval;
+
+#endif /* USE_SYSTEM */
+}
+
+/*
+ * The input characters are buffered to be able to check for a CTRL-C.
+ * This should be done with signals, but I don't know how to do that in
+ * a portable way for a tty in RAW mode.
+ */
+
+/*
+ * Internal typeahead buffer. Includes extra space for long key code
+ * descriptions which would otherwise overflow. The buffer is considered full
+ * when only this extra space (or part of it) remains.
+ */
+#define INBUFLEN 250
+
+static char_u inbuf[INBUFLEN + MAX_KEY_CODE_LEN];
+static int inbufcount = 0; /* number of chars in inbuf[] */
+
+/*
+ * is_input_buf_full(), is_input_buf_empty(), add_to_input_buf(), and
+ * trash_input_buf() are functions for manipulating the input buffer. These
+ * are used by the gui_* calls when a GUI is used to handle keyboard input.
+ *
+ * NOTE: These functions will be identical in msdos.c etc, and should probably
+ * be taken out and put elsewhere, but at the moment inbuf is only local.
+ */
+
+ int
+is_input_buf_full()
+{
+ return (inbufcount >= INBUFLEN);
+}
+
+ int
+is_input_buf_empty()
+{
+ return (inbufcount == 0);
+}
+
+/* Add the given bytes to the input buffer */
+ void
+add_to_input_buf(s, len)
+ char_u *s;
+ int len;
+{
+ if (inbufcount + len > INBUFLEN + MAX_KEY_CODE_LEN)
+ return; /* Shouldn't ever happen! */
+
+ while (len--)
+ inbuf[inbufcount++] = *s++;
+}
+
+/* Remove everything from the input buffer. Called when ^C is found */
+ void
+trash_input_buf()
+{
+ inbufcount = 0;
+}
+
+ static int
+Read(buf, maxlen)
+ char_u *buf;
+ long maxlen;
+{
+ if (inbufcount == 0) /* if the buffer is empty, fill it */
+ fill_inbuf(TRUE);
+ if (maxlen > inbufcount)
+ maxlen = inbufcount;
+ vim_memmove(buf, inbuf, (size_t)maxlen);
+ inbufcount -= maxlen;
+ if (inbufcount)
+ vim_memmove(inbuf, inbuf + maxlen, (size_t)inbufcount);
+ return (int)maxlen;
+}
+
+ void
+mch_breakcheck()
+{
+#ifdef USE_GUI
+ if (gui.in_use)
+ {
+ gui_mch_update();
+ return;
+ }
+#endif /* USE_GUI */
+
+/*
+ * Check for CTRL-C typed by reading all available characters.
+ * In cooked mode we should get SIGINT, no need to check.
+ */
+ if (curr_tmode && RealWaitForChar(0, 0L)) /* if characters available */
+ fill_inbuf(FALSE);
+}
+
+ static void
+fill_inbuf(exit_on_error)
+ int exit_on_error;
+{
+ int len;
+ int try;
+
+#ifdef USE_GUI
+ if (gui.in_use)
+ {
+ gui_mch_update();
+ return;
+ }
+#endif
+ if (is_input_buf_full())
+ return;
+ /*
+ * Fill_inbuf() is only called when we really need a character.
+ * If we can't get any, but there is some in the buffer, just return.
+ * If we can't get any, and there isn't any in the buffer, we give up and
+ * exit Vim.
+ */
+ for (try = 0; try < 100; ++try)
+ {
+ len = read(0, (char *)inbuf + inbufcount,
+ (size_t)(INBUFLEN - inbufcount));
+ if (len > 0)
+ break;
+ if (!exit_on_error)
+ return;
+ }
+ if (len <= 0)
+ {
+ windgoto((int)Rows - 1, 0);
+ fprintf(stderr, "Vim: Error reading input, exiting...\n");
+ ml_sync_all(FALSE, TRUE); /* preserve all swap files */
+ getout(1);
+ }
+ while (len-- > 0)
+ {
+ /*
+ * if a CTRL-C was typed, remove it from the buffer and set got_int
+ */
+ if (inbuf[inbufcount] == 3)
+ {
+ /* remove everything typed before the CTRL-C */
+ vim_memmove(inbuf, inbuf + inbufcount, (size_t)(len + 1));
+ inbufcount = 0;
+ got_int = TRUE;
+ }
+ ++inbufcount;
+ }
+}
+
+/*
+ * Wait "msec" msec until a character is available from the keyboard or from
+ * inbuf[]. msec == -1 will block forever.
+ * When a GUI is being used, this will never get called -- webb
+ */
+
+ static int
+WaitForChar(msec)
+ long msec;
+{
+ if (inbufcount) /* something in inbuf[] */
+ return 1;
+ return RealWaitForChar(0, msec);
+}
+
+/*
+ * Wait "msec" msec until a character is available from file descriptor "fd".
+ * Time == -1 will block forever.
+ * When a GUI is being used, this will not be used for input -- webb
+ */
+ static int
+RealWaitForChar(fd, msec)
+ int fd;
+ long msec;
+{
+#ifndef HAVE_SELECT
+ struct pollfd fds;
+
+ fds.fd = fd;
+ fds.events = POLLIN;
+ return (poll(&fds, 1, (int)msec) > 0); /* is this correct when fd != 0?? */
+#else
+ struct timeval tv;
+ fd_set rfds, efds;
+
+# ifdef __EMX__
+ /* don't check for incoming chars if not in raw mode, because select()
+ * always returns TRUE then (in some version of emx.dll) */
+ if (curr_tmode == 0)
+ return 0;
+# endif
+
+ if (msec >= 0)
+ {
+ tv.tv_sec = msec / 1000;
+ tv.tv_usec = (msec % 1000) * (1000000/1000);
+ }
+
+ /*
+ * Select on ready for reading and exceptional condition (end of file).
+ */
+ FD_ZERO(&rfds); /* calls bzero() on a sun */
+ FD_ZERO(&efds);
+ FD_SET(fd, &rfds);
+ FD_SET(fd, &efds);
+ return (select(fd + 1, &rfds, NULL, &efds, (msec >= 0) ? &tv : NULL) > 0);
+#endif
+}
+
+/*
+ * ExpandWildCards() - this code does wild-card pattern matching using the shell
+ *
+ * return OK for success, FAIL for error (you may lose some memory) and put
+ * an error message in *file.
+ *
+ * num_pat is number of input patterns
+ * pat is array of pointers to input patterns
+ * num_file is pointer to number of matched file names
+ * file is pointer to array of pointers to matched file names
+ * On Unix we do not check for files only yet
+ * list_notfound is ignored
+ */
+
+extern char *mktemp __ARGS((char *));
+#ifndef SEEK_SET
+# define SEEK_SET 0
+#endif
+#ifndef SEEK_END
+# define SEEK_END 2
+#endif
+
+ int
+ExpandWildCards(num_pat, pat, num_file, file, files_only, list_notfound)
+ int num_pat;
+ char_u **pat;
+ int *num_file;
+ char_u ***file;
+ int files_only;
+ int list_notfound;
+{
+ int i;
+ size_t len;
+ char_u *p;
+#ifdef __EMX__
+# define EXPL_ALLOC_INC 16
+ char_u **expl_files;
+ size_t files_alloced, files_free;
+
+ *num_file = 0; /* default: no files found */
+ files_alloced = EXPL_ALLOC_INC; /* how much space is allocated */
+ files_free = EXPL_ALLOC_INC; /* how much space is not used */
+ *file = (char_u **) alloc(sizeof(char_u **) * files_alloced);
+ if (!*file)
+ {
+ emsg(e_outofmem);
+ return FAIL;
+ }
+
+ for (; num_pat > 0; num_pat--, pat++)
+ {
+ expl_files = NULL;
+ if (vim_strchr(*pat, '$') || vim_strchr(*pat, '~'))
+ {
+ /* expand environment var or home dir */
+ char_u *buf = alloc(1024);
+ if (!buf)
+ {
+ emsg(e_outofmem);
+ return FAIL;
+ }
+ expand_env(*pat, buf, 1024);
+ if (mch_has_wildcard(buf)) /* still wildcards in there? */
+ {
+ expl_files = (char_u **)_fnexplode(buf);
+ }
+ if (expl_files == NULL)
+ {
+ /*
+ * If no wildcard still remaining, simply add
+ * the pattern to the results.
+ * If wildcard did not match, add the pattern to
+ * the list of results anyway. This way, doing
+ * :n exist.c notexist*
+ * will at least edits exist.c and then say
+ * notexist* [new file]
+ */
+ expl_files = (char_u **)alloc(sizeof(char_u **) * 2);
+ expl_files[0] = strsave(buf);
+ expl_files[1] = NULL;
+ }
+ vim_free(buf);
+ }
+ else
+ {
+ expl_files = (char_u **)_fnexplode(*pat);
+ if (expl_files == NULL)
+ {
+ /* see above for explanation */
+ expl_files = (char_u **)alloc(sizeof(char_u **) * 2);
+ expl_files[0] = strsave(*pat);
+ expl_files[1] = NULL;
+ }
+ }
+ if (!expl_files)
+ {
+ /* Can't happen */
+ char_u msg[128];
+ sprintf(msg, "%s (unix.c:%d)", e_internal, __LINE__);
+ emsg(msg);
+ *file = (char_u **)"";
+ *num_file = 0;
+ return OK;
+ }
+ /*
+ * Count number of names resulting from expansion,
+ * At the same time add a backslash to the end of names that happen to be
+ * directories, and replace slashes with backslashes.
+ */
+ for (i = 0; (p = expl_files[i]) != NULL; i++, (*num_file)++)
+ {
+ if (--files_free == 0)
+ {
+ /* need more room in table of pointers */
+ files_alloced += EXPL_ALLOC_INC;
+ *file = (char_u **) realloc(*file,
+ sizeof(char_u **) * files_alloced);
+ files_free = EXPL_ALLOC_INC;
+ }
+ slash_adjust(p);
+ if (mch_isdir(p))
+ {
+ len = strlen(p);
+ p = realloc(p, len + 2);
+ if (!p)
+ {
+ emsg(e_outofmem);
+ return FAIL;
+ }
+ (*file)[*num_file] = p;
+ p += len;
+ *p++ = '\\';
+ *p = 0;
+ }
+ else
+ {
+ (*file)[*num_file] = strsave(p);
+ }
+ }
+ _fnexplodefree(expl_files);
+ }
+ return OK;
+
+#else /* __EMX__ */
+
+ int dir;
+ char_u tmpname[TMPNAMELEN];
+ char_u *command;
+ FILE *fd;
+ char_u *buffer;
+ int use_glob = FALSE;
+
+ *num_file = 0; /* default: no files found */
+ *file = (char_u **)"";
+
+ /*
+ * If there are no wildcards, just copy the names to allocated memory.
+ * Saves a lot of time, because we don't have to start a new shell.
+ */
+ if (!have_wildcard(num_pat, pat))
+ {
+ *file = (char_u **)alloc(num_pat * sizeof(char_u *));
+ if (*file == NULL)
+ {
+ *file = (char_u **)"";
+ return FAIL;
+ }
+ for (i = 0; i < num_pat; i++)
+ (*file)[i] = strsave(pat[i]);
+ *num_file = num_pat;
+ return OK;
+ }
+
+/*
+ * get a name for the temp file
+ */
+ STRCPY(tmpname, TMPNAME2);
+ if (*mktemp((char *)tmpname) == NUL)
+ {
+ emsg(e_notmp);
+ return FAIL;
+ }
+
+/*
+ * let the shell expand the patterns and write the result into the temp file
+ * If we use csh, glob will work better than echo.
+ */
+ if ((len = STRLEN(p_sh)) >= 3 && STRCMP(p_sh + len - 3, "csh") == 0)
+ use_glob = TRUE;
+
+ len = TMPNAMELEN + 11;
+ for (i = 0; i < num_pat; ++i) /* count the length of the patterns */
+ len += STRLEN(pat[i]) + 3;
+ command = alloc(len);
+ if (command == NULL)
+ return FAIL;
+ if (use_glob)
+ STRCPY(command, "glob >"); /* build the shell command */
+ else
+ STRCPY(command, "echo >"); /* build the shell command */
+ STRCAT(command, tmpname);
+ for (i = 0; i < num_pat; ++i)
+ {
+#ifdef USE_SYSTEM
+ STRCAT(command, " \""); /* need extra quotes because we */
+ STRCAT(command, pat[i]); /* start the shell twice */
+ STRCAT(command, "\"");
+#else
+ STRCAT(command, " ");
+ STRCAT(command, pat[i]);
+#endif
+ }
+ if (expand_interactively)
+ show_shell_mess = FALSE;
+ /*
+ * If we use -f then shell variables set in .cshrc won't get expanded.
+ * vi can do it, so we will too, but it is only necessary if there is a "$"
+ * in one of the patterns, otherwise we can still use the fast option.
+ */
+ if (use_glob && !have_dollars(num_pat, pat)) /* Use csh fast option */
+ extra_shell_arg = (char_u *)"-f";
+ i = call_shell(command, SHELL_EXPAND); /* execute it */
+ extra_shell_arg = NULL;
+ show_shell_mess = TRUE;
+ vim_free(command);
+ if (i == FAIL) /* call_shell failed */
+ {
+ vim_remove(tmpname);
+ /*
+ * With interactive completion, the error message is not printed.
+ * However with USE_SYSTEM, I don't know how to turn off error messages
+ * from the shell, so screen may still get messed up -- webb.
+ */
+#ifndef USE_SYSTEM
+ if (!expand_interactively)
+#endif
+ {
+ must_redraw = CLEAR; /* probably messed up screen */
+ msg_outchar('\n'); /* clear bottom line quickly */
+ cmdline_row = Rows - 1; /* continue on last line */
+ }
+ return FAIL;
+ }
+
+/*
+ * read the names from the file into memory
+ */
+ fd = fopen((char *)tmpname, "r");
+ if (fd == NULL)
+ {
+ emsg2(e_notopen, tmpname);
+ return FAIL;
+ }
+ fseek(fd, 0L, SEEK_END);
+ len = ftell(fd); /* get size of temp file */
+ fseek(fd, 0L, SEEK_SET);
+ buffer = alloc(len + 1);
+ if (buffer == NULL)
+ {
+ vim_remove(tmpname);
+ fclose(fd);
+ return FAIL;
+ }
+ i = fread((char *)buffer, 1, len, fd);
+ fclose(fd);
+ vim_remove(tmpname);
+ if (i != len)
+ {
+ emsg2(e_notread, tmpname);
+ vim_free(buffer);
+ return FAIL;
+ }
+
+ if (use_glob) /* file names are separated with NUL */
+ {
+ buffer[len] = NUL; /* make sure the buffers ends in NUL */
+ i = 0;
+ for (p = buffer; p < buffer + len; ++p)
+ if (*p == NUL) /* count entry */
+ ++i;
+ if (len)
+ ++i; /* count last entry */
+ }
+ else /* file names are separated with SPACE */
+ {
+ buffer[len] = '\n'; /* make sure the buffers ends in NL */
+ p = buffer;
+ for (i = 0; *p != '\n'; ++i) /* count number of entries */
+ {
+ while (*p != ' ' && *p != '\n') /* skip entry */
+ ++p;
+ p = skipwhite(p); /* skip to next entry */
+ }
+ }
+ if (i == 0)
+ {
+ /*
+ * Can happen when using /bin/sh and typing ":e $NO_SUCH_VAR^I".
+ * /bin/sh will happily expand it to nothing rather than returning an
+ * error; and hey, it's good to check anyway -- webb.
+ */
+ vim_free(buffer);
+ *file = (char_u **)"";
+ return FAIL;
+ }
+ *num_file = i;
+ *file = (char_u **)alloc(sizeof(char_u *) * i);
+ if (*file == NULL)
+ {
+ vim_free(buffer);
+ *file = (char_u **)"";
+ return FAIL;
+ }
+
+ /*
+ * Isolate the individual file names.
+ */
+ p = buffer;
+ for (i = 0; i < *num_file; ++i)
+ {
+ (*file)[i] = p;
+ if (use_glob)
+ {
+ while (*p && p < buffer + len) /* skip entry */
+ ++p;
+ ++p; /* skip NUL */
+ }
+ else
+ {
+ while (*p != ' ' && *p != '\n') /* skip entry */
+ ++p;
+ if (*p == '\n') /* last entry */
+ *p = NUL;
+ else
+ {
+ *p++ = NUL;
+ p = skipwhite(p); /* skip to next entry */
+ }
+ }
+ }
+
+ /*
+ * Move the file names to allocated memory.
+ */
+ for (i = 0; i < *num_file; ++i)
+ {
+ /* Require the files to exist. Helps when using /bin/sh */
+ if (expand_interactively)
+ {
+ struct stat st;
+ int j;
+
+ if (stat((char *)((*file)[i]), &st) < 0)
+ {
+ for (j = i; j + 1 < *num_file; ++j)
+ (*file)[j] = (*file)[j + 1];
+ --*num_file;
+ --i;
+ continue;
+ }
+ }
+
+ /* if file doesn't exist don't add '/' */
+ dir = (mch_isdir((*file)[i]));
+ p = alloc((unsigned)(STRLEN((*file)[i]) + 1 + dir));
+ if (p)
+ {
+ STRCPY(p, (*file)[i]);
+ if (dir)
+ STRCAT(p, "/");
+ }
+ (*file)[i] = p;
+ }
+ vim_free(buffer);
+
+ if (*num_file == 0) /* rejected all entries */
+ {
+ vim_free(*file);
+ *file = (char_u **)"";
+ return FAIL;
+ }
+
+ return OK;
+
+#endif /* __EMX__ */
+}
+
+ int
+mch_has_wildcard(p)
+ char_u *p;
+{
+ for ( ; *p; ++p)
+ {
+ if (*p == '\\' && p[1] != NUL)
+ ++p;
+ else if (vim_strchr((char_u *)"*?[{`~$", *p) != NULL)
+ return TRUE;
+ }
+ return FALSE;
+}
+
+#ifndef __EMX__
+ static int
+have_wildcard(num, file)
+ int num;
+ char_u **file;
+{
+ register int i;
+
+ for (i = 0; i < num; i++)
+ if (mch_has_wildcard(file[i]))
+ return 1;
+ return 0;
+}
+
+ static int
+have_dollars(num, file)
+ int num;
+ char_u **file;
+{
+ register int i;
+
+ for (i = 0; i < num; i++)
+ if (vim_strchr(file[i], '$') != NULL)
+ return TRUE;
+ return FALSE;
+}
+#endif /* ifndef __EMX__ */
+
+#ifndef HAVE_RENAME
+/*
+ * Scaled-down version of rename, which is missing in Xenix.
+ * This version can only move regular files and will fail if the
+ * destination exists.
+ */
+ int
+rename(src, dest)
+ const char *src, *dest;
+{
+ struct stat st;
+
+ if (stat(dest, &st) >= 0) /* fail if destination exists */
+ return -1;
+ if (link(src, dest) != 0) /* link file to new name */
+ return -1;
+ if (vim_remove(src) == 0) /* delete link to old name */
+ return 0;
+ return -1;
+}
+#endif /* !HAVE_RENAME */