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 * misc1.c: functions that didn't seem to fit elsewhere
6 */
7
8 #include <assert.h>
9 #include <inttypes.h>
10 #include <limits.h>
11 #include <stdbool.h>
12 #include <string.h>
13
14 #include "nvim/ascii.h"
15 #include "nvim/buffer.h"
16 #include "nvim/buffer_updates.h"
17 #include "nvim/charset.h"
18 #include "nvim/cursor.h"
19 #include "nvim/diff.h"
20 #include "nvim/edit.h"
21 #include "nvim/eval.h"
22 #include "nvim/event/stream.h"
23 #include "nvim/ex_cmds.h"
24 #include "nvim/ex_docmd.h"
25 #include "nvim/ex_getln.h"
26 #include "nvim/fileio.h"
27 #include "nvim/fold.h"
28 #include "nvim/func_attr.h"
29 #include "nvim/garray.h"
30 #include "nvim/getchar.h"
31 #include "nvim/indent.h"
32 #include "nvim/indent_c.h"
33 #include "nvim/main.h"
34 #include "nvim/mbyte.h"
35 #include "nvim/memline.h"
36 #include "nvim/memory.h"
37 #include "nvim/message.h"
38 #include "nvim/misc1.h"
39 #include "nvim/mouse.h"
40 #include "nvim/move.h"
41 #include "nvim/option.h"
42 #include "nvim/os/input.h"
43 #include "nvim/os/os.h"
44 #include "nvim/os/shell.h"
45 #include "nvim/os/signal.h"
46 #include "nvim/os/time.h"
47 #include "nvim/os_unix.h"
48 #include "nvim/quickfix.h"
49 #include "nvim/regexp.h"
50 #include "nvim/screen.h"
51 #include "nvim/search.h"
52 #include "nvim/state.h"
53 #include "nvim/strings.h"
54 #include "nvim/tag.h"
55 #include "nvim/ui.h"
56 #include "nvim/undo.h"
57 #include "nvim/vim.h"
58 #include "nvim/window.h"
59
60 #ifdef INCLUDE_GENERATED_DECLARATIONS
61 # include "misc1.c.generated.h"
62 #endif
63 // All user names (for ~user completion as done by shell).
64 static garray_T ga_users = GA_EMPTY_INIT_VALUE;
65
66 /*
67 * get_leader_len() returns the length in bytes of the prefix of the given
68 * string which introduces a comment. If this string is not a comment then
69 * 0 is returned.
70 * When "flags" is not NULL, it is set to point to the flags of the recognized
71 * comment leader.
72 * "backward" must be true for the "O" command.
73 * If "include_space" is set, include trailing whitespace while calculating the
74 * length.
75 */
get_leader_len(char_u * line,char_u ** flags,bool backward,bool include_space)76 int get_leader_len(char_u *line, char_u **flags, bool backward, bool include_space)
77 {
78 int i, j;
79 int result;
80 int got_com = FALSE;
81 int found_one;
82 char_u part_buf[COM_MAX_LEN]; // buffer for one option part
83 char_u *string; // pointer to comment string
84 char_u *list;
85 int middle_match_len = 0;
86 char_u *prev_list;
87 char_u *saved_flags = NULL;
88
89 result = i = 0;
90 while (ascii_iswhite(line[i])) { // leading white space is ignored
91 ++i;
92 }
93
94 /*
95 * Repeat to match several nested comment strings.
96 */
97 while (line[i] != NUL) {
98 /*
99 * scan through the 'comments' option for a match
100 */
101 found_one = FALSE;
102 for (list = curbuf->b_p_com; *list;) {
103 // Get one option part into part_buf[]. Advance "list" to next
104 // one. Put "string" at start of string.
105 if (!got_com && flags != NULL) {
106 *flags = list; // remember where flags started
107 }
108 prev_list = list;
109 (void)copy_option_part(&list, part_buf, COM_MAX_LEN, ",");
110 string = vim_strchr(part_buf, ':');
111 if (string == NULL) { // missing ':', ignore this part
112 continue;
113 }
114 *string++ = NUL; // isolate flags from string
115
116 // If we found a middle match previously, use that match when this
117 // is not a middle or end.
118 if (middle_match_len != 0
119 && vim_strchr(part_buf, COM_MIDDLE) == NULL
120 && vim_strchr(part_buf, COM_END) == NULL) {
121 break;
122 }
123
124 // When we already found a nested comment, only accept further
125 // nested comments.
126 if (got_com && vim_strchr(part_buf, COM_NEST) == NULL) {
127 continue;
128 }
129
130 // When 'O' flag present and using "O" command skip this one.
131 if (backward && vim_strchr(part_buf, COM_NOBACK) != NULL) {
132 continue;
133 }
134
135 // Line contents and string must match.
136 // When string starts with white space, must have some white space
137 // (but the amount does not need to match, there might be a mix of
138 // TABs and spaces).
139 if (ascii_iswhite(string[0])) {
140 if (i == 0 || !ascii_iswhite(line[i - 1])) {
141 continue; // missing white space
142 }
143 while (ascii_iswhite(string[0])) {
144 ++string;
145 }
146 }
147 for (j = 0; string[j] != NUL && string[j] == line[i + j]; ++j) {
148 }
149 if (string[j] != NUL) {
150 continue; // string doesn't match
151 }
152 // When 'b' flag used, there must be white space or an
153 // end-of-line after the string in the line.
154 if (vim_strchr(part_buf, COM_BLANK) != NULL
155 && !ascii_iswhite(line[i + j]) && line[i + j] != NUL) {
156 continue;
157 }
158
159 // We have found a match, stop searching unless this is a middle
160 // comment. The middle comment can be a substring of the end
161 // comment in which case it's better to return the length of the
162 // end comment and its flags. Thus we keep searching with middle
163 // and end matches and use an end match if it matches better.
164 if (vim_strchr(part_buf, COM_MIDDLE) != NULL) {
165 if (middle_match_len == 0) {
166 middle_match_len = j;
167 saved_flags = prev_list;
168 }
169 continue;
170 }
171 if (middle_match_len != 0 && j > middle_match_len) {
172 // Use this match instead of the middle match, since it's a
173 // longer thus better match.
174 middle_match_len = 0;
175 }
176
177 if (middle_match_len == 0) {
178 i += j;
179 }
180 found_one = TRUE;
181 break;
182 }
183
184 if (middle_match_len != 0) {
185 // Use the previously found middle match after failing to find a
186 // match with an end.
187 if (!got_com && flags != NULL) {
188 *flags = saved_flags;
189 }
190 i += middle_match_len;
191 found_one = TRUE;
192 }
193
194 // No match found, stop scanning.
195 if (!found_one) {
196 break;
197 }
198
199 result = i;
200
201 // Include any trailing white space.
202 while (ascii_iswhite(line[i])) {
203 ++i;
204 }
205
206 if (include_space) {
207 result = i;
208 }
209
210 // If this comment doesn't nest, stop here.
211 got_com = TRUE;
212 if (vim_strchr(part_buf, COM_NEST) == NULL) {
213 break;
214 }
215 }
216 return result;
217 }
218
219 /*
220 * Return the offset at which the last comment in line starts. If there is no
221 * comment in the whole line, -1 is returned.
222 *
223 * When "flags" is not null, it is set to point to the flags describing the
224 * recognized comment leader.
225 */
get_last_leader_offset(char_u * line,char_u ** flags)226 int get_last_leader_offset(char_u *line, char_u **flags)
227 {
228 int result = -1;
229 int i, j;
230 int lower_check_bound = 0;
231 char_u *string;
232 char_u *com_leader;
233 char_u *com_flags;
234 char_u *list;
235 int found_one;
236 char_u part_buf[COM_MAX_LEN]; // buffer for one option part
237
238 /*
239 * Repeat to match several nested comment strings.
240 */
241 i = (int)STRLEN(line);
242 while (--i >= lower_check_bound) {
243 /*
244 * scan through the 'comments' option for a match
245 */
246 found_one = FALSE;
247 for (list = curbuf->b_p_com; *list;) {
248 char_u *flags_save = list;
249
250 /*
251 * Get one option part into part_buf[]. Advance list to next one.
252 * put string at start of string.
253 */
254 (void)copy_option_part(&list, part_buf, COM_MAX_LEN, ",");
255 string = vim_strchr(part_buf, ':');
256 if (string == NULL) { // If everything is fine, this cannot actually
257 // happen.
258 continue;
259 }
260 *string++ = NUL; // Isolate flags from string.
261 com_leader = string;
262
263 /*
264 * Line contents and string must match.
265 * When string starts with white space, must have some white space
266 * (but the amount does not need to match, there might be a mix of
267 * TABs and spaces).
268 */
269 if (ascii_iswhite(string[0])) {
270 if (i == 0 || !ascii_iswhite(line[i - 1])) {
271 continue;
272 }
273 while (ascii_iswhite(*string)) {
274 string++;
275 }
276 }
277 for (j = 0; string[j] != NUL && string[j] == line[i + j]; ++j) {
278 // do nothing
279 }
280 if (string[j] != NUL) {
281 continue;
282 }
283
284 /*
285 * When 'b' flag used, there must be white space or an
286 * end-of-line after the string in the line.
287 */
288 if (vim_strchr(part_buf, COM_BLANK) != NULL
289 && !ascii_iswhite(line[i + j]) && line[i + j] != NUL) {
290 continue;
291 }
292
293 if (vim_strchr(part_buf, COM_MIDDLE) != NULL) {
294 // For a middlepart comment, only consider it to match if
295 // everything before the current position in the line is
296 // whitespace. Otherwise we would think we are inside a
297 // comment if the middle part appears somewhere in the middle
298 // of the line. E.g. for C the "*" appears often.
299 for (j = 0; j <= i && ascii_iswhite(line[j]); j++) {
300 }
301 if (j < i) {
302 continue;
303 }
304 }
305
306 /*
307 * We have found a match, stop searching.
308 */
309 found_one = TRUE;
310
311 if (flags) {
312 *flags = flags_save;
313 }
314 com_flags = flags_save;
315
316 break;
317 }
318
319 if (found_one) {
320 char_u part_buf2[COM_MAX_LEN]; // buffer for one option part
321 int len1, len2, off;
322
323 result = i;
324 /*
325 * If this comment nests, continue searching.
326 */
327 if (vim_strchr(part_buf, COM_NEST) != NULL) {
328 continue;
329 }
330
331 lower_check_bound = i;
332
333 // Let's verify whether the comment leader found is a substring
334 // of other comment leaders. If it is, let's adjust the
335 // lower_check_bound so that we make sure that we have determined
336 // the comment leader correctly.
337
338 while (ascii_iswhite(*com_leader)) {
339 ++com_leader;
340 }
341 len1 = (int)STRLEN(com_leader);
342
343 for (list = curbuf->b_p_com; *list;) {
344 char_u *flags_save = list;
345
346 (void)copy_option_part(&list, part_buf2, COM_MAX_LEN, ",");
347 if (flags_save == com_flags) {
348 continue;
349 }
350 string = vim_strchr(part_buf2, ':');
351 ++string;
352 while (ascii_iswhite(*string)) {
353 ++string;
354 }
355 len2 = (int)STRLEN(string);
356 if (len2 == 0) {
357 continue;
358 }
359
360 // Now we have to verify whether string ends with a substring
361 // beginning the com_leader.
362 for (off = (len2 > i ? i : len2); off > 0 && off + len1 > len2;) {
363 --off;
364 if (!STRNCMP(string + off, com_leader, len2 - off)) {
365 if (i - off < lower_check_bound) {
366 lower_check_bound = i - off;
367 }
368 }
369 }
370 }
371 }
372 }
373 return result;
374 }
375
gchar_pos(pos_T * pos)376 int gchar_pos(pos_T *pos)
377 FUNC_ATTR_NONNULL_ARG(1)
378 {
379 // When searching columns is sometimes put at the end of a line.
380 if (pos->col == MAXCOL) {
381 return NUL;
382 }
383 return utf_ptr2char(ml_get_pos(pos));
384 }
385
386 /*
387 * check_status: called when the status bars for the buffer 'buf'
388 * need to be updated
389 */
check_status(buf_T * buf)390 void check_status(buf_T *buf)
391 {
392 FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
393 if (wp->w_buffer == buf && wp->w_status_height) {
394 wp->w_redr_status = TRUE;
395 if (must_redraw < VALID) {
396 must_redraw = VALID;
397 }
398 }
399 }
400 }
401
402 /// Ask for a reply from the user, 'y' or 'n'
403 ///
404 /// No other characters are accepted, the message is repeated until a valid
405 /// reply is entered or <C-c> is hit.
406 ///
407 /// @param[in] str Prompt: question to ask user. Is always followed by
408 /// " (y/n)?".
409 /// @param[in] direct Determines what function to use to get user input. If
410 /// true then ui_inchar() will be used, otherwise vgetc().
411 /// I.e. when direct is true then characters are obtained
412 /// directly from the user without buffers involved.
413 ///
414 /// @return 'y' or 'n'. Last is also what will be returned in case of interrupt.
ask_yesno(const char * const str,const bool direct)415 int ask_yesno(const char *const str, const bool direct)
416 {
417 const int save_State = State;
418
419 no_wait_return++;
420 State = CONFIRM; // Mouse behaves like with :confirm.
421 setmouse(); // Disable mouse in xterm.
422 no_mapping++;
423
424 int r = ' ';
425 while (r != 'y' && r != 'n') {
426 // Same highlighting as for wait_return.
427 smsg_attr(HL_ATTR(HLF_R), "%s (y/n)?", str);
428 if (direct) {
429 r = get_keystroke(NULL);
430 } else {
431 r = plain_vgetc();
432 }
433 if (r == Ctrl_C || r == ESC) {
434 r = 'n';
435 }
436 msg_putchar(r); // Show what you typed.
437 ui_flush();
438 }
439 no_wait_return--;
440 State = save_State;
441 setmouse();
442 no_mapping--;
443
444 return r;
445 }
446
447 /*
448 * Return TRUE if "c" is a mouse key.
449 */
is_mouse_key(int c)450 int is_mouse_key(int c)
451 {
452 return c == K_LEFTMOUSE
453 || c == K_LEFTMOUSE_NM
454 || c == K_LEFTDRAG
455 || c == K_LEFTRELEASE
456 || c == K_LEFTRELEASE_NM
457 || c == K_MOUSEMOVE
458 || c == K_MIDDLEMOUSE
459 || c == K_MIDDLEDRAG
460 || c == K_MIDDLERELEASE
461 || c == K_RIGHTMOUSE
462 || c == K_RIGHTDRAG
463 || c == K_RIGHTRELEASE
464 || c == K_MOUSEDOWN
465 || c == K_MOUSEUP
466 || c == K_MOUSELEFT
467 || c == K_MOUSERIGHT
468 || c == K_X1MOUSE
469 || c == K_X1DRAG
470 || c == K_X1RELEASE
471 || c == K_X2MOUSE
472 || c == K_X2DRAG
473 || c == K_X2RELEASE;
474 }
475
476 /*
477 * Get a key stroke directly from the user.
478 * Ignores mouse clicks and scrollbar events, except a click for the left
479 * button (used at the more prompt).
480 * Doesn't use vgetc(), because it syncs undo and eats mapped characters.
481 * Disadvantage: typeahead is ignored.
482 * Translates the interrupt character for unix to ESC.
483 */
get_keystroke(MultiQueue * events)484 int get_keystroke(MultiQueue *events)
485 {
486 char_u *buf = NULL;
487 int buflen = 150;
488 int maxlen;
489 int len = 0;
490 int n;
491 int save_mapped_ctrl_c = mapped_ctrl_c;
492 int waited = 0;
493
494 mapped_ctrl_c = 0; // mappings are not used here
495 for (;;) {
496 // flush output before waiting
497 ui_flush();
498 // Leave some room for check_termcode() to insert a key code into (max
499 // 5 chars plus NUL). And fix_input_buffer() can triple the number of
500 // bytes.
501 maxlen = (buflen - 6 - len) / 3;
502 if (buf == NULL) {
503 buf = xmalloc((size_t)buflen);
504 } else if (maxlen < 10) {
505 // Need some more space. This might happen when receiving a long
506 // escape sequence.
507 buflen += 100;
508 buf = xrealloc(buf, (size_t)buflen);
509 maxlen = (buflen - 6 - len) / 3;
510 }
511
512 // First time: blocking wait. Second time: wait up to 100ms for a
513 // terminal code to complete.
514 n = os_inchar(buf + len, maxlen, len == 0 ? -1L : 100L, 0, events);
515 if (n > 0) {
516 // Replace zero and CSI by a special key code.
517 n = fix_input_buffer(buf + len, n);
518 len += n;
519 waited = 0;
520 } else if (len > 0) {
521 ++waited; // keep track of the waiting time
522 }
523 if (n > 0) { // found a termcode: adjust length
524 len = n;
525 }
526 if (len == 0) { // nothing typed yet
527 continue;
528 }
529
530 // Handle modifier and/or special key code.
531 n = buf[0];
532 if (n == K_SPECIAL) {
533 n = TO_SPECIAL(buf[1], buf[2]);
534 if (buf[1] == KS_MODIFIER
535 || n == K_IGNORE
536 || (is_mouse_key(n) && n != K_LEFTMOUSE)) {
537 if (buf[1] == KS_MODIFIER) {
538 mod_mask = buf[2];
539 }
540 len -= 3;
541 if (len > 0) {
542 memmove(buf, buf + 3, (size_t)len);
543 }
544 continue;
545 }
546 break;
547 }
548 if (MB_BYTE2LEN(n) > len) {
549 // more bytes to get.
550 continue;
551 }
552 buf[len >= buflen ? buflen - 1 : len] = NUL;
553 n = utf_ptr2char(buf);
554 break;
555 }
556 xfree(buf);
557
558 mapped_ctrl_c = save_mapped_ctrl_c;
559 return n;
560 }
561
562 /// Get a number from the user.
563 /// When "mouse_used" is not NULL allow using the mouse.
564 ///
565 /// @param colon allow colon to abort
get_number(int colon,int * mouse_used)566 int get_number(int colon, int *mouse_used)
567 {
568 int n = 0;
569 int c;
570 int typed = 0;
571
572 if (mouse_used != NULL) {
573 *mouse_used = FALSE;
574 }
575
576 // When not printing messages, the user won't know what to type, return a
577 // zero (as if CR was hit).
578 if (msg_silent != 0) {
579 return 0;
580 }
581
582 no_mapping++;
583 for (;;) {
584 ui_cursor_goto(msg_row, msg_col);
585 c = safe_vgetc();
586 if (ascii_isdigit(c)) {
587 n = n * 10 + c - '0';
588 msg_putchar(c);
589 ++typed;
590 } else if (c == K_DEL || c == K_KDEL || c == K_BS || c == Ctrl_H) {
591 if (typed > 0) {
592 msg_puts("\b \b");
593 --typed;
594 }
595 n /= 10;
596 } else if (mouse_used != NULL && c == K_LEFTMOUSE) {
597 *mouse_used = TRUE;
598 n = mouse_row + 1;
599 break;
600 } else if (n == 0 && c == ':' && colon) {
601 stuffcharReadbuff(':');
602 if (!exmode_active) {
603 cmdline_row = msg_row;
604 }
605 skip_redraw = true; // skip redraw once
606 do_redraw = false;
607 break;
608 } else if (c == Ctrl_C || c == ESC || c == 'q') {
609 n = 0;
610 break;
611 } else if (c == CAR || c == NL) {
612 break;
613 }
614 }
615 no_mapping--;
616 return n;
617 }
618
619 /*
620 * Ask the user to enter a number.
621 * When "mouse_used" is not NULL allow using the mouse and in that case return
622 * the line number.
623 */
prompt_for_number(int * mouse_used)624 int prompt_for_number(int *mouse_used)
625 {
626 int i;
627 int save_cmdline_row;
628 int save_State;
629
630 // When using ":silent" assume that <CR> was entered.
631 if (mouse_used != NULL) {
632 msg_puts(_("Type number and <Enter> or click with the mouse "
633 "(q or empty cancels): "));
634 } else {
635 msg_puts(_("Type number and <Enter> (q or empty cancels): "));
636 }
637
638 /* Set the state such that text can be selected/copied/pasted and we still
639 * get mouse events. */
640 save_cmdline_row = cmdline_row;
641 cmdline_row = 0;
642 save_State = State;
643 State = ASKMORE; // prevents a screen update when using a timer
644 // May show different mouse shape.
645 setmouse();
646
647 i = get_number(TRUE, mouse_used);
648 if (KeyTyped) {
649 // don't call wait_return() now
650 if (msg_row > 0) {
651 cmdline_row = msg_row - 1;
652 }
653 need_wait_return = false;
654 msg_didany = false;
655 msg_didout = false;
656 } else {
657 cmdline_row = save_cmdline_row;
658 }
659 State = save_State;
660 // May need to restore mouse shape.
661 setmouse();
662
663 return i;
664 }
665
msgmore(long n)666 void msgmore(long n)
667 {
668 long pn;
669
670 if (global_busy // no messages now, wait until global is finished
671 || !messaging()) { // 'lazyredraw' set, don't do messages now
672 return;
673 }
674
675 // We don't want to overwrite another important message, but do overwrite
676 // a previous "more lines" or "fewer lines" message, so that "5dd" and
677 // then "put" reports the last action.
678 if (keep_msg != NULL && !keep_msg_more) {
679 return;
680 }
681
682 if (n > 0) {
683 pn = n;
684 } else {
685 pn = -n;
686 }
687
688 if (pn > p_report) {
689 if (n > 0) {
690 vim_snprintf(msg_buf, MSG_BUF_LEN,
691 NGETTEXT("%ld more line", "%ld more lines", pn),
692 pn);
693 } else {
694 vim_snprintf(msg_buf, MSG_BUF_LEN,
695 NGETTEXT("%ld line less", "%ld fewer lines", pn),
696 pn);
697 }
698 if (got_int) {
699 xstrlcat(msg_buf, _(" (Interrupted)"), MSG_BUF_LEN);
700 }
701 if (msg(msg_buf)) {
702 set_keep_msg(msg_buf, 0);
703 keep_msg_more = true;
704 }
705 }
706 }
707
708 /*
709 * flush map and typeahead buffers and give a warning for an error
710 */
beep_flush(void)711 void beep_flush(void)
712 {
713 if (emsg_silent == 0) {
714 flush_buffers(FLUSH_MINIMAL);
715 vim_beep(BO_ERROR);
716 }
717 }
718
719 // Give a warning for an error
720 // val is one of the BO_ values, e.g., BO_OPER
vim_beep(unsigned val)721 void vim_beep(unsigned val)
722 {
723 called_vim_beep = true;
724
725 if (emsg_silent == 0) {
726 if (!((bo_flags & val) || (bo_flags & BO_ALL))) {
727 static int beeps = 0;
728 static uint64_t start_time = 0;
729
730 // Only beep up to three times per half a second,
731 // otherwise a sequence of beeps would freeze Vim.
732 if (start_time == 0 || os_hrtime() - start_time > 500000000u) {
733 beeps = 0;
734 start_time = os_hrtime();
735 }
736 beeps++;
737 if (beeps <= 3) {
738 if (p_vb) {
739 ui_call_visual_bell();
740 } else {
741 ui_call_bell();
742 }
743 }
744 }
745
746 // When 'debug' contains "beep" produce a message. If we are sourcing
747 // a script or executing a function give the user a hint where the beep
748 // comes from.
749 if (vim_strchr(p_debug, 'e') != NULL) {
750 msg_source(HL_ATTR(HLF_W));
751 msg_attr(_("Beep!"), HL_ATTR(HLF_W));
752 }
753 }
754 }
755
756 #if defined(EXITFREE)
757
free_users(void)758 void free_users(void)
759 {
760 ga_clear_strings(&ga_users);
761 }
762
763 #endif
764
765 /*
766 * Find all user names for user completion.
767 * Done only once and then cached.
768 */
init_users(void)769 static void init_users(void)
770 {
771 static int lazy_init_done = FALSE;
772
773 if (lazy_init_done) {
774 return;
775 }
776
777 lazy_init_done = TRUE;
778
779 os_get_usernames(&ga_users);
780 }
781
782 /*
783 * Function given to ExpandGeneric() to obtain an user names.
784 */
get_users(expand_T * xp,int idx)785 char_u *get_users(expand_T *xp, int idx)
786 {
787 init_users();
788 if (idx < ga_users.ga_len) {
789 return ((char_u **)ga_users.ga_data)[idx];
790 }
791 return NULL;
792 }
793
794 /*
795 * Check whether name matches a user name. Return:
796 * 0 if name does not match any user name.
797 * 1 if name partially matches the beginning of a user name.
798 * 2 is name fully matches a user name.
799 */
match_user(char_u * name)800 int match_user(char_u *name)
801 {
802 int n = (int)STRLEN(name);
803 int result = 0;
804
805 init_users();
806 for (int i = 0; i < ga_users.ga_len; i++) {
807 if (STRCMP(((char_u **)ga_users.ga_data)[i], name) == 0) {
808 return 2; // full match
809 }
810 if (STRNCMP(((char_u **)ga_users.ga_data)[i], name, n) == 0) {
811 result = 1; // partial match
812 }
813 }
814 return result;
815 }
816
817 /// Preserve files and exit.
818 /// @note IObuff must contain a message.
819 /// @note This may be called from deadly_signal() in a signal handler, avoid
820 /// unsafe functions, such as allocating memory.
preserve_exit(void)821 void preserve_exit(void)
822 FUNC_ATTR_NORETURN
823 {
824 // 'true' when we are sure to exit, e.g., after a deadly signal
825 static bool really_exiting = false;
826
827 // Prevent repeated calls into this method.
828 if (really_exiting) {
829 if (input_global_fd() >= 0) {
830 // normalize stream (#2598)
831 stream_set_blocking(input_global_fd(), true);
832 }
833 exit(2);
834 }
835
836 really_exiting = true;
837 // Ignore SIGHUP while we are already exiting. #9274
838 signal_reject_deadly();
839 mch_errmsg(IObuff);
840 mch_errmsg("\n");
841 ui_flush();
842
843 ml_close_notmod(); // close all not-modified buffers
844
845 FOR_ALL_BUFFERS(buf) {
846 if (buf->b_ml.ml_mfp != NULL && buf->b_ml.ml_mfp->mf_fname != NULL) {
847 mch_errmsg("Vim: preserving files...\r\n");
848 ui_flush();
849 ml_sync_all(false, false, true); // preserve all swap files
850 break;
851 }
852 }
853
854 ml_close_all(false); // close all memfiles, without deleting
855
856 mch_errmsg("Vim: Finished.\r\n");
857
858 getout(1);
859 }
860
861 /*
862 * Check for CTRL-C pressed, but only once in a while.
863 * Should be used instead of os_breakcheck() for functions that check for
864 * each line in the file. Calling os_breakcheck() each time takes too much
865 * time, because it can be a system call.
866 */
867
868 #ifndef BREAKCHECK_SKIP
869 # define BREAKCHECK_SKIP 1000
870 #endif
871
872 static int breakcheck_count = 0;
873
line_breakcheck(void)874 void line_breakcheck(void)
875 {
876 if (++breakcheck_count >= BREAKCHECK_SKIP) {
877 breakcheck_count = 0;
878 os_breakcheck();
879 }
880 }
881
882 /*
883 * Like line_breakcheck() but check 10 times less often.
884 */
fast_breakcheck(void)885 void fast_breakcheck(void)
886 {
887 if (++breakcheck_count >= BREAKCHECK_SKIP * 10) {
888 breakcheck_count = 0;
889 os_breakcheck();
890 }
891 }
892
893 // Like line_breakcheck() but check 100 times less often.
veryfast_breakcheck(void)894 void veryfast_breakcheck(void)
895 {
896 if (++breakcheck_count >= BREAKCHECK_SKIP * 100) {
897 breakcheck_count = 0;
898 os_breakcheck();
899 }
900 }
901
902 /// os_call_shell() wrapper. Handles 'verbose', :profile, and v:shell_error.
903 /// Invalidates cached tags.
904 ///
905 /// @return shell command exit code
call_shell(char_u * cmd,ShellOpts opts,char_u * extra_shell_arg)906 int call_shell(char_u *cmd, ShellOpts opts, char_u *extra_shell_arg)
907 {
908 int retval;
909 proftime_T wait_time;
910
911 if (p_verbose > 3) {
912 verbose_enter();
913 smsg(_("Executing command: \"%s\""), cmd == NULL ? p_sh : cmd);
914 msg_putchar('\n');
915 verbose_leave();
916 }
917
918 if (do_profiling == PROF_YES) {
919 prof_child_enter(&wait_time);
920 }
921
922 if (*p_sh == NUL) {
923 emsg(_(e_shellempty));
924 retval = -1;
925 } else {
926 // The external command may update a tags file, clear cached tags.
927 tag_freematch();
928
929 retval = os_call_shell(cmd, opts, extra_shell_arg);
930 }
931
932 set_vim_var_nr(VV_SHELL_ERROR, (varnumber_T)retval);
933 if (do_profiling == PROF_YES) {
934 prof_child_exit(&wait_time);
935 }
936
937 return retval;
938 }
939
940 /// Get the stdout of an external command.
941 /// If "ret_len" is NULL replace NUL characters with NL. When "ret_len" is not
942 /// NULL store the length there.
943 ///
944 /// @param cmd command to execute
945 /// @param infile optional input file name
946 /// @param flags can be kShellOptSilent or 0
947 /// @param ret_len length of the stdout
948 ///
949 /// @return an allocated string, or NULL for error.
get_cmd_output(char_u * cmd,char_u * infile,ShellOpts flags,size_t * ret_len)950 char_u *get_cmd_output(char_u *cmd, char_u *infile, ShellOpts flags, size_t *ret_len)
951 {
952 char_u *buffer = NULL;
953
954 if (check_secure()) {
955 return NULL;
956 }
957
958 // get a name for the temp file
959 char_u *tempname = vim_tempname();
960 if (tempname == NULL) {
961 emsg(_(e_notmp));
962 return NULL;
963 }
964
965 // Add the redirection stuff
966 char_u *command = make_filter_cmd(cmd, infile, tempname);
967
968 /*
969 * Call the shell to execute the command (errors are ignored).
970 * Don't check timestamps here.
971 */
972 ++no_check_timestamps;
973 call_shell(command, kShellOptDoOut | kShellOptExpand | flags, NULL);
974 --no_check_timestamps;
975
976 xfree(command);
977
978 // read the names from the file into memory
979 FILE *fd = os_fopen((char *)tempname, READBIN);
980
981 if (fd == NULL) {
982 semsg(_(e_notopen), tempname);
983 goto done;
984 }
985
986 fseek(fd, 0L, SEEK_END);
987 size_t len = (size_t)ftell(fd); // get size of temp file
988 fseek(fd, 0L, SEEK_SET);
989
990 buffer = xmalloc(len + 1);
991 size_t i = fread((char *)buffer, 1, len, fd);
992 fclose(fd);
993 os_remove((char *)tempname);
994 if (i != len) {
995 semsg(_(e_notread), tempname);
996 XFREE_CLEAR(buffer);
997 } else if (ret_len == NULL) {
998 // Change NUL into SOH, otherwise the string is truncated.
999 for (i = 0; i < len; ++i) {
1000 if (buffer[i] == NUL) {
1001 buffer[i] = 1;
1002 }
1003 }
1004
1005 buffer[len] = NUL; // make sure the buffer is terminated
1006 } else {
1007 *ret_len = len;
1008 }
1009
1010 done:
1011 xfree(tempname);
1012 return buffer;
1013 }
1014
1015 /*
1016 * Free the list of files returned by expand_wildcards() or other expansion
1017 * functions.
1018 */
FreeWild(int count,char_u ** files)1019 void FreeWild(int count, char_u **files)
1020 {
1021 if (count <= 0 || files == NULL) {
1022 return;
1023 }
1024 while (count--) {
1025 xfree(files[count]);
1026 }
1027 xfree(files);
1028 }
1029
1030 /*
1031 * Return TRUE when need to go to Insert mode because of 'insertmode'.
1032 * Don't do this when still processing a command or a mapping.
1033 * Don't do this when inside a ":normal" command.
1034 */
goto_im(void)1035 int goto_im(void)
1036 {
1037 return p_im && stuff_empty() && typebuf_typed();
1038 }
1039
1040 /// Put the timestamp of an undo header in "buf[buflen]" in a nice format.
add_time(char_u * buf,size_t buflen,time_t tt)1041 void add_time(char_u *buf, size_t buflen, time_t tt)
1042 {
1043 struct tm curtime;
1044
1045 if (time(NULL) - tt >= 100) {
1046 os_localtime_r(&tt, &curtime);
1047 if (time(NULL) - tt < (60L * 60L * 12L)) {
1048 // within 12 hours
1049 (void)strftime((char *)buf, buflen, "%H:%M:%S", &curtime);
1050 } else {
1051 // longer ago
1052 (void)strftime((char *)buf, buflen, "%Y/%m/%d %H:%M:%S", &curtime);
1053 }
1054 } else {
1055 int64_t seconds = time(NULL) - tt;
1056 vim_snprintf((char *)buf, buflen,
1057 NGETTEXT("%" PRId64 " second ago",
1058 "%" PRId64 " seconds ago", (uint32_t)seconds),
1059 seconds);
1060 }
1061 }
1062