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. |
63 | vim_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). |
70 | void mch_set_acl(const char_u *fname, vim_acl_T aclent) |
71 | { |
72 | if (aclent == NULL) |
73 | return; |
74 | } |
75 | |
76 | void mch_free_acl(vim_acl_T aclent) |
77 | { |
78 | if (aclent == NULL) |
79 | return; |
80 | } |
81 | #endif |
82 | |
83 | void 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. |
128 | int 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 * = 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 | |
559 | notfound: |
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 | |
569 | static 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 | |
587 | static 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 | |
597 | static 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 | |