1// This is an open source non-commercial project. Dear PVS-Studio, please check
2// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
3
4/*
5 * os_unix.c -- code for all flavors of Unix (BSD, SYSV, SVR4, POSIX, ...)
6 *
7 * A lot of this file was originally written by Juergen Weigert and later
8 * changed beyond recognition.
9 */
10
11#include <assert.h>
12#include <errno.h>
13#include <inttypes.h>
14#include <stdbool.h>
15#include <string.h>
16
17#include "nvim/api/private/handle.h"
18#include "nvim/vim.h"
19#include "nvim/ascii.h"
20#include "nvim/os_unix.h"
21#include "nvim/buffer.h"
22#include "nvim/charset.h"
23#include "nvim/eval.h"
24#include "nvim/ex_cmds.h"
25#include "nvim/fileio.h"
26#include "nvim/getchar.h"
27#include "nvim/main.h"
28#include "nvim/mbyte.h"
29#include "nvim/memline.h"
30#include "nvim/memory.h"
31#include "nvim/message.h"
32#include "nvim/misc1.h"
33#include "nvim/mouse.h"
34#include "nvim/garray.h"
35#include "nvim/path.h"
36#include "nvim/screen.h"
37#include "nvim/strings.h"
38#include "nvim/syntax.h"
39#include "nvim/ui.h"
40#include "nvim/types.h"
41#include "nvim/os/os.h"
42#include "nvim/os/time.h"
43#include "nvim/os/input.h"
44#include "nvim/os/shell.h"
45#include "nvim/os/signal.h"
46#include "nvim/msgpack_rpc/helpers.h"
47
48#ifdef INCLUDE_GENERATED_DECLARATIONS
49# include "os_unix.c.generated.h"
50#endif
51
52#if defined(HAVE_ACL)
53# ifdef HAVE_SYS_ACL_H
54# include <sys/acl.h>
55# endif
56# ifdef HAVE_SYS_ACCESS_H
57# include <sys/access.h>
58# endif
59
60
61// Return a pointer to the ACL of file "fname" in allocated memory.
62// Return NULL if the ACL is not available for whatever reason.
63vim_acl_T mch_get_acl(const char_u *fname)
64{
65 vim_acl_T ret = NULL;
66 return ret;
67}
68
69// Set the ACL of file "fname" to "acl" (unless it's NULL).
70void mch_set_acl(const char_u *fname, vim_acl_T aclent)
71{
72 if (aclent == NULL)
73 return;
74}
75
76void mch_free_acl(vim_acl_T aclent)
77{
78 if (aclent == NULL)
79 return;
80}
81#endif
82
83void mch_exit(int r)
84 FUNC_ATTR_NORETURN
85{
86 exiting = true;
87
88 ui_flush();
89 ui_call_stop();
90 ml_close_all(true); // remove all memfiles
91
92 if (!event_teardown() && r == 0) {
93 r = 1; // Exit with error if main_loop did not teardown gracefully.
94 }
95 if (input_global_fd() >= 0) {
96 stream_set_blocking(input_global_fd(), true); // normalize stream (#2598)
97 }
98
99 ILOG("Nvim exit: %d", r);
100
101#ifdef EXITFREE
102 free_all_mem();
103#endif
104
105 exit(r);
106}
107
108#define SHELL_SPECIAL (char_u *)"\t \"&'$;<>()\\|"
109
110/// Does wildcard pattern matching using the shell.
111///
112/// @param num_pat is the number of input patterns.
113/// @param pat is an array of pointers to input patterns.
114/// @param[out] num_file is pointer to number of matched file names.
115/// Set to the number of pointers in *file.
116/// @param[out] file is pointer to array of pointers to matched file names.
117/// Memory pointed to by the initial value of *file will
118/// not be freed.
119/// Set to NULL if FAIL is returned. Otherwise points to
120/// allocated memory.
121/// @param flags is a combination of EW_* flags used in
122/// expand_wildcards().
123/// If matching fails but EW_NOTFOUND is set in flags or
124/// there are no wildcards, the patterns from pat are
125/// copied into *file.
126///
127/// @returns OK for success or FAIL for error.
128int mch_expand_wildcards(int num_pat, char_u **pat, int *num_file,
129 char_u ***file, int flags) FUNC_ATTR_NONNULL_ARG(3)
130 FUNC_ATTR_NONNULL_ARG(4)
131{
132 int i;
133 size_t len;
134 char_u *p;
135 bool dir;
136 char_u *extra_shell_arg = NULL;
137 ShellOpts shellopts = kShellOptExpand | kShellOptSilent;
138 int j;
139 char_u *tempname;
140 char_u *command;
141 FILE *fd;
142 char_u *buffer;
143#define STYLE_ECHO 0 /* use "echo", the default */
144#define STYLE_GLOB 1 /* use "glob", for csh */
145#define STYLE_VIMGLOB 2 /* use "vimglob", for Posix sh */
146#define STYLE_PRINT 3 /* use "print -N", for zsh */
147#define STYLE_BT 4 /* `cmd` expansion, execute the pattern
148 * directly */
149 int shell_style = STYLE_ECHO;
150 int check_spaces;
151 static bool did_find_nul = false;
152 bool ampersent = false;
153 // vimglob() function to define for Posix shell
154 static char *sh_vimglob_func =
155 "vimglob() { while [ $# -ge 1 ]; do echo \"$1\"; shift; done }; vimglob >";
156
157 bool is_fish_shell =
158#if defined(UNIX)
159 STRNCMP(invocation_path_tail(p_sh, NULL), "fish", 4) == 0;
160#else
161 false;
162#endif
163
164 *num_file = 0; // default: no files found
165 *file = NULL;
166
167 // If there are no wildcards, just copy the names to allocated memory.
168 // Saves a lot of time, because we don't have to start a new shell.
169 if (!have_wildcard(num_pat, pat)) {
170 save_patterns(num_pat, pat, num_file, file);
171 return OK;
172 }
173
174 // Don't allow any shell command in the sandbox.
175 if (sandbox != 0 && check_secure()) {
176 return FAIL;
177 }
178
179 // Don't allow the use of backticks in secure and restricted mode.
180 if (secure || restricted) {
181 for (i = 0; i < num_pat; i++) {
182 if (vim_strchr(pat[i], '`') != NULL
183 && (check_restricted() || check_secure())) {
184 return FAIL;
185 }
186 }
187 }
188
189 // get a name for the temp file
190 if ((tempname = vim_tempname()) == NULL) {
191 EMSG(_(e_notmp));
192 return FAIL;
193 }
194
195 // Let the shell expand the patterns and write the result into the temp
196 // file.
197 // STYLE_BT: NL separated
198 // If expanding `cmd` execute it directly.
199 // STYLE_GLOB: NUL separated
200 // If we use *csh, "glob" will work better than "echo".
201 // STYLE_PRINT: NL or NUL separated
202 // If we use *zsh, "print -N" will work better than "glob".
203 // STYLE_VIMGLOB: NL separated
204 // If we use *sh*, we define "vimglob()".
205 // STYLE_ECHO: space separated.
206 // A shell we don't know, stay safe and use "echo".
207 if (num_pat == 1 && *pat[0] == '`'
208 && (len = STRLEN(pat[0])) > 2
209 && *(pat[0] + len - 1) == '`') {
210 shell_style = STYLE_BT;
211 } else if ((len = STRLEN(p_sh)) >= 3) {
212 if (STRCMP(p_sh + len - 3, "csh") == 0) {
213 shell_style = STYLE_GLOB;
214 } else if (STRCMP(p_sh + len - 3, "zsh") == 0) {
215 shell_style = STYLE_PRINT;
216 }
217 }
218 if (shell_style == STYLE_ECHO && strstr((char *)path_tail(p_sh),
219 "sh") != NULL)
220 shell_style = STYLE_VIMGLOB;
221
222 // Compute the length of the command. We need 2 extra bytes: for the
223 // optional '&' and for the NUL.
224 // Worst case: "unset nonomatch; print -N >" plus two is 29
225 len = STRLEN(tempname) + 29;
226 if (shell_style == STYLE_VIMGLOB)
227 len += STRLEN(sh_vimglob_func);
228
229 for (i = 0; i < num_pat; i++) {
230 // Count the length of the patterns in the same way as they are put in
231 // "command" below.
232 len++; // add space
233 for (j = 0; pat[i][j] != NUL; j++) {
234 if (vim_strchr(SHELL_SPECIAL, pat[i][j]) != NULL) {
235 len++; // may add a backslash
236 }
237 len++;
238 }
239 }
240
241 if (is_fish_shell) {
242 len += sizeof("egin;"" end") - 1;
243 }
244
245 command = xmalloc(len);
246
247 // Build the shell command:
248 // - Set $nonomatch depending on EW_NOTFOUND (hopefully the shell
249 // recognizes this).
250 // - Add the shell command to print the expanded names.
251 // - Add the temp file name.
252 // - Add the file name patterns.
253 if (shell_style == STYLE_BT) {
254 // change `command; command& ` to (command; command )
255 if (is_fish_shell) {
256 STRCPY(command, "begin; ");
257 } else {
258 STRCPY(command, "(");
259 }
260 STRCAT(command, pat[0] + 1); // exclude first backtick
261 p = command + STRLEN(command) - 1;
262 if (is_fish_shell) {
263 *p-- = ';';
264 STRCAT(command, " end");
265 } else {
266 *p-- = ')'; // remove last backtick
267 }
268 while (p > command && ascii_iswhite(*p)) {
269 p--;
270 }
271 if (*p == '&') { // remove trailing '&'
272 ampersent = true;
273 *p = ' ';
274 }
275 STRCAT(command, ">");
276 } else {
277 if (flags & EW_NOTFOUND)
278 STRCPY(command, "set nonomatch; ");
279 else
280 STRCPY(command, "unset nonomatch; ");
281 if (shell_style == STYLE_GLOB)
282 STRCAT(command, "glob >");
283 else if (shell_style == STYLE_PRINT)
284 STRCAT(command, "print -N >");
285 else if (shell_style == STYLE_VIMGLOB)
286 STRCAT(command, sh_vimglob_func);
287 else
288 STRCAT(command, "echo >");
289 }
290
291 STRCAT(command, tempname);
292
293 if (shell_style != STYLE_BT) {
294 for (i = 0; i < num_pat; i++) {
295 // Put a backslash before special
296 // characters, except inside ``.
297 bool intick = false;
298
299 p = command + STRLEN(command);
300 *p++ = ' ';
301 for (j = 0; pat[i][j] != NUL; j++) {
302 if (pat[i][j] == '`') {
303 intick = !intick;
304 } else if (pat[i][j] == '\\' && pat[i][j + 1] != NUL) {
305 // Remove a backslash, take char literally. But keep
306 // backslash inside backticks, before a special character
307 // and before a backtick.
308 if (intick
309 || vim_strchr(SHELL_SPECIAL, pat[i][j + 1]) != NULL
310 || pat[i][j + 1] == '`') {
311 *p++ = '\\';
312 }
313 j++;
314 } else if (!intick
315 && ((flags & EW_KEEPDOLLAR) == 0 || pat[i][j] != '$')
316 && vim_strchr(SHELL_SPECIAL, pat[i][j]) != NULL) {
317 // Put a backslash before a special character, but not
318 // when inside ``. And not for $var when EW_KEEPDOLLAR is
319 // set.
320 *p++ = '\\';
321 }
322
323 // Copy one character.
324 *p++ = pat[i][j];
325 }
326 *p = NUL;
327 }
328 }
329
330 if (flags & EW_SILENT) {
331 shellopts |= kShellOptHideMess;
332 }
333
334 if (ampersent) {
335 STRCAT(command, "&"); // put the '&' after the redirection
336 }
337
338 // Using zsh -G: If a pattern has no matches, it is just deleted from
339 // the argument list, otherwise zsh gives an error message and doesn't
340 // expand any other pattern.
341 if (shell_style == STYLE_PRINT) {
342 extra_shell_arg = (char_u *)"-G"; // Use zsh NULL_GLOB option
343
344 // If we use -f then shell variables set in .cshrc won't get expanded.
345 // vi can do it, so we will too, but it is only necessary if there is a "$"
346 // in one of the patterns, otherwise we can still use the fast option.
347 } else if (shell_style == STYLE_GLOB && !have_dollars(num_pat, pat)) {
348 extra_shell_arg = (char_u *)"-f"; // Use csh fast option
349 }
350
351 // execute the shell command
352 i = call_shell(
353 command,
354 shellopts,
355 extra_shell_arg
356 );
357
358 // When running in the background, give it some time to create the temp
359 // file, but don't wait for it to finish.
360 if (ampersent) {
361 os_delay(10L, true);
362 }
363
364 xfree(command);
365
366 if (i) { // os_call_shell() failed
367 os_remove((char *)tempname);
368 xfree(tempname);
369 // With interactive completion, the error message is not printed.
370 if (!(flags & EW_SILENT)) {
371 msg_putchar('\n'); // clear bottom line quickly
372 cmdline_row = Rows - 1; // continue on last line
373 MSG(_(e_wildexpand));
374 msg_start(); // don't overwrite this message
375 }
376
377 // If a `cmd` expansion failed, don't list `cmd` as a match, even when
378 // EW_NOTFOUND is given
379 if (shell_style == STYLE_BT) {
380 return FAIL;
381 }
382 goto notfound;
383 }
384
385 // read the names from the file into memory
386 fd = fopen((char *)tempname, READBIN);
387 if (fd == NULL) {
388 // Something went wrong, perhaps a file name with a special char.
389 if (!(flags & EW_SILENT)) {
390 MSG(_(e_wildexpand));
391 msg_start(); // don't overwrite this message
392 }
393 xfree(tempname);
394 goto notfound;
395 }
396 int fseek_res = fseek(fd, 0L, SEEK_END);
397 if (fseek_res < 0) {
398 xfree(tempname);
399 fclose(fd);
400 return FAIL;
401 }
402 int64_t templen = ftell(fd); // get size of temp file
403 if (templen < 0) {
404 xfree(tempname);
405 fclose(fd);
406 return FAIL;
407 }
408#if SIZEOF_LONG_LONG > SIZEOF_SIZE_T
409 assert(templen <= (long long)SIZE_MAX);
410#endif
411 len = (size_t)templen;
412 fseek(fd, 0L, SEEK_SET);
413 buffer = xmalloc(len + 1);
414 // fread() doesn't terminate buffer with NUL;
415 // appropiate termination (not always NUL) is done below.
416 size_t readlen = fread((char *)buffer, 1, len, fd);
417 fclose(fd);
418 os_remove((char *)tempname);
419 if (readlen != len) {
420 // unexpected read error
421 EMSG2(_(e_notread), tempname);
422 xfree(tempname);
423 xfree(buffer);
424 return FAIL;
425 }
426 xfree(tempname);
427
428 // file names are separated with Space
429 if (shell_style == STYLE_ECHO) {
430 buffer[len] = '\n'; // make sure the buffer ends in NL
431 p = buffer;
432 for (i = 0; *p != '\n'; i++) { // count number of entries
433 while (*p != ' ' && *p != '\n') {
434 p++;
435 }
436 p = skipwhite(p); // skip to next entry
437 }
438 // file names are separated with NL
439 } else if (shell_style == STYLE_BT || shell_style == STYLE_VIMGLOB) {
440 buffer[len] = NUL; // make sure the buffer ends in NUL
441 p = buffer;
442 for (i = 0; *p != NUL; i++) { // count number of entries
443 while (*p != '\n' && *p != NUL) {
444 p++;
445 }
446 if (*p != NUL) {
447 p++;
448 }
449 p = skipwhite(p); // skip leading white space
450 }
451 // file names are separated with NUL
452 } else {
453 // Some versions of zsh use spaces instead of NULs to separate
454 // results. Only do this when there is no NUL before the end of the
455 // buffer, otherwise we would never be able to use file names with
456 // embedded spaces when zsh does use NULs.
457 // When we found a NUL once, we know zsh is OK, set did_find_nul and
458 // don't check for spaces again.
459 check_spaces = false;
460 if (shell_style == STYLE_PRINT && !did_find_nul) {
461 // If there is a NUL, set did_find_nul, else set check_spaces
462 buffer[len] = NUL;
463 if (len && (int)STRLEN(buffer) < (int)len)
464 did_find_nul = true;
465 else
466 check_spaces = true;
467 }
468
469 // Make sure the buffer ends with a NUL. For STYLE_PRINT there
470 // already is one, for STYLE_GLOB it needs to be added.
471 if (len && buffer[len - 1] == NUL) {
472 len--;
473 } else {
474 buffer[len] = NUL;
475 }
476 i = 0;
477 for (p = buffer; p < buffer + len; p++) {
478 if (*p == NUL || (*p == ' ' && check_spaces)) { // count entry
479 i++;
480 *p = NUL;
481 }
482 }
483 if (len) {
484 i++; // count last entry
485 }
486 }
487 assert(buffer[len] == NUL || buffer[len] == '\n');
488
489 if (i == 0) {
490 // Can happen when using /bin/sh and typing ":e $NO_SUCH_VAR^I".
491 // /bin/sh will happily expand it to nothing rather than returning an
492 // error; and hey, it's good to check anyway -- webb.
493 xfree(buffer);
494 goto notfound;
495 }
496 *num_file = i;
497 *file = xmalloc(sizeof(char_u *) * (size_t)i);
498
499 // Isolate the individual file names.
500 p = buffer;
501 for (i = 0; i < *num_file; ++i) {
502 (*file)[i] = p;
503 // Space or NL separates
504 if (shell_style == STYLE_ECHO || shell_style == STYLE_BT
505 || shell_style == STYLE_VIMGLOB) {
506 while (!(shell_style == STYLE_ECHO && *p == ' ')
507 && *p != '\n' && *p != NUL) {
508 p++;
509 }
510 if (p == buffer + len) { // last entry
511 *p = NUL;
512 } else {
513 *p++ = NUL;
514 p = skipwhite(p); // skip to next entry
515 }
516 } else { // NUL separates
517 while (*p && p < buffer + len) { // skip entry
518 p++;
519 }
520 p++; // skip NUL
521 }
522 }
523
524 // Move the file names to allocated memory.
525 for (j = 0, i = 0; i < *num_file; i++) {
526 // Require the files to exist. Helps when using /bin/sh
527 if (!(flags & EW_NOTFOUND) && !os_path_exists((*file)[i])) {
528 continue;
529 }
530
531 // check if this entry should be included
532 dir = (os_isdir((*file)[i]));
533 if ((dir && !(flags & EW_DIR)) || (!dir && !(flags & EW_FILE)))
534 continue;
535
536 // Skip files that are not executable if we check for that.
537 if (!dir && (flags & EW_EXEC)
538 && !os_can_exe((char *)(*file)[i], NULL, !(flags & EW_SHELLCMD))) {
539 continue;
540 }
541
542 p = xmalloc(STRLEN((*file)[i]) + 1 + dir);
543 STRCPY(p, (*file)[i]);
544 if (dir) {
545 add_pathsep((char *)p); // add '/' to a directory name
546 }
547 (*file)[j++] = p;
548 }
549 xfree(buffer);
550 *num_file = j;
551
552 if (*num_file == 0) { // rejected all entries
553 XFREE_CLEAR(*file);
554 goto notfound;
555 }
556
557 return OK;
558
559notfound:
560 if (flags & EW_NOTFOUND) {
561 save_patterns(num_pat, pat, num_file, file);
562 return OK;
563 }
564 return FAIL;
565
566}
567
568
569static void save_patterns(int num_pat, char_u **pat, int *num_file,
570 char_u ***file)
571{
572 int i;
573 char_u *s;
574
575 *file = xmalloc((size_t)num_pat * sizeof(char_u *));
576
577 for (i = 0; i < num_pat; i++) {
578 s = vim_strsave(pat[i]);
579 // Be compatible with expand_filename(): halve the number of
580 // backslashes.
581 backslash_halve(s);
582 (*file)[i] = s;
583 }
584 *num_file = num_pat;
585}
586
587static bool have_wildcard(int num, char_u **file)
588{
589 int i;
590
591 for (i = 0; i < num; i++)
592 if (path_has_wildcard(file[i]))
593 return true;
594 return false;
595}
596
597static bool have_dollars(int num, char_u **file)
598{
599 int i;
600
601 for (i = 0; i < num; i++)
602 if (vim_strchr(file[i], '$') != NULL)
603 return true;
604 return false;
605}
606