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