blob: 0af6016bb44a2ce3b591bca1be78a94f0bb17bce [file] [log] [blame]
Bram Moolenaaredf3f972016-08-29 22:49:24 +02001/* vi:set ts=8 sts=4 sw=4 noet:
Bram Moolenaar071d4272004-06-13 20:20:40 +00002 *
3 * VIM - Vi IMproved by Bram Moolenaar
4 * OS/2 port by Paul Slootman
5 * VMS merge by Zoltan Arpadffy
6 *
7 * Do ":help uganda" in Vim to read copying and usage conditions.
8 * Do ":help credits" in Vim to see a list of people who contributed.
9 * See README.txt for an overview of the Vim source code.
10 */
11
12/*
13 * os_unix.c -- code for all flavors of Unix (BSD, SYSV, SVR4, POSIX, ...)
14 * Also for OS/2, using the excellent EMX package!!!
Bram Moolenaar041c7102020-05-30 18:14:57 +020015 * Also for Atari MiNT.
Bram Moolenaar071d4272004-06-13 20:20:40 +000016 *
17 * A lot of this file was originally written by Juergen Weigert and later
18 * changed beyond recognition.
19 */
20
Bram Moolenaar071d4272004-06-13 20:20:40 +000021#include "vim.h"
22
Bram Moolenaar325b7a22004-07-05 15:58:32 +000023#ifdef FEAT_MZSCHEME
24# include "if_mzsch.h"
25#endif
26
Bram Moolenaar0f873732019-12-05 20:28:46 +010027#include "os_unixx.h" // unix includes for os_unix.c only
Bram Moolenaar071d4272004-06-13 20:20:40 +000028
29#ifdef USE_XSMP
30# include <X11/SM/SMlib.h>
31#endif
32
Bram Moolenaar588ebeb2008-05-07 17:09:24 +000033#ifdef HAVE_SELINUX
34# include <selinux/selinux.h>
35static int selinux_enabled = -1;
36#endif
37
Bram Moolenaar5bd32f42014-04-02 14:05:38 +020038#ifdef HAVE_SMACK
39# include <attr/xattr.h>
40# include <linux/xattr.h>
41# ifndef SMACK_LABEL_LEN
42# define SMACK_LABEL_LEN 1024
43# endif
44#endif
45
Bram Moolenaara2442432007-04-26 14:26:37 +000046#ifdef __CYGWIN__
Bram Moolenaar4f974752019-02-17 17:44:42 +010047# ifndef MSWIN
Bram Moolenaar0d1498e2008-06-29 12:00:49 +000048# include <cygwin/version.h>
Bram Moolenaar0f873732019-12-05 20:28:46 +010049# include <sys/cygwin.h> // for cygwin_conv_to_posix_path() and/or
50 // for cygwin_conv_path()
Bram Moolenaar693e40c2013-02-26 14:56:42 +010051# ifdef FEAT_CYGWIN_WIN32_CLIPBOARD
52# define WIN32_LEAN_AND_MEAN
53# include <windows.h>
54# include "winclip.pro"
55# endif
Bram Moolenaara2442432007-04-26 14:26:37 +000056# endif
57#endif
58
Bram Moolenaar071d4272004-06-13 20:20:40 +000059#ifdef FEAT_MOUSE_GPM
60# include <gpm.h>
Bram Moolenaar0f873732019-12-05 20:28:46 +010061// <linux/keyboard.h> contains defines conflicting with "keymap.h",
62// I just copied relevant defines here. A cleaner solution would be to put gpm
63// code into separate file and include there linux/keyboard.h
64// #include <linux/keyboard.h>
Bram Moolenaar071d4272004-06-13 20:20:40 +000065# define KG_SHIFT 0
66# define KG_CTRL 2
67# define KG_ALT 3
68# define KG_ALTGR 1
69# define KG_SHIFTL 4
70# define KG_SHIFTR 5
71# define KG_CTRLL 6
72# define KG_CTRLR 7
73# define KG_CAPSSHIFT 8
74
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +010075static void gpm_close(void);
76static int gpm_open(void);
77static int mch_gpm_process(void);
Bram Moolenaar071d4272004-06-13 20:20:40 +000078#endif
79
Bram Moolenaar3577c6f2008-06-24 21:16:56 +000080#ifdef FEAT_SYSMOUSE
81# include <sys/consio.h>
82# include <sys/fbio.h>
83
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +010084static int sysmouse_open(void);
85static void sysmouse_close(void);
Bram Moolenaard99df422016-01-29 23:20:40 +010086static RETSIGTYPE sig_sysmouse SIGPROTOARG;
Bram Moolenaar3577c6f2008-06-24 21:16:56 +000087#endif
88
Bram Moolenaar071d4272004-06-13 20:20:40 +000089/*
90 * end of autoconf section. To be extended...
91 */
92
Bram Moolenaar0f873732019-12-05 20:28:46 +010093// Are the following #ifdefs still required? And why? Is that for X11?
Bram Moolenaar071d4272004-06-13 20:20:40 +000094
95#if defined(ESIX) || defined(M_UNIX) && !defined(SCO)
96# ifdef SIGWINCH
97# undef SIGWINCH
98# endif
99# ifdef TIOCGWINSZ
100# undef TIOCGWINSZ
101# endif
102#endif
103
Bram Moolenaar0f873732019-12-05 20:28:46 +0100104#if defined(SIGWINDOW) && !defined(SIGWINCH) // hpux 9.01 has it
Bram Moolenaar071d4272004-06-13 20:20:40 +0000105# define SIGWINCH SIGWINDOW
106#endif
107
108#ifdef FEAT_X11
109# include <X11/Xlib.h>
110# include <X11/Xutil.h>
111# include <X11/Xatom.h>
112# ifdef FEAT_XCLIPBOARD
113# include <X11/Intrinsic.h>
114# include <X11/Shell.h>
115# include <X11/StringDefs.h>
116static Widget xterm_Shell = (Widget)0;
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100117static void clip_update(void);
118static void xterm_update(void);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000119# endif
120
Bram Moolenaar071d4272004-06-13 20:20:40 +0000121Window x11_window = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000122Display *x11_display = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000123#endif
124
Bram Moolenaar5c3128e2020-05-11 20:54:42 +0200125static int ignore_sigtstp = FALSE;
126
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100127static int get_x11_title(int);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000128
129static char_u *oldtitle = NULL;
Bram Moolenaar8e82c052018-08-21 19:47:48 +0200130static volatile sig_atomic_t oldtitle_outdated = FALSE;
Bram Moolenaardac13472019-09-16 21:06:21 +0200131static int unix_did_set_title = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000132static char_u *oldicon = NULL;
133static int did_set_icon = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000134
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100135static void may_core_dump(void);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000136
Bram Moolenaar205b8862011-09-07 15:04:31 +0200137#ifdef HAVE_UNION_WAIT
138typedef union wait waitstatus;
139#else
140typedef int waitstatus;
141#endif
Bram Moolenaare9c21ae2017-08-03 20:44:48 +0200142static int WaitForChar(long msec, int *interrupted, int ignore_input);
143static int WaitForCharOrMouse(long msec, int *interrupted, int ignore_input);
Bram Moolenaar041c7102020-05-30 18:14:57 +0200144#ifdef VMS
Bram Moolenaarcda77642016-06-04 13:32:35 +0200145int RealWaitForChar(int, long, int *, int *interrupted);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000146#else
Bram Moolenaarcda77642016-06-04 13:32:35 +0200147static int RealWaitForChar(int, long, int *, int *interrupted);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000148#endif
149
150#ifdef FEAT_XCLIPBOARD
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100151static int do_xterm_trace(void);
Bram Moolenaar0f873732019-12-05 20:28:46 +0100152# define XT_TRACE_DELAY 50 // delay for xterm tracing
Bram Moolenaar071d4272004-06-13 20:20:40 +0000153#endif
154
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100155static void handle_resize(void);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000156
157#if defined(SIGWINCH)
Bram Moolenaard99df422016-01-29 23:20:40 +0100158static RETSIGTYPE sig_winch SIGPROTOARG;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000159#endif
dbivolaruab16ad32021-12-29 19:41:47 +0000160#if defined(SIGTSTP)
161static RETSIGTYPE sig_tstp SIGPROTOARG;
162// volatile because it is used in signal handler sig_tstp() and sigcont_handler().
163static volatile sig_atomic_t in_mch_suspend = FALSE;
164#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000165#if defined(SIGINT)
Bram Moolenaard99df422016-01-29 23:20:40 +0100166static RETSIGTYPE catch_sigint SIGPROTOARG;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000167#endif
Bram Moolenaarbe5ee862020-06-10 20:56:58 +0200168#if defined(SIGUSR1)
169static RETSIGTYPE catch_sigusr1 SIGPROTOARG;
170#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000171#if defined(SIGPWR)
Bram Moolenaard99df422016-01-29 23:20:40 +0100172static RETSIGTYPE catch_sigpwr SIGPROTOARG;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000173#endif
Bram Moolenaar651fca82021-11-29 20:39:38 +0000174#if defined(SIGALRM) && defined(FEAT_X11) && !defined(FEAT_GUI_GTK)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000175# define SET_SIG_ALARM
Bram Moolenaard99df422016-01-29 23:20:40 +0100176static RETSIGTYPE sig_alarm SIGPROTOARG;
Bram Moolenaar0f873732019-12-05 20:28:46 +0100177// volatile because it is used in signal handler sig_alarm().
Bram Moolenaar8e82c052018-08-21 19:47:48 +0200178static volatile sig_atomic_t sig_alarm_called;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000179#endif
Bram Moolenaard99df422016-01-29 23:20:40 +0100180static RETSIGTYPE deathtrap SIGPROTOARG;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000181
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100182static void catch_int_signal(void);
183static void set_signals(void);
184static void catch_signals(RETSIGTYPE (*func_deadly)(), RETSIGTYPE (*func_other)());
Bram Moolenaarbb09ceb2016-10-18 16:27:23 +0200185#ifdef HAVE_SIGPROCMASK
186# define SIGSET_DECL(set) sigset_t set;
187# define BLOCK_SIGNALS(set) block_signals(set)
188# define UNBLOCK_SIGNALS(set) unblock_signals(set)
189#else
190# define SIGSET_DECL(set)
191# define BLOCK_SIGNALS(set) do { /**/ } while (0)
192# define UNBLOCK_SIGNALS(set) do { /**/ } while (0)
193#endif
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100194static int have_wildcard(int, char_u **);
195static int have_dollars(int, char_u **);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000196
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100197static int save_patterns(int num_pat, char_u **pat, int *num_file, char_u ***file);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000198
199#ifndef SIG_ERR
Bram Moolenaar8f0b2d42009-05-16 14:41:10 +0000200# define SIG_ERR ((RETSIGTYPE (*)())-1)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000201#endif
202
Bram Moolenaar0f873732019-12-05 20:28:46 +0100203// volatile because it is used in signal handler sig_winch().
Bram Moolenaar8e82c052018-08-21 19:47:48 +0200204static volatile sig_atomic_t do_resize = FALSE;
dbivolaruab16ad32021-12-29 19:41:47 +0000205// volatile because it is used in signal handler sig_tstp().
206static volatile sig_atomic_t got_tstp = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000207static char_u *extra_shell_arg = NULL;
208static int show_shell_mess = TRUE;
Bram Moolenaar0f873732019-12-05 20:28:46 +0100209// volatile because it is used in signal handler deathtrap().
210static volatile sig_atomic_t deadly_signal = 0; // The signal we caught
211// volatile because it is used in signal handler deathtrap().
212static volatile sig_atomic_t in_mch_delay = FALSE; // sleeping in mch_delay()
Bram Moolenaar071d4272004-06-13 20:20:40 +0000213
Bram Moolenaarc4d4ac22016-11-07 22:42:57 +0100214#if defined(FEAT_JOB_CHANNEL) && !defined(USE_SYSTEM)
215static int dont_check_job_ended = 0;
216#endif
217
Bram Moolenaar26e86442020-05-17 14:06:16 +0200218// Current terminal mode from mch_settmode(). Can differ from cur_tmode.
219static tmode_T mch_cur_tmode = TMODE_COOK;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000220
221#ifdef USE_XSMP
222typedef struct
223{
Bram Moolenaar0f873732019-12-05 20:28:46 +0100224 SmcConn smcconn; // The SM connection ID
225 IceConn iceconn; // The ICE connection ID
226 char *clientid; // The client ID for the current smc session
227 Bool save_yourself; // If we're in the middle of a save_yourself
228 Bool shutdown; // If we're in shutdown mode
Bram Moolenaar071d4272004-06-13 20:20:40 +0000229} xsmp_config_T;
230
231static xsmp_config_T xsmp;
232#endif
233
234#ifdef SYS_SIGLIST_DECLARED
235/*
236 * I have seen
237 * extern char *_sys_siglist[NSIG];
238 * on Irix, Linux, NetBSD and Solaris. It contains a nice list of strings
239 * that describe the signals. That is nearly what we want here. But
240 * autoconf does only check for sys_siglist (without the underscore), I
241 * do not want to change everything today.... jw.
Bram Moolenaar3f7d0902016-11-12 21:13:42 +0100242 * This is why AC_DECL_SYS_SIGLIST is commented out in configure.ac.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000243 */
244#endif
245
246static struct signalinfo
247{
Bram Moolenaar0f873732019-12-05 20:28:46 +0100248 int sig; // Signal number, eg. SIGSEGV etc
249 char *name; // Signal name (not char_u!).
250 char deadly; // Catch as a deadly signal?
Bram Moolenaar071d4272004-06-13 20:20:40 +0000251} signal_info[] =
252{
253#ifdef SIGHUP
254 {SIGHUP, "HUP", TRUE},
255#endif
256#ifdef SIGQUIT
257 {SIGQUIT, "QUIT", TRUE},
258#endif
259#ifdef SIGILL
260 {SIGILL, "ILL", TRUE},
261#endif
262#ifdef SIGTRAP
263 {SIGTRAP, "TRAP", TRUE},
264#endif
265#ifdef SIGABRT
266 {SIGABRT, "ABRT", TRUE},
267#endif
268#ifdef SIGEMT
269 {SIGEMT, "EMT", TRUE},
270#endif
271#ifdef SIGFPE
272 {SIGFPE, "FPE", TRUE},
273#endif
274#ifdef SIGBUS
275 {SIGBUS, "BUS", TRUE},
276#endif
Bram Moolenaar75676462013-01-30 14:55:42 +0100277#if defined(SIGSEGV) && !defined(FEAT_MZSCHEME)
Bram Moolenaar0f873732019-12-05 20:28:46 +0100278 // MzScheme uses SEGV in its garbage collector
Bram Moolenaar071d4272004-06-13 20:20:40 +0000279 {SIGSEGV, "SEGV", TRUE},
280#endif
281#ifdef SIGSYS
282 {SIGSYS, "SYS", TRUE},
283#endif
284#ifdef SIGALRM
Bram Moolenaar0f873732019-12-05 20:28:46 +0100285 {SIGALRM, "ALRM", FALSE}, // Perl's alarm() can trigger it
Bram Moolenaar071d4272004-06-13 20:20:40 +0000286#endif
287#ifdef SIGTERM
288 {SIGTERM, "TERM", TRUE},
289#endif
Bram Moolenaarb292a2a2011-02-09 18:47:40 +0100290#if defined(SIGVTALRM) && !defined(FEAT_RUBY)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000291 {SIGVTALRM, "VTALRM", TRUE},
292#endif
Bram Moolenaar02f07e02008-03-12 12:17:28 +0000293#if defined(SIGPROF) && !defined(FEAT_MZSCHEME) && !defined(WE_ARE_PROFILING)
Bram Moolenaar0f873732019-12-05 20:28:46 +0100294 // MzScheme uses SIGPROF for its own needs; On Linux with profiling
295 // this makes Vim exit. WE_ARE_PROFILING is defined in Makefile.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000296 {SIGPROF, "PROF", TRUE},
297#endif
298#ifdef SIGXCPU
299 {SIGXCPU, "XCPU", TRUE},
300#endif
301#ifdef SIGXFSZ
302 {SIGXFSZ, "XFSZ", TRUE},
303#endif
304#ifdef SIGUSR1
Bram Moolenaarbe5ee862020-06-10 20:56:58 +0200305 {SIGUSR1, "USR1", FALSE},
Bram Moolenaar071d4272004-06-13 20:20:40 +0000306#endif
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000307#if defined(SIGUSR2) && !defined(FEAT_SYSMOUSE)
Bram Moolenaar0f873732019-12-05 20:28:46 +0100308 // Used for sysmouse handling
Bram Moolenaar071d4272004-06-13 20:20:40 +0000309 {SIGUSR2, "USR2", TRUE},
310#endif
311#ifdef SIGINT
312 {SIGINT, "INT", FALSE},
313#endif
314#ifdef SIGWINCH
315 {SIGWINCH, "WINCH", FALSE},
316#endif
317#ifdef SIGTSTP
318 {SIGTSTP, "TSTP", FALSE},
319#endif
320#ifdef SIGPIPE
321 {SIGPIPE, "PIPE", FALSE},
322#endif
323 {-1, "Unknown!", FALSE}
324};
325
Bram Moolenaar25724922009-07-14 15:38:41 +0000326 int
Bram Moolenaar05540972016-01-30 20:31:25 +0100327mch_chdir(char *path)
Bram Moolenaar25724922009-07-14 15:38:41 +0000328{
329 if (p_verbose >= 5)
330 {
331 verbose_enter();
Bram Moolenaarf9e3e092019-01-13 23:38:42 +0100332 smsg("chdir(%s)", path);
Bram Moolenaar25724922009-07-14 15:38:41 +0000333 verbose_leave();
334 }
335# ifdef VMS
336 return chdir(vms_fixfilename(path));
337# else
338 return chdir(path);
339# endif
340}
341
Bram Moolenaar0f873732019-12-05 20:28:46 +0100342// Why is NeXT excluded here (and not in os_unixx.h)?
Bram Moolenaar1ecc5e42019-01-26 15:12:55 +0100343#if defined(ECHOE) && defined(ICANON) \
344 && (defined(HAVE_TERMIO_H) || defined(HAVE_TERMIOS_H)) \
345 && !defined(__NeXT__)
Bram Moolenaar4f44b882017-08-13 20:06:18 +0200346# define NEW_TTY_SYSTEM
347#endif
348
Bram Moolenaarfe86f2d2008-11-28 20:29:07 +0000349/*
Bram Moolenaard23a8232018-02-10 18:45:26 +0100350 * Write s[len] to the screen (stdout).
Bram Moolenaarfe86f2d2008-11-28 20:29:07 +0000351 */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000352 void
Bram Moolenaar05540972016-01-30 20:31:25 +0100353mch_write(char_u *s, int len)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000354{
Bram Moolenaar42335f52018-09-13 15:33:43 +0200355 vim_ignored = (int)write(1, (char *)s, len);
Bram Moolenaar0f873732019-12-05 20:28:46 +0100356 if (p_wd) // Unix is too fast, slow down a bit more
Bram Moolenaar8fdd7212016-03-26 19:41:48 +0100357 RealWaitForChar(read_cmd_fd, p_wd, NULL, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000358}
359
360/*
Bram Moolenaare40b9d42019-01-27 16:55:47 +0100361 * Function passed to inchar_loop() to handle window resizing.
362 * If "check_only" is TRUE: Return whether there was a resize.
363 * If "check_only" is FALSE: Deal with the window resized.
364 */
365 static int
366resize_func(int check_only)
367{
368 if (check_only)
369 return do_resize;
370 while (do_resize)
371 handle_resize();
372 return FALSE;
373}
374
375/*
Bram Moolenaarc2a27c32007-12-01 16:19:33 +0000376 * mch_inchar(): low level input function.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000377 * Get a characters from the keyboard.
378 * Return the number of characters that are available.
379 * If wtime == 0 do not wait for characters.
380 * If wtime == n wait a short time for characters.
381 * If wtime == -1 wait forever for characters.
382 */
383 int
Bram Moolenaar05540972016-01-30 20:31:25 +0100384mch_inchar(
385 char_u *buf,
386 int maxlen,
Bram Moolenaar0f873732019-12-05 20:28:46 +0100387 long wtime, // don't use "time", MIPS cannot handle it
Bram Moolenaar05540972016-01-30 20:31:25 +0100388 int tb_change_cnt)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000389{
Bram Moolenaare40b9d42019-01-27 16:55:47 +0100390 return inchar_loop(buf, maxlen, wtime, tb_change_cnt,
391 WaitForChar, resize_func);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000392}
393
394 static void
Bram Moolenaar05540972016-01-30 20:31:25 +0100395handle_resize(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000396{
397 do_resize = FALSE;
398 shell_resized();
399}
400
401/*
Bram Moolenaar40b1b542016-04-20 20:18:23 +0200402 * Return non-zero if a character is available.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000403 */
404 int
Bram Moolenaar05540972016-01-30 20:31:25 +0100405mch_char_avail(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000406{
Bram Moolenaare9c21ae2017-08-03 20:44:48 +0200407 return WaitForChar(0L, NULL, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000408}
409
Bram Moolenaare9c21ae2017-08-03 20:44:48 +0200410#if defined(FEAT_TERMINAL) || defined(PROTO)
411/*
412 * Check for any pending input or messages.
413 */
414 int
415mch_check_messages(void)
416{
417 return WaitForChar(0L, NULL, TRUE);
418}
419#endif
420
Bram Moolenaar071d4272004-06-13 20:20:40 +0000421#if defined(HAVE_TOTAL_MEM) || defined(PROTO)
422# ifdef HAVE_SYS_RESOURCE_H
Bram Moolenaar8f0b2d42009-05-16 14:41:10 +0000423# include <sys/resource.h>
Bram Moolenaar071d4272004-06-13 20:20:40 +0000424# endif
425# if defined(HAVE_SYS_SYSCTL_H) && defined(HAVE_SYSCTL)
426# include <sys/sysctl.h>
427# endif
428# if defined(HAVE_SYS_SYSINFO_H) && defined(HAVE_SYSINFO)
429# include <sys/sysinfo.h>
430# endif
Bram Moolenaar362dc332018-03-05 21:59:37 +0100431# ifdef MACOS_X
Bram Moolenaar988615f2018-02-27 17:58:20 +0100432# include <mach/mach_host.h>
433# include <mach/mach_port.h>
Bram Moolenaar988615f2018-02-27 17:58:20 +0100434# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000435
436/*
Bram Moolenaar914572a2007-05-01 11:37:47 +0000437 * Return total amount of memory available in Kbyte.
438 * Doesn't change when memory has been allocated.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000439 */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000440 long_u
Bram Moolenaar05540972016-01-30 20:31:25 +0100441mch_total_mem(int special UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000442{
Bram Moolenaar071d4272004-06-13 20:20:40 +0000443 long_u mem = 0;
Bram Moolenaar0f873732019-12-05 20:28:46 +0100444 long_u shiftright = 10; // how much to shift "mem" right for Kbyte
Bram Moolenaar071d4272004-06-13 20:20:40 +0000445
Bram Moolenaar362dc332018-03-05 21:59:37 +0100446# ifdef MACOS_X
Bram Moolenaar988615f2018-02-27 17:58:20 +0100447 {
Bram Moolenaar0f873732019-12-05 20:28:46 +0100448 // Mac (Darwin) way of getting the amount of RAM available
Bram Moolenaar988615f2018-02-27 17:58:20 +0100449 mach_port_t host = mach_host_self();
450 kern_return_t kret;
451# ifdef HOST_VM_INFO64
452 struct vm_statistics64 vm_stat;
453 natural_t count = HOST_VM_INFO64_COUNT;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000454
Bram Moolenaar988615f2018-02-27 17:58:20 +0100455 kret = host_statistics64(host, HOST_VM_INFO64,
456 (host_info64_t)&vm_stat, &count);
457# else
458 struct vm_statistics vm_stat;
459 natural_t count = HOST_VM_INFO_COUNT;
460
461 kret = host_statistics(host, HOST_VM_INFO,
462 (host_info_t)&vm_stat, &count);
463# endif
464 if (kret == KERN_SUCCESS)
Bram Moolenaar0f873732019-12-05 20:28:46 +0100465 // get the amount of user memory by summing each usage
Bram Moolenaar988615f2018-02-27 17:58:20 +0100466 mem = (long_u)(vm_stat.free_count + vm_stat.active_count
467 + vm_stat.inactive_count
468# ifdef MAC_OS_X_VERSION_10_9
469 + vm_stat.compressor_page_count
470# endif
Bram Moolenaar62b7f6a2018-03-22 21:44:07 +0100471 ) * sysconf(_SC_PAGESIZE);
Bram Moolenaar988615f2018-02-27 17:58:20 +0100472 mach_port_deallocate(mach_task_self(), host);
473 }
474# endif
475
476# ifdef HAVE_SYSCTL
477 if (mem == 0)
478 {
Bram Moolenaar0f873732019-12-05 20:28:46 +0100479 // BSD way of getting the amount of RAM available.
Bram Moolenaar988615f2018-02-27 17:58:20 +0100480 int mib[2];
481 size_t len = sizeof(long_u);
482# ifdef HW_USERMEM64
483 long_u physmem;
484# else
Bram Moolenaar0f873732019-12-05 20:28:46 +0100485 // sysctl() may return 32 bit or 64 bit, accept both
Bram Moolenaar988615f2018-02-27 17:58:20 +0100486 union {
487 int_u u32;
488 long_u u64;
489 } physmem;
490# endif
491
492 mib[0] = CTL_HW;
493# ifdef HW_USERMEM64
494 mib[1] = HW_USERMEM64;
495# else
496 mib[1] = HW_USERMEM;
497# endif
498 if (sysctl(mib, 2, &physmem, &len, NULL, 0) == 0)
499 {
500# ifdef HW_USERMEM64
501 mem = (long_u)physmem;
502# else
503 if (len == sizeof(physmem.u64))
504 mem = (long_u)physmem.u64;
505 else
506 mem = (long_u)physmem.u32;
507# endif
508 }
509 }
Bram Moolenaara06ecab2016-07-16 14:47:36 +0200510# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000511
Bram Moolenaara06ecab2016-07-16 14:47:36 +0200512# if defined(HAVE_SYS_SYSINFO_H) && defined(HAVE_SYSINFO)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000513 if (mem == 0)
514 {
515 struct sysinfo sinfo;
516
Bram Moolenaar0f873732019-12-05 20:28:46 +0100517 // Linux way of getting amount of RAM available
Bram Moolenaar071d4272004-06-13 20:20:40 +0000518 if (sysinfo(&sinfo) == 0)
Bram Moolenaar914572a2007-05-01 11:37:47 +0000519 {
Bram Moolenaara06ecab2016-07-16 14:47:36 +0200520# ifdef HAVE_SYSINFO_MEM_UNIT
Bram Moolenaar0f873732019-12-05 20:28:46 +0100521 // avoid overflow as much as possible
Bram Moolenaar914572a2007-05-01 11:37:47 +0000522 while (shiftright > 0 && (sinfo.mem_unit & 1) == 0)
523 {
524 sinfo.mem_unit = sinfo.mem_unit >> 1;
525 --shiftright;
526 }
527 mem = sinfo.totalram * sinfo.mem_unit;
Bram Moolenaara06ecab2016-07-16 14:47:36 +0200528# else
Bram Moolenaar071d4272004-06-13 20:20:40 +0000529 mem = sinfo.totalram;
Bram Moolenaara06ecab2016-07-16 14:47:36 +0200530# endif
Bram Moolenaar914572a2007-05-01 11:37:47 +0000531 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000532 }
Bram Moolenaara06ecab2016-07-16 14:47:36 +0200533# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000534
Bram Moolenaara06ecab2016-07-16 14:47:36 +0200535# ifdef HAVE_SYSCONF
Bram Moolenaar071d4272004-06-13 20:20:40 +0000536 if (mem == 0)
537 {
538 long pagesize, pagecount;
539
Bram Moolenaar0f873732019-12-05 20:28:46 +0100540 // Solaris way of getting amount of RAM available
Bram Moolenaar071d4272004-06-13 20:20:40 +0000541 pagesize = sysconf(_SC_PAGESIZE);
542 pagecount = sysconf(_SC_PHYS_PAGES);
543 if (pagesize > 0 && pagecount > 0)
Bram Moolenaar914572a2007-05-01 11:37:47 +0000544 {
Bram Moolenaar0f873732019-12-05 20:28:46 +0100545 // avoid overflow as much as possible
Bram Moolenaar914572a2007-05-01 11:37:47 +0000546 while (shiftright > 0 && (pagesize & 1) == 0)
547 {
Bram Moolenaar3d27a452007-05-10 17:44:18 +0000548 pagesize = (long_u)pagesize >> 1;
Bram Moolenaar914572a2007-05-01 11:37:47 +0000549 --shiftright;
550 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000551 mem = (long_u)pagesize * pagecount;
Bram Moolenaar914572a2007-05-01 11:37:47 +0000552 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000553 }
Bram Moolenaara06ecab2016-07-16 14:47:36 +0200554# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000555
Bram Moolenaar0f873732019-12-05 20:28:46 +0100556 // Return the minimum of the physical memory and the user limit, because
557 // using more than the user limit may cause Vim to be terminated.
Bram Moolenaara06ecab2016-07-16 14:47:36 +0200558# if defined(HAVE_SYS_RESOURCE_H) && defined(HAVE_GETRLIMIT)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000559 {
560 struct rlimit rlp;
561
562 if (getrlimit(RLIMIT_DATA, &rlp) == 0
563 && rlp.rlim_cur < ((rlim_t)1 << (sizeof(long_u) * 8 - 1))
Bram Moolenaara06ecab2016-07-16 14:47:36 +0200564# ifdef RLIM_INFINITY
Bram Moolenaar071d4272004-06-13 20:20:40 +0000565 && rlp.rlim_cur != RLIM_INFINITY
Bram Moolenaara06ecab2016-07-16 14:47:36 +0200566# endif
Bram Moolenaar914572a2007-05-01 11:37:47 +0000567 && ((long_u)rlp.rlim_cur >> 10) < (mem >> shiftright)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000568 )
Bram Moolenaar914572a2007-05-01 11:37:47 +0000569 {
570 mem = (long_u)rlp.rlim_cur;
571 shiftright = 10;
572 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000573 }
Bram Moolenaara06ecab2016-07-16 14:47:36 +0200574# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000575
576 if (mem > 0)
Bram Moolenaar914572a2007-05-01 11:37:47 +0000577 return mem >> shiftright;
578 return (long_u)0x1fffff;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000579}
580#endif
581
Bram Moolenaar0981c872020-08-23 14:28:37 +0200582/*
583 * "flags": MCH_DELAY_IGNOREINPUT - don't read input
584 * MCH_DELAY_SETTMODE - use settmode() even for short delays
585 */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000586 void
Bram Moolenaar0981c872020-08-23 14:28:37 +0200587mch_delay(long msec, int flags)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000588{
Bram Moolenaar26e86442020-05-17 14:06:16 +0200589 tmode_T old_tmode;
Bram Moolenaar80361a52020-10-05 21:39:25 +0200590 int call_settmode;
Bram Moolenaar325b7a22004-07-05 15:58:32 +0000591#ifdef FEAT_MZSCHEME
Bram Moolenaar0f873732019-12-05 20:28:46 +0100592 long total = msec; // remember original value
Bram Moolenaar325b7a22004-07-05 15:58:32 +0000593#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000594
Bram Moolenaar0981c872020-08-23 14:28:37 +0200595 if (flags & MCH_DELAY_IGNOREINPUT)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000596 {
Bram Moolenaar0f873732019-12-05 20:28:46 +0100597 // Go to cooked mode without echo, to allow SIGINT interrupting us
598 // here. But we don't want QUIT to kill us (CTRL-\ used in a
599 // shell may produce SIGQUIT).
Bram Moolenaar26e86442020-05-17 14:06:16 +0200600 // Only do this if sleeping for more than half a second.
Bram Moolenaarae0f2ca2008-02-10 21:25:55 +0000601 in_mch_delay = TRUE;
Bram Moolenaar80361a52020-10-05 21:39:25 +0200602 call_settmode = mch_cur_tmode == TMODE_RAW
603 && (msec > 500 || (flags & MCH_DELAY_SETTMODE));
604 if (call_settmode)
605 {
606 old_tmode = mch_cur_tmode;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000607 settmode(TMODE_SLEEP);
Bram Moolenaar80361a52020-10-05 21:39:25 +0200608 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000609
610 /*
611 * Everybody sleeps in a different way...
612 * Prefer nanosleep(), some versions of usleep() can only sleep up to
613 * one second.
614 */
Bram Moolenaar325b7a22004-07-05 15:58:32 +0000615#ifdef FEAT_MZSCHEME
616 do
617 {
Bram Moolenaar0f873732019-12-05 20:28:46 +0100618 // if total is large enough, wait by portions in p_mzq
Bram Moolenaar325b7a22004-07-05 15:58:32 +0000619 if (total > p_mzq)
620 msec = p_mzq;
621 else
622 msec = total;
623 total -= msec;
624#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000625#ifdef HAVE_NANOSLEEP
626 {
627 struct timespec ts;
628
629 ts.tv_sec = msec / 1000;
630 ts.tv_nsec = (msec % 1000) * 1000000;
631 (void)nanosleep(&ts, NULL);
632 }
633#else
634# ifdef HAVE_USLEEP
635 while (msec >= 1000)
636 {
637 usleep((unsigned int)(999 * 1000));
638 msec -= 999;
639 }
640 usleep((unsigned int)(msec * 1000));
641# else
642# ifndef HAVE_SELECT
643 poll(NULL, 0, (int)msec);
644# else
Bram Moolenaar071d4272004-06-13 20:20:40 +0000645 {
646 struct timeval tv;
647
648 tv.tv_sec = msec / 1000;
649 tv.tv_usec = (msec % 1000) * 1000;
Bram Moolenaar0981c872020-08-23 14:28:37 +0200650 // NOTE: Solaris 2.6 has a bug that makes select() hang here. Get
651 // a patch from Sun to fix this. Reported by Gunnar Pedersen.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000652 select(0, NULL, NULL, NULL, &tv);
653 }
Bram Moolenaar0f873732019-12-05 20:28:46 +0100654# endif // HAVE_SELECT
655# endif // HAVE_NANOSLEEP
656#endif // HAVE_USLEEP
Bram Moolenaar325b7a22004-07-05 15:58:32 +0000657#ifdef FEAT_MZSCHEME
658 }
659 while (total > 0);
660#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000661
Bram Moolenaar80361a52020-10-05 21:39:25 +0200662 if (call_settmode)
Bram Moolenaar26e86442020-05-17 14:06:16 +0200663 settmode(old_tmode);
Bram Moolenaarae0f2ca2008-02-10 21:25:55 +0000664 in_mch_delay = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000665 }
666 else
Bram Moolenaare9c21ae2017-08-03 20:44:48 +0200667 WaitForChar(msec, NULL, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000668}
669
Bram Moolenaarbc7aa852005-03-06 23:38:09 +0000670#if defined(HAVE_STACK_LIMIT) \
Bram Moolenaar071d4272004-06-13 20:20:40 +0000671 || (!defined(HAVE_SIGALTSTACK) && defined(HAVE_SIGSTACK))
672# define HAVE_CHECK_STACK_GROWTH
673/*
674 * Support for checking for an almost-out-of-stack-space situation.
675 */
676
677/*
678 * Return a pointer to an item on the stack. Used to find out if the stack
679 * grows up or down.
680 */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000681static int stack_grows_downwards;
682
683/*
684 * Find out if the stack grows upwards or downwards.
685 * "p" points to a variable on the stack of the caller.
686 */
687 static void
Bram Moolenaar05540972016-01-30 20:31:25 +0100688check_stack_growth(char *p)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000689{
690 int i;
691
692 stack_grows_downwards = (p > (char *)&i);
693}
694#endif
695
Bram Moolenaarbc7aa852005-03-06 23:38:09 +0000696#if defined(HAVE_STACK_LIMIT) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000697static char *stack_limit = NULL;
698
699#if defined(_THREAD_SAFE) && defined(HAVE_PTHREAD_NP_H)
700# include <pthread.h>
701# include <pthread_np.h>
702#endif
703
704/*
705 * Find out until how var the stack can grow without getting into trouble.
706 * Called when starting up and when switching to the signal stack in
707 * deathtrap().
708 */
709 static void
Bram Moolenaar05540972016-01-30 20:31:25 +0100710get_stack_limit(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000711{
712 struct rlimit rlp;
713 int i;
714 long lim;
715
Bram Moolenaar0f873732019-12-05 20:28:46 +0100716 // Set the stack limit to 15/16 of the allowable size. Skip this when the
717 // limit doesn't fit in a long (rlim_cur might be "long long").
Bram Moolenaar071d4272004-06-13 20:20:40 +0000718 if (getrlimit(RLIMIT_STACK, &rlp) == 0
719 && rlp.rlim_cur < ((rlim_t)1 << (sizeof(long_u) * 8 - 1))
720# ifdef RLIM_INFINITY
721 && rlp.rlim_cur != RLIM_INFINITY
722# endif
723 )
724 {
725 lim = (long)rlp.rlim_cur;
726#if defined(_THREAD_SAFE) && defined(HAVE_PTHREAD_NP_H)
727 {
728 pthread_attr_t attr;
729 size_t size;
730
Bram Moolenaar0f873732019-12-05 20:28:46 +0100731 // On FreeBSD the initial thread always has a fixed stack size, no
732 // matter what the limits are set to. Normally it's 1 Mbyte.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000733 pthread_attr_init(&attr);
734 if (pthread_attr_get_np(pthread_self(), &attr) == 0)
735 {
736 pthread_attr_getstacksize(&attr, &size);
737 if (lim > (long)size)
738 lim = (long)size;
739 }
740 pthread_attr_destroy(&attr);
741 }
742#endif
743 if (stack_grows_downwards)
744 {
745 stack_limit = (char *)((long)&i - (lim / 16L * 15L));
746 if (stack_limit >= (char *)&i)
Bram Moolenaar0f873732019-12-05 20:28:46 +0100747 // overflow, set to 1/16 of current stack position
Bram Moolenaar071d4272004-06-13 20:20:40 +0000748 stack_limit = (char *)((long)&i / 16L);
749 }
750 else
751 {
752 stack_limit = (char *)((long)&i + (lim / 16L * 15L));
753 if (stack_limit <= (char *)&i)
Bram Moolenaar0f873732019-12-05 20:28:46 +0100754 stack_limit = NULL; // overflow
Bram Moolenaar071d4272004-06-13 20:20:40 +0000755 }
756 }
757}
758
759/*
760 * Return FAIL when running out of stack space.
761 * "p" must point to any variable local to the caller that's on the stack.
762 */
763 int
Bram Moolenaar05540972016-01-30 20:31:25 +0100764mch_stackcheck(char *p)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000765{
766 if (stack_limit != NULL)
767 {
768 if (stack_grows_downwards)
769 {
770 if (p < stack_limit)
771 return FAIL;
772 }
773 else if (p > stack_limit)
774 return FAIL;
775 }
776 return OK;
777}
778#endif
779
780#if defined(HAVE_SIGALTSTACK) || defined(HAVE_SIGSTACK)
781/*
782 * Support for using the signal stack.
783 * This helps when we run out of stack space, which causes a SIGSEGV. The
784 * signal handler then must run on another stack, since the normal stack is
785 * completely full.
786 */
787
Bram Moolenaar071d4272004-06-13 20:20:40 +0000788# ifdef HAVE_SIGALTSTACK
Bram Moolenaar0f873732019-12-05 20:28:46 +0100789static stack_t sigstk; // for sigaltstack()
Bram Moolenaar071d4272004-06-13 20:20:40 +0000790# else
Bram Moolenaar0f873732019-12-05 20:28:46 +0100791static struct sigstack sigstk; // for sigstack()
Bram Moolenaar071d4272004-06-13 20:20:40 +0000792# endif
793
Zdenek Dohnalba9c23e2021-08-11 14:20:05 +0200794/*
795 * Get a size of signal stack.
796 * Preference (if available): sysconf > SIGSTKSZ > guessed size
797 */
798static long int get_signal_stack_size()
799{
800# ifdef HAVE_SYSCONF_SIGSTKSZ
801 long int size = -1;
802
803 // return size only if sysconf doesn't return an error
804 if ((size = sysconf(_SC_SIGSTKSZ)) > -1)
805 return size;
806# endif
807
808# ifdef SIGSTKSZ
809 // if sysconf() isn't available or gives error, return SIGSTKSZ
810 // if defined
811 return SIGSTKSZ;
812# endif
813
814 // otherwise guess the size
815 return 8000;
816}
817
Bram Moolenaar071d4272004-06-13 20:20:40 +0000818static char *signal_stack;
819
820 static void
Bram Moolenaar05540972016-01-30 20:31:25 +0100821init_signal_stack(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000822{
823 if (signal_stack != NULL)
824 {
825# ifdef HAVE_SIGALTSTACK
Bram Moolenaar071d4272004-06-13 20:20:40 +0000826# ifdef HAVE_SS_BASE
827 sigstk.ss_base = signal_stack;
828# else
829 sigstk.ss_sp = signal_stack;
830# endif
Zdenek Dohnalba9c23e2021-08-11 14:20:05 +0200831 sigstk.ss_size = get_signal_stack_size();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000832 sigstk.ss_flags = 0;
833 (void)sigaltstack(&sigstk, NULL);
834# else
835 sigstk.ss_sp = signal_stack;
836 if (stack_grows_downwards)
Zdenek Dohnalba9c23e2021-08-11 14:20:05 +0200837 sigstk.ss_sp += get_signal_stack_size() - 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000838 sigstk.ss_onstack = 0;
839 (void)sigstack(&sigstk, NULL);
840# endif
841 }
842}
843#endif
844
845/*
Bram Moolenaar76243bd2009-03-02 01:47:02 +0000846 * We need correct prototypes for a signal function, otherwise mean compilers
Bram Moolenaar071d4272004-06-13 20:20:40 +0000847 * will barf when the second argument to signal() is ``wrong''.
848 * Let me try it with a few tricky defines from my own osdef.h (jw).
849 */
850#if defined(SIGWINCH)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000851 static RETSIGTYPE
852sig_winch SIGDEFARG(sigarg)
853{
Bram Moolenaar0f873732019-12-05 20:28:46 +0100854 // this is not required on all systems, but it doesn't hurt anybody
Bram Moolenaar071d4272004-06-13 20:20:40 +0000855 signal(SIGWINCH, (RETSIGTYPE (*)())sig_winch);
856 do_resize = TRUE;
857 SIGRETURN;
858}
859#endif
860
dbivolaruab16ad32021-12-29 19:41:47 +0000861#if defined(SIGTSTP)
862 static RETSIGTYPE
863sig_tstp SIGDEFARG(sigarg)
864{
865 // Second time we get called we actually need to suspend
866 if (in_mch_suspend)
867 {
868 signal(SIGTSTP, ignore_sigtstp ? SIG_IGN : SIG_DFL);
869 raise(sigarg);
870 }
dbivolaru79a6e252022-01-23 16:41:14 +0000871 else
872 got_tstp = TRUE;
dbivolaruab16ad32021-12-29 19:41:47 +0000873
874 // this is not required on all systems, but it doesn't hurt anybody
875 signal(SIGTSTP, (RETSIGTYPE (*)())sig_tstp);
dbivolaruab16ad32021-12-29 19:41:47 +0000876 SIGRETURN;
877}
878#endif
879
Bram Moolenaar071d4272004-06-13 20:20:40 +0000880#if defined(SIGINT)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000881 static RETSIGTYPE
882catch_sigint SIGDEFARG(sigarg)
883{
Bram Moolenaar0f873732019-12-05 20:28:46 +0100884 // this is not required on all systems, but it doesn't hurt anybody
Bram Moolenaar071d4272004-06-13 20:20:40 +0000885 signal(SIGINT, (RETSIGTYPE (*)())catch_sigint);
886 got_int = TRUE;
887 SIGRETURN;
888}
889#endif
890
Bram Moolenaarbe5ee862020-06-10 20:56:58 +0200891#if defined(SIGUSR1)
892 static RETSIGTYPE
893catch_sigusr1 SIGDEFARG(sigarg)
894{
895 // this is not required on all systems, but it doesn't hurt anybody
896 signal(SIGUSR1, (RETSIGTYPE (*)())catch_sigusr1);
897 got_sigusr1 = TRUE;
898 SIGRETURN;
899}
900#endif
901
Bram Moolenaar071d4272004-06-13 20:20:40 +0000902#if defined(SIGPWR)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000903 static RETSIGTYPE
904catch_sigpwr SIGDEFARG(sigarg)
905{
Bram Moolenaar0f873732019-12-05 20:28:46 +0100906 // this is not required on all systems, but it doesn't hurt anybody
Bram Moolenaard8b0cf12004-12-12 11:33:30 +0000907 signal(SIGPWR, (RETSIGTYPE (*)())catch_sigpwr);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000908 /*
909 * I'm not sure we get the SIGPWR signal when the system is really going
910 * down or when the batteries are almost empty. Just preserve the swap
911 * files and don't exit, that can't do any harm.
912 */
913 ml_sync_all(FALSE, FALSE);
914 SIGRETURN;
915}
916#endif
917
918#ifdef SET_SIG_ALARM
919/*
920 * signal function for alarm().
921 */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000922 static RETSIGTYPE
923sig_alarm SIGDEFARG(sigarg)
924{
Bram Moolenaar0f873732019-12-05 20:28:46 +0100925 // doesn't do anything, just to break a system call
Bram Moolenaar071d4272004-06-13 20:20:40 +0000926 sig_alarm_called = TRUE;
927 SIGRETURN;
928}
929#endif
930
Bram Moolenaaredce7422019-01-20 18:39:30 +0100931#if (defined(HAVE_SETJMP_H) \
932 && ((defined(FEAT_X11) && defined(FEAT_XCLIPBOARD)) \
933 || defined(FEAT_LIBCALL))) \
934 || defined(PROTO)
Bram Moolenaarb2148f52019-01-20 23:43:57 +0100935# define USING_SETJMP 1
Bram Moolenaaredce7422019-01-20 18:39:30 +0100936
Bram Moolenaarb7a7e032018-12-28 17:01:59 +0100937// argument to SETJMP()
938static JMP_BUF lc_jump_env;
939
940# ifdef SIGHASARG
Bram Moolenaar32aa1022019-11-02 22:54:41 +0100941// Caught signal number, 0 when no signal was caught; used for mch_libcall().
Bram Moolenaarb7a7e032018-12-28 17:01:59 +0100942// Volatile because it is used in signal handlers.
943static volatile sig_atomic_t lc_signal;
944# endif
945
946// TRUE when lc_jump_env is valid.
947// Volatile because it is used in signal handler deathtrap().
948static volatile sig_atomic_t lc_active INIT(= FALSE);
949
Bram Moolenaar071d4272004-06-13 20:20:40 +0000950/*
951 * A simplistic version of setjmp() that only allows one level of using.
Bram Moolenaarb7a7e032018-12-28 17:01:59 +0100952 * Used to protect areas where we could crash.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000953 * Don't call twice before calling mch_endjmp()!.
Bram Moolenaarb7a7e032018-12-28 17:01:59 +0100954 *
Bram Moolenaar071d4272004-06-13 20:20:40 +0000955 * Usage:
956 * mch_startjmp();
957 * if (SETJMP(lc_jump_env) != 0)
958 * {
959 * mch_didjmp();
Bram Moolenaarf9e3e092019-01-13 23:38:42 +0100960 * emsg("crash!");
Bram Moolenaar071d4272004-06-13 20:20:40 +0000961 * }
962 * else
963 * {
964 * do_the_work;
965 * mch_endjmp();
966 * }
967 * Note: Can't move SETJMP() here, because a function calling setjmp() must
968 * not return before the saved environment is used.
969 * Returns OK for normal return, FAIL when the protected code caused a
970 * problem and LONGJMP() was used.
971 */
Bram Moolenaar113e1072019-01-20 15:30:40 +0100972 static void
Bram Moolenaar05540972016-01-30 20:31:25 +0100973mch_startjmp(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000974{
Bram Moolenaarb2148f52019-01-20 23:43:57 +0100975# ifdef SIGHASARG
Bram Moolenaar071d4272004-06-13 20:20:40 +0000976 lc_signal = 0;
Bram Moolenaarb2148f52019-01-20 23:43:57 +0100977# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000978 lc_active = TRUE;
979}
980
Bram Moolenaar113e1072019-01-20 15:30:40 +0100981 static void
Bram Moolenaar05540972016-01-30 20:31:25 +0100982mch_endjmp(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000983{
984 lc_active = FALSE;
985}
986
Bram Moolenaar113e1072019-01-20 15:30:40 +0100987 static void
Bram Moolenaar05540972016-01-30 20:31:25 +0100988mch_didjmp(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000989{
990# if defined(HAVE_SIGALTSTACK) || defined(HAVE_SIGSTACK)
Bram Moolenaarb7a7e032018-12-28 17:01:59 +0100991 // On FreeBSD the signal stack has to be reset after using siglongjmp(),
992 // otherwise catching the signal only works once.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000993 init_signal_stack();
994# endif
995}
996#endif
997
998/*
999 * This function handles deadly signals.
Bram Moolenaarbec9c202013-09-05 21:41:39 +02001000 * It tries to preserve any swap files and exit properly.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001001 * (partly from Elvis).
Bram Moolenaarbec9c202013-09-05 21:41:39 +02001002 * NOTE: Avoid unsafe functions, such as allocating memory, they can result in
1003 * a deadlock.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001004 */
1005 static RETSIGTYPE
1006deathtrap SIGDEFARG(sigarg)
1007{
Bram Moolenaar0f873732019-12-05 20:28:46 +01001008 static int entered = 0; // count the number of times we got here.
1009 // Note: when memory has been corrupted
1010 // this may get an arbitrary value!
Bram Moolenaar071d4272004-06-13 20:20:40 +00001011#ifdef SIGHASARG
1012 int i;
1013#endif
1014
Bram Moolenaarb2148f52019-01-20 23:43:57 +01001015#if defined(USING_SETJMP)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001016 /*
1017 * Catch a crash in protected code.
1018 * Restores the environment saved in lc_jump_env, which looks like
1019 * SETJMP() returns 1.
1020 */
1021 if (lc_active)
1022 {
1023# if defined(SIGHASARG)
1024 lc_signal = sigarg;
1025# endif
Bram Moolenaar0f873732019-12-05 20:28:46 +01001026 lc_active = FALSE; // don't jump again
Bram Moolenaar071d4272004-06-13 20:20:40 +00001027 LONGJMP(lc_jump_env, 1);
Bram Moolenaar0f873732019-12-05 20:28:46 +01001028 // NOTREACHED
Bram Moolenaar071d4272004-06-13 20:20:40 +00001029 }
1030#endif
1031
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00001032#ifdef SIGHASARG
Bram Moolenaarae0f2ca2008-02-10 21:25:55 +00001033# ifdef SIGQUIT
Bram Moolenaar0f873732019-12-05 20:28:46 +01001034 // While in mch_delay() we go to cooked mode to allow a CTRL-C to
1035 // interrupt us. But in cooked mode we may also get SIGQUIT, e.g., when
1036 // pressing CTRL-\, but we don't want Vim to exit then.
Bram Moolenaarae0f2ca2008-02-10 21:25:55 +00001037 if (in_mch_delay && sigarg == SIGQUIT)
1038 SIGRETURN;
1039# endif
1040
Bram Moolenaar0f873732019-12-05 20:28:46 +01001041 // When SIGHUP, SIGQUIT, etc. are blocked: postpone the effect and return
1042 // here. This avoids that a non-reentrant function is interrupted, e.g.,
1043 // free(). Calling free() again may then cause a crash.
Bram Moolenaard8b0cf12004-12-12 11:33:30 +00001044 if (entered == 0
1045 && (0
1046# ifdef SIGHUP
1047 || sigarg == SIGHUP
1048# endif
1049# ifdef SIGQUIT
1050 || sigarg == SIGQUIT
1051# endif
1052# ifdef SIGTERM
1053 || sigarg == SIGTERM
1054# endif
1055# ifdef SIGPWR
1056 || sigarg == SIGPWR
1057# endif
1058# ifdef SIGUSR1
1059 || sigarg == SIGUSR1
1060# endif
1061# ifdef SIGUSR2
1062 || sigarg == SIGUSR2
1063# endif
1064 )
Bram Moolenaar1f28b072005-07-12 22:42:41 +00001065 && !vim_handle_signal(sigarg))
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00001066 SIGRETURN;
1067#endif
1068
Bram Moolenaar0f873732019-12-05 20:28:46 +01001069 // Remember how often we have been called.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001070 ++entered;
1071
Bram Moolenaar0f873732019-12-05 20:28:46 +01001072 // Executing autocommands is likely to use more stack space than we have
1073 // available in the signal stack.
Bram Moolenaare429e702016-06-10 19:49:14 +02001074 block_autocmds();
Bram Moolenaare429e702016-06-10 19:49:14 +02001075
Bram Moolenaar071d4272004-06-13 20:20:40 +00001076#ifdef FEAT_EVAL
Bram Moolenaar0f873732019-12-05 20:28:46 +01001077 // Set the v:dying variable.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001078 set_vim_var_nr(VV_DYING, (long)entered);
1079#endif
Bram Moolenaarf2bd8ef2018-03-04 18:08:14 +01001080 v_dying = entered;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001081
Bram Moolenaarbc7aa852005-03-06 23:38:09 +00001082#ifdef HAVE_STACK_LIMIT
Bram Moolenaar0f873732019-12-05 20:28:46 +01001083 // Since we are now using the signal stack, need to reset the stack
1084 // limit. Otherwise using a regexp will fail.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001085 get_stack_limit();
1086#endif
1087
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001088#if 0
Bram Moolenaar0f873732019-12-05 20:28:46 +01001089 // This is for opening gdb the moment Vim crashes.
1090 // You need to manually adjust the file name and Vim executable name.
1091 // Suggested by SungHyun Nam.
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001092 {
1093# define VI_GDB_FILE "/tmp/vimgdb"
1094# define VIM_NAME "/usr/bin/vim"
1095 FILE *fp = fopen(VI_GDB_FILE, "w");
1096 if (fp)
1097 {
1098 fprintf(fp,
1099 "file %s\n"
1100 "attach %d\n"
1101 "set height 1000\n"
1102 "bt full\n"
1103 , VIM_NAME, getpid());
1104 fclose(fp);
1105 system("xterm -e gdb -x "VI_GDB_FILE);
1106 unlink(VI_GDB_FILE);
1107 }
1108 }
1109#endif
1110
Bram Moolenaar071d4272004-06-13 20:20:40 +00001111#ifdef SIGHASARG
Bram Moolenaar0f873732019-12-05 20:28:46 +01001112 // try to find the name of this signal
Bram Moolenaar071d4272004-06-13 20:20:40 +00001113 for (i = 0; signal_info[i].sig != -1; i++)
1114 if (sigarg == signal_info[i].sig)
1115 break;
1116 deadly_signal = sigarg;
1117#endif
1118
Bram Moolenaar0f873732019-12-05 20:28:46 +01001119 full_screen = FALSE; // don't write message to the GUI, it might be
1120 // part of the problem...
Bram Moolenaar071d4272004-06-13 20:20:40 +00001121 /*
1122 * If something goes wrong after entering here, we may get here again.
1123 * When this happens, give a message and try to exit nicely (resetting the
1124 * terminal mode, etc.)
1125 * When this happens twice, just exit, don't even try to give a message,
1126 * stack may be corrupt or something weird.
1127 * When this still happens again (or memory was corrupted in such a way
1128 * that "entered" was clobbered) use _exit(), don't try freeing resources.
1129 */
1130 if (entered >= 3)
1131 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01001132 reset_signals(); // don't catch any signals anymore
Bram Moolenaar071d4272004-06-13 20:20:40 +00001133 may_core_dump();
1134 if (entered >= 4)
1135 _exit(8);
1136 exit(7);
1137 }
1138 if (entered == 2)
1139 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01001140 // No translation, it may call malloc().
Bram Moolenaarbec9c202013-09-05 21:41:39 +02001141 OUT_STR("Vim: Double signal, exiting\n");
Bram Moolenaar071d4272004-06-13 20:20:40 +00001142 out_flush();
1143 getout(1);
1144 }
1145
Bram Moolenaar0f873732019-12-05 20:28:46 +01001146 // No translation, it may call malloc().
Bram Moolenaar071d4272004-06-13 20:20:40 +00001147#ifdef SIGHASARG
Bram Moolenaar69212b12020-05-10 14:14:03 +02001148 sprintf((char *)IObuff, "Vim: Caught deadly signal %s\r\n",
Bram Moolenaar071d4272004-06-13 20:20:40 +00001149 signal_info[i].name);
1150#else
Bram Moolenaar69212b12020-05-10 14:14:03 +02001151 sprintf((char *)IObuff, "Vim: Caught deadly signal\r\n");
Bram Moolenaar071d4272004-06-13 20:20:40 +00001152#endif
Bram Moolenaarbec9c202013-09-05 21:41:39 +02001153
Bram Moolenaar0f873732019-12-05 20:28:46 +01001154 // Preserve files and exit. This sets the really_exiting flag to prevent
1155 // calling free().
Bram Moolenaarbec9c202013-09-05 21:41:39 +02001156 preserve_exit();
Bram Moolenaar071d4272004-06-13 20:20:40 +00001157
Bram Moolenaar0f873732019-12-05 20:28:46 +01001158 // NOTREACHED
Bram Moolenaare429e702016-06-10 19:49:14 +02001159
Bram Moolenaar009b2592004-10-24 19:18:58 +00001160#ifdef NBDEBUG
1161 reset_signals();
1162 may_core_dump();
1163 abort();
1164#endif
1165
Bram Moolenaar071d4272004-06-13 20:20:40 +00001166 SIGRETURN;
1167}
1168
Bram Moolenaar071d4272004-06-13 20:20:40 +00001169/*
Bram Moolenaar2e310482018-08-21 13:09:10 +02001170 * Invoked after receiving SIGCONT. We don't know what happened while
1171 * sleeping, deal with part of that.
1172 */
1173 static void
1174after_sigcont(void)
1175{
Bram Moolenaar2e310482018-08-21 13:09:10 +02001176 // Don't change "oldtitle" in a signal handler, set a flag to obtain it
1177 // again later.
1178 oldtitle_outdated = TRUE;
Bram Moolenaar651fca82021-11-29 20:39:38 +00001179
Bram Moolenaar2e310482018-08-21 13:09:10 +02001180 settmode(TMODE_RAW);
1181 need_check_timestamps = TRUE;
1182 did_check_timestamps = FALSE;
1183}
1184
1185#if defined(SIGCONT)
1186static RETSIGTYPE sigcont_handler SIGPROTOARG;
Bram Moolenaar2e310482018-08-21 13:09:10 +02001187
1188/*
1189 * With multi-threading, suspending might not work immediately. Catch the
1190 * SIGCONT signal, which will be used as an indication whether the suspending
1191 * has been done or not.
Bram Moolenaar76243bd2009-03-02 01:47:02 +00001192 *
1193 * On Linux, signal is not always handled immediately either.
1194 * See https://bugs.launchpad.net/bugs/291373
Bram Moolenaar2e310482018-08-21 13:09:10 +02001195 * Probably because the signal is handled in another thread.
Bram Moolenaar76243bd2009-03-02 01:47:02 +00001196 *
Bram Moolenaarb292a2a2011-02-09 18:47:40 +01001197 * volatile because it is used in signal handler sigcont_handler().
Bram Moolenaar071d4272004-06-13 20:20:40 +00001198 */
Bram Moolenaar8e82c052018-08-21 19:47:48 +02001199static volatile sig_atomic_t sigcont_received;
Bram Moolenaarf1883472018-08-20 21:58:57 +02001200static RETSIGTYPE sigcont_handler SIGPROTOARG;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001201
1202/*
1203 * signal handler for SIGCONT
1204 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001205 static RETSIGTYPE
1206sigcont_handler SIGDEFARG(sigarg)
1207{
Bram Moolenaar2e310482018-08-21 13:09:10 +02001208 if (in_mch_suspend)
1209 {
1210 sigcont_received = TRUE;
1211 }
1212 else
1213 {
1214 // We didn't suspend ourselves, assume we were stopped by a SIGSTOP
1215 // signal (which can't be intercepted) and get a SIGCONT. Need to get
1216 // back to a sane mode. We should redraw, but we can't really do that
1217 // in a signal handler, do a redraw later.
1218 after_sigcont();
1219 redraw_later(CLEAR);
1220 cursor_on_force();
1221 out_flush();
1222 }
1223
Bram Moolenaar071d4272004-06-13 20:20:40 +00001224 SIGRETURN;
1225}
1226#endif
1227
Bram Moolenaar6dff58f2018-09-30 21:43:26 +02001228#if defined(FEAT_CLIPBOARD) && defined(FEAT_X11)
Bram Moolenaar090cfc12013-03-19 12:35:42 +01001229# ifdef USE_SYSTEM
Bram Moolenaar1a0316c2013-03-13 17:50:25 +01001230static void *clip_star_save = NULL;
1231static void *clip_plus_save = NULL;
Bram Moolenaar090cfc12013-03-19 12:35:42 +01001232# endif
Bram Moolenaar62b42182010-09-21 22:09:37 +02001233
1234/*
1235 * Called when Vim is going to sleep or execute a shell command.
1236 * We can't respond to requests for the X selections. Lose them, otherwise
1237 * other applications will hang. But first copy the text to cut buffer 0.
1238 */
1239 static void
Bram Moolenaar05540972016-01-30 20:31:25 +01001240loose_clipboard(void)
Bram Moolenaar62b42182010-09-21 22:09:37 +02001241{
1242 if (clip_star.owned || clip_plus.owned)
1243 {
1244 x11_export_final_selection();
1245 if (clip_star.owned)
1246 clip_lose_selection(&clip_star);
1247 if (clip_plus.owned)
1248 clip_lose_selection(&clip_plus);
1249 if (x11_display != NULL)
1250 XFlush(x11_display);
1251 }
1252}
Bram Moolenaar1a0316c2013-03-13 17:50:25 +01001253
Bram Moolenaar090cfc12013-03-19 12:35:42 +01001254# ifdef USE_SYSTEM
Bram Moolenaar1a0316c2013-03-13 17:50:25 +01001255/*
1256 * Save clipboard text to restore later.
1257 */
1258 static void
Bram Moolenaar05540972016-01-30 20:31:25 +01001259save_clipboard(void)
Bram Moolenaar1a0316c2013-03-13 17:50:25 +01001260{
1261 if (clip_star.owned)
1262 clip_star_save = get_register('*', TRUE);
1263 if (clip_plus.owned)
1264 clip_plus_save = get_register('+', TRUE);
1265}
1266
1267/*
1268 * Restore clipboard text if no one own the X selection.
1269 */
1270 static void
Bram Moolenaar05540972016-01-30 20:31:25 +01001271restore_clipboard(void)
Bram Moolenaar1a0316c2013-03-13 17:50:25 +01001272{
1273 if (clip_star_save != NULL)
1274 {
1275 if (!clip_gen_owner_exists(&clip_star))
1276 put_register('*', clip_star_save);
1277 else
1278 free_register(clip_star_save);
1279 clip_star_save = NULL;
1280 }
1281 if (clip_plus_save != NULL)
1282 {
1283 if (!clip_gen_owner_exists(&clip_plus))
1284 put_register('+', clip_plus_save);
1285 else
1286 free_register(clip_plus_save);
1287 clip_plus_save = NULL;
1288 }
1289}
Bram Moolenaar090cfc12013-03-19 12:35:42 +01001290# endif
Bram Moolenaar62b42182010-09-21 22:09:37 +02001291#endif
1292
Bram Moolenaar071d4272004-06-13 20:20:40 +00001293/*
1294 * If the machine has job control, use it to suspend the program,
1295 * otherwise fake it by starting a new shell.
1296 */
1297 void
Bram Moolenaar05540972016-01-30 20:31:25 +01001298mch_suspend(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001299{
Bram Moolenaar5c3128e2020-05-11 20:54:42 +02001300 if (ignore_sigtstp)
1301 return;
1302
Bram Moolenaar041c7102020-05-30 18:14:57 +02001303#if defined(SIGTSTP)
Bram Moolenaar2e310482018-08-21 13:09:10 +02001304 in_mch_suspend = TRUE;
1305
Bram Moolenaar0f873732019-12-05 20:28:46 +01001306 out_flush(); // needed to make cursor visible on some systems
Bram Moolenaar071d4272004-06-13 20:20:40 +00001307 settmode(TMODE_COOK);
Bram Moolenaar0f873732019-12-05 20:28:46 +01001308 out_flush(); // needed to disable mouse on some systems
Bram Moolenaar071d4272004-06-13 20:20:40 +00001309
1310# if defined(FEAT_CLIPBOARD) && defined(FEAT_X11)
Bram Moolenaar62b42182010-09-21 22:09:37 +02001311 loose_clipboard();
Bram Moolenaar071d4272004-06-13 20:20:40 +00001312# endif
Bram Moolenaar2e310482018-08-21 13:09:10 +02001313# if defined(SIGCONT)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001314 sigcont_received = FALSE;
1315# endif
Bram Moolenaar2e310482018-08-21 13:09:10 +02001316
Bram Moolenaar0f873732019-12-05 20:28:46 +01001317 kill(0, SIGTSTP); // send ourselves a STOP signal
Bram Moolenaar2e310482018-08-21 13:09:10 +02001318
1319# if defined(SIGCONT)
Bram Moolenaar76243bd2009-03-02 01:47:02 +00001320 /*
1321 * Wait for the SIGCONT signal to be handled. It generally happens
Bram Moolenaar2e310482018-08-21 13:09:10 +02001322 * immediately, but somehow not all the time, probably because it's handled
1323 * in another thread. Do not call pause() because there would be race
1324 * condition which would hang Vim if signal happened in between the test of
1325 * sigcont_received and the call to pause(). If signal is not yet received,
1326 * sleep 0, 1, 2, 3 ms. Don't bother waiting further if signal is not
1327 * received after 1+2+3 ms (not expected to happen).
Bram Moolenaar76243bd2009-03-02 01:47:02 +00001328 */
1329 {
Bram Moolenaar262735e2009-07-14 10:20:22 +00001330 long wait_time;
Bram Moolenaar2e310482018-08-21 13:09:10 +02001331
Bram Moolenaar262735e2009-07-14 10:20:22 +00001332 for (wait_time = 0; !sigcont_received && wait_time <= 3L; wait_time++)
Bram Moolenaar0981c872020-08-23 14:28:37 +02001333 mch_delay(wait_time, 0);
Bram Moolenaar76243bd2009-03-02 01:47:02 +00001334 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001335# endif
Bram Moolenaar2e310482018-08-21 13:09:10 +02001336 in_mch_suspend = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001337
Bram Moolenaar2e310482018-08-21 13:09:10 +02001338 after_sigcont();
Bram Moolenaar071d4272004-06-13 20:20:40 +00001339#else
1340 suspend_shell();
1341#endif
1342}
1343
1344 void
Bram Moolenaar05540972016-01-30 20:31:25 +01001345mch_init(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001346{
1347 Columns = 80;
1348 Rows = 24;
1349
1350 out_flush();
Bram Moolenaar5c3128e2020-05-11 20:54:42 +02001351
1352#ifdef SIGTSTP
1353 // Check whether we were invoked with SIGTSTP set to be ignored. If it is
1354 // that indicates the shell (or program) that launched us does not support
1355 // tty job control and thus we should ignore that signal. If invoked as a
1356 // restricted editor (e.g., as "rvim") SIGTSTP is always ignored.
1357 ignore_sigtstp = restricted || SIG_IGN == signal(SIGTSTP, SIG_ERR);
1358#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001359 set_signals();
Bram Moolenaardf177f62005-02-22 08:39:57 +00001360
Bram Moolenaar56718732006-03-15 22:53:57 +00001361#ifdef MACOS_CONVERT
Bram Moolenaardf177f62005-02-22 08:39:57 +00001362 mac_conv_init();
1363#endif
Bram Moolenaar693e40c2013-02-26 14:56:42 +01001364#ifdef FEAT_CYGWIN_WIN32_CLIPBOARD
1365 win_clip_init();
1366#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001367}
1368
1369 static void
Bram Moolenaar05540972016-01-30 20:31:25 +01001370set_signals(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001371{
1372#if defined(SIGWINCH)
1373 /*
1374 * WINDOW CHANGE signal is handled with sig_winch().
1375 */
1376 signal(SIGWINCH, (RETSIGTYPE (*)())sig_winch);
1377#endif
1378
Bram Moolenaar071d4272004-06-13 20:20:40 +00001379#ifdef SIGTSTP
Bram Moolenaar5c3128e2020-05-11 20:54:42 +02001380 // See mch_init() for the conditions under which we ignore SIGTSTP.
dbivolaruab16ad32021-12-29 19:41:47 +00001381 signal(SIGTSTP, ignore_sigtstp ? SIG_IGN : (RETSIGTYPE (*)())sig_tstp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001382#endif
Bram Moolenaar2e310482018-08-21 13:09:10 +02001383#if defined(SIGCONT)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001384 signal(SIGCONT, sigcont_handler);
1385#endif
Bram Moolenaarbe5ee862020-06-10 20:56:58 +02001386#ifdef SIGPIPE
Bram Moolenaar071d4272004-06-13 20:20:40 +00001387 /*
1388 * We want to ignore breaking of PIPEs.
1389 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001390 signal(SIGPIPE, SIG_IGN);
1391#endif
1392
Bram Moolenaar071d4272004-06-13 20:20:40 +00001393#ifdef SIGINT
Bram Moolenaardf177f62005-02-22 08:39:57 +00001394 catch_int_signal();
Bram Moolenaar071d4272004-06-13 20:20:40 +00001395#endif
1396
Bram Moolenaarbe5ee862020-06-10 20:56:58 +02001397#ifdef SIGUSR1
1398 /*
1399 * Call user's handler on SIGUSR1
1400 */
1401 signal(SIGUSR1, (RETSIGTYPE (*)())catch_sigusr1);
1402#endif
1403
Bram Moolenaar071d4272004-06-13 20:20:40 +00001404 /*
1405 * Ignore alarm signals (Perl's alarm() generates it).
1406 */
1407#ifdef SIGALRM
1408 signal(SIGALRM, SIG_IGN);
1409#endif
1410
Bram Moolenaarbe5ee862020-06-10 20:56:58 +02001411#ifdef SIGPWR
Bram Moolenaar071d4272004-06-13 20:20:40 +00001412 /*
1413 * Catch SIGPWR (power failure?) to preserve the swap files, so that no
1414 * work will be lost.
1415 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001416 signal(SIGPWR, (RETSIGTYPE (*)())catch_sigpwr);
1417#endif
1418
1419 /*
1420 * Arrange for other signals to gracefully shutdown Vim.
1421 */
1422 catch_signals(deathtrap, SIG_ERR);
1423
1424#if defined(FEAT_GUI) && defined(SIGHUP)
1425 /*
1426 * When the GUI is running, ignore the hangup signal.
1427 */
1428 if (gui.in_use)
1429 signal(SIGHUP, SIG_IGN);
1430#endif
1431}
1432
Bram Moolenaardf177f62005-02-22 08:39:57 +00001433#if defined(SIGINT) || defined(PROTO)
1434/*
1435 * Catch CTRL-C (only works while in Cooked mode).
1436 */
1437 static void
Bram Moolenaar05540972016-01-30 20:31:25 +01001438catch_int_signal(void)
Bram Moolenaardf177f62005-02-22 08:39:57 +00001439{
1440 signal(SIGINT, (RETSIGTYPE (*)())catch_sigint);
1441}
1442#endif
1443
Bram Moolenaar071d4272004-06-13 20:20:40 +00001444 void
Bram Moolenaar05540972016-01-30 20:31:25 +01001445reset_signals(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001446{
1447 catch_signals(SIG_DFL, SIG_DFL);
Bram Moolenaar2e310482018-08-21 13:09:10 +02001448#if defined(SIGCONT)
Bram Moolenaar0f873732019-12-05 20:28:46 +01001449 // SIGCONT isn't in the list, because its default action is ignore
Bram Moolenaar071d4272004-06-13 20:20:40 +00001450 signal(SIGCONT, SIG_DFL);
1451#endif
1452}
1453
1454 static void
Bram Moolenaar05540972016-01-30 20:31:25 +01001455catch_signals(
1456 RETSIGTYPE (*func_deadly)(),
1457 RETSIGTYPE (*func_other)())
Bram Moolenaar071d4272004-06-13 20:20:40 +00001458{
1459 int i;
1460
1461 for (i = 0; signal_info[i].sig != -1; i++)
Bram Moolenaar5c3128e2020-05-11 20:54:42 +02001462 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00001463 if (signal_info[i].deadly)
1464 {
1465#if defined(HAVE_SIGALTSTACK) && defined(HAVE_SIGACTION)
1466 struct sigaction sa;
1467
Bram Moolenaar0f873732019-12-05 20:28:46 +01001468 // Setup to use the alternate stack for the signal function.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001469 sa.sa_handler = func_deadly;
1470 sigemptyset(&sa.sa_mask);
1471# if defined(__linux__) && defined(_REENTRANT)
Bram Moolenaar0f873732019-12-05 20:28:46 +01001472 // On Linux, with glibc compiled for kernel 2.2, there is a bug in
1473 // thread handling in combination with using the alternate stack:
1474 // pthread library functions try to use the stack pointer to
1475 // identify the current thread, causing a SEGV signal, which
1476 // recursively calls deathtrap() and hangs.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001477 sa.sa_flags = 0;
1478# else
1479 sa.sa_flags = SA_ONSTACK;
1480# endif
1481 sigaction(signal_info[i].sig, &sa, NULL);
1482#else
1483# if defined(HAVE_SIGALTSTACK) && defined(HAVE_SIGVEC)
1484 struct sigvec sv;
1485
Bram Moolenaar0f873732019-12-05 20:28:46 +01001486 // Setup to use the alternate stack for the signal function.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001487 sv.sv_handler = func_deadly;
1488 sv.sv_mask = 0;
1489 sv.sv_flags = SV_ONSTACK;
1490 sigvec(signal_info[i].sig, &sv, NULL);
1491# else
1492 signal(signal_info[i].sig, func_deadly);
1493# endif
1494#endif
1495 }
1496 else if (func_other != SIG_ERR)
Bram Moolenaar5c3128e2020-05-11 20:54:42 +02001497 {
1498 // Deal with non-deadly signals.
1499#ifdef SIGTSTP
1500 signal(signal_info[i].sig,
1501 signal_info[i].sig == SIGTSTP && ignore_sigtstp
1502 ? SIG_IGN : func_other);
1503#else
Bram Moolenaar071d4272004-06-13 20:20:40 +00001504 signal(signal_info[i].sig, func_other);
Bram Moolenaar5c3128e2020-05-11 20:54:42 +02001505#endif
1506 }
1507 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001508}
1509
Bram Moolenaarbb09ceb2016-10-18 16:27:23 +02001510#ifdef HAVE_SIGPROCMASK
1511 static void
1512block_signals(sigset_t *set)
1513{
1514 sigset_t newset;
1515 int i;
1516
1517 sigemptyset(&newset);
1518
1519 for (i = 0; signal_info[i].sig != -1; i++)
1520 sigaddset(&newset, signal_info[i].sig);
1521
Bram Moolenaar2e310482018-08-21 13:09:10 +02001522# if defined(SIGCONT)
Bram Moolenaar0f873732019-12-05 20:28:46 +01001523 // SIGCONT isn't in the list, because its default action is ignore
Bram Moolenaarbb09ceb2016-10-18 16:27:23 +02001524 sigaddset(&newset, SIGCONT);
1525# endif
1526
1527 sigprocmask(SIG_BLOCK, &newset, set);
1528}
1529
1530 static void
1531unblock_signals(sigset_t *set)
1532{
1533 sigprocmask(SIG_SETMASK, set, NULL);
1534}
1535#endif
1536
Bram Moolenaar071d4272004-06-13 20:20:40 +00001537/*
Bram Moolenaard8b0cf12004-12-12 11:33:30 +00001538 * Handling of SIGHUP, SIGQUIT and SIGTERM:
Bram Moolenaar9e1d2832007-05-06 12:51:41 +00001539 * "when" == a signal: when busy, postpone and return FALSE, otherwise
1540 * return TRUE
1541 * "when" == SIGNAL_BLOCK: Going to be busy, block signals
1542 * "when" == SIGNAL_UNBLOCK: Going to wait, unblock signals, use postponed
Bram Moolenaar67c53842010-05-22 18:28:27 +02001543 * signal
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00001544 * Returns TRUE when Vim should exit.
1545 */
1546 int
Bram Moolenaar05540972016-01-30 20:31:25 +01001547vim_handle_signal(int sig)
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00001548{
Bram Moolenaard8b0cf12004-12-12 11:33:30 +00001549 static int got_signal = 0;
1550 static int blocked = TRUE;
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00001551
Bram Moolenaard8b0cf12004-12-12 11:33:30 +00001552 switch (sig)
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00001553 {
Bram Moolenaard8b0cf12004-12-12 11:33:30 +00001554 case SIGNAL_BLOCK: blocked = TRUE;
1555 break;
1556
1557 case SIGNAL_UNBLOCK: blocked = FALSE;
1558 if (got_signal != 0)
1559 {
1560 kill(getpid(), got_signal);
1561 got_signal = 0;
1562 }
1563 break;
1564
1565 default: if (!blocked)
Bram Moolenaar0f873732019-12-05 20:28:46 +01001566 return TRUE; // exit!
Bram Moolenaard8b0cf12004-12-12 11:33:30 +00001567 got_signal = sig;
1568#ifdef SIGPWR
1569 if (sig != SIGPWR)
1570#endif
Bram Moolenaar0f873732019-12-05 20:28:46 +01001571 got_int = TRUE; // break any loops
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00001572 break;
1573 }
1574 return FALSE;
1575}
1576
1577/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00001578 * Check_win checks whether we have an interactive stdout.
1579 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001580 int
Bram Moolenaar05540972016-01-30 20:31:25 +01001581mch_check_win(int argc UNUSED, char **argv UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001582{
Bram Moolenaar071d4272004-06-13 20:20:40 +00001583 if (isatty(1))
1584 return OK;
1585 return FAIL;
1586}
1587
1588/*
1589 * Return TRUE if the input comes from a terminal, FALSE otherwise.
1590 */
1591 int
Bram Moolenaar05540972016-01-30 20:31:25 +01001592mch_input_isatty(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001593{
1594 if (isatty(read_cmd_fd))
1595 return TRUE;
1596 return FALSE;
1597}
1598
1599#ifdef FEAT_X11
1600
Bram Moolenaar651fca82021-11-29 20:39:38 +00001601# if defined(ELAPSED_TIMEVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001602
Bram Moolenaar071d4272004-06-13 20:20:40 +00001603/*
1604 * Give a message about the elapsed time for opening the X window.
1605 */
1606 static void
Bram Moolenaar833eb1d2016-11-24 17:22:50 +01001607xopen_message(long elapsed_msec)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001608{
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001609 smsg(_("Opening the X display took %ld msec"), elapsed_msec);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001610}
1611# endif
1612#endif
1613
Bram Moolenaar651fca82021-11-29 20:39:38 +00001614#if defined(FEAT_X11)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001615/*
1616 * A few functions shared by X11 title and clipboard code.
1617 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001618
1619static int got_x_error = FALSE;
1620
1621/*
1622 * X Error handler, otherwise X just exits! (very rude) -- webb
1623 */
1624 static int
Bram Moolenaar05540972016-01-30 20:31:25 +01001625x_error_handler(Display *dpy, XErrorEvent *error_event)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001626{
Bram Moolenaar843ee412004-06-30 16:16:41 +00001627 XGetErrorText(dpy, error_event->error_code, (char *)IObuff, IOSIZE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001628 STRCAT(IObuff, _("\nVim: Got X error\n"));
1629
Bram Moolenaarb1062eb2020-05-09 16:11:33 +02001630 // In the GUI we cannot print a message and continue, because no X calls
1631 // are allowed here (causes my system to hang). Silently continuing seems
1632 // like the best alternative. Do preserve files, in case we crash.
1633 ml_sync_all(FALSE, FALSE);
1634
1635#ifdef FEAT_GUI
1636 if (!gui.in_use)
1637#endif
1638 msg((char *)IObuff);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001639
Bram Moolenaar0f873732019-12-05 20:28:46 +01001640 return 0; // NOTREACHED
Bram Moolenaar071d4272004-06-13 20:20:40 +00001641}
1642
1643/*
1644 * Another X Error handler, just used to check for errors.
1645 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001646 static int
Bram Moolenaar05540972016-01-30 20:31:25 +01001647x_error_check(Display *dpy UNUSED, XErrorEvent *error_event UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001648{
1649 got_x_error = TRUE;
1650 return 0;
1651}
1652
Bram Moolenaarc0c75492018-12-29 11:03:23 +01001653/*
1654 * Return TRUE when connection to the X server is desired.
1655 */
1656 static int
1657x_connect_to_server(void)
1658{
1659 // No point in connecting if we are exiting or dying.
1660 if (exiting || v_dying)
1661 return FALSE;
1662
1663#if defined(FEAT_CLIENTSERVER)
1664 if (x_force_connect)
1665 return TRUE;
1666#endif
1667 if (x_no_connect)
1668 return FALSE;
1669
Bram Moolenaara8bfa172018-12-29 22:28:46 +01001670 // Check for a match with "exclude:" from 'clipboard'.
Bram Moolenaarc0c75492018-12-29 11:03:23 +01001671 if (clip_exclude_prog != NULL)
1672 {
Bram Moolenaara8bfa172018-12-29 22:28:46 +01001673 // Just in case we get called recursively, return FALSE. This could
1674 // happen if vpeekc() is used while executing the prog and it causes a
1675 // related callback to be invoked.
1676 if (regprog_in_use(clip_exclude_prog))
1677 return FALSE;
1678
Bram Moolenaarc0c75492018-12-29 11:03:23 +01001679 if (vim_regexec_prog(&clip_exclude_prog, FALSE, T_NAME, (colnr_T)0))
1680 return FALSE;
1681 }
1682 return TRUE;
1683}
1684
Bram Moolenaar071d4272004-06-13 20:20:40 +00001685#if defined(FEAT_X11) && defined(FEAT_XCLIPBOARD)
Bram Moolenaarb2148f52019-01-20 23:43:57 +01001686# if defined(USING_SETJMP)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001687/*
1688 * An X IO Error handler, used to catch error while opening the display.
1689 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001690 static int
Bram Moolenaar05540972016-01-30 20:31:25 +01001691x_IOerror_check(Display *dpy UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001692{
Bram Moolenaar0f873732019-12-05 20:28:46 +01001693 // This function should not return, it causes exit(). Longjump instead.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001694 LONGJMP(lc_jump_env, 1);
Bram Moolenaar1eed5322019-02-26 17:03:54 +01001695# if defined(VMS) || defined(__CYGWIN__)
Bram Moolenaar0f873732019-12-05 20:28:46 +01001696 return 0; // avoid the compiler complains about missing return value
Bram Moolenaarb4990bf2010-02-11 18:19:38 +01001697# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001698}
1699# endif
1700
1701/*
1702 * An X IO Error handler, used to catch terminal errors.
1703 */
Bram Moolenaard155d7a2018-12-21 16:04:21 +01001704static int xterm_dpy_retry_count = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001705
Bram Moolenaar071d4272004-06-13 20:20:40 +00001706 static int
Bram Moolenaar05540972016-01-30 20:31:25 +01001707x_IOerror_handler(Display *dpy UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001708{
1709 xterm_dpy = NULL;
Bram Moolenaard155d7a2018-12-21 16:04:21 +01001710 xterm_dpy_retry_count = 5; // Try reconnecting five times
Bram Moolenaar071d4272004-06-13 20:20:40 +00001711 x11_window = 0;
1712 x11_display = NULL;
1713 xterm_Shell = (Widget)0;
1714
Bram Moolenaar0f873732019-12-05 20:28:46 +01001715 // This function should not return, it causes exit(). Longjump instead.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001716 LONGJMP(x_jump_env, 1);
Bram Moolenaar1eed5322019-02-26 17:03:54 +01001717# if defined(VMS) || defined(__CYGWIN__)
Bram Moolenaar0f873732019-12-05 20:28:46 +01001718 return 0; // avoid the compiler complains about missing return value
Bram Moolenaarb4990bf2010-02-11 18:19:38 +01001719# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001720}
Bram Moolenaarb1e26502014-11-19 18:48:46 +01001721
1722/*
1723 * If the X11 connection was lost try to restore it.
1724 * Helps when the X11 server was stopped and restarted while Vim was inactive
Bram Moolenaarcaad4f02014-12-17 14:36:14 +01001725 * (e.g. through tmux).
Bram Moolenaarb1e26502014-11-19 18:48:46 +01001726 */
1727 static void
Bram Moolenaar05540972016-01-30 20:31:25 +01001728may_restore_clipboard(void)
Bram Moolenaarb1e26502014-11-19 18:48:46 +01001729{
Bram Moolenaar01e51e52018-12-29 13:09:46 +01001730 // No point in restoring the connecting if we are exiting or dying.
1731 if (!exiting && !v_dying && xterm_dpy_retry_count > 0)
Bram Moolenaarb1e26502014-11-19 18:48:46 +01001732 {
Bram Moolenaard155d7a2018-12-21 16:04:21 +01001733 --xterm_dpy_retry_count;
Bram Moolenaar527a6782014-12-17 17:59:31 +01001734
1735# ifndef LESSTIF_VERSION
Bram Moolenaar0f873732019-12-05 20:28:46 +01001736 // This has been reported to avoid Vim getting stuck.
Bram Moolenaar527a6782014-12-17 17:59:31 +01001737 if (app_context != (XtAppContext)NULL)
1738 {
1739 XtDestroyApplicationContext(app_context);
1740 app_context = (XtAppContext)NULL;
Bram Moolenaar0f873732019-12-05 20:28:46 +01001741 x11_display = NULL; // freed by XtDestroyApplicationContext()
Bram Moolenaar527a6782014-12-17 17:59:31 +01001742 }
1743# endif
1744
Bram Moolenaarb1e26502014-11-19 18:48:46 +01001745 setup_term_clip();
1746 get_x11_title(FALSE);
1747 }
1748}
Bram Moolenaard4aa83a2019-05-09 18:59:31 +02001749
1750 void
1751ex_xrestore(exarg_T *eap)
1752{
1753 if (eap->arg != NULL && STRLEN(eap->arg) > 0)
1754 {
1755 if (xterm_display_allocated)
1756 vim_free(xterm_display);
1757 xterm_display = (char *)vim_strsave(eap->arg);
1758 xterm_display_allocated = TRUE;
1759 }
1760 smsg(_("restoring display %s"), xterm_display == NULL
Bram Moolenaar0c5c3fa2019-11-30 22:38:16 +01001761 ? (char *)mch_getenv((char_u *)"DISPLAY") : xterm_display);
Bram Moolenaard4aa83a2019-05-09 18:59:31 +02001762
1763 clear_xterm_clip();
1764 x11_window = 0;
1765 xterm_dpy_retry_count = 5; // Try reconnecting five times
1766 may_restore_clipboard();
1767}
Bram Moolenaar071d4272004-06-13 20:20:40 +00001768#endif
1769
1770/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00001771 * Test if "dpy" and x11_window are valid by getting the window title.
1772 * I don't actually want it yet, so there may be a simpler call to use, but
1773 * this will cause the error handler x_error_check() to be called if anything
1774 * is wrong, such as the window pointer being invalid (as can happen when the
1775 * user changes his DISPLAY, but not his WINDOWID) -- webb
1776 */
1777 static int
Bram Moolenaar05540972016-01-30 20:31:25 +01001778test_x11_window(Display *dpy)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001779{
1780 int (*old_handler)();
1781 XTextProperty text_prop;
1782
1783 old_handler = XSetErrorHandler(x_error_check);
1784 got_x_error = FALSE;
1785 if (XGetWMName(dpy, x11_window, &text_prop))
1786 XFree((void *)text_prop.value);
1787 XSync(dpy, False);
1788 (void)XSetErrorHandler(old_handler);
1789
1790 if (p_verbose > 0 && got_x_error)
Bram Moolenaar32526b32019-01-19 17:43:09 +01001791 verb_msg(_("Testing the X display failed"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00001792
1793 return (got_x_error ? FAIL : OK);
1794}
1795#endif
1796
Bram Moolenaar071d4272004-06-13 20:20:40 +00001797
1798#ifdef FEAT_X11
1799
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +01001800static int get_x11_thing(int get_title, int test_only);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001801
1802/*
1803 * try to get x11 window and display
1804 *
1805 * return FAIL for failure, OK otherwise
1806 */
1807 static int
Bram Moolenaar05540972016-01-30 20:31:25 +01001808get_x11_windis(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001809{
1810 char *winid;
1811 static int result = -1;
Bram Moolenaar0f873732019-12-05 20:28:46 +01001812#define XD_NONE 0 // x11_display not set here
1813#define XD_HERE 1 // x11_display opened here
1814#define XD_GUI 2 // x11_display used from gui.dpy
1815#define XD_XTERM 3 // x11_display used from xterm_dpy
Bram Moolenaar071d4272004-06-13 20:20:40 +00001816 static int x11_display_from = XD_NONE;
1817 static int did_set_error_handler = FALSE;
1818
1819 if (!did_set_error_handler)
1820 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01001821 // X just exits if it finds an error otherwise!
Bram Moolenaar071d4272004-06-13 20:20:40 +00001822 (void)XSetErrorHandler(x_error_handler);
1823 did_set_error_handler = TRUE;
1824 }
1825
Bram Moolenaar9372a112005-12-06 19:59:18 +00001826#if defined(FEAT_GUI_X11) || defined(FEAT_GUI_GTK)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001827 if (gui.in_use)
1828 {
1829 /*
1830 * If the X11 display was opened here before, for the window where Vim
1831 * was started, close that one now to avoid a memory leak.
1832 */
1833 if (x11_display_from == XD_HERE && x11_display != NULL)
1834 {
1835 XCloseDisplay(x11_display);
1836 x11_display_from = XD_NONE;
1837 }
1838 if (gui_get_x11_windis(&x11_window, &x11_display) == OK)
1839 {
1840 x11_display_from = XD_GUI;
1841 return OK;
1842 }
1843 x11_display = NULL;
1844 return FAIL;
1845 }
1846 else if (x11_display_from == XD_GUI)
1847 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01001848 // GUI must have stopped somehow, clear x11_display
Bram Moolenaar071d4272004-06-13 20:20:40 +00001849 x11_window = 0;
1850 x11_display = NULL;
1851 x11_display_from = XD_NONE;
1852 }
1853#endif
1854
Bram Moolenaar0f873732019-12-05 20:28:46 +01001855 // When started with the "-X" argument, don't try connecting.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001856 if (!x_connect_to_server())
1857 return FAIL;
1858
1859 /*
1860 * If WINDOWID not set, should try another method to find out
1861 * what the current window number is. The only code I know for
1862 * this is very complicated.
1863 * We assume that zero is invalid for WINDOWID.
1864 */
1865 if (x11_window == 0 && (winid = getenv("WINDOWID")) != NULL)
1866 x11_window = (Window)atol(winid);
1867
1868#ifdef FEAT_XCLIPBOARD
Bram Moolenaard4aa83a2019-05-09 18:59:31 +02001869 if (xterm_dpy == x11_display)
1870 // x11_display may have been set to xterm_dpy elsewhere
1871 x11_display_from = XD_XTERM;
1872
Bram Moolenaar071d4272004-06-13 20:20:40 +00001873 if (xterm_dpy != NULL && x11_window != 0)
1874 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01001875 // We may have checked it already, but Gnome terminal can move us to
1876 // another window, so we need to check every time.
Bram Moolenaarc2a27c32007-12-01 16:19:33 +00001877 if (x11_display_from != XD_XTERM)
1878 {
1879 /*
1880 * If the X11 display was opened here before, for the window where
1881 * Vim was started, close that one now to avoid a memory leak.
1882 */
1883 if (x11_display_from == XD_HERE && x11_display != NULL)
1884 XCloseDisplay(x11_display);
1885 x11_display = xterm_dpy;
1886 x11_display_from = XD_XTERM;
1887 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001888 if (test_x11_window(x11_display) == FAIL)
1889 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01001890 // probably bad $WINDOWID
Bram Moolenaar071d4272004-06-13 20:20:40 +00001891 x11_window = 0;
1892 x11_display = NULL;
1893 x11_display_from = XD_NONE;
1894 return FAIL;
1895 }
1896 return OK;
1897 }
1898#endif
1899
1900 if (x11_window == 0 || x11_display == NULL)
1901 result = -1;
1902
Bram Moolenaar0f873732019-12-05 20:28:46 +01001903 if (result != -1) // Have already been here and set this
1904 return result; // Don't do all these X calls again
Bram Moolenaar071d4272004-06-13 20:20:40 +00001905
1906 if (x11_window != 0 && x11_display == NULL)
1907 {
1908#ifdef SET_SIG_ALARM
1909 RETSIGTYPE (*sig_save)();
1910#endif
Bram Moolenaar1ac56c22019-01-17 22:28:22 +01001911#ifdef ELAPSED_FUNC
1912 elapsed_T start_tv;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001913
1914 if (p_verbose > 0)
Bram Moolenaar1ac56c22019-01-17 22:28:22 +01001915 ELAPSED_INIT(start_tv);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001916#endif
1917
1918#ifdef SET_SIG_ALARM
1919 /*
1920 * Opening the Display may hang if the DISPLAY setting is wrong, or
1921 * the network connection is bad. Set an alarm timer to get out.
1922 */
1923 sig_alarm_called = FALSE;
1924 sig_save = (RETSIGTYPE (*)())signal(SIGALRM,
1925 (RETSIGTYPE (*)())sig_alarm);
1926 alarm(2);
1927#endif
1928 x11_display = XOpenDisplay(NULL);
1929
1930#ifdef SET_SIG_ALARM
1931 alarm(0);
1932 signal(SIGALRM, (RETSIGTYPE (*)())sig_save);
1933 if (p_verbose > 0 && sig_alarm_called)
Bram Moolenaar563bbea2019-01-22 21:45:40 +01001934 verb_msg(_("Opening the X display timed out"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00001935#endif
1936 if (x11_display != NULL)
1937 {
Bram Moolenaar833eb1d2016-11-24 17:22:50 +01001938# ifdef ELAPSED_FUNC
Bram Moolenaar071d4272004-06-13 20:20:40 +00001939 if (p_verbose > 0)
Bram Moolenaara04f10b2005-05-31 22:09:46 +00001940 {
1941 verbose_enter();
Bram Moolenaar833eb1d2016-11-24 17:22:50 +01001942 xopen_message(ELAPSED_FUNC(start_tv));
Bram Moolenaara04f10b2005-05-31 22:09:46 +00001943 verbose_leave();
1944 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001945# endif
1946 if (test_x11_window(x11_display) == FAIL)
1947 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01001948 // Maybe window id is bad
Bram Moolenaar071d4272004-06-13 20:20:40 +00001949 x11_window = 0;
1950 XCloseDisplay(x11_display);
1951 x11_display = NULL;
1952 }
1953 else
1954 x11_display_from = XD_HERE;
1955 }
1956 }
1957 if (x11_window == 0 || x11_display == NULL)
1958 return (result = FAIL);
Bram Moolenaar727c8762010-10-20 19:17:48 +02001959
1960# ifdef FEAT_EVAL
1961 set_vim_var_nr(VV_WINDOWID, (long)x11_window);
1962# endif
1963
Bram Moolenaar071d4272004-06-13 20:20:40 +00001964 return (result = OK);
1965}
1966
1967/*
1968 * Determine original x11 Window Title
1969 */
1970 static int
Bram Moolenaar05540972016-01-30 20:31:25 +01001971get_x11_title(int test_only)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001972{
Bram Moolenaar47136d72004-10-12 20:02:24 +00001973 return get_x11_thing(TRUE, test_only);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001974}
1975
1976/*
1977 * Determine original x11 Window icon
1978 */
1979 static int
Bram Moolenaar05540972016-01-30 20:31:25 +01001980get_x11_icon(int test_only)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001981{
1982 int retval = FALSE;
1983
1984 retval = get_x11_thing(FALSE, test_only);
1985
Bram Moolenaar0f873732019-12-05 20:28:46 +01001986 // could not get old icon, use terminal name
Bram Moolenaar071d4272004-06-13 20:20:40 +00001987 if (oldicon == NULL && !test_only)
1988 {
1989 if (STRNCMP(T_NAME, "builtin_", 8) == 0)
Bram Moolenaar20de1c22009-07-22 11:28:11 +00001990 oldicon = vim_strsave(T_NAME + 8);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001991 else
Bram Moolenaar20de1c22009-07-22 11:28:11 +00001992 oldicon = vim_strsave(T_NAME);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001993 }
1994
1995 return retval;
1996}
1997
1998 static int
Bram Moolenaar05540972016-01-30 20:31:25 +01001999get_x11_thing(
Bram Moolenaar0f873732019-12-05 20:28:46 +01002000 int get_title, // get title string
Bram Moolenaar05540972016-01-30 20:31:25 +01002001 int test_only)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002002{
2003 XTextProperty text_prop;
2004 int retval = FALSE;
2005 Status status;
2006
2007 if (get_x11_windis() == OK)
2008 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01002009 // Get window/icon name if any
Bram Moolenaar071d4272004-06-13 20:20:40 +00002010 if (get_title)
2011 status = XGetWMName(x11_display, x11_window, &text_prop);
2012 else
2013 status = XGetWMIconName(x11_display, x11_window, &text_prop);
2014
2015 /*
2016 * If terminal is xterm, then x11_window may be a child window of the
2017 * outer xterm window that actually contains the window/icon name, so
2018 * keep traversing up the tree until a window with a title/icon is
2019 * found.
2020 */
Bram Moolenaar4b96df52020-01-26 22:00:26 +01002021 // Previously this was only done for xterm and alike. I don't see a
Bram Moolenaar0f873732019-12-05 20:28:46 +01002022 // reason why it would fail for other terminal emulators.
2023 // if (term_is_xterm)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002024 {
2025 Window root;
2026 Window parent;
2027 Window win = x11_window;
2028 Window *children;
2029 unsigned int num_children;
2030
2031 while (!status || text_prop.value == NULL)
2032 {
2033 if (!XQueryTree(x11_display, win, &root, &parent, &children,
2034 &num_children))
2035 break;
2036 if (children)
2037 XFree((void *)children);
2038 if (parent == root || parent == 0)
2039 break;
2040
2041 win = parent;
2042 if (get_title)
2043 status = XGetWMName(x11_display, win, &text_prop);
2044 else
2045 status = XGetWMIconName(x11_display, win, &text_prop);
2046 }
2047 }
2048 if (status && text_prop.value != NULL)
2049 {
2050 retval = TRUE;
2051 if (!test_only)
2052 {
Bram Moolenaar6b649ac2019-12-07 17:47:22 +01002053 if (get_title)
2054 vim_free(oldtitle);
2055 else
2056 vim_free(oldicon);
Bram Moolenaara12a1612019-01-24 16:39:02 +01002057 if (text_prop.encoding == XA_STRING && !has_mbyte)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002058 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00002059 if (get_title)
2060 oldtitle = vim_strsave((char_u *)text_prop.value);
2061 else
2062 oldicon = vim_strsave((char_u *)text_prop.value);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002063 }
2064 else
2065 {
2066 char **cl;
2067 Status transform_status;
2068 int n = 0;
2069
2070 transform_status = XmbTextPropertyToTextList(x11_display,
2071 &text_prop,
2072 &cl, &n);
2073 if (transform_status >= Success && n > 0 && cl[0])
2074 {
2075 if (get_title)
2076 oldtitle = vim_strsave((char_u *) cl[0]);
2077 else
2078 oldicon = vim_strsave((char_u *) cl[0]);
2079 XFreeStringList(cl);
2080 }
2081 else
2082 {
2083 if (get_title)
2084 oldtitle = vim_strsave((char_u *)text_prop.value);
2085 else
2086 oldicon = vim_strsave((char_u *)text_prop.value);
2087 }
2088 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002089 }
2090 XFree((void *)text_prop.value);
2091 }
2092 }
2093 return retval;
2094}
2095
Bram Moolenaar0f873732019-12-05 20:28:46 +01002096// Xutf8 functions are not available on older systems. Note that on some
2097// systems X_HAVE_UTF8_STRING may be defined in a header file but
2098// Xutf8SetWMProperties() is not in the X11 library. Configure checks for
2099// that and defines HAVE_XUTF8SETWMPROPERTIES.
Bram Moolenaara12a1612019-01-24 16:39:02 +01002100#if defined(X_HAVE_UTF8_STRING)
Bram Moolenaarcbc246a2014-10-11 14:47:26 +02002101# if X_HAVE_UTF8_STRING && HAVE_XUTF8SETWMPROPERTIES
Bram Moolenaar071d4272004-06-13 20:20:40 +00002102# define USE_UTF8_STRING
2103# endif
2104#endif
2105
2106/*
2107 * Set x11 Window Title
2108 *
2109 * get_x11_windis() must be called before this and have returned OK
2110 */
2111 static void
Bram Moolenaar05540972016-01-30 20:31:25 +01002112set_x11_title(char_u *title)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002113{
Bram Moolenaar0f873732019-12-05 20:28:46 +01002114 // XmbSetWMProperties() and Xutf8SetWMProperties() should use a STRING
2115 // when possible, COMPOUND_TEXT otherwise. COMPOUND_TEXT isn't
2116 // supported everywhere and STRING doesn't work for multi-byte titles.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002117#ifdef USE_UTF8_STRING
2118 if (enc_utf8)
2119 Xutf8SetWMProperties(x11_display, x11_window, (const char *)title,
2120 NULL, NULL, 0, NULL, NULL, NULL);
2121 else
2122#endif
2123 {
2124#if XtSpecificationRelease >= 4
2125# ifdef FEAT_XFONTSET
2126 XmbSetWMProperties(x11_display, x11_window, (const char *)title,
2127 NULL, NULL, 0, NULL, NULL, NULL);
2128# else
2129 XTextProperty text_prop;
Bram Moolenaar9d75c832005-01-25 21:57:23 +00002130 char *c_title = (char *)title;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002131
Bram Moolenaar0f873732019-12-05 20:28:46 +01002132 // directly from example 3-18 "basicwin" of Xlib Programming Manual
Bram Moolenaar9d75c832005-01-25 21:57:23 +00002133 (void)XStringListToTextProperty(&c_title, 1, &text_prop);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002134 XSetWMProperties(x11_display, x11_window, &text_prop,
2135 NULL, NULL, 0, NULL, NULL, NULL);
2136# endif
2137#else
2138 XStoreName(x11_display, x11_window, (char *)title);
2139#endif
2140 }
2141 XFlush(x11_display);
2142}
2143
2144/*
2145 * Set x11 Window icon
2146 *
2147 * get_x11_windis() must be called before this and have returned OK
2148 */
2149 static void
Bram Moolenaar05540972016-01-30 20:31:25 +01002150set_x11_icon(char_u *icon)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002151{
Bram Moolenaar0f873732019-12-05 20:28:46 +01002152 // See above for comments about using X*SetWMProperties().
Bram Moolenaar071d4272004-06-13 20:20:40 +00002153#ifdef USE_UTF8_STRING
2154 if (enc_utf8)
2155 Xutf8SetWMProperties(x11_display, x11_window, NULL, (const char *)icon,
2156 NULL, 0, NULL, NULL, NULL);
2157 else
2158#endif
2159 {
2160#if XtSpecificationRelease >= 4
2161# ifdef FEAT_XFONTSET
2162 XmbSetWMProperties(x11_display, x11_window, NULL, (const char *)icon,
2163 NULL, 0, NULL, NULL, NULL);
2164# else
2165 XTextProperty text_prop;
Bram Moolenaar9d75c832005-01-25 21:57:23 +00002166 char *c_icon = (char *)icon;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002167
Bram Moolenaar9d75c832005-01-25 21:57:23 +00002168 (void)XStringListToTextProperty(&c_icon, 1, &text_prop);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002169 XSetWMProperties(x11_display, x11_window, NULL, &text_prop,
2170 NULL, 0, NULL, NULL, NULL);
2171# endif
2172#else
2173 XSetIconName(x11_display, x11_window, (char *)icon);
2174#endif
2175 }
2176 XFlush(x11_display);
2177}
2178
Bram Moolenaar0f873732019-12-05 20:28:46 +01002179#else // FEAT_X11
Bram Moolenaar071d4272004-06-13 20:20:40 +00002180
Bram Moolenaar071d4272004-06-13 20:20:40 +00002181 static int
Bram Moolenaard14e00e2016-01-31 17:30:51 +01002182get_x11_title(int test_only UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002183{
2184 return FALSE;
2185}
2186
2187 static int
Bram Moolenaard14e00e2016-01-31 17:30:51 +01002188get_x11_icon(int test_only)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002189{
2190 if (!test_only)
2191 {
2192 if (STRNCMP(T_NAME, "builtin_", 8) == 0)
Bram Moolenaar20de1c22009-07-22 11:28:11 +00002193 oldicon = vim_strsave(T_NAME + 8);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002194 else
Bram Moolenaar20de1c22009-07-22 11:28:11 +00002195 oldicon = vim_strsave(T_NAME);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002196 }
2197 return FALSE;
2198}
2199
Bram Moolenaar0f873732019-12-05 20:28:46 +01002200#endif // FEAT_X11
Bram Moolenaar071d4272004-06-13 20:20:40 +00002201
2202 int
Bram Moolenaar05540972016-01-30 20:31:25 +01002203mch_can_restore_title(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002204{
2205 return get_x11_title(TRUE);
2206}
2207
2208 int
Bram Moolenaar05540972016-01-30 20:31:25 +01002209mch_can_restore_icon(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002210{
2211 return get_x11_icon(TRUE);
2212}
2213
2214/*
2215 * Set the window title and icon.
2216 */
2217 void
Bram Moolenaar05540972016-01-30 20:31:25 +01002218mch_settitle(char_u *title, char_u *icon)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002219{
2220 int type = 0;
2221 static int recursive = 0;
2222
Bram Moolenaar0f873732019-12-05 20:28:46 +01002223 if (T_NAME == NULL) // no terminal name (yet)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002224 return;
Bram Moolenaar0f873732019-12-05 20:28:46 +01002225 if (title == NULL && icon == NULL) // nothing to do
Bram Moolenaar071d4272004-06-13 20:20:40 +00002226 return;
2227
Bram Moolenaar0f873732019-12-05 20:28:46 +01002228 // When one of the X11 functions causes a deadly signal, we get here again
2229 // recursively. Avoid hanging then (something is probably locked).
Bram Moolenaar071d4272004-06-13 20:20:40 +00002230 if (recursive)
2231 return;
2232 ++recursive;
2233
2234 /*
2235 * if the window ID and the display is known, we may use X11 calls
2236 */
2237#ifdef FEAT_X11
2238 if (get_x11_windis() == OK)
2239 type = 1;
2240#else
Bram Moolenaar097148e2020-08-11 21:58:20 +02002241# if defined(FEAT_GUI_PHOTON) \
Bram Moolenaarb3f74062020-02-26 16:16:53 +01002242 || defined(FEAT_GUI_GTK) || defined(FEAT_GUI_HAIKU)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002243 if (gui.in_use)
2244 type = 1;
2245# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002246#endif
2247
2248 /*
Bram Moolenaarf82bac32010-07-25 22:30:20 +02002249 * Note: if "t_ts" is set, title is set with escape sequence rather
Bram Moolenaar071d4272004-06-13 20:20:40 +00002250 * than x11 calls, because the x11 calls don't always work
2251 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002252 if ((type || *T_TS != NUL) && title != NULL)
2253 {
Bram Moolenaard8f0cef2018-08-19 22:20:16 +02002254 if (oldtitle_outdated)
2255 {
2256 oldtitle_outdated = FALSE;
2257 VIM_CLEAR(oldtitle);
2258 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002259 if (oldtitle == NULL
2260#ifdef FEAT_GUI
2261 && !gui.in_use
2262#endif
Bram Moolenaar0f873732019-12-05 20:28:46 +01002263 ) // first call but not in GUI, save title
Bram Moolenaar071d4272004-06-13 20:20:40 +00002264 (void)get_x11_title(FALSE);
2265
Bram Moolenaar0f873732019-12-05 20:28:46 +01002266 if (*T_TS != NUL) // it's OK if t_fs is empty
Bram Moolenaar071d4272004-06-13 20:20:40 +00002267 term_settitle(title);
2268#ifdef FEAT_X11
2269 else
2270# ifdef FEAT_GUI_GTK
Bram Moolenaar0f873732019-12-05 20:28:46 +01002271 if (!gui.in_use) // don't do this if GTK+ is running
Bram Moolenaar071d4272004-06-13 20:20:40 +00002272# endif
Bram Moolenaar0f873732019-12-05 20:28:46 +01002273 set_x11_title(title); // x11
Bram Moolenaar071d4272004-06-13 20:20:40 +00002274#endif
Bram Moolenaarb3f74062020-02-26 16:16:53 +01002275#if defined(FEAT_GUI_GTK) || defined(FEAT_GUI_HAIKU) \
Bram Moolenaar097148e2020-08-11 21:58:20 +02002276 || defined(FEAT_GUI_PHOTON)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002277 else
2278 gui_mch_settitle(title, icon);
2279#endif
Bram Moolenaardac13472019-09-16 21:06:21 +02002280 unix_did_set_title = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002281 }
2282
2283 if ((type || *T_CIS != NUL) && icon != NULL)
2284 {
2285 if (oldicon == NULL
2286#ifdef FEAT_GUI
2287 && !gui.in_use
2288#endif
Bram Moolenaar0f873732019-12-05 20:28:46 +01002289 ) // first call, save icon
Bram Moolenaar071d4272004-06-13 20:20:40 +00002290 get_x11_icon(FALSE);
2291
2292 if (*T_CIS != NUL)
2293 {
Bram Moolenaarfaf626e2019-10-24 17:43:25 +02002294 out_str(T_CIS); // set icon start
Bram Moolenaar071d4272004-06-13 20:20:40 +00002295 out_str_nf(icon);
Bram Moolenaarfaf626e2019-10-24 17:43:25 +02002296 out_str(T_CIE); // set icon end
Bram Moolenaar071d4272004-06-13 20:20:40 +00002297 out_flush();
2298 }
2299#ifdef FEAT_X11
2300 else
2301# ifdef FEAT_GUI_GTK
Bram Moolenaar0f873732019-12-05 20:28:46 +01002302 if (!gui.in_use) // don't do this if GTK+ is running
Bram Moolenaar071d4272004-06-13 20:20:40 +00002303# endif
Bram Moolenaar0f873732019-12-05 20:28:46 +01002304 set_x11_icon(icon); // x11
Bram Moolenaar071d4272004-06-13 20:20:40 +00002305#endif
2306 did_set_icon = TRUE;
2307 }
2308 --recursive;
2309}
2310
2311/*
2312 * Restore the window/icon title.
2313 * "which" is one of:
Bram Moolenaar40385db2018-08-07 22:31:44 +02002314 * SAVE_RESTORE_TITLE only restore title
2315 * SAVE_RESTORE_ICON only restore icon
2316 * SAVE_RESTORE_BOTH restore title and icon
Bram Moolenaar071d4272004-06-13 20:20:40 +00002317 */
2318 void
Bram Moolenaar05540972016-01-30 20:31:25 +01002319mch_restore_title(int which)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002320{
Bram Moolenaardac13472019-09-16 21:06:21 +02002321 int do_push_pop = unix_did_set_title || did_set_icon;
Bram Moolenaare5c83282019-05-03 23:15:37 +02002322
Bram Moolenaar0f873732019-12-05 20:28:46 +01002323 // only restore the title or icon when it has been set
Bram Moolenaardac13472019-09-16 21:06:21 +02002324 mch_settitle(((which & SAVE_RESTORE_TITLE) && unix_did_set_title) ?
Bram Moolenaar071d4272004-06-13 20:20:40 +00002325 (oldtitle ? oldtitle : p_titleold) : NULL,
Bram Moolenaar40385db2018-08-07 22:31:44 +02002326 ((which & SAVE_RESTORE_ICON) && did_set_icon) ? oldicon : NULL);
2327
Bram Moolenaare5c83282019-05-03 23:15:37 +02002328 if (do_push_pop)
2329 {
2330 // pop and push from/to the stack
2331 term_pop_title(which);
2332 term_push_title(which);
2333 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002334}
2335
Bram Moolenaar071d4272004-06-13 20:20:40 +00002336
2337/*
2338 * Return TRUE if "name" looks like some xterm name.
Bram Moolenaar3a7c85b2005-02-05 21:39:53 +00002339 * Seiichi Sato mentioned that "mlterm" works like xterm.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002340 */
2341 int
Bram Moolenaar05540972016-01-30 20:31:25 +01002342vim_is_xterm(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002343{
2344 if (name == NULL)
2345 return FALSE;
2346 return (STRNICMP(name, "xterm", 5) == 0
2347 || STRNICMP(name, "nxterm", 6) == 0
2348 || STRNICMP(name, "kterm", 5) == 0
Bram Moolenaar3a7c85b2005-02-05 21:39:53 +00002349 || STRNICMP(name, "mlterm", 6) == 0
Bram Moolenaar071d4272004-06-13 20:20:40 +00002350 || STRNICMP(name, "rxvt", 4) == 0
Bram Moolenaar995e4af2017-09-01 20:24:03 +02002351 || STRNICMP(name, "screen.xterm", 12) == 0
Bram Moolenaar071d4272004-06-13 20:20:40 +00002352 || STRCMP(name, "builtin_xterm") == 0);
2353}
2354
Bram Moolenaar3577c6f2008-06-24 21:16:56 +00002355#if defined(FEAT_MOUSE_XTERM) || defined(PROTO)
2356/*
2357 * Return TRUE if "name" appears to be that of a terminal
2358 * known to support the xterm-style mouse protocol.
2359 * Relies on term_is_xterm having been set to its correct value.
2360 */
2361 int
Bram Moolenaar05540972016-01-30 20:31:25 +01002362use_xterm_like_mouse(char_u *name)
Bram Moolenaar3577c6f2008-06-24 21:16:56 +00002363{
2364 return (name != NULL
Bram Moolenaare56132b2016-08-14 18:23:21 +02002365 && (term_is_xterm
2366 || STRNICMP(name, "screen", 6) == 0
Bram Moolenaar0ba40702016-10-12 14:50:54 +02002367 || STRNICMP(name, "tmux", 4) == 0
Bram Moolenaar48873ae2021-12-08 21:00:24 +00002368 || STRNICMP(name, "gnome", 5) == 0
Bram Moolenaare56132b2016-08-14 18:23:21 +02002369 || STRICMP(name, "st") == 0
2370 || STRNICMP(name, "st-", 3) == 0
2371 || STRNICMP(name, "stterm", 6) == 0));
Bram Moolenaar3577c6f2008-06-24 21:16:56 +00002372}
2373#endif
2374
Bram Moolenaar071d4272004-06-13 20:20:40 +00002375/*
2376 * Return non-zero when using an xterm mouse, according to 'ttymouse'.
2377 * Return 1 for "xterm".
2378 * Return 2 for "xterm2".
Bram Moolenaarc8427482011-10-20 21:09:35 +02002379 * Return 3 for "urxvt".
Bram Moolenaar2b9578f2012-08-15 16:21:32 +02002380 * Return 4 for "sgr".
Bram Moolenaar071d4272004-06-13 20:20:40 +00002381 */
2382 int
Bram Moolenaar05540972016-01-30 20:31:25 +01002383use_xterm_mouse(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002384{
Bram Moolenaar2b9578f2012-08-15 16:21:32 +02002385 if (ttym_flags == TTYM_SGR)
2386 return 4;
Bram Moolenaarc8427482011-10-20 21:09:35 +02002387 if (ttym_flags == TTYM_URXVT)
2388 return 3;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002389 if (ttym_flags == TTYM_XTERM2)
2390 return 2;
2391 if (ttym_flags == TTYM_XTERM)
2392 return 1;
2393 return 0;
2394}
Bram Moolenaar071d4272004-06-13 20:20:40 +00002395
2396 int
Bram Moolenaar05540972016-01-30 20:31:25 +01002397vim_is_iris(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002398{
2399 if (name == NULL)
2400 return FALSE;
2401 return (STRNICMP(name, "iris-ansi", 9) == 0
2402 || STRCMP(name, "builtin_iris-ansi") == 0);
2403}
2404
2405 int
Bram Moolenaar05540972016-01-30 20:31:25 +01002406vim_is_vt300(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002407{
2408 if (name == NULL)
Bram Moolenaar0f873732019-12-05 20:28:46 +01002409 return FALSE; // actually all ANSI comp. terminals should be here
2410 // catch VT100 - VT5xx
Bram Moolenaard4755bb2004-09-02 19:12:26 +00002411 return ((STRNICMP(name, "vt", 2) == 0
2412 && vim_strchr((char_u *)"12345", name[2]) != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002413 || STRCMP(name, "builtin_vt320") == 0);
2414}
2415
2416/*
2417 * Return TRUE if "name" is a terminal for which 'ttyfast' should be set.
2418 * This should include all windowed terminal emulators.
2419 */
2420 int
Bram Moolenaar05540972016-01-30 20:31:25 +01002421vim_is_fastterm(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002422{
2423 if (name == NULL)
2424 return FALSE;
2425 if (vim_is_xterm(name) || vim_is_vt300(name) || vim_is_iris(name))
2426 return TRUE;
2427 return ( STRNICMP(name, "hpterm", 6) == 0
2428 || STRNICMP(name, "sun-cmd", 7) == 0
2429 || STRNICMP(name, "screen", 6) == 0
Bram Moolenaar0ba40702016-10-12 14:50:54 +02002430 || STRNICMP(name, "tmux", 4) == 0
Bram Moolenaar071d4272004-06-13 20:20:40 +00002431 || STRNICMP(name, "dtterm", 6) == 0);
2432}
2433
2434/*
2435 * Insert user name in s[len].
2436 * Return OK if a name found.
2437 */
2438 int
Bram Moolenaar05540972016-01-30 20:31:25 +01002439mch_get_user_name(char_u *s, int len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002440{
2441#ifdef VMS
Bram Moolenaarffb8ab02005-09-07 21:15:32 +00002442 vim_strncpy(s, (char_u *)cuserid(NULL), len - 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002443 return OK;
2444#else
2445 return mch_get_uname(getuid(), s, len);
2446#endif
2447}
2448
2449/*
2450 * Insert user name for "uid" in s[len].
2451 * Return OK if a name found.
2452 */
2453 int
Bram Moolenaar05540972016-01-30 20:31:25 +01002454mch_get_uname(uid_t uid, char_u *s, int len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002455{
2456#if defined(HAVE_PWD_H) && defined(HAVE_GETPWUID)
2457 struct passwd *pw;
2458
2459 if ((pw = getpwuid(uid)) != NULL
2460 && pw->pw_name != NULL && *(pw->pw_name) != NUL)
2461 {
Bram Moolenaarbbebc852005-07-18 21:47:53 +00002462 vim_strncpy(s, (char_u *)pw->pw_name, len - 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002463 return OK;
2464 }
2465#endif
Bram Moolenaar0f873732019-12-05 20:28:46 +01002466 sprintf((char *)s, "%d", (int)uid); // assumes s is long enough
2467 return FAIL; // a number is not a name
Bram Moolenaar071d4272004-06-13 20:20:40 +00002468}
2469
2470/*
2471 * Insert host name is s[len].
2472 */
2473
2474#ifdef HAVE_SYS_UTSNAME_H
2475 void
Bram Moolenaar05540972016-01-30 20:31:25 +01002476mch_get_host_name(char_u *s, int len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002477{
2478 struct utsname vutsname;
2479
2480 if (uname(&vutsname) < 0)
2481 *s = NUL;
2482 else
Bram Moolenaarbbebc852005-07-18 21:47:53 +00002483 vim_strncpy(s, (char_u *)vutsname.nodename, len - 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002484}
Bram Moolenaar0f873732019-12-05 20:28:46 +01002485#else // HAVE_SYS_UTSNAME_H
Bram Moolenaar071d4272004-06-13 20:20:40 +00002486
2487# ifdef HAVE_SYS_SYSTEMINFO_H
2488# define gethostname(nam, len) sysinfo(SI_HOSTNAME, nam, len)
2489# endif
2490
2491 void
Bram Moolenaard14e00e2016-01-31 17:30:51 +01002492mch_get_host_name(char_u *s, int len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002493{
2494# ifdef VAXC
2495 vaxc$gethostname((char *)s, len);
2496# else
2497 gethostname((char *)s, len);
2498# endif
Bram Moolenaar0f873732019-12-05 20:28:46 +01002499 s[len - 1] = NUL; // make sure it's terminated
Bram Moolenaar071d4272004-06-13 20:20:40 +00002500}
Bram Moolenaar0f873732019-12-05 20:28:46 +01002501#endif // HAVE_SYS_UTSNAME_H
Bram Moolenaar071d4272004-06-13 20:20:40 +00002502
2503/*
2504 * return process ID
2505 */
2506 long
Bram Moolenaar05540972016-01-30 20:31:25 +01002507mch_get_pid(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002508{
2509 return (long)getpid();
2510}
2511
Bram Moolenaar67cf86b2019-04-28 22:25:38 +02002512/*
2513 * return TRUE if process "pid" is still running
2514 */
2515 int
Bram Moolenaar1b243ea2019-04-28 22:50:40 +02002516mch_process_running(long pid)
Bram Moolenaar67cf86b2019-04-28 22:25:38 +02002517{
Bram Moolenaar44dea9d2021-06-23 21:13:20 +02002518 // If there is no error the process must be running.
2519 if (kill(pid, 0) == 0)
2520 return TRUE;
2521#ifdef ESRCH
2522 // If the error is ESRCH then the process is not running.
2523 if (errno == ESRCH)
2524 return FALSE;
2525#endif
2526 // If the process is running and owned by another user we get EPERM. With
2527 // other errors the process might be running, assuming it is then.
2528 return TRUE;
Bram Moolenaar67cf86b2019-04-28 22:25:38 +02002529}
2530
Bram Moolenaar071d4272004-06-13 20:20:40 +00002531#if !defined(HAVE_STRERROR) && defined(USE_GETCWD)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002532 static char *
Bram Moolenaar05540972016-01-30 20:31:25 +01002533strerror(int err)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002534{
2535 extern int sys_nerr;
2536 extern char *sys_errlist[];
2537 static char er[20];
2538
2539 if (err > 0 && err < sys_nerr)
2540 return (sys_errlist[err]);
2541 sprintf(er, "Error %d", err);
2542 return er;
2543}
2544#endif
2545
2546/*
Bram Moolenaar964b3742019-05-24 18:54:09 +02002547 * Get name of current directory into buffer "buf" of length "len" bytes.
2548 * "len" must be at least PATH_MAX.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002549 * Return OK for success, FAIL for failure.
2550 */
2551 int
Bram Moolenaar05540972016-01-30 20:31:25 +01002552mch_dirname(char_u *buf, int len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002553{
2554#if defined(USE_GETCWD)
2555 if (getcwd((char *)buf, len) == NULL)
2556 {
2557 STRCPY(buf, strerror(errno));
2558 return FAIL;
2559 }
2560 return OK;
2561#else
2562 return (getwd((char *)buf) != NULL ? OK : FAIL);
2563#endif
2564}
2565
Bram Moolenaar071d4272004-06-13 20:20:40 +00002566/*
Bram Moolenaar3d20ca12006-11-28 16:43:58 +00002567 * Get absolute file name into "buf[len]".
Bram Moolenaar071d4272004-06-13 20:20:40 +00002568 *
2569 * return FAIL for failure, OK for success
2570 */
2571 int
Bram Moolenaar05540972016-01-30 20:31:25 +01002572mch_FullName(
2573 char_u *fname,
2574 char_u *buf,
2575 int len,
Bram Moolenaar0f873732019-12-05 20:28:46 +01002576 int force) // also expand when already absolute path
Bram Moolenaar071d4272004-06-13 20:20:40 +00002577{
2578 int l;
Bram Moolenaar38323e42007-03-06 19:22:53 +00002579#ifdef HAVE_FCHDIR
Bram Moolenaar071d4272004-06-13 20:20:40 +00002580 int fd = -1;
Bram Moolenaar0f873732019-12-05 20:28:46 +01002581 static int dont_fchdir = FALSE; // TRUE when fchdir() doesn't work
Bram Moolenaar38323e42007-03-06 19:22:53 +00002582#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002583 char_u olddir[MAXPATHL];
2584 char_u *p;
2585 int retval = OK;
Bram Moolenaarbf820722008-06-21 11:12:49 +00002586#ifdef __CYGWIN__
Bram Moolenaar0f873732019-12-05 20:28:46 +01002587 char_u posix_fname[MAXPATHL]; // Cygwin docs mention MAX_PATH, but
2588 // it's not always defined
Bram Moolenaarbf820722008-06-21 11:12:49 +00002589#endif
2590
Bram Moolenaar38323e42007-03-06 19:22:53 +00002591#ifdef VMS
2592 fname = vms_fixfilename(fname);
2593#endif
2594
Bram Moolenaara2442432007-04-26 14:26:37 +00002595#ifdef __CYGWIN__
2596 /*
2597 * This helps for when "/etc/hosts" is a symlink to "c:/something/hosts".
2598 */
Bram Moolenaar0d1498e2008-06-29 12:00:49 +00002599# if CYGWIN_VERSION_DLL_MAJOR >= 1007
Bram Moolenaar0f873732019-12-05 20:28:46 +01002600 // Use CCP_RELATIVE to avoid that it sometimes returns a path that ends in
2601 // a forward slash.
Bram Moolenaar06b07342015-12-31 22:26:28 +01002602 cygwin_conv_path(CCP_WIN_A_TO_POSIX | CCP_RELATIVE,
2603 fname, posix_fname, MAXPATHL);
Bram Moolenaar0d1498e2008-06-29 12:00:49 +00002604# else
Bram Moolenaarbf820722008-06-21 11:12:49 +00002605 cygwin_conv_to_posix_path(fname, posix_fname);
Bram Moolenaar0d1498e2008-06-29 12:00:49 +00002606# endif
Bram Moolenaarbf820722008-06-21 11:12:49 +00002607 fname = posix_fname;
Bram Moolenaara2442432007-04-26 14:26:37 +00002608#endif
2609
Bram Moolenaar0f873732019-12-05 20:28:46 +01002610 // Expand it if forced or not an absolute path.
2611 // Do not do it for "/file", the result is always "/".
Bram Moolenaare3303cb2015-12-31 18:29:46 +01002612 if ((force || !mch_isFullName(fname))
2613 && ((p = vim_strrchr(fname, '/')) == NULL || p != fname))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002614 {
2615 /*
2616 * If the file name has a path, change to that directory for a moment,
Bram Moolenaar964b3742019-05-24 18:54:09 +02002617 * and then get the directory (and get back to where we were).
Bram Moolenaar071d4272004-06-13 20:20:40 +00002618 * This will get the correct path name with "../" things.
2619 */
Bram Moolenaare3303cb2015-12-31 18:29:46 +01002620 if (p != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002621 {
Bram Moolenaar4eaef992021-08-30 21:26:16 +02002622 if (STRCMP(p, "/..") == 0)
2623 // for "/path/dir/.." include the "/.."
2624 p += 3;
2625
Bram Moolenaar38323e42007-03-06 19:22:53 +00002626#ifdef HAVE_FCHDIR
Bram Moolenaar071d4272004-06-13 20:20:40 +00002627 /*
2628 * Use fchdir() if possible, it's said to be faster and more
2629 * reliable. But on SunOS 4 it might not work. Check this by
2630 * doing a fchdir() right now.
2631 */
2632 if (!dont_fchdir)
2633 {
2634 fd = open(".", O_RDONLY | O_EXTRA, 0);
2635 if (fd >= 0 && fchdir(fd) < 0)
2636 {
2637 close(fd);
2638 fd = -1;
Bram Moolenaar0f873732019-12-05 20:28:46 +01002639 dont_fchdir = TRUE; // don't try again
Bram Moolenaar071d4272004-06-13 20:20:40 +00002640 }
2641 }
Bram Moolenaar38323e42007-03-06 19:22:53 +00002642#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002643
Bram Moolenaar0f873732019-12-05 20:28:46 +01002644 // Only change directory when we are sure we can return to where
2645 // we are now. After doing "su" chdir(".") might not work.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002646 if (
Bram Moolenaar38323e42007-03-06 19:22:53 +00002647#ifdef HAVE_FCHDIR
Bram Moolenaar071d4272004-06-13 20:20:40 +00002648 fd < 0 &&
Bram Moolenaar38323e42007-03-06 19:22:53 +00002649#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002650 (mch_dirname(olddir, MAXPATHL) == FAIL
2651 || mch_chdir((char *)olddir) != 0))
2652 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01002653 p = NULL; // can't get current dir: don't chdir
Bram Moolenaar071d4272004-06-13 20:20:40 +00002654 retval = FAIL;
2655 }
2656 else
2657 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01002658 // The directory is copied into buf[], to be able to remove
2659 // the file name without changing it (could be a string in
2660 // read-only memory)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002661 if (p - fname >= len)
2662 retval = FAIL;
2663 else
2664 {
Bram Moolenaarbbebc852005-07-18 21:47:53 +00002665 vim_strncpy(buf, fname, p - fname);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002666 if (mch_chdir((char *)buf))
Bram Moolenaarc6376c72021-10-03 19:29:48 +01002667 {
2668 // Path does not exist (yet). For a full path fail,
2669 // will use the path as-is. For a relative path use
2670 // the current directory and append the file name.
2671 if (mch_isFullName(fname))
2672 retval = FAIL;
2673 else
2674 p = NULL;
2675 }
Bram Moolenaar4eaef992021-08-30 21:26:16 +02002676 else if (*p == '/')
Bram Moolenaar071d4272004-06-13 20:20:40 +00002677 fname = p + 1;
Bram Moolenaar4eaef992021-08-30 21:26:16 +02002678 else
2679 fname = p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002680 *buf = NUL;
2681 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002682 }
2683 }
2684 if (mch_dirname(buf, len) == FAIL)
2685 {
2686 retval = FAIL;
2687 *buf = NUL;
2688 }
2689 if (p != NULL)
2690 {
Bram Moolenaar38323e42007-03-06 19:22:53 +00002691#ifdef HAVE_FCHDIR
Bram Moolenaar071d4272004-06-13 20:20:40 +00002692 if (fd >= 0)
2693 {
Bram Moolenaar25724922009-07-14 15:38:41 +00002694 if (p_verbose >= 5)
2695 {
2696 verbose_enter();
Bram Moolenaar32526b32019-01-19 17:43:09 +01002697 msg("fchdir() to previous dir");
Bram Moolenaar25724922009-07-14 15:38:41 +00002698 verbose_leave();
2699 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002700 l = fchdir(fd);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002701 }
2702 else
Bram Moolenaar38323e42007-03-06 19:22:53 +00002703#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002704 l = mch_chdir((char *)olddir);
2705 if (l != 0)
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00002706 emsg(_(e_cannot_go_back_to_previous_directory));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002707 }
itchyny051a40c2021-10-20 10:00:05 +01002708#ifdef HAVE_FCHDIR
2709 if (fd >= 0)
2710 close(fd);
2711#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002712
2713 l = STRLEN(buf);
Bram Moolenaardac75692012-10-14 04:35:45 +02002714 if (l >= len - 1)
Bram Moolenaar0f873732019-12-05 20:28:46 +01002715 retval = FAIL; // no space for trailing "/"
Bram Moolenaar38323e42007-03-06 19:22:53 +00002716#ifndef VMS
Bram Moolenaardac75692012-10-14 04:35:45 +02002717 else if (l > 0 && buf[l - 1] != '/' && *fname != NUL
Bram Moolenaar071d4272004-06-13 20:20:40 +00002718 && STRCMP(fname, ".") != 0)
Bram Moolenaardac75692012-10-14 04:35:45 +02002719 STRCAT(buf, "/");
Bram Moolenaar38323e42007-03-06 19:22:53 +00002720#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002721 }
Bram Moolenaar3d20ca12006-11-28 16:43:58 +00002722
Bram Moolenaar0f873732019-12-05 20:28:46 +01002723 // Catch file names which are too long.
Bram Moolenaar78a15312009-05-15 19:33:18 +00002724 if (retval == FAIL || (int)(STRLEN(buf) + STRLEN(fname)) >= len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002725 return FAIL;
2726
Bram Moolenaar0f873732019-12-05 20:28:46 +01002727 // Do not append ".", "/dir/." is equal to "/dir".
Bram Moolenaar071d4272004-06-13 20:20:40 +00002728 if (STRCMP(fname, ".") != 0)
2729 STRCAT(buf, fname);
2730
2731 return OK;
2732}
2733
2734/*
2735 * Return TRUE if "fname" does not depend on the current directory.
2736 */
2737 int
Bram Moolenaar05540972016-01-30 20:31:25 +01002738mch_isFullName(char_u *fname)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002739{
Bram Moolenaara06ecab2016-07-16 14:47:36 +02002740#ifdef VMS
Bram Moolenaar071d4272004-06-13 20:20:40 +00002741 return ( fname[0] == '/' || fname[0] == '.' ||
2742 strchr((char *)fname,':') || strchr((char *)fname,'"') ||
2743 (strchr((char *)fname,'[') && strchr((char *)fname,']'))||
2744 (strchr((char *)fname,'<') && strchr((char *)fname,'>')) );
Bram Moolenaara06ecab2016-07-16 14:47:36 +02002745#else
Bram Moolenaar071d4272004-06-13 20:20:40 +00002746 return (*fname == '/' || *fname == '~');
Bram Moolenaar071d4272004-06-13 20:20:40 +00002747#endif
2748}
2749
Bram Moolenaar24552be2005-12-10 20:17:30 +00002750#if defined(USE_FNAME_CASE) || defined(PROTO)
2751/*
2752 * Set the case of the file name, if it already exists. This will cause the
2753 * file name to remain exactly the same.
Bram Moolenaarc2a27c32007-12-01 16:19:33 +00002754 * Only required for file systems where case is ignored and preserved.
Bram Moolenaar24552be2005-12-10 20:17:30 +00002755 */
Bram Moolenaar24552be2005-12-10 20:17:30 +00002756 void
Bram Moolenaar05540972016-01-30 20:31:25 +01002757fname_case(
2758 char_u *name,
Bram Moolenaar0f873732019-12-05 20:28:46 +01002759 int len UNUSED) // buffer size, only used when name gets longer
Bram Moolenaar24552be2005-12-10 20:17:30 +00002760{
2761 struct stat st;
2762 char_u *slash, *tail;
2763 DIR *dirp;
2764 struct dirent *dp;
2765
Bram Moolenaarde5e2c22016-11-04 20:35:31 +01002766 if (mch_lstat((char *)name, &st) >= 0)
Bram Moolenaar24552be2005-12-10 20:17:30 +00002767 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01002768 // Open the directory where the file is located.
Bram Moolenaar24552be2005-12-10 20:17:30 +00002769 slash = vim_strrchr(name, '/');
2770 if (slash == NULL)
2771 {
2772 dirp = opendir(".");
2773 tail = name;
2774 }
2775 else
2776 {
2777 *slash = NUL;
2778 dirp = opendir((char *)name);
2779 *slash = '/';
2780 tail = slash + 1;
2781 }
2782
2783 if (dirp != NULL)
2784 {
2785 while ((dp = readdir(dirp)) != NULL)
2786 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01002787 // Only accept names that differ in case and are the same byte
2788 // length. TODO: accept different length name.
Bram Moolenaar24552be2005-12-10 20:17:30 +00002789 if (STRICMP(tail, dp->d_name) == 0
2790 && STRLEN(tail) == STRLEN(dp->d_name))
2791 {
2792 char_u newname[MAXPATHL + 1];
2793 struct stat st2;
2794
Bram Moolenaar0f873732019-12-05 20:28:46 +01002795 // Verify the inode is equal.
Bram Moolenaar24552be2005-12-10 20:17:30 +00002796 vim_strncpy(newname, name, MAXPATHL);
2797 vim_strncpy(newname + (tail - name), (char_u *)dp->d_name,
2798 MAXPATHL - (tail - name));
Bram Moolenaarde5e2c22016-11-04 20:35:31 +01002799 if (mch_lstat((char *)newname, &st2) >= 0
Bram Moolenaar24552be2005-12-10 20:17:30 +00002800 && st.st_ino == st2.st_ino
2801 && st.st_dev == st2.st_dev)
2802 {
2803 STRCPY(tail, dp->d_name);
2804 break;
2805 }
2806 }
2807 }
2808
2809 closedir(dirp);
2810 }
2811 }
2812}
2813#endif
2814
Bram Moolenaar071d4272004-06-13 20:20:40 +00002815/*
2816 * Get file permissions for 'name'.
2817 * Returns -1 when it doesn't exist.
2818 */
2819 long
Bram Moolenaar05540972016-01-30 20:31:25 +01002820mch_getperm(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002821{
2822 struct stat statb;
2823
Bram Moolenaar0f873732019-12-05 20:28:46 +01002824 // Keep the #ifdef outside of stat(), it may be a macro.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002825#ifdef VMS
2826 if (stat((char *)vms_fixfilename(name), &statb))
2827#else
2828 if (stat((char *)name, &statb))
2829#endif
2830 return -1;
Bram Moolenaar708f62c2007-08-11 20:24:10 +00002831#ifdef __INTERIX
Bram Moolenaar0f873732019-12-05 20:28:46 +01002832 // The top bit makes the value negative, which means the file doesn't
2833 // exist. Remove the bit, we don't use it.
Bram Moolenaar708f62c2007-08-11 20:24:10 +00002834 return statb.st_mode & ~S_ADDACE;
2835#else
Bram Moolenaar071d4272004-06-13 20:20:40 +00002836 return statb.st_mode;
Bram Moolenaar708f62c2007-08-11 20:24:10 +00002837#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002838}
2839
2840/*
Bram Moolenaarcd142e32017-11-16 17:03:45 +01002841 * Set file permission for "name" to "perm".
2842 * Return FAIL for failure, OK otherwise.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002843 */
2844 int
Bram Moolenaar05540972016-01-30 20:31:25 +01002845mch_setperm(char_u *name, long perm)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002846{
2847 return (chmod((char *)
2848#ifdef VMS
2849 vms_fixfilename(name),
2850#else
2851 name,
2852#endif
2853 (mode_t)perm) == 0 ? OK : FAIL);
2854}
2855
Bram Moolenaarcd142e32017-11-16 17:03:45 +01002856#if defined(HAVE_FCHMOD) || defined(PROTO)
2857/*
2858 * Set file permission for open file "fd" to "perm".
2859 * Return FAIL for failure, OK otherwise.
2860 */
2861 int
2862mch_fsetperm(int fd, long perm)
2863{
2864 return (fchmod(fd, (mode_t)perm) == 0 ? OK : FAIL);
2865}
2866#endif
2867
Bram Moolenaar071d4272004-06-13 20:20:40 +00002868#if defined(HAVE_ACL) || defined(PROTO)
2869# ifdef HAVE_SYS_ACL_H
2870# include <sys/acl.h>
2871# endif
2872# ifdef HAVE_SYS_ACCESS_H
2873# include <sys/access.h>
2874# endif
2875
2876# ifdef HAVE_SOLARIS_ACL
2877typedef struct vim_acl_solaris_T {
2878 int acl_cnt;
2879 aclent_t *acl_entry;
2880} vim_acl_solaris_T;
2881# endif
2882
Bram Moolenaar588ebeb2008-05-07 17:09:24 +00002883#if defined(HAVE_SELINUX) || defined(PROTO)
2884/*
2885 * Copy security info from "from_file" to "to_file".
2886 */
2887 void
Bram Moolenaar05540972016-01-30 20:31:25 +01002888mch_copy_sec(char_u *from_file, char_u *to_file)
Bram Moolenaar588ebeb2008-05-07 17:09:24 +00002889{
2890 if (from_file == NULL)
2891 return;
2892
2893 if (selinux_enabled == -1)
2894 selinux_enabled = is_selinux_enabled();
2895
2896 if (selinux_enabled > 0)
2897 {
Bram Moolenaar89560232020-10-09 23:04:47 +02002898 // Use "char *" instead of "security_context_t" to avoid a deprecation
2899 // warning.
2900 char *from_context = NULL;
2901 char *to_context = NULL;
Bram Moolenaar588ebeb2008-05-07 17:09:24 +00002902
2903 if (getfilecon((char *)from_file, &from_context) < 0)
2904 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01002905 // If the filesystem doesn't support extended attributes,
2906 // the original had no special security context and the
2907 // target cannot have one either.
Bram Moolenaar588ebeb2008-05-07 17:09:24 +00002908 if (errno == EOPNOTSUPP)
2909 return;
2910
Bram Moolenaar32526b32019-01-19 17:43:09 +01002911 msg_puts(_("\nCould not get security context for "));
Bram Moolenaar588ebeb2008-05-07 17:09:24 +00002912 msg_outtrans(from_file);
2913 msg_putchar('\n');
2914 return;
2915 }
2916 if (getfilecon((char *)to_file, &to_context) < 0)
2917 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01002918 msg_puts(_("\nCould not get security context for "));
Bram Moolenaar588ebeb2008-05-07 17:09:24 +00002919 msg_outtrans(to_file);
2920 msg_putchar('\n');
2921 freecon (from_context);
2922 return ;
2923 }
2924 if (strcmp(from_context, to_context) != 0)
2925 {
2926 if (setfilecon((char *)to_file, from_context) < 0)
2927 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01002928 msg_puts(_("\nCould not set security context for "));
Bram Moolenaar588ebeb2008-05-07 17:09:24 +00002929 msg_outtrans(to_file);
2930 msg_putchar('\n');
2931 }
2932 }
2933 freecon(to_context);
2934 freecon(from_context);
2935 }
2936}
Bram Moolenaar0f873732019-12-05 20:28:46 +01002937#endif // HAVE_SELINUX
Bram Moolenaar588ebeb2008-05-07 17:09:24 +00002938
Bram Moolenaar5bd32f42014-04-02 14:05:38 +02002939#if defined(HAVE_SMACK) && !defined(PROTO)
2940/*
2941 * Copy security info from "from_file" to "to_file".
2942 */
2943 void
Bram Moolenaard14e00e2016-01-31 17:30:51 +01002944mch_copy_sec(char_u *from_file, char_u *to_file)
Bram Moolenaar5bd32f42014-04-02 14:05:38 +02002945{
Bram Moolenaar62f167f2014-04-23 12:52:40 +02002946 static const char * const smack_copied_attributes[] =
Bram Moolenaar5bd32f42014-04-02 14:05:38 +02002947 {
2948 XATTR_NAME_SMACK,
2949 XATTR_NAME_SMACKEXEC,
2950 XATTR_NAME_SMACKMMAP
2951 };
2952
2953 char buffer[SMACK_LABEL_LEN];
2954 const char *name;
2955 int index;
2956 int ret;
2957 ssize_t size;
2958
2959 if (from_file == NULL)
2960 return;
2961
2962 for (index = 0 ; index < (int)(sizeof(smack_copied_attributes)
2963 / sizeof(smack_copied_attributes)[0]) ; index++)
2964 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01002965 // get the name of the attribute to copy
Bram Moolenaar5bd32f42014-04-02 14:05:38 +02002966 name = smack_copied_attributes[index];
2967
Bram Moolenaar0f873732019-12-05 20:28:46 +01002968 // get the value of the attribute in buffer
Bram Moolenaar5bd32f42014-04-02 14:05:38 +02002969 size = getxattr((char*)from_file, name, buffer, sizeof(buffer));
2970 if (size >= 0)
2971 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01002972 // copy the attribute value of buffer
Bram Moolenaar5bd32f42014-04-02 14:05:38 +02002973 ret = setxattr((char*)to_file, name, buffer, (size_t)size, 0);
2974 if (ret < 0)
2975 {
Bram Moolenaar4a1314c2016-01-27 20:47:18 +01002976 vim_snprintf((char *)IObuff, IOSIZE,
2977 _("Could not set security context %s for %s"),
2978 name, to_file);
2979 msg_outtrans(IObuff);
Bram Moolenaar5bd32f42014-04-02 14:05:38 +02002980 msg_putchar('\n');
2981 }
2982 }
2983 else
2984 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01002985 // what reason of not having the attribute value?
Bram Moolenaar5bd32f42014-04-02 14:05:38 +02002986 switch (errno)
2987 {
2988 case ENOTSUP:
Bram Moolenaar0f873732019-12-05 20:28:46 +01002989 // extended attributes aren't supported or enabled
2990 // should a message be echoed? not sure...
2991 return; // leave because it isn't useful to continue
Bram Moolenaar5bd32f42014-04-02 14:05:38 +02002992
2993 case ERANGE:
2994 default:
Bram Moolenaar0f873732019-12-05 20:28:46 +01002995 // no enough size OR unexpected error
Bram Moolenaar4a1314c2016-01-27 20:47:18 +01002996 vim_snprintf((char *)IObuff, IOSIZE,
2997 _("Could not get security context %s for %s. Removing it!"),
2998 name, from_file);
Bram Moolenaar32526b32019-01-19 17:43:09 +01002999 msg_puts((char *)IObuff);
Bram Moolenaar4a1314c2016-01-27 20:47:18 +01003000 msg_putchar('\n');
Bram Moolenaar0f873732019-12-05 20:28:46 +01003001 // FALLTHROUGH to remove the attribute
Bram Moolenaar5bd32f42014-04-02 14:05:38 +02003002
3003 case ENODATA:
Bram Moolenaar0f873732019-12-05 20:28:46 +01003004 // no attribute of this name
Bram Moolenaar5bd32f42014-04-02 14:05:38 +02003005 ret = removexattr((char*)to_file, name);
Bram Moolenaar0f873732019-12-05 20:28:46 +01003006 // Silently ignore errors, apparently this happens when
3007 // smack is not actually being used.
Bram Moolenaar5bd32f42014-04-02 14:05:38 +02003008 break;
3009 }
3010 }
3011 }
3012}
Bram Moolenaar0f873732019-12-05 20:28:46 +01003013#endif // HAVE_SMACK
Bram Moolenaar5bd32f42014-04-02 14:05:38 +02003014
Bram Moolenaar071d4272004-06-13 20:20:40 +00003015/*
3016 * Return a pointer to the ACL of file "fname" in allocated memory.
3017 * Return NULL if the ACL is not available for whatever reason.
3018 */
3019 vim_acl_T
Bram Moolenaar05540972016-01-30 20:31:25 +01003020mch_get_acl(char_u *fname UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003021{
3022 vim_acl_T ret = NULL;
3023#ifdef HAVE_POSIX_ACL
3024 ret = (vim_acl_T)acl_get_file((char *)fname, ACL_TYPE_ACCESS);
3025#else
Bram Moolenaar8d462f92012-02-05 22:51:33 +01003026#ifdef HAVE_SOLARIS_ZFS_ACL
3027 acl_t *aclent;
3028
3029 if (acl_get((char *)fname, 0, &aclent) < 0)
3030 return NULL;
3031 ret = (vim_acl_T)aclent;
3032#else
Bram Moolenaar071d4272004-06-13 20:20:40 +00003033#ifdef HAVE_SOLARIS_ACL
3034 vim_acl_solaris_T *aclent;
3035
3036 aclent = malloc(sizeof(vim_acl_solaris_T));
3037 if ((aclent->acl_cnt = acl((char *)fname, GETACLCNT, 0, NULL)) < 0)
3038 {
3039 free(aclent);
3040 return NULL;
3041 }
3042 aclent->acl_entry = malloc(aclent->acl_cnt * sizeof(aclent_t));
3043 if (acl((char *)fname, GETACL, aclent->acl_cnt, aclent->acl_entry) < 0)
3044 {
3045 free(aclent->acl_entry);
3046 free(aclent);
3047 return NULL;
3048 }
3049 ret = (vim_acl_T)aclent;
3050#else
3051#if defined(HAVE_AIX_ACL)
3052 int aclsize;
3053 struct acl *aclent;
3054
3055 aclsize = sizeof(struct acl);
3056 aclent = malloc(aclsize);
3057 if (statacl((char *)fname, STX_NORMAL, aclent, aclsize) < 0)
3058 {
3059 if (errno == ENOSPC)
3060 {
3061 aclsize = aclent->acl_len;
3062 aclent = realloc(aclent, aclsize);
3063 if (statacl((char *)fname, STX_NORMAL, aclent, aclsize) < 0)
3064 {
3065 free(aclent);
3066 return NULL;
3067 }
3068 }
3069 else
3070 {
3071 free(aclent);
3072 return NULL;
3073 }
3074 }
3075 ret = (vim_acl_T)aclent;
Bram Moolenaar0f873732019-12-05 20:28:46 +01003076#endif // HAVE_AIX_ACL
3077#endif // HAVE_SOLARIS_ACL
3078#endif // HAVE_SOLARIS_ZFS_ACL
3079#endif // HAVE_POSIX_ACL
Bram Moolenaar071d4272004-06-13 20:20:40 +00003080 return ret;
3081}
3082
3083/*
3084 * Set the ACL of file "fname" to "acl" (unless it's NULL).
3085 */
3086 void
Bram Moolenaar05540972016-01-30 20:31:25 +01003087mch_set_acl(char_u *fname UNUSED, vim_acl_T aclent)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003088{
3089 if (aclent == NULL)
3090 return;
3091#ifdef HAVE_POSIX_ACL
3092 acl_set_file((char *)fname, ACL_TYPE_ACCESS, (acl_t)aclent);
3093#else
Bram Moolenaar8d462f92012-02-05 22:51:33 +01003094#ifdef HAVE_SOLARIS_ZFS_ACL
3095 acl_set((char *)fname, (acl_t *)aclent);
3096#else
Bram Moolenaar071d4272004-06-13 20:20:40 +00003097#ifdef HAVE_SOLARIS_ACL
3098 acl((char *)fname, SETACL, ((vim_acl_solaris_T *)aclent)->acl_cnt,
3099 ((vim_acl_solaris_T *)aclent)->acl_entry);
3100#else
3101#ifdef HAVE_AIX_ACL
3102 chacl((char *)fname, aclent, ((struct acl *)aclent)->acl_len);
Bram Moolenaar0f873732019-12-05 20:28:46 +01003103#endif // HAVE_AIX_ACL
3104#endif // HAVE_SOLARIS_ACL
3105#endif // HAVE_SOLARIS_ZFS_ACL
3106#endif // HAVE_POSIX_ACL
Bram Moolenaar071d4272004-06-13 20:20:40 +00003107}
3108
3109 void
Bram Moolenaar05540972016-01-30 20:31:25 +01003110mch_free_acl(vim_acl_T aclent)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003111{
3112 if (aclent == NULL)
3113 return;
3114#ifdef HAVE_POSIX_ACL
3115 acl_free((acl_t)aclent);
3116#else
Bram Moolenaar8d462f92012-02-05 22:51:33 +01003117#ifdef HAVE_SOLARIS_ZFS_ACL
3118 acl_free((acl_t *)aclent);
3119#else
Bram Moolenaar071d4272004-06-13 20:20:40 +00003120#ifdef HAVE_SOLARIS_ACL
3121 free(((vim_acl_solaris_T *)aclent)->acl_entry);
3122 free(aclent);
3123#else
3124#ifdef HAVE_AIX_ACL
3125 free(aclent);
Bram Moolenaar0f873732019-12-05 20:28:46 +01003126#endif // HAVE_AIX_ACL
3127#endif // HAVE_SOLARIS_ACL
3128#endif // HAVE_SOLARIS_ZFS_ACL
3129#endif // HAVE_POSIX_ACL
Bram Moolenaar071d4272004-06-13 20:20:40 +00003130}
3131#endif
3132
3133/*
3134 * Set hidden flag for "name".
3135 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003136 void
Bram Moolenaar05540972016-01-30 20:31:25 +01003137mch_hide(char_u *name UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003138{
Bram Moolenaar0f873732019-12-05 20:28:46 +01003139 // can't hide a file
Bram Moolenaar071d4272004-06-13 20:20:40 +00003140}
3141
3142/*
Bram Moolenaar43a34f92016-01-17 15:56:34 +01003143 * return TRUE if "name" is a directory or a symlink to a directory
Bram Moolenaar071d4272004-06-13 20:20:40 +00003144 * return FALSE if "name" is not a directory
3145 * return FALSE for error
3146 */
3147 int
Bram Moolenaar05540972016-01-30 20:31:25 +01003148mch_isdir(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003149{
3150 struct stat statb;
3151
Bram Moolenaar0f873732019-12-05 20:28:46 +01003152 if (*name == NUL) // Some stat()s don't flag "" as an error.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003153 return FALSE;
3154 if (stat((char *)name, &statb))
3155 return FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003156 return (S_ISDIR(statb.st_mode) ? TRUE : FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003157}
3158
Bram Moolenaar43a34f92016-01-17 15:56:34 +01003159/*
3160 * return TRUE if "name" is a directory, NOT a symlink to a directory
3161 * return FALSE if "name" is not a directory
3162 * return FALSE for error
3163 */
3164 int
Bram Moolenaar05540972016-01-30 20:31:25 +01003165mch_isrealdir(char_u *name)
Bram Moolenaar43a34f92016-01-17 15:56:34 +01003166{
3167 struct stat statb;
3168
Bram Moolenaar0f873732019-12-05 20:28:46 +01003169 if (*name == NUL) // Some stat()s don't flag "" as an error.
Bram Moolenaar43a34f92016-01-17 15:56:34 +01003170 return FALSE;
Bram Moolenaarde5e2c22016-11-04 20:35:31 +01003171 if (mch_lstat((char *)name, &statb))
Bram Moolenaar43a34f92016-01-17 15:56:34 +01003172 return FALSE;
Bram Moolenaar43a34f92016-01-17 15:56:34 +01003173 return (S_ISDIR(statb.st_mode) ? TRUE : FALSE);
Bram Moolenaar43a34f92016-01-17 15:56:34 +01003174}
3175
Bram Moolenaar071d4272004-06-13 20:20:40 +00003176/*
3177 * Return 1 if "name" is an executable file, 0 if not or it doesn't exist.
3178 */
3179 static int
Bram Moolenaar05540972016-01-30 20:31:25 +01003180executable_file(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003181{
3182 struct stat st;
3183
3184 if (stat((char *)name, &st))
3185 return 0;
Bram Moolenaar206f0112014-03-12 16:51:55 +01003186#ifdef VMS
Bram Moolenaar0f873732019-12-05 20:28:46 +01003187 // Like on Unix system file can have executable rights but not necessarily
3188 // be an executable, but on Unix is not a default for an ordinary file to
3189 // have an executable flag - on VMS it is in most cases.
3190 // Therefore, this check does not have any sense - let keep us to the
3191 // conventions instead:
3192 // *.COM and *.EXE files are the executables - the rest are not. This is
Dominique Pelleaf4a61a2021-12-27 17:21:41 +00003193 // not ideal but better than it was.
Bram Moolenaar206f0112014-03-12 16:51:55 +01003194 int vms_executable = 0;
3195 if (S_ISREG(st.st_mode) && mch_access((char *)name, X_OK) == 0)
3196 {
3197 if (strstr(vms_tolower((char*)name),".exe") != NULL
3198 || strstr(vms_tolower((char*)name),".com")!= NULL)
3199 vms_executable = 1;
3200 }
3201 return vms_executable;
3202#else
Bram Moolenaar071d4272004-06-13 20:20:40 +00003203 return S_ISREG(st.st_mode) && mch_access((char *)name, X_OK) == 0;
Bram Moolenaar206f0112014-03-12 16:51:55 +01003204#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003205}
3206
3207/*
Bram Moolenaar2fcf6682017-03-11 20:03:42 +01003208 * Return TRUE if "name" can be found in $PATH and executed, FALSE if not.
Bram Moolenaarb5971142015-03-21 17:32:19 +01003209 * If "use_path" is FALSE only check if "name" is executable.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003210 * Return -1 if unknown.
3211 */
3212 int
Bram Moolenaar05540972016-01-30 20:31:25 +01003213mch_can_exe(char_u *name, char_u **path, int use_path)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003214{
3215 char_u *buf;
3216 char_u *p, *e;
3217 int retval;
3218
Bram Moolenaar0f873732019-12-05 20:28:46 +01003219 // When "use_path" is false and if it's an absolute or relative path don't
3220 // need to use $PATH.
Bram Moolenaard08b8c42019-07-24 14:59:45 +02003221 if (!use_path || gettail(name) != name)
Bram Moolenaar206f0112014-03-12 16:51:55 +01003222 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01003223 // There must be a path separator, files in the current directory
3224 // can't be executed.
Bram Moolenaard08b8c42019-07-24 14:59:45 +02003225 if ((use_path || gettail(name) != name) && executable_file(name))
Bram Moolenaarc7f02552014-04-01 21:00:59 +02003226 {
3227 if (path != NULL)
3228 {
Bram Moolenaar43663192017-03-05 14:29:12 +01003229 if (name[0] != '/')
Bram Moolenaarc7f02552014-04-01 21:00:59 +02003230 *path = FullName_save(name, TRUE);
3231 else
3232 *path = vim_strsave(name);
3233 }
3234 return TRUE;
3235 }
3236 return FALSE;
Bram Moolenaar206f0112014-03-12 16:51:55 +01003237 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003238
3239 p = (char_u *)getenv("PATH");
3240 if (p == NULL || *p == NUL)
3241 return -1;
Bram Moolenaar964b3742019-05-24 18:54:09 +02003242 buf = alloc(STRLEN(name) + STRLEN(p) + 2);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003243 if (buf == NULL)
3244 return -1;
3245
3246 /*
3247 * Walk through all entries in $PATH to check if "name" exists there and
3248 * is an executable file.
3249 */
3250 for (;;)
3251 {
3252 e = (char_u *)strchr((char *)p, ':');
3253 if (e == NULL)
3254 e = p + STRLEN(p);
Bram Moolenaar0f873732019-12-05 20:28:46 +01003255 if (e - p <= 1) // empty entry means current dir
Bram Moolenaar071d4272004-06-13 20:20:40 +00003256 STRCPY(buf, "./");
3257 else
3258 {
Bram Moolenaarbbebc852005-07-18 21:47:53 +00003259 vim_strncpy(buf, p, e - p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003260 add_pathsep(buf);
3261 }
3262 STRCAT(buf, name);
3263 retval = executable_file(buf);
3264 if (retval == 1)
Bram Moolenaarc7f02552014-04-01 21:00:59 +02003265 {
3266 if (path != NULL)
3267 {
Bram Moolenaar43663192017-03-05 14:29:12 +01003268 if (buf[0] != '/')
Bram Moolenaarc7f02552014-04-01 21:00:59 +02003269 *path = FullName_save(buf, TRUE);
3270 else
3271 *path = vim_strsave(buf);
3272 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003273 break;
Bram Moolenaarc7f02552014-04-01 21:00:59 +02003274 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003275
3276 if (*e != ':')
3277 break;
3278 p = e + 1;
3279 }
3280
3281 vim_free(buf);
3282 return retval;
3283}
Bram Moolenaar071d4272004-06-13 20:20:40 +00003284
3285/*
3286 * Check what "name" is:
3287 * NODE_NORMAL: file or directory (or doesn't exist)
3288 * NODE_WRITABLE: writable device, socket, fifo, etc.
3289 * NODE_OTHER: non-writable things
3290 */
3291 int
Bram Moolenaar05540972016-01-30 20:31:25 +01003292mch_nodetype(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003293{
3294 struct stat st;
3295
3296 if (stat((char *)name, &st))
3297 return NODE_NORMAL;
3298 if (S_ISREG(st.st_mode) || S_ISDIR(st.st_mode))
3299 return NODE_NORMAL;
Bram Moolenaar0f873732019-12-05 20:28:46 +01003300 if (S_ISBLK(st.st_mode)) // block device isn't writable
Bram Moolenaar071d4272004-06-13 20:20:40 +00003301 return NODE_OTHER;
Bram Moolenaar0f873732019-12-05 20:28:46 +01003302 // Everything else is writable?
Bram Moolenaar071d4272004-06-13 20:20:40 +00003303 return NODE_WRITABLE;
3304}
3305
3306 void
Bram Moolenaar05540972016-01-30 20:31:25 +01003307mch_early_init(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003308{
3309#ifdef HAVE_CHECK_STACK_GROWTH
3310 int i;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003311
Bram Moolenaar071d4272004-06-13 20:20:40 +00003312 check_stack_growth((char *)&i);
3313
Bram Moolenaarbc7aa852005-03-06 23:38:09 +00003314# ifdef HAVE_STACK_LIMIT
Bram Moolenaar071d4272004-06-13 20:20:40 +00003315 get_stack_limit();
3316# endif
3317
3318#endif
3319
3320 /*
3321 * Setup an alternative stack for signals. Helps to catch signals when
3322 * running out of stack space.
3323 * Use of sigaltstack() is preferred, it's more portable.
3324 * Ignore any errors.
3325 */
3326#if defined(HAVE_SIGALTSTACK) || defined(HAVE_SIGSTACK)
Zdenek Dohnalba9c23e2021-08-11 14:20:05 +02003327 signal_stack = alloc(get_signal_stack_size());
Bram Moolenaar071d4272004-06-13 20:20:40 +00003328 init_signal_stack();
3329#endif
3330}
3331
Bram Moolenaar0a5fe212005-06-24 23:01:23 +00003332#if defined(EXITFREE) || defined(PROTO)
3333 void
Bram Moolenaar05540972016-01-30 20:31:25 +01003334mch_free_mem(void)
Bram Moolenaar0a5fe212005-06-24 23:01:23 +00003335{
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00003336# if defined(FEAT_CLIPBOARD) && defined(FEAT_X11)
3337 if (clip_star.owned)
3338 clip_lose_selection(&clip_star);
3339 if (clip_plus.owned)
3340 clip_lose_selection(&clip_plus);
Bram Moolenaar0a5fe212005-06-24 23:01:23 +00003341# endif
Bram Moolenaare8208012008-06-20 09:59:25 +00003342# if defined(FEAT_X11) && defined(FEAT_XCLIPBOARD)
Bram Moolenaar0a5fe212005-06-24 23:01:23 +00003343 if (xterm_Shell != (Widget)0)
3344 XtDestroyWidget(xterm_Shell);
Bram Moolenaare8208012008-06-20 09:59:25 +00003345# ifndef LESSTIF_VERSION
Bram Moolenaar0f873732019-12-05 20:28:46 +01003346 // Lesstif crashes here, lose some memory
Bram Moolenaar0a5fe212005-06-24 23:01:23 +00003347 if (xterm_dpy != NULL)
3348 XtCloseDisplay(xterm_dpy);
3349 if (app_context != (XtAppContext)NULL)
Bram Moolenaare8208012008-06-20 09:59:25 +00003350 {
Bram Moolenaar0a5fe212005-06-24 23:01:23 +00003351 XtDestroyApplicationContext(app_context);
Bram Moolenaare8208012008-06-20 09:59:25 +00003352# ifdef FEAT_X11
Bram Moolenaar0f873732019-12-05 20:28:46 +01003353 x11_display = NULL; // freed by XtDestroyApplicationContext()
Bram Moolenaare8208012008-06-20 09:59:25 +00003354# endif
3355 }
3356# endif
Bram Moolenaar0a5fe212005-06-24 23:01:23 +00003357# endif
Bram Moolenaar0eda7ac2010-06-26 05:38:18 +02003358# if defined(FEAT_X11)
Bram Moolenaare8208012008-06-20 09:59:25 +00003359 if (x11_display != NULL
3360# ifdef FEAT_XCLIPBOARD
3361 && x11_display != xterm_dpy
3362# endif
3363 )
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00003364 XCloseDisplay(x11_display);
3365# endif
3366# if defined(HAVE_SIGALTSTACK) || defined(HAVE_SIGSTACK)
Bram Moolenaard23a8232018-02-10 18:45:26 +01003367 VIM_CLEAR(signal_stack);
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00003368# endif
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00003369 vim_free(oldtitle);
3370 vim_free(oldicon);
Bram Moolenaar0a5fe212005-06-24 23:01:23 +00003371}
3372#endif
3373
Bram Moolenaar071d4272004-06-13 20:20:40 +00003374/*
3375 * Output a newline when exiting.
3376 * Make sure the newline goes to the same stream as the text.
3377 */
3378 static void
Bram Moolenaar05540972016-01-30 20:31:25 +01003379exit_scroll(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003380{
Bram Moolenaardf177f62005-02-22 08:39:57 +00003381 if (silent_mode)
3382 return;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003383 if (newline_on_exit || msg_didout)
3384 {
3385 if (msg_use_printf())
3386 {
3387 if (info_message)
3388 mch_msg("\n");
3389 else
3390 mch_errmsg("\r\n");
3391 }
3392 else
3393 out_char('\n');
3394 }
Bram Moolenaar7007e312021-03-27 12:11:33 +01003395 else if (!is_not_a_term())
Bram Moolenaar071d4272004-06-13 20:20:40 +00003396 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01003397 restore_cterm_colors(); // get original colors back
3398 msg_clr_eos_force(); // clear the rest of the display
3399 windgoto((int)Rows - 1, 0); // may have moved the cursor
Bram Moolenaar071d4272004-06-13 20:20:40 +00003400 }
3401}
3402
Bram Moolenaarb4151682020-05-11 22:13:28 +02003403#ifdef USE_GCOV_FLUSH
ichizokdee78e12021-12-09 21:08:01 +00003404# if (defined(__GNUC__) \
3405 && ((__GNUC__ == 11 && __GNUC_MINOR__ >= 1) || (__GNUC__ >= 12))) \
3406 || (defined(__clang__) && (__clang_major__ >= 12))
3407extern void __gcov_dump(void);
3408extern void __gcov_reset(void);
3409# define __gcov_flush() do { __gcov_dump(); __gcov_reset(); } while (0)
3410# else
3411extern void __gcov_flush(void);
3412# endif
Bram Moolenaarb4151682020-05-11 22:13:28 +02003413#endif
3414
Bram Moolenaar071d4272004-06-13 20:20:40 +00003415 void
Bram Moolenaar05540972016-01-30 20:31:25 +01003416mch_exit(int r)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003417{
3418 exiting = TRUE;
3419
3420#if defined(FEAT_X11) && defined(FEAT_CLIPBOARD)
3421 x11_export_final_selection();
3422#endif
3423
3424#ifdef FEAT_GUI
3425 if (!gui.in_use)
3426#endif
3427 {
3428 settmode(TMODE_COOK);
Bram Moolenaar7007e312021-03-27 12:11:33 +01003429 if (!is_not_a_term())
3430 {
3431 // restore xterm title and icon name
3432 mch_restore_title(SAVE_RESTORE_BOTH);
3433 term_pop_title(SAVE_RESTORE_BOTH);
3434 }
Bram Moolenaar651fca82021-11-29 20:39:38 +00003435
Bram Moolenaar071d4272004-06-13 20:20:40 +00003436 /*
3437 * When t_ti is not empty but it doesn't cause swapping terminal
3438 * pages, need to output a newline when msg_didout is set. But when
3439 * t_ti does swap pages it should not go to the shell page. Do this
3440 * before stoptermcap().
3441 */
3442 if (swapping_screen() && !newline_on_exit)
3443 exit_scroll();
3444
Bram Moolenaar0f873732019-12-05 20:28:46 +01003445 // Stop termcap: May need to check for T_CRV response, which
3446 // requires RAW mode.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003447 stoptermcap();
3448
3449 /*
3450 * A newline is only required after a message in the alternate screen.
3451 * This is set to TRUE by wait_return().
3452 */
3453 if (!swapping_screen() || newline_on_exit)
3454 exit_scroll();
3455
Bram Moolenaar0f873732019-12-05 20:28:46 +01003456 // Cursor may have been switched off without calling starttermcap()
3457 // when doing "vim -u vimrc" and vimrc contains ":q".
Bram Moolenaar071d4272004-06-13 20:20:40 +00003458 if (full_screen)
3459 cursor_on();
3460 }
3461 out_flush();
Bram Moolenaar0f873732019-12-05 20:28:46 +01003462 ml_close_all(TRUE); // remove all memfiles
Bram Moolenaarb4151682020-05-11 22:13:28 +02003463
3464#ifdef USE_GCOV_FLUSH
3465 // Flush coverage info before possibly being killed by a deadly signal.
3466 __gcov_flush();
3467#endif
3468
Bram Moolenaar071d4272004-06-13 20:20:40 +00003469 may_core_dump();
3470#ifdef FEAT_GUI
Bram Moolenaar071d4272004-06-13 20:20:40 +00003471 if (gui.in_use)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003472 gui_exit(r);
3473#endif
Bram Moolenaardf177f62005-02-22 08:39:57 +00003474
Bram Moolenaar56718732006-03-15 22:53:57 +00003475#ifdef MACOS_CONVERT
Bram Moolenaardf177f62005-02-22 08:39:57 +00003476 mac_conv_cleanup();
3477#endif
3478
Bram Moolenaar071d4272004-06-13 20:20:40 +00003479#ifdef __QNX__
Bram Moolenaar0f873732019-12-05 20:28:46 +01003480 // A core dump won't be created if the signal handler
3481 // doesn't return, so we can't call exit()
Bram Moolenaar071d4272004-06-13 20:20:40 +00003482 if (deadly_signal != 0)
3483 return;
3484#endif
3485
Bram Moolenaar009b2592004-10-24 19:18:58 +00003486#ifdef FEAT_NETBEANS_INTG
Bram Moolenaarb26e6322010-05-22 21:34:09 +02003487 netbeans_send_disconnect();
Bram Moolenaar009b2592004-10-24 19:18:58 +00003488#endif
Bram Moolenaar0a5fe212005-06-24 23:01:23 +00003489
3490#ifdef EXITFREE
3491 free_all_mem();
3492#endif
3493
Bram Moolenaar071d4272004-06-13 20:20:40 +00003494 exit(r);
3495}
3496
3497 static void
Bram Moolenaar05540972016-01-30 20:31:25 +01003498may_core_dump(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003499{
3500 if (deadly_signal != 0)
3501 {
3502 signal(deadly_signal, SIG_DFL);
Bram Moolenaar0f873732019-12-05 20:28:46 +01003503 kill(getpid(), deadly_signal); // Die using the signal we caught
Bram Moolenaar071d4272004-06-13 20:20:40 +00003504 }
3505}
3506
3507#ifndef VMS
3508
Bram Moolenaar1ecc5e42019-01-26 15:12:55 +01003509/*
3510 * Get the file descriptor to use for tty operations.
3511 */
3512 static int
3513get_tty_fd(int fd)
3514{
3515 int tty_fd = fd;
3516
3517#if defined(HAVE_SVR4_PTYS) && defined(SUN_SYSTEM)
3518 // On SunOS: Get the terminal parameters from "fd", or the slave device of
3519 // "fd" when it is a master device.
3520 if (mch_isatty(fd) > 1)
3521 {
3522 char *name;
3523
3524 name = ptsname(fd);
3525 if (name == NULL)
3526 return -1;
3527
3528 tty_fd = open(name, O_RDONLY | O_NOCTTY | O_EXTRA, 0);
3529 if (tty_fd < 0)
3530 return -1;
3531 }
3532#endif
3533 return tty_fd;
3534}
3535
3536 static int
3537mch_tcgetattr(int fd, void *term)
3538{
3539 int tty_fd;
3540 int retval = -1;
3541
3542 tty_fd = get_tty_fd(fd);
3543 if (tty_fd >= 0)
3544 {
3545#ifdef NEW_TTY_SYSTEM
3546# ifdef HAVE_TERMIOS_H
3547 retval = tcgetattr(tty_fd, (struct termios *)term);
3548# else
3549 retval = ioctl(tty_fd, TCGETA, (struct termio *)term);
3550# endif
3551#else
3552 // for "old" tty systems
3553 retval = ioctl(tty_fd, TIOCGETP, (struct sgttyb *)term);
3554#endif
3555 if (tty_fd != fd)
3556 close(tty_fd);
3557 }
3558 return retval;
3559}
3560
Bram Moolenaar071d4272004-06-13 20:20:40 +00003561 void
Bram Moolenaar26e86442020-05-17 14:06:16 +02003562mch_settmode(tmode_T tmode)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003563{
3564 static int first = TRUE;
3565
Bram Moolenaar4f44b882017-08-13 20:06:18 +02003566#ifdef NEW_TTY_SYSTEM
Bram Moolenaar071d4272004-06-13 20:20:40 +00003567# ifdef HAVE_TERMIOS_H
3568 static struct termios told;
3569 struct termios tnew;
3570# else
3571 static struct termio told;
3572 struct termio tnew;
3573# endif
3574
3575 if (first)
3576 {
3577 first = FALSE;
Bram Moolenaar1ecc5e42019-01-26 15:12:55 +01003578 mch_tcgetattr(read_cmd_fd, &told);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003579 }
3580
3581 tnew = told;
3582 if (tmode == TMODE_RAW)
3583 {
Bram Moolenaar041c7102020-05-30 18:14:57 +02003584 // ~ICRNL enables typing ^V^M
Bram Moolenaar928eec62020-05-31 13:09:47 +02003585 // ~IXON disables CTRL-S stopping output, so that it can be mapped.
3586 tnew.c_iflag &= ~(ICRNL | IXON);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003587 tnew.c_lflag &= ~(ICANON | ECHO | ISIG | ECHOE
Bram Moolenaare3f915d2020-07-14 23:02:44 +02003588# if defined(IEXTEN)
Bram Moolenaar0f873732019-12-05 20:28:46 +01003589 | IEXTEN // IEXTEN enables typing ^V on SOLARIS
Bram Moolenaar071d4272004-06-13 20:20:40 +00003590# endif
3591 );
Bram Moolenaarfaf626e2019-10-24 17:43:25 +02003592# ifdef ONLCR
3593 // Don't map NL -> CR NL, we do it ourselves.
3594 // Also disable expanding tabs if possible.
3595# ifdef XTABS
3596 tnew.c_oflag &= ~(ONLCR | XTABS);
3597# else
3598# ifdef TAB3
3599 tnew.c_oflag &= ~(ONLCR | TAB3);
3600# else
Bram Moolenaar071d4272004-06-13 20:20:40 +00003601 tnew.c_oflag &= ~ONLCR;
Bram Moolenaarfaf626e2019-10-24 17:43:25 +02003602# endif
3603# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003604# endif
Bram Moolenaarfaf626e2019-10-24 17:43:25 +02003605 tnew.c_cc[VMIN] = 1; // return after 1 char
3606 tnew.c_cc[VTIME] = 0; // don't wait
Bram Moolenaar071d4272004-06-13 20:20:40 +00003607 }
3608 else if (tmode == TMODE_SLEEP)
Bram Moolenaar40de4562016-07-01 15:03:46 +02003609 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01003610 // Also reset ICANON here, otherwise on Solaris select() won't see
3611 // typeahead characters.
Bram Moolenaar40de4562016-07-01 15:03:46 +02003612 tnew.c_lflag &= ~(ICANON | ECHO);
Bram Moolenaar0f873732019-12-05 20:28:46 +01003613 tnew.c_cc[VMIN] = 1; // return after 1 char
3614 tnew.c_cc[VTIME] = 0; // don't wait
Bram Moolenaar40de4562016-07-01 15:03:46 +02003615 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003616
3617# if defined(HAVE_TERMIOS_H)
3618 {
3619 int n = 10;
3620
Bram Moolenaar0f873732019-12-05 20:28:46 +01003621 // A signal may cause tcsetattr() to fail (e.g., SIGCONT). Retry a
3622 // few times.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003623 while (tcsetattr(read_cmd_fd, TCSANOW, &tnew) == -1
3624 && errno == EINTR && n > 0)
3625 --n;
3626 }
3627# else
3628 ioctl(read_cmd_fd, TCSETA, &tnew);
3629# endif
3630
3631#else
Bram Moolenaar071d4272004-06-13 20:20:40 +00003632 /*
3633 * for "old" tty systems
3634 */
3635# ifndef TIOCSETN
Bram Moolenaar0f873732019-12-05 20:28:46 +01003636# define TIOCSETN TIOCSETP // for hpux 9.0
Bram Moolenaar071d4272004-06-13 20:20:40 +00003637# endif
3638 static struct sgttyb ttybold;
3639 struct sgttyb ttybnew;
3640
3641 if (first)
3642 {
3643 first = FALSE;
Bram Moolenaar1ecc5e42019-01-26 15:12:55 +01003644 mch_tcgetattr(read_cmd_fd, &ttybold);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003645 }
3646
3647 ttybnew = ttybold;
3648 if (tmode == TMODE_RAW)
3649 {
3650 ttybnew.sg_flags &= ~(CRMOD | ECHO);
3651 ttybnew.sg_flags |= RAW;
3652 }
3653 else if (tmode == TMODE_SLEEP)
3654 ttybnew.sg_flags &= ~(ECHO);
3655 ioctl(read_cmd_fd, TIOCSETN, &ttybnew);
3656#endif
Bram Moolenaar26e86442020-05-17 14:06:16 +02003657 mch_cur_tmode = tmode;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003658}
3659
3660/*
3661 * Try to get the code for "t_kb" from the stty setting
3662 *
3663 * Even if termcap claims a backspace key, the user's setting *should*
3664 * prevail. stty knows more about reality than termcap does, and if
3665 * somebody's usual erase key is DEL (which, for most BSD users, it will
3666 * be), they're going to get really annoyed if their erase key starts
3667 * doing forward deletes for no reason. (Eric Fischer)
3668 */
3669 void
Bram Moolenaar05540972016-01-30 20:31:25 +01003670get_stty(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003671{
Bram Moolenaar4f44b882017-08-13 20:06:18 +02003672 ttyinfo_T info;
3673 char_u buf[2];
3674 char_u *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003675
Bram Moolenaar4f44b882017-08-13 20:06:18 +02003676 if (get_tty_info(read_cmd_fd, &info) == OK)
3677 {
3678 intr_char = info.interrupt;
3679 buf[0] = info.backspace;
3680 buf[1] = NUL;
3681 add_termcode((char_u *)"kb", buf, FALSE);
3682
Bram Moolenaar0f873732019-12-05 20:28:46 +01003683 // If <BS> and <DEL> are now the same, redefine <DEL>.
Bram Moolenaar4f44b882017-08-13 20:06:18 +02003684 p = find_termcode((char_u *)"kD");
3685 if (p != NULL && p[0] == buf[0] && p[1] == buf[1])
3686 do_fixdel(NULL);
3687 }
3688}
3689
3690/*
3691 * Obtain the characters that Backspace and Enter produce on "fd".
3692 * Returns OK or FAIL.
3693 */
3694 int
3695get_tty_info(int fd, ttyinfo_T *info)
3696{
3697#ifdef NEW_TTY_SYSTEM
Bram Moolenaar071d4272004-06-13 20:20:40 +00003698# ifdef HAVE_TERMIOS_H
3699 struct termios keys;
3700# else
3701 struct termio keys;
3702# endif
3703
Bram Moolenaar1ecc5e42019-01-26 15:12:55 +01003704 if (mch_tcgetattr(fd, &keys) != -1)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003705 {
Bram Moolenaar4f44b882017-08-13 20:06:18 +02003706 info->backspace = keys.c_cc[VERASE];
3707 info->interrupt = keys.c_cc[VINTR];
3708 if (keys.c_iflag & ICRNL)
3709 info->enter = NL;
3710 else
3711 info->enter = CAR;
3712 if (keys.c_oflag & ONLCR)
3713 info->nl_does_cr = TRUE;
3714 else
3715 info->nl_does_cr = FALSE;
3716 return OK;
3717 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003718#else
Bram Moolenaar0f873732019-12-05 20:28:46 +01003719 // for "old" tty systems
Bram Moolenaar071d4272004-06-13 20:20:40 +00003720 struct sgttyb keys;
3721
Bram Moolenaar1ecc5e42019-01-26 15:12:55 +01003722 if (mch_tcgetattr(fd, &keys) != -1)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003723 {
Bram Moolenaar4f44b882017-08-13 20:06:18 +02003724 info->backspace = keys.sg_erase;
3725 info->interrupt = keys.sg_kill;
3726 info->enter = CAR;
3727 info->nl_does_cr = TRUE;
3728 return OK;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003729 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003730#endif
Bram Moolenaar4f44b882017-08-13 20:06:18 +02003731 return FAIL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003732}
3733
Bram Moolenaar0f873732019-12-05 20:28:46 +01003734#endif // VMS
Bram Moolenaar071d4272004-06-13 20:20:40 +00003735
Bram Moolenaar51b0f372017-11-18 18:52:04 +01003736static int mouse_ison = FALSE;
3737
Bram Moolenaar071d4272004-06-13 20:20:40 +00003738/*
3739 * Set mouse clicks on or off.
3740 */
3741 void
Bram Moolenaar05540972016-01-30 20:31:25 +01003742mch_setmouse(int on)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003743{
Bram Moolenaara1cb1d12019-10-17 23:00:07 +02003744#ifdef FEAT_BEVAL_TERM
Bram Moolenaar51b0f372017-11-18 18:52:04 +01003745 static int bevalterm_ison = FALSE;
Bram Moolenaara1cb1d12019-10-17 23:00:07 +02003746#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003747 int xterm_mouse_vers;
3748
Bram Moolenaara1cb1d12019-10-17 23:00:07 +02003749#if defined(FEAT_X11) && defined(FEAT_XCLIPBOARD)
Bram Moolenaara06afc72018-08-27 23:24:16 +02003750 if (!on)
3751 // Make sure not tracing mouse movements. Important when a button-down
3752 // was received but no release yet.
3753 stop_xterm_trace();
Bram Moolenaara1cb1d12019-10-17 23:00:07 +02003754#endif
Bram Moolenaara06afc72018-08-27 23:24:16 +02003755
Bram Moolenaar51b0f372017-11-18 18:52:04 +01003756 if (on == mouse_ison
Bram Moolenaara1cb1d12019-10-17 23:00:07 +02003757#ifdef FEAT_BEVAL_TERM
Bram Moolenaar51b0f372017-11-18 18:52:04 +01003758 && p_bevalterm == bevalterm_ison
Bram Moolenaara1cb1d12019-10-17 23:00:07 +02003759#endif
Bram Moolenaar51b0f372017-11-18 18:52:04 +01003760 )
Bram Moolenaar0f873732019-12-05 20:28:46 +01003761 // return quickly if nothing to do
Bram Moolenaar071d4272004-06-13 20:20:40 +00003762 return;
3763
3764 xterm_mouse_vers = use_xterm_mouse();
Bram Moolenaarc8427482011-10-20 21:09:35 +02003765
Bram Moolenaara1cb1d12019-10-17 23:00:07 +02003766#ifdef FEAT_MOUSE_URXVT
Bram Moolenaar2b9578f2012-08-15 16:21:32 +02003767 if (ttym_flags == TTYM_URXVT)
3768 {
Bram Moolenaarc8427482011-10-20 21:09:35 +02003769 out_str_nf((char_u *)
3770 (on
3771 ? IF_EB("\033[?1015h", ESC_STR "[?1015h")
3772 : IF_EB("\033[?1015l", ESC_STR "[?1015l")));
Bram Moolenaar51b0f372017-11-18 18:52:04 +01003773 mouse_ison = on;
Bram Moolenaarc8427482011-10-20 21:09:35 +02003774 }
Bram Moolenaara1cb1d12019-10-17 23:00:07 +02003775#endif
Bram Moolenaarc8427482011-10-20 21:09:35 +02003776
Bram Moolenaar2b9578f2012-08-15 16:21:32 +02003777 if (ttym_flags == TTYM_SGR)
3778 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01003779 // SGR mode supports columns above 223
Bram Moolenaar2b9578f2012-08-15 16:21:32 +02003780 out_str_nf((char_u *)
3781 (on
3782 ? IF_EB("\033[?1006h", ESC_STR "[?1006h")
3783 : IF_EB("\033[?1006l", ESC_STR "[?1006l")));
Bram Moolenaar51b0f372017-11-18 18:52:04 +01003784 mouse_ison = on;
3785 }
Bram Moolenaar51b0f372017-11-18 18:52:04 +01003786
Bram Moolenaara1cb1d12019-10-17 23:00:07 +02003787#ifdef FEAT_BEVAL_TERM
Bram Moolenaar51b0f372017-11-18 18:52:04 +01003788 if (bevalterm_ison != (p_bevalterm && on))
3789 {
3790 bevalterm_ison = (p_bevalterm && on);
3791 if (xterm_mouse_vers > 1 && !bevalterm_ison)
Bram Moolenaar0f873732019-12-05 20:28:46 +01003792 // disable mouse movement events, enabling is below
Bram Moolenaar51b0f372017-11-18 18:52:04 +01003793 out_str_nf((char_u *)
3794 (IF_EB("\033[?1003l", ESC_STR "[?1003l")));
Bram Moolenaar2b9578f2012-08-15 16:21:32 +02003795 }
Bram Moolenaara1cb1d12019-10-17 23:00:07 +02003796#endif
Bram Moolenaar2b9578f2012-08-15 16:21:32 +02003797
Bram Moolenaar071d4272004-06-13 20:20:40 +00003798 if (xterm_mouse_vers > 0)
3799 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01003800 if (on) // enable mouse events, use mouse tracking if available
Bram Moolenaar071d4272004-06-13 20:20:40 +00003801 out_str_nf((char_u *)
3802 (xterm_mouse_vers > 1
Bram Moolenaar51b0f372017-11-18 18:52:04 +01003803 ? (
Bram Moolenaara1cb1d12019-10-17 23:00:07 +02003804#ifdef FEAT_BEVAL_TERM
Bram Moolenaar51b0f372017-11-18 18:52:04 +01003805 bevalterm_ison
3806 ? IF_EB("\033[?1003h", ESC_STR "[?1003h") :
Bram Moolenaara1cb1d12019-10-17 23:00:07 +02003807#endif
Bram Moolenaar51b0f372017-11-18 18:52:04 +01003808 IF_EB("\033[?1002h", ESC_STR "[?1002h"))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003809 : IF_EB("\033[?1000h", ESC_STR "[?1000h")));
Bram Moolenaar0f873732019-12-05 20:28:46 +01003810 else // disable mouse events, could probably always send the same
Bram Moolenaar071d4272004-06-13 20:20:40 +00003811 out_str_nf((char_u *)
3812 (xterm_mouse_vers > 1
3813 ? IF_EB("\033[?1002l", ESC_STR "[?1002l")
3814 : IF_EB("\033[?1000l", ESC_STR "[?1000l")));
Bram Moolenaar51b0f372017-11-18 18:52:04 +01003815 mouse_ison = on;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003816 }
3817
Bram Moolenaara1cb1d12019-10-17 23:00:07 +02003818#ifdef FEAT_MOUSE_DEC
Bram Moolenaar071d4272004-06-13 20:20:40 +00003819 else if (ttym_flags == TTYM_DEC)
3820 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01003821 if (on) // enable mouse events
Bram Moolenaar071d4272004-06-13 20:20:40 +00003822 out_str_nf((char_u *)"\033[1;2'z\033[1;3'{");
Bram Moolenaar0f873732019-12-05 20:28:46 +01003823 else // disable mouse events
Bram Moolenaar071d4272004-06-13 20:20:40 +00003824 out_str_nf((char_u *)"\033['z");
Bram Moolenaar51b0f372017-11-18 18:52:04 +01003825 mouse_ison = on;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003826 }
Bram Moolenaara1cb1d12019-10-17 23:00:07 +02003827#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003828
Bram Moolenaara1cb1d12019-10-17 23:00:07 +02003829#ifdef FEAT_MOUSE_GPM
Bram Moolenaar071d4272004-06-13 20:20:40 +00003830 else
3831 {
3832 if (on)
3833 {
3834 if (gpm_open())
Bram Moolenaar51b0f372017-11-18 18:52:04 +01003835 mouse_ison = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003836 }
3837 else
3838 {
3839 gpm_close();
Bram Moolenaar51b0f372017-11-18 18:52:04 +01003840 mouse_ison = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003841 }
3842 }
Bram Moolenaara1cb1d12019-10-17 23:00:07 +02003843#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003844
Bram Moolenaara1cb1d12019-10-17 23:00:07 +02003845#ifdef FEAT_SYSMOUSE
Bram Moolenaar3577c6f2008-06-24 21:16:56 +00003846 else
3847 {
3848 if (on)
3849 {
3850 if (sysmouse_open() == OK)
Bram Moolenaar51b0f372017-11-18 18:52:04 +01003851 mouse_ison = TRUE;
Bram Moolenaar3577c6f2008-06-24 21:16:56 +00003852 }
3853 else
3854 {
3855 sysmouse_close();
Bram Moolenaar51b0f372017-11-18 18:52:04 +01003856 mouse_ison = FALSE;
Bram Moolenaar3577c6f2008-06-24 21:16:56 +00003857 }
3858 }
Bram Moolenaara1cb1d12019-10-17 23:00:07 +02003859#endif
Bram Moolenaar3577c6f2008-06-24 21:16:56 +00003860
Bram Moolenaara1cb1d12019-10-17 23:00:07 +02003861#ifdef FEAT_MOUSE_JSB
Bram Moolenaar071d4272004-06-13 20:20:40 +00003862 else
3863 {
3864 if (on)
3865 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01003866 // D - Enable Mouse up/down messages
3867 // L - Enable Left Button Reporting
3868 // M - Enable Middle Button Reporting
3869 // R - Enable Right Button Reporting
3870 // K - Enable SHIFT and CTRL key Reporting
3871 // + - Enable Advanced messaging of mouse moves and up/down messages
3872 // Q - Quiet No Ack
3873 // # - Numeric value of mouse pointer required
3874 // 0 = Multiview 2000 cursor, used as standard
3875 // 1 = Windows Arrow
3876 // 2 = Windows I Beam
3877 // 3 = Windows Hour Glass
3878 // 4 = Windows Cross Hair
3879 // 5 = Windows UP Arrow
Bram Moolenaara1cb1d12019-10-17 23:00:07 +02003880# ifdef JSBTERM_MOUSE_NONADVANCED
Bram Moolenaar0f873732019-12-05 20:28:46 +01003881 // Disables full feedback of pointer movements
Bram Moolenaar071d4272004-06-13 20:20:40 +00003882 out_str_nf((char_u *)IF_EB("\033[0~ZwLMRK1Q\033\\",
3883 ESC_STR "[0~ZwLMRK1Q" ESC_STR "\\"));
Bram Moolenaara1cb1d12019-10-17 23:00:07 +02003884# else
Bram Moolenaar071d4272004-06-13 20:20:40 +00003885 out_str_nf((char_u *)IF_EB("\033[0~ZwLMRK+1Q\033\\",
3886 ESC_STR "[0~ZwLMRK+1Q" ESC_STR "\\"));
Bram Moolenaara1cb1d12019-10-17 23:00:07 +02003887# endif
Bram Moolenaar51b0f372017-11-18 18:52:04 +01003888 mouse_ison = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003889 }
3890 else
3891 {
3892 out_str_nf((char_u *)IF_EB("\033[0~ZwQ\033\\",
3893 ESC_STR "[0~ZwQ" ESC_STR "\\"));
Bram Moolenaar51b0f372017-11-18 18:52:04 +01003894 mouse_ison = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003895 }
3896 }
Bram Moolenaara1cb1d12019-10-17 23:00:07 +02003897#endif
3898#ifdef FEAT_MOUSE_PTERM
Bram Moolenaar071d4272004-06-13 20:20:40 +00003899 else
3900 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01003901 // 1 = button press, 6 = release, 7 = drag, 1h...9l = right button
Bram Moolenaar071d4272004-06-13 20:20:40 +00003902 if (on)
3903 out_str_nf("\033[>1h\033[>6h\033[>7h\033[>1h\033[>9l");
3904 else
3905 out_str_nf("\033[>1l\033[>6l\033[>7l\033[>1l\033[>9h");
Bram Moolenaar51b0f372017-11-18 18:52:04 +01003906 mouse_ison = on;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003907 }
Bram Moolenaara1cb1d12019-10-17 23:00:07 +02003908#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003909}
3910
Bram Moolenaarc3719bd2017-11-18 22:13:31 +01003911#if defined(FEAT_BEVAL_TERM) || defined(PROTO)
Bram Moolenaar51b0f372017-11-18 18:52:04 +01003912/*
3913 * Called when 'balloonevalterm' changed.
3914 */
3915 void
3916mch_bevalterm_changed(void)
3917{
3918 mch_setmouse(mouse_ison);
3919}
3920#endif
3921
Bram Moolenaar071d4272004-06-13 20:20:40 +00003922/*
3923 * Set the mouse termcode, depending on the 'term' and 'ttymouse' options.
3924 */
3925 void
Bram Moolenaar05540972016-01-30 20:31:25 +01003926check_mouse_termcode(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003927{
3928# ifdef FEAT_MOUSE_XTERM
3929 if (use_xterm_mouse()
Bram Moolenaarc8427482011-10-20 21:09:35 +02003930# ifdef FEAT_MOUSE_URXVT
3931 && use_xterm_mouse() != 3
3932# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003933# ifdef FEAT_GUI
3934 && !gui.in_use
3935# endif
3936 )
3937 {
3938 set_mouse_termcode(KS_MOUSE, (char_u *)(term_is_8bit(T_NAME)
Bram Moolenaarbc7aa852005-03-06 23:38:09 +00003939 ? IF_EB("\233M", CSI_STR "M")
3940 : IF_EB("\033[M", ESC_STR "[M")));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003941 if (*p_mouse != NUL)
3942 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01003943 // force mouse off and maybe on to send possibly new mouse
3944 // activation sequence to the xterm, with(out) drag tracing.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003945 mch_setmouse(FALSE);
3946 setmouse();
3947 }
3948 }
3949 else
3950 del_mouse_termcode(KS_MOUSE);
3951# endif
3952
3953# ifdef FEAT_MOUSE_GPM
3954 if (!use_xterm_mouse()
3955# ifdef FEAT_GUI
3956 && !gui.in_use
3957# endif
3958 )
Bram Moolenaarbedf0912019-05-04 16:58:45 +02003959 set_mouse_termcode(KS_GPM_MOUSE,
3960 (char_u *)IF_EB("\033MG", ESC_STR "MG"));
3961 else
3962 del_mouse_termcode(KS_GPM_MOUSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003963# endif
3964
Bram Moolenaar3577c6f2008-06-24 21:16:56 +00003965# ifdef FEAT_SYSMOUSE
3966 if (!use_xterm_mouse()
3967# ifdef FEAT_GUI
3968 && !gui.in_use
3969# endif
3970 )
3971 set_mouse_termcode(KS_MOUSE, (char_u *)IF_EB("\033MS", ESC_STR "MS"));
3972# endif
3973
Bram Moolenaar071d4272004-06-13 20:20:40 +00003974# ifdef FEAT_MOUSE_JSB
Bram Moolenaar0f873732019-12-05 20:28:46 +01003975 // Conflicts with xterm mouse: "\033[" and "\033[M" ???
Bram Moolenaar071d4272004-06-13 20:20:40 +00003976 if (!use_xterm_mouse()
3977# ifdef FEAT_GUI
3978 && !gui.in_use
3979# endif
3980 )
3981 set_mouse_termcode(KS_JSBTERM_MOUSE,
3982 (char_u *)IF_EB("\033[0~zw", ESC_STR "[0~zw"));
3983 else
3984 del_mouse_termcode(KS_JSBTERM_MOUSE);
3985# endif
3986
3987# ifdef FEAT_MOUSE_NET
Bram Moolenaar0f873732019-12-05 20:28:46 +01003988 // There is no conflict, but one may type "ESC }" from Insert mode. Don't
3989 // define it in the GUI or when using an xterm.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003990 if (!use_xterm_mouse()
3991# ifdef FEAT_GUI
3992 && !gui.in_use
3993# endif
3994 )
3995 set_mouse_termcode(KS_NETTERM_MOUSE,
3996 (char_u *)IF_EB("\033}", ESC_STR "}"));
3997 else
3998 del_mouse_termcode(KS_NETTERM_MOUSE);
3999# endif
4000
4001# ifdef FEAT_MOUSE_DEC
Bram Moolenaar0f873732019-12-05 20:28:46 +01004002 // Conflicts with xterm mouse: "\033[" and "\033[M"
Bram Moolenaarcbc17d62014-05-22 21:22:19 +02004003 if (!use_xterm_mouse()
Bram Moolenaar071d4272004-06-13 20:20:40 +00004004# ifdef FEAT_GUI
4005 && !gui.in_use
4006# endif
4007 )
Bram Moolenaarbc7aa852005-03-06 23:38:09 +00004008 set_mouse_termcode(KS_DEC_MOUSE, (char_u *)(term_is_8bit(T_NAME)
4009 ? IF_EB("\233", CSI_STR) : IF_EB("\033[", ESC_STR "[")));
Bram Moolenaar071d4272004-06-13 20:20:40 +00004010 else
4011 del_mouse_termcode(KS_DEC_MOUSE);
4012# endif
4013# ifdef FEAT_MOUSE_PTERM
Bram Moolenaar0f873732019-12-05 20:28:46 +01004014 // same conflict as the dec mouse
Bram Moolenaarcbc17d62014-05-22 21:22:19 +02004015 if (!use_xterm_mouse()
Bram Moolenaar071d4272004-06-13 20:20:40 +00004016# ifdef FEAT_GUI
4017 && !gui.in_use
4018# endif
4019 )
4020 set_mouse_termcode(KS_PTERM_MOUSE,
4021 (char_u *) IF_EB("\033[", ESC_STR "["));
4022 else
4023 del_mouse_termcode(KS_PTERM_MOUSE);
4024# endif
Bram Moolenaarc8427482011-10-20 21:09:35 +02004025# ifdef FEAT_MOUSE_URXVT
Bram Moolenaarcbc17d62014-05-22 21:22:19 +02004026 if (use_xterm_mouse() == 3
Bram Moolenaarc8427482011-10-20 21:09:35 +02004027# ifdef FEAT_GUI
4028 && !gui.in_use
4029# endif
4030 )
4031 {
4032 set_mouse_termcode(KS_URXVT_MOUSE, (char_u *)(term_is_8bit(T_NAME)
Bram Moolenaara529ce02017-06-22 22:37:57 +02004033 ? IF_EB("\233*M", CSI_STR "*M")
4034 : IF_EB("\033[*M", ESC_STR "[*M")));
Bram Moolenaarc8427482011-10-20 21:09:35 +02004035
4036 if (*p_mouse != NUL)
4037 {
4038 mch_setmouse(FALSE);
4039 setmouse();
4040 }
4041 }
4042 else
4043 del_mouse_termcode(KS_URXVT_MOUSE);
4044# endif
Bram Moolenaar2b9578f2012-08-15 16:21:32 +02004045 if (use_xterm_mouse() == 4
Bram Moolenaar2ace1bd2019-03-22 12:03:30 +01004046# ifdef FEAT_GUI
Bram Moolenaar2b9578f2012-08-15 16:21:32 +02004047 && !gui.in_use
Bram Moolenaar2ace1bd2019-03-22 12:03:30 +01004048# endif
Bram Moolenaar2b9578f2012-08-15 16:21:32 +02004049 )
4050 {
4051 set_mouse_termcode(KS_SGR_MOUSE, (char_u *)(term_is_8bit(T_NAME)
Bram Moolenaara529ce02017-06-22 22:37:57 +02004052 ? IF_EB("\233<*M", CSI_STR "<*M")
4053 : IF_EB("\033[<*M", ESC_STR "[<*M")));
4054
4055 set_mouse_termcode(KS_SGR_MOUSE_RELEASE, (char_u *)(term_is_8bit(T_NAME)
4056 ? IF_EB("\233<*m", CSI_STR "<*m")
4057 : IF_EB("\033[<*m", ESC_STR "[<*m")));
Bram Moolenaar2b9578f2012-08-15 16:21:32 +02004058
4059 if (*p_mouse != NUL)
4060 {
4061 mch_setmouse(FALSE);
4062 setmouse();
4063 }
4064 }
4065 else
Bram Moolenaara529ce02017-06-22 22:37:57 +02004066 {
Bram Moolenaar2b9578f2012-08-15 16:21:32 +02004067 del_mouse_termcode(KS_SGR_MOUSE);
Bram Moolenaara529ce02017-06-22 22:37:57 +02004068 del_mouse_termcode(KS_SGR_MOUSE_RELEASE);
4069 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004070}
Bram Moolenaar071d4272004-06-13 20:20:40 +00004071
Bram Moolenaar071d4272004-06-13 20:20:40 +00004072#ifndef VMS
4073
4074/*
4075 * Try to get the current window size:
4076 * 1. with an ioctl(), most accurate method
4077 * 2. from the environment variables LINES and COLUMNS
4078 * 3. from the termcap
4079 * 4. keep using the old values
4080 * Return OK when size could be determined, FAIL otherwise.
4081 */
4082 int
Bram Moolenaar05540972016-01-30 20:31:25 +01004083mch_get_shellsize(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004084{
4085 long rows = 0;
4086 long columns = 0;
4087 char_u *p;
4088
4089 /*
Bram Moolenaar071d4272004-06-13 20:20:40 +00004090 * 1. try using an ioctl. It is the most accurate method.
4091 *
4092 * Try using TIOCGWINSZ first, some systems that have it also define
4093 * TIOCGSIZE but don't have a struct ttysize.
4094 */
4095# ifdef TIOCGWINSZ
4096 {
4097 struct winsize ws;
4098 int fd = 1;
4099
Bram Moolenaar0f873732019-12-05 20:28:46 +01004100 // When stdout is not a tty, use stdin for the ioctl().
Bram Moolenaar071d4272004-06-13 20:20:40 +00004101 if (!isatty(fd) && isatty(read_cmd_fd))
4102 fd = read_cmd_fd;
4103 if (ioctl(fd, TIOCGWINSZ, &ws) == 0)
4104 {
4105 columns = ws.ws_col;
4106 rows = ws.ws_row;
4107 }
4108 }
Bram Moolenaar0f873732019-12-05 20:28:46 +01004109# else // TIOCGWINSZ
Bram Moolenaar071d4272004-06-13 20:20:40 +00004110# ifdef TIOCGSIZE
4111 {
4112 struct ttysize ts;
4113 int fd = 1;
4114
Bram Moolenaar0f873732019-12-05 20:28:46 +01004115 // When stdout is not a tty, use stdin for the ioctl().
Bram Moolenaar071d4272004-06-13 20:20:40 +00004116 if (!isatty(fd) && isatty(read_cmd_fd))
4117 fd = read_cmd_fd;
4118 if (ioctl(fd, TIOCGSIZE, &ts) == 0)
4119 {
4120 columns = ts.ts_cols;
4121 rows = ts.ts_lines;
4122 }
4123 }
Bram Moolenaar0f873732019-12-05 20:28:46 +01004124# endif // TIOCGSIZE
4125# endif // TIOCGWINSZ
Bram Moolenaar071d4272004-06-13 20:20:40 +00004126
4127 /*
4128 * 2. get size from environment
Bram Moolenaar4399ef42005-02-12 14:29:27 +00004129 * When being POSIX compliant ('|' flag in 'cpoptions') this overrules
4130 * the ioctl() values!
Bram Moolenaar071d4272004-06-13 20:20:40 +00004131 */
Bram Moolenaar4399ef42005-02-12 14:29:27 +00004132 if (columns == 0 || rows == 0 || vim_strchr(p_cpo, CPO_TSIZE) != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004133 {
4134 if ((p = (char_u *)getenv("LINES")))
4135 rows = atoi((char *)p);
4136 if ((p = (char_u *)getenv("COLUMNS")))
4137 columns = atoi((char *)p);
4138 }
4139
4140#ifdef HAVE_TGETENT
4141 /*
4142 * 3. try reading "co" and "li" entries from termcap
4143 */
4144 if (columns == 0 || rows == 0)
4145 getlinecol(&columns, &rows);
4146#endif
4147
4148 /*
4149 * 4. If everything fails, use the old values
4150 */
4151 if (columns <= 0 || rows <= 0)
4152 return FAIL;
4153
4154 Rows = rows;
4155 Columns = columns;
Bram Moolenaare057d402013-06-30 17:51:51 +02004156 limit_screen_size();
Bram Moolenaar071d4272004-06-13 20:20:40 +00004157 return OK;
4158}
4159
Bram Moolenaarb13501f2017-07-22 22:32:56 +02004160#if defined(FEAT_TERMINAL) || defined(PROTO)
4161/*
4162 * Report the windows size "rows" and "cols" to tty "fd".
4163 */
4164 int
4165mch_report_winsize(int fd, int rows, int cols)
4166{
Bram Moolenaar1ecc5e42019-01-26 15:12:55 +01004167 int tty_fd;
4168 int retval = -1;
Bram Moolenaarb13501f2017-07-22 22:32:56 +02004169
Bram Moolenaar1ecc5e42019-01-26 15:12:55 +01004170 tty_fd = get_tty_fd(fd);
4171 if (tty_fd >= 0)
Bram Moolenaarb13501f2017-07-22 22:32:56 +02004172 {
Bram Moolenaar1ecc5e42019-01-26 15:12:55 +01004173# if defined(TIOCSWINSZ)
4174 struct winsize ws;
Bram Moolenaarb13501f2017-07-22 22:32:56 +02004175
Bram Moolenaar1ecc5e42019-01-26 15:12:55 +01004176 ws.ws_col = cols;
4177 ws.ws_row = rows;
4178 ws.ws_xpixel = cols * 5;
4179 ws.ws_ypixel = rows * 10;
4180 retval = ioctl(tty_fd, TIOCSWINSZ, &ws);
4181 ch_log(NULL, "ioctl(TIOCSWINSZ) %s",
4182 retval == 0 ? "success" : "failed");
4183# elif defined(TIOCSSIZE)
4184 struct ttysize ts;
4185
4186 ts.ts_cols = cols;
4187 ts.ts_lines = rows;
4188 retval = ioctl(tty_fd, TIOCSSIZE, &ts);
4189 ch_log(NULL, "ioctl(TIOCSSIZE) %s",
4190 retval == 0 ? "success" : "failed");
Bram Moolenaarb13501f2017-07-22 22:32:56 +02004191# endif
Bram Moolenaar1ecc5e42019-01-26 15:12:55 +01004192 if (tty_fd != fd)
4193 close(tty_fd);
4194 }
4195 return retval == 0 ? OK : FAIL;
Bram Moolenaarb13501f2017-07-22 22:32:56 +02004196}
4197#endif
4198
Bram Moolenaar071d4272004-06-13 20:20:40 +00004199/*
4200 * Try to set the window size to Rows and Columns.
4201 */
4202 void
Bram Moolenaar05540972016-01-30 20:31:25 +01004203mch_set_shellsize(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004204{
4205 if (*T_CWS)
4206 {
4207 /*
4208 * NOTE: if you get an error here that term_set_winsize() is
4209 * undefined, check the output of configure. It could probably not
4210 * find a ncurses, termcap or termlib library.
4211 */
4212 term_set_winsize((int)Rows, (int)Columns);
4213 out_flush();
Bram Moolenaar0f873732019-12-05 20:28:46 +01004214 screen_start(); // don't know where cursor is now
Bram Moolenaar071d4272004-06-13 20:20:40 +00004215 }
4216}
4217
Bram Moolenaar0f873732019-12-05 20:28:46 +01004218#endif // VMS
Bram Moolenaar071d4272004-06-13 20:20:40 +00004219
4220/*
4221 * Rows and/or Columns has changed.
4222 */
4223 void
Bram Moolenaar05540972016-01-30 20:31:25 +01004224mch_new_shellsize(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004225{
Bram Moolenaar0f873732019-12-05 20:28:46 +01004226 // Nothing to do.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004227}
4228
Bram Moolenaar205b8862011-09-07 15:04:31 +02004229/*
4230 * Wait for process "child" to end.
4231 * Return "child" if it exited properly, <= 0 on error.
4232 */
4233 static pid_t
Bram Moolenaar05540972016-01-30 20:31:25 +01004234wait4pid(pid_t child, waitstatus *status)
Bram Moolenaar205b8862011-09-07 15:04:31 +02004235{
4236 pid_t wait_pid = 0;
Bram Moolenaar0abe0522016-08-28 16:53:12 +02004237 long delay_msec = 1;
Bram Moolenaar205b8862011-09-07 15:04:31 +02004238
4239 while (wait_pid != child)
4240 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01004241 // When compiled with Python threads are probably used, in which case
4242 // wait() sometimes hangs for no obvious reason. Use waitpid()
4243 // instead and loop (like the GUI). Also needed for other interfaces,
4244 // they might call system().
Bram Moolenaar6be120e2012-04-20 15:55:16 +02004245# ifdef __NeXT__
Bram Moolenaar205b8862011-09-07 15:04:31 +02004246 wait_pid = wait4(child, status, WNOHANG, (struct rusage *)0);
Bram Moolenaar6be120e2012-04-20 15:55:16 +02004247# else
Bram Moolenaar205b8862011-09-07 15:04:31 +02004248 wait_pid = waitpid(child, status, WNOHANG);
Bram Moolenaar6be120e2012-04-20 15:55:16 +02004249# endif
Bram Moolenaar205b8862011-09-07 15:04:31 +02004250 if (wait_pid == 0)
4251 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01004252 // Wait for 1 to 10 msec before trying again.
Bram Moolenaar0981c872020-08-23 14:28:37 +02004253 mch_delay(delay_msec, MCH_DELAY_IGNOREINPUT | MCH_DELAY_SETTMODE);
Bram Moolenaar0abe0522016-08-28 16:53:12 +02004254 if (++delay_msec > 10)
4255 delay_msec = 10;
Bram Moolenaar205b8862011-09-07 15:04:31 +02004256 continue;
4257 }
Bram Moolenaar205b8862011-09-07 15:04:31 +02004258 if (wait_pid <= 0
4259# ifdef ECHILD
4260 && errno == ECHILD
4261# endif
4262 )
4263 break;
4264 }
4265 return wait_pid;
4266}
4267
Bram Moolenaar509ce2a2016-03-11 22:52:15 +01004268#if !defined(USE_SYSTEM) || defined(FEAT_JOB_CHANNEL)
Bram Moolenaar7da34602017-08-01 17:14:21 +02004269/*
4270 * Set the environment for a child process.
4271 */
Bram Moolenaar6463ca22016-02-13 17:04:46 +01004272 static void
Bram Moolenaar493359e2018-06-12 20:25:52 +02004273set_child_environment(
4274 long rows,
4275 long columns,
4276 char *term,
4277 int is_terminal UNUSED)
Bram Moolenaar6463ca22016-02-13 17:04:46 +01004278{
4279# ifdef HAVE_SETENV
4280 char envbuf[50];
4281# else
Bram Moolenaar5f7e7bd2017-07-22 14:08:43 +02004282 static char envbuf_Term[30];
Bram Moolenaar6463ca22016-02-13 17:04:46 +01004283 static char envbuf_Rows[20];
Bram Moolenaar58556cd2017-07-20 23:04:46 +02004284 static char envbuf_Lines[20];
Bram Moolenaar6463ca22016-02-13 17:04:46 +01004285 static char envbuf_Columns[20];
Bram Moolenaarb7a8dfe2017-07-22 20:33:05 +02004286 static char envbuf_Colors[20];
Bram Moolenaar493359e2018-06-12 20:25:52 +02004287# ifdef FEAT_TERMINAL
Bram Moolenaard7a137f2018-06-12 18:05:24 +02004288 static char envbuf_Version[20];
Bram Moolenaar493359e2018-06-12 20:25:52 +02004289# endif
Bram Moolenaar2a4f06f2017-08-01 18:44:29 +02004290# ifdef FEAT_CLIENTSERVER
Bram Moolenaar7da34602017-08-01 17:14:21 +02004291 static char envbuf_Servername[60];
Bram Moolenaar2a4f06f2017-08-01 18:44:29 +02004292# endif
Bram Moolenaar6463ca22016-02-13 17:04:46 +01004293# endif
4294
Bram Moolenaar6463ca22016-02-13 17:04:46 +01004295# ifdef HAVE_SETENV
Bram Moolenaar58556cd2017-07-20 23:04:46 +02004296 setenv("TERM", term, 1);
4297 sprintf((char *)envbuf, "%ld", rows);
Bram Moolenaar6463ca22016-02-13 17:04:46 +01004298 setenv("ROWS", (char *)envbuf, 1);
Bram Moolenaar58556cd2017-07-20 23:04:46 +02004299 sprintf((char *)envbuf, "%ld", rows);
Bram Moolenaar6463ca22016-02-13 17:04:46 +01004300 setenv("LINES", (char *)envbuf, 1);
Bram Moolenaar58556cd2017-07-20 23:04:46 +02004301 sprintf((char *)envbuf, "%ld", columns);
Bram Moolenaar6463ca22016-02-13 17:04:46 +01004302 setenv("COLUMNS", (char *)envbuf, 1);
Bram Moolenaar759d8152020-04-26 16:52:49 +02004303 sprintf((char *)envbuf, "%d", t_colors);
Bram Moolenaarb7a8dfe2017-07-22 20:33:05 +02004304 setenv("COLORS", (char *)envbuf, 1);
Bram Moolenaar493359e2018-06-12 20:25:52 +02004305# ifdef FEAT_TERMINAL
4306 if (is_terminal)
4307 {
Bram Moolenaar5ecdf962018-06-13 20:49:50 +02004308 sprintf((char *)envbuf, "%ld", (long)get_vim_var_nr(VV_VERSION));
Bram Moolenaar493359e2018-06-12 20:25:52 +02004309 setenv("VIM_TERMINAL", (char *)envbuf, 1);
4310 }
4311# endif
Bram Moolenaar2a4f06f2017-08-01 18:44:29 +02004312# ifdef FEAT_CLIENTSERVER
Bram Moolenaar7da34602017-08-01 17:14:21 +02004313 setenv("VIM_SERVERNAME", serverName == NULL ? "" : (char *)serverName, 1);
Bram Moolenaar2a4f06f2017-08-01 18:44:29 +02004314# endif
Bram Moolenaar6463ca22016-02-13 17:04:46 +01004315# else
4316 /*
4317 * Putenv does not copy the string, it has to remain valid.
4318 * Use a static array to avoid losing allocated memory.
Bram Moolenaar7da34602017-08-01 17:14:21 +02004319 * This won't work well when running multiple children...
Bram Moolenaar6463ca22016-02-13 17:04:46 +01004320 */
Bram Moolenaar58556cd2017-07-20 23:04:46 +02004321 vim_snprintf(envbuf_Term, sizeof(envbuf_Term), "TERM=%s", term);
4322 putenv(envbuf_Term);
4323 vim_snprintf(envbuf_Rows, sizeof(envbuf_Rows), "ROWS=%ld", rows);
Bram Moolenaar6463ca22016-02-13 17:04:46 +01004324 putenv(envbuf_Rows);
Bram Moolenaar58556cd2017-07-20 23:04:46 +02004325 vim_snprintf(envbuf_Lines, sizeof(envbuf_Lines), "LINES=%ld", rows);
4326 putenv(envbuf_Lines);
4327 vim_snprintf(envbuf_Columns, sizeof(envbuf_Columns),
4328 "COLUMNS=%ld", columns);
Bram Moolenaar6463ca22016-02-13 17:04:46 +01004329 putenv(envbuf_Columns);
Bram Moolenaaraffc8fd2020-04-28 21:58:29 +02004330 vim_snprintf(envbuf_Colors, sizeof(envbuf_Colors), "COLORS=%ld", t_colors);
Bram Moolenaarb7a8dfe2017-07-22 20:33:05 +02004331 putenv(envbuf_Colors);
Bram Moolenaar493359e2018-06-12 20:25:52 +02004332# ifdef FEAT_TERMINAL
4333 if (is_terminal)
4334 {
4335 vim_snprintf(envbuf_Version, sizeof(envbuf_Version),
Bram Moolenaar5ecdf962018-06-13 20:49:50 +02004336 "VIM_TERMINAL=%ld", (long)get_vim_var_nr(VV_VERSION));
Bram Moolenaar493359e2018-06-12 20:25:52 +02004337 putenv(envbuf_Version);
4338 }
4339# endif
Bram Moolenaar2a4f06f2017-08-01 18:44:29 +02004340# ifdef FEAT_CLIENTSERVER
Bram Moolenaar7da34602017-08-01 17:14:21 +02004341 vim_snprintf(envbuf_Servername, sizeof(envbuf_Servername),
4342 "VIM_SERVERNAME=%s", serverName == NULL ? "" : (char *)serverName);
4343 putenv(envbuf_Servername);
Bram Moolenaar2a4f06f2017-08-01 18:44:29 +02004344# endif
Bram Moolenaar6463ca22016-02-13 17:04:46 +01004345# endif
4346}
Bram Moolenaar58556cd2017-07-20 23:04:46 +02004347
4348 static void
Bram Moolenaar493359e2018-06-12 20:25:52 +02004349set_default_child_environment(int is_terminal)
Bram Moolenaar58556cd2017-07-20 23:04:46 +02004350{
Bram Moolenaar493359e2018-06-12 20:25:52 +02004351 set_child_environment(Rows, Columns, "dumb", is_terminal);
Bram Moolenaar58556cd2017-07-20 23:04:46 +02004352}
Bram Moolenaar6463ca22016-02-13 17:04:46 +01004353#endif
4354
Bram Moolenaar5a1feb82017-07-22 18:04:08 +02004355#if defined(FEAT_GUI) || defined(FEAT_JOB_CHANNEL)
Bram Moolenaar979e8c52017-08-01 15:08:07 +02004356/*
4357 * Open a PTY, with FD for the master and slave side.
4358 * When failing "pty_master_fd" and "pty_slave_fd" are -1.
Bram Moolenaar59386482019-02-10 22:43:46 +01004359 * When successful both file descriptors are stored and the allocated pty name
4360 * is stored in both "*name1" and "*name2".
Bram Moolenaar979e8c52017-08-01 15:08:07 +02004361 */
Bram Moolenaar5a1feb82017-07-22 18:04:08 +02004362 static void
Bram Moolenaar59386482019-02-10 22:43:46 +01004363open_pty(int *pty_master_fd, int *pty_slave_fd, char_u **name1, char_u **name2)
Bram Moolenaar5a1feb82017-07-22 18:04:08 +02004364{
4365 char *tty_name;
4366
Bram Moolenaar59386482019-02-10 22:43:46 +01004367 if (name1 != NULL)
4368 *name1 = NULL;
4369 if (name2 != NULL)
4370 *name2 = NULL;
4371
Bram Moolenaar1ecc5e42019-01-26 15:12:55 +01004372 *pty_master_fd = mch_openpty(&tty_name); // open pty
Bram Moolenaar5a1feb82017-07-22 18:04:08 +02004373 if (*pty_master_fd >= 0)
4374 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01004375 // Leaving out O_NOCTTY may lead to waitpid() always returning
4376 // 0 on Mac OS X 10.7 thereby causing freezes. Let's assume
4377 // adding O_NOCTTY always works when defined.
Bram Moolenaar5a1feb82017-07-22 18:04:08 +02004378#ifdef O_NOCTTY
4379 *pty_slave_fd = open(tty_name, O_RDWR | O_NOCTTY | O_EXTRA, 0);
4380#else
4381 *pty_slave_fd = open(tty_name, O_RDWR | O_EXTRA, 0);
4382#endif
4383 if (*pty_slave_fd < 0)
4384 {
4385 close(*pty_master_fd);
4386 *pty_master_fd = -1;
4387 }
Bram Moolenaar59386482019-02-10 22:43:46 +01004388 else
4389 {
4390 if (name1 != NULL)
4391 *name1 = vim_strsave((char_u *)tty_name);
4392 if (name2 != NULL)
4393 *name2 = vim_strsave((char_u *)tty_name);
4394 }
Bram Moolenaar5a1feb82017-07-22 18:04:08 +02004395 }
4396}
4397#endif
4398
Bram Moolenaarfae42832017-08-01 22:24:26 +02004399/*
4400 * Send SIGINT to a child process if "c" is an interrupt character.
4401 */
Bram Moolenaar840d16f2019-09-10 21:27:18 +02004402 static void
Bram Moolenaarfae42832017-08-01 22:24:26 +02004403may_send_sigint(int c UNUSED, pid_t pid UNUSED, pid_t wpid UNUSED)
4404{
4405# ifdef SIGINT
4406 if (c == Ctrl_C || c == intr_char)
4407 {
4408# ifdef HAVE_SETSID
4409 kill(-pid, SIGINT);
4410# else
4411 kill(0, SIGINT);
4412# endif
4413 if (wpid > 0)
4414 kill(wpid, SIGINT);
4415 }
4416# endif
4417}
4418
Bram Moolenaar197c6b72019-11-03 23:37:12 +01004419#if !defined(USE_SYSTEM) || defined(FEAT_TERMINAL) || defined(PROTO)
Bram Moolenaar13568252018-03-16 20:46:58 +01004420
Bram Moolenaar0f873732019-12-05 20:28:46 +01004421/*
4422 * Parse "cmd" and return the result in "argvp" which is an allocated array of
4423 * pointers, the last one is NULL.
4424 * The "sh_tofree" and "shcf_tofree" must be later freed by the caller.
4425 */
Bram Moolenaar197c6b72019-11-03 23:37:12 +01004426 int
4427unix_build_argv(
Bram Moolenaar13568252018-03-16 20:46:58 +01004428 char_u *cmd,
4429 char ***argvp,
4430 char_u **sh_tofree,
4431 char_u **shcf_tofree)
4432{
4433 char **argv = NULL;
4434 int argc;
4435
4436 *sh_tofree = vim_strsave(p_sh);
Bram Moolenaar0f873732019-12-05 20:28:46 +01004437 if (*sh_tofree == NULL) // out of memory
Bram Moolenaar13568252018-03-16 20:46:58 +01004438 return FAIL;
4439
4440 if (mch_parse_cmd(*sh_tofree, TRUE, &argv, &argc) == FAIL)
4441 return FAIL;
4442 *argvp = argv;
4443
4444 if (cmd != NULL)
4445 {
4446 char_u *s;
4447 char_u *p;
4448
4449 if (extra_shell_arg != NULL)
4450 argv[argc++] = (char *)extra_shell_arg;
4451
Bram Moolenaar0f873732019-12-05 20:28:46 +01004452 // Break 'shellcmdflag' into white separated parts. This doesn't
4453 // handle quoted strings, they are very unlikely to appear.
Bram Moolenaar964b3742019-05-24 18:54:09 +02004454 *shcf_tofree = alloc(STRLEN(p_shcf) + 1);
Bram Moolenaar0f873732019-12-05 20:28:46 +01004455 if (*shcf_tofree == NULL) // out of memory
Bram Moolenaar13568252018-03-16 20:46:58 +01004456 return FAIL;
4457 s = *shcf_tofree;
4458 p = p_shcf;
4459 while (*p != NUL)
4460 {
4461 argv[argc++] = (char *)s;
4462 while (*p && *p != ' ' && *p != TAB)
4463 *s++ = *p++;
4464 *s++ = NUL;
4465 p = skipwhite(p);
4466 }
4467
4468 argv[argc++] = (char *)cmd;
4469 }
4470 argv[argc] = NULL;
4471 return OK;
4472}
4473#endif
4474
4475#if defined(FEAT_GUI) && defined(FEAT_TERMINAL)
4476/*
4477 * Use a terminal window to run a shell command in.
4478 */
4479 static int
4480mch_call_shell_terminal(
4481 char_u *cmd,
Bram Moolenaar0f873732019-12-05 20:28:46 +01004482 int options UNUSED) // SHELL_*, see vim.h
Bram Moolenaar13568252018-03-16 20:46:58 +01004483{
4484 jobopt_T opt;
4485 char **argv = NULL;
4486 char_u *tofree1 = NULL;
4487 char_u *tofree2 = NULL;
4488 int retval = -1;
4489 buf_T *buf;
Bram Moolenaarf9c38832018-06-19 19:59:20 +02004490 job_T *job;
Bram Moolenaar13568252018-03-16 20:46:58 +01004491 aco_save_T aco;
Bram Moolenaar0f873732019-12-05 20:28:46 +01004492 oparg_T oa; // operator arguments
Bram Moolenaar13568252018-03-16 20:46:58 +01004493
Bram Moolenaar197c6b72019-11-03 23:37:12 +01004494 if (unix_build_argv(cmd, &argv, &tofree1, &tofree2) == FAIL)
Bram Moolenaar13568252018-03-16 20:46:58 +01004495 goto theend;
4496
4497 init_job_options(&opt);
4498 ch_log(NULL, "starting terminal for system command '%s'", cmd);
4499 buf = term_start(NULL, argv, &opt, TERM_START_SYSTEM);
Bram Moolenaarf9c38832018-06-19 19:59:20 +02004500 if (buf == NULL)
4501 goto theend;
4502
4503 job = term_getjob(buf->b_term);
4504 ++job->jv_refcount;
Bram Moolenaar13568252018-03-16 20:46:58 +01004505
Bram Moolenaar0f873732019-12-05 20:28:46 +01004506 // Find a window to make "buf" curbuf.
Bram Moolenaar13568252018-03-16 20:46:58 +01004507 aucmd_prepbuf(&aco, buf);
4508
4509 clear_oparg(&oa);
4510 while (term_use_loop())
4511 {
4512 if (oa.op_type == OP_NOP && oa.regname == NUL && !VIsual_active)
4513 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01004514 // If terminal_loop() returns OK we got a key that is handled
4515 // in Normal model. We don't do redrawing anyway.
Bram Moolenaar13568252018-03-16 20:46:58 +01004516 if (terminal_loop(TRUE) == OK)
4517 normal_cmd(&oa, TRUE);
4518 }
4519 else
4520 normal_cmd(&oa, TRUE);
4521 }
Bram Moolenaarf9c38832018-06-19 19:59:20 +02004522 retval = job->jv_exitval;
Bram Moolenaar13568252018-03-16 20:46:58 +01004523 ch_log(NULL, "system command finished");
4524
Bram Moolenaarf9c38832018-06-19 19:59:20 +02004525 job_unref(job);
4526
Bram Moolenaar0f873732019-12-05 20:28:46 +01004527 // restore curwin/curbuf and a few other things
Bram Moolenaar13568252018-03-16 20:46:58 +01004528 aucmd_restbuf(&aco);
4529
4530 wait_return(TRUE);
4531 do_buffer(DOBUF_WIPE, DOBUF_FIRST, FORWARD, buf->b_fnum, TRUE);
4532
4533theend:
4534 vim_free(argv);
4535 vim_free(tofree1);
4536 vim_free(tofree2);
4537 return retval;
4538}
4539#endif
4540
4541#ifdef USE_SYSTEM
4542/*
4543 * Use system() to start the shell: simple but slow.
4544 */
4545 static int
4546mch_call_shell_system(
Bram Moolenaar05540972016-01-30 20:31:25 +01004547 char_u *cmd,
Bram Moolenaar0f873732019-12-05 20:28:46 +01004548 int options) // SHELL_*, see vim.h
Bram Moolenaar071d4272004-06-13 20:20:40 +00004549{
4550#ifdef VMS
4551 char *ifn = NULL;
4552 char *ofn = NULL;
4553#endif
Bram Moolenaar26e86442020-05-17 14:06:16 +02004554 tmode_T tmode = cur_tmode;
Bram Moolenaar0f873732019-12-05 20:28:46 +01004555 char_u *newcmd; // only needed for unix
Bram Moolenaarde5e2c22016-11-04 20:35:31 +01004556 int x;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004557
4558 out_flush();
4559
4560 if (options & SHELL_COOKED)
Bram Moolenaar0f873732019-12-05 20:28:46 +01004561 settmode(TMODE_COOK); // set to normal mode
Bram Moolenaar071d4272004-06-13 20:20:40 +00004562
Bram Moolenaar62b42182010-09-21 22:09:37 +02004563# if defined(FEAT_CLIPBOARD) && defined(FEAT_X11)
Bram Moolenaar1a0316c2013-03-13 17:50:25 +01004564 save_clipboard();
Bram Moolenaar62b42182010-09-21 22:09:37 +02004565 loose_clipboard();
4566# endif
4567
Bram Moolenaar071d4272004-06-13 20:20:40 +00004568 if (cmd == NULL)
4569 x = system((char *)p_sh);
4570 else
4571 {
Bram Moolenaara06ecab2016-07-16 14:47:36 +02004572# ifdef VMS
Bram Moolenaar071d4272004-06-13 20:20:40 +00004573 if (ofn = strchr((char *)cmd, '>'))
4574 *ofn++ = '\0';
4575 if (ifn = strchr((char *)cmd, '<'))
4576 {
4577 char *p;
4578
4579 *ifn++ = '\0';
Bram Moolenaar0f873732019-12-05 20:28:46 +01004580 p = strchr(ifn,' '); // chop off any trailing spaces
Bram Moolenaar071d4272004-06-13 20:20:40 +00004581 if (p)
4582 *p = '\0';
4583 }
4584 if (ofn)
4585 x = vms_sys((char *)cmd, ofn, ifn);
4586 else
4587 x = system((char *)cmd);
Bram Moolenaara06ecab2016-07-16 14:47:36 +02004588# else
Bram Moolenaar18a4ba22019-05-24 19:39:03 +02004589 newcmd = alloc(STRLEN(p_sh)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004590 + (extra_shell_arg == NULL ? 0 : STRLEN(extra_shell_arg))
Bram Moolenaar18a4ba22019-05-24 19:39:03 +02004591 + STRLEN(p_shcf) + STRLEN(cmd) + 4);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004592 if (newcmd == NULL)
4593 x = 0;
4594 else
4595 {
4596 sprintf((char *)newcmd, "%s %s %s %s", p_sh,
4597 extra_shell_arg == NULL ? "" : (char *)extra_shell_arg,
4598 (char *)p_shcf,
4599 (char *)cmd);
4600 x = system((char *)newcmd);
4601 vim_free(newcmd);
4602 }
Bram Moolenaara06ecab2016-07-16 14:47:36 +02004603# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004604 }
4605# ifdef VMS
4606 x = vms_sys_status(x);
4607# endif
4608 if (emsg_silent)
4609 ;
4610 else if (x == 127)
Bram Moolenaar32526b32019-01-19 17:43:09 +01004611 msg_puts(_("\nCannot execute shell sh\n"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00004612 else if (x && !(options & SHELL_SILENT))
4613 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01004614 msg_puts(_("\nshell returned "));
Bram Moolenaar071d4272004-06-13 20:20:40 +00004615 msg_outnum((long)x);
4616 msg_putchar('\n');
4617 }
4618
4619 if (tmode == TMODE_RAW)
Bram Moolenaar3b1f18f2020-05-16 23:15:08 +02004620 {
4621 // The shell may have messed with the mode, always set it.
4622 cur_tmode = TMODE_UNKNOWN;
Bram Moolenaar0f873732019-12-05 20:28:46 +01004623 settmode(TMODE_RAW); // set to raw mode
Bram Moolenaar3b1f18f2020-05-16 23:15:08 +02004624 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004625 resettitle();
Bram Moolenaar1a0316c2013-03-13 17:50:25 +01004626# if defined(FEAT_CLIPBOARD) && defined(FEAT_X11)
4627 restore_clipboard();
4628# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004629 return x;
Bram Moolenaar13568252018-03-16 20:46:58 +01004630}
Bram Moolenaar071d4272004-06-13 20:20:40 +00004631
Bram Moolenaar0f873732019-12-05 20:28:46 +01004632#else // USE_SYSTEM
Bram Moolenaar071d4272004-06-13 20:20:40 +00004633
Bram Moolenaar0f873732019-12-05 20:28:46 +01004634# define EXEC_FAILED 122 // Exit code when shell didn't execute. Don't use
4635 // 127, some shells use that already
4636# define OPEN_NULL_FAILED 123 // Exit code if /dev/null can't be opened
Bram Moolenaar071d4272004-06-13 20:20:40 +00004637
Bram Moolenaar13568252018-03-16 20:46:58 +01004638/*
4639 * Don't use system(), use fork()/exec().
4640 */
4641 static int
4642mch_call_shell_fork(
4643 char_u *cmd,
Bram Moolenaar0f873732019-12-05 20:28:46 +01004644 int options) // SHELL_*, see vim.h
Bram Moolenaar13568252018-03-16 20:46:58 +01004645{
Bram Moolenaar26e86442020-05-17 14:06:16 +02004646 tmode_T tmode = cur_tmode;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004647 pid_t pid;
Bram Moolenaardf177f62005-02-22 08:39:57 +00004648 pid_t wpid = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004649 pid_t wait_pid = 0;
4650# ifdef HAVE_UNION_WAIT
4651 union wait status;
4652# else
4653 int status = -1;
4654# endif
4655 int retval = -1;
4656 char **argv = NULL;
Bram Moolenaar13568252018-03-16 20:46:58 +01004657 char_u *tofree1 = NULL;
4658 char_u *tofree2 = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004659 int i;
Bram Moolenaar0f873732019-12-05 20:28:46 +01004660 int pty_master_fd = -1; // for pty's
Bram Moolenaardf177f62005-02-22 08:39:57 +00004661# ifdef FEAT_GUI
Bram Moolenaar071d4272004-06-13 20:20:40 +00004662 int pty_slave_fd = -1;
Bram Moolenaardf177f62005-02-22 08:39:57 +00004663# endif
Bram Moolenaar0f873732019-12-05 20:28:46 +01004664 int fd_toshell[2]; // for pipes
Bram Moolenaar071d4272004-06-13 20:20:40 +00004665 int fd_fromshell[2];
4666 int pipe_error = FALSE;
Bram Moolenaar0f873732019-12-05 20:28:46 +01004667 int did_settmode = FALSE; // settmode(TMODE_RAW) called
Bram Moolenaar071d4272004-06-13 20:20:40 +00004668
4669 out_flush();
4670 if (options & SHELL_COOKED)
Bram Moolenaar0f873732019-12-05 20:28:46 +01004671 settmode(TMODE_COOK); // set to normal mode
Bram Moolenaar3b1f18f2020-05-16 23:15:08 +02004672 if (tmode == TMODE_RAW)
4673 // The shell may have messed with the mode, always set it later.
4674 cur_tmode = TMODE_UNKNOWN;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004675
Bram Moolenaar197c6b72019-11-03 23:37:12 +01004676 if (unix_build_argv(cmd, &argv, &tofree1, &tofree2) == FAIL)
Bram Moolenaar835dc632016-02-07 14:27:38 +01004677 goto error;
Bram Moolenaarea35ef62011-08-04 22:59:28 +02004678
Bram Moolenaar071d4272004-06-13 20:20:40 +00004679 /*
Bram Moolenaardf177f62005-02-22 08:39:57 +00004680 * For the GUI, when writing the output into the buffer and when reading
4681 * input from the buffer: Try using a pseudo-tty to get the stdin/stdout
4682 * of the executed command into the Vim window. Or use a pipe.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004683 */
Bram Moolenaardf177f62005-02-22 08:39:57 +00004684 if ((options & (SHELL_READ|SHELL_WRITE))
4685# ifdef FEAT_GUI
4686 || (gui.in_use && show_shell_mess)
4687# endif
4688 )
Bram Moolenaar071d4272004-06-13 20:20:40 +00004689 {
Bram Moolenaardf177f62005-02-22 08:39:57 +00004690# ifdef FEAT_GUI
Bram Moolenaar071d4272004-06-13 20:20:40 +00004691 /*
4692 * Try to open a master pty.
4693 * If this works, open the slave pty.
4694 * If the slave can't be opened, close the master pty.
4695 */
Bram Moolenaardf177f62005-02-22 08:39:57 +00004696 if (p_guipty && !(options & (SHELL_READ|SHELL_WRITE)))
Bram Moolenaar59386482019-02-10 22:43:46 +01004697 open_pty(&pty_master_fd, &pty_slave_fd, NULL, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004698 /*
4699 * If not opening a pty or it didn't work, try using pipes.
4700 */
4701 if (pty_master_fd < 0)
Bram Moolenaardf177f62005-02-22 08:39:57 +00004702# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004703 {
4704 pipe_error = (pipe(fd_toshell) < 0);
Bram Moolenaar0f873732019-12-05 20:28:46 +01004705 if (!pipe_error) // pipe create OK
Bram Moolenaar071d4272004-06-13 20:20:40 +00004706 {
4707 pipe_error = (pipe(fd_fromshell) < 0);
Bram Moolenaar0f873732019-12-05 20:28:46 +01004708 if (pipe_error) // pipe create failed
Bram Moolenaar071d4272004-06-13 20:20:40 +00004709 {
4710 close(fd_toshell[0]);
4711 close(fd_toshell[1]);
4712 }
4713 }
4714 if (pipe_error)
4715 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01004716 msg_puts(_("\nCannot create pipes\n"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00004717 out_flush();
4718 }
4719 }
4720 }
4721
Bram Moolenaar0f873732019-12-05 20:28:46 +01004722 if (!pipe_error) // pty or pipe opened or not used
Bram Moolenaar071d4272004-06-13 20:20:40 +00004723 {
Bram Moolenaarbb09ceb2016-10-18 16:27:23 +02004724 SIGSET_DECL(curset)
Bram Moolenaarbb09ceb2016-10-18 16:27:23 +02004725 BLOCK_SIGNALS(&curset);
Bram Moolenaar0f873732019-12-05 20:28:46 +01004726 pid = fork(); // maybe we should use vfork()
Bram Moolenaarbb09ceb2016-10-18 16:27:23 +02004727 if (pid == -1)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004728 {
Bram Moolenaarbb09ceb2016-10-18 16:27:23 +02004729 UNBLOCK_SIGNALS(&curset);
4730
Bram Moolenaar32526b32019-01-19 17:43:09 +01004731 msg_puts(_("\nCannot fork\n"));
Bram Moolenaardf177f62005-02-22 08:39:57 +00004732 if ((options & (SHELL_READ|SHELL_WRITE))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004733# ifdef FEAT_GUI
Bram Moolenaardf177f62005-02-22 08:39:57 +00004734 || (gui.in_use && show_shell_mess)
4735# endif
4736 )
Bram Moolenaar071d4272004-06-13 20:20:40 +00004737 {
Bram Moolenaardf177f62005-02-22 08:39:57 +00004738# ifdef FEAT_GUI
Bram Moolenaar0f873732019-12-05 20:28:46 +01004739 if (pty_master_fd >= 0) // close the pseudo tty
Bram Moolenaar071d4272004-06-13 20:20:40 +00004740 {
4741 close(pty_master_fd);
4742 close(pty_slave_fd);
4743 }
Bram Moolenaar0f873732019-12-05 20:28:46 +01004744 else // close the pipes
Bram Moolenaardf177f62005-02-22 08:39:57 +00004745# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004746 {
4747 close(fd_toshell[0]);
4748 close(fd_toshell[1]);
4749 close(fd_fromshell[0]);
4750 close(fd_fromshell[1]);
4751 }
4752 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004753 }
Bram Moolenaar0f873732019-12-05 20:28:46 +01004754 else if (pid == 0) // child
Bram Moolenaar071d4272004-06-13 20:20:40 +00004755 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01004756 reset_signals(); // handle signals normally
Bram Moolenaarbb09ceb2016-10-18 16:27:23 +02004757 UNBLOCK_SIGNALS(&curset);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004758
Bram Moolenaar819524702018-02-27 19:10:00 +01004759# ifdef FEAT_JOB_CHANNEL
4760 if (ch_log_active())
Bram Moolenaar76603ba2020-08-30 17:24:37 +02004761 {
4762 ch_log(NULL, "closing channel log in the child process");
Bram Moolenaar819524702018-02-27 19:10:00 +01004763 ch_logfile((char_u *)"", (char_u *)"");
Bram Moolenaar76603ba2020-08-30 17:24:37 +02004764 }
Bram Moolenaar819524702018-02-27 19:10:00 +01004765# endif
4766
Bram Moolenaar071d4272004-06-13 20:20:40 +00004767 if (!show_shell_mess || (options & SHELL_EXPAND))
4768 {
4769 int fd;
4770
4771 /*
4772 * Don't want to show any message from the shell. Can't just
4773 * close stdout and stderr though, because some systems will
4774 * break if you try to write to them after that, so we must
4775 * use dup() to replace them with something else -- webb
4776 * Connect stdin to /dev/null too, so ":n `cat`" doesn't hang,
4777 * waiting for input.
4778 */
4779 fd = open("/dev/null", O_RDWR | O_EXTRA, 0);
4780 fclose(stdin);
4781 fclose(stdout);
4782 fclose(stderr);
4783
4784 /*
4785 * If any of these open()'s and dup()'s fail, we just continue
4786 * anyway. It's not fatal, and on most systems it will make
4787 * no difference at all. On a few it will cause the execvp()
4788 * to exit with a non-zero status even when the completion
4789 * could be done, which is nothing too serious. If the open()
4790 * or dup() failed we'd just do the same thing ourselves
4791 * anyway -- webb
4792 */
4793 if (fd >= 0)
4794 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01004795 vim_ignored = dup(fd); // To replace stdin (fd 0)
4796 vim_ignored = dup(fd); // To replace stdout (fd 1)
4797 vim_ignored = dup(fd); // To replace stderr (fd 2)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004798
Bram Moolenaar0f873732019-12-05 20:28:46 +01004799 // Don't need this now that we've duplicated it
Bram Moolenaar071d4272004-06-13 20:20:40 +00004800 close(fd);
4801 }
4802 }
Bram Moolenaardf177f62005-02-22 08:39:57 +00004803 else if ((options & (SHELL_READ|SHELL_WRITE))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004804# ifdef FEAT_GUI
Bram Moolenaardf177f62005-02-22 08:39:57 +00004805 || gui.in_use
4806# endif
4807 )
Bram Moolenaar071d4272004-06-13 20:20:40 +00004808 {
4809
Bram Moolenaardf177f62005-02-22 08:39:57 +00004810# ifdef HAVE_SETSID
Bram Moolenaar0f873732019-12-05 20:28:46 +01004811 // Create our own process group, so that the child and all its
4812 // children can be kill()ed. Don't do this when using pipes,
4813 // because stdin is not a tty, we would lose /dev/tty.
Bram Moolenaar12033fb2005-12-16 21:49:31 +00004814 if (p_stmp)
Bram Moolenaar07256082009-02-04 13:19:42 +00004815 {
Bram Moolenaar12033fb2005-12-16 21:49:31 +00004816 (void)setsid();
Bram Moolenaar07256082009-02-04 13:19:42 +00004817# if defined(SIGHUP)
Bram Moolenaar0f873732019-12-05 20:28:46 +01004818 // When doing "!xterm&" and 'shell' is bash: the shell
4819 // will exit and send SIGHUP to all processes in its
4820 // group, killing the just started process. Ignore SIGHUP
4821 // to avoid that. (suggested by Simon Schubert)
Bram Moolenaar07256082009-02-04 13:19:42 +00004822 signal(SIGHUP, SIG_IGN);
4823# endif
4824 }
Bram Moolenaardf177f62005-02-22 08:39:57 +00004825# endif
4826# ifdef FEAT_GUI
Bram Moolenaar12033fb2005-12-16 21:49:31 +00004827 if (pty_slave_fd >= 0)
4828 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01004829 // push stream discipline modules
Bram Moolenaar12033fb2005-12-16 21:49:31 +00004830 if (options & SHELL_COOKED)
Bram Moolenaar1ecc5e42019-01-26 15:12:55 +01004831 setup_slavepty(pty_slave_fd);
Bram Moolenaarfff10d92021-10-13 10:05:30 +01004832# ifdef TIOCSCTTY
4833 // Try to become controlling tty (probably doesn't work,
4834 // unless run by root)
4835 ioctl(pty_slave_fd, TIOCSCTTY, (char *)NULL);
4836# endif
Bram Moolenaar12033fb2005-12-16 21:49:31 +00004837 }
Bram Moolenaardf177f62005-02-22 08:39:57 +00004838# endif
Bram Moolenaar493359e2018-06-12 20:25:52 +02004839 set_default_child_environment(FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004840
Bram Moolenaara5792f52005-11-23 21:25:05 +00004841 /*
4842 * stderr is only redirected when using the GUI, so that a
4843 * program like gpg can still access the terminal to get a
4844 * passphrase using stderr.
4845 */
Bram Moolenaardf177f62005-02-22 08:39:57 +00004846# ifdef FEAT_GUI
Bram Moolenaar071d4272004-06-13 20:20:40 +00004847 if (pty_master_fd >= 0)
4848 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01004849 close(pty_master_fd); // close master side of pty
Bram Moolenaar071d4272004-06-13 20:20:40 +00004850
Bram Moolenaar0f873732019-12-05 20:28:46 +01004851 // set up stdin/stdout/stderr for the child
Bram Moolenaar071d4272004-06-13 20:20:40 +00004852 close(0);
Bram Moolenaar42335f52018-09-13 15:33:43 +02004853 vim_ignored = dup(pty_slave_fd);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004854 close(1);
Bram Moolenaar42335f52018-09-13 15:33:43 +02004855 vim_ignored = dup(pty_slave_fd);
Bram Moolenaara5792f52005-11-23 21:25:05 +00004856 if (gui.in_use)
4857 {
4858 close(2);
Bram Moolenaar42335f52018-09-13 15:33:43 +02004859 vim_ignored = dup(pty_slave_fd);
Bram Moolenaara5792f52005-11-23 21:25:05 +00004860 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004861
Bram Moolenaar0f873732019-12-05 20:28:46 +01004862 close(pty_slave_fd); // has been dupped, close it now
Bram Moolenaar071d4272004-06-13 20:20:40 +00004863 }
4864 else
Bram Moolenaardf177f62005-02-22 08:39:57 +00004865# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004866 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01004867 // set up stdin for the child
Bram Moolenaar071d4272004-06-13 20:20:40 +00004868 close(fd_toshell[1]);
4869 close(0);
Bram Moolenaar42335f52018-09-13 15:33:43 +02004870 vim_ignored = dup(fd_toshell[0]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004871 close(fd_toshell[0]);
4872
Bram Moolenaar0f873732019-12-05 20:28:46 +01004873 // set up stdout for the child
Bram Moolenaar071d4272004-06-13 20:20:40 +00004874 close(fd_fromshell[0]);
4875 close(1);
Bram Moolenaar42335f52018-09-13 15:33:43 +02004876 vim_ignored = dup(fd_fromshell[1]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004877 close(fd_fromshell[1]);
4878
Bram Moolenaara5792f52005-11-23 21:25:05 +00004879# ifdef FEAT_GUI
4880 if (gui.in_use)
4881 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01004882 // set up stderr for the child
Bram Moolenaara5792f52005-11-23 21:25:05 +00004883 close(2);
Bram Moolenaar42335f52018-09-13 15:33:43 +02004884 vim_ignored = dup(1);
Bram Moolenaara5792f52005-11-23 21:25:05 +00004885 }
4886# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004887 }
4888 }
Bram Moolenaardf177f62005-02-22 08:39:57 +00004889
Bram Moolenaar071d4272004-06-13 20:20:40 +00004890 /*
4891 * There is no type cast for the argv, because the type may be
4892 * different on different machines. This may cause a warning
4893 * message with strict compilers, don't worry about it.
4894 * Call _exit() instead of exit() to avoid closing the connection
4895 * to the X server (esp. with GTK, which uses atexit()).
4896 */
4897 execvp(argv[0], argv);
Bram Moolenaar0f873732019-12-05 20:28:46 +01004898 _exit(EXEC_FAILED); // exec failed, return failure code
Bram Moolenaar071d4272004-06-13 20:20:40 +00004899 }
Bram Moolenaar0f873732019-12-05 20:28:46 +01004900 else // parent
Bram Moolenaar071d4272004-06-13 20:20:40 +00004901 {
4902 /*
4903 * While child is running, ignore terminating signals.
Bram Moolenaardf177f62005-02-22 08:39:57 +00004904 * Do catch CTRL-C, so that "got_int" is set.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004905 */
4906 catch_signals(SIG_IGN, SIG_ERR);
Bram Moolenaardf177f62005-02-22 08:39:57 +00004907 catch_int_signal();
Bram Moolenaarbb09ceb2016-10-18 16:27:23 +02004908 UNBLOCK_SIGNALS(&curset);
Bram Moolenaarc4d4ac22016-11-07 22:42:57 +01004909# ifdef FEAT_JOB_CHANNEL
4910 ++dont_check_job_ended;
4911# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004912 /*
4913 * For the GUI we redirect stdin, stdout and stderr to our window.
Bram Moolenaardf177f62005-02-22 08:39:57 +00004914 * This is also used to pipe stdin/stdout to/from the external
4915 * command.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004916 */
Bram Moolenaardf177f62005-02-22 08:39:57 +00004917 if ((options & (SHELL_READ|SHELL_WRITE))
4918# ifdef FEAT_GUI
4919 || (gui.in_use && show_shell_mess)
4920# endif
4921 )
Bram Moolenaar071d4272004-06-13 20:20:40 +00004922 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01004923# define BUFLEN 100 // length for buffer, pseudo tty limit is 128
Bram Moolenaar071d4272004-06-13 20:20:40 +00004924 char_u buffer[BUFLEN + 1];
Bram Moolenaar0f873732019-12-05 20:28:46 +01004925 int buffer_off = 0; // valid bytes in buffer[]
4926 char_u ta_buf[BUFLEN + 1]; // TypeAHead
4927 int ta_len = 0; // valid bytes in ta_buf[]
Bram Moolenaar071d4272004-06-13 20:20:40 +00004928 int len;
4929 int p_more_save;
4930 int old_State;
4931 int c;
4932 int toshell_fd;
4933 int fromshell_fd;
Bram Moolenaardf177f62005-02-22 08:39:57 +00004934 garray_T ga;
4935 int noread_cnt;
Bram Moolenaar1ac56c22019-01-17 22:28:22 +01004936# ifdef ELAPSED_FUNC
4937 elapsed_T start_tv;
Bram Moolenaarb3dc8fd2009-02-22 01:52:59 +00004938# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004939
Bram Moolenaardf177f62005-02-22 08:39:57 +00004940# ifdef FEAT_GUI
Bram Moolenaar071d4272004-06-13 20:20:40 +00004941 if (pty_master_fd >= 0)
4942 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00004943 fromshell_fd = pty_master_fd;
4944 toshell_fd = dup(pty_master_fd);
4945 }
4946 else
Bram Moolenaardf177f62005-02-22 08:39:57 +00004947# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004948 {
4949 close(fd_toshell[0]);
4950 close(fd_fromshell[1]);
4951 toshell_fd = fd_toshell[1];
4952 fromshell_fd = fd_fromshell[0];
4953 }
4954
4955 /*
4956 * Write to the child if there are typed characters.
4957 * Read from the child if there are characters available.
4958 * Repeat the reading a few times if more characters are
4959 * available. Need to check for typed keys now and then, but
4960 * not too often (delays when no chars are available).
4961 * This loop is quit if no characters can be read from the pty
4962 * (WaitForChar detected special condition), or there are no
4963 * characters available and the child has exited.
4964 * Only check if the child has exited when there is no more
4965 * output. The child may exit before all the output has
4966 * been printed.
4967 *
4968 * Currently this busy loops!
4969 * This can probably dead-lock when the write blocks!
4970 */
4971 p_more_save = p_more;
4972 p_more = FALSE;
4973 old_State = State;
Bram Moolenaar0f873732019-12-05 20:28:46 +01004974 State = EXTERNCMD; // don't redraw at window resize
Bram Moolenaar071d4272004-06-13 20:20:40 +00004975
Bram Moolenaar12033fb2005-12-16 21:49:31 +00004976 if ((options & SHELL_WRITE) && toshell_fd >= 0)
Bram Moolenaardf177f62005-02-22 08:39:57 +00004977 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01004978 // Fork a process that will write the lines to the
4979 // external program.
Bram Moolenaardf177f62005-02-22 08:39:57 +00004980 if ((wpid = fork()) == -1)
4981 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01004982 msg_puts(_("\nCannot fork\n"));
Bram Moolenaardf177f62005-02-22 08:39:57 +00004983 }
Bram Moolenaar0f873732019-12-05 20:28:46 +01004984 else if (wpid == 0) // child
Bram Moolenaardf177f62005-02-22 08:39:57 +00004985 {
4986 linenr_T lnum = curbuf->b_op_start.lnum;
4987 int written = 0;
Bram Moolenaar89d40322006-08-29 15:30:07 +00004988 char_u *lp = ml_get(lnum);
Bram Moolenaardf177f62005-02-22 08:39:57 +00004989 size_t l;
4990
Bram Moolenaar8cd06ca2005-02-28 22:44:58 +00004991 close(fromshell_fd);
Bram Moolenaardf177f62005-02-22 08:39:57 +00004992 for (;;)
4993 {
Bram Moolenaar89d40322006-08-29 15:30:07 +00004994 l = STRLEN(lp + written);
Bram Moolenaardf177f62005-02-22 08:39:57 +00004995 if (l == 0)
4996 len = 0;
Bram Moolenaar89d40322006-08-29 15:30:07 +00004997 else if (lp[written] == NL)
Bram Moolenaar0f873732019-12-05 20:28:46 +01004998 // NL -> NUL translation
Bram Moolenaardf177f62005-02-22 08:39:57 +00004999 len = write(toshell_fd, "", (size_t)1);
5000 else
5001 {
Bram Moolenaar70b2a562012-01-10 22:26:17 +01005002 char_u *s = vim_strchr(lp + written, NL);
5003
Bram Moolenaar89d40322006-08-29 15:30:07 +00005004 len = write(toshell_fd, (char *)lp + written,
Bram Moolenaar78a15312009-05-15 19:33:18 +00005005 s == NULL ? l
5006 : (size_t)(s - (lp + written)));
Bram Moolenaardf177f62005-02-22 08:39:57 +00005007 }
Bram Moolenaar78a15312009-05-15 19:33:18 +00005008 if (len == (int)l)
Bram Moolenaardf177f62005-02-22 08:39:57 +00005009 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01005010 // Finished a line, add a NL, unless this line
5011 // should not have one.
Bram Moolenaardf177f62005-02-22 08:39:57 +00005012 if (lnum != curbuf->b_op_end.lnum
Bram Moolenaar34d72d42015-07-17 14:18:08 +02005013 || (!curbuf->b_p_bin
5014 && curbuf->b_p_fixeol)
Bram Moolenaarcab35ad2011-02-15 17:39:22 +01005015 || (lnum != curbuf->b_no_eol_lnum
Bram Moolenaardf177f62005-02-22 08:39:57 +00005016 && (lnum !=
5017 curbuf->b_ml.ml_line_count
5018 || curbuf->b_p_eol)))
Bram Moolenaar42335f52018-09-13 15:33:43 +02005019 vim_ignored = write(toshell_fd, "\n",
Bram Moolenaarfe86f2d2008-11-28 20:29:07 +00005020 (size_t)1);
Bram Moolenaardf177f62005-02-22 08:39:57 +00005021 ++lnum;
5022 if (lnum > curbuf->b_op_end.lnum)
5023 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01005024 // finished all the lines, close pipe
Bram Moolenaardf177f62005-02-22 08:39:57 +00005025 close(toshell_fd);
5026 toshell_fd = -1;
5027 break;
5028 }
Bram Moolenaar89d40322006-08-29 15:30:07 +00005029 lp = ml_get(lnum);
Bram Moolenaardf177f62005-02-22 08:39:57 +00005030 written = 0;
5031 }
5032 else if (len > 0)
5033 written += len;
5034 }
5035 _exit(0);
5036 }
Bram Moolenaar0f873732019-12-05 20:28:46 +01005037 else // parent
Bram Moolenaardf177f62005-02-22 08:39:57 +00005038 {
5039 close(toshell_fd);
5040 toshell_fd = -1;
5041 }
5042 }
5043
5044 if (options & SHELL_READ)
5045 ga_init2(&ga, 1, BUFLEN);
5046
5047 noread_cnt = 0;
Bram Moolenaar833eb1d2016-11-24 17:22:50 +01005048# ifdef ELAPSED_FUNC
5049 ELAPSED_INIT(start_tv);
Bram Moolenaarb3dc8fd2009-02-22 01:52:59 +00005050# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005051 for (;;)
5052 {
5053 /*
5054 * Check if keys have been typed, write them to the child
Bram Moolenaar5b962cf2005-12-12 21:58:40 +00005055 * if there are any.
5056 * Don't do this if we are expanding wild cards (would eat
5057 * typeahead).
5058 * Don't do this when filtering and terminal is in cooked
5059 * mode, the shell command will handle the I/O. Avoids
5060 * that a typed password is echoed for ssh or gpg command.
Bram Moolenaar12033fb2005-12-16 21:49:31 +00005061 * Don't get characters when the child has already
5062 * finished (wait_pid == 0).
Bram Moolenaardf177f62005-02-22 08:39:57 +00005063 * Don't read characters unless we didn't get output for a
Bram Moolenaarb3dc8fd2009-02-22 01:52:59 +00005064 * while (noread_cnt > 4), avoids that ":r !ls" eats
5065 * typeahead.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005066 */
5067 len = 0;
5068 if (!(options & SHELL_EXPAND)
Bram Moolenaar5b962cf2005-12-12 21:58:40 +00005069 && ((options &
5070 (SHELL_READ|SHELL_WRITE|SHELL_COOKED))
5071 != (SHELL_READ|SHELL_WRITE|SHELL_COOKED)
Bram Moolenaarb3dc8fd2009-02-22 01:52:59 +00005072# ifdef FEAT_GUI
Bram Moolenaar5b962cf2005-12-12 21:58:40 +00005073 || gui.in_use
Bram Moolenaarb3dc8fd2009-02-22 01:52:59 +00005074# endif
Bram Moolenaar5b962cf2005-12-12 21:58:40 +00005075 )
Bram Moolenaar12033fb2005-12-16 21:49:31 +00005076 && wait_pid == 0
Bram Moolenaarb3dc8fd2009-02-22 01:52:59 +00005077 && (ta_len > 0 || noread_cnt > 4))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005078 {
Bram Moolenaarb3dc8fd2009-02-22 01:52:59 +00005079 if (ta_len == 0)
5080 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01005081 // Get extra characters when we don't have any.
5082 // Reset the counter and timer.
Bram Moolenaarb3dc8fd2009-02-22 01:52:59 +00005083 noread_cnt = 0;
Bram Moolenaar833eb1d2016-11-24 17:22:50 +01005084# ifdef ELAPSED_FUNC
5085 ELAPSED_INIT(start_tv);
Bram Moolenaarb3dc8fd2009-02-22 01:52:59 +00005086# endif
5087 len = ui_inchar(ta_buf, BUFLEN, 10L, 0);
5088 }
5089 if (ta_len > 0 || len > 0)
5090 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00005091 /*
5092 * For pipes:
5093 * Check for CTRL-C: send interrupt signal to child.
5094 * Check for CTRL-D: EOF, close pipe to child.
5095 */
5096 if (len == 1 && (pty_master_fd < 0 || cmd != NULL))
5097 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00005098 /*
5099 * Send SIGINT to the child's group or all
5100 * processes in our group.
5101 */
Bram Moolenaarfae42832017-08-01 22:24:26 +02005102 may_send_sigint(ta_buf[ta_len], pid, wpid);
5103
Bram Moolenaar071d4272004-06-13 20:20:40 +00005104 if (pty_master_fd < 0 && toshell_fd >= 0
5105 && ta_buf[ta_len] == Ctrl_D)
5106 {
5107 close(toshell_fd);
5108 toshell_fd = -1;
5109 }
5110 }
5111
Bram Moolenaarf4140482020-02-15 23:06:45 +01005112 term_replace_bs_del_keycode(ta_buf, ta_len, len);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005113
5114 /*
5115 * For pipes: echo the typed characters.
5116 * For a pty this does not seem to work.
5117 */
5118 if (pty_master_fd < 0)
5119 {
5120 for (i = ta_len; i < ta_len + len; ++i)
5121 {
5122 if (ta_buf[i] == '\n' || ta_buf[i] == '\b')
5123 msg_putchar(ta_buf[i]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005124 else if (has_mbyte)
5125 {
Bram Moolenaar0fa313a2005-08-10 21:07:57 +00005126 int l = (*mb_ptr2len)(ta_buf + i);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005127
5128 msg_outtrans_len(ta_buf + i, l);
5129 i += l - 1;
5130 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005131 else
5132 msg_outtrans_len(ta_buf + i, 1);
5133 }
5134 windgoto(msg_row, msg_col);
5135 out_flush();
5136 }
5137
5138 ta_len += len;
5139
5140 /*
5141 * Write the characters to the child, unless EOF has
5142 * been typed for pipes. Write one character at a
Bram Moolenaare37d50a2008-08-06 17:06:04 +00005143 * time, to avoid losing too much typeahead.
Bram Moolenaardf177f62005-02-22 08:39:57 +00005144 * When writing buffer lines, drop the typed
5145 * characters (only check for CTRL-C).
Bram Moolenaar071d4272004-06-13 20:20:40 +00005146 */
Bram Moolenaardf177f62005-02-22 08:39:57 +00005147 if (options & SHELL_WRITE)
5148 ta_len = 0;
5149 else if (toshell_fd >= 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005150 {
5151 len = write(toshell_fd, (char *)ta_buf, (size_t)1);
5152 if (len > 0)
5153 {
5154 ta_len -= len;
5155 mch_memmove(ta_buf, ta_buf + len, ta_len);
5156 }
5157 }
Bram Moolenaarb3dc8fd2009-02-22 01:52:59 +00005158 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005159 }
5160
Bram Moolenaardf177f62005-02-22 08:39:57 +00005161 if (got_int)
5162 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01005163 // CTRL-C sends a signal to the child, we ignore it
5164 // ourselves
Bram Moolenaardf177f62005-02-22 08:39:57 +00005165# ifdef HAVE_SETSID
5166 kill(-pid, SIGINT);
5167# else
5168 kill(0, SIGINT);
5169# endif
5170 if (wpid > 0)
5171 kill(wpid, SIGINT);
5172 got_int = FALSE;
5173 }
5174
Bram Moolenaar071d4272004-06-13 20:20:40 +00005175 /*
5176 * Check if the child has any characters to be printed.
5177 * Read them and write them to our window. Repeat this as
5178 * long as there is something to do, avoid the 10ms wait
5179 * for mch_inchar(), or sending typeahead characters to
5180 * the external process.
5181 * TODO: This should handle escape sequences, compatible
5182 * to some terminal (vt52?).
5183 */
Bram Moolenaardf177f62005-02-22 08:39:57 +00005184 ++noread_cnt;
Bram Moolenaar8fdd7212016-03-26 19:41:48 +01005185 while (RealWaitForChar(fromshell_fd, 10L, NULL, NULL))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005186 {
Bram Moolenaar540fc6f2010-12-17 16:27:16 +01005187 len = read_eintr(fromshell_fd, buffer
Bram Moolenaar071d4272004-06-13 20:20:40 +00005188 + buffer_off, (size_t)(BUFLEN - buffer_off)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005189 );
Bram Moolenaar0f873732019-12-05 20:28:46 +01005190 if (len <= 0) // end of file or error
Bram Moolenaar071d4272004-06-13 20:20:40 +00005191 goto finished;
Bram Moolenaardf177f62005-02-22 08:39:57 +00005192
5193 noread_cnt = 0;
5194 if (options & SHELL_READ)
5195 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01005196 // Do NUL -> NL translation, append NL separated
5197 // lines to the current buffer.
Bram Moolenaardf177f62005-02-22 08:39:57 +00005198 for (i = 0; i < len; ++i)
5199 {
5200 if (buffer[i] == NL)
5201 append_ga_line(&ga);
5202 else if (buffer[i] == NUL)
5203 ga_append(&ga, NL);
5204 else
5205 ga_append(&ga, buffer[i]);
5206 }
5207 }
Bram Moolenaardf177f62005-02-22 08:39:57 +00005208 else if (has_mbyte)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005209 {
5210 int l;
Bram Moolenaara2150ac2018-03-17 13:15:17 +01005211 char_u *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005212
Bram Moolenaardf177f62005-02-22 08:39:57 +00005213 len += buffer_off;
5214 buffer[len] = NUL;
5215
Bram Moolenaar0f873732019-12-05 20:28:46 +01005216 // Check if the last character in buffer[] is
5217 // incomplete, keep these bytes for the next
5218 // round.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005219 for (p = buffer; p < buffer + len; p += l)
5220 {
Bram Moolenaard3c907b2016-08-17 21:32:09 +02005221 l = MB_CPTR2LEN(p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005222 if (l == 0)
Bram Moolenaar0f873732019-12-05 20:28:46 +01005223 l = 1; // NUL byte?
Bram Moolenaar071d4272004-06-13 20:20:40 +00005224 else if (MB_BYTE2LEN(*p) != l)
5225 break;
5226 }
Bram Moolenaar0f873732019-12-05 20:28:46 +01005227 if (p == buffer) // no complete character
Bram Moolenaar071d4272004-06-13 20:20:40 +00005228 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01005229 // avoid getting stuck at an illegal byte
Bram Moolenaar071d4272004-06-13 20:20:40 +00005230 if (len >= 12)
5231 ++p;
5232 else
5233 {
5234 buffer_off = len;
5235 continue;
5236 }
5237 }
5238 c = *p;
5239 *p = NUL;
Bram Moolenaar32526b32019-01-19 17:43:09 +01005240 msg_puts((char *)buffer);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005241 if (p < buffer + len)
5242 {
5243 *p = c;
5244 buffer_off = (buffer + len) - p;
5245 mch_memmove(buffer, p, buffer_off);
5246 continue;
5247 }
5248 buffer_off = 0;
5249 }
5250 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00005251 {
5252 buffer[len] = NUL;
Bram Moolenaar32526b32019-01-19 17:43:09 +01005253 msg_puts((char *)buffer);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005254 }
5255
5256 windgoto(msg_row, msg_col);
5257 cursor_on();
5258 out_flush();
5259 if (got_int)
5260 break;
Bram Moolenaarb3dc8fd2009-02-22 01:52:59 +00005261
Bram Moolenaar833eb1d2016-11-24 17:22:50 +01005262# ifdef ELAPSED_FUNC
Bram Moolenaar17fe5e12016-04-04 22:03:08 +02005263 if (wait_pid == 0)
Bram Moolenaarb3dc8fd2009-02-22 01:52:59 +00005264 {
Bram Moolenaar833eb1d2016-11-24 17:22:50 +01005265 long msec = ELAPSED_FUNC(start_tv);
Bram Moolenaarb3dc8fd2009-02-22 01:52:59 +00005266
Bram Moolenaar0f873732019-12-05 20:28:46 +01005267 // Avoid that we keep looping here without
5268 // checking for a CTRL-C for a long time. Don't
5269 // break out too often to avoid losing typeahead.
Bram Moolenaarb3dc8fd2009-02-22 01:52:59 +00005270 if (msec > 2000)
5271 {
5272 noread_cnt = 5;
5273 break;
5274 }
5275 }
5276# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005277 }
5278
Bram Moolenaar0f873732019-12-05 20:28:46 +01005279 // If we already detected the child has finished, continue
5280 // reading output for a short while. Some text may be
5281 // buffered.
Bram Moolenaar12033fb2005-12-16 21:49:31 +00005282 if (wait_pid == pid)
Bram Moolenaar17fe5e12016-04-04 22:03:08 +02005283 {
5284 if (noread_cnt < 5)
5285 continue;
Bram Moolenaar12033fb2005-12-16 21:49:31 +00005286 break;
Bram Moolenaar17fe5e12016-04-04 22:03:08 +02005287 }
Bram Moolenaar12033fb2005-12-16 21:49:31 +00005288
Bram Moolenaar071d4272004-06-13 20:20:40 +00005289 /*
5290 * Check if the child still exists, before checking for
Bram Moolenaare37d50a2008-08-06 17:06:04 +00005291 * typed characters (otherwise we would lose typeahead).
Bram Moolenaar071d4272004-06-13 20:20:40 +00005292 */
Bram Moolenaardf177f62005-02-22 08:39:57 +00005293# ifdef __NeXT__
Bram Moolenaar205b8862011-09-07 15:04:31 +02005294 wait_pid = wait4(pid, &status, WNOHANG, (struct rusage *)0);
Bram Moolenaardf177f62005-02-22 08:39:57 +00005295# else
Bram Moolenaar071d4272004-06-13 20:20:40 +00005296 wait_pid = waitpid(pid, &status, WNOHANG);
Bram Moolenaardf177f62005-02-22 08:39:57 +00005297# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005298 if ((wait_pid == (pid_t)-1 && errno == ECHILD)
5299 || (wait_pid == pid && WIFEXITED(status)))
5300 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01005301 // Don't break the loop yet, try reading more
5302 // characters from "fromshell_fd" first. When using
5303 // pipes there might still be something to read and
5304 // then we'll break the loop at the "break" above.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005305 wait_pid = pid;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005306 }
Bram Moolenaar12033fb2005-12-16 21:49:31 +00005307 else
5308 wait_pid = 0;
Bram Moolenaar090cfc12013-03-19 12:35:42 +01005309
Bram Moolenaar95a51352013-03-21 22:53:50 +01005310# if defined(FEAT_XCLIPBOARD) && defined(FEAT_X11)
Bram Moolenaar0f873732019-12-05 20:28:46 +01005311 // Handle any X events, e.g. serving the clipboard.
Bram Moolenaar090cfc12013-03-19 12:35:42 +01005312 clip_update();
5313# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005314 }
5315finished:
5316 p_more = p_more_save;
Bram Moolenaardf177f62005-02-22 08:39:57 +00005317 if (options & SHELL_READ)
5318 {
5319 if (ga.ga_len > 0)
5320 {
5321 append_ga_line(&ga);
Bram Moolenaar0f873732019-12-05 20:28:46 +01005322 // remember that the NL was missing
Bram Moolenaarcab35ad2011-02-15 17:39:22 +01005323 curbuf->b_no_eol_lnum = curwin->w_cursor.lnum;
Bram Moolenaardf177f62005-02-22 08:39:57 +00005324 }
5325 else
Bram Moolenaarcab35ad2011-02-15 17:39:22 +01005326 curbuf->b_no_eol_lnum = 0;
Bram Moolenaardf177f62005-02-22 08:39:57 +00005327 ga_clear(&ga);
5328 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005329
Bram Moolenaar071d4272004-06-13 20:20:40 +00005330 /*
5331 * Give all typeahead that wasn't used back to ui_inchar().
5332 */
5333 if (ta_len)
5334 ui_inchar_undo(ta_buf, ta_len);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005335 State = old_State;
5336 if (toshell_fd >= 0)
5337 close(toshell_fd);
5338 close(fromshell_fd);
5339 }
Bram Moolenaar95a51352013-03-21 22:53:50 +01005340# if defined(FEAT_XCLIPBOARD) && defined(FEAT_X11)
Bram Moolenaar090cfc12013-03-19 12:35:42 +01005341 else
5342 {
Bram Moolenaar0abe0522016-08-28 16:53:12 +02005343 long delay_msec = 1;
5344
Bram Moolenaar8a3da6a2020-12-08 19:18:37 +01005345 if (tmode == TMODE_RAW)
5346 // possibly disables modifyOtherKeys, so that the system
5347 // can recognize CTRL-C
5348 out_str(T_CTE);
Bram Moolenaar0981c872020-08-23 14:28:37 +02005349
Bram Moolenaar090cfc12013-03-19 12:35:42 +01005350 /*
5351 * Similar to the loop above, but only handle X events, no
5352 * I/O.
5353 */
5354 for (;;)
5355 {
5356 if (got_int)
5357 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01005358 // CTRL-C sends a signal to the child, we ignore it
5359 // ourselves
Bram Moolenaar090cfc12013-03-19 12:35:42 +01005360# ifdef HAVE_SETSID
5361 kill(-pid, SIGINT);
5362# else
5363 kill(0, SIGINT);
5364# endif
5365 got_int = FALSE;
5366 }
5367# ifdef __NeXT__
5368 wait_pid = wait4(pid, &status, WNOHANG, (struct rusage *)0);
5369# else
5370 wait_pid = waitpid(pid, &status, WNOHANG);
5371# endif
5372 if ((wait_pid == (pid_t)-1 && errno == ECHILD)
5373 || (wait_pid == pid && WIFEXITED(status)))
5374 {
5375 wait_pid = pid;
5376 break;
5377 }
5378
Bram Moolenaar0f873732019-12-05 20:28:46 +01005379 // Handle any X events, e.g. serving the clipboard.
Bram Moolenaar090cfc12013-03-19 12:35:42 +01005380 clip_update();
5381
Bram Moolenaar0f873732019-12-05 20:28:46 +01005382 // Wait for 1 to 10 msec. 1 is faster but gives the child
Bram Moolenaar0981c872020-08-23 14:28:37 +02005383 // less time, gradually wait longer.
5384 mch_delay(delay_msec,
5385 MCH_DELAY_IGNOREINPUT | MCH_DELAY_SETTMODE);
Bram Moolenaar0abe0522016-08-28 16:53:12 +02005386 if (++delay_msec > 10)
5387 delay_msec = 10;
Bram Moolenaar090cfc12013-03-19 12:35:42 +01005388 }
Bram Moolenaar0981c872020-08-23 14:28:37 +02005389
Bram Moolenaar8a3da6a2020-12-08 19:18:37 +01005390 if (tmode == TMODE_RAW)
5391 // possibly enables modifyOtherKeys again
5392 out_str(T_CTI);
Bram Moolenaar090cfc12013-03-19 12:35:42 +01005393 }
5394# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005395
5396 /*
5397 * Wait until our child has exited.
5398 * Ignore wait() returning pids of other children and returning
5399 * because of some signal like SIGWINCH.
5400 * Don't wait if wait_pid was already set above, indicating the
5401 * child already exited.
5402 */
Bram Moolenaar205b8862011-09-07 15:04:31 +02005403 if (wait_pid != pid)
5404 wait_pid = wait4pid(pid, &status);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005405
Bram Moolenaar624891f2010-10-13 16:22:09 +02005406# ifdef FEAT_GUI
Bram Moolenaar0f873732019-12-05 20:28:46 +01005407 // Close slave side of pty. Only do this after the child has
5408 // exited, otherwise the child may hang when it tries to write on
5409 // the pty.
Bram Moolenaar624891f2010-10-13 16:22:09 +02005410 if (pty_master_fd >= 0)
5411 close(pty_slave_fd);
5412# endif
5413
Bram Moolenaar0f873732019-12-05 20:28:46 +01005414 // Make sure the child that writes to the external program is
5415 // dead.
Bram Moolenaardf177f62005-02-22 08:39:57 +00005416 if (wpid > 0)
Bram Moolenaar205b8862011-09-07 15:04:31 +02005417 {
Bram Moolenaardf177f62005-02-22 08:39:57 +00005418 kill(wpid, SIGKILL);
Bram Moolenaar205b8862011-09-07 15:04:31 +02005419 wait4pid(wpid, NULL);
5420 }
Bram Moolenaardf177f62005-02-22 08:39:57 +00005421
Bram Moolenaarc4d4ac22016-11-07 22:42:57 +01005422# ifdef FEAT_JOB_CHANNEL
5423 --dont_check_job_ended;
5424# endif
5425
Bram Moolenaar071d4272004-06-13 20:20:40 +00005426 /*
5427 * Set to raw mode right now, otherwise a CTRL-C after
5428 * catch_signals() will kill Vim.
5429 */
5430 if (tmode == TMODE_RAW)
5431 settmode(TMODE_RAW);
5432 did_settmode = TRUE;
5433 set_signals();
5434
5435 if (WIFEXITED(status))
5436 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01005437 // LINTED avoid "bitwise operation on signed value"
Bram Moolenaar071d4272004-06-13 20:20:40 +00005438 retval = WEXITSTATUS(status);
Bram Moolenaar75676462013-01-30 14:55:42 +01005439 if (retval != 0 && !emsg_silent)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005440 {
5441 if (retval == EXEC_FAILED)
5442 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01005443 msg_puts(_("\nCannot execute shell "));
Bram Moolenaar071d4272004-06-13 20:20:40 +00005444 msg_outtrans(p_sh);
5445 msg_putchar('\n');
5446 }
5447 else if (!(options & SHELL_SILENT))
5448 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01005449 msg_puts(_("\nshell returned "));
Bram Moolenaar071d4272004-06-13 20:20:40 +00005450 msg_outnum((long)retval);
5451 msg_putchar('\n');
5452 }
5453 }
5454 }
5455 else
Bram Moolenaar32526b32019-01-19 17:43:09 +01005456 msg_puts(_("\nCommand terminated\n"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00005457 }
5458 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005459
5460error:
5461 if (!did_settmode)
5462 if (tmode == TMODE_RAW)
Bram Moolenaar0f873732019-12-05 20:28:46 +01005463 settmode(TMODE_RAW); // set to raw mode
Bram Moolenaar071d4272004-06-13 20:20:40 +00005464 resettitle();
Bram Moolenaar13568252018-03-16 20:46:58 +01005465 vim_free(argv);
5466 vim_free(tofree1);
5467 vim_free(tofree2);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005468
5469 return retval;
Bram Moolenaar13568252018-03-16 20:46:58 +01005470}
Bram Moolenaar0f873732019-12-05 20:28:46 +01005471#endif // USE_SYSTEM
Bram Moolenaar13568252018-03-16 20:46:58 +01005472
5473 int
5474mch_call_shell(
5475 char_u *cmd,
Bram Moolenaar0f873732019-12-05 20:28:46 +01005476 int options) // SHELL_*, see vim.h
Bram Moolenaar13568252018-03-16 20:46:58 +01005477{
5478#if defined(FEAT_GUI) && defined(FEAT_TERMINAL)
5479 if (gui.in_use && vim_strchr(p_go, GO_TERMINAL) != NULL)
5480 return mch_call_shell_terminal(cmd, options);
5481#endif
5482#ifdef USE_SYSTEM
5483 return mch_call_shell_system(cmd, options);
5484#else
5485 return mch_call_shell_fork(cmd, options);
5486#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005487}
5488
Bram Moolenaar509ce2a2016-03-11 22:52:15 +01005489#if defined(FEAT_JOB_CHANNEL) || defined(PROTO)
Bram Moolenaar835dc632016-02-07 14:27:38 +01005490 void
Bram Moolenaar493359e2018-06-12 20:25:52 +02005491mch_job_start(char **argv, job_T *job, jobopt_T *options, int is_terminal)
Bram Moolenaar835dc632016-02-07 14:27:38 +01005492{
Bram Moolenaar6463ca22016-02-13 17:04:46 +01005493 pid_t pid;
Bram Moolenaar0f873732019-12-05 20:28:46 +01005494 int fd_in[2] = {-1, -1}; // for stdin
5495 int fd_out[2] = {-1, -1}; // for stdout
5496 int fd_err[2] = {-1, -1}; // for stderr
Bram Moolenaar5a1feb82017-07-22 18:04:08 +02005497 int pty_master_fd = -1;
5498 int pty_slave_fd = -1;
Bram Moolenaar16eb4f82016-02-14 23:02:34 +01005499 channel_T *channel = NULL;
Bram Moolenaarf65333c2016-03-08 18:27:21 +01005500 int use_null_for_in = options->jo_io[PART_IN] == JIO_NULL;
5501 int use_null_for_out = options->jo_io[PART_OUT] == JIO_NULL;
5502 int use_null_for_err = options->jo_io[PART_ERR] == JIO_NULL;
Bram Moolenaarb69fccf2016-03-06 23:06:25 +01005503 int use_file_for_in = options->jo_io[PART_IN] == JIO_FILE;
Bram Moolenaare98d1212016-03-08 15:37:41 +01005504 int use_file_for_out = options->jo_io[PART_OUT] == JIO_FILE;
5505 int use_file_for_err = options->jo_io[PART_ERR] == JIO_FILE;
Bram Moolenaarb2412082017-08-20 18:09:14 +02005506 int use_buffer_for_in = options->jo_io[PART_IN] == JIO_BUFFER;
Bram Moolenaarc25558b2016-03-03 21:02:23 +01005507 int use_out_for_err = options->jo_io[PART_ERR] == JIO_OUT;
Bram Moolenaarbb09ceb2016-10-18 16:27:23 +02005508 SIGSET_DECL(curset)
Bram Moolenaar835dc632016-02-07 14:27:38 +01005509
Bram Moolenaarf65333c2016-03-08 18:27:21 +01005510 if (use_out_for_err && use_null_for_out)
5511 use_null_for_err = TRUE;
5512
Bram Moolenaar0f873732019-12-05 20:28:46 +01005513 // default is to fail
Bram Moolenaar6463ca22016-02-13 17:04:46 +01005514 job->jv_status = JOB_FAILED;
Bram Moolenaar6463ca22016-02-13 17:04:46 +01005515
Bram Moolenaarb2412082017-08-20 18:09:14 +02005516 if (options->jo_pty
5517 && (!(use_file_for_in || use_null_for_in)
Bram Moolenaar59386482019-02-10 22:43:46 +01005518 || !(use_file_for_out || use_null_for_out)
Bram Moolenaarb2412082017-08-20 18:09:14 +02005519 || !(use_out_for_err || use_file_for_err || use_null_for_err)))
Bram Moolenaar59386482019-02-10 22:43:46 +01005520 open_pty(&pty_master_fd, &pty_slave_fd,
5521 &job->jv_tty_out, &job->jv_tty_in);
Bram Moolenaar5a1feb82017-07-22 18:04:08 +02005522
Bram Moolenaar0f873732019-12-05 20:28:46 +01005523 // TODO: without the channel feature connect the child to /dev/null?
5524 // Open pipes for stdin, stdout, stderr.
Bram Moolenaarb69fccf2016-03-06 23:06:25 +01005525 if (use_file_for_in)
5526 {
5527 char_u *fname = options->jo_io_name[PART_IN];
5528
5529 fd_in[0] = mch_open((char *)fname, O_RDONLY, 0);
5530 if (fd_in[0] < 0)
5531 {
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00005532 semsg(_(e_cant_open_file_str), fname);
Bram Moolenaarb69fccf2016-03-06 23:06:25 +01005533 goto failed;
5534 }
5535 }
Bram Moolenaarb2412082017-08-20 18:09:14 +02005536 else
Bram Moolenaar0f873732019-12-05 20:28:46 +01005537 // When writing buffer lines to the input don't use the pty, so that
5538 // the pipe can be closed when all lines were written.
Bram Moolenaarb2412082017-08-20 18:09:14 +02005539 if (!use_null_for_in && (pty_master_fd < 0 || use_buffer_for_in)
5540 && pipe(fd_in) < 0)
5541 goto failed;
Bram Moolenaare98d1212016-03-08 15:37:41 +01005542
5543 if (use_file_for_out)
5544 {
5545 char_u *fname = options->jo_io_name[PART_OUT];
5546
5547 fd_out[1] = mch_open((char *)fname, O_WRONLY | O_CREAT | O_TRUNC, 0644);
5548 if (fd_out[1] < 0)
5549 {
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00005550 semsg(_(e_cant_open_file_str), fname);
Bram Moolenaare98d1212016-03-08 15:37:41 +01005551 goto failed;
5552 }
5553 }
Bram Moolenaar5a1feb82017-07-22 18:04:08 +02005554 else if (!use_null_for_out && pty_master_fd < 0 && pipe(fd_out) < 0)
Bram Moolenaarb69fccf2016-03-06 23:06:25 +01005555 goto failed;
Bram Moolenaare98d1212016-03-08 15:37:41 +01005556
5557 if (use_file_for_err)
5558 {
5559 char_u *fname = options->jo_io_name[PART_ERR];
5560
5561 fd_err[1] = mch_open((char *)fname, O_WRONLY | O_CREAT | O_TRUNC, 0600);
5562 if (fd_err[1] < 0)
5563 {
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00005564 semsg(_(e_cant_open_file_str), fname);
Bram Moolenaare98d1212016-03-08 15:37:41 +01005565 goto failed;
5566 }
5567 }
Bram Moolenaar5a1feb82017-07-22 18:04:08 +02005568 else if (!use_out_for_err && !use_null_for_err
5569 && pty_master_fd < 0 && pipe(fd_err) < 0)
Bram Moolenaar6463ca22016-02-13 17:04:46 +01005570 goto failed;
5571
Bram Moolenaarf65333c2016-03-08 18:27:21 +01005572 if (!use_null_for_in || !use_null_for_out || !use_null_for_err)
5573 {
Bram Moolenaarde279892016-03-11 22:19:44 +01005574 if (options->jo_set & JO_CHANNEL)
5575 {
5576 channel = options->jo_channel;
5577 if (channel != NULL)
5578 ++channel->ch_refcount;
5579 }
5580 else
5581 channel = add_channel();
Bram Moolenaarf65333c2016-03-08 18:27:21 +01005582 if (channel == NULL)
5583 goto failed;
Bram Moolenaarf3360612017-10-01 16:21:31 +02005584 if (job->jv_tty_out != NULL)
5585 ch_log(channel, "using pty %s on fd %d",
5586 job->jv_tty_out, pty_master_fd);
Bram Moolenaarf65333c2016-03-08 18:27:21 +01005587 }
Bram Moolenaar6463ca22016-02-13 17:04:46 +01005588
Bram Moolenaarbb09ceb2016-10-18 16:27:23 +02005589 BLOCK_SIGNALS(&curset);
Bram Moolenaar0f873732019-12-05 20:28:46 +01005590 pid = fork(); // maybe we should use vfork()
Bram Moolenaarbb09ceb2016-10-18 16:27:23 +02005591 if (pid == -1)
Bram Moolenaar835dc632016-02-07 14:27:38 +01005592 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01005593 // failed to fork
Bram Moolenaarbb09ceb2016-10-18 16:27:23 +02005594 UNBLOCK_SIGNALS(&curset);
Bram Moolenaar6463ca22016-02-13 17:04:46 +01005595 goto failed;
Bram Moolenaar835dc632016-02-07 14:27:38 +01005596 }
Bram Moolenaar6463ca22016-02-13 17:04:46 +01005597 if (pid == 0)
Bram Moolenaar835dc632016-02-07 14:27:38 +01005598 {
Bram Moolenaar4694a172016-04-21 14:05:23 +02005599 int null_fd = -1;
5600 int stderr_works = TRUE;
Bram Moolenaarf65333c2016-03-08 18:27:21 +01005601
Bram Moolenaar0f873732019-12-05 20:28:46 +01005602 // child
5603 reset_signals(); // handle signals normally
Bram Moolenaarbb09ceb2016-10-18 16:27:23 +02005604 UNBLOCK_SIGNALS(&curset);
Bram Moolenaar835dc632016-02-07 14:27:38 +01005605
Bram Moolenaar819524702018-02-27 19:10:00 +01005606# ifdef FEAT_JOB_CHANNEL
5607 if (ch_log_active())
Bram Moolenaar0f873732019-12-05 20:28:46 +01005608 // close the log file in the child
Bram Moolenaar819524702018-02-27 19:10:00 +01005609 ch_logfile((char_u *)"", (char_u *)"");
5610# endif
5611
Bram Moolenaar835dc632016-02-07 14:27:38 +01005612# ifdef HAVE_SETSID
Bram Moolenaar0f873732019-12-05 20:28:46 +01005613 // Create our own process group, so that the child and all its
5614 // children can be kill()ed. Don't do this when using pipes,
5615 // because stdin is not a tty, we would lose /dev/tty.
Bram Moolenaar835dc632016-02-07 14:27:38 +01005616 (void)setsid();
5617# endif
5618
Bram Moolenaar58556cd2017-07-20 23:04:46 +02005619# ifdef FEAT_TERMINAL
5620 if (options->jo_term_rows > 0)
Bram Moolenaar9a993e32018-04-05 22:15:22 +02005621 {
5622 char *term = (char *)T_NAME;
5623
5624#ifdef FEAT_GUI
5625 if (term_is_gui(T_NAME))
Bram Moolenaar0f873732019-12-05 20:28:46 +01005626 // In the GUI 'term' is not what we want, use $TERM.
Bram Moolenaar9a993e32018-04-05 22:15:22 +02005627 term = getenv("TERM");
5628#endif
Bram Moolenaar0f873732019-12-05 20:28:46 +01005629 // Use 'term' or $TERM if it starts with "xterm", otherwise fall
Bram Moolenaar4d5d0df2020-04-14 20:56:31 +02005630 // back to "xterm" or "xterm-color".
Bram Moolenaar9a993e32018-04-05 22:15:22 +02005631 if (term == NULL || *term == NUL || STRNCMP(term, "xterm", 5) != 0)
Bram Moolenaar5ba8d352020-04-05 21:42:12 +02005632 {
Bram Moolenaar5ba8d352020-04-05 21:42:12 +02005633 if (t_colors >= 256)
Bram Moolenaar4d5d0df2020-04-14 20:56:31 +02005634 // TODO: should we check this name is supported?
Bram Moolenaar5ba8d352020-04-05 21:42:12 +02005635 term = "xterm-256color";
Bram Moolenaar4d5d0df2020-04-14 20:56:31 +02005636 else if (t_colors > 16)
5637 term = "xterm-color";
Bram Moolenaar5ba8d352020-04-05 21:42:12 +02005638 else
5639 term = "xterm";
5640 }
Bram Moolenaar58556cd2017-07-20 23:04:46 +02005641 set_child_environment(
5642 (long)options->jo_term_rows,
5643 (long)options->jo_term_cols,
Bram Moolenaar493359e2018-06-12 20:25:52 +02005644 term,
5645 is_terminal);
Bram Moolenaar9a993e32018-04-05 22:15:22 +02005646 }
Bram Moolenaar58556cd2017-07-20 23:04:46 +02005647 else
5648# endif
Bram Moolenaar493359e2018-06-12 20:25:52 +02005649 set_default_child_environment(is_terminal);
Bram Moolenaar6463ca22016-02-13 17:04:46 +01005650
Bram Moolenaar05aafed2017-08-11 19:12:11 +02005651 if (options->jo_env != NULL)
5652 {
5653 dict_T *dict = options->jo_env;
5654 hashitem_T *hi;
5655 int todo = (int)dict->dv_hashtab.ht_used;
5656
5657 for (hi = dict->dv_hashtab.ht_array; todo > 0; ++hi)
5658 if (!HASHITEM_EMPTY(hi))
5659 {
5660 typval_T *item = &dict_lookup(hi)->di_tv;
5661
Bram Moolenaard155d7a2018-12-21 16:04:21 +01005662 vim_setenv((char_u*)hi->hi_key, tv_get_string(item));
Bram Moolenaar05aafed2017-08-11 19:12:11 +02005663 --todo;
5664 }
5665 }
5666
Bram Moolenaarf65333c2016-03-08 18:27:21 +01005667 if (use_null_for_in || use_null_for_out || use_null_for_err)
Bram Moolenaarb109bb42017-08-21 21:07:29 +02005668 {
Bram Moolenaarf65333c2016-03-08 18:27:21 +01005669 null_fd = open("/dev/null", O_RDWR | O_EXTRA, 0);
Bram Moolenaarb109bb42017-08-21 21:07:29 +02005670 if (null_fd < 0)
5671 {
5672 perror("opening /dev/null failed");
5673 _exit(OPEN_NULL_FAILED);
5674 }
5675 }
Bram Moolenaarf65333c2016-03-08 18:27:21 +01005676
Bram Moolenaar223896d2017-08-02 22:33:28 +02005677 if (pty_slave_fd >= 0)
5678 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01005679 // push stream discipline modules
Bram Moolenaar1ecc5e42019-01-26 15:12:55 +01005680 setup_slavepty(pty_slave_fd);
Bram Moolenaar223896d2017-08-02 22:33:28 +02005681# ifdef TIOCSCTTY
Bram Moolenaar0f873732019-12-05 20:28:46 +01005682 // Try to become controlling tty (probably doesn't work,
5683 // unless run by root)
Bram Moolenaar223896d2017-08-02 22:33:28 +02005684 ioctl(pty_slave_fd, TIOCSCTTY, (char *)NULL);
5685# endif
5686 }
5687
Bram Moolenaar0f873732019-12-05 20:28:46 +01005688 // set up stdin for the child
Bram Moolenaar5a1feb82017-07-22 18:04:08 +02005689 close(0);
Bram Moolenaarc0a1d7f2016-03-19 14:12:50 +01005690 if (use_null_for_in && null_fd >= 0)
Bram Moolenaar42335f52018-09-13 15:33:43 +02005691 vim_ignored = dup(null_fd);
Bram Moolenaar5a1feb82017-07-22 18:04:08 +02005692 else if (fd_in[0] < 0)
Bram Moolenaar42335f52018-09-13 15:33:43 +02005693 vim_ignored = dup(pty_slave_fd);
Bram Moolenaarf65333c2016-03-08 18:27:21 +01005694 else
Bram Moolenaar42335f52018-09-13 15:33:43 +02005695 vim_ignored = dup(fd_in[0]);
Bram Moolenaar6463ca22016-02-13 17:04:46 +01005696
Bram Moolenaar0f873732019-12-05 20:28:46 +01005697 // set up stderr for the child
Bram Moolenaar5a1feb82017-07-22 18:04:08 +02005698 close(2);
Bram Moolenaarc0a1d7f2016-03-19 14:12:50 +01005699 if (use_null_for_err && null_fd >= 0)
Bram Moolenaarf65333c2016-03-08 18:27:21 +01005700 {
Bram Moolenaar42335f52018-09-13 15:33:43 +02005701 vim_ignored = dup(null_fd);
Bram Moolenaar4694a172016-04-21 14:05:23 +02005702 stderr_works = FALSE;
Bram Moolenaarf65333c2016-03-08 18:27:21 +01005703 }
5704 else if (use_out_for_err)
Bram Moolenaar42335f52018-09-13 15:33:43 +02005705 vim_ignored = dup(fd_out[1]);
Bram Moolenaar5a1feb82017-07-22 18:04:08 +02005706 else if (fd_err[1] < 0)
Bram Moolenaar42335f52018-09-13 15:33:43 +02005707 vim_ignored = dup(pty_slave_fd);
Bram Moolenaarc25558b2016-03-03 21:02:23 +01005708 else
Bram Moolenaar42335f52018-09-13 15:33:43 +02005709 vim_ignored = dup(fd_err[1]);
Bram Moolenaarc25558b2016-03-03 21:02:23 +01005710
Bram Moolenaar0f873732019-12-05 20:28:46 +01005711 // set up stdout for the child
Bram Moolenaar5a1feb82017-07-22 18:04:08 +02005712 close(1);
Bram Moolenaarc0a1d7f2016-03-19 14:12:50 +01005713 if (use_null_for_out && null_fd >= 0)
Bram Moolenaar42335f52018-09-13 15:33:43 +02005714 vim_ignored = dup(null_fd);
Bram Moolenaar5a1feb82017-07-22 18:04:08 +02005715 else if (fd_out[1] < 0)
Bram Moolenaar42335f52018-09-13 15:33:43 +02005716 vim_ignored = dup(pty_slave_fd);
Bram Moolenaarf65333c2016-03-08 18:27:21 +01005717 else
Bram Moolenaar42335f52018-09-13 15:33:43 +02005718 vim_ignored = dup(fd_out[1]);
Bram Moolenaar5a1feb82017-07-22 18:04:08 +02005719
5720 if (fd_in[0] >= 0)
5721 close(fd_in[0]);
5722 if (fd_in[1] >= 0)
5723 close(fd_in[1]);
5724 if (fd_out[0] >= 0)
5725 close(fd_out[0]);
5726 if (fd_out[1] >= 0)
Bram Moolenaarf65333c2016-03-08 18:27:21 +01005727 close(fd_out[1]);
Bram Moolenaar5a1feb82017-07-22 18:04:08 +02005728 if (fd_err[0] >= 0)
5729 close(fd_err[0]);
5730 if (fd_err[1] >= 0)
5731 close(fd_err[1]);
5732 if (pty_master_fd >= 0)
5733 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01005734 close(pty_master_fd); // not used in the child
5735 close(pty_slave_fd); // was duped above
Bram Moolenaarf65333c2016-03-08 18:27:21 +01005736 }
Bram Moolenaarea83bf02016-05-08 09:40:51 +02005737
Bram Moolenaarf65333c2016-03-08 18:27:21 +01005738 if (null_fd >= 0)
5739 close(null_fd);
Bram Moolenaar6463ca22016-02-13 17:04:46 +01005740
Bram Moolenaar05aafed2017-08-11 19:12:11 +02005741 if (options->jo_cwd != NULL && mch_chdir((char *)options->jo_cwd) != 0)
5742 _exit(EXEC_FAILED);
5743
Bram Moolenaar0f873732019-12-05 20:28:46 +01005744 // See above for type of argv.
Bram Moolenaar835dc632016-02-07 14:27:38 +01005745 execvp(argv[0], argv);
5746
Bram Moolenaar4694a172016-04-21 14:05:23 +02005747 if (stderr_works)
5748 perror("executing job failed");
Bram Moolenaarfae42832017-08-01 22:24:26 +02005749# ifdef EXITFREE
Bram Moolenaar0f873732019-12-05 20:28:46 +01005750 // calling free_all_mem() here causes problems. Ignore valgrind
5751 // reporting possibly leaked memory.
Bram Moolenaarfae42832017-08-01 22:24:26 +02005752# endif
Bram Moolenaar0f873732019-12-05 20:28:46 +01005753 _exit(EXEC_FAILED); // exec failed, return failure code
Bram Moolenaar835dc632016-02-07 14:27:38 +01005754 }
Bram Moolenaar6463ca22016-02-13 17:04:46 +01005755
Bram Moolenaar0f873732019-12-05 20:28:46 +01005756 // parent
Bram Moolenaarbb09ceb2016-10-18 16:27:23 +02005757 UNBLOCK_SIGNALS(&curset);
5758
Bram Moolenaar6463ca22016-02-13 17:04:46 +01005759 job->jv_pid = pid;
5760 job->jv_status = JOB_STARTED;
Bram Moolenaar0f873732019-12-05 20:28:46 +01005761 job->jv_channel = channel; // ch_refcount was set above
Bram Moolenaar6463ca22016-02-13 17:04:46 +01005762
Bram Moolenaar5a1feb82017-07-22 18:04:08 +02005763 if (pty_master_fd >= 0)
Bram Moolenaar0f873732019-12-05 20:28:46 +01005764 close(pty_slave_fd); // not used in the parent
5765 // close child stdin, stdout and stderr
Bram Moolenaar819524702018-02-27 19:10:00 +01005766 if (fd_in[0] >= 0)
Bram Moolenaarb69fccf2016-03-06 23:06:25 +01005767 close(fd_in[0]);
Bram Moolenaar819524702018-02-27 19:10:00 +01005768 if (fd_out[1] >= 0)
Bram Moolenaare98d1212016-03-08 15:37:41 +01005769 close(fd_out[1]);
Bram Moolenaar819524702018-02-27 19:10:00 +01005770 if (fd_err[1] >= 0)
Bram Moolenaarc25558b2016-03-03 21:02:23 +01005771 close(fd_err[1]);
Bram Moolenaarf65333c2016-03-08 18:27:21 +01005772 if (channel != NULL)
5773 {
Bram Moolenaar652de232019-04-04 20:13:09 +02005774 int in_fd = INVALID_FD;
5775 int out_fd = INVALID_FD;
5776 int err_fd = INVALID_FD;
5777
5778 if (!(use_file_for_in || use_null_for_in))
5779 in_fd = fd_in[1] >= 0 ? fd_in[1] : pty_master_fd;
5780
5781 if (!(use_file_for_out || use_null_for_out))
5782 out_fd = fd_out[0] >= 0 ? fd_out[0] : pty_master_fd;
5783
5784 // When using pty_master_fd only set it for stdout, do not duplicate
5785 // it for stderr, it only needs to be read once.
5786 if (!(use_out_for_err || use_file_for_err || use_null_for_err))
5787 {
5788 if (fd_err[0] >= 0)
5789 err_fd = fd_err[0];
5790 else if (out_fd != pty_master_fd)
5791 err_fd = pty_master_fd;
5792 }
Bram Moolenaar4e9d4432018-04-24 20:54:07 +02005793
5794 channel_set_pipes(channel, in_fd, out_fd, err_fd);
Bram Moolenaarf65333c2016-03-08 18:27:21 +01005795 channel_set_job(channel, job, options);
Bram Moolenaarf65333c2016-03-08 18:27:21 +01005796 }
Bram Moolenaar979e8c52017-08-01 15:08:07 +02005797 else
5798 {
5799 if (fd_in[1] >= 0)
5800 close(fd_in[1]);
5801 if (fd_out[0] >= 0)
5802 close(fd_out[0]);
5803 if (fd_err[0] >= 0)
5804 close(fd_err[0]);
5805 if (pty_master_fd >= 0)
5806 close(pty_master_fd);
5807 }
Bram Moolenaar6463ca22016-02-13 17:04:46 +01005808
Bram Moolenaar0f873732019-12-05 20:28:46 +01005809 // success!
Bram Moolenaar6463ca22016-02-13 17:04:46 +01005810 return;
5811
Bram Moolenaar509ce2a2016-03-11 22:52:15 +01005812failed:
Bram Moolenaarde279892016-03-11 22:19:44 +01005813 channel_unref(channel);
Bram Moolenaar6463ca22016-02-13 17:04:46 +01005814 if (fd_in[0] >= 0)
Bram Moolenaar6463ca22016-02-13 17:04:46 +01005815 close(fd_in[0]);
Bram Moolenaare98d1212016-03-08 15:37:41 +01005816 if (fd_in[1] >= 0)
5817 close(fd_in[1]);
Bram Moolenaar6463ca22016-02-13 17:04:46 +01005818 if (fd_out[0] >= 0)
Bram Moolenaar6463ca22016-02-13 17:04:46 +01005819 close(fd_out[0]);
Bram Moolenaare98d1212016-03-08 15:37:41 +01005820 if (fd_out[1] >= 0)
Bram Moolenaar6463ca22016-02-13 17:04:46 +01005821 close(fd_out[1]);
Bram Moolenaar6463ca22016-02-13 17:04:46 +01005822 if (fd_err[0] >= 0)
Bram Moolenaar6463ca22016-02-13 17:04:46 +01005823 close(fd_err[0]);
Bram Moolenaare98d1212016-03-08 15:37:41 +01005824 if (fd_err[1] >= 0)
Bram Moolenaar6463ca22016-02-13 17:04:46 +01005825 close(fd_err[1]);
Bram Moolenaar5a1feb82017-07-22 18:04:08 +02005826 if (pty_master_fd >= 0)
5827 close(pty_master_fd);
5828 if (pty_slave_fd >= 0)
5829 close(pty_slave_fd);
Bram Moolenaar835dc632016-02-07 14:27:38 +01005830}
5831
Bram Moolenaarb3051ce2019-01-31 15:52:11 +01005832 static char_u *
5833get_signal_name(int sig)
5834{
5835 int i;
5836 char_u numbuf[NUMBUFLEN];
5837
5838 if (sig == SIGKILL)
5839 return vim_strsave((char_u *)"kill");
5840
5841 for (i = 0; signal_info[i].sig != -1; i++)
5842 if (sig == signal_info[i].sig)
5843 return strlow_save((char_u *)signal_info[i].name);
5844
5845 vim_snprintf((char *)numbuf, NUMBUFLEN, "%d", sig);
5846 return vim_strsave(numbuf);
5847}
5848
Bram Moolenaar835dc632016-02-07 14:27:38 +01005849 char *
5850mch_job_status(job_T *job)
5851{
5852# ifdef HAVE_UNION_WAIT
5853 union wait status;
5854# else
5855 int status = -1;
5856# endif
5857 pid_t wait_pid = 0;
5858
5859# ifdef __NeXT__
5860 wait_pid = wait4(job->jv_pid, &status, WNOHANG, (struct rusage *)0);
5861# else
5862 wait_pid = waitpid(job->jv_pid, &status, WNOHANG);
5863# endif
5864 if (wait_pid == -1)
5865 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01005866 // process must have exited
Bram Moolenaarb0b98d52018-05-05 21:01:00 +02005867 if (job->jv_status < JOB_ENDED)
5868 ch_log(job->jv_channel, "Job no longer exists: %s",
5869 strerror(errno));
Bram Moolenaar97792de2016-10-15 18:36:49 +02005870 goto return_dead;
Bram Moolenaar835dc632016-02-07 14:27:38 +01005871 }
5872 if (wait_pid == 0)
5873 return "run";
5874 if (WIFEXITED(status))
5875 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01005876 // LINTED avoid "bitwise operation on signed value"
Bram Moolenaar835dc632016-02-07 14:27:38 +01005877 job->jv_exitval = WEXITSTATUS(status);
Bram Moolenaarb0b98d52018-05-05 21:01:00 +02005878 if (job->jv_status < JOB_ENDED)
5879 ch_log(job->jv_channel, "Job exited with %d", job->jv_exitval);
Bram Moolenaar97792de2016-10-15 18:36:49 +02005880 goto return_dead;
Bram Moolenaar835dc632016-02-07 14:27:38 +01005881 }
Bram Moolenaar76467df2016-02-12 19:30:26 +01005882 if (WIFSIGNALED(status))
5883 {
5884 job->jv_exitval = -1;
Bram Moolenaarb3051ce2019-01-31 15:52:11 +01005885 job->jv_termsig = get_signal_name(WTERMSIG(status));
5886 if (job->jv_status < JOB_ENDED && job->jv_termsig != NULL)
5887 ch_log(job->jv_channel, "Job terminated by signal \"%s\"",
5888 job->jv_termsig);
Bram Moolenaar97792de2016-10-15 18:36:49 +02005889 goto return_dead;
Bram Moolenaar76467df2016-02-12 19:30:26 +01005890 }
Bram Moolenaar835dc632016-02-07 14:27:38 +01005891 return "run";
Bram Moolenaar97792de2016-10-15 18:36:49 +02005892
5893return_dead:
Bram Moolenaar7df915d2016-11-17 17:25:32 +01005894 if (job->jv_status < JOB_ENDED)
Bram Moolenaar97792de2016-10-15 18:36:49 +02005895 job->jv_status = JOB_ENDED;
Bram Moolenaar97792de2016-10-15 18:36:49 +02005896 return "dead";
5897}
5898
5899 job_T *
5900mch_detect_ended_job(job_T *job_list)
5901{
5902# ifdef HAVE_UNION_WAIT
5903 union wait status;
5904# else
5905 int status = -1;
5906# endif
5907 pid_t wait_pid = 0;
5908 job_T *job;
5909
Bram Moolenaarc4d4ac22016-11-07 22:42:57 +01005910# ifndef USE_SYSTEM
Bram Moolenaar0f873732019-12-05 20:28:46 +01005911 // Do not do this when waiting for a shell command to finish, we would get
5912 // the exit value here (and discard it), the exit value obtained there
5913 // would then be wrong.
Bram Moolenaarc4d4ac22016-11-07 22:42:57 +01005914 if (dont_check_job_ended > 0)
5915 return NULL;
5916# endif
5917
Bram Moolenaar97792de2016-10-15 18:36:49 +02005918# ifdef __NeXT__
5919 wait_pid = wait4(-1, &status, WNOHANG, (struct rusage *)0);
5920# else
5921 wait_pid = waitpid(-1, &status, WNOHANG);
5922# endif
5923 if (wait_pid <= 0)
Bram Moolenaar0f873732019-12-05 20:28:46 +01005924 // no process ended
Bram Moolenaar97792de2016-10-15 18:36:49 +02005925 return NULL;
5926 for (job = job_list; job != NULL; job = job->jv_next)
5927 {
5928 if (job->jv_pid == wait_pid)
5929 {
5930 if (WIFEXITED(status))
Bram Moolenaar0f873732019-12-05 20:28:46 +01005931 // LINTED avoid "bitwise operation on signed value"
Bram Moolenaar97792de2016-10-15 18:36:49 +02005932 job->jv_exitval = WEXITSTATUS(status);
5933 else if (WIFSIGNALED(status))
Bram Moolenaarb3051ce2019-01-31 15:52:11 +01005934 {
Bram Moolenaar97792de2016-10-15 18:36:49 +02005935 job->jv_exitval = -1;
Bram Moolenaarb3051ce2019-01-31 15:52:11 +01005936 job->jv_termsig = get_signal_name(WTERMSIG(status));
5937 }
Bram Moolenaar7df915d2016-11-17 17:25:32 +01005938 if (job->jv_status < JOB_ENDED)
Bram Moolenaar97792de2016-10-15 18:36:49 +02005939 {
5940 ch_log(job->jv_channel, "Job ended");
5941 job->jv_status = JOB_ENDED;
5942 }
5943 return job;
5944 }
5945 }
5946 return NULL;
Bram Moolenaar835dc632016-02-07 14:27:38 +01005947}
5948
Bram Moolenaar3a117e12016-10-30 21:57:52 +01005949/*
5950 * Send a (deadly) signal to "job".
5951 * Return FAIL if "how" is not a valid name.
5952 */
Bram Moolenaar835dc632016-02-07 14:27:38 +01005953 int
Bram Moolenaar2d33e902017-08-11 16:31:54 +02005954mch_signal_job(job_T *job, char_u *how)
Bram Moolenaar835dc632016-02-07 14:27:38 +01005955{
Bram Moolenaar6463ca22016-02-13 17:04:46 +01005956 int sig = -1;
Bram Moolenaar835dc632016-02-07 14:27:38 +01005957
Bram Moolenaar923d9262016-02-25 20:56:01 +01005958 if (*how == NUL || STRCMP(how, "term") == 0)
Bram Moolenaar835dc632016-02-07 14:27:38 +01005959 sig = SIGTERM;
Bram Moolenaar923d9262016-02-25 20:56:01 +01005960 else if (STRCMP(how, "hup") == 0)
5961 sig = SIGHUP;
Bram Moolenaar835dc632016-02-07 14:27:38 +01005962 else if (STRCMP(how, "quit") == 0)
5963 sig = SIGQUIT;
Bram Moolenaar923d9262016-02-25 20:56:01 +01005964 else if (STRCMP(how, "int") == 0)
5965 sig = SIGINT;
Bram Moolenaar835dc632016-02-07 14:27:38 +01005966 else if (STRCMP(how, "kill") == 0)
5967 sig = SIGKILL;
Bram Moolenaarb13501f2017-07-22 22:32:56 +02005968#ifdef SIGWINCH
5969 else if (STRCMP(how, "winch") == 0)
5970 sig = SIGWINCH;
5971#endif
Bram Moolenaar835dc632016-02-07 14:27:38 +01005972 else if (isdigit(*how))
5973 sig = atoi((char *)how);
5974 else
5975 return FAIL;
Bram Moolenaar76467df2016-02-12 19:30:26 +01005976
Bram Moolenaar76ab4fd2018-12-08 14:39:05 +01005977 // Never kill ourselves!
5978 if (job->jv_pid != 0)
5979 {
5980 // TODO: have an option to only kill the process, not the group?
5981 kill(-job->jv_pid, sig);
5982 kill(job->jv_pid, sig);
5983 }
Bram Moolenaar76467df2016-02-12 19:30:26 +01005984
Bram Moolenaar835dc632016-02-07 14:27:38 +01005985 return OK;
5986}
Bram Moolenaar76467df2016-02-12 19:30:26 +01005987
5988/*
5989 * Clear the data related to "job".
5990 */
5991 void
5992mch_clear_job(job_T *job)
5993{
Bram Moolenaar0f873732019-12-05 20:28:46 +01005994 // call waitpid because child process may become zombie
Bram Moolenaar76467df2016-02-12 19:30:26 +01005995# ifdef __NeXT__
Bram Moolenaar4ca812b2016-03-02 21:51:16 +01005996 (void)wait4(job->jv_pid, NULL, WNOHANG, (struct rusage *)0);
Bram Moolenaar76467df2016-02-12 19:30:26 +01005997# else
Bram Moolenaar4ca812b2016-03-02 21:51:16 +01005998 (void)waitpid(job->jv_pid, NULL, WNOHANG);
Bram Moolenaar76467df2016-02-12 19:30:26 +01005999# endif
6000}
Bram Moolenaar835dc632016-02-07 14:27:38 +01006001#endif
6002
Bram Moolenaar13ebb032017-08-26 22:02:51 +02006003#if defined(FEAT_TERMINAL) || defined(PROTO)
6004 int
6005mch_create_pty_channel(job_T *job, jobopt_T *options)
6006{
6007 int pty_master_fd = -1;
6008 int pty_slave_fd = -1;
6009 channel_T *channel;
6010
Bram Moolenaar59386482019-02-10 22:43:46 +01006011 open_pty(&pty_master_fd, &pty_slave_fd, &job->jv_tty_out, &job->jv_tty_in);
Bram Moolenaard0342202020-06-29 22:40:42 +02006012 if (pty_master_fd < 0 || pty_slave_fd < 0)
6013 return FAIL;
Bram Moolenaar13ebb032017-08-26 22:02:51 +02006014 close(pty_slave_fd);
6015
6016 channel = add_channel();
6017 if (channel == NULL)
Bram Moolenaar1b9f9d32017-09-05 23:32:38 +02006018 {
6019 close(pty_master_fd);
Bram Moolenaar13ebb032017-08-26 22:02:51 +02006020 return FAIL;
Bram Moolenaar1b9f9d32017-09-05 23:32:38 +02006021 }
Bram Moolenaarf3360612017-10-01 16:21:31 +02006022 if (job->jv_tty_out != NULL)
6023 ch_log(channel, "using pty %s on fd %d",
6024 job->jv_tty_out, pty_master_fd);
Bram Moolenaar0f873732019-12-05 20:28:46 +01006025 job->jv_channel = channel; // ch_refcount was set by add_channel()
Bram Moolenaar13ebb032017-08-26 22:02:51 +02006026 channel->ch_keep_open = TRUE;
6027
Bram Moolenaar0f873732019-12-05 20:28:46 +01006028 // Only set the pty_master_fd for stdout, do not duplicate it for stderr,
6029 // it only needs to be read once.
Bram Moolenaarb0b98d52018-05-05 21:01:00 +02006030 channel_set_pipes(channel, pty_master_fd, pty_master_fd, INVALID_FD);
Bram Moolenaar13ebb032017-08-26 22:02:51 +02006031 channel_set_job(channel, job, options);
6032 return OK;
6033}
6034#endif
6035
Bram Moolenaar071d4272004-06-13 20:20:40 +00006036/*
6037 * Check for CTRL-C typed by reading all available characters.
6038 * In cooked mode we should get SIGINT, no need to check.
6039 */
6040 void
Bram Moolenaarb9c31e72016-09-29 15:18:57 +02006041mch_breakcheck(int force)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006042{
Bram Moolenaar26e86442020-05-17 14:06:16 +02006043 if ((mch_cur_tmode == TMODE_RAW || force)
Bram Moolenaarb9c31e72016-09-29 15:18:57 +02006044 && RealWaitForChar(read_cmd_fd, 0L, NULL, NULL))
Bram Moolenaar071d4272004-06-13 20:20:40 +00006045 fill_input_buf(FALSE);
6046}
6047
6048/*
Bram Moolenaar943bb2b2016-03-19 14:11:18 +01006049 * Wait "msec" msec until a character is available from the mouse, keyboard,
6050 * from inbuf[].
6051 * "msec" == -1 will block forever.
6052 * Invokes timer callbacks when needed.
Bram Moolenaare9c21ae2017-08-03 20:44:48 +02006053 * When "ignore_input" is TRUE even check for pending input when input is
6054 * already available.
Bram Moolenaarcda77642016-06-04 13:32:35 +02006055 * "interrupted" (if not NULL) is set to TRUE when no character is available
6056 * but something else needs to be done.
Bram Moolenaar40b1b542016-04-20 20:18:23 +02006057 * Returns TRUE when a character is available.
Bram Moolenaarcda77642016-06-04 13:32:35 +02006058 * When a GUI is being used, this will never get called -- webb
Bram Moolenaar071d4272004-06-13 20:20:40 +00006059 */
6060 static int
Bram Moolenaare9c21ae2017-08-03 20:44:48 +02006061WaitForChar(long msec, int *interrupted, int ignore_input)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006062{
Bram Moolenaar943bb2b2016-03-19 14:11:18 +01006063#ifdef FEAT_TIMERS
Bram Moolenaarc9e649a2017-12-18 18:14:47 +01006064 return ui_wait_for_chars_or_timer(
6065 msec, WaitForCharOrMouse, interrupted, ignore_input) == OK;
Bram Moolenaar943bb2b2016-03-19 14:11:18 +01006066#else
Bram Moolenaare9c21ae2017-08-03 20:44:48 +02006067 return WaitForCharOrMouse(msec, interrupted, ignore_input);
Bram Moolenaar943bb2b2016-03-19 14:11:18 +01006068#endif
6069}
6070
6071/*
6072 * Wait "msec" msec until a character is available from the mouse or keyboard
6073 * or from inbuf[].
6074 * "msec" == -1 will block forever.
Bram Moolenaare9c21ae2017-08-03 20:44:48 +02006075 * for "ignore_input" see WaitForCharOr().
Bram Moolenaarcda77642016-06-04 13:32:35 +02006076 * "interrupted" (if not NULL) is set to TRUE when no character is available
6077 * but something else needs to be done.
Bram Moolenaar943bb2b2016-03-19 14:11:18 +01006078 * When a GUI is being used, this will never get called -- webb
6079 */
6080 static int
Bram Moolenaare9c21ae2017-08-03 20:44:48 +02006081WaitForCharOrMouse(long msec, int *interrupted, int ignore_input)
Bram Moolenaar943bb2b2016-03-19 14:11:18 +01006082{
Bram Moolenaar071d4272004-06-13 20:20:40 +00006083#ifdef FEAT_MOUSE_GPM
6084 int gpm_process_wanted;
6085#endif
6086#ifdef FEAT_XCLIPBOARD
6087 int rest;
6088#endif
6089 int avail;
6090
Bram Moolenaar0f873732019-12-05 20:28:46 +01006091 if (!ignore_input && input_available()) // something in inbuf[]
Bram Moolenaar071d4272004-06-13 20:20:40 +00006092 return 1;
6093
6094#if defined(FEAT_MOUSE_DEC)
Bram Moolenaar0f873732019-12-05 20:28:46 +01006095 // May need to query the mouse position.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006096 if (WantQueryMouse)
6097 {
Bram Moolenaar6bb68362005-03-22 23:03:44 +00006098 WantQueryMouse = FALSE;
Bram Moolenaar92fd5992019-05-02 23:00:22 +02006099 if (!no_query_mouse_for_testing)
6100 mch_write((char_u *)IF_EB("\033[1'|", ESC_STR "[1'|"), 5);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006101 }
6102#endif
6103
6104 /*
6105 * For FEAT_MOUSE_GPM and FEAT_XCLIPBOARD we loop here to process mouse
6106 * events. This is a bit complicated, because they might both be defined.
6107 */
6108#if defined(FEAT_MOUSE_GPM) || defined(FEAT_XCLIPBOARD)
6109# ifdef FEAT_XCLIPBOARD
6110 rest = 0;
6111 if (do_xterm_trace())
6112 rest = msec;
6113# endif
6114 do
6115 {
6116# ifdef FEAT_XCLIPBOARD
6117 if (rest != 0)
6118 {
6119 msec = XT_TRACE_DELAY;
6120 if (rest >= 0 && rest < XT_TRACE_DELAY)
6121 msec = rest;
6122 if (rest >= 0)
6123 rest -= msec;
6124 }
6125# endif
Bram Moolenaar28e67e02019-08-15 23:05:49 +02006126# ifdef FEAT_SOUND_CANBERRA
6127 // Invoke any pending sound callbacks.
6128 if (has_sound_callback_in_queue())
6129 invoke_sound_callback();
6130# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006131# ifdef FEAT_MOUSE_GPM
6132 gpm_process_wanted = 0;
Bram Moolenaar8fdd7212016-03-26 19:41:48 +01006133 avail = RealWaitForChar(read_cmd_fd, msec,
Bram Moolenaarcda77642016-06-04 13:32:35 +02006134 &gpm_process_wanted, interrupted);
Bram Moolenaarb5432d82019-08-30 19:28:25 +02006135 if (!avail && !gpm_process_wanted)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006136# else
Bram Moolenaarcda77642016-06-04 13:32:35 +02006137 avail = RealWaitForChar(read_cmd_fd, msec, NULL, interrupted);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006138 if (!avail)
Bram Moolenaarb5432d82019-08-30 19:28:25 +02006139# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006140 {
Bram Moolenaare9c21ae2017-08-03 20:44:48 +02006141 if (!ignore_input && input_available())
Bram Moolenaar071d4272004-06-13 20:20:40 +00006142 return 1;
6143# ifdef FEAT_XCLIPBOARD
6144 if (rest == 0 || !do_xterm_trace())
6145# endif
6146 break;
6147 }
6148 }
6149 while (FALSE
6150# ifdef FEAT_MOUSE_GPM
6151 || (gpm_process_wanted && mch_gpm_process() == 0)
6152# endif
6153# ifdef FEAT_XCLIPBOARD
6154 || (!avail && rest != 0)
6155# endif
Bram Moolenaar8fdd7212016-03-26 19:41:48 +01006156 )
6157 ;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006158
6159#else
Bram Moolenaarcda77642016-06-04 13:32:35 +02006160 avail = RealWaitForChar(read_cmd_fd, msec, NULL, interrupted);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006161#endif
6162 return avail;
6163}
6164
Bram Moolenaar4ffa0702013-12-11 17:12:37 +01006165#ifndef VMS
Bram Moolenaar071d4272004-06-13 20:20:40 +00006166/*
6167 * Wait "msec" msec until a character is available from file descriptor "fd".
Bram Moolenaar493c7a82011-09-07 14:06:47 +02006168 * "msec" == 0 will check for characters once.
6169 * "msec" == -1 will block until a character is available.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006170 * When a GUI is being used, this will not be used for input -- webb
Bram Moolenaar071d4272004-06-13 20:20:40 +00006171 * Or when a Linux GPM mouse event is waiting.
Bram Moolenaar93c88e02015-09-15 14:12:05 +02006172 * Or when a clientserver message is on the queue.
Bram Moolenaarcda77642016-06-04 13:32:35 +02006173 * "interrupted" (if not NULL) is set to TRUE when no character is available
6174 * but something else needs to be done.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006175 */
Bram Moolenaarec70bdd2016-02-18 22:17:42 +01006176 static int
Bram Moolenaarcda77642016-06-04 13:32:35 +02006177RealWaitForChar(int fd, long msec, int *check_for_gpm UNUSED, int *interrupted)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006178{
6179 int ret;
Bram Moolenaarec70bdd2016-02-18 22:17:42 +01006180 int result;
Bram Moolenaar325b7a22004-07-05 15:58:32 +00006181#if defined(FEAT_XCLIPBOARD) || defined(USE_XSMP) || defined(FEAT_MZSCHEME)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006182 static int busy = FALSE;
6183
Bram Moolenaar0f873732019-12-05 20:28:46 +01006184 // May retry getting characters after an event was handled.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006185# define MAY_LOOP
6186
Bram Moolenaar833eb1d2016-11-24 17:22:50 +01006187# ifdef ELAPSED_FUNC
Bram Moolenaar0f873732019-12-05 20:28:46 +01006188 // Remember at what time we started, so that we know how much longer we
6189 // should wait after being interrupted.
Bram Moolenaar1ac56c22019-01-17 22:28:22 +01006190 long start_msec = msec;
6191 elapsed_T start_tv;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006192
Bram Moolenaar76b6dfe2016-06-04 14:37:22 +02006193 if (msec > 0)
Bram Moolenaar833eb1d2016-11-24 17:22:50 +01006194 ELAPSED_INIT(start_tv);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006195# endif
6196
Bram Moolenaar0f873732019-12-05 20:28:46 +01006197 // Handle being called recursively. This may happen for the session
6198 // manager stuff, it may save the file, which does a breakcheck.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006199 if (busy)
6200 return 0;
6201#endif
6202
6203#ifdef MAY_LOOP
Bram Moolenaar35fdbb52005-07-09 21:08:57 +00006204 for (;;)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006205#endif
6206 {
6207#ifdef MAY_LOOP
Bram Moolenaar0f873732019-12-05 20:28:46 +01006208 int finished = TRUE; // default is to 'loop' just once
Bram Moolenaar325b7a22004-07-05 15:58:32 +00006209# ifdef FEAT_MZSCHEME
6210 int mzquantum_used = FALSE;
6211# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006212#endif
6213#ifndef HAVE_SELECT
Bram Moolenaar0f873732019-12-05 20:28:46 +01006214 // each channel may use in, out and err
Bram Moolenaar8b877ac2016-03-28 19:16:20 +02006215 struct pollfd fds[6 + 3 * MAX_OPEN_CHANNELS];
Bram Moolenaar071d4272004-06-13 20:20:40 +00006216 int nfd;
6217# ifdef FEAT_XCLIPBOARD
6218 int xterm_idx = -1;
6219# endif
6220# ifdef FEAT_MOUSE_GPM
6221 int gpm_idx = -1;
6222# endif
6223# ifdef USE_XSMP
6224 int xsmp_idx = -1;
6225# endif
Bram Moolenaar325b7a22004-07-05 15:58:32 +00006226 int towait = (int)msec;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006227
Bram Moolenaar325b7a22004-07-05 15:58:32 +00006228# ifdef FEAT_MZSCHEME
6229 mzvim_check_threads();
6230 if (mzthreads_allowed() && p_mzq > 0 && (msec < 0 || msec > p_mzq))
6231 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01006232 towait = (int)p_mzq; // don't wait longer than 'mzquantum'
Bram Moolenaar325b7a22004-07-05 15:58:32 +00006233 mzquantum_used = TRUE;
6234 }
6235# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006236 fds[0].fd = fd;
6237 fds[0].events = POLLIN;
6238 nfd = 1;
6239
Bram Moolenaar071d4272004-06-13 20:20:40 +00006240# ifdef FEAT_XCLIPBOARD
Bram Moolenaarb1e26502014-11-19 18:48:46 +01006241 may_restore_clipboard();
Bram Moolenaar071d4272004-06-13 20:20:40 +00006242 if (xterm_Shell != (Widget)0)
6243 {
6244 xterm_idx = nfd;
6245 fds[nfd].fd = ConnectionNumber(xterm_dpy);
6246 fds[nfd].events = POLLIN;
6247 nfd++;
6248 }
6249# endif
6250# ifdef FEAT_MOUSE_GPM
6251 if (check_for_gpm != NULL && gpm_flag && gpm_fd >= 0)
6252 {
6253 gpm_idx = nfd;
6254 fds[nfd].fd = gpm_fd;
6255 fds[nfd].events = POLLIN;
6256 nfd++;
6257 }
6258# endif
6259# ifdef USE_XSMP
6260 if (xsmp_icefd != -1)
6261 {
6262 xsmp_idx = nfd;
6263 fds[nfd].fd = xsmp_icefd;
6264 fds[nfd].events = POLLIN;
6265 nfd++;
6266 }
6267# endif
Bram Moolenaar509ce2a2016-03-11 22:52:15 +01006268#ifdef FEAT_JOB_CHANNEL
Bram Moolenaarf3360612017-10-01 16:21:31 +02006269 nfd = channel_poll_setup(nfd, &fds, &towait);
Bram Moolenaar67c53842010-05-22 18:28:27 +02006270#endif
Bram Moolenaarcda77642016-06-04 13:32:35 +02006271 if (interrupted != NULL)
6272 *interrupted = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006273
Bram Moolenaar325b7a22004-07-05 15:58:32 +00006274 ret = poll(fds, nfd, towait);
Bram Moolenaarec70bdd2016-02-18 22:17:42 +01006275
6276 result = ret > 0 && (fds[0].revents & POLLIN);
Bram Moolenaarcda77642016-06-04 13:32:35 +02006277 if (result == 0 && interrupted != NULL && ret > 0)
6278 *interrupted = TRUE;
Bram Moolenaarec70bdd2016-02-18 22:17:42 +01006279
Bram Moolenaar325b7a22004-07-05 15:58:32 +00006280# ifdef FEAT_MZSCHEME
6281 if (ret == 0 && mzquantum_used)
Bram Moolenaar0f873732019-12-05 20:28:46 +01006282 // MzThreads scheduling is required and timeout occurred
Bram Moolenaar325b7a22004-07-05 15:58:32 +00006283 finished = FALSE;
6284# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006285
Bram Moolenaar071d4272004-06-13 20:20:40 +00006286# ifdef FEAT_XCLIPBOARD
6287 if (xterm_Shell != (Widget)0 && (fds[xterm_idx].revents & POLLIN))
6288 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01006289 xterm_update(); // Maybe we should hand out clipboard
Bram Moolenaar071d4272004-06-13 20:20:40 +00006290 if (--ret == 0 && !input_available())
Bram Moolenaar0f873732019-12-05 20:28:46 +01006291 // Try again
Bram Moolenaar071d4272004-06-13 20:20:40 +00006292 finished = FALSE;
6293 }
6294# endif
6295# ifdef FEAT_MOUSE_GPM
6296 if (gpm_idx >= 0 && (fds[gpm_idx].revents & POLLIN))
Bram Moolenaar071d4272004-06-13 20:20:40 +00006297 *check_for_gpm = 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006298# endif
6299# ifdef USE_XSMP
6300 if (xsmp_idx >= 0 && (fds[xsmp_idx].revents & (POLLIN | POLLHUP)))
6301 {
6302 if (fds[xsmp_idx].revents & POLLIN)
6303 {
6304 busy = TRUE;
6305 xsmp_handle_requests();
6306 busy = FALSE;
6307 }
6308 else if (fds[xsmp_idx].revents & POLLHUP)
6309 {
6310 if (p_verbose > 0)
Bram Moolenaar32526b32019-01-19 17:43:09 +01006311 verb_msg(_("XSMP lost ICE connection"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00006312 xsmp_close();
6313 }
6314 if (--ret == 0)
Bram Moolenaar0f873732019-12-05 20:28:46 +01006315 finished = FALSE; // Try again
Bram Moolenaar071d4272004-06-13 20:20:40 +00006316 }
6317# endif
Bram Moolenaar509ce2a2016-03-11 22:52:15 +01006318#ifdef FEAT_JOB_CHANNEL
Bram Moolenaar2c519cf2019-03-21 21:45:34 +01006319 // also call when ret == 0, we may be polling a keep-open channel
Bram Moolenaarf3360612017-10-01 16:21:31 +02006320 if (ret >= 0)
Bram Moolenaar2c519cf2019-03-21 21:45:34 +01006321 channel_poll_check(ret, &fds);
Bram Moolenaar67c53842010-05-22 18:28:27 +02006322#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006323
Bram Moolenaar0f873732019-12-05 20:28:46 +01006324#else // HAVE_SELECT
Bram Moolenaar071d4272004-06-13 20:20:40 +00006325
6326 struct timeval tv;
Bram Moolenaar325b7a22004-07-05 15:58:32 +00006327 struct timeval *tvp;
Bram Moolenaar61fb8d82018-11-12 21:45:08 +01006328 // These are static because they can take 8 Kbyte each and cause the
6329 // signal stack to run out with -O3.
6330 static fd_set rfds, wfds, efds;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006331 int maxfd;
Bram Moolenaar325b7a22004-07-05 15:58:32 +00006332 long towait = msec;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006333
Bram Moolenaar325b7a22004-07-05 15:58:32 +00006334# ifdef FEAT_MZSCHEME
6335 mzvim_check_threads();
6336 if (mzthreads_allowed() && p_mzq > 0 && (msec < 0 || msec > p_mzq))
6337 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01006338 towait = p_mzq; // don't wait longer than 'mzquantum'
Bram Moolenaar325b7a22004-07-05 15:58:32 +00006339 mzquantum_used = TRUE;
6340 }
6341# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006342
Bram Moolenaar325b7a22004-07-05 15:58:32 +00006343 if (towait >= 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006344 {
Bram Moolenaar325b7a22004-07-05 15:58:32 +00006345 tv.tv_sec = towait / 1000;
6346 tv.tv_usec = (towait % 1000) * (1000000/1000);
6347 tvp = &tv;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006348 }
Bram Moolenaar325b7a22004-07-05 15:58:32 +00006349 else
6350 tvp = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006351
6352 /*
6353 * Select on ready for reading and exceptional condition (end of file).
6354 */
Bram Moolenaar493c7a82011-09-07 14:06:47 +02006355select_eintr:
6356 FD_ZERO(&rfds);
Bram Moolenaar8b877ac2016-03-28 19:16:20 +02006357 FD_ZERO(&wfds);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006358 FD_ZERO(&efds);
6359 FD_SET(fd, &rfds);
6360# if !defined(__QNX__) && !defined(__CYGWIN32__)
Bram Moolenaar0f873732019-12-05 20:28:46 +01006361 // For QNX select() always returns 1 if this is set. Why?
Bram Moolenaar071d4272004-06-13 20:20:40 +00006362 FD_SET(fd, &efds);
6363# endif
6364 maxfd = fd;
6365
Bram Moolenaar071d4272004-06-13 20:20:40 +00006366# ifdef FEAT_XCLIPBOARD
Bram Moolenaarb1e26502014-11-19 18:48:46 +01006367 may_restore_clipboard();
Bram Moolenaar071d4272004-06-13 20:20:40 +00006368 if (xterm_Shell != (Widget)0)
6369 {
6370 FD_SET(ConnectionNumber(xterm_dpy), &rfds);
6371 if (maxfd < ConnectionNumber(xterm_dpy))
6372 maxfd = ConnectionNumber(xterm_dpy);
Bram Moolenaardd82d692012-08-15 17:26:57 +02006373
Bram Moolenaar0f873732019-12-05 20:28:46 +01006374 // An event may have already been read but not handled. In
6375 // particularly, XFlush may cause this.
Bram Moolenaardd82d692012-08-15 17:26:57 +02006376 xterm_update();
Bram Moolenaar071d4272004-06-13 20:20:40 +00006377 }
6378# endif
6379# ifdef FEAT_MOUSE_GPM
6380 if (check_for_gpm != NULL && gpm_flag && gpm_fd >= 0)
6381 {
6382 FD_SET(gpm_fd, &rfds);
6383 FD_SET(gpm_fd, &efds);
6384 if (maxfd < gpm_fd)
6385 maxfd = gpm_fd;
6386 }
6387# endif
6388# ifdef USE_XSMP
6389 if (xsmp_icefd != -1)
6390 {
6391 FD_SET(xsmp_icefd, &rfds);
6392 FD_SET(xsmp_icefd, &efds);
6393 if (maxfd < xsmp_icefd)
6394 maxfd = xsmp_icefd;
6395 }
6396# endif
Bram Moolenaar509ce2a2016-03-11 22:52:15 +01006397# ifdef FEAT_JOB_CHANNEL
Bram Moolenaarf3360612017-10-01 16:21:31 +02006398 maxfd = channel_select_setup(maxfd, &rfds, &wfds, &tv, &tvp);
Bram Moolenaardd82d692012-08-15 17:26:57 +02006399# endif
Bram Moolenaarcda77642016-06-04 13:32:35 +02006400 if (interrupted != NULL)
6401 *interrupted = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006402
Bram Moolenaar643b6142018-09-12 20:29:09 +02006403 ret = select(maxfd + 1, SELECT_TYPE_ARG234 &rfds,
6404 SELECT_TYPE_ARG234 &wfds, SELECT_TYPE_ARG234 &efds, tvp);
Bram Moolenaarec70bdd2016-02-18 22:17:42 +01006405 result = ret > 0 && FD_ISSET(fd, &rfds);
6406 if (result)
6407 --ret;
Bram Moolenaarcda77642016-06-04 13:32:35 +02006408 else if (interrupted != NULL && ret > 0)
6409 *interrupted = TRUE;
Bram Moolenaarec70bdd2016-02-18 22:17:42 +01006410
Bram Moolenaar493c7a82011-09-07 14:06:47 +02006411# ifdef EINTR
6412 if (ret == -1 && errno == EINTR)
Bram Moolenaar2e7b1df2011-10-12 21:04:20 +02006413 {
dbivolaruab16ad32021-12-29 19:41:47 +00006414 // Check whether the EINTR is caused by SIGTSTP
6415 if (got_tstp && !in_mch_suspend)
6416 {
6417 exarg_T ea;
dbivolaru79a6e252022-01-23 16:41:14 +00006418
dbivolaruab16ad32021-12-29 19:41:47 +00006419 ea.forceit = TRUE;
6420 ex_stop(&ea);
6421 got_tstp = FALSE;
6422 }
6423
Bram Moolenaar0f873732019-12-05 20:28:46 +01006424 // Check whether window has been resized, EINTR may be caused by
6425 // SIGWINCH.
Bram Moolenaar2e7b1df2011-10-12 21:04:20 +02006426 if (do_resize)
6427 handle_resize();
6428
Bram Moolenaar0f873732019-12-05 20:28:46 +01006429 // Interrupted by a signal, need to try again. We ignore msec
6430 // here, because we do want to check even after a timeout if
6431 // characters are available. Needed for reading output of an
6432 // external command after the process has finished.
Bram Moolenaar493c7a82011-09-07 14:06:47 +02006433 goto select_eintr;
Bram Moolenaar2e7b1df2011-10-12 21:04:20 +02006434 }
Bram Moolenaar493c7a82011-09-07 14:06:47 +02006435# endif
Bram Moolenaar311d9822007-02-27 15:48:28 +00006436# ifdef __TANDEM
6437 if (ret == -1 && errno == ENOTSUP)
6438 {
6439 FD_ZERO(&rfds);
6440 FD_ZERO(&efds);
6441 ret = 0;
6442 }
Bram Moolenaar493c7a82011-09-07 14:06:47 +02006443# endif
Bram Moolenaar325b7a22004-07-05 15:58:32 +00006444# ifdef FEAT_MZSCHEME
6445 if (ret == 0 && mzquantum_used)
Bram Moolenaar0f873732019-12-05 20:28:46 +01006446 // loop if MzThreads must be scheduled and timeout occurred
Bram Moolenaar325b7a22004-07-05 15:58:32 +00006447 finished = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006448# endif
6449
Bram Moolenaar071d4272004-06-13 20:20:40 +00006450# ifdef FEAT_XCLIPBOARD
6451 if (ret > 0 && xterm_Shell != (Widget)0
6452 && FD_ISSET(ConnectionNumber(xterm_dpy), &rfds))
6453 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01006454 xterm_update(); // Maybe we should hand out clipboard
6455 // continue looping when we only got the X event and the input
6456 // buffer is empty
Bram Moolenaar071d4272004-06-13 20:20:40 +00006457 if (--ret == 0 && !input_available())
6458 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01006459 // Try again
Bram Moolenaar071d4272004-06-13 20:20:40 +00006460 finished = FALSE;
6461 }
6462 }
6463# endif
6464# ifdef FEAT_MOUSE_GPM
6465 if (ret > 0 && gpm_flag && check_for_gpm != NULL && gpm_fd >= 0)
6466 {
6467 if (FD_ISSET(gpm_fd, &efds))
6468 gpm_close();
6469 else if (FD_ISSET(gpm_fd, &rfds))
6470 *check_for_gpm = 1;
6471 }
6472# endif
6473# ifdef USE_XSMP
6474 if (ret > 0 && xsmp_icefd != -1)
6475 {
6476 if (FD_ISSET(xsmp_icefd, &efds))
6477 {
6478 if (p_verbose > 0)
Bram Moolenaar32526b32019-01-19 17:43:09 +01006479 verb_msg(_("XSMP lost ICE connection"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00006480 xsmp_close();
6481 if (--ret == 0)
Bram Moolenaar0f873732019-12-05 20:28:46 +01006482 finished = FALSE; // keep going if event was only one
Bram Moolenaar071d4272004-06-13 20:20:40 +00006483 }
6484 else if (FD_ISSET(xsmp_icefd, &rfds))
6485 {
6486 busy = TRUE;
6487 xsmp_handle_requests();
6488 busy = FALSE;
6489 if (--ret == 0)
Bram Moolenaar0f873732019-12-05 20:28:46 +01006490 finished = FALSE; // keep going if event was only one
Bram Moolenaar071d4272004-06-13 20:20:40 +00006491 }
6492 }
6493# endif
Bram Moolenaar509ce2a2016-03-11 22:52:15 +01006494#ifdef FEAT_JOB_CHANNEL
Bram Moolenaar0f873732019-12-05 20:28:46 +01006495 // also call when ret == 0, we may be polling a keep-open channel
Bram Moolenaarf3360612017-10-01 16:21:31 +02006496 if (ret >= 0)
Bram Moolenaar8b877ac2016-03-28 19:16:20 +02006497 ret = channel_select_check(ret, &rfds, &wfds);
Bram Moolenaar67c53842010-05-22 18:28:27 +02006498#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006499
Bram Moolenaar0f873732019-12-05 20:28:46 +01006500#endif // HAVE_SELECT
Bram Moolenaar071d4272004-06-13 20:20:40 +00006501
6502#ifdef MAY_LOOP
6503 if (finished || msec == 0)
6504 break;
6505
Bram Moolenaar93c88e02015-09-15 14:12:05 +02006506# ifdef FEAT_CLIENTSERVER
6507 if (server_waiting())
6508 break;
6509# endif
6510
Bram Moolenaar0f873732019-12-05 20:28:46 +01006511 // We're going to loop around again, find out for how long
Bram Moolenaar071d4272004-06-13 20:20:40 +00006512 if (msec > 0)
6513 {
Bram Moolenaar833eb1d2016-11-24 17:22:50 +01006514# ifdef ELAPSED_FUNC
Bram Moolenaar0f873732019-12-05 20:28:46 +01006515 // Compute remaining wait time.
Bram Moolenaar833eb1d2016-11-24 17:22:50 +01006516 msec = start_msec - ELAPSED_FUNC(start_tv);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006517# else
Bram Moolenaar0f873732019-12-05 20:28:46 +01006518 // Guess we got interrupted halfway.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006519 msec = msec / 2;
6520# endif
6521 if (msec <= 0)
Bram Moolenaar0f873732019-12-05 20:28:46 +01006522 break; // waited long enough
Bram Moolenaar071d4272004-06-13 20:20:40 +00006523 }
6524#endif
6525 }
6526
Bram Moolenaarec70bdd2016-02-18 22:17:42 +01006527 return result;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006528}
6529
Bram Moolenaar071d4272004-06-13 20:20:40 +00006530/*
Bram Moolenaar02743632005-07-25 20:42:36 +00006531 * Expand a path into all matching files and/or directories. Handles "*",
6532 * "?", "[a-z]", "**", etc.
6533 * "path" has backslashes before chars that are not to be expanded.
6534 * Returns the number of matches found.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006535 */
6536 int
Bram Moolenaar05540972016-01-30 20:31:25 +01006537mch_expandpath(
6538 garray_T *gap,
6539 char_u *path,
Bram Moolenaar0f873732019-12-05 20:28:46 +01006540 int flags) // EW_* flags
Bram Moolenaar071d4272004-06-13 20:20:40 +00006541{
Bram Moolenaar02743632005-07-25 20:42:36 +00006542 return unix_expandpath(gap, path, 0, flags, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006543}
Bram Moolenaar071d4272004-06-13 20:20:40 +00006544
6545/*
6546 * mch_expand_wildcards() - this code does wild-card pattern matching using
6547 * the shell
6548 *
6549 * return OK for success, FAIL for error (you may lose some memory) and put
6550 * an error message in *file.
6551 *
6552 * num_pat is number of input patterns
6553 * pat is array of pointers to input patterns
6554 * num_file is pointer to number of matched file names
6555 * file is pointer to array of pointers to matched file names
6556 */
6557
6558#ifndef SEEK_SET
6559# define SEEK_SET 0
6560#endif
6561#ifndef SEEK_END
6562# define SEEK_END 2
6563#endif
6564
Bram Moolenaar5555acc2006-04-07 21:33:12 +00006565#define SHELL_SPECIAL (char_u *)"\t \"&'$;<>()\\|"
Bram Moolenaar316059c2006-01-14 21:18:42 +00006566
Bram Moolenaar071d4272004-06-13 20:20:40 +00006567 int
Bram Moolenaar05540972016-01-30 20:31:25 +01006568mch_expand_wildcards(
6569 int num_pat,
6570 char_u **pat,
6571 int *num_file,
6572 char_u ***file,
Bram Moolenaar0f873732019-12-05 20:28:46 +01006573 int flags) // EW_* flags
Bram Moolenaar071d4272004-06-13 20:20:40 +00006574{
6575 int i;
6576 size_t len;
Bram Moolenaar85325f82017-03-30 21:18:45 +02006577 long llen;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006578 char_u *p;
6579 int dir;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006580
Bram Moolenaarc7247912008-01-13 12:54:11 +00006581 /*
6582 * This is the non-OS/2 implementation (really Unix).
6583 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006584 int j;
6585 char_u *tempname;
6586 char_u *command;
6587 FILE *fd;
6588 char_u *buffer;
Bram Moolenaar0f873732019-12-05 20:28:46 +01006589#define STYLE_ECHO 0 // use "echo", the default
6590#define STYLE_GLOB 1 // use "glob", for csh
6591#define STYLE_VIMGLOB 2 // use "vimglob", for Posix sh
6592#define STYLE_PRINT 3 // use "print -N", for zsh
6593#define STYLE_BT 4 // `cmd` expansion, execute the pattern
6594 // directly
Bram Moolenaar071d4272004-06-13 20:20:40 +00006595 int shell_style = STYLE_ECHO;
6596 int check_spaces;
6597 static int did_find_nul = FALSE;
Bram Moolenaarbdace832019-03-02 10:13:42 +01006598 int ampersand = FALSE;
Bram Moolenaar0f873732019-12-05 20:28:46 +01006599 // vimglob() function to define for Posix shell
Bram Moolenaar3577c6f2008-06-24 21:16:56 +00006600 static char *sh_vimglob_func = "vimglob() { while [ $# -ge 1 ]; do echo \"$1\"; shift; done }; vimglob >";
Bram Moolenaar071d4272004-06-13 20:20:40 +00006601
Bram Moolenaar0f873732019-12-05 20:28:46 +01006602 *num_file = 0; // default: no files found
Bram Moolenaar071d4272004-06-13 20:20:40 +00006603 *file = NULL;
6604
6605 /*
6606 * If there are no wildcards, just copy the names to allocated memory.
6607 * Saves a lot of time, because we don't have to start a new shell.
6608 */
6609 if (!have_wildcard(num_pat, pat))
6610 return save_patterns(num_pat, pat, num_file, file);
6611
Bram Moolenaar0e634da2005-07-20 21:57:28 +00006612# ifdef HAVE_SANDBOX
Bram Moolenaar0f873732019-12-05 20:28:46 +01006613 // Don't allow any shell command in the sandbox.
Bram Moolenaar0e634da2005-07-20 21:57:28 +00006614 if (sandbox != 0 && check_secure())
6615 return FAIL;
6616# endif
6617
Bram Moolenaar071d4272004-06-13 20:20:40 +00006618 /*
6619 * Don't allow the use of backticks in secure and restricted mode.
6620 */
Bram Moolenaar0e634da2005-07-20 21:57:28 +00006621 if (secure || restricted)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006622 for (i = 0; i < num_pat; ++i)
6623 if (vim_strchr(pat[i], '`') != NULL
6624 && (check_restricted() || check_secure()))
6625 return FAIL;
6626
6627 /*
6628 * get a name for the temp file
6629 */
Bram Moolenaare5c421c2015-03-31 13:33:08 +02006630 if ((tempname = vim_tempname('o', FALSE)) == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006631 {
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00006632 emsg(_(e_cant_get_temp_file_name));
Bram Moolenaar071d4272004-06-13 20:20:40 +00006633 return FAIL;
6634 }
6635
6636 /*
6637 * Let the shell expand the patterns and write the result into the temp
Bram Moolenaarc7247912008-01-13 12:54:11 +00006638 * file.
6639 * STYLE_BT: NL separated
6640 * If expanding `cmd` execute it directly.
6641 * STYLE_GLOB: NUL separated
6642 * If we use *csh, "glob" will work better than "echo".
6643 * STYLE_PRINT: NL or NUL separated
6644 * If we use *zsh, "print -N" will work better than "glob".
6645 * STYLE_VIMGLOB: NL separated
6646 * If we use *sh*, we define "vimglob()".
6647 * STYLE_ECHO: space separated.
6648 * A shell we don't know, stay safe and use "echo".
Bram Moolenaar071d4272004-06-13 20:20:40 +00006649 */
6650 if (num_pat == 1 && *pat[0] == '`'
6651 && (len = STRLEN(pat[0])) > 2
6652 && *(pat[0] + len - 1) == '`')
6653 shell_style = STYLE_BT;
6654 else if ((len = STRLEN(p_sh)) >= 3)
6655 {
6656 if (STRCMP(p_sh + len - 3, "csh") == 0)
6657 shell_style = STYLE_GLOB;
6658 else if (STRCMP(p_sh + len - 3, "zsh") == 0)
6659 shell_style = STYLE_PRINT;
6660 }
Bram Moolenaarc7247912008-01-13 12:54:11 +00006661 if (shell_style == STYLE_ECHO && strstr((char *)gettail(p_sh),
6662 "sh") != NULL)
6663 shell_style = STYLE_VIMGLOB;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006664
Bram Moolenaar0f873732019-12-05 20:28:46 +01006665 // Compute the length of the command. We need 2 extra bytes: for the
6666 // optional '&' and for the NUL.
6667 // Worst case: "unset nonomatch; print -N >" plus two is 29
Bram Moolenaar071d4272004-06-13 20:20:40 +00006668 len = STRLEN(tempname) + 29;
Bram Moolenaarc7247912008-01-13 12:54:11 +00006669 if (shell_style == STYLE_VIMGLOB)
6670 len += STRLEN(sh_vimglob_func);
6671
Bram Moolenaarb23c3382005-01-31 19:09:12 +00006672 for (i = 0; i < num_pat; ++i)
6673 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01006674 // Count the length of the patterns in the same way as they are put in
6675 // "command" below.
Bram Moolenaarb23c3382005-01-31 19:09:12 +00006676#ifdef USE_SYSTEM
Bram Moolenaar0f873732019-12-05 20:28:46 +01006677 len += STRLEN(pat[i]) + 3; // add space and two quotes
Bram Moolenaarb23c3382005-01-31 19:09:12 +00006678#else
Bram Moolenaar0f873732019-12-05 20:28:46 +01006679 ++len; // add space
Bram Moolenaar316059c2006-01-14 21:18:42 +00006680 for (j = 0; pat[i][j] != NUL; ++j)
6681 {
6682 if (vim_strchr(SHELL_SPECIAL, pat[i][j]) != NULL)
Bram Moolenaar0f873732019-12-05 20:28:46 +01006683 ++len; // may add a backslash
Bram Moolenaar316059c2006-01-14 21:18:42 +00006684 ++len;
6685 }
Bram Moolenaarb23c3382005-01-31 19:09:12 +00006686#endif
6687 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006688 command = alloc(len);
6689 if (command == NULL)
6690 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01006691 // out of memory
Bram Moolenaar071d4272004-06-13 20:20:40 +00006692 vim_free(tempname);
6693 return FAIL;
6694 }
6695
6696 /*
6697 * Build the shell command:
6698 * - Set $nonomatch depending on EW_NOTFOUND (hopefully the shell
6699 * recognizes this).
6700 * - Add the shell command to print the expanded names.
6701 * - Add the temp file name.
6702 * - Add the file name patterns.
6703 */
6704 if (shell_style == STYLE_BT)
6705 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01006706 // change `command; command& ` to (command; command )
Bram Moolenaar316059c2006-01-14 21:18:42 +00006707 STRCPY(command, "(");
Bram Moolenaar0f873732019-12-05 20:28:46 +01006708 STRCAT(command, pat[0] + 1); // exclude first backtick
Bram Moolenaar071d4272004-06-13 20:20:40 +00006709 p = command + STRLEN(command) - 1;
Bram Moolenaar0f873732019-12-05 20:28:46 +01006710 *p-- = ')'; // remove last backtick
Bram Moolenaar1c465442017-03-12 20:10:05 +01006711 while (p > command && VIM_ISWHITE(*p))
Bram Moolenaar071d4272004-06-13 20:20:40 +00006712 --p;
Bram Moolenaar0f873732019-12-05 20:28:46 +01006713 if (*p == '&') // remove trailing '&'
Bram Moolenaar071d4272004-06-13 20:20:40 +00006714 {
Bram Moolenaarbdace832019-03-02 10:13:42 +01006715 ampersand = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006716 *p = ' ';
6717 }
6718 STRCAT(command, ">");
6719 }
6720 else
6721 {
Christian Brabandt8b8d8292021-11-19 12:37:36 +00006722 STRCPY(command, "");
6723 if (shell_style == STYLE_GLOB)
6724 {
6725 // Assume the nonomatch option is valid only for csh like shells,
6726 // otherwise, this may set the positional parameters for the shell,
6727 // e.g. "$*".
6728 if (flags & EW_NOTFOUND)
6729 STRCAT(command, "set nonomatch; ");
6730 else
6731 STRCAT(command, "unset nonomatch; ");
6732 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006733 if (shell_style == STYLE_GLOB)
6734 STRCAT(command, "glob >");
6735 else if (shell_style == STYLE_PRINT)
6736 STRCAT(command, "print -N >");
Bram Moolenaarc7247912008-01-13 12:54:11 +00006737 else if (shell_style == STYLE_VIMGLOB)
6738 STRCAT(command, sh_vimglob_func);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006739 else
6740 STRCAT(command, "echo >");
6741 }
Bram Moolenaarc7247912008-01-13 12:54:11 +00006742
Bram Moolenaar071d4272004-06-13 20:20:40 +00006743 STRCAT(command, tempname);
Bram Moolenaarc7247912008-01-13 12:54:11 +00006744
Bram Moolenaar071d4272004-06-13 20:20:40 +00006745 if (shell_style != STYLE_BT)
6746 for (i = 0; i < num_pat; ++i)
6747 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01006748 // When using system() always add extra quotes, because the shell
6749 // is started twice. Otherwise put a backslash before special
6750 // characters, except inside ``.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006751#ifdef USE_SYSTEM
6752 STRCAT(command, " \"");
6753 STRCAT(command, pat[i]);
6754 STRCAT(command, "\"");
6755#else
Bram Moolenaar582fd852005-03-28 20:58:01 +00006756 int intick = FALSE;
6757
Bram Moolenaar071d4272004-06-13 20:20:40 +00006758 p = command + STRLEN(command);
6759 *p++ = ' ';
Bram Moolenaar316059c2006-01-14 21:18:42 +00006760 for (j = 0; pat[i][j] != NUL; ++j)
Bram Moolenaar582fd852005-03-28 20:58:01 +00006761 {
6762 if (pat[i][j] == '`')
Bram Moolenaar582fd852005-03-28 20:58:01 +00006763 intick = !intick;
Bram Moolenaar316059c2006-01-14 21:18:42 +00006764 else if (pat[i][j] == '\\' && pat[i][j + 1] != NUL)
6765 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01006766 // Remove a backslash, take char literally. But keep
6767 // backslash inside backticks, before a special character
6768 // and before a backtick.
Bram Moolenaard12f5c12006-01-25 22:10:52 +00006769 if (intick
Bram Moolenaar49315f62006-02-04 00:54:59 +00006770 || vim_strchr(SHELL_SPECIAL, pat[i][j + 1]) != NULL
6771 || pat[i][j + 1] == '`')
Bram Moolenaard12f5c12006-01-25 22:10:52 +00006772 *p++ = '\\';
Bram Moolenaar280f1262006-01-30 00:14:18 +00006773 ++j;
Bram Moolenaar316059c2006-01-14 21:18:42 +00006774 }
Bram Moolenaare4df1642014-08-29 12:58:44 +02006775 else if (!intick
6776 && ((flags & EW_KEEPDOLLAR) == 0 || pat[i][j] != '$')
6777 && vim_strchr(SHELL_SPECIAL, pat[i][j]) != NULL)
Bram Moolenaar0f873732019-12-05 20:28:46 +01006778 // Put a backslash before a special character, but not
6779 // when inside ``. And not for $var when EW_KEEPDOLLAR is
6780 // set.
Bram Moolenaar316059c2006-01-14 21:18:42 +00006781 *p++ = '\\';
Bram Moolenaar280f1262006-01-30 00:14:18 +00006782
Bram Moolenaar0f873732019-12-05 20:28:46 +01006783 // Copy one character.
Bram Moolenaar280f1262006-01-30 00:14:18 +00006784 *p++ = pat[i][j];
Bram Moolenaar582fd852005-03-28 20:58:01 +00006785 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006786 *p = NUL;
6787#endif
6788 }
6789 if (flags & EW_SILENT)
6790 show_shell_mess = FALSE;
Bram Moolenaarbdace832019-03-02 10:13:42 +01006791 if (ampersand)
Bram Moolenaar0f873732019-12-05 20:28:46 +01006792 STRCAT(command, "&"); // put the '&' after the redirection
Bram Moolenaar071d4272004-06-13 20:20:40 +00006793
6794 /*
6795 * Using zsh -G: If a pattern has no matches, it is just deleted from
6796 * the argument list, otherwise zsh gives an error message and doesn't
6797 * expand any other pattern.
6798 */
6799 if (shell_style == STYLE_PRINT)
Bram Moolenaar0f873732019-12-05 20:28:46 +01006800 extra_shell_arg = (char_u *)"-G"; // Use zsh NULL_GLOB option
Bram Moolenaar071d4272004-06-13 20:20:40 +00006801
6802 /*
6803 * If we use -f then shell variables set in .cshrc won't get expanded.
6804 * vi can do it, so we will too, but it is only necessary if there is a "$"
6805 * in one of the patterns, otherwise we can still use the fast option.
6806 */
6807 else if (shell_style == STYLE_GLOB && !have_dollars(num_pat, pat))
Bram Moolenaar0f873732019-12-05 20:28:46 +01006808 extra_shell_arg = (char_u *)"-f"; // Use csh fast option
Bram Moolenaar071d4272004-06-13 20:20:40 +00006809
6810 /*
6811 * execute the shell command
6812 */
6813 i = call_shell(command, SHELL_EXPAND | SHELL_SILENT);
6814
Bram Moolenaar0f873732019-12-05 20:28:46 +01006815 // When running in the background, give it some time to create the temp
6816 // file, but don't wait for it to finish.
Bram Moolenaarbdace832019-03-02 10:13:42 +01006817 if (ampersand)
Bram Moolenaar0981c872020-08-23 14:28:37 +02006818 mch_delay(10L, MCH_DELAY_IGNOREINPUT);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006819
Bram Moolenaar0f873732019-12-05 20:28:46 +01006820 extra_shell_arg = NULL; // cleanup
Bram Moolenaar071d4272004-06-13 20:20:40 +00006821 show_shell_mess = TRUE;
6822 vim_free(command);
6823
Bram Moolenaar0f873732019-12-05 20:28:46 +01006824 if (i != 0) // mch_call_shell() failed
Bram Moolenaar071d4272004-06-13 20:20:40 +00006825 {
6826 mch_remove(tempname);
6827 vim_free(tempname);
6828 /*
6829 * With interactive completion, the error message is not printed.
6830 * However with USE_SYSTEM, I don't know how to turn off error messages
6831 * from the shell, so screen may still get messed up -- webb.
6832 */
6833#ifndef USE_SYSTEM
6834 if (!(flags & EW_SILENT))
6835#endif
6836 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01006837 redraw_later_clear(); // probably messed up screen
6838 msg_putchar('\n'); // clear bottom line quickly
6839 cmdline_row = Rows - 1; // continue on last line
Bram Moolenaar071d4272004-06-13 20:20:40 +00006840#ifdef USE_SYSTEM
6841 if (!(flags & EW_SILENT))
6842#endif
6843 {
Bram Moolenaar40bcec12021-12-05 22:19:27 +00006844 msg(_(e_cannot_expand_wildcards));
Bram Moolenaar0f873732019-12-05 20:28:46 +01006845 msg_start(); // don't overwrite this message
Bram Moolenaar071d4272004-06-13 20:20:40 +00006846 }
6847 }
Bram Moolenaar0f873732019-12-05 20:28:46 +01006848 // If a `cmd` expansion failed, don't list `cmd` as a match, even when
6849 // EW_NOTFOUND is given
Bram Moolenaar071d4272004-06-13 20:20:40 +00006850 if (shell_style == STYLE_BT)
6851 return FAIL;
6852 goto notfound;
6853 }
6854
6855 /*
6856 * read the names from the file into memory
6857 */
6858 fd = fopen((char *)tempname, READBIN);
6859 if (fd == NULL)
6860 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01006861 // Something went wrong, perhaps a file name with a special char.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006862 if (!(flags & EW_SILENT))
6863 {
Bram Moolenaar40bcec12021-12-05 22:19:27 +00006864 msg(_(e_cannot_expand_wildcards));
Bram Moolenaar0f873732019-12-05 20:28:46 +01006865 msg_start(); // don't overwrite this message
Bram Moolenaar071d4272004-06-13 20:20:40 +00006866 }
6867 vim_free(tempname);
6868 goto notfound;
6869 }
6870 fseek(fd, 0L, SEEK_END);
Bram Moolenaar0f873732019-12-05 20:28:46 +01006871 llen = ftell(fd); // get size of temp file
Bram Moolenaar071d4272004-06-13 20:20:40 +00006872 fseek(fd, 0L, SEEK_SET);
Bram Moolenaar85325f82017-03-30 21:18:45 +02006873 if (llen < 0)
Bram Moolenaar0f873732019-12-05 20:28:46 +01006874 // just in case ftell() would fail
Bram Moolenaar85325f82017-03-30 21:18:45 +02006875 buffer = NULL;
6876 else
6877 buffer = alloc(llen + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006878 if (buffer == NULL)
6879 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01006880 // out of memory
Bram Moolenaar071d4272004-06-13 20:20:40 +00006881 mch_remove(tempname);
6882 vim_free(tempname);
6883 fclose(fd);
6884 return FAIL;
6885 }
Bram Moolenaar85325f82017-03-30 21:18:45 +02006886 len = llen;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006887 i = fread((char *)buffer, 1, len, fd);
6888 fclose(fd);
6889 mch_remove(tempname);
Bram Moolenaar78a15312009-05-15 19:33:18 +00006890 if (i != (int)len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006891 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01006892 // unexpected read error
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00006893 semsg(_(e_cant_read_file_str), tempname);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006894 vim_free(tempname);
6895 vim_free(buffer);
6896 return FAIL;
6897 }
6898 vim_free(tempname);
6899
Bram Moolenaar1eed5322019-02-26 17:03:54 +01006900# ifdef __CYGWIN__
Bram Moolenaar0f873732019-12-05 20:28:46 +01006901 // Translate <CR><NL> into <NL>. Caution, buffer may contain NUL.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006902 p = buffer;
Bram Moolenaarfe17e762013-06-29 14:17:02 +02006903 for (i = 0; i < (int)len; ++i)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006904 if (!(buffer[i] == CAR && buffer[i + 1] == NL))
6905 *p++ = buffer[i];
6906 len = p - buffer;
6907# endif
6908
6909
Bram Moolenaar0f873732019-12-05 20:28:46 +01006910 // file names are separated with Space
Bram Moolenaar071d4272004-06-13 20:20:40 +00006911 if (shell_style == STYLE_ECHO)
6912 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01006913 buffer[len] = '\n'; // make sure the buffer ends in NL
Bram Moolenaar071d4272004-06-13 20:20:40 +00006914 p = buffer;
Bram Moolenaar0f873732019-12-05 20:28:46 +01006915 for (i = 0; *p != '\n'; ++i) // count number of entries
Bram Moolenaar071d4272004-06-13 20:20:40 +00006916 {
6917 while (*p != ' ' && *p != '\n')
6918 ++p;
Bram Moolenaar0f873732019-12-05 20:28:46 +01006919 p = skipwhite(p); // skip to next entry
Bram Moolenaar071d4272004-06-13 20:20:40 +00006920 }
6921 }
Bram Moolenaar0f873732019-12-05 20:28:46 +01006922 // file names are separated with NL
Bram Moolenaarc7247912008-01-13 12:54:11 +00006923 else if (shell_style == STYLE_BT || shell_style == STYLE_VIMGLOB)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006924 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01006925 buffer[len] = NUL; // make sure the buffer ends in NUL
Bram Moolenaar071d4272004-06-13 20:20:40 +00006926 p = buffer;
Bram Moolenaar0f873732019-12-05 20:28:46 +01006927 for (i = 0; *p != NUL; ++i) // count number of entries
Bram Moolenaar071d4272004-06-13 20:20:40 +00006928 {
6929 while (*p != '\n' && *p != NUL)
6930 ++p;
6931 if (*p != NUL)
6932 ++p;
Bram Moolenaar0f873732019-12-05 20:28:46 +01006933 p = skipwhite(p); // skip leading white space
Bram Moolenaar071d4272004-06-13 20:20:40 +00006934 }
6935 }
Bram Moolenaar0f873732019-12-05 20:28:46 +01006936 // file names are separated with NUL
Bram Moolenaar071d4272004-06-13 20:20:40 +00006937 else
6938 {
6939 /*
6940 * Some versions of zsh use spaces instead of NULs to separate
6941 * results. Only do this when there is no NUL before the end of the
6942 * buffer, otherwise we would never be able to use file names with
6943 * embedded spaces when zsh does use NULs.
6944 * When we found a NUL once, we know zsh is OK, set did_find_nul and
6945 * don't check for spaces again.
6946 */
6947 check_spaces = FALSE;
6948 if (shell_style == STYLE_PRINT && !did_find_nul)
6949 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01006950 // If there is a NUL, set did_find_nul, else set check_spaces
Bram Moolenaaref9d6aa2011-04-11 16:56:35 +02006951 buffer[len] = NUL;
Bram Moolenaarb011af92013-12-11 13:21:51 +01006952 if (len && (int)STRLEN(buffer) < (int)len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006953 did_find_nul = TRUE;
6954 else
6955 check_spaces = TRUE;
6956 }
6957
6958 /*
6959 * Make sure the buffer ends with a NUL. For STYLE_PRINT there
6960 * already is one, for STYLE_GLOB it needs to be added.
6961 */
6962 if (len && buffer[len - 1] == NUL)
6963 --len;
6964 else
6965 buffer[len] = NUL;
6966 i = 0;
6967 for (p = buffer; p < buffer + len; ++p)
Bram Moolenaar0f873732019-12-05 20:28:46 +01006968 if (*p == NUL || (*p == ' ' && check_spaces)) // count entry
Bram Moolenaar071d4272004-06-13 20:20:40 +00006969 {
6970 ++i;
6971 *p = NUL;
6972 }
6973 if (len)
Bram Moolenaar0f873732019-12-05 20:28:46 +01006974 ++i; // count last entry
Bram Moolenaar071d4272004-06-13 20:20:40 +00006975 }
6976 if (i == 0)
6977 {
6978 /*
6979 * Can happen when using /bin/sh and typing ":e $NO_SUCH_VAR^I".
6980 * /bin/sh will happily expand it to nothing rather than returning an
6981 * error; and hey, it's good to check anyway -- webb.
6982 */
6983 vim_free(buffer);
6984 goto notfound;
6985 }
6986 *num_file = i;
Bram Moolenaarc799fe22019-05-28 23:08:19 +02006987 *file = ALLOC_MULT(char_u *, i);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006988 if (*file == NULL)
6989 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01006990 // out of memory
Bram Moolenaar071d4272004-06-13 20:20:40 +00006991 vim_free(buffer);
6992 return FAIL;
6993 }
6994
6995 /*
6996 * Isolate the individual file names.
6997 */
6998 p = buffer;
6999 for (i = 0; i < *num_file; ++i)
7000 {
7001 (*file)[i] = p;
Bram Moolenaar0f873732019-12-05 20:28:46 +01007002 // Space or NL separates
Bram Moolenaarc7247912008-01-13 12:54:11 +00007003 if (shell_style == STYLE_ECHO || shell_style == STYLE_BT
7004 || shell_style == STYLE_VIMGLOB)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007005 {
Bram Moolenaar49315f62006-02-04 00:54:59 +00007006 while (!(shell_style == STYLE_ECHO && *p == ' ')
7007 && *p != '\n' && *p != NUL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007008 ++p;
Bram Moolenaar0f873732019-12-05 20:28:46 +01007009 if (p == buffer + len) // last entry
Bram Moolenaar071d4272004-06-13 20:20:40 +00007010 *p = NUL;
7011 else
7012 {
7013 *p++ = NUL;
Bram Moolenaar0f873732019-12-05 20:28:46 +01007014 p = skipwhite(p); // skip to next entry
Bram Moolenaar071d4272004-06-13 20:20:40 +00007015 }
7016 }
Bram Moolenaar0f873732019-12-05 20:28:46 +01007017 else // NUL separates
Bram Moolenaar071d4272004-06-13 20:20:40 +00007018 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01007019 while (*p && p < buffer + len) // skip entry
Bram Moolenaar071d4272004-06-13 20:20:40 +00007020 ++p;
Bram Moolenaar0f873732019-12-05 20:28:46 +01007021 ++p; // skip NUL
Bram Moolenaar071d4272004-06-13 20:20:40 +00007022 }
7023 }
7024
7025 /*
7026 * Move the file names to allocated memory.
7027 */
7028 for (j = 0, i = 0; i < *num_file; ++i)
7029 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01007030 // Require the files to exist. Helps when using /bin/sh
Bram Moolenaar071d4272004-06-13 20:20:40 +00007031 if (!(flags & EW_NOTFOUND) && mch_getperm((*file)[i]) < 0)
7032 continue;
7033
Bram Moolenaar0f873732019-12-05 20:28:46 +01007034 // check if this entry should be included
Bram Moolenaar071d4272004-06-13 20:20:40 +00007035 dir = (mch_isdir((*file)[i]));
7036 if ((dir && !(flags & EW_DIR)) || (!dir && !(flags & EW_FILE)))
7037 continue;
7038
Bram Moolenaar0f873732019-12-05 20:28:46 +01007039 // Skip files that are not executable if we check for that.
Bram Moolenaarb5971142015-03-21 17:32:19 +01007040 if (!dir && (flags & EW_EXEC)
7041 && !mch_can_exe((*file)[i], NULL, !(flags & EW_SHELLCMD)))
Bram Moolenaara2031822006-03-07 22:29:51 +00007042 continue;
7043
Bram Moolenaar964b3742019-05-24 18:54:09 +02007044 p = alloc(STRLEN((*file)[i]) + 1 + dir);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007045 if (p)
7046 {
7047 STRCPY(p, (*file)[i]);
7048 if (dir)
Bram Moolenaar0f873732019-12-05 20:28:46 +01007049 add_pathsep(p); // add '/' to a directory name
Bram Moolenaar071d4272004-06-13 20:20:40 +00007050 (*file)[j++] = p;
7051 }
7052 }
7053 vim_free(buffer);
7054 *num_file = j;
7055
Bram Moolenaar0f873732019-12-05 20:28:46 +01007056 if (*num_file == 0) // rejected all entries
Bram Moolenaar071d4272004-06-13 20:20:40 +00007057 {
Bram Moolenaard23a8232018-02-10 18:45:26 +01007058 VIM_CLEAR(*file);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007059 goto notfound;
7060 }
7061
7062 return OK;
7063
7064notfound:
7065 if (flags & EW_NOTFOUND)
7066 return save_patterns(num_pat, pat, num_file, file);
7067 return FAIL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007068}
7069
Bram Moolenaar0f873732019-12-05 20:28:46 +01007070#endif // VMS
Bram Moolenaar071d4272004-06-13 20:20:40 +00007071
Bram Moolenaar071d4272004-06-13 20:20:40 +00007072 static int
Bram Moolenaar05540972016-01-30 20:31:25 +01007073save_patterns(
7074 int num_pat,
7075 char_u **pat,
7076 int *num_file,
7077 char_u ***file)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007078{
7079 int i;
Bram Moolenaard8b02732005-01-14 21:48:43 +00007080 char_u *s;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007081
Bram Moolenaarc799fe22019-05-28 23:08:19 +02007082 *file = ALLOC_MULT(char_u *, num_pat);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007083 if (*file == NULL)
7084 return FAIL;
7085 for (i = 0; i < num_pat; i++)
Bram Moolenaard8b02732005-01-14 21:48:43 +00007086 {
7087 s = vim_strsave(pat[i]);
7088 if (s != NULL)
Bram Moolenaar0f873732019-12-05 20:28:46 +01007089 // Be compatible with expand_filename(): halve the number of
7090 // backslashes.
Bram Moolenaard8b02732005-01-14 21:48:43 +00007091 backslash_halve(s);
7092 (*file)[i] = s;
7093 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007094 *num_file = num_pat;
7095 return OK;
7096}
Bram Moolenaar071d4272004-06-13 20:20:40 +00007097
Bram Moolenaar071d4272004-06-13 20:20:40 +00007098/*
7099 * Return TRUE if the string "p" contains a wildcard that mch_expandpath() can
7100 * expand.
7101 */
7102 int
Bram Moolenaar05540972016-01-30 20:31:25 +01007103mch_has_exp_wildcard(char_u *p)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007104{
Bram Moolenaar91acfff2017-03-12 19:22:36 +01007105 for ( ; *p; MB_PTR_ADV(p))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007106 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00007107 if (*p == '\\' && p[1] != NUL)
7108 ++p;
7109 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00007110 if (vim_strchr((char_u *)
7111#ifdef VMS
7112 "*?%"
7113#else
Bram Moolenaar071d4272004-06-13 20:20:40 +00007114 "*?[{'"
Bram Moolenaar071d4272004-06-13 20:20:40 +00007115#endif
7116 , *p) != NULL)
7117 return TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007118 }
7119 return FALSE;
7120}
7121
7122/*
7123 * Return TRUE if the string "p" contains a wildcard.
7124 * Don't recognize '~' at the end as a wildcard.
7125 */
7126 int
Bram Moolenaar05540972016-01-30 20:31:25 +01007127mch_has_wildcard(char_u *p)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007128{
Bram Moolenaar91acfff2017-03-12 19:22:36 +01007129 for ( ; *p; MB_PTR_ADV(p))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007130 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00007131 if (*p == '\\' && p[1] != NUL)
7132 ++p;
7133 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00007134 if (vim_strchr((char_u *)
7135#ifdef VMS
7136 "*?%$"
7137#else
Bram Moolenaar071d4272004-06-13 20:20:40 +00007138 "*?[{`'$"
Bram Moolenaar071d4272004-06-13 20:20:40 +00007139#endif
7140 , *p) != NULL
7141 || (*p == '~' && p[1] != NUL))
7142 return TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007143 }
7144 return FALSE;
7145}
7146
Bram Moolenaar071d4272004-06-13 20:20:40 +00007147 static int
Bram Moolenaar05540972016-01-30 20:31:25 +01007148have_wildcard(int num, char_u **file)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007149{
7150 int i;
7151
7152 for (i = 0; i < num; i++)
7153 if (mch_has_wildcard(file[i]))
7154 return 1;
7155 return 0;
7156}
7157
7158 static int
Bram Moolenaar05540972016-01-30 20:31:25 +01007159have_dollars(int num, char_u **file)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007160{
7161 int i;
7162
7163 for (i = 0; i < num; i++)
7164 if (vim_strchr(file[i], '$') != NULL)
7165 return TRUE;
7166 return FALSE;
7167}
Bram Moolenaar071d4272004-06-13 20:20:40 +00007168
Bram Moolenaarfdcc9af2016-02-29 12:52:39 +01007169#if !defined(HAVE_RENAME) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007170/*
7171 * Scaled-down version of rename(), which is missing in Xenix.
7172 * This version can only move regular files and will fail if the
7173 * destination exists.
7174 */
7175 int
Bram Moolenaarfdcc9af2016-02-29 12:52:39 +01007176mch_rename(const char *src, const char *dest)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007177{
7178 struct stat st;
7179
Bram Moolenaar0f873732019-12-05 20:28:46 +01007180 if (stat(dest, &st) >= 0) // fail if destination exists
Bram Moolenaar071d4272004-06-13 20:20:40 +00007181 return -1;
Bram Moolenaar0f873732019-12-05 20:28:46 +01007182 if (link(src, dest) != 0) // link file to new name
Bram Moolenaar071d4272004-06-13 20:20:40 +00007183 return -1;
Bram Moolenaar0f873732019-12-05 20:28:46 +01007184 if (mch_remove(src) == 0) // delete link to old name
Bram Moolenaar071d4272004-06-13 20:20:40 +00007185 return 0;
7186 return -1;
7187}
Bram Moolenaar0f873732019-12-05 20:28:46 +01007188#endif // !HAVE_RENAME
Bram Moolenaar071d4272004-06-13 20:20:40 +00007189
Bram Moolenaar4b8366b2019-05-04 17:34:34 +02007190#if defined(FEAT_MOUSE_GPM) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007191/*
7192 * Initializes connection with gpm (if it isn't already opened)
7193 * Return 1 if succeeded (or connection already opened), 0 if failed
7194 */
7195 static int
Bram Moolenaar05540972016-01-30 20:31:25 +01007196gpm_open(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007197{
Bram Moolenaar0f873732019-12-05 20:28:46 +01007198 static Gpm_Connect gpm_connect; // Must it be kept till closing ?
Bram Moolenaar071d4272004-06-13 20:20:40 +00007199
7200 if (!gpm_flag)
7201 {
7202 gpm_connect.eventMask = (GPM_UP | GPM_DRAG | GPM_DOWN);
7203 gpm_connect.defaultMask = ~GPM_HARD;
Bram Moolenaar0f873732019-12-05 20:28:46 +01007204 // Default handling for mouse move
7205 gpm_connect.minMod = 0; // Handle any modifier keys
Bram Moolenaar071d4272004-06-13 20:20:40 +00007206 gpm_connect.maxMod = 0xffff;
7207 if (Gpm_Open(&gpm_connect, 0) > 0)
7208 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01007209 // gpm library tries to handling TSTP causes
7210 // problems. Anyways, we close connection to Gpm whenever
7211 // we are going to suspend or starting an external process
7212 // so we shouldn't have problem with this
Bram Moolenaar76243bd2009-03-02 01:47:02 +00007213# ifdef SIGTSTP
dbivolaruab16ad32021-12-29 19:41:47 +00007214 signal(SIGTSTP, restricted ? SIG_IGN : (RETSIGTYPE (*)())sig_tstp);
Bram Moolenaar76243bd2009-03-02 01:47:02 +00007215# endif
Bram Moolenaar0f873732019-12-05 20:28:46 +01007216 return 1; // succeed
Bram Moolenaar071d4272004-06-13 20:20:40 +00007217 }
7218 if (gpm_fd == -2)
Bram Moolenaar0f873732019-12-05 20:28:46 +01007219 Gpm_Close(); // We don't want to talk to xterm via gpm
Bram Moolenaar071d4272004-06-13 20:20:40 +00007220 return 0;
7221 }
Bram Moolenaar0f873732019-12-05 20:28:46 +01007222 return 1; // already open
Bram Moolenaar071d4272004-06-13 20:20:40 +00007223}
7224
7225/*
Bram Moolenaar4b8366b2019-05-04 17:34:34 +02007226 * Returns TRUE if the GPM mouse is enabled.
7227 */
7228 int
7229gpm_enabled(void)
7230{
7231 return gpm_flag && gpm_fd >= 0;
7232}
7233
7234/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00007235 * Closes connection to gpm
Bram Moolenaar071d4272004-06-13 20:20:40 +00007236 */
7237 static void
Bram Moolenaar05540972016-01-30 20:31:25 +01007238gpm_close(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007239{
Bram Moolenaar4b8366b2019-05-04 17:34:34 +02007240 if (gpm_enabled())
Bram Moolenaar071d4272004-06-13 20:20:40 +00007241 Gpm_Close();
7242}
7243
Bram Moolenaarbedf0912019-05-04 16:58:45 +02007244/*
7245 * Reads gpm event and adds special keys to input buf. Returns length of
Bram Moolenaar071d4272004-06-13 20:20:40 +00007246 * generated key sequence.
Bram Moolenaarc7f02552014-04-01 21:00:59 +02007247 * This function is styled after gui_send_mouse_event().
Bram Moolenaar071d4272004-06-13 20:20:40 +00007248 */
7249 static int
Bram Moolenaar05540972016-01-30 20:31:25 +01007250mch_gpm_process(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007251{
7252 int button;
7253 static Gpm_Event gpm_event;
7254 char_u string[6];
7255 int_u vim_modifiers;
7256 int row,col;
7257 unsigned char buttons_mask;
7258 unsigned char gpm_modifiers;
7259 static unsigned char old_buttons = 0;
7260
7261 Gpm_GetEvent(&gpm_event);
7262
7263#ifdef FEAT_GUI
Bram Moolenaar0f873732019-12-05 20:28:46 +01007264 // Don't put events in the input queue now.
Bram Moolenaar071d4272004-06-13 20:20:40 +00007265 if (hold_gui_events)
7266 return 0;
7267#endif
7268
7269 row = gpm_event.y - 1;
7270 col = gpm_event.x - 1;
7271
Bram Moolenaar0f873732019-12-05 20:28:46 +01007272 string[0] = ESC; // Our termcode
Bram Moolenaar071d4272004-06-13 20:20:40 +00007273 string[1] = 'M';
7274 string[2] = 'G';
7275 switch (GPM_BARE_EVENTS(gpm_event.type))
7276 {
7277 case GPM_DRAG:
7278 string[3] = MOUSE_DRAG;
7279 break;
7280 case GPM_DOWN:
7281 buttons_mask = gpm_event.buttons & ~old_buttons;
7282 old_buttons = gpm_event.buttons;
7283 switch (buttons_mask)
7284 {
7285 case GPM_B_LEFT:
7286 button = MOUSE_LEFT;
7287 break;
7288 case GPM_B_MIDDLE:
7289 button = MOUSE_MIDDLE;
7290 break;
7291 case GPM_B_RIGHT:
7292 button = MOUSE_RIGHT;
7293 break;
7294 default:
7295 return 0;
Bram Moolenaar0f873732019-12-05 20:28:46 +01007296 // Don't know what to do. Can more than one button be
7297 // reported in one event?
Bram Moolenaar071d4272004-06-13 20:20:40 +00007298 }
7299 string[3] = (char_u)(button | 0x20);
7300 SET_NUM_MOUSE_CLICKS(string[3], gpm_event.clicks + 1);
7301 break;
7302 case GPM_UP:
7303 string[3] = MOUSE_RELEASE;
7304 old_buttons &= ~gpm_event.buttons;
7305 break;
7306 default:
7307 return 0;
7308 }
Bram Moolenaar0f873732019-12-05 20:28:46 +01007309 // This code is based on gui_x11_mouse_cb in gui_x11.c
Bram Moolenaar071d4272004-06-13 20:20:40 +00007310 gpm_modifiers = gpm_event.modifiers;
7311 vim_modifiers = 0x0;
Bram Moolenaar0f873732019-12-05 20:28:46 +01007312 // I ignore capslock stats. Aren't we all just hate capslock mixing with
7313 // Vim commands ? Besides, gpm_event.modifiers is unsigned char, and
7314 // K_CAPSSHIFT is defined 8, so it probably isn't even reported
Bram Moolenaar071d4272004-06-13 20:20:40 +00007315 if (gpm_modifiers & ((1 << KG_SHIFT) | (1 << KG_SHIFTR) | (1 << KG_SHIFTL)))
7316 vim_modifiers |= MOUSE_SHIFT;
7317
7318 if (gpm_modifiers & ((1 << KG_CTRL) | (1 << KG_CTRLR) | (1 << KG_CTRLL)))
7319 vim_modifiers |= MOUSE_CTRL;
7320 if (gpm_modifiers & ((1 << KG_ALT) | (1 << KG_ALTGR)))
7321 vim_modifiers |= MOUSE_ALT;
7322 string[3] |= vim_modifiers;
7323 string[4] = (char_u)(col + ' ' + 1);
7324 string[5] = (char_u)(row + ' ' + 1);
7325 add_to_input_buf(string, 6);
7326 return 6;
7327}
Bram Moolenaar0f873732019-12-05 20:28:46 +01007328#endif // FEAT_MOUSE_GPM
Bram Moolenaar071d4272004-06-13 20:20:40 +00007329
Bram Moolenaar3577c6f2008-06-24 21:16:56 +00007330#ifdef FEAT_SYSMOUSE
7331/*
7332 * Initialize connection with sysmouse.
7333 * Let virtual console inform us with SIGUSR2 for pending sysmouse
7334 * output, any sysmouse output than will be processed via sig_sysmouse().
7335 * Return OK if succeeded, FAIL if failed.
7336 */
7337 static int
Bram Moolenaar05540972016-01-30 20:31:25 +01007338sysmouse_open(void)
Bram Moolenaar3577c6f2008-06-24 21:16:56 +00007339{
7340 struct mouse_info mouse;
7341
7342 mouse.operation = MOUSE_MODE;
7343 mouse.u.mode.mode = 0;
7344 mouse.u.mode.signal = SIGUSR2;
7345 if (ioctl(1, CONS_MOUSECTL, &mouse) != -1)
7346 {
7347 signal(SIGUSR2, (RETSIGTYPE (*)())sig_sysmouse);
7348 mouse.operation = MOUSE_SHOW;
7349 ioctl(1, CONS_MOUSECTL, &mouse);
7350 return OK;
7351 }
7352 return FAIL;
7353}
7354
7355/*
7356 * Stop processing SIGUSR2 signals, and also make sure that
7357 * virtual console do not send us any sysmouse related signal.
7358 */
7359 static void
Bram Moolenaar05540972016-01-30 20:31:25 +01007360sysmouse_close(void)
Bram Moolenaar3577c6f2008-06-24 21:16:56 +00007361{
7362 struct mouse_info mouse;
7363
7364 signal(SIGUSR2, restricted ? SIG_IGN : SIG_DFL);
7365 mouse.operation = MOUSE_MODE;
7366 mouse.u.mode.mode = 0;
7367 mouse.u.mode.signal = 0;
7368 ioctl(1, CONS_MOUSECTL, &mouse);
7369}
7370
7371/*
7372 * Gets info from sysmouse and adds special keys to input buf.
7373 */
Bram Moolenaar3577c6f2008-06-24 21:16:56 +00007374 static RETSIGTYPE
7375sig_sysmouse SIGDEFARG(sigarg)
7376{
7377 struct mouse_info mouse;
7378 struct video_info video;
7379 char_u string[6];
7380 int row, col;
7381 int button;
7382 int buttons;
7383 static int oldbuttons = 0;
7384
7385#ifdef FEAT_GUI
Bram Moolenaar0f873732019-12-05 20:28:46 +01007386 // Don't put events in the input queue now.
Bram Moolenaar3577c6f2008-06-24 21:16:56 +00007387 if (hold_gui_events)
7388 return;
7389#endif
7390
7391 mouse.operation = MOUSE_GETINFO;
7392 if (ioctl(1, FBIO_GETMODE, &video.vi_mode) != -1
7393 && ioctl(1, FBIO_MODEINFO, &video) != -1
7394 && ioctl(1, CONS_MOUSECTL, &mouse) != -1
7395 && video.vi_cheight > 0 && video.vi_cwidth > 0)
7396 {
7397 row = mouse.u.data.y / video.vi_cheight;
7398 col = mouse.u.data.x / video.vi_cwidth;
7399 buttons = mouse.u.data.buttons;
Bram Moolenaar0f873732019-12-05 20:28:46 +01007400 string[0] = ESC; // Our termcode
Bram Moolenaar3577c6f2008-06-24 21:16:56 +00007401 string[1] = 'M';
7402 string[2] = 'S';
7403 if (oldbuttons == buttons && buttons != 0)
7404 {
7405 button = MOUSE_DRAG;
7406 }
7407 else
7408 {
7409 switch (buttons)
7410 {
7411 case 0:
7412 button = MOUSE_RELEASE;
7413 break;
7414 case 1:
7415 button = MOUSE_LEFT;
7416 break;
7417 case 2:
7418 button = MOUSE_MIDDLE;
7419 break;
7420 case 4:
7421 button = MOUSE_RIGHT;
7422 break;
7423 default:
7424 return;
7425 }
7426 oldbuttons = buttons;
7427 }
7428 string[3] = (char_u)(button);
7429 string[4] = (char_u)(col + ' ' + 1);
7430 string[5] = (char_u)(row + ' ' + 1);
7431 add_to_input_buf(string, 6);
7432 }
7433 return;
7434}
Bram Moolenaar0f873732019-12-05 20:28:46 +01007435#endif // FEAT_SYSMOUSE
Bram Moolenaar3577c6f2008-06-24 21:16:56 +00007436
Bram Moolenaar071d4272004-06-13 20:20:40 +00007437#if defined(FEAT_LIBCALL) || defined(PROTO)
Bram Moolenaard99df422016-01-29 23:20:40 +01007438typedef char_u * (*STRPROCSTR)(char_u *);
7439typedef char_u * (*INTPROCSTR)(int);
7440typedef int (*STRPROCINT)(char_u *);
7441typedef int (*INTPROCINT)(int);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007442
7443/*
7444 * Call a DLL routine which takes either a string or int param
7445 * and returns an allocated string.
7446 */
7447 int
Bram Moolenaar05540972016-01-30 20:31:25 +01007448mch_libcall(
7449 char_u *libname,
7450 char_u *funcname,
Bram Moolenaar0f873732019-12-05 20:28:46 +01007451 char_u *argstring, // NULL when using a argint
Bram Moolenaar05540972016-01-30 20:31:25 +01007452 int argint,
Bram Moolenaar0f873732019-12-05 20:28:46 +01007453 char_u **string_result, // NULL when using number_result
Bram Moolenaar05540972016-01-30 20:31:25 +01007454 int *number_result)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007455{
7456# if defined(USE_DLOPEN)
7457 void *hinstLib;
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00007458 char *dlerr = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007459# else
7460 shl_t hinstLib;
7461# endif
7462 STRPROCSTR ProcAdd;
7463 INTPROCSTR ProcAddI;
7464 char_u *retval_str = NULL;
7465 int retval_int = 0;
7466 int success = FALSE;
7467
Bram Moolenaarb39ef122006-06-22 16:19:31 +00007468 /*
7469 * Get a handle to the DLL module.
7470 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007471# if defined(USE_DLOPEN)
Bram Moolenaar0f873732019-12-05 20:28:46 +01007472 // First clear any error, it's not cleared by the dlopen() call.
Bram Moolenaarb39ef122006-06-22 16:19:31 +00007473 (void)dlerror();
7474
Bram Moolenaar071d4272004-06-13 20:20:40 +00007475 hinstLib = dlopen((char *)libname, RTLD_LAZY
7476# ifdef RTLD_LOCAL
7477 | RTLD_LOCAL
7478# endif
7479 );
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00007480 if (hinstLib == NULL)
7481 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01007482 // "dlerr" must be used before dlclose()
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00007483 dlerr = (char *)dlerror();
7484 if (dlerr != NULL)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01007485 semsg(_("dlerror = \"%s\""), dlerr);
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00007486 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007487# else
7488 hinstLib = shl_load((const char*)libname, BIND_IMMEDIATE|BIND_VERBOSE, 0L);
7489# endif
7490
Bram Moolenaar0f873732019-12-05 20:28:46 +01007491 // If the handle is valid, try to get the function address.
Bram Moolenaar071d4272004-06-13 20:20:40 +00007492 if (hinstLib != NULL)
7493 {
Bram Moolenaarb2148f52019-01-20 23:43:57 +01007494# ifdef USING_SETJMP
Bram Moolenaar071d4272004-06-13 20:20:40 +00007495 /*
7496 * Catch a crash when calling the library function. For example when
7497 * using a number where a string pointer is expected.
7498 */
7499 mch_startjmp();
7500 if (SETJMP(lc_jump_env) != 0)
7501 {
7502 success = FALSE;
Bram Moolenaard68071d2006-05-02 22:08:30 +00007503# if defined(USE_DLOPEN)
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00007504 dlerr = NULL;
Bram Moolenaard68071d2006-05-02 22:08:30 +00007505# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007506 mch_didjmp();
7507 }
7508 else
7509# endif
7510 {
7511 retval_str = NULL;
7512 retval_int = 0;
7513
7514 if (argstring != NULL)
7515 {
7516# if defined(USE_DLOPEN)
Bram Moolenaar6d721c72017-01-17 16:56:28 +01007517 *(void **)(&ProcAdd) = dlsym(hinstLib, (const char *)funcname);
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00007518 dlerr = (char *)dlerror();
Bram Moolenaar071d4272004-06-13 20:20:40 +00007519# else
7520 if (shl_findsym(&hinstLib, (const char *)funcname,
7521 TYPE_PROCEDURE, (void *)&ProcAdd) < 0)
7522 ProcAdd = NULL;
7523# endif
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00007524 if ((success = (ProcAdd != NULL
7525# if defined(USE_DLOPEN)
7526 && dlerr == NULL
7527# endif
7528 )))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007529 {
7530 if (string_result == NULL)
Bram Moolenaara4224862020-09-13 22:00:12 +02007531 retval_int = ((STRPROCINT)(void *)ProcAdd)(argstring);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007532 else
7533 retval_str = (ProcAdd)(argstring);
7534 }
7535 }
7536 else
7537 {
7538# if defined(USE_DLOPEN)
Bram Moolenaar6d721c72017-01-17 16:56:28 +01007539 *(void **)(&ProcAddI) = dlsym(hinstLib, (const char *)funcname);
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00007540 dlerr = (char *)dlerror();
Bram Moolenaar071d4272004-06-13 20:20:40 +00007541# else
7542 if (shl_findsym(&hinstLib, (const char *)funcname,
7543 TYPE_PROCEDURE, (void *)&ProcAddI) < 0)
7544 ProcAddI = NULL;
7545# endif
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00007546 if ((success = (ProcAddI != NULL
7547# if defined(USE_DLOPEN)
7548 && dlerr == NULL
7549# endif
7550 )))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007551 {
7552 if (string_result == NULL)
Bram Moolenaara4224862020-09-13 22:00:12 +02007553 retval_int = ((INTPROCINT)(void *)ProcAddI)(argint);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007554 else
7555 retval_str = (ProcAddI)(argint);
7556 }
7557 }
7558
Bram Moolenaar0f873732019-12-05 20:28:46 +01007559 // Save the string before we free the library.
7560 // Assume that a "1" or "-1" result is an illegal pointer.
Bram Moolenaar071d4272004-06-13 20:20:40 +00007561 if (string_result == NULL)
7562 *number_result = retval_int;
7563 else if (retval_str != NULL
7564 && retval_str != (char_u *)1
7565 && retval_str != (char_u *)-1)
7566 *string_result = vim_strsave(retval_str);
7567 }
7568
Bram Moolenaarb2148f52019-01-20 23:43:57 +01007569# ifdef USING_SETJMP
Bram Moolenaar071d4272004-06-13 20:20:40 +00007570 mch_endjmp();
7571# ifdef SIGHASARG
7572 if (lc_signal != 0)
7573 {
7574 int i;
7575
Bram Moolenaar0f873732019-12-05 20:28:46 +01007576 // try to find the name of this signal
Bram Moolenaar071d4272004-06-13 20:20:40 +00007577 for (i = 0; signal_info[i].sig != -1; i++)
7578 if (lc_signal == signal_info[i].sig)
7579 break;
Bram Moolenaarac78dd42022-01-02 19:25:26 +00007580 semsg(e_got_sig_str_in_libcall, signal_info[i].name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007581 }
7582# endif
7583# endif
7584
Bram Moolenaar071d4272004-06-13 20:20:40 +00007585# if defined(USE_DLOPEN)
Bram Moolenaar0f873732019-12-05 20:28:46 +01007586 // "dlerr" must be used before dlclose()
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00007587 if (dlerr != NULL)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01007588 semsg(_("dlerror = \"%s\""), dlerr);
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00007589
Bram Moolenaar0f873732019-12-05 20:28:46 +01007590 // Free the DLL module.
Bram Moolenaar071d4272004-06-13 20:20:40 +00007591 (void)dlclose(hinstLib);
7592# else
7593 (void)shl_unload(hinstLib);
7594# endif
7595 }
7596
7597 if (!success)
7598 {
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00007599 semsg(_(e_library_call_failed_for_str), funcname);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007600 return FAIL;
7601 }
7602
7603 return OK;
7604}
7605#endif
7606
7607#if (defined(FEAT_X11) && defined(FEAT_XCLIPBOARD)) || defined(PROTO)
Bram Moolenaar0f873732019-12-05 20:28:46 +01007608static int xterm_trace = -1; // default: disabled
Bram Moolenaar071d4272004-06-13 20:20:40 +00007609static int xterm_button;
7610
7611/*
7612 * Setup a dummy window for X selections in a terminal.
7613 */
7614 void
Bram Moolenaar05540972016-01-30 20:31:25 +01007615setup_term_clip(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007616{
7617 int z = 0;
7618 char *strp = "";
7619 Widget AppShell;
7620
7621 if (!x_connect_to_server())
7622 return;
7623
7624 open_app_context();
7625 if (app_context != NULL && xterm_Shell == (Widget)0)
7626 {
7627 int (*oldhandler)();
Bram Moolenaarb2148f52019-01-20 23:43:57 +01007628# if defined(USING_SETJMP)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007629 int (*oldIOhandler)();
Bram Moolenaaredce7422019-01-20 18:39:30 +01007630# endif
Bram Moolenaar833eb1d2016-11-24 17:22:50 +01007631# ifdef ELAPSED_FUNC
Bram Moolenaar1ac56c22019-01-17 22:28:22 +01007632 elapsed_T start_tv;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007633
7634 if (p_verbose > 0)
Bram Moolenaar833eb1d2016-11-24 17:22:50 +01007635 ELAPSED_INIT(start_tv);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007636# endif
7637
Bram Moolenaar0f873732019-12-05 20:28:46 +01007638 // Ignore X errors while opening the display
Bram Moolenaar071d4272004-06-13 20:20:40 +00007639 oldhandler = XSetErrorHandler(x_error_check);
7640
Bram Moolenaarb2148f52019-01-20 23:43:57 +01007641# if defined(USING_SETJMP)
Bram Moolenaar0f873732019-12-05 20:28:46 +01007642 // Ignore X IO errors while opening the display
Bram Moolenaar071d4272004-06-13 20:20:40 +00007643 oldIOhandler = XSetIOErrorHandler(x_IOerror_check);
7644 mch_startjmp();
7645 if (SETJMP(lc_jump_env) != 0)
7646 {
7647 mch_didjmp();
7648 xterm_dpy = NULL;
7649 }
7650 else
Bram Moolenaaredce7422019-01-20 18:39:30 +01007651# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007652 {
7653 xterm_dpy = XtOpenDisplay(app_context, xterm_display,
7654 "vim_xterm", "Vim_xterm", NULL, 0, &z, &strp);
Bram Moolenaard155d7a2018-12-21 16:04:21 +01007655 if (xterm_dpy != NULL)
7656 xterm_dpy_retry_count = 0;
Bram Moolenaarb2148f52019-01-20 23:43:57 +01007657# if defined(USING_SETJMP)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007658 mch_endjmp();
Bram Moolenaaredce7422019-01-20 18:39:30 +01007659# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007660 }
7661
Bram Moolenaarb2148f52019-01-20 23:43:57 +01007662# if defined(USING_SETJMP)
Bram Moolenaar0f873732019-12-05 20:28:46 +01007663 // Now handle X IO errors normally.
Bram Moolenaar071d4272004-06-13 20:20:40 +00007664 (void)XSetIOErrorHandler(oldIOhandler);
Bram Moolenaaredce7422019-01-20 18:39:30 +01007665# endif
Bram Moolenaar0f873732019-12-05 20:28:46 +01007666 // Now handle X errors normally.
Bram Moolenaar071d4272004-06-13 20:20:40 +00007667 (void)XSetErrorHandler(oldhandler);
7668
7669 if (xterm_dpy == NULL)
7670 {
7671 if (p_verbose > 0)
Bram Moolenaar32526b32019-01-19 17:43:09 +01007672 verb_msg(_("Opening the X display failed"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00007673 return;
7674 }
7675
Bram Moolenaar0f873732019-12-05 20:28:46 +01007676 // Catch terminating error of the X server connection.
Bram Moolenaar071d4272004-06-13 20:20:40 +00007677 (void)XSetIOErrorHandler(x_IOerror_handler);
7678
Bram Moolenaar833eb1d2016-11-24 17:22:50 +01007679# ifdef ELAPSED_FUNC
Bram Moolenaar071d4272004-06-13 20:20:40 +00007680 if (p_verbose > 0)
Bram Moolenaara04f10b2005-05-31 22:09:46 +00007681 {
7682 verbose_enter();
Bram Moolenaar833eb1d2016-11-24 17:22:50 +01007683 xopen_message(ELAPSED_FUNC(start_tv));
Bram Moolenaara04f10b2005-05-31 22:09:46 +00007684 verbose_leave();
7685 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007686# endif
7687
Bram Moolenaar0f873732019-12-05 20:28:46 +01007688 // Create a Shell to make converters work.
Bram Moolenaar071d4272004-06-13 20:20:40 +00007689 AppShell = XtVaAppCreateShell("vim_xterm", "Vim_xterm",
7690 applicationShellWidgetClass, xterm_dpy,
7691 NULL);
7692 if (AppShell == (Widget)0)
7693 return;
7694 xterm_Shell = XtVaCreatePopupShell("VIM",
7695 topLevelShellWidgetClass, AppShell,
7696 XtNmappedWhenManaged, 0,
7697 XtNwidth, 1,
7698 XtNheight, 1,
7699 NULL);
7700 if (xterm_Shell == (Widget)0)
7701 return;
7702
7703 x11_setup_atoms(xterm_dpy);
Bram Moolenaar7cfea752010-06-22 06:07:12 +02007704 x11_setup_selection(xterm_Shell);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007705 if (x11_display == NULL)
7706 x11_display = xterm_dpy;
7707
7708 XtRealizeWidget(xterm_Shell);
7709 XSync(xterm_dpy, False);
7710 xterm_update();
7711 }
7712 if (xterm_Shell != (Widget)0)
7713 {
7714 clip_init(TRUE);
7715 if (x11_window == 0 && (strp = getenv("WINDOWID")) != NULL)
7716 x11_window = (Window)atol(strp);
Bram Moolenaar0f873732019-12-05 20:28:46 +01007717 // Check if $WINDOWID is valid.
Bram Moolenaar071d4272004-06-13 20:20:40 +00007718 if (test_x11_window(xterm_dpy) == FAIL)
7719 x11_window = 0;
7720 if (x11_window != 0)
7721 xterm_trace = 0;
7722 }
7723}
7724
7725 void
Bram Moolenaar05540972016-01-30 20:31:25 +01007726start_xterm_trace(int button)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007727{
7728 if (x11_window == 0 || xterm_trace < 0 || xterm_Shell == (Widget)0)
7729 return;
7730 xterm_trace = 1;
7731 xterm_button = button;
7732 do_xterm_trace();
7733}
7734
7735
7736 void
Bram Moolenaar05540972016-01-30 20:31:25 +01007737stop_xterm_trace(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007738{
7739 if (xterm_trace < 0)
7740 return;
7741 xterm_trace = 0;
7742}
7743
7744/*
7745 * Query the xterm pointer and generate mouse termcodes if necessary
7746 * return TRUE if dragging is active, else FALSE
7747 */
7748 static int
Bram Moolenaar05540972016-01-30 20:31:25 +01007749do_xterm_trace(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007750{
7751 Window root, child;
7752 int root_x, root_y;
7753 int win_x, win_y;
7754 int row, col;
7755 int_u mask_return;
7756 char_u buf[50];
7757 char_u *strp;
7758 long got_hints;
7759 static char_u *mouse_code;
7760 static char_u mouse_name[2] = {KS_MOUSE, KE_FILLER};
7761 static int prev_row = 0, prev_col = 0;
7762 static XSizeHints xterm_hints;
7763
7764 if (xterm_trace <= 0)
7765 return FALSE;
7766
7767 if (xterm_trace == 1)
7768 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01007769 // Get the hints just before tracking starts. The font size might
7770 // have changed recently.
Bram Moolenaara6c2c912008-01-13 15:31:00 +00007771 if (!XGetWMNormalHints(xterm_dpy, x11_window, &xterm_hints, &got_hints)
7772 || !(got_hints & PResizeInc)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007773 || xterm_hints.width_inc <= 1
7774 || xterm_hints.height_inc <= 1)
7775 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01007776 xterm_trace = -1; // Not enough data -- disable tracing
Bram Moolenaar071d4272004-06-13 20:20:40 +00007777 return FALSE;
7778 }
7779
Bram Moolenaar0f873732019-12-05 20:28:46 +01007780 // Rely on the same mouse code for the duration of this
Bram Moolenaar071d4272004-06-13 20:20:40 +00007781 mouse_code = find_termcode(mouse_name);
7782 prev_row = mouse_row;
Bram Moolenaarcde88542015-08-11 19:14:00 +02007783 prev_col = mouse_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007784 xterm_trace = 2;
7785
Bram Moolenaar0f873732019-12-05 20:28:46 +01007786 // Find the offset of the chars, there might be a scrollbar on the
7787 // left of the window and/or a menu on the top (eterm etc.)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007788 XQueryPointer(xterm_dpy, x11_window, &root, &child, &root_x, &root_y,
7789 &win_x, &win_y, &mask_return);
7790 xterm_hints.y = win_y - (xterm_hints.height_inc * mouse_row)
7791 - (xterm_hints.height_inc / 2);
7792 if (xterm_hints.y <= xterm_hints.height_inc / 2)
7793 xterm_hints.y = 2;
7794 xterm_hints.x = win_x - (xterm_hints.width_inc * mouse_col)
7795 - (xterm_hints.width_inc / 2);
7796 if (xterm_hints.x <= xterm_hints.width_inc / 2)
7797 xterm_hints.x = 2;
7798 return TRUE;
7799 }
Bram Moolenaaref9d6aa2011-04-11 16:56:35 +02007800 if (mouse_code == NULL || STRLEN(mouse_code) > 45)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007801 {
7802 xterm_trace = 0;
7803 return FALSE;
7804 }
7805
7806 XQueryPointer(xterm_dpy, x11_window, &root, &child, &root_x, &root_y,
7807 &win_x, &win_y, &mask_return);
7808
7809 row = check_row((win_y - xterm_hints.y) / xterm_hints.height_inc);
7810 col = check_col((win_x - xterm_hints.x) / xterm_hints.width_inc);
7811 if (row == prev_row && col == prev_col)
7812 return TRUE;
7813
7814 STRCPY(buf, mouse_code);
7815 strp = buf + STRLEN(buf);
7816 *strp++ = (xterm_button | MOUSE_DRAG) & ~0x20;
7817 *strp++ = (char_u)(col + ' ' + 1);
7818 *strp++ = (char_u)(row + ' ' + 1);
7819 *strp = 0;
7820 add_to_input_buf(buf, STRLEN(buf));
7821
7822 prev_row = row;
7823 prev_col = col;
7824 return TRUE;
7825}
7826
Bram Moolenaard4aa83a2019-05-09 18:59:31 +02007827# if defined(FEAT_GUI) || defined(FEAT_XCLIPBOARD) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007828/*
7829 * Destroy the display, window and app_context. Required for GTK.
7830 */
7831 void
Bram Moolenaar05540972016-01-30 20:31:25 +01007832clear_xterm_clip(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007833{
7834 if (xterm_Shell != (Widget)0)
7835 {
7836 XtDestroyWidget(xterm_Shell);
7837 xterm_Shell = (Widget)0;
7838 }
7839 if (xterm_dpy != NULL)
7840 {
Bram Moolenaare8208012008-06-20 09:59:25 +00007841# if 0
Bram Moolenaar0f873732019-12-05 20:28:46 +01007842 // Lesstif and Solaris crash here, lose some memory
Bram Moolenaar071d4272004-06-13 20:20:40 +00007843 XtCloseDisplay(xterm_dpy);
Bram Moolenaare8208012008-06-20 09:59:25 +00007844# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007845 if (x11_display == xterm_dpy)
7846 x11_display = NULL;
7847 xterm_dpy = NULL;
7848 }
Bram Moolenaare8208012008-06-20 09:59:25 +00007849# if 0
Bram Moolenaar071d4272004-06-13 20:20:40 +00007850 if (app_context != (XtAppContext)NULL)
7851 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01007852 // Lesstif and Solaris crash here, lose some memory
Bram Moolenaar071d4272004-06-13 20:20:40 +00007853 XtDestroyApplicationContext(app_context);
7854 app_context = (XtAppContext)NULL;
7855 }
Bram Moolenaare8208012008-06-20 09:59:25 +00007856# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007857}
7858# endif
7859
7860/*
Bram Moolenaar090cfc12013-03-19 12:35:42 +01007861 * Catch up with GUI or X events.
7862 */
7863 static void
Bram Moolenaar05540972016-01-30 20:31:25 +01007864clip_update(void)
Bram Moolenaar090cfc12013-03-19 12:35:42 +01007865{
7866# ifdef FEAT_GUI
7867 if (gui.in_use)
7868 gui_mch_update();
7869 else
7870# endif
7871 if (xterm_Shell != (Widget)0)
7872 xterm_update();
7873}
7874
7875/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00007876 * Catch up with any queued X events. This may put keyboard input into the
7877 * input buffer, call resize call-backs, trigger timers etc. If there is
7878 * nothing in the X event queue (& no timers pending), then we return
7879 * immediately.
7880 */
7881 static void
Bram Moolenaar05540972016-01-30 20:31:25 +01007882xterm_update(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007883{
7884 XEvent event;
7885
Bram Moolenaarb1fc2bf2015-03-20 16:26:54 +01007886 for (;;)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007887 {
Bram Moolenaar93c88e02015-09-15 14:12:05 +02007888 XtInputMask mask = XtAppPending(app_context);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007889
Bram Moolenaar93c88e02015-09-15 14:12:05 +02007890 if (mask == 0 || vim_is_input_buf_full())
Bram Moolenaarb1fc2bf2015-03-20 16:26:54 +01007891 break;
7892
Bram Moolenaar93c88e02015-09-15 14:12:05 +02007893 if (mask & XtIMXEvent)
Bram Moolenaarb1fc2bf2015-03-20 16:26:54 +01007894 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01007895 // There is an event to process.
Bram Moolenaar93c88e02015-09-15 14:12:05 +02007896 XtAppNextEvent(app_context, &event);
Bram Moolenaarb1fc2bf2015-03-20 16:26:54 +01007897#ifdef FEAT_CLIENTSERVER
7898 {
7899 XPropertyEvent *e = (XPropertyEvent *)&event;
7900
7901 if (e->type == PropertyNotify && e->window == commWindow
Bram Moolenaar071d4272004-06-13 20:20:40 +00007902 && e->atom == commProperty && e->state == PropertyNewValue)
Bram Moolenaar93c88e02015-09-15 14:12:05 +02007903 serverEventProc(xterm_dpy, &event, 0);
Bram Moolenaarb1fc2bf2015-03-20 16:26:54 +01007904 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007905#endif
Bram Moolenaar93c88e02015-09-15 14:12:05 +02007906 XtDispatchEvent(&event);
7907 }
Bram Moolenaarb1fc2bf2015-03-20 16:26:54 +01007908 else
7909 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01007910 // There is something else than an event to process.
Bram Moolenaar93c88e02015-09-15 14:12:05 +02007911 XtAppProcessEvent(app_context, mask);
7912 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007913 }
7914}
7915
7916 int
Bram Moolenaar0554fa42019-06-14 21:36:54 +02007917clip_xterm_own_selection(Clipboard_T *cbd)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007918{
7919 if (xterm_Shell != (Widget)0)
7920 return clip_x11_own_selection(xterm_Shell, cbd);
7921 return FAIL;
7922}
7923
7924 void
Bram Moolenaar0554fa42019-06-14 21:36:54 +02007925clip_xterm_lose_selection(Clipboard_T *cbd)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007926{
7927 if (xterm_Shell != (Widget)0)
7928 clip_x11_lose_selection(xterm_Shell, cbd);
7929}
7930
7931 void
Bram Moolenaar0554fa42019-06-14 21:36:54 +02007932clip_xterm_request_selection(Clipboard_T *cbd)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007933{
7934 if (xterm_Shell != (Widget)0)
7935 clip_x11_request_selection(xterm_Shell, xterm_dpy, cbd);
7936}
7937
7938 void
Bram Moolenaar0554fa42019-06-14 21:36:54 +02007939clip_xterm_set_selection(Clipboard_T *cbd)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007940{
7941 clip_x11_set_selection(cbd);
7942}
7943#endif
7944
7945
7946#if defined(USE_XSMP) || defined(PROTO)
7947/*
7948 * Code for X Session Management Protocol.
7949 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007950
7951# if defined(FEAT_GUI) && defined(USE_XSMP_INTERACT)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007952/*
7953 * This is our chance to ask the user if they want to save,
7954 * or abort the logout
7955 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007956 static void
Bram Moolenaar05540972016-01-30 20:31:25 +01007957xsmp_handle_interaction(SmcConn smc_conn, SmPointer client_data UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007958{
Bram Moolenaare1004402020-10-24 20:49:43 +02007959 int save_cmod_flags;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007960 int cancel_shutdown = False;
7961
Bram Moolenaare1004402020-10-24 20:49:43 +02007962 save_cmod_flags = cmdmod.cmod_flags;
7963 cmdmod.cmod_flags |= CMOD_CONFIRM;
Bram Moolenaar027387f2016-01-02 22:25:52 +01007964 if (check_changed_any(FALSE, FALSE))
Bram Moolenaar0f873732019-12-05 20:28:46 +01007965 // Mustn't logout
Bram Moolenaar071d4272004-06-13 20:20:40 +00007966 cancel_shutdown = True;
Bram Moolenaare1004402020-10-24 20:49:43 +02007967 cmdmod.cmod_flags = save_cmod_flags;
Bram Moolenaar0f873732019-12-05 20:28:46 +01007968 setcursor(); // position cursor
Bram Moolenaar071d4272004-06-13 20:20:40 +00007969 out_flush();
7970
Bram Moolenaar0f873732019-12-05 20:28:46 +01007971 // Done interaction
Bram Moolenaar071d4272004-06-13 20:20:40 +00007972 SmcInteractDone(smc_conn, cancel_shutdown);
7973
Bram Moolenaar0f873732019-12-05 20:28:46 +01007974 // Finish off
7975 // Only end save-yourself here if we're not cancelling shutdown;
7976 // we'll get a cancelled callback later in which we'll end it.
7977 // Hopefully get around glitchy SMs (like GNOME-1)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007978 if (!cancel_shutdown)
7979 {
7980 xsmp.save_yourself = False;
7981 SmcSaveYourselfDone(smc_conn, True);
7982 }
7983}
7984# endif
7985
7986/*
7987 * Callback that starts save-yourself.
7988 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007989 static void
Bram Moolenaar05540972016-01-30 20:31:25 +01007990xsmp_handle_save_yourself(
7991 SmcConn smc_conn,
7992 SmPointer client_data UNUSED,
7993 int save_type UNUSED,
7994 Bool shutdown,
7995 int interact_style UNUSED,
7996 Bool fast UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007997{
Bram Moolenaar0f873732019-12-05 20:28:46 +01007998 // Handle already being in saveyourself
Bram Moolenaar071d4272004-06-13 20:20:40 +00007999 if (xsmp.save_yourself)
8000 SmcSaveYourselfDone(smc_conn, True);
8001 xsmp.save_yourself = True;
8002 xsmp.shutdown = shutdown;
8003
Bram Moolenaar0f873732019-12-05 20:28:46 +01008004 // First up, preserve all files
Bram Moolenaar071d4272004-06-13 20:20:40 +00008005 out_flush();
Bram Moolenaar0f873732019-12-05 20:28:46 +01008006 ml_sync_all(FALSE, FALSE); // preserve all swap files
Bram Moolenaar071d4272004-06-13 20:20:40 +00008007
8008 if (p_verbose > 0)
Bram Moolenaar32526b32019-01-19 17:43:09 +01008009 verb_msg(_("XSMP handling save-yourself request"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00008010
8011# if defined(FEAT_GUI) && defined(USE_XSMP_INTERACT)
Bram Moolenaar0f873732019-12-05 20:28:46 +01008012 // Now see if we can ask about unsaved files
Bram Moolenaar071d4272004-06-13 20:20:40 +00008013 if (shutdown && !fast && gui.in_use)
Bram Moolenaar0f873732019-12-05 20:28:46 +01008014 // Need to interact with user, but need SM's permission
Bram Moolenaar071d4272004-06-13 20:20:40 +00008015 SmcInteractRequest(smc_conn, SmDialogError,
8016 xsmp_handle_interaction, client_data);
8017 else
8018# endif
8019 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01008020 // Can stop the cycle here
Bram Moolenaar071d4272004-06-13 20:20:40 +00008021 SmcSaveYourselfDone(smc_conn, True);
8022 xsmp.save_yourself = False;
8023 }
8024}
8025
8026
8027/*
8028 * Callback to warn us of imminent death.
8029 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008030 static void
Bram Moolenaar05540972016-01-30 20:31:25 +01008031xsmp_die(SmcConn smc_conn UNUSED, SmPointer client_data UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008032{
8033 xsmp_close();
8034
Bram Moolenaar0f873732019-12-05 20:28:46 +01008035 // quit quickly leaving swapfiles for modified buffers behind
Bram Moolenaar071d4272004-06-13 20:20:40 +00008036 getout_preserve_modified(0);
8037}
8038
8039
8040/*
8041 * Callback to tell us that save-yourself has completed.
8042 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008043 static void
Bram Moolenaar05540972016-01-30 20:31:25 +01008044xsmp_save_complete(
8045 SmcConn smc_conn UNUSED,
8046 SmPointer client_data UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008047{
8048 xsmp.save_yourself = False;
8049}
8050
8051
8052/*
8053 * Callback to tell us that an instigated shutdown was cancelled
8054 * (maybe even by us)
8055 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008056 static void
Bram Moolenaar05540972016-01-30 20:31:25 +01008057xsmp_shutdown_cancelled(
8058 SmcConn smc_conn,
8059 SmPointer client_data UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008060{
8061 if (xsmp.save_yourself)
8062 SmcSaveYourselfDone(smc_conn, True);
8063 xsmp.save_yourself = False;
8064 xsmp.shutdown = False;
8065}
8066
8067
8068/*
8069 * Callback to tell us that a new ICE connection has been established.
8070 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008071 static void
Bram Moolenaar05540972016-01-30 20:31:25 +01008072xsmp_ice_connection(
8073 IceConn iceConn,
8074 IcePointer clientData UNUSED,
8075 Bool opening,
8076 IcePointer *watchData UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008077{
Bram Moolenaar0f873732019-12-05 20:28:46 +01008078 // Intercept creation of ICE connection fd
Bram Moolenaar071d4272004-06-13 20:20:40 +00008079 if (opening)
8080 {
8081 xsmp_icefd = IceConnectionNumber(iceConn);
8082 IceRemoveConnectionWatch(xsmp_ice_connection, NULL);
8083 }
8084}
8085
8086
Bram Moolenaar0f873732019-12-05 20:28:46 +01008087// Handle any ICE processing that's required; return FAIL if SM lost
Bram Moolenaar071d4272004-06-13 20:20:40 +00008088 int
Bram Moolenaar05540972016-01-30 20:31:25 +01008089xsmp_handle_requests(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008090{
8091 Bool rep;
8092
8093 if (IceProcessMessages(xsmp.iceconn, NULL, &rep)
8094 == IceProcessMessagesIOError)
8095 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01008096 // Lost ICE
Bram Moolenaar071d4272004-06-13 20:20:40 +00008097 if (p_verbose > 0)
Bram Moolenaar32526b32019-01-19 17:43:09 +01008098 verb_msg(_("XSMP lost ICE connection"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00008099 xsmp_close();
8100 return FAIL;
8101 }
8102 else
8103 return OK;
8104}
8105
8106static int dummy;
8107
Bram Moolenaar0f873732019-12-05 20:28:46 +01008108// Set up X Session Management Protocol
Bram Moolenaar071d4272004-06-13 20:20:40 +00008109 void
8110xsmp_init(void)
8111{
8112 char errorstring[80];
Bram Moolenaar071d4272004-06-13 20:20:40 +00008113 SmcCallbacks smcallbacks;
8114#if 0
8115 SmPropValue smname;
8116 SmProp smnameprop;
8117 SmProp *smprops[1];
8118#endif
8119
8120 if (p_verbose > 0)
Bram Moolenaar32526b32019-01-19 17:43:09 +01008121 verb_msg(_("XSMP opening connection"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00008122
8123 xsmp.save_yourself = xsmp.shutdown = False;
8124
Bram Moolenaar0f873732019-12-05 20:28:46 +01008125 // Set up SM callbacks - must have all, even if they're not used
Bram Moolenaar071d4272004-06-13 20:20:40 +00008126 smcallbacks.save_yourself.callback = xsmp_handle_save_yourself;
8127 smcallbacks.save_yourself.client_data = NULL;
8128 smcallbacks.die.callback = xsmp_die;
8129 smcallbacks.die.client_data = NULL;
8130 smcallbacks.save_complete.callback = xsmp_save_complete;
8131 smcallbacks.save_complete.client_data = NULL;
8132 smcallbacks.shutdown_cancelled.callback = xsmp_shutdown_cancelled;
8133 smcallbacks.shutdown_cancelled.client_data = NULL;
8134
Bram Moolenaar0f873732019-12-05 20:28:46 +01008135 // Set up a watch on ICE connection creations. The "dummy" argument is
8136 // apparently required for FreeBSD (we get a BUS error when using NULL).
Bram Moolenaar071d4272004-06-13 20:20:40 +00008137 if (IceAddConnectionWatch(xsmp_ice_connection, &dummy) == 0)
8138 {
8139 if (p_verbose > 0)
Bram Moolenaar32526b32019-01-19 17:43:09 +01008140 verb_msg(_("XSMP ICE connection watch failed"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00008141 return;
8142 }
8143
Bram Moolenaar0f873732019-12-05 20:28:46 +01008144 // Create an SM connection
Bram Moolenaar071d4272004-06-13 20:20:40 +00008145 xsmp.smcconn = SmcOpenConnection(
8146 NULL,
8147 NULL,
8148 SmProtoMajor,
8149 SmProtoMinor,
8150 SmcSaveYourselfProcMask | SmcDieProcMask
8151 | SmcSaveCompleteProcMask | SmcShutdownCancelledProcMask,
8152 &smcallbacks,
8153 NULL,
Bram Moolenaare8208012008-06-20 09:59:25 +00008154 &xsmp.clientid,
Bram Moolenaar4841a7c2018-09-22 14:08:49 +02008155 sizeof(errorstring) - 1,
Bram Moolenaar071d4272004-06-13 20:20:40 +00008156 errorstring);
8157 if (xsmp.smcconn == NULL)
8158 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00008159 if (p_verbose > 0)
Bram Moolenaara04f10b2005-05-31 22:09:46 +00008160 {
Bram Moolenaare1be1182020-10-24 13:30:51 +02008161 char errorreport[132];
8162
8163 // If the message is too long it might not be NUL terminated. Add
8164 // a NUL at the end to make sure we don't go over the end.
8165 errorstring[sizeof(errorstring) - 1] = NUL;
Bram Moolenaara04f10b2005-05-31 22:09:46 +00008166 vim_snprintf(errorreport, sizeof(errorreport),
8167 _("XSMP SmcOpenConnection failed: %s"), errorstring);
Bram Moolenaar32526b32019-01-19 17:43:09 +01008168 verb_msg(errorreport);
Bram Moolenaara04f10b2005-05-31 22:09:46 +00008169 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00008170 return;
8171 }
8172 xsmp.iceconn = SmcGetIceConnection(xsmp.smcconn);
8173
8174#if 0
Bram Moolenaar0f873732019-12-05 20:28:46 +01008175 // ID ourselves
Bram Moolenaar071d4272004-06-13 20:20:40 +00008176 smname.value = "vim";
8177 smname.length = 3;
8178 smnameprop.name = "SmProgram";
8179 smnameprop.type = "SmARRAY8";
8180 smnameprop.num_vals = 1;
8181 smnameprop.vals = &smname;
8182
8183 smprops[0] = &smnameprop;
8184 SmcSetProperties(xsmp.smcconn, 1, smprops);
8185#endif
8186}
8187
8188
Bram Moolenaar0f873732019-12-05 20:28:46 +01008189// Shut down XSMP comms.
Bram Moolenaar071d4272004-06-13 20:20:40 +00008190 void
Bram Moolenaar05540972016-01-30 20:31:25 +01008191xsmp_close(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008192{
8193 if (xsmp_icefd != -1)
8194 {
8195 SmcCloseConnection(xsmp.smcconn, 0, NULL);
Bram Moolenaar5a221812008-11-12 12:08:45 +00008196 if (xsmp.clientid != NULL)
8197 free(xsmp.clientid);
Bram Moolenaare8208012008-06-20 09:59:25 +00008198 xsmp.clientid = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008199 xsmp_icefd = -1;
8200 }
8201}
Bram Moolenaar0f873732019-12-05 20:28:46 +01008202#endif // USE_XSMP
Bram Moolenaar071d4272004-06-13 20:20:40 +00008203
8204
8205#ifdef EBCDIC
Bram Moolenaar0f873732019-12-05 20:28:46 +01008206// Translate character to its CTRL- value
Bram Moolenaar071d4272004-06-13 20:20:40 +00008207char CtrlTable[] =
8208{
8209/* 00 - 5E */
8210 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
8211 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
8212 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
8213 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
8214 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
8215 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
8216/* ^ */ 0x1E,
8217/* - */ 0x1F,
8218/* 61 - 6C */
8219 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
8220/* _ */ 0x1F,
8221/* 6E - 80 */
8222 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
8223/* a */ 0x01,
8224/* b */ 0x02,
8225/* c */ 0x03,
8226/* d */ 0x37,
8227/* e */ 0x2D,
8228/* f */ 0x2E,
8229/* g */ 0x2F,
8230/* h */ 0x16,
8231/* i */ 0x05,
8232/* 8A - 90 */
8233 0, 0, 0, 0, 0, 0, 0,
8234/* j */ 0x15,
8235/* k */ 0x0B,
8236/* l */ 0x0C,
8237/* m */ 0x0D,
8238/* n */ 0x0E,
8239/* o */ 0x0F,
8240/* p */ 0x10,
8241/* q */ 0x11,
8242/* r */ 0x12,
8243/* 9A - A1 */
8244 0, 0, 0, 0, 0, 0, 0, 0,
8245/* s */ 0x13,
8246/* t */ 0x3C,
8247/* u */ 0x3D,
8248/* v */ 0x32,
8249/* w */ 0x26,
8250/* x */ 0x18,
8251/* y */ 0x19,
8252/* z */ 0x3F,
8253/* AA - AC */
8254 0, 0, 0,
8255/* [ */ 0x27,
8256/* AE - BC */
8257 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
8258/* ] */ 0x1D,
8259/* BE - C0 */ 0, 0, 0,
8260/* A */ 0x01,
8261/* B */ 0x02,
8262/* C */ 0x03,
8263/* D */ 0x37,
8264/* E */ 0x2D,
8265/* F */ 0x2E,
8266/* G */ 0x2F,
8267/* H */ 0x16,
8268/* I */ 0x05,
8269/* CA - D0 */ 0, 0, 0, 0, 0, 0, 0,
8270/* J */ 0x15,
8271/* K */ 0x0B,
8272/* L */ 0x0C,
8273/* M */ 0x0D,
8274/* N */ 0x0E,
8275/* O */ 0x0F,
8276/* P */ 0x10,
8277/* Q */ 0x11,
8278/* R */ 0x12,
8279/* DA - DF */ 0, 0, 0, 0, 0, 0,
8280/* \ */ 0x1C,
8281/* E1 */ 0,
8282/* S */ 0x13,
8283/* T */ 0x3C,
8284/* U */ 0x3D,
8285/* V */ 0x32,
8286/* W */ 0x26,
8287/* X */ 0x18,
8288/* Y */ 0x19,
8289/* Z */ 0x3F,
8290/* EA - FF*/ 0, 0, 0, 0, 0, 0,
8291 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
8292};
8293
8294char MetaCharTable[]=
Bram Moolenaar0f873732019-12-05 20:28:46 +01008295{// 0 1 2 3 4 5 6 7 8 9 A B C D E F
Bram Moolenaar071d4272004-06-13 20:20:40 +00008296 0, 0, 0, 0,'\\', 0,'F', 0,'W','M','N', 0, 0, 0, 0, 0,
8297 0, 0, 0, 0,']', 0, 0,'G', 0, 0,'R','O', 0, 0, 0, 0,
8298 '@','A','B','C','D','E', 0, 0,'H','I','J','K','L', 0, 0, 0,
8299 'P','Q', 0,'S','T','U','V', 0,'X','Y','Z','[', 0, 0,'^', 0
8300};
8301
8302
Bram Moolenaar0f873732019-12-05 20:28:46 +01008303// TODO: Use characters NOT numbers!!!
Bram Moolenaar071d4272004-06-13 20:20:40 +00008304char CtrlCharTable[]=
Bram Moolenaar0f873732019-12-05 20:28:46 +01008305{// 0 1 2 3 4 5 6 7 8 9 A B C D E F
Bram Moolenaar071d4272004-06-13 20:20:40 +00008306 124,193,194,195, 0,201, 0, 0, 0, 0, 0,210,211,212,213,214,
8307 215,216,217,226, 0,209,200, 0,231,232, 0, 0,224,189, 95,109,
8308 0, 0, 0, 0, 0, 0,230,173, 0, 0, 0, 0, 0,197,198,199,
8309 0, 0,229, 0, 0, 0, 0,196, 0, 0, 0, 0,227,228, 0,233,
8310};
8311
8312
8313#endif