/* vi:set ts=8 sts=4 sw=4:
 *
 * VIM - Vi IMproved	by Bram Moolenaar
 *	      OS/2 port by Paul Slootman
 *	      VMS merge by Zoltan Arpadffy
 *
 * Do ":help uganda"  in Vim to read copying and usage conditions.
 * Do ":help credits" in Vim to see a list of people who contributed.
 * See README.txt for an overview of the Vim source code.
 */

/*
 * os_unix.c -- code for all flavors of Unix (BSD, SYSV, SVR4, POSIX, ...)
 *	     Also for OS/2, using the excellent EMX package!!!
 *	     Also for BeOS and Atari MiNT.
 *
 * 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 define
 * our own prototype below.
 * Don't use it for the Mac, it causes a warning for precompiled headers.
 * TODO: use a configure check for precompiled headers?
 */
#ifndef __APPLE__
# define select select_declared_wrong
#endif

#include "vim.h"

#ifdef FEAT_MZSCHEME
# include "if_mzsch.h"
#endif

#ifdef HAVE_FCNTL_H
# include <fcntl.h>
#endif

#include "os_unixx.h"	    /* unix includes for os_unix.c only */

#ifdef USE_XSMP
# include <X11/SM/SMlib.h>
#endif

/*
 * Use this prototype for select, some include files have a wrong prototype
 */
#undef select
#ifdef __BEOS__
# define select	beos_select
#endif

#if defined(HAVE_SELECT)
extern int   select __ARGS((int, fd_set *, fd_set *, fd_set *, struct timeval *));
#endif

#ifdef FEAT_MOUSE_GPM
# include <gpm.h>
/* <linux/keyboard.h> contains defines conflicting with "keymap.h",
 * I just copied relevant defines here. A cleaner solution would be to put gpm
 * code into separate file and include there linux/keyboard.h
 */
/* #include <linux/keyboard.h> */
# define KG_SHIFT	0
# define KG_CTRL	2
# define KG_ALT		3
# define KG_ALTGR	1
# define KG_SHIFTL	4
# define KG_SHIFTR	5
# define KG_CTRLL	6
# define KG_CTRLR	7
# define KG_CAPSSHIFT	8

static void gpm_close __ARGS((void));
static int gpm_open __ARGS((void));
static int mch_gpm_process __ARGS((void));
#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

#ifdef FEAT_X11
# include <X11/Xlib.h>
# include <X11/Xutil.h>
# include <X11/Xatom.h>
# ifdef FEAT_XCLIPBOARD
#  include <X11/Intrinsic.h>
#  include <X11/Shell.h>
#  include <X11/StringDefs.h>
static Widget	xterm_Shell = (Widget)0;
static void xterm_update __ARGS((void));
# endif

# if defined(FEAT_XCLIPBOARD) || defined(FEAT_TITLE)
Window	    x11_window = 0;
# endif
Display	    *x11_display = NULL;

# ifdef FEAT_TITLE
static int  get_x11_windis __ARGS((void));
static void set_x11_title __ARGS((char_u *));
static void set_x11_icon __ARGS((char_u *));
# endif
#endif

#ifdef FEAT_TITLE
static int get_x11_title __ARGS((int));
static int get_x11_icon __ARGS((int));

static char_u	*oldtitle = NULL;
static int	did_set_title = FALSE;
static char_u	*oldicon = NULL;
static int	did_set_icon = FALSE;
#endif

static void may_core_dump __ARGS((void));

static int  WaitForChar __ARGS((long));
#if defined(__BEOS__)
int  RealWaitForChar __ARGS((int, long, int *));
#else
static int  RealWaitForChar __ARGS((int, long, int *));
#endif

#ifdef FEAT_XCLIPBOARD
static int do_xterm_trace __ARGS((void));
# define XT_TRACE_DELAY	50	/* delay for xterm tracing */
#endif

static void handle_resize __ARGS((void));

#if defined(SIGWINCH)
static RETSIGTYPE sig_winch __ARGS(SIGPROTOARG);
#endif
#if defined(SIGINT)
static RETSIGTYPE catch_sigint __ARGS(SIGPROTOARG);
#endif
#if defined(SIGPWR)
static RETSIGTYPE catch_sigpwr __ARGS(SIGPROTOARG);
#endif
#if defined(SIGALRM) && defined(FEAT_X11) \
	&& defined(FEAT_TITLE) && !defined(FEAT_GUI_GTK)
# define SET_SIG_ALARM
static RETSIGTYPE sig_alarm __ARGS(SIGPROTOARG);
static int sig_alarm_called;
#endif
static RETSIGTYPE deathtrap __ARGS(SIGPROTOARG);

static void catch_int_signal __ARGS((void));
static void set_signals __ARGS((void));
static void catch_signals __ARGS((RETSIGTYPE (*func_deadly)(), RETSIGTYPE (*func_other)()));
#ifndef __EMX__
static int  have_wildcard __ARGS((int, char_u **));
static int  have_dollars __ARGS((int, char_u **));
#endif

#ifndef __EMX__
static int save_patterns __ARGS((int num_pat, char_u **pat, int *num_file, char_u ***file));
#endif

#ifndef SIG_ERR
# define SIG_ERR	((RETSIGTYPE (*)())-1)
#endif

static int	do_resize = FALSE;
#ifndef __EMX__
static char_u	*extra_shell_arg = NULL;
static int	show_shell_mess = TRUE;
#endif
static int	deadly_signal = 0;	    /* The signal we caught */

static int curr_tmode = TMODE_COOK;	/* contains current terminal mode */

#ifdef USE_XSMP
typedef struct
{
    SmcConn smcconn;	    /* The SM connection ID */
    IceConn iceconn;	    /* The ICE connection ID */
    Bool save_yourself;     /* If we're in the middle of a save_yourself */
    Bool shutdown;	    /* If we're in shutdown mode */
} xsmp_config_T;

static xsmp_config_T xsmp;
#endif

#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 signalinfo
{
    int	    sig;	/* Signal number, eg. SIGSEGV etc */
    char    *name;	/* Signal name (not char_u!). */
    char    deadly;	/* Catch as a deadly signal? */
} signal_info[] =
{
#ifdef SIGHUP
    {SIGHUP,	    "HUP",	TRUE},
#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},	/* Perl's alarm() can trigger it */
#endif
#ifdef SIGTERM
    {SIGTERM,	    "TERM",	TRUE},
#endif
#ifdef SIGVTALRM
    {SIGVTALRM,	    "VTALRM",	TRUE},
#endif
#if defined(SIGPROF) && !defined(FEAT_MZSCHEME)
    /* MzScheme uses SIGPROF for its own needs */
    {SIGPROF,	    "PROF",	TRUE},
#endif
#ifdef SIGXCPU
    {SIGXCPU,	    "XCPU",	TRUE},
#endif
#ifdef SIGXFSZ
    {SIGXFSZ,	    "XFSZ",	TRUE},
#endif
#ifdef SIGUSR1
    {SIGUSR1,	    "USR1",	TRUE},
#endif
#ifdef SIGUSR2
    {SIGUSR2,	    "USR2",	TRUE},
#endif
#ifdef SIGINT
    {SIGINT,	    "INT",	FALSE},
#endif
#ifdef SIGWINCH
    {SIGWINCH,	    "WINCH",	FALSE},
#endif
#ifdef SIGTSTP
    {SIGTSTP,	    "TSTP",	FALSE},
#endif
#ifdef SIGPIPE
    {SIGPIPE,	    "PIPE",	FALSE},
#endif
    {-1,	    "Unknown!", FALSE}
};

    void
mch_write(s, len)
    char_u	*s;
    int		len;
{
    write(1, (char *)s, len);
    if (p_wd)		/* Unix is too fast, slow down a bit more */
	RealWaitForChar(read_cmd_fd, p_wd, NULL);
}

/*
 * 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, tb_change_cnt)
    char_u	*buf;
    int		maxlen;
    long	wtime;	    /* don't use "time", MIPS cannot handle it */
    int		tb_change_cnt;
{
    int		len;

    /* Check if window changed size while we were busy, perhaps the ":set
     * columns=99" command was used. */
    while (do_resize)
	handle_resize();

    if (wtime >= 0)
    {
	while (WaitForChar(wtime) == 0)		/* no character available */
	{
	    if (!do_resize)	/* return if not interrupted by resize */
		return 0;
	    handle_resize();
	}
    }
    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)
	{
#ifdef FEAT_AUTOCMD
	    if (trigger_cursorhold() && maxlen >= 3
					   && !typebuf_changed(tb_change_cnt))
	    {
		buf[0] = K_SPECIAL;
		buf[1] = KS_EXTRA;
		buf[2] = (int)KE_CURSORHOLD;
		return 3;
	    }
#endif
	    before_blocking();
	}
    }

    for (;;)	/* repeat until we got a character */
    {
	while (do_resize)    /* window changed size */
	    handle_resize();
	/*
	 * we want to be interrupted by the winch signal
	 */
	WaitForChar(-1L);
	if (do_resize)	    /* interrupted by SIGWINCH signal */
	    continue;

	/* If input was put directly in typeahead buffer bail out here. */
	if (typebuf_changed(tb_change_cnt))
	    return 0;

	/*
	 * 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_from_input_buf(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;
	}
    }
}

    static void
handle_resize()
{
    do_resize = FALSE;
    shell_resized();
}

/*
 * return non-zero if a character is available
 */
    int
mch_char_avail()
{
    return WaitForChar(0L);
}

#if defined(HAVE_TOTAL_MEM) || defined(PROTO)
# ifdef HAVE_SYS_RESOURCE_H
#  include <sys/resource.h>
# endif
# if defined(HAVE_SYS_SYSCTL_H) && defined(HAVE_SYSCTL)
#  include <sys/sysctl.h>
# endif
# if defined(HAVE_SYS_SYSINFO_H) && defined(HAVE_SYSINFO)
#  include <sys/sysinfo.h>
# endif

/*
 * Return total amount of memory available.  Doesn't change when memory has
 * been allocated.
 */
/* ARGSUSED */
    long_u
mch_total_mem(special)
    int special;
{
# ifdef __EMX__
    return ulimit(3, 0L);   /* always 32MB? */
# else
    long_u	mem = 0;

#  ifdef HAVE_SYSCTL
    int		mib[2], physmem;
    size_t	len;

    /* BSD way of getting the amount of RAM available. */
    mib[0] = CTL_HW;
    mib[1] = HW_USERMEM;
    len = sizeof(physmem);
    if (sysctl(mib, 2, &physmem, &len, NULL, 0) == 0)
	mem = (long_u)physmem;
#  endif

#  if defined(HAVE_SYS_SYSINFO_H) && defined(HAVE_SYSINFO)
    if (mem == 0)
    {
	struct sysinfo sinfo;

	/* Linux way of getting amount of RAM available */
	if (sysinfo(&sinfo) == 0)
	    mem = sinfo.totalram;
    }
#  endif

#  ifdef HAVE_SYSCONF
    if (mem == 0)
    {
	long	    pagesize, pagecount;

	/* Solaris way of getting amount of RAM available */
	pagesize = sysconf(_SC_PAGESIZE);
	pagecount = sysconf(_SC_PHYS_PAGES);
	if (pagesize > 0 && pagecount > 0)
	    mem = (long_u)pagesize * pagecount;
    }
#  endif

    /* Return the minimum of the physical memory and the user limit, because
     * using more than the user limit may cause Vim to be terminated. */
#  if defined(HAVE_SYS_RESOURCE_H) && defined(HAVE_GETRLIMIT)
    {
	struct rlimit	rlp;

	if (getrlimit(RLIMIT_DATA, &rlp) == 0
		&& rlp.rlim_cur < ((rlim_t)1 << (sizeof(long_u) * 8 - 1))
#   ifdef RLIM_INFINITY
		&& rlp.rlim_cur != RLIM_INFINITY
#   endif
		&& (long_u)rlp.rlim_cur < mem
	   )
	    return (long_u)rlp.rlim_cur;
    }
#  endif

    if (mem > 0)
	return mem;
    return (long_u)0x7fffffff;
# endif
}
#endif

    void
mch_delay(msec, ignoreinput)
    long	msec;
    int		ignoreinput;
{
    int		old_tmode;
#ifdef FEAT_MZSCHEME
    long	total = msec; /* remember original value */
#endif

    if (ignoreinput)
    {
	/* Go to cooked mode without echo, to allow SIGINT interrupting us
	 * here */
	old_tmode = curr_tmode;
	if (curr_tmode == TMODE_RAW)
	    settmode(TMODE_SLEEP);

	/*
	 * Everybody sleeps in a different way...
	 * Prefer nanosleep(), some versions of usleep() can only sleep up to
	 * one second.
	 */
#ifdef FEAT_MZSCHEME
	do
	{
	    /* if total is large enough, wait by portions in p_mzq */
	    if (total > p_mzq)
		msec = p_mzq;
	    else
		msec = total;
	    total -= msec;
#endif
#ifdef HAVE_NANOSLEEP
	{
	    struct timespec ts;

	    ts.tv_sec = msec / 1000;
	    ts.tv_nsec = (msec % 1000) * 1000000;
	    (void)nanosleep(&ts, NULL);
	}
#else
# ifdef HAVE_USLEEP
	while (msec >= 1000)
	{
	    usleep((unsigned int)(999 * 1000));
	    msec -= 999;
	}
	usleep((unsigned int)(msec * 1000));
# else
#  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;
	    /*
	     * NOTE: Solaris 2.6 has a bug that makes select() hang here.  Get
	     * a patch from Sun to fix this.  Reported by Gunnar Pedersen.
	     */
	    select(0, NULL, NULL, NULL, &tv);
	}
#   endif /* __EMX__ */
#  endif /* HAVE_SELECT */
# endif /* HAVE_NANOSLEEP */
#endif /* HAVE_USLEEP */
#ifdef FEAT_MZSCHEME
	}
	while (total > 0);
#endif

	settmode(old_tmode);
    }
    else
	WaitForChar(msec);
}

#if 0    /* disabled, no longer needed now that regmatch() is not recursive */
# if defined(HAVE_GETRLIMIT)
#  define HAVE_STACK_LIMIT
# endif
#endif

#if defined(HAVE_STACK_LIMIT) \
	|| (!defined(HAVE_SIGALTSTACK) && defined(HAVE_SIGSTACK))
# define HAVE_CHECK_STACK_GROWTH
/*
 * Support for checking for an almost-out-of-stack-space situation.
 */

/*
 * Return a pointer to an item on the stack.  Used to find out if the stack
 * grows up or down.
 */
static void check_stack_growth __ARGS((char *p));
static int stack_grows_downwards;

/*
 * Find out if the stack grows upwards or downwards.
 * "p" points to a variable on the stack of the caller.
 */
    static void
check_stack_growth(p)
    char	*p;
{
    int		i;

    stack_grows_downwards = (p > (char *)&i);
}
#endif

#if defined(HAVE_STACK_LIMIT) || defined(PROTO)
static char *stack_limit = NULL;

#if defined(_THREAD_SAFE) && defined(HAVE_PTHREAD_NP_H)
# include <pthread.h>
# include <pthread_np.h>
#endif

/*
 * Find out until how var the stack can grow without getting into trouble.
 * Called when starting up and when switching to the signal stack in
 * deathtrap().
 */
    static void
get_stack_limit()
{
    struct rlimit	rlp;
    int			i;
    long		lim;

    /* Set the stack limit to 15/16 of the allowable size.  Skip this when the
     * limit doesn't fit in a long (rlim_cur might be "long long"). */
    if (getrlimit(RLIMIT_STACK, &rlp) == 0
	    && rlp.rlim_cur < ((rlim_t)1 << (sizeof(long_u) * 8 - 1))
#  ifdef RLIM_INFINITY
	    && rlp.rlim_cur != RLIM_INFINITY
#  endif
       )
    {
	lim = (long)rlp.rlim_cur;
#if defined(_THREAD_SAFE) && defined(HAVE_PTHREAD_NP_H)
	{
	    pthread_attr_t  attr;
	    size_t	    size;

	    /* On FreeBSD the initial thread always has a fixed stack size, no
	     * matter what the limits are set to.  Normally it's 1 Mbyte. */
	    pthread_attr_init(&attr);
	    if (pthread_attr_get_np(pthread_self(), &attr) == 0)
	    {
		pthread_attr_getstacksize(&attr, &size);
		if (lim > (long)size)
		    lim = (long)size;
	    }
	    pthread_attr_destroy(&attr);
	}
#endif
	if (stack_grows_downwards)
	{
	    stack_limit = (char *)((long)&i - (lim / 16L * 15L));
	    if (stack_limit >= (char *)&i)
		/* overflow, set to 1/16 of current stack position */
		stack_limit = (char *)((long)&i / 16L);
	}
	else
	{
	    stack_limit = (char *)((long)&i + (lim / 16L * 15L));
	    if (stack_limit <= (char *)&i)
		stack_limit = NULL;	/* overflow */
	}
    }
}

/*
 * Return FAIL when running out of stack space.
 * "p" must point to any variable local to the caller that's on the stack.
 */
    int
mch_stackcheck(p)
    char	*p;
{
    if (stack_limit != NULL)
    {
	if (stack_grows_downwards)
	{
	    if (p < stack_limit)
		return FAIL;
	}
	else if (p > stack_limit)
	    return FAIL;
    }
    return OK;
}
#endif

#if defined(HAVE_SIGALTSTACK) || defined(HAVE_SIGSTACK)
/*
 * Support for using the signal stack.
 * This helps when we run out of stack space, which causes a SIGSEGV.  The
 * signal handler then must run on another stack, since the normal stack is
 * completely full.
 */

#ifndef SIGSTKSZ
# define SIGSTKSZ 8000    /* just a guess of how much stack is needed... */
#endif

# ifdef HAVE_SIGALTSTACK
static stack_t sigstk;			/* for sigaltstack() */
# else
static struct sigstack sigstk;		/* for sigstack() */
# endif

static void init_signal_stack __ARGS((void));
static char *signal_stack;

    static void
init_signal_stack()
{
    if (signal_stack != NULL)
    {
# ifdef HAVE_SIGALTSTACK
#  ifdef __APPLE__
	/* missing prototype.  Adding it to osdef?.h.in doesn't work, because
	 * "struct sigaltstack" needs to be declared. */
	extern int sigaltstack __ARGS((const struct sigaltstack *ss, struct sigaltstack *oss));
#  endif

#  ifdef HAVE_SS_BASE
	sigstk.ss_base = signal_stack;
#  else
	sigstk.ss_sp = signal_stack;
#  endif
	sigstk.ss_size = SIGSTKSZ;
	sigstk.ss_flags = 0;
	(void)sigaltstack(&sigstk, NULL);
# else
	sigstk.ss_sp = signal_stack;
	if (stack_grows_downwards)
	    sigstk.ss_sp += SIGSTKSZ - 1;
	sigstk.ss_onstack = 0;
	(void)sigstack(&sigstk, NULL);
# endif
    }
}
#endif

/*
 * We need correct potatotypes for a signal function, 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).
 */
#if defined(SIGWINCH)
/* ARGSUSED */
    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(SIGINT)
/* ARGSUSED */
    static RETSIGTYPE
catch_sigint SIGDEFARG(sigarg)
{
    /* this is not required on all systems, but it doesn't hurt anybody */
    signal(SIGINT, (RETSIGTYPE (*)())catch_sigint);
    got_int = TRUE;
    SIGRETURN;
}
#endif

#if defined(SIGPWR)
/* ARGSUSED */
    static RETSIGTYPE
catch_sigpwr SIGDEFARG(sigarg)
{
    /* this is not required on all systems, but it doesn't hurt anybody */
    signal(SIGPWR, (RETSIGTYPE (*)())catch_sigpwr);
    /*
     * I'm not sure we get the SIGPWR signal when the system is really going
     * down or when the batteries are almost empty.  Just preserve the swap
     * files and don't exit, that can't do any harm.
     */
    ml_sync_all(FALSE, FALSE);
    SIGRETURN;
}
#endif

#ifdef SET_SIG_ALARM
/*
 * signal function for alarm().
 */
/* ARGSUSED */
    static RETSIGTYPE
sig_alarm SIGDEFARG(sigarg)
{
    /* doesn't do anything, just to break a system call */
    sig_alarm_called = TRUE;
    SIGRETURN;
}
#endif

#if (defined(HAVE_SETJMP_H) \
	&& ((defined(FEAT_X11) && defined(FEAT_XCLIPBOARD)) \
	    || defined(FEAT_LIBCALL))) \
    || defined(PROTO)
/*
 * A simplistic version of setjmp() that only allows one level of using.
 * Don't call twice before calling mch_endjmp()!.
 * Usage:
 *	mch_startjmp();
 *	if (SETJMP(lc_jump_env) != 0)
 *	{
 *	    mch_didjmp();
 *	    EMSG("crash!");
 *	}
 *	else
 *	{
 *	    do_the_work;
 *	    mch_endjmp();
 *	}
 * Note: Can't move SETJMP() here, because a function calling setjmp() must
 * not return before the saved environment is used.
 * Returns OK for normal return, FAIL when the protected code caused a
 * problem and LONGJMP() was used.
 */
    void
mch_startjmp()
{
#ifdef SIGHASARG
    lc_signal = 0;
#endif
    lc_active = TRUE;
}

    void
mch_endjmp()
{
    lc_active = FALSE;
}

    void
mch_didjmp()
{
# if defined(HAVE_SIGALTSTACK) || defined(HAVE_SIGSTACK)
    /* On FreeBSD the signal stack has to be reset after using siglongjmp(),
     * otherwise catching the signal only works once. */
    init_signal_stack();
# endif
}
#endif

/*
 * 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;	    /* count the number of times we got here.
				       Note: when memory has been corrupted
				       this may get an arbitrary value! */
#ifdef SIGHASARG
    int		i;
#endif

#if defined(HAVE_SETJMP_H)
    /*
     * Catch a crash in protected code.
     * Restores the environment saved in lc_jump_env, which looks like
     * SETJMP() returns 1.
     */
    if (lc_active)
    {
# if defined(SIGHASARG)
	lc_signal = sigarg;
# endif
	lc_active = FALSE;	/* don't jump again */
	LONGJMP(lc_jump_env, 1);
	/* NOTREACHED */
    }
#endif

#ifdef SIGHASARG
    /* When SIGHUP, SIGQUIT, etc. are blocked: postpone the effect and return
     * here.  This avoids that a non-reentrant function is interrupted, e.g.,
     * free().  Calling free() again may then cause a crash. */
    if (entered == 0
	    && (0
# ifdef SIGHUP
		|| sigarg == SIGHUP
# endif
# ifdef SIGQUIT
		|| sigarg == SIGQUIT
# endif
# ifdef SIGTERM
		|| sigarg == SIGTERM
# endif
# ifdef SIGPWR
		|| sigarg == SIGPWR
# endif
# ifdef SIGUSR1
		|| sigarg == SIGUSR1
# endif
# ifdef SIGUSR2
		|| sigarg == SIGUSR2
# endif
		)
	    && !vim_handle_signal(sigarg))
	SIGRETURN;
#endif

    /* Remember how often we have been called. */
    ++entered;

#ifdef FEAT_EVAL
    /* Set the v:dying variable. */
    set_vim_var_nr(VV_DYING, (long)entered);
#endif

#ifdef HAVE_STACK_LIMIT
    /* Since we are now using the signal stack, need to reset the stack
     * limit.  Otherwise using a regexp will fail. */
    get_stack_limit();
#endif

#if 0
    /* This is for opening gdb the moment Vim crashes.
     * You need to manually adjust the file name and Vim executable name.
     * Suggested by SungHyun Nam. */
    {
# define VI_GDB_FILE "/tmp/vimgdb"
# define VIM_NAME "/usr/bin/vim"
	FILE *fp = fopen(VI_GDB_FILE, "w");
	if (fp)
	{
	    fprintf(fp,
		    "file %s\n"
		    "attach %d\n"
		    "set height 1000\n"
		    "bt full\n"
		    , VIM_NAME, getpid());
	    fclose(fp);
	    system("xterm -e gdb -x "VI_GDB_FILE);
	    unlink(VI_GDB_FILE);
	}
    }
#endif

#ifdef SIGHASARG
    /* try to find the name of this signal */
    for (i = 0; signal_info[i].sig != -1; i++)
	if (sigarg == signal_info[i].sig)
	    break;
    deadly_signal = sigarg;
#endif

    full_screen = FALSE;	/* don't write message to the GUI, it might be
				 * part of the problem... */
    /*
     * 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.
     * When this still happens again (or memory was corrupted in such a way
     * that "entered" was clobbered) use _exit(), don't try freeing resources.
     */
    if (entered >= 3)
    {
	reset_signals();	/* don't catch any signals anymore */
	may_core_dump();
	if (entered >= 4)
	    _exit(8);
	exit(7);
    }
    if (entered == 2)
    {
	OUT_STR(_("Vim: Double signal, exiting\n"));
	out_flush();
	getout(1);
    }

#ifdef SIGHASARG
    sprintf((char *)IObuff, _("Vim: Caught deadly signal %s\n"),
							 signal_info[i].name);
#else
    sprintf((char *)IObuff, _("Vim: Caught deadly signal\n"));
#endif
    preserve_exit();		    /* preserve files and exit */

#ifdef NBDEBUG
    reset_signals();
    may_core_dump();
    abort();
#endif

    SIGRETURN;
}

#ifdef _REENTRANT
/*
 * On Solaris with multi-threading, suspending might not work immediately.
 * Catch the SIGCONT signal, which will be used as an indication whether the
 * suspending has been done or not.
 */
static int sigcont_received;
static RETSIGTYPE sigcont_handler __ARGS(SIGPROTOARG);

/*
 * signal handler for SIGCONT
 */
/* ARGSUSED */
    static RETSIGTYPE
sigcont_handler SIGDEFARG(sigarg)
{
    sigcont_received = TRUE;
    SIGRETURN;
}
#endif

/*
 * If the machine has job control, use it to suspend the program,
 * otherwise fake it by starting a new shell.
 */
    void
mch_suspend()
{
    /* BeOS does have SIGTSTP, but it doesn't work. */
#if defined(SIGTSTP) && !defined(__BEOS__)
    out_flush();	    /* needed to make cursor visible on some systems */
    settmode(TMODE_COOK);
    out_flush();	    /* needed to disable mouse on some systems */

# if defined(FEAT_CLIPBOARD) && defined(FEAT_X11)
    /* Since we are going to sleep, we can't respond to requests for the X
     * selections.  Lose them, otherwise other applications will hang.  But
     * first copy the text to cut buffer 0. */
    if (clip_star.owned || clip_plus.owned)
    {
	x11_export_final_selection();
	if (clip_star.owned)
	    clip_lose_selection(&clip_star);
	if (clip_plus.owned)
	    clip_lose_selection(&clip_plus);
	if (x11_display != NULL)
	    XFlush(x11_display);
    }
# endif

# ifdef _REENTRANT
    sigcont_received = FALSE;
# endif
    kill(0, SIGTSTP);	    /* send ourselves a STOP signal */
# ifdef _REENTRANT
    /* When we didn't suspend immediately in the kill(), do it now.  Happens
     * on multi-threaded Solaris. */
    if (!sigcont_received)
	pause();
# endif

# ifdef FEAT_TITLE
    /*
     * Set oldtitle to NULL, so the current title is obtained again.
     */
    vim_free(oldtitle);
    oldtitle = NULL;
# endif
    settmode(TMODE_RAW);
    need_check_timestamps = TRUE;
    did_check_timestamps = FALSE;
#else
    suspend_shell();
#endif
}

    void
mch_init()
{
    Columns = 80;
    Rows = 24;

    out_flush();
    set_signals();

#ifdef MACOS_CONVERT
    mac_conv_init();
#endif
}

    static void
set_signals()
{
#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.
     * For "rvim" the STOP signal is ignored.
     */
#ifdef SIGTSTP
    signal(SIGTSTP, restricted ? SIG_IGN : SIG_DFL);
#endif
#ifdef _REENTRANT
    signal(SIGCONT, sigcont_handler);
#endif

    /*
     * We want to ignore breaking of PIPEs.
     */
#ifdef SIGPIPE
    signal(SIGPIPE, SIG_IGN);
#endif

#ifdef SIGINT
    catch_int_signal();
#endif

    /*
     * Ignore alarm signals (Perl's alarm() generates it).
     */
#ifdef SIGALRM
    signal(SIGALRM, SIG_IGN);
#endif

    /*
     * Catch SIGPWR (power failure?) to preserve the swap files, so that no
     * work will be lost.
     */
#ifdef SIGPWR
    signal(SIGPWR, (RETSIGTYPE (*)())catch_sigpwr);
#endif

    /*
     * Arrange for other signals to gracefully shutdown Vim.
     */
    catch_signals(deathtrap, SIG_ERR);

#if defined(FEAT_GUI) && defined(SIGHUP)
    /*
     * When the GUI is running, ignore the hangup signal.
     */
    if (gui.in_use)
	signal(SIGHUP, SIG_IGN);
#endif
}

#if defined(SIGINT) || defined(PROTO)
/*
 * Catch CTRL-C (only works while in Cooked mode).
 */
    static void
catch_int_signal()
{
    signal(SIGINT, (RETSIGTYPE (*)())catch_sigint);
}
#endif

    void
reset_signals()
{
    catch_signals(SIG_DFL, SIG_DFL);
#ifdef _REENTRANT
    /* SIGCONT isn't in the list, because its default action is ignore */
    signal(SIGCONT, SIG_DFL);
#endif
}

    static void
catch_signals(func_deadly, func_other)
    RETSIGTYPE (*func_deadly)();
    RETSIGTYPE (*func_other)();
{
    int	    i;

    for (i = 0; signal_info[i].sig != -1; i++)
	if (signal_info[i].deadly)
	{
#if defined(HAVE_SIGALTSTACK) && defined(HAVE_SIGACTION)
	    struct sigaction sa;

	    /* Setup to use the alternate stack for the signal function. */
	    sa.sa_handler = func_deadly;
	    sigemptyset(&sa.sa_mask);
# if defined(__linux__) && defined(_REENTRANT)
	    /* On Linux, with glibc compiled for kernel 2.2, there is a bug in
	     * thread handling in combination with using the alternate stack:
	     * pthread library functions try to use the stack pointer to
	     * identify the current thread, causing a SEGV signal, which
	     * recursively calls deathtrap() and hangs. */
	    sa.sa_flags = 0;
# else
	    sa.sa_flags = SA_ONSTACK;
# endif
	    sigaction(signal_info[i].sig, &sa, NULL);
#else
# if defined(HAVE_SIGALTSTACK) && defined(HAVE_SIGVEC)
	    struct sigvec sv;

	    /* Setup to use the alternate stack for the signal function. */
	    sv.sv_handler = func_deadly;
	    sv.sv_mask = 0;
	    sv.sv_flags = SV_ONSTACK;
	    sigvec(signal_info[i].sig, &sv, NULL);
# else
	    signal(signal_info[i].sig, func_deadly);
# endif
#endif
	}
	else if (func_other != SIG_ERR)
	    signal(signal_info[i].sig, func_other);
}

/*
 * Handling of SIGHUP, SIGQUIT and SIGTERM:
 * "when" == a signal: when busy, postpone, otherwise return TRUE
 * "when" == SIGNAL_BLOCK: Going to be busy, block signals
 * "when" == SIGNAL_UNBLOCK: Going wait, unblock signals
 * Returns TRUE when Vim should exit.
 */
    int
vim_handle_signal(sig)
    int		sig;
{
    static int got_signal = 0;
    static int blocked = TRUE;

    switch (sig)
    {
	case SIGNAL_BLOCK:   blocked = TRUE;
			     break;

	case SIGNAL_UNBLOCK: blocked = FALSE;
			     if (got_signal != 0)
			     {
				 kill(getpid(), got_signal);
				 got_signal = 0;
			     }
			     break;

	default:	     if (!blocked)
				 return TRUE;	/* exit! */
			     got_signal = sig;
#ifdef SIGPWR
			     if (sig != SIGPWR)
#endif
				 got_int = TRUE;    /* break any loops */
			     break;
    }
    return FALSE;
}

/*
 * Check_win checks whether we have an interactive stdout.
 */
/* ARGSUSED */
    int
mch_check_win(argc, argv)
    int	    argc;
    char    **argv;
{
#ifdef OS2
    /*
     * Store argv[0], may be used for $VIM.  Only use it if it is an absolute
     * name, mostly it's just "vim" and found in the path, which is unusable.
     */
    if (mch_isFullName(argv[0]))
	exe_name = vim_strsave((char_u *)argv[0]);
#endif
    if (isatty(1))
	return OK;
    return FAIL;
}

/*
 * Return TRUE if the input comes from a terminal, FALSE otherwise.
 */
    int
mch_input_isatty()
{
    if (isatty(read_cmd_fd))
	return TRUE;
    return FALSE;
}

#ifdef FEAT_X11

# if defined(HAVE_GETTIMEOFDAY) && defined(HAVE_SYS_TIME_H) \
	&& (defined(FEAT_XCLIPBOARD) || defined(FEAT_TITLE))

static void xopen_message __ARGS((struct timeval *tvp));

/*
 * Give a message about the elapsed time for opening the X window.
 */
    static void
xopen_message(tvp)
    struct timeval *tvp;	/* must contain start time */
{
    struct timeval  end_tv;

    /* Compute elapsed time. */
    gettimeofday(&end_tv, NULL);
    smsg((char_u *)_("Opening the X display took %ld msec"),
	    (end_tv.tv_sec - tvp->tv_sec) * 1000L
				   + (end_tv.tv_usec - tvp->tv_usec) / 1000L);
}
# endif
#endif

#if defined(FEAT_X11) && (defined(FEAT_TITLE) || defined(FEAT_XCLIPBOARD))
/*
 * A few functions shared by X11 title and clipboard code.
 */
static int x_error_handler __ARGS((Display *dpy, XErrorEvent *error_event));
static int x_error_check __ARGS((Display *dpy, XErrorEvent *error_event));
static int x_connect_to_server __ARGS((void));
static int test_x11_window __ARGS((Display *dpy));

static int	got_x_error = FALSE;

/*
 * 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"));

    /* We cannot print a message and continue, because no X calls are allowed
     * here (causes my system to hang).  Silently continuing might be an
     * alternative... */
    preserve_exit();		    /* preserve files and exit */

    return 0;		/* NOTREACHED */
}

/*
 * Another X Error handler, just used to check for errors.
 */
/* ARGSUSED */
    static int
x_error_check(dpy, error_event)
    Display *dpy;
    XErrorEvent	*error_event;
{
    got_x_error = TRUE;
    return 0;
}

#if defined(FEAT_X11) && defined(FEAT_XCLIPBOARD)
# if defined(HAVE_SETJMP_H)
/*
 * An X IO Error handler, used to catch error while opening the display.
 */
static int x_IOerror_check __ARGS((Display *dpy));

/* ARGSUSED */
    static int
x_IOerror_check(dpy)
    Display *dpy;
{
    /* This function should not return, it causes exit().  Longjump instead. */
    LONGJMP(lc_jump_env, 1);
    /*NOTREACHED*/
    return 0;
}
# endif

/*
 * An X IO Error handler, used to catch terminal errors.
 */
static int x_IOerror_handler __ARGS((Display *dpy));

/* ARGSUSED */
    static int
x_IOerror_handler(dpy)
    Display *dpy;
{
    xterm_dpy = NULL;
    x11_window = 0;
    x11_display = NULL;
    xterm_Shell = (Widget)0;

    /* This function should not return, it causes exit().  Longjump instead. */
    LONGJMP(x_jump_env, 1);
    /*NOTREACHED*/
    return 0;
}
#endif

/*
 * Return TRUE when connection to the X server is desired.
 */
    static int
x_connect_to_server()
{
    regmatch_T	regmatch;

#if defined(FEAT_CLIENTSERVER)
    if (x_force_connect)
	return TRUE;
#endif
    if (x_no_connect)
	return FALSE;

    /* Check for a match with "exclude:" from 'clipboard'. */
    if (clip_exclude_prog != NULL)
    {
	regmatch.rm_ic = FALSE;		/* Don't ignore case */
	regmatch.regprog = clip_exclude_prog;
	if (vim_regexec(&regmatch, T_NAME, (colnr_T)0))
	    return FALSE;
    }
    return TRUE;
}

/*
 * Test if "dpy" and x11_window are valid by getting 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
 */
    static int
test_x11_window(dpy)
    Display	*dpy;
{
    int			(*old_handler)();
    XTextProperty	text_prop;

    old_handler = XSetErrorHandler(x_error_check);
    got_x_error = FALSE;
    if (XGetWMName(dpy, x11_window, &text_prop))
	XFree((void *)text_prop.value);
    XSync(dpy, False);
    (void)XSetErrorHandler(old_handler);

    if (p_verbose > 0 && got_x_error)
	verb_msg((char_u *)_("Testing the X display failed"));

    return (got_x_error ? FAIL : OK);
}
#endif

#ifdef FEAT_TITLE

#ifdef FEAT_X11

static int get_x11_thing __ARGS((int get_title, int test_only));

/*
 * try to get x11 window and display
 *
 * return FAIL for failure, OK otherwise
 */
    static int
get_x11_windis()
{
    char	    *winid;
    static int	    result = -1;
#define XD_NONE	 0	/* x11_display not set here */
#define XD_HERE	 1	/* x11_display opened here */
#define XD_GUI	 2	/* x11_display used from gui.dpy */
#define XD_XTERM 3	/* x11_display used from xterm_dpy */
    static int	    x11_display_from = XD_NONE;
    static int	    did_set_error_handler = FALSE;

    if (!did_set_error_handler)
    {
	/* X just exits if it finds an error otherwise! */
	(void)XSetErrorHandler(x_error_handler);
	did_set_error_handler = TRUE;
    }

#if defined(FEAT_GUI_X11) || defined(FEAT_GUI_GTK)
    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_from == XD_HERE && x11_display != NULL)
	{
	    XCloseDisplay(x11_display);
	    x11_display_from = XD_NONE;
	}
	if (gui_get_x11_windis(&x11_window, &x11_display) == OK)
	{
	    x11_display_from = XD_GUI;
	    return OK;
	}
	x11_display = NULL;
	return FAIL;
    }
    else if (x11_display_from == XD_GUI)
    {
	/* GUI must have stopped somehow, clear x11_display */
	x11_window = 0;
	x11_display = NULL;
	x11_display_from = XD_NONE;
    }
#endif

    /* When started with the "-X" argument, don't try connecting. */
    if (!x_connect_to_server())
	return FAIL;

    /*
     * 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);

#ifdef FEAT_XCLIPBOARD
    if (xterm_dpy != NULL && x11_window != 0)
    {
	/* Checked it already. */
	if (x11_display_from == XD_XTERM)
	    return OK;

	/*
	 * 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_from == XD_HERE && x11_display != NULL)
	    XCloseDisplay(x11_display);
	x11_display = xterm_dpy;
	x11_display_from = XD_XTERM;
	if (test_x11_window(x11_display) == FAIL)
	{
	    /* probably bad $WINDOWID */
	    x11_window = 0;
	    x11_display = NULL;
	    x11_display_from = XD_NONE;
	    return FAIL;
	}
	return OK;
    }
#endif

    if (x11_window == 0 || x11_display == NULL)
	result = -1;

    if (result != -1)	    /* Have already been here and set this */
	return result;	    /* Don't do all these X calls again */

    if (x11_window != 0 && x11_display == NULL)
    {
#ifdef SET_SIG_ALARM
	RETSIGTYPE (*sig_save)();
#endif
#if defined(HAVE_GETTIMEOFDAY) && defined(HAVE_SYS_TIME_H)
	struct timeval  start_tv;

	if (p_verbose > 0)
	    gettimeofday(&start_tv, NULL);
#endif

#ifdef SET_SIG_ALARM
	/*
	 * 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_alarm_called = FALSE;
	sig_save = (RETSIGTYPE (*)())signal(SIGALRM,
						 (RETSIGTYPE (*)())sig_alarm);
	alarm(2);
#endif
	x11_display = XOpenDisplay(NULL);

#ifdef SET_SIG_ALARM
	alarm(0);
	signal(SIGALRM, (RETSIGTYPE (*)())sig_save);
	if (p_verbose > 0 && sig_alarm_called)
	    verb_msg((char_u *)_("Opening the X display timed out"));
#endif
	if (x11_display != NULL)
	{
# if defined(HAVE_GETTIMEOFDAY) && defined(HAVE_SYS_TIME_H)
	    if (p_verbose > 0)
	    {
		verbose_enter();
		xopen_message(&start_tv);
		verbose_leave();
	    }
# endif
	    if (test_x11_window(x11_display) == FAIL)
	    {
		/* Maybe window id is bad */
		x11_window = 0;
		XCloseDisplay(x11_display);
		x11_display = NULL;
	    }
	    else
		x11_display_from = XD_HERE;
	}
    }
    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;
{
    return get_x11_thing(TRUE, test_only);
}

/*
 * Determine original x11 Window icon
 */
    static int
get_x11_icon(test_only)
    int		test_only;
{
    int		retval = FALSE;

    retval = get_x11_thing(FALSE, test_only);

    /* could not get old icon, use terminal name */
    if (oldicon == NULL && !test_only)
    {
	if (STRNCMP(T_NAME, "builtin_", 8) == 0)
	    oldicon = T_NAME + 8;
	else
	    oldicon = T_NAME;
    }

    return retval;
}

    static int
get_x11_thing(get_title, test_only)
    int		get_title;	/* get title string */
    int		test_only;
{
    XTextProperty	text_prop;
    int			retval = FALSE;
    Status		status;

    if (get_x11_windis() == OK)
    {
	/* Get window/icon name if any */
	if (get_title)
	    status = XGetWMName(x11_display, x11_window, &text_prop);
	else
	    status = XGetWMIconName(x11_display, x11_window, &text_prop);

	/*
	 * If terminal is xterm, then x11_window may be a child window of the
	 * outer xterm window that actually contains the window/icon name, so
	 * keep traversing up the tree until a window with a title/icon is
	 * found.
	 */
	/* Previously this was only done for xterm and alikes.  I don't see a
	 * reason why it would fail for other terminal emulators.
	 * if (term_is_xterm) */
	{
	    Window	    root;
	    Window	    parent;
	    Window	    win = x11_window;
	    Window	   *children;
	    unsigned int    num_children;

	    while (!status || text_prop.value == NULL)
	    {
		if (!XQueryTree(x11_display, win, &root, &parent, &children,
							       &num_children))
		    break;
		if (children)
		    XFree((void *)children);
		if (parent == root || parent == 0)
		    break;

		win = parent;
		if (get_title)
		    status = XGetWMName(x11_display, win, &text_prop);
		else
		    status = XGetWMIconName(x11_display, win, &text_prop);
	    }
	}
	if (status && text_prop.value != NULL)
	{
	    retval = TRUE;
	    if (!test_only)
	    {
#ifdef FEAT_XFONTSET
		if (text_prop.encoding == XA_STRING)
		{
#endif
		    if (get_title)
			oldtitle = vim_strsave((char_u *)text_prop.value);
		    else
			oldicon = vim_strsave((char_u *)text_prop.value);
#ifdef FEAT_XFONTSET
		}
		else
		{
		    char    **cl;
		    Status  transform_status;
		    int	    n = 0;

		    transform_status = XmbTextPropertyToTextList(x11_display,
								 &text_prop,
								 &cl, &n);
		    if (transform_status >= Success && n > 0 && cl[0])
		    {
			if (get_title)
			    oldtitle = vim_strsave((char_u *) cl[0]);
			else
			    oldicon = vim_strsave((char_u *) cl[0]);
			XFreeStringList(cl);
		    }
		    else
		    {
			if (get_title)
			    oldtitle = vim_strsave((char_u *)text_prop.value);
			else
			    oldicon = vim_strsave((char_u *)text_prop.value);
		    }
		}
#endif
	    }
	    XFree((void *)text_prop.value);
	}
    }
    return retval;
}

/* Are Xutf8 functions available?  Avoid error from old compilers. */
#if defined(X_HAVE_UTF8_STRING) && defined(FEAT_MBYTE)
# if X_HAVE_UTF8_STRING
#  define USE_UTF8_STRING
# endif
#endif

/*
 * 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;
{
	/* XmbSetWMProperties() and Xutf8SetWMProperties() should use a STRING
	 * when possible, COMPOUND_TEXT otherwise.  COMPOUND_TEXT isn't
	 * supported everywhere and STRING doesn't work for multi-byte titles.
	 */
#ifdef USE_UTF8_STRING
    if (enc_utf8)
	Xutf8SetWMProperties(x11_display, x11_window, (const char *)title,
					     NULL, NULL, 0, NULL, NULL, NULL);
    else
#endif
    {
#if XtSpecificationRelease >= 4
# ifdef FEAT_XFONTSET
	XmbSetWMProperties(x11_display, x11_window, (const char *)title,
					     NULL, NULL, 0, NULL, NULL, NULL);
# else
	XTextProperty	text_prop;
	char		*c_title = (char *)title;

	/* directly from example 3-18 "basicwin" of Xlib Programming Manual */
	(void)XStringListToTextProperty(&c_title, 1, &text_prop);
	XSetWMProperties(x11_display, x11_window, &text_prop,
					     NULL, NULL, 0, NULL, NULL, NULL);
# endif
#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;
{
    /* See above for comments about using X*SetWMProperties(). */
#ifdef USE_UTF8_STRING
    if (enc_utf8)
	Xutf8SetWMProperties(x11_display, x11_window, NULL, (const char *)icon,
						   NULL, 0, NULL, NULL, NULL);
    else
#endif
    {
#if XtSpecificationRelease >= 4
# ifdef FEAT_XFONTSET
	XmbSetWMProperties(x11_display, x11_window, NULL, (const char *)icon,
						   NULL, 0, NULL, NULL, NULL);
# else
	XTextProperty	text_prop;
	char		*c_icon = (char *)icon;

	(void)XStringListToTextProperty(&c_icon, 1, &text_prop);
	XSetWMProperties(x11_display, x11_window, NULL, &text_prop,
						   NULL, 0, NULL, NULL, NULL);
# endif
#else
	XSetIconName(x11_display, x11_window, (char *)icon);
#endif
    }
    XFlush(x11_display);
}

#else  /* FEAT_X11 */

/*ARGSUSED*/
    static int
get_x11_title(test_only)
    int	    test_only;
{
    return FALSE;
}

    static int
get_x11_icon(test_only)
    int	    test_only;
{
    if (!test_only)
    {
	if (STRNCMP(T_NAME, "builtin_", 8) == 0)
	    oldicon = T_NAME + 8;
	else
	    oldicon = T_NAME;
    }
    return FALSE;
}

#endif /* FEAT_X11 */

    int
mch_can_restore_title()
{
    return get_x11_title(TRUE);
}

    int
mch_can_restore_icon()
{
    return get_x11_icon(TRUE);
}

/*
 * Set the window title and icon.
 */
    void
mch_settitle(title, icon)
    char_u *title;
    char_u *icon;
{
    int		type = 0;
    static int	recursive = 0;

    if (T_NAME == NULL)	    /* no terminal name (yet) */
	return;
    if (title == NULL && icon == NULL)	    /* nothing to do */
	return;

    /* When one of the X11 functions causes a deadly signal, we get here again
     * recursively.  Avoid hanging then (something is probably locked). */
    if (recursive)
	return;
    ++recursive;

    /*
     * if the window ID and the display is known, we may use X11 calls
     */
#ifdef FEAT_X11
    if (get_x11_windis() == OK)
	type = 1;
#else
# if defined(FEAT_GUI_PHOTON) || defined(FEAT_GUI_MAC) || defined(FEAT_GUI_GTK)
    if (gui.in_use)
	type = 1;
# endif
#endif

    /*
     * Note: if "t_TS" is set, title is set with escape sequence rather
     *	     than x11 calls, because the x11 calls don't always work
     */
    if ((type || *T_TS != NUL) && title != NULL)
    {
	if (oldtitle == NULL
#ifdef FEAT_GUI
		&& !gui.in_use
#endif
		)		/* first call but not in GUI, save title */
	    (void)get_x11_title(FALSE);

	if (*T_TS != NUL)		/* it's OK if t_fs is empty */
	    term_settitle(title);
#ifdef FEAT_X11
	else
# ifdef FEAT_GUI_GTK
	if (!gui.in_use)		/* don't do this if GTK+ is running */
# endif
	    set_x11_title(title);		/* x11 */
#endif
#if defined(FEAT_GUI_GTK) \
	|| defined(FEAT_GUI_PHOTON) || defined(FEAT_GUI_MAC)
	else
	    gui_mch_settitle(title, icon);
#endif
	did_set_title = TRUE;
    }

    if ((type || *T_CIS != NUL) && icon != NULL)
    {
	if (oldicon == NULL
#ifdef FEAT_GUI
		&& !gui.in_use
#endif
		)		/* first call, save icon */
	    get_x11_icon(FALSE);

	if (*T_CIS != NUL)
	{
	    out_str(T_CIS);			/* set icon start */
	    out_str_nf(icon);
	    out_str(T_CIE);			/* set icon end */
	    out_flush();
	}
#ifdef FEAT_X11
	else
# ifdef FEAT_GUI_GTK
	if (!gui.in_use)		/* don't do this if GTK+ is running */
# endif
	    set_x11_icon(icon);			/* x11 */
#endif
	did_set_icon = TRUE;
    }
    --recursive;
}

/*
 * Restore the window/icon title.
 * "which" is one of:
 *  1  only restore title
 *  2  only restore icon
 *  3  restore title and icon
 */
    void
mch_restore_title(which)
    int which;
{
    /* only restore the title or icon when it has been set */
    mch_settitle(((which & 1) && did_set_title) ?
			(oldtitle ? oldtitle : p_titleold) : NULL,
			      ((which & 2) && did_set_icon) ? oldicon : NULL);
}

#endif /* FEAT_TITLE */

/*
 * Return TRUE if "name" looks like some xterm name.
 * Seiichi Sato mentioned that "mlterm" works like xterm.
 */
    int
vim_is_xterm(name)
    char_u *name;
{
    if (name == NULL)
	return FALSE;
    return (STRNICMP(name, "xterm", 5) == 0
		|| STRNICMP(name, "nxterm", 6) == 0
		|| STRNICMP(name, "kterm", 5) == 0
		|| STRNICMP(name, "mlterm", 6) == 0
		|| STRNICMP(name, "rxvt", 4) == 0
		|| STRCMP(name, "builtin_xterm") == 0);
}

#if defined(FEAT_MOUSE_TTY) || defined(PROTO)
/*
 * Return non-zero when using an xterm mouse, according to 'ttymouse'.
 * Return 1 for "xterm".
 * Return 2 for "xterm2".
 */
    int
use_xterm_mouse()
{
    if (ttym_flags == TTYM_XTERM2)
	return 2;
    if (ttym_flags == TTYM_XTERM)
	return 1;
    return 0;
}
#endif

    int
vim_is_iris(name)
    char_u  *name;
{
    if (name == NULL)
	return FALSE;
    return (STRNICMP(name, "iris-ansi", 9) == 0
	    || STRCMP(name, "builtin_iris-ansi") == 0);
}

    int
vim_is_vt300(name)
    char_u  *name;
{
    if (name == NULL)
	return FALSE;	       /* actually all ANSI comp. terminals should be here  */
    /* catch VT100 - VT5xx */
    return ((STRNICMP(name, "vt", 2) == 0
		&& vim_strchr((char_u *)"12345", name[2]) != NULL)
	    || STRCMP(name, "builtin_vt320") == 0);
}

/*
 * Return TRUE if "name" is a terminal for which 'ttyfast' should be set.
 * This should include all windowed terminal emulators.
 */
    int
vim_is_fastterm(name)
    char_u  *name;
{
    if (name == NULL)
	return FALSE;
    if (vim_is_xterm(name) || vim_is_vt300(name) || vim_is_iris(name))
	return TRUE;
    return (   STRNICMP(name, "hpterm", 6) == 0
	    || STRNICMP(name, "sun-cmd", 7) == 0
	    || STRNICMP(name, "screen", 6) == 0
	    || STRNICMP(name, "dtterm", 6) == 0);
}

/*
 * Insert user name in s[len].
 * Return OK if a name found.
 */
    int
mch_get_user_name(s, len)
    char_u  *s;
    int	    len;
{
#ifdef VMS
    vim_strncpy(s, (char_u *)cuserid(NULL), len - 1);
    return OK;
#else
    return mch_get_uname(getuid(), s, len);
#endif
}

/*
 * Insert user name for "uid" in s[len].
 * Return OK if a name found.
 */
    int
mch_get_uname(uid, s, len)
    uid_t	uid;
    char_u	*s;
    int		len;
{
#if defined(HAVE_PWD_H) && defined(HAVE_GETPWUID)
    struct passwd   *pw;

    if ((pw = getpwuid(uid)) != NULL
	    && pw->pw_name != NULL && *(pw->pw_name) != NUL)
    {
	vim_strncpy(s, (char_u *)pw->pw_name, len - 1);
	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;

    if (uname(&vutsname) < 0)
	*s = NUL;
    else
	vim_strncpy(s, (char_u *)vutsname.nodename, len - 1);
}
#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;
{
# ifdef VAXC
    vaxc$gethostname((char *)s, len);
# else
    gethostname((char *)s, len);
# endif
    s[len - 1] = NUL;	/* make sure it's terminated */
}
#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
}

#if defined(OS2) || defined(PROTO)
/*
 * Replace all slashes by backslashes.
 * When 'shellslash' set do it the other way around.
 */
    void
slash_adjust(p)
    char_u  *p;
{
    while (*p)
    {
	if (*p == psepcN)
	    *p = psepc;
	mb_ptr_adv(p);
    }
}
#endif

/*
 * Get absolute file name into buffer 'buf' of length 'len' bytes.
 *
 * return FAIL for failure, OK for success
 */
    int
mch_FullName(fname, buf, len, force)
    char_u	*fname, *buf;
    int		len;
    int		force;		/* also expand when already absolute path */
{
    int		l;
#ifdef OS2
    int		only_drive;	/* file name is only a drive letter */
#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;
    int		retval = OK;

#ifdef VMS
    fname = vms_fixfilename(fname);
#endif

    /* expand it if forced or not an absolute path */
    if (force || !mch_isFullName(fname))
    {
	/*
	 * 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, 0);
		if (fd >= 0 && fchdir(fd) < 0)
		{
		    close(fd);
		    fd = -1;
		    dont_fchdir = TRUE;	    /* don't try again */
		}
	    }
#endif

	    /* Only change directory when we are sure we can return to where
	     * we are now.  After doing "su" chdir(".") might not work. */
	    if (
#ifdef HAVE_FCHDIR
		fd < 0 &&
#endif
			(mch_dirname(olddir, MAXPATHL) == FAIL
					   || mch_chdir((char *)olddir) != 0))
	    {
		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
		/* The directory is copied into buf[], to be able to remove
		 * the file name without changing it (could be a string in
		 * read-only memory) */
		if (p - fname >= len)
		    retval = FAIL;
		else
		{
		    vim_strncpy(buf, fname, p - fname);
		    if (mch_chdir((char *)buf))
			retval = FAIL;
		    else
			fname = p + 1;
		    *buf = NUL;
		}
#ifdef OS2
		if (only_drive)
		{
		    p--;
		    if (retval != FAIL)
			fname--;
		}
#endif
	    }
	}
	if (mch_dirname(buf, len) == FAIL)
	{
	    retval = FAIL;
	    *buf = NUL;
	}
	if (p != NULL)
	{
#ifdef HAVE_FCHDIR
	    if (fd >= 0)
	    {
		l = fchdir(fd);
		close(fd);
	    }
	    else
#endif
		l = mch_chdir((char *)olddir);
	    if (l != 0)
		EMSG(_(e_prev_dir));
	}

	l = STRLEN(buf);
	if (l >= len)
	    retval = FAIL;
#ifndef VMS
	else
	{
	    if (l > 0 && buf[l - 1] != '/' && *fname != NUL
						   && STRCMP(fname, ".") != 0)
		STRCAT(buf, "/");
	}
#endif
    }
    /* Catch file names which are too long. */
    if (retval == FAIL || STRLEN(buf) + STRLEN(fname) >= len)
	return FAIL;

    /* Do not append ".", "/dir/." is equal to "/dir". */
    if (STRCMP(fname, ".") != 0)
	STRCAT(buf, fname);

    return OK;
}

/*
 * Return TRUE if "fname" does not depend on the current directory.
 */
    int
mch_isFullName(fname)
    char_u	*fname;
{
#ifdef __EMX__
    return _fnisabs(fname);
#else
# ifdef VMS
    return ( fname[0] == '/'	       || fname[0] == '.'	    ||
	     strchr((char *)fname,':') || strchr((char *)fname,'"') ||
	    (strchr((char *)fname,'[') && strchr((char *)fname,']'))||
	    (strchr((char *)fname,'<') && strchr((char *)fname,'>'))   );
# else
    return (*fname == '/' || *fname == '~');
# endif
#endif
}

#if defined(USE_FNAME_CASE) || defined(PROTO)
/*
 * Set the case of the file name, if it already exists.  This will cause the
 * file name to remain exactly the same.
 * Only required for file systems where case is ingored and preserved.
 */
/*ARGSUSED*/
    void
fname_case(name, len)
    char_u	*name;
    int		len;	    /* buffer size, only used when name gets longer */
{
    struct stat st;
    char_u	*slash, *tail;
    DIR		*dirp;
    struct dirent *dp;

    if (lstat((char *)name, &st) >= 0)
    {
	/* Open the directory where the file is located. */
	slash = vim_strrchr(name, '/');
	if (slash == NULL)
	{
	    dirp = opendir(".");
	    tail = name;
	}
	else
	{
	    *slash = NUL;
	    dirp = opendir((char *)name);
	    *slash = '/';
	    tail = slash + 1;
	}

	if (dirp != NULL)
	{
	    while ((dp = readdir(dirp)) != NULL)
	    {
		/* Only accept names that differ in case and are the same byte
		 * length. TODO: accept different length name. */
		if (STRICMP(tail, dp->d_name) == 0
			&& STRLEN(tail) == STRLEN(dp->d_name))
		{
		    char_u	newname[MAXPATHL + 1];
		    struct stat st2;

		    /* Verify the inode is equal. */
		    vim_strncpy(newname, name, MAXPATHL);
		    vim_strncpy(newname + (tail - name), (char_u *)dp->d_name,
						    MAXPATHL - (tail - name));
		    if (lstat((char *)newname, &st2) >= 0
			    && st.st_ino == st2.st_ino
			    && st.st_dev == st2.st_dev)
		    {
			STRCPY(tail, dp->d_name);
			break;
		    }
		}
	    }

	    closedir(dirp);
	}
    }
}
#endif

/*
 * Get file permissions for 'name'.
 * Returns -1 when it doesn't exist.
 */
    long
mch_getperm(name)
    char_u *name;
{
    struct stat statb;

    /* Keep the #ifdef outside of stat(), it may be a macro. */
#ifdef VMS
    if (stat((char *)vms_fixfilename(name), &statb))
#else
    if (stat((char *)name, &statb))
#endif
	return -1;
    return statb.st_mode;
}

/*
 * set file permission for 'name' to 'perm'
 *
 * return FAIL for failure, OK otherwise
 */
    int
mch_setperm(name, perm)
    char_u  *name;
    long    perm;
{
    return (chmod((char *)
#ifdef VMS
		    vms_fixfilename(name),
#else
		    name,
#endif
		    (mode_t)perm) == 0 ? OK : FAIL);
}

#if defined(HAVE_ACL) || defined(PROTO)
# ifdef HAVE_SYS_ACL_H
#  include <sys/acl.h>
# endif
# ifdef HAVE_SYS_ACCESS_H
#  include <sys/access.h>
# endif

# ifdef HAVE_SOLARIS_ACL
typedef struct vim_acl_solaris_T {
    int acl_cnt;
    aclent_t *acl_entry;
} vim_acl_solaris_T;
# endif

/*
 * Return a pointer to the ACL of file "fname" in allocated memory.
 * Return NULL if the ACL is not available for whatever reason.
 */
    vim_acl_T
mch_get_acl(fname)
    char_u	*fname;
{
    vim_acl_T	ret = NULL;
#ifdef HAVE_POSIX_ACL
    ret = (vim_acl_T)acl_get_file((char *)fname, ACL_TYPE_ACCESS);
#else
#ifdef HAVE_SOLARIS_ACL
    vim_acl_solaris_T   *aclent;

    aclent = malloc(sizeof(vim_acl_solaris_T));
    if ((aclent->acl_cnt = acl((char *)fname, GETACLCNT, 0, NULL)) < 0)
    {
	free(aclent);
	return NULL;
    }
    aclent->acl_entry = malloc(aclent->acl_cnt * sizeof(aclent_t));
    if (acl((char *)fname, GETACL, aclent->acl_cnt, aclent->acl_entry) < 0)
    {
	free(aclent->acl_entry);
	free(aclent);
	return NULL;
    }
    ret = (vim_acl_T)aclent;
#else
#if defined(HAVE_AIX_ACL)
    int		aclsize;
    struct acl *aclent;

    aclsize = sizeof(struct acl);
    aclent = malloc(aclsize);
    if (statacl((char *)fname, STX_NORMAL, aclent, aclsize) < 0)
    {
	if (errno == ENOSPC)
	{
	    aclsize = aclent->acl_len;
	    aclent = realloc(aclent, aclsize);
	    if (statacl((char *)fname, STX_NORMAL, aclent, aclsize) < 0)
	    {
		free(aclent);
		return NULL;
	    }
	}
	else
	{
	    free(aclent);
	    return NULL;
	}
    }
    ret = (vim_acl_T)aclent;
#endif /* HAVE_AIX_ACL */
#endif /* HAVE_SOLARIS_ACL */
#endif /* HAVE_POSIX_ACL */
    return ret;
}

/*
 * Set the ACL of file "fname" to "acl" (unless it's NULL).
 */
    void
mch_set_acl(fname, aclent)
    char_u	*fname;
    vim_acl_T	aclent;
{
    if (aclent == NULL)
	return;
#ifdef HAVE_POSIX_ACL
    acl_set_file((char *)fname, ACL_TYPE_ACCESS, (acl_t)aclent);
#else
#ifdef HAVE_SOLARIS_ACL
    acl((char *)fname, SETACL, ((vim_acl_solaris_T *)aclent)->acl_cnt,
	    ((vim_acl_solaris_T *)aclent)->acl_entry);
#else
#ifdef HAVE_AIX_ACL
    chacl((char *)fname, aclent, ((struct acl *)aclent)->acl_len);
#endif /* HAVE_AIX_ACL */
#endif /* HAVE_SOLARIS_ACL */
#endif /* HAVE_POSIX_ACL */
}

    void
mch_free_acl(aclent)
    vim_acl_T	aclent;
{
    if (aclent == NULL)
	return;
#ifdef HAVE_POSIX_ACL
    acl_free((acl_t)aclent);
#else
#ifdef HAVE_SOLARIS_ACL
    free(((vim_acl_solaris_T *)aclent)->acl_entry);
    free(aclent);
#else
#ifdef HAVE_AIX_ACL
    free(aclent);
#endif /* HAVE_AIX_ACL */
#endif /* HAVE_SOLARIS_ACL */
#endif /* HAVE_POSIX_ACL */
}
#endif

/*
 * Set hidden flag for "name".
 */
/* ARGSUSED */
    void
mch_hide(name)
    char_u	*name;
{
    /* can't hide a file */
}

/*
 * 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 (*name == NUL)	    /* Some stat()s don't flag "" as an error. */
	return FALSE;
    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
}

static int executable_file __ARGS((char_u *name));

/*
 * Return 1 if "name" is an executable file, 0 if not or it doesn't exist.
 */
    static int
executable_file(name)
    char_u	*name;
{
    struct stat	st;

    if (stat((char *)name, &st))
	return 0;
    return S_ISREG(st.st_mode) && mch_access((char *)name, X_OK) == 0;
}

/*
 * Return 1 if "name" can be found in $PATH and executed, 0 if not.
 * Return -1 if unknown.
 */
    int
mch_can_exe(name)
    char_u	*name;
{
    char_u	*buf;
    char_u	*p, *e;
    int		retval;

    /* If it's an absolute or relative path don't need to use $PATH. */
    if (mch_isFullName(name) || (name[0] == '.' && (name[1] == '/'
				      || (name[1] == '.' && name[2] == '/'))))
	return executable_file(name);

    p = (char_u *)getenv("PATH");
    if (p == NULL || *p == NUL)
	return -1;
    buf = alloc((unsigned)(STRLEN(name) + STRLEN(p) + 2));
    if (buf == NULL)
	return -1;

    /*
     * Walk through all entries in $PATH to check if "name" exists there and
     * is an executable file.
     */
    for (;;)
    {
	e = (char_u *)strchr((char *)p, ':');
	if (e == NULL)
	    e = p + STRLEN(p);
	if (e - p <= 1)		/* empty entry means current dir */
	    STRCPY(buf, "./");
	else
	{
	    vim_strncpy(buf, p, e - p);
	    add_pathsep(buf);
	}
	STRCAT(buf, name);
	retval = executable_file(buf);
	if (retval == 1)
	    break;

	if (*e != ':')
	    break;
	p = e + 1;
    }

    vim_free(buf);
    return retval;
}

/*
 * Check what "name" is:
 * NODE_NORMAL: file or directory (or doesn't exist)
 * NODE_WRITABLE: writable device, socket, fifo, etc.
 * NODE_OTHER: non-writable things
 */
    int
mch_nodetype(name)
    char_u	*name;
{
    struct stat	st;

    if (stat((char *)name, &st))
	return NODE_NORMAL;
    if (S_ISREG(st.st_mode) || S_ISDIR(st.st_mode))
	return NODE_NORMAL;
#ifndef OS2
    if (S_ISBLK(st.st_mode))	/* block device isn't writable */
	return NODE_OTHER;
#endif
    /* Everything else is writable? */
    return NODE_WRITABLE;
}

    void
mch_early_init()
{
#ifdef HAVE_CHECK_STACK_GROWTH
    int			i;

    check_stack_growth((char *)&i);

# ifdef HAVE_STACK_LIMIT
    get_stack_limit();
# endif

#endif

    /*
     * Setup an alternative stack for signals.  Helps to catch signals when
     * running out of stack space.
     * Use of sigaltstack() is preferred, it's more portable.
     * Ignore any errors.
     */
#if defined(HAVE_SIGALTSTACK) || defined(HAVE_SIGSTACK)
    signal_stack = malloc(SIGSTKSZ);
    init_signal_stack();
#endif
}

#if defined(EXITFREE) || defined(PROTO)
    void
mch_free_mem()
{
# if defined(FEAT_CLIPBOARD) && defined(FEAT_X11)
    if (clip_star.owned)
	clip_lose_selection(&clip_star);
    if (clip_plus.owned)
	clip_lose_selection(&clip_plus);
# endif
# if (defined(FEAT_X11) && defined(FEAT_XCLIPBOARD)) || defined(PROTO)
    if (xterm_Shell != (Widget)0)
	XtDestroyWidget(xterm_Shell);
    if (xterm_dpy != NULL)
	XtCloseDisplay(xterm_dpy);
    if (app_context != (XtAppContext)NULL)
	XtDestroyApplicationContext(app_context);
# endif
# ifdef FEAT_X11
    if (x11_display != NULL && x11_display != xterm_dpy)
	XCloseDisplay(x11_display);
# endif
# if defined(HAVE_SIGALTSTACK) || defined(HAVE_SIGSTACK)
    vim_free(signal_stack);
    signal_stack = NULL;
# endif
# ifdef FEAT_TITLE
    vim_free(oldtitle);
    vim_free(oldicon);
# endif
}
#endif

static void exit_scroll __ARGS((void));

/*
 * Output a newline when exiting.
 * Make sure the newline goes to the same stream as the text.
 */
    static void
exit_scroll()
{
    if (silent_mode)
	return;
    if (newline_on_exit || msg_didout)
    {
	if (msg_use_printf())
	{
	    if (info_message)
		mch_msg("\n");
	    else
		mch_errmsg("\r\n");
	}
	else
	    out_char('\n');
    }
    else
    {
	restore_cterm_colors();		/* get original colors back */
	msg_clr_eos_force();		/* clear the rest of the display */
	windgoto((int)Rows - 1, 0);	/* may have moved the cursor */
    }
}

    void
mch_exit(r)
    int r;
{
    exiting = TRUE;

#if defined(FEAT_X11) && defined(FEAT_CLIPBOARD)
    x11_export_final_selection();
#endif

#ifdef FEAT_GUI
    if (!gui.in_use)
#endif
    {
	settmode(TMODE_COOK);
#ifdef FEAT_TITLE
	mch_restore_title(3);	/* restore xterm title and icon name */
#endif
	/*
	 * When t_ti is not empty but it doesn't cause swapping terminal
	 * pages, need to output a newline when msg_didout is set.  But when
	 * t_ti does swap pages it should not go to the shell page.  Do this
	 * before stoptermcap().
	 */
	if (swapping_screen() && !newline_on_exit)
	    exit_scroll();

	/* Stop termcap: May need to check for T_CRV response, which
	 * requires RAW mode. */
	stoptermcap();

	/*
	 * A newline is only required after a message in the alternate screen.
	 * This is set to TRUE by wait_return().
	 */
	if (!swapping_screen() || newline_on_exit)
	    exit_scroll();

	/* Cursor may have been switched off without calling starttermcap()
	 * when doing "vim -u vimrc" and vimrc contains ":q". */
	if (full_screen)
	    cursor_on();
    }
    out_flush();
    ml_close_all(TRUE);		/* remove all memfiles */
    may_core_dump();
#ifdef FEAT_GUI
    if (gui.in_use)
	gui_exit(r);
#endif

#ifdef MACOS_CONVERT
    mac_conv_cleanup();
#endif

#ifdef __QNX__
    /* A core dump won't be created if the signal handler
     * doesn't return, so we can't call exit() */
    if (deadly_signal != 0)
	return;
#endif

#ifdef FEAT_NETBEANS_INTG
    if (usingNetbeans)
	netbeans_send_disconnect();
#endif

#ifdef EXITFREE
    free_all_mem();
#endif

    exit(r);
}

    static void
may_core_dump()
{
    if (deadly_signal != 0)
    {
	signal(deadly_signal, SIG_DFL);
	kill(getpid(), deadly_signal);	/* Die using the signal we caught */
    }
}

#ifndef VMS

    void
mch_settmode(tmode)
    int		tmode;
{
    static int first = TRUE;

    /* Why is NeXT excluded here (and not in os_unixx.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

    if (first)
    {
	first = FALSE;
# if defined(HAVE_TERMIOS_H)
	tcgetattr(read_cmd_fd, &told);
# else
	ioctl(read_cmd_fd, TCGETA, &told);
# endif
    }

    tnew = told;
    if (tmode == TMODE_RAW)
    {
	/*
	 * ~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
				);
# ifdef ONLCR	    /* don't map NL -> CR NL, we do it ourselves */
	tnew.c_oflag &= ~ONLCR;
# endif
	tnew.c_cc[VMIN] = 1;		/* return after 1 char */
	tnew.c_cc[VTIME] = 0;		/* don't wait */
    }
    else if (tmode == TMODE_SLEEP)
	tnew.c_lflag &= ~(ECHO);

# if defined(HAVE_TERMIOS_H)
    {
	int	n = 10;

	/* A signal may cause tcsetattr() to fail (e.g., SIGCONT).  Retry a
	 * few times. */
	while (tcsetattr(read_cmd_fd, TCSANOW, &tnew) == -1
						   && errno == EINTR && n > 0)
	    --n;
    }
# else
    ioctl(read_cmd_fd, TCSETA, &tnew);
# endif

#else

    /*
     * for "old" tty systems
     */
# ifndef TIOCSETN
#  define TIOCSETN TIOCSETP	/* for hpux 9.0 */
# endif
    static struct sgttyb ttybold;
	   struct sgttyb ttybnew;

    if (first)
    {
	first = FALSE;
	ioctl(read_cmd_fd, TIOCGETP, &ttybold);
    }

    ttybnew = ttybold;
    if (tmode == TMODE_RAW)
    {
	ttybnew.sg_flags &= ~(CRMOD | ECHO);
	ttybnew.sg_flags |= RAW;
    }
    else if (tmode == TMODE_SLEEP)
	ttybnew.sg_flags &= ~(ECHO);
    ioctl(read_cmd_fd, TIOCSETN, &ttybnew);
#endif
    curr_tmode = tmode;
}

/*
 * 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 os_unixx.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(read_cmd_fd, &keys) != -1)
# else
    if (ioctl(read_cmd_fd, TCGETA, &keys) != -1)
# endif
    {
	buf[0] = keys.c_cc[VERASE];
	intr_char = keys.c_cc[VINTR];
#else
    /* for "old" tty systems */
    struct sgttyb keys;

    if (ioctl(read_cmd_fd, TIOCGETP, &keys) != -1)
    {
	buf[0] = keys.sg_erase;
	intr_char = keys.sg_kill;
#endif
	buf[1] = NUL;
	add_termcode((char_u *)"kb", buf, FALSE);

	/*
	 * 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(NULL);
    }
#if 0
    }	    /* to keep cindent happy */
#endif
}

#endif /* VMS  */

#if defined(FEAT_MOUSE_TTY) || defined(PROTO)
/*
 * Set mouse clicks on or off.
 */
    void
mch_setmouse(on)
    int		on;
{
    static int	ison = FALSE;
    int		xterm_mouse_vers;

    if (on == ison)	/* return quickly if nothing to do */
	return;

    xterm_mouse_vers = use_xterm_mouse();
    if (xterm_mouse_vers > 0)
    {
	if (on)	/* enable mouse events, use mouse tracking if available */
	    out_str_nf((char_u *)
		       (xterm_mouse_vers > 1
			? IF_EB("\033[?1002h", ESC_STR "[?1002h")
			: IF_EB("\033[?1000h", ESC_STR "[?1000h")));
	else	/* disable mouse events, could probably always send the same */
	    out_str_nf((char_u *)
		       (xterm_mouse_vers > 1
			? IF_EB("\033[?1002l", ESC_STR "[?1002l")
			: IF_EB("\033[?1000l", ESC_STR "[?1000l")));
	ison = on;
    }

# ifdef FEAT_MOUSE_DEC
    else if (ttym_flags == TTYM_DEC)
    {
	if (on)	/* enable mouse events */
	    out_str_nf((char_u *)"\033[1;2'z\033[1;3'{");
	else	/* disable mouse events */
	    out_str_nf((char_u *)"\033['z");
	ison = on;
    }
# endif

# ifdef FEAT_MOUSE_GPM
    else
    {
	if (on)
	{
	    if (gpm_open())
		ison = TRUE;
	}
	else
	{
	    gpm_close();
	    ison = FALSE;
	}
    }
# endif

# ifdef FEAT_MOUSE_JSB
    else
    {
	if (on)
	{
	    /* D - Enable Mouse up/down messages
	     * L - Enable Left Button Reporting
	     * M - Enable Middle Button Reporting
	     * R - Enable Right Button Reporting
	     * K - Enable SHIFT and CTRL key Reporting
	     * + - Enable Advanced messaging of mouse moves and up/down messages
	     * Q - Quiet No Ack
	     * # - Numeric value of mouse pointer required
	     *	  0 = Multiview 2000 cursor, used as standard
	     *	  1 = Windows Arrow
	     *	  2 = Windows I Beam
	     *	  3 = Windows Hour Glass
	     *	  4 = Windows Cross Hair
	     *	  5 = Windows UP Arrow
	     */
#ifdef JSBTERM_MOUSE_NONADVANCED /* Disables full feedback of pointer movements */
	    out_str_nf((char_u *)IF_EB("\033[0~ZwLMRK1Q\033\\",
					 ESC_STR "[0~ZwLMRK1Q" ESC_STR "\\"));
#else
	    out_str_nf((char_u *)IF_EB("\033[0~ZwLMRK+1Q\033\\",
					ESC_STR "[0~ZwLMRK+1Q" ESC_STR "\\"));
#endif
	    ison = TRUE;
	}
	else
	{
	    out_str_nf((char_u *)IF_EB("\033[0~ZwQ\033\\",
					      ESC_STR "[0~ZwQ" ESC_STR "\\"));
	    ison = FALSE;
	}
    }
# endif
# ifdef FEAT_MOUSE_PTERM
    else
    {
	/* 1 = button press, 6 = release, 7 = drag, 1h...9l = right button */
	if (on)
	    out_str_nf("\033[>1h\033[>6h\033[>7h\033[>1h\033[>9l");
	else
	    out_str_nf("\033[>1l\033[>6l\033[>7l\033[>1l\033[>9h");
	ison = on;
    }
# endif
}

/*
 * Set the mouse termcode, depending on the 'term' and 'ttymouse' options.
 */
    void
check_mouse_termcode()
{
# ifdef FEAT_MOUSE_XTERM
    if (use_xterm_mouse()
#  ifdef FEAT_GUI
	    && !gui.in_use
#  endif
	    )
    {
	set_mouse_termcode(KS_MOUSE, (char_u *)(term_is_8bit(T_NAME)
		    ? IF_EB("\233M", CSI_STR "M")
		    : IF_EB("\033[M", ESC_STR "[M")));
	if (*p_mouse != NUL)
	{
	    /* force mouse off and maybe on to send possibly new mouse
	     * activation sequence to the xterm, with(out) drag tracing. */
	    mch_setmouse(FALSE);
	    setmouse();
	}
    }
    else
	del_mouse_termcode(KS_MOUSE);
# endif

# ifdef FEAT_MOUSE_GPM
    if (!use_xterm_mouse()
#  ifdef FEAT_GUI
	    && !gui.in_use
#  endif
	    )
	set_mouse_termcode(KS_MOUSE, (char_u *)IF_EB("\033MG", ESC_STR "MG"));
# endif

# ifdef FEAT_MOUSE_JSB
    /* conflicts with xterm mouse: "\033[" and "\033[M" ??? */
    if (!use_xterm_mouse()
#  ifdef FEAT_GUI
	    && !gui.in_use
#  endif
	    )
	set_mouse_termcode(KS_JSBTERM_MOUSE,
			       (char_u *)IF_EB("\033[0~zw", ESC_STR "[0~zw"));
    else
	del_mouse_termcode(KS_JSBTERM_MOUSE);
# endif

# ifdef FEAT_MOUSE_NET
    /* There is no conflict, but one may type "ESC }" from Insert mode.  Don't
     * define it in the GUI or when using an xterm. */
    if (!use_xterm_mouse()
#  ifdef FEAT_GUI
	    && !gui.in_use
#  endif
	    )
	set_mouse_termcode(KS_NETTERM_MOUSE,
				       (char_u *)IF_EB("\033}", ESC_STR "}"));
    else
	del_mouse_termcode(KS_NETTERM_MOUSE);
# endif

# ifdef FEAT_MOUSE_DEC
    /* conflicts with xterm mouse: "\033[" and "\033[M" */
    if (!use_xterm_mouse()
#  ifdef FEAT_GUI
	    && !gui.in_use
#  endif
	    )
	set_mouse_termcode(KS_DEC_MOUSE, (char_u *)(term_is_8bit(T_NAME)
		     ? IF_EB("\233", CSI_STR) : IF_EB("\033[", ESC_STR "[")));
    else
	del_mouse_termcode(KS_DEC_MOUSE);
# endif
# ifdef FEAT_MOUSE_PTERM
    /* same as the dec mouse */
    if (!use_xterm_mouse()
#  ifdef FEAT_GUI
	    && !gui.in_use
#  endif
	    )
	set_mouse_termcode(KS_PTERM_MOUSE,
				      (char_u *) IF_EB("\033[", ESC_STR "["));
    else
	del_mouse_termcode(KS_PTERM_MOUSE);
# endif
}
#endif

/*
 * set screen mode, always fails.
 */
/* ARGSUSED */
    int
mch_screenmode(arg)
    char_u   *arg;
{
    EMSG(_(e_screenmode));
    return FAIL;
}

#ifndef VMS

/*
 * 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
 * Return OK when size could be determined, FAIL otherwise.
 */
    int
mch_get_shellsize()
{
    long	rows = 0;
    long	columns = 0;
    char_u	*p;

    /*
     * 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;
	int fd = 1;

	/* When stdout is not a tty, use stdin for the ioctl(). */
	if (!isatty(fd) && isatty(read_cmd_fd))
	    fd = read_cmd_fd;
	if (ioctl(fd, TIOCGWINSZ, &ws) == 0)
	{
	    columns = ws.ws_col;
	    rows = ws.ws_row;
	}
    }
# else /* TIOCGWINSZ */
#  ifdef TIOCGSIZE
    {
	struct ttysize	ts;
	int fd = 1;

	/* When stdout is not a tty, use stdin for the ioctl(). */
	if (!isatty(fd) && isatty(read_cmd_fd))
	    fd = read_cmd_fd;
	if (ioctl(fd, TIOCGSIZE, &ts) == 0)
	{
	    columns = ts.ts_cols;
	    rows = ts.ts_lines;
	}
    }
#  endif /* TIOCGSIZE */
# endif /* TIOCGWINSZ */

    /*
     * 2. get size from environment
     *    When being POSIX compliant ('|' flag in 'cpoptions') this overrules
     *    the ioctl() values!
     */
    if (columns == 0 || rows == 0 || vim_strchr(p_cpo, CPO_TSIZE) != NULL)
    {
	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 "co" and "li" entries from termcap
     */
    if (columns == 0 || rows == 0)
	getlinecol(&columns, &rows);
#endif

    /*
     * 4. If everything fails, use the old values
     */
    if (columns <= 0 || rows <= 0)
	return FAIL;

    Rows = rows;
    Columns = columns;
    return OK;
}

/*
 * Try to set the window size to Rows and Columns.
 */
    void
mch_set_shellsize()
{
    if (*T_CWS)
    {
	/*
	 * NOTE: if you get an error here that term_set_winsize() is
	 * undefined, check the output of configure.  It could probably not
	 * find a ncurses, termcap or termlib library.
	 */
	term_set_winsize((int)Rows, (int)Columns);
	out_flush();
	screen_start();			/* don't know where cursor is now */
    }
}

#endif /* VMS */

/*
 * Rows and/or Columns has changed.
 */
    void
mch_new_shellsize()
{
    /* Nothing to do. */
}

#ifndef USE_SYSTEM
static void append_ga_line __ARGS((garray_T *gap));

/*
 * Append the text in "gap" below the cursor line and clear "gap".
 */
    static void
append_ga_line(gap)
    garray_T	*gap;
{
    /* Remove trailing CR. */
    if (gap->ga_len > 0
	    && !curbuf->b_p_bin
	    && ((char_u *)gap->ga_data)[gap->ga_len - 1] == CAR)
	--gap->ga_len;
    ga_append(gap, NUL);
    ml_append(curwin->w_cursor.lnum++, gap->ga_data, 0, FALSE);
    gap->ga_len = 0;
}
#endif

    int
mch_call_shell(cmd, options)
    char_u	*cmd;
    int		options;	/* SHELL_*, see vim.h */
{
#ifdef VMS
    char	*ifn = NULL;
    char	*ofn = NULL;
#endif
    int		tmode = cur_tmode;
#ifdef USE_SYSTEM	/* use system() to start the shell: simple but slow */
    int	    x;
# ifndef __EMX__
    char_u  *newcmd;   /* only needed for unix */
# else
    /*
     * 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 = (char_u *)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((char *)p, "EMXSHELL=%s", p_sh);
	    putenv((char *)p);	/* don't free the pointer! */
	}
    }
# endif

    out_flush();

    if (options & SHELL_COOKED)
	settmode(TMODE_COOK);	    /* set to normal mode */

# ifdef __EMX__
    if (cmd == NULL)
	x = system("");	/* this starts an interactive shell in emx */
    else
	x = system((char *)cmd);
    /* system() returns -1 when error occurs in starting shell */
    if (x == -1 && !emsg_silent)
    {
	MSG_PUTS(_("\nCannot execute shell "));
	msg_outtrans(p_sh);
	msg_putchar('\n');
    }
# else /* not __EMX__ */
    if (cmd == NULL)
	x = system((char *)p_sh);
    else
    {
#  ifdef VMS
	if (ofn = strchr((char *)cmd, '>'))
	    *ofn++ = '\0';
	if (ifn = strchr((char *)cmd, '<'))
	{
	    char *p;

	    *ifn++ = '\0';
	    p = strchr(ifn,' '); /* chop off any trailing spaces */
	    if (p)
		*p = '\0';
	}
	if (ofn)
	    x = vms_sys((char *)cmd, ofn, ifn);
	else
	    x = system((char *)cmd);
#  else
	newcmd = lalloc(STRLEN(p_sh)
		+ (extra_shell_arg == NULL ? 0 : STRLEN(extra_shell_arg))
		+ STRLEN(p_shcf) + STRLEN(cmd) + 4, TRUE);
	if (newcmd == NULL)
	    x = 0;
	else
	{
	    sprintf((char *)newcmd, "%s %s %s %s", p_sh,
		    extra_shell_arg == NULL ? "" : (char *)extra_shell_arg,
		    (char *)p_shcf,
		    (char *)cmd);
	    x = system((char *)newcmd);
	    vim_free(newcmd);
	}
#  endif
    }
# ifdef VMS
    x = vms_sys_status(x);
# endif
    if (emsg_silent)
	;
    else if (x == 127)
	MSG_PUTS(_("\nCannot execute shell sh\n"));
# endif	/* __EMX__ */
    else if (x && !(options & SHELL_SILENT))
    {
	MSG_PUTS(_("\nshell returned "));
	msg_outnum((long)x);
	msg_putchar('\n');
    }

    if (tmode == TMODE_RAW)
	settmode(TMODE_RAW);	/* set to raw mode */
# ifdef FEAT_TITLE
    resettitle();
# endif
    return x;

#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 shells use that already */

    char_u	*newcmd = NULL;
    pid_t	pid;
    pid_t	wpid = 0;
    pid_t	wait_pid = 0;
# ifdef HAVE_UNION_WAIT
    union wait	status;
# else
    int		status = -1;
# endif
    int		retval = -1;
    char	**argv = NULL;
    int		argc;
    int		i;
    char_u	*p;
    int		inquote;
    int		pty_master_fd = -1;	    /* for pty's */
# ifdef FEAT_GUI
    int		pty_slave_fd = -1;
    char	*tty_name;
# endif
    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
    int		did_settmode = FALSE;	/* settmode(TMODE_RAW) called */

    out_flush();
    if (options & SHELL_COOKED)
	settmode(TMODE_COOK);		/* set to normal mode */

    newcmd = vim_strsave(p_sh);
    if (newcmd == NULL)		/* out of memory */
	goto error;

    /*
     * Do this loop twice:
     * 1: find number of arguments
     * 2: separate them and build argv[]
     */
    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++] = (char *)p_shcf;
	argv[argc++] = (char *)cmd;
    }
    argv[argc] = NULL;

    /*
     * For the GUI, when writing the output into the buffer and when reading
     * input from the buffer: Try using a pseudo-tty to get the stdin/stdout
     * of the executed command into the Vim window.  Or use a pipe.
     */
    if ((options & (SHELL_READ|SHELL_WRITE))
# ifdef FEAT_GUI
	    || (gui.in_use && show_shell_mess)
# endif
		    )
    {
# ifdef FEAT_GUI
	/*
	 * 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 && !(options & (SHELL_READ|SHELL_WRITE)))
	{
	    pty_master_fd = OpenPTY(&tty_name);	    /* open pty */
	    if (pty_master_fd >= 0 && ((pty_slave_fd =
				    open(tty_name, O_RDWR | O_EXTRA, 0)) < 0))
	    {
		close(pty_master_fd);
		pty_master_fd = -1;
	    }
	}
	/*
	 * If not opening a pty or it didn't work, try using pipes.
	 */
	if (pty_master_fd < 0)
# endif
	{
	    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_PUTS(_("\nCannot create pipes\n"));
		out_flush();
	    }
	}
    }

    if (!pipe_error)			/* pty or pipe opened or not used */
    {
# ifdef __BEOS__
	beos_cleanup_read_thread();
# endif

	if ((pid = fork()) == -1)	/* maybe we should use vfork() */
	{
	    MSG_PUTS(_("\nCannot fork\n"));
	    if ((options & (SHELL_READ|SHELL_WRITE))
# ifdef FEAT_GUI
		|| (gui.in_use && show_shell_mess)
# endif
		    )
	    {
# ifdef FEAT_GUI
		if (pty_master_fd >= 0)		/* close the pseudo tty */
		{
		    close(pty_master_fd);
		    close(pty_slave_fd);
		}
		else				/* close the pipes */
# endif
		{
		    close(fd_toshell[0]);
		    close(fd_toshell[1]);
		    close(fd_fromshell[0]);
		    close(fd_fromshell[1]);
		}
	    }
	}
	else if (pid == 0)	/* child */
	{
	    reset_signals();		/* handle signals normally */

	    if (!show_shell_mess || (options & SHELL_EXPAND))
	    {
		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
		 * Connect stdin to /dev/null too, so ":n `cat`" doesn't hang,
		 * waiting for input.
		 */
		fd = open("/dev/null", O_RDWR | O_EXTRA, 0);
		fclose(stdin);
		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)
		{
		    dup(fd); /* To replace stdin  (file descriptor 0) */
		    dup(fd); /* To replace stdout (file descriptor 1) */
		    dup(fd); /* To replace stderr (file descriptor 2) */

		    /* Don't need this now that we've duplicated it */
		    close(fd);
		}
	    }
	    else if ((options & (SHELL_READ|SHELL_WRITE))
# ifdef FEAT_GUI
		    || gui.in_use
# endif
		    )
	    {

# ifdef HAVE_SETSID
		/* Create our own process group, so that the child and all its
		 * children can be kill()ed.  Don't do this when using pipes,
		 * because stdin is not a tty, we would loose /dev/tty. */
		if (p_stmp)
		    (void)setsid();
# endif
# ifdef FEAT_GUI
		if (pty_slave_fd >= 0)
		{
		    /* push stream discipline modules */
		    if (options & SHELL_COOKED)
			SetupSlavePTY(pty_slave_fd);
#  ifdef TIOCSCTTY
		    /* Try to become controlling tty (probably doesn't work,
		     * unless run by root) */
		    ioctl(pty_slave_fd, TIOCSCTTY, (char *)NULL);
#  endif
		}
# 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", Rows);
		setenv("LINES", (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_Rows, "LINES=%ld", Rows);
		putenv(envbuf_Rows);
		sprintf(envbuf_Columns, "COLUMNS=%ld", Columns);
		putenv(envbuf_Columns);
# endif

		/*
		 * stderr is only redirected when using the GUI, so that a
		 * program like gpg can still access the terminal to get a
		 * passphrase using stderr.
		 */
# ifdef FEAT_GUI
		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);
		    if (gui.in_use)
		    {
			close(2);
			dup(pty_slave_fd);
		    }

		    close(pty_slave_fd);    /* has been dupped, close it now */
		}
		else
# endif
		{
		    /* 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]);

# ifdef FEAT_GUI
		    if (gui.in_use)
		    {
			/* 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.
	     * Call _exit() instead of exit() to avoid closing the connection
	     * to the X server (esp. with GTK, which uses atexit()).
	     */
	    execvp(argv[0], argv);
	    _exit(EXEC_FAILED);	    /* exec failed, return failure code */
	}
	else			/* parent */
	{
	    /*
	     * While child is running, ignore terminating signals.
	     * Do catch CTRL-C, so that "got_int" is set.
	     */
	    catch_signals(SIG_IGN, SIG_ERR);
	    catch_int_signal();

	    /*
	     * For the GUI we redirect stdin, stdout and stderr to our window.
	     * This is also used to pipe stdin/stdout to/from the external
	     * command.
	     */
	    if ((options & (SHELL_READ|SHELL_WRITE))
# ifdef FEAT_GUI
		    || (gui.in_use && show_shell_mess)
# endif
	       )
	    {
# define BUFLEN 100		/* length for buffer, pseudo tty limit is 128 */
		char_u	    buffer[BUFLEN + 1];
# ifdef FEAT_MBYTE
		int	    buffer_off = 0;	/* valid bytes in buffer[] */
# endif
		char_u	    ta_buf[BUFLEN + 1];	/* TypeAHead */
		int	    ta_len = 0;		/* valid bytes in ta_buf[] */
		int	    len;
		int	    p_more_save;
		int	    old_State;
		int	    c;
		int	    toshell_fd;
		int	    fromshell_fd;
		garray_T    ga;
		int	    noread_cnt;

# ifdef FEAT_GUI
		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
# endif
		{
		    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 */

		if ((options & SHELL_WRITE) && toshell_fd >= 0)
		{
		    /* Fork a process that will write the lines to the
		     * external program. */
		    if ((wpid = fork()) == -1)
		    {
			MSG_PUTS(_("\nCannot fork\n"));
		    }
		    else if (wpid == 0)
		    {
			linenr_T    lnum = curbuf->b_op_start.lnum;
			int	    written = 0;
			char_u	    *p = ml_get(lnum);
			char_u	    *s;
			size_t	    l;

			/* child */
			close(fromshell_fd);
			for (;;)
			{
			    l = STRLEN(p + written);
			    if (l == 0)
				len = 0;
			    else if (p[written] == NL)
				/* NL -> NUL translation */
				len = write(toshell_fd, "", (size_t)1);
			    else
			    {
				s = vim_strchr(p + written, NL);
				len = write(toshell_fd, (char *)p + written,
					   s == NULL ? l : s - (p + written));
			    }
			    if (len == l)
			    {
				/* Finished a line, add a NL, unless this line
				 * should not have one. */
				if (lnum != curbuf->b_op_end.lnum
					|| !curbuf->b_p_bin
					|| (lnum != write_no_eol_lnum
					    && (lnum !=
						    curbuf->b_ml.ml_line_count
						    || curbuf->b_p_eol)))
				    write(toshell_fd, "\n", (size_t)1);
				++lnum;
				if (lnum > curbuf->b_op_end.lnum)
				{
				    /* finished all the lines, close pipe */
				    close(toshell_fd);
				    toshell_fd = -1;
				    break;
				}
				p = ml_get(lnum);
				written = 0;
			    }
			    else if (len > 0)
				written += len;
			}
			_exit(0);
		    }
		    else
		    {
			close(toshell_fd);
			toshell_fd = -1;
		    }
		}

		if (options & SHELL_READ)
		    ga_init2(&ga, 1, BUFLEN);

		noread_cnt = 0;

		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).
		     * Don't do this when filtering and terminal is in cooked
		     * mode, the shell command will handle the I/O.  Avoids
		     * that a typed password is echoed for ssh or gpg command.
		     * Don't get characters when the child has already
		     * finished (wait_pid == 0).
		     * Don't get extra characters when we already have one.
		     * Don't read characters unless we didn't get output for a
		     * while, avoids that ":r !ls" eats typeahead.
		     */
		    len = 0;
		    if (!(options & SHELL_EXPAND)
			    && ((options &
					 (SHELL_READ|SHELL_WRITE|SHELL_COOKED))
				      != (SHELL_READ|SHELL_WRITE|SHELL_COOKED)
#ifdef FEAT_GUI
						    || gui.in_use
#endif
						    )
			    && wait_pid == 0
			    && (ta_len > 0
				|| (noread_cnt > 4
				    && (len = ui_inchar(ta_buf,
						       BUFLEN, 10L, 0)) > 0)))
		    {
			/*
			 * For pipes:
			 * Check for CTRL-C: send interrupt signal to child.
			 * Check for CTRL-D: EOF, close pipe to child.
			 */
			if (len == 1 && (pty_master_fd < 0 || cmd != NULL))
			{
# ifdef SIGINT
			    /*
			     * Send SIGINT to the child's group or all
			     * processes in our group.
			     */
			    if (ta_buf[ta_len] == Ctrl_C
					       || ta_buf[ta_len] == intr_char)
			    {
#  ifdef HAVE_SETSID
				kill(-pid, SIGINT);
#  else
				kill(0, SIGINT);
#  endif
				if (wpid > 0)
				    kill(wpid, SIGINT);
			    }
# endif
			    if (pty_master_fd < 0 && toshell_fd >= 0
					       && ta_buf[ta_len] == Ctrl_D)
			    {
				close(toshell_fd);
				toshell_fd = -1;
			    }
			}

			/* replace K_BS by <BS> and K_DEL by <DEL> */
			for (i = ta_len; i < ta_len + len; ++i)
			{
			    if (ta_buf[i] == CSI && len - i > 2)
			    {
				c = TERMCAP2KEY(ta_buf[i + 1], ta_buf[i + 2]);
				if (c == K_DEL || c == K_KDEL || c == K_BS)
				{
				    mch_memmove(ta_buf + i + 1, ta_buf + i + 3,
						       (size_t)(len - i - 2));
				    if (c == K_DEL || c == K_KDEL)
					ta_buf[i] = DEL;
				    else
					ta_buf[i] = Ctrl_H;
				    len -= 2;
				}
			    }
			    else if (ta_buf[i] == '\r')
				ta_buf[i] = '\n';
# ifdef FEAT_MBYTE
			    if (has_mbyte)
				i += (*mb_ptr2len)(ta_buf + i) - 1;
# endif
			}

			/*
			 * For pipes: echo the typed characters.
			 * For a pty this does not seem to work.
			 */
			if (pty_master_fd < 0)
			{
			    for (i = ta_len; i < ta_len + len; ++i)
			    {
				if (ta_buf[i] == '\n' || ta_buf[i] == '\b')
				    msg_putchar(ta_buf[i]);
# ifdef FEAT_MBYTE
				else if (has_mbyte)
				{
				    int l = (*mb_ptr2len)(ta_buf + i);

				    msg_outtrans_len(ta_buf + i, l);
				    i += l - 1;
				}
# endif
				else
				    msg_outtrans_len(ta_buf + i, 1);
			    }
			    windgoto(msg_row, msg_col);
			    out_flush();
			}

			ta_len += len;

			/*
			 * Write the characters to the child, unless EOF has
			 * been typed for pipes.  Write one character at a
			 * time, to avoid loosing too much typeahead.
			 * When writing buffer lines, drop the typed
			 * characters (only check for CTRL-C).
			 */
			if (options & SHELL_WRITE)
			    ta_len = 0;
			else if (toshell_fd >= 0)
			{
			    len = write(toshell_fd, (char *)ta_buf, (size_t)1);
			    if (len > 0)
			    {
				ta_len -= len;
				mch_memmove(ta_buf, ta_buf + len, ta_len);
				noread_cnt = 0;
			    }
			}
		    }

		    if (got_int)
		    {
			/* CTRL-C sends a signal to the child, we ignore it
			 * ourselves */
#  ifdef HAVE_SETSID
			kill(-pid, SIGINT);
#  else
			kill(0, SIGINT);
#  endif
			if (wpid > 0)
			    kill(wpid, SIGINT);
			got_int = FALSE;
		    }

		    /*
		     * Check if the child has any characters to be printed.
		     * Read them and write them to our window.	Repeat this as
		     * long as there is something to do, avoid the 10ms wait
		     * for mch_inchar(), or sending typeahead characters to
		     * the external process.
		     * TODO: This should handle escape sequences, compatible
		     * to some terminal (vt52?).
		     */
		    ++noread_cnt;
		    while (RealWaitForChar(fromshell_fd, 10L, NULL))
		    {
			len = read(fromshell_fd, (char *)buffer
# ifdef FEAT_MBYTE
				+ buffer_off, (size_t)(BUFLEN - buffer_off)
# else
				, (size_t)BUFLEN
# endif
				);
			if (len <= 0)		    /* end of file or error */
			    goto finished;

			noread_cnt = 0;
			if (options & SHELL_READ)
			{
			    /* Do NUL -> NL translation, append NL separated
			     * lines to the current buffer. */
			    for (i = 0; i < len; ++i)
			    {
				if (buffer[i] == NL)
				    append_ga_line(&ga);
				else if (buffer[i] == NUL)
				    ga_append(&ga, NL);
				else
				    ga_append(&ga, buffer[i]);
			    }
			}
# ifdef FEAT_MBYTE
			else if (has_mbyte)
			{
			    int		l;

			    len += buffer_off;
			    buffer[len] = NUL;

			    /* Check if the last character in buffer[] is
			     * incomplete, keep these bytes for the next
			     * round. */
			    for (p = buffer; p < buffer + len; p += l)
			    {
				l = mb_cptr2len(p);
				if (l == 0)
				    l = 1;  /* NUL byte? */
				else if (MB_BYTE2LEN(*p) != l)
				    break;
			    }
			    if (p == buffer)	/* no complete character */
			    {
				/* avoid getting stuck at an illegal byte */
				if (len >= 12)
				    ++p;
				else
				{
				    buffer_off = len;
				    continue;
				}
			    }
			    c = *p;
			    *p = NUL;
			    msg_puts(buffer);
			    if (p < buffer + len)
			    {
				*p = c;
				buffer_off = (buffer + len) - p;
				mch_memmove(buffer, p, buffer_off);
				continue;
			    }
			    buffer_off = 0;
			}
# endif /* FEAT_MBYTE */
			else
			{
			    buffer[len] = NUL;
			    msg_puts(buffer);
			}

			windgoto(msg_row, msg_col);
			cursor_on();
			out_flush();
			if (got_int)
			    break;
		    }

		    /* If we already detected the child has finished break the
		     * loop now. */
		    if (wait_pid == pid)
			break;

		    /*
		     * Check if the child still exists, before checking for
		     * typed characters (otherwise we would loose typeahead).
		     */
# ifdef __NeXT__
		    wait_pid = wait4(pid, &status, WNOHANG, (struct rusage *) 0);
# else
		    wait_pid = waitpid(pid, &status, WNOHANG);
# endif
		    if ((wait_pid == (pid_t)-1 && errno == ECHILD)
			    || (wait_pid == pid && WIFEXITED(status)))
		    {
			/* Don't break the loop yet, try reading more
			 * characters from "fromshell_fd" first.  When using
			 * pipes there might still be something to read and
			 * then we'll break the loop at the "break" above. */
			wait_pid = pid;
		    }
		    else
			wait_pid = 0;
		}
finished:
		p_more = p_more_save;
		if (options & SHELL_READ)
		{
		    if (ga.ga_len > 0)
		    {
			append_ga_line(&ga);
			/* remember that the NL was missing */
			write_no_eol_lnum = curwin->w_cursor.lnum;
		    }
		    else
			write_no_eol_lnum = 0;
		    ga_clear(&ga);
		}

		/*
		 * Give all typeahead that wasn't used back to ui_inchar().
		 */
		if (ta_len)
		    ui_inchar_undo(ta_buf, ta_len);
		State = old_State;
		if (toshell_fd >= 0)
		    close(toshell_fd);
		close(fromshell_fd);
	    }

	    /*
	     * Wait until our child has exited.
	     * Ignore wait() returning pids of other children and returning
	     * because of some signal like SIGWINCH.
	     * Don't wait if wait_pid was already set above, indicating the
	     * child already exited.
	     */
	    while (wait_pid != pid)
	    {
# ifdef _THREAD_SAFE
		/* Ugly hack: when compiled with Python threads are probably
		 * used, in which case wait() sometimes hangs for no obvious
		 * reason.  Use waitpid() instead and loop (like the GUI). */
#  ifdef __NeXT__
		wait_pid = wait4(pid, &status, WNOHANG, (struct rusage *)0);
#  else
		wait_pid = waitpid(pid, &status, WNOHANG);
#  endif
		if (wait_pid == 0)
		{
		    /* Wait for 1/100 sec before trying again. */
		    mch_delay(10L, TRUE);
		    continue;
		}
# else
		wait_pid = wait(&status);
# endif
		if (wait_pid <= 0
# ifdef ECHILD
			&& errno == ECHILD
# endif
		   )
		    break;
	    }

	    /* Make sure the child that writes to the external program is
	     * dead. */
	    if (wpid > 0)
		kill(wpid, SIGKILL);

	    /*
	     * Set to raw mode right now, otherwise a CTRL-C after
	     * catch_signals() will kill Vim.
	     */
	    if (tmode == TMODE_RAW)
		settmode(TMODE_RAW);
	    did_settmode = TRUE;
	    set_signals();

	    if (WIFEXITED(status))
	    {
		/* LINTED avoid "bitwise operation on signed value" */
		retval = WEXITSTATUS(status);
		if (retval && !emsg_silent)
		{
		    if (retval == EXEC_FAILED)
		    {
			MSG_PUTS(_("\nCannot execute shell "));
			msg_outtrans(p_sh);
			msg_putchar('\n');
		    }
		    else if (!(options & SHELL_SILENT))
		    {
			MSG_PUTS(_("\nshell returned "));
			msg_outnum((long)retval);
			msg_putchar('\n');
		    }
		}
	    }
	    else
		MSG_PUTS(_("\nCommand terminated\n"));
	}
    }
    vim_free(argv);

error:
    if (!did_settmode)
	if (tmode == TMODE_RAW)
	    settmode(TMODE_RAW);	/* set to raw mode */
# ifdef FEAT_TITLE
    resettitle();
# endif
    vim_free(newcmd);

    return retval;

#endif /* USE_SYSTEM */
}

/*
 * Check for CTRL-C typed by reading all available characters.
 * In cooked mode we should get SIGINT, no need to check.
 */
    void
mch_breakcheck()
{
    if (curr_tmode == TMODE_RAW && RealWaitForChar(read_cmd_fd, 0L, NULL))
	fill_input_buf(FALSE);
}

/*
 * 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;
{
#ifdef FEAT_MOUSE_GPM
    int		gpm_process_wanted;
#endif
#ifdef FEAT_XCLIPBOARD
    int		rest;
#endif
    int		avail;

    if (input_available())	    /* something in inbuf[] */
	return 1;

#if defined(FEAT_MOUSE_DEC)
    /* May need to query the mouse position. */
    if (WantQueryMouse)
    {
	WantQueryMouse = FALSE;
	mch_write((char_u *)IF_EB("\033[1'|", ESC_STR "[1'|"), 5);
    }
#endif

    /*
     * For FEAT_MOUSE_GPM and FEAT_XCLIPBOARD we loop here to process mouse
     * events.  This is a bit complicated, because they might both be defined.
     */
#if defined(FEAT_MOUSE_GPM) || defined(FEAT_XCLIPBOARD)
# ifdef FEAT_XCLIPBOARD
    rest = 0;
    if (do_xterm_trace())
	rest = msec;
# endif
    do
    {
# ifdef FEAT_XCLIPBOARD
	if (rest != 0)
	{
	    msec = XT_TRACE_DELAY;
	    if (rest >= 0 && rest < XT_TRACE_DELAY)
		msec = rest;
	    if (rest >= 0)
		rest -= msec;
	}
# endif
# ifdef FEAT_MOUSE_GPM
	gpm_process_wanted = 0;
	avail = RealWaitForChar(read_cmd_fd, msec, &gpm_process_wanted);
# else
	avail = RealWaitForChar(read_cmd_fd, msec, NULL);
# endif
	if (!avail)
	{
	    if (input_available())
		return 1;
# ifdef FEAT_XCLIPBOARD
	    if (rest == 0 || !do_xterm_trace())
# endif
		break;
	}
    }
    while (FALSE
# ifdef FEAT_MOUSE_GPM
	   || (gpm_process_wanted && mch_gpm_process() == 0)
# endif
# ifdef FEAT_XCLIPBOARD
	   || (!avail && rest != 0)
# endif
	  );

#else
    avail = RealWaitForChar(read_cmd_fd, msec, NULL);
#endif
    return avail;
}

/*
 * 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
 * Returns also, when a request from Sniff is waiting -- toni.
 * Or when a Linux GPM mouse event is waiting.
 */
/* ARGSUSED */
#if defined(__BEOS__)
    int
#else
    static  int
#endif
RealWaitForChar(fd, msec, check_for_gpm)
    int		fd;
    long	msec;
    int		*check_for_gpm;
{
    int		ret;
#if defined(FEAT_XCLIPBOARD) || defined(USE_XSMP) || defined(FEAT_MZSCHEME)
    static int	busy = FALSE;

    /* May retry getting characters after an event was handled. */
# define MAY_LOOP

# if defined(HAVE_GETTIMEOFDAY) && defined(HAVE_SYS_TIME_H)
    /* Remember at what time we started, so that we know how much longer we
     * should wait after being interrupted. */
#  define USE_START_TV
    struct timeval  start_tv;

    if (msec > 0 && (
#  ifdef FEAT_XCLIPBOARD
	    xterm_Shell != (Widget)0
#   if defined(USE_XSMP) || defined(FEAT_MZSCHEME)
	    ||
#   endif
#  endif
#  ifdef USE_XSMP
	    xsmp_icefd != -1
#   ifdef FEAT_MZSCHEME
	    ||
#   endif
#  endif
#  ifdef FEAT_MZSCHEME
	(mzthreads_allowed() && p_mzq > 0)
#  endif
	    ))
	gettimeofday(&start_tv, NULL);
# endif

    /* Handle being called recursively.  This may happen for the session
     * manager stuff, it may save the file, which does a breakcheck. */
    if (busy)
	return 0;
#endif

#ifdef MAY_LOOP
    for (;;)
#endif
    {
#ifdef MAY_LOOP
	int		finished = TRUE; /* default is to 'loop' just once */
# ifdef FEAT_MZSCHEME
	int		mzquantum_used = FALSE;
# endif
#endif
#ifndef HAVE_SELECT
	struct pollfd   fds[5];
	int		nfd;
# ifdef FEAT_XCLIPBOARD
	int		xterm_idx = -1;
# endif
# ifdef FEAT_MOUSE_GPM
	int		gpm_idx = -1;
# endif
# ifdef USE_XSMP
	int		xsmp_idx = -1;
# endif
	int		towait = (int)msec;

# ifdef FEAT_MZSCHEME
	mzvim_check_threads();
	if (mzthreads_allowed() && p_mzq > 0 && (msec < 0 || msec > p_mzq))
	{
	    towait = (int)p_mzq;    /* don't wait longer than 'mzquantum' */
	    mzquantum_used = TRUE;
	}
# endif
	fds[0].fd = fd;
	fds[0].events = POLLIN;
	nfd = 1;

# ifdef FEAT_SNIFF
#  define SNIFF_IDX 1
	if (want_sniff_request)
	{
	    fds[SNIFF_IDX].fd = fd_from_sniff;
	    fds[SNIFF_IDX].events = POLLIN;
	    nfd++;
	}
# endif
# ifdef FEAT_XCLIPBOARD
	if (xterm_Shell != (Widget)0)
	{
	    xterm_idx = nfd;
	    fds[nfd].fd = ConnectionNumber(xterm_dpy);
	    fds[nfd].events = POLLIN;
	    nfd++;
	}
# endif
# ifdef FEAT_MOUSE_GPM
	if (check_for_gpm != NULL && gpm_flag && gpm_fd >= 0)
	{
	    gpm_idx = nfd;
	    fds[nfd].fd = gpm_fd;
	    fds[nfd].events = POLLIN;
	    nfd++;
	}
# endif
# ifdef USE_XSMP
	if (xsmp_icefd != -1)
	{
	    xsmp_idx = nfd;
	    fds[nfd].fd = xsmp_icefd;
	    fds[nfd].events = POLLIN;
	    nfd++;
	}
# endif

	ret = poll(fds, nfd, towait);
# ifdef FEAT_MZSCHEME
	if (ret == 0 && mzquantum_used)
	    /* MzThreads scheduling is required and timeout occured */
	    finished = FALSE;
# endif

# ifdef FEAT_SNIFF
	if (ret < 0)
	    sniff_disconnect(1);
	else if (want_sniff_request)
	{
	    if (fds[SNIFF_IDX].revents & POLLHUP)
		sniff_disconnect(1);
	    if (fds[SNIFF_IDX].revents & POLLIN)
		sniff_request_waiting = 1;
	}
# endif
# ifdef FEAT_XCLIPBOARD
	if (xterm_Shell != (Widget)0 && (fds[xterm_idx].revents & POLLIN))
	{
	    xterm_update();      /* Maybe we should hand out clipboard */
	    if (--ret == 0 && !input_available())
		/* Try again */
		finished = FALSE;
	}
# endif
# ifdef FEAT_MOUSE_GPM
	if (gpm_idx >= 0 && (fds[gpm_idx].revents & POLLIN))
	{
	    *check_for_gpm = 1;
	}
# endif
# ifdef USE_XSMP
	if (xsmp_idx >= 0 && (fds[xsmp_idx].revents & (POLLIN | POLLHUP)))
	{
	    if (fds[xsmp_idx].revents & POLLIN)
	    {
		busy = TRUE;
		xsmp_handle_requests();
		busy = FALSE;
	    }
	    else if (fds[xsmp_idx].revents & POLLHUP)
	    {
		if (p_verbose > 0)
		    verb_msg((char_u *)_("XSMP lost ICE connection"));
		xsmp_close();
	    }
	    if (--ret == 0)
		finished = FALSE;	/* Try again */
	}
# endif


#else /* HAVE_SELECT */

	struct timeval  tv;
	struct timeval	*tvp;
	fd_set		rfds, efds;
	int		maxfd;
	long		towait = msec;

# ifdef FEAT_MZSCHEME
	mzvim_check_threads();
	if (mzthreads_allowed() && p_mzq > 0 && (msec < 0 || msec > p_mzq))
	{
	    towait = p_mzq;	/* don't wait longer than 'mzquantum' */
	    mzquantum_used = TRUE;
	}
# endif
# 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 != TMODE_RAW)
	    return 0;
# endif

	if (towait >= 0)
	{
	    tv.tv_sec = towait / 1000;
	    tv.tv_usec = (towait % 1000) * (1000000/1000);
	    tvp = &tv;
	}
	else
	    tvp = NULL;

	/*
	 * 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);
# if !defined(__QNX__) && !defined(__CYGWIN32__)
	/* For QNX select() always returns 1 if this is set.  Why? */
	FD_SET(fd, &efds);
# endif
	maxfd = fd;

# ifdef FEAT_SNIFF
	if (want_sniff_request)
	{
	    FD_SET(fd_from_sniff, &rfds);
	    FD_SET(fd_from_sniff, &efds);
	    if (maxfd < fd_from_sniff)
		maxfd = fd_from_sniff;
	}
# endif
# ifdef FEAT_XCLIPBOARD
	if (xterm_Shell != (Widget)0)
	{
	    FD_SET(ConnectionNumber(xterm_dpy), &rfds);
	    if (maxfd < ConnectionNumber(xterm_dpy))
		maxfd = ConnectionNumber(xterm_dpy);
	}
# endif
# ifdef FEAT_MOUSE_GPM
	if (check_for_gpm != NULL && gpm_flag && gpm_fd >= 0)
	{
	    FD_SET(gpm_fd, &rfds);
	    FD_SET(gpm_fd, &efds);
	    if (maxfd < gpm_fd)
		maxfd = gpm_fd;
	}
# endif
# ifdef USE_XSMP
	if (xsmp_icefd != -1)
	{
	    FD_SET(xsmp_icefd, &rfds);
	    FD_SET(xsmp_icefd, &efds);
	    if (maxfd < xsmp_icefd)
		maxfd = xsmp_icefd;
	}
# endif

# ifdef OLD_VMS
	/* Old VMS as v6.2 and older have broken select(). It waits more than
	 * required. Should not be used */
	ret = 0;
# else
	ret = select(maxfd + 1, &rfds, NULL, &efds, tvp);
# endif
# ifdef FEAT_MZSCHEME
	if (ret == 0 && mzquantum_used)
	    /* loop if MzThreads must be scheduled and timeout occured */
	    finished = FALSE;
# endif

# ifdef FEAT_SNIFF
	if (ret < 0 )
	    sniff_disconnect(1);
	else if (ret > 0 && want_sniff_request)
	{
	    if (FD_ISSET(fd_from_sniff, &efds))
		sniff_disconnect(1);
	    if (FD_ISSET(fd_from_sniff, &rfds))
		sniff_request_waiting = 1;
	}
# endif
# ifdef FEAT_XCLIPBOARD
	if (ret > 0 && xterm_Shell != (Widget)0
		&& FD_ISSET(ConnectionNumber(xterm_dpy), &rfds))
	{
	    xterm_update();	      /* Maybe we should hand out clipboard */
	    /* continue looping when we only got the X event and the input
	     * buffer is empty */
	    if (--ret == 0 && !input_available())
	    {
		/* Try again */
		finished = FALSE;
	    }
	}
# endif
# ifdef FEAT_MOUSE_GPM
	if (ret > 0 && gpm_flag && check_for_gpm != NULL && gpm_fd >= 0)
	{
	    if (FD_ISSET(gpm_fd, &efds))
		gpm_close();
	    else if (FD_ISSET(gpm_fd, &rfds))
		*check_for_gpm = 1;
	}
# endif
# ifdef USE_XSMP
	if (ret > 0 && xsmp_icefd != -1)
	{
	    if (FD_ISSET(xsmp_icefd, &efds))
	    {
		if (p_verbose > 0)
		    verb_msg((char_u *)_("XSMP lost ICE connection"));
		xsmp_close();
		if (--ret == 0)
		    finished = FALSE;   /* keep going if event was only one */
	    }
	    else if (FD_ISSET(xsmp_icefd, &rfds))
	    {
		busy = TRUE;
		xsmp_handle_requests();
		busy = FALSE;
		if (--ret == 0)
		    finished = FALSE;   /* keep going if event was only one */
	    }
	}
# endif

#endif /* HAVE_SELECT */

#ifdef MAY_LOOP
	if (finished || msec == 0)
	    break;

	/* We're going to loop around again, find out for how long */
	if (msec > 0)
	{
# ifdef USE_START_TV
	    struct timeval  mtv;

	    /* Compute remaining wait time. */
	    gettimeofday(&mtv, NULL);
	    msec -= (mtv.tv_sec - start_tv.tv_sec) * 1000L
				   + (mtv.tv_usec - start_tv.tv_usec) / 1000L;
# else
	    /* Guess we got interrupted halfway. */
	    msec = msec / 2;
# endif
	    if (msec <= 0)
		break;	/* waited long enough */
	}
#endif
    }

    return (ret > 0);
}

#ifndef VMS

#ifndef NO_EXPANDPATH
/*
 * Expand a path into all matching files and/or directories.  Handles "*",
 * "?", "[a-z]", "**", etc.
 * "path" has backslashes before chars that are not to be expanded.
 * Returns the number of matches found.
 */
    int
mch_expandpath(gap, path, flags)
    garray_T	*gap;
    char_u	*path;
    int		flags;		/* EW_* flags */
{
    return unix_expandpath(gap, path, 0, flags, FALSE);
}
#endif

/*
 * mch_expand_wildcards() - 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
 */

#ifndef SEEK_SET
# define SEEK_SET 0
#endif
#ifndef SEEK_END
# define SEEK_END 2
#endif

#define SHELL_SPECIAL (char_u *)"\t \"&'$;<>()\\|"

/* ARGSUSED */
    int
mch_expand_wildcards(num_pat, pat, num_file, file, flags)
    int		   num_pat;
    char_u	 **pat;
    int		  *num_file;
    char_u	***file;
    int		   flags;	/* EW_* flags */
{
    int		i;
    size_t	len;
    char_u	*p;
    int		dir;
#ifdef __EMX__
# define EXPL_ALLOC_INC	16
    char_u	**expl_files;
    size_t	files_alloced, files_free;
    char_u	*buf;
    int		has_wildcard;

    *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 == NULL)
	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 */
	    buf = expand_env_save(*pat);
	else
	    buf = vim_strsave(*pat);
	expl_files = NULL;
	has_wildcard = mch_has_exp_wildcard(buf);  /* (still) wildcards? */
	if (has_wildcard)   /* yes, so expand them */
	    expl_files = (char_u **)_fnexplode(buf);

	/*
	 * return value of buf if no wildcards left,
	 * OR if no match AND EW_NOTFOUND is set.
	 */
	if ((!has_wildcard && ((flags & EW_NOTFOUND) || mch_getperm(buf) >= 0))
		|| (expl_files == NULL && (flags & EW_NOTFOUND)))
	{   /* simply save the current contents of *buf */
	    expl_files = (char_u **)alloc(sizeof(char_u **) * 2);
	    if (expl_files != NULL)
	    {
		expl_files[0] = vim_strsave(buf);
		expl_files[1] = NULL;
	    }
	}
	vim_free(buf);

	/*
	 * 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.
	 */
	if (expl_files)
	{
	    for (i = 0; (p = expl_files[i]) != NULL; i++)
	    {
		dir = mch_isdir(p);
		/* If we don't want dirs and this is one, skip it */
		if ((dir && !(flags & EW_DIR)) || (!dir && !(flags & EW_FILE)))
		    continue;

		/* Skip files that are not executable if we check for that. */
		if (!dir && (flags & EW_EXEC) && !mch_can_exe(p))
		    continue;

		if (--files_free == 0)
		{
		    /* need more room in table of pointers */
		    files_alloced += EXPL_ALLOC_INC;
		    *file = (char_u **)vim_realloc(*file,
					   sizeof(char_u **) * files_alloced);
		    if (*file == NULL)
		    {
			EMSG(_(e_outofmem));
			*num_file = 0;
			return FAIL;
		    }
		    files_free = EXPL_ALLOC_INC;
		}
		slash_adjust(p);
		if (dir)
		{
		    /* For a directory we add a '/', unless it's already
		     * there. */
		    len = STRLEN(p);
		    if (((*file)[*num_file] = alloc(len + 2)) != NULL)
		    {
			STRCPY((*file)[*num_file], p);
			if (!after_pathsep((*file)[*num_file] + len))
			{
			    (*file)[*num_file][len] = psepc;
			    (*file)[*num_file][len + 1] = NUL;
			}
		    }
		}
		else
		{
		    (*file)[*num_file] = vim_strsave(p);
		}

		/*
		 * Error message already given by either alloc or vim_strsave.
		 * Should return FAIL, but returning OK works also.
		 */
		if ((*file)[*num_file] == NULL)
		    break;
		(*num_file)++;
	    }
	    _fnexplodefree((char **)expl_files);
	}
    }
    return OK;

#else /* __EMX__ */

    int		j;
    char_u	*tempname;
    char_u	*command;
    FILE	*fd;
    char_u	*buffer;
#define STYLE_ECHO  0	    /* use "echo" to expand */
#define STYLE_GLOB  1	    /* use "glob" to expand, for csh */
#define STYLE_PRINT 2	    /* use "print -N" to expand, for zsh */
#define STYLE_BT    3	    /* `cmd` expansion, execute the pattern directly */
    int		shell_style = STYLE_ECHO;
    int		check_spaces;
    static int	did_find_nul = FALSE;
    int		ampersent = FALSE;

    *num_file = 0;	/* default: no files found */
    *file = NULL;

    /*
     * 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))
	return save_patterns(num_pat, pat, num_file, file);

# ifdef HAVE_SANDBOX
    /* Don't allow any shell command in the sandbox. */
    if (sandbox != 0 && check_secure())
	return FAIL;
# endif

    /*
     * Don't allow the use of backticks in secure and restricted mode.
     */
    if (secure || restricted)
	for (i = 0; i < num_pat; ++i)
	    if (vim_strchr(pat[i], '`') != NULL
		    && (check_restricted() || check_secure()))
		return FAIL;

    /*
     * get a name for the temp file
     */
    if ((tempname = vim_tempname('o')) == NULL)
    {
	EMSG(_(e_notmp));
	return FAIL;
    }

    /*
     * Let the shell expand the patterns and write the result into the temp
     * file.  if expanding `cmd` execute it directly.
     * If we use csh, glob will work better than echo.
     * If we use zsh, print -N will work better than glob.
     */
    if (num_pat == 1 && *pat[0] == '`'
	    && (len = STRLEN(pat[0])) > 2
	    && *(pat[0] + len - 1) == '`')
	shell_style = STYLE_BT;
    else if ((len = STRLEN(p_sh)) >= 3)
    {
	if (STRCMP(p_sh + len - 3, "csh") == 0)
	    shell_style = STYLE_GLOB;
	else if (STRCMP(p_sh + len - 3, "zsh") == 0)
	    shell_style = STYLE_PRINT;
    }

    /* "unset nonomatch; print -N >" plus two is 29 */
    len = STRLEN(tempname) + 29;
    for (i = 0; i < num_pat; ++i)
    {
	/* Count the length of the patterns in the same way as they are put in
	 * "command" below. */
#ifdef USE_SYSTEM
	len += STRLEN(pat[i]) + 3;	/* add space and two quotes */
#else
	++len;				/* add space */
	for (j = 0; pat[i][j] != NUL; ++j)
	{
	    if (vim_strchr(SHELL_SPECIAL, pat[i][j]) != NULL)
		++len;		/* may add a backslash */
	    ++len;
	}
#endif
    }
    command = alloc(len);
    if (command == NULL)
    {
	/* out of memory */
	vim_free(tempname);
	return FAIL;
    }

    /*
     * Build the shell command:
     * - Set $nonomatch depending on EW_NOTFOUND (hopefully the shell
     *	 recognizes this).
     * - Add the shell command to print the expanded names.
     * - Add the temp file name.
     * - Add the file name patterns.
     */
    if (shell_style == STYLE_BT)
    {
	/* change `command; command& ` to (command; command ) */
	STRCPY(command, "(");
	STRCAT(command, pat[0] + 1);		/* exclude first backtick */
	p = command + STRLEN(command) - 1;
	*p-- = ')';				/* remove last backtick */
	while (p > command && vim_iswhite(*p))
	    --p;
	if (*p == '&')				/* remove trailing '&' */
	{
	    ampersent = TRUE;
	    *p = ' ';
	}
	STRCAT(command, ">");
    }
    else
    {
	if (flags & EW_NOTFOUND)
	    STRCPY(command, "set nonomatch; ");
	else
	    STRCPY(command, "unset nonomatch; ");
	if (shell_style == STYLE_GLOB)
	    STRCAT(command, "glob >");
	else if (shell_style == STYLE_PRINT)
	    STRCAT(command, "print -N >");
	else
	    STRCAT(command, "echo >");
    }
    STRCAT(command, tempname);
    if (shell_style != STYLE_BT)
	for (i = 0; i < num_pat; ++i)
	{
	    /* When using system() always add extra quotes, because the shell
	     * is started twice.  Otherwise put a backslash before special
	     * characters, except insice ``. */
#ifdef USE_SYSTEM
	    STRCAT(command, " \"");
	    STRCAT(command, pat[i]);
	    STRCAT(command, "\"");
#else
	    int intick = FALSE;

	    p = command + STRLEN(command);
	    *p++ = ' ';
	    for (j = 0; pat[i][j] != NUL; ++j)
	    {
		if (pat[i][j] == '`')
		    intick = !intick;
		else if (pat[i][j] == '\\' && pat[i][j + 1] != NUL)
		{
		    /* Remove a backslash, take char literally.  But keep
		     * backslash inside backticks, before a special character
		     * and before a backtick. */
		    if (intick
			  || vim_strchr(SHELL_SPECIAL, pat[i][j + 1]) != NULL
			  || pat[i][j + 1] == '`')
			*p++ = '\\';
		    ++j;
		}
		else if (!intick && vim_strchr(SHELL_SPECIAL,
							   pat[i][j]) != NULL)
		    /* Put a backslash before a special character, but not
		     * when inside ``. */
		    *p++ = '\\';

		/* Copy one character. */
		*p++ = pat[i][j];
	    }
	    *p = NUL;
#endif
	}
    if (flags & EW_SILENT)
	show_shell_mess = FALSE;
    if (ampersent)
	STRCAT(command, "&");		/* put the '&' back after the
					   redirection */

    /*
     * Using zsh -G: If a pattern has no matches, it is just deleted from
     * the argument list, otherwise zsh gives an error message and doesn't
     * expand any other pattern.
     */
    if (shell_style == STYLE_PRINT)
	extra_shell_arg = (char_u *)"-G";   /* Use zsh NULL_GLOB option */

    /*
     * 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.
     */
    else if (shell_style == STYLE_GLOB && !have_dollars(num_pat, pat))
	extra_shell_arg = (char_u *)"-f";	/* Use csh fast option */

    /*
     * execute the shell command
     */
    i = call_shell(command, SHELL_EXPAND | SHELL_SILENT);

    /* When running in the background, give it some time to create the temp
     * file, but don't wait for it to finish. */
    if (ampersent)
	mch_delay(10L, TRUE);

    extra_shell_arg = NULL;		/* cleanup */
    show_shell_mess = TRUE;
    vim_free(command);

    if (i)				/* mch_call_shell() failed */
    {
	mch_remove(tempname);
	vim_free(tempname);
	/*
	 * 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 (!(flags & EW_SILENT))
#endif
	{
	    redraw_later_clear();	/* probably messed up screen */
	    msg_putchar('\n');		/* clear bottom line quickly */
	    cmdline_row = Rows - 1;	/* continue on last line */
#ifdef USE_SYSTEM
	    if (!(flags & EW_SILENT))
#endif
	    {
		MSG(_(e_wildexpand));
		msg_start();		/* don't overwrite this message */
	    }
	}
	/* If a `cmd` expansion failed, don't list `cmd` as a match, even when
	 * EW_NOTFOUND is given */
	if (shell_style == STYLE_BT)
	    return FAIL;
	goto notfound;
    }

    /*
     * read the names from the file into memory
     */
    fd = fopen((char *)tempname, READBIN);
    if (fd == NULL)
    {
	/* Something went wrong, perhaps a file name with a special char. */
	if (!(flags & EW_SILENT))
	{
	    MSG(_(e_wildexpand));
	    msg_start();		/* don't overwrite this message */
	}
	vim_free(tempname);
	goto notfound;
    }
    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)
    {
	/* out of memory */
	mch_remove(tempname);
	vim_free(tempname);
	fclose(fd);
	return FAIL;
    }
    i = fread((char *)buffer, 1, len, fd);
    fclose(fd);
    mch_remove(tempname);
    if (i != len)
    {
	/* unexpected read error */
	EMSG2(_(e_notread), tempname);
	vim_free(tempname);
	vim_free(buffer);
	return FAIL;
    }
    vim_free(tempname);

#if defined(__CYGWIN__) || defined(__CYGWIN32__)
    /* Translate <CR><NL> into <NL>.  Caution, buffer may contain NUL. */
    p = buffer;
    for (i = 0; i < len; ++i)
	if (!(buffer[i] == CAR && buffer[i + 1] == NL))
	    *p++ = buffer[i];
    len = p - buffer;
# endif


    /* file names are separated with Space */
    if (shell_style == STYLE_ECHO)
    {
	buffer[len] = '\n';		/* make sure the buffer ends in NL */
	p = buffer;
	for (i = 0; *p != '\n'; ++i)	/* count number of entries */
	{
	    while (*p != ' ' && *p != '\n')
		++p;
	    p = skipwhite(p);		/* skip to next entry */
	}
    }
    /* file names are separated with NL */
    else if (shell_style == STYLE_BT)
    {
	buffer[len] = NUL;		/* make sure the buffer ends in NUL */
	p = buffer;
	for (i = 0; *p != NUL; ++i)	/* count number of entries */
	{
	    while (*p != '\n' && *p != NUL)
		++p;
	    if (*p != NUL)
		++p;
	    p = skipwhite(p);		/* skip leading white space */
	}
    }
    /* file names are separated with NUL */
    else
    {
	/*
	 * Some versions of zsh use spaces instead of NULs to separate
	 * results.  Only do this when there is no NUL before the end of the
	 * buffer, otherwise we would never be able to use file names with
	 * embedded spaces when zsh does use NULs.
	 * When we found a NUL once, we know zsh is OK, set did_find_nul and
	 * don't check for spaces again.
	 */
	check_spaces = FALSE;
	if (shell_style == STYLE_PRINT && !did_find_nul)
	{
	    /* If there is a NUL, set did_find_nul, else set check_spaces */
	    if (len && (int)STRLEN(buffer) < len - 1)
		did_find_nul = TRUE;
	    else
		check_spaces = TRUE;
	}

	/*
	 * Make sure the buffer ends with a NUL.  For STYLE_PRINT there
	 * already is one, for STYLE_GLOB it needs to be added.
	 */
	if (len && buffer[len - 1] == NUL)
	    --len;
	else
	    buffer[len] = NUL;
	i = 0;
	for (p = buffer; p < buffer + len; ++p)
	    if (*p == NUL || (*p == ' ' && check_spaces))   /* count entry */
	    {
		++i;
		*p = NUL;
	    }
	if (len)
	    ++i;			/* count last 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);
	goto notfound;
    }
    *num_file = i;
    *file = (char_u **)alloc(sizeof(char_u *) * i);
    if (*file == NULL)
    {
	/* out of memory */
	vim_free(buffer);
	return FAIL;
    }

    /*
     * Isolate the individual file names.
     */
    p = buffer;
    for (i = 0; i < *num_file; ++i)
    {
	(*file)[i] = p;
	/* Space or NL separates */
	if (shell_style == STYLE_ECHO || shell_style == STYLE_BT)
	{
	    while (!(shell_style == STYLE_ECHO && *p == ' ')
						   && *p != '\n' && *p != NUL)
		++p;
	    if (p == buffer + len)		/* last entry */
		*p = NUL;
	    else
	    {
		*p++ = NUL;
		p = skipwhite(p);		/* skip to next entry */
	    }
	}
	else		/* NUL separates */
	{
	    while (*p && p < buffer + len)	/* skip entry */
		++p;
	    ++p;				/* skip NUL */
	}
    }

    /*
     * Move the file names to allocated memory.
     */
    for (j = 0, i = 0; i < *num_file; ++i)
    {
	/* Require the files to exist.	Helps when using /bin/sh */
	if (!(flags & EW_NOTFOUND) && mch_getperm((*file)[i]) < 0)
	    continue;

	/* check if this entry should be included */
	dir = (mch_isdir((*file)[i]));
	if ((dir && !(flags & EW_DIR)) || (!dir && !(flags & EW_FILE)))
	    continue;

	/* Skip files that are not executable if we check for that. */
	if (!dir && (flags & EW_EXEC) && !mch_can_exe((*file)[i]))
	    continue;

	p = alloc((unsigned)(STRLEN((*file)[i]) + 1 + dir));
	if (p)
	{
	    STRCPY(p, (*file)[i]);
	    if (dir)
		STRCAT(p, "/");	    /* add '/' to a directory name */
	    (*file)[j++] = p;
	}
    }
    vim_free(buffer);
    *num_file = j;

    if (*num_file == 0)	    /* rejected all entries */
    {
	vim_free(*file);
	*file = NULL;
	goto notfound;
    }

    return OK;

notfound:
    if (flags & EW_NOTFOUND)
	return save_patterns(num_pat, pat, num_file, file);
    return FAIL;

#endif /* __EMX__ */
}

#endif /* VMS */

#ifndef __EMX__
    static int
save_patterns(num_pat, pat, num_file, file)
    int		num_pat;
    char_u	**pat;
    int		*num_file;
    char_u	***file;
{
    int		i;
    char_u	*s;

    *file = (char_u **)alloc(num_pat * sizeof(char_u *));
    if (*file == NULL)
	return FAIL;
    for (i = 0; i < num_pat; i++)
    {
	s = vim_strsave(pat[i]);
	if (s != NULL)
	    /* Be compatible with expand_filename(): halve the number of
	     * backslashes. */
	    backslash_halve(s);
	(*file)[i] = s;
    }
    *num_file = num_pat;
    return OK;
}
#endif


/*
 * Return TRUE if the string "p" contains a wildcard that mch_expandpath() can
 * expand.
 */
    int
mch_has_exp_wildcard(p)
    char_u  *p;
{
    for ( ; *p; mb_ptr_adv(p))
    {
#ifndef OS2
	if (*p == '\\' && p[1] != NUL)
	    ++p;
	else
#endif
	    if (vim_strchr((char_u *)
#ifdef VMS
				    "*?%"
#else
# ifdef OS2
				    "*?"
# else
				    "*?[{'"
# endif
#endif
						, *p) != NULL)
	    return TRUE;
    }
    return FALSE;
}

/*
 * Return TRUE if the string "p" contains a wildcard.
 * Don't recognize '~' at the end as a wildcard.
 */
    int
mch_has_wildcard(p)
    char_u  *p;
{
    for ( ; *p; mb_ptr_adv(p))
    {
#ifndef OS2
	if (*p == '\\' && p[1] != NUL)
	    ++p;
	else
#endif
	    if (vim_strchr((char_u *)
#ifdef VMS
				    "*?%$"
#else
# ifdef OS2
#  ifdef VIM_BACKTICK
				    "*?$`"
#  else
				    "*?$"
#  endif
# else
				    "*?[{`'$"
# endif
#endif
						, *p) != NULL
		|| (*p == '~' && p[1] != NUL))
	    return TRUE;
    }
    return FALSE;
}

#ifndef __EMX__
    static int
have_wildcard(num, file)
    int	    num;
    char_u  **file;
{
    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;
{
    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
mch_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 (mch_remove(src) == 0)	    /* delete link to old name */
	return 0;
    return -1;
}
#endif /* !HAVE_RENAME */

#ifdef FEAT_MOUSE_GPM
/*
 * Initializes connection with gpm (if it isn't already opened)
 * Return 1 if succeeded (or connection already opened), 0 if failed
 */
    static int
gpm_open()
{
    static Gpm_Connect gpm_connect; /* Must it be kept till closing ? */

    if (!gpm_flag)
    {
	gpm_connect.eventMask = (GPM_UP | GPM_DRAG | GPM_DOWN);
	gpm_connect.defaultMask = ~GPM_HARD;
	/* Default handling for mouse move*/
	gpm_connect.minMod = 0; /* Handle any modifier keys */
	gpm_connect.maxMod = 0xffff;
	if (Gpm_Open(&gpm_connect, 0) > 0)
	{
	    /* gpm library tries to handling TSTP causes
	     * problems. Anyways, we close connection to Gpm whenever
	     * we are going to suspend or starting an external process
	     * so we should'nt  have problem with this
	     */
	    signal(SIGTSTP, restricted ? SIG_IGN : SIG_DFL);
	    return 1; /* succeed */
	}
	if (gpm_fd == -2)
	    Gpm_Close(); /* We don't want to talk to xterm via gpm */
	return 0;
    }
    return 1; /* already open */
}

/*
 * Closes connection to gpm
 * returns non-zero if connection succesfully closed
 */
    static void
gpm_close()
{
    if (gpm_flag && gpm_fd >= 0) /* if Open */
	Gpm_Close();
}

/* Reads gpm event and adds special keys to input buf. Returns length of
 * generated key sequence.
 * This function is made after gui_send_mouse_event
 */
    static int
mch_gpm_process()
{
    int			button;
    static Gpm_Event	gpm_event;
    char_u		string[6];
    int_u		vim_modifiers;
    int			row,col;
    unsigned char	buttons_mask;
    unsigned char	gpm_modifiers;
    static unsigned char old_buttons = 0;

    Gpm_GetEvent(&gpm_event);

#ifdef FEAT_GUI
    /* Don't put events in the input queue now. */
    if (hold_gui_events)
	return 0;
#endif

    row = gpm_event.y - 1;
    col = gpm_event.x - 1;

    string[0] = ESC; /* Our termcode */
    string[1] = 'M';
    string[2] = 'G';
    switch (GPM_BARE_EVENTS(gpm_event.type))
    {
	case GPM_DRAG:
	    string[3] = MOUSE_DRAG;
	    break;
	case GPM_DOWN:
	    buttons_mask = gpm_event.buttons & ~old_buttons;
	    old_buttons = gpm_event.buttons;
	    switch (buttons_mask)
	    {
		case GPM_B_LEFT:
		    button = MOUSE_LEFT;
		    break;
		case GPM_B_MIDDLE:
		    button = MOUSE_MIDDLE;
		    break;
		case GPM_B_RIGHT:
		    button = MOUSE_RIGHT;
		    break;
		default:
		    return 0;
		    /*Don't know what to do. Can more than one button be
		     * reported in one event? */
	    }
	    string[3] = (char_u)(button | 0x20);
	    SET_NUM_MOUSE_CLICKS(string[3], gpm_event.clicks + 1);
	    break;
	case GPM_UP:
	    string[3] = MOUSE_RELEASE;
	    old_buttons &= ~gpm_event.buttons;
	    break;
	default:
	    return 0;
    }
    /*This code is based on gui_x11_mouse_cb in gui_x11.c */
    gpm_modifiers = gpm_event.modifiers;
    vim_modifiers = 0x0;
    /* I ignore capslock stats. Aren't we all just hate capslock mixing with
     * Vim commands ? Besides, gpm_event.modifiers is unsigned char, and
     * K_CAPSSHIFT is defined 8, so it probably isn't even reported
     */
    if (gpm_modifiers & ((1 << KG_SHIFT) | (1 << KG_SHIFTR) | (1 << KG_SHIFTL)))
	vim_modifiers |= MOUSE_SHIFT;

    if (gpm_modifiers & ((1 << KG_CTRL) | (1 << KG_CTRLR) | (1 << KG_CTRLL)))
	vim_modifiers |= MOUSE_CTRL;
    if (gpm_modifiers & ((1 << KG_ALT) | (1 << KG_ALTGR)))
	vim_modifiers |= MOUSE_ALT;
    string[3] |= vim_modifiers;
    string[4] = (char_u)(col + ' ' + 1);
    string[5] = (char_u)(row + ' ' + 1);
    add_to_input_buf(string, 6);
    return 6;
}
#endif /* FEAT_MOUSE_GPM */

#if defined(FEAT_LIBCALL) || defined(PROTO)
typedef char_u * (*STRPROCSTR)__ARGS((char_u *));
typedef char_u * (*INTPROCSTR)__ARGS((int));
typedef int (*STRPROCINT)__ARGS((char_u *));
typedef int (*INTPROCINT)__ARGS((int));

/*
 * Call a DLL routine which takes either a string or int param
 * and returns an allocated string.
 */
    int
mch_libcall(libname, funcname, argstring, argint, string_result, number_result)
    char_u	*libname;
    char_u	*funcname;
    char_u	*argstring;	/* NULL when using a argint */
    int		argint;
    char_u	**string_result;/* NULL when using number_result */
    int		*number_result;
{
# if defined(USE_DLOPEN)
    void	*hinstLib;
    char	*dlerr = NULL;
# else
    shl_t	hinstLib;
# endif
    STRPROCSTR	ProcAdd;
    INTPROCSTR	ProcAddI;
    char_u	*retval_str = NULL;
    int		retval_int = 0;
    int		success = FALSE;

    /* Get a handle to the DLL module. */
# if defined(USE_DLOPEN)
    hinstLib = dlopen((char *)libname, RTLD_LAZY
#  ifdef RTLD_LOCAL
	    | RTLD_LOCAL
#  endif
	    );
    if (hinstLib == NULL)
    {
	/* "dlerr" must be used before dlclose() */
	dlerr = (char *)dlerror();
	if (dlerr != NULL)
	    EMSG2(_("dlerror = \"%s\""), dlerr);
    }
# else
    hinstLib = shl_load((const char*)libname, BIND_IMMEDIATE|BIND_VERBOSE, 0L);
# endif

    /* If the handle is valid, try to get the function address. */
    if (hinstLib != NULL)
    {
# ifdef HAVE_SETJMP_H
	/*
	 * Catch a crash when calling the library function.  For example when
	 * using a number where a string pointer is expected.
	 */
	mch_startjmp();
	if (SETJMP(lc_jump_env) != 0)
	{
	    success = FALSE;
	    dlerr = NULL;
	    mch_didjmp();
	}
	else
# endif
	{
	    retval_str = NULL;
	    retval_int = 0;

	    if (argstring != NULL)
	    {
# if defined(USE_DLOPEN)
		ProcAdd = (STRPROCSTR)dlsym(hinstLib, (const char *)funcname);
		dlerr = (char *)dlerror();
# else
		if (shl_findsym(&hinstLib, (const char *)funcname,
					TYPE_PROCEDURE, (void *)&ProcAdd) < 0)
		    ProcAdd = NULL;
# endif
		if ((success = (ProcAdd != NULL
# if defined(USE_DLOPEN)
			    && dlerr == NULL
# endif
			    )))
		{
		    if (string_result == NULL)
			retval_int = ((STRPROCINT)ProcAdd)(argstring);
		    else
			retval_str = (ProcAdd)(argstring);
		}
	    }
	    else
	    {
# if defined(USE_DLOPEN)
		ProcAddI = (INTPROCSTR)dlsym(hinstLib, (const char *)funcname);
		dlerr = (char *)dlerror();
# else
		if (shl_findsym(&hinstLib, (const char *)funcname,
				       TYPE_PROCEDURE, (void *)&ProcAddI) < 0)
		    ProcAddI = NULL;
# endif
		if ((success = (ProcAddI != NULL
# if defined(USE_DLOPEN)
			    && dlerr == NULL
# endif
			    )))
		{
		    if (string_result == NULL)
			retval_int = ((INTPROCINT)ProcAddI)(argint);
		    else
			retval_str = (ProcAddI)(argint);
		}
	    }

	    /* Save the string before we free the library. */
	    /* Assume that a "1" or "-1" result is an illegal pointer. */
	    if (string_result == NULL)
		*number_result = retval_int;
	    else if (retval_str != NULL
		    && retval_str != (char_u *)1
		    && retval_str != (char_u *)-1)
		*string_result = vim_strsave(retval_str);
	}

# ifdef HAVE_SETJMP_H
	mch_endjmp();
#  ifdef SIGHASARG
	if (lc_signal != 0)
	{
	    int i;

	    /* try to find the name of this signal */
	    for (i = 0; signal_info[i].sig != -1; i++)
		if (lc_signal == signal_info[i].sig)
		    break;
	    EMSG2("E368: got SIG%s in libcall()", signal_info[i].name);
	}
#  endif
# endif

# if defined(USE_DLOPEN)
	/* "dlerr" must be used before dlclose() */
	if (dlerr != NULL)
	    EMSG2(_("dlerror = \"%s\""), dlerr);

	/* Free the DLL module. */
	(void)dlclose(hinstLib);
# else
	(void)shl_unload(hinstLib);
# endif
    }

    if (!success)
    {
	EMSG2(_(e_libcall), funcname);
	return FAIL;
    }

    return OK;
}
#endif

#if (defined(FEAT_X11) && defined(FEAT_XCLIPBOARD)) || defined(PROTO)
static int	xterm_trace = -1;	/* default: disabled */
static int	xterm_button;

/*
 * Setup a dummy window for X selections in a terminal.
 */
    void
setup_term_clip()
{
    int		z = 0;
    char	*strp = "";
    Widget	AppShell;

    if (!x_connect_to_server())
	return;

    open_app_context();
    if (app_context != NULL && xterm_Shell == (Widget)0)
    {
	int (*oldhandler)();
#if defined(HAVE_SETJMP_H)
	int (*oldIOhandler)();
#endif
# if defined(HAVE_GETTIMEOFDAY) && defined(HAVE_SYS_TIME_H)
	struct timeval  start_tv;

	if (p_verbose > 0)
	    gettimeofday(&start_tv, NULL);
# endif

	/* Ignore X errors while opening the display */
	oldhandler = XSetErrorHandler(x_error_check);

#if defined(HAVE_SETJMP_H)
	/* Ignore X IO errors while opening the display */
	oldIOhandler = XSetIOErrorHandler(x_IOerror_check);
	mch_startjmp();
	if (SETJMP(lc_jump_env) != 0)
	{
	    mch_didjmp();
	    xterm_dpy = NULL;
	}
	else
#endif
	{
	    xterm_dpy = XtOpenDisplay(app_context, xterm_display,
		    "vim_xterm", "Vim_xterm", NULL, 0, &z, &strp);
#if defined(HAVE_SETJMP_H)
	    mch_endjmp();
#endif
	}

#if defined(HAVE_SETJMP_H)
	/* Now handle X IO errors normally. */
	(void)XSetIOErrorHandler(oldIOhandler);
#endif
	/* Now handle X errors normally. */
	(void)XSetErrorHandler(oldhandler);

	if (xterm_dpy == NULL)
	{
	    if (p_verbose > 0)
		verb_msg((char_u *)_("Opening the X display failed"));
	    return;
	}

	/* Catch terminating error of the X server connection. */
	(void)XSetIOErrorHandler(x_IOerror_handler);

# if defined(HAVE_GETTIMEOFDAY) && defined(HAVE_SYS_TIME_H)
	if (p_verbose > 0)
	{
	    verbose_enter();
	    xopen_message(&start_tv);
	    verbose_leave();
	}
# endif

	/* Create a Shell to make converters work. */
	AppShell = XtVaAppCreateShell("vim_xterm", "Vim_xterm",
		applicationShellWidgetClass, xterm_dpy,
		NULL);
	if (AppShell == (Widget)0)
	    return;
	xterm_Shell = XtVaCreatePopupShell("VIM",
		topLevelShellWidgetClass, AppShell,
		XtNmappedWhenManaged, 0,
		XtNwidth, 1,
		XtNheight, 1,
		NULL);
	if (xterm_Shell == (Widget)0)
	    return;

	x11_setup_atoms(xterm_dpy);
	if (x11_display == NULL)
	    x11_display = xterm_dpy;

	XtRealizeWidget(xterm_Shell);
	XSync(xterm_dpy, False);
	xterm_update();
    }
    if (xterm_Shell != (Widget)0)
    {
	clip_init(TRUE);
	if (x11_window == 0 && (strp = getenv("WINDOWID")) != NULL)
	    x11_window = (Window)atol(strp);
	/* Check if $WINDOWID is valid. */
	if (test_x11_window(xterm_dpy) == FAIL)
	    x11_window = 0;
	if (x11_window != 0)
	    xterm_trace = 0;
    }
}

    void
start_xterm_trace(button)
    int button;
{
    if (x11_window == 0 || xterm_trace < 0 || xterm_Shell == (Widget)0)
	return;
    xterm_trace = 1;
    xterm_button = button;
    do_xterm_trace();
}


    void
stop_xterm_trace()
{
    if (xterm_trace < 0)
	return;
    xterm_trace = 0;
}

/*
 * Query the xterm pointer and generate mouse termcodes if necessary
 * return TRUE if dragging is active, else FALSE
 */
    static int
do_xterm_trace()
{
    Window		root, child;
    int			root_x, root_y;
    int			win_x, win_y;
    int			row, col;
    int_u		mask_return;
    char_u		buf[50];
    char_u		*strp;
    long		got_hints;
    static char_u	*mouse_code;
    static char_u	mouse_name[2] = {KS_MOUSE, KE_FILLER};
    static int		prev_row = 0, prev_col = 0;
    static XSizeHints	xterm_hints;

    if (xterm_trace <= 0)
	return FALSE;

    if (xterm_trace == 1)
    {
	/* Get the hints just before tracking starts.  The font size might
	 * have changed recently */
	XGetWMNormalHints(xterm_dpy, x11_window, &xterm_hints, &got_hints);
	if (!(got_hints & PResizeInc)
		|| xterm_hints.width_inc <= 1
		|| xterm_hints.height_inc <= 1)
	{
	    xterm_trace = -1;  /* Not enough data -- disable tracing */
	    return FALSE;
	}

	/* Rely on the same mouse code for the duration of this */
	mouse_code = find_termcode(mouse_name);
	prev_row = mouse_row;
	prev_row = mouse_col;
	xterm_trace = 2;

	/* Find the offset of the chars, there might be a scrollbar on the
	 * left of the window and/or a menu on the top (eterm etc.) */
	XQueryPointer(xterm_dpy, x11_window, &root, &child, &root_x, &root_y,
		      &win_x, &win_y, &mask_return);
	xterm_hints.y = win_y - (xterm_hints.height_inc * mouse_row)
			      - (xterm_hints.height_inc / 2);
	if (xterm_hints.y <= xterm_hints.height_inc / 2)
	    xterm_hints.y = 2;
	xterm_hints.x = win_x - (xterm_hints.width_inc * mouse_col)
			      - (xterm_hints.width_inc / 2);
	if (xterm_hints.x <= xterm_hints.width_inc / 2)
	    xterm_hints.x = 2;
	return TRUE;
    }
    if (mouse_code == NULL)
    {
	xterm_trace = 0;
	return FALSE;
    }

    XQueryPointer(xterm_dpy, x11_window, &root, &child, &root_x, &root_y,
		  &win_x, &win_y, &mask_return);

    row = check_row((win_y - xterm_hints.y) / xterm_hints.height_inc);
    col = check_col((win_x - xterm_hints.x) / xterm_hints.width_inc);
    if (row == prev_row && col == prev_col)
	return TRUE;

    STRCPY(buf, mouse_code);
    strp = buf + STRLEN(buf);
    *strp++ = (xterm_button | MOUSE_DRAG) & ~0x20;
    *strp++ = (char_u)(col + ' ' + 1);
    *strp++ = (char_u)(row + ' ' + 1);
    *strp = 0;
    add_to_input_buf(buf, STRLEN(buf));

    prev_row = row;
    prev_col = col;
    return TRUE;
}

# if defined(FEAT_GUI) || defined(PROTO)
/*
 * Destroy the display, window and app_context.  Required for GTK.
 */
    void
clear_xterm_clip()
{
    if (xterm_Shell != (Widget)0)
    {
	XtDestroyWidget(xterm_Shell);
	xterm_Shell = (Widget)0;
    }
    if (xterm_dpy != NULL)
    {
#if 0
	/* Lesstif and Solaris crash here, lose some memory */
	XtCloseDisplay(xterm_dpy);
#endif
	if (x11_display == xterm_dpy)
	    x11_display = NULL;
	xterm_dpy = NULL;
    }
#if 0
    if (app_context != (XtAppContext)NULL)
    {
	/* Lesstif and Solaris crash here, lose some memory */
	XtDestroyApplicationContext(app_context);
	app_context = (XtAppContext)NULL;
    }
#endif
}
# endif

/*
 * Catch up with any queued X events.  This may put keyboard input into the
 * input buffer, call resize call-backs, trigger timers etc.  If there is
 * nothing in the X event queue (& no timers pending), then we return
 * immediately.
 */
    static void
xterm_update()
{
    XEvent event;

    while (XtAppPending(app_context) && !vim_is_input_buf_full())
    {
	XtAppNextEvent(app_context, &event);
#ifdef FEAT_CLIENTSERVER
	{
	    XPropertyEvent *e = (XPropertyEvent *)&event;

	    if (e->type == PropertyNotify && e->window == commWindow
		   && e->atom == commProperty && e->state == PropertyNewValue)
		serverEventProc(xterm_dpy, &event);
	}
#endif
	XtDispatchEvent(&event);
    }
}

    int
clip_xterm_own_selection(cbd)
    VimClipboard *cbd;
{
    if (xterm_Shell != (Widget)0)
	return clip_x11_own_selection(xterm_Shell, cbd);
    return FAIL;
}

    void
clip_xterm_lose_selection(cbd)
    VimClipboard *cbd;
{
    if (xterm_Shell != (Widget)0)
	clip_x11_lose_selection(xterm_Shell, cbd);
}

    void
clip_xterm_request_selection(cbd)
    VimClipboard *cbd;
{
    if (xterm_Shell != (Widget)0)
	clip_x11_request_selection(xterm_Shell, xterm_dpy, cbd);
}

    void
clip_xterm_set_selection(cbd)
    VimClipboard *cbd;
{
    clip_x11_set_selection(cbd);
}
#endif


#if defined(USE_XSMP) || defined(PROTO)
/*
 * Code for X Session Management Protocol.
 */
static void xsmp_handle_save_yourself __ARGS((SmcConn smc_conn, SmPointer client_data, int save_type, Bool shutdown, int interact_style, Bool fast));
static void xsmp_die __ARGS((SmcConn smc_conn, SmPointer client_data));
static void xsmp_save_complete __ARGS((SmcConn smc_conn, SmPointer client_data));
static void xsmp_shutdown_cancelled __ARGS((SmcConn smc_conn, SmPointer	client_data));
static void xsmp_ice_connection __ARGS((IceConn iceConn, IcePointer clientData, Bool opening, IcePointer *watchData));


# if defined(FEAT_GUI) && defined(USE_XSMP_INTERACT)
static void xsmp_handle_interaction __ARGS((SmcConn smc_conn, SmPointer client_data));

/*
 * This is our chance to ask the user if they want to save,
 * or abort the logout
 */
/*ARGSUSED*/
    static void
xsmp_handle_interaction(smc_conn, client_data)
    SmcConn	smc_conn;
    SmPointer	client_data;
{
    cmdmod_T	save_cmdmod;
    int		cancel_shutdown = False;

    save_cmdmod = cmdmod;
    cmdmod.confirm = TRUE;
    if (check_changed_any(FALSE))
	/* Mustn't logout */
	cancel_shutdown = True;
    cmdmod = save_cmdmod;
    setcursor();		/* position cursor */
    out_flush();

    /* Done interaction */
    SmcInteractDone(smc_conn, cancel_shutdown);

    /* Finish off
     * Only end save-yourself here if we're not cancelling shutdown;
     * we'll get a cancelled callback later in which we'll end it.
     * Hopefully get around glitchy SMs (like GNOME-1)
     */
    if (!cancel_shutdown)
    {
	xsmp.save_yourself = False;
	SmcSaveYourselfDone(smc_conn, True);
    }
}
# endif

/*
 * Callback that starts save-yourself.
 */
/*ARGSUSED*/
    static void
xsmp_handle_save_yourself(smc_conn, client_data, save_type,
					       shutdown, interact_style, fast)
    SmcConn	smc_conn;
    SmPointer	client_data;
    int		save_type;
    Bool	shutdown;
    int		interact_style;
    Bool	fast;
{
    /* Handle already being in saveyourself */
    if (xsmp.save_yourself)
	SmcSaveYourselfDone(smc_conn, True);
    xsmp.save_yourself = True;
    xsmp.shutdown = shutdown;

    /* First up, preserve all files */
    out_flush();
    ml_sync_all(FALSE, FALSE);	/* preserve all swap files */

    if (p_verbose > 0)
	verb_msg((char_u *)_("XSMP handling save-yourself request"));

# if defined(FEAT_GUI) && defined(USE_XSMP_INTERACT)
    /* Now see if we can ask about unsaved files */
    if (shutdown && !fast && gui.in_use)
	/* Need to interact with user, but need SM's permission */
	SmcInteractRequest(smc_conn, SmDialogError,
					xsmp_handle_interaction, client_data);
    else
# endif
    {
	/* Can stop the cycle here */
	SmcSaveYourselfDone(smc_conn, True);
	xsmp.save_yourself = False;
    }
}


/*
 * Callback to warn us of imminent death.
 */
/*ARGSUSED*/
    static void
xsmp_die(smc_conn, client_data)
    SmcConn	smc_conn;
    SmPointer	client_data;
{
    xsmp_close();

    /* quit quickly leaving swapfiles for modified buffers behind */
    getout_preserve_modified(0);
}


/*
 * Callback to tell us that save-yourself has completed.
 */
/*ARGSUSED*/
    static void
xsmp_save_complete(smc_conn, client_data)
    SmcConn	smc_conn;
    SmPointer	client_data;
{
    xsmp.save_yourself = False;
}


/*
 * Callback to tell us that an instigated shutdown was cancelled
 * (maybe even by us)
 */
/*ARGSUSED*/
    static void
xsmp_shutdown_cancelled(smc_conn, client_data)
    SmcConn	smc_conn;
    SmPointer	client_data;
{
    if (xsmp.save_yourself)
	SmcSaveYourselfDone(smc_conn, True);
    xsmp.save_yourself = False;
    xsmp.shutdown = False;
}


/*
 * Callback to tell us that a new ICE connection has been established.
 */
/*ARGSUSED*/
    static void
xsmp_ice_connection(iceConn, clientData, opening, watchData)
    IceConn	iceConn;
    IcePointer	clientData;
    Bool	opening;
    IcePointer	*watchData;
{
    /* Intercept creation of ICE connection fd */
    if (opening)
    {
	xsmp_icefd = IceConnectionNumber(iceConn);
	IceRemoveConnectionWatch(xsmp_ice_connection, NULL);
    }
}


/* Handle any ICE processing that's required; return FAIL if SM lost */
    int
xsmp_handle_requests()
{
    Bool rep;

    if (IceProcessMessages(xsmp.iceconn, NULL, &rep)
						 == IceProcessMessagesIOError)
    {
	/* Lost ICE */
	if (p_verbose > 0)
	    verb_msg((char_u *)_("XSMP lost ICE connection"));
	xsmp_close();
	return FAIL;
    }
    else
	return OK;
}

static int dummy;

/* Set up X Session Management Protocol */
    void
xsmp_init(void)
{
    char		errorstring[80];
    char		*clientid;
    SmcCallbacks	smcallbacks;
#if 0
    SmPropValue		smname;
    SmProp		smnameprop;
    SmProp		*smprops[1];
#endif

    if (p_verbose > 0)
	verb_msg((char_u *)_("XSMP opening connection"));

    xsmp.save_yourself = xsmp.shutdown = False;

    /* Set up SM callbacks - must have all, even if they're not used */
    smcallbacks.save_yourself.callback = xsmp_handle_save_yourself;
    smcallbacks.save_yourself.client_data = NULL;
    smcallbacks.die.callback = xsmp_die;
    smcallbacks.die.client_data = NULL;
    smcallbacks.save_complete.callback = xsmp_save_complete;
    smcallbacks.save_complete.client_data = NULL;
    smcallbacks.shutdown_cancelled.callback = xsmp_shutdown_cancelled;
    smcallbacks.shutdown_cancelled.client_data = NULL;

    /* Set up a watch on ICE connection creations.  The "dummy" argument is
     * apparently required for FreeBSD (we get a BUS error when using NULL). */
    if (IceAddConnectionWatch(xsmp_ice_connection, &dummy) == 0)
    {
	if (p_verbose > 0)
	    verb_msg((char_u *)_("XSMP ICE connection watch failed"));
	return;
    }

    /* Create an SM connection */
    xsmp.smcconn = SmcOpenConnection(
	    NULL,
	    NULL,
	    SmProtoMajor,
	    SmProtoMinor,
	    SmcSaveYourselfProcMask | SmcDieProcMask
		     | SmcSaveCompleteProcMask | SmcShutdownCancelledProcMask,
	    &smcallbacks,
	    NULL,
	    &clientid,
	    sizeof(errorstring),
	    errorstring);
    if (xsmp.smcconn == NULL)
    {
	char errorreport[132];

	if (p_verbose > 0)
	{
	    vim_snprintf(errorreport, sizeof(errorreport),
			 _("XSMP SmcOpenConnection failed: %s"), errorstring);
	    verb_msg((char_u *)errorreport);
	}
	return;
    }
    xsmp.iceconn = SmcGetIceConnection(xsmp.smcconn);

#if 0
    /* ID ourselves */
    smname.value = "vim";
    smname.length = 3;
    smnameprop.name = "SmProgram";
    smnameprop.type = "SmARRAY8";
    smnameprop.num_vals = 1;
    smnameprop.vals = &smname;

    smprops[0] = &smnameprop;
    SmcSetProperties(xsmp.smcconn, 1, smprops);
#endif
}


/* Shut down XSMP comms. */
    void
xsmp_close()
{
    if (xsmp_icefd != -1)
    {
	SmcCloseConnection(xsmp.smcconn, 0, NULL);
	xsmp_icefd = -1;
    }
}
#endif /* USE_XSMP */


#ifdef EBCDIC
/* Translate character to its CTRL- value */
char CtrlTable[] =
{
/* 00 - 5E */
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* ^ */ 0x1E,
/* - */ 0x1F,
/* 61 - 6C */
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* _ */ 0x1F,
/* 6E - 80 */
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* a */ 0x01,
/* b */ 0x02,
/* c */ 0x03,
/* d */ 0x37,
/* e */ 0x2D,
/* f */ 0x2E,
/* g */ 0x2F,
/* h */ 0x16,
/* i */ 0x05,
/* 8A - 90 */
	0, 0, 0, 0, 0, 0, 0,
/* j */ 0x15,
/* k */ 0x0B,
/* l */ 0x0C,
/* m */ 0x0D,
/* n */ 0x0E,
/* o */ 0x0F,
/* p */ 0x10,
/* q */ 0x11,
/* r */ 0x12,
/* 9A - A1 */
	0, 0, 0, 0, 0, 0, 0, 0,
/* s */ 0x13,
/* t */ 0x3C,
/* u */ 0x3D,
/* v */ 0x32,
/* w */ 0x26,
/* x */ 0x18,
/* y */ 0x19,
/* z */ 0x3F,
/* AA - AC */
	0, 0, 0,
/* [ */ 0x27,
/* AE - BC */
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* ] */ 0x1D,
/* BE - C0 */ 0, 0, 0,
/* A */ 0x01,
/* B */ 0x02,
/* C */ 0x03,
/* D */ 0x37,
/* E */ 0x2D,
/* F */ 0x2E,
/* G */ 0x2F,
/* H */ 0x16,
/* I */ 0x05,
/* CA - D0 */ 0, 0, 0, 0, 0, 0, 0,
/* J */ 0x15,
/* K */ 0x0B,
/* L */ 0x0C,
/* M */ 0x0D,
/* N */ 0x0E,
/* O */ 0x0F,
/* P */ 0x10,
/* Q */ 0x11,
/* R */ 0x12,
/* DA - DF */ 0, 0, 0, 0, 0, 0,
/* \ */ 0x1C,
/* E1 */ 0,
/* S */ 0x13,
/* T */ 0x3C,
/* U */ 0x3D,
/* V */ 0x32,
/* W */ 0x26,
/* X */ 0x18,
/* Y */ 0x19,
/* Z */ 0x3F,
/* EA - FF*/ 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
};

char MetaCharTable[]=
{/*   0   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F */
      0,  0,  0,  0,'\\', 0,'F',  0,'W','M','N',  0,  0,  0,  0,  0,
      0,  0,  0,  0,']',  0,  0,'G',  0,  0,'R','O',  0,  0,  0,  0,
    '@','A','B','C','D','E',  0,  0,'H','I','J','K','L',  0,  0,  0,
    'P','Q',  0,'S','T','U','V',  0,'X','Y','Z','[',  0,  0,'^',  0
};


/* TODO: Use characters NOT numbers!!! */
char CtrlCharTable[]=
{/*   0   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F */
    124,193,194,195,  0,201,  0,  0,  0,  0,  0,210,211,212,213,214,
    215,216,217,226,  0,209,200,  0,231,232,  0,  0,224,189, 95,109,
      0,  0,  0,  0,  0,  0,230,173,  0,  0,  0,  0,  0,197,198,199,
      0,  0,229,  0,  0,  0,  0,196,  0,  0,  0,  0,227,228,  0,233,
};


#endif
