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