blob: 9a4880493f8522f40182546cdb1517517307baf9 [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.
Bram Moolenaar8e4af852022-01-24 12:20:45 +00001381 // In the GUI default TSTP processing is OK.
1382 // Checking both gui.in_use and gui.starting because gui.in_use is not set
1383 // at this point (set after menus are displayed), but gui.starting is set.
1384 signal(SIGTSTP, ignore_sigtstp ? SIG_IGN
1385# ifdef FEAT_GUI
1386 : gui.in_use || gui.starting ? SIG_DFL
1387# endif
1388 : (RETSIGTYPE (*)())sig_tstp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001389#endif
Bram Moolenaar2e310482018-08-21 13:09:10 +02001390#if defined(SIGCONT)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001391 signal(SIGCONT, sigcont_handler);
1392#endif
Bram Moolenaarbe5ee862020-06-10 20:56:58 +02001393#ifdef SIGPIPE
Bram Moolenaar071d4272004-06-13 20:20:40 +00001394 /*
1395 * We want to ignore breaking of PIPEs.
1396 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001397 signal(SIGPIPE, SIG_IGN);
1398#endif
1399
Bram Moolenaar071d4272004-06-13 20:20:40 +00001400#ifdef SIGINT
Bram Moolenaardf177f62005-02-22 08:39:57 +00001401 catch_int_signal();
Bram Moolenaar071d4272004-06-13 20:20:40 +00001402#endif
1403
Bram Moolenaarbe5ee862020-06-10 20:56:58 +02001404#ifdef SIGUSR1
1405 /*
1406 * Call user's handler on SIGUSR1
1407 */
1408 signal(SIGUSR1, (RETSIGTYPE (*)())catch_sigusr1);
1409#endif
1410
Bram Moolenaar071d4272004-06-13 20:20:40 +00001411 /*
1412 * Ignore alarm signals (Perl's alarm() generates it).
1413 */
1414#ifdef SIGALRM
1415 signal(SIGALRM, SIG_IGN);
1416#endif
1417
Bram Moolenaarbe5ee862020-06-10 20:56:58 +02001418#ifdef SIGPWR
Bram Moolenaar071d4272004-06-13 20:20:40 +00001419 /*
1420 * Catch SIGPWR (power failure?) to preserve the swap files, so that no
1421 * work will be lost.
1422 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001423 signal(SIGPWR, (RETSIGTYPE (*)())catch_sigpwr);
1424#endif
1425
1426 /*
1427 * Arrange for other signals to gracefully shutdown Vim.
1428 */
1429 catch_signals(deathtrap, SIG_ERR);
1430
1431#if defined(FEAT_GUI) && defined(SIGHUP)
1432 /*
1433 * When the GUI is running, ignore the hangup signal.
1434 */
1435 if (gui.in_use)
1436 signal(SIGHUP, SIG_IGN);
1437#endif
1438}
1439
Bram Moolenaardf177f62005-02-22 08:39:57 +00001440#if defined(SIGINT) || defined(PROTO)
1441/*
1442 * Catch CTRL-C (only works while in Cooked mode).
1443 */
1444 static void
Bram Moolenaar05540972016-01-30 20:31:25 +01001445catch_int_signal(void)
Bram Moolenaardf177f62005-02-22 08:39:57 +00001446{
1447 signal(SIGINT, (RETSIGTYPE (*)())catch_sigint);
1448}
1449#endif
1450
Bram Moolenaar071d4272004-06-13 20:20:40 +00001451 void
Bram Moolenaar05540972016-01-30 20:31:25 +01001452reset_signals(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001453{
1454 catch_signals(SIG_DFL, SIG_DFL);
Bram Moolenaar2e310482018-08-21 13:09:10 +02001455#if defined(SIGCONT)
Bram Moolenaar0f873732019-12-05 20:28:46 +01001456 // SIGCONT isn't in the list, because its default action is ignore
Bram Moolenaar071d4272004-06-13 20:20:40 +00001457 signal(SIGCONT, SIG_DFL);
1458#endif
1459}
1460
1461 static void
Bram Moolenaar05540972016-01-30 20:31:25 +01001462catch_signals(
1463 RETSIGTYPE (*func_deadly)(),
1464 RETSIGTYPE (*func_other)())
Bram Moolenaar071d4272004-06-13 20:20:40 +00001465{
1466 int i;
1467
1468 for (i = 0; signal_info[i].sig != -1; i++)
Bram Moolenaar5c3128e2020-05-11 20:54:42 +02001469 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00001470 if (signal_info[i].deadly)
1471 {
1472#if defined(HAVE_SIGALTSTACK) && defined(HAVE_SIGACTION)
1473 struct sigaction sa;
1474
Bram Moolenaar0f873732019-12-05 20:28:46 +01001475 // Setup to use the alternate stack for the signal function.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001476 sa.sa_handler = func_deadly;
1477 sigemptyset(&sa.sa_mask);
1478# if defined(__linux__) && defined(_REENTRANT)
Bram Moolenaar0f873732019-12-05 20:28:46 +01001479 // On Linux, with glibc compiled for kernel 2.2, there is a bug in
1480 // thread handling in combination with using the alternate stack:
1481 // pthread library functions try to use the stack pointer to
1482 // identify the current thread, causing a SEGV signal, which
1483 // recursively calls deathtrap() and hangs.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001484 sa.sa_flags = 0;
1485# else
1486 sa.sa_flags = SA_ONSTACK;
1487# endif
1488 sigaction(signal_info[i].sig, &sa, NULL);
1489#else
1490# if defined(HAVE_SIGALTSTACK) && defined(HAVE_SIGVEC)
1491 struct sigvec sv;
1492
Bram Moolenaar0f873732019-12-05 20:28:46 +01001493 // Setup to use the alternate stack for the signal function.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001494 sv.sv_handler = func_deadly;
1495 sv.sv_mask = 0;
1496 sv.sv_flags = SV_ONSTACK;
1497 sigvec(signal_info[i].sig, &sv, NULL);
1498# else
1499 signal(signal_info[i].sig, func_deadly);
1500# endif
1501#endif
1502 }
1503 else if (func_other != SIG_ERR)
Bram Moolenaar5c3128e2020-05-11 20:54:42 +02001504 {
1505 // Deal with non-deadly signals.
1506#ifdef SIGTSTP
1507 signal(signal_info[i].sig,
1508 signal_info[i].sig == SIGTSTP && ignore_sigtstp
1509 ? SIG_IGN : func_other);
1510#else
Bram Moolenaar071d4272004-06-13 20:20:40 +00001511 signal(signal_info[i].sig, func_other);
Bram Moolenaar5c3128e2020-05-11 20:54:42 +02001512#endif
1513 }
1514 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001515}
1516
Bram Moolenaarbb09ceb2016-10-18 16:27:23 +02001517#ifdef HAVE_SIGPROCMASK
1518 static void
1519block_signals(sigset_t *set)
1520{
1521 sigset_t newset;
1522 int i;
1523
1524 sigemptyset(&newset);
1525
1526 for (i = 0; signal_info[i].sig != -1; i++)
1527 sigaddset(&newset, signal_info[i].sig);
1528
Bram Moolenaar2e310482018-08-21 13:09:10 +02001529# if defined(SIGCONT)
Bram Moolenaar0f873732019-12-05 20:28:46 +01001530 // SIGCONT isn't in the list, because its default action is ignore
Bram Moolenaarbb09ceb2016-10-18 16:27:23 +02001531 sigaddset(&newset, SIGCONT);
1532# endif
1533
1534 sigprocmask(SIG_BLOCK, &newset, set);
1535}
1536
1537 static void
1538unblock_signals(sigset_t *set)
1539{
1540 sigprocmask(SIG_SETMASK, set, NULL);
1541}
1542#endif
1543
Bram Moolenaar071d4272004-06-13 20:20:40 +00001544/*
Bram Moolenaard8b0cf12004-12-12 11:33:30 +00001545 * Handling of SIGHUP, SIGQUIT and SIGTERM:
Bram Moolenaar9e1d2832007-05-06 12:51:41 +00001546 * "when" == a signal: when busy, postpone and return FALSE, otherwise
1547 * return TRUE
1548 * "when" == SIGNAL_BLOCK: Going to be busy, block signals
1549 * "when" == SIGNAL_UNBLOCK: Going to wait, unblock signals, use postponed
Bram Moolenaar67c53842010-05-22 18:28:27 +02001550 * signal
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00001551 * Returns TRUE when Vim should exit.
1552 */
1553 int
Bram Moolenaar05540972016-01-30 20:31:25 +01001554vim_handle_signal(int sig)
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00001555{
Bram Moolenaard8b0cf12004-12-12 11:33:30 +00001556 static int got_signal = 0;
1557 static int blocked = TRUE;
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00001558
Bram Moolenaard8b0cf12004-12-12 11:33:30 +00001559 switch (sig)
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00001560 {
Bram Moolenaard8b0cf12004-12-12 11:33:30 +00001561 case SIGNAL_BLOCK: blocked = TRUE;
1562 break;
1563
1564 case SIGNAL_UNBLOCK: blocked = FALSE;
1565 if (got_signal != 0)
1566 {
1567 kill(getpid(), got_signal);
1568 got_signal = 0;
1569 }
1570 break;
1571
1572 default: if (!blocked)
Bram Moolenaar0f873732019-12-05 20:28:46 +01001573 return TRUE; // exit!
Bram Moolenaard8b0cf12004-12-12 11:33:30 +00001574 got_signal = sig;
1575#ifdef SIGPWR
1576 if (sig != SIGPWR)
1577#endif
Bram Moolenaar0f873732019-12-05 20:28:46 +01001578 got_int = TRUE; // break any loops
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00001579 break;
1580 }
1581 return FALSE;
1582}
1583
1584/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00001585 * Check_win checks whether we have an interactive stdout.
1586 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001587 int
Bram Moolenaar05540972016-01-30 20:31:25 +01001588mch_check_win(int argc UNUSED, char **argv UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001589{
Bram Moolenaar071d4272004-06-13 20:20:40 +00001590 if (isatty(1))
1591 return OK;
1592 return FAIL;
1593}
1594
1595/*
1596 * Return TRUE if the input comes from a terminal, FALSE otherwise.
1597 */
1598 int
Bram Moolenaar05540972016-01-30 20:31:25 +01001599mch_input_isatty(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001600{
1601 if (isatty(read_cmd_fd))
1602 return TRUE;
1603 return FALSE;
1604}
1605
1606#ifdef FEAT_X11
1607
Bram Moolenaar651fca82021-11-29 20:39:38 +00001608# if defined(ELAPSED_TIMEVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001609
Bram Moolenaar071d4272004-06-13 20:20:40 +00001610/*
1611 * Give a message about the elapsed time for opening the X window.
1612 */
1613 static void
Bram Moolenaar833eb1d2016-11-24 17:22:50 +01001614xopen_message(long elapsed_msec)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001615{
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001616 smsg(_("Opening the X display took %ld msec"), elapsed_msec);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001617}
1618# endif
1619#endif
1620
Bram Moolenaar651fca82021-11-29 20:39:38 +00001621#if defined(FEAT_X11)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001622/*
1623 * A few functions shared by X11 title and clipboard code.
1624 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001625
1626static int got_x_error = FALSE;
1627
1628/*
1629 * X Error handler, otherwise X just exits! (very rude) -- webb
1630 */
1631 static int
Bram Moolenaar05540972016-01-30 20:31:25 +01001632x_error_handler(Display *dpy, XErrorEvent *error_event)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001633{
Bram Moolenaar843ee412004-06-30 16:16:41 +00001634 XGetErrorText(dpy, error_event->error_code, (char *)IObuff, IOSIZE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001635 STRCAT(IObuff, _("\nVim: Got X error\n"));
1636
Bram Moolenaarb1062eb2020-05-09 16:11:33 +02001637 // In the GUI we cannot print a message and continue, because no X calls
1638 // are allowed here (causes my system to hang). Silently continuing seems
1639 // like the best alternative. Do preserve files, in case we crash.
1640 ml_sync_all(FALSE, FALSE);
1641
1642#ifdef FEAT_GUI
1643 if (!gui.in_use)
1644#endif
1645 msg((char *)IObuff);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001646
Bram Moolenaar0f873732019-12-05 20:28:46 +01001647 return 0; // NOTREACHED
Bram Moolenaar071d4272004-06-13 20:20:40 +00001648}
1649
1650/*
1651 * Another X Error handler, just used to check for errors.
1652 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001653 static int
Bram Moolenaar05540972016-01-30 20:31:25 +01001654x_error_check(Display *dpy UNUSED, XErrorEvent *error_event UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001655{
1656 got_x_error = TRUE;
1657 return 0;
1658}
1659
Bram Moolenaarc0c75492018-12-29 11:03:23 +01001660/*
1661 * Return TRUE when connection to the X server is desired.
1662 */
1663 static int
1664x_connect_to_server(void)
1665{
1666 // No point in connecting if we are exiting or dying.
1667 if (exiting || v_dying)
1668 return FALSE;
1669
1670#if defined(FEAT_CLIENTSERVER)
1671 if (x_force_connect)
1672 return TRUE;
1673#endif
1674 if (x_no_connect)
1675 return FALSE;
1676
Bram Moolenaara8bfa172018-12-29 22:28:46 +01001677 // Check for a match with "exclude:" from 'clipboard'.
Bram Moolenaarc0c75492018-12-29 11:03:23 +01001678 if (clip_exclude_prog != NULL)
1679 {
Bram Moolenaara8bfa172018-12-29 22:28:46 +01001680 // Just in case we get called recursively, return FALSE. This could
1681 // happen if vpeekc() is used while executing the prog and it causes a
1682 // related callback to be invoked.
1683 if (regprog_in_use(clip_exclude_prog))
1684 return FALSE;
1685
Bram Moolenaarc0c75492018-12-29 11:03:23 +01001686 if (vim_regexec_prog(&clip_exclude_prog, FALSE, T_NAME, (colnr_T)0))
1687 return FALSE;
1688 }
1689 return TRUE;
1690}
1691
Bram Moolenaar071d4272004-06-13 20:20:40 +00001692#if defined(FEAT_X11) && defined(FEAT_XCLIPBOARD)
Bram Moolenaarb2148f52019-01-20 23:43:57 +01001693# if defined(USING_SETJMP)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001694/*
1695 * An X IO Error handler, used to catch error while opening the display.
1696 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001697 static int
Bram Moolenaar05540972016-01-30 20:31:25 +01001698x_IOerror_check(Display *dpy UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001699{
Bram Moolenaar0f873732019-12-05 20:28:46 +01001700 // This function should not return, it causes exit(). Longjump instead.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001701 LONGJMP(lc_jump_env, 1);
Bram Moolenaar1eed5322019-02-26 17:03:54 +01001702# if defined(VMS) || defined(__CYGWIN__)
Bram Moolenaar0f873732019-12-05 20:28:46 +01001703 return 0; // avoid the compiler complains about missing return value
Bram Moolenaarb4990bf2010-02-11 18:19:38 +01001704# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001705}
1706# endif
1707
1708/*
1709 * An X IO Error handler, used to catch terminal errors.
1710 */
Bram Moolenaard155d7a2018-12-21 16:04:21 +01001711static int xterm_dpy_retry_count = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001712
Bram Moolenaar071d4272004-06-13 20:20:40 +00001713 static int
Bram Moolenaar05540972016-01-30 20:31:25 +01001714x_IOerror_handler(Display *dpy UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001715{
1716 xterm_dpy = NULL;
Bram Moolenaard155d7a2018-12-21 16:04:21 +01001717 xterm_dpy_retry_count = 5; // Try reconnecting five times
Bram Moolenaar071d4272004-06-13 20:20:40 +00001718 x11_window = 0;
1719 x11_display = NULL;
1720 xterm_Shell = (Widget)0;
1721
Bram Moolenaar0f873732019-12-05 20:28:46 +01001722 // This function should not return, it causes exit(). Longjump instead.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001723 LONGJMP(x_jump_env, 1);
Bram Moolenaar1eed5322019-02-26 17:03:54 +01001724# if defined(VMS) || defined(__CYGWIN__)
Bram Moolenaar0f873732019-12-05 20:28:46 +01001725 return 0; // avoid the compiler complains about missing return value
Bram Moolenaarb4990bf2010-02-11 18:19:38 +01001726# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001727}
Bram Moolenaarb1e26502014-11-19 18:48:46 +01001728
1729/*
1730 * If the X11 connection was lost try to restore it.
1731 * Helps when the X11 server was stopped and restarted while Vim was inactive
Bram Moolenaarcaad4f02014-12-17 14:36:14 +01001732 * (e.g. through tmux).
Bram Moolenaarb1e26502014-11-19 18:48:46 +01001733 */
1734 static void
Bram Moolenaar05540972016-01-30 20:31:25 +01001735may_restore_clipboard(void)
Bram Moolenaarb1e26502014-11-19 18:48:46 +01001736{
Bram Moolenaar01e51e52018-12-29 13:09:46 +01001737 // No point in restoring the connecting if we are exiting or dying.
1738 if (!exiting && !v_dying && xterm_dpy_retry_count > 0)
Bram Moolenaarb1e26502014-11-19 18:48:46 +01001739 {
Bram Moolenaard155d7a2018-12-21 16:04:21 +01001740 --xterm_dpy_retry_count;
Bram Moolenaar527a6782014-12-17 17:59:31 +01001741
1742# ifndef LESSTIF_VERSION
Bram Moolenaar0f873732019-12-05 20:28:46 +01001743 // This has been reported to avoid Vim getting stuck.
Bram Moolenaar527a6782014-12-17 17:59:31 +01001744 if (app_context != (XtAppContext)NULL)
1745 {
1746 XtDestroyApplicationContext(app_context);
1747 app_context = (XtAppContext)NULL;
Bram Moolenaar0f873732019-12-05 20:28:46 +01001748 x11_display = NULL; // freed by XtDestroyApplicationContext()
Bram Moolenaar527a6782014-12-17 17:59:31 +01001749 }
1750# endif
1751
Bram Moolenaarb1e26502014-11-19 18:48:46 +01001752 setup_term_clip();
1753 get_x11_title(FALSE);
1754 }
1755}
Bram Moolenaard4aa83a2019-05-09 18:59:31 +02001756
1757 void
1758ex_xrestore(exarg_T *eap)
1759{
1760 if (eap->arg != NULL && STRLEN(eap->arg) > 0)
1761 {
1762 if (xterm_display_allocated)
1763 vim_free(xterm_display);
1764 xterm_display = (char *)vim_strsave(eap->arg);
1765 xterm_display_allocated = TRUE;
1766 }
1767 smsg(_("restoring display %s"), xterm_display == NULL
Bram Moolenaar0c5c3fa2019-11-30 22:38:16 +01001768 ? (char *)mch_getenv((char_u *)"DISPLAY") : xterm_display);
Bram Moolenaard4aa83a2019-05-09 18:59:31 +02001769
1770 clear_xterm_clip();
1771 x11_window = 0;
1772 xterm_dpy_retry_count = 5; // Try reconnecting five times
1773 may_restore_clipboard();
1774}
Bram Moolenaar071d4272004-06-13 20:20:40 +00001775#endif
1776
1777/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00001778 * Test if "dpy" and x11_window are valid by getting the window title.
1779 * I don't actually want it yet, so there may be a simpler call to use, but
1780 * this will cause the error handler x_error_check() to be called if anything
1781 * is wrong, such as the window pointer being invalid (as can happen when the
1782 * user changes his DISPLAY, but not his WINDOWID) -- webb
1783 */
1784 static int
Bram Moolenaar05540972016-01-30 20:31:25 +01001785test_x11_window(Display *dpy)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001786{
1787 int (*old_handler)();
1788 XTextProperty text_prop;
1789
1790 old_handler = XSetErrorHandler(x_error_check);
1791 got_x_error = FALSE;
1792 if (XGetWMName(dpy, x11_window, &text_prop))
1793 XFree((void *)text_prop.value);
1794 XSync(dpy, False);
1795 (void)XSetErrorHandler(old_handler);
1796
1797 if (p_verbose > 0 && got_x_error)
Bram Moolenaar32526b32019-01-19 17:43:09 +01001798 verb_msg(_("Testing the X display failed"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00001799
1800 return (got_x_error ? FAIL : OK);
1801}
1802#endif
1803
Bram Moolenaar071d4272004-06-13 20:20:40 +00001804
1805#ifdef FEAT_X11
1806
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +01001807static int get_x11_thing(int get_title, int test_only);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001808
1809/*
1810 * try to get x11 window and display
1811 *
1812 * return FAIL for failure, OK otherwise
1813 */
1814 static int
Bram Moolenaar05540972016-01-30 20:31:25 +01001815get_x11_windis(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001816{
1817 char *winid;
1818 static int result = -1;
Bram Moolenaar0f873732019-12-05 20:28:46 +01001819#define XD_NONE 0 // x11_display not set here
1820#define XD_HERE 1 // x11_display opened here
1821#define XD_GUI 2 // x11_display used from gui.dpy
1822#define XD_XTERM 3 // x11_display used from xterm_dpy
Bram Moolenaar071d4272004-06-13 20:20:40 +00001823 static int x11_display_from = XD_NONE;
1824 static int did_set_error_handler = FALSE;
1825
1826 if (!did_set_error_handler)
1827 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01001828 // X just exits if it finds an error otherwise!
Bram Moolenaar071d4272004-06-13 20:20:40 +00001829 (void)XSetErrorHandler(x_error_handler);
1830 did_set_error_handler = TRUE;
1831 }
1832
Bram Moolenaar9372a112005-12-06 19:59:18 +00001833#if defined(FEAT_GUI_X11) || defined(FEAT_GUI_GTK)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001834 if (gui.in_use)
1835 {
1836 /*
1837 * If the X11 display was opened here before, for the window where Vim
1838 * was started, close that one now to avoid a memory leak.
1839 */
1840 if (x11_display_from == XD_HERE && x11_display != NULL)
1841 {
1842 XCloseDisplay(x11_display);
1843 x11_display_from = XD_NONE;
1844 }
1845 if (gui_get_x11_windis(&x11_window, &x11_display) == OK)
1846 {
1847 x11_display_from = XD_GUI;
1848 return OK;
1849 }
1850 x11_display = NULL;
1851 return FAIL;
1852 }
1853 else if (x11_display_from == XD_GUI)
1854 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01001855 // GUI must have stopped somehow, clear x11_display
Bram Moolenaar071d4272004-06-13 20:20:40 +00001856 x11_window = 0;
1857 x11_display = NULL;
1858 x11_display_from = XD_NONE;
1859 }
1860#endif
1861
Bram Moolenaar0f873732019-12-05 20:28:46 +01001862 // When started with the "-X" argument, don't try connecting.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001863 if (!x_connect_to_server())
1864 return FAIL;
1865
1866 /*
1867 * If WINDOWID not set, should try another method to find out
1868 * what the current window number is. The only code I know for
1869 * this is very complicated.
1870 * We assume that zero is invalid for WINDOWID.
1871 */
1872 if (x11_window == 0 && (winid = getenv("WINDOWID")) != NULL)
1873 x11_window = (Window)atol(winid);
1874
1875#ifdef FEAT_XCLIPBOARD
Bram Moolenaard4aa83a2019-05-09 18:59:31 +02001876 if (xterm_dpy == x11_display)
1877 // x11_display may have been set to xterm_dpy elsewhere
1878 x11_display_from = XD_XTERM;
1879
Bram Moolenaar071d4272004-06-13 20:20:40 +00001880 if (xterm_dpy != NULL && x11_window != 0)
1881 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01001882 // We may have checked it already, but Gnome terminal can move us to
1883 // another window, so we need to check every time.
Bram Moolenaarc2a27c32007-12-01 16:19:33 +00001884 if (x11_display_from != XD_XTERM)
1885 {
1886 /*
1887 * If the X11 display was opened here before, for the window where
1888 * Vim was started, close that one now to avoid a memory leak.
1889 */
1890 if (x11_display_from == XD_HERE && x11_display != NULL)
1891 XCloseDisplay(x11_display);
1892 x11_display = xterm_dpy;
1893 x11_display_from = XD_XTERM;
1894 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001895 if (test_x11_window(x11_display) == FAIL)
1896 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01001897 // probably bad $WINDOWID
Bram Moolenaar071d4272004-06-13 20:20:40 +00001898 x11_window = 0;
1899 x11_display = NULL;
1900 x11_display_from = XD_NONE;
1901 return FAIL;
1902 }
1903 return OK;
1904 }
1905#endif
1906
1907 if (x11_window == 0 || x11_display == NULL)
1908 result = -1;
1909
Bram Moolenaar0f873732019-12-05 20:28:46 +01001910 if (result != -1) // Have already been here and set this
1911 return result; // Don't do all these X calls again
Bram Moolenaar071d4272004-06-13 20:20:40 +00001912
1913 if (x11_window != 0 && x11_display == NULL)
1914 {
1915#ifdef SET_SIG_ALARM
1916 RETSIGTYPE (*sig_save)();
1917#endif
Bram Moolenaar1ac56c22019-01-17 22:28:22 +01001918#ifdef ELAPSED_FUNC
1919 elapsed_T start_tv;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001920
1921 if (p_verbose > 0)
Bram Moolenaar1ac56c22019-01-17 22:28:22 +01001922 ELAPSED_INIT(start_tv);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001923#endif
1924
1925#ifdef SET_SIG_ALARM
1926 /*
1927 * Opening the Display may hang if the DISPLAY setting is wrong, or
1928 * the network connection is bad. Set an alarm timer to get out.
1929 */
1930 sig_alarm_called = FALSE;
1931 sig_save = (RETSIGTYPE (*)())signal(SIGALRM,
1932 (RETSIGTYPE (*)())sig_alarm);
1933 alarm(2);
1934#endif
1935 x11_display = XOpenDisplay(NULL);
1936
1937#ifdef SET_SIG_ALARM
1938 alarm(0);
1939 signal(SIGALRM, (RETSIGTYPE (*)())sig_save);
1940 if (p_verbose > 0 && sig_alarm_called)
Bram Moolenaar563bbea2019-01-22 21:45:40 +01001941 verb_msg(_("Opening the X display timed out"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00001942#endif
1943 if (x11_display != NULL)
1944 {
Bram Moolenaar833eb1d2016-11-24 17:22:50 +01001945# ifdef ELAPSED_FUNC
Bram Moolenaar071d4272004-06-13 20:20:40 +00001946 if (p_verbose > 0)
Bram Moolenaara04f10b2005-05-31 22:09:46 +00001947 {
1948 verbose_enter();
Bram Moolenaar833eb1d2016-11-24 17:22:50 +01001949 xopen_message(ELAPSED_FUNC(start_tv));
Bram Moolenaara04f10b2005-05-31 22:09:46 +00001950 verbose_leave();
1951 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001952# endif
1953 if (test_x11_window(x11_display) == FAIL)
1954 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01001955 // Maybe window id is bad
Bram Moolenaar071d4272004-06-13 20:20:40 +00001956 x11_window = 0;
1957 XCloseDisplay(x11_display);
1958 x11_display = NULL;
1959 }
1960 else
1961 x11_display_from = XD_HERE;
1962 }
1963 }
1964 if (x11_window == 0 || x11_display == NULL)
1965 return (result = FAIL);
Bram Moolenaar727c8762010-10-20 19:17:48 +02001966
1967# ifdef FEAT_EVAL
1968 set_vim_var_nr(VV_WINDOWID, (long)x11_window);
1969# endif
1970
Bram Moolenaar071d4272004-06-13 20:20:40 +00001971 return (result = OK);
1972}
1973
1974/*
1975 * Determine original x11 Window Title
1976 */
1977 static int
Bram Moolenaar05540972016-01-30 20:31:25 +01001978get_x11_title(int test_only)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001979{
Bram Moolenaar47136d72004-10-12 20:02:24 +00001980 return get_x11_thing(TRUE, test_only);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001981}
1982
1983/*
1984 * Determine original x11 Window icon
1985 */
1986 static int
Bram Moolenaar05540972016-01-30 20:31:25 +01001987get_x11_icon(int test_only)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001988{
1989 int retval = FALSE;
1990
1991 retval = get_x11_thing(FALSE, test_only);
1992
Bram Moolenaar0f873732019-12-05 20:28:46 +01001993 // could not get old icon, use terminal name
Bram Moolenaar071d4272004-06-13 20:20:40 +00001994 if (oldicon == NULL && !test_only)
1995 {
1996 if (STRNCMP(T_NAME, "builtin_", 8) == 0)
Bram Moolenaar20de1c22009-07-22 11:28:11 +00001997 oldicon = vim_strsave(T_NAME + 8);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001998 else
Bram Moolenaar20de1c22009-07-22 11:28:11 +00001999 oldicon = vim_strsave(T_NAME);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002000 }
2001
2002 return retval;
2003}
2004
2005 static int
Bram Moolenaar05540972016-01-30 20:31:25 +01002006get_x11_thing(
Bram Moolenaar0f873732019-12-05 20:28:46 +01002007 int get_title, // get title string
Bram Moolenaar05540972016-01-30 20:31:25 +01002008 int test_only)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002009{
2010 XTextProperty text_prop;
2011 int retval = FALSE;
2012 Status status;
2013
2014 if (get_x11_windis() == OK)
2015 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01002016 // Get window/icon name if any
Bram Moolenaar071d4272004-06-13 20:20:40 +00002017 if (get_title)
2018 status = XGetWMName(x11_display, x11_window, &text_prop);
2019 else
2020 status = XGetWMIconName(x11_display, x11_window, &text_prop);
2021
2022 /*
2023 * If terminal is xterm, then x11_window may be a child window of the
2024 * outer xterm window that actually contains the window/icon name, so
2025 * keep traversing up the tree until a window with a title/icon is
2026 * found.
2027 */
Bram Moolenaar4b96df52020-01-26 22:00:26 +01002028 // Previously this was only done for xterm and alike. I don't see a
Bram Moolenaar0f873732019-12-05 20:28:46 +01002029 // reason why it would fail for other terminal emulators.
2030 // if (term_is_xterm)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002031 {
2032 Window root;
2033 Window parent;
2034 Window win = x11_window;
2035 Window *children;
2036 unsigned int num_children;
2037
2038 while (!status || text_prop.value == NULL)
2039 {
2040 if (!XQueryTree(x11_display, win, &root, &parent, &children,
2041 &num_children))
2042 break;
2043 if (children)
2044 XFree((void *)children);
2045 if (parent == root || parent == 0)
2046 break;
2047
2048 win = parent;
2049 if (get_title)
2050 status = XGetWMName(x11_display, win, &text_prop);
2051 else
2052 status = XGetWMIconName(x11_display, win, &text_prop);
2053 }
2054 }
2055 if (status && text_prop.value != NULL)
2056 {
2057 retval = TRUE;
2058 if (!test_only)
2059 {
Bram Moolenaar6b649ac2019-12-07 17:47:22 +01002060 if (get_title)
2061 vim_free(oldtitle);
2062 else
2063 vim_free(oldicon);
Bram Moolenaara12a1612019-01-24 16:39:02 +01002064 if (text_prop.encoding == XA_STRING && !has_mbyte)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002065 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00002066 if (get_title)
2067 oldtitle = vim_strsave((char_u *)text_prop.value);
2068 else
2069 oldicon = vim_strsave((char_u *)text_prop.value);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002070 }
2071 else
2072 {
2073 char **cl;
2074 Status transform_status;
2075 int n = 0;
2076
2077 transform_status = XmbTextPropertyToTextList(x11_display,
2078 &text_prop,
2079 &cl, &n);
2080 if (transform_status >= Success && n > 0 && cl[0])
2081 {
2082 if (get_title)
2083 oldtitle = vim_strsave((char_u *) cl[0]);
2084 else
2085 oldicon = vim_strsave((char_u *) cl[0]);
2086 XFreeStringList(cl);
2087 }
2088 else
2089 {
2090 if (get_title)
2091 oldtitle = vim_strsave((char_u *)text_prop.value);
2092 else
2093 oldicon = vim_strsave((char_u *)text_prop.value);
2094 }
2095 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002096 }
2097 XFree((void *)text_prop.value);
2098 }
2099 }
2100 return retval;
2101}
2102
Bram Moolenaar0f873732019-12-05 20:28:46 +01002103// Xutf8 functions are not available on older systems. Note that on some
2104// systems X_HAVE_UTF8_STRING may be defined in a header file but
2105// Xutf8SetWMProperties() is not in the X11 library. Configure checks for
2106// that and defines HAVE_XUTF8SETWMPROPERTIES.
Bram Moolenaara12a1612019-01-24 16:39:02 +01002107#if defined(X_HAVE_UTF8_STRING)
Bram Moolenaarcbc246a2014-10-11 14:47:26 +02002108# if X_HAVE_UTF8_STRING && HAVE_XUTF8SETWMPROPERTIES
Bram Moolenaar071d4272004-06-13 20:20:40 +00002109# define USE_UTF8_STRING
2110# endif
2111#endif
2112
2113/*
2114 * Set x11 Window Title
2115 *
2116 * get_x11_windis() must be called before this and have returned OK
2117 */
2118 static void
Bram Moolenaar05540972016-01-30 20:31:25 +01002119set_x11_title(char_u *title)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002120{
Bram Moolenaar0f873732019-12-05 20:28:46 +01002121 // XmbSetWMProperties() and Xutf8SetWMProperties() should use a STRING
2122 // when possible, COMPOUND_TEXT otherwise. COMPOUND_TEXT isn't
2123 // supported everywhere and STRING doesn't work for multi-byte titles.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002124#ifdef USE_UTF8_STRING
2125 if (enc_utf8)
2126 Xutf8SetWMProperties(x11_display, x11_window, (const char *)title,
2127 NULL, NULL, 0, NULL, NULL, NULL);
2128 else
2129#endif
2130 {
2131#if XtSpecificationRelease >= 4
2132# ifdef FEAT_XFONTSET
2133 XmbSetWMProperties(x11_display, x11_window, (const char *)title,
2134 NULL, NULL, 0, NULL, NULL, NULL);
2135# else
2136 XTextProperty text_prop;
Bram Moolenaar9d75c832005-01-25 21:57:23 +00002137 char *c_title = (char *)title;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002138
Bram Moolenaar0f873732019-12-05 20:28:46 +01002139 // directly from example 3-18 "basicwin" of Xlib Programming Manual
Bram Moolenaar9d75c832005-01-25 21:57:23 +00002140 (void)XStringListToTextProperty(&c_title, 1, &text_prop);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002141 XSetWMProperties(x11_display, x11_window, &text_prop,
2142 NULL, NULL, 0, NULL, NULL, NULL);
2143# endif
2144#else
2145 XStoreName(x11_display, x11_window, (char *)title);
2146#endif
2147 }
2148 XFlush(x11_display);
2149}
2150
2151/*
2152 * Set x11 Window icon
2153 *
2154 * get_x11_windis() must be called before this and have returned OK
2155 */
2156 static void
Bram Moolenaar05540972016-01-30 20:31:25 +01002157set_x11_icon(char_u *icon)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002158{
Bram Moolenaar0f873732019-12-05 20:28:46 +01002159 // See above for comments about using X*SetWMProperties().
Bram Moolenaar071d4272004-06-13 20:20:40 +00002160#ifdef USE_UTF8_STRING
2161 if (enc_utf8)
2162 Xutf8SetWMProperties(x11_display, x11_window, NULL, (const char *)icon,
2163 NULL, 0, NULL, NULL, NULL);
2164 else
2165#endif
2166 {
2167#if XtSpecificationRelease >= 4
2168# ifdef FEAT_XFONTSET
2169 XmbSetWMProperties(x11_display, x11_window, NULL, (const char *)icon,
2170 NULL, 0, NULL, NULL, NULL);
2171# else
2172 XTextProperty text_prop;
Bram Moolenaar9d75c832005-01-25 21:57:23 +00002173 char *c_icon = (char *)icon;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002174
Bram Moolenaar9d75c832005-01-25 21:57:23 +00002175 (void)XStringListToTextProperty(&c_icon, 1, &text_prop);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002176 XSetWMProperties(x11_display, x11_window, NULL, &text_prop,
2177 NULL, 0, NULL, NULL, NULL);
2178# endif
2179#else
2180 XSetIconName(x11_display, x11_window, (char *)icon);
2181#endif
2182 }
2183 XFlush(x11_display);
2184}
2185
Bram Moolenaar0f873732019-12-05 20:28:46 +01002186#else // FEAT_X11
Bram Moolenaar071d4272004-06-13 20:20:40 +00002187
Bram Moolenaar071d4272004-06-13 20:20:40 +00002188 static int
Bram Moolenaard14e00e2016-01-31 17:30:51 +01002189get_x11_title(int test_only UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002190{
2191 return FALSE;
2192}
2193
2194 static int
Bram Moolenaard14e00e2016-01-31 17:30:51 +01002195get_x11_icon(int test_only)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002196{
2197 if (!test_only)
2198 {
2199 if (STRNCMP(T_NAME, "builtin_", 8) == 0)
Bram Moolenaar20de1c22009-07-22 11:28:11 +00002200 oldicon = vim_strsave(T_NAME + 8);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002201 else
Bram Moolenaar20de1c22009-07-22 11:28:11 +00002202 oldicon = vim_strsave(T_NAME);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002203 }
2204 return FALSE;
2205}
2206
Bram Moolenaar0f873732019-12-05 20:28:46 +01002207#endif // FEAT_X11
Bram Moolenaar071d4272004-06-13 20:20:40 +00002208
2209 int
Bram Moolenaar05540972016-01-30 20:31:25 +01002210mch_can_restore_title(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002211{
2212 return get_x11_title(TRUE);
2213}
2214
2215 int
Bram Moolenaar05540972016-01-30 20:31:25 +01002216mch_can_restore_icon(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002217{
2218 return get_x11_icon(TRUE);
2219}
2220
2221/*
2222 * Set the window title and icon.
2223 */
2224 void
Bram Moolenaar05540972016-01-30 20:31:25 +01002225mch_settitle(char_u *title, char_u *icon)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002226{
2227 int type = 0;
2228 static int recursive = 0;
2229
Bram Moolenaar0f873732019-12-05 20:28:46 +01002230 if (T_NAME == NULL) // no terminal name (yet)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002231 return;
Bram Moolenaar0f873732019-12-05 20:28:46 +01002232 if (title == NULL && icon == NULL) // nothing to do
Bram Moolenaar071d4272004-06-13 20:20:40 +00002233 return;
2234
Bram Moolenaar0f873732019-12-05 20:28:46 +01002235 // When one of the X11 functions causes a deadly signal, we get here again
2236 // recursively. Avoid hanging then (something is probably locked).
Bram Moolenaar071d4272004-06-13 20:20:40 +00002237 if (recursive)
2238 return;
2239 ++recursive;
2240
2241 /*
2242 * if the window ID and the display is known, we may use X11 calls
2243 */
2244#ifdef FEAT_X11
2245 if (get_x11_windis() == OK)
2246 type = 1;
2247#else
Bram Moolenaar097148e2020-08-11 21:58:20 +02002248# if defined(FEAT_GUI_PHOTON) \
Bram Moolenaarb3f74062020-02-26 16:16:53 +01002249 || defined(FEAT_GUI_GTK) || defined(FEAT_GUI_HAIKU)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002250 if (gui.in_use)
2251 type = 1;
2252# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002253#endif
2254
2255 /*
Bram Moolenaarf82bac32010-07-25 22:30:20 +02002256 * Note: if "t_ts" is set, title is set with escape sequence rather
Bram Moolenaar071d4272004-06-13 20:20:40 +00002257 * than x11 calls, because the x11 calls don't always work
2258 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002259 if ((type || *T_TS != NUL) && title != NULL)
2260 {
Bram Moolenaard8f0cef2018-08-19 22:20:16 +02002261 if (oldtitle_outdated)
2262 {
2263 oldtitle_outdated = FALSE;
2264 VIM_CLEAR(oldtitle);
2265 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002266 if (oldtitle == NULL
2267#ifdef FEAT_GUI
2268 && !gui.in_use
2269#endif
Bram Moolenaar0f873732019-12-05 20:28:46 +01002270 ) // first call but not in GUI, save title
Bram Moolenaar071d4272004-06-13 20:20:40 +00002271 (void)get_x11_title(FALSE);
2272
Bram Moolenaar0f873732019-12-05 20:28:46 +01002273 if (*T_TS != NUL) // it's OK if t_fs is empty
Bram Moolenaar071d4272004-06-13 20:20:40 +00002274 term_settitle(title);
2275#ifdef FEAT_X11
2276 else
2277# ifdef FEAT_GUI_GTK
Bram Moolenaar0f873732019-12-05 20:28:46 +01002278 if (!gui.in_use) // don't do this if GTK+ is running
Bram Moolenaar071d4272004-06-13 20:20:40 +00002279# endif
Bram Moolenaar0f873732019-12-05 20:28:46 +01002280 set_x11_title(title); // x11
Bram Moolenaar071d4272004-06-13 20:20:40 +00002281#endif
Bram Moolenaarb3f74062020-02-26 16:16:53 +01002282#if defined(FEAT_GUI_GTK) || defined(FEAT_GUI_HAIKU) \
Bram Moolenaar097148e2020-08-11 21:58:20 +02002283 || defined(FEAT_GUI_PHOTON)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002284 else
2285 gui_mch_settitle(title, icon);
2286#endif
Bram Moolenaardac13472019-09-16 21:06:21 +02002287 unix_did_set_title = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002288 }
2289
2290 if ((type || *T_CIS != NUL) && icon != NULL)
2291 {
2292 if (oldicon == NULL
2293#ifdef FEAT_GUI
2294 && !gui.in_use
2295#endif
Bram Moolenaar0f873732019-12-05 20:28:46 +01002296 ) // first call, save icon
Bram Moolenaar071d4272004-06-13 20:20:40 +00002297 get_x11_icon(FALSE);
2298
2299 if (*T_CIS != NUL)
2300 {
Bram Moolenaarfaf626e2019-10-24 17:43:25 +02002301 out_str(T_CIS); // set icon start
Bram Moolenaar071d4272004-06-13 20:20:40 +00002302 out_str_nf(icon);
Bram Moolenaarfaf626e2019-10-24 17:43:25 +02002303 out_str(T_CIE); // set icon end
Bram Moolenaar071d4272004-06-13 20:20:40 +00002304 out_flush();
2305 }
2306#ifdef FEAT_X11
2307 else
2308# ifdef FEAT_GUI_GTK
Bram Moolenaar0f873732019-12-05 20:28:46 +01002309 if (!gui.in_use) // don't do this if GTK+ is running
Bram Moolenaar071d4272004-06-13 20:20:40 +00002310# endif
Bram Moolenaar0f873732019-12-05 20:28:46 +01002311 set_x11_icon(icon); // x11
Bram Moolenaar071d4272004-06-13 20:20:40 +00002312#endif
2313 did_set_icon = TRUE;
2314 }
2315 --recursive;
2316}
2317
2318/*
2319 * Restore the window/icon title.
2320 * "which" is one of:
Bram Moolenaar40385db2018-08-07 22:31:44 +02002321 * SAVE_RESTORE_TITLE only restore title
2322 * SAVE_RESTORE_ICON only restore icon
2323 * SAVE_RESTORE_BOTH restore title and icon
Bram Moolenaar071d4272004-06-13 20:20:40 +00002324 */
2325 void
Bram Moolenaar05540972016-01-30 20:31:25 +01002326mch_restore_title(int which)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002327{
Bram Moolenaardac13472019-09-16 21:06:21 +02002328 int do_push_pop = unix_did_set_title || did_set_icon;
Bram Moolenaare5c83282019-05-03 23:15:37 +02002329
Bram Moolenaar0f873732019-12-05 20:28:46 +01002330 // only restore the title or icon when it has been set
Bram Moolenaardac13472019-09-16 21:06:21 +02002331 mch_settitle(((which & SAVE_RESTORE_TITLE) && unix_did_set_title) ?
Bram Moolenaar071d4272004-06-13 20:20:40 +00002332 (oldtitle ? oldtitle : p_titleold) : NULL,
Bram Moolenaar40385db2018-08-07 22:31:44 +02002333 ((which & SAVE_RESTORE_ICON) && did_set_icon) ? oldicon : NULL);
2334
Bram Moolenaare5c83282019-05-03 23:15:37 +02002335 if (do_push_pop)
2336 {
2337 // pop and push from/to the stack
2338 term_pop_title(which);
2339 term_push_title(which);
2340 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002341}
2342
Bram Moolenaar071d4272004-06-13 20:20:40 +00002343
2344/*
2345 * Return TRUE if "name" looks like some xterm name.
Bram Moolenaar3a7c85b2005-02-05 21:39:53 +00002346 * Seiichi Sato mentioned that "mlterm" works like xterm.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002347 */
2348 int
Bram Moolenaar05540972016-01-30 20:31:25 +01002349vim_is_xterm(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002350{
2351 if (name == NULL)
2352 return FALSE;
2353 return (STRNICMP(name, "xterm", 5) == 0
2354 || STRNICMP(name, "nxterm", 6) == 0
2355 || STRNICMP(name, "kterm", 5) == 0
Bram Moolenaar3a7c85b2005-02-05 21:39:53 +00002356 || STRNICMP(name, "mlterm", 6) == 0
Bram Moolenaar071d4272004-06-13 20:20:40 +00002357 || STRNICMP(name, "rxvt", 4) == 0
Bram Moolenaar995e4af2017-09-01 20:24:03 +02002358 || STRNICMP(name, "screen.xterm", 12) == 0
Bram Moolenaar071d4272004-06-13 20:20:40 +00002359 || STRCMP(name, "builtin_xterm") == 0);
2360}
2361
Bram Moolenaar3577c6f2008-06-24 21:16:56 +00002362#if defined(FEAT_MOUSE_XTERM) || defined(PROTO)
2363/*
2364 * Return TRUE if "name" appears to be that of a terminal
2365 * known to support the xterm-style mouse protocol.
2366 * Relies on term_is_xterm having been set to its correct value.
2367 */
2368 int
Bram Moolenaar05540972016-01-30 20:31:25 +01002369use_xterm_like_mouse(char_u *name)
Bram Moolenaar3577c6f2008-06-24 21:16:56 +00002370{
2371 return (name != NULL
Bram Moolenaare56132b2016-08-14 18:23:21 +02002372 && (term_is_xterm
2373 || STRNICMP(name, "screen", 6) == 0
Bram Moolenaar0ba40702016-10-12 14:50:54 +02002374 || STRNICMP(name, "tmux", 4) == 0
Bram Moolenaar48873ae2021-12-08 21:00:24 +00002375 || STRNICMP(name, "gnome", 5) == 0
Bram Moolenaare56132b2016-08-14 18:23:21 +02002376 || STRICMP(name, "st") == 0
2377 || STRNICMP(name, "st-", 3) == 0
2378 || STRNICMP(name, "stterm", 6) == 0));
Bram Moolenaar3577c6f2008-06-24 21:16:56 +00002379}
2380#endif
2381
Bram Moolenaar071d4272004-06-13 20:20:40 +00002382/*
2383 * Return non-zero when using an xterm mouse, according to 'ttymouse'.
2384 * Return 1 for "xterm".
2385 * Return 2 for "xterm2".
Bram Moolenaarc8427482011-10-20 21:09:35 +02002386 * Return 3 for "urxvt".
Bram Moolenaar2b9578f2012-08-15 16:21:32 +02002387 * Return 4 for "sgr".
Bram Moolenaar071d4272004-06-13 20:20:40 +00002388 */
2389 int
Bram Moolenaar05540972016-01-30 20:31:25 +01002390use_xterm_mouse(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002391{
Bram Moolenaar2b9578f2012-08-15 16:21:32 +02002392 if (ttym_flags == TTYM_SGR)
2393 return 4;
Bram Moolenaarc8427482011-10-20 21:09:35 +02002394 if (ttym_flags == TTYM_URXVT)
2395 return 3;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002396 if (ttym_flags == TTYM_XTERM2)
2397 return 2;
2398 if (ttym_flags == TTYM_XTERM)
2399 return 1;
2400 return 0;
2401}
Bram Moolenaar071d4272004-06-13 20:20:40 +00002402
2403 int
Bram Moolenaar05540972016-01-30 20:31:25 +01002404vim_is_iris(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002405{
2406 if (name == NULL)
2407 return FALSE;
2408 return (STRNICMP(name, "iris-ansi", 9) == 0
2409 || STRCMP(name, "builtin_iris-ansi") == 0);
2410}
2411
2412 int
Bram Moolenaar05540972016-01-30 20:31:25 +01002413vim_is_vt300(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002414{
2415 if (name == NULL)
Bram Moolenaar0f873732019-12-05 20:28:46 +01002416 return FALSE; // actually all ANSI comp. terminals should be here
2417 // catch VT100 - VT5xx
Bram Moolenaard4755bb2004-09-02 19:12:26 +00002418 return ((STRNICMP(name, "vt", 2) == 0
2419 && vim_strchr((char_u *)"12345", name[2]) != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002420 || STRCMP(name, "builtin_vt320") == 0);
2421}
2422
2423/*
2424 * Return TRUE if "name" is a terminal for which 'ttyfast' should be set.
2425 * This should include all windowed terminal emulators.
2426 */
2427 int
Bram Moolenaar05540972016-01-30 20:31:25 +01002428vim_is_fastterm(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002429{
2430 if (name == NULL)
2431 return FALSE;
2432 if (vim_is_xterm(name) || vim_is_vt300(name) || vim_is_iris(name))
2433 return TRUE;
2434 return ( STRNICMP(name, "hpterm", 6) == 0
2435 || STRNICMP(name, "sun-cmd", 7) == 0
2436 || STRNICMP(name, "screen", 6) == 0
Bram Moolenaar0ba40702016-10-12 14:50:54 +02002437 || STRNICMP(name, "tmux", 4) == 0
Bram Moolenaar071d4272004-06-13 20:20:40 +00002438 || STRNICMP(name, "dtterm", 6) == 0);
2439}
2440
2441/*
2442 * Insert user name in s[len].
2443 * Return OK if a name found.
2444 */
2445 int
Bram Moolenaar05540972016-01-30 20:31:25 +01002446mch_get_user_name(char_u *s, int len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002447{
2448#ifdef VMS
Bram Moolenaarffb8ab02005-09-07 21:15:32 +00002449 vim_strncpy(s, (char_u *)cuserid(NULL), len - 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002450 return OK;
2451#else
2452 return mch_get_uname(getuid(), s, len);
2453#endif
2454}
2455
2456/*
2457 * Insert user name for "uid" in s[len].
2458 * Return OK if a name found.
2459 */
2460 int
Bram Moolenaar05540972016-01-30 20:31:25 +01002461mch_get_uname(uid_t uid, char_u *s, int len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002462{
2463#if defined(HAVE_PWD_H) && defined(HAVE_GETPWUID)
2464 struct passwd *pw;
2465
2466 if ((pw = getpwuid(uid)) != NULL
2467 && pw->pw_name != NULL && *(pw->pw_name) != NUL)
2468 {
Bram Moolenaarbbebc852005-07-18 21:47:53 +00002469 vim_strncpy(s, (char_u *)pw->pw_name, len - 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002470 return OK;
2471 }
2472#endif
Bram Moolenaar0f873732019-12-05 20:28:46 +01002473 sprintf((char *)s, "%d", (int)uid); // assumes s is long enough
2474 return FAIL; // a number is not a name
Bram Moolenaar071d4272004-06-13 20:20:40 +00002475}
2476
2477/*
2478 * Insert host name is s[len].
2479 */
2480
2481#ifdef HAVE_SYS_UTSNAME_H
2482 void
Bram Moolenaar05540972016-01-30 20:31:25 +01002483mch_get_host_name(char_u *s, int len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002484{
2485 struct utsname vutsname;
2486
2487 if (uname(&vutsname) < 0)
2488 *s = NUL;
2489 else
Bram Moolenaarbbebc852005-07-18 21:47:53 +00002490 vim_strncpy(s, (char_u *)vutsname.nodename, len - 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002491}
Bram Moolenaar0f873732019-12-05 20:28:46 +01002492#else // HAVE_SYS_UTSNAME_H
Bram Moolenaar071d4272004-06-13 20:20:40 +00002493
2494# ifdef HAVE_SYS_SYSTEMINFO_H
2495# define gethostname(nam, len) sysinfo(SI_HOSTNAME, nam, len)
2496# endif
2497
2498 void
Bram Moolenaard14e00e2016-01-31 17:30:51 +01002499mch_get_host_name(char_u *s, int len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002500{
2501# ifdef VAXC
2502 vaxc$gethostname((char *)s, len);
2503# else
2504 gethostname((char *)s, len);
2505# endif
Bram Moolenaar0f873732019-12-05 20:28:46 +01002506 s[len - 1] = NUL; // make sure it's terminated
Bram Moolenaar071d4272004-06-13 20:20:40 +00002507}
Bram Moolenaar0f873732019-12-05 20:28:46 +01002508#endif // HAVE_SYS_UTSNAME_H
Bram Moolenaar071d4272004-06-13 20:20:40 +00002509
2510/*
2511 * return process ID
2512 */
2513 long
Bram Moolenaar05540972016-01-30 20:31:25 +01002514mch_get_pid(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002515{
2516 return (long)getpid();
2517}
2518
Bram Moolenaar67cf86b2019-04-28 22:25:38 +02002519/*
2520 * return TRUE if process "pid" is still running
2521 */
2522 int
Bram Moolenaar1b243ea2019-04-28 22:50:40 +02002523mch_process_running(long pid)
Bram Moolenaar67cf86b2019-04-28 22:25:38 +02002524{
Bram Moolenaar44dea9d2021-06-23 21:13:20 +02002525 // If there is no error the process must be running.
2526 if (kill(pid, 0) == 0)
2527 return TRUE;
2528#ifdef ESRCH
2529 // If the error is ESRCH then the process is not running.
2530 if (errno == ESRCH)
2531 return FALSE;
2532#endif
2533 // If the process is running and owned by another user we get EPERM. With
2534 // other errors the process might be running, assuming it is then.
2535 return TRUE;
Bram Moolenaar67cf86b2019-04-28 22:25:38 +02002536}
2537
Bram Moolenaar071d4272004-06-13 20:20:40 +00002538#if !defined(HAVE_STRERROR) && defined(USE_GETCWD)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002539 static char *
Bram Moolenaar05540972016-01-30 20:31:25 +01002540strerror(int err)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002541{
2542 extern int sys_nerr;
2543 extern char *sys_errlist[];
2544 static char er[20];
2545
2546 if (err > 0 && err < sys_nerr)
2547 return (sys_errlist[err]);
2548 sprintf(er, "Error %d", err);
2549 return er;
2550}
2551#endif
2552
2553/*
Bram Moolenaar964b3742019-05-24 18:54:09 +02002554 * Get name of current directory into buffer "buf" of length "len" bytes.
2555 * "len" must be at least PATH_MAX.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002556 * Return OK for success, FAIL for failure.
2557 */
2558 int
Bram Moolenaar05540972016-01-30 20:31:25 +01002559mch_dirname(char_u *buf, int len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002560{
2561#if defined(USE_GETCWD)
2562 if (getcwd((char *)buf, len) == NULL)
2563 {
2564 STRCPY(buf, strerror(errno));
2565 return FAIL;
2566 }
2567 return OK;
2568#else
2569 return (getwd((char *)buf) != NULL ? OK : FAIL);
2570#endif
2571}
2572
Bram Moolenaar071d4272004-06-13 20:20:40 +00002573/*
Bram Moolenaar3d20ca12006-11-28 16:43:58 +00002574 * Get absolute file name into "buf[len]".
Bram Moolenaar071d4272004-06-13 20:20:40 +00002575 *
2576 * return FAIL for failure, OK for success
2577 */
2578 int
Bram Moolenaar05540972016-01-30 20:31:25 +01002579mch_FullName(
2580 char_u *fname,
2581 char_u *buf,
2582 int len,
Bram Moolenaar0f873732019-12-05 20:28:46 +01002583 int force) // also expand when already absolute path
Bram Moolenaar071d4272004-06-13 20:20:40 +00002584{
2585 int l;
Bram Moolenaar38323e42007-03-06 19:22:53 +00002586#ifdef HAVE_FCHDIR
Bram Moolenaar071d4272004-06-13 20:20:40 +00002587 int fd = -1;
Bram Moolenaar0f873732019-12-05 20:28:46 +01002588 static int dont_fchdir = FALSE; // TRUE when fchdir() doesn't work
Bram Moolenaar38323e42007-03-06 19:22:53 +00002589#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002590 char_u olddir[MAXPATHL];
2591 char_u *p;
2592 int retval = OK;
Bram Moolenaarbf820722008-06-21 11:12:49 +00002593#ifdef __CYGWIN__
Bram Moolenaar0f873732019-12-05 20:28:46 +01002594 char_u posix_fname[MAXPATHL]; // Cygwin docs mention MAX_PATH, but
2595 // it's not always defined
Bram Moolenaarbf820722008-06-21 11:12:49 +00002596#endif
2597
Bram Moolenaar38323e42007-03-06 19:22:53 +00002598#ifdef VMS
2599 fname = vms_fixfilename(fname);
2600#endif
2601
Bram Moolenaara2442432007-04-26 14:26:37 +00002602#ifdef __CYGWIN__
2603 /*
2604 * This helps for when "/etc/hosts" is a symlink to "c:/something/hosts".
2605 */
Bram Moolenaar0d1498e2008-06-29 12:00:49 +00002606# if CYGWIN_VERSION_DLL_MAJOR >= 1007
Bram Moolenaar0f873732019-12-05 20:28:46 +01002607 // Use CCP_RELATIVE to avoid that it sometimes returns a path that ends in
2608 // a forward slash.
Bram Moolenaar06b07342015-12-31 22:26:28 +01002609 cygwin_conv_path(CCP_WIN_A_TO_POSIX | CCP_RELATIVE,
2610 fname, posix_fname, MAXPATHL);
Bram Moolenaar0d1498e2008-06-29 12:00:49 +00002611# else
Bram Moolenaarbf820722008-06-21 11:12:49 +00002612 cygwin_conv_to_posix_path(fname, posix_fname);
Bram Moolenaar0d1498e2008-06-29 12:00:49 +00002613# endif
Bram Moolenaarbf820722008-06-21 11:12:49 +00002614 fname = posix_fname;
Bram Moolenaara2442432007-04-26 14:26:37 +00002615#endif
2616
Bram Moolenaar0f873732019-12-05 20:28:46 +01002617 // Expand it if forced or not an absolute path.
2618 // Do not do it for "/file", the result is always "/".
Bram Moolenaare3303cb2015-12-31 18:29:46 +01002619 if ((force || !mch_isFullName(fname))
2620 && ((p = vim_strrchr(fname, '/')) == NULL || p != fname))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002621 {
2622 /*
2623 * If the file name has a path, change to that directory for a moment,
Bram Moolenaar964b3742019-05-24 18:54:09 +02002624 * and then get the directory (and get back to where we were).
Bram Moolenaar071d4272004-06-13 20:20:40 +00002625 * This will get the correct path name with "../" things.
2626 */
Bram Moolenaare3303cb2015-12-31 18:29:46 +01002627 if (p != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002628 {
Bram Moolenaar4eaef992021-08-30 21:26:16 +02002629 if (STRCMP(p, "/..") == 0)
2630 // for "/path/dir/.." include the "/.."
2631 p += 3;
2632
Bram Moolenaar38323e42007-03-06 19:22:53 +00002633#ifdef HAVE_FCHDIR
Bram Moolenaar071d4272004-06-13 20:20:40 +00002634 /*
2635 * Use fchdir() if possible, it's said to be faster and more
2636 * reliable. But on SunOS 4 it might not work. Check this by
2637 * doing a fchdir() right now.
2638 */
2639 if (!dont_fchdir)
2640 {
2641 fd = open(".", O_RDONLY | O_EXTRA, 0);
2642 if (fd >= 0 && fchdir(fd) < 0)
2643 {
2644 close(fd);
2645 fd = -1;
Bram Moolenaar0f873732019-12-05 20:28:46 +01002646 dont_fchdir = TRUE; // don't try again
Bram Moolenaar071d4272004-06-13 20:20:40 +00002647 }
2648 }
Bram Moolenaar38323e42007-03-06 19:22:53 +00002649#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002650
Bram Moolenaar0f873732019-12-05 20:28:46 +01002651 // Only change directory when we are sure we can return to where
2652 // we are now. After doing "su" chdir(".") might not work.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002653 if (
Bram Moolenaar38323e42007-03-06 19:22:53 +00002654#ifdef HAVE_FCHDIR
Bram Moolenaar071d4272004-06-13 20:20:40 +00002655 fd < 0 &&
Bram Moolenaar38323e42007-03-06 19:22:53 +00002656#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002657 (mch_dirname(olddir, MAXPATHL) == FAIL
2658 || mch_chdir((char *)olddir) != 0))
2659 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01002660 p = NULL; // can't get current dir: don't chdir
Bram Moolenaar071d4272004-06-13 20:20:40 +00002661 retval = FAIL;
2662 }
2663 else
2664 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01002665 // The directory is copied into buf[], to be able to remove
2666 // the file name without changing it (could be a string in
2667 // read-only memory)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002668 if (p - fname >= len)
2669 retval = FAIL;
2670 else
2671 {
Bram Moolenaarbbebc852005-07-18 21:47:53 +00002672 vim_strncpy(buf, fname, p - fname);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002673 if (mch_chdir((char *)buf))
Bram Moolenaarc6376c72021-10-03 19:29:48 +01002674 {
2675 // Path does not exist (yet). For a full path fail,
2676 // will use the path as-is. For a relative path use
2677 // the current directory and append the file name.
2678 if (mch_isFullName(fname))
2679 retval = FAIL;
2680 else
2681 p = NULL;
2682 }
Bram Moolenaar4eaef992021-08-30 21:26:16 +02002683 else if (*p == '/')
Bram Moolenaar071d4272004-06-13 20:20:40 +00002684 fname = p + 1;
Bram Moolenaar4eaef992021-08-30 21:26:16 +02002685 else
2686 fname = p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002687 *buf = NUL;
2688 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002689 }
2690 }
2691 if (mch_dirname(buf, len) == FAIL)
2692 {
2693 retval = FAIL;
2694 *buf = NUL;
2695 }
2696 if (p != NULL)
2697 {
Bram Moolenaar38323e42007-03-06 19:22:53 +00002698#ifdef HAVE_FCHDIR
Bram Moolenaar071d4272004-06-13 20:20:40 +00002699 if (fd >= 0)
2700 {
Bram Moolenaar25724922009-07-14 15:38:41 +00002701 if (p_verbose >= 5)
2702 {
2703 verbose_enter();
Bram Moolenaar32526b32019-01-19 17:43:09 +01002704 msg("fchdir() to previous dir");
Bram Moolenaar25724922009-07-14 15:38:41 +00002705 verbose_leave();
2706 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002707 l = fchdir(fd);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002708 }
2709 else
Bram Moolenaar38323e42007-03-06 19:22:53 +00002710#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002711 l = mch_chdir((char *)olddir);
2712 if (l != 0)
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00002713 emsg(_(e_cannot_go_back_to_previous_directory));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002714 }
itchyny051a40c2021-10-20 10:00:05 +01002715#ifdef HAVE_FCHDIR
2716 if (fd >= 0)
2717 close(fd);
2718#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002719
2720 l = STRLEN(buf);
Bram Moolenaardac75692012-10-14 04:35:45 +02002721 if (l >= len - 1)
Bram Moolenaar0f873732019-12-05 20:28:46 +01002722 retval = FAIL; // no space for trailing "/"
Bram Moolenaar38323e42007-03-06 19:22:53 +00002723#ifndef VMS
Bram Moolenaardac75692012-10-14 04:35:45 +02002724 else if (l > 0 && buf[l - 1] != '/' && *fname != NUL
Bram Moolenaar071d4272004-06-13 20:20:40 +00002725 && STRCMP(fname, ".") != 0)
Bram Moolenaardac75692012-10-14 04:35:45 +02002726 STRCAT(buf, "/");
Bram Moolenaar38323e42007-03-06 19:22:53 +00002727#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002728 }
Bram Moolenaar3d20ca12006-11-28 16:43:58 +00002729
Bram Moolenaar0f873732019-12-05 20:28:46 +01002730 // Catch file names which are too long.
Bram Moolenaar78a15312009-05-15 19:33:18 +00002731 if (retval == FAIL || (int)(STRLEN(buf) + STRLEN(fname)) >= len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002732 return FAIL;
2733
Bram Moolenaar0f873732019-12-05 20:28:46 +01002734 // Do not append ".", "/dir/." is equal to "/dir".
Bram Moolenaar071d4272004-06-13 20:20:40 +00002735 if (STRCMP(fname, ".") != 0)
2736 STRCAT(buf, fname);
2737
2738 return OK;
2739}
2740
2741/*
2742 * Return TRUE if "fname" does not depend on the current directory.
2743 */
2744 int
Bram Moolenaar05540972016-01-30 20:31:25 +01002745mch_isFullName(char_u *fname)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002746{
Bram Moolenaara06ecab2016-07-16 14:47:36 +02002747#ifdef VMS
Bram Moolenaar071d4272004-06-13 20:20:40 +00002748 return ( fname[0] == '/' || fname[0] == '.' ||
2749 strchr((char *)fname,':') || strchr((char *)fname,'"') ||
2750 (strchr((char *)fname,'[') && strchr((char *)fname,']'))||
2751 (strchr((char *)fname,'<') && strchr((char *)fname,'>')) );
Bram Moolenaara06ecab2016-07-16 14:47:36 +02002752#else
Bram Moolenaar071d4272004-06-13 20:20:40 +00002753 return (*fname == '/' || *fname == '~');
Bram Moolenaar071d4272004-06-13 20:20:40 +00002754#endif
2755}
2756
Bram Moolenaar24552be2005-12-10 20:17:30 +00002757#if defined(USE_FNAME_CASE) || defined(PROTO)
2758/*
2759 * Set the case of the file name, if it already exists. This will cause the
2760 * file name to remain exactly the same.
Bram Moolenaarc2a27c32007-12-01 16:19:33 +00002761 * Only required for file systems where case is ignored and preserved.
Bram Moolenaar24552be2005-12-10 20:17:30 +00002762 */
Bram Moolenaar24552be2005-12-10 20:17:30 +00002763 void
Bram Moolenaar05540972016-01-30 20:31:25 +01002764fname_case(
2765 char_u *name,
Bram Moolenaar0f873732019-12-05 20:28:46 +01002766 int len UNUSED) // buffer size, only used when name gets longer
Bram Moolenaar24552be2005-12-10 20:17:30 +00002767{
2768 struct stat st;
2769 char_u *slash, *tail;
2770 DIR *dirp;
2771 struct dirent *dp;
2772
Bram Moolenaarde5e2c22016-11-04 20:35:31 +01002773 if (mch_lstat((char *)name, &st) >= 0)
Bram Moolenaar24552be2005-12-10 20:17:30 +00002774 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01002775 // Open the directory where the file is located.
Bram Moolenaar24552be2005-12-10 20:17:30 +00002776 slash = vim_strrchr(name, '/');
2777 if (slash == NULL)
2778 {
2779 dirp = opendir(".");
2780 tail = name;
2781 }
2782 else
2783 {
2784 *slash = NUL;
2785 dirp = opendir((char *)name);
2786 *slash = '/';
2787 tail = slash + 1;
2788 }
2789
2790 if (dirp != NULL)
2791 {
2792 while ((dp = readdir(dirp)) != NULL)
2793 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01002794 // Only accept names that differ in case and are the same byte
2795 // length. TODO: accept different length name.
Bram Moolenaar24552be2005-12-10 20:17:30 +00002796 if (STRICMP(tail, dp->d_name) == 0
2797 && STRLEN(tail) == STRLEN(dp->d_name))
2798 {
2799 char_u newname[MAXPATHL + 1];
2800 struct stat st2;
2801
Bram Moolenaar0f873732019-12-05 20:28:46 +01002802 // Verify the inode is equal.
Bram Moolenaar24552be2005-12-10 20:17:30 +00002803 vim_strncpy(newname, name, MAXPATHL);
2804 vim_strncpy(newname + (tail - name), (char_u *)dp->d_name,
2805 MAXPATHL - (tail - name));
Bram Moolenaarde5e2c22016-11-04 20:35:31 +01002806 if (mch_lstat((char *)newname, &st2) >= 0
Bram Moolenaar24552be2005-12-10 20:17:30 +00002807 && st.st_ino == st2.st_ino
2808 && st.st_dev == st2.st_dev)
2809 {
2810 STRCPY(tail, dp->d_name);
2811 break;
2812 }
2813 }
2814 }
2815
2816 closedir(dirp);
2817 }
2818 }
2819}
2820#endif
2821
Bram Moolenaar071d4272004-06-13 20:20:40 +00002822/*
2823 * Get file permissions for 'name'.
2824 * Returns -1 when it doesn't exist.
2825 */
2826 long
Bram Moolenaar05540972016-01-30 20:31:25 +01002827mch_getperm(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002828{
2829 struct stat statb;
2830
Bram Moolenaar0f873732019-12-05 20:28:46 +01002831 // Keep the #ifdef outside of stat(), it may be a macro.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002832#ifdef VMS
2833 if (stat((char *)vms_fixfilename(name), &statb))
2834#else
2835 if (stat((char *)name, &statb))
2836#endif
2837 return -1;
Bram Moolenaar708f62c2007-08-11 20:24:10 +00002838#ifdef __INTERIX
Bram Moolenaar0f873732019-12-05 20:28:46 +01002839 // The top bit makes the value negative, which means the file doesn't
2840 // exist. Remove the bit, we don't use it.
Bram Moolenaar708f62c2007-08-11 20:24:10 +00002841 return statb.st_mode & ~S_ADDACE;
2842#else
Bram Moolenaar071d4272004-06-13 20:20:40 +00002843 return statb.st_mode;
Bram Moolenaar708f62c2007-08-11 20:24:10 +00002844#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002845}
2846
2847/*
Bram Moolenaarcd142e32017-11-16 17:03:45 +01002848 * Set file permission for "name" to "perm".
2849 * Return FAIL for failure, OK otherwise.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002850 */
2851 int
Bram Moolenaar05540972016-01-30 20:31:25 +01002852mch_setperm(char_u *name, long perm)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002853{
2854 return (chmod((char *)
2855#ifdef VMS
2856 vms_fixfilename(name),
2857#else
2858 name,
2859#endif
2860 (mode_t)perm) == 0 ? OK : FAIL);
2861}
2862
Bram Moolenaarcd142e32017-11-16 17:03:45 +01002863#if defined(HAVE_FCHMOD) || defined(PROTO)
2864/*
2865 * Set file permission for open file "fd" to "perm".
2866 * Return FAIL for failure, OK otherwise.
2867 */
2868 int
2869mch_fsetperm(int fd, long perm)
2870{
2871 return (fchmod(fd, (mode_t)perm) == 0 ? OK : FAIL);
2872}
2873#endif
2874
Bram Moolenaar071d4272004-06-13 20:20:40 +00002875#if defined(HAVE_ACL) || defined(PROTO)
2876# ifdef HAVE_SYS_ACL_H
2877# include <sys/acl.h>
2878# endif
2879# ifdef HAVE_SYS_ACCESS_H
2880# include <sys/access.h>
2881# endif
2882
2883# ifdef HAVE_SOLARIS_ACL
2884typedef struct vim_acl_solaris_T {
2885 int acl_cnt;
2886 aclent_t *acl_entry;
2887} vim_acl_solaris_T;
2888# endif
2889
Bram Moolenaar588ebeb2008-05-07 17:09:24 +00002890#if defined(HAVE_SELINUX) || defined(PROTO)
2891/*
2892 * Copy security info from "from_file" to "to_file".
2893 */
2894 void
Bram Moolenaar05540972016-01-30 20:31:25 +01002895mch_copy_sec(char_u *from_file, char_u *to_file)
Bram Moolenaar588ebeb2008-05-07 17:09:24 +00002896{
2897 if (from_file == NULL)
2898 return;
2899
2900 if (selinux_enabled == -1)
2901 selinux_enabled = is_selinux_enabled();
2902
2903 if (selinux_enabled > 0)
2904 {
Bram Moolenaar89560232020-10-09 23:04:47 +02002905 // Use "char *" instead of "security_context_t" to avoid a deprecation
2906 // warning.
2907 char *from_context = NULL;
2908 char *to_context = NULL;
Bram Moolenaar588ebeb2008-05-07 17:09:24 +00002909
2910 if (getfilecon((char *)from_file, &from_context) < 0)
2911 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01002912 // If the filesystem doesn't support extended attributes,
2913 // the original had no special security context and the
2914 // target cannot have one either.
Bram Moolenaar588ebeb2008-05-07 17:09:24 +00002915 if (errno == EOPNOTSUPP)
2916 return;
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(from_file);
2920 msg_putchar('\n');
2921 return;
2922 }
2923 if (getfilecon((char *)to_file, &to_context) < 0)
2924 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01002925 msg_puts(_("\nCould not get security context for "));
Bram Moolenaar588ebeb2008-05-07 17:09:24 +00002926 msg_outtrans(to_file);
2927 msg_putchar('\n');
2928 freecon (from_context);
2929 return ;
2930 }
2931 if (strcmp(from_context, to_context) != 0)
2932 {
2933 if (setfilecon((char *)to_file, from_context) < 0)
2934 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01002935 msg_puts(_("\nCould not set security context for "));
Bram Moolenaar588ebeb2008-05-07 17:09:24 +00002936 msg_outtrans(to_file);
2937 msg_putchar('\n');
2938 }
2939 }
2940 freecon(to_context);
2941 freecon(from_context);
2942 }
2943}
Bram Moolenaar0f873732019-12-05 20:28:46 +01002944#endif // HAVE_SELINUX
Bram Moolenaar588ebeb2008-05-07 17:09:24 +00002945
Bram Moolenaar5bd32f42014-04-02 14:05:38 +02002946#if defined(HAVE_SMACK) && !defined(PROTO)
2947/*
2948 * Copy security info from "from_file" to "to_file".
2949 */
2950 void
Bram Moolenaard14e00e2016-01-31 17:30:51 +01002951mch_copy_sec(char_u *from_file, char_u *to_file)
Bram Moolenaar5bd32f42014-04-02 14:05:38 +02002952{
Bram Moolenaar62f167f2014-04-23 12:52:40 +02002953 static const char * const smack_copied_attributes[] =
Bram Moolenaar5bd32f42014-04-02 14:05:38 +02002954 {
2955 XATTR_NAME_SMACK,
2956 XATTR_NAME_SMACKEXEC,
2957 XATTR_NAME_SMACKMMAP
2958 };
2959
2960 char buffer[SMACK_LABEL_LEN];
2961 const char *name;
2962 int index;
2963 int ret;
2964 ssize_t size;
2965
2966 if (from_file == NULL)
2967 return;
2968
2969 for (index = 0 ; index < (int)(sizeof(smack_copied_attributes)
2970 / sizeof(smack_copied_attributes)[0]) ; index++)
2971 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01002972 // get the name of the attribute to copy
Bram Moolenaar5bd32f42014-04-02 14:05:38 +02002973 name = smack_copied_attributes[index];
2974
Bram Moolenaar0f873732019-12-05 20:28:46 +01002975 // get the value of the attribute in buffer
Bram Moolenaar5bd32f42014-04-02 14:05:38 +02002976 size = getxattr((char*)from_file, name, buffer, sizeof(buffer));
2977 if (size >= 0)
2978 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01002979 // copy the attribute value of buffer
Bram Moolenaar5bd32f42014-04-02 14:05:38 +02002980 ret = setxattr((char*)to_file, name, buffer, (size_t)size, 0);
2981 if (ret < 0)
2982 {
Bram Moolenaar4a1314c2016-01-27 20:47:18 +01002983 vim_snprintf((char *)IObuff, IOSIZE,
2984 _("Could not set security context %s for %s"),
2985 name, to_file);
2986 msg_outtrans(IObuff);
Bram Moolenaar5bd32f42014-04-02 14:05:38 +02002987 msg_putchar('\n');
2988 }
2989 }
2990 else
2991 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01002992 // what reason of not having the attribute value?
Bram Moolenaar5bd32f42014-04-02 14:05:38 +02002993 switch (errno)
2994 {
2995 case ENOTSUP:
Bram Moolenaar0f873732019-12-05 20:28:46 +01002996 // extended attributes aren't supported or enabled
2997 // should a message be echoed? not sure...
2998 return; // leave because it isn't useful to continue
Bram Moolenaar5bd32f42014-04-02 14:05:38 +02002999
3000 case ERANGE:
3001 default:
Bram Moolenaar0f873732019-12-05 20:28:46 +01003002 // no enough size OR unexpected error
Bram Moolenaar4a1314c2016-01-27 20:47:18 +01003003 vim_snprintf((char *)IObuff, IOSIZE,
3004 _("Could not get security context %s for %s. Removing it!"),
3005 name, from_file);
Bram Moolenaar32526b32019-01-19 17:43:09 +01003006 msg_puts((char *)IObuff);
Bram Moolenaar4a1314c2016-01-27 20:47:18 +01003007 msg_putchar('\n');
Bram Moolenaar0f873732019-12-05 20:28:46 +01003008 // FALLTHROUGH to remove the attribute
Bram Moolenaar5bd32f42014-04-02 14:05:38 +02003009
3010 case ENODATA:
Bram Moolenaar0f873732019-12-05 20:28:46 +01003011 // no attribute of this name
Bram Moolenaar5bd32f42014-04-02 14:05:38 +02003012 ret = removexattr((char*)to_file, name);
Bram Moolenaar0f873732019-12-05 20:28:46 +01003013 // Silently ignore errors, apparently this happens when
3014 // smack is not actually being used.
Bram Moolenaar5bd32f42014-04-02 14:05:38 +02003015 break;
3016 }
3017 }
3018 }
3019}
Bram Moolenaar0f873732019-12-05 20:28:46 +01003020#endif // HAVE_SMACK
Bram Moolenaar5bd32f42014-04-02 14:05:38 +02003021
Bram Moolenaar071d4272004-06-13 20:20:40 +00003022/*
3023 * Return a pointer to the ACL of file "fname" in allocated memory.
3024 * Return NULL if the ACL is not available for whatever reason.
3025 */
3026 vim_acl_T
Bram Moolenaar05540972016-01-30 20:31:25 +01003027mch_get_acl(char_u *fname UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003028{
3029 vim_acl_T ret = NULL;
3030#ifdef HAVE_POSIX_ACL
3031 ret = (vim_acl_T)acl_get_file((char *)fname, ACL_TYPE_ACCESS);
3032#else
Bram Moolenaar8d462f92012-02-05 22:51:33 +01003033#ifdef HAVE_SOLARIS_ZFS_ACL
3034 acl_t *aclent;
3035
3036 if (acl_get((char *)fname, 0, &aclent) < 0)
3037 return NULL;
3038 ret = (vim_acl_T)aclent;
3039#else
Bram Moolenaar071d4272004-06-13 20:20:40 +00003040#ifdef HAVE_SOLARIS_ACL
3041 vim_acl_solaris_T *aclent;
3042
3043 aclent = malloc(sizeof(vim_acl_solaris_T));
3044 if ((aclent->acl_cnt = acl((char *)fname, GETACLCNT, 0, NULL)) < 0)
3045 {
3046 free(aclent);
3047 return NULL;
3048 }
3049 aclent->acl_entry = malloc(aclent->acl_cnt * sizeof(aclent_t));
3050 if (acl((char *)fname, GETACL, aclent->acl_cnt, aclent->acl_entry) < 0)
3051 {
3052 free(aclent->acl_entry);
3053 free(aclent);
3054 return NULL;
3055 }
3056 ret = (vim_acl_T)aclent;
3057#else
3058#if defined(HAVE_AIX_ACL)
3059 int aclsize;
3060 struct acl *aclent;
3061
3062 aclsize = sizeof(struct acl);
3063 aclent = malloc(aclsize);
3064 if (statacl((char *)fname, STX_NORMAL, aclent, aclsize) < 0)
3065 {
3066 if (errno == ENOSPC)
3067 {
3068 aclsize = aclent->acl_len;
3069 aclent = realloc(aclent, aclsize);
3070 if (statacl((char *)fname, STX_NORMAL, aclent, aclsize) < 0)
3071 {
3072 free(aclent);
3073 return NULL;
3074 }
3075 }
3076 else
3077 {
3078 free(aclent);
3079 return NULL;
3080 }
3081 }
3082 ret = (vim_acl_T)aclent;
Bram Moolenaar0f873732019-12-05 20:28:46 +01003083#endif // HAVE_AIX_ACL
3084#endif // HAVE_SOLARIS_ACL
3085#endif // HAVE_SOLARIS_ZFS_ACL
3086#endif // HAVE_POSIX_ACL
Bram Moolenaar071d4272004-06-13 20:20:40 +00003087 return ret;
3088}
3089
3090/*
3091 * Set the ACL of file "fname" to "acl" (unless it's NULL).
3092 */
3093 void
Bram Moolenaar05540972016-01-30 20:31:25 +01003094mch_set_acl(char_u *fname UNUSED, vim_acl_T aclent)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003095{
3096 if (aclent == NULL)
3097 return;
3098#ifdef HAVE_POSIX_ACL
3099 acl_set_file((char *)fname, ACL_TYPE_ACCESS, (acl_t)aclent);
3100#else
Bram Moolenaar8d462f92012-02-05 22:51:33 +01003101#ifdef HAVE_SOLARIS_ZFS_ACL
3102 acl_set((char *)fname, (acl_t *)aclent);
3103#else
Bram Moolenaar071d4272004-06-13 20:20:40 +00003104#ifdef HAVE_SOLARIS_ACL
3105 acl((char *)fname, SETACL, ((vim_acl_solaris_T *)aclent)->acl_cnt,
3106 ((vim_acl_solaris_T *)aclent)->acl_entry);
3107#else
3108#ifdef HAVE_AIX_ACL
3109 chacl((char *)fname, aclent, ((struct acl *)aclent)->acl_len);
Bram Moolenaar0f873732019-12-05 20:28:46 +01003110#endif // HAVE_AIX_ACL
3111#endif // HAVE_SOLARIS_ACL
3112#endif // HAVE_SOLARIS_ZFS_ACL
3113#endif // HAVE_POSIX_ACL
Bram Moolenaar071d4272004-06-13 20:20:40 +00003114}
3115
3116 void
Bram Moolenaar05540972016-01-30 20:31:25 +01003117mch_free_acl(vim_acl_T aclent)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003118{
3119 if (aclent == NULL)
3120 return;
3121#ifdef HAVE_POSIX_ACL
3122 acl_free((acl_t)aclent);
3123#else
Bram Moolenaar8d462f92012-02-05 22:51:33 +01003124#ifdef HAVE_SOLARIS_ZFS_ACL
3125 acl_free((acl_t *)aclent);
3126#else
Bram Moolenaar071d4272004-06-13 20:20:40 +00003127#ifdef HAVE_SOLARIS_ACL
3128 free(((vim_acl_solaris_T *)aclent)->acl_entry);
3129 free(aclent);
3130#else
3131#ifdef HAVE_AIX_ACL
3132 free(aclent);
Bram Moolenaar0f873732019-12-05 20:28:46 +01003133#endif // HAVE_AIX_ACL
3134#endif // HAVE_SOLARIS_ACL
3135#endif // HAVE_SOLARIS_ZFS_ACL
3136#endif // HAVE_POSIX_ACL
Bram Moolenaar071d4272004-06-13 20:20:40 +00003137}
3138#endif
3139
3140/*
3141 * Set hidden flag for "name".
3142 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003143 void
Bram Moolenaar05540972016-01-30 20:31:25 +01003144mch_hide(char_u *name UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003145{
Bram Moolenaar0f873732019-12-05 20:28:46 +01003146 // can't hide a file
Bram Moolenaar071d4272004-06-13 20:20:40 +00003147}
3148
3149/*
Bram Moolenaar43a34f92016-01-17 15:56:34 +01003150 * return TRUE if "name" is a directory or a symlink to a directory
Bram Moolenaar071d4272004-06-13 20:20:40 +00003151 * return FALSE if "name" is not a directory
3152 * return FALSE for error
3153 */
3154 int
Bram Moolenaar05540972016-01-30 20:31:25 +01003155mch_isdir(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003156{
3157 struct stat statb;
3158
Bram Moolenaar0f873732019-12-05 20:28:46 +01003159 if (*name == NUL) // Some stat()s don't flag "" as an error.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003160 return FALSE;
3161 if (stat((char *)name, &statb))
3162 return FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003163 return (S_ISDIR(statb.st_mode) ? TRUE : FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003164}
3165
Bram Moolenaar43a34f92016-01-17 15:56:34 +01003166/*
3167 * return TRUE if "name" is a directory, NOT a symlink to a directory
3168 * return FALSE if "name" is not a directory
3169 * return FALSE for error
3170 */
3171 int
Bram Moolenaar05540972016-01-30 20:31:25 +01003172mch_isrealdir(char_u *name)
Bram Moolenaar43a34f92016-01-17 15:56:34 +01003173{
3174 struct stat statb;
3175
Bram Moolenaar0f873732019-12-05 20:28:46 +01003176 if (*name == NUL) // Some stat()s don't flag "" as an error.
Bram Moolenaar43a34f92016-01-17 15:56:34 +01003177 return FALSE;
Bram Moolenaarde5e2c22016-11-04 20:35:31 +01003178 if (mch_lstat((char *)name, &statb))
Bram Moolenaar43a34f92016-01-17 15:56:34 +01003179 return FALSE;
Bram Moolenaar43a34f92016-01-17 15:56:34 +01003180 return (S_ISDIR(statb.st_mode) ? TRUE : FALSE);
Bram Moolenaar43a34f92016-01-17 15:56:34 +01003181}
3182
Bram Moolenaar071d4272004-06-13 20:20:40 +00003183/*
3184 * Return 1 if "name" is an executable file, 0 if not or it doesn't exist.
3185 */
3186 static int
Bram Moolenaar05540972016-01-30 20:31:25 +01003187executable_file(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003188{
3189 struct stat st;
3190
3191 if (stat((char *)name, &st))
3192 return 0;
Bram Moolenaar206f0112014-03-12 16:51:55 +01003193#ifdef VMS
Bram Moolenaar0f873732019-12-05 20:28:46 +01003194 // Like on Unix system file can have executable rights but not necessarily
3195 // be an executable, but on Unix is not a default for an ordinary file to
3196 // have an executable flag - on VMS it is in most cases.
3197 // Therefore, this check does not have any sense - let keep us to the
3198 // conventions instead:
3199 // *.COM and *.EXE files are the executables - the rest are not. This is
Dominique Pelleaf4a61a2021-12-27 17:21:41 +00003200 // not ideal but better than it was.
Bram Moolenaar206f0112014-03-12 16:51:55 +01003201 int vms_executable = 0;
3202 if (S_ISREG(st.st_mode) && mch_access((char *)name, X_OK) == 0)
3203 {
3204 if (strstr(vms_tolower((char*)name),".exe") != NULL
3205 || strstr(vms_tolower((char*)name),".com")!= NULL)
3206 vms_executable = 1;
3207 }
3208 return vms_executable;
3209#else
Bram Moolenaar071d4272004-06-13 20:20:40 +00003210 return S_ISREG(st.st_mode) && mch_access((char *)name, X_OK) == 0;
Bram Moolenaar206f0112014-03-12 16:51:55 +01003211#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003212}
3213
3214/*
Bram Moolenaar2fcf6682017-03-11 20:03:42 +01003215 * Return TRUE if "name" can be found in $PATH and executed, FALSE if not.
Bram Moolenaarb5971142015-03-21 17:32:19 +01003216 * If "use_path" is FALSE only check if "name" is executable.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003217 * Return -1 if unknown.
3218 */
3219 int
Bram Moolenaar05540972016-01-30 20:31:25 +01003220mch_can_exe(char_u *name, char_u **path, int use_path)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003221{
3222 char_u *buf;
3223 char_u *p, *e;
3224 int retval;
3225
Bram Moolenaar0f873732019-12-05 20:28:46 +01003226 // When "use_path" is false and if it's an absolute or relative path don't
3227 // need to use $PATH.
Bram Moolenaard08b8c42019-07-24 14:59:45 +02003228 if (!use_path || gettail(name) != name)
Bram Moolenaar206f0112014-03-12 16:51:55 +01003229 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01003230 // There must be a path separator, files in the current directory
3231 // can't be executed.
Bram Moolenaard08b8c42019-07-24 14:59:45 +02003232 if ((use_path || gettail(name) != name) && executable_file(name))
Bram Moolenaarc7f02552014-04-01 21:00:59 +02003233 {
3234 if (path != NULL)
3235 {
Bram Moolenaar43663192017-03-05 14:29:12 +01003236 if (name[0] != '/')
Bram Moolenaarc7f02552014-04-01 21:00:59 +02003237 *path = FullName_save(name, TRUE);
3238 else
3239 *path = vim_strsave(name);
3240 }
3241 return TRUE;
3242 }
3243 return FALSE;
Bram Moolenaar206f0112014-03-12 16:51:55 +01003244 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003245
3246 p = (char_u *)getenv("PATH");
3247 if (p == NULL || *p == NUL)
3248 return -1;
Bram Moolenaar964b3742019-05-24 18:54:09 +02003249 buf = alloc(STRLEN(name) + STRLEN(p) + 2);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003250 if (buf == NULL)
3251 return -1;
3252
3253 /*
3254 * Walk through all entries in $PATH to check if "name" exists there and
3255 * is an executable file.
3256 */
3257 for (;;)
3258 {
3259 e = (char_u *)strchr((char *)p, ':');
3260 if (e == NULL)
3261 e = p + STRLEN(p);
Bram Moolenaar0f873732019-12-05 20:28:46 +01003262 if (e - p <= 1) // empty entry means current dir
Bram Moolenaar071d4272004-06-13 20:20:40 +00003263 STRCPY(buf, "./");
3264 else
3265 {
Bram Moolenaarbbebc852005-07-18 21:47:53 +00003266 vim_strncpy(buf, p, e - p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003267 add_pathsep(buf);
3268 }
3269 STRCAT(buf, name);
3270 retval = executable_file(buf);
3271 if (retval == 1)
Bram Moolenaarc7f02552014-04-01 21:00:59 +02003272 {
3273 if (path != NULL)
3274 {
Bram Moolenaar43663192017-03-05 14:29:12 +01003275 if (buf[0] != '/')
Bram Moolenaarc7f02552014-04-01 21:00:59 +02003276 *path = FullName_save(buf, TRUE);
3277 else
3278 *path = vim_strsave(buf);
3279 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003280 break;
Bram Moolenaarc7f02552014-04-01 21:00:59 +02003281 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003282
3283 if (*e != ':')
3284 break;
3285 p = e + 1;
3286 }
3287
3288 vim_free(buf);
3289 return retval;
3290}
Bram Moolenaar071d4272004-06-13 20:20:40 +00003291
3292/*
3293 * Check what "name" is:
3294 * NODE_NORMAL: file or directory (or doesn't exist)
3295 * NODE_WRITABLE: writable device, socket, fifo, etc.
3296 * NODE_OTHER: non-writable things
3297 */
3298 int
Bram Moolenaar05540972016-01-30 20:31:25 +01003299mch_nodetype(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003300{
3301 struct stat st;
3302
3303 if (stat((char *)name, &st))
3304 return NODE_NORMAL;
3305 if (S_ISREG(st.st_mode) || S_ISDIR(st.st_mode))
3306 return NODE_NORMAL;
Bram Moolenaar0f873732019-12-05 20:28:46 +01003307 if (S_ISBLK(st.st_mode)) // block device isn't writable
Bram Moolenaar071d4272004-06-13 20:20:40 +00003308 return NODE_OTHER;
Bram Moolenaar0f873732019-12-05 20:28:46 +01003309 // Everything else is writable?
Bram Moolenaar071d4272004-06-13 20:20:40 +00003310 return NODE_WRITABLE;
3311}
3312
3313 void
Bram Moolenaar05540972016-01-30 20:31:25 +01003314mch_early_init(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003315{
3316#ifdef HAVE_CHECK_STACK_GROWTH
3317 int i;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003318
Bram Moolenaar071d4272004-06-13 20:20:40 +00003319 check_stack_growth((char *)&i);
3320
Bram Moolenaarbc7aa852005-03-06 23:38:09 +00003321# ifdef HAVE_STACK_LIMIT
Bram Moolenaar071d4272004-06-13 20:20:40 +00003322 get_stack_limit();
3323# endif
3324
3325#endif
3326
3327 /*
3328 * Setup an alternative stack for signals. Helps to catch signals when
3329 * running out of stack space.
3330 * Use of sigaltstack() is preferred, it's more portable.
3331 * Ignore any errors.
3332 */
3333#if defined(HAVE_SIGALTSTACK) || defined(HAVE_SIGSTACK)
Zdenek Dohnalba9c23e2021-08-11 14:20:05 +02003334 signal_stack = alloc(get_signal_stack_size());
Bram Moolenaar071d4272004-06-13 20:20:40 +00003335 init_signal_stack();
3336#endif
3337}
3338
Bram Moolenaar0a5fe212005-06-24 23:01:23 +00003339#if defined(EXITFREE) || defined(PROTO)
3340 void
Bram Moolenaar05540972016-01-30 20:31:25 +01003341mch_free_mem(void)
Bram Moolenaar0a5fe212005-06-24 23:01:23 +00003342{
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00003343# if defined(FEAT_CLIPBOARD) && defined(FEAT_X11)
3344 if (clip_star.owned)
3345 clip_lose_selection(&clip_star);
3346 if (clip_plus.owned)
3347 clip_lose_selection(&clip_plus);
Bram Moolenaar0a5fe212005-06-24 23:01:23 +00003348# endif
Bram Moolenaare8208012008-06-20 09:59:25 +00003349# if defined(FEAT_X11) && defined(FEAT_XCLIPBOARD)
Bram Moolenaar0a5fe212005-06-24 23:01:23 +00003350 if (xterm_Shell != (Widget)0)
3351 XtDestroyWidget(xterm_Shell);
Bram Moolenaare8208012008-06-20 09:59:25 +00003352# ifndef LESSTIF_VERSION
Bram Moolenaar0f873732019-12-05 20:28:46 +01003353 // Lesstif crashes here, lose some memory
Bram Moolenaar0a5fe212005-06-24 23:01:23 +00003354 if (xterm_dpy != NULL)
3355 XtCloseDisplay(xterm_dpy);
3356 if (app_context != (XtAppContext)NULL)
Bram Moolenaare8208012008-06-20 09:59:25 +00003357 {
Bram Moolenaar0a5fe212005-06-24 23:01:23 +00003358 XtDestroyApplicationContext(app_context);
Bram Moolenaare8208012008-06-20 09:59:25 +00003359# ifdef FEAT_X11
Bram Moolenaar0f873732019-12-05 20:28:46 +01003360 x11_display = NULL; // freed by XtDestroyApplicationContext()
Bram Moolenaare8208012008-06-20 09:59:25 +00003361# endif
3362 }
3363# endif
Bram Moolenaar0a5fe212005-06-24 23:01:23 +00003364# endif
Bram Moolenaar0eda7ac2010-06-26 05:38:18 +02003365# if defined(FEAT_X11)
Bram Moolenaare8208012008-06-20 09:59:25 +00003366 if (x11_display != NULL
3367# ifdef FEAT_XCLIPBOARD
3368 && x11_display != xterm_dpy
3369# endif
3370 )
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00003371 XCloseDisplay(x11_display);
3372# endif
3373# if defined(HAVE_SIGALTSTACK) || defined(HAVE_SIGSTACK)
Bram Moolenaard23a8232018-02-10 18:45:26 +01003374 VIM_CLEAR(signal_stack);
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00003375# endif
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00003376 vim_free(oldtitle);
3377 vim_free(oldicon);
Bram Moolenaar0a5fe212005-06-24 23:01:23 +00003378}
3379#endif
3380
Bram Moolenaar071d4272004-06-13 20:20:40 +00003381/*
3382 * Output a newline when exiting.
3383 * Make sure the newline goes to the same stream as the text.
3384 */
3385 static void
Bram Moolenaar05540972016-01-30 20:31:25 +01003386exit_scroll(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003387{
Bram Moolenaardf177f62005-02-22 08:39:57 +00003388 if (silent_mode)
3389 return;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003390 if (newline_on_exit || msg_didout)
3391 {
3392 if (msg_use_printf())
3393 {
3394 if (info_message)
3395 mch_msg("\n");
3396 else
3397 mch_errmsg("\r\n");
3398 }
3399 else
3400 out_char('\n');
3401 }
Bram Moolenaar7007e312021-03-27 12:11:33 +01003402 else if (!is_not_a_term())
Bram Moolenaar071d4272004-06-13 20:20:40 +00003403 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01003404 restore_cterm_colors(); // get original colors back
3405 msg_clr_eos_force(); // clear the rest of the display
3406 windgoto((int)Rows - 1, 0); // may have moved the cursor
Bram Moolenaar071d4272004-06-13 20:20:40 +00003407 }
3408}
3409
Bram Moolenaarb4151682020-05-11 22:13:28 +02003410#ifdef USE_GCOV_FLUSH
ichizokdee78e12021-12-09 21:08:01 +00003411# if (defined(__GNUC__) \
3412 && ((__GNUC__ == 11 && __GNUC_MINOR__ >= 1) || (__GNUC__ >= 12))) \
3413 || (defined(__clang__) && (__clang_major__ >= 12))
3414extern void __gcov_dump(void);
3415extern void __gcov_reset(void);
3416# define __gcov_flush() do { __gcov_dump(); __gcov_reset(); } while (0)
3417# else
3418extern void __gcov_flush(void);
3419# endif
Bram Moolenaarb4151682020-05-11 22:13:28 +02003420#endif
3421
Bram Moolenaar071d4272004-06-13 20:20:40 +00003422 void
Bram Moolenaar05540972016-01-30 20:31:25 +01003423mch_exit(int r)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003424{
3425 exiting = TRUE;
3426
3427#if defined(FEAT_X11) && defined(FEAT_CLIPBOARD)
3428 x11_export_final_selection();
3429#endif
3430
3431#ifdef FEAT_GUI
3432 if (!gui.in_use)
3433#endif
3434 {
3435 settmode(TMODE_COOK);
Bram Moolenaar7007e312021-03-27 12:11:33 +01003436 if (!is_not_a_term())
3437 {
3438 // restore xterm title and icon name
3439 mch_restore_title(SAVE_RESTORE_BOTH);
3440 term_pop_title(SAVE_RESTORE_BOTH);
3441 }
Bram Moolenaar651fca82021-11-29 20:39:38 +00003442
Bram Moolenaar071d4272004-06-13 20:20:40 +00003443 /*
3444 * When t_ti is not empty but it doesn't cause swapping terminal
3445 * pages, need to output a newline when msg_didout is set. But when
3446 * t_ti does swap pages it should not go to the shell page. Do this
3447 * before stoptermcap().
3448 */
3449 if (swapping_screen() && !newline_on_exit)
3450 exit_scroll();
3451
Bram Moolenaar0f873732019-12-05 20:28:46 +01003452 // Stop termcap: May need to check for T_CRV response, which
3453 // requires RAW mode.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003454 stoptermcap();
3455
3456 /*
3457 * A newline is only required after a message in the alternate screen.
3458 * This is set to TRUE by wait_return().
3459 */
3460 if (!swapping_screen() || newline_on_exit)
3461 exit_scroll();
3462
Bram Moolenaar0f873732019-12-05 20:28:46 +01003463 // Cursor may have been switched off without calling starttermcap()
3464 // when doing "vim -u vimrc" and vimrc contains ":q".
Bram Moolenaar071d4272004-06-13 20:20:40 +00003465 if (full_screen)
3466 cursor_on();
3467 }
3468 out_flush();
Bram Moolenaar0f873732019-12-05 20:28:46 +01003469 ml_close_all(TRUE); // remove all memfiles
Bram Moolenaarb4151682020-05-11 22:13:28 +02003470
3471#ifdef USE_GCOV_FLUSH
3472 // Flush coverage info before possibly being killed by a deadly signal.
3473 __gcov_flush();
3474#endif
3475
Bram Moolenaar071d4272004-06-13 20:20:40 +00003476 may_core_dump();
3477#ifdef FEAT_GUI
Bram Moolenaar071d4272004-06-13 20:20:40 +00003478 if (gui.in_use)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003479 gui_exit(r);
3480#endif
Bram Moolenaardf177f62005-02-22 08:39:57 +00003481
Bram Moolenaar56718732006-03-15 22:53:57 +00003482#ifdef MACOS_CONVERT
Bram Moolenaardf177f62005-02-22 08:39:57 +00003483 mac_conv_cleanup();
3484#endif
3485
Bram Moolenaar071d4272004-06-13 20:20:40 +00003486#ifdef __QNX__
Bram Moolenaar0f873732019-12-05 20:28:46 +01003487 // A core dump won't be created if the signal handler
3488 // doesn't return, so we can't call exit()
Bram Moolenaar071d4272004-06-13 20:20:40 +00003489 if (deadly_signal != 0)
3490 return;
3491#endif
3492
Bram Moolenaar009b2592004-10-24 19:18:58 +00003493#ifdef FEAT_NETBEANS_INTG
Bram Moolenaarb26e6322010-05-22 21:34:09 +02003494 netbeans_send_disconnect();
Bram Moolenaar009b2592004-10-24 19:18:58 +00003495#endif
Bram Moolenaar0a5fe212005-06-24 23:01:23 +00003496
3497#ifdef EXITFREE
3498 free_all_mem();
3499#endif
3500
Bram Moolenaar071d4272004-06-13 20:20:40 +00003501 exit(r);
3502}
3503
3504 static void
Bram Moolenaar05540972016-01-30 20:31:25 +01003505may_core_dump(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003506{
3507 if (deadly_signal != 0)
3508 {
3509 signal(deadly_signal, SIG_DFL);
Bram Moolenaar0f873732019-12-05 20:28:46 +01003510 kill(getpid(), deadly_signal); // Die using the signal we caught
Bram Moolenaar071d4272004-06-13 20:20:40 +00003511 }
3512}
3513
3514#ifndef VMS
3515
Bram Moolenaar1ecc5e42019-01-26 15:12:55 +01003516/*
3517 * Get the file descriptor to use for tty operations.
3518 */
3519 static int
3520get_tty_fd(int fd)
3521{
3522 int tty_fd = fd;
3523
3524#if defined(HAVE_SVR4_PTYS) && defined(SUN_SYSTEM)
3525 // On SunOS: Get the terminal parameters from "fd", or the slave device of
3526 // "fd" when it is a master device.
3527 if (mch_isatty(fd) > 1)
3528 {
3529 char *name;
3530
3531 name = ptsname(fd);
3532 if (name == NULL)
3533 return -1;
3534
3535 tty_fd = open(name, O_RDONLY | O_NOCTTY | O_EXTRA, 0);
3536 if (tty_fd < 0)
3537 return -1;
3538 }
3539#endif
3540 return tty_fd;
3541}
3542
3543 static int
3544mch_tcgetattr(int fd, void *term)
3545{
3546 int tty_fd;
3547 int retval = -1;
3548
3549 tty_fd = get_tty_fd(fd);
3550 if (tty_fd >= 0)
3551 {
3552#ifdef NEW_TTY_SYSTEM
3553# ifdef HAVE_TERMIOS_H
3554 retval = tcgetattr(tty_fd, (struct termios *)term);
3555# else
3556 retval = ioctl(tty_fd, TCGETA, (struct termio *)term);
3557# endif
3558#else
3559 // for "old" tty systems
3560 retval = ioctl(tty_fd, TIOCGETP, (struct sgttyb *)term);
3561#endif
3562 if (tty_fd != fd)
3563 close(tty_fd);
3564 }
3565 return retval;
3566}
3567
Bram Moolenaar071d4272004-06-13 20:20:40 +00003568 void
Bram Moolenaar26e86442020-05-17 14:06:16 +02003569mch_settmode(tmode_T tmode)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003570{
3571 static int first = TRUE;
3572
Bram Moolenaar4f44b882017-08-13 20:06:18 +02003573#ifdef NEW_TTY_SYSTEM
Bram Moolenaar071d4272004-06-13 20:20:40 +00003574# ifdef HAVE_TERMIOS_H
3575 static struct termios told;
3576 struct termios tnew;
3577# else
3578 static struct termio told;
3579 struct termio tnew;
3580# endif
3581
3582 if (first)
3583 {
3584 first = FALSE;
Bram Moolenaar1ecc5e42019-01-26 15:12:55 +01003585 mch_tcgetattr(read_cmd_fd, &told);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003586 }
3587
3588 tnew = told;
3589 if (tmode == TMODE_RAW)
3590 {
Bram Moolenaar041c7102020-05-30 18:14:57 +02003591 // ~ICRNL enables typing ^V^M
Bram Moolenaar928eec62020-05-31 13:09:47 +02003592 // ~IXON disables CTRL-S stopping output, so that it can be mapped.
3593 tnew.c_iflag &= ~(ICRNL | IXON);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003594 tnew.c_lflag &= ~(ICANON | ECHO | ISIG | ECHOE
Bram Moolenaare3f915d2020-07-14 23:02:44 +02003595# if defined(IEXTEN)
Bram Moolenaar0f873732019-12-05 20:28:46 +01003596 | IEXTEN // IEXTEN enables typing ^V on SOLARIS
Bram Moolenaar071d4272004-06-13 20:20:40 +00003597# endif
3598 );
Bram Moolenaarfaf626e2019-10-24 17:43:25 +02003599# ifdef ONLCR
3600 // Don't map NL -> CR NL, we do it ourselves.
3601 // Also disable expanding tabs if possible.
3602# ifdef XTABS
3603 tnew.c_oflag &= ~(ONLCR | XTABS);
3604# else
3605# ifdef TAB3
3606 tnew.c_oflag &= ~(ONLCR | TAB3);
3607# else
Bram Moolenaar071d4272004-06-13 20:20:40 +00003608 tnew.c_oflag &= ~ONLCR;
Bram Moolenaarfaf626e2019-10-24 17:43:25 +02003609# endif
3610# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003611# endif
Bram Moolenaarfaf626e2019-10-24 17:43:25 +02003612 tnew.c_cc[VMIN] = 1; // return after 1 char
3613 tnew.c_cc[VTIME] = 0; // don't wait
Bram Moolenaar071d4272004-06-13 20:20:40 +00003614 }
3615 else if (tmode == TMODE_SLEEP)
Bram Moolenaar40de4562016-07-01 15:03:46 +02003616 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01003617 // Also reset ICANON here, otherwise on Solaris select() won't see
3618 // typeahead characters.
Bram Moolenaar40de4562016-07-01 15:03:46 +02003619 tnew.c_lflag &= ~(ICANON | ECHO);
Bram Moolenaar0f873732019-12-05 20:28:46 +01003620 tnew.c_cc[VMIN] = 1; // return after 1 char
3621 tnew.c_cc[VTIME] = 0; // don't wait
Bram Moolenaar40de4562016-07-01 15:03:46 +02003622 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003623
3624# if defined(HAVE_TERMIOS_H)
3625 {
3626 int n = 10;
3627
Bram Moolenaar0f873732019-12-05 20:28:46 +01003628 // A signal may cause tcsetattr() to fail (e.g., SIGCONT). Retry a
3629 // few times.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003630 while (tcsetattr(read_cmd_fd, TCSANOW, &tnew) == -1
3631 && errno == EINTR && n > 0)
3632 --n;
3633 }
3634# else
3635 ioctl(read_cmd_fd, TCSETA, &tnew);
3636# endif
3637
3638#else
Bram Moolenaar071d4272004-06-13 20:20:40 +00003639 /*
3640 * for "old" tty systems
3641 */
3642# ifndef TIOCSETN
Bram Moolenaar0f873732019-12-05 20:28:46 +01003643# define TIOCSETN TIOCSETP // for hpux 9.0
Bram Moolenaar071d4272004-06-13 20:20:40 +00003644# endif
3645 static struct sgttyb ttybold;
3646 struct sgttyb ttybnew;
3647
3648 if (first)
3649 {
3650 first = FALSE;
Bram Moolenaar1ecc5e42019-01-26 15:12:55 +01003651 mch_tcgetattr(read_cmd_fd, &ttybold);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003652 }
3653
3654 ttybnew = ttybold;
3655 if (tmode == TMODE_RAW)
3656 {
3657 ttybnew.sg_flags &= ~(CRMOD | ECHO);
3658 ttybnew.sg_flags |= RAW;
3659 }
3660 else if (tmode == TMODE_SLEEP)
3661 ttybnew.sg_flags &= ~(ECHO);
3662 ioctl(read_cmd_fd, TIOCSETN, &ttybnew);
3663#endif
Bram Moolenaar26e86442020-05-17 14:06:16 +02003664 mch_cur_tmode = tmode;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003665}
3666
3667/*
3668 * Try to get the code for "t_kb" from the stty setting
3669 *
3670 * Even if termcap claims a backspace key, the user's setting *should*
3671 * prevail. stty knows more about reality than termcap does, and if
3672 * somebody's usual erase key is DEL (which, for most BSD users, it will
3673 * be), they're going to get really annoyed if their erase key starts
3674 * doing forward deletes for no reason. (Eric Fischer)
3675 */
3676 void
Bram Moolenaar05540972016-01-30 20:31:25 +01003677get_stty(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003678{
Bram Moolenaar4f44b882017-08-13 20:06:18 +02003679 ttyinfo_T info;
3680 char_u buf[2];
3681 char_u *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003682
Bram Moolenaar4f44b882017-08-13 20:06:18 +02003683 if (get_tty_info(read_cmd_fd, &info) == OK)
3684 {
3685 intr_char = info.interrupt;
3686 buf[0] = info.backspace;
3687 buf[1] = NUL;
3688 add_termcode((char_u *)"kb", buf, FALSE);
3689
Bram Moolenaar0f873732019-12-05 20:28:46 +01003690 // If <BS> and <DEL> are now the same, redefine <DEL>.
Bram Moolenaar4f44b882017-08-13 20:06:18 +02003691 p = find_termcode((char_u *)"kD");
3692 if (p != NULL && p[0] == buf[0] && p[1] == buf[1])
3693 do_fixdel(NULL);
3694 }
3695}
3696
3697/*
3698 * Obtain the characters that Backspace and Enter produce on "fd".
3699 * Returns OK or FAIL.
3700 */
3701 int
3702get_tty_info(int fd, ttyinfo_T *info)
3703{
3704#ifdef NEW_TTY_SYSTEM
Bram Moolenaar071d4272004-06-13 20:20:40 +00003705# ifdef HAVE_TERMIOS_H
3706 struct termios keys;
3707# else
3708 struct termio keys;
3709# endif
3710
Bram Moolenaar1ecc5e42019-01-26 15:12:55 +01003711 if (mch_tcgetattr(fd, &keys) != -1)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003712 {
Bram Moolenaar4f44b882017-08-13 20:06:18 +02003713 info->backspace = keys.c_cc[VERASE];
3714 info->interrupt = keys.c_cc[VINTR];
3715 if (keys.c_iflag & ICRNL)
3716 info->enter = NL;
3717 else
3718 info->enter = CAR;
3719 if (keys.c_oflag & ONLCR)
3720 info->nl_does_cr = TRUE;
3721 else
3722 info->nl_does_cr = FALSE;
3723 return OK;
3724 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003725#else
Bram Moolenaar0f873732019-12-05 20:28:46 +01003726 // for "old" tty systems
Bram Moolenaar071d4272004-06-13 20:20:40 +00003727 struct sgttyb keys;
3728
Bram Moolenaar1ecc5e42019-01-26 15:12:55 +01003729 if (mch_tcgetattr(fd, &keys) != -1)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003730 {
Bram Moolenaar4f44b882017-08-13 20:06:18 +02003731 info->backspace = keys.sg_erase;
3732 info->interrupt = keys.sg_kill;
3733 info->enter = CAR;
3734 info->nl_does_cr = TRUE;
3735 return OK;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003736 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003737#endif
Bram Moolenaar4f44b882017-08-13 20:06:18 +02003738 return FAIL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003739}
3740
Bram Moolenaar0f873732019-12-05 20:28:46 +01003741#endif // VMS
Bram Moolenaar071d4272004-06-13 20:20:40 +00003742
Bram Moolenaar51b0f372017-11-18 18:52:04 +01003743static int mouse_ison = FALSE;
3744
Bram Moolenaar071d4272004-06-13 20:20:40 +00003745/*
3746 * Set mouse clicks on or off.
3747 */
3748 void
Bram Moolenaar05540972016-01-30 20:31:25 +01003749mch_setmouse(int on)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003750{
Bram Moolenaara1cb1d12019-10-17 23:00:07 +02003751#ifdef FEAT_BEVAL_TERM
Bram Moolenaar51b0f372017-11-18 18:52:04 +01003752 static int bevalterm_ison = FALSE;
Bram Moolenaara1cb1d12019-10-17 23:00:07 +02003753#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003754 int xterm_mouse_vers;
3755
Bram Moolenaara1cb1d12019-10-17 23:00:07 +02003756#if defined(FEAT_X11) && defined(FEAT_XCLIPBOARD)
Bram Moolenaara06afc72018-08-27 23:24:16 +02003757 if (!on)
3758 // Make sure not tracing mouse movements. Important when a button-down
3759 // was received but no release yet.
3760 stop_xterm_trace();
Bram Moolenaara1cb1d12019-10-17 23:00:07 +02003761#endif
Bram Moolenaara06afc72018-08-27 23:24:16 +02003762
Bram Moolenaar51b0f372017-11-18 18:52:04 +01003763 if (on == mouse_ison
Bram Moolenaara1cb1d12019-10-17 23:00:07 +02003764#ifdef FEAT_BEVAL_TERM
Bram Moolenaar51b0f372017-11-18 18:52:04 +01003765 && p_bevalterm == bevalterm_ison
Bram Moolenaara1cb1d12019-10-17 23:00:07 +02003766#endif
Bram Moolenaar51b0f372017-11-18 18:52:04 +01003767 )
Bram Moolenaar0f873732019-12-05 20:28:46 +01003768 // return quickly if nothing to do
Bram Moolenaar071d4272004-06-13 20:20:40 +00003769 return;
3770
3771 xterm_mouse_vers = use_xterm_mouse();
Bram Moolenaarc8427482011-10-20 21:09:35 +02003772
Bram Moolenaara1cb1d12019-10-17 23:00:07 +02003773#ifdef FEAT_MOUSE_URXVT
Bram Moolenaar2b9578f2012-08-15 16:21:32 +02003774 if (ttym_flags == TTYM_URXVT)
3775 {
Bram Moolenaarc8427482011-10-20 21:09:35 +02003776 out_str_nf((char_u *)
3777 (on
3778 ? IF_EB("\033[?1015h", ESC_STR "[?1015h")
3779 : IF_EB("\033[?1015l", ESC_STR "[?1015l")));
Bram Moolenaar51b0f372017-11-18 18:52:04 +01003780 mouse_ison = on;
Bram Moolenaarc8427482011-10-20 21:09:35 +02003781 }
Bram Moolenaara1cb1d12019-10-17 23:00:07 +02003782#endif
Bram Moolenaarc8427482011-10-20 21:09:35 +02003783
Bram Moolenaar2b9578f2012-08-15 16:21:32 +02003784 if (ttym_flags == TTYM_SGR)
3785 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01003786 // SGR mode supports columns above 223
Bram Moolenaar2b9578f2012-08-15 16:21:32 +02003787 out_str_nf((char_u *)
3788 (on
3789 ? IF_EB("\033[?1006h", ESC_STR "[?1006h")
3790 : IF_EB("\033[?1006l", ESC_STR "[?1006l")));
Bram Moolenaar51b0f372017-11-18 18:52:04 +01003791 mouse_ison = on;
3792 }
Bram Moolenaar51b0f372017-11-18 18:52:04 +01003793
Bram Moolenaara1cb1d12019-10-17 23:00:07 +02003794#ifdef FEAT_BEVAL_TERM
Bram Moolenaar51b0f372017-11-18 18:52:04 +01003795 if (bevalterm_ison != (p_bevalterm && on))
3796 {
3797 bevalterm_ison = (p_bevalterm && on);
3798 if (xterm_mouse_vers > 1 && !bevalterm_ison)
Bram Moolenaar0f873732019-12-05 20:28:46 +01003799 // disable mouse movement events, enabling is below
Bram Moolenaar51b0f372017-11-18 18:52:04 +01003800 out_str_nf((char_u *)
3801 (IF_EB("\033[?1003l", ESC_STR "[?1003l")));
Bram Moolenaar2b9578f2012-08-15 16:21:32 +02003802 }
Bram Moolenaara1cb1d12019-10-17 23:00:07 +02003803#endif
Bram Moolenaar2b9578f2012-08-15 16:21:32 +02003804
Bram Moolenaar071d4272004-06-13 20:20:40 +00003805 if (xterm_mouse_vers > 0)
3806 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01003807 if (on) // enable mouse events, use mouse tracking if available
Bram Moolenaar071d4272004-06-13 20:20:40 +00003808 out_str_nf((char_u *)
3809 (xterm_mouse_vers > 1
Bram Moolenaar51b0f372017-11-18 18:52:04 +01003810 ? (
Bram Moolenaara1cb1d12019-10-17 23:00:07 +02003811#ifdef FEAT_BEVAL_TERM
Bram Moolenaar51b0f372017-11-18 18:52:04 +01003812 bevalterm_ison
3813 ? IF_EB("\033[?1003h", ESC_STR "[?1003h") :
Bram Moolenaara1cb1d12019-10-17 23:00:07 +02003814#endif
Bram Moolenaar51b0f372017-11-18 18:52:04 +01003815 IF_EB("\033[?1002h", ESC_STR "[?1002h"))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003816 : IF_EB("\033[?1000h", ESC_STR "[?1000h")));
Bram Moolenaar0f873732019-12-05 20:28:46 +01003817 else // disable mouse events, could probably always send the same
Bram Moolenaar071d4272004-06-13 20:20:40 +00003818 out_str_nf((char_u *)
3819 (xterm_mouse_vers > 1
3820 ? IF_EB("\033[?1002l", ESC_STR "[?1002l")
3821 : IF_EB("\033[?1000l", ESC_STR "[?1000l")));
Bram Moolenaar51b0f372017-11-18 18:52:04 +01003822 mouse_ison = on;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003823 }
3824
Bram Moolenaara1cb1d12019-10-17 23:00:07 +02003825#ifdef FEAT_MOUSE_DEC
Bram Moolenaar071d4272004-06-13 20:20:40 +00003826 else if (ttym_flags == TTYM_DEC)
3827 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01003828 if (on) // enable mouse events
Bram Moolenaar071d4272004-06-13 20:20:40 +00003829 out_str_nf((char_u *)"\033[1;2'z\033[1;3'{");
Bram Moolenaar0f873732019-12-05 20:28:46 +01003830 else // disable mouse events
Bram Moolenaar071d4272004-06-13 20:20:40 +00003831 out_str_nf((char_u *)"\033['z");
Bram Moolenaar51b0f372017-11-18 18:52:04 +01003832 mouse_ison = on;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003833 }
Bram Moolenaara1cb1d12019-10-17 23:00:07 +02003834#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003835
Bram Moolenaara1cb1d12019-10-17 23:00:07 +02003836#ifdef FEAT_MOUSE_GPM
Bram Moolenaar071d4272004-06-13 20:20:40 +00003837 else
3838 {
3839 if (on)
3840 {
3841 if (gpm_open())
Bram Moolenaar51b0f372017-11-18 18:52:04 +01003842 mouse_ison = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003843 }
3844 else
3845 {
3846 gpm_close();
Bram Moolenaar51b0f372017-11-18 18:52:04 +01003847 mouse_ison = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003848 }
3849 }
Bram Moolenaara1cb1d12019-10-17 23:00:07 +02003850#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003851
Bram Moolenaara1cb1d12019-10-17 23:00:07 +02003852#ifdef FEAT_SYSMOUSE
Bram Moolenaar3577c6f2008-06-24 21:16:56 +00003853 else
3854 {
3855 if (on)
3856 {
3857 if (sysmouse_open() == OK)
Bram Moolenaar51b0f372017-11-18 18:52:04 +01003858 mouse_ison = TRUE;
Bram Moolenaar3577c6f2008-06-24 21:16:56 +00003859 }
3860 else
3861 {
3862 sysmouse_close();
Bram Moolenaar51b0f372017-11-18 18:52:04 +01003863 mouse_ison = FALSE;
Bram Moolenaar3577c6f2008-06-24 21:16:56 +00003864 }
3865 }
Bram Moolenaara1cb1d12019-10-17 23:00:07 +02003866#endif
Bram Moolenaar3577c6f2008-06-24 21:16:56 +00003867
Bram Moolenaara1cb1d12019-10-17 23:00:07 +02003868#ifdef FEAT_MOUSE_JSB
Bram Moolenaar071d4272004-06-13 20:20:40 +00003869 else
3870 {
3871 if (on)
3872 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01003873 // D - Enable Mouse up/down messages
3874 // L - Enable Left Button Reporting
3875 // M - Enable Middle Button Reporting
3876 // R - Enable Right Button Reporting
3877 // K - Enable SHIFT and CTRL key Reporting
3878 // + - Enable Advanced messaging of mouse moves and up/down messages
3879 // Q - Quiet No Ack
3880 // # - Numeric value of mouse pointer required
3881 // 0 = Multiview 2000 cursor, used as standard
3882 // 1 = Windows Arrow
3883 // 2 = Windows I Beam
3884 // 3 = Windows Hour Glass
3885 // 4 = Windows Cross Hair
3886 // 5 = Windows UP Arrow
Bram Moolenaara1cb1d12019-10-17 23:00:07 +02003887# ifdef JSBTERM_MOUSE_NONADVANCED
Bram Moolenaar0f873732019-12-05 20:28:46 +01003888 // Disables full feedback of pointer movements
Bram Moolenaar071d4272004-06-13 20:20:40 +00003889 out_str_nf((char_u *)IF_EB("\033[0~ZwLMRK1Q\033\\",
3890 ESC_STR "[0~ZwLMRK1Q" ESC_STR "\\"));
Bram Moolenaara1cb1d12019-10-17 23:00:07 +02003891# else
Bram Moolenaar071d4272004-06-13 20:20:40 +00003892 out_str_nf((char_u *)IF_EB("\033[0~ZwLMRK+1Q\033\\",
3893 ESC_STR "[0~ZwLMRK+1Q" ESC_STR "\\"));
Bram Moolenaara1cb1d12019-10-17 23:00:07 +02003894# endif
Bram Moolenaar51b0f372017-11-18 18:52:04 +01003895 mouse_ison = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003896 }
3897 else
3898 {
3899 out_str_nf((char_u *)IF_EB("\033[0~ZwQ\033\\",
3900 ESC_STR "[0~ZwQ" ESC_STR "\\"));
Bram Moolenaar51b0f372017-11-18 18:52:04 +01003901 mouse_ison = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003902 }
3903 }
Bram Moolenaara1cb1d12019-10-17 23:00:07 +02003904#endif
3905#ifdef FEAT_MOUSE_PTERM
Bram Moolenaar071d4272004-06-13 20:20:40 +00003906 else
3907 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01003908 // 1 = button press, 6 = release, 7 = drag, 1h...9l = right button
Bram Moolenaar071d4272004-06-13 20:20:40 +00003909 if (on)
3910 out_str_nf("\033[>1h\033[>6h\033[>7h\033[>1h\033[>9l");
3911 else
3912 out_str_nf("\033[>1l\033[>6l\033[>7l\033[>1l\033[>9h");
Bram Moolenaar51b0f372017-11-18 18:52:04 +01003913 mouse_ison = on;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003914 }
Bram Moolenaara1cb1d12019-10-17 23:00:07 +02003915#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003916}
3917
Bram Moolenaarc3719bd2017-11-18 22:13:31 +01003918#if defined(FEAT_BEVAL_TERM) || defined(PROTO)
Bram Moolenaar51b0f372017-11-18 18:52:04 +01003919/*
3920 * Called when 'balloonevalterm' changed.
3921 */
3922 void
3923mch_bevalterm_changed(void)
3924{
3925 mch_setmouse(mouse_ison);
3926}
3927#endif
3928
Bram Moolenaar071d4272004-06-13 20:20:40 +00003929/*
3930 * Set the mouse termcode, depending on the 'term' and 'ttymouse' options.
3931 */
3932 void
Bram Moolenaar05540972016-01-30 20:31:25 +01003933check_mouse_termcode(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003934{
3935# ifdef FEAT_MOUSE_XTERM
3936 if (use_xterm_mouse()
Bram Moolenaarc8427482011-10-20 21:09:35 +02003937# ifdef FEAT_MOUSE_URXVT
3938 && use_xterm_mouse() != 3
3939# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003940# ifdef FEAT_GUI
3941 && !gui.in_use
3942# endif
3943 )
3944 {
3945 set_mouse_termcode(KS_MOUSE, (char_u *)(term_is_8bit(T_NAME)
Bram Moolenaarbc7aa852005-03-06 23:38:09 +00003946 ? IF_EB("\233M", CSI_STR "M")
3947 : IF_EB("\033[M", ESC_STR "[M")));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003948 if (*p_mouse != NUL)
3949 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01003950 // force mouse off and maybe on to send possibly new mouse
3951 // activation sequence to the xterm, with(out) drag tracing.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003952 mch_setmouse(FALSE);
3953 setmouse();
3954 }
3955 }
3956 else
3957 del_mouse_termcode(KS_MOUSE);
3958# endif
3959
3960# ifdef FEAT_MOUSE_GPM
3961 if (!use_xterm_mouse()
3962# ifdef FEAT_GUI
3963 && !gui.in_use
3964# endif
3965 )
Bram Moolenaarbedf0912019-05-04 16:58:45 +02003966 set_mouse_termcode(KS_GPM_MOUSE,
3967 (char_u *)IF_EB("\033MG", ESC_STR "MG"));
3968 else
3969 del_mouse_termcode(KS_GPM_MOUSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003970# endif
3971
Bram Moolenaar3577c6f2008-06-24 21:16:56 +00003972# ifdef FEAT_SYSMOUSE
3973 if (!use_xterm_mouse()
3974# ifdef FEAT_GUI
3975 && !gui.in_use
3976# endif
3977 )
3978 set_mouse_termcode(KS_MOUSE, (char_u *)IF_EB("\033MS", ESC_STR "MS"));
3979# endif
3980
Bram Moolenaar071d4272004-06-13 20:20:40 +00003981# ifdef FEAT_MOUSE_JSB
Bram Moolenaar0f873732019-12-05 20:28:46 +01003982 // Conflicts with xterm mouse: "\033[" and "\033[M" ???
Bram Moolenaar071d4272004-06-13 20:20:40 +00003983 if (!use_xterm_mouse()
3984# ifdef FEAT_GUI
3985 && !gui.in_use
3986# endif
3987 )
3988 set_mouse_termcode(KS_JSBTERM_MOUSE,
3989 (char_u *)IF_EB("\033[0~zw", ESC_STR "[0~zw"));
3990 else
3991 del_mouse_termcode(KS_JSBTERM_MOUSE);
3992# endif
3993
3994# ifdef FEAT_MOUSE_NET
Bram Moolenaar0f873732019-12-05 20:28:46 +01003995 // There is no conflict, but one may type "ESC }" from Insert mode. Don't
3996 // define it in the GUI or when using an xterm.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003997 if (!use_xterm_mouse()
3998# ifdef FEAT_GUI
3999 && !gui.in_use
4000# endif
4001 )
4002 set_mouse_termcode(KS_NETTERM_MOUSE,
4003 (char_u *)IF_EB("\033}", ESC_STR "}"));
4004 else
4005 del_mouse_termcode(KS_NETTERM_MOUSE);
4006# endif
4007
4008# ifdef FEAT_MOUSE_DEC
Bram Moolenaar0f873732019-12-05 20:28:46 +01004009 // Conflicts with xterm mouse: "\033[" and "\033[M"
Bram Moolenaarcbc17d62014-05-22 21:22:19 +02004010 if (!use_xterm_mouse()
Bram Moolenaar071d4272004-06-13 20:20:40 +00004011# ifdef FEAT_GUI
4012 && !gui.in_use
4013# endif
4014 )
Bram Moolenaarbc7aa852005-03-06 23:38:09 +00004015 set_mouse_termcode(KS_DEC_MOUSE, (char_u *)(term_is_8bit(T_NAME)
4016 ? IF_EB("\233", CSI_STR) : IF_EB("\033[", ESC_STR "[")));
Bram Moolenaar071d4272004-06-13 20:20:40 +00004017 else
4018 del_mouse_termcode(KS_DEC_MOUSE);
4019# endif
4020# ifdef FEAT_MOUSE_PTERM
Bram Moolenaar0f873732019-12-05 20:28:46 +01004021 // same conflict as the dec mouse
Bram Moolenaarcbc17d62014-05-22 21:22:19 +02004022 if (!use_xterm_mouse()
Bram Moolenaar071d4272004-06-13 20:20:40 +00004023# ifdef FEAT_GUI
4024 && !gui.in_use
4025# endif
4026 )
4027 set_mouse_termcode(KS_PTERM_MOUSE,
4028 (char_u *) IF_EB("\033[", ESC_STR "["));
4029 else
4030 del_mouse_termcode(KS_PTERM_MOUSE);
4031# endif
Bram Moolenaarc8427482011-10-20 21:09:35 +02004032# ifdef FEAT_MOUSE_URXVT
Bram Moolenaarcbc17d62014-05-22 21:22:19 +02004033 if (use_xterm_mouse() == 3
Bram Moolenaarc8427482011-10-20 21:09:35 +02004034# ifdef FEAT_GUI
4035 && !gui.in_use
4036# endif
4037 )
4038 {
4039 set_mouse_termcode(KS_URXVT_MOUSE, (char_u *)(term_is_8bit(T_NAME)
Bram Moolenaara529ce02017-06-22 22:37:57 +02004040 ? IF_EB("\233*M", CSI_STR "*M")
4041 : IF_EB("\033[*M", ESC_STR "[*M")));
Bram Moolenaarc8427482011-10-20 21:09:35 +02004042
4043 if (*p_mouse != NUL)
4044 {
4045 mch_setmouse(FALSE);
4046 setmouse();
4047 }
4048 }
4049 else
4050 del_mouse_termcode(KS_URXVT_MOUSE);
4051# endif
Bram Moolenaar2b9578f2012-08-15 16:21:32 +02004052 if (use_xterm_mouse() == 4
Bram Moolenaar2ace1bd2019-03-22 12:03:30 +01004053# ifdef FEAT_GUI
Bram Moolenaar2b9578f2012-08-15 16:21:32 +02004054 && !gui.in_use
Bram Moolenaar2ace1bd2019-03-22 12:03:30 +01004055# endif
Bram Moolenaar2b9578f2012-08-15 16:21:32 +02004056 )
4057 {
4058 set_mouse_termcode(KS_SGR_MOUSE, (char_u *)(term_is_8bit(T_NAME)
Bram Moolenaara529ce02017-06-22 22:37:57 +02004059 ? IF_EB("\233<*M", CSI_STR "<*M")
4060 : IF_EB("\033[<*M", ESC_STR "[<*M")));
4061
4062 set_mouse_termcode(KS_SGR_MOUSE_RELEASE, (char_u *)(term_is_8bit(T_NAME)
4063 ? IF_EB("\233<*m", CSI_STR "<*m")
4064 : IF_EB("\033[<*m", ESC_STR "[<*m")));
Bram Moolenaar2b9578f2012-08-15 16:21:32 +02004065
4066 if (*p_mouse != NUL)
4067 {
4068 mch_setmouse(FALSE);
4069 setmouse();
4070 }
4071 }
4072 else
Bram Moolenaara529ce02017-06-22 22:37:57 +02004073 {
Bram Moolenaar2b9578f2012-08-15 16:21:32 +02004074 del_mouse_termcode(KS_SGR_MOUSE);
Bram Moolenaara529ce02017-06-22 22:37:57 +02004075 del_mouse_termcode(KS_SGR_MOUSE_RELEASE);
4076 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004077}
Bram Moolenaar071d4272004-06-13 20:20:40 +00004078
Bram Moolenaar071d4272004-06-13 20:20:40 +00004079#ifndef VMS
4080
4081/*
4082 * Try to get the current window size:
4083 * 1. with an ioctl(), most accurate method
4084 * 2. from the environment variables LINES and COLUMNS
4085 * 3. from the termcap
4086 * 4. keep using the old values
4087 * Return OK when size could be determined, FAIL otherwise.
4088 */
4089 int
Bram Moolenaar05540972016-01-30 20:31:25 +01004090mch_get_shellsize(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004091{
4092 long rows = 0;
4093 long columns = 0;
4094 char_u *p;
4095
4096 /*
Bram Moolenaar071d4272004-06-13 20:20:40 +00004097 * 1. try using an ioctl. It is the most accurate method.
4098 *
4099 * Try using TIOCGWINSZ first, some systems that have it also define
4100 * TIOCGSIZE but don't have a struct ttysize.
4101 */
4102# ifdef TIOCGWINSZ
4103 {
4104 struct winsize ws;
4105 int fd = 1;
4106
Bram Moolenaar0f873732019-12-05 20:28:46 +01004107 // When stdout is not a tty, use stdin for the ioctl().
Bram Moolenaar071d4272004-06-13 20:20:40 +00004108 if (!isatty(fd) && isatty(read_cmd_fd))
4109 fd = read_cmd_fd;
4110 if (ioctl(fd, TIOCGWINSZ, &ws) == 0)
4111 {
4112 columns = ws.ws_col;
4113 rows = ws.ws_row;
4114 }
4115 }
Bram Moolenaar0f873732019-12-05 20:28:46 +01004116# else // TIOCGWINSZ
Bram Moolenaar071d4272004-06-13 20:20:40 +00004117# ifdef TIOCGSIZE
4118 {
4119 struct ttysize ts;
4120 int fd = 1;
4121
Bram Moolenaar0f873732019-12-05 20:28:46 +01004122 // When stdout is not a tty, use stdin for the ioctl().
Bram Moolenaar071d4272004-06-13 20:20:40 +00004123 if (!isatty(fd) && isatty(read_cmd_fd))
4124 fd = read_cmd_fd;
4125 if (ioctl(fd, TIOCGSIZE, &ts) == 0)
4126 {
4127 columns = ts.ts_cols;
4128 rows = ts.ts_lines;
4129 }
4130 }
Bram Moolenaar0f873732019-12-05 20:28:46 +01004131# endif // TIOCGSIZE
4132# endif // TIOCGWINSZ
Bram Moolenaar071d4272004-06-13 20:20:40 +00004133
4134 /*
4135 * 2. get size from environment
Bram Moolenaar4399ef42005-02-12 14:29:27 +00004136 * When being POSIX compliant ('|' flag in 'cpoptions') this overrules
4137 * the ioctl() values!
Bram Moolenaar071d4272004-06-13 20:20:40 +00004138 */
Bram Moolenaar4399ef42005-02-12 14:29:27 +00004139 if (columns == 0 || rows == 0 || vim_strchr(p_cpo, CPO_TSIZE) != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004140 {
4141 if ((p = (char_u *)getenv("LINES")))
4142 rows = atoi((char *)p);
4143 if ((p = (char_u *)getenv("COLUMNS")))
4144 columns = atoi((char *)p);
4145 }
4146
4147#ifdef HAVE_TGETENT
4148 /*
4149 * 3. try reading "co" and "li" entries from termcap
4150 */
4151 if (columns == 0 || rows == 0)
4152 getlinecol(&columns, &rows);
4153#endif
4154
4155 /*
4156 * 4. If everything fails, use the old values
4157 */
4158 if (columns <= 0 || rows <= 0)
4159 return FAIL;
4160
4161 Rows = rows;
4162 Columns = columns;
Bram Moolenaare057d402013-06-30 17:51:51 +02004163 limit_screen_size();
Bram Moolenaar071d4272004-06-13 20:20:40 +00004164 return OK;
4165}
4166
Bram Moolenaarb13501f2017-07-22 22:32:56 +02004167#if defined(FEAT_TERMINAL) || defined(PROTO)
4168/*
4169 * Report the windows size "rows" and "cols" to tty "fd".
4170 */
4171 int
4172mch_report_winsize(int fd, int rows, int cols)
4173{
Bram Moolenaar1ecc5e42019-01-26 15:12:55 +01004174 int tty_fd;
4175 int retval = -1;
Bram Moolenaarb13501f2017-07-22 22:32:56 +02004176
Bram Moolenaar1ecc5e42019-01-26 15:12:55 +01004177 tty_fd = get_tty_fd(fd);
4178 if (tty_fd >= 0)
Bram Moolenaarb13501f2017-07-22 22:32:56 +02004179 {
Bram Moolenaar1ecc5e42019-01-26 15:12:55 +01004180# if defined(TIOCSWINSZ)
4181 struct winsize ws;
Bram Moolenaarb13501f2017-07-22 22:32:56 +02004182
Bram Moolenaar1ecc5e42019-01-26 15:12:55 +01004183 ws.ws_col = cols;
4184 ws.ws_row = rows;
4185 ws.ws_xpixel = cols * 5;
4186 ws.ws_ypixel = rows * 10;
4187 retval = ioctl(tty_fd, TIOCSWINSZ, &ws);
4188 ch_log(NULL, "ioctl(TIOCSWINSZ) %s",
4189 retval == 0 ? "success" : "failed");
4190# elif defined(TIOCSSIZE)
4191 struct ttysize ts;
4192
4193 ts.ts_cols = cols;
4194 ts.ts_lines = rows;
4195 retval = ioctl(tty_fd, TIOCSSIZE, &ts);
4196 ch_log(NULL, "ioctl(TIOCSSIZE) %s",
4197 retval == 0 ? "success" : "failed");
Bram Moolenaarb13501f2017-07-22 22:32:56 +02004198# endif
Bram Moolenaar1ecc5e42019-01-26 15:12:55 +01004199 if (tty_fd != fd)
4200 close(tty_fd);
4201 }
4202 return retval == 0 ? OK : FAIL;
Bram Moolenaarb13501f2017-07-22 22:32:56 +02004203}
4204#endif
4205
Bram Moolenaar071d4272004-06-13 20:20:40 +00004206/*
4207 * Try to set the window size to Rows and Columns.
4208 */
4209 void
Bram Moolenaar05540972016-01-30 20:31:25 +01004210mch_set_shellsize(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004211{
4212 if (*T_CWS)
4213 {
4214 /*
4215 * NOTE: if you get an error here that term_set_winsize() is
4216 * undefined, check the output of configure. It could probably not
4217 * find a ncurses, termcap or termlib library.
4218 */
4219 term_set_winsize((int)Rows, (int)Columns);
4220 out_flush();
Bram Moolenaar0f873732019-12-05 20:28:46 +01004221 screen_start(); // don't know where cursor is now
Bram Moolenaar071d4272004-06-13 20:20:40 +00004222 }
4223}
4224
Bram Moolenaar0f873732019-12-05 20:28:46 +01004225#endif // VMS
Bram Moolenaar071d4272004-06-13 20:20:40 +00004226
4227/*
4228 * Rows and/or Columns has changed.
4229 */
4230 void
Bram Moolenaar05540972016-01-30 20:31:25 +01004231mch_new_shellsize(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004232{
Bram Moolenaar0f873732019-12-05 20:28:46 +01004233 // Nothing to do.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004234}
4235
Bram Moolenaar205b8862011-09-07 15:04:31 +02004236/*
4237 * Wait for process "child" to end.
4238 * Return "child" if it exited properly, <= 0 on error.
4239 */
4240 static pid_t
Bram Moolenaar05540972016-01-30 20:31:25 +01004241wait4pid(pid_t child, waitstatus *status)
Bram Moolenaar205b8862011-09-07 15:04:31 +02004242{
4243 pid_t wait_pid = 0;
Bram Moolenaar0abe0522016-08-28 16:53:12 +02004244 long delay_msec = 1;
Bram Moolenaar205b8862011-09-07 15:04:31 +02004245
4246 while (wait_pid != child)
4247 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01004248 // When compiled with Python threads are probably used, in which case
4249 // wait() sometimes hangs for no obvious reason. Use waitpid()
4250 // instead and loop (like the GUI). Also needed for other interfaces,
4251 // they might call system().
Bram Moolenaar6be120e2012-04-20 15:55:16 +02004252# ifdef __NeXT__
Bram Moolenaar205b8862011-09-07 15:04:31 +02004253 wait_pid = wait4(child, status, WNOHANG, (struct rusage *)0);
Bram Moolenaar6be120e2012-04-20 15:55:16 +02004254# else
Bram Moolenaar205b8862011-09-07 15:04:31 +02004255 wait_pid = waitpid(child, status, WNOHANG);
Bram Moolenaar6be120e2012-04-20 15:55:16 +02004256# endif
Bram Moolenaar205b8862011-09-07 15:04:31 +02004257 if (wait_pid == 0)
4258 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01004259 // Wait for 1 to 10 msec before trying again.
Bram Moolenaar0981c872020-08-23 14:28:37 +02004260 mch_delay(delay_msec, MCH_DELAY_IGNOREINPUT | MCH_DELAY_SETTMODE);
Bram Moolenaar0abe0522016-08-28 16:53:12 +02004261 if (++delay_msec > 10)
4262 delay_msec = 10;
Bram Moolenaar205b8862011-09-07 15:04:31 +02004263 continue;
4264 }
Bram Moolenaar205b8862011-09-07 15:04:31 +02004265 if (wait_pid <= 0
4266# ifdef ECHILD
4267 && errno == ECHILD
4268# endif
4269 )
4270 break;
4271 }
4272 return wait_pid;
4273}
4274
Bram Moolenaar509ce2a2016-03-11 22:52:15 +01004275#if !defined(USE_SYSTEM) || defined(FEAT_JOB_CHANNEL)
Bram Moolenaar7da34602017-08-01 17:14:21 +02004276/*
4277 * Set the environment for a child process.
4278 */
Bram Moolenaar6463ca22016-02-13 17:04:46 +01004279 static void
Bram Moolenaar493359e2018-06-12 20:25:52 +02004280set_child_environment(
4281 long rows,
4282 long columns,
4283 char *term,
4284 int is_terminal UNUSED)
Bram Moolenaar6463ca22016-02-13 17:04:46 +01004285{
4286# ifdef HAVE_SETENV
4287 char envbuf[50];
4288# else
Bram Moolenaar5f7e7bd2017-07-22 14:08:43 +02004289 static char envbuf_Term[30];
Bram Moolenaar6463ca22016-02-13 17:04:46 +01004290 static char envbuf_Rows[20];
Bram Moolenaar58556cd2017-07-20 23:04:46 +02004291 static char envbuf_Lines[20];
Bram Moolenaar6463ca22016-02-13 17:04:46 +01004292 static char envbuf_Columns[20];
Bram Moolenaarb7a8dfe2017-07-22 20:33:05 +02004293 static char envbuf_Colors[20];
Bram Moolenaar493359e2018-06-12 20:25:52 +02004294# ifdef FEAT_TERMINAL
Bram Moolenaard7a137f2018-06-12 18:05:24 +02004295 static char envbuf_Version[20];
Bram Moolenaar493359e2018-06-12 20:25:52 +02004296# endif
Bram Moolenaar2a4f06f2017-08-01 18:44:29 +02004297# ifdef FEAT_CLIENTSERVER
Bram Moolenaar7da34602017-08-01 17:14:21 +02004298 static char envbuf_Servername[60];
Bram Moolenaar2a4f06f2017-08-01 18:44:29 +02004299# endif
Bram Moolenaar6463ca22016-02-13 17:04:46 +01004300# endif
4301
Bram Moolenaar6463ca22016-02-13 17:04:46 +01004302# ifdef HAVE_SETENV
Bram Moolenaar58556cd2017-07-20 23:04:46 +02004303 setenv("TERM", term, 1);
4304 sprintf((char *)envbuf, "%ld", rows);
Bram Moolenaar6463ca22016-02-13 17:04:46 +01004305 setenv("ROWS", (char *)envbuf, 1);
Bram Moolenaar58556cd2017-07-20 23:04:46 +02004306 sprintf((char *)envbuf, "%ld", rows);
Bram Moolenaar6463ca22016-02-13 17:04:46 +01004307 setenv("LINES", (char *)envbuf, 1);
Bram Moolenaar58556cd2017-07-20 23:04:46 +02004308 sprintf((char *)envbuf, "%ld", columns);
Bram Moolenaar6463ca22016-02-13 17:04:46 +01004309 setenv("COLUMNS", (char *)envbuf, 1);
Bram Moolenaar759d8152020-04-26 16:52:49 +02004310 sprintf((char *)envbuf, "%d", t_colors);
Bram Moolenaarb7a8dfe2017-07-22 20:33:05 +02004311 setenv("COLORS", (char *)envbuf, 1);
Bram Moolenaar493359e2018-06-12 20:25:52 +02004312# ifdef FEAT_TERMINAL
4313 if (is_terminal)
4314 {
Bram Moolenaar5ecdf962018-06-13 20:49:50 +02004315 sprintf((char *)envbuf, "%ld", (long)get_vim_var_nr(VV_VERSION));
Bram Moolenaar493359e2018-06-12 20:25:52 +02004316 setenv("VIM_TERMINAL", (char *)envbuf, 1);
4317 }
4318# endif
Bram Moolenaar2a4f06f2017-08-01 18:44:29 +02004319# ifdef FEAT_CLIENTSERVER
Bram Moolenaar7da34602017-08-01 17:14:21 +02004320 setenv("VIM_SERVERNAME", serverName == NULL ? "" : (char *)serverName, 1);
Bram Moolenaar2a4f06f2017-08-01 18:44:29 +02004321# endif
Bram Moolenaar6463ca22016-02-13 17:04:46 +01004322# else
4323 /*
4324 * Putenv does not copy the string, it has to remain valid.
4325 * Use a static array to avoid losing allocated memory.
Bram Moolenaar7da34602017-08-01 17:14:21 +02004326 * This won't work well when running multiple children...
Bram Moolenaar6463ca22016-02-13 17:04:46 +01004327 */
Bram Moolenaar58556cd2017-07-20 23:04:46 +02004328 vim_snprintf(envbuf_Term, sizeof(envbuf_Term), "TERM=%s", term);
4329 putenv(envbuf_Term);
4330 vim_snprintf(envbuf_Rows, sizeof(envbuf_Rows), "ROWS=%ld", rows);
Bram Moolenaar6463ca22016-02-13 17:04:46 +01004331 putenv(envbuf_Rows);
Bram Moolenaar58556cd2017-07-20 23:04:46 +02004332 vim_snprintf(envbuf_Lines, sizeof(envbuf_Lines), "LINES=%ld", rows);
4333 putenv(envbuf_Lines);
4334 vim_snprintf(envbuf_Columns, sizeof(envbuf_Columns),
4335 "COLUMNS=%ld", columns);
Bram Moolenaar6463ca22016-02-13 17:04:46 +01004336 putenv(envbuf_Columns);
Bram Moolenaaraffc8fd2020-04-28 21:58:29 +02004337 vim_snprintf(envbuf_Colors, sizeof(envbuf_Colors), "COLORS=%ld", t_colors);
Bram Moolenaarb7a8dfe2017-07-22 20:33:05 +02004338 putenv(envbuf_Colors);
Bram Moolenaar493359e2018-06-12 20:25:52 +02004339# ifdef FEAT_TERMINAL
4340 if (is_terminal)
4341 {
4342 vim_snprintf(envbuf_Version, sizeof(envbuf_Version),
Bram Moolenaar5ecdf962018-06-13 20:49:50 +02004343 "VIM_TERMINAL=%ld", (long)get_vim_var_nr(VV_VERSION));
Bram Moolenaar493359e2018-06-12 20:25:52 +02004344 putenv(envbuf_Version);
4345 }
4346# endif
Bram Moolenaar2a4f06f2017-08-01 18:44:29 +02004347# ifdef FEAT_CLIENTSERVER
Bram Moolenaar7da34602017-08-01 17:14:21 +02004348 vim_snprintf(envbuf_Servername, sizeof(envbuf_Servername),
4349 "VIM_SERVERNAME=%s", serverName == NULL ? "" : (char *)serverName);
4350 putenv(envbuf_Servername);
Bram Moolenaar2a4f06f2017-08-01 18:44:29 +02004351# endif
Bram Moolenaar6463ca22016-02-13 17:04:46 +01004352# endif
4353}
Bram Moolenaar58556cd2017-07-20 23:04:46 +02004354
4355 static void
Bram Moolenaar493359e2018-06-12 20:25:52 +02004356set_default_child_environment(int is_terminal)
Bram Moolenaar58556cd2017-07-20 23:04:46 +02004357{
Bram Moolenaar493359e2018-06-12 20:25:52 +02004358 set_child_environment(Rows, Columns, "dumb", is_terminal);
Bram Moolenaar58556cd2017-07-20 23:04:46 +02004359}
Bram Moolenaar6463ca22016-02-13 17:04:46 +01004360#endif
4361
Bram Moolenaar5a1feb82017-07-22 18:04:08 +02004362#if defined(FEAT_GUI) || defined(FEAT_JOB_CHANNEL)
Bram Moolenaar979e8c52017-08-01 15:08:07 +02004363/*
4364 * Open a PTY, with FD for the master and slave side.
4365 * When failing "pty_master_fd" and "pty_slave_fd" are -1.
Bram Moolenaar59386482019-02-10 22:43:46 +01004366 * When successful both file descriptors are stored and the allocated pty name
4367 * is stored in both "*name1" and "*name2".
Bram Moolenaar979e8c52017-08-01 15:08:07 +02004368 */
Bram Moolenaar5a1feb82017-07-22 18:04:08 +02004369 static void
Bram Moolenaar59386482019-02-10 22:43:46 +01004370open_pty(int *pty_master_fd, int *pty_slave_fd, char_u **name1, char_u **name2)
Bram Moolenaar5a1feb82017-07-22 18:04:08 +02004371{
4372 char *tty_name;
4373
Bram Moolenaar59386482019-02-10 22:43:46 +01004374 if (name1 != NULL)
4375 *name1 = NULL;
4376 if (name2 != NULL)
4377 *name2 = NULL;
4378
Bram Moolenaar1ecc5e42019-01-26 15:12:55 +01004379 *pty_master_fd = mch_openpty(&tty_name); // open pty
Bram Moolenaar5a1feb82017-07-22 18:04:08 +02004380 if (*pty_master_fd >= 0)
4381 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01004382 // Leaving out O_NOCTTY may lead to waitpid() always returning
4383 // 0 on Mac OS X 10.7 thereby causing freezes. Let's assume
4384 // adding O_NOCTTY always works when defined.
Bram Moolenaar5a1feb82017-07-22 18:04:08 +02004385#ifdef O_NOCTTY
4386 *pty_slave_fd = open(tty_name, O_RDWR | O_NOCTTY | O_EXTRA, 0);
4387#else
4388 *pty_slave_fd = open(tty_name, O_RDWR | O_EXTRA, 0);
4389#endif
4390 if (*pty_slave_fd < 0)
4391 {
4392 close(*pty_master_fd);
4393 *pty_master_fd = -1;
4394 }
Bram Moolenaar59386482019-02-10 22:43:46 +01004395 else
4396 {
4397 if (name1 != NULL)
4398 *name1 = vim_strsave((char_u *)tty_name);
4399 if (name2 != NULL)
4400 *name2 = vim_strsave((char_u *)tty_name);
4401 }
Bram Moolenaar5a1feb82017-07-22 18:04:08 +02004402 }
4403}
4404#endif
4405
Bram Moolenaarfae42832017-08-01 22:24:26 +02004406/*
4407 * Send SIGINT to a child process if "c" is an interrupt character.
4408 */
Bram Moolenaar840d16f2019-09-10 21:27:18 +02004409 static void
Bram Moolenaarfae42832017-08-01 22:24:26 +02004410may_send_sigint(int c UNUSED, pid_t pid UNUSED, pid_t wpid UNUSED)
4411{
4412# ifdef SIGINT
4413 if (c == Ctrl_C || c == intr_char)
4414 {
4415# ifdef HAVE_SETSID
4416 kill(-pid, SIGINT);
4417# else
4418 kill(0, SIGINT);
4419# endif
4420 if (wpid > 0)
4421 kill(wpid, SIGINT);
4422 }
4423# endif
4424}
4425
Bram Moolenaar197c6b72019-11-03 23:37:12 +01004426#if !defined(USE_SYSTEM) || defined(FEAT_TERMINAL) || defined(PROTO)
Bram Moolenaar13568252018-03-16 20:46:58 +01004427
Bram Moolenaar0f873732019-12-05 20:28:46 +01004428/*
4429 * Parse "cmd" and return the result in "argvp" which is an allocated array of
4430 * pointers, the last one is NULL.
4431 * The "sh_tofree" and "shcf_tofree" must be later freed by the caller.
4432 */
Bram Moolenaar197c6b72019-11-03 23:37:12 +01004433 int
4434unix_build_argv(
Bram Moolenaar13568252018-03-16 20:46:58 +01004435 char_u *cmd,
4436 char ***argvp,
4437 char_u **sh_tofree,
4438 char_u **shcf_tofree)
4439{
4440 char **argv = NULL;
4441 int argc;
4442
4443 *sh_tofree = vim_strsave(p_sh);
Bram Moolenaar0f873732019-12-05 20:28:46 +01004444 if (*sh_tofree == NULL) // out of memory
Bram Moolenaar13568252018-03-16 20:46:58 +01004445 return FAIL;
4446
4447 if (mch_parse_cmd(*sh_tofree, TRUE, &argv, &argc) == FAIL)
4448 return FAIL;
4449 *argvp = argv;
4450
4451 if (cmd != NULL)
4452 {
4453 char_u *s;
4454 char_u *p;
4455
4456 if (extra_shell_arg != NULL)
4457 argv[argc++] = (char *)extra_shell_arg;
4458
Bram Moolenaar0f873732019-12-05 20:28:46 +01004459 // Break 'shellcmdflag' into white separated parts. This doesn't
4460 // handle quoted strings, they are very unlikely to appear.
Bram Moolenaar964b3742019-05-24 18:54:09 +02004461 *shcf_tofree = alloc(STRLEN(p_shcf) + 1);
Bram Moolenaar0f873732019-12-05 20:28:46 +01004462 if (*shcf_tofree == NULL) // out of memory
Bram Moolenaar13568252018-03-16 20:46:58 +01004463 return FAIL;
4464 s = *shcf_tofree;
4465 p = p_shcf;
4466 while (*p != NUL)
4467 {
4468 argv[argc++] = (char *)s;
4469 while (*p && *p != ' ' && *p != TAB)
4470 *s++ = *p++;
4471 *s++ = NUL;
4472 p = skipwhite(p);
4473 }
4474
4475 argv[argc++] = (char *)cmd;
4476 }
4477 argv[argc] = NULL;
4478 return OK;
4479}
4480#endif
4481
4482#if defined(FEAT_GUI) && defined(FEAT_TERMINAL)
4483/*
4484 * Use a terminal window to run a shell command in.
4485 */
4486 static int
4487mch_call_shell_terminal(
4488 char_u *cmd,
Bram Moolenaar0f873732019-12-05 20:28:46 +01004489 int options UNUSED) // SHELL_*, see vim.h
Bram Moolenaar13568252018-03-16 20:46:58 +01004490{
4491 jobopt_T opt;
4492 char **argv = NULL;
4493 char_u *tofree1 = NULL;
4494 char_u *tofree2 = NULL;
4495 int retval = -1;
4496 buf_T *buf;
Bram Moolenaarf9c38832018-06-19 19:59:20 +02004497 job_T *job;
Bram Moolenaar13568252018-03-16 20:46:58 +01004498 aco_save_T aco;
Bram Moolenaar0f873732019-12-05 20:28:46 +01004499 oparg_T oa; // operator arguments
Bram Moolenaar13568252018-03-16 20:46:58 +01004500
Bram Moolenaar197c6b72019-11-03 23:37:12 +01004501 if (unix_build_argv(cmd, &argv, &tofree1, &tofree2) == FAIL)
Bram Moolenaar13568252018-03-16 20:46:58 +01004502 goto theend;
4503
4504 init_job_options(&opt);
4505 ch_log(NULL, "starting terminal for system command '%s'", cmd);
4506 buf = term_start(NULL, argv, &opt, TERM_START_SYSTEM);
Bram Moolenaarf9c38832018-06-19 19:59:20 +02004507 if (buf == NULL)
4508 goto theend;
4509
4510 job = term_getjob(buf->b_term);
4511 ++job->jv_refcount;
Bram Moolenaar13568252018-03-16 20:46:58 +01004512
Bram Moolenaar0f873732019-12-05 20:28:46 +01004513 // Find a window to make "buf" curbuf.
Bram Moolenaar13568252018-03-16 20:46:58 +01004514 aucmd_prepbuf(&aco, buf);
4515
4516 clear_oparg(&oa);
4517 while (term_use_loop())
4518 {
4519 if (oa.op_type == OP_NOP && oa.regname == NUL && !VIsual_active)
4520 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01004521 // If terminal_loop() returns OK we got a key that is handled
4522 // in Normal model. We don't do redrawing anyway.
Bram Moolenaar13568252018-03-16 20:46:58 +01004523 if (terminal_loop(TRUE) == OK)
4524 normal_cmd(&oa, TRUE);
4525 }
4526 else
4527 normal_cmd(&oa, TRUE);
4528 }
Bram Moolenaarf9c38832018-06-19 19:59:20 +02004529 retval = job->jv_exitval;
Bram Moolenaar13568252018-03-16 20:46:58 +01004530 ch_log(NULL, "system command finished");
4531
Bram Moolenaarf9c38832018-06-19 19:59:20 +02004532 job_unref(job);
4533
Bram Moolenaar0f873732019-12-05 20:28:46 +01004534 // restore curwin/curbuf and a few other things
Bram Moolenaar13568252018-03-16 20:46:58 +01004535 aucmd_restbuf(&aco);
4536
4537 wait_return(TRUE);
4538 do_buffer(DOBUF_WIPE, DOBUF_FIRST, FORWARD, buf->b_fnum, TRUE);
4539
4540theend:
4541 vim_free(argv);
4542 vim_free(tofree1);
4543 vim_free(tofree2);
4544 return retval;
4545}
4546#endif
4547
4548#ifdef USE_SYSTEM
4549/*
4550 * Use system() to start the shell: simple but slow.
4551 */
4552 static int
4553mch_call_shell_system(
Bram Moolenaar05540972016-01-30 20:31:25 +01004554 char_u *cmd,
Bram Moolenaar0f873732019-12-05 20:28:46 +01004555 int options) // SHELL_*, see vim.h
Bram Moolenaar071d4272004-06-13 20:20:40 +00004556{
4557#ifdef VMS
4558 char *ifn = NULL;
4559 char *ofn = NULL;
4560#endif
Bram Moolenaar26e86442020-05-17 14:06:16 +02004561 tmode_T tmode = cur_tmode;
Bram Moolenaar0f873732019-12-05 20:28:46 +01004562 char_u *newcmd; // only needed for unix
Bram Moolenaarde5e2c22016-11-04 20:35:31 +01004563 int x;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004564
4565 out_flush();
4566
4567 if (options & SHELL_COOKED)
Bram Moolenaar0f873732019-12-05 20:28:46 +01004568 settmode(TMODE_COOK); // set to normal mode
Bram Moolenaar071d4272004-06-13 20:20:40 +00004569
Bram Moolenaar62b42182010-09-21 22:09:37 +02004570# if defined(FEAT_CLIPBOARD) && defined(FEAT_X11)
Bram Moolenaar1a0316c2013-03-13 17:50:25 +01004571 save_clipboard();
Bram Moolenaar62b42182010-09-21 22:09:37 +02004572 loose_clipboard();
4573# endif
4574
Bram Moolenaar071d4272004-06-13 20:20:40 +00004575 if (cmd == NULL)
4576 x = system((char *)p_sh);
4577 else
4578 {
Bram Moolenaara06ecab2016-07-16 14:47:36 +02004579# ifdef VMS
Bram Moolenaar071d4272004-06-13 20:20:40 +00004580 if (ofn = strchr((char *)cmd, '>'))
4581 *ofn++ = '\0';
4582 if (ifn = strchr((char *)cmd, '<'))
4583 {
4584 char *p;
4585
4586 *ifn++ = '\0';
Bram Moolenaar0f873732019-12-05 20:28:46 +01004587 p = strchr(ifn,' '); // chop off any trailing spaces
Bram Moolenaar071d4272004-06-13 20:20:40 +00004588 if (p)
4589 *p = '\0';
4590 }
4591 if (ofn)
4592 x = vms_sys((char *)cmd, ofn, ifn);
4593 else
4594 x = system((char *)cmd);
Bram Moolenaara06ecab2016-07-16 14:47:36 +02004595# else
Bram Moolenaar18a4ba22019-05-24 19:39:03 +02004596 newcmd = alloc(STRLEN(p_sh)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004597 + (extra_shell_arg == NULL ? 0 : STRLEN(extra_shell_arg))
Bram Moolenaar18a4ba22019-05-24 19:39:03 +02004598 + STRLEN(p_shcf) + STRLEN(cmd) + 4);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004599 if (newcmd == NULL)
4600 x = 0;
4601 else
4602 {
4603 sprintf((char *)newcmd, "%s %s %s %s", p_sh,
4604 extra_shell_arg == NULL ? "" : (char *)extra_shell_arg,
4605 (char *)p_shcf,
4606 (char *)cmd);
4607 x = system((char *)newcmd);
4608 vim_free(newcmd);
4609 }
Bram Moolenaara06ecab2016-07-16 14:47:36 +02004610# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004611 }
4612# ifdef VMS
4613 x = vms_sys_status(x);
4614# endif
4615 if (emsg_silent)
4616 ;
4617 else if (x == 127)
Bram Moolenaar32526b32019-01-19 17:43:09 +01004618 msg_puts(_("\nCannot execute shell sh\n"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00004619 else if (x && !(options & SHELL_SILENT))
4620 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01004621 msg_puts(_("\nshell returned "));
Bram Moolenaar071d4272004-06-13 20:20:40 +00004622 msg_outnum((long)x);
4623 msg_putchar('\n');
4624 }
4625
4626 if (tmode == TMODE_RAW)
Bram Moolenaar3b1f18f2020-05-16 23:15:08 +02004627 {
4628 // The shell may have messed with the mode, always set it.
4629 cur_tmode = TMODE_UNKNOWN;
Bram Moolenaar0f873732019-12-05 20:28:46 +01004630 settmode(TMODE_RAW); // set to raw mode
Bram Moolenaar3b1f18f2020-05-16 23:15:08 +02004631 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004632 resettitle();
Bram Moolenaar1a0316c2013-03-13 17:50:25 +01004633# if defined(FEAT_CLIPBOARD) && defined(FEAT_X11)
4634 restore_clipboard();
4635# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004636 return x;
Bram Moolenaar13568252018-03-16 20:46:58 +01004637}
Bram Moolenaar071d4272004-06-13 20:20:40 +00004638
Bram Moolenaar0f873732019-12-05 20:28:46 +01004639#else // USE_SYSTEM
Bram Moolenaar071d4272004-06-13 20:20:40 +00004640
Bram Moolenaar0f873732019-12-05 20:28:46 +01004641# define EXEC_FAILED 122 // Exit code when shell didn't execute. Don't use
4642 // 127, some shells use that already
4643# define OPEN_NULL_FAILED 123 // Exit code if /dev/null can't be opened
Bram Moolenaar071d4272004-06-13 20:20:40 +00004644
Bram Moolenaar13568252018-03-16 20:46:58 +01004645/*
4646 * Don't use system(), use fork()/exec().
4647 */
4648 static int
4649mch_call_shell_fork(
4650 char_u *cmd,
Bram Moolenaar0f873732019-12-05 20:28:46 +01004651 int options) // SHELL_*, see vim.h
Bram Moolenaar13568252018-03-16 20:46:58 +01004652{
Bram Moolenaar26e86442020-05-17 14:06:16 +02004653 tmode_T tmode = cur_tmode;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004654 pid_t pid;
Bram Moolenaardf177f62005-02-22 08:39:57 +00004655 pid_t wpid = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004656 pid_t wait_pid = 0;
4657# ifdef HAVE_UNION_WAIT
4658 union wait status;
4659# else
4660 int status = -1;
4661# endif
4662 int retval = -1;
4663 char **argv = NULL;
Bram Moolenaar13568252018-03-16 20:46:58 +01004664 char_u *tofree1 = NULL;
4665 char_u *tofree2 = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004666 int i;
Bram Moolenaar0f873732019-12-05 20:28:46 +01004667 int pty_master_fd = -1; // for pty's
Bram Moolenaardf177f62005-02-22 08:39:57 +00004668# ifdef FEAT_GUI
Bram Moolenaar071d4272004-06-13 20:20:40 +00004669 int pty_slave_fd = -1;
Bram Moolenaardf177f62005-02-22 08:39:57 +00004670# endif
Bram Moolenaar0f873732019-12-05 20:28:46 +01004671 int fd_toshell[2]; // for pipes
Bram Moolenaar071d4272004-06-13 20:20:40 +00004672 int fd_fromshell[2];
4673 int pipe_error = FALSE;
Bram Moolenaar0f873732019-12-05 20:28:46 +01004674 int did_settmode = FALSE; // settmode(TMODE_RAW) called
Bram Moolenaar071d4272004-06-13 20:20:40 +00004675
4676 out_flush();
4677 if (options & SHELL_COOKED)
Bram Moolenaar0f873732019-12-05 20:28:46 +01004678 settmode(TMODE_COOK); // set to normal mode
Bram Moolenaar3b1f18f2020-05-16 23:15:08 +02004679 if (tmode == TMODE_RAW)
4680 // The shell may have messed with the mode, always set it later.
4681 cur_tmode = TMODE_UNKNOWN;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004682
Bram Moolenaar197c6b72019-11-03 23:37:12 +01004683 if (unix_build_argv(cmd, &argv, &tofree1, &tofree2) == FAIL)
Bram Moolenaar835dc632016-02-07 14:27:38 +01004684 goto error;
Bram Moolenaarea35ef62011-08-04 22:59:28 +02004685
Bram Moolenaar071d4272004-06-13 20:20:40 +00004686 /*
Bram Moolenaardf177f62005-02-22 08:39:57 +00004687 * For the GUI, when writing the output into the buffer and when reading
4688 * input from the buffer: Try using a pseudo-tty to get the stdin/stdout
4689 * of the executed command into the Vim window. Or use a pipe.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004690 */
Bram Moolenaardf177f62005-02-22 08:39:57 +00004691 if ((options & (SHELL_READ|SHELL_WRITE))
4692# ifdef FEAT_GUI
4693 || (gui.in_use && show_shell_mess)
4694# endif
4695 )
Bram Moolenaar071d4272004-06-13 20:20:40 +00004696 {
Bram Moolenaardf177f62005-02-22 08:39:57 +00004697# ifdef FEAT_GUI
Bram Moolenaar071d4272004-06-13 20:20:40 +00004698 /*
4699 * Try to open a master pty.
4700 * If this works, open the slave pty.
4701 * If the slave can't be opened, close the master pty.
4702 */
Bram Moolenaardf177f62005-02-22 08:39:57 +00004703 if (p_guipty && !(options & (SHELL_READ|SHELL_WRITE)))
Bram Moolenaar59386482019-02-10 22:43:46 +01004704 open_pty(&pty_master_fd, &pty_slave_fd, NULL, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004705 /*
4706 * If not opening a pty or it didn't work, try using pipes.
4707 */
4708 if (pty_master_fd < 0)
Bram Moolenaardf177f62005-02-22 08:39:57 +00004709# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004710 {
4711 pipe_error = (pipe(fd_toshell) < 0);
Bram Moolenaar0f873732019-12-05 20:28:46 +01004712 if (!pipe_error) // pipe create OK
Bram Moolenaar071d4272004-06-13 20:20:40 +00004713 {
4714 pipe_error = (pipe(fd_fromshell) < 0);
Bram Moolenaar0f873732019-12-05 20:28:46 +01004715 if (pipe_error) // pipe create failed
Bram Moolenaar071d4272004-06-13 20:20:40 +00004716 {
4717 close(fd_toshell[0]);
4718 close(fd_toshell[1]);
4719 }
4720 }
4721 if (pipe_error)
4722 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01004723 msg_puts(_("\nCannot create pipes\n"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00004724 out_flush();
4725 }
4726 }
4727 }
4728
Bram Moolenaar0f873732019-12-05 20:28:46 +01004729 if (!pipe_error) // pty or pipe opened or not used
Bram Moolenaar071d4272004-06-13 20:20:40 +00004730 {
Bram Moolenaarbb09ceb2016-10-18 16:27:23 +02004731 SIGSET_DECL(curset)
Bram Moolenaarbb09ceb2016-10-18 16:27:23 +02004732 BLOCK_SIGNALS(&curset);
Bram Moolenaar0f873732019-12-05 20:28:46 +01004733 pid = fork(); // maybe we should use vfork()
Bram Moolenaarbb09ceb2016-10-18 16:27:23 +02004734 if (pid == -1)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004735 {
Bram Moolenaarbb09ceb2016-10-18 16:27:23 +02004736 UNBLOCK_SIGNALS(&curset);
4737
Bram Moolenaar32526b32019-01-19 17:43:09 +01004738 msg_puts(_("\nCannot fork\n"));
Bram Moolenaardf177f62005-02-22 08:39:57 +00004739 if ((options & (SHELL_READ|SHELL_WRITE))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004740# ifdef FEAT_GUI
Bram Moolenaardf177f62005-02-22 08:39:57 +00004741 || (gui.in_use && show_shell_mess)
4742# endif
4743 )
Bram Moolenaar071d4272004-06-13 20:20:40 +00004744 {
Bram Moolenaardf177f62005-02-22 08:39:57 +00004745# ifdef FEAT_GUI
Bram Moolenaar0f873732019-12-05 20:28:46 +01004746 if (pty_master_fd >= 0) // close the pseudo tty
Bram Moolenaar071d4272004-06-13 20:20:40 +00004747 {
4748 close(pty_master_fd);
4749 close(pty_slave_fd);
4750 }
Bram Moolenaar0f873732019-12-05 20:28:46 +01004751 else // close the pipes
Bram Moolenaardf177f62005-02-22 08:39:57 +00004752# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004753 {
4754 close(fd_toshell[0]);
4755 close(fd_toshell[1]);
4756 close(fd_fromshell[0]);
4757 close(fd_fromshell[1]);
4758 }
4759 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004760 }
Bram Moolenaar0f873732019-12-05 20:28:46 +01004761 else if (pid == 0) // child
Bram Moolenaar071d4272004-06-13 20:20:40 +00004762 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01004763 reset_signals(); // handle signals normally
Bram Moolenaarbb09ceb2016-10-18 16:27:23 +02004764 UNBLOCK_SIGNALS(&curset);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004765
Bram Moolenaar819524702018-02-27 19:10:00 +01004766# ifdef FEAT_JOB_CHANNEL
4767 if (ch_log_active())
Bram Moolenaar76603ba2020-08-30 17:24:37 +02004768 {
4769 ch_log(NULL, "closing channel log in the child process");
Bram Moolenaar819524702018-02-27 19:10:00 +01004770 ch_logfile((char_u *)"", (char_u *)"");
Bram Moolenaar76603ba2020-08-30 17:24:37 +02004771 }
Bram Moolenaar819524702018-02-27 19:10:00 +01004772# endif
4773
Bram Moolenaar071d4272004-06-13 20:20:40 +00004774 if (!show_shell_mess || (options & SHELL_EXPAND))
4775 {
4776 int fd;
4777
4778 /*
4779 * Don't want to show any message from the shell. Can't just
4780 * close stdout and stderr though, because some systems will
4781 * break if you try to write to them after that, so we must
4782 * use dup() to replace them with something else -- webb
4783 * Connect stdin to /dev/null too, so ":n `cat`" doesn't hang,
4784 * waiting for input.
4785 */
4786 fd = open("/dev/null", O_RDWR | O_EXTRA, 0);
4787 fclose(stdin);
4788 fclose(stdout);
4789 fclose(stderr);
4790
4791 /*
4792 * If any of these open()'s and dup()'s fail, we just continue
4793 * anyway. It's not fatal, and on most systems it will make
4794 * no difference at all. On a few it will cause the execvp()
4795 * to exit with a non-zero status even when the completion
4796 * could be done, which is nothing too serious. If the open()
4797 * or dup() failed we'd just do the same thing ourselves
4798 * anyway -- webb
4799 */
4800 if (fd >= 0)
4801 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01004802 vim_ignored = dup(fd); // To replace stdin (fd 0)
4803 vim_ignored = dup(fd); // To replace stdout (fd 1)
4804 vim_ignored = dup(fd); // To replace stderr (fd 2)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004805
Bram Moolenaar0f873732019-12-05 20:28:46 +01004806 // Don't need this now that we've duplicated it
Bram Moolenaar071d4272004-06-13 20:20:40 +00004807 close(fd);
4808 }
4809 }
Bram Moolenaardf177f62005-02-22 08:39:57 +00004810 else if ((options & (SHELL_READ|SHELL_WRITE))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004811# ifdef FEAT_GUI
Bram Moolenaardf177f62005-02-22 08:39:57 +00004812 || gui.in_use
4813# endif
4814 )
Bram Moolenaar071d4272004-06-13 20:20:40 +00004815 {
4816
Bram Moolenaardf177f62005-02-22 08:39:57 +00004817# ifdef HAVE_SETSID
Bram Moolenaar0f873732019-12-05 20:28:46 +01004818 // Create our own process group, so that the child and all its
4819 // children can be kill()ed. Don't do this when using pipes,
4820 // because stdin is not a tty, we would lose /dev/tty.
Bram Moolenaar12033fb2005-12-16 21:49:31 +00004821 if (p_stmp)
Bram Moolenaar07256082009-02-04 13:19:42 +00004822 {
Bram Moolenaar12033fb2005-12-16 21:49:31 +00004823 (void)setsid();
Bram Moolenaar07256082009-02-04 13:19:42 +00004824# if defined(SIGHUP)
Bram Moolenaar0f873732019-12-05 20:28:46 +01004825 // When doing "!xterm&" and 'shell' is bash: the shell
4826 // will exit and send SIGHUP to all processes in its
4827 // group, killing the just started process. Ignore SIGHUP
4828 // to avoid that. (suggested by Simon Schubert)
Bram Moolenaar07256082009-02-04 13:19:42 +00004829 signal(SIGHUP, SIG_IGN);
4830# endif
4831 }
Bram Moolenaardf177f62005-02-22 08:39:57 +00004832# endif
4833# ifdef FEAT_GUI
Bram Moolenaar12033fb2005-12-16 21:49:31 +00004834 if (pty_slave_fd >= 0)
4835 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01004836 // push stream discipline modules
Bram Moolenaar12033fb2005-12-16 21:49:31 +00004837 if (options & SHELL_COOKED)
Bram Moolenaar1ecc5e42019-01-26 15:12:55 +01004838 setup_slavepty(pty_slave_fd);
Bram Moolenaarfff10d92021-10-13 10:05:30 +01004839# ifdef TIOCSCTTY
4840 // Try to become controlling tty (probably doesn't work,
4841 // unless run by root)
4842 ioctl(pty_slave_fd, TIOCSCTTY, (char *)NULL);
4843# endif
Bram Moolenaar12033fb2005-12-16 21:49:31 +00004844 }
Bram Moolenaardf177f62005-02-22 08:39:57 +00004845# endif
Bram Moolenaar493359e2018-06-12 20:25:52 +02004846 set_default_child_environment(FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004847
Bram Moolenaara5792f52005-11-23 21:25:05 +00004848 /*
4849 * stderr is only redirected when using the GUI, so that a
4850 * program like gpg can still access the terminal to get a
4851 * passphrase using stderr.
4852 */
Bram Moolenaardf177f62005-02-22 08:39:57 +00004853# ifdef FEAT_GUI
Bram Moolenaar071d4272004-06-13 20:20:40 +00004854 if (pty_master_fd >= 0)
4855 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01004856 close(pty_master_fd); // close master side of pty
Bram Moolenaar071d4272004-06-13 20:20:40 +00004857
Bram Moolenaar0f873732019-12-05 20:28:46 +01004858 // set up stdin/stdout/stderr for the child
Bram Moolenaar071d4272004-06-13 20:20:40 +00004859 close(0);
Bram Moolenaar42335f52018-09-13 15:33:43 +02004860 vim_ignored = dup(pty_slave_fd);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004861 close(1);
Bram Moolenaar42335f52018-09-13 15:33:43 +02004862 vim_ignored = dup(pty_slave_fd);
Bram Moolenaara5792f52005-11-23 21:25:05 +00004863 if (gui.in_use)
4864 {
4865 close(2);
Bram Moolenaar42335f52018-09-13 15:33:43 +02004866 vim_ignored = dup(pty_slave_fd);
Bram Moolenaara5792f52005-11-23 21:25:05 +00004867 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004868
Bram Moolenaar0f873732019-12-05 20:28:46 +01004869 close(pty_slave_fd); // has been dupped, close it now
Bram Moolenaar071d4272004-06-13 20:20:40 +00004870 }
4871 else
Bram Moolenaardf177f62005-02-22 08:39:57 +00004872# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004873 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01004874 // set up stdin for the child
Bram Moolenaar071d4272004-06-13 20:20:40 +00004875 close(fd_toshell[1]);
4876 close(0);
Bram Moolenaar42335f52018-09-13 15:33:43 +02004877 vim_ignored = dup(fd_toshell[0]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004878 close(fd_toshell[0]);
4879
Bram Moolenaar0f873732019-12-05 20:28:46 +01004880 // set up stdout for the child
Bram Moolenaar071d4272004-06-13 20:20:40 +00004881 close(fd_fromshell[0]);
4882 close(1);
Bram Moolenaar42335f52018-09-13 15:33:43 +02004883 vim_ignored = dup(fd_fromshell[1]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004884 close(fd_fromshell[1]);
4885
Bram Moolenaara5792f52005-11-23 21:25:05 +00004886# ifdef FEAT_GUI
4887 if (gui.in_use)
4888 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01004889 // set up stderr for the child
Bram Moolenaara5792f52005-11-23 21:25:05 +00004890 close(2);
Bram Moolenaar42335f52018-09-13 15:33:43 +02004891 vim_ignored = dup(1);
Bram Moolenaara5792f52005-11-23 21:25:05 +00004892 }
4893# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004894 }
4895 }
Bram Moolenaardf177f62005-02-22 08:39:57 +00004896
Bram Moolenaar071d4272004-06-13 20:20:40 +00004897 /*
4898 * There is no type cast for the argv, because the type may be
4899 * different on different machines. This may cause a warning
4900 * message with strict compilers, don't worry about it.
4901 * Call _exit() instead of exit() to avoid closing the connection
4902 * to the X server (esp. with GTK, which uses atexit()).
4903 */
4904 execvp(argv[0], argv);
Bram Moolenaar0f873732019-12-05 20:28:46 +01004905 _exit(EXEC_FAILED); // exec failed, return failure code
Bram Moolenaar071d4272004-06-13 20:20:40 +00004906 }
Bram Moolenaar0f873732019-12-05 20:28:46 +01004907 else // parent
Bram Moolenaar071d4272004-06-13 20:20:40 +00004908 {
4909 /*
4910 * While child is running, ignore terminating signals.
Bram Moolenaardf177f62005-02-22 08:39:57 +00004911 * Do catch CTRL-C, so that "got_int" is set.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004912 */
4913 catch_signals(SIG_IGN, SIG_ERR);
Bram Moolenaardf177f62005-02-22 08:39:57 +00004914 catch_int_signal();
Bram Moolenaarbb09ceb2016-10-18 16:27:23 +02004915 UNBLOCK_SIGNALS(&curset);
Bram Moolenaarc4d4ac22016-11-07 22:42:57 +01004916# ifdef FEAT_JOB_CHANNEL
4917 ++dont_check_job_ended;
4918# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004919 /*
4920 * For the GUI we redirect stdin, stdout and stderr to our window.
Bram Moolenaardf177f62005-02-22 08:39:57 +00004921 * This is also used to pipe stdin/stdout to/from the external
4922 * command.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004923 */
Bram Moolenaardf177f62005-02-22 08:39:57 +00004924 if ((options & (SHELL_READ|SHELL_WRITE))
4925# ifdef FEAT_GUI
4926 || (gui.in_use && show_shell_mess)
4927# endif
4928 )
Bram Moolenaar071d4272004-06-13 20:20:40 +00004929 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01004930# define BUFLEN 100 // length for buffer, pseudo tty limit is 128
Bram Moolenaar071d4272004-06-13 20:20:40 +00004931 char_u buffer[BUFLEN + 1];
Bram Moolenaar0f873732019-12-05 20:28:46 +01004932 int buffer_off = 0; // valid bytes in buffer[]
4933 char_u ta_buf[BUFLEN + 1]; // TypeAHead
4934 int ta_len = 0; // valid bytes in ta_buf[]
Bram Moolenaar071d4272004-06-13 20:20:40 +00004935 int len;
4936 int p_more_save;
4937 int old_State;
4938 int c;
4939 int toshell_fd;
4940 int fromshell_fd;
Bram Moolenaardf177f62005-02-22 08:39:57 +00004941 garray_T ga;
4942 int noread_cnt;
Bram Moolenaar1ac56c22019-01-17 22:28:22 +01004943# ifdef ELAPSED_FUNC
4944 elapsed_T start_tv;
Bram Moolenaarb3dc8fd2009-02-22 01:52:59 +00004945# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004946
Bram Moolenaardf177f62005-02-22 08:39:57 +00004947# ifdef FEAT_GUI
Bram Moolenaar071d4272004-06-13 20:20:40 +00004948 if (pty_master_fd >= 0)
4949 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00004950 fromshell_fd = pty_master_fd;
4951 toshell_fd = dup(pty_master_fd);
4952 }
4953 else
Bram Moolenaardf177f62005-02-22 08:39:57 +00004954# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004955 {
4956 close(fd_toshell[0]);
4957 close(fd_fromshell[1]);
4958 toshell_fd = fd_toshell[1];
4959 fromshell_fd = fd_fromshell[0];
4960 }
4961
4962 /*
4963 * Write to the child if there are typed characters.
4964 * Read from the child if there are characters available.
4965 * Repeat the reading a few times if more characters are
4966 * available. Need to check for typed keys now and then, but
4967 * not too often (delays when no chars are available).
4968 * This loop is quit if no characters can be read from the pty
4969 * (WaitForChar detected special condition), or there are no
4970 * characters available and the child has exited.
4971 * Only check if the child has exited when there is no more
4972 * output. The child may exit before all the output has
4973 * been printed.
4974 *
4975 * Currently this busy loops!
4976 * This can probably dead-lock when the write blocks!
4977 */
4978 p_more_save = p_more;
4979 p_more = FALSE;
4980 old_State = State;
Bram Moolenaar0f873732019-12-05 20:28:46 +01004981 State = EXTERNCMD; // don't redraw at window resize
Bram Moolenaar071d4272004-06-13 20:20:40 +00004982
Bram Moolenaar12033fb2005-12-16 21:49:31 +00004983 if ((options & SHELL_WRITE) && toshell_fd >= 0)
Bram Moolenaardf177f62005-02-22 08:39:57 +00004984 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01004985 // Fork a process that will write the lines to the
4986 // external program.
Bram Moolenaardf177f62005-02-22 08:39:57 +00004987 if ((wpid = fork()) == -1)
4988 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01004989 msg_puts(_("\nCannot fork\n"));
Bram Moolenaardf177f62005-02-22 08:39:57 +00004990 }
Bram Moolenaar0f873732019-12-05 20:28:46 +01004991 else if (wpid == 0) // child
Bram Moolenaardf177f62005-02-22 08:39:57 +00004992 {
4993 linenr_T lnum = curbuf->b_op_start.lnum;
4994 int written = 0;
Bram Moolenaar89d40322006-08-29 15:30:07 +00004995 char_u *lp = ml_get(lnum);
Bram Moolenaardf177f62005-02-22 08:39:57 +00004996 size_t l;
4997
Bram Moolenaar8cd06ca2005-02-28 22:44:58 +00004998 close(fromshell_fd);
Bram Moolenaardf177f62005-02-22 08:39:57 +00004999 for (;;)
5000 {
Bram Moolenaar89d40322006-08-29 15:30:07 +00005001 l = STRLEN(lp + written);
Bram Moolenaardf177f62005-02-22 08:39:57 +00005002 if (l == 0)
5003 len = 0;
Bram Moolenaar89d40322006-08-29 15:30:07 +00005004 else if (lp[written] == NL)
Bram Moolenaar0f873732019-12-05 20:28:46 +01005005 // NL -> NUL translation
Bram Moolenaardf177f62005-02-22 08:39:57 +00005006 len = write(toshell_fd, "", (size_t)1);
5007 else
5008 {
Bram Moolenaar70b2a562012-01-10 22:26:17 +01005009 char_u *s = vim_strchr(lp + written, NL);
5010
Bram Moolenaar89d40322006-08-29 15:30:07 +00005011 len = write(toshell_fd, (char *)lp + written,
Bram Moolenaar78a15312009-05-15 19:33:18 +00005012 s == NULL ? l
5013 : (size_t)(s - (lp + written)));
Bram Moolenaardf177f62005-02-22 08:39:57 +00005014 }
Bram Moolenaar78a15312009-05-15 19:33:18 +00005015 if (len == (int)l)
Bram Moolenaardf177f62005-02-22 08:39:57 +00005016 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01005017 // Finished a line, add a NL, unless this line
5018 // should not have one.
Bram Moolenaardf177f62005-02-22 08:39:57 +00005019 if (lnum != curbuf->b_op_end.lnum
Bram Moolenaar34d72d42015-07-17 14:18:08 +02005020 || (!curbuf->b_p_bin
5021 && curbuf->b_p_fixeol)
Bram Moolenaarcab35ad2011-02-15 17:39:22 +01005022 || (lnum != curbuf->b_no_eol_lnum
Bram Moolenaardf177f62005-02-22 08:39:57 +00005023 && (lnum !=
5024 curbuf->b_ml.ml_line_count
5025 || curbuf->b_p_eol)))
Bram Moolenaar42335f52018-09-13 15:33:43 +02005026 vim_ignored = write(toshell_fd, "\n",
Bram Moolenaarfe86f2d2008-11-28 20:29:07 +00005027 (size_t)1);
Bram Moolenaardf177f62005-02-22 08:39:57 +00005028 ++lnum;
5029 if (lnum > curbuf->b_op_end.lnum)
5030 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01005031 // finished all the lines, close pipe
Bram Moolenaardf177f62005-02-22 08:39:57 +00005032 close(toshell_fd);
5033 toshell_fd = -1;
5034 break;
5035 }
Bram Moolenaar89d40322006-08-29 15:30:07 +00005036 lp = ml_get(lnum);
Bram Moolenaardf177f62005-02-22 08:39:57 +00005037 written = 0;
5038 }
5039 else if (len > 0)
5040 written += len;
5041 }
5042 _exit(0);
5043 }
Bram Moolenaar0f873732019-12-05 20:28:46 +01005044 else // parent
Bram Moolenaardf177f62005-02-22 08:39:57 +00005045 {
5046 close(toshell_fd);
5047 toshell_fd = -1;
5048 }
5049 }
5050
5051 if (options & SHELL_READ)
5052 ga_init2(&ga, 1, BUFLEN);
5053
5054 noread_cnt = 0;
Bram Moolenaar833eb1d2016-11-24 17:22:50 +01005055# ifdef ELAPSED_FUNC
5056 ELAPSED_INIT(start_tv);
Bram Moolenaarb3dc8fd2009-02-22 01:52:59 +00005057# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005058 for (;;)
5059 {
5060 /*
5061 * Check if keys have been typed, write them to the child
Bram Moolenaar5b962cf2005-12-12 21:58:40 +00005062 * if there are any.
5063 * Don't do this if we are expanding wild cards (would eat
5064 * typeahead).
5065 * Don't do this when filtering and terminal is in cooked
5066 * mode, the shell command will handle the I/O. Avoids
5067 * that a typed password is echoed for ssh or gpg command.
Bram Moolenaar12033fb2005-12-16 21:49:31 +00005068 * Don't get characters when the child has already
5069 * finished (wait_pid == 0).
Bram Moolenaardf177f62005-02-22 08:39:57 +00005070 * Don't read characters unless we didn't get output for a
Bram Moolenaarb3dc8fd2009-02-22 01:52:59 +00005071 * while (noread_cnt > 4), avoids that ":r !ls" eats
5072 * typeahead.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005073 */
5074 len = 0;
5075 if (!(options & SHELL_EXPAND)
Bram Moolenaar5b962cf2005-12-12 21:58:40 +00005076 && ((options &
5077 (SHELL_READ|SHELL_WRITE|SHELL_COOKED))
5078 != (SHELL_READ|SHELL_WRITE|SHELL_COOKED)
Bram Moolenaarb3dc8fd2009-02-22 01:52:59 +00005079# ifdef FEAT_GUI
Bram Moolenaar5b962cf2005-12-12 21:58:40 +00005080 || gui.in_use
Bram Moolenaarb3dc8fd2009-02-22 01:52:59 +00005081# endif
Bram Moolenaar5b962cf2005-12-12 21:58:40 +00005082 )
Bram Moolenaar12033fb2005-12-16 21:49:31 +00005083 && wait_pid == 0
Bram Moolenaarb3dc8fd2009-02-22 01:52:59 +00005084 && (ta_len > 0 || noread_cnt > 4))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005085 {
Bram Moolenaarb3dc8fd2009-02-22 01:52:59 +00005086 if (ta_len == 0)
5087 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01005088 // Get extra characters when we don't have any.
5089 // Reset the counter and timer.
Bram Moolenaarb3dc8fd2009-02-22 01:52:59 +00005090 noread_cnt = 0;
Bram Moolenaar833eb1d2016-11-24 17:22:50 +01005091# ifdef ELAPSED_FUNC
5092 ELAPSED_INIT(start_tv);
Bram Moolenaarb3dc8fd2009-02-22 01:52:59 +00005093# endif
5094 len = ui_inchar(ta_buf, BUFLEN, 10L, 0);
5095 }
5096 if (ta_len > 0 || len > 0)
5097 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00005098 /*
5099 * For pipes:
5100 * Check for CTRL-C: send interrupt signal to child.
5101 * Check for CTRL-D: EOF, close pipe to child.
5102 */
5103 if (len == 1 && (pty_master_fd < 0 || cmd != NULL))
5104 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00005105 /*
5106 * Send SIGINT to the child's group or all
5107 * processes in our group.
5108 */
Bram Moolenaarfae42832017-08-01 22:24:26 +02005109 may_send_sigint(ta_buf[ta_len], pid, wpid);
5110
Bram Moolenaar071d4272004-06-13 20:20:40 +00005111 if (pty_master_fd < 0 && toshell_fd >= 0
5112 && ta_buf[ta_len] == Ctrl_D)
5113 {
5114 close(toshell_fd);
5115 toshell_fd = -1;
5116 }
5117 }
5118
Bram Moolenaarf4140482020-02-15 23:06:45 +01005119 term_replace_bs_del_keycode(ta_buf, ta_len, len);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005120
5121 /*
5122 * For pipes: echo the typed characters.
5123 * For a pty this does not seem to work.
5124 */
5125 if (pty_master_fd < 0)
5126 {
5127 for (i = ta_len; i < ta_len + len; ++i)
5128 {
5129 if (ta_buf[i] == '\n' || ta_buf[i] == '\b')
5130 msg_putchar(ta_buf[i]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005131 else if (has_mbyte)
5132 {
Bram Moolenaar0fa313a2005-08-10 21:07:57 +00005133 int l = (*mb_ptr2len)(ta_buf + i);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005134
5135 msg_outtrans_len(ta_buf + i, l);
5136 i += l - 1;
5137 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005138 else
5139 msg_outtrans_len(ta_buf + i, 1);
5140 }
5141 windgoto(msg_row, msg_col);
5142 out_flush();
5143 }
5144
5145 ta_len += len;
5146
5147 /*
5148 * Write the characters to the child, unless EOF has
5149 * been typed for pipes. Write one character at a
Bram Moolenaare37d50a2008-08-06 17:06:04 +00005150 * time, to avoid losing too much typeahead.
Bram Moolenaardf177f62005-02-22 08:39:57 +00005151 * When writing buffer lines, drop the typed
5152 * characters (only check for CTRL-C).
Bram Moolenaar071d4272004-06-13 20:20:40 +00005153 */
Bram Moolenaardf177f62005-02-22 08:39:57 +00005154 if (options & SHELL_WRITE)
5155 ta_len = 0;
5156 else if (toshell_fd >= 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005157 {
5158 len = write(toshell_fd, (char *)ta_buf, (size_t)1);
5159 if (len > 0)
5160 {
5161 ta_len -= len;
5162 mch_memmove(ta_buf, ta_buf + len, ta_len);
5163 }
5164 }
Bram Moolenaarb3dc8fd2009-02-22 01:52:59 +00005165 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005166 }
5167
Bram Moolenaardf177f62005-02-22 08:39:57 +00005168 if (got_int)
5169 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01005170 // CTRL-C sends a signal to the child, we ignore it
5171 // ourselves
Bram Moolenaardf177f62005-02-22 08:39:57 +00005172# ifdef HAVE_SETSID
5173 kill(-pid, SIGINT);
5174# else
5175 kill(0, SIGINT);
5176# endif
5177 if (wpid > 0)
5178 kill(wpid, SIGINT);
5179 got_int = FALSE;
5180 }
5181
Bram Moolenaar071d4272004-06-13 20:20:40 +00005182 /*
5183 * Check if the child has any characters to be printed.
5184 * Read them and write them to our window. Repeat this as
5185 * long as there is something to do, avoid the 10ms wait
5186 * for mch_inchar(), or sending typeahead characters to
5187 * the external process.
5188 * TODO: This should handle escape sequences, compatible
5189 * to some terminal (vt52?).
5190 */
Bram Moolenaardf177f62005-02-22 08:39:57 +00005191 ++noread_cnt;
Bram Moolenaar8fdd7212016-03-26 19:41:48 +01005192 while (RealWaitForChar(fromshell_fd, 10L, NULL, NULL))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005193 {
Bram Moolenaar540fc6f2010-12-17 16:27:16 +01005194 len = read_eintr(fromshell_fd, buffer
Bram Moolenaar071d4272004-06-13 20:20:40 +00005195 + buffer_off, (size_t)(BUFLEN - buffer_off)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005196 );
Bram Moolenaar0f873732019-12-05 20:28:46 +01005197 if (len <= 0) // end of file or error
Bram Moolenaar071d4272004-06-13 20:20:40 +00005198 goto finished;
Bram Moolenaardf177f62005-02-22 08:39:57 +00005199
5200 noread_cnt = 0;
5201 if (options & SHELL_READ)
5202 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01005203 // Do NUL -> NL translation, append NL separated
5204 // lines to the current buffer.
Bram Moolenaardf177f62005-02-22 08:39:57 +00005205 for (i = 0; i < len; ++i)
5206 {
5207 if (buffer[i] == NL)
5208 append_ga_line(&ga);
5209 else if (buffer[i] == NUL)
5210 ga_append(&ga, NL);
5211 else
5212 ga_append(&ga, buffer[i]);
5213 }
5214 }
Bram Moolenaardf177f62005-02-22 08:39:57 +00005215 else if (has_mbyte)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005216 {
5217 int l;
Bram Moolenaara2150ac2018-03-17 13:15:17 +01005218 char_u *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005219
Bram Moolenaardf177f62005-02-22 08:39:57 +00005220 len += buffer_off;
5221 buffer[len] = NUL;
5222
Bram Moolenaar0f873732019-12-05 20:28:46 +01005223 // Check if the last character in buffer[] is
5224 // incomplete, keep these bytes for the next
5225 // round.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005226 for (p = buffer; p < buffer + len; p += l)
5227 {
Bram Moolenaard3c907b2016-08-17 21:32:09 +02005228 l = MB_CPTR2LEN(p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005229 if (l == 0)
Bram Moolenaar0f873732019-12-05 20:28:46 +01005230 l = 1; // NUL byte?
Bram Moolenaar071d4272004-06-13 20:20:40 +00005231 else if (MB_BYTE2LEN(*p) != l)
5232 break;
5233 }
Bram Moolenaar0f873732019-12-05 20:28:46 +01005234 if (p == buffer) // no complete character
Bram Moolenaar071d4272004-06-13 20:20:40 +00005235 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01005236 // avoid getting stuck at an illegal byte
Bram Moolenaar071d4272004-06-13 20:20:40 +00005237 if (len >= 12)
5238 ++p;
5239 else
5240 {
5241 buffer_off = len;
5242 continue;
5243 }
5244 }
5245 c = *p;
5246 *p = NUL;
Bram Moolenaar32526b32019-01-19 17:43:09 +01005247 msg_puts((char *)buffer);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005248 if (p < buffer + len)
5249 {
5250 *p = c;
5251 buffer_off = (buffer + len) - p;
5252 mch_memmove(buffer, p, buffer_off);
5253 continue;
5254 }
5255 buffer_off = 0;
5256 }
5257 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00005258 {
5259 buffer[len] = NUL;
Bram Moolenaar32526b32019-01-19 17:43:09 +01005260 msg_puts((char *)buffer);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005261 }
5262
5263 windgoto(msg_row, msg_col);
5264 cursor_on();
5265 out_flush();
5266 if (got_int)
5267 break;
Bram Moolenaarb3dc8fd2009-02-22 01:52:59 +00005268
Bram Moolenaar833eb1d2016-11-24 17:22:50 +01005269# ifdef ELAPSED_FUNC
Bram Moolenaar17fe5e12016-04-04 22:03:08 +02005270 if (wait_pid == 0)
Bram Moolenaarb3dc8fd2009-02-22 01:52:59 +00005271 {
Bram Moolenaar833eb1d2016-11-24 17:22:50 +01005272 long msec = ELAPSED_FUNC(start_tv);
Bram Moolenaarb3dc8fd2009-02-22 01:52:59 +00005273
Bram Moolenaar0f873732019-12-05 20:28:46 +01005274 // Avoid that we keep looping here without
5275 // checking for a CTRL-C for a long time. Don't
5276 // break out too often to avoid losing typeahead.
Bram Moolenaarb3dc8fd2009-02-22 01:52:59 +00005277 if (msec > 2000)
5278 {
5279 noread_cnt = 5;
5280 break;
5281 }
5282 }
5283# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005284 }
5285
Bram Moolenaar0f873732019-12-05 20:28:46 +01005286 // If we already detected the child has finished, continue
5287 // reading output for a short while. Some text may be
5288 // buffered.
Bram Moolenaar12033fb2005-12-16 21:49:31 +00005289 if (wait_pid == pid)
Bram Moolenaar17fe5e12016-04-04 22:03:08 +02005290 {
5291 if (noread_cnt < 5)
5292 continue;
Bram Moolenaar12033fb2005-12-16 21:49:31 +00005293 break;
Bram Moolenaar17fe5e12016-04-04 22:03:08 +02005294 }
Bram Moolenaar12033fb2005-12-16 21:49:31 +00005295
Bram Moolenaar071d4272004-06-13 20:20:40 +00005296 /*
5297 * Check if the child still exists, before checking for
Bram Moolenaare37d50a2008-08-06 17:06:04 +00005298 * typed characters (otherwise we would lose typeahead).
Bram Moolenaar071d4272004-06-13 20:20:40 +00005299 */
Bram Moolenaardf177f62005-02-22 08:39:57 +00005300# ifdef __NeXT__
Bram Moolenaar205b8862011-09-07 15:04:31 +02005301 wait_pid = wait4(pid, &status, WNOHANG, (struct rusage *)0);
Bram Moolenaardf177f62005-02-22 08:39:57 +00005302# else
Bram Moolenaar071d4272004-06-13 20:20:40 +00005303 wait_pid = waitpid(pid, &status, WNOHANG);
Bram Moolenaardf177f62005-02-22 08:39:57 +00005304# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005305 if ((wait_pid == (pid_t)-1 && errno == ECHILD)
5306 || (wait_pid == pid && WIFEXITED(status)))
5307 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01005308 // Don't break the loop yet, try reading more
5309 // characters from "fromshell_fd" first. When using
5310 // pipes there might still be something to read and
5311 // then we'll break the loop at the "break" above.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005312 wait_pid = pid;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005313 }
Bram Moolenaar12033fb2005-12-16 21:49:31 +00005314 else
5315 wait_pid = 0;
Bram Moolenaar090cfc12013-03-19 12:35:42 +01005316
Bram Moolenaar95a51352013-03-21 22:53:50 +01005317# if defined(FEAT_XCLIPBOARD) && defined(FEAT_X11)
Bram Moolenaar0f873732019-12-05 20:28:46 +01005318 // Handle any X events, e.g. serving the clipboard.
Bram Moolenaar090cfc12013-03-19 12:35:42 +01005319 clip_update();
5320# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005321 }
5322finished:
5323 p_more = p_more_save;
Bram Moolenaardf177f62005-02-22 08:39:57 +00005324 if (options & SHELL_READ)
5325 {
5326 if (ga.ga_len > 0)
5327 {
5328 append_ga_line(&ga);
Bram Moolenaar0f873732019-12-05 20:28:46 +01005329 // remember that the NL was missing
Bram Moolenaarcab35ad2011-02-15 17:39:22 +01005330 curbuf->b_no_eol_lnum = curwin->w_cursor.lnum;
Bram Moolenaardf177f62005-02-22 08:39:57 +00005331 }
5332 else
Bram Moolenaarcab35ad2011-02-15 17:39:22 +01005333 curbuf->b_no_eol_lnum = 0;
Bram Moolenaardf177f62005-02-22 08:39:57 +00005334 ga_clear(&ga);
5335 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005336
Bram Moolenaar071d4272004-06-13 20:20:40 +00005337 /*
5338 * Give all typeahead that wasn't used back to ui_inchar().
5339 */
5340 if (ta_len)
5341 ui_inchar_undo(ta_buf, ta_len);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005342 State = old_State;
5343 if (toshell_fd >= 0)
5344 close(toshell_fd);
5345 close(fromshell_fd);
5346 }
Bram Moolenaar95a51352013-03-21 22:53:50 +01005347# if defined(FEAT_XCLIPBOARD) && defined(FEAT_X11)
Bram Moolenaar090cfc12013-03-19 12:35:42 +01005348 else
5349 {
Bram Moolenaar0abe0522016-08-28 16:53:12 +02005350 long delay_msec = 1;
5351
Bram Moolenaar8a3da6a2020-12-08 19:18:37 +01005352 if (tmode == TMODE_RAW)
5353 // possibly disables modifyOtherKeys, so that the system
5354 // can recognize CTRL-C
5355 out_str(T_CTE);
Bram Moolenaar0981c872020-08-23 14:28:37 +02005356
Bram Moolenaar090cfc12013-03-19 12:35:42 +01005357 /*
5358 * Similar to the loop above, but only handle X events, no
5359 * I/O.
5360 */
5361 for (;;)
5362 {
5363 if (got_int)
5364 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01005365 // CTRL-C sends a signal to the child, we ignore it
5366 // ourselves
Bram Moolenaar090cfc12013-03-19 12:35:42 +01005367# ifdef HAVE_SETSID
5368 kill(-pid, SIGINT);
5369# else
5370 kill(0, SIGINT);
5371# endif
5372 got_int = FALSE;
5373 }
5374# ifdef __NeXT__
5375 wait_pid = wait4(pid, &status, WNOHANG, (struct rusage *)0);
5376# else
5377 wait_pid = waitpid(pid, &status, WNOHANG);
5378# endif
5379 if ((wait_pid == (pid_t)-1 && errno == ECHILD)
5380 || (wait_pid == pid && WIFEXITED(status)))
5381 {
5382 wait_pid = pid;
5383 break;
5384 }
5385
Bram Moolenaar0f873732019-12-05 20:28:46 +01005386 // Handle any X events, e.g. serving the clipboard.
Bram Moolenaar090cfc12013-03-19 12:35:42 +01005387 clip_update();
5388
Bram Moolenaar0f873732019-12-05 20:28:46 +01005389 // Wait for 1 to 10 msec. 1 is faster but gives the child
Bram Moolenaar0981c872020-08-23 14:28:37 +02005390 // less time, gradually wait longer.
5391 mch_delay(delay_msec,
5392 MCH_DELAY_IGNOREINPUT | MCH_DELAY_SETTMODE);
Bram Moolenaar0abe0522016-08-28 16:53:12 +02005393 if (++delay_msec > 10)
5394 delay_msec = 10;
Bram Moolenaar090cfc12013-03-19 12:35:42 +01005395 }
Bram Moolenaar0981c872020-08-23 14:28:37 +02005396
Bram Moolenaar8a3da6a2020-12-08 19:18:37 +01005397 if (tmode == TMODE_RAW)
5398 // possibly enables modifyOtherKeys again
5399 out_str(T_CTI);
Bram Moolenaar090cfc12013-03-19 12:35:42 +01005400 }
5401# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005402
5403 /*
5404 * Wait until our child has exited.
5405 * Ignore wait() returning pids of other children and returning
5406 * because of some signal like SIGWINCH.
5407 * Don't wait if wait_pid was already set above, indicating the
5408 * child already exited.
5409 */
Bram Moolenaar205b8862011-09-07 15:04:31 +02005410 if (wait_pid != pid)
5411 wait_pid = wait4pid(pid, &status);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005412
Bram Moolenaar624891f2010-10-13 16:22:09 +02005413# ifdef FEAT_GUI
Bram Moolenaar0f873732019-12-05 20:28:46 +01005414 // Close slave side of pty. Only do this after the child has
5415 // exited, otherwise the child may hang when it tries to write on
5416 // the pty.
Bram Moolenaar624891f2010-10-13 16:22:09 +02005417 if (pty_master_fd >= 0)
5418 close(pty_slave_fd);
5419# endif
5420
Bram Moolenaar0f873732019-12-05 20:28:46 +01005421 // Make sure the child that writes to the external program is
5422 // dead.
Bram Moolenaardf177f62005-02-22 08:39:57 +00005423 if (wpid > 0)
Bram Moolenaar205b8862011-09-07 15:04:31 +02005424 {
Bram Moolenaardf177f62005-02-22 08:39:57 +00005425 kill(wpid, SIGKILL);
Bram Moolenaar205b8862011-09-07 15:04:31 +02005426 wait4pid(wpid, NULL);
5427 }
Bram Moolenaardf177f62005-02-22 08:39:57 +00005428
Bram Moolenaarc4d4ac22016-11-07 22:42:57 +01005429# ifdef FEAT_JOB_CHANNEL
5430 --dont_check_job_ended;
5431# endif
5432
Bram Moolenaar071d4272004-06-13 20:20:40 +00005433 /*
5434 * Set to raw mode right now, otherwise a CTRL-C after
5435 * catch_signals() will kill Vim.
5436 */
5437 if (tmode == TMODE_RAW)
5438 settmode(TMODE_RAW);
5439 did_settmode = TRUE;
5440 set_signals();
5441
5442 if (WIFEXITED(status))
5443 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01005444 // LINTED avoid "bitwise operation on signed value"
Bram Moolenaar071d4272004-06-13 20:20:40 +00005445 retval = WEXITSTATUS(status);
Bram Moolenaar75676462013-01-30 14:55:42 +01005446 if (retval != 0 && !emsg_silent)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005447 {
5448 if (retval == EXEC_FAILED)
5449 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01005450 msg_puts(_("\nCannot execute shell "));
Bram Moolenaar071d4272004-06-13 20:20:40 +00005451 msg_outtrans(p_sh);
5452 msg_putchar('\n');
5453 }
5454 else if (!(options & SHELL_SILENT))
5455 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01005456 msg_puts(_("\nshell returned "));
Bram Moolenaar071d4272004-06-13 20:20:40 +00005457 msg_outnum((long)retval);
5458 msg_putchar('\n');
5459 }
5460 }
5461 }
5462 else
Bram Moolenaar32526b32019-01-19 17:43:09 +01005463 msg_puts(_("\nCommand terminated\n"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00005464 }
5465 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005466
5467error:
5468 if (!did_settmode)
5469 if (tmode == TMODE_RAW)
Bram Moolenaar0f873732019-12-05 20:28:46 +01005470 settmode(TMODE_RAW); // set to raw mode
Bram Moolenaar071d4272004-06-13 20:20:40 +00005471 resettitle();
Bram Moolenaar13568252018-03-16 20:46:58 +01005472 vim_free(argv);
5473 vim_free(tofree1);
5474 vim_free(tofree2);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005475
5476 return retval;
Bram Moolenaar13568252018-03-16 20:46:58 +01005477}
Bram Moolenaar0f873732019-12-05 20:28:46 +01005478#endif // USE_SYSTEM
Bram Moolenaar13568252018-03-16 20:46:58 +01005479
5480 int
5481mch_call_shell(
5482 char_u *cmd,
Bram Moolenaar0f873732019-12-05 20:28:46 +01005483 int options) // SHELL_*, see vim.h
Bram Moolenaar13568252018-03-16 20:46:58 +01005484{
5485#if defined(FEAT_GUI) && defined(FEAT_TERMINAL)
5486 if (gui.in_use && vim_strchr(p_go, GO_TERMINAL) != NULL)
5487 return mch_call_shell_terminal(cmd, options);
5488#endif
5489#ifdef USE_SYSTEM
5490 return mch_call_shell_system(cmd, options);
5491#else
5492 return mch_call_shell_fork(cmd, options);
5493#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005494}
5495
Bram Moolenaar509ce2a2016-03-11 22:52:15 +01005496#if defined(FEAT_JOB_CHANNEL) || defined(PROTO)
Bram Moolenaar835dc632016-02-07 14:27:38 +01005497 void
Bram Moolenaar493359e2018-06-12 20:25:52 +02005498mch_job_start(char **argv, job_T *job, jobopt_T *options, int is_terminal)
Bram Moolenaar835dc632016-02-07 14:27:38 +01005499{
Bram Moolenaar6463ca22016-02-13 17:04:46 +01005500 pid_t pid;
Bram Moolenaar0f873732019-12-05 20:28:46 +01005501 int fd_in[2] = {-1, -1}; // for stdin
5502 int fd_out[2] = {-1, -1}; // for stdout
5503 int fd_err[2] = {-1, -1}; // for stderr
Bram Moolenaar5a1feb82017-07-22 18:04:08 +02005504 int pty_master_fd = -1;
5505 int pty_slave_fd = -1;
Bram Moolenaar16eb4f82016-02-14 23:02:34 +01005506 channel_T *channel = NULL;
Bram Moolenaarf65333c2016-03-08 18:27:21 +01005507 int use_null_for_in = options->jo_io[PART_IN] == JIO_NULL;
5508 int use_null_for_out = options->jo_io[PART_OUT] == JIO_NULL;
5509 int use_null_for_err = options->jo_io[PART_ERR] == JIO_NULL;
Bram Moolenaarb69fccf2016-03-06 23:06:25 +01005510 int use_file_for_in = options->jo_io[PART_IN] == JIO_FILE;
Bram Moolenaare98d1212016-03-08 15:37:41 +01005511 int use_file_for_out = options->jo_io[PART_OUT] == JIO_FILE;
5512 int use_file_for_err = options->jo_io[PART_ERR] == JIO_FILE;
Bram Moolenaarb2412082017-08-20 18:09:14 +02005513 int use_buffer_for_in = options->jo_io[PART_IN] == JIO_BUFFER;
Bram Moolenaarc25558b2016-03-03 21:02:23 +01005514 int use_out_for_err = options->jo_io[PART_ERR] == JIO_OUT;
Bram Moolenaarbb09ceb2016-10-18 16:27:23 +02005515 SIGSET_DECL(curset)
Bram Moolenaar835dc632016-02-07 14:27:38 +01005516
Bram Moolenaarf65333c2016-03-08 18:27:21 +01005517 if (use_out_for_err && use_null_for_out)
5518 use_null_for_err = TRUE;
5519
Bram Moolenaar0f873732019-12-05 20:28:46 +01005520 // default is to fail
Bram Moolenaar6463ca22016-02-13 17:04:46 +01005521 job->jv_status = JOB_FAILED;
Bram Moolenaar6463ca22016-02-13 17:04:46 +01005522
Bram Moolenaarb2412082017-08-20 18:09:14 +02005523 if (options->jo_pty
5524 && (!(use_file_for_in || use_null_for_in)
Bram Moolenaar59386482019-02-10 22:43:46 +01005525 || !(use_file_for_out || use_null_for_out)
Bram Moolenaarb2412082017-08-20 18:09:14 +02005526 || !(use_out_for_err || use_file_for_err || use_null_for_err)))
Bram Moolenaar59386482019-02-10 22:43:46 +01005527 open_pty(&pty_master_fd, &pty_slave_fd,
5528 &job->jv_tty_out, &job->jv_tty_in);
Bram Moolenaar5a1feb82017-07-22 18:04:08 +02005529
Bram Moolenaar0f873732019-12-05 20:28:46 +01005530 // TODO: without the channel feature connect the child to /dev/null?
5531 // Open pipes for stdin, stdout, stderr.
Bram Moolenaarb69fccf2016-03-06 23:06:25 +01005532 if (use_file_for_in)
5533 {
5534 char_u *fname = options->jo_io_name[PART_IN];
5535
5536 fd_in[0] = mch_open((char *)fname, O_RDONLY, 0);
5537 if (fd_in[0] < 0)
5538 {
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00005539 semsg(_(e_cant_open_file_str), fname);
Bram Moolenaarb69fccf2016-03-06 23:06:25 +01005540 goto failed;
5541 }
5542 }
Bram Moolenaarb2412082017-08-20 18:09:14 +02005543 else
Bram Moolenaar0f873732019-12-05 20:28:46 +01005544 // When writing buffer lines to the input don't use the pty, so that
5545 // the pipe can be closed when all lines were written.
Bram Moolenaarb2412082017-08-20 18:09:14 +02005546 if (!use_null_for_in && (pty_master_fd < 0 || use_buffer_for_in)
5547 && pipe(fd_in) < 0)
5548 goto failed;
Bram Moolenaare98d1212016-03-08 15:37:41 +01005549
5550 if (use_file_for_out)
5551 {
5552 char_u *fname = options->jo_io_name[PART_OUT];
5553
5554 fd_out[1] = mch_open((char *)fname, O_WRONLY | O_CREAT | O_TRUNC, 0644);
5555 if (fd_out[1] < 0)
5556 {
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00005557 semsg(_(e_cant_open_file_str), fname);
Bram Moolenaare98d1212016-03-08 15:37:41 +01005558 goto failed;
5559 }
5560 }
Bram Moolenaar5a1feb82017-07-22 18:04:08 +02005561 else if (!use_null_for_out && pty_master_fd < 0 && pipe(fd_out) < 0)
Bram Moolenaarb69fccf2016-03-06 23:06:25 +01005562 goto failed;
Bram Moolenaare98d1212016-03-08 15:37:41 +01005563
5564 if (use_file_for_err)
5565 {
5566 char_u *fname = options->jo_io_name[PART_ERR];
5567
5568 fd_err[1] = mch_open((char *)fname, O_WRONLY | O_CREAT | O_TRUNC, 0600);
5569 if (fd_err[1] < 0)
5570 {
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00005571 semsg(_(e_cant_open_file_str), fname);
Bram Moolenaare98d1212016-03-08 15:37:41 +01005572 goto failed;
5573 }
5574 }
Bram Moolenaar5a1feb82017-07-22 18:04:08 +02005575 else if (!use_out_for_err && !use_null_for_err
5576 && pty_master_fd < 0 && pipe(fd_err) < 0)
Bram Moolenaar6463ca22016-02-13 17:04:46 +01005577 goto failed;
5578
Bram Moolenaarf65333c2016-03-08 18:27:21 +01005579 if (!use_null_for_in || !use_null_for_out || !use_null_for_err)
5580 {
Bram Moolenaarde279892016-03-11 22:19:44 +01005581 if (options->jo_set & JO_CHANNEL)
5582 {
5583 channel = options->jo_channel;
5584 if (channel != NULL)
5585 ++channel->ch_refcount;
5586 }
5587 else
5588 channel = add_channel();
Bram Moolenaarf65333c2016-03-08 18:27:21 +01005589 if (channel == NULL)
5590 goto failed;
Bram Moolenaarf3360612017-10-01 16:21:31 +02005591 if (job->jv_tty_out != NULL)
5592 ch_log(channel, "using pty %s on fd %d",
5593 job->jv_tty_out, pty_master_fd);
Bram Moolenaarf65333c2016-03-08 18:27:21 +01005594 }
Bram Moolenaar6463ca22016-02-13 17:04:46 +01005595
Bram Moolenaarbb09ceb2016-10-18 16:27:23 +02005596 BLOCK_SIGNALS(&curset);
Bram Moolenaar0f873732019-12-05 20:28:46 +01005597 pid = fork(); // maybe we should use vfork()
Bram Moolenaarbb09ceb2016-10-18 16:27:23 +02005598 if (pid == -1)
Bram Moolenaar835dc632016-02-07 14:27:38 +01005599 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01005600 // failed to fork
Bram Moolenaarbb09ceb2016-10-18 16:27:23 +02005601 UNBLOCK_SIGNALS(&curset);
Bram Moolenaar6463ca22016-02-13 17:04:46 +01005602 goto failed;
Bram Moolenaar835dc632016-02-07 14:27:38 +01005603 }
Bram Moolenaar6463ca22016-02-13 17:04:46 +01005604 if (pid == 0)
Bram Moolenaar835dc632016-02-07 14:27:38 +01005605 {
Bram Moolenaar4694a172016-04-21 14:05:23 +02005606 int null_fd = -1;
5607 int stderr_works = TRUE;
Bram Moolenaarf65333c2016-03-08 18:27:21 +01005608
Bram Moolenaar0f873732019-12-05 20:28:46 +01005609 // child
5610 reset_signals(); // handle signals normally
Bram Moolenaarbb09ceb2016-10-18 16:27:23 +02005611 UNBLOCK_SIGNALS(&curset);
Bram Moolenaar835dc632016-02-07 14:27:38 +01005612
Bram Moolenaar819524702018-02-27 19:10:00 +01005613# ifdef FEAT_JOB_CHANNEL
5614 if (ch_log_active())
Bram Moolenaar0f873732019-12-05 20:28:46 +01005615 // close the log file in the child
Bram Moolenaar819524702018-02-27 19:10:00 +01005616 ch_logfile((char_u *)"", (char_u *)"");
5617# endif
5618
Bram Moolenaar835dc632016-02-07 14:27:38 +01005619# ifdef HAVE_SETSID
Bram Moolenaar0f873732019-12-05 20:28:46 +01005620 // Create our own process group, so that the child and all its
5621 // children can be kill()ed. Don't do this when using pipes,
5622 // because stdin is not a tty, we would lose /dev/tty.
Bram Moolenaar835dc632016-02-07 14:27:38 +01005623 (void)setsid();
5624# endif
5625
Bram Moolenaar58556cd2017-07-20 23:04:46 +02005626# ifdef FEAT_TERMINAL
5627 if (options->jo_term_rows > 0)
Bram Moolenaar9a993e32018-04-05 22:15:22 +02005628 {
5629 char *term = (char *)T_NAME;
5630
5631#ifdef FEAT_GUI
5632 if (term_is_gui(T_NAME))
Bram Moolenaar0f873732019-12-05 20:28:46 +01005633 // In the GUI 'term' is not what we want, use $TERM.
Bram Moolenaar9a993e32018-04-05 22:15:22 +02005634 term = getenv("TERM");
5635#endif
Bram Moolenaar0f873732019-12-05 20:28:46 +01005636 // Use 'term' or $TERM if it starts with "xterm", otherwise fall
Bram Moolenaar4d5d0df2020-04-14 20:56:31 +02005637 // back to "xterm" or "xterm-color".
Bram Moolenaar9a993e32018-04-05 22:15:22 +02005638 if (term == NULL || *term == NUL || STRNCMP(term, "xterm", 5) != 0)
Bram Moolenaar5ba8d352020-04-05 21:42:12 +02005639 {
Bram Moolenaar5ba8d352020-04-05 21:42:12 +02005640 if (t_colors >= 256)
Bram Moolenaar4d5d0df2020-04-14 20:56:31 +02005641 // TODO: should we check this name is supported?
Bram Moolenaar5ba8d352020-04-05 21:42:12 +02005642 term = "xterm-256color";
Bram Moolenaar4d5d0df2020-04-14 20:56:31 +02005643 else if (t_colors > 16)
5644 term = "xterm-color";
Bram Moolenaar5ba8d352020-04-05 21:42:12 +02005645 else
5646 term = "xterm";
5647 }
Bram Moolenaar58556cd2017-07-20 23:04:46 +02005648 set_child_environment(
5649 (long)options->jo_term_rows,
5650 (long)options->jo_term_cols,
Bram Moolenaar493359e2018-06-12 20:25:52 +02005651 term,
5652 is_terminal);
Bram Moolenaar9a993e32018-04-05 22:15:22 +02005653 }
Bram Moolenaar58556cd2017-07-20 23:04:46 +02005654 else
5655# endif
Bram Moolenaar493359e2018-06-12 20:25:52 +02005656 set_default_child_environment(is_terminal);
Bram Moolenaar6463ca22016-02-13 17:04:46 +01005657
Bram Moolenaar05aafed2017-08-11 19:12:11 +02005658 if (options->jo_env != NULL)
5659 {
5660 dict_T *dict = options->jo_env;
5661 hashitem_T *hi;
5662 int todo = (int)dict->dv_hashtab.ht_used;
5663
5664 for (hi = dict->dv_hashtab.ht_array; todo > 0; ++hi)
5665 if (!HASHITEM_EMPTY(hi))
5666 {
5667 typval_T *item = &dict_lookup(hi)->di_tv;
5668
=?UTF-8?q?Dundar=20G=C3=B6c?=420fabc2022-01-28 15:28:04 +00005669 vim_setenv(hi->hi_key, tv_get_string(item));
Bram Moolenaar05aafed2017-08-11 19:12:11 +02005670 --todo;
5671 }
5672 }
5673
Bram Moolenaarf65333c2016-03-08 18:27:21 +01005674 if (use_null_for_in || use_null_for_out || use_null_for_err)
Bram Moolenaarb109bb42017-08-21 21:07:29 +02005675 {
Bram Moolenaarf65333c2016-03-08 18:27:21 +01005676 null_fd = open("/dev/null", O_RDWR | O_EXTRA, 0);
Bram Moolenaarb109bb42017-08-21 21:07:29 +02005677 if (null_fd < 0)
5678 {
5679 perror("opening /dev/null failed");
5680 _exit(OPEN_NULL_FAILED);
5681 }
5682 }
Bram Moolenaarf65333c2016-03-08 18:27:21 +01005683
Bram Moolenaar223896d2017-08-02 22:33:28 +02005684 if (pty_slave_fd >= 0)
5685 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01005686 // push stream discipline modules
Bram Moolenaar1ecc5e42019-01-26 15:12:55 +01005687 setup_slavepty(pty_slave_fd);
Bram Moolenaar223896d2017-08-02 22:33:28 +02005688# ifdef TIOCSCTTY
Bram Moolenaar0f873732019-12-05 20:28:46 +01005689 // Try to become controlling tty (probably doesn't work,
5690 // unless run by root)
Bram Moolenaar223896d2017-08-02 22:33:28 +02005691 ioctl(pty_slave_fd, TIOCSCTTY, (char *)NULL);
5692# endif
5693 }
5694
Bram Moolenaar0f873732019-12-05 20:28:46 +01005695 // set up stdin for the child
Bram Moolenaar5a1feb82017-07-22 18:04:08 +02005696 close(0);
Bram Moolenaarc0a1d7f2016-03-19 14:12:50 +01005697 if (use_null_for_in && null_fd >= 0)
Bram Moolenaar42335f52018-09-13 15:33:43 +02005698 vim_ignored = dup(null_fd);
Bram Moolenaar5a1feb82017-07-22 18:04:08 +02005699 else if (fd_in[0] < 0)
Bram Moolenaar42335f52018-09-13 15:33:43 +02005700 vim_ignored = dup(pty_slave_fd);
Bram Moolenaarf65333c2016-03-08 18:27:21 +01005701 else
Bram Moolenaar42335f52018-09-13 15:33:43 +02005702 vim_ignored = dup(fd_in[0]);
Bram Moolenaar6463ca22016-02-13 17:04:46 +01005703
Bram Moolenaar0f873732019-12-05 20:28:46 +01005704 // set up stderr for the child
Bram Moolenaar5a1feb82017-07-22 18:04:08 +02005705 close(2);
Bram Moolenaarc0a1d7f2016-03-19 14:12:50 +01005706 if (use_null_for_err && null_fd >= 0)
Bram Moolenaarf65333c2016-03-08 18:27:21 +01005707 {
Bram Moolenaar42335f52018-09-13 15:33:43 +02005708 vim_ignored = dup(null_fd);
Bram Moolenaar4694a172016-04-21 14:05:23 +02005709 stderr_works = FALSE;
Bram Moolenaarf65333c2016-03-08 18:27:21 +01005710 }
5711 else if (use_out_for_err)
Bram Moolenaar42335f52018-09-13 15:33:43 +02005712 vim_ignored = dup(fd_out[1]);
Bram Moolenaar5a1feb82017-07-22 18:04:08 +02005713 else if (fd_err[1] < 0)
Bram Moolenaar42335f52018-09-13 15:33:43 +02005714 vim_ignored = dup(pty_slave_fd);
Bram Moolenaarc25558b2016-03-03 21:02:23 +01005715 else
Bram Moolenaar42335f52018-09-13 15:33:43 +02005716 vim_ignored = dup(fd_err[1]);
Bram Moolenaarc25558b2016-03-03 21:02:23 +01005717
Bram Moolenaar0f873732019-12-05 20:28:46 +01005718 // set up stdout for the child
Bram Moolenaar5a1feb82017-07-22 18:04:08 +02005719 close(1);
Bram Moolenaarc0a1d7f2016-03-19 14:12:50 +01005720 if (use_null_for_out && null_fd >= 0)
Bram Moolenaar42335f52018-09-13 15:33:43 +02005721 vim_ignored = dup(null_fd);
Bram Moolenaar5a1feb82017-07-22 18:04:08 +02005722 else if (fd_out[1] < 0)
Bram Moolenaar42335f52018-09-13 15:33:43 +02005723 vim_ignored = dup(pty_slave_fd);
Bram Moolenaarf65333c2016-03-08 18:27:21 +01005724 else
Bram Moolenaar42335f52018-09-13 15:33:43 +02005725 vim_ignored = dup(fd_out[1]);
Bram Moolenaar5a1feb82017-07-22 18:04:08 +02005726
5727 if (fd_in[0] >= 0)
5728 close(fd_in[0]);
5729 if (fd_in[1] >= 0)
5730 close(fd_in[1]);
5731 if (fd_out[0] >= 0)
5732 close(fd_out[0]);
5733 if (fd_out[1] >= 0)
Bram Moolenaarf65333c2016-03-08 18:27:21 +01005734 close(fd_out[1]);
Bram Moolenaar5a1feb82017-07-22 18:04:08 +02005735 if (fd_err[0] >= 0)
5736 close(fd_err[0]);
5737 if (fd_err[1] >= 0)
5738 close(fd_err[1]);
5739 if (pty_master_fd >= 0)
5740 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01005741 close(pty_master_fd); // not used in the child
5742 close(pty_slave_fd); // was duped above
Bram Moolenaarf65333c2016-03-08 18:27:21 +01005743 }
Bram Moolenaarea83bf02016-05-08 09:40:51 +02005744
Bram Moolenaarf65333c2016-03-08 18:27:21 +01005745 if (null_fd >= 0)
5746 close(null_fd);
Bram Moolenaar6463ca22016-02-13 17:04:46 +01005747
Bram Moolenaar05aafed2017-08-11 19:12:11 +02005748 if (options->jo_cwd != NULL && mch_chdir((char *)options->jo_cwd) != 0)
5749 _exit(EXEC_FAILED);
5750
Bram Moolenaar0f873732019-12-05 20:28:46 +01005751 // See above for type of argv.
Bram Moolenaar835dc632016-02-07 14:27:38 +01005752 execvp(argv[0], argv);
5753
Bram Moolenaar4694a172016-04-21 14:05:23 +02005754 if (stderr_works)
5755 perror("executing job failed");
Bram Moolenaarfae42832017-08-01 22:24:26 +02005756# ifdef EXITFREE
Bram Moolenaar0f873732019-12-05 20:28:46 +01005757 // calling free_all_mem() here causes problems. Ignore valgrind
5758 // reporting possibly leaked memory.
Bram Moolenaarfae42832017-08-01 22:24:26 +02005759# endif
Bram Moolenaar0f873732019-12-05 20:28:46 +01005760 _exit(EXEC_FAILED); // exec failed, return failure code
Bram Moolenaar835dc632016-02-07 14:27:38 +01005761 }
Bram Moolenaar6463ca22016-02-13 17:04:46 +01005762
Bram Moolenaar0f873732019-12-05 20:28:46 +01005763 // parent
Bram Moolenaarbb09ceb2016-10-18 16:27:23 +02005764 UNBLOCK_SIGNALS(&curset);
5765
Bram Moolenaar6463ca22016-02-13 17:04:46 +01005766 job->jv_pid = pid;
5767 job->jv_status = JOB_STARTED;
Bram Moolenaar0f873732019-12-05 20:28:46 +01005768 job->jv_channel = channel; // ch_refcount was set above
Bram Moolenaar6463ca22016-02-13 17:04:46 +01005769
Bram Moolenaar5a1feb82017-07-22 18:04:08 +02005770 if (pty_master_fd >= 0)
Bram Moolenaar0f873732019-12-05 20:28:46 +01005771 close(pty_slave_fd); // not used in the parent
5772 // close child stdin, stdout and stderr
Bram Moolenaar819524702018-02-27 19:10:00 +01005773 if (fd_in[0] >= 0)
Bram Moolenaarb69fccf2016-03-06 23:06:25 +01005774 close(fd_in[0]);
Bram Moolenaar819524702018-02-27 19:10:00 +01005775 if (fd_out[1] >= 0)
Bram Moolenaare98d1212016-03-08 15:37:41 +01005776 close(fd_out[1]);
Bram Moolenaar819524702018-02-27 19:10:00 +01005777 if (fd_err[1] >= 0)
Bram Moolenaarc25558b2016-03-03 21:02:23 +01005778 close(fd_err[1]);
Bram Moolenaarf65333c2016-03-08 18:27:21 +01005779 if (channel != NULL)
5780 {
Bram Moolenaar652de232019-04-04 20:13:09 +02005781 int in_fd = INVALID_FD;
5782 int out_fd = INVALID_FD;
5783 int err_fd = INVALID_FD;
5784
5785 if (!(use_file_for_in || use_null_for_in))
5786 in_fd = fd_in[1] >= 0 ? fd_in[1] : pty_master_fd;
5787
5788 if (!(use_file_for_out || use_null_for_out))
5789 out_fd = fd_out[0] >= 0 ? fd_out[0] : pty_master_fd;
5790
5791 // When using pty_master_fd only set it for stdout, do not duplicate
5792 // it for stderr, it only needs to be read once.
5793 if (!(use_out_for_err || use_file_for_err || use_null_for_err))
5794 {
5795 if (fd_err[0] >= 0)
5796 err_fd = fd_err[0];
5797 else if (out_fd != pty_master_fd)
5798 err_fd = pty_master_fd;
5799 }
Bram Moolenaar4e9d4432018-04-24 20:54:07 +02005800
5801 channel_set_pipes(channel, in_fd, out_fd, err_fd);
Bram Moolenaarf65333c2016-03-08 18:27:21 +01005802 channel_set_job(channel, job, options);
Bram Moolenaarf65333c2016-03-08 18:27:21 +01005803 }
Bram Moolenaar979e8c52017-08-01 15:08:07 +02005804 else
5805 {
5806 if (fd_in[1] >= 0)
5807 close(fd_in[1]);
5808 if (fd_out[0] >= 0)
5809 close(fd_out[0]);
5810 if (fd_err[0] >= 0)
5811 close(fd_err[0]);
5812 if (pty_master_fd >= 0)
5813 close(pty_master_fd);
5814 }
Bram Moolenaar6463ca22016-02-13 17:04:46 +01005815
Bram Moolenaar0f873732019-12-05 20:28:46 +01005816 // success!
Bram Moolenaar6463ca22016-02-13 17:04:46 +01005817 return;
5818
Bram Moolenaar509ce2a2016-03-11 22:52:15 +01005819failed:
Bram Moolenaarde279892016-03-11 22:19:44 +01005820 channel_unref(channel);
Bram Moolenaar6463ca22016-02-13 17:04:46 +01005821 if (fd_in[0] >= 0)
Bram Moolenaar6463ca22016-02-13 17:04:46 +01005822 close(fd_in[0]);
Bram Moolenaare98d1212016-03-08 15:37:41 +01005823 if (fd_in[1] >= 0)
5824 close(fd_in[1]);
Bram Moolenaar6463ca22016-02-13 17:04:46 +01005825 if (fd_out[0] >= 0)
Bram Moolenaar6463ca22016-02-13 17:04:46 +01005826 close(fd_out[0]);
Bram Moolenaare98d1212016-03-08 15:37:41 +01005827 if (fd_out[1] >= 0)
Bram Moolenaar6463ca22016-02-13 17:04:46 +01005828 close(fd_out[1]);
Bram Moolenaar6463ca22016-02-13 17:04:46 +01005829 if (fd_err[0] >= 0)
Bram Moolenaar6463ca22016-02-13 17:04:46 +01005830 close(fd_err[0]);
Bram Moolenaare98d1212016-03-08 15:37:41 +01005831 if (fd_err[1] >= 0)
Bram Moolenaar6463ca22016-02-13 17:04:46 +01005832 close(fd_err[1]);
Bram Moolenaar5a1feb82017-07-22 18:04:08 +02005833 if (pty_master_fd >= 0)
5834 close(pty_master_fd);
5835 if (pty_slave_fd >= 0)
5836 close(pty_slave_fd);
Bram Moolenaar835dc632016-02-07 14:27:38 +01005837}
5838
Bram Moolenaarb3051ce2019-01-31 15:52:11 +01005839 static char_u *
5840get_signal_name(int sig)
5841{
5842 int i;
5843 char_u numbuf[NUMBUFLEN];
5844
5845 if (sig == SIGKILL)
5846 return vim_strsave((char_u *)"kill");
5847
5848 for (i = 0; signal_info[i].sig != -1; i++)
5849 if (sig == signal_info[i].sig)
5850 return strlow_save((char_u *)signal_info[i].name);
5851
5852 vim_snprintf((char *)numbuf, NUMBUFLEN, "%d", sig);
5853 return vim_strsave(numbuf);
5854}
5855
Bram Moolenaar835dc632016-02-07 14:27:38 +01005856 char *
5857mch_job_status(job_T *job)
5858{
5859# ifdef HAVE_UNION_WAIT
5860 union wait status;
5861# else
5862 int status = -1;
5863# endif
5864 pid_t wait_pid = 0;
5865
5866# ifdef __NeXT__
5867 wait_pid = wait4(job->jv_pid, &status, WNOHANG, (struct rusage *)0);
5868# else
5869 wait_pid = waitpid(job->jv_pid, &status, WNOHANG);
5870# endif
5871 if (wait_pid == -1)
5872 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01005873 // process must have exited
Bram Moolenaarb0b98d52018-05-05 21:01:00 +02005874 if (job->jv_status < JOB_ENDED)
5875 ch_log(job->jv_channel, "Job no longer exists: %s",
5876 strerror(errno));
Bram Moolenaar97792de2016-10-15 18:36:49 +02005877 goto return_dead;
Bram Moolenaar835dc632016-02-07 14:27:38 +01005878 }
5879 if (wait_pid == 0)
5880 return "run";
5881 if (WIFEXITED(status))
5882 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01005883 // LINTED avoid "bitwise operation on signed value"
Bram Moolenaar835dc632016-02-07 14:27:38 +01005884 job->jv_exitval = WEXITSTATUS(status);
Bram Moolenaarb0b98d52018-05-05 21:01:00 +02005885 if (job->jv_status < JOB_ENDED)
5886 ch_log(job->jv_channel, "Job exited with %d", job->jv_exitval);
Bram Moolenaar97792de2016-10-15 18:36:49 +02005887 goto return_dead;
Bram Moolenaar835dc632016-02-07 14:27:38 +01005888 }
Bram Moolenaar76467df2016-02-12 19:30:26 +01005889 if (WIFSIGNALED(status))
5890 {
5891 job->jv_exitval = -1;
Bram Moolenaarb3051ce2019-01-31 15:52:11 +01005892 job->jv_termsig = get_signal_name(WTERMSIG(status));
5893 if (job->jv_status < JOB_ENDED && job->jv_termsig != NULL)
5894 ch_log(job->jv_channel, "Job terminated by signal \"%s\"",
5895 job->jv_termsig);
Bram Moolenaar97792de2016-10-15 18:36:49 +02005896 goto return_dead;
Bram Moolenaar76467df2016-02-12 19:30:26 +01005897 }
Bram Moolenaar835dc632016-02-07 14:27:38 +01005898 return "run";
Bram Moolenaar97792de2016-10-15 18:36:49 +02005899
5900return_dead:
Bram Moolenaar7df915d2016-11-17 17:25:32 +01005901 if (job->jv_status < JOB_ENDED)
Bram Moolenaar97792de2016-10-15 18:36:49 +02005902 job->jv_status = JOB_ENDED;
Bram Moolenaar97792de2016-10-15 18:36:49 +02005903 return "dead";
5904}
5905
5906 job_T *
5907mch_detect_ended_job(job_T *job_list)
5908{
5909# ifdef HAVE_UNION_WAIT
5910 union wait status;
5911# else
5912 int status = -1;
5913# endif
5914 pid_t wait_pid = 0;
5915 job_T *job;
5916
Bram Moolenaarc4d4ac22016-11-07 22:42:57 +01005917# ifndef USE_SYSTEM
Bram Moolenaar0f873732019-12-05 20:28:46 +01005918 // Do not do this when waiting for a shell command to finish, we would get
5919 // the exit value here (and discard it), the exit value obtained there
5920 // would then be wrong.
Bram Moolenaarc4d4ac22016-11-07 22:42:57 +01005921 if (dont_check_job_ended > 0)
5922 return NULL;
5923# endif
5924
Bram Moolenaar97792de2016-10-15 18:36:49 +02005925# ifdef __NeXT__
5926 wait_pid = wait4(-1, &status, WNOHANG, (struct rusage *)0);
5927# else
5928 wait_pid = waitpid(-1, &status, WNOHANG);
5929# endif
5930 if (wait_pid <= 0)
Bram Moolenaar0f873732019-12-05 20:28:46 +01005931 // no process ended
Bram Moolenaar97792de2016-10-15 18:36:49 +02005932 return NULL;
5933 for (job = job_list; job != NULL; job = job->jv_next)
5934 {
5935 if (job->jv_pid == wait_pid)
5936 {
5937 if (WIFEXITED(status))
Bram Moolenaar0f873732019-12-05 20:28:46 +01005938 // LINTED avoid "bitwise operation on signed value"
Bram Moolenaar97792de2016-10-15 18:36:49 +02005939 job->jv_exitval = WEXITSTATUS(status);
5940 else if (WIFSIGNALED(status))
Bram Moolenaarb3051ce2019-01-31 15:52:11 +01005941 {
Bram Moolenaar97792de2016-10-15 18:36:49 +02005942 job->jv_exitval = -1;
Bram Moolenaarb3051ce2019-01-31 15:52:11 +01005943 job->jv_termsig = get_signal_name(WTERMSIG(status));
5944 }
Bram Moolenaar7df915d2016-11-17 17:25:32 +01005945 if (job->jv_status < JOB_ENDED)
Bram Moolenaar97792de2016-10-15 18:36:49 +02005946 {
5947 ch_log(job->jv_channel, "Job ended");
5948 job->jv_status = JOB_ENDED;
5949 }
5950 return job;
5951 }
5952 }
5953 return NULL;
Bram Moolenaar835dc632016-02-07 14:27:38 +01005954}
5955
Bram Moolenaar3a117e12016-10-30 21:57:52 +01005956/*
5957 * Send a (deadly) signal to "job".
5958 * Return FAIL if "how" is not a valid name.
5959 */
Bram Moolenaar835dc632016-02-07 14:27:38 +01005960 int
Bram Moolenaar2d33e902017-08-11 16:31:54 +02005961mch_signal_job(job_T *job, char_u *how)
Bram Moolenaar835dc632016-02-07 14:27:38 +01005962{
Bram Moolenaar6463ca22016-02-13 17:04:46 +01005963 int sig = -1;
Bram Moolenaar835dc632016-02-07 14:27:38 +01005964
Bram Moolenaar923d9262016-02-25 20:56:01 +01005965 if (*how == NUL || STRCMP(how, "term") == 0)
Bram Moolenaar835dc632016-02-07 14:27:38 +01005966 sig = SIGTERM;
Bram Moolenaar923d9262016-02-25 20:56:01 +01005967 else if (STRCMP(how, "hup") == 0)
5968 sig = SIGHUP;
Bram Moolenaar835dc632016-02-07 14:27:38 +01005969 else if (STRCMP(how, "quit") == 0)
5970 sig = SIGQUIT;
Bram Moolenaar923d9262016-02-25 20:56:01 +01005971 else if (STRCMP(how, "int") == 0)
5972 sig = SIGINT;
Bram Moolenaar835dc632016-02-07 14:27:38 +01005973 else if (STRCMP(how, "kill") == 0)
5974 sig = SIGKILL;
Bram Moolenaarb13501f2017-07-22 22:32:56 +02005975#ifdef SIGWINCH
5976 else if (STRCMP(how, "winch") == 0)
5977 sig = SIGWINCH;
5978#endif
Bram Moolenaar835dc632016-02-07 14:27:38 +01005979 else if (isdigit(*how))
5980 sig = atoi((char *)how);
5981 else
5982 return FAIL;
Bram Moolenaar76467df2016-02-12 19:30:26 +01005983
Bram Moolenaar76ab4fd2018-12-08 14:39:05 +01005984 // Never kill ourselves!
5985 if (job->jv_pid != 0)
5986 {
5987 // TODO: have an option to only kill the process, not the group?
5988 kill(-job->jv_pid, sig);
5989 kill(job->jv_pid, sig);
5990 }
Bram Moolenaar76467df2016-02-12 19:30:26 +01005991
Bram Moolenaar835dc632016-02-07 14:27:38 +01005992 return OK;
5993}
Bram Moolenaar76467df2016-02-12 19:30:26 +01005994
5995/*
5996 * Clear the data related to "job".
5997 */
5998 void
5999mch_clear_job(job_T *job)
6000{
Bram Moolenaar0f873732019-12-05 20:28:46 +01006001 // call waitpid because child process may become zombie
Bram Moolenaar76467df2016-02-12 19:30:26 +01006002# ifdef __NeXT__
Bram Moolenaar4ca812b2016-03-02 21:51:16 +01006003 (void)wait4(job->jv_pid, NULL, WNOHANG, (struct rusage *)0);
Bram Moolenaar76467df2016-02-12 19:30:26 +01006004# else
Bram Moolenaar4ca812b2016-03-02 21:51:16 +01006005 (void)waitpid(job->jv_pid, NULL, WNOHANG);
Bram Moolenaar76467df2016-02-12 19:30:26 +01006006# endif
6007}
Bram Moolenaar835dc632016-02-07 14:27:38 +01006008#endif
6009
Bram Moolenaar13ebb032017-08-26 22:02:51 +02006010#if defined(FEAT_TERMINAL) || defined(PROTO)
6011 int
6012mch_create_pty_channel(job_T *job, jobopt_T *options)
6013{
6014 int pty_master_fd = -1;
6015 int pty_slave_fd = -1;
6016 channel_T *channel;
6017
Bram Moolenaar59386482019-02-10 22:43:46 +01006018 open_pty(&pty_master_fd, &pty_slave_fd, &job->jv_tty_out, &job->jv_tty_in);
Bram Moolenaard0342202020-06-29 22:40:42 +02006019 if (pty_master_fd < 0 || pty_slave_fd < 0)
6020 return FAIL;
Bram Moolenaar13ebb032017-08-26 22:02:51 +02006021 close(pty_slave_fd);
6022
6023 channel = add_channel();
6024 if (channel == NULL)
Bram Moolenaar1b9f9d32017-09-05 23:32:38 +02006025 {
6026 close(pty_master_fd);
Bram Moolenaar13ebb032017-08-26 22:02:51 +02006027 return FAIL;
Bram Moolenaar1b9f9d32017-09-05 23:32:38 +02006028 }
Bram Moolenaarf3360612017-10-01 16:21:31 +02006029 if (job->jv_tty_out != NULL)
6030 ch_log(channel, "using pty %s on fd %d",
6031 job->jv_tty_out, pty_master_fd);
Bram Moolenaar0f873732019-12-05 20:28:46 +01006032 job->jv_channel = channel; // ch_refcount was set by add_channel()
Bram Moolenaar13ebb032017-08-26 22:02:51 +02006033 channel->ch_keep_open = TRUE;
6034
Bram Moolenaar0f873732019-12-05 20:28:46 +01006035 // Only set the pty_master_fd for stdout, do not duplicate it for stderr,
6036 // it only needs to be read once.
Bram Moolenaarb0b98d52018-05-05 21:01:00 +02006037 channel_set_pipes(channel, pty_master_fd, pty_master_fd, INVALID_FD);
Bram Moolenaar13ebb032017-08-26 22:02:51 +02006038 channel_set_job(channel, job, options);
6039 return OK;
6040}
6041#endif
6042
Bram Moolenaar071d4272004-06-13 20:20:40 +00006043/*
6044 * Check for CTRL-C typed by reading all available characters.
6045 * In cooked mode we should get SIGINT, no need to check.
6046 */
6047 void
Bram Moolenaarb9c31e72016-09-29 15:18:57 +02006048mch_breakcheck(int force)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006049{
Bram Moolenaar26e86442020-05-17 14:06:16 +02006050 if ((mch_cur_tmode == TMODE_RAW || force)
Bram Moolenaarb9c31e72016-09-29 15:18:57 +02006051 && RealWaitForChar(read_cmd_fd, 0L, NULL, NULL))
Bram Moolenaar071d4272004-06-13 20:20:40 +00006052 fill_input_buf(FALSE);
6053}
6054
6055/*
Bram Moolenaar943bb2b2016-03-19 14:11:18 +01006056 * Wait "msec" msec until a character is available from the mouse, keyboard,
6057 * from inbuf[].
6058 * "msec" == -1 will block forever.
6059 * Invokes timer callbacks when needed.
Bram Moolenaare9c21ae2017-08-03 20:44:48 +02006060 * When "ignore_input" is TRUE even check for pending input when input is
6061 * already available.
Bram Moolenaarcda77642016-06-04 13:32:35 +02006062 * "interrupted" (if not NULL) is set to TRUE when no character is available
6063 * but something else needs to be done.
Bram Moolenaar40b1b542016-04-20 20:18:23 +02006064 * Returns TRUE when a character is available.
Bram Moolenaarcda77642016-06-04 13:32:35 +02006065 * When a GUI is being used, this will never get called -- webb
Bram Moolenaar071d4272004-06-13 20:20:40 +00006066 */
6067 static int
Bram Moolenaare9c21ae2017-08-03 20:44:48 +02006068WaitForChar(long msec, int *interrupted, int ignore_input)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006069{
Bram Moolenaar943bb2b2016-03-19 14:11:18 +01006070#ifdef FEAT_TIMERS
Bram Moolenaarc9e649a2017-12-18 18:14:47 +01006071 return ui_wait_for_chars_or_timer(
6072 msec, WaitForCharOrMouse, interrupted, ignore_input) == OK;
Bram Moolenaar943bb2b2016-03-19 14:11:18 +01006073#else
Bram Moolenaare9c21ae2017-08-03 20:44:48 +02006074 return WaitForCharOrMouse(msec, interrupted, ignore_input);
Bram Moolenaar943bb2b2016-03-19 14:11:18 +01006075#endif
6076}
6077
6078/*
6079 * Wait "msec" msec until a character is available from the mouse or keyboard
6080 * or from inbuf[].
6081 * "msec" == -1 will block forever.
Bram Moolenaare9c21ae2017-08-03 20:44:48 +02006082 * for "ignore_input" see WaitForCharOr().
Bram Moolenaarcda77642016-06-04 13:32:35 +02006083 * "interrupted" (if not NULL) is set to TRUE when no character is available
6084 * but something else needs to be done.
Bram Moolenaar943bb2b2016-03-19 14:11:18 +01006085 * When a GUI is being used, this will never get called -- webb
6086 */
6087 static int
Bram Moolenaare9c21ae2017-08-03 20:44:48 +02006088WaitForCharOrMouse(long msec, int *interrupted, int ignore_input)
Bram Moolenaar943bb2b2016-03-19 14:11:18 +01006089{
Bram Moolenaar071d4272004-06-13 20:20:40 +00006090#ifdef FEAT_MOUSE_GPM
6091 int gpm_process_wanted;
6092#endif
6093#ifdef FEAT_XCLIPBOARD
6094 int rest;
6095#endif
6096 int avail;
6097
Bram Moolenaar0f873732019-12-05 20:28:46 +01006098 if (!ignore_input && input_available()) // something in inbuf[]
Bram Moolenaar071d4272004-06-13 20:20:40 +00006099 return 1;
6100
6101#if defined(FEAT_MOUSE_DEC)
Bram Moolenaar0f873732019-12-05 20:28:46 +01006102 // May need to query the mouse position.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006103 if (WantQueryMouse)
6104 {
Bram Moolenaar6bb68362005-03-22 23:03:44 +00006105 WantQueryMouse = FALSE;
Bram Moolenaar92fd5992019-05-02 23:00:22 +02006106 if (!no_query_mouse_for_testing)
6107 mch_write((char_u *)IF_EB("\033[1'|", ESC_STR "[1'|"), 5);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006108 }
6109#endif
6110
6111 /*
6112 * For FEAT_MOUSE_GPM and FEAT_XCLIPBOARD we loop here to process mouse
6113 * events. This is a bit complicated, because they might both be defined.
6114 */
6115#if defined(FEAT_MOUSE_GPM) || defined(FEAT_XCLIPBOARD)
6116# ifdef FEAT_XCLIPBOARD
6117 rest = 0;
6118 if (do_xterm_trace())
6119 rest = msec;
6120# endif
6121 do
6122 {
6123# ifdef FEAT_XCLIPBOARD
6124 if (rest != 0)
6125 {
6126 msec = XT_TRACE_DELAY;
6127 if (rest >= 0 && rest < XT_TRACE_DELAY)
6128 msec = rest;
6129 if (rest >= 0)
6130 rest -= msec;
6131 }
6132# endif
Bram Moolenaar28e67e02019-08-15 23:05:49 +02006133# ifdef FEAT_SOUND_CANBERRA
6134 // Invoke any pending sound callbacks.
6135 if (has_sound_callback_in_queue())
6136 invoke_sound_callback();
6137# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006138# ifdef FEAT_MOUSE_GPM
6139 gpm_process_wanted = 0;
Bram Moolenaar8fdd7212016-03-26 19:41:48 +01006140 avail = RealWaitForChar(read_cmd_fd, msec,
Bram Moolenaarcda77642016-06-04 13:32:35 +02006141 &gpm_process_wanted, interrupted);
Bram Moolenaarb5432d82019-08-30 19:28:25 +02006142 if (!avail && !gpm_process_wanted)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006143# else
Bram Moolenaarcda77642016-06-04 13:32:35 +02006144 avail = RealWaitForChar(read_cmd_fd, msec, NULL, interrupted);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006145 if (!avail)
Bram Moolenaarb5432d82019-08-30 19:28:25 +02006146# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006147 {
Bram Moolenaare9c21ae2017-08-03 20:44:48 +02006148 if (!ignore_input && input_available())
Bram Moolenaar071d4272004-06-13 20:20:40 +00006149 return 1;
6150# ifdef FEAT_XCLIPBOARD
6151 if (rest == 0 || !do_xterm_trace())
6152# endif
6153 break;
6154 }
6155 }
6156 while (FALSE
6157# ifdef FEAT_MOUSE_GPM
6158 || (gpm_process_wanted && mch_gpm_process() == 0)
6159# endif
6160# ifdef FEAT_XCLIPBOARD
6161 || (!avail && rest != 0)
6162# endif
Bram Moolenaar8fdd7212016-03-26 19:41:48 +01006163 )
6164 ;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006165
6166#else
Bram Moolenaarcda77642016-06-04 13:32:35 +02006167 avail = RealWaitForChar(read_cmd_fd, msec, NULL, interrupted);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006168#endif
6169 return avail;
6170}
6171
Bram Moolenaar4ffa0702013-12-11 17:12:37 +01006172#ifndef VMS
Bram Moolenaar071d4272004-06-13 20:20:40 +00006173/*
6174 * Wait "msec" msec until a character is available from file descriptor "fd".
Bram Moolenaar493c7a82011-09-07 14:06:47 +02006175 * "msec" == 0 will check for characters once.
6176 * "msec" == -1 will block until a character is available.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006177 * When a GUI is being used, this will not be used for input -- webb
Bram Moolenaar071d4272004-06-13 20:20:40 +00006178 * Or when a Linux GPM mouse event is waiting.
Bram Moolenaar93c88e02015-09-15 14:12:05 +02006179 * Or when a clientserver message is on the queue.
Bram Moolenaarcda77642016-06-04 13:32:35 +02006180 * "interrupted" (if not NULL) is set to TRUE when no character is available
6181 * but something else needs to be done.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006182 */
Bram Moolenaarec70bdd2016-02-18 22:17:42 +01006183 static int
Bram Moolenaarcda77642016-06-04 13:32:35 +02006184RealWaitForChar(int fd, long msec, int *check_for_gpm UNUSED, int *interrupted)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006185{
6186 int ret;
Bram Moolenaarec70bdd2016-02-18 22:17:42 +01006187 int result;
Bram Moolenaar325b7a22004-07-05 15:58:32 +00006188#if defined(FEAT_XCLIPBOARD) || defined(USE_XSMP) || defined(FEAT_MZSCHEME)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006189 static int busy = FALSE;
6190
Bram Moolenaar0f873732019-12-05 20:28:46 +01006191 // May retry getting characters after an event was handled.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006192# define MAY_LOOP
6193
Bram Moolenaar833eb1d2016-11-24 17:22:50 +01006194# ifdef ELAPSED_FUNC
Bram Moolenaar0f873732019-12-05 20:28:46 +01006195 // Remember at what time we started, so that we know how much longer we
6196 // should wait after being interrupted.
Bram Moolenaar1ac56c22019-01-17 22:28:22 +01006197 long start_msec = msec;
6198 elapsed_T start_tv;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006199
Bram Moolenaar76b6dfe2016-06-04 14:37:22 +02006200 if (msec > 0)
Bram Moolenaar833eb1d2016-11-24 17:22:50 +01006201 ELAPSED_INIT(start_tv);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006202# endif
6203
Bram Moolenaar0f873732019-12-05 20:28:46 +01006204 // Handle being called recursively. This may happen for the session
6205 // manager stuff, it may save the file, which does a breakcheck.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006206 if (busy)
6207 return 0;
6208#endif
6209
6210#ifdef MAY_LOOP
Bram Moolenaar35fdbb52005-07-09 21:08:57 +00006211 for (;;)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006212#endif
6213 {
6214#ifdef MAY_LOOP
Bram Moolenaar0f873732019-12-05 20:28:46 +01006215 int finished = TRUE; // default is to 'loop' just once
Bram Moolenaar325b7a22004-07-05 15:58:32 +00006216# ifdef FEAT_MZSCHEME
6217 int mzquantum_used = FALSE;
6218# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006219#endif
6220#ifndef HAVE_SELECT
Bram Moolenaar0f873732019-12-05 20:28:46 +01006221 // each channel may use in, out and err
Bram Moolenaar8b877ac2016-03-28 19:16:20 +02006222 struct pollfd fds[6 + 3 * MAX_OPEN_CHANNELS];
Bram Moolenaar071d4272004-06-13 20:20:40 +00006223 int nfd;
6224# ifdef FEAT_XCLIPBOARD
6225 int xterm_idx = -1;
6226# endif
6227# ifdef FEAT_MOUSE_GPM
6228 int gpm_idx = -1;
6229# endif
6230# ifdef USE_XSMP
6231 int xsmp_idx = -1;
6232# endif
Bram Moolenaar325b7a22004-07-05 15:58:32 +00006233 int towait = (int)msec;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006234
Bram Moolenaar325b7a22004-07-05 15:58:32 +00006235# ifdef FEAT_MZSCHEME
6236 mzvim_check_threads();
6237 if (mzthreads_allowed() && p_mzq > 0 && (msec < 0 || msec > p_mzq))
6238 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01006239 towait = (int)p_mzq; // don't wait longer than 'mzquantum'
Bram Moolenaar325b7a22004-07-05 15:58:32 +00006240 mzquantum_used = TRUE;
6241 }
6242# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006243 fds[0].fd = fd;
6244 fds[0].events = POLLIN;
6245 nfd = 1;
6246
Bram Moolenaar071d4272004-06-13 20:20:40 +00006247# ifdef FEAT_XCLIPBOARD
Bram Moolenaarb1e26502014-11-19 18:48:46 +01006248 may_restore_clipboard();
Bram Moolenaar071d4272004-06-13 20:20:40 +00006249 if (xterm_Shell != (Widget)0)
6250 {
6251 xterm_idx = nfd;
6252 fds[nfd].fd = ConnectionNumber(xterm_dpy);
6253 fds[nfd].events = POLLIN;
6254 nfd++;
6255 }
6256# endif
6257# ifdef FEAT_MOUSE_GPM
6258 if (check_for_gpm != NULL && gpm_flag && gpm_fd >= 0)
6259 {
6260 gpm_idx = nfd;
6261 fds[nfd].fd = gpm_fd;
6262 fds[nfd].events = POLLIN;
6263 nfd++;
6264 }
6265# endif
6266# ifdef USE_XSMP
6267 if (xsmp_icefd != -1)
6268 {
6269 xsmp_idx = nfd;
6270 fds[nfd].fd = xsmp_icefd;
6271 fds[nfd].events = POLLIN;
6272 nfd++;
6273 }
6274# endif
Bram Moolenaar509ce2a2016-03-11 22:52:15 +01006275#ifdef FEAT_JOB_CHANNEL
Bram Moolenaarf3360612017-10-01 16:21:31 +02006276 nfd = channel_poll_setup(nfd, &fds, &towait);
Bram Moolenaar67c53842010-05-22 18:28:27 +02006277#endif
Bram Moolenaarcda77642016-06-04 13:32:35 +02006278 if (interrupted != NULL)
6279 *interrupted = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006280
Bram Moolenaar325b7a22004-07-05 15:58:32 +00006281 ret = poll(fds, nfd, towait);
Bram Moolenaarec70bdd2016-02-18 22:17:42 +01006282
6283 result = ret > 0 && (fds[0].revents & POLLIN);
Bram Moolenaarcda77642016-06-04 13:32:35 +02006284 if (result == 0 && interrupted != NULL && ret > 0)
6285 *interrupted = TRUE;
Bram Moolenaarec70bdd2016-02-18 22:17:42 +01006286
Bram Moolenaar325b7a22004-07-05 15:58:32 +00006287# ifdef FEAT_MZSCHEME
6288 if (ret == 0 && mzquantum_used)
Bram Moolenaar0f873732019-12-05 20:28:46 +01006289 // MzThreads scheduling is required and timeout occurred
Bram Moolenaar325b7a22004-07-05 15:58:32 +00006290 finished = FALSE;
6291# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006292
Bram Moolenaar071d4272004-06-13 20:20:40 +00006293# ifdef FEAT_XCLIPBOARD
6294 if (xterm_Shell != (Widget)0 && (fds[xterm_idx].revents & POLLIN))
6295 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01006296 xterm_update(); // Maybe we should hand out clipboard
Bram Moolenaar071d4272004-06-13 20:20:40 +00006297 if (--ret == 0 && !input_available())
Bram Moolenaar0f873732019-12-05 20:28:46 +01006298 // Try again
Bram Moolenaar071d4272004-06-13 20:20:40 +00006299 finished = FALSE;
6300 }
6301# endif
6302# ifdef FEAT_MOUSE_GPM
6303 if (gpm_idx >= 0 && (fds[gpm_idx].revents & POLLIN))
Bram Moolenaar071d4272004-06-13 20:20:40 +00006304 *check_for_gpm = 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006305# endif
6306# ifdef USE_XSMP
6307 if (xsmp_idx >= 0 && (fds[xsmp_idx].revents & (POLLIN | POLLHUP)))
6308 {
6309 if (fds[xsmp_idx].revents & POLLIN)
6310 {
6311 busy = TRUE;
6312 xsmp_handle_requests();
6313 busy = FALSE;
6314 }
6315 else if (fds[xsmp_idx].revents & POLLHUP)
6316 {
6317 if (p_verbose > 0)
Bram Moolenaar32526b32019-01-19 17:43:09 +01006318 verb_msg(_("XSMP lost ICE connection"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00006319 xsmp_close();
6320 }
6321 if (--ret == 0)
Bram Moolenaar0f873732019-12-05 20:28:46 +01006322 finished = FALSE; // Try again
Bram Moolenaar071d4272004-06-13 20:20:40 +00006323 }
6324# endif
Bram Moolenaar509ce2a2016-03-11 22:52:15 +01006325#ifdef FEAT_JOB_CHANNEL
Bram Moolenaar2c519cf2019-03-21 21:45:34 +01006326 // also call when ret == 0, we may be polling a keep-open channel
Bram Moolenaarf3360612017-10-01 16:21:31 +02006327 if (ret >= 0)
Bram Moolenaar2c519cf2019-03-21 21:45:34 +01006328 channel_poll_check(ret, &fds);
Bram Moolenaar67c53842010-05-22 18:28:27 +02006329#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006330
Bram Moolenaar0f873732019-12-05 20:28:46 +01006331#else // HAVE_SELECT
Bram Moolenaar071d4272004-06-13 20:20:40 +00006332
6333 struct timeval tv;
Bram Moolenaar325b7a22004-07-05 15:58:32 +00006334 struct timeval *tvp;
Bram Moolenaar61fb8d82018-11-12 21:45:08 +01006335 // These are static because they can take 8 Kbyte each and cause the
6336 // signal stack to run out with -O3.
6337 static fd_set rfds, wfds, efds;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006338 int maxfd;
Bram Moolenaar325b7a22004-07-05 15:58:32 +00006339 long towait = msec;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006340
Bram Moolenaar325b7a22004-07-05 15:58:32 +00006341# ifdef FEAT_MZSCHEME
6342 mzvim_check_threads();
6343 if (mzthreads_allowed() && p_mzq > 0 && (msec < 0 || msec > p_mzq))
6344 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01006345 towait = p_mzq; // don't wait longer than 'mzquantum'
Bram Moolenaar325b7a22004-07-05 15:58:32 +00006346 mzquantum_used = TRUE;
6347 }
6348# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006349
Bram Moolenaar325b7a22004-07-05 15:58:32 +00006350 if (towait >= 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006351 {
Bram Moolenaar325b7a22004-07-05 15:58:32 +00006352 tv.tv_sec = towait / 1000;
6353 tv.tv_usec = (towait % 1000) * (1000000/1000);
6354 tvp = &tv;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006355 }
Bram Moolenaar325b7a22004-07-05 15:58:32 +00006356 else
6357 tvp = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006358
6359 /*
6360 * Select on ready for reading and exceptional condition (end of file).
6361 */
Bram Moolenaar493c7a82011-09-07 14:06:47 +02006362select_eintr:
6363 FD_ZERO(&rfds);
Bram Moolenaar8b877ac2016-03-28 19:16:20 +02006364 FD_ZERO(&wfds);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006365 FD_ZERO(&efds);
6366 FD_SET(fd, &rfds);
6367# if !defined(__QNX__) && !defined(__CYGWIN32__)
Bram Moolenaar0f873732019-12-05 20:28:46 +01006368 // For QNX select() always returns 1 if this is set. Why?
Bram Moolenaar071d4272004-06-13 20:20:40 +00006369 FD_SET(fd, &efds);
6370# endif
6371 maxfd = fd;
6372
Bram Moolenaar071d4272004-06-13 20:20:40 +00006373# ifdef FEAT_XCLIPBOARD
Bram Moolenaarb1e26502014-11-19 18:48:46 +01006374 may_restore_clipboard();
Bram Moolenaar071d4272004-06-13 20:20:40 +00006375 if (xterm_Shell != (Widget)0)
6376 {
6377 FD_SET(ConnectionNumber(xterm_dpy), &rfds);
6378 if (maxfd < ConnectionNumber(xterm_dpy))
6379 maxfd = ConnectionNumber(xterm_dpy);
Bram Moolenaardd82d692012-08-15 17:26:57 +02006380
Bram Moolenaar0f873732019-12-05 20:28:46 +01006381 // An event may have already been read but not handled. In
6382 // particularly, XFlush may cause this.
Bram Moolenaardd82d692012-08-15 17:26:57 +02006383 xterm_update();
Bram Moolenaar071d4272004-06-13 20:20:40 +00006384 }
6385# endif
6386# ifdef FEAT_MOUSE_GPM
6387 if (check_for_gpm != NULL && gpm_flag && gpm_fd >= 0)
6388 {
6389 FD_SET(gpm_fd, &rfds);
6390 FD_SET(gpm_fd, &efds);
6391 if (maxfd < gpm_fd)
6392 maxfd = gpm_fd;
6393 }
6394# endif
6395# ifdef USE_XSMP
6396 if (xsmp_icefd != -1)
6397 {
6398 FD_SET(xsmp_icefd, &rfds);
6399 FD_SET(xsmp_icefd, &efds);
6400 if (maxfd < xsmp_icefd)
6401 maxfd = xsmp_icefd;
6402 }
6403# endif
Bram Moolenaar509ce2a2016-03-11 22:52:15 +01006404# ifdef FEAT_JOB_CHANNEL
Bram Moolenaarf3360612017-10-01 16:21:31 +02006405 maxfd = channel_select_setup(maxfd, &rfds, &wfds, &tv, &tvp);
Bram Moolenaardd82d692012-08-15 17:26:57 +02006406# endif
Bram Moolenaarcda77642016-06-04 13:32:35 +02006407 if (interrupted != NULL)
6408 *interrupted = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006409
Bram Moolenaar643b6142018-09-12 20:29:09 +02006410 ret = select(maxfd + 1, SELECT_TYPE_ARG234 &rfds,
6411 SELECT_TYPE_ARG234 &wfds, SELECT_TYPE_ARG234 &efds, tvp);
Bram Moolenaarec70bdd2016-02-18 22:17:42 +01006412 result = ret > 0 && FD_ISSET(fd, &rfds);
6413 if (result)
6414 --ret;
Bram Moolenaarcda77642016-06-04 13:32:35 +02006415 else if (interrupted != NULL && ret > 0)
6416 *interrupted = TRUE;
Bram Moolenaarec70bdd2016-02-18 22:17:42 +01006417
Bram Moolenaar493c7a82011-09-07 14:06:47 +02006418# ifdef EINTR
6419 if (ret == -1 && errno == EINTR)
Bram Moolenaar2e7b1df2011-10-12 21:04:20 +02006420 {
dbivolaruab16ad32021-12-29 19:41:47 +00006421 // Check whether the EINTR is caused by SIGTSTP
6422 if (got_tstp && !in_mch_suspend)
6423 {
6424 exarg_T ea;
dbivolaru79a6e252022-01-23 16:41:14 +00006425
dbivolaruab16ad32021-12-29 19:41:47 +00006426 ea.forceit = TRUE;
6427 ex_stop(&ea);
6428 got_tstp = FALSE;
6429 }
6430
Bram Moolenaar0f873732019-12-05 20:28:46 +01006431 // Check whether window has been resized, EINTR may be caused by
6432 // SIGWINCH.
Bram Moolenaar2e7b1df2011-10-12 21:04:20 +02006433 if (do_resize)
6434 handle_resize();
6435
Bram Moolenaar0f873732019-12-05 20:28:46 +01006436 // Interrupted by a signal, need to try again. We ignore msec
6437 // here, because we do want to check even after a timeout if
6438 // characters are available. Needed for reading output of an
6439 // external command after the process has finished.
Bram Moolenaar493c7a82011-09-07 14:06:47 +02006440 goto select_eintr;
Bram Moolenaar2e7b1df2011-10-12 21:04:20 +02006441 }
Bram Moolenaar493c7a82011-09-07 14:06:47 +02006442# endif
Bram Moolenaar311d9822007-02-27 15:48:28 +00006443# ifdef __TANDEM
6444 if (ret == -1 && errno == ENOTSUP)
6445 {
6446 FD_ZERO(&rfds);
6447 FD_ZERO(&efds);
6448 ret = 0;
6449 }
Bram Moolenaar493c7a82011-09-07 14:06:47 +02006450# endif
Bram Moolenaar325b7a22004-07-05 15:58:32 +00006451# ifdef FEAT_MZSCHEME
6452 if (ret == 0 && mzquantum_used)
Bram Moolenaar0f873732019-12-05 20:28:46 +01006453 // loop if MzThreads must be scheduled and timeout occurred
Bram Moolenaar325b7a22004-07-05 15:58:32 +00006454 finished = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006455# endif
6456
Bram Moolenaar071d4272004-06-13 20:20:40 +00006457# ifdef FEAT_XCLIPBOARD
6458 if (ret > 0 && xterm_Shell != (Widget)0
6459 && FD_ISSET(ConnectionNumber(xterm_dpy), &rfds))
6460 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01006461 xterm_update(); // Maybe we should hand out clipboard
6462 // continue looping when we only got the X event and the input
6463 // buffer is empty
Bram Moolenaar071d4272004-06-13 20:20:40 +00006464 if (--ret == 0 && !input_available())
6465 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01006466 // Try again
Bram Moolenaar071d4272004-06-13 20:20:40 +00006467 finished = FALSE;
6468 }
6469 }
6470# endif
6471# ifdef FEAT_MOUSE_GPM
6472 if (ret > 0 && gpm_flag && check_for_gpm != NULL && gpm_fd >= 0)
6473 {
6474 if (FD_ISSET(gpm_fd, &efds))
6475 gpm_close();
6476 else if (FD_ISSET(gpm_fd, &rfds))
6477 *check_for_gpm = 1;
6478 }
6479# endif
6480# ifdef USE_XSMP
6481 if (ret > 0 && xsmp_icefd != -1)
6482 {
6483 if (FD_ISSET(xsmp_icefd, &efds))
6484 {
6485 if (p_verbose > 0)
Bram Moolenaar32526b32019-01-19 17:43:09 +01006486 verb_msg(_("XSMP lost ICE connection"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00006487 xsmp_close();
6488 if (--ret == 0)
Bram Moolenaar0f873732019-12-05 20:28:46 +01006489 finished = FALSE; // keep going if event was only one
Bram Moolenaar071d4272004-06-13 20:20:40 +00006490 }
6491 else if (FD_ISSET(xsmp_icefd, &rfds))
6492 {
6493 busy = TRUE;
6494 xsmp_handle_requests();
6495 busy = FALSE;
6496 if (--ret == 0)
Bram Moolenaar0f873732019-12-05 20:28:46 +01006497 finished = FALSE; // keep going if event was only one
Bram Moolenaar071d4272004-06-13 20:20:40 +00006498 }
6499 }
6500# endif
Bram Moolenaar509ce2a2016-03-11 22:52:15 +01006501#ifdef FEAT_JOB_CHANNEL
Bram Moolenaar0f873732019-12-05 20:28:46 +01006502 // also call when ret == 0, we may be polling a keep-open channel
Bram Moolenaarf3360612017-10-01 16:21:31 +02006503 if (ret >= 0)
Bram Moolenaar8b877ac2016-03-28 19:16:20 +02006504 ret = channel_select_check(ret, &rfds, &wfds);
Bram Moolenaar67c53842010-05-22 18:28:27 +02006505#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006506
Bram Moolenaar0f873732019-12-05 20:28:46 +01006507#endif // HAVE_SELECT
Bram Moolenaar071d4272004-06-13 20:20:40 +00006508
6509#ifdef MAY_LOOP
6510 if (finished || msec == 0)
6511 break;
6512
Bram Moolenaar93c88e02015-09-15 14:12:05 +02006513# ifdef FEAT_CLIENTSERVER
6514 if (server_waiting())
6515 break;
6516# endif
6517
Bram Moolenaar0f873732019-12-05 20:28:46 +01006518 // We're going to loop around again, find out for how long
Bram Moolenaar071d4272004-06-13 20:20:40 +00006519 if (msec > 0)
6520 {
Bram Moolenaar833eb1d2016-11-24 17:22:50 +01006521# ifdef ELAPSED_FUNC
Bram Moolenaar0f873732019-12-05 20:28:46 +01006522 // Compute remaining wait time.
Bram Moolenaar833eb1d2016-11-24 17:22:50 +01006523 msec = start_msec - ELAPSED_FUNC(start_tv);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006524# else
Bram Moolenaar0f873732019-12-05 20:28:46 +01006525 // Guess we got interrupted halfway.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006526 msec = msec / 2;
6527# endif
6528 if (msec <= 0)
Bram Moolenaar0f873732019-12-05 20:28:46 +01006529 break; // waited long enough
Bram Moolenaar071d4272004-06-13 20:20:40 +00006530 }
6531#endif
6532 }
6533
Bram Moolenaarec70bdd2016-02-18 22:17:42 +01006534 return result;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006535}
6536
Bram Moolenaar071d4272004-06-13 20:20:40 +00006537/*
Bram Moolenaar02743632005-07-25 20:42:36 +00006538 * Expand a path into all matching files and/or directories. Handles "*",
6539 * "?", "[a-z]", "**", etc.
6540 * "path" has backslashes before chars that are not to be expanded.
6541 * Returns the number of matches found.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006542 */
6543 int
Bram Moolenaar05540972016-01-30 20:31:25 +01006544mch_expandpath(
6545 garray_T *gap,
6546 char_u *path,
Bram Moolenaar0f873732019-12-05 20:28:46 +01006547 int flags) // EW_* flags
Bram Moolenaar071d4272004-06-13 20:20:40 +00006548{
Bram Moolenaar02743632005-07-25 20:42:36 +00006549 return unix_expandpath(gap, path, 0, flags, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006550}
Bram Moolenaar071d4272004-06-13 20:20:40 +00006551
6552/*
6553 * mch_expand_wildcards() - this code does wild-card pattern matching using
6554 * the shell
6555 *
6556 * return OK for success, FAIL for error (you may lose some memory) and put
6557 * an error message in *file.
6558 *
6559 * num_pat is number of input patterns
6560 * pat is array of pointers to input patterns
6561 * num_file is pointer to number of matched file names
6562 * file is pointer to array of pointers to matched file names
6563 */
6564
6565#ifndef SEEK_SET
6566# define SEEK_SET 0
6567#endif
6568#ifndef SEEK_END
6569# define SEEK_END 2
6570#endif
6571
Bram Moolenaar5555acc2006-04-07 21:33:12 +00006572#define SHELL_SPECIAL (char_u *)"\t \"&'$;<>()\\|"
Bram Moolenaar316059c2006-01-14 21:18:42 +00006573
Bram Moolenaar071d4272004-06-13 20:20:40 +00006574 int
Bram Moolenaar05540972016-01-30 20:31:25 +01006575mch_expand_wildcards(
6576 int num_pat,
6577 char_u **pat,
6578 int *num_file,
6579 char_u ***file,
Bram Moolenaar0f873732019-12-05 20:28:46 +01006580 int flags) // EW_* flags
Bram Moolenaar071d4272004-06-13 20:20:40 +00006581{
6582 int i;
6583 size_t len;
Bram Moolenaar85325f82017-03-30 21:18:45 +02006584 long llen;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006585 char_u *p;
6586 int dir;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006587
Bram Moolenaarc7247912008-01-13 12:54:11 +00006588 /*
6589 * This is the non-OS/2 implementation (really Unix).
6590 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006591 int j;
6592 char_u *tempname;
6593 char_u *command;
6594 FILE *fd;
6595 char_u *buffer;
Bram Moolenaar0f873732019-12-05 20:28:46 +01006596#define STYLE_ECHO 0 // use "echo", the default
6597#define STYLE_GLOB 1 // use "glob", for csh
6598#define STYLE_VIMGLOB 2 // use "vimglob", for Posix sh
6599#define STYLE_PRINT 3 // use "print -N", for zsh
6600#define STYLE_BT 4 // `cmd` expansion, execute the pattern
6601 // directly
Bram Moolenaar071d4272004-06-13 20:20:40 +00006602 int shell_style = STYLE_ECHO;
6603 int check_spaces;
6604 static int did_find_nul = FALSE;
Bram Moolenaarbdace832019-03-02 10:13:42 +01006605 int ampersand = FALSE;
Bram Moolenaar0f873732019-12-05 20:28:46 +01006606 // vimglob() function to define for Posix shell
Bram Moolenaar3577c6f2008-06-24 21:16:56 +00006607 static char *sh_vimglob_func = "vimglob() { while [ $# -ge 1 ]; do echo \"$1\"; shift; done }; vimglob >";
Bram Moolenaar071d4272004-06-13 20:20:40 +00006608
Bram Moolenaar0f873732019-12-05 20:28:46 +01006609 *num_file = 0; // default: no files found
Bram Moolenaar071d4272004-06-13 20:20:40 +00006610 *file = NULL;
6611
6612 /*
6613 * If there are no wildcards, just copy the names to allocated memory.
6614 * Saves a lot of time, because we don't have to start a new shell.
6615 */
6616 if (!have_wildcard(num_pat, pat))
6617 return save_patterns(num_pat, pat, num_file, file);
6618
Bram Moolenaar0e634da2005-07-20 21:57:28 +00006619# ifdef HAVE_SANDBOX
Bram Moolenaar0f873732019-12-05 20:28:46 +01006620 // Don't allow any shell command in the sandbox.
Bram Moolenaar0e634da2005-07-20 21:57:28 +00006621 if (sandbox != 0 && check_secure())
6622 return FAIL;
6623# endif
6624
Bram Moolenaar071d4272004-06-13 20:20:40 +00006625 /*
6626 * Don't allow the use of backticks in secure and restricted mode.
6627 */
Bram Moolenaar0e634da2005-07-20 21:57:28 +00006628 if (secure || restricted)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006629 for (i = 0; i < num_pat; ++i)
6630 if (vim_strchr(pat[i], '`') != NULL
6631 && (check_restricted() || check_secure()))
6632 return FAIL;
6633
6634 /*
6635 * get a name for the temp file
6636 */
Bram Moolenaare5c421c2015-03-31 13:33:08 +02006637 if ((tempname = vim_tempname('o', FALSE)) == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006638 {
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00006639 emsg(_(e_cant_get_temp_file_name));
Bram Moolenaar071d4272004-06-13 20:20:40 +00006640 return FAIL;
6641 }
6642
6643 /*
6644 * Let the shell expand the patterns and write the result into the temp
Bram Moolenaarc7247912008-01-13 12:54:11 +00006645 * file.
6646 * STYLE_BT: NL separated
6647 * If expanding `cmd` execute it directly.
6648 * STYLE_GLOB: NUL separated
6649 * If we use *csh, "glob" will work better than "echo".
6650 * STYLE_PRINT: NL or NUL separated
6651 * If we use *zsh, "print -N" will work better than "glob".
6652 * STYLE_VIMGLOB: NL separated
6653 * If we use *sh*, we define "vimglob()".
6654 * STYLE_ECHO: space separated.
6655 * A shell we don't know, stay safe and use "echo".
Bram Moolenaar071d4272004-06-13 20:20:40 +00006656 */
6657 if (num_pat == 1 && *pat[0] == '`'
6658 && (len = STRLEN(pat[0])) > 2
6659 && *(pat[0] + len - 1) == '`')
6660 shell_style = STYLE_BT;
6661 else if ((len = STRLEN(p_sh)) >= 3)
6662 {
6663 if (STRCMP(p_sh + len - 3, "csh") == 0)
6664 shell_style = STYLE_GLOB;
6665 else if (STRCMP(p_sh + len - 3, "zsh") == 0)
6666 shell_style = STYLE_PRINT;
6667 }
Bram Moolenaarc7247912008-01-13 12:54:11 +00006668 if (shell_style == STYLE_ECHO && strstr((char *)gettail(p_sh),
6669 "sh") != NULL)
6670 shell_style = STYLE_VIMGLOB;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006671
Bram Moolenaar0f873732019-12-05 20:28:46 +01006672 // Compute the length of the command. We need 2 extra bytes: for the
6673 // optional '&' and for the NUL.
6674 // Worst case: "unset nonomatch; print -N >" plus two is 29
Bram Moolenaar071d4272004-06-13 20:20:40 +00006675 len = STRLEN(tempname) + 29;
Bram Moolenaarc7247912008-01-13 12:54:11 +00006676 if (shell_style == STYLE_VIMGLOB)
6677 len += STRLEN(sh_vimglob_func);
6678
Bram Moolenaarb23c3382005-01-31 19:09:12 +00006679 for (i = 0; i < num_pat; ++i)
6680 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01006681 // Count the length of the patterns in the same way as they are put in
6682 // "command" below.
Bram Moolenaarb23c3382005-01-31 19:09:12 +00006683#ifdef USE_SYSTEM
Bram Moolenaar0f873732019-12-05 20:28:46 +01006684 len += STRLEN(pat[i]) + 3; // add space and two quotes
Bram Moolenaarb23c3382005-01-31 19:09:12 +00006685#else
Bram Moolenaar0f873732019-12-05 20:28:46 +01006686 ++len; // add space
Bram Moolenaar316059c2006-01-14 21:18:42 +00006687 for (j = 0; pat[i][j] != NUL; ++j)
6688 {
6689 if (vim_strchr(SHELL_SPECIAL, pat[i][j]) != NULL)
Bram Moolenaar0f873732019-12-05 20:28:46 +01006690 ++len; // may add a backslash
Bram Moolenaar316059c2006-01-14 21:18:42 +00006691 ++len;
6692 }
Bram Moolenaarb23c3382005-01-31 19:09:12 +00006693#endif
6694 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006695 command = alloc(len);
6696 if (command == NULL)
6697 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01006698 // out of memory
Bram Moolenaar071d4272004-06-13 20:20:40 +00006699 vim_free(tempname);
6700 return FAIL;
6701 }
6702
6703 /*
6704 * Build the shell command:
6705 * - Set $nonomatch depending on EW_NOTFOUND (hopefully the shell
6706 * recognizes this).
6707 * - Add the shell command to print the expanded names.
6708 * - Add the temp file name.
6709 * - Add the file name patterns.
6710 */
6711 if (shell_style == STYLE_BT)
6712 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01006713 // change `command; command& ` to (command; command )
Bram Moolenaar316059c2006-01-14 21:18:42 +00006714 STRCPY(command, "(");
Bram Moolenaar0f873732019-12-05 20:28:46 +01006715 STRCAT(command, pat[0] + 1); // exclude first backtick
Bram Moolenaar071d4272004-06-13 20:20:40 +00006716 p = command + STRLEN(command) - 1;
Bram Moolenaar0f873732019-12-05 20:28:46 +01006717 *p-- = ')'; // remove last backtick
Bram Moolenaar1c465442017-03-12 20:10:05 +01006718 while (p > command && VIM_ISWHITE(*p))
Bram Moolenaar071d4272004-06-13 20:20:40 +00006719 --p;
Bram Moolenaar0f873732019-12-05 20:28:46 +01006720 if (*p == '&') // remove trailing '&'
Bram Moolenaar071d4272004-06-13 20:20:40 +00006721 {
Bram Moolenaarbdace832019-03-02 10:13:42 +01006722 ampersand = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006723 *p = ' ';
6724 }
6725 STRCAT(command, ">");
6726 }
6727 else
6728 {
Christian Brabandt8b8d8292021-11-19 12:37:36 +00006729 STRCPY(command, "");
6730 if (shell_style == STYLE_GLOB)
6731 {
6732 // Assume the nonomatch option is valid only for csh like shells,
6733 // otherwise, this may set the positional parameters for the shell,
6734 // e.g. "$*".
6735 if (flags & EW_NOTFOUND)
6736 STRCAT(command, "set nonomatch; ");
6737 else
6738 STRCAT(command, "unset nonomatch; ");
6739 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006740 if (shell_style == STYLE_GLOB)
6741 STRCAT(command, "glob >");
6742 else if (shell_style == STYLE_PRINT)
6743 STRCAT(command, "print -N >");
Bram Moolenaarc7247912008-01-13 12:54:11 +00006744 else if (shell_style == STYLE_VIMGLOB)
6745 STRCAT(command, sh_vimglob_func);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006746 else
6747 STRCAT(command, "echo >");
6748 }
Bram Moolenaarc7247912008-01-13 12:54:11 +00006749
Bram Moolenaar071d4272004-06-13 20:20:40 +00006750 STRCAT(command, tempname);
Bram Moolenaarc7247912008-01-13 12:54:11 +00006751
Bram Moolenaar071d4272004-06-13 20:20:40 +00006752 if (shell_style != STYLE_BT)
6753 for (i = 0; i < num_pat; ++i)
6754 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01006755 // When using system() always add extra quotes, because the shell
6756 // is started twice. Otherwise put a backslash before special
6757 // characters, except inside ``.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006758#ifdef USE_SYSTEM
6759 STRCAT(command, " \"");
6760 STRCAT(command, pat[i]);
6761 STRCAT(command, "\"");
6762#else
Bram Moolenaar582fd852005-03-28 20:58:01 +00006763 int intick = FALSE;
6764
Bram Moolenaar071d4272004-06-13 20:20:40 +00006765 p = command + STRLEN(command);
6766 *p++ = ' ';
Bram Moolenaar316059c2006-01-14 21:18:42 +00006767 for (j = 0; pat[i][j] != NUL; ++j)
Bram Moolenaar582fd852005-03-28 20:58:01 +00006768 {
6769 if (pat[i][j] == '`')
Bram Moolenaar582fd852005-03-28 20:58:01 +00006770 intick = !intick;
Bram Moolenaar316059c2006-01-14 21:18:42 +00006771 else if (pat[i][j] == '\\' && pat[i][j + 1] != NUL)
6772 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01006773 // Remove a backslash, take char literally. But keep
6774 // backslash inside backticks, before a special character
6775 // and before a backtick.
Bram Moolenaard12f5c12006-01-25 22:10:52 +00006776 if (intick
Bram Moolenaar49315f62006-02-04 00:54:59 +00006777 || vim_strchr(SHELL_SPECIAL, pat[i][j + 1]) != NULL
6778 || pat[i][j + 1] == '`')
Bram Moolenaard12f5c12006-01-25 22:10:52 +00006779 *p++ = '\\';
Bram Moolenaar280f1262006-01-30 00:14:18 +00006780 ++j;
Bram Moolenaar316059c2006-01-14 21:18:42 +00006781 }
Bram Moolenaare4df1642014-08-29 12:58:44 +02006782 else if (!intick
6783 && ((flags & EW_KEEPDOLLAR) == 0 || pat[i][j] != '$')
6784 && vim_strchr(SHELL_SPECIAL, pat[i][j]) != NULL)
Bram Moolenaar0f873732019-12-05 20:28:46 +01006785 // Put a backslash before a special character, but not
6786 // when inside ``. And not for $var when EW_KEEPDOLLAR is
6787 // set.
Bram Moolenaar316059c2006-01-14 21:18:42 +00006788 *p++ = '\\';
Bram Moolenaar280f1262006-01-30 00:14:18 +00006789
Bram Moolenaar0f873732019-12-05 20:28:46 +01006790 // Copy one character.
Bram Moolenaar280f1262006-01-30 00:14:18 +00006791 *p++ = pat[i][j];
Bram Moolenaar582fd852005-03-28 20:58:01 +00006792 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006793 *p = NUL;
6794#endif
6795 }
6796 if (flags & EW_SILENT)
6797 show_shell_mess = FALSE;
Bram Moolenaarbdace832019-03-02 10:13:42 +01006798 if (ampersand)
Bram Moolenaar0f873732019-12-05 20:28:46 +01006799 STRCAT(command, "&"); // put the '&' after the redirection
Bram Moolenaar071d4272004-06-13 20:20:40 +00006800
6801 /*
6802 * Using zsh -G: If a pattern has no matches, it is just deleted from
6803 * the argument list, otherwise zsh gives an error message and doesn't
6804 * expand any other pattern.
6805 */
6806 if (shell_style == STYLE_PRINT)
Bram Moolenaar0f873732019-12-05 20:28:46 +01006807 extra_shell_arg = (char_u *)"-G"; // Use zsh NULL_GLOB option
Bram Moolenaar071d4272004-06-13 20:20:40 +00006808
6809 /*
6810 * If we use -f then shell variables set in .cshrc won't get expanded.
6811 * vi can do it, so we will too, but it is only necessary if there is a "$"
6812 * in one of the patterns, otherwise we can still use the fast option.
6813 */
6814 else if (shell_style == STYLE_GLOB && !have_dollars(num_pat, pat))
Bram Moolenaar0f873732019-12-05 20:28:46 +01006815 extra_shell_arg = (char_u *)"-f"; // Use csh fast option
Bram Moolenaar071d4272004-06-13 20:20:40 +00006816
6817 /*
6818 * execute the shell command
6819 */
6820 i = call_shell(command, SHELL_EXPAND | SHELL_SILENT);
6821
Bram Moolenaar0f873732019-12-05 20:28:46 +01006822 // When running in the background, give it some time to create the temp
6823 // file, but don't wait for it to finish.
Bram Moolenaarbdace832019-03-02 10:13:42 +01006824 if (ampersand)
Bram Moolenaar0981c872020-08-23 14:28:37 +02006825 mch_delay(10L, MCH_DELAY_IGNOREINPUT);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006826
Bram Moolenaar0f873732019-12-05 20:28:46 +01006827 extra_shell_arg = NULL; // cleanup
Bram Moolenaar071d4272004-06-13 20:20:40 +00006828 show_shell_mess = TRUE;
6829 vim_free(command);
6830
Bram Moolenaar0f873732019-12-05 20:28:46 +01006831 if (i != 0) // mch_call_shell() failed
Bram Moolenaar071d4272004-06-13 20:20:40 +00006832 {
6833 mch_remove(tempname);
6834 vim_free(tempname);
6835 /*
6836 * With interactive completion, the error message is not printed.
6837 * However with USE_SYSTEM, I don't know how to turn off error messages
6838 * from the shell, so screen may still get messed up -- webb.
6839 */
6840#ifndef USE_SYSTEM
6841 if (!(flags & EW_SILENT))
6842#endif
6843 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01006844 redraw_later_clear(); // probably messed up screen
6845 msg_putchar('\n'); // clear bottom line quickly
6846 cmdline_row = Rows - 1; // continue on last line
Bram Moolenaar071d4272004-06-13 20:20:40 +00006847#ifdef USE_SYSTEM
6848 if (!(flags & EW_SILENT))
6849#endif
6850 {
Bram Moolenaar40bcec12021-12-05 22:19:27 +00006851 msg(_(e_cannot_expand_wildcards));
Bram Moolenaar0f873732019-12-05 20:28:46 +01006852 msg_start(); // don't overwrite this message
Bram Moolenaar071d4272004-06-13 20:20:40 +00006853 }
6854 }
Bram Moolenaar0f873732019-12-05 20:28:46 +01006855 // If a `cmd` expansion failed, don't list `cmd` as a match, even when
6856 // EW_NOTFOUND is given
Bram Moolenaar071d4272004-06-13 20:20:40 +00006857 if (shell_style == STYLE_BT)
6858 return FAIL;
6859 goto notfound;
6860 }
6861
6862 /*
6863 * read the names from the file into memory
6864 */
6865 fd = fopen((char *)tempname, READBIN);
6866 if (fd == NULL)
6867 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01006868 // Something went wrong, perhaps a file name with a special char.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006869 if (!(flags & EW_SILENT))
6870 {
Bram Moolenaar40bcec12021-12-05 22:19:27 +00006871 msg(_(e_cannot_expand_wildcards));
Bram Moolenaar0f873732019-12-05 20:28:46 +01006872 msg_start(); // don't overwrite this message
Bram Moolenaar071d4272004-06-13 20:20:40 +00006873 }
6874 vim_free(tempname);
6875 goto notfound;
6876 }
6877 fseek(fd, 0L, SEEK_END);
Bram Moolenaar0f873732019-12-05 20:28:46 +01006878 llen = ftell(fd); // get size of temp file
Bram Moolenaar071d4272004-06-13 20:20:40 +00006879 fseek(fd, 0L, SEEK_SET);
Bram Moolenaar85325f82017-03-30 21:18:45 +02006880 if (llen < 0)
Bram Moolenaar0f873732019-12-05 20:28:46 +01006881 // just in case ftell() would fail
Bram Moolenaar85325f82017-03-30 21:18:45 +02006882 buffer = NULL;
6883 else
6884 buffer = alloc(llen + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006885 if (buffer == NULL)
6886 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01006887 // out of memory
Bram Moolenaar071d4272004-06-13 20:20:40 +00006888 mch_remove(tempname);
6889 vim_free(tempname);
6890 fclose(fd);
6891 return FAIL;
6892 }
Bram Moolenaar85325f82017-03-30 21:18:45 +02006893 len = llen;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006894 i = fread((char *)buffer, 1, len, fd);
6895 fclose(fd);
6896 mch_remove(tempname);
Bram Moolenaar78a15312009-05-15 19:33:18 +00006897 if (i != (int)len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006898 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01006899 // unexpected read error
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00006900 semsg(_(e_cant_read_file_str), tempname);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006901 vim_free(tempname);
6902 vim_free(buffer);
6903 return FAIL;
6904 }
6905 vim_free(tempname);
6906
Bram Moolenaar1eed5322019-02-26 17:03:54 +01006907# ifdef __CYGWIN__
Bram Moolenaar0f873732019-12-05 20:28:46 +01006908 // Translate <CR><NL> into <NL>. Caution, buffer may contain NUL.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006909 p = buffer;
Bram Moolenaarfe17e762013-06-29 14:17:02 +02006910 for (i = 0; i < (int)len; ++i)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006911 if (!(buffer[i] == CAR && buffer[i + 1] == NL))
6912 *p++ = buffer[i];
6913 len = p - buffer;
6914# endif
6915
6916
Bram Moolenaar0f873732019-12-05 20:28:46 +01006917 // file names are separated with Space
Bram Moolenaar071d4272004-06-13 20:20:40 +00006918 if (shell_style == STYLE_ECHO)
6919 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01006920 buffer[len] = '\n'; // make sure the buffer ends in NL
Bram Moolenaar071d4272004-06-13 20:20:40 +00006921 p = buffer;
Bram Moolenaar0f873732019-12-05 20:28:46 +01006922 for (i = 0; *p != '\n'; ++i) // count number of entries
Bram Moolenaar071d4272004-06-13 20:20:40 +00006923 {
6924 while (*p != ' ' && *p != '\n')
6925 ++p;
Bram Moolenaar0f873732019-12-05 20:28:46 +01006926 p = skipwhite(p); // skip to next entry
Bram Moolenaar071d4272004-06-13 20:20:40 +00006927 }
6928 }
Bram Moolenaar0f873732019-12-05 20:28:46 +01006929 // file names are separated with NL
Bram Moolenaarc7247912008-01-13 12:54:11 +00006930 else if (shell_style == STYLE_BT || shell_style == STYLE_VIMGLOB)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006931 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01006932 buffer[len] = NUL; // make sure the buffer ends in NUL
Bram Moolenaar071d4272004-06-13 20:20:40 +00006933 p = buffer;
Bram Moolenaar0f873732019-12-05 20:28:46 +01006934 for (i = 0; *p != NUL; ++i) // count number of entries
Bram Moolenaar071d4272004-06-13 20:20:40 +00006935 {
6936 while (*p != '\n' && *p != NUL)
6937 ++p;
6938 if (*p != NUL)
6939 ++p;
Bram Moolenaar0f873732019-12-05 20:28:46 +01006940 p = skipwhite(p); // skip leading white space
Bram Moolenaar071d4272004-06-13 20:20:40 +00006941 }
6942 }
Bram Moolenaar0f873732019-12-05 20:28:46 +01006943 // file names are separated with NUL
Bram Moolenaar071d4272004-06-13 20:20:40 +00006944 else
6945 {
6946 /*
6947 * Some versions of zsh use spaces instead of NULs to separate
6948 * results. Only do this when there is no NUL before the end of the
6949 * buffer, otherwise we would never be able to use file names with
6950 * embedded spaces when zsh does use NULs.
6951 * When we found a NUL once, we know zsh is OK, set did_find_nul and
6952 * don't check for spaces again.
6953 */
6954 check_spaces = FALSE;
6955 if (shell_style == STYLE_PRINT && !did_find_nul)
6956 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01006957 // If there is a NUL, set did_find_nul, else set check_spaces
Bram Moolenaaref9d6aa2011-04-11 16:56:35 +02006958 buffer[len] = NUL;
Bram Moolenaarb011af92013-12-11 13:21:51 +01006959 if (len && (int)STRLEN(buffer) < (int)len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006960 did_find_nul = TRUE;
6961 else
6962 check_spaces = TRUE;
6963 }
6964
6965 /*
6966 * Make sure the buffer ends with a NUL. For STYLE_PRINT there
6967 * already is one, for STYLE_GLOB it needs to be added.
6968 */
6969 if (len && buffer[len - 1] == NUL)
6970 --len;
6971 else
6972 buffer[len] = NUL;
6973 i = 0;
6974 for (p = buffer; p < buffer + len; ++p)
Bram Moolenaar0f873732019-12-05 20:28:46 +01006975 if (*p == NUL || (*p == ' ' && check_spaces)) // count entry
Bram Moolenaar071d4272004-06-13 20:20:40 +00006976 {
6977 ++i;
6978 *p = NUL;
6979 }
6980 if (len)
Bram Moolenaar0f873732019-12-05 20:28:46 +01006981 ++i; // count last entry
Bram Moolenaar071d4272004-06-13 20:20:40 +00006982 }
6983 if (i == 0)
6984 {
6985 /*
6986 * Can happen when using /bin/sh and typing ":e $NO_SUCH_VAR^I".
6987 * /bin/sh will happily expand it to nothing rather than returning an
6988 * error; and hey, it's good to check anyway -- webb.
6989 */
6990 vim_free(buffer);
6991 goto notfound;
6992 }
6993 *num_file = i;
Bram Moolenaarc799fe22019-05-28 23:08:19 +02006994 *file = ALLOC_MULT(char_u *, i);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006995 if (*file == NULL)
6996 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01006997 // out of memory
Bram Moolenaar071d4272004-06-13 20:20:40 +00006998 vim_free(buffer);
6999 return FAIL;
7000 }
7001
7002 /*
7003 * Isolate the individual file names.
7004 */
7005 p = buffer;
7006 for (i = 0; i < *num_file; ++i)
7007 {
7008 (*file)[i] = p;
Bram Moolenaar0f873732019-12-05 20:28:46 +01007009 // Space or NL separates
Bram Moolenaarc7247912008-01-13 12:54:11 +00007010 if (shell_style == STYLE_ECHO || shell_style == STYLE_BT
7011 || shell_style == STYLE_VIMGLOB)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007012 {
Bram Moolenaar49315f62006-02-04 00:54:59 +00007013 while (!(shell_style == STYLE_ECHO && *p == ' ')
7014 && *p != '\n' && *p != NUL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007015 ++p;
Bram Moolenaar0f873732019-12-05 20:28:46 +01007016 if (p == buffer + len) // last entry
Bram Moolenaar071d4272004-06-13 20:20:40 +00007017 *p = NUL;
7018 else
7019 {
7020 *p++ = NUL;
Bram Moolenaar0f873732019-12-05 20:28:46 +01007021 p = skipwhite(p); // skip to next entry
Bram Moolenaar071d4272004-06-13 20:20:40 +00007022 }
7023 }
Bram Moolenaar0f873732019-12-05 20:28:46 +01007024 else // NUL separates
Bram Moolenaar071d4272004-06-13 20:20:40 +00007025 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01007026 while (*p && p < buffer + len) // skip entry
Bram Moolenaar071d4272004-06-13 20:20:40 +00007027 ++p;
Bram Moolenaar0f873732019-12-05 20:28:46 +01007028 ++p; // skip NUL
Bram Moolenaar071d4272004-06-13 20:20:40 +00007029 }
7030 }
7031
7032 /*
7033 * Move the file names to allocated memory.
7034 */
7035 for (j = 0, i = 0; i < *num_file; ++i)
7036 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01007037 // Require the files to exist. Helps when using /bin/sh
Bram Moolenaar071d4272004-06-13 20:20:40 +00007038 if (!(flags & EW_NOTFOUND) && mch_getperm((*file)[i]) < 0)
7039 continue;
7040
Bram Moolenaar0f873732019-12-05 20:28:46 +01007041 // check if this entry should be included
Bram Moolenaar071d4272004-06-13 20:20:40 +00007042 dir = (mch_isdir((*file)[i]));
7043 if ((dir && !(flags & EW_DIR)) || (!dir && !(flags & EW_FILE)))
7044 continue;
7045
Bram Moolenaar0f873732019-12-05 20:28:46 +01007046 // Skip files that are not executable if we check for that.
Bram Moolenaarb5971142015-03-21 17:32:19 +01007047 if (!dir && (flags & EW_EXEC)
7048 && !mch_can_exe((*file)[i], NULL, !(flags & EW_SHELLCMD)))
Bram Moolenaara2031822006-03-07 22:29:51 +00007049 continue;
7050
Bram Moolenaar964b3742019-05-24 18:54:09 +02007051 p = alloc(STRLEN((*file)[i]) + 1 + dir);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007052 if (p)
7053 {
7054 STRCPY(p, (*file)[i]);
7055 if (dir)
Bram Moolenaar0f873732019-12-05 20:28:46 +01007056 add_pathsep(p); // add '/' to a directory name
Bram Moolenaar071d4272004-06-13 20:20:40 +00007057 (*file)[j++] = p;
7058 }
7059 }
7060 vim_free(buffer);
7061 *num_file = j;
7062
Bram Moolenaar0f873732019-12-05 20:28:46 +01007063 if (*num_file == 0) // rejected all entries
Bram Moolenaar071d4272004-06-13 20:20:40 +00007064 {
Bram Moolenaard23a8232018-02-10 18:45:26 +01007065 VIM_CLEAR(*file);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007066 goto notfound;
7067 }
7068
7069 return OK;
7070
7071notfound:
7072 if (flags & EW_NOTFOUND)
7073 return save_patterns(num_pat, pat, num_file, file);
7074 return FAIL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007075}
7076
Bram Moolenaar0f873732019-12-05 20:28:46 +01007077#endif // VMS
Bram Moolenaar071d4272004-06-13 20:20:40 +00007078
Bram Moolenaar071d4272004-06-13 20:20:40 +00007079 static int
Bram Moolenaar05540972016-01-30 20:31:25 +01007080save_patterns(
7081 int num_pat,
7082 char_u **pat,
7083 int *num_file,
7084 char_u ***file)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007085{
7086 int i;
Bram Moolenaard8b02732005-01-14 21:48:43 +00007087 char_u *s;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007088
Bram Moolenaarc799fe22019-05-28 23:08:19 +02007089 *file = ALLOC_MULT(char_u *, num_pat);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007090 if (*file == NULL)
7091 return FAIL;
7092 for (i = 0; i < num_pat; i++)
Bram Moolenaard8b02732005-01-14 21:48:43 +00007093 {
7094 s = vim_strsave(pat[i]);
7095 if (s != NULL)
Bram Moolenaar0f873732019-12-05 20:28:46 +01007096 // Be compatible with expand_filename(): halve the number of
7097 // backslashes.
Bram Moolenaard8b02732005-01-14 21:48:43 +00007098 backslash_halve(s);
7099 (*file)[i] = s;
7100 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007101 *num_file = num_pat;
7102 return OK;
7103}
Bram Moolenaar071d4272004-06-13 20:20:40 +00007104
Bram Moolenaar071d4272004-06-13 20:20:40 +00007105/*
7106 * Return TRUE if the string "p" contains a wildcard that mch_expandpath() can
7107 * expand.
7108 */
7109 int
Bram Moolenaar05540972016-01-30 20:31:25 +01007110mch_has_exp_wildcard(char_u *p)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007111{
Bram Moolenaar91acfff2017-03-12 19:22:36 +01007112 for ( ; *p; MB_PTR_ADV(p))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007113 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00007114 if (*p == '\\' && p[1] != NUL)
7115 ++p;
7116 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00007117 if (vim_strchr((char_u *)
7118#ifdef VMS
7119 "*?%"
7120#else
Bram Moolenaar071d4272004-06-13 20:20:40 +00007121 "*?[{'"
Bram Moolenaar071d4272004-06-13 20:20:40 +00007122#endif
7123 , *p) != NULL)
7124 return TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007125 }
7126 return FALSE;
7127}
7128
7129/*
7130 * Return TRUE if the string "p" contains a wildcard.
7131 * Don't recognize '~' at the end as a wildcard.
7132 */
7133 int
Bram Moolenaar05540972016-01-30 20:31:25 +01007134mch_has_wildcard(char_u *p)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007135{
Bram Moolenaar91acfff2017-03-12 19:22:36 +01007136 for ( ; *p; MB_PTR_ADV(p))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007137 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00007138 if (*p == '\\' && p[1] != NUL)
7139 ++p;
7140 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00007141 if (vim_strchr((char_u *)
7142#ifdef VMS
7143 "*?%$"
7144#else
Bram Moolenaar071d4272004-06-13 20:20:40 +00007145 "*?[{`'$"
Bram Moolenaar071d4272004-06-13 20:20:40 +00007146#endif
7147 , *p) != NULL
7148 || (*p == '~' && p[1] != NUL))
7149 return TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007150 }
7151 return FALSE;
7152}
7153
Bram Moolenaar071d4272004-06-13 20:20:40 +00007154 static int
Bram Moolenaar05540972016-01-30 20:31:25 +01007155have_wildcard(int num, char_u **file)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007156{
7157 int i;
7158
7159 for (i = 0; i < num; i++)
7160 if (mch_has_wildcard(file[i]))
7161 return 1;
7162 return 0;
7163}
7164
7165 static int
Bram Moolenaar05540972016-01-30 20:31:25 +01007166have_dollars(int num, char_u **file)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007167{
7168 int i;
7169
7170 for (i = 0; i < num; i++)
7171 if (vim_strchr(file[i], '$') != NULL)
7172 return TRUE;
7173 return FALSE;
7174}
Bram Moolenaar071d4272004-06-13 20:20:40 +00007175
Bram Moolenaarfdcc9af2016-02-29 12:52:39 +01007176#if !defined(HAVE_RENAME) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007177/*
7178 * Scaled-down version of rename(), which is missing in Xenix.
7179 * This version can only move regular files and will fail if the
7180 * destination exists.
7181 */
7182 int
Bram Moolenaarfdcc9af2016-02-29 12:52:39 +01007183mch_rename(const char *src, const char *dest)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007184{
7185 struct stat st;
7186
Bram Moolenaar0f873732019-12-05 20:28:46 +01007187 if (stat(dest, &st) >= 0) // fail if destination exists
Bram Moolenaar071d4272004-06-13 20:20:40 +00007188 return -1;
Bram Moolenaar0f873732019-12-05 20:28:46 +01007189 if (link(src, dest) != 0) // link file to new name
Bram Moolenaar071d4272004-06-13 20:20:40 +00007190 return -1;
Bram Moolenaar0f873732019-12-05 20:28:46 +01007191 if (mch_remove(src) == 0) // delete link to old name
Bram Moolenaar071d4272004-06-13 20:20:40 +00007192 return 0;
7193 return -1;
7194}
Bram Moolenaar0f873732019-12-05 20:28:46 +01007195#endif // !HAVE_RENAME
Bram Moolenaar071d4272004-06-13 20:20:40 +00007196
Bram Moolenaar4b8366b2019-05-04 17:34:34 +02007197#if defined(FEAT_MOUSE_GPM) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007198/*
7199 * Initializes connection with gpm (if it isn't already opened)
7200 * Return 1 if succeeded (or connection already opened), 0 if failed
7201 */
7202 static int
Bram Moolenaar05540972016-01-30 20:31:25 +01007203gpm_open(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007204{
Bram Moolenaar0f873732019-12-05 20:28:46 +01007205 static Gpm_Connect gpm_connect; // Must it be kept till closing ?
Bram Moolenaar071d4272004-06-13 20:20:40 +00007206
7207 if (!gpm_flag)
7208 {
7209 gpm_connect.eventMask = (GPM_UP | GPM_DRAG | GPM_DOWN);
7210 gpm_connect.defaultMask = ~GPM_HARD;
Bram Moolenaar0f873732019-12-05 20:28:46 +01007211 // Default handling for mouse move
7212 gpm_connect.minMod = 0; // Handle any modifier keys
Bram Moolenaar071d4272004-06-13 20:20:40 +00007213 gpm_connect.maxMod = 0xffff;
7214 if (Gpm_Open(&gpm_connect, 0) > 0)
7215 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01007216 // gpm library tries to handling TSTP causes
7217 // problems. Anyways, we close connection to Gpm whenever
7218 // we are going to suspend or starting an external process
7219 // so we shouldn't have problem with this
Bram Moolenaar76243bd2009-03-02 01:47:02 +00007220# ifdef SIGTSTP
dbivolaruab16ad32021-12-29 19:41:47 +00007221 signal(SIGTSTP, restricted ? SIG_IGN : (RETSIGTYPE (*)())sig_tstp);
Bram Moolenaar76243bd2009-03-02 01:47:02 +00007222# endif
Bram Moolenaar0f873732019-12-05 20:28:46 +01007223 return 1; // succeed
Bram Moolenaar071d4272004-06-13 20:20:40 +00007224 }
7225 if (gpm_fd == -2)
Bram Moolenaar0f873732019-12-05 20:28:46 +01007226 Gpm_Close(); // We don't want to talk to xterm via gpm
Bram Moolenaar071d4272004-06-13 20:20:40 +00007227 return 0;
7228 }
Bram Moolenaar0f873732019-12-05 20:28:46 +01007229 return 1; // already open
Bram Moolenaar071d4272004-06-13 20:20:40 +00007230}
7231
7232/*
Bram Moolenaar4b8366b2019-05-04 17:34:34 +02007233 * Returns TRUE if the GPM mouse is enabled.
7234 */
7235 int
7236gpm_enabled(void)
7237{
7238 return gpm_flag && gpm_fd >= 0;
7239}
7240
7241/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00007242 * Closes connection to gpm
Bram Moolenaar071d4272004-06-13 20:20:40 +00007243 */
7244 static void
Bram Moolenaar05540972016-01-30 20:31:25 +01007245gpm_close(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007246{
Bram Moolenaar4b8366b2019-05-04 17:34:34 +02007247 if (gpm_enabled())
Bram Moolenaar071d4272004-06-13 20:20:40 +00007248 Gpm_Close();
7249}
7250
Bram Moolenaarbedf0912019-05-04 16:58:45 +02007251/*
7252 * Reads gpm event and adds special keys to input buf. Returns length of
Bram Moolenaar071d4272004-06-13 20:20:40 +00007253 * generated key sequence.
Bram Moolenaarc7f02552014-04-01 21:00:59 +02007254 * This function is styled after gui_send_mouse_event().
Bram Moolenaar071d4272004-06-13 20:20:40 +00007255 */
7256 static int
Bram Moolenaar05540972016-01-30 20:31:25 +01007257mch_gpm_process(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007258{
7259 int button;
7260 static Gpm_Event gpm_event;
7261 char_u string[6];
7262 int_u vim_modifiers;
7263 int row,col;
7264 unsigned char buttons_mask;
7265 unsigned char gpm_modifiers;
7266 static unsigned char old_buttons = 0;
7267
7268 Gpm_GetEvent(&gpm_event);
7269
7270#ifdef FEAT_GUI
Bram Moolenaar0f873732019-12-05 20:28:46 +01007271 // Don't put events in the input queue now.
Bram Moolenaar071d4272004-06-13 20:20:40 +00007272 if (hold_gui_events)
7273 return 0;
7274#endif
7275
7276 row = gpm_event.y - 1;
7277 col = gpm_event.x - 1;
7278
Bram Moolenaar0f873732019-12-05 20:28:46 +01007279 string[0] = ESC; // Our termcode
Bram Moolenaar071d4272004-06-13 20:20:40 +00007280 string[1] = 'M';
7281 string[2] = 'G';
7282 switch (GPM_BARE_EVENTS(gpm_event.type))
7283 {
7284 case GPM_DRAG:
7285 string[3] = MOUSE_DRAG;
7286 break;
7287 case GPM_DOWN:
7288 buttons_mask = gpm_event.buttons & ~old_buttons;
7289 old_buttons = gpm_event.buttons;
7290 switch (buttons_mask)
7291 {
7292 case GPM_B_LEFT:
7293 button = MOUSE_LEFT;
7294 break;
7295 case GPM_B_MIDDLE:
7296 button = MOUSE_MIDDLE;
7297 break;
7298 case GPM_B_RIGHT:
7299 button = MOUSE_RIGHT;
7300 break;
7301 default:
7302 return 0;
Bram Moolenaar0f873732019-12-05 20:28:46 +01007303 // Don't know what to do. Can more than one button be
7304 // reported in one event?
Bram Moolenaar071d4272004-06-13 20:20:40 +00007305 }
7306 string[3] = (char_u)(button | 0x20);
7307 SET_NUM_MOUSE_CLICKS(string[3], gpm_event.clicks + 1);
7308 break;
7309 case GPM_UP:
7310 string[3] = MOUSE_RELEASE;
7311 old_buttons &= ~gpm_event.buttons;
7312 break;
7313 default:
7314 return 0;
7315 }
Bram Moolenaar0f873732019-12-05 20:28:46 +01007316 // This code is based on gui_x11_mouse_cb in gui_x11.c
Bram Moolenaar071d4272004-06-13 20:20:40 +00007317 gpm_modifiers = gpm_event.modifiers;
7318 vim_modifiers = 0x0;
Bram Moolenaar0f873732019-12-05 20:28:46 +01007319 // I ignore capslock stats. Aren't we all just hate capslock mixing with
7320 // Vim commands ? Besides, gpm_event.modifiers is unsigned char, and
7321 // K_CAPSSHIFT is defined 8, so it probably isn't even reported
Bram Moolenaar071d4272004-06-13 20:20:40 +00007322 if (gpm_modifiers & ((1 << KG_SHIFT) | (1 << KG_SHIFTR) | (1 << KG_SHIFTL)))
7323 vim_modifiers |= MOUSE_SHIFT;
7324
7325 if (gpm_modifiers & ((1 << KG_CTRL) | (1 << KG_CTRLR) | (1 << KG_CTRLL)))
7326 vim_modifiers |= MOUSE_CTRL;
7327 if (gpm_modifiers & ((1 << KG_ALT) | (1 << KG_ALTGR)))
7328 vim_modifiers |= MOUSE_ALT;
7329 string[3] |= vim_modifiers;
7330 string[4] = (char_u)(col + ' ' + 1);
7331 string[5] = (char_u)(row + ' ' + 1);
7332 add_to_input_buf(string, 6);
7333 return 6;
7334}
Bram Moolenaar0f873732019-12-05 20:28:46 +01007335#endif // FEAT_MOUSE_GPM
Bram Moolenaar071d4272004-06-13 20:20:40 +00007336
Bram Moolenaar3577c6f2008-06-24 21:16:56 +00007337#ifdef FEAT_SYSMOUSE
7338/*
7339 * Initialize connection with sysmouse.
7340 * Let virtual console inform us with SIGUSR2 for pending sysmouse
7341 * output, any sysmouse output than will be processed via sig_sysmouse().
7342 * Return OK if succeeded, FAIL if failed.
7343 */
7344 static int
Bram Moolenaar05540972016-01-30 20:31:25 +01007345sysmouse_open(void)
Bram Moolenaar3577c6f2008-06-24 21:16:56 +00007346{
7347 struct mouse_info mouse;
7348
7349 mouse.operation = MOUSE_MODE;
7350 mouse.u.mode.mode = 0;
7351 mouse.u.mode.signal = SIGUSR2;
7352 if (ioctl(1, CONS_MOUSECTL, &mouse) != -1)
7353 {
7354 signal(SIGUSR2, (RETSIGTYPE (*)())sig_sysmouse);
7355 mouse.operation = MOUSE_SHOW;
7356 ioctl(1, CONS_MOUSECTL, &mouse);
7357 return OK;
7358 }
7359 return FAIL;
7360}
7361
7362/*
7363 * Stop processing SIGUSR2 signals, and also make sure that
7364 * virtual console do not send us any sysmouse related signal.
7365 */
7366 static void
Bram Moolenaar05540972016-01-30 20:31:25 +01007367sysmouse_close(void)
Bram Moolenaar3577c6f2008-06-24 21:16:56 +00007368{
7369 struct mouse_info mouse;
7370
7371 signal(SIGUSR2, restricted ? SIG_IGN : SIG_DFL);
7372 mouse.operation = MOUSE_MODE;
7373 mouse.u.mode.mode = 0;
7374 mouse.u.mode.signal = 0;
7375 ioctl(1, CONS_MOUSECTL, &mouse);
7376}
7377
7378/*
7379 * Gets info from sysmouse and adds special keys to input buf.
7380 */
Bram Moolenaar3577c6f2008-06-24 21:16:56 +00007381 static RETSIGTYPE
7382sig_sysmouse SIGDEFARG(sigarg)
7383{
7384 struct mouse_info mouse;
7385 struct video_info video;
7386 char_u string[6];
7387 int row, col;
7388 int button;
7389 int buttons;
7390 static int oldbuttons = 0;
7391
7392#ifdef FEAT_GUI
Bram Moolenaar0f873732019-12-05 20:28:46 +01007393 // Don't put events in the input queue now.
Bram Moolenaar3577c6f2008-06-24 21:16:56 +00007394 if (hold_gui_events)
7395 return;
7396#endif
7397
7398 mouse.operation = MOUSE_GETINFO;
7399 if (ioctl(1, FBIO_GETMODE, &video.vi_mode) != -1
7400 && ioctl(1, FBIO_MODEINFO, &video) != -1
7401 && ioctl(1, CONS_MOUSECTL, &mouse) != -1
7402 && video.vi_cheight > 0 && video.vi_cwidth > 0)
7403 {
7404 row = mouse.u.data.y / video.vi_cheight;
7405 col = mouse.u.data.x / video.vi_cwidth;
7406 buttons = mouse.u.data.buttons;
Bram Moolenaar0f873732019-12-05 20:28:46 +01007407 string[0] = ESC; // Our termcode
Bram Moolenaar3577c6f2008-06-24 21:16:56 +00007408 string[1] = 'M';
7409 string[2] = 'S';
7410 if (oldbuttons == buttons && buttons != 0)
7411 {
7412 button = MOUSE_DRAG;
7413 }
7414 else
7415 {
7416 switch (buttons)
7417 {
7418 case 0:
7419 button = MOUSE_RELEASE;
7420 break;
7421 case 1:
7422 button = MOUSE_LEFT;
7423 break;
7424 case 2:
7425 button = MOUSE_MIDDLE;
7426 break;
7427 case 4:
7428 button = MOUSE_RIGHT;
7429 break;
7430 default:
7431 return;
7432 }
7433 oldbuttons = buttons;
7434 }
7435 string[3] = (char_u)(button);
7436 string[4] = (char_u)(col + ' ' + 1);
7437 string[5] = (char_u)(row + ' ' + 1);
7438 add_to_input_buf(string, 6);
7439 }
7440 return;
7441}
Bram Moolenaar0f873732019-12-05 20:28:46 +01007442#endif // FEAT_SYSMOUSE
Bram Moolenaar3577c6f2008-06-24 21:16:56 +00007443
Bram Moolenaar071d4272004-06-13 20:20:40 +00007444#if defined(FEAT_LIBCALL) || defined(PROTO)
Bram Moolenaard99df422016-01-29 23:20:40 +01007445typedef char_u * (*STRPROCSTR)(char_u *);
7446typedef char_u * (*INTPROCSTR)(int);
7447typedef int (*STRPROCINT)(char_u *);
7448typedef int (*INTPROCINT)(int);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007449
7450/*
7451 * Call a DLL routine which takes either a string or int param
7452 * and returns an allocated string.
7453 */
7454 int
Bram Moolenaar05540972016-01-30 20:31:25 +01007455mch_libcall(
7456 char_u *libname,
7457 char_u *funcname,
Bram Moolenaar0f873732019-12-05 20:28:46 +01007458 char_u *argstring, // NULL when using a argint
Bram Moolenaar05540972016-01-30 20:31:25 +01007459 int argint,
Bram Moolenaar0f873732019-12-05 20:28:46 +01007460 char_u **string_result, // NULL when using number_result
Bram Moolenaar05540972016-01-30 20:31:25 +01007461 int *number_result)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007462{
7463# if defined(USE_DLOPEN)
7464 void *hinstLib;
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00007465 char *dlerr = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007466# else
7467 shl_t hinstLib;
7468# endif
7469 STRPROCSTR ProcAdd;
7470 INTPROCSTR ProcAddI;
7471 char_u *retval_str = NULL;
7472 int retval_int = 0;
7473 int success = FALSE;
7474
Bram Moolenaarb39ef122006-06-22 16:19:31 +00007475 /*
7476 * Get a handle to the DLL module.
7477 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007478# if defined(USE_DLOPEN)
Bram Moolenaar0f873732019-12-05 20:28:46 +01007479 // First clear any error, it's not cleared by the dlopen() call.
Bram Moolenaarb39ef122006-06-22 16:19:31 +00007480 (void)dlerror();
7481
Bram Moolenaar071d4272004-06-13 20:20:40 +00007482 hinstLib = dlopen((char *)libname, RTLD_LAZY
7483# ifdef RTLD_LOCAL
7484 | RTLD_LOCAL
7485# endif
7486 );
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00007487 if (hinstLib == NULL)
7488 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01007489 // "dlerr" must be used before dlclose()
=?UTF-8?q?Dundar=20G=C3=B6c?=420fabc2022-01-28 15:28:04 +00007490 dlerr = dlerror();
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00007491 if (dlerr != NULL)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01007492 semsg(_("dlerror = \"%s\""), dlerr);
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00007493 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007494# else
7495 hinstLib = shl_load((const char*)libname, BIND_IMMEDIATE|BIND_VERBOSE, 0L);
7496# endif
7497
Bram Moolenaar0f873732019-12-05 20:28:46 +01007498 // If the handle is valid, try to get the function address.
Bram Moolenaar071d4272004-06-13 20:20:40 +00007499 if (hinstLib != NULL)
7500 {
Bram Moolenaarb2148f52019-01-20 23:43:57 +01007501# ifdef USING_SETJMP
Bram Moolenaar071d4272004-06-13 20:20:40 +00007502 /*
7503 * Catch a crash when calling the library function. For example when
7504 * using a number where a string pointer is expected.
7505 */
7506 mch_startjmp();
7507 if (SETJMP(lc_jump_env) != 0)
7508 {
7509 success = FALSE;
Bram Moolenaard68071d2006-05-02 22:08:30 +00007510# if defined(USE_DLOPEN)
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00007511 dlerr = NULL;
Bram Moolenaard68071d2006-05-02 22:08:30 +00007512# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007513 mch_didjmp();
7514 }
7515 else
7516# endif
7517 {
7518 retval_str = NULL;
7519 retval_int = 0;
7520
7521 if (argstring != NULL)
7522 {
7523# if defined(USE_DLOPEN)
Bram Moolenaar6d721c72017-01-17 16:56:28 +01007524 *(void **)(&ProcAdd) = dlsym(hinstLib, (const char *)funcname);
=?UTF-8?q?Dundar=20G=C3=B6c?=420fabc2022-01-28 15:28:04 +00007525 dlerr = dlerror();
Bram Moolenaar071d4272004-06-13 20:20:40 +00007526# else
7527 if (shl_findsym(&hinstLib, (const char *)funcname,
7528 TYPE_PROCEDURE, (void *)&ProcAdd) < 0)
7529 ProcAdd = NULL;
7530# endif
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00007531 if ((success = (ProcAdd != NULL
7532# if defined(USE_DLOPEN)
7533 && dlerr == NULL
7534# endif
7535 )))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007536 {
7537 if (string_result == NULL)
Bram Moolenaara4224862020-09-13 22:00:12 +02007538 retval_int = ((STRPROCINT)(void *)ProcAdd)(argstring);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007539 else
7540 retval_str = (ProcAdd)(argstring);
7541 }
7542 }
7543 else
7544 {
7545# if defined(USE_DLOPEN)
Bram Moolenaar6d721c72017-01-17 16:56:28 +01007546 *(void **)(&ProcAddI) = dlsym(hinstLib, (const char *)funcname);
=?UTF-8?q?Dundar=20G=C3=B6c?=420fabc2022-01-28 15:28:04 +00007547 dlerr = dlerror();
Bram Moolenaar071d4272004-06-13 20:20:40 +00007548# else
7549 if (shl_findsym(&hinstLib, (const char *)funcname,
7550 TYPE_PROCEDURE, (void *)&ProcAddI) < 0)
7551 ProcAddI = NULL;
7552# endif
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00007553 if ((success = (ProcAddI != NULL
7554# if defined(USE_DLOPEN)
7555 && dlerr == NULL
7556# endif
7557 )))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007558 {
7559 if (string_result == NULL)
Bram Moolenaara4224862020-09-13 22:00:12 +02007560 retval_int = ((INTPROCINT)(void *)ProcAddI)(argint);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007561 else
7562 retval_str = (ProcAddI)(argint);
7563 }
7564 }
7565
Bram Moolenaar0f873732019-12-05 20:28:46 +01007566 // Save the string before we free the library.
7567 // Assume that a "1" or "-1" result is an illegal pointer.
Bram Moolenaar071d4272004-06-13 20:20:40 +00007568 if (string_result == NULL)
7569 *number_result = retval_int;
7570 else if (retval_str != NULL
7571 && retval_str != (char_u *)1
7572 && retval_str != (char_u *)-1)
7573 *string_result = vim_strsave(retval_str);
7574 }
7575
Bram Moolenaarb2148f52019-01-20 23:43:57 +01007576# ifdef USING_SETJMP
Bram Moolenaar071d4272004-06-13 20:20:40 +00007577 mch_endjmp();
7578# ifdef SIGHASARG
7579 if (lc_signal != 0)
7580 {
7581 int i;
7582
Bram Moolenaar0f873732019-12-05 20:28:46 +01007583 // try to find the name of this signal
Bram Moolenaar071d4272004-06-13 20:20:40 +00007584 for (i = 0; signal_info[i].sig != -1; i++)
7585 if (lc_signal == signal_info[i].sig)
7586 break;
Bram Moolenaarac78dd42022-01-02 19:25:26 +00007587 semsg(e_got_sig_str_in_libcall, signal_info[i].name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007588 }
7589# endif
7590# endif
7591
Bram Moolenaar071d4272004-06-13 20:20:40 +00007592# if defined(USE_DLOPEN)
Bram Moolenaar0f873732019-12-05 20:28:46 +01007593 // "dlerr" must be used before dlclose()
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00007594 if (dlerr != NULL)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01007595 semsg(_("dlerror = \"%s\""), dlerr);
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00007596
Bram Moolenaar0f873732019-12-05 20:28:46 +01007597 // Free the DLL module.
Bram Moolenaar071d4272004-06-13 20:20:40 +00007598 (void)dlclose(hinstLib);
7599# else
7600 (void)shl_unload(hinstLib);
7601# endif
7602 }
7603
7604 if (!success)
7605 {
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00007606 semsg(_(e_library_call_failed_for_str), funcname);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007607 return FAIL;
7608 }
7609
7610 return OK;
7611}
7612#endif
7613
7614#if (defined(FEAT_X11) && defined(FEAT_XCLIPBOARD)) || defined(PROTO)
Bram Moolenaar0f873732019-12-05 20:28:46 +01007615static int xterm_trace = -1; // default: disabled
Bram Moolenaar071d4272004-06-13 20:20:40 +00007616static int xterm_button;
7617
7618/*
7619 * Setup a dummy window for X selections in a terminal.
7620 */
7621 void
Bram Moolenaar05540972016-01-30 20:31:25 +01007622setup_term_clip(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007623{
7624 int z = 0;
7625 char *strp = "";
7626 Widget AppShell;
7627
7628 if (!x_connect_to_server())
7629 return;
7630
7631 open_app_context();
7632 if (app_context != NULL && xterm_Shell == (Widget)0)
7633 {
7634 int (*oldhandler)();
Bram Moolenaarb2148f52019-01-20 23:43:57 +01007635# if defined(USING_SETJMP)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007636 int (*oldIOhandler)();
Bram Moolenaaredce7422019-01-20 18:39:30 +01007637# endif
Bram Moolenaar833eb1d2016-11-24 17:22:50 +01007638# ifdef ELAPSED_FUNC
Bram Moolenaar1ac56c22019-01-17 22:28:22 +01007639 elapsed_T start_tv;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007640
7641 if (p_verbose > 0)
Bram Moolenaar833eb1d2016-11-24 17:22:50 +01007642 ELAPSED_INIT(start_tv);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007643# endif
7644
Bram Moolenaar0f873732019-12-05 20:28:46 +01007645 // Ignore X errors while opening the display
Bram Moolenaar071d4272004-06-13 20:20:40 +00007646 oldhandler = XSetErrorHandler(x_error_check);
7647
Bram Moolenaarb2148f52019-01-20 23:43:57 +01007648# if defined(USING_SETJMP)
Bram Moolenaar0f873732019-12-05 20:28:46 +01007649 // Ignore X IO errors while opening the display
Bram Moolenaar071d4272004-06-13 20:20:40 +00007650 oldIOhandler = XSetIOErrorHandler(x_IOerror_check);
7651 mch_startjmp();
7652 if (SETJMP(lc_jump_env) != 0)
7653 {
7654 mch_didjmp();
7655 xterm_dpy = NULL;
7656 }
7657 else
Bram Moolenaaredce7422019-01-20 18:39:30 +01007658# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007659 {
7660 xterm_dpy = XtOpenDisplay(app_context, xterm_display,
7661 "vim_xterm", "Vim_xterm", NULL, 0, &z, &strp);
Bram Moolenaard155d7a2018-12-21 16:04:21 +01007662 if (xterm_dpy != NULL)
7663 xterm_dpy_retry_count = 0;
Bram Moolenaarb2148f52019-01-20 23:43:57 +01007664# if defined(USING_SETJMP)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007665 mch_endjmp();
Bram Moolenaaredce7422019-01-20 18:39:30 +01007666# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007667 }
7668
Bram Moolenaarb2148f52019-01-20 23:43:57 +01007669# if defined(USING_SETJMP)
Bram Moolenaar0f873732019-12-05 20:28:46 +01007670 // Now handle X IO errors normally.
Bram Moolenaar071d4272004-06-13 20:20:40 +00007671 (void)XSetIOErrorHandler(oldIOhandler);
Bram Moolenaaredce7422019-01-20 18:39:30 +01007672# endif
Bram Moolenaar0f873732019-12-05 20:28:46 +01007673 // Now handle X errors normally.
Bram Moolenaar071d4272004-06-13 20:20:40 +00007674 (void)XSetErrorHandler(oldhandler);
7675
7676 if (xterm_dpy == NULL)
7677 {
7678 if (p_verbose > 0)
Bram Moolenaar32526b32019-01-19 17:43:09 +01007679 verb_msg(_("Opening the X display failed"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00007680 return;
7681 }
7682
Bram Moolenaar0f873732019-12-05 20:28:46 +01007683 // Catch terminating error of the X server connection.
Bram Moolenaar071d4272004-06-13 20:20:40 +00007684 (void)XSetIOErrorHandler(x_IOerror_handler);
7685
Bram Moolenaar833eb1d2016-11-24 17:22:50 +01007686# ifdef ELAPSED_FUNC
Bram Moolenaar071d4272004-06-13 20:20:40 +00007687 if (p_verbose > 0)
Bram Moolenaara04f10b2005-05-31 22:09:46 +00007688 {
7689 verbose_enter();
Bram Moolenaar833eb1d2016-11-24 17:22:50 +01007690 xopen_message(ELAPSED_FUNC(start_tv));
Bram Moolenaara04f10b2005-05-31 22:09:46 +00007691 verbose_leave();
7692 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007693# endif
7694
Bram Moolenaar0f873732019-12-05 20:28:46 +01007695 // Create a Shell to make converters work.
Bram Moolenaar071d4272004-06-13 20:20:40 +00007696 AppShell = XtVaAppCreateShell("vim_xterm", "Vim_xterm",
7697 applicationShellWidgetClass, xterm_dpy,
7698 NULL);
7699 if (AppShell == (Widget)0)
7700 return;
7701 xterm_Shell = XtVaCreatePopupShell("VIM",
7702 topLevelShellWidgetClass, AppShell,
7703 XtNmappedWhenManaged, 0,
7704 XtNwidth, 1,
7705 XtNheight, 1,
7706 NULL);
7707 if (xterm_Shell == (Widget)0)
7708 return;
7709
7710 x11_setup_atoms(xterm_dpy);
Bram Moolenaar7cfea752010-06-22 06:07:12 +02007711 x11_setup_selection(xterm_Shell);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007712 if (x11_display == NULL)
7713 x11_display = xterm_dpy;
7714
7715 XtRealizeWidget(xterm_Shell);
7716 XSync(xterm_dpy, False);
7717 xterm_update();
7718 }
7719 if (xterm_Shell != (Widget)0)
7720 {
7721 clip_init(TRUE);
7722 if (x11_window == 0 && (strp = getenv("WINDOWID")) != NULL)
7723 x11_window = (Window)atol(strp);
Bram Moolenaar0f873732019-12-05 20:28:46 +01007724 // Check if $WINDOWID is valid.
Bram Moolenaar071d4272004-06-13 20:20:40 +00007725 if (test_x11_window(xterm_dpy) == FAIL)
7726 x11_window = 0;
7727 if (x11_window != 0)
7728 xterm_trace = 0;
7729 }
7730}
7731
7732 void
Bram Moolenaar05540972016-01-30 20:31:25 +01007733start_xterm_trace(int button)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007734{
7735 if (x11_window == 0 || xterm_trace < 0 || xterm_Shell == (Widget)0)
7736 return;
7737 xterm_trace = 1;
7738 xterm_button = button;
7739 do_xterm_trace();
7740}
7741
7742
7743 void
Bram Moolenaar05540972016-01-30 20:31:25 +01007744stop_xterm_trace(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007745{
7746 if (xterm_trace < 0)
7747 return;
7748 xterm_trace = 0;
7749}
7750
7751/*
7752 * Query the xterm pointer and generate mouse termcodes if necessary
7753 * return TRUE if dragging is active, else FALSE
7754 */
7755 static int
Bram Moolenaar05540972016-01-30 20:31:25 +01007756do_xterm_trace(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007757{
7758 Window root, child;
7759 int root_x, root_y;
7760 int win_x, win_y;
7761 int row, col;
7762 int_u mask_return;
7763 char_u buf[50];
7764 char_u *strp;
7765 long got_hints;
7766 static char_u *mouse_code;
7767 static char_u mouse_name[2] = {KS_MOUSE, KE_FILLER};
7768 static int prev_row = 0, prev_col = 0;
7769 static XSizeHints xterm_hints;
7770
7771 if (xterm_trace <= 0)
7772 return FALSE;
7773
7774 if (xterm_trace == 1)
7775 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01007776 // Get the hints just before tracking starts. The font size might
7777 // have changed recently.
Bram Moolenaara6c2c912008-01-13 15:31:00 +00007778 if (!XGetWMNormalHints(xterm_dpy, x11_window, &xterm_hints, &got_hints)
7779 || !(got_hints & PResizeInc)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007780 || xterm_hints.width_inc <= 1
7781 || xterm_hints.height_inc <= 1)
7782 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01007783 xterm_trace = -1; // Not enough data -- disable tracing
Bram Moolenaar071d4272004-06-13 20:20:40 +00007784 return FALSE;
7785 }
7786
Bram Moolenaar0f873732019-12-05 20:28:46 +01007787 // Rely on the same mouse code for the duration of this
Bram Moolenaar071d4272004-06-13 20:20:40 +00007788 mouse_code = find_termcode(mouse_name);
7789 prev_row = mouse_row;
Bram Moolenaarcde88542015-08-11 19:14:00 +02007790 prev_col = mouse_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007791 xterm_trace = 2;
7792
Bram Moolenaar0f873732019-12-05 20:28:46 +01007793 // Find the offset of the chars, there might be a scrollbar on the
7794 // left of the window and/or a menu on the top (eterm etc.)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007795 XQueryPointer(xterm_dpy, x11_window, &root, &child, &root_x, &root_y,
7796 &win_x, &win_y, &mask_return);
7797 xterm_hints.y = win_y - (xterm_hints.height_inc * mouse_row)
7798 - (xterm_hints.height_inc / 2);
7799 if (xterm_hints.y <= xterm_hints.height_inc / 2)
7800 xterm_hints.y = 2;
7801 xterm_hints.x = win_x - (xterm_hints.width_inc * mouse_col)
7802 - (xterm_hints.width_inc / 2);
7803 if (xterm_hints.x <= xterm_hints.width_inc / 2)
7804 xterm_hints.x = 2;
7805 return TRUE;
7806 }
Bram Moolenaaref9d6aa2011-04-11 16:56:35 +02007807 if (mouse_code == NULL || STRLEN(mouse_code) > 45)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007808 {
7809 xterm_trace = 0;
7810 return FALSE;
7811 }
7812
7813 XQueryPointer(xterm_dpy, x11_window, &root, &child, &root_x, &root_y,
7814 &win_x, &win_y, &mask_return);
7815
7816 row = check_row((win_y - xterm_hints.y) / xterm_hints.height_inc);
7817 col = check_col((win_x - xterm_hints.x) / xterm_hints.width_inc);
7818 if (row == prev_row && col == prev_col)
7819 return TRUE;
7820
7821 STRCPY(buf, mouse_code);
7822 strp = buf + STRLEN(buf);
7823 *strp++ = (xterm_button | MOUSE_DRAG) & ~0x20;
7824 *strp++ = (char_u)(col + ' ' + 1);
7825 *strp++ = (char_u)(row + ' ' + 1);
7826 *strp = 0;
7827 add_to_input_buf(buf, STRLEN(buf));
7828
7829 prev_row = row;
7830 prev_col = col;
7831 return TRUE;
7832}
7833
Bram Moolenaard4aa83a2019-05-09 18:59:31 +02007834# if defined(FEAT_GUI) || defined(FEAT_XCLIPBOARD) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007835/*
7836 * Destroy the display, window and app_context. Required for GTK.
7837 */
7838 void
Bram Moolenaar05540972016-01-30 20:31:25 +01007839clear_xterm_clip(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007840{
7841 if (xterm_Shell != (Widget)0)
7842 {
7843 XtDestroyWidget(xterm_Shell);
7844 xterm_Shell = (Widget)0;
7845 }
7846 if (xterm_dpy != NULL)
7847 {
Bram Moolenaare8208012008-06-20 09:59:25 +00007848# if 0
Bram Moolenaar0f873732019-12-05 20:28:46 +01007849 // Lesstif and Solaris crash here, lose some memory
Bram Moolenaar071d4272004-06-13 20:20:40 +00007850 XtCloseDisplay(xterm_dpy);
Bram Moolenaare8208012008-06-20 09:59:25 +00007851# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007852 if (x11_display == xterm_dpy)
7853 x11_display = NULL;
7854 xterm_dpy = NULL;
7855 }
Bram Moolenaare8208012008-06-20 09:59:25 +00007856# if 0
Bram Moolenaar071d4272004-06-13 20:20:40 +00007857 if (app_context != (XtAppContext)NULL)
7858 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01007859 // Lesstif and Solaris crash here, lose some memory
Bram Moolenaar071d4272004-06-13 20:20:40 +00007860 XtDestroyApplicationContext(app_context);
7861 app_context = (XtAppContext)NULL;
7862 }
Bram Moolenaare8208012008-06-20 09:59:25 +00007863# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007864}
7865# endif
7866
7867/*
Bram Moolenaar090cfc12013-03-19 12:35:42 +01007868 * Catch up with GUI or X events.
7869 */
7870 static void
Bram Moolenaar05540972016-01-30 20:31:25 +01007871clip_update(void)
Bram Moolenaar090cfc12013-03-19 12:35:42 +01007872{
7873# ifdef FEAT_GUI
7874 if (gui.in_use)
7875 gui_mch_update();
7876 else
7877# endif
7878 if (xterm_Shell != (Widget)0)
7879 xterm_update();
7880}
7881
7882/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00007883 * Catch up with any queued X events. This may put keyboard input into the
7884 * input buffer, call resize call-backs, trigger timers etc. If there is
7885 * nothing in the X event queue (& no timers pending), then we return
7886 * immediately.
7887 */
7888 static void
Bram Moolenaar05540972016-01-30 20:31:25 +01007889xterm_update(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007890{
7891 XEvent event;
7892
Bram Moolenaarb1fc2bf2015-03-20 16:26:54 +01007893 for (;;)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007894 {
Bram Moolenaar93c88e02015-09-15 14:12:05 +02007895 XtInputMask mask = XtAppPending(app_context);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007896
Bram Moolenaar93c88e02015-09-15 14:12:05 +02007897 if (mask == 0 || vim_is_input_buf_full())
Bram Moolenaarb1fc2bf2015-03-20 16:26:54 +01007898 break;
7899
Bram Moolenaar93c88e02015-09-15 14:12:05 +02007900 if (mask & XtIMXEvent)
Bram Moolenaarb1fc2bf2015-03-20 16:26:54 +01007901 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01007902 // There is an event to process.
Bram Moolenaar93c88e02015-09-15 14:12:05 +02007903 XtAppNextEvent(app_context, &event);
Bram Moolenaarb1fc2bf2015-03-20 16:26:54 +01007904#ifdef FEAT_CLIENTSERVER
7905 {
7906 XPropertyEvent *e = (XPropertyEvent *)&event;
7907
7908 if (e->type == PropertyNotify && e->window == commWindow
Bram Moolenaar071d4272004-06-13 20:20:40 +00007909 && e->atom == commProperty && e->state == PropertyNewValue)
Bram Moolenaar93c88e02015-09-15 14:12:05 +02007910 serverEventProc(xterm_dpy, &event, 0);
Bram Moolenaarb1fc2bf2015-03-20 16:26:54 +01007911 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007912#endif
Bram Moolenaar93c88e02015-09-15 14:12:05 +02007913 XtDispatchEvent(&event);
7914 }
Bram Moolenaarb1fc2bf2015-03-20 16:26:54 +01007915 else
7916 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01007917 // There is something else than an event to process.
Bram Moolenaar93c88e02015-09-15 14:12:05 +02007918 XtAppProcessEvent(app_context, mask);
7919 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007920 }
7921}
7922
7923 int
Bram Moolenaar0554fa42019-06-14 21:36:54 +02007924clip_xterm_own_selection(Clipboard_T *cbd)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007925{
7926 if (xterm_Shell != (Widget)0)
7927 return clip_x11_own_selection(xterm_Shell, cbd);
7928 return FAIL;
7929}
7930
7931 void
Bram Moolenaar0554fa42019-06-14 21:36:54 +02007932clip_xterm_lose_selection(Clipboard_T *cbd)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007933{
7934 if (xterm_Shell != (Widget)0)
7935 clip_x11_lose_selection(xterm_Shell, cbd);
7936}
7937
7938 void
Bram Moolenaar0554fa42019-06-14 21:36:54 +02007939clip_xterm_request_selection(Clipboard_T *cbd)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007940{
7941 if (xterm_Shell != (Widget)0)
7942 clip_x11_request_selection(xterm_Shell, xterm_dpy, cbd);
7943}
7944
7945 void
Bram Moolenaar0554fa42019-06-14 21:36:54 +02007946clip_xterm_set_selection(Clipboard_T *cbd)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007947{
7948 clip_x11_set_selection(cbd);
7949}
7950#endif
7951
7952
7953#if defined(USE_XSMP) || defined(PROTO)
7954/*
7955 * Code for X Session Management Protocol.
7956 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007957
7958# if defined(FEAT_GUI) && defined(USE_XSMP_INTERACT)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007959/*
7960 * This is our chance to ask the user if they want to save,
7961 * or abort the logout
7962 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007963 static void
Bram Moolenaar05540972016-01-30 20:31:25 +01007964xsmp_handle_interaction(SmcConn smc_conn, SmPointer client_data UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007965{
Bram Moolenaare1004402020-10-24 20:49:43 +02007966 int save_cmod_flags;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007967 int cancel_shutdown = False;
7968
Bram Moolenaare1004402020-10-24 20:49:43 +02007969 save_cmod_flags = cmdmod.cmod_flags;
7970 cmdmod.cmod_flags |= CMOD_CONFIRM;
Bram Moolenaar027387f2016-01-02 22:25:52 +01007971 if (check_changed_any(FALSE, FALSE))
Bram Moolenaar0f873732019-12-05 20:28:46 +01007972 // Mustn't logout
Bram Moolenaar071d4272004-06-13 20:20:40 +00007973 cancel_shutdown = True;
Bram Moolenaare1004402020-10-24 20:49:43 +02007974 cmdmod.cmod_flags = save_cmod_flags;
Bram Moolenaar0f873732019-12-05 20:28:46 +01007975 setcursor(); // position cursor
Bram Moolenaar071d4272004-06-13 20:20:40 +00007976 out_flush();
7977
Bram Moolenaar0f873732019-12-05 20:28:46 +01007978 // Done interaction
Bram Moolenaar071d4272004-06-13 20:20:40 +00007979 SmcInteractDone(smc_conn, cancel_shutdown);
7980
Bram Moolenaar0f873732019-12-05 20:28:46 +01007981 // Finish off
7982 // Only end save-yourself here if we're not cancelling shutdown;
7983 // we'll get a cancelled callback later in which we'll end it.
7984 // Hopefully get around glitchy SMs (like GNOME-1)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007985 if (!cancel_shutdown)
7986 {
7987 xsmp.save_yourself = False;
7988 SmcSaveYourselfDone(smc_conn, True);
7989 }
7990}
7991# endif
7992
7993/*
7994 * Callback that starts save-yourself.
7995 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007996 static void
Bram Moolenaar05540972016-01-30 20:31:25 +01007997xsmp_handle_save_yourself(
7998 SmcConn smc_conn,
7999 SmPointer client_data UNUSED,
8000 int save_type UNUSED,
8001 Bool shutdown,
8002 int interact_style UNUSED,
8003 Bool fast UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008004{
Bram Moolenaar0f873732019-12-05 20:28:46 +01008005 // Handle already being in saveyourself
Bram Moolenaar071d4272004-06-13 20:20:40 +00008006 if (xsmp.save_yourself)
8007 SmcSaveYourselfDone(smc_conn, True);
8008 xsmp.save_yourself = True;
8009 xsmp.shutdown = shutdown;
8010
Bram Moolenaar0f873732019-12-05 20:28:46 +01008011 // First up, preserve all files
Bram Moolenaar071d4272004-06-13 20:20:40 +00008012 out_flush();
Bram Moolenaar0f873732019-12-05 20:28:46 +01008013 ml_sync_all(FALSE, FALSE); // preserve all swap files
Bram Moolenaar071d4272004-06-13 20:20:40 +00008014
8015 if (p_verbose > 0)
Bram Moolenaar32526b32019-01-19 17:43:09 +01008016 verb_msg(_("XSMP handling save-yourself request"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00008017
8018# if defined(FEAT_GUI) && defined(USE_XSMP_INTERACT)
Bram Moolenaar0f873732019-12-05 20:28:46 +01008019 // Now see if we can ask about unsaved files
Bram Moolenaar071d4272004-06-13 20:20:40 +00008020 if (shutdown && !fast && gui.in_use)
Bram Moolenaar0f873732019-12-05 20:28:46 +01008021 // Need to interact with user, but need SM's permission
Bram Moolenaar071d4272004-06-13 20:20:40 +00008022 SmcInteractRequest(smc_conn, SmDialogError,
8023 xsmp_handle_interaction, client_data);
8024 else
8025# endif
8026 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01008027 // Can stop the cycle here
Bram Moolenaar071d4272004-06-13 20:20:40 +00008028 SmcSaveYourselfDone(smc_conn, True);
8029 xsmp.save_yourself = False;
8030 }
8031}
8032
8033
8034/*
8035 * Callback to warn us of imminent death.
8036 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008037 static void
Bram Moolenaar05540972016-01-30 20:31:25 +01008038xsmp_die(SmcConn smc_conn UNUSED, SmPointer client_data UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008039{
8040 xsmp_close();
8041
Bram Moolenaar0f873732019-12-05 20:28:46 +01008042 // quit quickly leaving swapfiles for modified buffers behind
Bram Moolenaar071d4272004-06-13 20:20:40 +00008043 getout_preserve_modified(0);
8044}
8045
8046
8047/*
8048 * Callback to tell us that save-yourself has completed.
8049 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008050 static void
Bram Moolenaar05540972016-01-30 20:31:25 +01008051xsmp_save_complete(
8052 SmcConn smc_conn UNUSED,
8053 SmPointer client_data UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008054{
8055 xsmp.save_yourself = False;
8056}
8057
8058
8059/*
8060 * Callback to tell us that an instigated shutdown was cancelled
8061 * (maybe even by us)
8062 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008063 static void
Bram Moolenaar05540972016-01-30 20:31:25 +01008064xsmp_shutdown_cancelled(
8065 SmcConn smc_conn,
8066 SmPointer client_data UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008067{
8068 if (xsmp.save_yourself)
8069 SmcSaveYourselfDone(smc_conn, True);
8070 xsmp.save_yourself = False;
8071 xsmp.shutdown = False;
8072}
8073
8074
8075/*
8076 * Callback to tell us that a new ICE connection has been established.
8077 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008078 static void
Bram Moolenaar05540972016-01-30 20:31:25 +01008079xsmp_ice_connection(
8080 IceConn iceConn,
8081 IcePointer clientData UNUSED,
8082 Bool opening,
8083 IcePointer *watchData UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008084{
Bram Moolenaar0f873732019-12-05 20:28:46 +01008085 // Intercept creation of ICE connection fd
Bram Moolenaar071d4272004-06-13 20:20:40 +00008086 if (opening)
8087 {
8088 xsmp_icefd = IceConnectionNumber(iceConn);
8089 IceRemoveConnectionWatch(xsmp_ice_connection, NULL);
8090 }
8091}
8092
8093
Bram Moolenaar0f873732019-12-05 20:28:46 +01008094// Handle any ICE processing that's required; return FAIL if SM lost
Bram Moolenaar071d4272004-06-13 20:20:40 +00008095 int
Bram Moolenaar05540972016-01-30 20:31:25 +01008096xsmp_handle_requests(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008097{
8098 Bool rep;
8099
8100 if (IceProcessMessages(xsmp.iceconn, NULL, &rep)
8101 == IceProcessMessagesIOError)
8102 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01008103 // Lost ICE
Bram Moolenaar071d4272004-06-13 20:20:40 +00008104 if (p_verbose > 0)
Bram Moolenaar32526b32019-01-19 17:43:09 +01008105 verb_msg(_("XSMP lost ICE connection"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00008106 xsmp_close();
8107 return FAIL;
8108 }
8109 else
8110 return OK;
8111}
8112
8113static int dummy;
8114
Bram Moolenaar0f873732019-12-05 20:28:46 +01008115// Set up X Session Management Protocol
Bram Moolenaar071d4272004-06-13 20:20:40 +00008116 void
8117xsmp_init(void)
8118{
8119 char errorstring[80];
Bram Moolenaar071d4272004-06-13 20:20:40 +00008120 SmcCallbacks smcallbacks;
8121#if 0
8122 SmPropValue smname;
8123 SmProp smnameprop;
8124 SmProp *smprops[1];
8125#endif
8126
8127 if (p_verbose > 0)
Bram Moolenaar32526b32019-01-19 17:43:09 +01008128 verb_msg(_("XSMP opening connection"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00008129
8130 xsmp.save_yourself = xsmp.shutdown = False;
8131
Bram Moolenaar0f873732019-12-05 20:28:46 +01008132 // Set up SM callbacks - must have all, even if they're not used
Bram Moolenaar071d4272004-06-13 20:20:40 +00008133 smcallbacks.save_yourself.callback = xsmp_handle_save_yourself;
8134 smcallbacks.save_yourself.client_data = NULL;
8135 smcallbacks.die.callback = xsmp_die;
8136 smcallbacks.die.client_data = NULL;
8137 smcallbacks.save_complete.callback = xsmp_save_complete;
8138 smcallbacks.save_complete.client_data = NULL;
8139 smcallbacks.shutdown_cancelled.callback = xsmp_shutdown_cancelled;
8140 smcallbacks.shutdown_cancelled.client_data = NULL;
8141
Bram Moolenaar0f873732019-12-05 20:28:46 +01008142 // Set up a watch on ICE connection creations. The "dummy" argument is
8143 // apparently required for FreeBSD (we get a BUS error when using NULL).
Bram Moolenaar071d4272004-06-13 20:20:40 +00008144 if (IceAddConnectionWatch(xsmp_ice_connection, &dummy) == 0)
8145 {
8146 if (p_verbose > 0)
Bram Moolenaar32526b32019-01-19 17:43:09 +01008147 verb_msg(_("XSMP ICE connection watch failed"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00008148 return;
8149 }
8150
Bram Moolenaar0f873732019-12-05 20:28:46 +01008151 // Create an SM connection
Bram Moolenaar071d4272004-06-13 20:20:40 +00008152 xsmp.smcconn = SmcOpenConnection(
8153 NULL,
8154 NULL,
8155 SmProtoMajor,
8156 SmProtoMinor,
8157 SmcSaveYourselfProcMask | SmcDieProcMask
8158 | SmcSaveCompleteProcMask | SmcShutdownCancelledProcMask,
8159 &smcallbacks,
8160 NULL,
Bram Moolenaare8208012008-06-20 09:59:25 +00008161 &xsmp.clientid,
Bram Moolenaar4841a7c2018-09-22 14:08:49 +02008162 sizeof(errorstring) - 1,
Bram Moolenaar071d4272004-06-13 20:20:40 +00008163 errorstring);
8164 if (xsmp.smcconn == NULL)
8165 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00008166 if (p_verbose > 0)
Bram Moolenaara04f10b2005-05-31 22:09:46 +00008167 {
Bram Moolenaare1be1182020-10-24 13:30:51 +02008168 char errorreport[132];
8169
8170 // If the message is too long it might not be NUL terminated. Add
8171 // a NUL at the end to make sure we don't go over the end.
8172 errorstring[sizeof(errorstring) - 1] = NUL;
Bram Moolenaara04f10b2005-05-31 22:09:46 +00008173 vim_snprintf(errorreport, sizeof(errorreport),
8174 _("XSMP SmcOpenConnection failed: %s"), errorstring);
Bram Moolenaar32526b32019-01-19 17:43:09 +01008175 verb_msg(errorreport);
Bram Moolenaara04f10b2005-05-31 22:09:46 +00008176 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00008177 return;
8178 }
8179 xsmp.iceconn = SmcGetIceConnection(xsmp.smcconn);
8180
8181#if 0
Bram Moolenaar0f873732019-12-05 20:28:46 +01008182 // ID ourselves
Bram Moolenaar071d4272004-06-13 20:20:40 +00008183 smname.value = "vim";
8184 smname.length = 3;
8185 smnameprop.name = "SmProgram";
8186 smnameprop.type = "SmARRAY8";
8187 smnameprop.num_vals = 1;
8188 smnameprop.vals = &smname;
8189
8190 smprops[0] = &smnameprop;
8191 SmcSetProperties(xsmp.smcconn, 1, smprops);
8192#endif
8193}
8194
8195
Bram Moolenaar0f873732019-12-05 20:28:46 +01008196// Shut down XSMP comms.
Bram Moolenaar071d4272004-06-13 20:20:40 +00008197 void
Bram Moolenaar05540972016-01-30 20:31:25 +01008198xsmp_close(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008199{
8200 if (xsmp_icefd != -1)
8201 {
8202 SmcCloseConnection(xsmp.smcconn, 0, NULL);
Bram Moolenaar5a221812008-11-12 12:08:45 +00008203 if (xsmp.clientid != NULL)
8204 free(xsmp.clientid);
Bram Moolenaare8208012008-06-20 09:59:25 +00008205 xsmp.clientid = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008206 xsmp_icefd = -1;
8207 }
8208}
Bram Moolenaar0f873732019-12-05 20:28:46 +01008209#endif // USE_XSMP
Bram Moolenaar071d4272004-06-13 20:20:40 +00008210
8211
8212#ifdef EBCDIC
Bram Moolenaar0f873732019-12-05 20:28:46 +01008213// Translate character to its CTRL- value
Bram Moolenaar071d4272004-06-13 20:20:40 +00008214char CtrlTable[] =
8215{
8216/* 00 - 5E */
8217 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
8218 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
8219 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
8220 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
8221 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
8222 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
8223/* ^ */ 0x1E,
8224/* - */ 0x1F,
8225/* 61 - 6C */
8226 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
8227/* _ */ 0x1F,
8228/* 6E - 80 */
8229 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
8230/* a */ 0x01,
8231/* b */ 0x02,
8232/* c */ 0x03,
8233/* d */ 0x37,
8234/* e */ 0x2D,
8235/* f */ 0x2E,
8236/* g */ 0x2F,
8237/* h */ 0x16,
8238/* i */ 0x05,
8239/* 8A - 90 */
8240 0, 0, 0, 0, 0, 0, 0,
8241/* j */ 0x15,
8242/* k */ 0x0B,
8243/* l */ 0x0C,
8244/* m */ 0x0D,
8245/* n */ 0x0E,
8246/* o */ 0x0F,
8247/* p */ 0x10,
8248/* q */ 0x11,
8249/* r */ 0x12,
8250/* 9A - A1 */
8251 0, 0, 0, 0, 0, 0, 0, 0,
8252/* s */ 0x13,
8253/* t */ 0x3C,
8254/* u */ 0x3D,
8255/* v */ 0x32,
8256/* w */ 0x26,
8257/* x */ 0x18,
8258/* y */ 0x19,
8259/* z */ 0x3F,
8260/* AA - AC */
8261 0, 0, 0,
8262/* [ */ 0x27,
8263/* AE - BC */
8264 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
8265/* ] */ 0x1D,
8266/* BE - C0 */ 0, 0, 0,
8267/* A */ 0x01,
8268/* B */ 0x02,
8269/* C */ 0x03,
8270/* D */ 0x37,
8271/* E */ 0x2D,
8272/* F */ 0x2E,
8273/* G */ 0x2F,
8274/* H */ 0x16,
8275/* I */ 0x05,
8276/* CA - D0 */ 0, 0, 0, 0, 0, 0, 0,
8277/* J */ 0x15,
8278/* K */ 0x0B,
8279/* L */ 0x0C,
8280/* M */ 0x0D,
8281/* N */ 0x0E,
8282/* O */ 0x0F,
8283/* P */ 0x10,
8284/* Q */ 0x11,
8285/* R */ 0x12,
8286/* DA - DF */ 0, 0, 0, 0, 0, 0,
8287/* \ */ 0x1C,
8288/* E1 */ 0,
8289/* S */ 0x13,
8290/* T */ 0x3C,
8291/* U */ 0x3D,
8292/* V */ 0x32,
8293/* W */ 0x26,
8294/* X */ 0x18,
8295/* Y */ 0x19,
8296/* Z */ 0x3F,
8297/* EA - FF*/ 0, 0, 0, 0, 0, 0,
8298 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
8299};
8300
8301char MetaCharTable[]=
Bram Moolenaar0f873732019-12-05 20:28:46 +01008302{// 0 1 2 3 4 5 6 7 8 9 A B C D E F
Bram Moolenaar071d4272004-06-13 20:20:40 +00008303 0, 0, 0, 0,'\\', 0,'F', 0,'W','M','N', 0, 0, 0, 0, 0,
8304 0, 0, 0, 0,']', 0, 0,'G', 0, 0,'R','O', 0, 0, 0, 0,
8305 '@','A','B','C','D','E', 0, 0,'H','I','J','K','L', 0, 0, 0,
8306 'P','Q', 0,'S','T','U','V', 0,'X','Y','Z','[', 0, 0,'^', 0
8307};
8308
8309
Bram Moolenaar0f873732019-12-05 20:28:46 +01008310// TODO: Use characters NOT numbers!!!
Bram Moolenaar071d4272004-06-13 20:20:40 +00008311char CtrlCharTable[]=
Bram Moolenaar0f873732019-12-05 20:28:46 +01008312{// 0 1 2 3 4 5 6 7 8 9 A B C D E F
Bram Moolenaar071d4272004-06-13 20:20:40 +00008313 124,193,194,195, 0,201, 0, 0, 0, 0, 0,210,211,212,213,214,
8314 215,216,217,226, 0,209,200, 0,231,232, 0, 0,224,189, 95,109,
8315 0, 0, 0, 0, 0, 0,230,173, 0, 0, 0, 0, 0,197,198,199,
8316 0, 0,229, 0, 0, 0, 0,196, 0, 0, 0, 0,227,228, 0,233,
8317};
8318
8319
8320#endif