diff options
Diffstat (limited to 'src/os_unix.c')
-rw-r--r-- | src/os_unix.c | 1664 |
1 files changed, 0 insertions, 1664 deletions
diff --git a/src/os_unix.c b/src/os_unix.c deleted file mode 100644 index 93cd0272a8..0000000000 --- a/src/os_unix.c +++ /dev/null @@ -1,1664 +0,0 @@ -/* - * 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 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? - */ -# define select select_declared_wrong - -#include <string.h> - -#include "vim.h" -#include "os_unix.h" -#include "buffer.h" -#include "charset.h" -#include "eval.h" -#include "ex_cmds.h" -#include "fileio.h" -#include "getchar.h" -#include "main.h" -#include "mbyte.h" -#include "memline.h" -#include "memory.h" -#include "message.h" -#include "misc1.h" -#include "misc2.h" -#include "garray.h" -#include "path.h" -#include "screen.h" -#include "syntax.h" -#include "term.h" -#include "ui.h" -#include "os/os.h" -#include "os/time.h" -#include "os/event.h" -#include "os/input.h" -#include "os/shell.h" -#include "os/signal.h" -#include "os/job.h" - -#if defined(HAVE_SYS_IOCTL_H) -# include <sys/ioctl.h> -#endif - -#ifdef HAVE_STROPTS_H -# include <stropts.h> -#endif - -#if defined(HAVE_TERMIOS_H) -# include <termios.h> -#endif - -/* shared library access */ -#if defined(HAVE_DLFCN_H) && defined(USE_DLOPEN) -# include <dlfcn.h> -#endif - -#ifdef HAVE_SELINUX -# include <selinux/selinux.h> -static int selinux_enabled = -1; -#endif - -static int get_x11_title(int); -static int get_x11_icon(int); - -static char_u *oldtitle = NULL; -static int did_set_title = FALSE; -static char_u *oldicon = NULL; -static int did_set_icon = FALSE; - -static int have_wildcard(int, char_u **); -static int have_dollars(int, char_u **); - -static void save_patterns(int num_pat, char_u **pat, int *num_file, - char_u ***file); - -/* - * Write s[len] to the screen. - */ -void mch_write(char_u *s, int len) -{ - ignored = (int)write(1, (char *)s, len); - if (p_wd) /* Unix is too fast, slow down a bit more */ - os_microdelay(p_wd, false); -} - -/* - * 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() -{ - lc_active = TRUE; -} - -void mch_endjmp() -{ - lc_active = FALSE; -} - -/* - * 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(_REENTRANT) && defined(SIGCONT) - sigcont_received = FALSE; -# endif - kill(0, SIGTSTP); /* send ourselves a STOP signal */ -# if defined(_REENTRANT) && defined(SIGCONT) - /* - * Wait for the SIGCONT signal to be handled. It generally happens - * immediately, but somehow not all the time. Do not call pause() - * because there would be race condition which would hang Vim if - * signal happened in between the test of sigcont_received and the - * call to pause(). If signal is not yet received, call sleep(0) - * to just yield CPU. Signal should then be received. If somehow - * it's still not received, sleep 1, 2, 3 ms. Don't bother waiting - * further if signal is not received after 1+2+3+4 ms (not expected - * to happen). - */ - { - long wait_time; - for (wait_time = 0; !sigcont_received && wait_time <= 3L; wait_time++) - /* Loop is not entered most of the time */ - os_delay(wait_time, FALSE); - } -# endif - - /* - * Set oldtitle to NULL, so the current title is obtained again. - */ - free(oldtitle); - oldtitle = NULL; - settmode(TMODE_RAW); - need_check_timestamps = TRUE; - did_check_timestamps = FALSE; -#endif -} - -void mch_init() -{ - Columns = 80; - Rows = 24; - - out_flush(); - -#ifdef MACOS_CONVERT - mac_conv_init(); -#endif - - event_init(); -} - -static int get_x11_title(int test_only) -{ - return FALSE; -} - -static int get_x11_icon(int test_only) -{ - if (!test_only) { - if (STRNCMP(T_NAME, "builtin_", 8) == 0) - oldicon = vim_strsave(T_NAME + 8); - else - oldicon = vim_strsave(T_NAME); - } - return FALSE; -} - - -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(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 - */ - - /* - * 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 - ) /* 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); - did_set_title = TRUE; - } - - if ((type || *T_CIS != NUL) && icon != NULL) { - if (oldicon == NULL - ) /* 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(); - } - 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(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); -} - - -/* - * Return TRUE if "name" looks like some xterm name. - * Seiichi Sato mentioned that "mlterm" works like xterm. - */ -int vim_is_xterm(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; -} - -/* - * Return TRUE if "name" appears to be that of a terminal - * known to support the xterm-style mouse protocol. - * Relies on term_is_xterm having been set to its correct value. - */ -int use_xterm_like_mouse(char_u *name) -{ - return name != NULL - && (term_is_xterm || STRNICMP(name, "screen", 6) == 0); -} - -/* - * Return non-zero when using an xterm mouse, according to 'ttymouse'. - * Return 1 for "xterm". - * Return 2 for "xterm2". - * Return 3 for "urxvt". - * Return 4 for "sgr". - */ -int use_xterm_mouse() -{ - if (ttym_flags == TTYM_SGR) - return 4; - if (ttym_flags == TTYM_URXVT) - return 3; - if (ttym_flags == TTYM_XTERM2) - return 2; - if (ttym_flags == TTYM_XTERM) - return 1; - return 0; -} - -int vim_is_iris(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(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(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; -} - -#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 ignored and preserved. - */ -void fname_case( -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 - -#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 - - -#if defined(HAVE_SELINUX) || defined(PROTO) -/* - * Copy security info from "from_file" to "to_file". - */ -void mch_copy_sec(char_u *from_file, char_u *to_file) -{ - if (from_file == NULL) - return; - - if (selinux_enabled == -1) - selinux_enabled = is_selinux_enabled(); - - if (selinux_enabled > 0) { - security_context_t from_context = NULL; - security_context_t to_context = NULL; - - if (getfilecon((char *)from_file, &from_context) < 0) { - /* If the filesystem doesn't support extended attributes, - the original had no special security context and the - target cannot have one either. */ - if (errno == EOPNOTSUPP) - return; - - MSG_PUTS(_("\nCould not get security context for ")); - msg_outtrans(from_file); - msg_putchar('\n'); - return; - } - if (getfilecon((char *)to_file, &to_context) < 0) { - MSG_PUTS(_("\nCould not get security context for ")); - msg_outtrans(to_file); - msg_putchar('\n'); - freecon (from_context); - return; - } - if (strcmp(from_context, to_context) != 0) { - if (setfilecon((char *)to_file, from_context) < 0) { - MSG_PUTS(_("\nCould not set security context for ")); - msg_outtrans(to_file); - msg_putchar('\n'); - } - } - freecon(to_context); - freecon(from_context); - } -} -#endif /* HAVE_SELINUX */ - -/* - * 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(char_u *fname) -{ - vim_acl_T ret = NULL; - return ret; -} - -/* - * Set the ACL of file "fname" to "acl" (unless it's NULL). - */ -void mch_set_acl(char_u *fname, vim_acl_T aclent) -{ - if (aclent == NULL) - return; -} - -void mch_free_acl(vim_acl_T aclent) -{ - if (aclent == NULL) - return; -} -#endif - -/* - * Set hidden flag for "name". - */ -void mch_hide(char_u *name) -{ - /* can't hide a file */ -} - -/* - * 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(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; - if (S_ISBLK(st.st_mode)) /* block device isn't writable */ - return NODE_OTHER; - /* Everything else is writable? */ - return NODE_WRITABLE; -} - -void mch_early_init() -{ - time_init(); -} - -#if defined(EXITFREE) || defined(PROTO) -void mch_free_mem() { - free(oldtitle); - free(oldicon); -} - -#endif - -static void exit_scroll(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(int r) -{ - exiting = TRUE; - - event_teardown(); - - { - settmode(TMODE_COOK); - mch_restore_title(3); /* restore xterm title and icon name */ - /* - * 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 */ - -#ifdef MACOS_CONVERT - mac_conv_cleanup(); -#endif - - - -#ifdef EXITFREE - free_all_mem(); -#endif - - exit(r); -} - -void mch_settmode(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); - } -} - -/* - * 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 (ttym_flags == TTYM_URXVT) { - out_str_nf((char_u *) - (on - ? IF_EB("\033[?1015h", ESC_STR "[?1015h") - : IF_EB("\033[?1015l", ESC_STR "[?1015l"))); - ison = on; - } - - if (ttym_flags == TTYM_SGR) { - out_str_nf((char_u *) - (on - ? IF_EB("\033[?1006h", ESC_STR "[?1006h") - : IF_EB("\033[?1006l", ESC_STR "[?1006l"))); - ison = on; - } - - 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; - } 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; - } - -} - -/* - * Set the mouse termcode, depending on the 'term' and 'ttymouse' options. - */ -void check_mouse_termcode() -{ - if (use_xterm_mouse() - && use_xterm_mouse() != 3 - ) { - 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); - - - /* 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() - ) - set_mouse_termcode(KS_NETTERM_MOUSE, - (char_u *)IF_EB("\033}", ESC_STR "}")); - else - del_mouse_termcode(KS_NETTERM_MOUSE); - - /* conflicts with xterm mouse: "\033[" and "\033[M" */ - if (!use_xterm_mouse() - ) - 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); - /* same as the dec mouse */ - if (use_xterm_mouse() == 3 - ) { - set_mouse_termcode(KS_URXVT_MOUSE, (char_u *)(term_is_8bit(T_NAME) - ? IF_EB("\233", CSI_STR) - : IF_EB("\033[", ESC_STR "["))); - - if (*p_mouse != NUL) { - mch_setmouse(FALSE); - setmouse(); - } - } else - del_mouse_termcode(KS_URXVT_MOUSE); - /* same as the dec mouse */ - if (use_xterm_mouse() == 4 - ) { - set_mouse_termcode(KS_SGR_MOUSE, (char_u *)(term_is_8bit(T_NAME) - ? IF_EB("\233<", CSI_STR "<") - : IF_EB("\033[<", ESC_STR "[<"))); - - if (*p_mouse != NUL) { - mch_setmouse(FALSE); - setmouse(); - } - } else - del_mouse_termcode(KS_SGR_MOUSE); -} - -/* - * 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; - - /* - * 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 *)os_getenv("LINES"))) - rows = atoi((char *)p); - if ((p = (char_u *)os_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; - limit_screen_size(); - 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 */ - } -} - -/* - * 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 \"&'$;<>()\\|" - -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; - bool dir; - char_u *extra_shell_arg = NULL; - ShellOpts shellopts = kShellOptExpand | kShellOptSilent; - int j; - char_u *tempname; - char_u *command; - FILE *fd; - char_u *buffer; -#define STYLE_ECHO 0 /* use "echo", the default */ -#define STYLE_GLOB 1 /* use "glob", for csh */ -#define STYLE_VIMGLOB 2 /* use "vimglob", for Posix sh */ -#define STYLE_PRINT 3 /* use "print -N", for zsh */ -#define STYLE_BT 4 /* `cmd` expansion, execute the pattern - * directly */ - int shell_style = STYLE_ECHO; - int check_spaces; - static int did_find_nul = FALSE; - int ampersent = FALSE; - /* vimglob() function to define for Posix shell */ - static char *sh_vimglob_func = - "vimglob() { while [ $# -ge 1 ]; do echo \"$1\"; shift; done }; vimglob >"; - - *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)) { - save_patterns(num_pat, pat, num_file, file); - return OK; - } - -# 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. - * STYLE_BT: NL separated - * If expanding `cmd` execute it directly. - * STYLE_GLOB: NUL separated - * If we use *csh, "glob" will work better than "echo". - * STYLE_PRINT: NL or NUL separated - * If we use *zsh, "print -N" will work better than "glob". - * STYLE_VIMGLOB: NL separated - * If we use *sh*, we define "vimglob()". - * STYLE_ECHO: space separated. - * A shell we don't know, stay safe and use "echo". - */ - 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; - } - if (shell_style == STYLE_ECHO && strstr((char *)path_tail(p_sh), - "sh") != NULL) - shell_style = STYLE_VIMGLOB; - - /* Compute the length of the command. We need 2 extra bytes: for the - * optional '&' and for the NUL. - * Worst case: "unset nonomatch; print -N >" plus two is 29 */ - len = STRLEN(tempname) + 29; - if (shell_style == STYLE_VIMGLOB) - len += STRLEN(sh_vimglob_func); - - for (i = 0; i < num_pat; ++i) { - /* Count the length of the patterns in the same way as they are put in - * "command" below. */ - ++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; - } - } - command = alloc(len); - - /* - * 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 if (shell_style == STYLE_VIMGLOB) - STRCAT(command, sh_vimglob_func); - else - STRCAT(command, "echo >"); - } - - STRCAT(command, tempname); - - if (shell_style != STYLE_BT) - for (i = 0; i < num_pat; ++i) { - /* Put a backslash before special - * characters, except inside ``. */ - 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; - } - - if (flags & EW_SILENT) { - shellopts |= kShellOptHideMess; - } - - if (ampersent) - STRCAT(command, "&"); /* put the '&' 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, - shellopts, - extra_shell_arg - ); - - /* When running in the background, give it some time to create the temp - * file, but don't wait for it to finish. */ - if (ampersent) - os_delay(10L, TRUE); - - free(command); - - if (i != 0) { /* mch_call_shell() failed */ - os_remove((char *)tempname); - free(tempname); - /* - * With interactive completion, the error message is not printed. - */ - if (!(flags & EW_SILENT)) - { - redraw_later_clear(); /* probably messed up screen */ - msg_putchar('\n'); /* clear bottom line quickly */ - cmdline_row = Rows - 1; /* continue on last line */ - 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 */ - } - 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); - i = fread((char *)buffer, 1, len, fd); - fclose(fd); - os_remove((char *)tempname); - if (i != (int)len) { - /* unexpected read error */ - EMSG2(_(e_notread), tempname); - free(tempname); - free(buffer); - return FAIL; - } - free(tempname); - - - - /* 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 || shell_style == STYLE_VIMGLOB) { - 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 */ - buffer[len] = NUL; - if (len && (int)STRLEN(buffer) < (int)len) - 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. - */ - free(buffer); - goto notfound; - } - *num_file = i; - *file = (char_u **)alloc(sizeof(char_u *) * i); - - /* - * 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 - || shell_style == STYLE_VIMGLOB) { - 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) && !os_file_exists((*file)[i])) - continue; - - /* check if this entry should be included */ - dir = (os_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) && !os_can_exe((*file)[i])) - continue; - - p = alloc((unsigned)(STRLEN((*file)[i]) + 1 + dir)); - STRCPY(p, (*file)[i]); - if (dir) - add_pathsep(p); /* add '/' to a directory name */ - (*file)[j++] = p; - } - free(buffer); - *num_file = j; - - if (*num_file == 0) { /* rejected all entries */ - free(*file); - *file = NULL; - goto notfound; - } - - return OK; - -notfound: - if (flags & EW_NOTFOUND) { - save_patterns(num_pat, pat, num_file, file); - return OK; - } - return FAIL; - -} - - -static void 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 = xmalloc((size_t)num_pat * sizeof(char_u *)); - - 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 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)) { - if (*p == '\\' && p[1] != NUL) - ++p; - else if (vim_strchr((char_u *) - "*?[{'" - , *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)) { - if (*p == '\\' && p[1] != NUL) - ++p; - else if (vim_strchr((char_u *) - "*?[{`'$" - , *p) != NULL - || (*p == '~' && p[1] != NUL)) - return TRUE; - } - return FALSE; -} - -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; -} - -#if defined(FEAT_LIBCALL) || defined(PROTO) -typedef char_u * (*STRPROCSTR)(char_u *); -typedef char_u * (*INTPROCSTR)(int); -typedef int (*STRPROCINT)(char_u *); -typedef int (*INTPROCINT)(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) - /* First clear any error, it's not cleared by the dlopen() call. */ - (void)dlerror(); - - 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) { - /* - * 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; -# if defined(USE_DLOPEN) - dlerr = NULL; -# endif - } else - { - 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); - } - - 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 - -# 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 - - - |