xref: /vim-8.2.3635/src/misc1.c (revision 3075a455)
1edf3f97aSBram Moolenaar /* vi:set ts=8 sts=4 sw=4 noet:
2071d4279SBram Moolenaar  *
3071d4279SBram Moolenaar  * VIM - Vi IMproved	by Bram Moolenaar
4071d4279SBram Moolenaar  *
5071d4279SBram Moolenaar  * Do ":help uganda"  in Vim to read copying and usage conditions.
6071d4279SBram Moolenaar  * Do ":help credits" in Vim to see a list of people who contributed.
7071d4279SBram Moolenaar  * See README.txt for an overview of the Vim source code.
8071d4279SBram Moolenaar  */
9071d4279SBram Moolenaar 
10071d4279SBram Moolenaar /*
11071d4279SBram Moolenaar  * misc1.c: functions that didn't seem to fit elsewhere
12071d4279SBram Moolenaar  */
13071d4279SBram Moolenaar 
14071d4279SBram Moolenaar #include "vim.h"
15071d4279SBram Moolenaar #include "version.h"
16071d4279SBram Moolenaar 
17b3f74069SBram Moolenaar #if defined(__HAIKU__)
18b3f74069SBram Moolenaar # include <storage/FindDirectory.h>
19b3f74069SBram Moolenaar #endif
20b3f74069SBram Moolenaar 
210a52df50SBram Moolenaar #if defined(MSWIN)
22828c3d70SBram Moolenaar # include <lm.h>
23828c3d70SBram Moolenaar #endif
24828c3d70SBram Moolenaar 
2585a2002aSBram Moolenaar #define URL_SLASH	1		// path_is_url() has found "://"
2685a2002aSBram Moolenaar #define URL_BACKSLASH	2		// path_is_url() has found ":\\"
275fd0f505SBram Moolenaar 
280a52df50SBram Moolenaar // All user names (for ~user completion as done by shell).
2924305866SBram Moolenaar static garray_T	ga_users;
3024305866SBram Moolenaar 
31071d4279SBram Moolenaar /*
322c019c86SBram Moolenaar  * get_leader_len() returns the length in bytes of the prefix of the given
332c019c86SBram Moolenaar  * string which introduces a comment.  If this string is not a comment then
342c019c86SBram Moolenaar  * 0 is returned.
35071d4279SBram Moolenaar  * When "flags" is not NULL, it is set to point to the flags of the recognized
36071d4279SBram Moolenaar  * comment leader.
37071d4279SBram Moolenaar  * "backward" must be true for the "O" command.
3881340397SBram Moolenaar  * If "include_space" is set, include trailing whitespace while calculating the
3981340397SBram Moolenaar  * length.
40071d4279SBram Moolenaar  */
41071d4279SBram Moolenaar     int
get_leader_len(char_u * line,char_u ** flags,int backward,int include_space)429b57814dSBram Moolenaar get_leader_len(
439b57814dSBram Moolenaar     char_u	*line,
449b57814dSBram Moolenaar     char_u	**flags,
459b57814dSBram Moolenaar     int		backward,
469b57814dSBram Moolenaar     int		include_space)
47071d4279SBram Moolenaar {
48071d4279SBram Moolenaar     int		i, j;
4981340397SBram Moolenaar     int		result;
50071d4279SBram Moolenaar     int		got_com = FALSE;
51071d4279SBram Moolenaar     int		found_one;
5285a2002aSBram Moolenaar     char_u	part_buf[COM_MAX_LEN];	// buffer for one option part
5385a2002aSBram Moolenaar     char_u	*string;		// pointer to comment string
54071d4279SBram Moolenaar     char_u	*list;
55a4271d59SBram Moolenaar     int		middle_match_len = 0;
56a4271d59SBram Moolenaar     char_u	*prev_list;
5705da4284SBram Moolenaar     char_u	*saved_flags = NULL;
58071d4279SBram Moolenaar 
5981340397SBram Moolenaar     result = i = 0;
6085a2002aSBram Moolenaar     while (VIM_ISWHITE(line[i]))    // leading white space is ignored
61071d4279SBram Moolenaar 	++i;
62071d4279SBram Moolenaar 
63071d4279SBram Moolenaar     /*
64071d4279SBram Moolenaar      * Repeat to match several nested comment strings.
65071d4279SBram Moolenaar      */
66a4271d59SBram Moolenaar     while (line[i] != NUL)
67071d4279SBram Moolenaar     {
68071d4279SBram Moolenaar 	/*
69071d4279SBram Moolenaar 	 * scan through the 'comments' option for a match
70071d4279SBram Moolenaar 	 */
71071d4279SBram Moolenaar 	found_one = FALSE;
72071d4279SBram Moolenaar 	for (list = curbuf->b_p_com; *list; )
73071d4279SBram Moolenaar 	{
7485a2002aSBram Moolenaar 	    // Get one option part into part_buf[].  Advance "list" to next
7585a2002aSBram Moolenaar 	    // one.  Put "string" at start of string.
76a4271d59SBram Moolenaar 	    if (!got_com && flags != NULL)
7785a2002aSBram Moolenaar 		*flags = list;	    // remember where flags started
78a4271d59SBram Moolenaar 	    prev_list = list;
79071d4279SBram Moolenaar 	    (void)copy_option_part(&list, part_buf, COM_MAX_LEN, ",");
80071d4279SBram Moolenaar 	    string = vim_strchr(part_buf, ':');
8185a2002aSBram Moolenaar 	    if (string == NULL)	    // missing ':', ignore this part
82071d4279SBram Moolenaar 		continue;
8385a2002aSBram Moolenaar 	    *string++ = NUL;	    // isolate flags from string
84071d4279SBram Moolenaar 
8585a2002aSBram Moolenaar 	    // If we found a middle match previously, use that match when this
8685a2002aSBram Moolenaar 	    // is not a middle or end.
87a4271d59SBram Moolenaar 	    if (middle_match_len != 0
88a4271d59SBram Moolenaar 		    && vim_strchr(part_buf, COM_MIDDLE) == NULL
89a4271d59SBram Moolenaar 		    && vim_strchr(part_buf, COM_END) == NULL)
90a4271d59SBram Moolenaar 		break;
91a4271d59SBram Moolenaar 
9285a2002aSBram Moolenaar 	    // When we already found a nested comment, only accept further
9385a2002aSBram Moolenaar 	    // nested comments.
94071d4279SBram Moolenaar 	    if (got_com && vim_strchr(part_buf, COM_NEST) == NULL)
95071d4279SBram Moolenaar 		continue;
96071d4279SBram Moolenaar 
9785a2002aSBram Moolenaar 	    // When 'O' flag present and using "O" command skip this one.
98071d4279SBram Moolenaar 	    if (backward && vim_strchr(part_buf, COM_NOBACK) != NULL)
99071d4279SBram Moolenaar 		continue;
100071d4279SBram Moolenaar 
10185a2002aSBram Moolenaar 	    // Line contents and string must match.
10285a2002aSBram Moolenaar 	    // When string starts with white space, must have some white space
10385a2002aSBram Moolenaar 	    // (but the amount does not need to match, there might be a mix of
10485a2002aSBram Moolenaar 	    // TABs and spaces).
1051c465444SBram Moolenaar 	    if (VIM_ISWHITE(string[0]))
106071d4279SBram Moolenaar 	    {
1071c465444SBram Moolenaar 		if (i == 0 || !VIM_ISWHITE(line[i - 1]))
10885a2002aSBram Moolenaar 		    continue;  // missing white space
1091c465444SBram Moolenaar 		while (VIM_ISWHITE(string[0]))
110071d4279SBram Moolenaar 		    ++string;
111071d4279SBram Moolenaar 	    }
112071d4279SBram Moolenaar 	    for (j = 0; string[j] != NUL && string[j] == line[i + j]; ++j)
113071d4279SBram Moolenaar 		;
114071d4279SBram Moolenaar 	    if (string[j] != NUL)
11585a2002aSBram Moolenaar 		continue;  // string doesn't match
116071d4279SBram Moolenaar 
11785a2002aSBram Moolenaar 	    // When 'b' flag used, there must be white space or an
11885a2002aSBram Moolenaar 	    // end-of-line after the string in the line.
119071d4279SBram Moolenaar 	    if (vim_strchr(part_buf, COM_BLANK) != NULL
1201c465444SBram Moolenaar 			   && !VIM_ISWHITE(line[i + j]) && line[i + j] != NUL)
121071d4279SBram Moolenaar 		continue;
122071d4279SBram Moolenaar 
12385a2002aSBram Moolenaar 	    // We have found a match, stop searching unless this is a middle
12485a2002aSBram Moolenaar 	    // comment. The middle comment can be a substring of the end
12585a2002aSBram Moolenaar 	    // comment in which case it's better to return the length of the
12685a2002aSBram Moolenaar 	    // end comment and its flags.  Thus we keep searching with middle
12785a2002aSBram Moolenaar 	    // and end matches and use an end match if it matches better.
128a4271d59SBram Moolenaar 	    if (vim_strchr(part_buf, COM_MIDDLE) != NULL)
129a4271d59SBram Moolenaar 	    {
130a4271d59SBram Moolenaar 		if (middle_match_len == 0)
131a4271d59SBram Moolenaar 		{
132a4271d59SBram Moolenaar 		    middle_match_len = j;
133a4271d59SBram Moolenaar 		    saved_flags = prev_list;
134a4271d59SBram Moolenaar 		}
135a4271d59SBram Moolenaar 		continue;
136a4271d59SBram Moolenaar 	    }
137a4271d59SBram Moolenaar 	    if (middle_match_len != 0 && j > middle_match_len)
13885a2002aSBram Moolenaar 		// Use this match instead of the middle match, since it's a
13985a2002aSBram Moolenaar 		// longer thus better match.
140a4271d59SBram Moolenaar 		middle_match_len = 0;
141a4271d59SBram Moolenaar 
142a4271d59SBram Moolenaar 	    if (middle_match_len == 0)
143071d4279SBram Moolenaar 		i += j;
144071d4279SBram Moolenaar 	    found_one = TRUE;
145071d4279SBram Moolenaar 	    break;
146071d4279SBram Moolenaar 	}
147071d4279SBram Moolenaar 
148a4271d59SBram Moolenaar 	if (middle_match_len != 0)
149a4271d59SBram Moolenaar 	{
15085a2002aSBram Moolenaar 	    // Use the previously found middle match after failing to find a
15185a2002aSBram Moolenaar 	    // match with an end.
152a4271d59SBram Moolenaar 	    if (!got_com && flags != NULL)
153a4271d59SBram Moolenaar 		*flags = saved_flags;
154a4271d59SBram Moolenaar 	    i += middle_match_len;
155a4271d59SBram Moolenaar 	    found_one = TRUE;
156a4271d59SBram Moolenaar 	}
157a4271d59SBram Moolenaar 
15885a2002aSBram Moolenaar 	// No match found, stop scanning.
159071d4279SBram Moolenaar 	if (!found_one)
160071d4279SBram Moolenaar 	    break;
161071d4279SBram Moolenaar 
16281340397SBram Moolenaar 	result = i;
16381340397SBram Moolenaar 
16485a2002aSBram Moolenaar 	// Include any trailing white space.
1651c465444SBram Moolenaar 	while (VIM_ISWHITE(line[i]))
166071d4279SBram Moolenaar 	    ++i;
167071d4279SBram Moolenaar 
16881340397SBram Moolenaar 	if (include_space)
16981340397SBram Moolenaar 	    result = i;
17081340397SBram Moolenaar 
17185a2002aSBram Moolenaar 	// If this comment doesn't nest, stop here.
172a4271d59SBram Moolenaar 	got_com = TRUE;
173071d4279SBram Moolenaar 	if (vim_strchr(part_buf, COM_NEST) == NULL)
174071d4279SBram Moolenaar 	    break;
175071d4279SBram Moolenaar     }
17681340397SBram Moolenaar     return result;
17781340397SBram Moolenaar }
178a4271d59SBram Moolenaar 
17981340397SBram Moolenaar /*
18081340397SBram Moolenaar  * Return the offset at which the last comment in line starts. If there is no
18181340397SBram Moolenaar  * comment in the whole line, -1 is returned.
18281340397SBram Moolenaar  *
18381340397SBram Moolenaar  * When "flags" is not null, it is set to point to the flags describing the
18481340397SBram Moolenaar  * recognized comment leader.
18581340397SBram Moolenaar  */
18681340397SBram Moolenaar     int
get_last_leader_offset(char_u * line,char_u ** flags)1879b57814dSBram Moolenaar get_last_leader_offset(char_u *line, char_u **flags)
18881340397SBram Moolenaar {
18981340397SBram Moolenaar     int		result = -1;
19081340397SBram Moolenaar     int		i, j;
19181340397SBram Moolenaar     int		lower_check_bound = 0;
19281340397SBram Moolenaar     char_u	*string;
19381340397SBram Moolenaar     char_u	*com_leader;
19481340397SBram Moolenaar     char_u	*com_flags;
19581340397SBram Moolenaar     char_u	*list;
19681340397SBram Moolenaar     int		found_one;
19785a2002aSBram Moolenaar     char_u	part_buf[COM_MAX_LEN];	// buffer for one option part
19881340397SBram Moolenaar 
19981340397SBram Moolenaar     /*
20081340397SBram Moolenaar      * Repeat to match several nested comment strings.
20181340397SBram Moolenaar      */
20281340397SBram Moolenaar     i = (int)STRLEN(line);
20381340397SBram Moolenaar     while (--i >= lower_check_bound)
20481340397SBram Moolenaar     {
20581340397SBram Moolenaar 	/*
20681340397SBram Moolenaar 	 * scan through the 'comments' option for a match
20781340397SBram Moolenaar 	 */
20881340397SBram Moolenaar 	found_one = FALSE;
20981340397SBram Moolenaar 	for (list = curbuf->b_p_com; *list; )
21081340397SBram Moolenaar 	{
21181340397SBram Moolenaar 	    char_u *flags_save = list;
21281340397SBram Moolenaar 
21381340397SBram Moolenaar 	    /*
21481340397SBram Moolenaar 	     * Get one option part into part_buf[].  Advance list to next one.
21581340397SBram Moolenaar 	     * put string at start of string.
21681340397SBram Moolenaar 	     */
21781340397SBram Moolenaar 	    (void)copy_option_part(&list, part_buf, COM_MAX_LEN, ",");
21881340397SBram Moolenaar 	    string = vim_strchr(part_buf, ':');
21985a2002aSBram Moolenaar 	    if (string == NULL)	// If everything is fine, this cannot actually
22085a2002aSBram Moolenaar 				// happen.
22181340397SBram Moolenaar 		continue;
22285a2002aSBram Moolenaar 	    *string++ = NUL;	// Isolate flags from string.
22381340397SBram Moolenaar 	    com_leader = string;
22481340397SBram Moolenaar 
22581340397SBram Moolenaar 	    /*
22681340397SBram Moolenaar 	     * Line contents and string must match.
22781340397SBram Moolenaar 	     * When string starts with white space, must have some white space
22881340397SBram Moolenaar 	     * (but the amount does not need to match, there might be a mix of
22981340397SBram Moolenaar 	     * TABs and spaces).
23081340397SBram Moolenaar 	     */
2311c465444SBram Moolenaar 	    if (VIM_ISWHITE(string[0]))
23281340397SBram Moolenaar 	    {
2331c465444SBram Moolenaar 		if (i == 0 || !VIM_ISWHITE(line[i - 1]))
23481340397SBram Moolenaar 		    continue;
23553932819SBram Moolenaar 		while (VIM_ISWHITE(*string))
23681340397SBram Moolenaar 		    ++string;
23781340397SBram Moolenaar 	    }
23881340397SBram Moolenaar 	    for (j = 0; string[j] != NUL && string[j] == line[i + j]; ++j)
23981340397SBram Moolenaar 		/* do nothing */;
24081340397SBram Moolenaar 	    if (string[j] != NUL)
24181340397SBram Moolenaar 		continue;
24281340397SBram Moolenaar 
24381340397SBram Moolenaar 	    /*
24481340397SBram Moolenaar 	     * When 'b' flag used, there must be white space or an
24581340397SBram Moolenaar 	     * end-of-line after the string in the line.
24681340397SBram Moolenaar 	     */
24781340397SBram Moolenaar 	    if (vim_strchr(part_buf, COM_BLANK) != NULL
2481c465444SBram Moolenaar 		    && !VIM_ISWHITE(line[i + j]) && line[i + j] != NUL)
24953932819SBram Moolenaar 		continue;
25053932819SBram Moolenaar 
2514af7259bSBram Moolenaar 	    if (vim_strchr(part_buf, COM_MIDDLE) != NULL)
25281340397SBram Moolenaar 	    {
2534af7259bSBram Moolenaar 		// For a middlepart comment, only consider it to match if
2544af7259bSBram Moolenaar 		// everything before the current position in the line is
2554af7259bSBram Moolenaar 		// whitespace.  Otherwise we would think we are inside a
2564af7259bSBram Moolenaar 		// comment if the middle part appears somewhere in the middle
2574af7259bSBram Moolenaar 		// of the line.  E.g. for C the "*" appears often.
25853932819SBram Moolenaar 		for (j = 0; VIM_ISWHITE(line[j]) && j <= i; j++)
25953932819SBram Moolenaar 		    ;
26053932819SBram Moolenaar 		if (j < i)
26181340397SBram Moolenaar 		    continue;
26281340397SBram Moolenaar 	    }
26381340397SBram Moolenaar 
26481340397SBram Moolenaar 	    /*
26581340397SBram Moolenaar 	     * We have found a match, stop searching.
26681340397SBram Moolenaar 	     */
26781340397SBram Moolenaar 	    found_one = TRUE;
26881340397SBram Moolenaar 
26981340397SBram Moolenaar 	    if (flags)
27081340397SBram Moolenaar 		*flags = flags_save;
27181340397SBram Moolenaar 	    com_flags = flags_save;
27281340397SBram Moolenaar 
27381340397SBram Moolenaar 	    break;
27481340397SBram Moolenaar 	}
27581340397SBram Moolenaar 
27681340397SBram Moolenaar 	if (found_one)
27781340397SBram Moolenaar 	{
27885a2002aSBram Moolenaar 	    char_u  part_buf2[COM_MAX_LEN];	// buffer for one option part
27981340397SBram Moolenaar 	    int     len1, len2, off;
28081340397SBram Moolenaar 
28181340397SBram Moolenaar 	    result = i;
28281340397SBram Moolenaar 	    /*
28381340397SBram Moolenaar 	     * If this comment nests, continue searching.
28481340397SBram Moolenaar 	     */
28581340397SBram Moolenaar 	    if (vim_strchr(part_buf, COM_NEST) != NULL)
28681340397SBram Moolenaar 		continue;
28781340397SBram Moolenaar 
28881340397SBram Moolenaar 	    lower_check_bound = i;
28981340397SBram Moolenaar 
29085a2002aSBram Moolenaar 	    // Let's verify whether the comment leader found is a substring
29185a2002aSBram Moolenaar 	    // of other comment leaders. If it is, let's adjust the
29285a2002aSBram Moolenaar 	    // lower_check_bound so that we make sure that we have determined
29385a2002aSBram Moolenaar 	    // the comment leader correctly.
29481340397SBram Moolenaar 
2951c465444SBram Moolenaar 	    while (VIM_ISWHITE(*com_leader))
29681340397SBram Moolenaar 		++com_leader;
29781340397SBram Moolenaar 	    len1 = (int)STRLEN(com_leader);
29881340397SBram Moolenaar 
29981340397SBram Moolenaar 	    for (list = curbuf->b_p_com; *list; )
30081340397SBram Moolenaar 	    {
30181340397SBram Moolenaar 		char_u *flags_save = list;
30281340397SBram Moolenaar 
30381340397SBram Moolenaar 		(void)copy_option_part(&list, part_buf2, COM_MAX_LEN, ",");
30481340397SBram Moolenaar 		if (flags_save == com_flags)
30581340397SBram Moolenaar 		    continue;
30681340397SBram Moolenaar 		string = vim_strchr(part_buf2, ':');
30781340397SBram Moolenaar 		++string;
3081c465444SBram Moolenaar 		while (VIM_ISWHITE(*string))
30981340397SBram Moolenaar 		    ++string;
31081340397SBram Moolenaar 		len2 = (int)STRLEN(string);
31181340397SBram Moolenaar 		if (len2 == 0)
31281340397SBram Moolenaar 		    continue;
31381340397SBram Moolenaar 
31485a2002aSBram Moolenaar 		// Now we have to verify whether string ends with a substring
31585a2002aSBram Moolenaar 		// beginning the com_leader.
31681340397SBram Moolenaar 		for (off = (len2 > i ? i : len2); off > 0 && off + len1 > len2;)
31781340397SBram Moolenaar 		{
31881340397SBram Moolenaar 		    --off;
31981340397SBram Moolenaar 		    if (!STRNCMP(string + off, com_leader, len2 - off))
32081340397SBram Moolenaar 		    {
32181340397SBram Moolenaar 			if (i - off < lower_check_bound)
32281340397SBram Moolenaar 			    lower_check_bound = i - off;
32381340397SBram Moolenaar 		    }
32481340397SBram Moolenaar 		}
32581340397SBram Moolenaar 	    }
32681340397SBram Moolenaar 	}
32781340397SBram Moolenaar     }
32881340397SBram Moolenaar     return result;
329071d4279SBram Moolenaar }
330071d4279SBram Moolenaar 
331071d4279SBram Moolenaar /*
332071d4279SBram Moolenaar  * Return the number of window lines occupied by buffer line "lnum".
333071d4279SBram Moolenaar  */
334071d4279SBram Moolenaar     int
plines(linenr_T lnum)3359b57814dSBram Moolenaar plines(linenr_T lnum)
336071d4279SBram Moolenaar {
337071d4279SBram Moolenaar     return plines_win(curwin, lnum, TRUE);
338071d4279SBram Moolenaar }
339071d4279SBram Moolenaar 
340071d4279SBram Moolenaar     int
plines_win(win_T * wp,linenr_T lnum,int winheight)3419b57814dSBram Moolenaar plines_win(
3429b57814dSBram Moolenaar     win_T	*wp,
3439b57814dSBram Moolenaar     linenr_T	lnum,
34485a2002aSBram Moolenaar     int		winheight)	// when TRUE limit to window height
345071d4279SBram Moolenaar {
346071d4279SBram Moolenaar #if defined(FEAT_DIFF) || defined(PROTO)
34785a2002aSBram Moolenaar     // Check for filler lines above this buffer line.  When folded the result
34885a2002aSBram Moolenaar     // is one line anyway.
349071d4279SBram Moolenaar     return plines_win_nofill(wp, lnum, winheight) + diff_check_fill(wp, lnum);
350071d4279SBram Moolenaar }
351071d4279SBram Moolenaar 
352071d4279SBram Moolenaar     int
plines_nofill(linenr_T lnum)3539b57814dSBram Moolenaar plines_nofill(linenr_T lnum)
354071d4279SBram Moolenaar {
355071d4279SBram Moolenaar     return plines_win_nofill(curwin, lnum, TRUE);
356071d4279SBram Moolenaar }
357071d4279SBram Moolenaar 
358071d4279SBram Moolenaar     int
plines_win_nofill(win_T * wp,linenr_T lnum,int winheight)3599b57814dSBram Moolenaar plines_win_nofill(
3609b57814dSBram Moolenaar     win_T	*wp,
3619b57814dSBram Moolenaar     linenr_T	lnum,
36285a2002aSBram Moolenaar     int		winheight)	// when TRUE limit to window height
363071d4279SBram Moolenaar {
364071d4279SBram Moolenaar #endif
365071d4279SBram Moolenaar     int		lines;
366071d4279SBram Moolenaar 
367071d4279SBram Moolenaar     if (!wp->w_p_wrap)
368071d4279SBram Moolenaar 	return 1;
369071d4279SBram Moolenaar 
370071d4279SBram Moolenaar     if (wp->w_width == 0)
371071d4279SBram Moolenaar 	return 1;
372071d4279SBram Moolenaar 
373071d4279SBram Moolenaar #ifdef FEAT_FOLDING
37485a2002aSBram Moolenaar     // A folded lines is handled just like an empty line.
37585a2002aSBram Moolenaar     // NOTE: Caller must handle lines that are MAYBE folded.
376071d4279SBram Moolenaar     if (lineFolded(wp, lnum) == TRUE)
377071d4279SBram Moolenaar 	return 1;
378071d4279SBram Moolenaar #endif
379071d4279SBram Moolenaar 
380071d4279SBram Moolenaar     lines = plines_win_nofold(wp, lnum);
381071d4279SBram Moolenaar     if (winheight > 0 && lines > wp->w_height)
382071d4279SBram Moolenaar 	return (int)wp->w_height;
383071d4279SBram Moolenaar     return lines;
384071d4279SBram Moolenaar }
385071d4279SBram Moolenaar 
386071d4279SBram Moolenaar /*
387071d4279SBram Moolenaar  * Return number of window lines physical line "lnum" will occupy in window
388071d4279SBram Moolenaar  * "wp".  Does not care about folding, 'wrap' or 'diff'.
389071d4279SBram Moolenaar  */
390071d4279SBram Moolenaar     int
plines_win_nofold(win_T * wp,linenr_T lnum)3919b57814dSBram Moolenaar plines_win_nofold(win_T *wp, linenr_T lnum)
392071d4279SBram Moolenaar {
393071d4279SBram Moolenaar     char_u	*s;
394071d4279SBram Moolenaar     long	col;
395071d4279SBram Moolenaar     int		width;
396071d4279SBram Moolenaar 
397071d4279SBram Moolenaar     s = ml_get_buf(wp->w_buffer, lnum, FALSE);
39885a2002aSBram Moolenaar     if (*s == NUL)		// empty line
399071d4279SBram Moolenaar 	return 1;
400071d4279SBram Moolenaar     col = win_linetabsize(wp, s, (colnr_T)MAXCOL);
401071d4279SBram Moolenaar 
402071d4279SBram Moolenaar     /*
403071d4279SBram Moolenaar      * If list mode is on, then the '$' at the end of the line may take up one
404071d4279SBram Moolenaar      * extra column.
405071d4279SBram Moolenaar      */
406eed9d462SBram Moolenaar     if (wp->w_p_list && wp->w_lcs_chars.eol != NUL)
407071d4279SBram Moolenaar 	col += 1;
408071d4279SBram Moolenaar 
409071d4279SBram Moolenaar     /*
41064486671SBram Moolenaar      * Add column offset for 'number', 'relativenumber' and 'foldcolumn'.
411071d4279SBram Moolenaar      */
4120263146bSBram Moolenaar     width = wp->w_width - win_col_off(wp);
413071d4279SBram Moolenaar     if (width <= 0)
414071d4279SBram Moolenaar 	return 32000;
415071d4279SBram Moolenaar     if (col <= width)
416071d4279SBram Moolenaar 	return 1;
417071d4279SBram Moolenaar     col -= width;
418071d4279SBram Moolenaar     width += win_col_off2(wp);
419071d4279SBram Moolenaar     return (col + (width - 1)) / width + 1;
420071d4279SBram Moolenaar }
421071d4279SBram Moolenaar 
422071d4279SBram Moolenaar /*
423071d4279SBram Moolenaar  * Like plines_win(), but only reports the number of physical screen lines
424071d4279SBram Moolenaar  * used from the start of the line to the given column number.
425071d4279SBram Moolenaar  */
426071d4279SBram Moolenaar     int
plines_win_col(win_T * wp,linenr_T lnum,long column)4279b57814dSBram Moolenaar plines_win_col(win_T *wp, linenr_T lnum, long column)
428071d4279SBram Moolenaar {
429071d4279SBram Moolenaar     long	col;
430071d4279SBram Moolenaar     char_u	*s;
431071d4279SBram Moolenaar     int		lines = 0;
432071d4279SBram Moolenaar     int		width;
433597a4224SBram Moolenaar     char_u	*line;
434071d4279SBram Moolenaar 
435071d4279SBram Moolenaar #ifdef FEAT_DIFF
43685a2002aSBram Moolenaar     // Check for filler lines above this buffer line.  When folded the result
43785a2002aSBram Moolenaar     // is one line anyway.
438071d4279SBram Moolenaar     lines = diff_check_fill(wp, lnum);
439071d4279SBram Moolenaar #endif
440071d4279SBram Moolenaar 
441071d4279SBram Moolenaar     if (!wp->w_p_wrap)
442071d4279SBram Moolenaar 	return lines + 1;
443071d4279SBram Moolenaar 
444071d4279SBram Moolenaar     if (wp->w_width == 0)
445071d4279SBram Moolenaar 	return lines + 1;
446071d4279SBram Moolenaar 
447597a4224SBram Moolenaar     line = s = ml_get_buf(wp->w_buffer, lnum, FALSE);
448071d4279SBram Moolenaar 
449071d4279SBram Moolenaar     col = 0;
450071d4279SBram Moolenaar     while (*s != NUL && --column >= 0)
451071d4279SBram Moolenaar     {
452597a4224SBram Moolenaar 	col += win_lbr_chartabsize(wp, line, s, (colnr_T)col, NULL);
45391acfffcSBram Moolenaar 	MB_PTR_ADV(s);
454071d4279SBram Moolenaar     }
455071d4279SBram Moolenaar 
456071d4279SBram Moolenaar     /*
457071d4279SBram Moolenaar      * If *s is a TAB, and the TAB is not displayed as ^I, and we're not in
458071d4279SBram Moolenaar      * INSERT mode, then col must be adjusted so that it represents the last
459071d4279SBram Moolenaar      * screen position of the TAB.  This only fixes an error when the TAB wraps
460071d4279SBram Moolenaar      * from one screen line to the next (when 'columns' is not a multiple of
461071d4279SBram Moolenaar      * 'ts') -- webb.
462071d4279SBram Moolenaar      */
463eed9d462SBram Moolenaar     if (*s == TAB && (State & NORMAL) && (!wp->w_p_list ||
464eed9d462SBram Moolenaar 							wp->w_lcs_chars.tab1))
465597a4224SBram Moolenaar 	col += win_lbr_chartabsize(wp, line, s, (colnr_T)col, NULL) - 1;
466071d4279SBram Moolenaar 
467071d4279SBram Moolenaar     /*
46864486671SBram Moolenaar      * Add column offset for 'number', 'relativenumber', 'foldcolumn', etc.
469071d4279SBram Moolenaar      */
4700263146bSBram Moolenaar     width = wp->w_width - win_col_off(wp);
47126470639SBram Moolenaar     if (width <= 0)
47226470639SBram Moolenaar 	return 9999;
47326470639SBram Moolenaar 
474071d4279SBram Moolenaar     lines += 1;
47526470639SBram Moolenaar     if (col > width)
47626470639SBram Moolenaar 	lines += (col - width) / (width + win_col_off2(wp)) + 1;
477071d4279SBram Moolenaar     return lines;
478071d4279SBram Moolenaar }
479071d4279SBram Moolenaar 
480071d4279SBram Moolenaar     int
plines_m_win(win_T * wp,linenr_T first,linenr_T last)4819b57814dSBram Moolenaar plines_m_win(win_T *wp, linenr_T first, linenr_T last)
482071d4279SBram Moolenaar {
483071d4279SBram Moolenaar     int		count = 0;
484071d4279SBram Moolenaar 
485071d4279SBram Moolenaar     while (first <= last)
486071d4279SBram Moolenaar     {
487071d4279SBram Moolenaar #ifdef FEAT_FOLDING
488071d4279SBram Moolenaar 	int	x;
489071d4279SBram Moolenaar 
49085a2002aSBram Moolenaar 	// Check if there are any really folded lines, but also included lines
49185a2002aSBram Moolenaar 	// that are maybe folded.
492071d4279SBram Moolenaar 	x = foldedCount(wp, first, NULL);
493071d4279SBram Moolenaar 	if (x > 0)
494071d4279SBram Moolenaar 	{
49585a2002aSBram Moolenaar 	    ++count;	    // count 1 for "+-- folded" line
496071d4279SBram Moolenaar 	    first += x;
497071d4279SBram Moolenaar 	}
498071d4279SBram Moolenaar 	else
499071d4279SBram Moolenaar #endif
500071d4279SBram Moolenaar 	{
501071d4279SBram Moolenaar #ifdef FEAT_DIFF
502071d4279SBram Moolenaar 	    if (first == wp->w_topline)
503071d4279SBram Moolenaar 		count += plines_win_nofill(wp, first, TRUE) + wp->w_topfill;
504071d4279SBram Moolenaar 	    else
505071d4279SBram Moolenaar #endif
506071d4279SBram Moolenaar 		count += plines_win(wp, first, TRUE);
507071d4279SBram Moolenaar 	    ++first;
508071d4279SBram Moolenaar 	}
509071d4279SBram Moolenaar     }
510071d4279SBram Moolenaar     return (count);
511071d4279SBram Moolenaar }
512071d4279SBram Moolenaar 
513071d4279SBram Moolenaar     int
gchar_pos(pos_T * pos)5149b57814dSBram Moolenaar gchar_pos(pos_T *pos)
515071d4279SBram Moolenaar {
5168ada6aa9SBram Moolenaar     char_u	*ptr;
517071d4279SBram Moolenaar 
51885a2002aSBram Moolenaar     // When searching columns is sometimes put at the end of a line.
5198ada6aa9SBram Moolenaar     if (pos->col == MAXCOL)
5208ada6aa9SBram Moolenaar 	return NUL;
5218ada6aa9SBram Moolenaar     ptr = ml_get_pos(pos);
522071d4279SBram Moolenaar     if (has_mbyte)
523071d4279SBram Moolenaar 	return (*mb_ptr2char)(ptr);
524071d4279SBram Moolenaar     return (int)*ptr;
525071d4279SBram Moolenaar }
526071d4279SBram Moolenaar 
527071d4279SBram Moolenaar     int
gchar_cursor(void)5289b57814dSBram Moolenaar gchar_cursor(void)
529071d4279SBram Moolenaar {
530071d4279SBram Moolenaar     if (has_mbyte)
531071d4279SBram Moolenaar 	return (*mb_ptr2char)(ml_get_cursor());
532071d4279SBram Moolenaar     return (int)*ml_get_cursor();
533071d4279SBram Moolenaar }
534071d4279SBram Moolenaar 
535071d4279SBram Moolenaar /*
536071d4279SBram Moolenaar  * Write a character at the current cursor position.
537071d4279SBram Moolenaar  * It is directly written into the block.
538071d4279SBram Moolenaar  */
539071d4279SBram Moolenaar     void
pchar_cursor(int c)5409b57814dSBram Moolenaar pchar_cursor(int c)
541071d4279SBram Moolenaar {
542071d4279SBram Moolenaar     *(ml_get_buf(curbuf, curwin->w_cursor.lnum, TRUE)
543071d4279SBram Moolenaar 						  + curwin->w_cursor.col) = c;
544071d4279SBram Moolenaar }
545071d4279SBram Moolenaar 
546071d4279SBram Moolenaar /*
547071d4279SBram Moolenaar  * Skip to next part of an option argument: Skip space and comma.
548071d4279SBram Moolenaar  */
549071d4279SBram Moolenaar     char_u *
skip_to_option_part(char_u * p)5509b57814dSBram Moolenaar skip_to_option_part(char_u *p)
551071d4279SBram Moolenaar {
552071d4279SBram Moolenaar     if (*p == ',')
553071d4279SBram Moolenaar 	++p;
554071d4279SBram Moolenaar     while (*p == ' ')
555071d4279SBram Moolenaar 	++p;
556071d4279SBram Moolenaar     return p;
557071d4279SBram Moolenaar }
558071d4279SBram Moolenaar 
559071d4279SBram Moolenaar /*
560071d4279SBram Moolenaar  * check_status: called when the status bars for the buffer 'buf'
561071d4279SBram Moolenaar  *		 need to be updated
562071d4279SBram Moolenaar  */
563071d4279SBram Moolenaar     void
check_status(buf_T * buf)5649b57814dSBram Moolenaar check_status(buf_T *buf)
565071d4279SBram Moolenaar {
566071d4279SBram Moolenaar     win_T	*wp;
567071d4279SBram Moolenaar 
56829323590SBram Moolenaar     FOR_ALL_WINDOWS(wp)
569071d4279SBram Moolenaar 	if (wp->w_buffer == buf && wp->w_status_height)
570071d4279SBram Moolenaar 	{
571071d4279SBram Moolenaar 	    wp->w_redr_status = TRUE;
572071d4279SBram Moolenaar 	    if (must_redraw < VALID)
573071d4279SBram Moolenaar 		must_redraw = VALID;
574071d4279SBram Moolenaar 	}
575071d4279SBram Moolenaar }
576071d4279SBram Moolenaar 
577071d4279SBram Moolenaar /*
578071d4279SBram Moolenaar  * Ask for a reply from the user, a 'y' or a 'n'.
579071d4279SBram Moolenaar  * No other characters are accepted, the message is repeated until a valid
580071d4279SBram Moolenaar  * reply is entered or CTRL-C is hit.
581071d4279SBram Moolenaar  * If direct is TRUE, don't use vgetc() but ui_inchar(), don't get characters
582071d4279SBram Moolenaar  * from any buffers but directly from the user.
583071d4279SBram Moolenaar  *
584071d4279SBram Moolenaar  * return the 'y' or 'n'
585071d4279SBram Moolenaar  */
586071d4279SBram Moolenaar     int
ask_yesno(char_u * str,int direct)5879b57814dSBram Moolenaar ask_yesno(char_u *str, int direct)
588071d4279SBram Moolenaar {
589071d4279SBram Moolenaar     int	    r = ' ';
590071d4279SBram Moolenaar     int	    save_State = State;
591071d4279SBram Moolenaar 
59285a2002aSBram Moolenaar     if (exiting)		// put terminal in raw mode for this question
593071d4279SBram Moolenaar 	settmode(TMODE_RAW);
594071d4279SBram Moolenaar     ++no_wait_return;
595071d4279SBram Moolenaar #ifdef USE_ON_FLY_SCROLL
596b20b9e14SBram Moolenaar     dont_scroll = TRUE;		// disallow scrolling here
597071d4279SBram Moolenaar #endif
598b20b9e14SBram Moolenaar     State = CONFIRM;		// mouse behaves like with :confirm
599b20b9e14SBram Moolenaar     setmouse();			// disables mouse for xterm
600071d4279SBram Moolenaar     ++no_mapping;
601b20b9e14SBram Moolenaar     ++allow_keys;		// no mapping here, but recognize keys
602071d4279SBram Moolenaar 
603071d4279SBram Moolenaar     while (r != 'y' && r != 'n')
604071d4279SBram Moolenaar     {
60585a2002aSBram Moolenaar 	// same highlighting as for wait_return
606f9e3e09fSBram Moolenaar 	smsg_attr(HL_ATTR(HLF_R), "%s (y/n)?", str);
607071d4279SBram Moolenaar 	if (direct)
608071d4279SBram Moolenaar 	    r = get_keystroke();
609071d4279SBram Moolenaar 	else
610913626ceSBram Moolenaar 	    r = plain_vgetc();
611071d4279SBram Moolenaar 	if (r == Ctrl_C || r == ESC)
612071d4279SBram Moolenaar 	    r = 'n';
61385a2002aSBram Moolenaar 	msg_putchar(r);	    // show what you typed
614071d4279SBram Moolenaar 	out_flush();
615071d4279SBram Moolenaar     }
616071d4279SBram Moolenaar     --no_wait_return;
617071d4279SBram Moolenaar     State = save_State;
618071d4279SBram Moolenaar     setmouse();
619071d4279SBram Moolenaar     --no_mapping;
620071d4279SBram Moolenaar     --allow_keys;
621071d4279SBram Moolenaar 
622071d4279SBram Moolenaar     return r;
623071d4279SBram Moolenaar }
624071d4279SBram Moolenaar 
6250e57dd85SBram Moolenaar #if defined(FEAT_EVAL) || defined(PROTO)
6260e57dd85SBram Moolenaar 
6270e57dd85SBram Moolenaar /*
6280e57dd85SBram Moolenaar  * "mode()" function
6290e57dd85SBram Moolenaar  */
6300e57dd85SBram Moolenaar     void
f_mode(typval_T * argvars,typval_T * rettv)6310e57dd85SBram Moolenaar f_mode(typval_T *argvars, typval_T *rettv)
6320e57dd85SBram Moolenaar {
633f1e8876fS=?UTF-8?q?Magnus=20Gro=C3=9F?=     char_u	buf[MODE_MAX_LENGTH];
6340e57dd85SBram Moolenaar 
6354490ec4eSYegappan Lakshmanan     if (in_vim9script() && check_for_opt_bool_arg(argvars, 0) == FAIL)
6364490ec4eSYegappan Lakshmanan 	return;
6374490ec4eSYegappan Lakshmanan 
638a80faa89SBram Moolenaar     CLEAR_FIELD(buf);
6390e57dd85SBram Moolenaar 
6400e57dd85SBram Moolenaar     if (time_for_testing == 93784)
6410e57dd85SBram Moolenaar     {
64285a2002aSBram Moolenaar 	// Testing the two-character code.
6430e57dd85SBram Moolenaar 	buf[0] = 'x';
6440e57dd85SBram Moolenaar 	buf[1] = '!';
6450e57dd85SBram Moolenaar     }
6460e57dd85SBram Moolenaar #ifdef FEAT_TERMINAL
6470e57dd85SBram Moolenaar     else if (term_use_loop())
6480e57dd85SBram Moolenaar 	buf[0] = 't';
6490e57dd85SBram Moolenaar #endif
6500e57dd85SBram Moolenaar     else if (VIsual_active)
6510e57dd85SBram Moolenaar     {
6520e57dd85SBram Moolenaar 	if (VIsual_select)
6530e57dd85SBram Moolenaar 	    buf[0] = VIsual_mode + 's' - 'v';
6540e57dd85SBram Moolenaar 	else
655eaf3f361Szeertzjq 	{
6560e57dd85SBram Moolenaar 	    buf[0] = VIsual_mode;
657eaf3f361Szeertzjq 	    if (restart_VIsual_select)
658eaf3f361Szeertzjq 	        buf[1] = 's';
659eaf3f361Szeertzjq 	}
6600e57dd85SBram Moolenaar     }
6610e57dd85SBram Moolenaar     else if (State == HITRETURN || State == ASKMORE || State == SETWSIZE
6620e57dd85SBram Moolenaar 		|| State == CONFIRM)
6630e57dd85SBram Moolenaar     {
6640e57dd85SBram Moolenaar 	buf[0] = 'r';
6650e57dd85SBram Moolenaar 	if (State == ASKMORE)
6660e57dd85SBram Moolenaar 	    buf[1] = 'm';
6670e57dd85SBram Moolenaar 	else if (State == CONFIRM)
6680e57dd85SBram Moolenaar 	    buf[1] = '?';
6690e57dd85SBram Moolenaar     }
6700e57dd85SBram Moolenaar     else if (State == EXTERNCMD)
6710e57dd85SBram Moolenaar 	buf[0] = '!';
6720e57dd85SBram Moolenaar     else if (State & INSERT)
6730e57dd85SBram Moolenaar     {
6740e57dd85SBram Moolenaar 	if (State & VREPLACE_FLAG)
6750e57dd85SBram Moolenaar 	{
6760e57dd85SBram Moolenaar 	    buf[0] = 'R';
6770e57dd85SBram Moolenaar 	    buf[1] = 'v';
678cc8cd445Szeertzjq 
679cc8cd445Szeertzjq 	    if (ins_compl_active())
680cc8cd445Szeertzjq 		buf[2] = 'c';
681cc8cd445Szeertzjq 	    else if (ctrl_x_mode_not_defined_yet())
682cc8cd445Szeertzjq 		buf[2] = 'x';
6830e57dd85SBram Moolenaar 	}
6840e57dd85SBram Moolenaar 	else
6850e57dd85SBram Moolenaar 	{
6860e57dd85SBram Moolenaar 	    if (State & REPLACE_FLAG)
6870e57dd85SBram Moolenaar 		buf[0] = 'R';
6880e57dd85SBram Moolenaar 	    else
6890e57dd85SBram Moolenaar 		buf[0] = 'i';
690cc8cd445Szeertzjq 
6910e57dd85SBram Moolenaar 	    if (ins_compl_active())
6920e57dd85SBram Moolenaar 		buf[1] = 'c';
6930e57dd85SBram Moolenaar 	    else if (ctrl_x_mode_not_defined_yet())
6940e57dd85SBram Moolenaar 		buf[1] = 'x';
6950e57dd85SBram Moolenaar 	}
6960e57dd85SBram Moolenaar     }
6970e57dd85SBram Moolenaar     else if ((State & CMDLINE) || exmode_active)
6980e57dd85SBram Moolenaar     {
6990e57dd85SBram Moolenaar 	buf[0] = 'c';
7000e57dd85SBram Moolenaar 	if (exmode_active == EXMODE_VIM)
7010e57dd85SBram Moolenaar 	    buf[1] = 'v';
7020e57dd85SBram Moolenaar 	else if (exmode_active == EXMODE_NORMAL)
7030e57dd85SBram Moolenaar 	    buf[1] = 'e';
7040e57dd85SBram Moolenaar     }
7050e57dd85SBram Moolenaar     else
7060e57dd85SBram Moolenaar     {
7070e57dd85SBram Moolenaar 	buf[0] = 'n';
7080e57dd85SBram Moolenaar 	if (finish_op)
7090e57dd85SBram Moolenaar 	{
7100e57dd85SBram Moolenaar 	    buf[1] = 'o';
711a2438132SYegappan Lakshmanan 	    // to be able to detect force-linewise/blockwise/characterwise
712a2438132SYegappan Lakshmanan 	    // operations
7130e57dd85SBram Moolenaar 	    buf[2] = motion_force;
7140e57dd85SBram Moolenaar 	}
7150e57dd85SBram Moolenaar 	else if (restart_edit == 'I' || restart_edit == 'R'
7160e57dd85SBram Moolenaar 							|| restart_edit == 'V')
7170e57dd85SBram Moolenaar 	{
7180e57dd85SBram Moolenaar 	    buf[1] = 'i';
7190e57dd85SBram Moolenaar 	    buf[2] = restart_edit;
7200e57dd85SBram Moolenaar 	}
72172406a4bSBram Moolenaar #ifdef FEAT_TERMINAL
72272406a4bSBram Moolenaar 	else if (term_in_normal_mode())
72372406a4bSBram Moolenaar 	    buf[1] = 't';
72472406a4bSBram Moolenaar #endif
7250e57dd85SBram Moolenaar     }
7260e57dd85SBram Moolenaar 
72785a2002aSBram Moolenaar     // Clear out the minor mode when the argument is not a non-zero number or
72885a2002aSBram Moolenaar     // non-empty string.
7290e57dd85SBram Moolenaar     if (!non_zero_arg(&argvars[0]))
7300e57dd85SBram Moolenaar 	buf[1] = NUL;
7310e57dd85SBram Moolenaar 
7320e57dd85SBram Moolenaar     rettv->vval.v_string = vim_strsave(buf);
7330e57dd85SBram Moolenaar     rettv->v_type = VAR_STRING;
7340e57dd85SBram Moolenaar }
7350e57dd85SBram Moolenaar 
7360e57dd85SBram Moolenaar     static void
may_add_state_char(garray_T * gap,char_u * include,int c)7370e57dd85SBram Moolenaar may_add_state_char(garray_T *gap, char_u *include, int c)
7380e57dd85SBram Moolenaar {
7390e57dd85SBram Moolenaar     if (include == NULL || vim_strchr(include, c) != NULL)
7400e57dd85SBram Moolenaar 	ga_append(gap, c);
7410e57dd85SBram Moolenaar }
7420e57dd85SBram Moolenaar 
7430e57dd85SBram Moolenaar /*
7440e57dd85SBram Moolenaar  * "state()" function
7450e57dd85SBram Moolenaar  */
7460e57dd85SBram Moolenaar     void
f_state(typval_T * argvars,typval_T * rettv)7470e57dd85SBram Moolenaar f_state(typval_T *argvars, typval_T *rettv)
7480e57dd85SBram Moolenaar {
7490e57dd85SBram Moolenaar     garray_T	ga;
7500e57dd85SBram Moolenaar     char_u	*include = NULL;
7510e57dd85SBram Moolenaar     int		i;
7520e57dd85SBram Moolenaar 
7534490ec4eSYegappan Lakshmanan     if (in_vim9script() && check_for_opt_string_arg(argvars, 0) == FAIL)
7544490ec4eSYegappan Lakshmanan 	return;
7554490ec4eSYegappan Lakshmanan 
7560e57dd85SBram Moolenaar     ga_init2(&ga, 1, 20);
7570e57dd85SBram Moolenaar     if (argvars[0].v_type != VAR_UNKNOWN)
7580e57dd85SBram Moolenaar 	include = tv_get_string(&argvars[0]);
7590e57dd85SBram Moolenaar 
7600e57dd85SBram Moolenaar     if (!(stuff_empty() && typebuf.tb_len == 0 && scriptin[curscript] == NULL))
7610e57dd85SBram Moolenaar 	may_add_state_char(&ga, include, 'm');
7620e57dd85SBram Moolenaar     if (op_pending())
7630e57dd85SBram Moolenaar 	may_add_state_char(&ga, include, 'o');
7640e57dd85SBram Moolenaar     if (autocmd_busy)
7650e57dd85SBram Moolenaar 	may_add_state_char(&ga, include, 'x');
766c2585490SBram Moolenaar     if (ins_compl_active())
7670e57dd85SBram Moolenaar 	may_add_state_char(&ga, include, 'a');
7680e57dd85SBram Moolenaar 
7690e57dd85SBram Moolenaar # ifdef FEAT_JOB_CHANNEL
7700e57dd85SBram Moolenaar     if (channel_in_blocking_wait())
7710e57dd85SBram Moolenaar 	may_add_state_char(&ga, include, 'w');
7720e57dd85SBram Moolenaar # endif
773b20b9e14SBram Moolenaar     if (!get_was_safe_state())
774b20b9e14SBram Moolenaar 	may_add_state_char(&ga, include, 'S');
7750e57dd85SBram Moolenaar     for (i = 0; i < get_callback_depth() && i < 3; ++i)
7760e57dd85SBram Moolenaar 	may_add_state_char(&ga, include, 'c');
7770e57dd85SBram Moolenaar     if (msg_scrolled > 0)
7780e57dd85SBram Moolenaar 	may_add_state_char(&ga, include, 's');
7790e57dd85SBram Moolenaar 
7800e57dd85SBram Moolenaar     rettv->v_type = VAR_STRING;
7810e57dd85SBram Moolenaar     rettv->vval.v_string = ga.ga_data;
7820e57dd85SBram Moolenaar }
7830e57dd85SBram Moolenaar 
7840e57dd85SBram Moolenaar #endif // FEAT_EVAL
7850e57dd85SBram Moolenaar 
786071d4279SBram Moolenaar /*
787071d4279SBram Moolenaar  * Get a key stroke directly from the user.
788071d4279SBram Moolenaar  * Ignores mouse clicks and scrollbar events, except a click for the left
789071d4279SBram Moolenaar  * button (used at the more prompt).
790071d4279SBram Moolenaar  * Doesn't use vgetc(), because it syncs undo and eats mapped characters.
791071d4279SBram Moolenaar  * Disadvantage: typeahead is ignored.
792071d4279SBram Moolenaar  * Translates the interrupt character for unix to ESC.
793071d4279SBram Moolenaar  */
794071d4279SBram Moolenaar     int
get_keystroke(void)7959b57814dSBram Moolenaar get_keystroke(void)
796071d4279SBram Moolenaar {
797a8c8a688SBram Moolenaar     char_u	*buf = NULL;
798a8c8a688SBram Moolenaar     int		buflen = 150;
799a8c8a688SBram Moolenaar     int		maxlen;
800071d4279SBram Moolenaar     int		len = 0;
801071d4279SBram Moolenaar     int		n;
802071d4279SBram Moolenaar     int		save_mapped_ctrl_c = mapped_ctrl_c;
8034395a71dSBram Moolenaar     int		waited = 0;
804071d4279SBram Moolenaar 
80585a2002aSBram Moolenaar     mapped_ctrl_c = FALSE;	// mappings are not used here
806071d4279SBram Moolenaar     for (;;)
807071d4279SBram Moolenaar     {
808071d4279SBram Moolenaar 	cursor_on();
809071d4279SBram Moolenaar 	out_flush();
810071d4279SBram Moolenaar 
81185a2002aSBram Moolenaar 	// Leave some room for check_termcode() to insert a key code into (max
81285a2002aSBram Moolenaar 	// 5 chars plus NUL).  And fix_input_buffer() can triple the number of
81385a2002aSBram Moolenaar 	// bytes.
814a8c8a688SBram Moolenaar 	maxlen = (buflen - 6 - len) / 3;
815a8c8a688SBram Moolenaar 	if (buf == NULL)
816a8c8a688SBram Moolenaar 	    buf = alloc(buflen);
817a8c8a688SBram Moolenaar 	else if (maxlen < 10)
818a8c8a688SBram Moolenaar 	{
8199abd5c65SBram Moolenaar 	    char_u  *t_buf = buf;
8209abd5c65SBram Moolenaar 
82185a2002aSBram Moolenaar 	    // Need some more space. This might happen when receiving a long
82285a2002aSBram Moolenaar 	    // escape sequence.
823a8c8a688SBram Moolenaar 	    buflen += 100;
824a8c8a688SBram Moolenaar 	    buf = vim_realloc(buf, buflen);
8259abd5c65SBram Moolenaar 	    if (buf == NULL)
8269abd5c65SBram Moolenaar 		vim_free(t_buf);
827a8c8a688SBram Moolenaar 	    maxlen = (buflen - 6 - len) / 3;
828a8c8a688SBram Moolenaar 	}
829a8c8a688SBram Moolenaar 	if (buf == NULL)
830a8c8a688SBram Moolenaar 	{
831a8c8a688SBram Moolenaar 	    do_outofmem_msg((long_u)buflen);
83285a2002aSBram Moolenaar 	    return ESC;  // panic!
833a8c8a688SBram Moolenaar 	}
834a8c8a688SBram Moolenaar 
83585a2002aSBram Moolenaar 	// First time: blocking wait.  Second time: wait up to 100ms for a
83685a2002aSBram Moolenaar 	// terminal code to complete.
837a8c8a688SBram Moolenaar 	n = ui_inchar(buf + len, maxlen, len == 0 ? -1L : 100L, 0);
838071d4279SBram Moolenaar 	if (n > 0)
839071d4279SBram Moolenaar 	{
84085a2002aSBram Moolenaar 	    // Replace zero and CSI by a special key code.
8416bff02ebSBram Moolenaar 	    n = fix_input_buffer(buf + len, n);
842071d4279SBram Moolenaar 	    len += n;
8434395a71dSBram Moolenaar 	    waited = 0;
844071d4279SBram Moolenaar 	}
8454395a71dSBram Moolenaar 	else if (len > 0)
84685a2002aSBram Moolenaar 	    ++waited;	    // keep track of the waiting time
847071d4279SBram Moolenaar 
84885a2002aSBram Moolenaar 	// Incomplete termcode and not timed out yet: get more characters
849a8c8a688SBram Moolenaar 	if ((n = check_termcode(1, buf, buflen, &len)) < 0
8504395a71dSBram Moolenaar 	       && (!p_ttimeout || waited * 100L < (p_ttm < 0 ? p_tm : p_ttm)))
851071d4279SBram Moolenaar 	    continue;
8524395a71dSBram Moolenaar 
85385a2002aSBram Moolenaar 	if (n == KEYLEN_REMOVED)  // key code removed
8546eb634efSBram Moolenaar 	{
855fd30cd41SBram Moolenaar 	    if (must_redraw != 0 && !need_wait_return && (State & CMDLINE) == 0)
8566eb634efSBram Moolenaar 	    {
85785a2002aSBram Moolenaar 		// Redrawing was postponed, do it now.
8586eb634efSBram Moolenaar 		update_screen(0);
85985a2002aSBram Moolenaar 		setcursor(); // put cursor back where it belongs
8606eb634efSBram Moolenaar 	    }
861946ffd46SBram Moolenaar 	    continue;
8626eb634efSBram Moolenaar 	}
86385a2002aSBram Moolenaar 	if (n > 0)		// found a termcode: adjust length
864071d4279SBram Moolenaar 	    len = n;
86585a2002aSBram Moolenaar 	if (len == 0)		// nothing typed yet
866071d4279SBram Moolenaar 	    continue;
867071d4279SBram Moolenaar 
86885a2002aSBram Moolenaar 	// Handle modifier and/or special key code.
869071d4279SBram Moolenaar 	n = buf[0];
870071d4279SBram Moolenaar 	if (n == K_SPECIAL)
871071d4279SBram Moolenaar 	{
872071d4279SBram Moolenaar 	    n = TO_SPECIAL(buf[1], buf[2]);
873071d4279SBram Moolenaar 	    if (buf[1] == KS_MODIFIER
874071d4279SBram Moolenaar 		    || n == K_IGNORE
8752526ef27SBram Moolenaar 		    || (is_mouse_key(n) && n != K_LEFTMOUSE)
876071d4279SBram Moolenaar #ifdef FEAT_GUI
877071d4279SBram Moolenaar 		    || n == K_VER_SCROLLBAR
878071d4279SBram Moolenaar 		    || n == K_HOR_SCROLLBAR
879071d4279SBram Moolenaar #endif
880071d4279SBram Moolenaar 	       )
881071d4279SBram Moolenaar 	    {
882071d4279SBram Moolenaar 		if (buf[1] == KS_MODIFIER)
883071d4279SBram Moolenaar 		    mod_mask = buf[2];
884071d4279SBram Moolenaar 		len -= 3;
885071d4279SBram Moolenaar 		if (len > 0)
886071d4279SBram Moolenaar 		    mch_memmove(buf, buf + 3, (size_t)len);
887071d4279SBram Moolenaar 		continue;
888071d4279SBram Moolenaar 	    }
8897fc904b6SBram Moolenaar 	    break;
890071d4279SBram Moolenaar 	}
891071d4279SBram Moolenaar 	if (has_mbyte)
892071d4279SBram Moolenaar 	{
893071d4279SBram Moolenaar 	    if (MB_BYTE2LEN(n) > len)
89485a2002aSBram Moolenaar 		continue;	// more bytes to get
895a8c8a688SBram Moolenaar 	    buf[len >= buflen ? buflen - 1 : len] = NUL;
896071d4279SBram Moolenaar 	    n = (*mb_ptr2char)(buf);
897071d4279SBram Moolenaar 	}
898071d4279SBram Moolenaar #ifdef UNIX
899071d4279SBram Moolenaar 	if (n == intr_char)
900071d4279SBram Moolenaar 	    n = ESC;
901071d4279SBram Moolenaar #endif
902071d4279SBram Moolenaar 	break;
903071d4279SBram Moolenaar     }
904a8c8a688SBram Moolenaar     vim_free(buf);
905071d4279SBram Moolenaar 
906071d4279SBram Moolenaar     mapped_ctrl_c = save_mapped_ctrl_c;
907071d4279SBram Moolenaar     return n;
908071d4279SBram Moolenaar }
909071d4279SBram Moolenaar 
910071d4279SBram Moolenaar /*
91124bbcfe8SBram Moolenaar  * Get a number from the user.
91224bbcfe8SBram Moolenaar  * When "mouse_used" is not NULL allow using the mouse.
913071d4279SBram Moolenaar  */
914071d4279SBram Moolenaar     int
get_number(int colon,int * mouse_used)9159b57814dSBram Moolenaar get_number(
91685a2002aSBram Moolenaar     int	    colon,			// allow colon to abort
9179b57814dSBram Moolenaar     int	    *mouse_used)
918071d4279SBram Moolenaar {
919071d4279SBram Moolenaar     int	n = 0;
920071d4279SBram Moolenaar     int	c;
9213991dab8SBram Moolenaar     int typed = 0;
922071d4279SBram Moolenaar 
92324bbcfe8SBram Moolenaar     if (mouse_used != NULL)
92424bbcfe8SBram Moolenaar 	*mouse_used = FALSE;
92524bbcfe8SBram Moolenaar 
92685a2002aSBram Moolenaar     // When not printing messages, the user won't know what to type, return a
92785a2002aSBram Moolenaar     // zero (as if CR was hit).
928071d4279SBram Moolenaar     if (msg_silent != 0)
929071d4279SBram Moolenaar 	return 0;
930071d4279SBram Moolenaar 
931071d4279SBram Moolenaar #ifdef USE_ON_FLY_SCROLL
93285a2002aSBram Moolenaar     dont_scroll = TRUE;		// disallow scrolling here
933071d4279SBram Moolenaar #endif
934071d4279SBram Moolenaar     ++no_mapping;
93585a2002aSBram Moolenaar     ++allow_keys;		// no mapping here, but recognize keys
936071d4279SBram Moolenaar     for (;;)
937071d4279SBram Moolenaar     {
938071d4279SBram Moolenaar 	windgoto(msg_row, msg_col);
939071d4279SBram Moolenaar 	c = safe_vgetc();
940071d4279SBram Moolenaar 	if (VIM_ISDIGIT(c))
941071d4279SBram Moolenaar 	{
942071d4279SBram Moolenaar 	    n = n * 10 + c - '0';
943071d4279SBram Moolenaar 	    msg_putchar(c);
9443991dab8SBram Moolenaar 	    ++typed;
945071d4279SBram Moolenaar 	}
946071d4279SBram Moolenaar 	else if (c == K_DEL || c == K_KDEL || c == K_BS || c == Ctrl_H)
947071d4279SBram Moolenaar 	{
9483991dab8SBram Moolenaar 	    if (typed > 0)
9493991dab8SBram Moolenaar 	    {
95032526b3cSBram Moolenaar 		msg_puts("\b \b");
9513991dab8SBram Moolenaar 		--typed;
9523991dab8SBram Moolenaar 	    }
9533991dab8SBram Moolenaar 	    n /= 10;
954071d4279SBram Moolenaar 	}
95524bbcfe8SBram Moolenaar 	else if (mouse_used != NULL && c == K_LEFTMOUSE)
95624bbcfe8SBram Moolenaar 	{
95724bbcfe8SBram Moolenaar 	    *mouse_used = TRUE;
95824bbcfe8SBram Moolenaar 	    n = mouse_row + 1;
95924bbcfe8SBram Moolenaar 	    break;
96024bbcfe8SBram Moolenaar 	}
961071d4279SBram Moolenaar 	else if (n == 0 && c == ':' && colon)
962071d4279SBram Moolenaar 	{
963071d4279SBram Moolenaar 	    stuffcharReadbuff(':');
964071d4279SBram Moolenaar 	    if (!exmode_active)
965071d4279SBram Moolenaar 		cmdline_row = msg_row;
96685a2002aSBram Moolenaar 	    skip_redraw = TRUE;	    // skip redraw once
967071d4279SBram Moolenaar 	    do_redraw = FALSE;
968071d4279SBram Moolenaar 	    break;
969071d4279SBram Moolenaar 	}
9705cf94577S=?UTF-8?q?Luka=20Marku=C5=A1i=C4=87?= 	else if (c == Ctrl_C || c == ESC || c == 'q')
9715cf94577S=?UTF-8?q?Luka=20Marku=C5=A1i=C4=87?= 	{
9725cf94577S=?UTF-8?q?Luka=20Marku=C5=A1i=C4=87?= 	    n = 0;
9735cf94577S=?UTF-8?q?Luka=20Marku=C5=A1i=C4=87?= 	    break;
9745cf94577S=?UTF-8?q?Luka=20Marku=C5=A1i=C4=87?= 	}
9755cf94577S=?UTF-8?q?Luka=20Marku=C5=A1i=C4=87?= 	else if (c == CAR || c == NL )
976071d4279SBram Moolenaar 	    break;
977071d4279SBram Moolenaar     }
978071d4279SBram Moolenaar     --no_mapping;
979071d4279SBram Moolenaar     --allow_keys;
980071d4279SBram Moolenaar     return n;
981071d4279SBram Moolenaar }
982071d4279SBram Moolenaar 
9839ba0eb85SBram Moolenaar /*
9849ba0eb85SBram Moolenaar  * Ask the user to enter a number.
98524bbcfe8SBram Moolenaar  * When "mouse_used" is not NULL allow using the mouse and in that case return
98624bbcfe8SBram Moolenaar  * the line number.
9879ba0eb85SBram Moolenaar  */
9889ba0eb85SBram Moolenaar     int
prompt_for_number(int * mouse_used)9899b57814dSBram Moolenaar prompt_for_number(int *mouse_used)
9909ba0eb85SBram Moolenaar {
9919ba0eb85SBram Moolenaar     int		i;
992d857f0e0SBram Moolenaar     int		save_cmdline_row;
993d857f0e0SBram Moolenaar     int		save_State;
9949ba0eb85SBram Moolenaar 
99585a2002aSBram Moolenaar     // When using ":silent" assume that <CR> was entered.
99642eeac35SBram Moolenaar     if (mouse_used != NULL)
997eebd5557SBram Moolenaar 	msg_puts(_("Type number and <Enter> or click with the mouse (q or empty cancels): "));
99842eeac35SBram Moolenaar     else
999eebd5557SBram Moolenaar 	msg_puts(_("Type number and <Enter> (q or empty cancels): "));
1000d857f0e0SBram Moolenaar 
10014cbdf155SBram Moolenaar     // Set the state such that text can be selected/copied/pasted and we still
10024cbdf155SBram Moolenaar     // get mouse events. redraw_after_callback() will not redraw if cmdline_row
10034cbdf155SBram Moolenaar     // is zero.
1004d857f0e0SBram Moolenaar     save_cmdline_row = cmdline_row;
1005203335e4SBram Moolenaar     cmdline_row = 0;
1006d857f0e0SBram Moolenaar     save_State = State;
10074cbdf155SBram Moolenaar     State = CMDLINE;
10084cbdf155SBram Moolenaar     // May show different mouse shape.
100973658317SBram Moolenaar     setmouse();
101073658317SBram Moolenaar 
101124bbcfe8SBram Moolenaar     i = get_number(TRUE, mouse_used);
101224bbcfe8SBram Moolenaar     if (KeyTyped)
10139ba0eb85SBram Moolenaar     {
1014954bb063SBram Moolenaar 	// don't call wait_return() now
1015954bb063SBram Moolenaar 	if (msg_row > 0)
10169ba0eb85SBram Moolenaar 	    cmdline_row = msg_row - 1;
10179ba0eb85SBram Moolenaar 	need_wait_return = FALSE;
1018dc968e7aSBram Moolenaar 	msg_didany = FALSE;
1019dc968e7aSBram Moolenaar 	msg_didout = FALSE;
10209ba0eb85SBram Moolenaar     }
1021d857f0e0SBram Moolenaar     else
1022d857f0e0SBram Moolenaar 	cmdline_row = save_cmdline_row;
1023d857f0e0SBram Moolenaar     State = save_State;
10244cbdf155SBram Moolenaar     // May need to restore mouse shape.
102573658317SBram Moolenaar     setmouse();
1026d857f0e0SBram Moolenaar 
10279ba0eb85SBram Moolenaar     return i;
10289ba0eb85SBram Moolenaar }
10299ba0eb85SBram Moolenaar 
1030071d4279SBram Moolenaar     void
msgmore(long n)10319b57814dSBram Moolenaar msgmore(long n)
1032071d4279SBram Moolenaar {
1033071d4279SBram Moolenaar     long pn;
1034071d4279SBram Moolenaar 
103585a2002aSBram Moolenaar     if (global_busy	    // no messages now, wait until global is finished
103685a2002aSBram Moolenaar 	    || !messaging())  // 'lazyredraw' set, don't do messages now
1037071d4279SBram Moolenaar 	return;
1038071d4279SBram Moolenaar 
103985a2002aSBram Moolenaar     // We don't want to overwrite another important message, but do overwrite
104085a2002aSBram Moolenaar     // a previous "more lines" or "fewer lines" message, so that "5dd" and
104185a2002aSBram Moolenaar     // then "put" reports the last action.
10427df2d662SBram Moolenaar     if (keep_msg != NULL && !keep_msg_more)
10437df2d662SBram Moolenaar 	return;
10447df2d662SBram Moolenaar 
1045071d4279SBram Moolenaar     if (n > 0)
1046071d4279SBram Moolenaar 	pn = n;
1047071d4279SBram Moolenaar     else
1048071d4279SBram Moolenaar 	pn = -n;
1049071d4279SBram Moolenaar 
1050071d4279SBram Moolenaar     if (pn > p_report)
1051071d4279SBram Moolenaar     {
1052071d4279SBram Moolenaar 	if (n > 0)
105332526b3cSBram Moolenaar 	    vim_snprintf(msg_buf, MSG_BUF_LEN,
1054da6e8919SBram Moolenaar 		    NGETTEXT("%ld more line", "%ld more lines", pn), pn);
1055071d4279SBram Moolenaar 	else
105632526b3cSBram Moolenaar 	    vim_snprintf(msg_buf, MSG_BUF_LEN,
1057da6e8919SBram Moolenaar 		    NGETTEXT("%ld line less", "%ld fewer lines", pn), pn);
1058071d4279SBram Moolenaar 	if (got_int)
105932526b3cSBram Moolenaar 	    vim_strcat((char_u *)msg_buf, (char_u *)_(" (Interrupted)"),
106032526b3cSBram Moolenaar 								  MSG_BUF_LEN);
1061071d4279SBram Moolenaar 	if (msg(msg_buf))
1062071d4279SBram Moolenaar 	{
106332526b3cSBram Moolenaar 	    set_keep_msg((char_u *)msg_buf, 0);
10647df2d662SBram Moolenaar 	    keep_msg_more = TRUE;
1065071d4279SBram Moolenaar 	}
1066071d4279SBram Moolenaar     }
1067071d4279SBram Moolenaar }
1068071d4279SBram Moolenaar 
1069071d4279SBram Moolenaar /*
1070071d4279SBram Moolenaar  * flush map and typeahead buffers and give a warning for an error
1071071d4279SBram Moolenaar  */
1072071d4279SBram Moolenaar     void
beep_flush(void)10739b57814dSBram Moolenaar beep_flush(void)
1074071d4279SBram Moolenaar {
1075071d4279SBram Moolenaar     if (emsg_silent == 0)
1076071d4279SBram Moolenaar     {
10776a2633b0SBram Moolenaar 	flush_buffers(FLUSH_MINIMAL);
1078165bc69dSBram Moolenaar 	vim_beep(BO_ERROR);
1079071d4279SBram Moolenaar     }
1080071d4279SBram Moolenaar }
1081071d4279SBram Moolenaar 
1082071d4279SBram Moolenaar /*
1083165bc69dSBram Moolenaar  * Give a warning for an error.
1084071d4279SBram Moolenaar  */
1085071d4279SBram Moolenaar     void
vim_beep(unsigned val)10869b57814dSBram Moolenaar vim_beep(
108785a2002aSBram Moolenaar     unsigned val) // one of the BO_ values, e.g., BO_OPER
1088071d4279SBram Moolenaar {
1089b48e96f6SBram Moolenaar #ifdef FEAT_EVAL
1090b48e96f6SBram Moolenaar     called_vim_beep = TRUE;
1091b48e96f6SBram Moolenaar #endif
1092b48e96f6SBram Moolenaar 
109328ee892aSBram Moolenaar     if (emsg_silent == 0 && !in_assert_fails)
1094071d4279SBram Moolenaar     {
1095165bc69dSBram Moolenaar 	if (!((bo_flags & val) || (bo_flags & BO_ALL)))
1096165bc69dSBram Moolenaar 	{
10972e147caaSBram Moolenaar #ifdef ELAPSED_FUNC
10982e147caaSBram Moolenaar 	    static int		did_init = FALSE;
10991ac56c2dSBram Moolenaar 	    static elapsed_T	start_tv;
11002e147caaSBram Moolenaar 
110185a2002aSBram Moolenaar 	    // Only beep once per half a second, otherwise a sequence of beeps
110285a2002aSBram Moolenaar 	    // would freeze Vim.
11032e147caaSBram Moolenaar 	    if (!did_init || ELAPSED_FUNC(start_tv) > 500)
11042e147caaSBram Moolenaar 	    {
11052e147caaSBram Moolenaar 		did_init = TRUE;
11062e147caaSBram Moolenaar 		ELAPSED_INIT(start_tv);
11072e147caaSBram Moolenaar #endif
1108071d4279SBram Moolenaar 		if (p_vb
1109071d4279SBram Moolenaar #ifdef FEAT_GUI
111085a2002aSBram Moolenaar 			// While the GUI is starting up the termcap is set for
111185a2002aSBram Moolenaar 			// the GUI but the output still goes to a terminal.
1112071d4279SBram Moolenaar 			&& !(gui.in_use && gui.starting)
1113071d4279SBram Moolenaar #endif
1114071d4279SBram Moolenaar 			)
1115cafafb38SBram Moolenaar 		{
11162e147caaSBram Moolenaar 		    out_str_cf(T_VB);
1117cafafb38SBram Moolenaar #ifdef FEAT_VTP
111885a2002aSBram Moolenaar 		    // No restore color information, refresh the screen.
1119cafafb38SBram Moolenaar 		    if (has_vtp_working() != 0
1120cafafb38SBram Moolenaar # ifdef FEAT_TERMGUICOLORS
1121c5cd8855SBram Moolenaar 			    && (p_tgc || (!p_tgc && t_colors >= 256))
1122cafafb38SBram Moolenaar # endif
1123cafafb38SBram Moolenaar 			)
1124cafafb38SBram Moolenaar 		    {
1125cafafb38SBram Moolenaar 			redraw_later(CLEAR);
1126cafafb38SBram Moolenaar 			update_screen(0);
1127cafafb38SBram Moolenaar 			redrawcmd();
1128cafafb38SBram Moolenaar 		    }
1129cafafb38SBram Moolenaar #endif
1130cafafb38SBram Moolenaar 		}
1131071d4279SBram Moolenaar 		else
1132071d4279SBram Moolenaar 		    out_char(BELL);
11332e147caaSBram Moolenaar #ifdef ELAPSED_FUNC
11342e147caaSBram Moolenaar 	    }
11352e147caaSBram Moolenaar #endif
1136165bc69dSBram Moolenaar 	}
11375313dcb7SBram Moolenaar 
113885a2002aSBram Moolenaar 	// When 'debug' contains "beep" produce a message.  If we are sourcing
113985a2002aSBram Moolenaar 	// a script or executing a function give the user a hint where the beep
114085a2002aSBram Moolenaar 	// comes from.
11415313dcb7SBram Moolenaar 	if (vim_strchr(p_debug, 'e') != NULL)
11425313dcb7SBram Moolenaar 	{
11438820b486SBram Moolenaar 	    msg_source(HL_ATTR(HLF_W));
114432526b3cSBram Moolenaar 	    msg_attr(_("Beep!"), HL_ATTR(HLF_W));
11455313dcb7SBram Moolenaar 	}
1146071d4279SBram Moolenaar     }
1147071d4279SBram Moolenaar }
1148071d4279SBram Moolenaar 
1149071d4279SBram Moolenaar /*
1150071d4279SBram Moolenaar  * To get the "real" home directory:
1151071d4279SBram Moolenaar  * - get value of $HOME
1152071d4279SBram Moolenaar  * For Unix:
1153071d4279SBram Moolenaar  *  - go to that directory
1154071d4279SBram Moolenaar  *  - do mch_dirname() to get the real name of that directory.
1155071d4279SBram Moolenaar  *  This also works with mounts and links.
1156071d4279SBram Moolenaar  *  Don't do this for MS-DOS, it will change the "current dir" for a drive.
115725a494ceSBram Moolenaar  * For Windows:
115825a494ceSBram Moolenaar  *  This code is duplicated in init_homedir() in dosinst.c.  Keep in sync!
1159071d4279SBram Moolenaar  */
1160071d4279SBram Moolenaar     void
init_homedir(void)11619b57814dSBram Moolenaar init_homedir(void)
1162071d4279SBram Moolenaar {
1163071d4279SBram Moolenaar     char_u  *var;
1164071d4279SBram Moolenaar 
116585a2002aSBram Moolenaar     // In case we are called a second time (when 'encoding' changes).
1166d23a8236SBram Moolenaar     VIM_CLEAR(homedir);
116705159a0cSBram Moolenaar 
1168071d4279SBram Moolenaar #ifdef VMS
1169071d4279SBram Moolenaar     var = mch_getenv((char_u *)"SYS$LOGIN");
1170071d4279SBram Moolenaar #else
1171071d4279SBram Moolenaar     var = mch_getenv((char_u *)"HOME");
1172071d4279SBram Moolenaar #endif
1173071d4279SBram Moolenaar 
11744f97475dSBram Moolenaar #ifdef MSWIN
1175071d4279SBram Moolenaar     /*
117648340b62SBram Moolenaar      * Typically, $HOME is not defined on Windows, unless the user has
117748340b62SBram Moolenaar      * specifically defined it for Vim's sake.  However, on Windows NT
117848340b62SBram Moolenaar      * platforms, $HOMEDRIVE and $HOMEPATH are automatically defined for
117948340b62SBram Moolenaar      * each user.  Try constructing $HOME from these.
118048340b62SBram Moolenaar      */
1181b47a2597SBram Moolenaar     if (var == NULL || *var == NUL)
118248340b62SBram Moolenaar     {
118348340b62SBram Moolenaar 	char_u *homedrive, *homepath;
118448340b62SBram Moolenaar 
118548340b62SBram Moolenaar 	homedrive = mch_getenv((char_u *)"HOMEDRIVE");
118648340b62SBram Moolenaar 	homepath = mch_getenv((char_u *)"HOMEPATH");
118748340b62SBram Moolenaar 	if (homepath == NULL || *homepath == NUL)
118848340b62SBram Moolenaar 	    homepath = (char_u *)"\\";
118948340b62SBram Moolenaar 	if (homedrive != NULL
119048340b62SBram Moolenaar 			   && STRLEN(homedrive) + STRLEN(homepath) < MAXPATHL)
119148340b62SBram Moolenaar 	{
119248340b62SBram Moolenaar 	    sprintf((char *)NameBuff, "%s%s", homedrive, homepath);
119348340b62SBram Moolenaar 	    if (NameBuff[0] != NUL)
119448340b62SBram Moolenaar 		var = NameBuff;
119548340b62SBram Moolenaar 	}
119648340b62SBram Moolenaar     }
119748340b62SBram Moolenaar 
119848340b62SBram Moolenaar     if (var == NULL)
119948340b62SBram Moolenaar 	var = mch_getenv((char_u *)"USERPROFILE");
120048340b62SBram Moolenaar 
120148340b62SBram Moolenaar     /*
1202071d4279SBram Moolenaar      * Weird but true: $HOME may contain an indirect reference to another
1203071d4279SBram Moolenaar      * variable, esp. "%USERPROFILE%".  Happens when $USERPROFILE isn't set
1204071d4279SBram Moolenaar      * when $HOME is being set.
1205071d4279SBram Moolenaar      */
1206071d4279SBram Moolenaar     if (var != NULL && *var == '%')
1207071d4279SBram Moolenaar     {
1208071d4279SBram Moolenaar 	char_u	*p;
1209071d4279SBram Moolenaar 	char_u	*exp;
1210071d4279SBram Moolenaar 
1211071d4279SBram Moolenaar 	p = vim_strchr(var + 1, '%');
1212071d4279SBram Moolenaar 	if (p != NULL)
1213071d4279SBram Moolenaar 	{
1214ce0842a6SBram Moolenaar 	    vim_strncpy(NameBuff, var + 1, p - (var + 1));
1215071d4279SBram Moolenaar 	    exp = mch_getenv(NameBuff);
1216071d4279SBram Moolenaar 	    if (exp != NULL && *exp != NUL
1217071d4279SBram Moolenaar 					&& STRLEN(exp) + STRLEN(p) < MAXPATHL)
1218071d4279SBram Moolenaar 	    {
1219555b280fSBram Moolenaar 		vim_snprintf((char *)NameBuff, MAXPATHL, "%s%s", exp, p + 1);
1220071d4279SBram Moolenaar 		var = NameBuff;
1221071d4279SBram Moolenaar 	    }
1222071d4279SBram Moolenaar 	}
1223071d4279SBram Moolenaar     }
1224071d4279SBram Moolenaar 
122585a2002aSBram Moolenaar     if (var != NULL && *var == NUL)	// empty is same as not set
122648340b62SBram Moolenaar 	var = NULL;
1227071d4279SBram Moolenaar 
122805159a0cSBram Moolenaar     if (enc_utf8 && var != NULL)
122905159a0cSBram Moolenaar     {
123005159a0cSBram Moolenaar 	int	len;
1231b453a53bSBram Moolenaar 	char_u  *pp = NULL;
123205159a0cSBram Moolenaar 
123385a2002aSBram Moolenaar 	// Convert from active codepage to UTF-8.  Other conversions are
123485a2002aSBram Moolenaar 	// not done, because they would fail for non-ASCII characters.
1235a93fa7eeSBram Moolenaar 	acp_to_enc(var, (int)STRLEN(var), &pp, &len);
123605159a0cSBram Moolenaar 	if (pp != NULL)
123705159a0cSBram Moolenaar 	{
123805159a0cSBram Moolenaar 	    homedir = pp;
123905159a0cSBram Moolenaar 	    return;
124005159a0cSBram Moolenaar 	}
124105159a0cSBram Moolenaar     }
1242071d4279SBram Moolenaar 
1243071d4279SBram Moolenaar     /*
1244071d4279SBram Moolenaar      * Default home dir is C:/
1245071d4279SBram Moolenaar      * Best assumption we can make in such a situation.
1246071d4279SBram Moolenaar      */
1247071d4279SBram Moolenaar     if (var == NULL)
12486aa2cd4bSBram Moolenaar 	var = (char_u *)"C:/";
1249071d4279SBram Moolenaar #endif
125048340b62SBram Moolenaar 
1251071d4279SBram Moolenaar     if (var != NULL)
1252071d4279SBram Moolenaar     {
1253071d4279SBram Moolenaar #ifdef UNIX
1254071d4279SBram Moolenaar 	/*
1255071d4279SBram Moolenaar 	 * Change to the directory and get the actual path.  This resolves
1256071d4279SBram Moolenaar 	 * links.  Don't do it when we can't return.
1257071d4279SBram Moolenaar 	 */
1258071d4279SBram Moolenaar 	if (mch_dirname(NameBuff, MAXPATHL) == OK
1259071d4279SBram Moolenaar 					  && mch_chdir((char *)NameBuff) == 0)
1260071d4279SBram Moolenaar 	{
1261071d4279SBram Moolenaar 	    if (!mch_chdir((char *)var) && mch_dirname(IObuff, IOSIZE) == OK)
1262071d4279SBram Moolenaar 		var = IObuff;
1263071d4279SBram Moolenaar 	    if (mch_chdir((char *)NameBuff) != 0)
1264f9e3e09fSBram Moolenaar 		emsg(_(e_prev_dir));
1265071d4279SBram Moolenaar 	}
1266071d4279SBram Moolenaar #endif
1267071d4279SBram Moolenaar 	homedir = vim_strsave(var);
1268071d4279SBram Moolenaar     }
1269071d4279SBram Moolenaar }
1270071d4279SBram Moolenaar 
1271f461c8e7SBram Moolenaar #if defined(EXITFREE) || defined(PROTO)
1272f461c8e7SBram Moolenaar     void
free_homedir(void)12739b57814dSBram Moolenaar free_homedir(void)
1274f461c8e7SBram Moolenaar {
1275f461c8e7SBram Moolenaar     vim_free(homedir);
1276f461c8e7SBram Moolenaar }
127724305866SBram Moolenaar 
127824305866SBram Moolenaar     void
free_users(void)12799b57814dSBram Moolenaar free_users(void)
128024305866SBram Moolenaar {
128124305866SBram Moolenaar     ga_clear_strings(&ga_users);
128224305866SBram Moolenaar }
128324305866SBram Moolenaar #endif
1284f461c8e7SBram Moolenaar 
1285071d4279SBram Moolenaar /*
12869f0545d6SBram Moolenaar  * Call expand_env() and store the result in an allocated string.
12879f0545d6SBram Moolenaar  * This is not very memory efficient, this expects the result to be freed
12889f0545d6SBram Moolenaar  * again soon.
12899f0545d6SBram Moolenaar  */
12909f0545d6SBram Moolenaar     char_u *
expand_env_save(char_u * src)12919b57814dSBram Moolenaar expand_env_save(char_u *src)
12929f0545d6SBram Moolenaar {
12939f0545d6SBram Moolenaar     return expand_env_save_opt(src, FALSE);
12949f0545d6SBram Moolenaar }
12959f0545d6SBram Moolenaar 
12969f0545d6SBram Moolenaar /*
12979f0545d6SBram Moolenaar  * Idem, but when "one" is TRUE handle the string as one file name, only
12989f0545d6SBram Moolenaar  * expand "~" at the start.
12999f0545d6SBram Moolenaar  */
13009f0545d6SBram Moolenaar     char_u *
expand_env_save_opt(char_u * src,int one)13019b57814dSBram Moolenaar expand_env_save_opt(char_u *src, int one)
13029f0545d6SBram Moolenaar {
13039f0545d6SBram Moolenaar     char_u	*p;
13049f0545d6SBram Moolenaar 
13059f0545d6SBram Moolenaar     p = alloc(MAXPATHL);
13069f0545d6SBram Moolenaar     if (p != NULL)
13079f0545d6SBram Moolenaar 	expand_env_esc(src, p, MAXPATHL, FALSE, one, NULL);
13089f0545d6SBram Moolenaar     return p;
13099f0545d6SBram Moolenaar }
13109f0545d6SBram Moolenaar 
13119f0545d6SBram Moolenaar /*
1312071d4279SBram Moolenaar  * Expand environment variable with path name.
1313071d4279SBram Moolenaar  * "~/" is also expanded, using $HOME.	For Unix "~user/" is expanded.
13149f0545d6SBram Moolenaar  * Skips over "\ ", "\~" and "\$" (not for Win32 though).
1315071d4279SBram Moolenaar  * If anything fails no expansion is done and dst equals src.
1316071d4279SBram Moolenaar  */
1317071d4279SBram Moolenaar     void
expand_env(char_u * src,char_u * dst,int dstlen)13189b57814dSBram Moolenaar expand_env(
131985a2002aSBram Moolenaar     char_u	*src,		// input string e.g. "$HOME/vim.hlp"
132085a2002aSBram Moolenaar     char_u	*dst,		// where to put the result
132185a2002aSBram Moolenaar     int		dstlen)		// maximum length of the result
1322071d4279SBram Moolenaar {
13239f0545d6SBram Moolenaar     expand_env_esc(src, dst, dstlen, FALSE, FALSE, NULL);
1324071d4279SBram Moolenaar }
1325071d4279SBram Moolenaar 
1326071d4279SBram Moolenaar     void
expand_env_esc(char_u * srcp,char_u * dst,int dstlen,int esc,int one,char_u * startstr)13279b57814dSBram Moolenaar expand_env_esc(
132885a2002aSBram Moolenaar     char_u	*srcp,		// input string e.g. "$HOME/vim.hlp"
132985a2002aSBram Moolenaar     char_u	*dst,		// where to put the result
133085a2002aSBram Moolenaar     int		dstlen,		// maximum length of the result
133185a2002aSBram Moolenaar     int		esc,		// escape spaces in expanded variables
133285a2002aSBram Moolenaar     int		one,		// "srcp" is one file name
133385a2002aSBram Moolenaar     char_u	*startstr)	// start again after this (can be NULL)
1334071d4279SBram Moolenaar {
133524bbcfe8SBram Moolenaar     char_u	*src;
1336071d4279SBram Moolenaar     char_u	*tail;
1337071d4279SBram Moolenaar     int		c;
1338071d4279SBram Moolenaar     char_u	*var;
1339071d4279SBram Moolenaar     int		copy_char;
134085a2002aSBram Moolenaar     int		mustfree;	// var was allocated, need to free it later
134185a2002aSBram Moolenaar     int		at_start = TRUE; // at start of a name
134224bbcfe8SBram Moolenaar     int		startstr_len = 0;
1343071d4279SBram Moolenaar 
134424bbcfe8SBram Moolenaar     if (startstr != NULL)
1345a93fa7eeSBram Moolenaar 	startstr_len = (int)STRLEN(startstr);
134624bbcfe8SBram Moolenaar 
134724bbcfe8SBram Moolenaar     src = skipwhite(srcp);
134885a2002aSBram Moolenaar     --dstlen;		    // leave one char space for "\,"
1349071d4279SBram Moolenaar     while (*src && dstlen > 0)
1350071d4279SBram Moolenaar     {
1351be83b73dSBram Moolenaar #ifdef FEAT_EVAL
135285a2002aSBram Moolenaar 	// Skip over `=expr`.
1353be83b73dSBram Moolenaar 	if (src[0] == '`' && src[1] == '=')
1354be83b73dSBram Moolenaar 	{
1355be83b73dSBram Moolenaar 	    size_t len;
1356be83b73dSBram Moolenaar 
1357be83b73dSBram Moolenaar 	    var = src;
1358be83b73dSBram Moolenaar 	    src += 2;
1359683581ebSBram Moolenaar 	    (void)skip_expr(&src, NULL);
1360be83b73dSBram Moolenaar 	    if (*src == '`')
1361be83b73dSBram Moolenaar 		++src;
1362be83b73dSBram Moolenaar 	    len = src - var;
1363be83b73dSBram Moolenaar 	    if (len > (size_t)dstlen)
1364be83b73dSBram Moolenaar 		len = dstlen;
1365be83b73dSBram Moolenaar 	    vim_strncpy(dst, var, len);
1366be83b73dSBram Moolenaar 	    dst += len;
13675df1ed2dSBram Moolenaar 	    dstlen -= (int)len;
1368be83b73dSBram Moolenaar 	    continue;
1369be83b73dSBram Moolenaar 	}
1370be83b73dSBram Moolenaar #endif
1371071d4279SBram Moolenaar 	copy_char = TRUE;
1372d4755bb0SBram Moolenaar 	if ((*src == '$'
1373d4755bb0SBram Moolenaar #ifdef VMS
1374d4755bb0SBram Moolenaar 		    && at_start
1375d4755bb0SBram Moolenaar #endif
1376d4755bb0SBram Moolenaar 	   )
137748e330afSBram Moolenaar #if defined(MSWIN)
1378071d4279SBram Moolenaar 		|| *src == '%'
1379071d4279SBram Moolenaar #endif
1380071d4279SBram Moolenaar 		|| (*src == '~' && at_start))
1381071d4279SBram Moolenaar 	{
1382071d4279SBram Moolenaar 	    mustfree = FALSE;
1383071d4279SBram Moolenaar 
1384071d4279SBram Moolenaar 	    /*
1385071d4279SBram Moolenaar 	     * The variable name is copied into dst temporarily, because it may
1386071d4279SBram Moolenaar 	     * be a string in read-only memory and a NUL needs to be appended.
1387071d4279SBram Moolenaar 	     */
138885a2002aSBram Moolenaar 	    if (*src != '~')				// environment var
1389071d4279SBram Moolenaar 	    {
1390071d4279SBram Moolenaar 		tail = src + 1;
1391071d4279SBram Moolenaar 		var = dst;
1392071d4279SBram Moolenaar 		c = dstlen - 1;
1393071d4279SBram Moolenaar 
1394071d4279SBram Moolenaar #ifdef UNIX
139585a2002aSBram Moolenaar 		// Unix has ${var-name} type environment vars
1396071d4279SBram Moolenaar 		if (*tail == '{' && !vim_isIDc('{'))
1397071d4279SBram Moolenaar 		{
139885a2002aSBram Moolenaar 		    tail++;	// ignore '{'
1399071d4279SBram Moolenaar 		    while (c-- > 0 && *tail && *tail != '}')
1400071d4279SBram Moolenaar 			*var++ = *tail++;
1401071d4279SBram Moolenaar 		}
1402071d4279SBram Moolenaar 		else
1403071d4279SBram Moolenaar #endif
1404071d4279SBram Moolenaar 		{
1405071d4279SBram Moolenaar 		    while (c-- > 0 && *tail != NUL && ((vim_isIDc(*tail))
140648e330afSBram Moolenaar #if defined(MSWIN)
1407071d4279SBram Moolenaar 			    || (*src == '%' && *tail != '%')
1408071d4279SBram Moolenaar #endif
1409071d4279SBram Moolenaar 			    ))
1410071d4279SBram Moolenaar 			*var++ = *tail++;
1411071d4279SBram Moolenaar 		}
1412071d4279SBram Moolenaar 
141348e330afSBram Moolenaar #if defined(MSWIN) || defined(UNIX)
1414071d4279SBram Moolenaar # ifdef UNIX
1415071d4279SBram Moolenaar 		if (src[1] == '{' && *tail != '}')
1416071d4279SBram Moolenaar # else
1417071d4279SBram Moolenaar 		if (*src == '%' && *tail != '%')
1418071d4279SBram Moolenaar # endif
1419071d4279SBram Moolenaar 		    var = NULL;
1420071d4279SBram Moolenaar 		else
1421071d4279SBram Moolenaar 		{
1422071d4279SBram Moolenaar # ifdef UNIX
1423071d4279SBram Moolenaar 		    if (src[1] == '{')
1424071d4279SBram Moolenaar # else
1425071d4279SBram Moolenaar 		    if (*src == '%')
1426071d4279SBram Moolenaar #endif
1427071d4279SBram Moolenaar 			++tail;
1428071d4279SBram Moolenaar #endif
1429071d4279SBram Moolenaar 		    *var = NUL;
1430071d4279SBram Moolenaar 		    var = vim_getenv(dst, &mustfree);
143148e330afSBram Moolenaar #if defined(MSWIN) || defined(UNIX)
1432071d4279SBram Moolenaar 		}
1433071d4279SBram Moolenaar #endif
1434071d4279SBram Moolenaar 	    }
143585a2002aSBram Moolenaar 							// home directory
1436071d4279SBram Moolenaar 	    else if (  src[1] == NUL
1437071d4279SBram Moolenaar 		    || vim_ispathsep(src[1])
1438071d4279SBram Moolenaar 		    || vim_strchr((char_u *)" ,\t\n", src[1]) != NULL)
1439071d4279SBram Moolenaar 	    {
1440071d4279SBram Moolenaar 		var = homedir;
1441071d4279SBram Moolenaar 		tail = src + 1;
1442071d4279SBram Moolenaar 	    }
144385a2002aSBram Moolenaar 	    else					// user directory
1444071d4279SBram Moolenaar 	    {
1445071d4279SBram Moolenaar #if defined(UNIX) || (defined(VMS) && defined(USER_HOME))
1446071d4279SBram Moolenaar 		/*
1447071d4279SBram Moolenaar 		 * Copy ~user to dst[], so we can put a NUL after it.
1448071d4279SBram Moolenaar 		 */
1449071d4279SBram Moolenaar 		tail = src;
1450071d4279SBram Moolenaar 		var = dst;
1451071d4279SBram Moolenaar 		c = dstlen - 1;
1452071d4279SBram Moolenaar 		while (	   c-- > 0
1453071d4279SBram Moolenaar 			&& *tail
1454071d4279SBram Moolenaar 			&& vim_isfilec(*tail)
1455071d4279SBram Moolenaar 			&& !vim_ispathsep(*tail))
1456071d4279SBram Moolenaar 		    *var++ = *tail++;
1457071d4279SBram Moolenaar 		*var = NUL;
1458071d4279SBram Moolenaar # ifdef UNIX
1459071d4279SBram Moolenaar 		/*
1460071d4279SBram Moolenaar 		 * If the system supports getpwnam(), use it.
1461071d4279SBram Moolenaar 		 * Otherwise, or if getpwnam() fails, the shell is used to
1462071d4279SBram Moolenaar 		 * expand ~user.  This is slower and may fail if the shell
1463071d4279SBram Moolenaar 		 * does not support ~user (old versions of /bin/sh).
1464071d4279SBram Moolenaar 		 */
1465071d4279SBram Moolenaar #  if defined(HAVE_GETPWNAM) && defined(HAVE_PWD_H)
1466071d4279SBram Moolenaar 		{
146785a2002aSBram Moolenaar 		    // Note: memory allocated by getpwnam() is never freed.
146885a2002aSBram Moolenaar 		    // Calling endpwent() apparently doesn't help.
1469187a4f28SBram Moolenaar 		    struct passwd *pw = (*dst == NUL)
1470187a4f28SBram Moolenaar 					? NULL : getpwnam((char *)dst + 1);
1471187a4f28SBram Moolenaar 
1472187a4f28SBram Moolenaar 		    var = (pw == NULL) ? NULL : (char_u *)pw->pw_dir;
1473071d4279SBram Moolenaar 		}
1474071d4279SBram Moolenaar 		if (var == NULL)
1475071d4279SBram Moolenaar #  endif
1476071d4279SBram Moolenaar 		{
1477071d4279SBram Moolenaar 		    expand_T	xpc;
1478071d4279SBram Moolenaar 
1479071d4279SBram Moolenaar 		    ExpandInit(&xpc);
1480071d4279SBram Moolenaar 		    xpc.xp_context = EXPAND_FILES;
1481071d4279SBram Moolenaar 		    var = ExpandOne(&xpc, dst, NULL,
1482071d4279SBram Moolenaar 				WILD_ADD_SLASH|WILD_SILENT, WILD_EXPAND_FREE);
1483071d4279SBram Moolenaar 		    mustfree = TRUE;
1484071d4279SBram Moolenaar 		}
1485071d4279SBram Moolenaar 
148685a2002aSBram Moolenaar # else	// !UNIX, thus VMS
1487071d4279SBram Moolenaar 		/*
1488071d4279SBram Moolenaar 		 * USER_HOME is a comma-separated list of
1489071d4279SBram Moolenaar 		 * directories to search for the user account in.
1490071d4279SBram Moolenaar 		 */
1491071d4279SBram Moolenaar 		{
1492071d4279SBram Moolenaar 		    char_u	test[MAXPATHL], paths[MAXPATHL];
1493071d4279SBram Moolenaar 		    char_u	*path, *next_path, *ptr;
14948767f52fSBram Moolenaar 		    stat_T	st;
1495071d4279SBram Moolenaar 
1496071d4279SBram Moolenaar 		    STRCPY(paths, USER_HOME);
1497071d4279SBram Moolenaar 		    next_path = paths;
1498071d4279SBram Moolenaar 		    while (*next_path)
1499071d4279SBram Moolenaar 		    {
1500071d4279SBram Moolenaar 			for (path = next_path; *next_path && *next_path != ',';
1501071d4279SBram Moolenaar 				next_path++);
1502071d4279SBram Moolenaar 			if (*next_path)
1503071d4279SBram Moolenaar 			    *next_path++ = NUL;
1504071d4279SBram Moolenaar 			STRCPY(test, path);
1505071d4279SBram Moolenaar 			STRCAT(test, "/");
1506071d4279SBram Moolenaar 			STRCAT(test, dst + 1);
1507071d4279SBram Moolenaar 			if (mch_stat(test, &st) == 0)
1508071d4279SBram Moolenaar 			{
1509071d4279SBram Moolenaar 			    var = alloc(STRLEN(test) + 1);
1510071d4279SBram Moolenaar 			    STRCPY(var, test);
1511071d4279SBram Moolenaar 			    mustfree = TRUE;
1512071d4279SBram Moolenaar 			    break;
1513071d4279SBram Moolenaar 			}
1514071d4279SBram Moolenaar 		    }
1515071d4279SBram Moolenaar 		}
151685a2002aSBram Moolenaar # endif // UNIX
1517071d4279SBram Moolenaar #else
151885a2002aSBram Moolenaar 		// cannot expand user's home directory, so don't try
1519071d4279SBram Moolenaar 		var = NULL;
152085a2002aSBram Moolenaar 		tail = (char_u *)"";	// for gcc
152185a2002aSBram Moolenaar #endif // UNIX || VMS
1522071d4279SBram Moolenaar 	    }
1523071d4279SBram Moolenaar 
1524071d4279SBram Moolenaar #ifdef BACKSLASH_IN_FILENAME
152585a2002aSBram Moolenaar 	    // If 'shellslash' is set change backslashes to forward slashes.
152685a2002aSBram Moolenaar 	    // Can't use slash_adjust(), p_ssl may be set temporarily.
1527071d4279SBram Moolenaar 	    if (p_ssl && var != NULL && vim_strchr(var, '\\') != NULL)
1528071d4279SBram Moolenaar 	    {
1529071d4279SBram Moolenaar 		char_u	*p = vim_strsave(var);
1530071d4279SBram Moolenaar 
1531071d4279SBram Moolenaar 		if (p != NULL)
1532071d4279SBram Moolenaar 		{
1533071d4279SBram Moolenaar 		    if (mustfree)
1534071d4279SBram Moolenaar 			vim_free(var);
1535071d4279SBram Moolenaar 		    var = p;
1536071d4279SBram Moolenaar 		    mustfree = TRUE;
1537071d4279SBram Moolenaar 		    forward_slash(var);
1538071d4279SBram Moolenaar 		}
1539071d4279SBram Moolenaar 	    }
1540071d4279SBram Moolenaar #endif
1541071d4279SBram Moolenaar 
154285a2002aSBram Moolenaar 	    // If "var" contains white space, escape it with a backslash.
154385a2002aSBram Moolenaar 	    // Required for ":e ~/tt" when $HOME includes a space.
1544071d4279SBram Moolenaar 	    if (esc && var != NULL && vim_strpbrk(var, (char_u *)" \t") != NULL)
1545071d4279SBram Moolenaar 	    {
1546071d4279SBram Moolenaar 		char_u	*p = vim_strsave_escaped(var, (char_u *)" \t");
1547071d4279SBram Moolenaar 
1548071d4279SBram Moolenaar 		if (p != NULL)
1549071d4279SBram Moolenaar 		{
1550071d4279SBram Moolenaar 		    if (mustfree)
1551071d4279SBram Moolenaar 			vim_free(var);
1552071d4279SBram Moolenaar 		    var = p;
1553071d4279SBram Moolenaar 		    mustfree = TRUE;
1554071d4279SBram Moolenaar 		}
1555071d4279SBram Moolenaar 	    }
1556071d4279SBram Moolenaar 
1557071d4279SBram Moolenaar 	    if (var != NULL && *var != NUL
1558071d4279SBram Moolenaar 		    && (STRLEN(var) + STRLEN(tail) + 1 < (unsigned)dstlen))
1559071d4279SBram Moolenaar 	    {
1560071d4279SBram Moolenaar 		STRCPY(dst, var);
1561071d4279SBram Moolenaar 		dstlen -= (int)STRLEN(var);
1562a93fa7eeSBram Moolenaar 		c = (int)STRLEN(var);
156385a2002aSBram Moolenaar 		// if var[] ends in a path separator and tail[] starts
156485a2002aSBram Moolenaar 		// with it, skip a character
15651cd871b5SBram Moolenaar 		if (*var != NUL && after_pathsep(dst, dst + c)
1566071d4279SBram Moolenaar #if defined(BACKSLASH_IN_FILENAME) || defined(AMIGA)
1567071d4279SBram Moolenaar 			&& dst[-1] != ':'
1568071d4279SBram Moolenaar #endif
1569071d4279SBram Moolenaar 			&& vim_ispathsep(*tail))
1570071d4279SBram Moolenaar 		    ++tail;
15711cd871b5SBram Moolenaar 		dst += c;
1572071d4279SBram Moolenaar 		src = tail;
1573071d4279SBram Moolenaar 		copy_char = FALSE;
1574071d4279SBram Moolenaar 	    }
1575071d4279SBram Moolenaar 	    if (mustfree)
1576071d4279SBram Moolenaar 		vim_free(var);
1577071d4279SBram Moolenaar 	}
1578071d4279SBram Moolenaar 
157985a2002aSBram Moolenaar 	if (copy_char)	    // copy at least one char
1580071d4279SBram Moolenaar 	{
1581071d4279SBram Moolenaar 	    /*
158225394022SBram Moolenaar 	     * Recognize the start of a new name, for '~'.
15839f0545d6SBram Moolenaar 	     * Don't do this when "one" is TRUE, to avoid expanding "~" in
15849f0545d6SBram Moolenaar 	     * ":edit foo ~ foo".
1585071d4279SBram Moolenaar 	     */
1586071d4279SBram Moolenaar 	    at_start = FALSE;
1587071d4279SBram Moolenaar 	    if (src[0] == '\\' && src[1] != NUL)
1588071d4279SBram Moolenaar 	    {
1589071d4279SBram Moolenaar 		*dst++ = *src++;
1590071d4279SBram Moolenaar 		--dstlen;
1591071d4279SBram Moolenaar 	    }
15929f0545d6SBram Moolenaar 	    else if ((src[0] == ' ' || src[0] == ',') && !one)
1593071d4279SBram Moolenaar 		at_start = TRUE;
15941c864093SBram Moolenaar 	    if (dstlen > 0)
15951c864093SBram Moolenaar 	    {
1596071d4279SBram Moolenaar 		*dst++ = *src++;
1597071d4279SBram Moolenaar 		--dstlen;
159824bbcfe8SBram Moolenaar 
159924bbcfe8SBram Moolenaar 		if (startstr != NULL && src - startstr_len >= srcp
16001c864093SBram Moolenaar 			&& STRNCMP(src - startstr_len, startstr,
16011c864093SBram Moolenaar 							    startstr_len) == 0)
160224bbcfe8SBram Moolenaar 		    at_start = TRUE;
1603071d4279SBram Moolenaar 	    }
1604071d4279SBram Moolenaar 	}
16051c864093SBram Moolenaar 
16061c864093SBram Moolenaar     }
1607071d4279SBram Moolenaar     *dst = NUL;
1608071d4279SBram Moolenaar }
1609071d4279SBram Moolenaar 
1610071d4279SBram Moolenaar /*
161126262f87SBram Moolenaar  * If the string between "p" and "pend" ends in "name/", return "pend" minus
161226262f87SBram Moolenaar  * the length of "name/".  Otherwise return "pend".
161326262f87SBram Moolenaar  */
161426262f87SBram Moolenaar     static char_u *
remove_tail(char_u * p,char_u * pend,char_u * name)161526262f87SBram Moolenaar remove_tail(char_u *p, char_u *pend, char_u *name)
161626262f87SBram Moolenaar {
161726262f87SBram Moolenaar     int		len = (int)STRLEN(name) + 1;
161826262f87SBram Moolenaar     char_u	*newend = pend - len;
161926262f87SBram Moolenaar 
162026262f87SBram Moolenaar     if (newend >= p
162126262f87SBram Moolenaar 	    && fnamencmp(newend, name, len - 1) == 0
162226262f87SBram Moolenaar 	    && (newend == p || after_pathsep(p, newend)))
162326262f87SBram Moolenaar 	return newend;
162426262f87SBram Moolenaar     return pend;
162526262f87SBram Moolenaar }
162626262f87SBram Moolenaar 
162726262f87SBram Moolenaar /*
162826262f87SBram Moolenaar  * Check if the directory "vimdir/<version>" or "vimdir/runtime" exists.
162926262f87SBram Moolenaar  * Return NULL if not, return its name in allocated memory otherwise.
163026262f87SBram Moolenaar  */
163126262f87SBram Moolenaar     static char_u *
vim_version_dir(char_u * vimdir)163226262f87SBram Moolenaar vim_version_dir(char_u *vimdir)
163326262f87SBram Moolenaar {
163426262f87SBram Moolenaar     char_u	*p;
163526262f87SBram Moolenaar 
163626262f87SBram Moolenaar     if (vimdir == NULL || *vimdir == NUL)
163726262f87SBram Moolenaar 	return NULL;
163826262f87SBram Moolenaar     p = concat_fnames(vimdir, (char_u *)VIM_VERSION_NODOT, TRUE);
163926262f87SBram Moolenaar     if (p != NULL && mch_isdir(p))
164026262f87SBram Moolenaar 	return p;
164126262f87SBram Moolenaar     vim_free(p);
164226262f87SBram Moolenaar     p = concat_fnames(vimdir, (char_u *)RUNTIME_DIRNAME, TRUE);
164326262f87SBram Moolenaar     if (p != NULL && mch_isdir(p))
164426262f87SBram Moolenaar 	return p;
164526262f87SBram Moolenaar     vim_free(p);
164626262f87SBram Moolenaar     return NULL;
164726262f87SBram Moolenaar }
164826262f87SBram Moolenaar 
164926262f87SBram Moolenaar /*
1650071d4279SBram Moolenaar  * Vim's version of getenv().
1651071d4279SBram Moolenaar  * Special handling of $HOME, $VIM and $VIMRUNTIME.
16522f6b0b8fSBram Moolenaar  * Also does ACP to 'enc' conversion for Win32.
1653b453a53bSBram Moolenaar  * "mustfree" is set to TRUE when returned is allocated, it must be
1654b453a53bSBram Moolenaar  * initialized to FALSE by the caller.
1655071d4279SBram Moolenaar  */
1656071d4279SBram Moolenaar     char_u *
vim_getenv(char_u * name,int * mustfree)16579b57814dSBram Moolenaar vim_getenv(char_u *name, int *mustfree)
1658071d4279SBram Moolenaar {
1659f0908e6fSBram Moolenaar     char_u	*p = NULL;
1660071d4279SBram Moolenaar     char_u	*pend;
1661071d4279SBram Moolenaar     int		vimruntime;
1662f0908e6fSBram Moolenaar #ifdef MSWIN
1663f0908e6fSBram Moolenaar     WCHAR	*wn, *wp;
1664071d4279SBram Moolenaar 
1665f0908e6fSBram Moolenaar     // use "C:/" when $HOME is not set
1666071d4279SBram Moolenaar     if (STRCMP(name, "HOME") == 0)
1667071d4279SBram Moolenaar 	return homedir;
1668071d4279SBram Moolenaar 
1669f0908e6fSBram Moolenaar     // Use Wide function
1670f0908e6fSBram Moolenaar     wn = enc_to_utf16(name, NULL);
1671f0908e6fSBram Moolenaar     if (wn == NULL)
1672f0908e6fSBram Moolenaar 	return NULL;
1673f0908e6fSBram Moolenaar 
1674f0908e6fSBram Moolenaar     wp = _wgetenv(wn);
1675f0908e6fSBram Moolenaar     vim_free(wn);
1676f0908e6fSBram Moolenaar 
1677f0908e6fSBram Moolenaar     if (wp != NULL && *wp == NUL)   // empty is the same as not set
1678f0908e6fSBram Moolenaar 	wp = NULL;
1679f0908e6fSBram Moolenaar 
1680f0908e6fSBram Moolenaar     if (wp != NULL)
1681f0908e6fSBram Moolenaar     {
1682f0908e6fSBram Moolenaar 	p = utf16_to_enc(wp, NULL);
1683f0908e6fSBram Moolenaar 	if (p == NULL)
1684f0908e6fSBram Moolenaar 	    return NULL;
1685f0908e6fSBram Moolenaar 
1686f0908e6fSBram Moolenaar 	*mustfree = TRUE;
1687f0908e6fSBram Moolenaar 	return p;
1688f0908e6fSBram Moolenaar     }
1689f0908e6fSBram Moolenaar #else
1690071d4279SBram Moolenaar     p = mch_getenv(name);
1691f0908e6fSBram Moolenaar     if (p != NULL && *p == NUL)	    // empty is the same as not set
1692071d4279SBram Moolenaar 	p = NULL;
1693071d4279SBram Moolenaar 
1694071d4279SBram Moolenaar     if (p != NULL)
1695071d4279SBram Moolenaar 	return p;
169680a8d388SBram Moolenaar 
169780a8d388SBram Moolenaar # ifdef __HAIKU__
169880a8d388SBram Moolenaar     // special handling for user settings directory...
169980a8d388SBram Moolenaar     if (STRCMP(name, "BE_USER_SETTINGS") == 0)
170080a8d388SBram Moolenaar     {
170180a8d388SBram Moolenaar 	static char userSettingsPath[MAXPATHL];
170280a8d388SBram Moolenaar 
170380a8d388SBram Moolenaar 	if (find_directory(B_USER_SETTINGS_DIRECTORY, 0, false,
170480a8d388SBram Moolenaar 					   userSettingsPath, MAXPATHL) == B_OK)
170580a8d388SBram Moolenaar 	    return (char_u *)userSettingsPath;
170680a8d388SBram Moolenaar 	else
170780a8d388SBram Moolenaar 	    return NULL;
170880a8d388SBram Moolenaar     }
170980a8d388SBram Moolenaar # endif
1710f0908e6fSBram Moolenaar #endif
1711071d4279SBram Moolenaar 
1712f0908e6fSBram Moolenaar     // handling $VIMRUNTIME and $VIM is below, bail out if it's another name.
1713071d4279SBram Moolenaar     vimruntime = (STRCMP(name, "VIMRUNTIME") == 0);
1714071d4279SBram Moolenaar     if (!vimruntime && STRCMP(name, "VIM") != 0)
1715071d4279SBram Moolenaar 	return NULL;
1716071d4279SBram Moolenaar 
1717071d4279SBram Moolenaar     /*
1718071d4279SBram Moolenaar      * When expanding $VIMRUNTIME fails, try using $VIM/vim<version> or $VIM.
1719071d4279SBram Moolenaar      * Don't do this when default_vimruntime_dir is non-empty.
1720071d4279SBram Moolenaar      */
1721071d4279SBram Moolenaar     if (vimruntime
1722071d4279SBram Moolenaar #ifdef HAVE_PATHDEF
1723071d4279SBram Moolenaar 	    && *default_vimruntime_dir == NUL
1724071d4279SBram Moolenaar #endif
1725071d4279SBram Moolenaar        )
1726071d4279SBram Moolenaar     {
1727f0908e6fSBram Moolenaar #ifdef MSWIN
1728f0908e6fSBram Moolenaar 	// Use Wide function
1729f0908e6fSBram Moolenaar 	wp = _wgetenv(L"VIM");
1730f0908e6fSBram Moolenaar 	if (wp != NULL && *wp == NUL)	    // empty is the same as not set
1731f0908e6fSBram Moolenaar 	    wp = NULL;
1732f0908e6fSBram Moolenaar 	if (wp != NULL)
1733f0908e6fSBram Moolenaar 	{
1734f0908e6fSBram Moolenaar 	    char_u *q = utf16_to_enc(wp, NULL);
1735f0908e6fSBram Moolenaar 	    if (q != NULL)
1736f0908e6fSBram Moolenaar 	    {
1737f0908e6fSBram Moolenaar 		p = vim_version_dir(q);
1738f0908e6fSBram Moolenaar 		*mustfree = TRUE;
1739f0908e6fSBram Moolenaar 		if (p == NULL)
1740f0908e6fSBram Moolenaar 		    p = q;
1741f0908e6fSBram Moolenaar 	    }
1742f0908e6fSBram Moolenaar 	}
1743f0908e6fSBram Moolenaar #else
1744071d4279SBram Moolenaar 	p = mch_getenv((char_u *)"VIM");
1745f0908e6fSBram Moolenaar 	if (p != NULL && *p == NUL)	    // empty is the same as not set
1746071d4279SBram Moolenaar 	    p = NULL;
1747071d4279SBram Moolenaar 	if (p != NULL)
1748071d4279SBram Moolenaar 	{
1749071d4279SBram Moolenaar 	    p = vim_version_dir(p);
1750071d4279SBram Moolenaar 	    if (p != NULL)
1751071d4279SBram Moolenaar 		*mustfree = TRUE;
1752071d4279SBram Moolenaar 	    else
1753071d4279SBram Moolenaar 		p = mch_getenv((char_u *)"VIM");
175405159a0cSBram Moolenaar 	}
175505159a0cSBram Moolenaar #endif
1756071d4279SBram Moolenaar     }
1757071d4279SBram Moolenaar 
1758071d4279SBram Moolenaar     /*
1759071d4279SBram Moolenaar      * When expanding $VIM or $VIMRUNTIME fails, try using:
1760071d4279SBram Moolenaar      * - the directory name from 'helpfile' (unless it contains '$')
1761071d4279SBram Moolenaar      * - the executable name from argv[0]
1762071d4279SBram Moolenaar      */
1763071d4279SBram Moolenaar     if (p == NULL)
1764071d4279SBram Moolenaar     {
1765071d4279SBram Moolenaar 	if (p_hf != NULL && vim_strchr(p_hf, '$') == NULL)
1766071d4279SBram Moolenaar 	    p = p_hf;
1767071d4279SBram Moolenaar #ifdef USE_EXE_NAME
1768071d4279SBram Moolenaar 	/*
1769071d4279SBram Moolenaar 	 * Use the name of the executable, obtained from argv[0].
1770071d4279SBram Moolenaar 	 */
1771071d4279SBram Moolenaar 	else
1772071d4279SBram Moolenaar 	    p = exe_name;
1773071d4279SBram Moolenaar #endif
1774071d4279SBram Moolenaar 	if (p != NULL)
1775071d4279SBram Moolenaar 	{
177685a2002aSBram Moolenaar 	    // remove the file name
1777071d4279SBram Moolenaar 	    pend = gettail(p);
1778071d4279SBram Moolenaar 
177985a2002aSBram Moolenaar 	    // remove "doc/" from 'helpfile', if present
1780071d4279SBram Moolenaar 	    if (p == p_hf)
1781071d4279SBram Moolenaar 		pend = remove_tail(p, pend, (char_u *)"doc");
1782071d4279SBram Moolenaar 
1783071d4279SBram Moolenaar #ifdef USE_EXE_NAME
1784071d4279SBram Moolenaar # ifdef MACOS_X
178585a2002aSBram Moolenaar 	    // remove "MacOS" from exe_name and add "Resources/vim"
1786071d4279SBram Moolenaar 	    if (p == exe_name)
1787071d4279SBram Moolenaar 	    {
1788071d4279SBram Moolenaar 		char_u	*pend1;
178995e9b495SBram Moolenaar 		char_u	*pnew;
1790071d4279SBram Moolenaar 
179195e9b495SBram Moolenaar 		pend1 = remove_tail(p, pend, (char_u *)"MacOS");
179295e9b495SBram Moolenaar 		if (pend1 != pend)
179395e9b495SBram Moolenaar 		{
1794964b3746SBram Moolenaar 		    pnew = alloc(pend1 - p + 15);
179595e9b495SBram Moolenaar 		    if (pnew != NULL)
179695e9b495SBram Moolenaar 		    {
179795e9b495SBram Moolenaar 			STRNCPY(pnew, p, (pend1 - p));
179895e9b495SBram Moolenaar 			STRCPY(pnew + (pend1 - p), "Resources/vim");
179995e9b495SBram Moolenaar 			p = pnew;
180095e9b495SBram Moolenaar 			pend = p + STRLEN(p);
180195e9b495SBram Moolenaar 		    }
180295e9b495SBram Moolenaar 		}
1803071d4279SBram Moolenaar 	    }
1804071d4279SBram Moolenaar # endif
180585a2002aSBram Moolenaar 	    // remove "src/" from exe_name, if present
1806071d4279SBram Moolenaar 	    if (p == exe_name)
1807071d4279SBram Moolenaar 		pend = remove_tail(p, pend, (char_u *)"src");
1808071d4279SBram Moolenaar #endif
1809071d4279SBram Moolenaar 
181085a2002aSBram Moolenaar 	    // for $VIM, remove "runtime/" or "vim54/", if present
1811071d4279SBram Moolenaar 	    if (!vimruntime)
1812071d4279SBram Moolenaar 	    {
1813071d4279SBram Moolenaar 		pend = remove_tail(p, pend, (char_u *)RUNTIME_DIRNAME);
1814071d4279SBram Moolenaar 		pend = remove_tail(p, pend, (char_u *)VIM_VERSION_NODOT);
1815071d4279SBram Moolenaar 	    }
1816071d4279SBram Moolenaar 
181785a2002aSBram Moolenaar 	    // remove trailing path separator
18181cd871b5SBram Moolenaar 	    if (pend > p && after_pathsep(p, pend))
1819071d4279SBram Moolenaar 		--pend;
1820071d4279SBram Moolenaar 
182195e9b495SBram Moolenaar #ifdef MACOS_X
182295e9b495SBram Moolenaar 	    if (p == exe_name || p == p_hf)
182395e9b495SBram Moolenaar #endif
182485a2002aSBram Moolenaar 		// check that the result is a directory name
182571ccd03eSBram Moolenaar 		p = vim_strnsave(p, pend - p);
1826071d4279SBram Moolenaar 
1827071d4279SBram Moolenaar 	    if (p != NULL && !mch_isdir(p))
1828d23a8236SBram Moolenaar 		VIM_CLEAR(p);
1829071d4279SBram Moolenaar 	    else
1830071d4279SBram Moolenaar 	    {
1831071d4279SBram Moolenaar #ifdef USE_EXE_NAME
183285a2002aSBram Moolenaar 		// may add "/vim54" or "/runtime" if it exists
1833071d4279SBram Moolenaar 		if (vimruntime && (pend = vim_version_dir(p)) != NULL)
1834071d4279SBram Moolenaar 		{
1835071d4279SBram Moolenaar 		    vim_free(p);
1836071d4279SBram Moolenaar 		    p = pend;
1837071d4279SBram Moolenaar 		}
1838071d4279SBram Moolenaar #endif
1839071d4279SBram Moolenaar 		*mustfree = TRUE;
1840071d4279SBram Moolenaar 	    }
1841071d4279SBram Moolenaar 	}
1842071d4279SBram Moolenaar     }
1843071d4279SBram Moolenaar 
1844071d4279SBram Moolenaar #ifdef HAVE_PATHDEF
184585a2002aSBram Moolenaar     // When there is a pathdef.c file we can use default_vim_dir and
184685a2002aSBram Moolenaar     // default_vimruntime_dir
1847071d4279SBram Moolenaar     if (p == NULL)
1848071d4279SBram Moolenaar     {
184985a2002aSBram Moolenaar 	// Only use default_vimruntime_dir when it is not empty
1850071d4279SBram Moolenaar 	if (vimruntime && *default_vimruntime_dir != NUL)
1851071d4279SBram Moolenaar 	{
1852071d4279SBram Moolenaar 	    p = default_vimruntime_dir;
1853071d4279SBram Moolenaar 	    *mustfree = FALSE;
1854071d4279SBram Moolenaar 	}
1855071d4279SBram Moolenaar 	else if (*default_vim_dir != NUL)
1856071d4279SBram Moolenaar 	{
1857071d4279SBram Moolenaar 	    if (vimruntime && (p = vim_version_dir(default_vim_dir)) != NULL)
1858071d4279SBram Moolenaar 		*mustfree = TRUE;
1859071d4279SBram Moolenaar 	    else
1860071d4279SBram Moolenaar 	    {
1861071d4279SBram Moolenaar 		p = default_vim_dir;
1862071d4279SBram Moolenaar 		*mustfree = FALSE;
1863071d4279SBram Moolenaar 	    }
1864071d4279SBram Moolenaar 	}
1865071d4279SBram Moolenaar     }
1866071d4279SBram Moolenaar #endif
1867071d4279SBram Moolenaar 
1868071d4279SBram Moolenaar     /*
1869071d4279SBram Moolenaar      * Set the environment variable, so that the new value can be found fast
1870071d4279SBram Moolenaar      * next time, and others can also use it (e.g. Perl).
1871071d4279SBram Moolenaar      */
1872071d4279SBram Moolenaar     if (p != NULL)
1873071d4279SBram Moolenaar     {
1874071d4279SBram Moolenaar 	if (vimruntime)
1875071d4279SBram Moolenaar 	{
1876071d4279SBram Moolenaar 	    vim_setenv((char_u *)"VIMRUNTIME", p);
1877071d4279SBram Moolenaar 	    didset_vimruntime = TRUE;
1878071d4279SBram Moolenaar 	}
1879071d4279SBram Moolenaar 	else
1880071d4279SBram Moolenaar 	{
1881071d4279SBram Moolenaar 	    vim_setenv((char_u *)"VIM", p);
1882071d4279SBram Moolenaar 	    didset_vim = TRUE;
1883071d4279SBram Moolenaar 	}
1884071d4279SBram Moolenaar     }
1885071d4279SBram Moolenaar     return p;
1886071d4279SBram Moolenaar }
1887071d4279SBram Moolenaar 
1888113e1072SBram Moolenaar #if defined(FEAT_EVAL) || defined(PROTO)
1889137374fdSBram Moolenaar     void
vim_unsetenv(char_u * var)1890137374fdSBram Moolenaar vim_unsetenv(char_u *var)
1891137374fdSBram Moolenaar {
1892137374fdSBram Moolenaar #ifdef HAVE_UNSETENV
1893137374fdSBram Moolenaar     unsetenv((char *)var);
1894137374fdSBram Moolenaar #else
18951af6a4b8SBram Moolenaar     vim_setenv(var, (char_u *)"");
1896137374fdSBram Moolenaar #endif
1897137374fdSBram Moolenaar }
1898113e1072SBram Moolenaar #endif
1899137374fdSBram Moolenaar 
1900137374fdSBram Moolenaar 
1901071d4279SBram Moolenaar /*
1902b283a8a6SBram Moolenaar  * Set environment variable "name" and take care of side effects.
1903b283a8a6SBram Moolenaar  */
1904b283a8a6SBram Moolenaar     void
vim_setenv_ext(char_u * name,char_u * val)1905b283a8a6SBram Moolenaar vim_setenv_ext(char_u *name, char_u *val)
1906b283a8a6SBram Moolenaar {
1907b283a8a6SBram Moolenaar     vim_setenv(name, val);
1908b283a8a6SBram Moolenaar     if (STRICMP(name, "HOME") == 0)
1909b283a8a6SBram Moolenaar 	init_homedir();
1910b283a8a6SBram Moolenaar     else if (didset_vim && STRICMP(name, "VIM") == 0)
1911b283a8a6SBram Moolenaar 	didset_vim = FALSE;
1912b283a8a6SBram Moolenaar     else if (didset_vimruntime
1913b283a8a6SBram Moolenaar 	    && STRICMP(name, "VIMRUNTIME") == 0)
1914b283a8a6SBram Moolenaar 	didset_vimruntime = FALSE;
1915b283a8a6SBram Moolenaar }
1916b283a8a6SBram Moolenaar 
1917b283a8a6SBram Moolenaar /*
1918071d4279SBram Moolenaar  * Our portable version of setenv.
1919071d4279SBram Moolenaar  */
1920071d4279SBram Moolenaar     void
vim_setenv(char_u * name,char_u * val)19219b57814dSBram Moolenaar vim_setenv(char_u *name, char_u *val)
1922071d4279SBram Moolenaar {
1923071d4279SBram Moolenaar #ifdef HAVE_SETENV
1924071d4279SBram Moolenaar     mch_setenv((char *)name, (char *)val, 1);
1925071d4279SBram Moolenaar #else
1926071d4279SBram Moolenaar     char_u	*envbuf;
1927071d4279SBram Moolenaar 
1928071d4279SBram Moolenaar     /*
1929071d4279SBram Moolenaar      * Putenv does not copy the string, it has to remain
1930071d4279SBram Moolenaar      * valid.  The allocated memory will never be freed.
1931071d4279SBram Moolenaar      */
1932964b3746SBram Moolenaar     envbuf = alloc(STRLEN(name) + STRLEN(val) + 2);
1933071d4279SBram Moolenaar     if (envbuf != NULL)
1934071d4279SBram Moolenaar     {
1935071d4279SBram Moolenaar 	sprintf((char *)envbuf, "%s=%s", name, val);
1936071d4279SBram Moolenaar 	putenv((char *)envbuf);
1937071d4279SBram Moolenaar     }
1938071d4279SBram Moolenaar #endif
1939011a34d7SBram Moolenaar #ifdef FEAT_GETTEXT
1940011a34d7SBram Moolenaar     /*
1941011a34d7SBram Moolenaar      * When setting $VIMRUNTIME adjust the directory to find message
1942011a34d7SBram Moolenaar      * translations to $VIMRUNTIME/lang.
1943011a34d7SBram Moolenaar      */
1944011a34d7SBram Moolenaar     if (*val != NUL && STRICMP(name, "VIMRUNTIME") == 0)
1945011a34d7SBram Moolenaar     {
1946011a34d7SBram Moolenaar 	char_u	*buf = concat_str(val, (char_u *)"/lang");
1947011a34d7SBram Moolenaar 
1948011a34d7SBram Moolenaar 	if (buf != NULL)
1949011a34d7SBram Moolenaar 	{
1950011a34d7SBram Moolenaar 	    bindtextdomain(VIMPACKAGE, (char *)buf);
1951011a34d7SBram Moolenaar 	    vim_free(buf);
1952011a34d7SBram Moolenaar 	}
1953011a34d7SBram Moolenaar     }
1954011a34d7SBram Moolenaar #endif
1955071d4279SBram Moolenaar }
1956071d4279SBram Moolenaar 
1957071d4279SBram Moolenaar /*
1958071d4279SBram Moolenaar  * Function given to ExpandGeneric() to obtain an environment variable name.
1959071d4279SBram Moolenaar  */
1960071d4279SBram Moolenaar     char_u *
get_env_name(expand_T * xp UNUSED,int idx)19619b57814dSBram Moolenaar get_env_name(
19629b57814dSBram Moolenaar     expand_T	*xp UNUSED,
19639b57814dSBram Moolenaar     int		idx)
1964071d4279SBram Moolenaar {
1965d057301bSBram Moolenaar # if defined(AMIGA)
1966071d4279SBram Moolenaar     /*
1967d057301bSBram Moolenaar      * No environ[] on the Amiga.
1968071d4279SBram Moolenaar      */
1969071d4279SBram Moolenaar     return NULL;
1970071d4279SBram Moolenaar # else
1971071d4279SBram Moolenaar # ifndef __WIN32__
197285a2002aSBram Moolenaar     // Borland C++ 5.2 has this in a header file.
1973071d4279SBram Moolenaar     extern char		**environ;
1974071d4279SBram Moolenaar # endif
197521cf823aSBram Moolenaar # define ENVNAMELEN 100
197621cf823aSBram Moolenaar     static char_u	name[ENVNAMELEN];
1977071d4279SBram Moolenaar     char_u		*str;
1978071d4279SBram Moolenaar     int			n;
1979071d4279SBram Moolenaar 
1980071d4279SBram Moolenaar     str = (char_u *)environ[idx];
1981071d4279SBram Moolenaar     if (str == NULL)
1982071d4279SBram Moolenaar 	return NULL;
1983071d4279SBram Moolenaar 
198421cf823aSBram Moolenaar     for (n = 0; n < ENVNAMELEN - 1; ++n)
1985071d4279SBram Moolenaar     {
1986071d4279SBram Moolenaar 	if (str[n] == '=' || str[n] == NUL)
1987071d4279SBram Moolenaar 	    break;
1988071d4279SBram Moolenaar 	name[n] = str[n];
1989071d4279SBram Moolenaar     }
1990071d4279SBram Moolenaar     name[n] = NUL;
1991071d4279SBram Moolenaar     return name;
1992071d4279SBram Moolenaar # endif
1993071d4279SBram Moolenaar }
199424305866SBram Moolenaar 
199524305866SBram Moolenaar /*
19966b0b83f7SBram Moolenaar  * Add a user name to the list of users in ga_users.
19976b0b83f7SBram Moolenaar  * Do nothing if user name is NULL or empty.
19986b0b83f7SBram Moolenaar  */
19996b0b83f7SBram Moolenaar     static void
add_user(char_u * user,int need_copy)20006b0b83f7SBram Moolenaar add_user(char_u *user, int need_copy)
20016b0b83f7SBram Moolenaar {
20026b0b83f7SBram Moolenaar     char_u	*user_copy = (user != NULL && need_copy)
20036b0b83f7SBram Moolenaar 						    ? vim_strsave(user) : user;
20046b0b83f7SBram Moolenaar 
20056b0b83f7SBram Moolenaar     if (user_copy == NULL || *user_copy == NUL || ga_grow(&ga_users, 1) == FAIL)
20066b0b83f7SBram Moolenaar     {
20076b0b83f7SBram Moolenaar 	if (need_copy)
20086b0b83f7SBram Moolenaar 	    vim_free(user);
20096b0b83f7SBram Moolenaar 	return;
20106b0b83f7SBram Moolenaar     }
20116b0b83f7SBram Moolenaar     ((char_u **)(ga_users.ga_data))[ga_users.ga_len++] = user_copy;
20126b0b83f7SBram Moolenaar }
20136b0b83f7SBram Moolenaar 
20146b0b83f7SBram Moolenaar /*
201524305866SBram Moolenaar  * Find all user names for user completion.
201624305866SBram Moolenaar  * Done only once and then cached.
201724305866SBram Moolenaar  */
201824305866SBram Moolenaar     static void
init_users(void)20199b57814dSBram Moolenaar init_users(void)
202001b626c2SBram Moolenaar {
202124305866SBram Moolenaar     static int	lazy_init_done = FALSE;
202224305866SBram Moolenaar 
202324305866SBram Moolenaar     if (lazy_init_done)
202424305866SBram Moolenaar 	return;
202524305866SBram Moolenaar 
202624305866SBram Moolenaar     lazy_init_done = TRUE;
202724305866SBram Moolenaar     ga_init2(&ga_users, sizeof(char_u *), 20);
202824305866SBram Moolenaar 
202924305866SBram Moolenaar # if defined(HAVE_GETPWENT) && defined(HAVE_PWD_H)
203024305866SBram Moolenaar     {
203124305866SBram Moolenaar 	struct passwd*	pw;
203224305866SBram Moolenaar 
203324305866SBram Moolenaar 	setpwent();
203424305866SBram Moolenaar 	while ((pw = getpwent()) != NULL)
20356b0b83f7SBram Moolenaar 	    add_user((char_u *)pw->pw_name, TRUE);
203624305866SBram Moolenaar 	endpwent();
203724305866SBram Moolenaar     }
20384f97475dSBram Moolenaar # elif defined(MSWIN)
2039828c3d70SBram Moolenaar     {
2040828c3d70SBram Moolenaar 	DWORD		nusers = 0, ntotal = 0, i;
2041828c3d70SBram Moolenaar 	PUSER_INFO_0	uinfo;
2042828c3d70SBram Moolenaar 
2043828c3d70SBram Moolenaar 	if (NetUserEnum(NULL, 0, 0, (LPBYTE *) &uinfo, MAX_PREFERRED_LENGTH,
2044828c3d70SBram Moolenaar 				       &nusers, &ntotal, NULL) == NERR_Success)
2045828c3d70SBram Moolenaar 	{
2046828c3d70SBram Moolenaar 	    for (i = 0; i < nusers; i++)
20476b0b83f7SBram Moolenaar 		add_user(utf16_to_enc(uinfo[i].usri0_name, NULL), FALSE);
2048828c3d70SBram Moolenaar 
2049828c3d70SBram Moolenaar 	    NetApiBufferFree(uinfo);
2050828c3d70SBram Moolenaar 	}
2051828c3d70SBram Moolenaar     }
205224305866SBram Moolenaar # endif
20536b0b83f7SBram Moolenaar # if defined(HAVE_GETPWNAM)
20546b0b83f7SBram Moolenaar     {
20556b0b83f7SBram Moolenaar 	char_u	*user_env = mch_getenv((char_u *)"USER");
20566b0b83f7SBram Moolenaar 
20576b0b83f7SBram Moolenaar 	// The $USER environment variable may be a valid remote user name (NIS,
20586b0b83f7SBram Moolenaar 	// LDAP) not already listed by getpwent(), as getpwent() only lists
20596b0b83f7SBram Moolenaar 	// local user names.  If $USER is not already listed, check whether it
20606b0b83f7SBram Moolenaar 	// is a valid remote user name using getpwnam() and if it is, add it to
20616b0b83f7SBram Moolenaar 	// the list of user names.
20626b0b83f7SBram Moolenaar 
20636b0b83f7SBram Moolenaar 	if (user_env != NULL && *user_env != NUL)
20646b0b83f7SBram Moolenaar 	{
20656b0b83f7SBram Moolenaar 	    int	i;
20666b0b83f7SBram Moolenaar 
20676b0b83f7SBram Moolenaar 	    for (i = 0; i < ga_users.ga_len; i++)
20686b0b83f7SBram Moolenaar 	    {
20696b0b83f7SBram Moolenaar 		char_u	*local_user = ((char_u **)ga_users.ga_data)[i];
20706b0b83f7SBram Moolenaar 
20716b0b83f7SBram Moolenaar 		if (STRCMP(local_user, user_env) == 0)
20726b0b83f7SBram Moolenaar 		    break;
20736b0b83f7SBram Moolenaar 	    }
20746b0b83f7SBram Moolenaar 
20756b0b83f7SBram Moolenaar 	    if (i == ga_users.ga_len)
20766b0b83f7SBram Moolenaar 	    {
20776b0b83f7SBram Moolenaar 		struct passwd	*pw = getpwnam((char *)user_env);
20786b0b83f7SBram Moolenaar 
20796b0b83f7SBram Moolenaar 		if (pw != NULL)
20806b0b83f7SBram Moolenaar 		    add_user((char_u *)pw->pw_name, TRUE);
20816b0b83f7SBram Moolenaar 	    }
20826b0b83f7SBram Moolenaar 	}
20836b0b83f7SBram Moolenaar     }
20846b0b83f7SBram Moolenaar # endif
208524305866SBram Moolenaar }
208624305866SBram Moolenaar 
208724305866SBram Moolenaar /*
208824305866SBram Moolenaar  * Function given to ExpandGeneric() to obtain an user names.
208924305866SBram Moolenaar  */
209024305866SBram Moolenaar     char_u*
get_users(expand_T * xp UNUSED,int idx)20919b57814dSBram Moolenaar get_users(expand_T *xp UNUSED, int idx)
209224305866SBram Moolenaar {
209324305866SBram Moolenaar     init_users();
209424305866SBram Moolenaar     if (idx < ga_users.ga_len)
209524305866SBram Moolenaar 	return ((char_u **)ga_users.ga_data)[idx];
209624305866SBram Moolenaar     return NULL;
209724305866SBram Moolenaar }
209824305866SBram Moolenaar 
209924305866SBram Moolenaar /*
210024305866SBram Moolenaar  * Check whether name matches a user name. Return:
210124305866SBram Moolenaar  * 0 if name does not match any user name.
210224305866SBram Moolenaar  * 1 if name partially matches the beginning of a user name.
210324305866SBram Moolenaar  * 2 is name fully matches a user name.
210424305866SBram Moolenaar  */
21056c5d1043SBram Moolenaar     int
match_user(char_u * name)21066c5d1043SBram Moolenaar match_user(char_u *name)
210724305866SBram Moolenaar {
210824305866SBram Moolenaar     int i;
210924305866SBram Moolenaar     int n = (int)STRLEN(name);
211024305866SBram Moolenaar     int result = 0;
211124305866SBram Moolenaar 
211224305866SBram Moolenaar     init_users();
211324305866SBram Moolenaar     for (i = 0; i < ga_users.ga_len; i++)
211424305866SBram Moolenaar     {
211524305866SBram Moolenaar 	if (STRCMP(((char_u **)ga_users.ga_data)[i], name) == 0)
211685a2002aSBram Moolenaar 	    return 2; // full match
211724305866SBram Moolenaar 	if (STRNCMP(((char_u **)ga_users.ga_data)[i], name, n) == 0)
211885a2002aSBram Moolenaar 	    result = 1; // partial match
211924305866SBram Moolenaar     }
212024305866SBram Moolenaar     return result;
212124305866SBram Moolenaar }
2122071d4279SBram Moolenaar 
21235843f5f3SBram Moolenaar     static void
prepare_to_exit(void)21249b57814dSBram Moolenaar prepare_to_exit(void)
2125071d4279SBram Moolenaar {
21261cd871b5SBram Moolenaar #if defined(SIGHUP) && defined(SIG_IGN)
212785a2002aSBram Moolenaar     // Ignore SIGHUP, because a dropped connection causes a read error, which
212885a2002aSBram Moolenaar     // makes Vim exit and then handling SIGHUP causes various reentrance
212985a2002aSBram Moolenaar     // problems.
2130293ee4d4SBram Moolenaar     signal(SIGHUP, SIG_IGN);
2131293ee4d4SBram Moolenaar #endif
2132293ee4d4SBram Moolenaar 
2133071d4279SBram Moolenaar #ifdef FEAT_GUI
2134071d4279SBram Moolenaar     if (gui.in_use)
2135071d4279SBram Moolenaar     {
2136071d4279SBram Moolenaar 	gui.dying = TRUE;
213785a2002aSBram Moolenaar 	out_trash();	// trash any pending output
2138071d4279SBram Moolenaar     }
2139071d4279SBram Moolenaar     else
2140071d4279SBram Moolenaar #endif
2141071d4279SBram Moolenaar     {
2142071d4279SBram Moolenaar 	windgoto((int)Rows - 1, 0);
2143071d4279SBram Moolenaar 
2144071d4279SBram Moolenaar 	/*
2145071d4279SBram Moolenaar 	 * Switch terminal mode back now, so messages end up on the "normal"
2146071d4279SBram Moolenaar 	 * screen (if there are two screens).
2147071d4279SBram Moolenaar 	 */
2148071d4279SBram Moolenaar 	settmode(TMODE_COOK);
2149071d4279SBram Moolenaar 	stoptermcap();
2150071d4279SBram Moolenaar 	out_flush();
2151071d4279SBram Moolenaar     }
2152071d4279SBram Moolenaar }
2153071d4279SBram Moolenaar 
2154071d4279SBram Moolenaar /*
2155071d4279SBram Moolenaar  * Preserve files and exit.
2156071d4279SBram Moolenaar  * When called IObuff must contain a message.
2157bec9c208SBram Moolenaar  * NOTE: This may be called from deathtrap() in a signal handler, avoid unsafe
2158bec9c208SBram Moolenaar  * functions, such as allocating memory.
2159071d4279SBram Moolenaar  */
2160071d4279SBram Moolenaar     void
preserve_exit(void)21619b57814dSBram Moolenaar preserve_exit(void)
2162071d4279SBram Moolenaar {
2163071d4279SBram Moolenaar     buf_T	*buf;
2164071d4279SBram Moolenaar 
2165071d4279SBram Moolenaar     prepare_to_exit();
2166071d4279SBram Moolenaar 
216785a2002aSBram Moolenaar     // Setting this will prevent free() calls.  That avoids calling free()
216885a2002aSBram Moolenaar     // recursively when free() was invoked with a bad pointer.
21694770d09aSBram Moolenaar     really_exiting = TRUE;
21704770d09aSBram Moolenaar 
2171071d4279SBram Moolenaar     out_str(IObuff);
217285a2002aSBram Moolenaar     screen_start();		    // don't know where cursor is now
2173071d4279SBram Moolenaar     out_flush();
2174071d4279SBram Moolenaar 
217585a2002aSBram Moolenaar     ml_close_notmod();		    // close all not-modified buffers
2176071d4279SBram Moolenaar 
217729323590SBram Moolenaar     FOR_ALL_BUFFERS(buf)
2178071d4279SBram Moolenaar     {
2179071d4279SBram Moolenaar 	if (buf->b_ml.ml_mfp != NULL && buf->b_ml.ml_mfp->mf_fname != NULL)
2180071d4279SBram Moolenaar 	{
218169212b11SBram Moolenaar 	    OUT_STR("Vim: preserving files...\r\n");
218285a2002aSBram Moolenaar 	    screen_start();	    // don't know where cursor is now
2183071d4279SBram Moolenaar 	    out_flush();
218485a2002aSBram Moolenaar 	    ml_sync_all(FALSE, FALSE);	// preserve all swap files
2185071d4279SBram Moolenaar 	    break;
2186071d4279SBram Moolenaar 	}
2187071d4279SBram Moolenaar     }
2188071d4279SBram Moolenaar 
218985a2002aSBram Moolenaar     ml_close_all(FALSE);	    // close all memfiles, without deleting
2190071d4279SBram Moolenaar 
219169212b11SBram Moolenaar     OUT_STR("Vim: Finished.\r\n");
2192071d4279SBram Moolenaar 
2193071d4279SBram Moolenaar     getout(1);
2194071d4279SBram Moolenaar }
2195071d4279SBram Moolenaar 
2196071d4279SBram Moolenaar /*
2197071d4279SBram Moolenaar  * Check for CTRL-C pressed, but only once in a while.
2198071d4279SBram Moolenaar  * Should be used instead of ui_breakcheck() for functions that check for
2199071d4279SBram Moolenaar  * each line in the file.  Calling ui_breakcheck() each time takes too much
2200071d4279SBram Moolenaar  * time, because it can be a system call.
2201071d4279SBram Moolenaar  */
2202071d4279SBram Moolenaar 
2203071d4279SBram Moolenaar #ifndef BREAKCHECK_SKIP
2204b4fe0eb4SBram Moolenaar # define BREAKCHECK_SKIP 1000
2205071d4279SBram Moolenaar #endif
2206071d4279SBram Moolenaar 
2207071d4279SBram Moolenaar static int	breakcheck_count = 0;
2208071d4279SBram Moolenaar 
2209071d4279SBram Moolenaar     void
line_breakcheck(void)22109b57814dSBram Moolenaar line_breakcheck(void)
2211071d4279SBram Moolenaar {
2212071d4279SBram Moolenaar     if (++breakcheck_count >= BREAKCHECK_SKIP)
2213071d4279SBram Moolenaar     {
2214071d4279SBram Moolenaar 	breakcheck_count = 0;
2215071d4279SBram Moolenaar 	ui_breakcheck();
2216071d4279SBram Moolenaar     }
2217071d4279SBram Moolenaar }
2218071d4279SBram Moolenaar 
2219071d4279SBram Moolenaar /*
2220071d4279SBram Moolenaar  * Like line_breakcheck() but check 10 times less often.
2221071d4279SBram Moolenaar  */
2222071d4279SBram Moolenaar     void
fast_breakcheck(void)22239b57814dSBram Moolenaar fast_breakcheck(void)
2224071d4279SBram Moolenaar {
2225071d4279SBram Moolenaar     if (++breakcheck_count >= BREAKCHECK_SKIP * 10)
2226071d4279SBram Moolenaar     {
2227071d4279SBram Moolenaar 	breakcheck_count = 0;
2228071d4279SBram Moolenaar 	ui_breakcheck();
2229071d4279SBram Moolenaar     }
2230071d4279SBram Moolenaar }
2231071d4279SBram Moolenaar 
2232f1ec378bSBram Moolenaar /*
2233f1ec378bSBram Moolenaar  * Like line_breakcheck() but check 100 times less often.
2234f1ec378bSBram Moolenaar  */
2235f1ec378bSBram Moolenaar     void
veryfast_breakcheck(void)2236f1ec378bSBram Moolenaar veryfast_breakcheck(void)
2237f1ec378bSBram Moolenaar {
2238f1ec378bSBram Moolenaar     if (++breakcheck_count >= BREAKCHECK_SKIP * 100)
2239f1ec378bSBram Moolenaar     {
2240f1ec378bSBram Moolenaar 	breakcheck_count = 0;
2241f1ec378bSBram Moolenaar 	ui_breakcheck();
2242f1ec378bSBram Moolenaar     }
2243f1ec378bSBram Moolenaar }
2244f1ec378bSBram Moolenaar 
22450a52df50SBram Moolenaar #if defined(VIM_BACKTICK) || defined(FEAT_EVAL) \
22460a52df50SBram Moolenaar 	|| (defined(HAVE_LOCALE_H) || defined(X_LOCALE)) \
22470a52df50SBram Moolenaar 	|| defined(PROTO)
2248071d4279SBram Moolenaar 
2249071d4279SBram Moolenaar #ifndef SEEK_SET
2250071d4279SBram Moolenaar # define SEEK_SET 0
2251071d4279SBram Moolenaar #endif
2252071d4279SBram Moolenaar #ifndef SEEK_END
2253071d4279SBram Moolenaar # define SEEK_END 2
2254071d4279SBram Moolenaar #endif
2255071d4279SBram Moolenaar 
2256071d4279SBram Moolenaar /*
2257071d4279SBram Moolenaar  * Get the stdout of an external command.
225839c29ed5SBram Moolenaar  * If "ret_len" is NULL replace NUL characters with NL.  When "ret_len" is not
225939c29ed5SBram Moolenaar  * NULL store the length there.
2260071d4279SBram Moolenaar  * Returns an allocated string, or NULL for error.
2261071d4279SBram Moolenaar  */
2262071d4279SBram Moolenaar     char_u *
get_cmd_output(char_u * cmd,char_u * infile,int flags,int * ret_len)22639b57814dSBram Moolenaar get_cmd_output(
22649b57814dSBram Moolenaar     char_u	*cmd,
226585a2002aSBram Moolenaar     char_u	*infile,	// optional input file name
226685a2002aSBram Moolenaar     int		flags,		// can be SHELL_SILENT
22679b57814dSBram Moolenaar     int		*ret_len)
2268071d4279SBram Moolenaar {
2269071d4279SBram Moolenaar     char_u	*tempname;
2270071d4279SBram Moolenaar     char_u	*command;
2271071d4279SBram Moolenaar     char_u	*buffer = NULL;
2272071d4279SBram Moolenaar     int		len;
2273071d4279SBram Moolenaar     int		i = 0;
2274071d4279SBram Moolenaar     FILE	*fd;
2275071d4279SBram Moolenaar 
2276071d4279SBram Moolenaar     if (check_restricted() || check_secure())
2277071d4279SBram Moolenaar 	return NULL;
2278071d4279SBram Moolenaar 
227985a2002aSBram Moolenaar     // get a name for the temp file
2280e5c421cfSBram Moolenaar     if ((tempname = vim_tempname('o', FALSE)) == NULL)
2281071d4279SBram Moolenaar     {
2282f9e3e09fSBram Moolenaar 	emsg(_(e_notmp));
2283071d4279SBram Moolenaar 	return NULL;
2284071d4279SBram Moolenaar     }
2285071d4279SBram Moolenaar 
228685a2002aSBram Moolenaar     // Add the redirection stuff
2287c0197e28SBram Moolenaar     command = make_filter_cmd(cmd, infile, tempname);
2288071d4279SBram Moolenaar     if (command == NULL)
2289071d4279SBram Moolenaar 	goto done;
2290071d4279SBram Moolenaar 
2291071d4279SBram Moolenaar     /*
2292071d4279SBram Moolenaar      * Call the shell to execute the command (errors are ignored).
2293071d4279SBram Moolenaar      * Don't check timestamps here.
2294071d4279SBram Moolenaar      */
2295071d4279SBram Moolenaar     ++no_check_timestamps;
2296071d4279SBram Moolenaar     call_shell(command, SHELL_DOOUT | SHELL_EXPAND | flags);
2297071d4279SBram Moolenaar     --no_check_timestamps;
2298071d4279SBram Moolenaar 
2299071d4279SBram Moolenaar     vim_free(command);
2300071d4279SBram Moolenaar 
2301071d4279SBram Moolenaar     /*
2302071d4279SBram Moolenaar      * read the names from the file into memory
2303071d4279SBram Moolenaar      */
2304071d4279SBram Moolenaar # ifdef VMS
230585a2002aSBram Moolenaar     // created temporary file is not always readable as binary
2306071d4279SBram Moolenaar     fd = mch_fopen((char *)tempname, "r");
2307071d4279SBram Moolenaar # else
2308071d4279SBram Moolenaar     fd = mch_fopen((char *)tempname, READBIN);
2309071d4279SBram Moolenaar # endif
2310071d4279SBram Moolenaar 
2311071d4279SBram Moolenaar     if (fd == NULL)
2312071d4279SBram Moolenaar     {
2313f9e3e09fSBram Moolenaar 	semsg(_(e_notopen), tempname);
2314071d4279SBram Moolenaar 	goto done;
2315071d4279SBram Moolenaar     }
2316071d4279SBram Moolenaar 
2317071d4279SBram Moolenaar     fseek(fd, 0L, SEEK_END);
231885a2002aSBram Moolenaar     len = ftell(fd);		    // get size of temp file
2319071d4279SBram Moolenaar     fseek(fd, 0L, SEEK_SET);
2320071d4279SBram Moolenaar 
2321071d4279SBram Moolenaar     buffer = alloc(len + 1);
2322071d4279SBram Moolenaar     if (buffer != NULL)
2323071d4279SBram Moolenaar 	i = (int)fread((char *)buffer, (size_t)1, (size_t)len, fd);
2324071d4279SBram Moolenaar     fclose(fd);
2325071d4279SBram Moolenaar     mch_remove(tempname);
2326071d4279SBram Moolenaar     if (buffer == NULL)
2327071d4279SBram Moolenaar 	goto done;
2328071d4279SBram Moolenaar #ifdef VMS
232985a2002aSBram Moolenaar     len = i;	// VMS doesn't give us what we asked for...
2330071d4279SBram Moolenaar #endif
2331071d4279SBram Moolenaar     if (i != len)
2332071d4279SBram Moolenaar     {
2333f9e3e09fSBram Moolenaar 	semsg(_(e_notread), tempname);
2334d23a8236SBram Moolenaar 	VIM_CLEAR(buffer);
2335071d4279SBram Moolenaar     }
233639c29ed5SBram Moolenaar     else if (ret_len == NULL)
2337fb332a2bSBram Moolenaar     {
233885a2002aSBram Moolenaar 	// Change NUL into SOH, otherwise the string is truncated.
2339fb332a2bSBram Moolenaar 	for (i = 0; i < len; ++i)
2340f40f4ab8SBram Moolenaar 	    if (buffer[i] == NUL)
2341f40f4ab8SBram Moolenaar 		buffer[i] = 1;
2342fb332a2bSBram Moolenaar 
234385a2002aSBram Moolenaar 	buffer[len] = NUL;	// make sure the buffer is terminated
2344fb332a2bSBram Moolenaar     }
234539c29ed5SBram Moolenaar     else
234639c29ed5SBram Moolenaar 	*ret_len = len;
2347071d4279SBram Moolenaar 
2348071d4279SBram Moolenaar done:
2349071d4279SBram Moolenaar     vim_free(tempname);
2350071d4279SBram Moolenaar     return buffer;
2351071d4279SBram Moolenaar }
235226262f87SBram Moolenaar 
235326262f87SBram Moolenaar # if defined(FEAT_EVAL) || defined(PROTO)
235426262f87SBram Moolenaar 
2355840d16fdSBram Moolenaar     static void
get_cmd_output_as_rettv(typval_T * argvars,typval_T * rettv,int retlist)235626262f87SBram Moolenaar get_cmd_output_as_rettv(
235726262f87SBram Moolenaar     typval_T	*argvars,
235826262f87SBram Moolenaar     typval_T	*rettv,
235926262f87SBram Moolenaar     int		retlist)
236026262f87SBram Moolenaar {
236126262f87SBram Moolenaar     char_u	*res = NULL;
236226262f87SBram Moolenaar     char_u	*p;
236326262f87SBram Moolenaar     char_u	*infile = NULL;
236426262f87SBram Moolenaar     int		err = FALSE;
236526262f87SBram Moolenaar     FILE	*fd;
236626262f87SBram Moolenaar     list_T	*list = NULL;
236726262f87SBram Moolenaar     int		flags = SHELL_SILENT;
236826262f87SBram Moolenaar 
236926262f87SBram Moolenaar     rettv->v_type = VAR_STRING;
237026262f87SBram Moolenaar     rettv->vval.v_string = NULL;
237126262f87SBram Moolenaar     if (check_restricted() || check_secure())
237226262f87SBram Moolenaar 	goto errret;
237326262f87SBram Moolenaar 
23740ad871dcSYegappan Lakshmanan     if (in_vim9script()
23750ad871dcSYegappan Lakshmanan 	    && (check_for_string_arg(argvars, 0) == FAIL
23767e6a2a64SYegappan Lakshmanan 		|| check_for_opt_string_or_number_or_list_arg(argvars, 1) == FAIL))
23770ad871dcSYegappan Lakshmanan 	return;
23780ad871dcSYegappan Lakshmanan 
237926262f87SBram Moolenaar     if (argvars[1].v_type != VAR_UNKNOWN)
238026262f87SBram Moolenaar     {
238126262f87SBram Moolenaar 	/*
238226262f87SBram Moolenaar 	 * Write the text to a temp file, to be used for input of the shell
238326262f87SBram Moolenaar 	 * command.
238426262f87SBram Moolenaar 	 */
238526262f87SBram Moolenaar 	if ((infile = vim_tempname('i', TRUE)) == NULL)
238626262f87SBram Moolenaar 	{
238726262f87SBram Moolenaar 	    emsg(_(e_notmp));
238826262f87SBram Moolenaar 	    goto errret;
238926262f87SBram Moolenaar 	}
239026262f87SBram Moolenaar 
239126262f87SBram Moolenaar 	fd = mch_fopen((char *)infile, WRITEBIN);
239226262f87SBram Moolenaar 	if (fd == NULL)
239326262f87SBram Moolenaar 	{
239426262f87SBram Moolenaar 	    semsg(_(e_notopen), infile);
239526262f87SBram Moolenaar 	    goto errret;
239626262f87SBram Moolenaar 	}
239726262f87SBram Moolenaar 	if (argvars[1].v_type == VAR_NUMBER)
239826262f87SBram Moolenaar 	{
239926262f87SBram Moolenaar 	    linenr_T	lnum;
240026262f87SBram Moolenaar 	    buf_T	*buf;
240126262f87SBram Moolenaar 
240226262f87SBram Moolenaar 	    buf = buflist_findnr(argvars[1].vval.v_number);
240326262f87SBram Moolenaar 	    if (buf == NULL)
240426262f87SBram Moolenaar 	    {
240526262f87SBram Moolenaar 		semsg(_(e_nobufnr), argvars[1].vval.v_number);
240626262f87SBram Moolenaar 		fclose(fd);
240726262f87SBram Moolenaar 		goto errret;
240826262f87SBram Moolenaar 	    }
240926262f87SBram Moolenaar 
241026262f87SBram Moolenaar 	    for (lnum = 1; lnum <= buf->b_ml.ml_line_count; lnum++)
241126262f87SBram Moolenaar 	    {
241226262f87SBram Moolenaar 		for (p = ml_get_buf(buf, lnum, FALSE); *p != NUL; ++p)
241326262f87SBram Moolenaar 		    if (putc(*p == '\n' ? NUL : *p, fd) == EOF)
241426262f87SBram Moolenaar 		    {
241526262f87SBram Moolenaar 			err = TRUE;
241626262f87SBram Moolenaar 			break;
241726262f87SBram Moolenaar 		    }
241826262f87SBram Moolenaar 		if (putc(NL, fd) == EOF)
241926262f87SBram Moolenaar 		{
242026262f87SBram Moolenaar 		    err = TRUE;
242126262f87SBram Moolenaar 		    break;
242226262f87SBram Moolenaar 		}
242326262f87SBram Moolenaar 	    }
242426262f87SBram Moolenaar 	}
242526262f87SBram Moolenaar 	else if (argvars[1].v_type == VAR_LIST)
242626262f87SBram Moolenaar 	{
242726262f87SBram Moolenaar 	    if (write_list(fd, argvars[1].vval.v_list, TRUE) == FAIL)
242826262f87SBram Moolenaar 		err = TRUE;
242926262f87SBram Moolenaar 	}
243026262f87SBram Moolenaar 	else
243126262f87SBram Moolenaar 	{
243226262f87SBram Moolenaar 	    size_t	len;
243326262f87SBram Moolenaar 	    char_u	buf[NUMBUFLEN];
243426262f87SBram Moolenaar 
243526262f87SBram Moolenaar 	    p = tv_get_string_buf_chk(&argvars[1], buf);
243626262f87SBram Moolenaar 	    if (p == NULL)
243726262f87SBram Moolenaar 	    {
243826262f87SBram Moolenaar 		fclose(fd);
243985a2002aSBram Moolenaar 		goto errret;		// type error; errmsg already given
244026262f87SBram Moolenaar 	    }
244126262f87SBram Moolenaar 	    len = STRLEN(p);
244226262f87SBram Moolenaar 	    if (len > 0 && fwrite(p, len, 1, fd) != 1)
244326262f87SBram Moolenaar 		err = TRUE;
244426262f87SBram Moolenaar 	}
244526262f87SBram Moolenaar 	if (fclose(fd) != 0)
244626262f87SBram Moolenaar 	    err = TRUE;
244726262f87SBram Moolenaar 	if (err)
244826262f87SBram Moolenaar 	{
244926262f87SBram Moolenaar 	    emsg(_("E677: Error writing temp file"));
245026262f87SBram Moolenaar 	    goto errret;
245126262f87SBram Moolenaar 	}
245226262f87SBram Moolenaar     }
245326262f87SBram Moolenaar 
245485a2002aSBram Moolenaar     // Omit SHELL_COOKED when invoked with ":silent".  Avoids that the shell
245585a2002aSBram Moolenaar     // echoes typeahead, that messes up the display.
245626262f87SBram Moolenaar     if (!msg_silent)
245726262f87SBram Moolenaar 	flags += SHELL_COOKED;
245826262f87SBram Moolenaar 
245926262f87SBram Moolenaar     if (retlist)
246026262f87SBram Moolenaar     {
246126262f87SBram Moolenaar 	int		len;
246226262f87SBram Moolenaar 	listitem_T	*li;
246326262f87SBram Moolenaar 	char_u		*s = NULL;
246426262f87SBram Moolenaar 	char_u		*start;
246526262f87SBram Moolenaar 	char_u		*end;
246626262f87SBram Moolenaar 	int		i;
246726262f87SBram Moolenaar 
246826262f87SBram Moolenaar 	res = get_cmd_output(tv_get_string(&argvars[0]), infile, flags, &len);
246926262f87SBram Moolenaar 	if (res == NULL)
247026262f87SBram Moolenaar 	    goto errret;
247126262f87SBram Moolenaar 
247226262f87SBram Moolenaar 	list = list_alloc();
247326262f87SBram Moolenaar 	if (list == NULL)
247426262f87SBram Moolenaar 	    goto errret;
247526262f87SBram Moolenaar 
247626262f87SBram Moolenaar 	for (i = 0; i < len; ++i)
247726262f87SBram Moolenaar 	{
247826262f87SBram Moolenaar 	    start = res + i;
247926262f87SBram Moolenaar 	    while (i < len && res[i] != NL)
248026262f87SBram Moolenaar 		++i;
248126262f87SBram Moolenaar 	    end = res + i;
248226262f87SBram Moolenaar 
248326262f87SBram Moolenaar 	    s = alloc(end - start + 1);
248426262f87SBram Moolenaar 	    if (s == NULL)
248526262f87SBram Moolenaar 		goto errret;
248626262f87SBram Moolenaar 
248726262f87SBram Moolenaar 	    for (p = s; start < end; ++p, ++start)
248826262f87SBram Moolenaar 		*p = *start == NUL ? NL : *start;
248926262f87SBram Moolenaar 	    *p = NUL;
249026262f87SBram Moolenaar 
249126262f87SBram Moolenaar 	    li = listitem_alloc();
249226262f87SBram Moolenaar 	    if (li == NULL)
249326262f87SBram Moolenaar 	    {
249426262f87SBram Moolenaar 		vim_free(s);
249526262f87SBram Moolenaar 		goto errret;
249626262f87SBram Moolenaar 	    }
249726262f87SBram Moolenaar 	    li->li_tv.v_type = VAR_STRING;
249826262f87SBram Moolenaar 	    li->li_tv.v_lock = 0;
249926262f87SBram Moolenaar 	    li->li_tv.vval.v_string = s;
250026262f87SBram Moolenaar 	    list_append(list, li);
250126262f87SBram Moolenaar 	}
250226262f87SBram Moolenaar 
250326262f87SBram Moolenaar 	rettv_list_set(rettv, list);
250426262f87SBram Moolenaar 	list = NULL;
250526262f87SBram Moolenaar     }
250626262f87SBram Moolenaar     else
250726262f87SBram Moolenaar     {
250826262f87SBram Moolenaar 	res = get_cmd_output(tv_get_string(&argvars[0]), infile, flags, NULL);
250926262f87SBram Moolenaar #ifdef USE_CRNL
251085a2002aSBram Moolenaar 	// translate <CR><NL> into <NL>
251126262f87SBram Moolenaar 	if (res != NULL)
251226262f87SBram Moolenaar 	{
251326262f87SBram Moolenaar 	    char_u	*s, *d;
251426262f87SBram Moolenaar 
251526262f87SBram Moolenaar 	    d = res;
251626262f87SBram Moolenaar 	    for (s = res; *s; ++s)
251726262f87SBram Moolenaar 	    {
251826262f87SBram Moolenaar 		if (s[0] == CAR && s[1] == NL)
251926262f87SBram Moolenaar 		    ++s;
252026262f87SBram Moolenaar 		*d++ = *s;
252126262f87SBram Moolenaar 	    }
252226262f87SBram Moolenaar 	    *d = NUL;
252326262f87SBram Moolenaar 	}
2524071d4279SBram Moolenaar #endif
252526262f87SBram Moolenaar 	rettv->vval.v_string = res;
252626262f87SBram Moolenaar 	res = NULL;
252726262f87SBram Moolenaar     }
252826262f87SBram Moolenaar 
252926262f87SBram Moolenaar errret:
253026262f87SBram Moolenaar     if (infile != NULL)
253126262f87SBram Moolenaar     {
253226262f87SBram Moolenaar 	mch_remove(infile);
253326262f87SBram Moolenaar 	vim_free(infile);
253426262f87SBram Moolenaar     }
253526262f87SBram Moolenaar     if (res != NULL)
253626262f87SBram Moolenaar 	vim_free(res);
253726262f87SBram Moolenaar     if (list != NULL)
253826262f87SBram Moolenaar 	list_free(list);
253926262f87SBram Moolenaar }
2540071d4279SBram Moolenaar 
2541071d4279SBram Moolenaar /*
254226262f87SBram Moolenaar  * "system()" function
2543071d4279SBram Moolenaar  */
2544071d4279SBram Moolenaar     void
f_system(typval_T * argvars,typval_T * rettv)254526262f87SBram Moolenaar f_system(typval_T *argvars, typval_T *rettv)
2546071d4279SBram Moolenaar {
254726262f87SBram Moolenaar     get_cmd_output_as_rettv(argvars, rettv, FALSE);
2548071d4279SBram Moolenaar }
2549071d4279SBram Moolenaar 
2550071d4279SBram Moolenaar /*
255126262f87SBram Moolenaar  * "systemlist()" function
255226262f87SBram Moolenaar  */
255326262f87SBram Moolenaar     void
f_systemlist(typval_T * argvars,typval_T * rettv)255426262f87SBram Moolenaar f_systemlist(typval_T *argvars, typval_T *rettv)
255526262f87SBram Moolenaar {
255626262f87SBram Moolenaar     get_cmd_output_as_rettv(argvars, rettv, TRUE);
255726262f87SBram Moolenaar }
255826262f87SBram Moolenaar # endif // FEAT_EVAL
255926262f87SBram Moolenaar 
256026262f87SBram Moolenaar #endif
256126262f87SBram Moolenaar 
256226262f87SBram Moolenaar /*
2563a9dc3757SBram Moolenaar  * Return TRUE when need to go to Insert mode because of 'insertmode'.
2564071d4279SBram Moolenaar  * Don't do this when still processing a command or a mapping.
2565071d4279SBram Moolenaar  * Don't do this when inside a ":normal" command.
2566071d4279SBram Moolenaar  */
2567071d4279SBram Moolenaar     int
goto_im(void)25689b57814dSBram Moolenaar goto_im(void)
2569071d4279SBram Moolenaar {
2570071d4279SBram Moolenaar     return (p_im && stuff_empty() && typebuf_typed());
257175a8d74cSBram Moolenaar }
257275a8d74cSBram Moolenaar 
257375a8d74cSBram Moolenaar /*
2574050fe7ebSBram Moolenaar  * Returns the isolated name of the shell in allocated memory:
257575a8d74cSBram Moolenaar  * - Skip beyond any path.  E.g., "/usr/bin/csh -f" -> "csh -f".
257675a8d74cSBram Moolenaar  * - Remove any argument.  E.g., "csh -f" -> "csh".
257775a8d74cSBram Moolenaar  * But don't allow a space in the path, so that this works:
257875a8d74cSBram Moolenaar  *   "/usr/bin/csh --rcfile ~/.cshrc"
257975a8d74cSBram Moolenaar  * But don't do that for Windows, it's common to have a space in the path.
258028ee892aSBram Moolenaar  * Returns NULL when out of memory.
258175a8d74cSBram Moolenaar  */
258275a8d74cSBram Moolenaar     char_u *
get_isolated_shell_name(void)25839b57814dSBram Moolenaar get_isolated_shell_name(void)
258475a8d74cSBram Moolenaar {
258575a8d74cSBram Moolenaar     char_u *p;
258675a8d74cSBram Moolenaar 
25874f97475dSBram Moolenaar #ifdef MSWIN
258875a8d74cSBram Moolenaar     p = gettail(p_sh);
258971ccd03eSBram Moolenaar     p = vim_strnsave(p, skiptowhite(p) - p);
259075a8d74cSBram Moolenaar #else
259175a8d74cSBram Moolenaar     p = skiptowhite(p_sh);
259275a8d74cSBram Moolenaar     if (*p == NUL)
259375a8d74cSBram Moolenaar     {
259485a2002aSBram Moolenaar 	// No white space, use the tail.
259575a8d74cSBram Moolenaar 	p = vim_strsave(gettail(p_sh));
259675a8d74cSBram Moolenaar     }
259775a8d74cSBram Moolenaar     else
259875a8d74cSBram Moolenaar     {
259975a8d74cSBram Moolenaar 	char_u  *p1, *p2;
260075a8d74cSBram Moolenaar 
260185a2002aSBram Moolenaar 	// Find the last path separator before the space.
260275a8d74cSBram Moolenaar 	p1 = p_sh;
260391acfffcSBram Moolenaar 	for (p2 = p_sh; p2 < p; MB_PTR_ADV(p2))
260475a8d74cSBram Moolenaar 	    if (vim_ispathsep(*p2))
260575a8d74cSBram Moolenaar 		p1 = p2 + 1;
260671ccd03eSBram Moolenaar 	p = vim_strnsave(p1, p - p1);
260775a8d74cSBram Moolenaar     }
260875a8d74cSBram Moolenaar #endif
260975a8d74cSBram Moolenaar     return p;
2610071d4279SBram Moolenaar }
26115fd0f505SBram Moolenaar 
26125fd0f505SBram Moolenaar /*
26135fd0f505SBram Moolenaar  * Check if the "://" of a URL is at the pointer, return URL_SLASH.
26145fd0f505SBram Moolenaar  * Also check for ":\\", which MS Internet Explorer accepts, return
26155fd0f505SBram Moolenaar  * URL_BACKSLASH.
26165fd0f505SBram Moolenaar  */
26175fd0f505SBram Moolenaar     int
path_is_url(char_u * p)26185fd0f505SBram Moolenaar path_is_url(char_u *p)
26195fd0f505SBram Moolenaar {
26205fd0f505SBram Moolenaar     if (STRNCMP(p, "://", (size_t)3) == 0)
26215fd0f505SBram Moolenaar 	return URL_SLASH;
26225fd0f505SBram Moolenaar     else if (STRNCMP(p, ":\\\\", (size_t)3) == 0)
26235fd0f505SBram Moolenaar 	return URL_BACKSLASH;
26245fd0f505SBram Moolenaar     return 0;
26255fd0f505SBram Moolenaar }
26265fd0f505SBram Moolenaar 
26275fd0f505SBram Moolenaar /*
26287b7a118eSTsuyoshi CHO  * Check if "fname" starts with "name://" or "name:\\".
26297b7a118eSTsuyoshi CHO  * Return URL_SLASH for "name://", URL_BACKSLASH for "name:\\".
26305fd0f505SBram Moolenaar  * Return zero otherwise.
26315fd0f505SBram Moolenaar  */
26325fd0f505SBram Moolenaar     int
path_with_url(char_u * fname)26335fd0f505SBram Moolenaar path_with_url(char_u *fname)
26345fd0f505SBram Moolenaar {
26355fd0f505SBram Moolenaar     char_u *p;
26365fd0f505SBram Moolenaar 
26377b7a118eSTsuyoshi CHO     // We accept alphabetic characters and a dash in scheme part.
26387b7a118eSTsuyoshi CHO     // RFC 3986 allows for more, but it increases the risk of matching
26397b7a118eSTsuyoshi CHO     // non-URL text.
26407b7a118eSTsuyoshi CHO 
26417b7a118eSTsuyoshi CHO     // first character must be alpha
26427b7a118eSTsuyoshi CHO     if (!isalpha(*fname))
26437b7a118eSTsuyoshi CHO 	return 0;
26447b7a118eSTsuyoshi CHO 
26457b7a118eSTsuyoshi CHO     // check body: alpha or dash
264694e7d345Sitchyny     for (p = fname + 1; (isalpha(*p) || (*p == '-')); ++p)
26475fd0f505SBram Moolenaar 	;
26487b7a118eSTsuyoshi CHO 
26497b7a118eSTsuyoshi CHO     // check last char is not a dash
26507b7a118eSTsuyoshi CHO     if (p[-1] == '-')
26517b7a118eSTsuyoshi CHO 	return 0;
26527b7a118eSTsuyoshi CHO 
26537b7a118eSTsuyoshi CHO     // "://" or ":\\" must follow
26545fd0f505SBram Moolenaar     return path_is_url(p);
26555fd0f505SBram Moolenaar }
2656f1e8876fS=?UTF-8?q?Magnus=20Gro=C3=9F?= 
2657*3075a455SBram Moolenaar #if defined(FEAT_EVAL) || defined(PROTO)
2658*3075a455SBram Moolenaar /*
2659*3075a455SBram Moolenaar  * Return the dictionary of v:event.
2660*3075a455SBram Moolenaar  * Save and clear the value in case it already has items.
2661*3075a455SBram Moolenaar  */
2662*3075a455SBram Moolenaar     dict_T *
get_v_event(save_v_event_T * sve)2663*3075a455SBram Moolenaar get_v_event(save_v_event_T *sve)
2664*3075a455SBram Moolenaar {
2665*3075a455SBram Moolenaar     dict_T	*v_event = get_vim_var_dict(VV_EVENT);
2666*3075a455SBram Moolenaar 
2667*3075a455SBram Moolenaar     if (v_event->dv_hashtab.ht_used > 0)
2668*3075a455SBram Moolenaar     {
2669*3075a455SBram Moolenaar 	// recursive use of v:event, save, make empty and restore later
2670*3075a455SBram Moolenaar 	sve->sve_did_save = TRUE;
2671*3075a455SBram Moolenaar 	sve->sve_hashtab = v_event->dv_hashtab;
2672*3075a455SBram Moolenaar 	hash_init(&v_event->dv_hashtab);
2673*3075a455SBram Moolenaar     }
2674*3075a455SBram Moolenaar     else
2675*3075a455SBram Moolenaar 	sve->sve_did_save = FALSE;
2676*3075a455SBram Moolenaar     return v_event;
2677*3075a455SBram Moolenaar }
2678*3075a455SBram Moolenaar 
2679*3075a455SBram Moolenaar     void
restore_v_event(dict_T * v_event,save_v_event_T * sve)2680*3075a455SBram Moolenaar restore_v_event(dict_T *v_event, save_v_event_T *sve)
2681*3075a455SBram Moolenaar {
2682*3075a455SBram Moolenaar     dict_free_contents(v_event);
2683*3075a455SBram Moolenaar     if (sve->sve_did_save)
2684*3075a455SBram Moolenaar 	v_event->dv_hashtab = sve->sve_hashtab;
2685*3075a455SBram Moolenaar     else
2686*3075a455SBram Moolenaar 	hash_init(&v_event->dv_hashtab);
2687*3075a455SBram Moolenaar }
2688*3075a455SBram Moolenaar #endif
2689*3075a455SBram Moolenaar 
2690f1e8876fS=?UTF-8?q?Magnus=20Gro=C3=9F?= /*
2691f1e8876fS=?UTF-8?q?Magnus=20Gro=C3=9F?=  * Fires a ModeChanged autocmd
2692f1e8876fS=?UTF-8?q?Magnus=20Gro=C3=9F?=  */
2693f1e8876fS=?UTF-8?q?Magnus=20Gro=C3=9F?=     void
trigger_modechanged()2694f1e8876fS=?UTF-8?q?Magnus=20Gro=C3=9F?= trigger_modechanged()
2695f1e8876fS=?UTF-8?q?Magnus=20Gro=C3=9F?= {
2696*3075a455SBram Moolenaar #ifdef FEAT_EVAL
2697f1e8876fS=?UTF-8?q?Magnus=20Gro=C3=9F?=     dict_T	    *v_event;
2698f1e8876fS=?UTF-8?q?Magnus=20Gro=C3=9F?=     typval_T	    rettv;
2699d85931e6SBram Moolenaar     typval_T	    tv[2];
2700f1e8876fS=?UTF-8?q?Magnus=20Gro=C3=9F?=     char_u	    *pat_pre;
2701f1e8876fS=?UTF-8?q?Magnus=20Gro=C3=9F?=     char_u	    *pat;
2702*3075a455SBram Moolenaar     save_v_event_T  save_v_event;
2703f1e8876fS=?UTF-8?q?Magnus=20Gro=C3=9F?= 
2704f1e8876fS=?UTF-8?q?Magnus=20Gro=C3=9F?=     if (!has_modechanged())
2705f1e8876fS=?UTF-8?q?Magnus=20Gro=C3=9F?= 	return;
2706f1e8876fS=?UTF-8?q?Magnus=20Gro=C3=9F?= 
2707d85931e6SBram Moolenaar     tv[0].v_type = VAR_NUMBER;
2708d85931e6SBram Moolenaar     tv[0].vval.v_number = 1;	    // get full mode
2709d85931e6SBram Moolenaar     tv[1].v_type = VAR_UNKNOWN;
2710d85931e6SBram Moolenaar     f_mode(tv, &rettv);
271125def2c8S=?UTF-8?q?Magnus=20Gro=C3=9F?=     if (STRCMP(rettv.vval.v_string, last_mode) == 0)
271225def2c8S=?UTF-8?q?Magnus=20Gro=C3=9F?=     {
271325def2c8S=?UTF-8?q?Magnus=20Gro=C3=9F?= 	vim_free(rettv.vval.v_string);
271425def2c8S=?UTF-8?q?Magnus=20Gro=C3=9F?= 	return;
271525def2c8S=?UTF-8?q?Magnus=20Gro=C3=9F?=     }
271625def2c8S=?UTF-8?q?Magnus=20Gro=C3=9F?= 
2717*3075a455SBram Moolenaar     v_event = get_v_event(&save_v_event);
2718f1e8876fS=?UTF-8?q?Magnus=20Gro=C3=9F?=     (void)dict_add_string(v_event, "new_mode", rettv.vval.v_string);
2719f1e8876fS=?UTF-8?q?Magnus=20Gro=C3=9F?=     (void)dict_add_string(v_event, "old_mode", last_mode);
2720f1e8876fS=?UTF-8?q?Magnus=20Gro=C3=9F?=     dict_set_items_ro(v_event);
2721f1e8876fS=?UTF-8?q?Magnus=20Gro=C3=9F?= 
2722f1e8876fS=?UTF-8?q?Magnus=20Gro=C3=9F?=     // concatenate modes in format "old_mode:new_mode"
2723f1e8876fS=?UTF-8?q?Magnus=20Gro=C3=9F?=     pat_pre = concat_str(last_mode, (char_u*)":");
2724f1e8876fS=?UTF-8?q?Magnus=20Gro=C3=9F?=     pat = concat_str(pat_pre, rettv.vval.v_string);
2725f1e8876fS=?UTF-8?q?Magnus=20Gro=C3=9F?=     vim_free(pat_pre);
2726f1e8876fS=?UTF-8?q?Magnus=20Gro=C3=9F?= 
2727f1e8876fS=?UTF-8?q?Magnus=20Gro=C3=9F?=     apply_autocmds(EVENT_MODECHANGED, pat, NULL, FALSE, curbuf);
2728f1e8876fS=?UTF-8?q?Magnus=20Gro=C3=9F?=     STRCPY(last_mode, rettv.vval.v_string);
2729f1e8876fS=?UTF-8?q?Magnus=20Gro=C3=9F?= 
2730f1e8876fS=?UTF-8?q?Magnus=20Gro=C3=9F?=     vim_free(pat);
2731*3075a455SBram Moolenaar     restore_v_event(v_event, &save_v_event);
273225def2c8S=?UTF-8?q?Magnus=20Gro=C3=9F?=     vim_free(rettv.vval.v_string);
2733f1e8876fS=?UTF-8?q?Magnus=20Gro=C3=9F?= #endif
2734f1e8876fS=?UTF-8?q?Magnus=20Gro=C3=9F?= }
2735