xref: /vim-8.2.3635/src/strings.c (revision d85fccdf)
1a2438132SYegappan Lakshmanan /* vi:set ts=8 sts=4 sw=4 noet:
2a2438132SYegappan Lakshmanan  *
3a2438132SYegappan Lakshmanan  * VIM - Vi IMproved	by Bram Moolenaar
4a2438132SYegappan Lakshmanan  *
5a2438132SYegappan Lakshmanan  * Do ":help uganda"  in Vim to read copying and usage conditions.
6a2438132SYegappan Lakshmanan  * Do ":help credits" in Vim to see a list of people who contributed.
7a2438132SYegappan Lakshmanan  * See README.txt for an overview of the Vim source code.
8a2438132SYegappan Lakshmanan  */
9a2438132SYegappan Lakshmanan 
10a2438132SYegappan Lakshmanan /*
11a2438132SYegappan Lakshmanan  * strings.c: string manipulation functions
12a2438132SYegappan Lakshmanan  */
13a2438132SYegappan Lakshmanan 
148ee52affSYegappan Lakshmanan #define USING_FLOAT_STUFF
15a2438132SYegappan Lakshmanan #include "vim.h"
16a2438132SYegappan Lakshmanan 
17a2438132SYegappan Lakshmanan /*
18a2438132SYegappan Lakshmanan  * Copy "string" into newly allocated memory.
19a2438132SYegappan Lakshmanan  */
20a2438132SYegappan Lakshmanan     char_u *
vim_strsave(char_u * string)21a2438132SYegappan Lakshmanan vim_strsave(char_u *string)
22a2438132SYegappan Lakshmanan {
23a2438132SYegappan Lakshmanan     char_u	*p;
24a2438132SYegappan Lakshmanan     size_t	len;
25a2438132SYegappan Lakshmanan 
26a2438132SYegappan Lakshmanan     len = STRLEN(string) + 1;
27a2438132SYegappan Lakshmanan     p = alloc(len);
28a2438132SYegappan Lakshmanan     if (p != NULL)
29a2438132SYegappan Lakshmanan 	mch_memmove(p, string, len);
30a2438132SYegappan Lakshmanan     return p;
31a2438132SYegappan Lakshmanan }
32a2438132SYegappan Lakshmanan 
33a2438132SYegappan Lakshmanan /*
34a2438132SYegappan Lakshmanan  * Copy up to "len" bytes of "string" into newly allocated memory and
35a2438132SYegappan Lakshmanan  * terminate with a NUL.
36a2438132SYegappan Lakshmanan  * The allocated memory always has size "len + 1", also when "string" is
37a2438132SYegappan Lakshmanan  * shorter.
38a2438132SYegappan Lakshmanan  */
39a2438132SYegappan Lakshmanan     char_u *
vim_strnsave(char_u * string,size_t len)40a2438132SYegappan Lakshmanan vim_strnsave(char_u *string, size_t len)
41a2438132SYegappan Lakshmanan {
42a2438132SYegappan Lakshmanan     char_u	*p;
43a2438132SYegappan Lakshmanan 
44a2438132SYegappan Lakshmanan     p = alloc(len + 1);
45a2438132SYegappan Lakshmanan     if (p != NULL)
46a2438132SYegappan Lakshmanan     {
47a2438132SYegappan Lakshmanan 	STRNCPY(p, string, len);
48a2438132SYegappan Lakshmanan 	p[len] = NUL;
49a2438132SYegappan Lakshmanan     }
50a2438132SYegappan Lakshmanan     return p;
51a2438132SYegappan Lakshmanan }
52a2438132SYegappan Lakshmanan 
53a2438132SYegappan Lakshmanan /*
54a2438132SYegappan Lakshmanan  * Same as vim_strsave(), but any characters found in esc_chars are preceded
55a2438132SYegappan Lakshmanan  * by a backslash.
56a2438132SYegappan Lakshmanan  */
57a2438132SYegappan Lakshmanan     char_u *
vim_strsave_escaped(char_u * string,char_u * esc_chars)58a2438132SYegappan Lakshmanan vim_strsave_escaped(char_u *string, char_u *esc_chars)
59a2438132SYegappan Lakshmanan {
60a2438132SYegappan Lakshmanan     return vim_strsave_escaped_ext(string, esc_chars, '\\', FALSE);
61a2438132SYegappan Lakshmanan }
62a2438132SYegappan Lakshmanan 
63a2438132SYegappan Lakshmanan /*
64a2438132SYegappan Lakshmanan  * Same as vim_strsave_escaped(), but when "bsl" is TRUE also escape
65a2438132SYegappan Lakshmanan  * characters where rem_backslash() would remove the backslash.
66a2438132SYegappan Lakshmanan  * Escape the characters with "cc".
67a2438132SYegappan Lakshmanan  */
68a2438132SYegappan Lakshmanan     char_u *
vim_strsave_escaped_ext(char_u * string,char_u * esc_chars,int cc,int bsl)69a2438132SYegappan Lakshmanan vim_strsave_escaped_ext(
70a2438132SYegappan Lakshmanan     char_u	*string,
71a2438132SYegappan Lakshmanan     char_u	*esc_chars,
72a2438132SYegappan Lakshmanan     int		cc,
73a2438132SYegappan Lakshmanan     int		bsl)
74a2438132SYegappan Lakshmanan {
75a2438132SYegappan Lakshmanan     char_u	*p;
76a2438132SYegappan Lakshmanan     char_u	*p2;
77a2438132SYegappan Lakshmanan     char_u	*escaped_string;
78a2438132SYegappan Lakshmanan     unsigned	length;
79a2438132SYegappan Lakshmanan     int		l;
80a2438132SYegappan Lakshmanan 
818ee52affSYegappan Lakshmanan     // First count the number of backslashes required.
828ee52affSYegappan Lakshmanan     // Then allocate the memory and insert them.
83a2438132SYegappan Lakshmanan     length = 1;				// count the trailing NUL
84a2438132SYegappan Lakshmanan     for (p = string; *p; p++)
85a2438132SYegappan Lakshmanan     {
86a2438132SYegappan Lakshmanan 	if (has_mbyte && (l = (*mb_ptr2len)(p)) > 1)
87a2438132SYegappan Lakshmanan 	{
88a2438132SYegappan Lakshmanan 	    length += l;		// count a multibyte char
89a2438132SYegappan Lakshmanan 	    p += l - 1;
90a2438132SYegappan Lakshmanan 	    continue;
91a2438132SYegappan Lakshmanan 	}
92a2438132SYegappan Lakshmanan 	if (vim_strchr(esc_chars, *p) != NULL || (bsl && rem_backslash(p)))
93a2438132SYegappan Lakshmanan 	    ++length;			// count a backslash
94a2438132SYegappan Lakshmanan 	++length;			// count an ordinary char
95a2438132SYegappan Lakshmanan     }
96a2438132SYegappan Lakshmanan     escaped_string = alloc(length);
97a2438132SYegappan Lakshmanan     if (escaped_string != NULL)
98a2438132SYegappan Lakshmanan     {
99a2438132SYegappan Lakshmanan 	p2 = escaped_string;
100a2438132SYegappan Lakshmanan 	for (p = string; *p; p++)
101a2438132SYegappan Lakshmanan 	{
102a2438132SYegappan Lakshmanan 	    if (has_mbyte && (l = (*mb_ptr2len)(p)) > 1)
103a2438132SYegappan Lakshmanan 	    {
104a2438132SYegappan Lakshmanan 		mch_memmove(p2, p, (size_t)l);
105a2438132SYegappan Lakshmanan 		p2 += l;
106a2438132SYegappan Lakshmanan 		p += l - 1;		// skip multibyte char
107a2438132SYegappan Lakshmanan 		continue;
108a2438132SYegappan Lakshmanan 	    }
109a2438132SYegappan Lakshmanan 	    if (vim_strchr(esc_chars, *p) != NULL || (bsl && rem_backslash(p)))
110a2438132SYegappan Lakshmanan 		*p2++ = cc;
111a2438132SYegappan Lakshmanan 	    *p2++ = *p;
112a2438132SYegappan Lakshmanan 	}
113a2438132SYegappan Lakshmanan 	*p2 = NUL;
114a2438132SYegappan Lakshmanan     }
115a2438132SYegappan Lakshmanan     return escaped_string;
116a2438132SYegappan Lakshmanan }
117a2438132SYegappan Lakshmanan 
118a2438132SYegappan Lakshmanan /*
119a2438132SYegappan Lakshmanan  * Return TRUE when 'shell' has "csh" in the tail.
120a2438132SYegappan Lakshmanan  */
121a2438132SYegappan Lakshmanan     int
csh_like_shell(void)122a2438132SYegappan Lakshmanan csh_like_shell(void)
123a2438132SYegappan Lakshmanan {
124a2438132SYegappan Lakshmanan     return (strstr((char *)gettail(p_sh), "csh") != NULL);
125a2438132SYegappan Lakshmanan }
126a2438132SYegappan Lakshmanan 
127a2438132SYegappan Lakshmanan /*
1286e823511SJason Cox  * Return TRUE when 'shell' has "fish" in the tail.
1296e823511SJason Cox  */
130de05ae71SDominique Pelle     static int
fish_like_shell(void)1316e823511SJason Cox fish_like_shell(void)
1326e823511SJason Cox {
1336e823511SJason Cox     return (strstr((char *)gettail(p_sh), "fish") != NULL);
1346e823511SJason Cox }
1356e823511SJason Cox 
1366e823511SJason Cox /*
137a2438132SYegappan Lakshmanan  * Escape "string" for use as a shell argument with system().
138a2438132SYegappan Lakshmanan  * This uses single quotes, except when we know we need to use double quotes
139a2438132SYegappan Lakshmanan  * (MS-DOS and MS-Windows not using PowerShell and without 'shellslash' set).
140a2438132SYegappan Lakshmanan  * PowerShell also uses a novel escaping for enclosed single quotes - double
141a2438132SYegappan Lakshmanan  * them up.
142a2438132SYegappan Lakshmanan  * Escape a newline, depending on the 'shell' option.
143a2438132SYegappan Lakshmanan  * When "do_special" is TRUE also replace "!", "%", "#" and things starting
144a2438132SYegappan Lakshmanan  * with "<" like "<cfile>".
145a2438132SYegappan Lakshmanan  * When "do_newline" is FALSE do not escape newline unless it is csh shell.
146a2438132SYegappan Lakshmanan  * Returns the result in allocated memory, NULL if we have run out.
147a2438132SYegappan Lakshmanan  */
148a2438132SYegappan Lakshmanan     char_u *
vim_strsave_shellescape(char_u * string,int do_special,int do_newline)149a2438132SYegappan Lakshmanan vim_strsave_shellescape(char_u *string, int do_special, int do_newline)
150a2438132SYegappan Lakshmanan {
151a2438132SYegappan Lakshmanan     unsigned	length;
152a2438132SYegappan Lakshmanan     char_u	*p;
153a2438132SYegappan Lakshmanan     char_u	*d;
154a2438132SYegappan Lakshmanan     char_u	*escaped_string;
155a2438132SYegappan Lakshmanan     int		l;
156a2438132SYegappan Lakshmanan     int		csh_like;
1576e823511SJason Cox     int		fish_like;
158a2438132SYegappan Lakshmanan     char_u	*shname;
159a2438132SYegappan Lakshmanan     int		powershell;
160a2438132SYegappan Lakshmanan # ifdef MSWIN
161a2438132SYegappan Lakshmanan     int		double_quotes;
162a2438132SYegappan Lakshmanan # endif
163a2438132SYegappan Lakshmanan 
164a2438132SYegappan Lakshmanan     // Only csh and similar shells expand '!' within single quotes.  For sh and
165a2438132SYegappan Lakshmanan     // the like we must not put a backslash before it, it will be taken
166a2438132SYegappan Lakshmanan     // literally.  If do_special is set the '!' will be escaped twice.
167a2438132SYegappan Lakshmanan     // Csh also needs to have "\n" escaped twice when do_special is set.
168a2438132SYegappan Lakshmanan     csh_like = csh_like_shell();
169a2438132SYegappan Lakshmanan 
1706e823511SJason Cox     // Fish shell uses '\' as an escape character within single quotes, so '\'
1716e823511SJason Cox     // itself must be escaped to get a literal '\'.
1726e823511SJason Cox     fish_like = fish_like_shell();
1736e823511SJason Cox 
174a2438132SYegappan Lakshmanan     // PowerShell uses it's own version for quoting single quotes
175a2438132SYegappan Lakshmanan     shname = gettail(p_sh);
176a2438132SYegappan Lakshmanan     powershell = strstr((char *)shname, "pwsh") != NULL;
177a2438132SYegappan Lakshmanan # ifdef MSWIN
178a2438132SYegappan Lakshmanan     powershell = powershell || strstr((char *)shname, "powershell") != NULL;
179a2438132SYegappan Lakshmanan     // PowerShell only accepts single quotes so override shellslash.
180a2438132SYegappan Lakshmanan     double_quotes = !powershell && !p_ssl;
181a2438132SYegappan Lakshmanan # endif
182a2438132SYegappan Lakshmanan 
183a2438132SYegappan Lakshmanan     // First count the number of extra bytes required.
184a2438132SYegappan Lakshmanan     length = (unsigned)STRLEN(string) + 3;  // two quotes and a trailing NUL
185a2438132SYegappan Lakshmanan     for (p = string; *p != NUL; MB_PTR_ADV(p))
186a2438132SYegappan Lakshmanan     {
187a2438132SYegappan Lakshmanan # ifdef MSWIN
188a2438132SYegappan Lakshmanan 	if (double_quotes)
189a2438132SYegappan Lakshmanan 	{
190a2438132SYegappan Lakshmanan 	    if (*p == '"')
191a2438132SYegappan Lakshmanan 		++length;		// " -> ""
192a2438132SYegappan Lakshmanan 	}
193a2438132SYegappan Lakshmanan 	else
194a2438132SYegappan Lakshmanan # endif
195a2438132SYegappan Lakshmanan 	if (*p == '\'')
196a2438132SYegappan Lakshmanan 	{
197a2438132SYegappan Lakshmanan 	    if (powershell)
198a2438132SYegappan Lakshmanan 		length +=2;		// ' => ''
199a2438132SYegappan Lakshmanan 	    else
200a2438132SYegappan Lakshmanan 		length += 3;		// ' => '\''
201a2438132SYegappan Lakshmanan 	}
202a2438132SYegappan Lakshmanan 	if ((*p == '\n' && (csh_like || do_newline))
203a2438132SYegappan Lakshmanan 		|| (*p == '!' && (csh_like || do_special)))
204a2438132SYegappan Lakshmanan 	{
205a2438132SYegappan Lakshmanan 	    ++length;			// insert backslash
206a2438132SYegappan Lakshmanan 	    if (csh_like && do_special)
207a2438132SYegappan Lakshmanan 		++length;		// insert backslash
208a2438132SYegappan Lakshmanan 	}
209a2438132SYegappan Lakshmanan 	if (do_special && find_cmdline_var(p, &l) >= 0)
210a2438132SYegappan Lakshmanan 	{
211a2438132SYegappan Lakshmanan 	    ++length;			// insert backslash
212a2438132SYegappan Lakshmanan 	    p += l - 1;
213a2438132SYegappan Lakshmanan 	}
2146e823511SJason Cox 	if (*p == '\\' && fish_like)
2156e823511SJason Cox 	    ++length;			// insert backslash
216a2438132SYegappan Lakshmanan     }
217a2438132SYegappan Lakshmanan 
218a2438132SYegappan Lakshmanan     // Allocate memory for the result and fill it.
219a2438132SYegappan Lakshmanan     escaped_string = alloc(length);
220a2438132SYegappan Lakshmanan     if (escaped_string != NULL)
221a2438132SYegappan Lakshmanan     {
222a2438132SYegappan Lakshmanan 	d = escaped_string;
223a2438132SYegappan Lakshmanan 
224a2438132SYegappan Lakshmanan 	// add opening quote
225a2438132SYegappan Lakshmanan # ifdef MSWIN
226a2438132SYegappan Lakshmanan 	if (double_quotes)
227a2438132SYegappan Lakshmanan 	    *d++ = '"';
228a2438132SYegappan Lakshmanan 	else
229a2438132SYegappan Lakshmanan # endif
230a2438132SYegappan Lakshmanan 	    *d++ = '\'';
231a2438132SYegappan Lakshmanan 
232a2438132SYegappan Lakshmanan 	for (p = string; *p != NUL; )
233a2438132SYegappan Lakshmanan 	{
234a2438132SYegappan Lakshmanan # ifdef MSWIN
235a2438132SYegappan Lakshmanan 	    if (double_quotes)
236a2438132SYegappan Lakshmanan 	    {
237a2438132SYegappan Lakshmanan 		if (*p == '"')
238a2438132SYegappan Lakshmanan 		{
239a2438132SYegappan Lakshmanan 		    *d++ = '"';
240a2438132SYegappan Lakshmanan 		    *d++ = '"';
241a2438132SYegappan Lakshmanan 		    ++p;
242a2438132SYegappan Lakshmanan 		    continue;
243a2438132SYegappan Lakshmanan 		}
244a2438132SYegappan Lakshmanan 	    }
245a2438132SYegappan Lakshmanan 	    else
246a2438132SYegappan Lakshmanan # endif
247a2438132SYegappan Lakshmanan 	    if (*p == '\'')
248a2438132SYegappan Lakshmanan 	    {
249a2438132SYegappan Lakshmanan 		if (powershell)
250a2438132SYegappan Lakshmanan 		{
251a2438132SYegappan Lakshmanan 		    *d++ = '\'';
252a2438132SYegappan Lakshmanan 		    *d++ = '\'';
253a2438132SYegappan Lakshmanan 		}
254a2438132SYegappan Lakshmanan 		else
255a2438132SYegappan Lakshmanan 		{
256a2438132SYegappan Lakshmanan 		    *d++ = '\'';
257a2438132SYegappan Lakshmanan 		    *d++ = '\\';
258a2438132SYegappan Lakshmanan 		    *d++ = '\'';
259a2438132SYegappan Lakshmanan 		    *d++ = '\'';
260a2438132SYegappan Lakshmanan 		}
261a2438132SYegappan Lakshmanan 		++p;
262a2438132SYegappan Lakshmanan 		continue;
263a2438132SYegappan Lakshmanan 	    }
264a2438132SYegappan Lakshmanan 	    if ((*p == '\n' && (csh_like || do_newline))
265a2438132SYegappan Lakshmanan 		    || (*p == '!' && (csh_like || do_special)))
266a2438132SYegappan Lakshmanan 	    {
267a2438132SYegappan Lakshmanan 		*d++ = '\\';
268a2438132SYegappan Lakshmanan 		if (csh_like && do_special)
269a2438132SYegappan Lakshmanan 		    *d++ = '\\';
270a2438132SYegappan Lakshmanan 		*d++ = *p++;
271a2438132SYegappan Lakshmanan 		continue;
272a2438132SYegappan Lakshmanan 	    }
273a2438132SYegappan Lakshmanan 	    if (do_special && find_cmdline_var(p, &l) >= 0)
274a2438132SYegappan Lakshmanan 	    {
275a2438132SYegappan Lakshmanan 		*d++ = '\\';		// insert backslash
276a2438132SYegappan Lakshmanan 		while (--l >= 0)	// copy the var
277a2438132SYegappan Lakshmanan 		    *d++ = *p++;
278a2438132SYegappan Lakshmanan 		continue;
279a2438132SYegappan Lakshmanan 	    }
2806e823511SJason Cox 	    if (*p == '\\' && fish_like)
2816e823511SJason Cox 	    {
2826e823511SJason Cox 		*d++ = '\\';
2836e823511SJason Cox 		*d++ = *p++;
28466315974SBram Moolenaar 		continue;
2856e823511SJason Cox 	    }
286a2438132SYegappan Lakshmanan 
287a2438132SYegappan Lakshmanan 	    MB_COPY_CHAR(p, d);
288a2438132SYegappan Lakshmanan 	}
289a2438132SYegappan Lakshmanan 
290a2438132SYegappan Lakshmanan 	// add terminating quote and finish with a NUL
291a2438132SYegappan Lakshmanan # ifdef MSWIN
292a2438132SYegappan Lakshmanan 	if (double_quotes)
293a2438132SYegappan Lakshmanan 	    *d++ = '"';
294a2438132SYegappan Lakshmanan 	else
295a2438132SYegappan Lakshmanan # endif
296a2438132SYegappan Lakshmanan 	    *d++ = '\'';
297a2438132SYegappan Lakshmanan 	*d = NUL;
298a2438132SYegappan Lakshmanan     }
299a2438132SYegappan Lakshmanan 
300a2438132SYegappan Lakshmanan     return escaped_string;
301a2438132SYegappan Lakshmanan }
302a2438132SYegappan Lakshmanan 
303a2438132SYegappan Lakshmanan /*
304a2438132SYegappan Lakshmanan  * Like vim_strsave(), but make all characters uppercase.
305a2438132SYegappan Lakshmanan  * This uses ASCII lower-to-upper case translation, language independent.
306a2438132SYegappan Lakshmanan  */
307a2438132SYegappan Lakshmanan     char_u *
vim_strsave_up(char_u * string)308a2438132SYegappan Lakshmanan vim_strsave_up(char_u *string)
309a2438132SYegappan Lakshmanan {
310a2438132SYegappan Lakshmanan     char_u *p1;
311a2438132SYegappan Lakshmanan 
312a2438132SYegappan Lakshmanan     p1 = vim_strsave(string);
313a2438132SYegappan Lakshmanan     vim_strup(p1);
314a2438132SYegappan Lakshmanan     return p1;
315a2438132SYegappan Lakshmanan }
316a2438132SYegappan Lakshmanan 
317a2438132SYegappan Lakshmanan /*
318a2438132SYegappan Lakshmanan  * Like vim_strnsave(), but make all characters uppercase.
319a2438132SYegappan Lakshmanan  * This uses ASCII lower-to-upper case translation, language independent.
320a2438132SYegappan Lakshmanan  */
321a2438132SYegappan Lakshmanan     char_u *
vim_strnsave_up(char_u * string,size_t len)322a2438132SYegappan Lakshmanan vim_strnsave_up(char_u *string, size_t len)
323a2438132SYegappan Lakshmanan {
324a2438132SYegappan Lakshmanan     char_u *p1;
325a2438132SYegappan Lakshmanan 
326a2438132SYegappan Lakshmanan     p1 = vim_strnsave(string, len);
327a2438132SYegappan Lakshmanan     vim_strup(p1);
328a2438132SYegappan Lakshmanan     return p1;
329a2438132SYegappan Lakshmanan }
330a2438132SYegappan Lakshmanan 
331a2438132SYegappan Lakshmanan /*
332a2438132SYegappan Lakshmanan  * ASCII lower-to-upper case translation, language independent.
333a2438132SYegappan Lakshmanan  */
334a2438132SYegappan Lakshmanan     void
vim_strup(char_u * p)335a2438132SYegappan Lakshmanan vim_strup(
336a2438132SYegappan Lakshmanan     char_u	*p)
337a2438132SYegappan Lakshmanan {
338a2438132SYegappan Lakshmanan     char_u  *p2;
339a2438132SYegappan Lakshmanan     int	    c;
340a2438132SYegappan Lakshmanan 
341a2438132SYegappan Lakshmanan     if (p != NULL)
342a2438132SYegappan Lakshmanan     {
343a2438132SYegappan Lakshmanan 	p2 = p;
344a2438132SYegappan Lakshmanan 	while ((c = *p2) != NUL)
345a2438132SYegappan Lakshmanan #ifdef EBCDIC
346a2438132SYegappan Lakshmanan 	    *p2++ = isalpha(c) ? toupper(c) : c;
347a2438132SYegappan Lakshmanan #else
348a2438132SYegappan Lakshmanan 	    *p2++ = (c < 'a' || c > 'z') ? c : (c - 0x20);
349a2438132SYegappan Lakshmanan #endif
350a2438132SYegappan Lakshmanan     }
351a2438132SYegappan Lakshmanan }
352a2438132SYegappan Lakshmanan 
353a2438132SYegappan Lakshmanan #if defined(FEAT_EVAL) || defined(FEAT_SPELL) || defined(PROTO)
354a2438132SYegappan Lakshmanan /*
355a2438132SYegappan Lakshmanan  * Make string "s" all upper-case and return it in allocated memory.
356a2438132SYegappan Lakshmanan  * Handles multi-byte characters as well as possible.
357a2438132SYegappan Lakshmanan  * Returns NULL when out of memory.
358a2438132SYegappan Lakshmanan  */
359a2438132SYegappan Lakshmanan     static char_u *
strup_save(char_u * orig)360a2438132SYegappan Lakshmanan strup_save(char_u *orig)
361a2438132SYegappan Lakshmanan {
362a2438132SYegappan Lakshmanan     char_u	*p;
363a2438132SYegappan Lakshmanan     char_u	*res;
364a2438132SYegappan Lakshmanan 
365a2438132SYegappan Lakshmanan     res = p = vim_strsave(orig);
366a2438132SYegappan Lakshmanan 
367a2438132SYegappan Lakshmanan     if (res != NULL)
368a2438132SYegappan Lakshmanan 	while (*p != NUL)
369a2438132SYegappan Lakshmanan 	{
370a2438132SYegappan Lakshmanan 	    int		l;
371a2438132SYegappan Lakshmanan 
372a2438132SYegappan Lakshmanan 	    if (enc_utf8)
373a2438132SYegappan Lakshmanan 	    {
374a2438132SYegappan Lakshmanan 		int	c, uc;
375a2438132SYegappan Lakshmanan 		int	newl;
376a2438132SYegappan Lakshmanan 		char_u	*s;
377a2438132SYegappan Lakshmanan 
378a2438132SYegappan Lakshmanan 		c = utf_ptr2char(p);
379a2438132SYegappan Lakshmanan 		l = utf_ptr2len(p);
380a2438132SYegappan Lakshmanan 		if (c == 0)
381a2438132SYegappan Lakshmanan 		{
382a2438132SYegappan Lakshmanan 		    // overlong sequence, use only the first byte
383a2438132SYegappan Lakshmanan 		    c = *p;
384a2438132SYegappan Lakshmanan 		    l = 1;
385a2438132SYegappan Lakshmanan 		}
386a2438132SYegappan Lakshmanan 		uc = utf_toupper(c);
387a2438132SYegappan Lakshmanan 
388a2438132SYegappan Lakshmanan 		// Reallocate string when byte count changes.  This is rare,
389a2438132SYegappan Lakshmanan 		// thus it's OK to do another malloc()/free().
390a2438132SYegappan Lakshmanan 		newl = utf_char2len(uc);
391a2438132SYegappan Lakshmanan 		if (newl != l)
392a2438132SYegappan Lakshmanan 		{
393a2438132SYegappan Lakshmanan 		    s = alloc(STRLEN(res) + 1 + newl - l);
394a2438132SYegappan Lakshmanan 		    if (s == NULL)
395a2438132SYegappan Lakshmanan 		    {
396a2438132SYegappan Lakshmanan 			vim_free(res);
397a2438132SYegappan Lakshmanan 			return NULL;
398a2438132SYegappan Lakshmanan 		    }
399a2438132SYegappan Lakshmanan 		    mch_memmove(s, res, p - res);
400a2438132SYegappan Lakshmanan 		    STRCPY(s + (p - res) + newl, p + l);
401a2438132SYegappan Lakshmanan 		    p = s + (p - res);
402a2438132SYegappan Lakshmanan 		    vim_free(res);
403a2438132SYegappan Lakshmanan 		    res = s;
404a2438132SYegappan Lakshmanan 		}
405a2438132SYegappan Lakshmanan 
406a2438132SYegappan Lakshmanan 		utf_char2bytes(uc, p);
407a2438132SYegappan Lakshmanan 		p += newl;
408a2438132SYegappan Lakshmanan 	    }
409a2438132SYegappan Lakshmanan 	    else if (has_mbyte && (l = (*mb_ptr2len)(p)) > 1)
410a2438132SYegappan Lakshmanan 		p += l;		// skip multi-byte character
411a2438132SYegappan Lakshmanan 	    else
412a2438132SYegappan Lakshmanan 	    {
413a2438132SYegappan Lakshmanan 		*p = TOUPPER_LOC(*p); // note that toupper() can be a macro
414a2438132SYegappan Lakshmanan 		p++;
415a2438132SYegappan Lakshmanan 	    }
416a2438132SYegappan Lakshmanan 	}
417a2438132SYegappan Lakshmanan 
418a2438132SYegappan Lakshmanan     return res;
419a2438132SYegappan Lakshmanan }
420a2438132SYegappan Lakshmanan 
421a2438132SYegappan Lakshmanan /*
422a2438132SYegappan Lakshmanan  * Make string "s" all lower-case and return it in allocated memory.
423a2438132SYegappan Lakshmanan  * Handles multi-byte characters as well as possible.
424a2438132SYegappan Lakshmanan  * Returns NULL when out of memory.
425a2438132SYegappan Lakshmanan  */
426a2438132SYegappan Lakshmanan     char_u *
strlow_save(char_u * orig)427a2438132SYegappan Lakshmanan strlow_save(char_u *orig)
428a2438132SYegappan Lakshmanan {
429a2438132SYegappan Lakshmanan     char_u	*p;
430a2438132SYegappan Lakshmanan     char_u	*res;
431a2438132SYegappan Lakshmanan 
432a2438132SYegappan Lakshmanan     res = p = vim_strsave(orig);
433a2438132SYegappan Lakshmanan 
434a2438132SYegappan Lakshmanan     if (res != NULL)
435a2438132SYegappan Lakshmanan 	while (*p != NUL)
436a2438132SYegappan Lakshmanan 	{
437a2438132SYegappan Lakshmanan 	    int		l;
438a2438132SYegappan Lakshmanan 
439a2438132SYegappan Lakshmanan 	    if (enc_utf8)
440a2438132SYegappan Lakshmanan 	    {
441a2438132SYegappan Lakshmanan 		int	c, lc;
442a2438132SYegappan Lakshmanan 		int	newl;
443a2438132SYegappan Lakshmanan 		char_u	*s;
444a2438132SYegappan Lakshmanan 
445a2438132SYegappan Lakshmanan 		c = utf_ptr2char(p);
446a2438132SYegappan Lakshmanan 		l = utf_ptr2len(p);
447a2438132SYegappan Lakshmanan 		if (c == 0)
448a2438132SYegappan Lakshmanan 		{
449a2438132SYegappan Lakshmanan 		    // overlong sequence, use only the first byte
450a2438132SYegappan Lakshmanan 		    c = *p;
451a2438132SYegappan Lakshmanan 		    l = 1;
452a2438132SYegappan Lakshmanan 		}
453a2438132SYegappan Lakshmanan 		lc = utf_tolower(c);
454a2438132SYegappan Lakshmanan 
455a2438132SYegappan Lakshmanan 		// Reallocate string when byte count changes.  This is rare,
456a2438132SYegappan Lakshmanan 		// thus it's OK to do another malloc()/free().
457a2438132SYegappan Lakshmanan 		newl = utf_char2len(lc);
458a2438132SYegappan Lakshmanan 		if (newl != l)
459a2438132SYegappan Lakshmanan 		{
460a2438132SYegappan Lakshmanan 		    s = alloc(STRLEN(res) + 1 + newl - l);
461a2438132SYegappan Lakshmanan 		    if (s == NULL)
462a2438132SYegappan Lakshmanan 		    {
463a2438132SYegappan Lakshmanan 			vim_free(res);
464a2438132SYegappan Lakshmanan 			return NULL;
465a2438132SYegappan Lakshmanan 		    }
466a2438132SYegappan Lakshmanan 		    mch_memmove(s, res, p - res);
467a2438132SYegappan Lakshmanan 		    STRCPY(s + (p - res) + newl, p + l);
468a2438132SYegappan Lakshmanan 		    p = s + (p - res);
469a2438132SYegappan Lakshmanan 		    vim_free(res);
470a2438132SYegappan Lakshmanan 		    res = s;
471a2438132SYegappan Lakshmanan 		}
472a2438132SYegappan Lakshmanan 
473a2438132SYegappan Lakshmanan 		utf_char2bytes(lc, p);
474a2438132SYegappan Lakshmanan 		p += newl;
475a2438132SYegappan Lakshmanan 	    }
476a2438132SYegappan Lakshmanan 	    else if (has_mbyte && (l = (*mb_ptr2len)(p)) > 1)
477a2438132SYegappan Lakshmanan 		p += l;		// skip multi-byte character
478a2438132SYegappan Lakshmanan 	    else
479a2438132SYegappan Lakshmanan 	    {
480a2438132SYegappan Lakshmanan 		*p = TOLOWER_LOC(*p); // note that tolower() can be a macro
481a2438132SYegappan Lakshmanan 		p++;
482a2438132SYegappan Lakshmanan 	    }
483a2438132SYegappan Lakshmanan 	}
484a2438132SYegappan Lakshmanan 
485a2438132SYegappan Lakshmanan     return res;
486a2438132SYegappan Lakshmanan }
487a2438132SYegappan Lakshmanan #endif
488a2438132SYegappan Lakshmanan 
489a2438132SYegappan Lakshmanan /*
490a2438132SYegappan Lakshmanan  * delete spaces at the end of a string
491a2438132SYegappan Lakshmanan  */
492a2438132SYegappan Lakshmanan     void
del_trailing_spaces(char_u * ptr)493a2438132SYegappan Lakshmanan del_trailing_spaces(char_u *ptr)
494a2438132SYegappan Lakshmanan {
495a2438132SYegappan Lakshmanan     char_u	*q;
496a2438132SYegappan Lakshmanan 
497a2438132SYegappan Lakshmanan     q = ptr + STRLEN(ptr);
498a2438132SYegappan Lakshmanan     while (--q > ptr && VIM_ISWHITE(q[0]) && q[-1] != '\\' && q[-1] != Ctrl_V)
499a2438132SYegappan Lakshmanan 	*q = NUL;
500a2438132SYegappan Lakshmanan }
501a2438132SYegappan Lakshmanan 
502a2438132SYegappan Lakshmanan /*
503a2438132SYegappan Lakshmanan  * Like strncpy(), but always terminate the result with one NUL.
504a2438132SYegappan Lakshmanan  * "to" must be "len + 1" long!
505a2438132SYegappan Lakshmanan  */
506a2438132SYegappan Lakshmanan     void
vim_strncpy(char_u * to,char_u * from,size_t len)507a2438132SYegappan Lakshmanan vim_strncpy(char_u *to, char_u *from, size_t len)
508a2438132SYegappan Lakshmanan {
509a2438132SYegappan Lakshmanan     STRNCPY(to, from, len);
510a2438132SYegappan Lakshmanan     to[len] = NUL;
511a2438132SYegappan Lakshmanan }
512a2438132SYegappan Lakshmanan 
513a2438132SYegappan Lakshmanan /*
514a2438132SYegappan Lakshmanan  * Like strcat(), but make sure the result fits in "tosize" bytes and is
515a2438132SYegappan Lakshmanan  * always NUL terminated. "from" and "to" may overlap.
516a2438132SYegappan Lakshmanan  */
517a2438132SYegappan Lakshmanan     void
vim_strcat(char_u * to,char_u * from,size_t tosize)518a2438132SYegappan Lakshmanan vim_strcat(char_u *to, char_u *from, size_t tosize)
519a2438132SYegappan Lakshmanan {
520a2438132SYegappan Lakshmanan     size_t tolen = STRLEN(to);
521a2438132SYegappan Lakshmanan     size_t fromlen = STRLEN(from);
522a2438132SYegappan Lakshmanan 
523a2438132SYegappan Lakshmanan     if (tolen + fromlen + 1 > tosize)
524a2438132SYegappan Lakshmanan     {
525a2438132SYegappan Lakshmanan 	mch_memmove(to + tolen, from, tosize - tolen - 1);
526a2438132SYegappan Lakshmanan 	to[tosize - 1] = NUL;
527a2438132SYegappan Lakshmanan     }
528a2438132SYegappan Lakshmanan     else
529a2438132SYegappan Lakshmanan 	mch_memmove(to + tolen, from, fromlen + 1);
530a2438132SYegappan Lakshmanan }
531a2438132SYegappan Lakshmanan 
532a2438132SYegappan Lakshmanan #if (!defined(HAVE_STRCASECMP) && !defined(HAVE_STRICMP)) || defined(PROTO)
533a2438132SYegappan Lakshmanan /*
534a2438132SYegappan Lakshmanan  * Compare two strings, ignoring case, using current locale.
535a2438132SYegappan Lakshmanan  * Doesn't work for multi-byte characters.
536a2438132SYegappan Lakshmanan  * return 0 for match, < 0 for smaller, > 0 for bigger
537a2438132SYegappan Lakshmanan  */
538a2438132SYegappan Lakshmanan     int
vim_stricmp(char * s1,char * s2)539a2438132SYegappan Lakshmanan vim_stricmp(char *s1, char *s2)
540a2438132SYegappan Lakshmanan {
541a2438132SYegappan Lakshmanan     int		i;
542a2438132SYegappan Lakshmanan 
543a2438132SYegappan Lakshmanan     for (;;)
544a2438132SYegappan Lakshmanan     {
545a2438132SYegappan Lakshmanan 	i = (int)TOLOWER_LOC(*s1) - (int)TOLOWER_LOC(*s2);
546a2438132SYegappan Lakshmanan 	if (i != 0)
547a2438132SYegappan Lakshmanan 	    return i;			    // this character different
548a2438132SYegappan Lakshmanan 	if (*s1 == NUL)
549a2438132SYegappan Lakshmanan 	    break;			    // strings match until NUL
550a2438132SYegappan Lakshmanan 	++s1;
551a2438132SYegappan Lakshmanan 	++s2;
552a2438132SYegappan Lakshmanan     }
553a2438132SYegappan Lakshmanan     return 0;				    // strings match
554a2438132SYegappan Lakshmanan }
555a2438132SYegappan Lakshmanan #endif
556a2438132SYegappan Lakshmanan 
557a2438132SYegappan Lakshmanan #if (!defined(HAVE_STRNCASECMP) && !defined(HAVE_STRNICMP)) || defined(PROTO)
558a2438132SYegappan Lakshmanan /*
559a2438132SYegappan Lakshmanan  * Compare two strings, for length "len", ignoring case, using current locale.
560a2438132SYegappan Lakshmanan  * Doesn't work for multi-byte characters.
561a2438132SYegappan Lakshmanan  * return 0 for match, < 0 for smaller, > 0 for bigger
562a2438132SYegappan Lakshmanan  */
563a2438132SYegappan Lakshmanan     int
vim_strnicmp(char * s1,char * s2,size_t len)564a2438132SYegappan Lakshmanan vim_strnicmp(char *s1, char *s2, size_t len)
565a2438132SYegappan Lakshmanan {
566a2438132SYegappan Lakshmanan     int		i;
567a2438132SYegappan Lakshmanan 
568a2438132SYegappan Lakshmanan     while (len > 0)
569a2438132SYegappan Lakshmanan     {
570a2438132SYegappan Lakshmanan 	i = (int)TOLOWER_LOC(*s1) - (int)TOLOWER_LOC(*s2);
571a2438132SYegappan Lakshmanan 	if (i != 0)
572a2438132SYegappan Lakshmanan 	    return i;			    // this character different
573a2438132SYegappan Lakshmanan 	if (*s1 == NUL)
574a2438132SYegappan Lakshmanan 	    break;			    // strings match until NUL
575a2438132SYegappan Lakshmanan 	++s1;
576a2438132SYegappan Lakshmanan 	++s2;
577a2438132SYegappan Lakshmanan 	--len;
578a2438132SYegappan Lakshmanan     }
579a2438132SYegappan Lakshmanan     return 0;				    // strings match
580a2438132SYegappan Lakshmanan }
581a2438132SYegappan Lakshmanan #endif
582a2438132SYegappan Lakshmanan 
583a2438132SYegappan Lakshmanan /*
584a2438132SYegappan Lakshmanan  * Search for first occurrence of "c" in "string".
585a2438132SYegappan Lakshmanan  * Version of strchr() that handles unsigned char strings with characters from
586a2438132SYegappan Lakshmanan  * 128 to 255 correctly.  It also doesn't return a pointer to the NUL at the
587a2438132SYegappan Lakshmanan  * end of the string.
588a2438132SYegappan Lakshmanan  */
589a2438132SYegappan Lakshmanan     char_u  *
vim_strchr(char_u * string,int c)590a2438132SYegappan Lakshmanan vim_strchr(char_u *string, int c)
591a2438132SYegappan Lakshmanan {
592a2438132SYegappan Lakshmanan     char_u	*p;
593a2438132SYegappan Lakshmanan     int		b;
594a2438132SYegappan Lakshmanan 
595a2438132SYegappan Lakshmanan     p = string;
596a2438132SYegappan Lakshmanan     if (enc_utf8 && c >= 0x80)
597a2438132SYegappan Lakshmanan     {
598a2438132SYegappan Lakshmanan 	while (*p != NUL)
599a2438132SYegappan Lakshmanan 	{
600a2438132SYegappan Lakshmanan 	    int l = utfc_ptr2len(p);
601a2438132SYegappan Lakshmanan 
602a2438132SYegappan Lakshmanan 	    // Avoid matching an illegal byte here.
603a2438132SYegappan Lakshmanan 	    if (utf_ptr2char(p) == c && l > 1)
604a2438132SYegappan Lakshmanan 		return p;
605a2438132SYegappan Lakshmanan 	    p += l;
606a2438132SYegappan Lakshmanan 	}
607a2438132SYegappan Lakshmanan 	return NULL;
608a2438132SYegappan Lakshmanan     }
609a2438132SYegappan Lakshmanan     if (enc_dbcs != 0 && c > 255)
610a2438132SYegappan Lakshmanan     {
611a2438132SYegappan Lakshmanan 	int	n2 = c & 0xff;
612a2438132SYegappan Lakshmanan 
613a2438132SYegappan Lakshmanan 	c = ((unsigned)c >> 8) & 0xff;
614a2438132SYegappan Lakshmanan 	while ((b = *p) != NUL)
615a2438132SYegappan Lakshmanan 	{
616a2438132SYegappan Lakshmanan 	    if (b == c && p[1] == n2)
617a2438132SYegappan Lakshmanan 		return p;
618a2438132SYegappan Lakshmanan 	    p += (*mb_ptr2len)(p);
619a2438132SYegappan Lakshmanan 	}
620a2438132SYegappan Lakshmanan 	return NULL;
621a2438132SYegappan Lakshmanan     }
622a2438132SYegappan Lakshmanan     if (has_mbyte)
623a2438132SYegappan Lakshmanan     {
624a2438132SYegappan Lakshmanan 	while ((b = *p) != NUL)
625a2438132SYegappan Lakshmanan 	{
626a2438132SYegappan Lakshmanan 	    if (b == c)
627a2438132SYegappan Lakshmanan 		return p;
628a2438132SYegappan Lakshmanan 	    p += (*mb_ptr2len)(p);
629a2438132SYegappan Lakshmanan 	}
630a2438132SYegappan Lakshmanan 	return NULL;
631a2438132SYegappan Lakshmanan     }
632a2438132SYegappan Lakshmanan     while ((b = *p) != NUL)
633a2438132SYegappan Lakshmanan     {
634a2438132SYegappan Lakshmanan 	if (b == c)
635a2438132SYegappan Lakshmanan 	    return p;
636a2438132SYegappan Lakshmanan 	++p;
637a2438132SYegappan Lakshmanan     }
638a2438132SYegappan Lakshmanan     return NULL;
639a2438132SYegappan Lakshmanan }
640a2438132SYegappan Lakshmanan 
641a2438132SYegappan Lakshmanan /*
642a2438132SYegappan Lakshmanan  * Version of strchr() that only works for bytes and handles unsigned char
643a2438132SYegappan Lakshmanan  * strings with characters above 128 correctly. It also doesn't return a
644a2438132SYegappan Lakshmanan  * pointer to the NUL at the end of the string.
645a2438132SYegappan Lakshmanan  */
646a2438132SYegappan Lakshmanan     char_u  *
vim_strbyte(char_u * string,int c)647a2438132SYegappan Lakshmanan vim_strbyte(char_u *string, int c)
648a2438132SYegappan Lakshmanan {
649a2438132SYegappan Lakshmanan     char_u	*p = string;
650a2438132SYegappan Lakshmanan 
651a2438132SYegappan Lakshmanan     while (*p != NUL)
652a2438132SYegappan Lakshmanan     {
653a2438132SYegappan Lakshmanan 	if (*p == c)
654a2438132SYegappan Lakshmanan 	    return p;
655a2438132SYegappan Lakshmanan 	++p;
656a2438132SYegappan Lakshmanan     }
657a2438132SYegappan Lakshmanan     return NULL;
658a2438132SYegappan Lakshmanan }
659a2438132SYegappan Lakshmanan 
660a2438132SYegappan Lakshmanan /*
661a2438132SYegappan Lakshmanan  * Search for last occurrence of "c" in "string".
662a2438132SYegappan Lakshmanan  * Version of strrchr() that handles unsigned char strings with characters from
663a2438132SYegappan Lakshmanan  * 128 to 255 correctly.  It also doesn't return a pointer to the NUL at the
664a2438132SYegappan Lakshmanan  * end of the string.
665a2438132SYegappan Lakshmanan  * Return NULL if not found.
666a2438132SYegappan Lakshmanan  * Does not handle multi-byte char for "c"!
667a2438132SYegappan Lakshmanan  */
668a2438132SYegappan Lakshmanan     char_u  *
vim_strrchr(char_u * string,int c)669a2438132SYegappan Lakshmanan vim_strrchr(char_u *string, int c)
670a2438132SYegappan Lakshmanan {
671a2438132SYegappan Lakshmanan     char_u	*retval = NULL;
672a2438132SYegappan Lakshmanan     char_u	*p = string;
673a2438132SYegappan Lakshmanan 
674a2438132SYegappan Lakshmanan     while (*p)
675a2438132SYegappan Lakshmanan     {
676a2438132SYegappan Lakshmanan 	if (*p == c)
677a2438132SYegappan Lakshmanan 	    retval = p;
678a2438132SYegappan Lakshmanan 	MB_PTR_ADV(p);
679a2438132SYegappan Lakshmanan     }
680a2438132SYegappan Lakshmanan     return retval;
681a2438132SYegappan Lakshmanan }
682a2438132SYegappan Lakshmanan 
683a2438132SYegappan Lakshmanan /*
684a2438132SYegappan Lakshmanan  * Vim's version of strpbrk(), in case it's missing.
685a2438132SYegappan Lakshmanan  * Don't generate a prototype for this, causes problems when it's not used.
686a2438132SYegappan Lakshmanan  */
687a2438132SYegappan Lakshmanan #ifndef PROTO
688a2438132SYegappan Lakshmanan # ifndef HAVE_STRPBRK
689a2438132SYegappan Lakshmanan #  ifdef vim_strpbrk
690a2438132SYegappan Lakshmanan #   undef vim_strpbrk
691a2438132SYegappan Lakshmanan #  endif
692a2438132SYegappan Lakshmanan     char_u *
vim_strpbrk(char_u * s,char_u * charset)693a2438132SYegappan Lakshmanan vim_strpbrk(char_u *s, char_u *charset)
694a2438132SYegappan Lakshmanan {
695a2438132SYegappan Lakshmanan     while (*s)
696a2438132SYegappan Lakshmanan     {
697a2438132SYegappan Lakshmanan 	if (vim_strchr(charset, *s) != NULL)
698a2438132SYegappan Lakshmanan 	    return s;
699a2438132SYegappan Lakshmanan 	MB_PTR_ADV(s);
700a2438132SYegappan Lakshmanan     }
701a2438132SYegappan Lakshmanan     return NULL;
702a2438132SYegappan Lakshmanan }
703a2438132SYegappan Lakshmanan # endif
704a2438132SYegappan Lakshmanan #endif
705a2438132SYegappan Lakshmanan 
706a2438132SYegappan Lakshmanan /*
707a2438132SYegappan Lakshmanan  * Sort an array of strings.
708a2438132SYegappan Lakshmanan  */
709a2438132SYegappan Lakshmanan static int sort_compare(const void *s1, const void *s2);
710a2438132SYegappan Lakshmanan 
711a2438132SYegappan Lakshmanan     static int
sort_compare(const void * s1,const void * s2)712a2438132SYegappan Lakshmanan sort_compare(const void *s1, const void *s2)
713a2438132SYegappan Lakshmanan {
714a2438132SYegappan Lakshmanan     return STRCMP(*(char **)s1, *(char **)s2);
715a2438132SYegappan Lakshmanan }
716a2438132SYegappan Lakshmanan 
717a2438132SYegappan Lakshmanan     void
sort_strings(char_u ** files,int count)718a2438132SYegappan Lakshmanan sort_strings(
719a2438132SYegappan Lakshmanan     char_u	**files,
720a2438132SYegappan Lakshmanan     int		count)
721a2438132SYegappan Lakshmanan {
722a2438132SYegappan Lakshmanan     qsort((void *)files, (size_t)count, sizeof(char_u *), sort_compare);
723a2438132SYegappan Lakshmanan }
724a2438132SYegappan Lakshmanan 
725a2438132SYegappan Lakshmanan #if defined(FEAT_QUICKFIX) || defined(FEAT_SPELL) || defined(PROTO)
726a2438132SYegappan Lakshmanan /*
727a2438132SYegappan Lakshmanan  * Return TRUE if string "s" contains a non-ASCII character (128 or higher).
728a2438132SYegappan Lakshmanan  * When "s" is NULL FALSE is returned.
729a2438132SYegappan Lakshmanan  */
730a2438132SYegappan Lakshmanan     int
has_non_ascii(char_u * s)731a2438132SYegappan Lakshmanan has_non_ascii(char_u *s)
732a2438132SYegappan Lakshmanan {
733a2438132SYegappan Lakshmanan     char_u	*p;
734a2438132SYegappan Lakshmanan 
735a2438132SYegappan Lakshmanan     if (s != NULL)
736a2438132SYegappan Lakshmanan 	for (p = s; *p != NUL; ++p)
737a2438132SYegappan Lakshmanan 	    if (*p >= 128)
738a2438132SYegappan Lakshmanan 		return TRUE;
739a2438132SYegappan Lakshmanan     return FALSE;
740a2438132SYegappan Lakshmanan }
741a2438132SYegappan Lakshmanan #endif
742a2438132SYegappan Lakshmanan 
743a2438132SYegappan Lakshmanan /*
744a2438132SYegappan Lakshmanan  * Concatenate two strings and return the result in allocated memory.
745a2438132SYegappan Lakshmanan  * Returns NULL when out of memory.
746a2438132SYegappan Lakshmanan  */
747a2438132SYegappan Lakshmanan     char_u  *
concat_str(char_u * str1,char_u * str2)748a2438132SYegappan Lakshmanan concat_str(char_u *str1, char_u *str2)
749a2438132SYegappan Lakshmanan {
750a2438132SYegappan Lakshmanan     char_u  *dest;
751a2438132SYegappan Lakshmanan     size_t  l = str1 == NULL ? 0 : STRLEN(str1);
752a2438132SYegappan Lakshmanan 
753a2438132SYegappan Lakshmanan     dest = alloc(l + (str2 == NULL ? 0 : STRLEN(str2)) + 1L);
754a2438132SYegappan Lakshmanan     if (dest != NULL)
755a2438132SYegappan Lakshmanan     {
756a2438132SYegappan Lakshmanan 	if (str1 == NULL)
757a2438132SYegappan Lakshmanan 	    *dest = NUL;
758a2438132SYegappan Lakshmanan 	else
759a2438132SYegappan Lakshmanan 	    STRCPY(dest, str1);
760a2438132SYegappan Lakshmanan 	if (str2 != NULL)
761a2438132SYegappan Lakshmanan 	    STRCPY(dest + l, str2);
762a2438132SYegappan Lakshmanan     }
763a2438132SYegappan Lakshmanan     return dest;
764a2438132SYegappan Lakshmanan }
765a2438132SYegappan Lakshmanan 
766a2438132SYegappan Lakshmanan #if defined(FEAT_EVAL) || defined(PROTO)
767a2438132SYegappan Lakshmanan 
768a2438132SYegappan Lakshmanan /*
769a2438132SYegappan Lakshmanan  * Return string "str" in ' quotes, doubling ' characters.
770a2438132SYegappan Lakshmanan  * If "str" is NULL an empty string is assumed.
771a2438132SYegappan Lakshmanan  * If "function" is TRUE make it function('string').
772a2438132SYegappan Lakshmanan  */
773a2438132SYegappan Lakshmanan     char_u *
string_quote(char_u * str,int function)774a2438132SYegappan Lakshmanan string_quote(char_u *str, int function)
775a2438132SYegappan Lakshmanan {
776a2438132SYegappan Lakshmanan     unsigned	len;
777a2438132SYegappan Lakshmanan     char_u	*p, *r, *s;
778a2438132SYegappan Lakshmanan 
779a2438132SYegappan Lakshmanan     len = (function ? 13 : 3);
780a2438132SYegappan Lakshmanan     if (str != NULL)
781a2438132SYegappan Lakshmanan     {
782a2438132SYegappan Lakshmanan 	len += (unsigned)STRLEN(str);
783a2438132SYegappan Lakshmanan 	for (p = str; *p != NUL; MB_PTR_ADV(p))
784a2438132SYegappan Lakshmanan 	    if (*p == '\'')
785a2438132SYegappan Lakshmanan 		++len;
786a2438132SYegappan Lakshmanan     }
787a2438132SYegappan Lakshmanan     s = r = alloc(len);
788a2438132SYegappan Lakshmanan     if (r != NULL)
789a2438132SYegappan Lakshmanan     {
790a2438132SYegappan Lakshmanan 	if (function)
791a2438132SYegappan Lakshmanan 	{
792a2438132SYegappan Lakshmanan 	    STRCPY(r, "function('");
793a2438132SYegappan Lakshmanan 	    r += 10;
794a2438132SYegappan Lakshmanan 	}
795a2438132SYegappan Lakshmanan 	else
796a2438132SYegappan Lakshmanan 	    *r++ = '\'';
797a2438132SYegappan Lakshmanan 	if (str != NULL)
798a2438132SYegappan Lakshmanan 	    for (p = str; *p != NUL; )
799a2438132SYegappan Lakshmanan 	    {
800a2438132SYegappan Lakshmanan 		if (*p == '\'')
801a2438132SYegappan Lakshmanan 		    *r++ = '\'';
802a2438132SYegappan Lakshmanan 		MB_COPY_CHAR(p, r);
803a2438132SYegappan Lakshmanan 	    }
804a2438132SYegappan Lakshmanan 	*r++ = '\'';
805a2438132SYegappan Lakshmanan 	if (function)
806a2438132SYegappan Lakshmanan 	    *r++ = ')';
807a2438132SYegappan Lakshmanan 	*r++ = NUL;
808a2438132SYegappan Lakshmanan     }
809a2438132SYegappan Lakshmanan     return s;
810a2438132SYegappan Lakshmanan }
811a2438132SYegappan Lakshmanan 
812a2438132SYegappan Lakshmanan     static void
byteidx(typval_T * argvars,typval_T * rettv,int comp UNUSED)813a2438132SYegappan Lakshmanan byteidx(typval_T *argvars, typval_T *rettv, int comp UNUSED)
814a2438132SYegappan Lakshmanan {
815a2438132SYegappan Lakshmanan     char_u	*t;
816a2438132SYegappan Lakshmanan     char_u	*str;
817a2438132SYegappan Lakshmanan     varnumber_T	idx;
818a2438132SYegappan Lakshmanan 
8191a71d31bSYegappan Lakshmanan     rettv->vval.v_number = -1;
8201a71d31bSYegappan Lakshmanan 
8211a71d31bSYegappan Lakshmanan     if (in_vim9script()
8221a71d31bSYegappan Lakshmanan 	    && (check_for_string_arg(argvars, 0) == FAIL
8231a71d31bSYegappan Lakshmanan 		|| check_for_number_arg(argvars, 1) == FAIL))
8241a71d31bSYegappan Lakshmanan 	return;
8251a71d31bSYegappan Lakshmanan 
826a2438132SYegappan Lakshmanan     str = tv_get_string_chk(&argvars[0]);
827a2438132SYegappan Lakshmanan     idx = tv_get_number_chk(&argvars[1], NULL);
828a2438132SYegappan Lakshmanan     if (str == NULL || idx < 0)
829a2438132SYegappan Lakshmanan 	return;
830a2438132SYegappan Lakshmanan 
831a2438132SYegappan Lakshmanan     t = str;
832a2438132SYegappan Lakshmanan     for ( ; idx > 0; idx--)
833a2438132SYegappan Lakshmanan     {
834a2438132SYegappan Lakshmanan 	if (*t == NUL)		// EOL reached
835a2438132SYegappan Lakshmanan 	    return;
836a2438132SYegappan Lakshmanan 	if (enc_utf8 && comp)
837a2438132SYegappan Lakshmanan 	    t += utf_ptr2len(t);
838a2438132SYegappan Lakshmanan 	else
839a2438132SYegappan Lakshmanan 	    t += (*mb_ptr2len)(t);
840a2438132SYegappan Lakshmanan     }
841a2438132SYegappan Lakshmanan     rettv->vval.v_number = (varnumber_T)(t - str);
842a2438132SYegappan Lakshmanan }
843a2438132SYegappan Lakshmanan 
844a2438132SYegappan Lakshmanan /*
845a2438132SYegappan Lakshmanan  * "byteidx()" function
846a2438132SYegappan Lakshmanan  */
847a2438132SYegappan Lakshmanan     void
f_byteidx(typval_T * argvars,typval_T * rettv)848a2438132SYegappan Lakshmanan f_byteidx(typval_T *argvars, typval_T *rettv)
849a2438132SYegappan Lakshmanan {
850a2438132SYegappan Lakshmanan     byteidx(argvars, rettv, FALSE);
851a2438132SYegappan Lakshmanan }
852a2438132SYegappan Lakshmanan 
853a2438132SYegappan Lakshmanan /*
854a2438132SYegappan Lakshmanan  * "byteidxcomp()" function
855a2438132SYegappan Lakshmanan  */
856a2438132SYegappan Lakshmanan     void
f_byteidxcomp(typval_T * argvars,typval_T * rettv)857a2438132SYegappan Lakshmanan f_byteidxcomp(typval_T *argvars, typval_T *rettv)
858a2438132SYegappan Lakshmanan {
859a2438132SYegappan Lakshmanan     byteidx(argvars, rettv, TRUE);
860a2438132SYegappan Lakshmanan }
861a2438132SYegappan Lakshmanan 
862a2438132SYegappan Lakshmanan /*
863a2438132SYegappan Lakshmanan  * "charidx()" function
864a2438132SYegappan Lakshmanan  */
865a2438132SYegappan Lakshmanan     void
f_charidx(typval_T * argvars,typval_T * rettv)866a2438132SYegappan Lakshmanan f_charidx(typval_T *argvars, typval_T *rettv)
867a2438132SYegappan Lakshmanan {
868a2438132SYegappan Lakshmanan     char_u	*str;
869a2438132SYegappan Lakshmanan     varnumber_T	idx;
870a2438132SYegappan Lakshmanan     varnumber_T	countcc = FALSE;
871a2438132SYegappan Lakshmanan     char_u	*p;
872a2438132SYegappan Lakshmanan     int		len;
873a2438132SYegappan Lakshmanan     int		(*ptr2len)(char_u *);
874a2438132SYegappan Lakshmanan 
875a2438132SYegappan Lakshmanan     rettv->vval.v_number = -1;
876a2438132SYegappan Lakshmanan 
8774490ec4eSYegappan Lakshmanan     if (in_vim9script()
8784490ec4eSYegappan Lakshmanan 	    && (check_for_string_arg(argvars, 0) == FAIL
8794490ec4eSYegappan Lakshmanan 		|| check_for_number_arg(argvars, 1) == FAIL
8804490ec4eSYegappan Lakshmanan 		|| check_for_opt_bool_arg(argvars, 2) == FAIL))
8814490ec4eSYegappan Lakshmanan 	return;
8824490ec4eSYegappan Lakshmanan 
883a2438132SYegappan Lakshmanan     if (argvars[0].v_type != VAR_STRING || argvars[1].v_type != VAR_NUMBER
884a2438132SYegappan Lakshmanan 	    || (argvars[2].v_type != VAR_UNKNOWN
885a2438132SYegappan Lakshmanan 					   && argvars[2].v_type != VAR_NUMBER
886a2438132SYegappan Lakshmanan 					   && argvars[2].v_type != VAR_BOOL))
887a2438132SYegappan Lakshmanan     {
888a2438132SYegappan Lakshmanan 	emsg(_(e_invarg));
889a2438132SYegappan Lakshmanan 	return;
890a2438132SYegappan Lakshmanan     }
891a2438132SYegappan Lakshmanan 
892a2438132SYegappan Lakshmanan     str = tv_get_string_chk(&argvars[0]);
893a2438132SYegappan Lakshmanan     idx = tv_get_number_chk(&argvars[1], NULL);
894a2438132SYegappan Lakshmanan     if (str == NULL || idx < 0)
895a2438132SYegappan Lakshmanan 	return;
896a2438132SYegappan Lakshmanan 
897a2438132SYegappan Lakshmanan     if (argvars[2].v_type != VAR_UNKNOWN)
898a2438132SYegappan Lakshmanan 	countcc = tv_get_bool(&argvars[2]);
899a2438132SYegappan Lakshmanan     if (countcc < 0 || countcc > 1)
900a2438132SYegappan Lakshmanan     {
901a2438132SYegappan Lakshmanan 	semsg(_(e_using_number_as_bool_nr), countcc);
902a2438132SYegappan Lakshmanan 	return;
903a2438132SYegappan Lakshmanan     }
904a2438132SYegappan Lakshmanan 
905a2438132SYegappan Lakshmanan     if (enc_utf8 && countcc)
906a2438132SYegappan Lakshmanan 	ptr2len = utf_ptr2len;
907a2438132SYegappan Lakshmanan     else
908a2438132SYegappan Lakshmanan 	ptr2len = mb_ptr2len;
909a2438132SYegappan Lakshmanan 
910a2438132SYegappan Lakshmanan     for (p = str, len = 0; p <= str + idx; len++)
911a2438132SYegappan Lakshmanan     {
912a2438132SYegappan Lakshmanan 	if (*p == NUL)
913a2438132SYegappan Lakshmanan 	    return;
914a2438132SYegappan Lakshmanan 	p += ptr2len(p);
915a2438132SYegappan Lakshmanan     }
916a2438132SYegappan Lakshmanan 
917a2438132SYegappan Lakshmanan     rettv->vval.v_number = len > 0 ? len - 1 : 0;
918a2438132SYegappan Lakshmanan }
919a2438132SYegappan Lakshmanan 
920a2438132SYegappan Lakshmanan /*
921a2438132SYegappan Lakshmanan  * "str2list()" function
922a2438132SYegappan Lakshmanan  */
923a2438132SYegappan Lakshmanan     void
f_str2list(typval_T * argvars,typval_T * rettv)924a2438132SYegappan Lakshmanan f_str2list(typval_T *argvars, typval_T *rettv)
925a2438132SYegappan Lakshmanan {
926a2438132SYegappan Lakshmanan     char_u	*p;
927a2438132SYegappan Lakshmanan     int		utf8 = FALSE;
928a2438132SYegappan Lakshmanan 
929a2438132SYegappan Lakshmanan     if (rettv_list_alloc(rettv) == FAIL)
930a2438132SYegappan Lakshmanan 	return;
931a2438132SYegappan Lakshmanan 
932a9a7c0c6SYegappan Lakshmanan     if (in_vim9script()
933a9a7c0c6SYegappan Lakshmanan 	    && (check_for_string_arg(argvars, 0) == FAIL
93483494b4aSYegappan Lakshmanan 		|| check_for_opt_bool_arg(argvars, 1) == FAIL))
935a9a7c0c6SYegappan Lakshmanan 	return;
936a9a7c0c6SYegappan Lakshmanan 
937a2438132SYegappan Lakshmanan     if (argvars[1].v_type != VAR_UNKNOWN)
938a2438132SYegappan Lakshmanan 	utf8 = (int)tv_get_bool_chk(&argvars[1], NULL);
939a2438132SYegappan Lakshmanan 
940a2438132SYegappan Lakshmanan     p = tv_get_string(&argvars[0]);
941a2438132SYegappan Lakshmanan 
942a2438132SYegappan Lakshmanan     if (has_mbyte || utf8)
943a2438132SYegappan Lakshmanan     {
944a2438132SYegappan Lakshmanan 	int (*ptr2len)(char_u *);
945a2438132SYegappan Lakshmanan 	int (*ptr2char)(char_u *);
946a2438132SYegappan Lakshmanan 
947a2438132SYegappan Lakshmanan 	if (utf8 || enc_utf8)
948a2438132SYegappan Lakshmanan 	{
949a2438132SYegappan Lakshmanan 	    ptr2len = utf_ptr2len;
950a2438132SYegappan Lakshmanan 	    ptr2char = utf_ptr2char;
951a2438132SYegappan Lakshmanan 	}
952a2438132SYegappan Lakshmanan 	else
953a2438132SYegappan Lakshmanan 	{
954a2438132SYegappan Lakshmanan 	    ptr2len = mb_ptr2len;
955a2438132SYegappan Lakshmanan 	    ptr2char = mb_ptr2char;
956a2438132SYegappan Lakshmanan 	}
957a2438132SYegappan Lakshmanan 
958a2438132SYegappan Lakshmanan 	for ( ; *p != NUL; p += (*ptr2len)(p))
959a2438132SYegappan Lakshmanan 	    list_append_number(rettv->vval.v_list, (*ptr2char)(p));
960a2438132SYegappan Lakshmanan     }
961a2438132SYegappan Lakshmanan     else
962a2438132SYegappan Lakshmanan 	for ( ; *p != NUL; ++p)
963a2438132SYegappan Lakshmanan 	    list_append_number(rettv->vval.v_list, *p);
964a2438132SYegappan Lakshmanan }
965a2438132SYegappan Lakshmanan 
966a2438132SYegappan Lakshmanan /*
967a2438132SYegappan Lakshmanan  * "str2nr()" function
968a2438132SYegappan Lakshmanan  */
969a2438132SYegappan Lakshmanan     void
f_str2nr(typval_T * argvars,typval_T * rettv)970a2438132SYegappan Lakshmanan f_str2nr(typval_T *argvars, typval_T *rettv)
971a2438132SYegappan Lakshmanan {
972a2438132SYegappan Lakshmanan     int		base = 10;
973a2438132SYegappan Lakshmanan     char_u	*p;
974a2438132SYegappan Lakshmanan     varnumber_T	n;
975a2438132SYegappan Lakshmanan     int		what = 0;
976a2438132SYegappan Lakshmanan     int		isneg;
977a2438132SYegappan Lakshmanan 
9784490ec4eSYegappan Lakshmanan     if (in_vim9script()
9794490ec4eSYegappan Lakshmanan 	    && (check_for_string_arg(argvars, 0) == FAIL
9804490ec4eSYegappan Lakshmanan 		|| check_for_opt_number_arg(argvars, 1) == FAIL
9814490ec4eSYegappan Lakshmanan 		|| (argvars[1].v_type != VAR_UNKNOWN
9824490ec4eSYegappan Lakshmanan 		    && check_for_opt_bool_arg(argvars, 2) == FAIL)))
9834490ec4eSYegappan Lakshmanan 	return;
9844490ec4eSYegappan Lakshmanan 
985a2438132SYegappan Lakshmanan     if (argvars[1].v_type != VAR_UNKNOWN)
986a2438132SYegappan Lakshmanan     {
987a2438132SYegappan Lakshmanan 	base = (int)tv_get_number(&argvars[1]);
988a2438132SYegappan Lakshmanan 	if (base != 2 && base != 8 && base != 10 && base != 16)
989a2438132SYegappan Lakshmanan 	{
990a2438132SYegappan Lakshmanan 	    emsg(_(e_invarg));
991a2438132SYegappan Lakshmanan 	    return;
992a2438132SYegappan Lakshmanan 	}
993a2438132SYegappan Lakshmanan 	if (argvars[2].v_type != VAR_UNKNOWN && tv_get_bool(&argvars[2]))
994a2438132SYegappan Lakshmanan 	    what |= STR2NR_QUOTE;
995a2438132SYegappan Lakshmanan     }
996a2438132SYegappan Lakshmanan 
997a2438132SYegappan Lakshmanan     p = skipwhite(tv_get_string_strict(&argvars[0]));
998a2438132SYegappan Lakshmanan     isneg = (*p == '-');
999a2438132SYegappan Lakshmanan     if (*p == '+' || *p == '-')
1000a2438132SYegappan Lakshmanan 	p = skipwhite(p + 1);
1001a2438132SYegappan Lakshmanan     switch (base)
1002a2438132SYegappan Lakshmanan     {
1003a2438132SYegappan Lakshmanan 	case 2: what |= STR2NR_BIN + STR2NR_FORCE; break;
1004a2438132SYegappan Lakshmanan 	case 8: what |= STR2NR_OCT + STR2NR_OOCT + STR2NR_FORCE; break;
1005a2438132SYegappan Lakshmanan 	case 16: what |= STR2NR_HEX + STR2NR_FORCE; break;
1006a2438132SYegappan Lakshmanan     }
1007a2438132SYegappan Lakshmanan     vim_str2nr(p, NULL, NULL, what, &n, NULL, 0, FALSE);
1008a2438132SYegappan Lakshmanan     // Text after the number is silently ignored.
1009a2438132SYegappan Lakshmanan     if (isneg)
1010a2438132SYegappan Lakshmanan 	rettv->vval.v_number = -n;
1011a2438132SYegappan Lakshmanan     else
1012a2438132SYegappan Lakshmanan 	rettv->vval.v_number = n;
1013a2438132SYegappan Lakshmanan 
1014a2438132SYegappan Lakshmanan }
1015a2438132SYegappan Lakshmanan 
1016a2438132SYegappan Lakshmanan /*
1017a2438132SYegappan Lakshmanan  * "strgetchar()" function
1018a2438132SYegappan Lakshmanan  */
1019a2438132SYegappan Lakshmanan     void
f_strgetchar(typval_T * argvars,typval_T * rettv)1020a2438132SYegappan Lakshmanan f_strgetchar(typval_T *argvars, typval_T *rettv)
1021a2438132SYegappan Lakshmanan {
1022a2438132SYegappan Lakshmanan     char_u	*str;
1023a2438132SYegappan Lakshmanan     int		len;
1024a2438132SYegappan Lakshmanan     int		error = FALSE;
1025a2438132SYegappan Lakshmanan     int		charidx;
1026a2438132SYegappan Lakshmanan     int		byteidx = 0;
1027a2438132SYegappan Lakshmanan 
1028a2438132SYegappan Lakshmanan     rettv->vval.v_number = -1;
10291a71d31bSYegappan Lakshmanan 
10301a71d31bSYegappan Lakshmanan     if (in_vim9script()
10311a71d31bSYegappan Lakshmanan 	    && (check_for_string_arg(argvars, 0) == FAIL
10321a71d31bSYegappan Lakshmanan 		|| check_for_number_arg(argvars, 1) == FAIL))
10331a71d31bSYegappan Lakshmanan 	return;
10341a71d31bSYegappan Lakshmanan 
1035a2438132SYegappan Lakshmanan     str = tv_get_string_chk(&argvars[0]);
1036a2438132SYegappan Lakshmanan     if (str == NULL)
1037a2438132SYegappan Lakshmanan 	return;
1038a2438132SYegappan Lakshmanan     len = (int)STRLEN(str);
1039a2438132SYegappan Lakshmanan     charidx = (int)tv_get_number_chk(&argvars[1], &error);
1040a2438132SYegappan Lakshmanan     if (error)
1041a2438132SYegappan Lakshmanan 	return;
1042a2438132SYegappan Lakshmanan 
1043a2438132SYegappan Lakshmanan     while (charidx >= 0 && byteidx < len)
1044a2438132SYegappan Lakshmanan     {
1045a2438132SYegappan Lakshmanan 	if (charidx == 0)
1046a2438132SYegappan Lakshmanan 	{
1047a2438132SYegappan Lakshmanan 	    rettv->vval.v_number = mb_ptr2char(str + byteidx);
1048a2438132SYegappan Lakshmanan 	    break;
1049a2438132SYegappan Lakshmanan 	}
1050a2438132SYegappan Lakshmanan 	--charidx;
1051a2438132SYegappan Lakshmanan 	byteidx += MB_CPTR2LEN(str + byteidx);
1052a2438132SYegappan Lakshmanan     }
1053a2438132SYegappan Lakshmanan }
1054a2438132SYegappan Lakshmanan 
1055a2438132SYegappan Lakshmanan /*
1056a2438132SYegappan Lakshmanan  * "stridx()" function
1057a2438132SYegappan Lakshmanan  */
1058a2438132SYegappan Lakshmanan     void
f_stridx(typval_T * argvars,typval_T * rettv)1059a2438132SYegappan Lakshmanan f_stridx(typval_T *argvars, typval_T *rettv)
1060a2438132SYegappan Lakshmanan {
1061a2438132SYegappan Lakshmanan     char_u	buf[NUMBUFLEN];
1062a2438132SYegappan Lakshmanan     char_u	*needle;
1063a2438132SYegappan Lakshmanan     char_u	*haystack;
1064a2438132SYegappan Lakshmanan     char_u	*save_haystack;
1065a2438132SYegappan Lakshmanan     char_u	*pos;
1066a2438132SYegappan Lakshmanan     int		start_idx;
1067a2438132SYegappan Lakshmanan 
10684490ec4eSYegappan Lakshmanan     if (in_vim9script()
10694490ec4eSYegappan Lakshmanan 	    && (check_for_string_arg(argvars, 0) == FAIL
10704490ec4eSYegappan Lakshmanan 		|| check_for_string_arg(argvars, 1) == FAIL
10714490ec4eSYegappan Lakshmanan 		|| check_for_opt_number_arg(argvars, 2) == FAIL))
10724490ec4eSYegappan Lakshmanan 	return;
10734490ec4eSYegappan Lakshmanan 
1074a2438132SYegappan Lakshmanan     needle = tv_get_string_chk(&argvars[1]);
1075a2438132SYegappan Lakshmanan     save_haystack = haystack = tv_get_string_buf_chk(&argvars[0], buf);
1076a2438132SYegappan Lakshmanan     rettv->vval.v_number = -1;
1077a2438132SYegappan Lakshmanan     if (needle == NULL || haystack == NULL)
1078a2438132SYegappan Lakshmanan 	return;		// type error; errmsg already given
1079a2438132SYegappan Lakshmanan 
1080a2438132SYegappan Lakshmanan     if (argvars[2].v_type != VAR_UNKNOWN)
1081a2438132SYegappan Lakshmanan     {
1082a2438132SYegappan Lakshmanan 	int	    error = FALSE;
1083a2438132SYegappan Lakshmanan 
1084a2438132SYegappan Lakshmanan 	start_idx = (int)tv_get_number_chk(&argvars[2], &error);
1085a2438132SYegappan Lakshmanan 	if (error || start_idx >= (int)STRLEN(haystack))
1086a2438132SYegappan Lakshmanan 	    return;
1087a2438132SYegappan Lakshmanan 	if (start_idx >= 0)
1088a2438132SYegappan Lakshmanan 	    haystack += start_idx;
1089a2438132SYegappan Lakshmanan     }
1090a2438132SYegappan Lakshmanan 
1091a2438132SYegappan Lakshmanan     pos	= (char_u *)strstr((char *)haystack, (char *)needle);
1092a2438132SYegappan Lakshmanan     if (pos != NULL)
1093a2438132SYegappan Lakshmanan 	rettv->vval.v_number = (varnumber_T)(pos - save_haystack);
1094a2438132SYegappan Lakshmanan }
1095a2438132SYegappan Lakshmanan 
1096a2438132SYegappan Lakshmanan /*
1097a2438132SYegappan Lakshmanan  * "string()" function
1098a2438132SYegappan Lakshmanan  */
1099a2438132SYegappan Lakshmanan     void
f_string(typval_T * argvars,typval_T * rettv)1100a2438132SYegappan Lakshmanan f_string(typval_T *argvars, typval_T *rettv)
1101a2438132SYegappan Lakshmanan {
1102a2438132SYegappan Lakshmanan     char_u	*tofree;
1103a2438132SYegappan Lakshmanan     char_u	numbuf[NUMBUFLEN];
1104a2438132SYegappan Lakshmanan 
1105a2438132SYegappan Lakshmanan     rettv->v_type = VAR_STRING;
1106a2438132SYegappan Lakshmanan     rettv->vval.v_string = tv2string(&argvars[0], &tofree, numbuf,
1107a2438132SYegappan Lakshmanan 								get_copyID());
1108a2438132SYegappan Lakshmanan     // Make a copy if we have a value but it's not in allocated memory.
1109a2438132SYegappan Lakshmanan     if (rettv->vval.v_string != NULL && tofree == NULL)
1110a2438132SYegappan Lakshmanan 	rettv->vval.v_string = vim_strsave(rettv->vval.v_string);
1111a2438132SYegappan Lakshmanan }
1112a2438132SYegappan Lakshmanan 
1113a2438132SYegappan Lakshmanan /*
1114a2438132SYegappan Lakshmanan  * "strlen()" function
1115a2438132SYegappan Lakshmanan  */
1116a2438132SYegappan Lakshmanan     void
f_strlen(typval_T * argvars,typval_T * rettv)1117a2438132SYegappan Lakshmanan f_strlen(typval_T *argvars, typval_T *rettv)
1118a2438132SYegappan Lakshmanan {
11194490ec4eSYegappan Lakshmanan     if (in_vim9script()
11204490ec4eSYegappan Lakshmanan 	    && check_for_string_or_number_arg(argvars, 0) == FAIL)
11214490ec4eSYegappan Lakshmanan 	return;
11224490ec4eSYegappan Lakshmanan 
1123a2438132SYegappan Lakshmanan     rettv->vval.v_number = (varnumber_T)(STRLEN(
1124a2438132SYegappan Lakshmanan 					      tv_get_string(&argvars[0])));
1125a2438132SYegappan Lakshmanan }
1126a2438132SYegappan Lakshmanan 
1127a2438132SYegappan Lakshmanan     static void
strchar_common(typval_T * argvars,typval_T * rettv,int skipcc)1128a2438132SYegappan Lakshmanan strchar_common(typval_T *argvars, typval_T *rettv, int skipcc)
1129a2438132SYegappan Lakshmanan {
1130a2438132SYegappan Lakshmanan     char_u		*s = tv_get_string(&argvars[0]);
1131a2438132SYegappan Lakshmanan     varnumber_T		len = 0;
1132a2438132SYegappan Lakshmanan     int			(*func_mb_ptr2char_adv)(char_u **pp);
1133a2438132SYegappan Lakshmanan 
1134a2438132SYegappan Lakshmanan     func_mb_ptr2char_adv = skipcc ? mb_ptr2char_adv : mb_cptr2char_adv;
1135a2438132SYegappan Lakshmanan     while (*s != NUL)
1136a2438132SYegappan Lakshmanan     {
1137a2438132SYegappan Lakshmanan 	func_mb_ptr2char_adv(&s);
1138a2438132SYegappan Lakshmanan 	++len;
1139a2438132SYegappan Lakshmanan     }
1140a2438132SYegappan Lakshmanan     rettv->vval.v_number = len;
1141a2438132SYegappan Lakshmanan }
1142a2438132SYegappan Lakshmanan 
1143a2438132SYegappan Lakshmanan /*
1144a2438132SYegappan Lakshmanan  * "strcharlen()" function
1145a2438132SYegappan Lakshmanan  */
1146a2438132SYegappan Lakshmanan     void
f_strcharlen(typval_T * argvars,typval_T * rettv)1147a2438132SYegappan Lakshmanan f_strcharlen(typval_T *argvars, typval_T *rettv)
1148a2438132SYegappan Lakshmanan {
11494490ec4eSYegappan Lakshmanan     if (in_vim9script()
11504490ec4eSYegappan Lakshmanan 	    && check_for_string_or_number_arg(argvars, 0) == FAIL)
11514490ec4eSYegappan Lakshmanan 	return;
11524490ec4eSYegappan Lakshmanan 
1153a2438132SYegappan Lakshmanan     strchar_common(argvars, rettv, TRUE);
1154a2438132SYegappan Lakshmanan }
1155a2438132SYegappan Lakshmanan 
1156a2438132SYegappan Lakshmanan /*
1157a2438132SYegappan Lakshmanan  * "strchars()" function
1158a2438132SYegappan Lakshmanan  */
1159a2438132SYegappan Lakshmanan     void
f_strchars(typval_T * argvars,typval_T * rettv)1160a2438132SYegappan Lakshmanan f_strchars(typval_T *argvars, typval_T *rettv)
1161a2438132SYegappan Lakshmanan {
1162a2438132SYegappan Lakshmanan     varnumber_T		skipcc = FALSE;
1163a2438132SYegappan Lakshmanan 
1164a9a7c0c6SYegappan Lakshmanan     if (in_vim9script()
1165a9a7c0c6SYegappan Lakshmanan 	    && (check_for_string_arg(argvars, 0) == FAIL
116683494b4aSYegappan Lakshmanan 		|| check_for_opt_bool_arg(argvars, 1) == FAIL))
1167a9a7c0c6SYegappan Lakshmanan 	return;
1168a9a7c0c6SYegappan Lakshmanan 
1169a2438132SYegappan Lakshmanan     if (argvars[1].v_type != VAR_UNKNOWN)
1170a2438132SYegappan Lakshmanan 	skipcc = tv_get_bool(&argvars[1]);
1171a2438132SYegappan Lakshmanan     if (skipcc < 0 || skipcc > 1)
1172a2438132SYegappan Lakshmanan 	semsg(_(e_using_number_as_bool_nr), skipcc);
1173a2438132SYegappan Lakshmanan     else
1174a2438132SYegappan Lakshmanan 	strchar_common(argvars, rettv, skipcc);
1175a2438132SYegappan Lakshmanan }
1176a2438132SYegappan Lakshmanan 
1177a2438132SYegappan Lakshmanan /*
1178a2438132SYegappan Lakshmanan  * "strdisplaywidth()" function
1179a2438132SYegappan Lakshmanan  */
1180a2438132SYegappan Lakshmanan     void
f_strdisplaywidth(typval_T * argvars,typval_T * rettv)1181a2438132SYegappan Lakshmanan f_strdisplaywidth(typval_T *argvars, typval_T *rettv)
1182a2438132SYegappan Lakshmanan {
11831a71d31bSYegappan Lakshmanan     char_u	*s;
1184a2438132SYegappan Lakshmanan     int		col = 0;
1185a2438132SYegappan Lakshmanan 
11861a71d31bSYegappan Lakshmanan     rettv->vval.v_number = -1;
11871a71d31bSYegappan Lakshmanan 
11881a71d31bSYegappan Lakshmanan     if (in_vim9script()
11891a71d31bSYegappan Lakshmanan 	    && (check_for_string_arg(argvars, 0) == FAIL
119083494b4aSYegappan Lakshmanan 		|| check_for_opt_number_arg(argvars, 1) == FAIL))
11911a71d31bSYegappan Lakshmanan 	return;
11921a71d31bSYegappan Lakshmanan 
11931a71d31bSYegappan Lakshmanan     s = tv_get_string(&argvars[0]);
1194a2438132SYegappan Lakshmanan     if (argvars[1].v_type != VAR_UNKNOWN)
1195a2438132SYegappan Lakshmanan 	col = (int)tv_get_number(&argvars[1]);
1196a2438132SYegappan Lakshmanan 
1197a2438132SYegappan Lakshmanan     rettv->vval.v_number = (varnumber_T)(linetabsize_col(col, s) - col);
1198a2438132SYegappan Lakshmanan }
1199a2438132SYegappan Lakshmanan 
1200a2438132SYegappan Lakshmanan /*
1201a2438132SYegappan Lakshmanan  * "strwidth()" function
1202a2438132SYegappan Lakshmanan  */
1203a2438132SYegappan Lakshmanan     void
f_strwidth(typval_T * argvars,typval_T * rettv)1204a2438132SYegappan Lakshmanan f_strwidth(typval_T *argvars, typval_T *rettv)
1205a2438132SYegappan Lakshmanan {
12064490ec4eSYegappan Lakshmanan     char_u	*s;
1207a2438132SYegappan Lakshmanan 
12084490ec4eSYegappan Lakshmanan     if (in_vim9script() && check_for_string_arg(argvars, 0) == FAIL)
12094490ec4eSYegappan Lakshmanan 	return;
12104490ec4eSYegappan Lakshmanan 
12114490ec4eSYegappan Lakshmanan     s = tv_get_string_strict(&argvars[0]);
1212a2438132SYegappan Lakshmanan     rettv->vval.v_number = (varnumber_T)(mb_string2cells(s, -1));
1213a2438132SYegappan Lakshmanan }
1214a2438132SYegappan Lakshmanan 
1215a2438132SYegappan Lakshmanan /*
1216a2438132SYegappan Lakshmanan  * "strcharpart()" function
1217a2438132SYegappan Lakshmanan  */
1218a2438132SYegappan Lakshmanan     void
f_strcharpart(typval_T * argvars,typval_T * rettv)1219a2438132SYegappan Lakshmanan f_strcharpart(typval_T *argvars, typval_T *rettv)
1220a2438132SYegappan Lakshmanan {
1221a2438132SYegappan Lakshmanan     char_u	*p;
1222a2438132SYegappan Lakshmanan     int		nchar;
1223a2438132SYegappan Lakshmanan     int		nbyte = 0;
1224a2438132SYegappan Lakshmanan     int		charlen;
1225a2438132SYegappan Lakshmanan     int		skipcc = FALSE;
1226a2438132SYegappan Lakshmanan     int		len = 0;
1227a2438132SYegappan Lakshmanan     int		slen;
1228a2438132SYegappan Lakshmanan     int		error = FALSE;
1229a2438132SYegappan Lakshmanan 
123083494b4aSYegappan Lakshmanan     if (in_vim9script()
123183494b4aSYegappan Lakshmanan 	    && (check_for_string_arg(argvars, 0) == FAIL
123283494b4aSYegappan Lakshmanan 		|| check_for_number_arg(argvars, 1) == FAIL
123383494b4aSYegappan Lakshmanan 		|| check_for_opt_number_arg(argvars, 2) == FAIL
123483494b4aSYegappan Lakshmanan 		|| (argvars[2].v_type != VAR_UNKNOWN
123583494b4aSYegappan Lakshmanan 		    && check_for_opt_bool_arg(argvars, 3) == FAIL)))
123683494b4aSYegappan Lakshmanan 	return;
123783494b4aSYegappan Lakshmanan 
1238a2438132SYegappan Lakshmanan     p = tv_get_string(&argvars[0]);
1239a2438132SYegappan Lakshmanan     slen = (int)STRLEN(p);
1240a2438132SYegappan Lakshmanan 
1241a2438132SYegappan Lakshmanan     nchar = (int)tv_get_number_chk(&argvars[1], &error);
1242a2438132SYegappan Lakshmanan     if (!error)
1243a2438132SYegappan Lakshmanan     {
1244a2438132SYegappan Lakshmanan 	if (argvars[2].v_type != VAR_UNKNOWN
1245a2438132SYegappan Lakshmanan 					   && argvars[3].v_type != VAR_UNKNOWN)
1246a2438132SYegappan Lakshmanan 	{
1247a2438132SYegappan Lakshmanan 	    skipcc = tv_get_bool(&argvars[3]);
1248a2438132SYegappan Lakshmanan 	    if (skipcc < 0 || skipcc > 1)
1249a2438132SYegappan Lakshmanan 	    {
1250a2438132SYegappan Lakshmanan 		semsg(_(e_using_number_as_bool_nr), skipcc);
1251a2438132SYegappan Lakshmanan 		return;
1252a2438132SYegappan Lakshmanan 	    }
1253a2438132SYegappan Lakshmanan 	}
1254a2438132SYegappan Lakshmanan 
1255a2438132SYegappan Lakshmanan 	if (nchar > 0)
1256a2438132SYegappan Lakshmanan 	    while (nchar > 0 && nbyte < slen)
1257a2438132SYegappan Lakshmanan 	    {
1258a2438132SYegappan Lakshmanan 		if (skipcc)
1259a2438132SYegappan Lakshmanan 		    nbyte += mb_ptr2len(p + nbyte);
1260a2438132SYegappan Lakshmanan 		else
1261a2438132SYegappan Lakshmanan 		    nbyte += MB_CPTR2LEN(p + nbyte);
1262a2438132SYegappan Lakshmanan 		--nchar;
1263a2438132SYegappan Lakshmanan 	    }
1264a2438132SYegappan Lakshmanan 	else
1265a2438132SYegappan Lakshmanan 	    nbyte = nchar;
1266a2438132SYegappan Lakshmanan 	if (argvars[2].v_type != VAR_UNKNOWN)
1267a2438132SYegappan Lakshmanan 	{
1268a2438132SYegappan Lakshmanan 	    charlen = (int)tv_get_number(&argvars[2]);
1269a2438132SYegappan Lakshmanan 	    while (charlen > 0 && nbyte + len < slen)
1270a2438132SYegappan Lakshmanan 	    {
1271a2438132SYegappan Lakshmanan 		int off = nbyte + len;
1272a2438132SYegappan Lakshmanan 
1273a2438132SYegappan Lakshmanan 		if (off < 0)
1274a2438132SYegappan Lakshmanan 		    len += 1;
1275a2438132SYegappan Lakshmanan 		else
1276a2438132SYegappan Lakshmanan 		{
1277a2438132SYegappan Lakshmanan 		    if (skipcc)
1278a2438132SYegappan Lakshmanan 			len += mb_ptr2len(p + off);
1279a2438132SYegappan Lakshmanan 		    else
1280a2438132SYegappan Lakshmanan 			len += MB_CPTR2LEN(p + off);
1281a2438132SYegappan Lakshmanan 		}
1282a2438132SYegappan Lakshmanan 		--charlen;
1283a2438132SYegappan Lakshmanan 	    }
1284a2438132SYegappan Lakshmanan 	}
1285a2438132SYegappan Lakshmanan 	else
1286a2438132SYegappan Lakshmanan 	    len = slen - nbyte;    // default: all bytes that are available.
1287a2438132SYegappan Lakshmanan     }
1288a2438132SYegappan Lakshmanan 
12898ee52affSYegappan Lakshmanan     // Only return the overlap between the specified part and the actual
12908ee52affSYegappan Lakshmanan     // string.
1291a2438132SYegappan Lakshmanan     if (nbyte < 0)
1292a2438132SYegappan Lakshmanan     {
1293a2438132SYegappan Lakshmanan 	len += nbyte;
1294a2438132SYegappan Lakshmanan 	nbyte = 0;
1295a2438132SYegappan Lakshmanan     }
1296a2438132SYegappan Lakshmanan     else if (nbyte > slen)
1297a2438132SYegappan Lakshmanan 	nbyte = slen;
1298a2438132SYegappan Lakshmanan     if (len < 0)
1299a2438132SYegappan Lakshmanan 	len = 0;
1300a2438132SYegappan Lakshmanan     else if (nbyte + len > slen)
1301a2438132SYegappan Lakshmanan 	len = slen - nbyte;
1302a2438132SYegappan Lakshmanan 
1303a2438132SYegappan Lakshmanan     rettv->v_type = VAR_STRING;
1304a2438132SYegappan Lakshmanan     rettv->vval.v_string = vim_strnsave(p + nbyte, len);
1305a2438132SYegappan Lakshmanan }
1306a2438132SYegappan Lakshmanan 
1307a2438132SYegappan Lakshmanan /*
1308a2438132SYegappan Lakshmanan  * "strpart()" function
1309a2438132SYegappan Lakshmanan  */
1310a2438132SYegappan Lakshmanan     void
f_strpart(typval_T * argvars,typval_T * rettv)1311a2438132SYegappan Lakshmanan f_strpart(typval_T *argvars, typval_T *rettv)
1312a2438132SYegappan Lakshmanan {
1313a2438132SYegappan Lakshmanan     char_u	*p;
1314a2438132SYegappan Lakshmanan     int		n;
1315a2438132SYegappan Lakshmanan     int		len;
1316a2438132SYegappan Lakshmanan     int		slen;
1317a2438132SYegappan Lakshmanan     int		error = FALSE;
1318a2438132SYegappan Lakshmanan 
131983494b4aSYegappan Lakshmanan     if (in_vim9script()
132083494b4aSYegappan Lakshmanan 	    && (check_for_string_arg(argvars, 0) == FAIL
132183494b4aSYegappan Lakshmanan 		|| check_for_number_arg(argvars, 1) == FAIL
132283494b4aSYegappan Lakshmanan 		|| check_for_opt_number_arg(argvars, 2) == FAIL
132383494b4aSYegappan Lakshmanan 		|| (argvars[2].v_type != VAR_UNKNOWN
132483494b4aSYegappan Lakshmanan 		    && check_for_opt_bool_arg(argvars, 3) == FAIL)))
132583494b4aSYegappan Lakshmanan 	return;
132683494b4aSYegappan Lakshmanan 
1327a2438132SYegappan Lakshmanan     p = tv_get_string(&argvars[0]);
1328a2438132SYegappan Lakshmanan     slen = (int)STRLEN(p);
1329a2438132SYegappan Lakshmanan 
1330a2438132SYegappan Lakshmanan     n = (int)tv_get_number_chk(&argvars[1], &error);
1331a2438132SYegappan Lakshmanan     if (error)
1332a2438132SYegappan Lakshmanan 	len = 0;
1333a2438132SYegappan Lakshmanan     else if (argvars[2].v_type != VAR_UNKNOWN)
1334a2438132SYegappan Lakshmanan 	len = (int)tv_get_number(&argvars[2]);
1335a2438132SYegappan Lakshmanan     else
1336a2438132SYegappan Lakshmanan 	len = slen - n;	    // default len: all bytes that are available.
1337a2438132SYegappan Lakshmanan 
1338a2438132SYegappan Lakshmanan     // Only return the overlap between the specified part and the actual
1339a2438132SYegappan Lakshmanan     // string.
1340a2438132SYegappan Lakshmanan     if (n < 0)
1341a2438132SYegappan Lakshmanan     {
1342a2438132SYegappan Lakshmanan 	len += n;
1343a2438132SYegappan Lakshmanan 	n = 0;
1344a2438132SYegappan Lakshmanan     }
1345a2438132SYegappan Lakshmanan     else if (n > slen)
1346a2438132SYegappan Lakshmanan 	n = slen;
1347a2438132SYegappan Lakshmanan     if (len < 0)
1348a2438132SYegappan Lakshmanan 	len = 0;
1349a2438132SYegappan Lakshmanan     else if (n + len > slen)
1350a2438132SYegappan Lakshmanan 	len = slen - n;
1351a2438132SYegappan Lakshmanan 
1352a2438132SYegappan Lakshmanan     if (argvars[2].v_type != VAR_UNKNOWN && argvars[3].v_type != VAR_UNKNOWN)
1353a2438132SYegappan Lakshmanan     {
1354a2438132SYegappan Lakshmanan 	int off;
1355a2438132SYegappan Lakshmanan 
1356a2438132SYegappan Lakshmanan 	// length in characters
1357a2438132SYegappan Lakshmanan 	for (off = n; off < slen && len > 0; --len)
1358a2438132SYegappan Lakshmanan 	    off += mb_ptr2len(p + off);
1359a2438132SYegappan Lakshmanan 	len = off - n;
1360a2438132SYegappan Lakshmanan     }
1361a2438132SYegappan Lakshmanan 
1362a2438132SYegappan Lakshmanan     rettv->v_type = VAR_STRING;
1363a2438132SYegappan Lakshmanan     rettv->vval.v_string = vim_strnsave(p + n, len);
1364a2438132SYegappan Lakshmanan }
1365a2438132SYegappan Lakshmanan 
1366a2438132SYegappan Lakshmanan /*
1367a2438132SYegappan Lakshmanan  * "strridx()" function
1368a2438132SYegappan Lakshmanan  */
1369a2438132SYegappan Lakshmanan     void
f_strridx(typval_T * argvars,typval_T * rettv)1370a2438132SYegappan Lakshmanan f_strridx(typval_T *argvars, typval_T *rettv)
1371a2438132SYegappan Lakshmanan {
1372a2438132SYegappan Lakshmanan     char_u	buf[NUMBUFLEN];
1373a2438132SYegappan Lakshmanan     char_u	*needle;
1374a2438132SYegappan Lakshmanan     char_u	*haystack;
1375a2438132SYegappan Lakshmanan     char_u	*rest;
1376a2438132SYegappan Lakshmanan     char_u	*lastmatch = NULL;
1377a2438132SYegappan Lakshmanan     int		haystack_len, end_idx;
1378a2438132SYegappan Lakshmanan 
13794490ec4eSYegappan Lakshmanan     if (in_vim9script()
13804490ec4eSYegappan Lakshmanan 	    && (check_for_string_arg(argvars, 0) == FAIL
13814490ec4eSYegappan Lakshmanan 		|| check_for_string_arg(argvars, 1) == FAIL
13824490ec4eSYegappan Lakshmanan 		|| check_for_opt_number_arg(argvars, 2) == FAIL))
13834490ec4eSYegappan Lakshmanan 	return;
13844490ec4eSYegappan Lakshmanan 
1385a2438132SYegappan Lakshmanan     needle = tv_get_string_chk(&argvars[1]);
1386a2438132SYegappan Lakshmanan     haystack = tv_get_string_buf_chk(&argvars[0], buf);
1387a2438132SYegappan Lakshmanan 
1388a2438132SYegappan Lakshmanan     rettv->vval.v_number = -1;
1389a2438132SYegappan Lakshmanan     if (needle == NULL || haystack == NULL)
1390a2438132SYegappan Lakshmanan 	return;		// type error; errmsg already given
1391a2438132SYegappan Lakshmanan 
1392a2438132SYegappan Lakshmanan     haystack_len = (int)STRLEN(haystack);
1393a2438132SYegappan Lakshmanan     if (argvars[2].v_type != VAR_UNKNOWN)
1394a2438132SYegappan Lakshmanan     {
1395a2438132SYegappan Lakshmanan 	// Third argument: upper limit for index
1396a2438132SYegappan Lakshmanan 	end_idx = (int)tv_get_number_chk(&argvars[2], NULL);
1397a2438132SYegappan Lakshmanan 	if (end_idx < 0)
1398a2438132SYegappan Lakshmanan 	    return;	// can never find a match
1399a2438132SYegappan Lakshmanan     }
1400a2438132SYegappan Lakshmanan     else
1401a2438132SYegappan Lakshmanan 	end_idx = haystack_len;
1402a2438132SYegappan Lakshmanan 
1403a2438132SYegappan Lakshmanan     if (*needle == NUL)
1404a2438132SYegappan Lakshmanan     {
1405a2438132SYegappan Lakshmanan 	// Empty string matches past the end.
1406a2438132SYegappan Lakshmanan 	lastmatch = haystack + end_idx;
1407a2438132SYegappan Lakshmanan     }
1408a2438132SYegappan Lakshmanan     else
1409a2438132SYegappan Lakshmanan     {
1410a2438132SYegappan Lakshmanan 	for (rest = haystack; *rest != '\0'; ++rest)
1411a2438132SYegappan Lakshmanan 	{
1412a2438132SYegappan Lakshmanan 	    rest = (char_u *)strstr((char *)rest, (char *)needle);
1413a2438132SYegappan Lakshmanan 	    if (rest == NULL || rest > haystack + end_idx)
1414a2438132SYegappan Lakshmanan 		break;
1415a2438132SYegappan Lakshmanan 	    lastmatch = rest;
1416a2438132SYegappan Lakshmanan 	}
1417a2438132SYegappan Lakshmanan     }
1418a2438132SYegappan Lakshmanan 
1419a2438132SYegappan Lakshmanan     if (lastmatch == NULL)
1420a2438132SYegappan Lakshmanan 	rettv->vval.v_number = -1;
1421a2438132SYegappan Lakshmanan     else
1422a2438132SYegappan Lakshmanan 	rettv->vval.v_number = (varnumber_T)(lastmatch - haystack);
1423a2438132SYegappan Lakshmanan }
1424a2438132SYegappan Lakshmanan 
1425a2438132SYegappan Lakshmanan /*
1426a2438132SYegappan Lakshmanan  * "strtrans()" function
1427a2438132SYegappan Lakshmanan  */
1428a2438132SYegappan Lakshmanan     void
f_strtrans(typval_T * argvars,typval_T * rettv)1429a2438132SYegappan Lakshmanan f_strtrans(typval_T *argvars, typval_T *rettv)
1430a2438132SYegappan Lakshmanan {
14314490ec4eSYegappan Lakshmanan     if (in_vim9script() && check_for_string_arg(argvars, 0) == FAIL)
14324490ec4eSYegappan Lakshmanan 	return;
14334490ec4eSYegappan Lakshmanan 
1434a2438132SYegappan Lakshmanan     rettv->v_type = VAR_STRING;
1435a2438132SYegappan Lakshmanan     rettv->vval.v_string = transstr(tv_get_string(&argvars[0]));
1436a2438132SYegappan Lakshmanan }
1437a2438132SYegappan Lakshmanan 
1438a2438132SYegappan Lakshmanan /*
1439a2438132SYegappan Lakshmanan  * "tolower(string)" function
1440a2438132SYegappan Lakshmanan  */
1441a2438132SYegappan Lakshmanan     void
f_tolower(typval_T * argvars,typval_T * rettv)1442a2438132SYegappan Lakshmanan f_tolower(typval_T *argvars, typval_T *rettv)
1443a2438132SYegappan Lakshmanan {
14444490ec4eSYegappan Lakshmanan     if (in_vim9script() && check_for_string_arg(argvars, 0) == FAIL)
14454490ec4eSYegappan Lakshmanan 	return;
14464490ec4eSYegappan Lakshmanan 
1447a2438132SYegappan Lakshmanan     rettv->v_type = VAR_STRING;
1448a2438132SYegappan Lakshmanan     rettv->vval.v_string = strlow_save(tv_get_string(&argvars[0]));
1449a2438132SYegappan Lakshmanan }
1450a2438132SYegappan Lakshmanan 
1451a2438132SYegappan Lakshmanan /*
1452a2438132SYegappan Lakshmanan  * "toupper(string)" function
1453a2438132SYegappan Lakshmanan  */
1454a2438132SYegappan Lakshmanan     void
f_toupper(typval_T * argvars,typval_T * rettv)1455a2438132SYegappan Lakshmanan f_toupper(typval_T *argvars, typval_T *rettv)
1456a2438132SYegappan Lakshmanan {
14574490ec4eSYegappan Lakshmanan     if (in_vim9script() && check_for_string_arg(argvars, 0) == FAIL)
14584490ec4eSYegappan Lakshmanan 	return;
14594490ec4eSYegappan Lakshmanan 
1460a2438132SYegappan Lakshmanan     rettv->v_type = VAR_STRING;
1461a2438132SYegappan Lakshmanan     rettv->vval.v_string = strup_save(tv_get_string(&argvars[0]));
1462a2438132SYegappan Lakshmanan }
1463a2438132SYegappan Lakshmanan 
1464a2438132SYegappan Lakshmanan /*
1465a2438132SYegappan Lakshmanan  * "tr(string, fromstr, tostr)" function
1466a2438132SYegappan Lakshmanan  */
1467a2438132SYegappan Lakshmanan     void
f_tr(typval_T * argvars,typval_T * rettv)1468a2438132SYegappan Lakshmanan f_tr(typval_T *argvars, typval_T *rettv)
1469a2438132SYegappan Lakshmanan {
1470a2438132SYegappan Lakshmanan     char_u	*in_str;
1471a2438132SYegappan Lakshmanan     char_u	*fromstr;
1472a2438132SYegappan Lakshmanan     char_u	*tostr;
1473a2438132SYegappan Lakshmanan     char_u	*p;
1474a2438132SYegappan Lakshmanan     int		inlen;
1475a2438132SYegappan Lakshmanan     int		fromlen;
1476a2438132SYegappan Lakshmanan     int		tolen;
1477a2438132SYegappan Lakshmanan     int		idx;
1478a2438132SYegappan Lakshmanan     char_u	*cpstr;
1479a2438132SYegappan Lakshmanan     int		cplen;
1480a2438132SYegappan Lakshmanan     int		first = TRUE;
1481a2438132SYegappan Lakshmanan     char_u	buf[NUMBUFLEN];
1482a2438132SYegappan Lakshmanan     char_u	buf2[NUMBUFLEN];
1483a2438132SYegappan Lakshmanan     garray_T	ga;
1484a2438132SYegappan Lakshmanan 
14854490ec4eSYegappan Lakshmanan     if (in_vim9script()
14864490ec4eSYegappan Lakshmanan 	    && (check_for_string_arg(argvars, 0) == FAIL
14874490ec4eSYegappan Lakshmanan 		|| check_for_string_arg(argvars, 1) == FAIL
14884490ec4eSYegappan Lakshmanan 		|| check_for_string_arg(argvars, 2) == FAIL))
14894490ec4eSYegappan Lakshmanan 	return;
14904490ec4eSYegappan Lakshmanan 
1491a2438132SYegappan Lakshmanan     in_str = tv_get_string(&argvars[0]);
1492a2438132SYegappan Lakshmanan     fromstr = tv_get_string_buf_chk(&argvars[1], buf);
1493a2438132SYegappan Lakshmanan     tostr = tv_get_string_buf_chk(&argvars[2], buf2);
1494a2438132SYegappan Lakshmanan 
1495a2438132SYegappan Lakshmanan     // Default return value: empty string.
1496a2438132SYegappan Lakshmanan     rettv->v_type = VAR_STRING;
1497a2438132SYegappan Lakshmanan     rettv->vval.v_string = NULL;
1498a2438132SYegappan Lakshmanan     if (fromstr == NULL || tostr == NULL)
1499a2438132SYegappan Lakshmanan 	    return;		// type error; errmsg already given
1500a2438132SYegappan Lakshmanan     ga_init2(&ga, (int)sizeof(char), 80);
1501a2438132SYegappan Lakshmanan 
1502a2438132SYegappan Lakshmanan     if (!has_mbyte)
1503a2438132SYegappan Lakshmanan 	// not multi-byte: fromstr and tostr must be the same length
1504a2438132SYegappan Lakshmanan 	if (STRLEN(fromstr) != STRLEN(tostr))
1505a2438132SYegappan Lakshmanan 	{
1506a2438132SYegappan Lakshmanan error:
1507a2438132SYegappan Lakshmanan 	    semsg(_(e_invarg2), fromstr);
1508a2438132SYegappan Lakshmanan 	    ga_clear(&ga);
1509a2438132SYegappan Lakshmanan 	    return;
1510a2438132SYegappan Lakshmanan 	}
1511a2438132SYegappan Lakshmanan 
1512a2438132SYegappan Lakshmanan     // fromstr and tostr have to contain the same number of chars
1513a2438132SYegappan Lakshmanan     while (*in_str != NUL)
1514a2438132SYegappan Lakshmanan     {
1515a2438132SYegappan Lakshmanan 	if (has_mbyte)
1516a2438132SYegappan Lakshmanan 	{
1517a2438132SYegappan Lakshmanan 	    inlen = (*mb_ptr2len)(in_str);
1518a2438132SYegappan Lakshmanan 	    cpstr = in_str;
1519a2438132SYegappan Lakshmanan 	    cplen = inlen;
1520a2438132SYegappan Lakshmanan 	    idx = 0;
1521a2438132SYegappan Lakshmanan 	    for (p = fromstr; *p != NUL; p += fromlen)
1522a2438132SYegappan Lakshmanan 	    {
1523a2438132SYegappan Lakshmanan 		fromlen = (*mb_ptr2len)(p);
1524a2438132SYegappan Lakshmanan 		if (fromlen == inlen && STRNCMP(in_str, p, inlen) == 0)
1525a2438132SYegappan Lakshmanan 		{
1526a2438132SYegappan Lakshmanan 		    for (p = tostr; *p != NUL; p += tolen)
1527a2438132SYegappan Lakshmanan 		    {
1528a2438132SYegappan Lakshmanan 			tolen = (*mb_ptr2len)(p);
1529a2438132SYegappan Lakshmanan 			if (idx-- == 0)
1530a2438132SYegappan Lakshmanan 			{
1531a2438132SYegappan Lakshmanan 			    cplen = tolen;
1532a2438132SYegappan Lakshmanan 			    cpstr = p;
1533a2438132SYegappan Lakshmanan 			    break;
1534a2438132SYegappan Lakshmanan 			}
1535a2438132SYegappan Lakshmanan 		    }
1536a2438132SYegappan Lakshmanan 		    if (*p == NUL)	// tostr is shorter than fromstr
1537a2438132SYegappan Lakshmanan 			goto error;
1538a2438132SYegappan Lakshmanan 		    break;
1539a2438132SYegappan Lakshmanan 		}
1540a2438132SYegappan Lakshmanan 		++idx;
1541a2438132SYegappan Lakshmanan 	    }
1542a2438132SYegappan Lakshmanan 
1543a2438132SYegappan Lakshmanan 	    if (first && cpstr == in_str)
1544a2438132SYegappan Lakshmanan 	    {
1545a2438132SYegappan Lakshmanan 		// Check that fromstr and tostr have the same number of
1546a2438132SYegappan Lakshmanan 		// (multi-byte) characters.  Done only once when a character
1547a2438132SYegappan Lakshmanan 		// of in_str doesn't appear in fromstr.
1548a2438132SYegappan Lakshmanan 		first = FALSE;
1549a2438132SYegappan Lakshmanan 		for (p = tostr; *p != NUL; p += tolen)
1550a2438132SYegappan Lakshmanan 		{
1551a2438132SYegappan Lakshmanan 		    tolen = (*mb_ptr2len)(p);
1552a2438132SYegappan Lakshmanan 		    --idx;
1553a2438132SYegappan Lakshmanan 		}
1554a2438132SYegappan Lakshmanan 		if (idx != 0)
1555a2438132SYegappan Lakshmanan 		    goto error;
1556a2438132SYegappan Lakshmanan 	    }
1557a2438132SYegappan Lakshmanan 
1558a2438132SYegappan Lakshmanan 	    (void)ga_grow(&ga, cplen);
1559a2438132SYegappan Lakshmanan 	    mch_memmove((char *)ga.ga_data + ga.ga_len, cpstr, (size_t)cplen);
1560a2438132SYegappan Lakshmanan 	    ga.ga_len += cplen;
1561a2438132SYegappan Lakshmanan 
1562a2438132SYegappan Lakshmanan 	    in_str += inlen;
1563a2438132SYegappan Lakshmanan 	}
1564a2438132SYegappan Lakshmanan 	else
1565a2438132SYegappan Lakshmanan 	{
1566a2438132SYegappan Lakshmanan 	    // When not using multi-byte chars we can do it faster.
1567a2438132SYegappan Lakshmanan 	    p = vim_strchr(fromstr, *in_str);
1568a2438132SYegappan Lakshmanan 	    if (p != NULL)
1569a2438132SYegappan Lakshmanan 		ga_append(&ga, tostr[p - fromstr]);
1570a2438132SYegappan Lakshmanan 	    else
1571a2438132SYegappan Lakshmanan 		ga_append(&ga, *in_str);
1572a2438132SYegappan Lakshmanan 	    ++in_str;
1573a2438132SYegappan Lakshmanan 	}
1574a2438132SYegappan Lakshmanan     }
1575a2438132SYegappan Lakshmanan 
1576a2438132SYegappan Lakshmanan     // add a terminating NUL
1577a2438132SYegappan Lakshmanan     (void)ga_grow(&ga, 1);
1578a2438132SYegappan Lakshmanan     ga_append(&ga, NUL);
1579a2438132SYegappan Lakshmanan 
1580a2438132SYegappan Lakshmanan     rettv->vval.v_string = ga.ga_data;
1581a2438132SYegappan Lakshmanan }
1582a2438132SYegappan Lakshmanan 
1583a2438132SYegappan Lakshmanan /*
1584a2438132SYegappan Lakshmanan  * "trim({expr})" function
1585a2438132SYegappan Lakshmanan  */
1586a2438132SYegappan Lakshmanan     void
f_trim(typval_T * argvars,typval_T * rettv)1587a2438132SYegappan Lakshmanan f_trim(typval_T *argvars, typval_T *rettv)
1588a2438132SYegappan Lakshmanan {
1589a2438132SYegappan Lakshmanan     char_u	buf1[NUMBUFLEN];
1590a2438132SYegappan Lakshmanan     char_u	buf2[NUMBUFLEN];
15914490ec4eSYegappan Lakshmanan     char_u	*head;
1592a2438132SYegappan Lakshmanan     char_u	*mask = NULL;
1593a2438132SYegappan Lakshmanan     char_u	*tail;
1594a2438132SYegappan Lakshmanan     char_u	*prev;
1595a2438132SYegappan Lakshmanan     char_u	*p;
1596a2438132SYegappan Lakshmanan     int		c1;
1597a2438132SYegappan Lakshmanan     int		dir = 0;
1598a2438132SYegappan Lakshmanan 
1599a2438132SYegappan Lakshmanan     rettv->v_type = VAR_STRING;
1600a2438132SYegappan Lakshmanan     rettv->vval.v_string = NULL;
16014490ec4eSYegappan Lakshmanan 
16024490ec4eSYegappan Lakshmanan     if (in_vim9script()
16034490ec4eSYegappan Lakshmanan 	    && (check_for_string_arg(argvars, 0) == FAIL
16044490ec4eSYegappan Lakshmanan 		|| check_for_opt_string_arg(argvars, 1) == FAIL
16054490ec4eSYegappan Lakshmanan 		|| (argvars[1].v_type != VAR_UNKNOWN
16064490ec4eSYegappan Lakshmanan 		    && check_for_opt_number_arg(argvars, 2) == FAIL)))
16074490ec4eSYegappan Lakshmanan 	return;
16084490ec4eSYegappan Lakshmanan 
16094490ec4eSYegappan Lakshmanan     head = tv_get_string_buf_chk(&argvars[0], buf1);
1610a2438132SYegappan Lakshmanan     if (head == NULL)
1611a2438132SYegappan Lakshmanan 	return;
1612a2438132SYegappan Lakshmanan 
1613a2438132SYegappan Lakshmanan     if (argvars[1].v_type != VAR_UNKNOWN && argvars[1].v_type != VAR_STRING)
1614a2438132SYegappan Lakshmanan     {
1615a2438132SYegappan Lakshmanan 	semsg(_(e_invarg2), tv_get_string(&argvars[1]));
1616a2438132SYegappan Lakshmanan 	return;
1617a2438132SYegappan Lakshmanan     }
1618a2438132SYegappan Lakshmanan 
1619a2438132SYegappan Lakshmanan     if (argvars[1].v_type == VAR_STRING)
1620a2438132SYegappan Lakshmanan     {
1621a2438132SYegappan Lakshmanan 	mask = tv_get_string_buf_chk(&argvars[1], buf2);
1622a2438132SYegappan Lakshmanan 
1623a2438132SYegappan Lakshmanan 	if (argvars[2].v_type != VAR_UNKNOWN)
1624a2438132SYegappan Lakshmanan 	{
1625a2438132SYegappan Lakshmanan 	    int	error = 0;
1626a2438132SYegappan Lakshmanan 
1627a2438132SYegappan Lakshmanan 	    // leading or trailing characters to trim
1628a2438132SYegappan Lakshmanan 	    dir = (int)tv_get_number_chk(&argvars[2], &error);
1629a2438132SYegappan Lakshmanan 	    if (error)
1630a2438132SYegappan Lakshmanan 		return;
1631a2438132SYegappan Lakshmanan 	    if (dir < 0 || dir > 2)
1632a2438132SYegappan Lakshmanan 	    {
1633a2438132SYegappan Lakshmanan 		semsg(_(e_invarg2), tv_get_string(&argvars[2]));
1634a2438132SYegappan Lakshmanan 		return;
1635a2438132SYegappan Lakshmanan 	    }
1636a2438132SYegappan Lakshmanan 	}
1637a2438132SYegappan Lakshmanan     }
1638a2438132SYegappan Lakshmanan 
1639a2438132SYegappan Lakshmanan     if (dir == 0 || dir == 1)
1640a2438132SYegappan Lakshmanan     {
1641a2438132SYegappan Lakshmanan 	// Trim leading characters
1642a2438132SYegappan Lakshmanan 	while (*head != NUL)
1643a2438132SYegappan Lakshmanan 	{
1644a2438132SYegappan Lakshmanan 	    c1 = PTR2CHAR(head);
1645a2438132SYegappan Lakshmanan 	    if (mask == NULL)
1646a2438132SYegappan Lakshmanan 	    {
1647a2438132SYegappan Lakshmanan 		if (c1 > ' ' && c1 != 0xa0)
1648a2438132SYegappan Lakshmanan 		    break;
1649a2438132SYegappan Lakshmanan 	    }
1650a2438132SYegappan Lakshmanan 	    else
1651a2438132SYegappan Lakshmanan 	    {
1652a2438132SYegappan Lakshmanan 		for (p = mask; *p != NUL; MB_PTR_ADV(p))
1653a2438132SYegappan Lakshmanan 		    if (c1 == PTR2CHAR(p))
1654a2438132SYegappan Lakshmanan 			break;
1655a2438132SYegappan Lakshmanan 		if (*p == NUL)
1656a2438132SYegappan Lakshmanan 		    break;
1657a2438132SYegappan Lakshmanan 	    }
1658a2438132SYegappan Lakshmanan 	    MB_PTR_ADV(head);
1659a2438132SYegappan Lakshmanan 	}
1660a2438132SYegappan Lakshmanan     }
1661a2438132SYegappan Lakshmanan 
1662a2438132SYegappan Lakshmanan     tail = head + STRLEN(head);
1663a2438132SYegappan Lakshmanan     if (dir == 0 || dir == 2)
1664a2438132SYegappan Lakshmanan     {
1665a2438132SYegappan Lakshmanan 	// Trim trailing characters
1666a2438132SYegappan Lakshmanan 	for (; tail > head; tail = prev)
1667a2438132SYegappan Lakshmanan 	{
1668a2438132SYegappan Lakshmanan 	    prev = tail;
1669a2438132SYegappan Lakshmanan 	    MB_PTR_BACK(head, prev);
1670a2438132SYegappan Lakshmanan 	    c1 = PTR2CHAR(prev);
1671a2438132SYegappan Lakshmanan 	    if (mask == NULL)
1672a2438132SYegappan Lakshmanan 	    {
1673a2438132SYegappan Lakshmanan 		if (c1 > ' ' && c1 != 0xa0)
1674a2438132SYegappan Lakshmanan 		    break;
1675a2438132SYegappan Lakshmanan 	    }
1676a2438132SYegappan Lakshmanan 	    else
1677a2438132SYegappan Lakshmanan 	    {
1678a2438132SYegappan Lakshmanan 		for (p = mask; *p != NUL; MB_PTR_ADV(p))
1679a2438132SYegappan Lakshmanan 		    if (c1 == PTR2CHAR(p))
1680a2438132SYegappan Lakshmanan 			break;
1681a2438132SYegappan Lakshmanan 		if (*p == NUL)
1682a2438132SYegappan Lakshmanan 		    break;
1683a2438132SYegappan Lakshmanan 	    }
1684a2438132SYegappan Lakshmanan 	}
1685a2438132SYegappan Lakshmanan     }
1686a2438132SYegappan Lakshmanan     rettv->vval.v_string = vim_strnsave(head, tail - head);
1687a2438132SYegappan Lakshmanan }
1688a2438132SYegappan Lakshmanan 
1689a2438132SYegappan Lakshmanan #endif
16908ee52affSYegappan Lakshmanan 
16918ee52affSYegappan Lakshmanan #if defined(FEAT_EVAL)
16928ee52affSYegappan Lakshmanan static char *e_printf = N_("E766: Insufficient arguments for printf()");
16938ee52affSYegappan Lakshmanan 
16948ee52affSYegappan Lakshmanan /*
16958ee52affSYegappan Lakshmanan  * Get number argument from "idxp" entry in "tvs".  First entry is 1.
16968ee52affSYegappan Lakshmanan  */
16978ee52affSYegappan Lakshmanan     static varnumber_T
tv_nr(typval_T * tvs,int * idxp)16988ee52affSYegappan Lakshmanan tv_nr(typval_T *tvs, int *idxp)
16998ee52affSYegappan Lakshmanan {
17008ee52affSYegappan Lakshmanan     int		idx = *idxp - 1;
17018ee52affSYegappan Lakshmanan     varnumber_T	n = 0;
17028ee52affSYegappan Lakshmanan     int		err = FALSE;
17038ee52affSYegappan Lakshmanan 
17048ee52affSYegappan Lakshmanan     if (tvs[idx].v_type == VAR_UNKNOWN)
17058ee52affSYegappan Lakshmanan 	emsg(_(e_printf));
17068ee52affSYegappan Lakshmanan     else
17078ee52affSYegappan Lakshmanan     {
17088ee52affSYegappan Lakshmanan 	++*idxp;
17098ee52affSYegappan Lakshmanan 	n = tv_get_number_chk(&tvs[idx], &err);
17108ee52affSYegappan Lakshmanan 	if (err)
17118ee52affSYegappan Lakshmanan 	    n = 0;
17128ee52affSYegappan Lakshmanan     }
17138ee52affSYegappan Lakshmanan     return n;
17148ee52affSYegappan Lakshmanan }
17158ee52affSYegappan Lakshmanan 
17168ee52affSYegappan Lakshmanan /*
17178ee52affSYegappan Lakshmanan  * Get string argument from "idxp" entry in "tvs".  First entry is 1.
17188ee52affSYegappan Lakshmanan  * If "tofree" is NULL tv_get_string_chk() is used.  Some types (e.g. List)
17198ee52affSYegappan Lakshmanan  * are not converted to a string.
17208ee52affSYegappan Lakshmanan  * If "tofree" is not NULL echo_string() is used.  All types are converted to
17218ee52affSYegappan Lakshmanan  * a string with the same format as ":echo".  The caller must free "*tofree".
17228ee52affSYegappan Lakshmanan  * Returns NULL for an error.
17238ee52affSYegappan Lakshmanan  */
17248ee52affSYegappan Lakshmanan     static char *
tv_str(typval_T * tvs,int * idxp,char_u ** tofree)17258ee52affSYegappan Lakshmanan tv_str(typval_T *tvs, int *idxp, char_u **tofree)
17268ee52affSYegappan Lakshmanan {
17278ee52affSYegappan Lakshmanan     int		    idx = *idxp - 1;
17288ee52affSYegappan Lakshmanan     char	    *s = NULL;
17298ee52affSYegappan Lakshmanan     static char_u   numbuf[NUMBUFLEN];
17308ee52affSYegappan Lakshmanan 
17318ee52affSYegappan Lakshmanan     if (tvs[idx].v_type == VAR_UNKNOWN)
17328ee52affSYegappan Lakshmanan 	emsg(_(e_printf));
17338ee52affSYegappan Lakshmanan     else
17348ee52affSYegappan Lakshmanan     {
17358ee52affSYegappan Lakshmanan 	++*idxp;
17368ee52affSYegappan Lakshmanan 	if (tofree != NULL)
17378ee52affSYegappan Lakshmanan 	    s = (char *)echo_string(&tvs[idx], tofree, numbuf, get_copyID());
17388ee52affSYegappan Lakshmanan 	else
17398ee52affSYegappan Lakshmanan 	    s = (char *)tv_get_string_chk(&tvs[idx]);
17408ee52affSYegappan Lakshmanan     }
17418ee52affSYegappan Lakshmanan     return s;
17428ee52affSYegappan Lakshmanan }
17438ee52affSYegappan Lakshmanan 
17448ee52affSYegappan Lakshmanan # ifdef FEAT_FLOAT
17458ee52affSYegappan Lakshmanan /*
17468ee52affSYegappan Lakshmanan  * Get float argument from "idxp" entry in "tvs".  First entry is 1.
17478ee52affSYegappan Lakshmanan  */
17488ee52affSYegappan Lakshmanan     static double
tv_float(typval_T * tvs,int * idxp)17498ee52affSYegappan Lakshmanan tv_float(typval_T *tvs, int *idxp)
17508ee52affSYegappan Lakshmanan {
17518ee52affSYegappan Lakshmanan     int		idx = *idxp - 1;
17528ee52affSYegappan Lakshmanan     double	f = 0;
17538ee52affSYegappan Lakshmanan 
17548ee52affSYegappan Lakshmanan     if (tvs[idx].v_type == VAR_UNKNOWN)
17558ee52affSYegappan Lakshmanan 	emsg(_(e_printf));
17568ee52affSYegappan Lakshmanan     else
17578ee52affSYegappan Lakshmanan     {
17588ee52affSYegappan Lakshmanan 	++*idxp;
17598ee52affSYegappan Lakshmanan 	if (tvs[idx].v_type == VAR_FLOAT)
17608ee52affSYegappan Lakshmanan 	    f = tvs[idx].vval.v_float;
17618ee52affSYegappan Lakshmanan 	else if (tvs[idx].v_type == VAR_NUMBER)
17628ee52affSYegappan Lakshmanan 	    f = (double)tvs[idx].vval.v_number;
17638ee52affSYegappan Lakshmanan 	else
17648ee52affSYegappan Lakshmanan 	    emsg(_("E807: Expected Float argument for printf()"));
17658ee52affSYegappan Lakshmanan     }
17668ee52affSYegappan Lakshmanan     return f;
17678ee52affSYegappan Lakshmanan }
17688ee52affSYegappan Lakshmanan # endif
17698ee52affSYegappan Lakshmanan #endif
17708ee52affSYegappan Lakshmanan 
17718ee52affSYegappan Lakshmanan #ifdef FEAT_FLOAT
17728ee52affSYegappan Lakshmanan /*
17738ee52affSYegappan Lakshmanan  * Return the representation of infinity for printf() function:
17748ee52affSYegappan Lakshmanan  * "-inf", "inf", "+inf", " inf", "-INF", "INF", "+INF" or " INF".
17758ee52affSYegappan Lakshmanan  */
17768ee52affSYegappan Lakshmanan     static const char *
infinity_str(int positive,char fmt_spec,int force_sign,int space_for_positive)17778ee52affSYegappan Lakshmanan infinity_str(int positive,
17788ee52affSYegappan Lakshmanan 	     char fmt_spec,
17798ee52affSYegappan Lakshmanan 	     int force_sign,
17808ee52affSYegappan Lakshmanan 	     int space_for_positive)
17818ee52affSYegappan Lakshmanan {
17828ee52affSYegappan Lakshmanan     static const char *table[] =
17838ee52affSYegappan Lakshmanan     {
17848ee52affSYegappan Lakshmanan 	"-inf", "inf", "+inf", " inf",
17858ee52affSYegappan Lakshmanan 	"-INF", "INF", "+INF", " INF"
17868ee52affSYegappan Lakshmanan     };
17878ee52affSYegappan Lakshmanan     int idx = positive * (1 + force_sign + force_sign * space_for_positive);
17888ee52affSYegappan Lakshmanan 
17898ee52affSYegappan Lakshmanan     if (ASCII_ISUPPER(fmt_spec))
17908ee52affSYegappan Lakshmanan 	idx += 4;
17918ee52affSYegappan Lakshmanan     return table[idx];
17928ee52affSYegappan Lakshmanan }
17938ee52affSYegappan Lakshmanan #endif
17948ee52affSYegappan Lakshmanan 
17958ee52affSYegappan Lakshmanan /*
17968ee52affSYegappan Lakshmanan  * This code was included to provide a portable vsnprintf() and snprintf().
17978ee52affSYegappan Lakshmanan  * Some systems may provide their own, but we always use this one for
17988ee52affSYegappan Lakshmanan  * consistency.
17998ee52affSYegappan Lakshmanan  *
18008ee52affSYegappan Lakshmanan  * This code is based on snprintf.c - a portable implementation of snprintf
18018ee52affSYegappan Lakshmanan  * by Mark Martinec <[email protected]>, Version 2.2, 2000-10-06.
18028ee52affSYegappan Lakshmanan  * Included with permission.  It was heavily modified to fit in Vim.
18038ee52affSYegappan Lakshmanan  * The original code, including useful comments, can be found here:
18048ee52affSYegappan Lakshmanan  *	http://www.ijs.si/software/snprintf/
18058ee52affSYegappan Lakshmanan  *
18068ee52affSYegappan Lakshmanan  * This snprintf() only supports the following conversion specifiers:
18078ee52affSYegappan Lakshmanan  * s, c, d, u, o, x, X, p  (and synonyms: i, D, U, O - see below)
18088ee52affSYegappan Lakshmanan  * with flags: '-', '+', ' ', '0' and '#'.
18098ee52affSYegappan Lakshmanan  * An asterisk is supported for field width as well as precision.
18108ee52affSYegappan Lakshmanan  *
18118ee52affSYegappan Lakshmanan  * Limited support for floating point was added: 'f', 'F', 'e', 'E', 'g', 'G'.
18128ee52affSYegappan Lakshmanan  *
18138ee52affSYegappan Lakshmanan  * Length modifiers 'h' (short int) and 'l' (long int) and 'll' (long long int)
18148ee52affSYegappan Lakshmanan  * are supported.  NOTE: for 'll' the argument is varnumber_T or uvarnumber_T.
18158ee52affSYegappan Lakshmanan  *
18168ee52affSYegappan Lakshmanan  * The locale is not used, the string is used as a byte string.  This is only
18178ee52affSYegappan Lakshmanan  * relevant for double-byte encodings where the second byte may be '%'.
18188ee52affSYegappan Lakshmanan  *
18198ee52affSYegappan Lakshmanan  * It is permitted for "str_m" to be zero, and it is permitted to specify NULL
18208ee52affSYegappan Lakshmanan  * pointer for resulting string argument if "str_m" is zero (as per ISO C99).
18218ee52affSYegappan Lakshmanan  *
18228ee52affSYegappan Lakshmanan  * The return value is the number of characters which would be generated
18238ee52affSYegappan Lakshmanan  * for the given input, excluding the trailing NUL. If this value
18248ee52affSYegappan Lakshmanan  * is greater or equal to "str_m", not all characters from the result
18258ee52affSYegappan Lakshmanan  * have been stored in str, output bytes beyond the ("str_m"-1) -th character
18268ee52affSYegappan Lakshmanan  * are discarded. If "str_m" is greater than zero it is guaranteed
18278ee52affSYegappan Lakshmanan  * the resulting string will be NUL-terminated.
18288ee52affSYegappan Lakshmanan  */
18298ee52affSYegappan Lakshmanan 
18308ee52affSYegappan Lakshmanan /*
18318ee52affSYegappan Lakshmanan  * When va_list is not supported we only define vim_snprintf().
18328ee52affSYegappan Lakshmanan  *
18338ee52affSYegappan Lakshmanan  * vim_vsnprintf_typval() can be invoked with either "va_list" or a list of
18348ee52affSYegappan Lakshmanan  * "typval_T".  When the latter is not used it must be NULL.
18358ee52affSYegappan Lakshmanan  */
18368ee52affSYegappan Lakshmanan 
18378ee52affSYegappan Lakshmanan // When generating prototypes all of this is skipped, cproto doesn't
18388ee52affSYegappan Lakshmanan // understand this.
18398ee52affSYegappan Lakshmanan #ifndef PROTO
18408ee52affSYegappan Lakshmanan 
18418ee52affSYegappan Lakshmanan // Like vim_vsnprintf() but append to the string.
18428ee52affSYegappan Lakshmanan     int
vim_snprintf_add(char * str,size_t str_m,const char * fmt,...)18438ee52affSYegappan Lakshmanan vim_snprintf_add(char *str, size_t str_m, const char *fmt, ...)
18448ee52affSYegappan Lakshmanan {
18458ee52affSYegappan Lakshmanan     va_list	ap;
18468ee52affSYegappan Lakshmanan     int		str_l;
18478ee52affSYegappan Lakshmanan     size_t	len = STRLEN(str);
18488ee52affSYegappan Lakshmanan     size_t	space;
18498ee52affSYegappan Lakshmanan 
18508ee52affSYegappan Lakshmanan     if (str_m <= len)
18518ee52affSYegappan Lakshmanan 	space = 0;
18528ee52affSYegappan Lakshmanan     else
18538ee52affSYegappan Lakshmanan 	space = str_m - len;
18548ee52affSYegappan Lakshmanan     va_start(ap, fmt);
18558ee52affSYegappan Lakshmanan     str_l = vim_vsnprintf(str + len, space, fmt, ap);
18568ee52affSYegappan Lakshmanan     va_end(ap);
18578ee52affSYegappan Lakshmanan     return str_l;
18588ee52affSYegappan Lakshmanan }
18598ee52affSYegappan Lakshmanan 
18608ee52affSYegappan Lakshmanan     int
vim_snprintf(char * str,size_t str_m,const char * fmt,...)18618ee52affSYegappan Lakshmanan vim_snprintf(char *str, size_t str_m, const char *fmt, ...)
18628ee52affSYegappan Lakshmanan {
18638ee52affSYegappan Lakshmanan     va_list	ap;
18648ee52affSYegappan Lakshmanan     int		str_l;
18658ee52affSYegappan Lakshmanan 
18668ee52affSYegappan Lakshmanan     va_start(ap, fmt);
18678ee52affSYegappan Lakshmanan     str_l = vim_vsnprintf(str, str_m, fmt, ap);
18688ee52affSYegappan Lakshmanan     va_end(ap);
18698ee52affSYegappan Lakshmanan     return str_l;
18708ee52affSYegappan Lakshmanan }
18718ee52affSYegappan Lakshmanan 
18728ee52affSYegappan Lakshmanan     int
vim_vsnprintf(char * str,size_t str_m,const char * fmt,va_list ap)18738ee52affSYegappan Lakshmanan vim_vsnprintf(
18748ee52affSYegappan Lakshmanan     char	*str,
18758ee52affSYegappan Lakshmanan     size_t	str_m,
18768ee52affSYegappan Lakshmanan     const char	*fmt,
18778ee52affSYegappan Lakshmanan     va_list	ap)
18788ee52affSYegappan Lakshmanan {
18798ee52affSYegappan Lakshmanan     return vim_vsnprintf_typval(str, str_m, fmt, ap, NULL);
18808ee52affSYegappan Lakshmanan }
18818ee52affSYegappan Lakshmanan 
18828ee52affSYegappan Lakshmanan     int
vim_vsnprintf_typval(char * str,size_t str_m,const char * fmt,va_list ap,typval_T * tvs)18838ee52affSYegappan Lakshmanan vim_vsnprintf_typval(
18848ee52affSYegappan Lakshmanan     char	*str,
18858ee52affSYegappan Lakshmanan     size_t	str_m,
18868ee52affSYegappan Lakshmanan     const char	*fmt,
18878ee52affSYegappan Lakshmanan     va_list	ap,
18888ee52affSYegappan Lakshmanan     typval_T	*tvs)
18898ee52affSYegappan Lakshmanan {
18908ee52affSYegappan Lakshmanan     size_t	str_l = 0;
18918ee52affSYegappan Lakshmanan     const char	*p = fmt;
18928ee52affSYegappan Lakshmanan     int		arg_idx = 1;
18938ee52affSYegappan Lakshmanan 
18948ee52affSYegappan Lakshmanan     if (p == NULL)
18958ee52affSYegappan Lakshmanan 	p = "";
18968ee52affSYegappan Lakshmanan     while (*p != NUL)
18978ee52affSYegappan Lakshmanan     {
18988ee52affSYegappan Lakshmanan 	if (*p != '%')
18998ee52affSYegappan Lakshmanan 	{
19008ee52affSYegappan Lakshmanan 	    char    *q = strchr(p + 1, '%');
19018ee52affSYegappan Lakshmanan 	    size_t  n = (q == NULL) ? STRLEN(p) : (size_t)(q - p);
19028ee52affSYegappan Lakshmanan 
19038ee52affSYegappan Lakshmanan 	    // Copy up to the next '%' or NUL without any changes.
19048ee52affSYegappan Lakshmanan 	    if (str_l < str_m)
19058ee52affSYegappan Lakshmanan 	    {
19068ee52affSYegappan Lakshmanan 		size_t avail = str_m - str_l;
19078ee52affSYegappan Lakshmanan 
19088ee52affSYegappan Lakshmanan 		mch_memmove(str + str_l, p, n > avail ? avail : n);
19098ee52affSYegappan Lakshmanan 	    }
19108ee52affSYegappan Lakshmanan 	    p += n;
19118ee52affSYegappan Lakshmanan 	    str_l += n;
19128ee52affSYegappan Lakshmanan 	}
19138ee52affSYegappan Lakshmanan 	else
19148ee52affSYegappan Lakshmanan 	{
19158ee52affSYegappan Lakshmanan 	    size_t  min_field_width = 0, precision = 0;
19168ee52affSYegappan Lakshmanan 	    int	    zero_padding = 0, precision_specified = 0, justify_left = 0;
19178ee52affSYegappan Lakshmanan 	    int	    alternate_form = 0, force_sign = 0;
19188ee52affSYegappan Lakshmanan 
19198ee52affSYegappan Lakshmanan 	    // If both the ' ' and '+' flags appear, the ' ' flag should be
19208ee52affSYegappan Lakshmanan 	    // ignored.
19218ee52affSYegappan Lakshmanan 	    int	    space_for_positive = 1;
19228ee52affSYegappan Lakshmanan 
19238ee52affSYegappan Lakshmanan 	    // allowed values: \0, h, l, L
19248ee52affSYegappan Lakshmanan 	    char    length_modifier = '\0';
19258ee52affSYegappan Lakshmanan 
19268ee52affSYegappan Lakshmanan 	    // temporary buffer for simple numeric->string conversion
19278ee52affSYegappan Lakshmanan # if defined(FEAT_FLOAT)
19288ee52affSYegappan Lakshmanan #  define TMP_LEN 350	// On my system 1e308 is the biggest number possible.
19298ee52affSYegappan Lakshmanan 			// That sounds reasonable to use as the maximum
19308ee52affSYegappan Lakshmanan 			// printable.
19318ee52affSYegappan Lakshmanan # else
19328ee52affSYegappan Lakshmanan #  define TMP_LEN 66
19338ee52affSYegappan Lakshmanan # endif
19348ee52affSYegappan Lakshmanan 	    char    tmp[TMP_LEN];
19358ee52affSYegappan Lakshmanan 
19368ee52affSYegappan Lakshmanan 	    // string address in case of string argument
19378ee52affSYegappan Lakshmanan 	    const char  *str_arg = NULL;
19388ee52affSYegappan Lakshmanan 
19398ee52affSYegappan Lakshmanan 	    // natural field width of arg without padding and sign
19408ee52affSYegappan Lakshmanan 	    size_t  str_arg_l;
19418ee52affSYegappan Lakshmanan 
19428ee52affSYegappan Lakshmanan 	    // unsigned char argument value - only defined for c conversion.
19438ee52affSYegappan Lakshmanan 	    // N.B. standard explicitly states the char argument for the c
19448ee52affSYegappan Lakshmanan 	    // conversion is unsigned
19458ee52affSYegappan Lakshmanan 	    unsigned char uchar_arg;
19468ee52affSYegappan Lakshmanan 
19478ee52affSYegappan Lakshmanan 	    // number of zeros to be inserted for numeric conversions as
19488ee52affSYegappan Lakshmanan 	    // required by the precision or minimal field width
19498ee52affSYegappan Lakshmanan 	    size_t  number_of_zeros_to_pad = 0;
19508ee52affSYegappan Lakshmanan 
19518ee52affSYegappan Lakshmanan 	    // index into tmp where zero padding is to be inserted
19528ee52affSYegappan Lakshmanan 	    size_t  zero_padding_insertion_ind = 0;
19538ee52affSYegappan Lakshmanan 
19548ee52affSYegappan Lakshmanan 	    // current conversion specifier character
19558ee52affSYegappan Lakshmanan 	    char    fmt_spec = '\0';
19568ee52affSYegappan Lakshmanan 
19578ee52affSYegappan Lakshmanan 	    // buffer for 's' and 'S' specs
19588ee52affSYegappan Lakshmanan 	    char_u  *tofree = NULL;
19598ee52affSYegappan Lakshmanan 
19608ee52affSYegappan Lakshmanan 
19618ee52affSYegappan Lakshmanan 	    p++;  // skip '%'
19628ee52affSYegappan Lakshmanan 
19638ee52affSYegappan Lakshmanan 	    // parse flags
19648ee52affSYegappan Lakshmanan 	    while (*p == '0' || *p == '-' || *p == '+' || *p == ' '
19658ee52affSYegappan Lakshmanan 						   || *p == '#' || *p == '\'')
19668ee52affSYegappan Lakshmanan 	    {
19678ee52affSYegappan Lakshmanan 		switch (*p)
19688ee52affSYegappan Lakshmanan 		{
19698ee52affSYegappan Lakshmanan 		    case '0': zero_padding = 1; break;
19708ee52affSYegappan Lakshmanan 		    case '-': justify_left = 1; break;
19718ee52affSYegappan Lakshmanan 		    case '+': force_sign = 1; space_for_positive = 0; break;
19728ee52affSYegappan Lakshmanan 		    case ' ': force_sign = 1;
19738ee52affSYegappan Lakshmanan 			      // If both the ' ' and '+' flags appear, the ' '
19748ee52affSYegappan Lakshmanan 			      // flag should be ignored
19758ee52affSYegappan Lakshmanan 			      break;
19768ee52affSYegappan Lakshmanan 		    case '#': alternate_form = 1; break;
19778ee52affSYegappan Lakshmanan 		    case '\'': break;
19788ee52affSYegappan Lakshmanan 		}
19798ee52affSYegappan Lakshmanan 		p++;
19808ee52affSYegappan Lakshmanan 	    }
19818ee52affSYegappan Lakshmanan 	    // If the '0' and '-' flags both appear, the '0' flag should be
19828ee52affSYegappan Lakshmanan 	    // ignored.
19838ee52affSYegappan Lakshmanan 
19848ee52affSYegappan Lakshmanan 	    // parse field width
19858ee52affSYegappan Lakshmanan 	    if (*p == '*')
19868ee52affSYegappan Lakshmanan 	    {
19878ee52affSYegappan Lakshmanan 		int j;
19888ee52affSYegappan Lakshmanan 
19898ee52affSYegappan Lakshmanan 		p++;
19908ee52affSYegappan Lakshmanan 		j =
19918ee52affSYegappan Lakshmanan # if defined(FEAT_EVAL)
19928ee52affSYegappan Lakshmanan 		    tvs != NULL ? tv_nr(tvs, &arg_idx) :
19938ee52affSYegappan Lakshmanan # endif
19948ee52affSYegappan Lakshmanan 			va_arg(ap, int);
19958ee52affSYegappan Lakshmanan 		if (j >= 0)
19968ee52affSYegappan Lakshmanan 		    min_field_width = j;
19978ee52affSYegappan Lakshmanan 		else
19988ee52affSYegappan Lakshmanan 		{
19998ee52affSYegappan Lakshmanan 		    min_field_width = -j;
20008ee52affSYegappan Lakshmanan 		    justify_left = 1;
20018ee52affSYegappan Lakshmanan 		}
20028ee52affSYegappan Lakshmanan 	    }
20038ee52affSYegappan Lakshmanan 	    else if (VIM_ISDIGIT((int)(*p)))
20048ee52affSYegappan Lakshmanan 	    {
20058ee52affSYegappan Lakshmanan 		// size_t could be wider than unsigned int; make sure we treat
20068ee52affSYegappan Lakshmanan 		// argument like common implementations do
20078ee52affSYegappan Lakshmanan 		unsigned int uj = *p++ - '0';
20088ee52affSYegappan Lakshmanan 
20098ee52affSYegappan Lakshmanan 		while (VIM_ISDIGIT((int)(*p)))
20108ee52affSYegappan Lakshmanan 		    uj = 10 * uj + (unsigned int)(*p++ - '0');
20118ee52affSYegappan Lakshmanan 		min_field_width = uj;
20128ee52affSYegappan Lakshmanan 	    }
20138ee52affSYegappan Lakshmanan 
20148ee52affSYegappan Lakshmanan 	    // parse precision
20158ee52affSYegappan Lakshmanan 	    if (*p == '.')
20168ee52affSYegappan Lakshmanan 	    {
20178ee52affSYegappan Lakshmanan 		p++;
20188ee52affSYegappan Lakshmanan 		precision_specified = 1;
20198ee52affSYegappan Lakshmanan 		if (*p == '*')
20208ee52affSYegappan Lakshmanan 		{
20218ee52affSYegappan Lakshmanan 		    int j;
20228ee52affSYegappan Lakshmanan 
20238ee52affSYegappan Lakshmanan 		    j =
20248ee52affSYegappan Lakshmanan # if defined(FEAT_EVAL)
20258ee52affSYegappan Lakshmanan 			tvs != NULL ? tv_nr(tvs, &arg_idx) :
20268ee52affSYegappan Lakshmanan # endif
20278ee52affSYegappan Lakshmanan 			    va_arg(ap, int);
20288ee52affSYegappan Lakshmanan 		    p++;
20298ee52affSYegappan Lakshmanan 		    if (j >= 0)
20308ee52affSYegappan Lakshmanan 			precision = j;
20318ee52affSYegappan Lakshmanan 		    else
20328ee52affSYegappan Lakshmanan 		    {
20338ee52affSYegappan Lakshmanan 			precision_specified = 0;
20348ee52affSYegappan Lakshmanan 			precision = 0;
20358ee52affSYegappan Lakshmanan 		    }
20368ee52affSYegappan Lakshmanan 		}
20378ee52affSYegappan Lakshmanan 		else if (VIM_ISDIGIT((int)(*p)))
20388ee52affSYegappan Lakshmanan 		{
20398ee52affSYegappan Lakshmanan 		    // size_t could be wider than unsigned int; make sure we
20408ee52affSYegappan Lakshmanan 		    // treat argument like common implementations do
20418ee52affSYegappan Lakshmanan 		    unsigned int uj = *p++ - '0';
20428ee52affSYegappan Lakshmanan 
20438ee52affSYegappan Lakshmanan 		    while (VIM_ISDIGIT((int)(*p)))
20448ee52affSYegappan Lakshmanan 			uj = 10 * uj + (unsigned int)(*p++ - '0');
20458ee52affSYegappan Lakshmanan 		    precision = uj;
20468ee52affSYegappan Lakshmanan 		}
20478ee52affSYegappan Lakshmanan 	    }
20488ee52affSYegappan Lakshmanan 
20498ee52affSYegappan Lakshmanan 	    // parse 'h', 'l' and 'll' length modifiers
20508ee52affSYegappan Lakshmanan 	    if (*p == 'h' || *p == 'l')
20518ee52affSYegappan Lakshmanan 	    {
20528ee52affSYegappan Lakshmanan 		length_modifier = *p;
20538ee52affSYegappan Lakshmanan 		p++;
20548ee52affSYegappan Lakshmanan 		if (length_modifier == 'l' && *p == 'l')
20558ee52affSYegappan Lakshmanan 		{
20568ee52affSYegappan Lakshmanan 		    // double l = __int64 / varnumber_T
20578ee52affSYegappan Lakshmanan 		    length_modifier = 'L';
20588ee52affSYegappan Lakshmanan 		    p++;
20598ee52affSYegappan Lakshmanan 		}
20608ee52affSYegappan Lakshmanan 	    }
20618ee52affSYegappan Lakshmanan 	    fmt_spec = *p;
20628ee52affSYegappan Lakshmanan 
20638ee52affSYegappan Lakshmanan 	    // common synonyms:
20648ee52affSYegappan Lakshmanan 	    switch (fmt_spec)
20658ee52affSYegappan Lakshmanan 	    {
20668ee52affSYegappan Lakshmanan 		case 'i': fmt_spec = 'd'; break;
20678ee52affSYegappan Lakshmanan 		case 'D': fmt_spec = 'd'; length_modifier = 'l'; break;
20688ee52affSYegappan Lakshmanan 		case 'U': fmt_spec = 'u'; length_modifier = 'l'; break;
20698ee52affSYegappan Lakshmanan 		case 'O': fmt_spec = 'o'; length_modifier = 'l'; break;
20708ee52affSYegappan Lakshmanan 		default: break;
20718ee52affSYegappan Lakshmanan 	    }
20728ee52affSYegappan Lakshmanan 
20738ee52affSYegappan Lakshmanan # if defined(FEAT_EVAL)
20748ee52affSYegappan Lakshmanan 	    switch (fmt_spec)
20758ee52affSYegappan Lakshmanan 	    {
20768ee52affSYegappan Lakshmanan 		case 'd': case 'u': case 'o': case 'x': case 'X':
20778ee52affSYegappan Lakshmanan 		    if (tvs != NULL && length_modifier == '\0')
20788ee52affSYegappan Lakshmanan 			length_modifier = 'L';
20798ee52affSYegappan Lakshmanan 	    }
20808ee52affSYegappan Lakshmanan # endif
20818ee52affSYegappan Lakshmanan 
20828ee52affSYegappan Lakshmanan 	    // get parameter value, do initial processing
20838ee52affSYegappan Lakshmanan 	    switch (fmt_spec)
20848ee52affSYegappan Lakshmanan 	    {
20858ee52affSYegappan Lakshmanan 		// '%' and 'c' behave similar to 's' regarding flags and field
20868ee52affSYegappan Lakshmanan 		// widths
20878ee52affSYegappan Lakshmanan 	    case '%':
20888ee52affSYegappan Lakshmanan 	    case 'c':
20898ee52affSYegappan Lakshmanan 	    case 's':
20908ee52affSYegappan Lakshmanan 	    case 'S':
20918ee52affSYegappan Lakshmanan 		str_arg_l = 1;
20928ee52affSYegappan Lakshmanan 		switch (fmt_spec)
20938ee52affSYegappan Lakshmanan 		{
20948ee52affSYegappan Lakshmanan 		case '%':
20958ee52affSYegappan Lakshmanan 		    str_arg = p;
20968ee52affSYegappan Lakshmanan 		    break;
20978ee52affSYegappan Lakshmanan 
20988ee52affSYegappan Lakshmanan 		case 'c':
20998ee52affSYegappan Lakshmanan 		    {
21008ee52affSYegappan Lakshmanan 			int j;
21018ee52affSYegappan Lakshmanan 
21028ee52affSYegappan Lakshmanan 			j =
21038ee52affSYegappan Lakshmanan # if defined(FEAT_EVAL)
21048ee52affSYegappan Lakshmanan 			    tvs != NULL ? tv_nr(tvs, &arg_idx) :
21058ee52affSYegappan Lakshmanan # endif
21068ee52affSYegappan Lakshmanan 				va_arg(ap, int);
21078ee52affSYegappan Lakshmanan 			// standard demands unsigned char
21088ee52affSYegappan Lakshmanan 			uchar_arg = (unsigned char)j;
21098ee52affSYegappan Lakshmanan 			str_arg = (char *)&uchar_arg;
21108ee52affSYegappan Lakshmanan 			break;
21118ee52affSYegappan Lakshmanan 		    }
21128ee52affSYegappan Lakshmanan 
21138ee52affSYegappan Lakshmanan 		case 's':
21148ee52affSYegappan Lakshmanan 		case 'S':
21158ee52affSYegappan Lakshmanan 		    str_arg =
21168ee52affSYegappan Lakshmanan # if defined(FEAT_EVAL)
21178ee52affSYegappan Lakshmanan 				tvs != NULL ? tv_str(tvs, &arg_idx, &tofree) :
21188ee52affSYegappan Lakshmanan # endif
21198ee52affSYegappan Lakshmanan 				    va_arg(ap, char *);
21208ee52affSYegappan Lakshmanan 		    if (str_arg == NULL)
21218ee52affSYegappan Lakshmanan 		    {
21228ee52affSYegappan Lakshmanan 			str_arg = "[NULL]";
21238ee52affSYegappan Lakshmanan 			str_arg_l = 6;
21248ee52affSYegappan Lakshmanan 		    }
21258ee52affSYegappan Lakshmanan 		    // make sure not to address string beyond the specified
21268ee52affSYegappan Lakshmanan 		    // precision !!!
21278ee52affSYegappan Lakshmanan 		    else if (!precision_specified)
21288ee52affSYegappan Lakshmanan 			str_arg_l = strlen(str_arg);
21298ee52affSYegappan Lakshmanan 		    // truncate string if necessary as requested by precision
21308ee52affSYegappan Lakshmanan 		    else if (precision == 0)
21318ee52affSYegappan Lakshmanan 			str_arg_l = 0;
21328ee52affSYegappan Lakshmanan 		    else
21338ee52affSYegappan Lakshmanan 		    {
21348ee52affSYegappan Lakshmanan 			// Don't put the #if inside memchr(), it can be a
21358ee52affSYegappan Lakshmanan 			// macro.
21368ee52affSYegappan Lakshmanan 			// memchr on HP does not like n > 2^31  !!!
21378ee52affSYegappan Lakshmanan 			char *q = memchr(str_arg, '\0',
21388ee52affSYegappan Lakshmanan 				  precision <= (size_t)0x7fffffffL ? precision
21398ee52affSYegappan Lakshmanan 						       : (size_t)0x7fffffffL);
2140*d85fccdfSpresuku 
21418ee52affSYegappan Lakshmanan 			str_arg_l = (q == NULL) ? precision
21428ee52affSYegappan Lakshmanan 						      : (size_t)(q - str_arg);
21438ee52affSYegappan Lakshmanan 		    }
21448ee52affSYegappan Lakshmanan 		    if (fmt_spec == 'S')
21458ee52affSYegappan Lakshmanan 		    {
2146*d85fccdfSpresuku 			size_t base_width = min_field_width;
2147*d85fccdfSpresuku 			size_t pad_cell = 0;
2148*d85fccdfSpresuku 
21498ee52affSYegappan Lakshmanan 			if (precision)
21508ee52affSYegappan Lakshmanan 			{
21518ee52affSYegappan Lakshmanan 			    char_u  *p1;
21528ee52affSYegappan Lakshmanan 			    size_t  i = 0;
21538ee52affSYegappan Lakshmanan 
21548ee52affSYegappan Lakshmanan 			    for (p1 = (char_u *)str_arg; *p1;
21558ee52affSYegappan Lakshmanan 							  p1 += mb_ptr2len(p1))
21568ee52affSYegappan Lakshmanan 			    {
21578ee52affSYegappan Lakshmanan 				i += (size_t)mb_ptr2cells(p1);
21588ee52affSYegappan Lakshmanan 				if (i > precision)
21598ee52affSYegappan Lakshmanan 				    break;
21608ee52affSYegappan Lakshmanan 			    }
2161*d85fccdfSpresuku 			    pad_cell = min_field_width - precision;
2162*d85fccdfSpresuku 			    base_width = str_arg_l = precision =
2163*d85fccdfSpresuku 							p1 - (char_u *)str_arg;
21648ee52affSYegappan Lakshmanan 			}
2165*d85fccdfSpresuku 			if (min_field_width != 0)
2166*d85fccdfSpresuku 			    min_field_width = base_width + pad_cell;
21678ee52affSYegappan Lakshmanan 		    }
21688ee52affSYegappan Lakshmanan 		    break;
21698ee52affSYegappan Lakshmanan 
21708ee52affSYegappan Lakshmanan 		default:
21718ee52affSYegappan Lakshmanan 		    break;
21728ee52affSYegappan Lakshmanan 		}
21738ee52affSYegappan Lakshmanan 		break;
21748ee52affSYegappan Lakshmanan 
21758ee52affSYegappan Lakshmanan 	    case 'd': case 'u':
21768ee52affSYegappan Lakshmanan 	    case 'b': case 'B':
21778ee52affSYegappan Lakshmanan 	    case 'o':
21788ee52affSYegappan Lakshmanan 	    case 'x': case 'X':
21798ee52affSYegappan Lakshmanan 	    case 'p':
21808ee52affSYegappan Lakshmanan 		{
21818ee52affSYegappan Lakshmanan 		    // NOTE: the u, b, o, x, X and p conversion specifiers
21828ee52affSYegappan Lakshmanan 		    // imply the value is unsigned;  d implies a signed
21838ee52affSYegappan Lakshmanan 		    // value
21848ee52affSYegappan Lakshmanan 
21858ee52affSYegappan Lakshmanan 		    // 0 if numeric argument is zero (or if pointer is
21868ee52affSYegappan Lakshmanan 		    // NULL for 'p'), +1 if greater than zero (or nonzero
21878ee52affSYegappan Lakshmanan 		    // for unsigned arguments), -1 if negative (unsigned
21888ee52affSYegappan Lakshmanan 		    // argument is never negative)
21898ee52affSYegappan Lakshmanan 		    int arg_sign = 0;
21908ee52affSYegappan Lakshmanan 
21918ee52affSYegappan Lakshmanan 		    // only set for length modifier h, or for no length
21928ee52affSYegappan Lakshmanan 		    // modifiers
21938ee52affSYegappan Lakshmanan 		    int int_arg = 0;
21948ee52affSYegappan Lakshmanan 		    unsigned int uint_arg = 0;
21958ee52affSYegappan Lakshmanan 
21968ee52affSYegappan Lakshmanan 		    // only set for length modifier l
21978ee52affSYegappan Lakshmanan 		    long int long_arg = 0;
21988ee52affSYegappan Lakshmanan 		    unsigned long int ulong_arg = 0;
21998ee52affSYegappan Lakshmanan 
22008ee52affSYegappan Lakshmanan 		    // only set for length modifier ll
22018ee52affSYegappan Lakshmanan 		    varnumber_T llong_arg = 0;
22028ee52affSYegappan Lakshmanan 		    uvarnumber_T ullong_arg = 0;
22038ee52affSYegappan Lakshmanan 
22048ee52affSYegappan Lakshmanan 		    // only set for b conversion
22058ee52affSYegappan Lakshmanan 		    uvarnumber_T bin_arg = 0;
22068ee52affSYegappan Lakshmanan 
22078ee52affSYegappan Lakshmanan 		    // pointer argument value -only defined for p
22088ee52affSYegappan Lakshmanan 		    // conversion
22098ee52affSYegappan Lakshmanan 		    void *ptr_arg = NULL;
22108ee52affSYegappan Lakshmanan 
22118ee52affSYegappan Lakshmanan 		    if (fmt_spec == 'p')
22128ee52affSYegappan Lakshmanan 		    {
22138ee52affSYegappan Lakshmanan 			length_modifier = '\0';
22148ee52affSYegappan Lakshmanan 			ptr_arg =
22158ee52affSYegappan Lakshmanan # if defined(FEAT_EVAL)
22168ee52affSYegappan Lakshmanan 				 tvs != NULL ? (void *)tv_str(tvs, &arg_idx,
22178ee52affSYegappan Lakshmanan 									NULL) :
22188ee52affSYegappan Lakshmanan # endif
22198ee52affSYegappan Lakshmanan 					va_arg(ap, void *);
22208ee52affSYegappan Lakshmanan 			if (ptr_arg != NULL)
22218ee52affSYegappan Lakshmanan 			    arg_sign = 1;
22228ee52affSYegappan Lakshmanan 		    }
22238ee52affSYegappan Lakshmanan 		    else if (fmt_spec == 'b' || fmt_spec == 'B')
22248ee52affSYegappan Lakshmanan 		    {
22258ee52affSYegappan Lakshmanan 			bin_arg =
22268ee52affSYegappan Lakshmanan # if defined(FEAT_EVAL)
22278ee52affSYegappan Lakshmanan 				    tvs != NULL ?
22288ee52affSYegappan Lakshmanan 					   (uvarnumber_T)tv_nr(tvs, &arg_idx) :
22298ee52affSYegappan Lakshmanan # endif
22308ee52affSYegappan Lakshmanan 					va_arg(ap, uvarnumber_T);
22318ee52affSYegappan Lakshmanan 			if (bin_arg != 0)
22328ee52affSYegappan Lakshmanan 			    arg_sign = 1;
22338ee52affSYegappan Lakshmanan 		    }
22348ee52affSYegappan Lakshmanan 		    else if (fmt_spec == 'd')
22358ee52affSYegappan Lakshmanan 		    {
22368ee52affSYegappan Lakshmanan 			// signed
22378ee52affSYegappan Lakshmanan 			switch (length_modifier)
22388ee52affSYegappan Lakshmanan 			{
22398ee52affSYegappan Lakshmanan 			case '\0':
22408ee52affSYegappan Lakshmanan 			case 'h':
22418ee52affSYegappan Lakshmanan 			    // char and short arguments are passed as int.
22428ee52affSYegappan Lakshmanan 			    int_arg =
22438ee52affSYegappan Lakshmanan # if defined(FEAT_EVAL)
22448ee52affSYegappan Lakshmanan 					tvs != NULL ? tv_nr(tvs, &arg_idx) :
22458ee52affSYegappan Lakshmanan # endif
22468ee52affSYegappan Lakshmanan 					    va_arg(ap, int);
22478ee52affSYegappan Lakshmanan 			    if (int_arg > 0)
22488ee52affSYegappan Lakshmanan 				arg_sign =  1;
22498ee52affSYegappan Lakshmanan 			    else if (int_arg < 0)
22508ee52affSYegappan Lakshmanan 				arg_sign = -1;
22518ee52affSYegappan Lakshmanan 			    break;
22528ee52affSYegappan Lakshmanan 			case 'l':
22538ee52affSYegappan Lakshmanan 			    long_arg =
22548ee52affSYegappan Lakshmanan # if defined(FEAT_EVAL)
22558ee52affSYegappan Lakshmanan 					tvs != NULL ? tv_nr(tvs, &arg_idx) :
22568ee52affSYegappan Lakshmanan # endif
22578ee52affSYegappan Lakshmanan 					    va_arg(ap, long int);
22588ee52affSYegappan Lakshmanan 			    if (long_arg > 0)
22598ee52affSYegappan Lakshmanan 				arg_sign =  1;
22608ee52affSYegappan Lakshmanan 			    else if (long_arg < 0)
22618ee52affSYegappan Lakshmanan 				arg_sign = -1;
22628ee52affSYegappan Lakshmanan 			    break;
22638ee52affSYegappan Lakshmanan 			case 'L':
22648ee52affSYegappan Lakshmanan 			    llong_arg =
22658ee52affSYegappan Lakshmanan # if defined(FEAT_EVAL)
22668ee52affSYegappan Lakshmanan 					tvs != NULL ? tv_nr(tvs, &arg_idx) :
22678ee52affSYegappan Lakshmanan # endif
22688ee52affSYegappan Lakshmanan 					    va_arg(ap, varnumber_T);
22698ee52affSYegappan Lakshmanan 			    if (llong_arg > 0)
22708ee52affSYegappan Lakshmanan 				arg_sign =  1;
22718ee52affSYegappan Lakshmanan 			    else if (llong_arg < 0)
22728ee52affSYegappan Lakshmanan 				arg_sign = -1;
22738ee52affSYegappan Lakshmanan 			    break;
22748ee52affSYegappan Lakshmanan 			}
22758ee52affSYegappan Lakshmanan 		    }
22768ee52affSYegappan Lakshmanan 		    else
22778ee52affSYegappan Lakshmanan 		    {
22788ee52affSYegappan Lakshmanan 			// unsigned
22798ee52affSYegappan Lakshmanan 			switch (length_modifier)
22808ee52affSYegappan Lakshmanan 			{
22818ee52affSYegappan Lakshmanan 			    case '\0':
22828ee52affSYegappan Lakshmanan 			    case 'h':
22838ee52affSYegappan Lakshmanan 				uint_arg =
22848ee52affSYegappan Lakshmanan # if defined(FEAT_EVAL)
22858ee52affSYegappan Lakshmanan 					    tvs != NULL ? (unsigned)
22868ee52affSYegappan Lakshmanan 							tv_nr(tvs, &arg_idx) :
22878ee52affSYegappan Lakshmanan # endif
22888ee52affSYegappan Lakshmanan 						va_arg(ap, unsigned int);
22898ee52affSYegappan Lakshmanan 				if (uint_arg != 0)
22908ee52affSYegappan Lakshmanan 				    arg_sign = 1;
22918ee52affSYegappan Lakshmanan 				break;
22928ee52affSYegappan Lakshmanan 			    case 'l':
22938ee52affSYegappan Lakshmanan 				ulong_arg =
22948ee52affSYegappan Lakshmanan # if defined(FEAT_EVAL)
22958ee52affSYegappan Lakshmanan 					    tvs != NULL ? (unsigned long)
22968ee52affSYegappan Lakshmanan 							tv_nr(tvs, &arg_idx) :
22978ee52affSYegappan Lakshmanan # endif
22988ee52affSYegappan Lakshmanan 						va_arg(ap, unsigned long int);
22998ee52affSYegappan Lakshmanan 				if (ulong_arg != 0)
23008ee52affSYegappan Lakshmanan 				    arg_sign = 1;
23018ee52affSYegappan Lakshmanan 				break;
23028ee52affSYegappan Lakshmanan 			    case 'L':
23038ee52affSYegappan Lakshmanan 				ullong_arg =
23048ee52affSYegappan Lakshmanan # if defined(FEAT_EVAL)
23058ee52affSYegappan Lakshmanan 					    tvs != NULL ? (uvarnumber_T)
23068ee52affSYegappan Lakshmanan 							tv_nr(tvs, &arg_idx) :
23078ee52affSYegappan Lakshmanan # endif
23088ee52affSYegappan Lakshmanan 						va_arg(ap, uvarnumber_T);
23098ee52affSYegappan Lakshmanan 				if (ullong_arg != 0)
23108ee52affSYegappan Lakshmanan 				    arg_sign = 1;
23118ee52affSYegappan Lakshmanan 				break;
23128ee52affSYegappan Lakshmanan 			}
23138ee52affSYegappan Lakshmanan 		    }
23148ee52affSYegappan Lakshmanan 
23158ee52affSYegappan Lakshmanan 		    str_arg = tmp;
23168ee52affSYegappan Lakshmanan 		    str_arg_l = 0;
23178ee52affSYegappan Lakshmanan 
23188ee52affSYegappan Lakshmanan 		    // NOTE:
23198ee52affSYegappan Lakshmanan 		    //   For d, i, u, o, x, and X conversions, if precision is
23208ee52affSYegappan Lakshmanan 		    //   specified, the '0' flag should be ignored. This is so
23218ee52affSYegappan Lakshmanan 		    //   with Solaris 2.6, Digital UNIX 4.0, HPUX 10, Linux,
23228ee52affSYegappan Lakshmanan 		    //   FreeBSD, NetBSD; but not with Perl.
23238ee52affSYegappan Lakshmanan 		    if (precision_specified)
23248ee52affSYegappan Lakshmanan 			zero_padding = 0;
23258ee52affSYegappan Lakshmanan 		    if (fmt_spec == 'd')
23268ee52affSYegappan Lakshmanan 		    {
23278ee52affSYegappan Lakshmanan 			if (force_sign && arg_sign >= 0)
23288ee52affSYegappan Lakshmanan 			    tmp[str_arg_l++] = space_for_positive ? ' ' : '+';
23298ee52affSYegappan Lakshmanan 			// leave negative numbers for sprintf to handle, to
23308ee52affSYegappan Lakshmanan 			// avoid handling tricky cases like (short int)-32768
23318ee52affSYegappan Lakshmanan 		    }
23328ee52affSYegappan Lakshmanan 		    else if (alternate_form)
23338ee52affSYegappan Lakshmanan 		    {
23348ee52affSYegappan Lakshmanan 			if (arg_sign != 0
23358ee52affSYegappan Lakshmanan 				     && (fmt_spec == 'b' || fmt_spec == 'B'
23368ee52affSYegappan Lakshmanan 				      || fmt_spec == 'x' || fmt_spec == 'X') )
23378ee52affSYegappan Lakshmanan 			{
23388ee52affSYegappan Lakshmanan 			    tmp[str_arg_l++] = '0';
23398ee52affSYegappan Lakshmanan 			    tmp[str_arg_l++] = fmt_spec;
23408ee52affSYegappan Lakshmanan 			}
23418ee52affSYegappan Lakshmanan 			// alternate form should have no effect for p
23428ee52affSYegappan Lakshmanan 			// conversion, but ...
23438ee52affSYegappan Lakshmanan 		    }
23448ee52affSYegappan Lakshmanan 
23458ee52affSYegappan Lakshmanan 		    zero_padding_insertion_ind = str_arg_l;
23468ee52affSYegappan Lakshmanan 		    if (!precision_specified)
23478ee52affSYegappan Lakshmanan 			precision = 1;   // default precision is 1
23488ee52affSYegappan Lakshmanan 		    if (precision == 0 && arg_sign == 0)
23498ee52affSYegappan Lakshmanan 		    {
23508ee52affSYegappan Lakshmanan 			// When zero value is formatted with an explicit
23518ee52affSYegappan Lakshmanan 			// precision 0, the resulting formatted string is
23528ee52affSYegappan Lakshmanan 			// empty (d, i, u, b, B, o, x, X, p).
23538ee52affSYegappan Lakshmanan 		    }
23548ee52affSYegappan Lakshmanan 		    else
23558ee52affSYegappan Lakshmanan 		    {
23568ee52affSYegappan Lakshmanan 			char	f[6];
23578ee52affSYegappan Lakshmanan 			int	f_l = 0;
23588ee52affSYegappan Lakshmanan 
23598ee52affSYegappan Lakshmanan 			// construct a simple format string for sprintf
23608ee52affSYegappan Lakshmanan 			f[f_l++] = '%';
23618ee52affSYegappan Lakshmanan 			if (!length_modifier)
23628ee52affSYegappan Lakshmanan 			    ;
23638ee52affSYegappan Lakshmanan 			else if (length_modifier == 'L')
23648ee52affSYegappan Lakshmanan 			{
23658ee52affSYegappan Lakshmanan # ifdef MSWIN
23668ee52affSYegappan Lakshmanan 			    f[f_l++] = 'I';
23678ee52affSYegappan Lakshmanan 			    f[f_l++] = '6';
23688ee52affSYegappan Lakshmanan 			    f[f_l++] = '4';
23698ee52affSYegappan Lakshmanan # else
23708ee52affSYegappan Lakshmanan 			    f[f_l++] = 'l';
23718ee52affSYegappan Lakshmanan 			    f[f_l++] = 'l';
23728ee52affSYegappan Lakshmanan # endif
23738ee52affSYegappan Lakshmanan 			}
23748ee52affSYegappan Lakshmanan 			else
23758ee52affSYegappan Lakshmanan 			    f[f_l++] = length_modifier;
23768ee52affSYegappan Lakshmanan 			f[f_l++] = fmt_spec;
23778ee52affSYegappan Lakshmanan 			f[f_l++] = '\0';
23788ee52affSYegappan Lakshmanan 
23798ee52affSYegappan Lakshmanan 			if (fmt_spec == 'p')
23808ee52affSYegappan Lakshmanan 			    str_arg_l += sprintf(tmp + str_arg_l, f, ptr_arg);
23818ee52affSYegappan Lakshmanan 			else if (fmt_spec == 'b' || fmt_spec == 'B')
23828ee52affSYegappan Lakshmanan 			{
23838ee52affSYegappan Lakshmanan 			    char	    b[8 * sizeof(uvarnumber_T)];
23848ee52affSYegappan Lakshmanan 			    size_t	    b_l = 0;
23858ee52affSYegappan Lakshmanan 			    uvarnumber_T    bn = bin_arg;
23868ee52affSYegappan Lakshmanan 
23878ee52affSYegappan Lakshmanan 			    do
23888ee52affSYegappan Lakshmanan 			    {
23898ee52affSYegappan Lakshmanan 				b[sizeof(b) - ++b_l] = '0' + (bn & 0x1);
23908ee52affSYegappan Lakshmanan 				bn >>= 1;
23918ee52affSYegappan Lakshmanan 			    }
23928ee52affSYegappan Lakshmanan 			    while (bn != 0);
23938ee52affSYegappan Lakshmanan 
23948ee52affSYegappan Lakshmanan 			    memcpy(tmp + str_arg_l, b + sizeof(b) - b_l, b_l);
23958ee52affSYegappan Lakshmanan 			    str_arg_l += b_l;
23968ee52affSYegappan Lakshmanan 			}
23978ee52affSYegappan Lakshmanan 			else if (fmt_spec == 'd')
23988ee52affSYegappan Lakshmanan 			{
23998ee52affSYegappan Lakshmanan 			    // signed
24008ee52affSYegappan Lakshmanan 			    switch (length_modifier)
24018ee52affSYegappan Lakshmanan 			    {
24028ee52affSYegappan Lakshmanan 			    case '\0': str_arg_l += sprintf(
24038ee52affSYegappan Lakshmanan 						 tmp + str_arg_l, f,
24048ee52affSYegappan Lakshmanan 						 int_arg);
24058ee52affSYegappan Lakshmanan 				       break;
24068ee52affSYegappan Lakshmanan 			    case 'h': str_arg_l += sprintf(
24078ee52affSYegappan Lakshmanan 						 tmp + str_arg_l, f,
24088ee52affSYegappan Lakshmanan 						 (short)int_arg);
24098ee52affSYegappan Lakshmanan 				      break;
24108ee52affSYegappan Lakshmanan 			    case 'l': str_arg_l += sprintf(
24118ee52affSYegappan Lakshmanan 						tmp + str_arg_l, f, long_arg);
24128ee52affSYegappan Lakshmanan 				      break;
24138ee52affSYegappan Lakshmanan 			    case 'L': str_arg_l += sprintf(
24148ee52affSYegappan Lakshmanan 					       tmp + str_arg_l, f, llong_arg);
24158ee52affSYegappan Lakshmanan 				      break;
24168ee52affSYegappan Lakshmanan 			    }
24178ee52affSYegappan Lakshmanan 			}
24188ee52affSYegappan Lakshmanan 			else
24198ee52affSYegappan Lakshmanan 			{
24208ee52affSYegappan Lakshmanan 			    // unsigned
24218ee52affSYegappan Lakshmanan 			    switch (length_modifier)
24228ee52affSYegappan Lakshmanan 			    {
24238ee52affSYegappan Lakshmanan 			    case '\0': str_arg_l += sprintf(
24248ee52affSYegappan Lakshmanan 						tmp + str_arg_l, f,
24258ee52affSYegappan Lakshmanan 						uint_arg);
24268ee52affSYegappan Lakshmanan 				       break;
24278ee52affSYegappan Lakshmanan 			    case 'h': str_arg_l += sprintf(
24288ee52affSYegappan Lakshmanan 						tmp + str_arg_l, f,
24298ee52affSYegappan Lakshmanan 						(unsigned short)uint_arg);
24308ee52affSYegappan Lakshmanan 				      break;
24318ee52affSYegappan Lakshmanan 			    case 'l': str_arg_l += sprintf(
24328ee52affSYegappan Lakshmanan 					       tmp + str_arg_l, f, ulong_arg);
24338ee52affSYegappan Lakshmanan 				      break;
24348ee52affSYegappan Lakshmanan 			    case 'L': str_arg_l += sprintf(
24358ee52affSYegappan Lakshmanan 					      tmp + str_arg_l, f, ullong_arg);
24368ee52affSYegappan Lakshmanan 				      break;
24378ee52affSYegappan Lakshmanan 			    }
24388ee52affSYegappan Lakshmanan 			}
24398ee52affSYegappan Lakshmanan 
24408ee52affSYegappan Lakshmanan 			// include the optional minus sign and possible
24418ee52affSYegappan Lakshmanan 			// "0x" in the region before the zero padding
24428ee52affSYegappan Lakshmanan 			// insertion point
24438ee52affSYegappan Lakshmanan 			if (zero_padding_insertion_ind < str_arg_l
24448ee52affSYegappan Lakshmanan 				&& tmp[zero_padding_insertion_ind] == '-')
24458ee52affSYegappan Lakshmanan 			    zero_padding_insertion_ind++;
24468ee52affSYegappan Lakshmanan 			if (zero_padding_insertion_ind + 1 < str_arg_l
24478ee52affSYegappan Lakshmanan 				&& tmp[zero_padding_insertion_ind]   == '0'
24488ee52affSYegappan Lakshmanan 				&& (tmp[zero_padding_insertion_ind + 1] == 'x'
24498ee52affSYegappan Lakshmanan 				 || tmp[zero_padding_insertion_ind + 1] == 'X'))
24508ee52affSYegappan Lakshmanan 			    zero_padding_insertion_ind += 2;
24518ee52affSYegappan Lakshmanan 		    }
24528ee52affSYegappan Lakshmanan 
24538ee52affSYegappan Lakshmanan 		    {
24548ee52affSYegappan Lakshmanan 			size_t num_of_digits = str_arg_l
24558ee52affSYegappan Lakshmanan 						 - zero_padding_insertion_ind;
24568ee52affSYegappan Lakshmanan 
24578ee52affSYegappan Lakshmanan 			if (alternate_form && fmt_spec == 'o'
24588ee52affSYegappan Lakshmanan 				// unless zero is already the first
24598ee52affSYegappan Lakshmanan 				// character
24608ee52affSYegappan Lakshmanan 				&& !(zero_padding_insertion_ind < str_arg_l
24618ee52affSYegappan Lakshmanan 				    && tmp[zero_padding_insertion_ind] == '0'))
24628ee52affSYegappan Lakshmanan 			{
24638ee52affSYegappan Lakshmanan 			    // assure leading zero for alternate-form
24648ee52affSYegappan Lakshmanan 			    // octal numbers
24658ee52affSYegappan Lakshmanan 			    if (!precision_specified
24668ee52affSYegappan Lakshmanan 					     || precision < num_of_digits + 1)
24678ee52affSYegappan Lakshmanan 			    {
24688ee52affSYegappan Lakshmanan 				// precision is increased to force the
24698ee52affSYegappan Lakshmanan 				// first character to be zero, except if a
24708ee52affSYegappan Lakshmanan 				// zero value is formatted with an
24718ee52affSYegappan Lakshmanan 				// explicit precision of zero
24728ee52affSYegappan Lakshmanan 				precision = num_of_digits + 1;
24738ee52affSYegappan Lakshmanan 			    }
24748ee52affSYegappan Lakshmanan 			}
24758ee52affSYegappan Lakshmanan 			// zero padding to specified precision?
24768ee52affSYegappan Lakshmanan 			if (num_of_digits < precision)
24778ee52affSYegappan Lakshmanan 			    number_of_zeros_to_pad = precision - num_of_digits;
24788ee52affSYegappan Lakshmanan 		    }
24798ee52affSYegappan Lakshmanan 		    // zero padding to specified minimal field width?
24808ee52affSYegappan Lakshmanan 		    if (!justify_left && zero_padding)
24818ee52affSYegappan Lakshmanan 		    {
24828ee52affSYegappan Lakshmanan 			int n = (int)(min_field_width - (str_arg_l
24838ee52affSYegappan Lakshmanan 						    + number_of_zeros_to_pad));
24848ee52affSYegappan Lakshmanan 			if (n > 0)
24858ee52affSYegappan Lakshmanan 			    number_of_zeros_to_pad += n;
24868ee52affSYegappan Lakshmanan 		    }
24878ee52affSYegappan Lakshmanan 		    break;
24888ee52affSYegappan Lakshmanan 		}
24898ee52affSYegappan Lakshmanan 
24908ee52affSYegappan Lakshmanan # ifdef FEAT_FLOAT
24918ee52affSYegappan Lakshmanan 	    case 'f':
24928ee52affSYegappan Lakshmanan 	    case 'F':
24938ee52affSYegappan Lakshmanan 	    case 'e':
24948ee52affSYegappan Lakshmanan 	    case 'E':
24958ee52affSYegappan Lakshmanan 	    case 'g':
24968ee52affSYegappan Lakshmanan 	    case 'G':
24978ee52affSYegappan Lakshmanan 		{
24988ee52affSYegappan Lakshmanan 		    // Floating point.
24998ee52affSYegappan Lakshmanan 		    double	f;
25008ee52affSYegappan Lakshmanan 		    double	abs_f;
25018ee52affSYegappan Lakshmanan 		    char	format[40];
25028ee52affSYegappan Lakshmanan 		    int		l;
25038ee52affSYegappan Lakshmanan 		    int		remove_trailing_zeroes = FALSE;
25048ee52affSYegappan Lakshmanan 
25058ee52affSYegappan Lakshmanan 		    f =
25068ee52affSYegappan Lakshmanan #  if defined(FEAT_EVAL)
25078ee52affSYegappan Lakshmanan 			tvs != NULL ? tv_float(tvs, &arg_idx) :
25088ee52affSYegappan Lakshmanan #  endif
25098ee52affSYegappan Lakshmanan 			    va_arg(ap, double);
25108ee52affSYegappan Lakshmanan 		    abs_f = f < 0 ? -f : f;
25118ee52affSYegappan Lakshmanan 
25128ee52affSYegappan Lakshmanan 		    if (fmt_spec == 'g' || fmt_spec == 'G')
25138ee52affSYegappan Lakshmanan 		    {
25148ee52affSYegappan Lakshmanan 			// Would be nice to use %g directly, but it prints
25158ee52affSYegappan Lakshmanan 			// "1.0" as "1", we don't want that.
25168ee52affSYegappan Lakshmanan 			if ((abs_f >= 0.001 && abs_f < 10000000.0)
25178ee52affSYegappan Lakshmanan 							      || abs_f == 0.0)
25188ee52affSYegappan Lakshmanan 			    fmt_spec = ASCII_ISUPPER(fmt_spec) ? 'F' : 'f';
25198ee52affSYegappan Lakshmanan 			else
25208ee52affSYegappan Lakshmanan 			    fmt_spec = fmt_spec == 'g' ? 'e' : 'E';
25218ee52affSYegappan Lakshmanan 			remove_trailing_zeroes = TRUE;
25228ee52affSYegappan Lakshmanan 		    }
25238ee52affSYegappan Lakshmanan 
25248ee52affSYegappan Lakshmanan 		    if ((fmt_spec == 'f' || fmt_spec == 'F') &&
25258ee52affSYegappan Lakshmanan #  ifdef VAX
25268ee52affSYegappan Lakshmanan 			    abs_f > 1.0e38
25278ee52affSYegappan Lakshmanan #  else
25288ee52affSYegappan Lakshmanan 			    abs_f > 1.0e307
25298ee52affSYegappan Lakshmanan #  endif
25308ee52affSYegappan Lakshmanan 			    )
25318ee52affSYegappan Lakshmanan 		    {
25328ee52affSYegappan Lakshmanan 			// Avoid a buffer overflow
25338ee52affSYegappan Lakshmanan 			STRCPY(tmp, infinity_str(f > 0.0, fmt_spec,
25348ee52affSYegappan Lakshmanan 					      force_sign, space_for_positive));
25358ee52affSYegappan Lakshmanan 			str_arg_l = STRLEN(tmp);
25368ee52affSYegappan Lakshmanan 			zero_padding = 0;
25378ee52affSYegappan Lakshmanan 		    }
25388ee52affSYegappan Lakshmanan 		    else
25398ee52affSYegappan Lakshmanan 		    {
25408ee52affSYegappan Lakshmanan 			if (isnan(f))
25418ee52affSYegappan Lakshmanan 			{
25428ee52affSYegappan Lakshmanan 			    // Not a number: nan or NAN
25438ee52affSYegappan Lakshmanan 			    STRCPY(tmp, ASCII_ISUPPER(fmt_spec) ? "NAN"
25448ee52affSYegappan Lakshmanan 								      : "nan");
25458ee52affSYegappan Lakshmanan 			    str_arg_l = 3;
25468ee52affSYegappan Lakshmanan 			    zero_padding = 0;
25478ee52affSYegappan Lakshmanan 			}
25488ee52affSYegappan Lakshmanan 			else if (isinf(f))
25498ee52affSYegappan Lakshmanan 			{
25508ee52affSYegappan Lakshmanan 			    STRCPY(tmp, infinity_str(f > 0.0, fmt_spec,
25518ee52affSYegappan Lakshmanan 					      force_sign, space_for_positive));
25528ee52affSYegappan Lakshmanan 			    str_arg_l = STRLEN(tmp);
25538ee52affSYegappan Lakshmanan 			    zero_padding = 0;
25548ee52affSYegappan Lakshmanan 			}
25558ee52affSYegappan Lakshmanan 			else
25568ee52affSYegappan Lakshmanan 			{
25578ee52affSYegappan Lakshmanan 			    // Regular float number
25588ee52affSYegappan Lakshmanan 			    format[0] = '%';
25598ee52affSYegappan Lakshmanan 			    l = 1;
25608ee52affSYegappan Lakshmanan 			    if (force_sign)
25618ee52affSYegappan Lakshmanan 				format[l++] = space_for_positive ? ' ' : '+';
25628ee52affSYegappan Lakshmanan 			    if (precision_specified)
25638ee52affSYegappan Lakshmanan 			    {
25648ee52affSYegappan Lakshmanan 				size_t max_prec = TMP_LEN - 10;
25658ee52affSYegappan Lakshmanan 
25668ee52affSYegappan Lakshmanan 				// Make sure we don't get more digits than we
25678ee52affSYegappan Lakshmanan 				// have room for.
25688ee52affSYegappan Lakshmanan 				if ((fmt_spec == 'f' || fmt_spec == 'F')
25698ee52affSYegappan Lakshmanan 								&& abs_f > 1.0)
25708ee52affSYegappan Lakshmanan 				    max_prec -= (size_t)log10(abs_f);
25718ee52affSYegappan Lakshmanan 				if (precision > max_prec)
25728ee52affSYegappan Lakshmanan 				    precision = max_prec;
25738ee52affSYegappan Lakshmanan 				l += sprintf(format + l, ".%d", (int)precision);
25748ee52affSYegappan Lakshmanan 			    }
25758ee52affSYegappan Lakshmanan 			    format[l] = fmt_spec == 'F' ? 'f' : fmt_spec;
25768ee52affSYegappan Lakshmanan 			    format[l + 1] = NUL;
25778ee52affSYegappan Lakshmanan 
25788ee52affSYegappan Lakshmanan 			    str_arg_l = sprintf(tmp, format, f);
25798ee52affSYegappan Lakshmanan 			}
25808ee52affSYegappan Lakshmanan 
25818ee52affSYegappan Lakshmanan 			if (remove_trailing_zeroes)
25828ee52affSYegappan Lakshmanan 			{
25838ee52affSYegappan Lakshmanan 			    int i;
25848ee52affSYegappan Lakshmanan 			    char *tp;
25858ee52affSYegappan Lakshmanan 
25868ee52affSYegappan Lakshmanan 			    // Using %g or %G: remove superfluous zeroes.
25878ee52affSYegappan Lakshmanan 			    if (fmt_spec == 'f' || fmt_spec == 'F')
25888ee52affSYegappan Lakshmanan 				tp = tmp + str_arg_l - 1;
25898ee52affSYegappan Lakshmanan 			    else
25908ee52affSYegappan Lakshmanan 			    {
25918ee52affSYegappan Lakshmanan 				tp = (char *)vim_strchr((char_u *)tmp,
25928ee52affSYegappan Lakshmanan 						 fmt_spec == 'e' ? 'e' : 'E');
25938ee52affSYegappan Lakshmanan 				if (tp != NULL)
25948ee52affSYegappan Lakshmanan 				{
25958ee52affSYegappan Lakshmanan 				    // Remove superfluous '+' and leading
25968ee52affSYegappan Lakshmanan 				    // zeroes from the exponent.
25978ee52affSYegappan Lakshmanan 				    if (tp[1] == '+')
25988ee52affSYegappan Lakshmanan 				    {
25998ee52affSYegappan Lakshmanan 					// Change "1.0e+07" to "1.0e07"
26008ee52affSYegappan Lakshmanan 					STRMOVE(tp + 1, tp + 2);
26018ee52affSYegappan Lakshmanan 					--str_arg_l;
26028ee52affSYegappan Lakshmanan 				    }
26038ee52affSYegappan Lakshmanan 				    i = (tp[1] == '-') ? 2 : 1;
26048ee52affSYegappan Lakshmanan 				    while (tp[i] == '0')
26058ee52affSYegappan Lakshmanan 				    {
26068ee52affSYegappan Lakshmanan 					// Change "1.0e07" to "1.0e7"
26078ee52affSYegappan Lakshmanan 					STRMOVE(tp + i, tp + i + 1);
26088ee52affSYegappan Lakshmanan 					--str_arg_l;
26098ee52affSYegappan Lakshmanan 				    }
26108ee52affSYegappan Lakshmanan 				    --tp;
26118ee52affSYegappan Lakshmanan 				}
26128ee52affSYegappan Lakshmanan 			    }
26138ee52affSYegappan Lakshmanan 
26148ee52affSYegappan Lakshmanan 			    if (tp != NULL && !precision_specified)
26158ee52affSYegappan Lakshmanan 				// Remove trailing zeroes, but keep the one
26168ee52affSYegappan Lakshmanan 				// just after a dot.
26178ee52affSYegappan Lakshmanan 				while (tp > tmp + 2 && *tp == '0'
26188ee52affSYegappan Lakshmanan 							     && tp[-1] != '.')
26198ee52affSYegappan Lakshmanan 				{
26208ee52affSYegappan Lakshmanan 				    STRMOVE(tp, tp + 1);
26218ee52affSYegappan Lakshmanan 				    --tp;
26228ee52affSYegappan Lakshmanan 				    --str_arg_l;
26238ee52affSYegappan Lakshmanan 				}
26248ee52affSYegappan Lakshmanan 			}
26258ee52affSYegappan Lakshmanan 			else
26268ee52affSYegappan Lakshmanan 			{
26278ee52affSYegappan Lakshmanan 			    char *tp;
26288ee52affSYegappan Lakshmanan 
26298ee52affSYegappan Lakshmanan 			    // Be consistent: some printf("%e") use 1.0e+12
26308ee52affSYegappan Lakshmanan 			    // and some 1.0e+012.  Remove one zero in the last
26318ee52affSYegappan Lakshmanan 			    // case.
26328ee52affSYegappan Lakshmanan 			    tp = (char *)vim_strchr((char_u *)tmp,
26338ee52affSYegappan Lakshmanan 						 fmt_spec == 'e' ? 'e' : 'E');
26348ee52affSYegappan Lakshmanan 			    if (tp != NULL && (tp[1] == '+' || tp[1] == '-')
26358ee52affSYegappan Lakshmanan 					  && tp[2] == '0'
26368ee52affSYegappan Lakshmanan 					  && vim_isdigit(tp[3])
26378ee52affSYegappan Lakshmanan 					  && vim_isdigit(tp[4]))
26388ee52affSYegappan Lakshmanan 			    {
26398ee52affSYegappan Lakshmanan 				STRMOVE(tp + 2, tp + 3);
26408ee52affSYegappan Lakshmanan 				--str_arg_l;
26418ee52affSYegappan Lakshmanan 			    }
26428ee52affSYegappan Lakshmanan 			}
26438ee52affSYegappan Lakshmanan 		    }
26448ee52affSYegappan Lakshmanan 		    if (zero_padding && min_field_width > str_arg_l
26458ee52affSYegappan Lakshmanan 					      && (tmp[0] == '-' || force_sign))
26468ee52affSYegappan Lakshmanan 		    {
26478ee52affSYegappan Lakshmanan 			// padding 0's should be inserted after the sign
26488ee52affSYegappan Lakshmanan 			number_of_zeros_to_pad = min_field_width - str_arg_l;
26498ee52affSYegappan Lakshmanan 			zero_padding_insertion_ind = 1;
26508ee52affSYegappan Lakshmanan 		    }
26518ee52affSYegappan Lakshmanan 		    str_arg = tmp;
26528ee52affSYegappan Lakshmanan 		    break;
26538ee52affSYegappan Lakshmanan 		}
26548ee52affSYegappan Lakshmanan # endif
26558ee52affSYegappan Lakshmanan 
26568ee52affSYegappan Lakshmanan 	    default:
26578ee52affSYegappan Lakshmanan 		// unrecognized conversion specifier, keep format string
26588ee52affSYegappan Lakshmanan 		// as-is
26598ee52affSYegappan Lakshmanan 		zero_padding = 0;  // turn zero padding off for non-numeric
26608ee52affSYegappan Lakshmanan 				   // conversion
26618ee52affSYegappan Lakshmanan 		justify_left = 1;
26628ee52affSYegappan Lakshmanan 		min_field_width = 0;		    // reset flags
26638ee52affSYegappan Lakshmanan 
26648ee52affSYegappan Lakshmanan 		// discard the unrecognized conversion, just keep *
26658ee52affSYegappan Lakshmanan 		// the unrecognized conversion character
26668ee52affSYegappan Lakshmanan 		str_arg = p;
26678ee52affSYegappan Lakshmanan 		str_arg_l = 0;
26688ee52affSYegappan Lakshmanan 		if (*p != NUL)
26698ee52affSYegappan Lakshmanan 		    str_arg_l++;  // include invalid conversion specifier
26708ee52affSYegappan Lakshmanan 				  // unchanged if not at end-of-string
26718ee52affSYegappan Lakshmanan 		break;
26728ee52affSYegappan Lakshmanan 	    }
26738ee52affSYegappan Lakshmanan 
26748ee52affSYegappan Lakshmanan 	    if (*p != NUL)
26758ee52affSYegappan Lakshmanan 		p++;     // step over the just processed conversion specifier
26768ee52affSYegappan Lakshmanan 
26778ee52affSYegappan Lakshmanan 	    // insert padding to the left as requested by min_field_width;
26788ee52affSYegappan Lakshmanan 	    // this does not include the zero padding in case of numerical
26798ee52affSYegappan Lakshmanan 	    // conversions
26808ee52affSYegappan Lakshmanan 	    if (!justify_left)
26818ee52affSYegappan Lakshmanan 	    {
26828ee52affSYegappan Lakshmanan 		// left padding with blank or zero
26838ee52affSYegappan Lakshmanan 		int pn = (int)(min_field_width - (str_arg_l + number_of_zeros_to_pad));
26848ee52affSYegappan Lakshmanan 
26858ee52affSYegappan Lakshmanan 		if (pn > 0)
26868ee52affSYegappan Lakshmanan 		{
26878ee52affSYegappan Lakshmanan 		    if (str_l < str_m)
26888ee52affSYegappan Lakshmanan 		    {
26898ee52affSYegappan Lakshmanan 			size_t avail = str_m - str_l;
26908ee52affSYegappan Lakshmanan 
26918ee52affSYegappan Lakshmanan 			vim_memset(str + str_l, zero_padding ? '0' : ' ',
26928ee52affSYegappan Lakshmanan 					     (size_t)pn > avail ? avail
26938ee52affSYegappan Lakshmanan 								: (size_t)pn);
26948ee52affSYegappan Lakshmanan 		    }
26958ee52affSYegappan Lakshmanan 		    str_l += pn;
26968ee52affSYegappan Lakshmanan 		}
26978ee52affSYegappan Lakshmanan 	    }
26988ee52affSYegappan Lakshmanan 
26998ee52affSYegappan Lakshmanan 	    // zero padding as requested by the precision or by the minimal
27008ee52affSYegappan Lakshmanan 	    // field width for numeric conversions required?
27018ee52affSYegappan Lakshmanan 	    if (number_of_zeros_to_pad == 0)
27028ee52affSYegappan Lakshmanan 	    {
27038ee52affSYegappan Lakshmanan 		// will not copy first part of numeric right now, *
27048ee52affSYegappan Lakshmanan 		// force it to be copied later in its entirety
27058ee52affSYegappan Lakshmanan 		zero_padding_insertion_ind = 0;
27068ee52affSYegappan Lakshmanan 	    }
27078ee52affSYegappan Lakshmanan 	    else
27088ee52affSYegappan Lakshmanan 	    {
27098ee52affSYegappan Lakshmanan 		// insert first part of numerics (sign or '0x') before zero
27108ee52affSYegappan Lakshmanan 		// padding
27118ee52affSYegappan Lakshmanan 		int zn = (int)zero_padding_insertion_ind;
27128ee52affSYegappan Lakshmanan 
27138ee52affSYegappan Lakshmanan 		if (zn > 0)
27148ee52affSYegappan Lakshmanan 		{
27158ee52affSYegappan Lakshmanan 		    if (str_l < str_m)
27168ee52affSYegappan Lakshmanan 		    {
27178ee52affSYegappan Lakshmanan 			size_t avail = str_m - str_l;
27188ee52affSYegappan Lakshmanan 
27198ee52affSYegappan Lakshmanan 			mch_memmove(str + str_l, str_arg,
27208ee52affSYegappan Lakshmanan 					     (size_t)zn > avail ? avail
27218ee52affSYegappan Lakshmanan 								: (size_t)zn);
27228ee52affSYegappan Lakshmanan 		    }
27238ee52affSYegappan Lakshmanan 		    str_l += zn;
27248ee52affSYegappan Lakshmanan 		}
27258ee52affSYegappan Lakshmanan 
27268ee52affSYegappan Lakshmanan 		// insert zero padding as requested by the precision or min
27278ee52affSYegappan Lakshmanan 		// field width
27288ee52affSYegappan Lakshmanan 		zn = (int)number_of_zeros_to_pad;
27298ee52affSYegappan Lakshmanan 		if (zn > 0)
27308ee52affSYegappan Lakshmanan 		{
27318ee52affSYegappan Lakshmanan 		    if (str_l < str_m)
27328ee52affSYegappan Lakshmanan 		    {
27338ee52affSYegappan Lakshmanan 			size_t avail = str_m - str_l;
27348ee52affSYegappan Lakshmanan 
27358ee52affSYegappan Lakshmanan 			vim_memset(str + str_l, '0',
27368ee52affSYegappan Lakshmanan 					     (size_t)zn > avail ? avail
27378ee52affSYegappan Lakshmanan 								: (size_t)zn);
27388ee52affSYegappan Lakshmanan 		    }
27398ee52affSYegappan Lakshmanan 		    str_l += zn;
27408ee52affSYegappan Lakshmanan 		}
27418ee52affSYegappan Lakshmanan 	    }
27428ee52affSYegappan Lakshmanan 
27438ee52affSYegappan Lakshmanan 	    // insert formatted string
27448ee52affSYegappan Lakshmanan 	    // (or as-is conversion specifier for unknown conversions)
27458ee52affSYegappan Lakshmanan 	    {
27468ee52affSYegappan Lakshmanan 		int sn = (int)(str_arg_l - zero_padding_insertion_ind);
27478ee52affSYegappan Lakshmanan 
27488ee52affSYegappan Lakshmanan 		if (sn > 0)
27498ee52affSYegappan Lakshmanan 		{
27508ee52affSYegappan Lakshmanan 		    if (str_l < str_m)
27518ee52affSYegappan Lakshmanan 		    {
27528ee52affSYegappan Lakshmanan 			size_t avail = str_m - str_l;
27538ee52affSYegappan Lakshmanan 
27548ee52affSYegappan Lakshmanan 			mch_memmove(str + str_l,
27558ee52affSYegappan Lakshmanan 				str_arg + zero_padding_insertion_ind,
27568ee52affSYegappan Lakshmanan 				(size_t)sn > avail ? avail : (size_t)sn);
27578ee52affSYegappan Lakshmanan 		    }
27588ee52affSYegappan Lakshmanan 		    str_l += sn;
27598ee52affSYegappan Lakshmanan 		}
27608ee52affSYegappan Lakshmanan 	    }
27618ee52affSYegappan Lakshmanan 
27628ee52affSYegappan Lakshmanan 	    // insert right padding
27638ee52affSYegappan Lakshmanan 	    if (justify_left)
27648ee52affSYegappan Lakshmanan 	    {
27658ee52affSYegappan Lakshmanan 		// right blank padding to the field width
27668ee52affSYegappan Lakshmanan 		int pn = (int)(min_field_width
27678ee52affSYegappan Lakshmanan 				      - (str_arg_l + number_of_zeros_to_pad));
27688ee52affSYegappan Lakshmanan 
27698ee52affSYegappan Lakshmanan 		if (pn > 0)
27708ee52affSYegappan Lakshmanan 		{
27718ee52affSYegappan Lakshmanan 		    if (str_l < str_m)
27728ee52affSYegappan Lakshmanan 		    {
27738ee52affSYegappan Lakshmanan 			size_t avail = str_m - str_l;
27748ee52affSYegappan Lakshmanan 
27758ee52affSYegappan Lakshmanan 			vim_memset(str + str_l, ' ',
27768ee52affSYegappan Lakshmanan 					     (size_t)pn > avail ? avail
27778ee52affSYegappan Lakshmanan 								: (size_t)pn);
27788ee52affSYegappan Lakshmanan 		    }
27798ee52affSYegappan Lakshmanan 		    str_l += pn;
27808ee52affSYegappan Lakshmanan 		}
27818ee52affSYegappan Lakshmanan 	    }
27828ee52affSYegappan Lakshmanan 	    vim_free(tofree);
27838ee52affSYegappan Lakshmanan 	}
27848ee52affSYegappan Lakshmanan     }
27858ee52affSYegappan Lakshmanan 
27868ee52affSYegappan Lakshmanan     if (str_m > 0)
27878ee52affSYegappan Lakshmanan     {
27888ee52affSYegappan Lakshmanan 	// make sure the string is nul-terminated even at the expense of
27898ee52affSYegappan Lakshmanan 	// overwriting the last character (shouldn't happen, but just in case)
27908ee52affSYegappan Lakshmanan 	//
27918ee52affSYegappan Lakshmanan 	str[str_l <= str_m - 1 ? str_l : str_m - 1] = '\0';
27928ee52affSYegappan Lakshmanan     }
27938ee52affSYegappan Lakshmanan 
27948ee52affSYegappan Lakshmanan     if (tvs != NULL && tvs[arg_idx - 1].v_type != VAR_UNKNOWN)
27958ee52affSYegappan Lakshmanan 	emsg(_("E767: Too many arguments to printf()"));
27968ee52affSYegappan Lakshmanan 
27978ee52affSYegappan Lakshmanan     // Return the number of characters formatted (excluding trailing nul
27988ee52affSYegappan Lakshmanan     // character), that is, the number of characters that would have been
27998ee52affSYegappan Lakshmanan     // written to the buffer if it were large enough.
28008ee52affSYegappan Lakshmanan     return (int)str_l;
28018ee52affSYegappan Lakshmanan }
28028ee52affSYegappan Lakshmanan 
28038ee52affSYegappan Lakshmanan #endif // PROTO
2804