1 /* vi:set ts=8 sts=4 sw=4 noet:
2  *
3  * VIM - Vi IMproved	by Bram Moolenaar
4  *
5  * Do ":help uganda"  in Vim to read copying and usage conditions.
6  * Do ":help credits" in Vim to see a list of people who contributed.
7  * See README.txt for an overview of the Vim source code.
8  */
9 
10 /*
11  * strings.c: string manipulation functions
12  */
13 
14 #define USING_FLOAT_STUFF
15 #include "vim.h"
16 
17 /*
18  * Copy "string" into newly allocated memory.
19  */
20     char_u *
vim_strsave(char_u * string)21 vim_strsave(char_u *string)
22 {
23     char_u	*p;
24     size_t	len;
25 
26     len = STRLEN(string) + 1;
27     p = alloc(len);
28     if (p != NULL)
29 	mch_memmove(p, string, len);
30     return p;
31 }
32 
33 /*
34  * Copy up to "len" bytes of "string" into newly allocated memory and
35  * terminate with a NUL.
36  * The allocated memory always has size "len + 1", also when "string" is
37  * shorter.
38  */
39     char_u *
vim_strnsave(char_u * string,size_t len)40 vim_strnsave(char_u *string, size_t len)
41 {
42     char_u	*p;
43 
44     p = alloc(len + 1);
45     if (p != NULL)
46     {
47 	STRNCPY(p, string, len);
48 	p[len] = NUL;
49     }
50     return p;
51 }
52 
53 /*
54  * Same as vim_strsave(), but any characters found in esc_chars are preceded
55  * by a backslash.
56  */
57     char_u *
vim_strsave_escaped(char_u * string,char_u * esc_chars)58 vim_strsave_escaped(char_u *string, char_u *esc_chars)
59 {
60     return vim_strsave_escaped_ext(string, esc_chars, '\\', FALSE);
61 }
62 
63 /*
64  * Same as vim_strsave_escaped(), but when "bsl" is TRUE also escape
65  * characters where rem_backslash() would remove the backslash.
66  * Escape the characters with "cc".
67  */
68     char_u *
vim_strsave_escaped_ext(char_u * string,char_u * esc_chars,int cc,int bsl)69 vim_strsave_escaped_ext(
70     char_u	*string,
71     char_u	*esc_chars,
72     int		cc,
73     int		bsl)
74 {
75     char_u	*p;
76     char_u	*p2;
77     char_u	*escaped_string;
78     unsigned	length;
79     int		l;
80 
81     // First count the number of backslashes required.
82     // Then allocate the memory and insert them.
83     length = 1;				// count the trailing NUL
84     for (p = string; *p; p++)
85     {
86 	if (has_mbyte && (l = (*mb_ptr2len)(p)) > 1)
87 	{
88 	    length += l;		// count a multibyte char
89 	    p += l - 1;
90 	    continue;
91 	}
92 	if (vim_strchr(esc_chars, *p) != NULL || (bsl && rem_backslash(p)))
93 	    ++length;			// count a backslash
94 	++length;			// count an ordinary char
95     }
96     escaped_string = alloc(length);
97     if (escaped_string != NULL)
98     {
99 	p2 = escaped_string;
100 	for (p = string; *p; p++)
101 	{
102 	    if (has_mbyte && (l = (*mb_ptr2len)(p)) > 1)
103 	    {
104 		mch_memmove(p2, p, (size_t)l);
105 		p2 += l;
106 		p += l - 1;		// skip multibyte char
107 		continue;
108 	    }
109 	    if (vim_strchr(esc_chars, *p) != NULL || (bsl && rem_backslash(p)))
110 		*p2++ = cc;
111 	    *p2++ = *p;
112 	}
113 	*p2 = NUL;
114     }
115     return escaped_string;
116 }
117 
118 /*
119  * Return TRUE when 'shell' has "csh" in the tail.
120  */
121     int
csh_like_shell(void)122 csh_like_shell(void)
123 {
124     return (strstr((char *)gettail(p_sh), "csh") != NULL);
125 }
126 
127 /*
128  * Return TRUE when 'shell' has "fish" in the tail.
129  */
130     static int
fish_like_shell(void)131 fish_like_shell(void)
132 {
133     return (strstr((char *)gettail(p_sh), "fish") != NULL);
134 }
135 
136 /*
137  * Escape "string" for use as a shell argument with system().
138  * This uses single quotes, except when we know we need to use double quotes
139  * (MS-DOS and MS-Windows not using PowerShell and without 'shellslash' set).
140  * PowerShell also uses a novel escaping for enclosed single quotes - double
141  * them up.
142  * Escape a newline, depending on the 'shell' option.
143  * When "do_special" is TRUE also replace "!", "%", "#" and things starting
144  * with "<" like "<cfile>".
145  * When "do_newline" is FALSE do not escape newline unless it is csh shell.
146  * Returns the result in allocated memory, NULL if we have run out.
147  */
148     char_u *
vim_strsave_shellescape(char_u * string,int do_special,int do_newline)149 vim_strsave_shellescape(char_u *string, int do_special, int do_newline)
150 {
151     unsigned	length;
152     char_u	*p;
153     char_u	*d;
154     char_u	*escaped_string;
155     int		l;
156     int		csh_like;
157     int		fish_like;
158     char_u	*shname;
159     int		powershell;
160 # ifdef MSWIN
161     int		double_quotes;
162 # endif
163 
164     // Only csh and similar shells expand '!' within single quotes.  For sh and
165     // the like we must not put a backslash before it, it will be taken
166     // literally.  If do_special is set the '!' will be escaped twice.
167     // Csh also needs to have "\n" escaped twice when do_special is set.
168     csh_like = csh_like_shell();
169 
170     // Fish shell uses '\' as an escape character within single quotes, so '\'
171     // itself must be escaped to get a literal '\'.
172     fish_like = fish_like_shell();
173 
174     // PowerShell uses it's own version for quoting single quotes
175     shname = gettail(p_sh);
176     powershell = strstr((char *)shname, "pwsh") != NULL;
177 # ifdef MSWIN
178     powershell = powershell || strstr((char *)shname, "powershell") != NULL;
179     // PowerShell only accepts single quotes so override shellslash.
180     double_quotes = !powershell && !p_ssl;
181 # endif
182 
183     // First count the number of extra bytes required.
184     length = (unsigned)STRLEN(string) + 3;  // two quotes and a trailing NUL
185     for (p = string; *p != NUL; MB_PTR_ADV(p))
186     {
187 # ifdef MSWIN
188 	if (double_quotes)
189 	{
190 	    if (*p == '"')
191 		++length;		// " -> ""
192 	}
193 	else
194 # endif
195 	if (*p == '\'')
196 	{
197 	    if (powershell)
198 		length +=2;		// ' => ''
199 	    else
200 		length += 3;		// ' => '\''
201 	}
202 	if ((*p == '\n' && (csh_like || do_newline))
203 		|| (*p == '!' && (csh_like || do_special)))
204 	{
205 	    ++length;			// insert backslash
206 	    if (csh_like && do_special)
207 		++length;		// insert backslash
208 	}
209 	if (do_special && find_cmdline_var(p, &l) >= 0)
210 	{
211 	    ++length;			// insert backslash
212 	    p += l - 1;
213 	}
214 	if (*p == '\\' && fish_like)
215 	    ++length;			// insert backslash
216     }
217 
218     // Allocate memory for the result and fill it.
219     escaped_string = alloc(length);
220     if (escaped_string != NULL)
221     {
222 	d = escaped_string;
223 
224 	// add opening quote
225 # ifdef MSWIN
226 	if (double_quotes)
227 	    *d++ = '"';
228 	else
229 # endif
230 	    *d++ = '\'';
231 
232 	for (p = string; *p != NUL; )
233 	{
234 # ifdef MSWIN
235 	    if (double_quotes)
236 	    {
237 		if (*p == '"')
238 		{
239 		    *d++ = '"';
240 		    *d++ = '"';
241 		    ++p;
242 		    continue;
243 		}
244 	    }
245 	    else
246 # endif
247 	    if (*p == '\'')
248 	    {
249 		if (powershell)
250 		{
251 		    *d++ = '\'';
252 		    *d++ = '\'';
253 		}
254 		else
255 		{
256 		    *d++ = '\'';
257 		    *d++ = '\\';
258 		    *d++ = '\'';
259 		    *d++ = '\'';
260 		}
261 		++p;
262 		continue;
263 	    }
264 	    if ((*p == '\n' && (csh_like || do_newline))
265 		    || (*p == '!' && (csh_like || do_special)))
266 	    {
267 		*d++ = '\\';
268 		if (csh_like && do_special)
269 		    *d++ = '\\';
270 		*d++ = *p++;
271 		continue;
272 	    }
273 	    if (do_special && find_cmdline_var(p, &l) >= 0)
274 	    {
275 		*d++ = '\\';		// insert backslash
276 		while (--l >= 0)	// copy the var
277 		    *d++ = *p++;
278 		continue;
279 	    }
280 	    if (*p == '\\' && fish_like)
281 	    {
282 		*d++ = '\\';
283 		*d++ = *p++;
284 		continue;
285 	    }
286 
287 	    MB_COPY_CHAR(p, d);
288 	}
289 
290 	// add terminating quote and finish with a NUL
291 # ifdef MSWIN
292 	if (double_quotes)
293 	    *d++ = '"';
294 	else
295 # endif
296 	    *d++ = '\'';
297 	*d = NUL;
298     }
299 
300     return escaped_string;
301 }
302 
303 /*
304  * Like vim_strsave(), but make all characters uppercase.
305  * This uses ASCII lower-to-upper case translation, language independent.
306  */
307     char_u *
vim_strsave_up(char_u * string)308 vim_strsave_up(char_u *string)
309 {
310     char_u *p1;
311 
312     p1 = vim_strsave(string);
313     vim_strup(p1);
314     return p1;
315 }
316 
317 /*
318  * Like vim_strnsave(), but make all characters uppercase.
319  * This uses ASCII lower-to-upper case translation, language independent.
320  */
321     char_u *
vim_strnsave_up(char_u * string,size_t len)322 vim_strnsave_up(char_u *string, size_t len)
323 {
324     char_u *p1;
325 
326     p1 = vim_strnsave(string, len);
327     vim_strup(p1);
328     return p1;
329 }
330 
331 /*
332  * ASCII lower-to-upper case translation, language independent.
333  */
334     void
vim_strup(char_u * p)335 vim_strup(
336     char_u	*p)
337 {
338     char_u  *p2;
339     int	    c;
340 
341     if (p != NULL)
342     {
343 	p2 = p;
344 	while ((c = *p2) != NUL)
345 #ifdef EBCDIC
346 	    *p2++ = isalpha(c) ? toupper(c) : c;
347 #else
348 	    *p2++ = (c < 'a' || c > 'z') ? c : (c - 0x20);
349 #endif
350     }
351 }
352 
353 #if defined(FEAT_EVAL) || defined(FEAT_SPELL) || defined(PROTO)
354 /*
355  * Make string "s" all upper-case and return it in allocated memory.
356  * Handles multi-byte characters as well as possible.
357  * Returns NULL when out of memory.
358  */
359     static char_u *
strup_save(char_u * orig)360 strup_save(char_u *orig)
361 {
362     char_u	*p;
363     char_u	*res;
364 
365     res = p = vim_strsave(orig);
366 
367     if (res != NULL)
368 	while (*p != NUL)
369 	{
370 	    int		l;
371 
372 	    if (enc_utf8)
373 	    {
374 		int	c, uc;
375 		int	newl;
376 		char_u	*s;
377 
378 		c = utf_ptr2char(p);
379 		l = utf_ptr2len(p);
380 		if (c == 0)
381 		{
382 		    // overlong sequence, use only the first byte
383 		    c = *p;
384 		    l = 1;
385 		}
386 		uc = utf_toupper(c);
387 
388 		// Reallocate string when byte count changes.  This is rare,
389 		// thus it's OK to do another malloc()/free().
390 		newl = utf_char2len(uc);
391 		if (newl != l)
392 		{
393 		    s = alloc(STRLEN(res) + 1 + newl - l);
394 		    if (s == NULL)
395 		    {
396 			vim_free(res);
397 			return NULL;
398 		    }
399 		    mch_memmove(s, res, p - res);
400 		    STRCPY(s + (p - res) + newl, p + l);
401 		    p = s + (p - res);
402 		    vim_free(res);
403 		    res = s;
404 		}
405 
406 		utf_char2bytes(uc, p);
407 		p += newl;
408 	    }
409 	    else if (has_mbyte && (l = (*mb_ptr2len)(p)) > 1)
410 		p += l;		// skip multi-byte character
411 	    else
412 	    {
413 		*p = TOUPPER_LOC(*p); // note that toupper() can be a macro
414 		p++;
415 	    }
416 	}
417 
418     return res;
419 }
420 
421 /*
422  * Make string "s" all lower-case and return it in allocated memory.
423  * Handles multi-byte characters as well as possible.
424  * Returns NULL when out of memory.
425  */
426     char_u *
strlow_save(char_u * orig)427 strlow_save(char_u *orig)
428 {
429     char_u	*p;
430     char_u	*res;
431 
432     res = p = vim_strsave(orig);
433 
434     if (res != NULL)
435 	while (*p != NUL)
436 	{
437 	    int		l;
438 
439 	    if (enc_utf8)
440 	    {
441 		int	c, lc;
442 		int	newl;
443 		char_u	*s;
444 
445 		c = utf_ptr2char(p);
446 		l = utf_ptr2len(p);
447 		if (c == 0)
448 		{
449 		    // overlong sequence, use only the first byte
450 		    c = *p;
451 		    l = 1;
452 		}
453 		lc = utf_tolower(c);
454 
455 		// Reallocate string when byte count changes.  This is rare,
456 		// thus it's OK to do another malloc()/free().
457 		newl = utf_char2len(lc);
458 		if (newl != l)
459 		{
460 		    s = alloc(STRLEN(res) + 1 + newl - l);
461 		    if (s == NULL)
462 		    {
463 			vim_free(res);
464 			return NULL;
465 		    }
466 		    mch_memmove(s, res, p - res);
467 		    STRCPY(s + (p - res) + newl, p + l);
468 		    p = s + (p - res);
469 		    vim_free(res);
470 		    res = s;
471 		}
472 
473 		utf_char2bytes(lc, p);
474 		p += newl;
475 	    }
476 	    else if (has_mbyte && (l = (*mb_ptr2len)(p)) > 1)
477 		p += l;		// skip multi-byte character
478 	    else
479 	    {
480 		*p = TOLOWER_LOC(*p); // note that tolower() can be a macro
481 		p++;
482 	    }
483 	}
484 
485     return res;
486 }
487 #endif
488 
489 /*
490  * delete spaces at the end of a string
491  */
492     void
del_trailing_spaces(char_u * ptr)493 del_trailing_spaces(char_u *ptr)
494 {
495     char_u	*q;
496 
497     q = ptr + STRLEN(ptr);
498     while (--q > ptr && VIM_ISWHITE(q[0]) && q[-1] != '\\' && q[-1] != Ctrl_V)
499 	*q = NUL;
500 }
501 
502 /*
503  * Like strncpy(), but always terminate the result with one NUL.
504  * "to" must be "len + 1" long!
505  */
506     void
vim_strncpy(char_u * to,char_u * from,size_t len)507 vim_strncpy(char_u *to, char_u *from, size_t len)
508 {
509     STRNCPY(to, from, len);
510     to[len] = NUL;
511 }
512 
513 /*
514  * Like strcat(), but make sure the result fits in "tosize" bytes and is
515  * always NUL terminated. "from" and "to" may overlap.
516  */
517     void
vim_strcat(char_u * to,char_u * from,size_t tosize)518 vim_strcat(char_u *to, char_u *from, size_t tosize)
519 {
520     size_t tolen = STRLEN(to);
521     size_t fromlen = STRLEN(from);
522 
523     if (tolen + fromlen + 1 > tosize)
524     {
525 	mch_memmove(to + tolen, from, tosize - tolen - 1);
526 	to[tosize - 1] = NUL;
527     }
528     else
529 	mch_memmove(to + tolen, from, fromlen + 1);
530 }
531 
532 #if (!defined(HAVE_STRCASECMP) && !defined(HAVE_STRICMP)) || defined(PROTO)
533 /*
534  * Compare two strings, ignoring case, using current locale.
535  * Doesn't work for multi-byte characters.
536  * return 0 for match, < 0 for smaller, > 0 for bigger
537  */
538     int
vim_stricmp(char * s1,char * s2)539 vim_stricmp(char *s1, char *s2)
540 {
541     int		i;
542 
543     for (;;)
544     {
545 	i = (int)TOLOWER_LOC(*s1) - (int)TOLOWER_LOC(*s2);
546 	if (i != 0)
547 	    return i;			    // this character different
548 	if (*s1 == NUL)
549 	    break;			    // strings match until NUL
550 	++s1;
551 	++s2;
552     }
553     return 0;				    // strings match
554 }
555 #endif
556 
557 #if (!defined(HAVE_STRNCASECMP) && !defined(HAVE_STRNICMP)) || defined(PROTO)
558 /*
559  * Compare two strings, for length "len", ignoring case, using current locale.
560  * Doesn't work for multi-byte characters.
561  * return 0 for match, < 0 for smaller, > 0 for bigger
562  */
563     int
vim_strnicmp(char * s1,char * s2,size_t len)564 vim_strnicmp(char *s1, char *s2, size_t len)
565 {
566     int		i;
567 
568     while (len > 0)
569     {
570 	i = (int)TOLOWER_LOC(*s1) - (int)TOLOWER_LOC(*s2);
571 	if (i != 0)
572 	    return i;			    // this character different
573 	if (*s1 == NUL)
574 	    break;			    // strings match until NUL
575 	++s1;
576 	++s2;
577 	--len;
578     }
579     return 0;				    // strings match
580 }
581 #endif
582 
583 /*
584  * Search for first occurrence of "c" in "string".
585  * Version of strchr() that handles unsigned char strings with characters from
586  * 128 to 255 correctly.  It also doesn't return a pointer to the NUL at the
587  * end of the string.
588  */
589     char_u  *
vim_strchr(char_u * string,int c)590 vim_strchr(char_u *string, int c)
591 {
592     char_u	*p;
593     int		b;
594 
595     p = string;
596     if (enc_utf8 && c >= 0x80)
597     {
598 	while (*p != NUL)
599 	{
600 	    int l = utfc_ptr2len(p);
601 
602 	    // Avoid matching an illegal byte here.
603 	    if (utf_ptr2char(p) == c && l > 1)
604 		return p;
605 	    p += l;
606 	}
607 	return NULL;
608     }
609     if (enc_dbcs != 0 && c > 255)
610     {
611 	int	n2 = c & 0xff;
612 
613 	c = ((unsigned)c >> 8) & 0xff;
614 	while ((b = *p) != NUL)
615 	{
616 	    if (b == c && p[1] == n2)
617 		return p;
618 	    p += (*mb_ptr2len)(p);
619 	}
620 	return NULL;
621     }
622     if (has_mbyte)
623     {
624 	while ((b = *p) != NUL)
625 	{
626 	    if (b == c)
627 		return p;
628 	    p += (*mb_ptr2len)(p);
629 	}
630 	return NULL;
631     }
632     while ((b = *p) != NUL)
633     {
634 	if (b == c)
635 	    return p;
636 	++p;
637     }
638     return NULL;
639 }
640 
641 /*
642  * Version of strchr() that only works for bytes and handles unsigned char
643  * strings with characters above 128 correctly. It also doesn't return a
644  * pointer to the NUL at the end of the string.
645  */
646     char_u  *
vim_strbyte(char_u * string,int c)647 vim_strbyte(char_u *string, int c)
648 {
649     char_u	*p = string;
650 
651     while (*p != NUL)
652     {
653 	if (*p == c)
654 	    return p;
655 	++p;
656     }
657     return NULL;
658 }
659 
660 /*
661  * Search for last occurrence of "c" in "string".
662  * Version of strrchr() that handles unsigned char strings with characters from
663  * 128 to 255 correctly.  It also doesn't return a pointer to the NUL at the
664  * end of the string.
665  * Return NULL if not found.
666  * Does not handle multi-byte char for "c"!
667  */
668     char_u  *
vim_strrchr(char_u * string,int c)669 vim_strrchr(char_u *string, int c)
670 {
671     char_u	*retval = NULL;
672     char_u	*p = string;
673 
674     while (*p)
675     {
676 	if (*p == c)
677 	    retval = p;
678 	MB_PTR_ADV(p);
679     }
680     return retval;
681 }
682 
683 /*
684  * Vim's version of strpbrk(), in case it's missing.
685  * Don't generate a prototype for this, causes problems when it's not used.
686  */
687 #ifndef PROTO
688 # ifndef HAVE_STRPBRK
689 #  ifdef vim_strpbrk
690 #   undef vim_strpbrk
691 #  endif
692     char_u *
vim_strpbrk(char_u * s,char_u * charset)693 vim_strpbrk(char_u *s, char_u *charset)
694 {
695     while (*s)
696     {
697 	if (vim_strchr(charset, *s) != NULL)
698 	    return s;
699 	MB_PTR_ADV(s);
700     }
701     return NULL;
702 }
703 # endif
704 #endif
705 
706 /*
707  * Sort an array of strings.
708  */
709 static int sort_compare(const void *s1, const void *s2);
710 
711     static int
sort_compare(const void * s1,const void * s2)712 sort_compare(const void *s1, const void *s2)
713 {
714     return STRCMP(*(char **)s1, *(char **)s2);
715 }
716 
717     void
sort_strings(char_u ** files,int count)718 sort_strings(
719     char_u	**files,
720     int		count)
721 {
722     qsort((void *)files, (size_t)count, sizeof(char_u *), sort_compare);
723 }
724 
725 #if defined(FEAT_QUICKFIX) || defined(FEAT_SPELL) || defined(PROTO)
726 /*
727  * Return TRUE if string "s" contains a non-ASCII character (128 or higher).
728  * When "s" is NULL FALSE is returned.
729  */
730     int
has_non_ascii(char_u * s)731 has_non_ascii(char_u *s)
732 {
733     char_u	*p;
734 
735     if (s != NULL)
736 	for (p = s; *p != NUL; ++p)
737 	    if (*p >= 128)
738 		return TRUE;
739     return FALSE;
740 }
741 #endif
742 
743 /*
744  * Concatenate two strings and return the result in allocated memory.
745  * Returns NULL when out of memory.
746  */
747     char_u  *
concat_str(char_u * str1,char_u * str2)748 concat_str(char_u *str1, char_u *str2)
749 {
750     char_u  *dest;
751     size_t  l = str1 == NULL ? 0 : STRLEN(str1);
752 
753     dest = alloc(l + (str2 == NULL ? 0 : STRLEN(str2)) + 1L);
754     if (dest != NULL)
755     {
756 	if (str1 == NULL)
757 	    *dest = NUL;
758 	else
759 	    STRCPY(dest, str1);
760 	if (str2 != NULL)
761 	    STRCPY(dest + l, str2);
762     }
763     return dest;
764 }
765 
766 #if defined(FEAT_EVAL) || defined(PROTO)
767 
768 /*
769  * Return string "str" in ' quotes, doubling ' characters.
770  * If "str" is NULL an empty string is assumed.
771  * If "function" is TRUE make it function('string').
772  */
773     char_u *
string_quote(char_u * str,int function)774 string_quote(char_u *str, int function)
775 {
776     unsigned	len;
777     char_u	*p, *r, *s;
778 
779     len = (function ? 13 : 3);
780     if (str != NULL)
781     {
782 	len += (unsigned)STRLEN(str);
783 	for (p = str; *p != NUL; MB_PTR_ADV(p))
784 	    if (*p == '\'')
785 		++len;
786     }
787     s = r = alloc(len);
788     if (r != NULL)
789     {
790 	if (function)
791 	{
792 	    STRCPY(r, "function('");
793 	    r += 10;
794 	}
795 	else
796 	    *r++ = '\'';
797 	if (str != NULL)
798 	    for (p = str; *p != NUL; )
799 	    {
800 		if (*p == '\'')
801 		    *r++ = '\'';
802 		MB_COPY_CHAR(p, r);
803 	    }
804 	*r++ = '\'';
805 	if (function)
806 	    *r++ = ')';
807 	*r++ = NUL;
808     }
809     return s;
810 }
811 
812     static void
byteidx(typval_T * argvars,typval_T * rettv,int comp UNUSED)813 byteidx(typval_T *argvars, typval_T *rettv, int comp UNUSED)
814 {
815     char_u	*t;
816     char_u	*str;
817     varnumber_T	idx;
818 
819     rettv->vval.v_number = -1;
820 
821     if (in_vim9script()
822 	    && (check_for_string_arg(argvars, 0) == FAIL
823 		|| check_for_number_arg(argvars, 1) == FAIL))
824 	return;
825 
826     str = tv_get_string_chk(&argvars[0]);
827     idx = tv_get_number_chk(&argvars[1], NULL);
828     if (str == NULL || idx < 0)
829 	return;
830 
831     t = str;
832     for ( ; idx > 0; idx--)
833     {
834 	if (*t == NUL)		// EOL reached
835 	    return;
836 	if (enc_utf8 && comp)
837 	    t += utf_ptr2len(t);
838 	else
839 	    t += (*mb_ptr2len)(t);
840     }
841     rettv->vval.v_number = (varnumber_T)(t - str);
842 }
843 
844 /*
845  * "byteidx()" function
846  */
847     void
f_byteidx(typval_T * argvars,typval_T * rettv)848 f_byteidx(typval_T *argvars, typval_T *rettv)
849 {
850     byteidx(argvars, rettv, FALSE);
851 }
852 
853 /*
854  * "byteidxcomp()" function
855  */
856     void
f_byteidxcomp(typval_T * argvars,typval_T * rettv)857 f_byteidxcomp(typval_T *argvars, typval_T *rettv)
858 {
859     byteidx(argvars, rettv, TRUE);
860 }
861 
862 /*
863  * "charidx()" function
864  */
865     void
f_charidx(typval_T * argvars,typval_T * rettv)866 f_charidx(typval_T *argvars, typval_T *rettv)
867 {
868     char_u	*str;
869     varnumber_T	idx;
870     varnumber_T	countcc = FALSE;
871     char_u	*p;
872     int		len;
873     int		(*ptr2len)(char_u *);
874 
875     rettv->vval.v_number = -1;
876 
877     if (in_vim9script()
878 	    && (check_for_string_arg(argvars, 0) == FAIL
879 		|| check_for_number_arg(argvars, 1) == FAIL
880 		|| check_for_opt_bool_arg(argvars, 2) == FAIL))
881 	return;
882 
883     if (argvars[0].v_type != VAR_STRING || argvars[1].v_type != VAR_NUMBER
884 	    || (argvars[2].v_type != VAR_UNKNOWN
885 					   && argvars[2].v_type != VAR_NUMBER
886 					   && argvars[2].v_type != VAR_BOOL))
887     {
888 	emsg(_(e_invarg));
889 	return;
890     }
891 
892     str = tv_get_string_chk(&argvars[0]);
893     idx = tv_get_number_chk(&argvars[1], NULL);
894     if (str == NULL || idx < 0)
895 	return;
896 
897     if (argvars[2].v_type != VAR_UNKNOWN)
898 	countcc = tv_get_bool(&argvars[2]);
899     if (countcc < 0 || countcc > 1)
900     {
901 	semsg(_(e_using_number_as_bool_nr), countcc);
902 	return;
903     }
904 
905     if (enc_utf8 && countcc)
906 	ptr2len = utf_ptr2len;
907     else
908 	ptr2len = mb_ptr2len;
909 
910     for (p = str, len = 0; p <= str + idx; len++)
911     {
912 	if (*p == NUL)
913 	    return;
914 	p += ptr2len(p);
915     }
916 
917     rettv->vval.v_number = len > 0 ? len - 1 : 0;
918 }
919 
920 /*
921  * "str2list()" function
922  */
923     void
f_str2list(typval_T * argvars,typval_T * rettv)924 f_str2list(typval_T *argvars, typval_T *rettv)
925 {
926     char_u	*p;
927     int		utf8 = FALSE;
928 
929     if (rettv_list_alloc(rettv) == FAIL)
930 	return;
931 
932     if (in_vim9script()
933 	    && (check_for_string_arg(argvars, 0) == FAIL
934 		|| check_for_opt_bool_arg(argvars, 1) == FAIL))
935 	return;
936 
937     if (argvars[1].v_type != VAR_UNKNOWN)
938 	utf8 = (int)tv_get_bool_chk(&argvars[1], NULL);
939 
940     p = tv_get_string(&argvars[0]);
941 
942     if (has_mbyte || utf8)
943     {
944 	int (*ptr2len)(char_u *);
945 	int (*ptr2char)(char_u *);
946 
947 	if (utf8 || enc_utf8)
948 	{
949 	    ptr2len = utf_ptr2len;
950 	    ptr2char = utf_ptr2char;
951 	}
952 	else
953 	{
954 	    ptr2len = mb_ptr2len;
955 	    ptr2char = mb_ptr2char;
956 	}
957 
958 	for ( ; *p != NUL; p += (*ptr2len)(p))
959 	    list_append_number(rettv->vval.v_list, (*ptr2char)(p));
960     }
961     else
962 	for ( ; *p != NUL; ++p)
963 	    list_append_number(rettv->vval.v_list, *p);
964 }
965 
966 /*
967  * "str2nr()" function
968  */
969     void
f_str2nr(typval_T * argvars,typval_T * rettv)970 f_str2nr(typval_T *argvars, typval_T *rettv)
971 {
972     int		base = 10;
973     char_u	*p;
974     varnumber_T	n;
975     int		what = 0;
976     int		isneg;
977 
978     if (in_vim9script()
979 	    && (check_for_string_arg(argvars, 0) == FAIL
980 		|| check_for_opt_number_arg(argvars, 1) == FAIL
981 		|| (argvars[1].v_type != VAR_UNKNOWN
982 		    && check_for_opt_bool_arg(argvars, 2) == FAIL)))
983 	return;
984 
985     if (argvars[1].v_type != VAR_UNKNOWN)
986     {
987 	base = (int)tv_get_number(&argvars[1]);
988 	if (base != 2 && base != 8 && base != 10 && base != 16)
989 	{
990 	    emsg(_(e_invarg));
991 	    return;
992 	}
993 	if (argvars[2].v_type != VAR_UNKNOWN && tv_get_bool(&argvars[2]))
994 	    what |= STR2NR_QUOTE;
995     }
996 
997     p = skipwhite(tv_get_string_strict(&argvars[0]));
998     isneg = (*p == '-');
999     if (*p == '+' || *p == '-')
1000 	p = skipwhite(p + 1);
1001     switch (base)
1002     {
1003 	case 2: what |= STR2NR_BIN + STR2NR_FORCE; break;
1004 	case 8: what |= STR2NR_OCT + STR2NR_OOCT + STR2NR_FORCE; break;
1005 	case 16: what |= STR2NR_HEX + STR2NR_FORCE; break;
1006     }
1007     vim_str2nr(p, NULL, NULL, what, &n, NULL, 0, FALSE);
1008     // Text after the number is silently ignored.
1009     if (isneg)
1010 	rettv->vval.v_number = -n;
1011     else
1012 	rettv->vval.v_number = n;
1013 
1014 }
1015 
1016 /*
1017  * "strgetchar()" function
1018  */
1019     void
f_strgetchar(typval_T * argvars,typval_T * rettv)1020 f_strgetchar(typval_T *argvars, typval_T *rettv)
1021 {
1022     char_u	*str;
1023     int		len;
1024     int		error = FALSE;
1025     int		charidx;
1026     int		byteidx = 0;
1027 
1028     rettv->vval.v_number = -1;
1029 
1030     if (in_vim9script()
1031 	    && (check_for_string_arg(argvars, 0) == FAIL
1032 		|| check_for_number_arg(argvars, 1) == FAIL))
1033 	return;
1034 
1035     str = tv_get_string_chk(&argvars[0]);
1036     if (str == NULL)
1037 	return;
1038     len = (int)STRLEN(str);
1039     charidx = (int)tv_get_number_chk(&argvars[1], &error);
1040     if (error)
1041 	return;
1042 
1043     while (charidx >= 0 && byteidx < len)
1044     {
1045 	if (charidx == 0)
1046 	{
1047 	    rettv->vval.v_number = mb_ptr2char(str + byteidx);
1048 	    break;
1049 	}
1050 	--charidx;
1051 	byteidx += MB_CPTR2LEN(str + byteidx);
1052     }
1053 }
1054 
1055 /*
1056  * "stridx()" function
1057  */
1058     void
f_stridx(typval_T * argvars,typval_T * rettv)1059 f_stridx(typval_T *argvars, typval_T *rettv)
1060 {
1061     char_u	buf[NUMBUFLEN];
1062     char_u	*needle;
1063     char_u	*haystack;
1064     char_u	*save_haystack;
1065     char_u	*pos;
1066     int		start_idx;
1067 
1068     if (in_vim9script()
1069 	    && (check_for_string_arg(argvars, 0) == FAIL
1070 		|| check_for_string_arg(argvars, 1) == FAIL
1071 		|| check_for_opt_number_arg(argvars, 2) == FAIL))
1072 	return;
1073 
1074     needle = tv_get_string_chk(&argvars[1]);
1075     save_haystack = haystack = tv_get_string_buf_chk(&argvars[0], buf);
1076     rettv->vval.v_number = -1;
1077     if (needle == NULL || haystack == NULL)
1078 	return;		// type error; errmsg already given
1079 
1080     if (argvars[2].v_type != VAR_UNKNOWN)
1081     {
1082 	int	    error = FALSE;
1083 
1084 	start_idx = (int)tv_get_number_chk(&argvars[2], &error);
1085 	if (error || start_idx >= (int)STRLEN(haystack))
1086 	    return;
1087 	if (start_idx >= 0)
1088 	    haystack += start_idx;
1089     }
1090 
1091     pos	= (char_u *)strstr((char *)haystack, (char *)needle);
1092     if (pos != NULL)
1093 	rettv->vval.v_number = (varnumber_T)(pos - save_haystack);
1094 }
1095 
1096 /*
1097  * "string()" function
1098  */
1099     void
f_string(typval_T * argvars,typval_T * rettv)1100 f_string(typval_T *argvars, typval_T *rettv)
1101 {
1102     char_u	*tofree;
1103     char_u	numbuf[NUMBUFLEN];
1104 
1105     rettv->v_type = VAR_STRING;
1106     rettv->vval.v_string = tv2string(&argvars[0], &tofree, numbuf,
1107 								get_copyID());
1108     // Make a copy if we have a value but it's not in allocated memory.
1109     if (rettv->vval.v_string != NULL && tofree == NULL)
1110 	rettv->vval.v_string = vim_strsave(rettv->vval.v_string);
1111 }
1112 
1113 /*
1114  * "strlen()" function
1115  */
1116     void
f_strlen(typval_T * argvars,typval_T * rettv)1117 f_strlen(typval_T *argvars, typval_T *rettv)
1118 {
1119     if (in_vim9script()
1120 	    && check_for_string_or_number_arg(argvars, 0) == FAIL)
1121 	return;
1122 
1123     rettv->vval.v_number = (varnumber_T)(STRLEN(
1124 					      tv_get_string(&argvars[0])));
1125 }
1126 
1127     static void
strchar_common(typval_T * argvars,typval_T * rettv,int skipcc)1128 strchar_common(typval_T *argvars, typval_T *rettv, int skipcc)
1129 {
1130     char_u		*s = tv_get_string(&argvars[0]);
1131     varnumber_T		len = 0;
1132     int			(*func_mb_ptr2char_adv)(char_u **pp);
1133 
1134     func_mb_ptr2char_adv = skipcc ? mb_ptr2char_adv : mb_cptr2char_adv;
1135     while (*s != NUL)
1136     {
1137 	func_mb_ptr2char_adv(&s);
1138 	++len;
1139     }
1140     rettv->vval.v_number = len;
1141 }
1142 
1143 /*
1144  * "strcharlen()" function
1145  */
1146     void
f_strcharlen(typval_T * argvars,typval_T * rettv)1147 f_strcharlen(typval_T *argvars, typval_T *rettv)
1148 {
1149     if (in_vim9script()
1150 	    && check_for_string_or_number_arg(argvars, 0) == FAIL)
1151 	return;
1152 
1153     strchar_common(argvars, rettv, TRUE);
1154 }
1155 
1156 /*
1157  * "strchars()" function
1158  */
1159     void
f_strchars(typval_T * argvars,typval_T * rettv)1160 f_strchars(typval_T *argvars, typval_T *rettv)
1161 {
1162     varnumber_T		skipcc = FALSE;
1163 
1164     if (in_vim9script()
1165 	    && (check_for_string_arg(argvars, 0) == FAIL
1166 		|| check_for_opt_bool_arg(argvars, 1) == FAIL))
1167 	return;
1168 
1169     if (argvars[1].v_type != VAR_UNKNOWN)
1170 	skipcc = tv_get_bool(&argvars[1]);
1171     if (skipcc < 0 || skipcc > 1)
1172 	semsg(_(e_using_number_as_bool_nr), skipcc);
1173     else
1174 	strchar_common(argvars, rettv, skipcc);
1175 }
1176 
1177 /*
1178  * "strdisplaywidth()" function
1179  */
1180     void
f_strdisplaywidth(typval_T * argvars,typval_T * rettv)1181 f_strdisplaywidth(typval_T *argvars, typval_T *rettv)
1182 {
1183     char_u	*s;
1184     int		col = 0;
1185 
1186     rettv->vval.v_number = -1;
1187 
1188     if (in_vim9script()
1189 	    && (check_for_string_arg(argvars, 0) == FAIL
1190 		|| check_for_opt_number_arg(argvars, 1) == FAIL))
1191 	return;
1192 
1193     s = tv_get_string(&argvars[0]);
1194     if (argvars[1].v_type != VAR_UNKNOWN)
1195 	col = (int)tv_get_number(&argvars[1]);
1196 
1197     rettv->vval.v_number = (varnumber_T)(linetabsize_col(col, s) - col);
1198 }
1199 
1200 /*
1201  * "strwidth()" function
1202  */
1203     void
f_strwidth(typval_T * argvars,typval_T * rettv)1204 f_strwidth(typval_T *argvars, typval_T *rettv)
1205 {
1206     char_u	*s;
1207 
1208     if (in_vim9script() && check_for_string_arg(argvars, 0) == FAIL)
1209 	return;
1210 
1211     s = tv_get_string_strict(&argvars[0]);
1212     rettv->vval.v_number = (varnumber_T)(mb_string2cells(s, -1));
1213 }
1214 
1215 /*
1216  * "strcharpart()" function
1217  */
1218     void
f_strcharpart(typval_T * argvars,typval_T * rettv)1219 f_strcharpart(typval_T *argvars, typval_T *rettv)
1220 {
1221     char_u	*p;
1222     int		nchar;
1223     int		nbyte = 0;
1224     int		charlen;
1225     int		skipcc = FALSE;
1226     int		len = 0;
1227     int		slen;
1228     int		error = FALSE;
1229 
1230     if (in_vim9script()
1231 	    && (check_for_string_arg(argvars, 0) == FAIL
1232 		|| check_for_number_arg(argvars, 1) == FAIL
1233 		|| check_for_opt_number_arg(argvars, 2) == FAIL
1234 		|| (argvars[2].v_type != VAR_UNKNOWN
1235 		    && check_for_opt_bool_arg(argvars, 3) == FAIL)))
1236 	return;
1237 
1238     p = tv_get_string(&argvars[0]);
1239     slen = (int)STRLEN(p);
1240 
1241     nchar = (int)tv_get_number_chk(&argvars[1], &error);
1242     if (!error)
1243     {
1244 	if (argvars[2].v_type != VAR_UNKNOWN
1245 					   && argvars[3].v_type != VAR_UNKNOWN)
1246 	{
1247 	    skipcc = tv_get_bool(&argvars[3]);
1248 	    if (skipcc < 0 || skipcc > 1)
1249 	    {
1250 		semsg(_(e_using_number_as_bool_nr), skipcc);
1251 		return;
1252 	    }
1253 	}
1254 
1255 	if (nchar > 0)
1256 	    while (nchar > 0 && nbyte < slen)
1257 	    {
1258 		if (skipcc)
1259 		    nbyte += mb_ptr2len(p + nbyte);
1260 		else
1261 		    nbyte += MB_CPTR2LEN(p + nbyte);
1262 		--nchar;
1263 	    }
1264 	else
1265 	    nbyte = nchar;
1266 	if (argvars[2].v_type != VAR_UNKNOWN)
1267 	{
1268 	    charlen = (int)tv_get_number(&argvars[2]);
1269 	    while (charlen > 0 && nbyte + len < slen)
1270 	    {
1271 		int off = nbyte + len;
1272 
1273 		if (off < 0)
1274 		    len += 1;
1275 		else
1276 		{
1277 		    if (skipcc)
1278 			len += mb_ptr2len(p + off);
1279 		    else
1280 			len += MB_CPTR2LEN(p + off);
1281 		}
1282 		--charlen;
1283 	    }
1284 	}
1285 	else
1286 	    len = slen - nbyte;    // default: all bytes that are available.
1287     }
1288 
1289     // Only return the overlap between the specified part and the actual
1290     // string.
1291     if (nbyte < 0)
1292     {
1293 	len += nbyte;
1294 	nbyte = 0;
1295     }
1296     else if (nbyte > slen)
1297 	nbyte = slen;
1298     if (len < 0)
1299 	len = 0;
1300     else if (nbyte + len > slen)
1301 	len = slen - nbyte;
1302 
1303     rettv->v_type = VAR_STRING;
1304     rettv->vval.v_string = vim_strnsave(p + nbyte, len);
1305 }
1306 
1307 /*
1308  * "strpart()" function
1309  */
1310     void
f_strpart(typval_T * argvars,typval_T * rettv)1311 f_strpart(typval_T *argvars, typval_T *rettv)
1312 {
1313     char_u	*p;
1314     int		n;
1315     int		len;
1316     int		slen;
1317     int		error = FALSE;
1318 
1319     if (in_vim9script()
1320 	    && (check_for_string_arg(argvars, 0) == FAIL
1321 		|| check_for_number_arg(argvars, 1) == FAIL
1322 		|| check_for_opt_number_arg(argvars, 2) == FAIL
1323 		|| (argvars[2].v_type != VAR_UNKNOWN
1324 		    && check_for_opt_bool_arg(argvars, 3) == FAIL)))
1325 	return;
1326 
1327     p = tv_get_string(&argvars[0]);
1328     slen = (int)STRLEN(p);
1329 
1330     n = (int)tv_get_number_chk(&argvars[1], &error);
1331     if (error)
1332 	len = 0;
1333     else if (argvars[2].v_type != VAR_UNKNOWN)
1334 	len = (int)tv_get_number(&argvars[2]);
1335     else
1336 	len = slen - n;	    // default len: all bytes that are available.
1337 
1338     // Only return the overlap between the specified part and the actual
1339     // string.
1340     if (n < 0)
1341     {
1342 	len += n;
1343 	n = 0;
1344     }
1345     else if (n > slen)
1346 	n = slen;
1347     if (len < 0)
1348 	len = 0;
1349     else if (n + len > slen)
1350 	len = slen - n;
1351 
1352     if (argvars[2].v_type != VAR_UNKNOWN && argvars[3].v_type != VAR_UNKNOWN)
1353     {
1354 	int off;
1355 
1356 	// length in characters
1357 	for (off = n; off < slen && len > 0; --len)
1358 	    off += mb_ptr2len(p + off);
1359 	len = off - n;
1360     }
1361 
1362     rettv->v_type = VAR_STRING;
1363     rettv->vval.v_string = vim_strnsave(p + n, len);
1364 }
1365 
1366 /*
1367  * "strridx()" function
1368  */
1369     void
f_strridx(typval_T * argvars,typval_T * rettv)1370 f_strridx(typval_T *argvars, typval_T *rettv)
1371 {
1372     char_u	buf[NUMBUFLEN];
1373     char_u	*needle;
1374     char_u	*haystack;
1375     char_u	*rest;
1376     char_u	*lastmatch = NULL;
1377     int		haystack_len, end_idx;
1378 
1379     if (in_vim9script()
1380 	    && (check_for_string_arg(argvars, 0) == FAIL
1381 		|| check_for_string_arg(argvars, 1) == FAIL
1382 		|| check_for_opt_number_arg(argvars, 2) == FAIL))
1383 	return;
1384 
1385     needle = tv_get_string_chk(&argvars[1]);
1386     haystack = tv_get_string_buf_chk(&argvars[0], buf);
1387 
1388     rettv->vval.v_number = -1;
1389     if (needle == NULL || haystack == NULL)
1390 	return;		// type error; errmsg already given
1391 
1392     haystack_len = (int)STRLEN(haystack);
1393     if (argvars[2].v_type != VAR_UNKNOWN)
1394     {
1395 	// Third argument: upper limit for index
1396 	end_idx = (int)tv_get_number_chk(&argvars[2], NULL);
1397 	if (end_idx < 0)
1398 	    return;	// can never find a match
1399     }
1400     else
1401 	end_idx = haystack_len;
1402 
1403     if (*needle == NUL)
1404     {
1405 	// Empty string matches past the end.
1406 	lastmatch = haystack + end_idx;
1407     }
1408     else
1409     {
1410 	for (rest = haystack; *rest != '\0'; ++rest)
1411 	{
1412 	    rest = (char_u *)strstr((char *)rest, (char *)needle);
1413 	    if (rest == NULL || rest > haystack + end_idx)
1414 		break;
1415 	    lastmatch = rest;
1416 	}
1417     }
1418 
1419     if (lastmatch == NULL)
1420 	rettv->vval.v_number = -1;
1421     else
1422 	rettv->vval.v_number = (varnumber_T)(lastmatch - haystack);
1423 }
1424 
1425 /*
1426  * "strtrans()" function
1427  */
1428     void
f_strtrans(typval_T * argvars,typval_T * rettv)1429 f_strtrans(typval_T *argvars, typval_T *rettv)
1430 {
1431     if (in_vim9script() && check_for_string_arg(argvars, 0) == FAIL)
1432 	return;
1433 
1434     rettv->v_type = VAR_STRING;
1435     rettv->vval.v_string = transstr(tv_get_string(&argvars[0]));
1436 }
1437 
1438 /*
1439  * "tolower(string)" function
1440  */
1441     void
f_tolower(typval_T * argvars,typval_T * rettv)1442 f_tolower(typval_T *argvars, typval_T *rettv)
1443 {
1444     if (in_vim9script() && check_for_string_arg(argvars, 0) == FAIL)
1445 	return;
1446 
1447     rettv->v_type = VAR_STRING;
1448     rettv->vval.v_string = strlow_save(tv_get_string(&argvars[0]));
1449 }
1450 
1451 /*
1452  * "toupper(string)" function
1453  */
1454     void
f_toupper(typval_T * argvars,typval_T * rettv)1455 f_toupper(typval_T *argvars, typval_T *rettv)
1456 {
1457     if (in_vim9script() && check_for_string_arg(argvars, 0) == FAIL)
1458 	return;
1459 
1460     rettv->v_type = VAR_STRING;
1461     rettv->vval.v_string = strup_save(tv_get_string(&argvars[0]));
1462 }
1463 
1464 /*
1465  * "tr(string, fromstr, tostr)" function
1466  */
1467     void
f_tr(typval_T * argvars,typval_T * rettv)1468 f_tr(typval_T *argvars, typval_T *rettv)
1469 {
1470     char_u	*in_str;
1471     char_u	*fromstr;
1472     char_u	*tostr;
1473     char_u	*p;
1474     int		inlen;
1475     int		fromlen;
1476     int		tolen;
1477     int		idx;
1478     char_u	*cpstr;
1479     int		cplen;
1480     int		first = TRUE;
1481     char_u	buf[NUMBUFLEN];
1482     char_u	buf2[NUMBUFLEN];
1483     garray_T	ga;
1484 
1485     if (in_vim9script()
1486 	    && (check_for_string_arg(argvars, 0) == FAIL
1487 		|| check_for_string_arg(argvars, 1) == FAIL
1488 		|| check_for_string_arg(argvars, 2) == FAIL))
1489 	return;
1490 
1491     in_str = tv_get_string(&argvars[0]);
1492     fromstr = tv_get_string_buf_chk(&argvars[1], buf);
1493     tostr = tv_get_string_buf_chk(&argvars[2], buf2);
1494 
1495     // Default return value: empty string.
1496     rettv->v_type = VAR_STRING;
1497     rettv->vval.v_string = NULL;
1498     if (fromstr == NULL || tostr == NULL)
1499 	    return;		// type error; errmsg already given
1500     ga_init2(&ga, (int)sizeof(char), 80);
1501 
1502     if (!has_mbyte)
1503 	// not multi-byte: fromstr and tostr must be the same length
1504 	if (STRLEN(fromstr) != STRLEN(tostr))
1505 	{
1506 error:
1507 	    semsg(_(e_invarg2), fromstr);
1508 	    ga_clear(&ga);
1509 	    return;
1510 	}
1511 
1512     // fromstr and tostr have to contain the same number of chars
1513     while (*in_str != NUL)
1514     {
1515 	if (has_mbyte)
1516 	{
1517 	    inlen = (*mb_ptr2len)(in_str);
1518 	    cpstr = in_str;
1519 	    cplen = inlen;
1520 	    idx = 0;
1521 	    for (p = fromstr; *p != NUL; p += fromlen)
1522 	    {
1523 		fromlen = (*mb_ptr2len)(p);
1524 		if (fromlen == inlen && STRNCMP(in_str, p, inlen) == 0)
1525 		{
1526 		    for (p = tostr; *p != NUL; p += tolen)
1527 		    {
1528 			tolen = (*mb_ptr2len)(p);
1529 			if (idx-- == 0)
1530 			{
1531 			    cplen = tolen;
1532 			    cpstr = p;
1533 			    break;
1534 			}
1535 		    }
1536 		    if (*p == NUL)	// tostr is shorter than fromstr
1537 			goto error;
1538 		    break;
1539 		}
1540 		++idx;
1541 	    }
1542 
1543 	    if (first && cpstr == in_str)
1544 	    {
1545 		// Check that fromstr and tostr have the same number of
1546 		// (multi-byte) characters.  Done only once when a character
1547 		// of in_str doesn't appear in fromstr.
1548 		first = FALSE;
1549 		for (p = tostr; *p != NUL; p += tolen)
1550 		{
1551 		    tolen = (*mb_ptr2len)(p);
1552 		    --idx;
1553 		}
1554 		if (idx != 0)
1555 		    goto error;
1556 	    }
1557 
1558 	    (void)ga_grow(&ga, cplen);
1559 	    mch_memmove((char *)ga.ga_data + ga.ga_len, cpstr, (size_t)cplen);
1560 	    ga.ga_len += cplen;
1561 
1562 	    in_str += inlen;
1563 	}
1564 	else
1565 	{
1566 	    // When not using multi-byte chars we can do it faster.
1567 	    p = vim_strchr(fromstr, *in_str);
1568 	    if (p != NULL)
1569 		ga_append(&ga, tostr[p - fromstr]);
1570 	    else
1571 		ga_append(&ga, *in_str);
1572 	    ++in_str;
1573 	}
1574     }
1575 
1576     // add a terminating NUL
1577     (void)ga_grow(&ga, 1);
1578     ga_append(&ga, NUL);
1579 
1580     rettv->vval.v_string = ga.ga_data;
1581 }
1582 
1583 /*
1584  * "trim({expr})" function
1585  */
1586     void
f_trim(typval_T * argvars,typval_T * rettv)1587 f_trim(typval_T *argvars, typval_T *rettv)
1588 {
1589     char_u	buf1[NUMBUFLEN];
1590     char_u	buf2[NUMBUFLEN];
1591     char_u	*head;
1592     char_u	*mask = NULL;
1593     char_u	*tail;
1594     char_u	*prev;
1595     char_u	*p;
1596     int		c1;
1597     int		dir = 0;
1598 
1599     rettv->v_type = VAR_STRING;
1600     rettv->vval.v_string = NULL;
1601 
1602     if (in_vim9script()
1603 	    && (check_for_string_arg(argvars, 0) == FAIL
1604 		|| check_for_opt_string_arg(argvars, 1) == FAIL
1605 		|| (argvars[1].v_type != VAR_UNKNOWN
1606 		    && check_for_opt_number_arg(argvars, 2) == FAIL)))
1607 	return;
1608 
1609     head = tv_get_string_buf_chk(&argvars[0], buf1);
1610     if (head == NULL)
1611 	return;
1612 
1613     if (argvars[1].v_type != VAR_UNKNOWN && argvars[1].v_type != VAR_STRING)
1614     {
1615 	semsg(_(e_invarg2), tv_get_string(&argvars[1]));
1616 	return;
1617     }
1618 
1619     if (argvars[1].v_type == VAR_STRING)
1620     {
1621 	mask = tv_get_string_buf_chk(&argvars[1], buf2);
1622 
1623 	if (argvars[2].v_type != VAR_UNKNOWN)
1624 	{
1625 	    int	error = 0;
1626 
1627 	    // leading or trailing characters to trim
1628 	    dir = (int)tv_get_number_chk(&argvars[2], &error);
1629 	    if (error)
1630 		return;
1631 	    if (dir < 0 || dir > 2)
1632 	    {
1633 		semsg(_(e_invarg2), tv_get_string(&argvars[2]));
1634 		return;
1635 	    }
1636 	}
1637     }
1638 
1639     if (dir == 0 || dir == 1)
1640     {
1641 	// Trim leading characters
1642 	while (*head != NUL)
1643 	{
1644 	    c1 = PTR2CHAR(head);
1645 	    if (mask == NULL)
1646 	    {
1647 		if (c1 > ' ' && c1 != 0xa0)
1648 		    break;
1649 	    }
1650 	    else
1651 	    {
1652 		for (p = mask; *p != NUL; MB_PTR_ADV(p))
1653 		    if (c1 == PTR2CHAR(p))
1654 			break;
1655 		if (*p == NUL)
1656 		    break;
1657 	    }
1658 	    MB_PTR_ADV(head);
1659 	}
1660     }
1661 
1662     tail = head + STRLEN(head);
1663     if (dir == 0 || dir == 2)
1664     {
1665 	// Trim trailing characters
1666 	for (; tail > head; tail = prev)
1667 	{
1668 	    prev = tail;
1669 	    MB_PTR_BACK(head, prev);
1670 	    c1 = PTR2CHAR(prev);
1671 	    if (mask == NULL)
1672 	    {
1673 		if (c1 > ' ' && c1 != 0xa0)
1674 		    break;
1675 	    }
1676 	    else
1677 	    {
1678 		for (p = mask; *p != NUL; MB_PTR_ADV(p))
1679 		    if (c1 == PTR2CHAR(p))
1680 			break;
1681 		if (*p == NUL)
1682 		    break;
1683 	    }
1684 	}
1685     }
1686     rettv->vval.v_string = vim_strnsave(head, tail - head);
1687 }
1688 
1689 #endif
1690 
1691 #if defined(FEAT_EVAL)
1692 static char *e_printf = N_("E766: Insufficient arguments for printf()");
1693 
1694 /*
1695  * Get number argument from "idxp" entry in "tvs".  First entry is 1.
1696  */
1697     static varnumber_T
tv_nr(typval_T * tvs,int * idxp)1698 tv_nr(typval_T *tvs, int *idxp)
1699 {
1700     int		idx = *idxp - 1;
1701     varnumber_T	n = 0;
1702     int		err = FALSE;
1703 
1704     if (tvs[idx].v_type == VAR_UNKNOWN)
1705 	emsg(_(e_printf));
1706     else
1707     {
1708 	++*idxp;
1709 	n = tv_get_number_chk(&tvs[idx], &err);
1710 	if (err)
1711 	    n = 0;
1712     }
1713     return n;
1714 }
1715 
1716 /*
1717  * Get string argument from "idxp" entry in "tvs".  First entry is 1.
1718  * If "tofree" is NULL tv_get_string_chk() is used.  Some types (e.g. List)
1719  * are not converted to a string.
1720  * If "tofree" is not NULL echo_string() is used.  All types are converted to
1721  * a string with the same format as ":echo".  The caller must free "*tofree".
1722  * Returns NULL for an error.
1723  */
1724     static char *
tv_str(typval_T * tvs,int * idxp,char_u ** tofree)1725 tv_str(typval_T *tvs, int *idxp, char_u **tofree)
1726 {
1727     int		    idx = *idxp - 1;
1728     char	    *s = NULL;
1729     static char_u   numbuf[NUMBUFLEN];
1730 
1731     if (tvs[idx].v_type == VAR_UNKNOWN)
1732 	emsg(_(e_printf));
1733     else
1734     {
1735 	++*idxp;
1736 	if (tofree != NULL)
1737 	    s = (char *)echo_string(&tvs[idx], tofree, numbuf, get_copyID());
1738 	else
1739 	    s = (char *)tv_get_string_chk(&tvs[idx]);
1740     }
1741     return s;
1742 }
1743 
1744 # ifdef FEAT_FLOAT
1745 /*
1746  * Get float argument from "idxp" entry in "tvs".  First entry is 1.
1747  */
1748     static double
tv_float(typval_T * tvs,int * idxp)1749 tv_float(typval_T *tvs, int *idxp)
1750 {
1751     int		idx = *idxp - 1;
1752     double	f = 0;
1753 
1754     if (tvs[idx].v_type == VAR_UNKNOWN)
1755 	emsg(_(e_printf));
1756     else
1757     {
1758 	++*idxp;
1759 	if (tvs[idx].v_type == VAR_FLOAT)
1760 	    f = tvs[idx].vval.v_float;
1761 	else if (tvs[idx].v_type == VAR_NUMBER)
1762 	    f = (double)tvs[idx].vval.v_number;
1763 	else
1764 	    emsg(_("E807: Expected Float argument for printf()"));
1765     }
1766     return f;
1767 }
1768 # endif
1769 #endif
1770 
1771 #ifdef FEAT_FLOAT
1772 /*
1773  * Return the representation of infinity for printf() function:
1774  * "-inf", "inf", "+inf", " inf", "-INF", "INF", "+INF" or " INF".
1775  */
1776     static const char *
infinity_str(int positive,char fmt_spec,int force_sign,int space_for_positive)1777 infinity_str(int positive,
1778 	     char fmt_spec,
1779 	     int force_sign,
1780 	     int space_for_positive)
1781 {
1782     static const char *table[] =
1783     {
1784 	"-inf", "inf", "+inf", " inf",
1785 	"-INF", "INF", "+INF", " INF"
1786     };
1787     int idx = positive * (1 + force_sign + force_sign * space_for_positive);
1788 
1789     if (ASCII_ISUPPER(fmt_spec))
1790 	idx += 4;
1791     return table[idx];
1792 }
1793 #endif
1794 
1795 /*
1796  * This code was included to provide a portable vsnprintf() and snprintf().
1797  * Some systems may provide their own, but we always use this one for
1798  * consistency.
1799  *
1800  * This code is based on snprintf.c - a portable implementation of snprintf
1801  * by Mark Martinec <mark.martinec@ijs.si>, Version 2.2, 2000-10-06.
1802  * Included with permission.  It was heavily modified to fit in Vim.
1803  * The original code, including useful comments, can be found here:
1804  *	http://www.ijs.si/software/snprintf/
1805  *
1806  * This snprintf() only supports the following conversion specifiers:
1807  * s, c, d, u, o, x, X, p  (and synonyms: i, D, U, O - see below)
1808  * with flags: '-', '+', ' ', '0' and '#'.
1809  * An asterisk is supported for field width as well as precision.
1810  *
1811  * Limited support for floating point was added: 'f', 'F', 'e', 'E', 'g', 'G'.
1812  *
1813  * Length modifiers 'h' (short int) and 'l' (long int) and 'll' (long long int)
1814  * are supported.  NOTE: for 'll' the argument is varnumber_T or uvarnumber_T.
1815  *
1816  * The locale is not used, the string is used as a byte string.  This is only
1817  * relevant for double-byte encodings where the second byte may be '%'.
1818  *
1819  * It is permitted for "str_m" to be zero, and it is permitted to specify NULL
1820  * pointer for resulting string argument if "str_m" is zero (as per ISO C99).
1821  *
1822  * The return value is the number of characters which would be generated
1823  * for the given input, excluding the trailing NUL. If this value
1824  * is greater or equal to "str_m", not all characters from the result
1825  * have been stored in str, output bytes beyond the ("str_m"-1) -th character
1826  * are discarded. If "str_m" is greater than zero it is guaranteed
1827  * the resulting string will be NUL-terminated.
1828  */
1829 
1830 /*
1831  * When va_list is not supported we only define vim_snprintf().
1832  *
1833  * vim_vsnprintf_typval() can be invoked with either "va_list" or a list of
1834  * "typval_T".  When the latter is not used it must be NULL.
1835  */
1836 
1837 // When generating prototypes all of this is skipped, cproto doesn't
1838 // understand this.
1839 #ifndef PROTO
1840 
1841 // Like vim_vsnprintf() but append to the string.
1842     int
vim_snprintf_add(char * str,size_t str_m,const char * fmt,...)1843 vim_snprintf_add(char *str, size_t str_m, const char *fmt, ...)
1844 {
1845     va_list	ap;
1846     int		str_l;
1847     size_t	len = STRLEN(str);
1848     size_t	space;
1849 
1850     if (str_m <= len)
1851 	space = 0;
1852     else
1853 	space = str_m - len;
1854     va_start(ap, fmt);
1855     str_l = vim_vsnprintf(str + len, space, fmt, ap);
1856     va_end(ap);
1857     return str_l;
1858 }
1859 
1860     int
vim_snprintf(char * str,size_t str_m,const char * fmt,...)1861 vim_snprintf(char *str, size_t str_m, const char *fmt, ...)
1862 {
1863     va_list	ap;
1864     int		str_l;
1865 
1866     va_start(ap, fmt);
1867     str_l = vim_vsnprintf(str, str_m, fmt, ap);
1868     va_end(ap);
1869     return str_l;
1870 }
1871 
1872     int
vim_vsnprintf(char * str,size_t str_m,const char * fmt,va_list ap)1873 vim_vsnprintf(
1874     char	*str,
1875     size_t	str_m,
1876     const char	*fmt,
1877     va_list	ap)
1878 {
1879     return vim_vsnprintf_typval(str, str_m, fmt, ap, NULL);
1880 }
1881 
1882     int
vim_vsnprintf_typval(char * str,size_t str_m,const char * fmt,va_list ap,typval_T * tvs)1883 vim_vsnprintf_typval(
1884     char	*str,
1885     size_t	str_m,
1886     const char	*fmt,
1887     va_list	ap,
1888     typval_T	*tvs)
1889 {
1890     size_t	str_l = 0;
1891     const char	*p = fmt;
1892     int		arg_idx = 1;
1893 
1894     if (p == NULL)
1895 	p = "";
1896     while (*p != NUL)
1897     {
1898 	if (*p != '%')
1899 	{
1900 	    char    *q = strchr(p + 1, '%');
1901 	    size_t  n = (q == NULL) ? STRLEN(p) : (size_t)(q - p);
1902 
1903 	    // Copy up to the next '%' or NUL without any changes.
1904 	    if (str_l < str_m)
1905 	    {
1906 		size_t avail = str_m - str_l;
1907 
1908 		mch_memmove(str + str_l, p, n > avail ? avail : n);
1909 	    }
1910 	    p += n;
1911 	    str_l += n;
1912 	}
1913 	else
1914 	{
1915 	    size_t  min_field_width = 0, precision = 0;
1916 	    int	    zero_padding = 0, precision_specified = 0, justify_left = 0;
1917 	    int	    alternate_form = 0, force_sign = 0;
1918 
1919 	    // If both the ' ' and '+' flags appear, the ' ' flag should be
1920 	    // ignored.
1921 	    int	    space_for_positive = 1;
1922 
1923 	    // allowed values: \0, h, l, L
1924 	    char    length_modifier = '\0';
1925 
1926 	    // temporary buffer for simple numeric->string conversion
1927 # if defined(FEAT_FLOAT)
1928 #  define TMP_LEN 350	// On my system 1e308 is the biggest number possible.
1929 			// That sounds reasonable to use as the maximum
1930 			// printable.
1931 # else
1932 #  define TMP_LEN 66
1933 # endif
1934 	    char    tmp[TMP_LEN];
1935 
1936 	    // string address in case of string argument
1937 	    const char  *str_arg = NULL;
1938 
1939 	    // natural field width of arg without padding and sign
1940 	    size_t  str_arg_l;
1941 
1942 	    // unsigned char argument value - only defined for c conversion.
1943 	    // N.B. standard explicitly states the char argument for the c
1944 	    // conversion is unsigned
1945 	    unsigned char uchar_arg;
1946 
1947 	    // number of zeros to be inserted for numeric conversions as
1948 	    // required by the precision or minimal field width
1949 	    size_t  number_of_zeros_to_pad = 0;
1950 
1951 	    // index into tmp where zero padding is to be inserted
1952 	    size_t  zero_padding_insertion_ind = 0;
1953 
1954 	    // current conversion specifier character
1955 	    char    fmt_spec = '\0';
1956 
1957 	    // buffer for 's' and 'S' specs
1958 	    char_u  *tofree = NULL;
1959 
1960 
1961 	    p++;  // skip '%'
1962 
1963 	    // parse flags
1964 	    while (*p == '0' || *p == '-' || *p == '+' || *p == ' '
1965 						   || *p == '#' || *p == '\'')
1966 	    {
1967 		switch (*p)
1968 		{
1969 		    case '0': zero_padding = 1; break;
1970 		    case '-': justify_left = 1; break;
1971 		    case '+': force_sign = 1; space_for_positive = 0; break;
1972 		    case ' ': force_sign = 1;
1973 			      // If both the ' ' and '+' flags appear, the ' '
1974 			      // flag should be ignored
1975 			      break;
1976 		    case '#': alternate_form = 1; break;
1977 		    case '\'': break;
1978 		}
1979 		p++;
1980 	    }
1981 	    // If the '0' and '-' flags both appear, the '0' flag should be
1982 	    // ignored.
1983 
1984 	    // parse field width
1985 	    if (*p == '*')
1986 	    {
1987 		int j;
1988 
1989 		p++;
1990 		j =
1991 # if defined(FEAT_EVAL)
1992 		    tvs != NULL ? tv_nr(tvs, &arg_idx) :
1993 # endif
1994 			va_arg(ap, int);
1995 		if (j >= 0)
1996 		    min_field_width = j;
1997 		else
1998 		{
1999 		    min_field_width = -j;
2000 		    justify_left = 1;
2001 		}
2002 	    }
2003 	    else if (VIM_ISDIGIT((int)(*p)))
2004 	    {
2005 		// size_t could be wider than unsigned int; make sure we treat
2006 		// argument like common implementations do
2007 		unsigned int uj = *p++ - '0';
2008 
2009 		while (VIM_ISDIGIT((int)(*p)))
2010 		    uj = 10 * uj + (unsigned int)(*p++ - '0');
2011 		min_field_width = uj;
2012 	    }
2013 
2014 	    // parse precision
2015 	    if (*p == '.')
2016 	    {
2017 		p++;
2018 		precision_specified = 1;
2019 		if (*p == '*')
2020 		{
2021 		    int j;
2022 
2023 		    j =
2024 # if defined(FEAT_EVAL)
2025 			tvs != NULL ? tv_nr(tvs, &arg_idx) :
2026 # endif
2027 			    va_arg(ap, int);
2028 		    p++;
2029 		    if (j >= 0)
2030 			precision = j;
2031 		    else
2032 		    {
2033 			precision_specified = 0;
2034 			precision = 0;
2035 		    }
2036 		}
2037 		else if (VIM_ISDIGIT((int)(*p)))
2038 		{
2039 		    // size_t could be wider than unsigned int; make sure we
2040 		    // treat argument like common implementations do
2041 		    unsigned int uj = *p++ - '0';
2042 
2043 		    while (VIM_ISDIGIT((int)(*p)))
2044 			uj = 10 * uj + (unsigned int)(*p++ - '0');
2045 		    precision = uj;
2046 		}
2047 	    }
2048 
2049 	    // parse 'h', 'l' and 'll' length modifiers
2050 	    if (*p == 'h' || *p == 'l')
2051 	    {
2052 		length_modifier = *p;
2053 		p++;
2054 		if (length_modifier == 'l' && *p == 'l')
2055 		{
2056 		    // double l = __int64 / varnumber_T
2057 		    length_modifier = 'L';
2058 		    p++;
2059 		}
2060 	    }
2061 	    fmt_spec = *p;
2062 
2063 	    // common synonyms:
2064 	    switch (fmt_spec)
2065 	    {
2066 		case 'i': fmt_spec = 'd'; break;
2067 		case 'D': fmt_spec = 'd'; length_modifier = 'l'; break;
2068 		case 'U': fmt_spec = 'u'; length_modifier = 'l'; break;
2069 		case 'O': fmt_spec = 'o'; length_modifier = 'l'; break;
2070 		default: break;
2071 	    }
2072 
2073 # if defined(FEAT_EVAL)
2074 	    switch (fmt_spec)
2075 	    {
2076 		case 'd': case 'u': case 'o': case 'x': case 'X':
2077 		    if (tvs != NULL && length_modifier == '\0')
2078 			length_modifier = 'L';
2079 	    }
2080 # endif
2081 
2082 	    // get parameter value, do initial processing
2083 	    switch (fmt_spec)
2084 	    {
2085 		// '%' and 'c' behave similar to 's' regarding flags and field
2086 		// widths
2087 	    case '%':
2088 	    case 'c':
2089 	    case 's':
2090 	    case 'S':
2091 		str_arg_l = 1;
2092 		switch (fmt_spec)
2093 		{
2094 		case '%':
2095 		    str_arg = p;
2096 		    break;
2097 
2098 		case 'c':
2099 		    {
2100 			int j;
2101 
2102 			j =
2103 # if defined(FEAT_EVAL)
2104 			    tvs != NULL ? tv_nr(tvs, &arg_idx) :
2105 # endif
2106 				va_arg(ap, int);
2107 			// standard demands unsigned char
2108 			uchar_arg = (unsigned char)j;
2109 			str_arg = (char *)&uchar_arg;
2110 			break;
2111 		    }
2112 
2113 		case 's':
2114 		case 'S':
2115 		    str_arg =
2116 # if defined(FEAT_EVAL)
2117 				tvs != NULL ? tv_str(tvs, &arg_idx, &tofree) :
2118 # endif
2119 				    va_arg(ap, char *);
2120 		    if (str_arg == NULL)
2121 		    {
2122 			str_arg = "[NULL]";
2123 			str_arg_l = 6;
2124 		    }
2125 		    // make sure not to address string beyond the specified
2126 		    // precision !!!
2127 		    else if (!precision_specified)
2128 			str_arg_l = strlen(str_arg);
2129 		    // truncate string if necessary as requested by precision
2130 		    else if (precision == 0)
2131 			str_arg_l = 0;
2132 		    else
2133 		    {
2134 			// Don't put the #if inside memchr(), it can be a
2135 			// macro.
2136 			// memchr on HP does not like n > 2^31  !!!
2137 			char *q = memchr(str_arg, '\0',
2138 				  precision <= (size_t)0x7fffffffL ? precision
2139 						       : (size_t)0x7fffffffL);
2140 
2141 			str_arg_l = (q == NULL) ? precision
2142 						      : (size_t)(q - str_arg);
2143 		    }
2144 		    if (fmt_spec == 'S')
2145 		    {
2146 			char_u	*p1;
2147 			size_t	i;
2148 			int	cell;
2149 
2150 			for (i = 0, p1 = (char_u *)str_arg; *p1;
2151 							  p1 += mb_ptr2len(p1))
2152 			{
2153 			    cell = mb_ptr2cells(p1);
2154 			    if (precision_specified && i + cell > precision)
2155 				break;
2156 			    i += cell;
2157 			}
2158 
2159 			str_arg_l = p1 - (char_u *)str_arg;
2160 			if (min_field_width != 0)
2161 			    min_field_width += str_arg_l - i;
2162 		    }
2163 		    break;
2164 
2165 		default:
2166 		    break;
2167 		}
2168 		break;
2169 
2170 	    case 'd': case 'u':
2171 	    case 'b': case 'B':
2172 	    case 'o':
2173 	    case 'x': case 'X':
2174 	    case 'p':
2175 		{
2176 		    // NOTE: the u, b, o, x, X and p conversion specifiers
2177 		    // imply the value is unsigned;  d implies a signed
2178 		    // value
2179 
2180 		    // 0 if numeric argument is zero (or if pointer is
2181 		    // NULL for 'p'), +1 if greater than zero (or nonzero
2182 		    // for unsigned arguments), -1 if negative (unsigned
2183 		    // argument is never negative)
2184 		    int arg_sign = 0;
2185 
2186 		    // only set for length modifier h, or for no length
2187 		    // modifiers
2188 		    int int_arg = 0;
2189 		    unsigned int uint_arg = 0;
2190 
2191 		    // only set for length modifier l
2192 		    long int long_arg = 0;
2193 		    unsigned long int ulong_arg = 0;
2194 
2195 		    // only set for length modifier ll
2196 		    varnumber_T llong_arg = 0;
2197 		    uvarnumber_T ullong_arg = 0;
2198 
2199 		    // only set for b conversion
2200 		    uvarnumber_T bin_arg = 0;
2201 
2202 		    // pointer argument value -only defined for p
2203 		    // conversion
2204 		    void *ptr_arg = NULL;
2205 
2206 		    if (fmt_spec == 'p')
2207 		    {
2208 			length_modifier = '\0';
2209 			ptr_arg =
2210 # if defined(FEAT_EVAL)
2211 				 tvs != NULL ? (void *)tv_str(tvs, &arg_idx,
2212 									NULL) :
2213 # endif
2214 					va_arg(ap, void *);
2215 			if (ptr_arg != NULL)
2216 			    arg_sign = 1;
2217 		    }
2218 		    else if (fmt_spec == 'b' || fmt_spec == 'B')
2219 		    {
2220 			bin_arg =
2221 # if defined(FEAT_EVAL)
2222 				    tvs != NULL ?
2223 					   (uvarnumber_T)tv_nr(tvs, &arg_idx) :
2224 # endif
2225 					va_arg(ap, uvarnumber_T);
2226 			if (bin_arg != 0)
2227 			    arg_sign = 1;
2228 		    }
2229 		    else if (fmt_spec == 'd')
2230 		    {
2231 			// signed
2232 			switch (length_modifier)
2233 			{
2234 			case '\0':
2235 			case 'h':
2236 			    // char and short arguments are passed as int.
2237 			    int_arg =
2238 # if defined(FEAT_EVAL)
2239 					tvs != NULL ? tv_nr(tvs, &arg_idx) :
2240 # endif
2241 					    va_arg(ap, int);
2242 			    if (int_arg > 0)
2243 				arg_sign =  1;
2244 			    else if (int_arg < 0)
2245 				arg_sign = -1;
2246 			    break;
2247 			case 'l':
2248 			    long_arg =
2249 # if defined(FEAT_EVAL)
2250 					tvs != NULL ? tv_nr(tvs, &arg_idx) :
2251 # endif
2252 					    va_arg(ap, long int);
2253 			    if (long_arg > 0)
2254 				arg_sign =  1;
2255 			    else if (long_arg < 0)
2256 				arg_sign = -1;
2257 			    break;
2258 			case 'L':
2259 			    llong_arg =
2260 # if defined(FEAT_EVAL)
2261 					tvs != NULL ? tv_nr(tvs, &arg_idx) :
2262 # endif
2263 					    va_arg(ap, varnumber_T);
2264 			    if (llong_arg > 0)
2265 				arg_sign =  1;
2266 			    else if (llong_arg < 0)
2267 				arg_sign = -1;
2268 			    break;
2269 			}
2270 		    }
2271 		    else
2272 		    {
2273 			// unsigned
2274 			switch (length_modifier)
2275 			{
2276 			    case '\0':
2277 			    case 'h':
2278 				uint_arg =
2279 # if defined(FEAT_EVAL)
2280 					    tvs != NULL ? (unsigned)
2281 							tv_nr(tvs, &arg_idx) :
2282 # endif
2283 						va_arg(ap, unsigned int);
2284 				if (uint_arg != 0)
2285 				    arg_sign = 1;
2286 				break;
2287 			    case 'l':
2288 				ulong_arg =
2289 # if defined(FEAT_EVAL)
2290 					    tvs != NULL ? (unsigned long)
2291 							tv_nr(tvs, &arg_idx) :
2292 # endif
2293 						va_arg(ap, unsigned long int);
2294 				if (ulong_arg != 0)
2295 				    arg_sign = 1;
2296 				break;
2297 			    case 'L':
2298 				ullong_arg =
2299 # if defined(FEAT_EVAL)
2300 					    tvs != NULL ? (uvarnumber_T)
2301 							tv_nr(tvs, &arg_idx) :
2302 # endif
2303 						va_arg(ap, uvarnumber_T);
2304 				if (ullong_arg != 0)
2305 				    arg_sign = 1;
2306 				break;
2307 			}
2308 		    }
2309 
2310 		    str_arg = tmp;
2311 		    str_arg_l = 0;
2312 
2313 		    // NOTE:
2314 		    //   For d, i, u, o, x, and X conversions, if precision is
2315 		    //   specified, the '0' flag should be ignored. This is so
2316 		    //   with Solaris 2.6, Digital UNIX 4.0, HPUX 10, Linux,
2317 		    //   FreeBSD, NetBSD; but not with Perl.
2318 		    if (precision_specified)
2319 			zero_padding = 0;
2320 		    if (fmt_spec == 'd')
2321 		    {
2322 			if (force_sign && arg_sign >= 0)
2323 			    tmp[str_arg_l++] = space_for_positive ? ' ' : '+';
2324 			// leave negative numbers for sprintf to handle, to
2325 			// avoid handling tricky cases like (short int)-32768
2326 		    }
2327 		    else if (alternate_form)
2328 		    {
2329 			if (arg_sign != 0
2330 				     && (fmt_spec == 'b' || fmt_spec == 'B'
2331 				      || fmt_spec == 'x' || fmt_spec == 'X') )
2332 			{
2333 			    tmp[str_arg_l++] = '0';
2334 			    tmp[str_arg_l++] = fmt_spec;
2335 			}
2336 			// alternate form should have no effect for p
2337 			// conversion, but ...
2338 		    }
2339 
2340 		    zero_padding_insertion_ind = str_arg_l;
2341 		    if (!precision_specified)
2342 			precision = 1;   // default precision is 1
2343 		    if (precision == 0 && arg_sign == 0)
2344 		    {
2345 			// When zero value is formatted with an explicit
2346 			// precision 0, the resulting formatted string is
2347 			// empty (d, i, u, b, B, o, x, X, p).
2348 		    }
2349 		    else
2350 		    {
2351 			char	f[6];
2352 			int	f_l = 0;
2353 
2354 			// construct a simple format string for sprintf
2355 			f[f_l++] = '%';
2356 			if (!length_modifier)
2357 			    ;
2358 			else if (length_modifier == 'L')
2359 			{
2360 # ifdef MSWIN
2361 			    f[f_l++] = 'I';
2362 			    f[f_l++] = '6';
2363 			    f[f_l++] = '4';
2364 # else
2365 			    f[f_l++] = 'l';
2366 			    f[f_l++] = 'l';
2367 # endif
2368 			}
2369 			else
2370 			    f[f_l++] = length_modifier;
2371 			f[f_l++] = fmt_spec;
2372 			f[f_l++] = '\0';
2373 
2374 			if (fmt_spec == 'p')
2375 			    str_arg_l += sprintf(tmp + str_arg_l, f, ptr_arg);
2376 			else if (fmt_spec == 'b' || fmt_spec == 'B')
2377 			{
2378 			    char	    b[8 * sizeof(uvarnumber_T)];
2379 			    size_t	    b_l = 0;
2380 			    uvarnumber_T    bn = bin_arg;
2381 
2382 			    do
2383 			    {
2384 				b[sizeof(b) - ++b_l] = '0' + (bn & 0x1);
2385 				bn >>= 1;
2386 			    }
2387 			    while (bn != 0);
2388 
2389 			    memcpy(tmp + str_arg_l, b + sizeof(b) - b_l, b_l);
2390 			    str_arg_l += b_l;
2391 			}
2392 			else if (fmt_spec == 'd')
2393 			{
2394 			    // signed
2395 			    switch (length_modifier)
2396 			    {
2397 			    case '\0': str_arg_l += sprintf(
2398 						 tmp + str_arg_l, f,
2399 						 int_arg);
2400 				       break;
2401 			    case 'h': str_arg_l += sprintf(
2402 						 tmp + str_arg_l, f,
2403 						 (short)int_arg);
2404 				      break;
2405 			    case 'l': str_arg_l += sprintf(
2406 						tmp + str_arg_l, f, long_arg);
2407 				      break;
2408 			    case 'L': str_arg_l += sprintf(
2409 					       tmp + str_arg_l, f, llong_arg);
2410 				      break;
2411 			    }
2412 			}
2413 			else
2414 			{
2415 			    // unsigned
2416 			    switch (length_modifier)
2417 			    {
2418 			    case '\0': str_arg_l += sprintf(
2419 						tmp + str_arg_l, f,
2420 						uint_arg);
2421 				       break;
2422 			    case 'h': str_arg_l += sprintf(
2423 						tmp + str_arg_l, f,
2424 						(unsigned short)uint_arg);
2425 				      break;
2426 			    case 'l': str_arg_l += sprintf(
2427 					       tmp + str_arg_l, f, ulong_arg);
2428 				      break;
2429 			    case 'L': str_arg_l += sprintf(
2430 					      tmp + str_arg_l, f, ullong_arg);
2431 				      break;
2432 			    }
2433 			}
2434 
2435 			// include the optional minus sign and possible
2436 			// "0x" in the region before the zero padding
2437 			// insertion point
2438 			if (zero_padding_insertion_ind < str_arg_l
2439 				&& tmp[zero_padding_insertion_ind] == '-')
2440 			    zero_padding_insertion_ind++;
2441 			if (zero_padding_insertion_ind + 1 < str_arg_l
2442 				&& tmp[zero_padding_insertion_ind]   == '0'
2443 				&& (tmp[zero_padding_insertion_ind + 1] == 'x'
2444 				 || tmp[zero_padding_insertion_ind + 1] == 'X'))
2445 			    zero_padding_insertion_ind += 2;
2446 		    }
2447 
2448 		    {
2449 			size_t num_of_digits = str_arg_l
2450 						 - zero_padding_insertion_ind;
2451 
2452 			if (alternate_form && fmt_spec == 'o'
2453 				// unless zero is already the first
2454 				// character
2455 				&& !(zero_padding_insertion_ind < str_arg_l
2456 				    && tmp[zero_padding_insertion_ind] == '0'))
2457 			{
2458 			    // assure leading zero for alternate-form
2459 			    // octal numbers
2460 			    if (!precision_specified
2461 					     || precision < num_of_digits + 1)
2462 			    {
2463 				// precision is increased to force the
2464 				// first character to be zero, except if a
2465 				// zero value is formatted with an
2466 				// explicit precision of zero
2467 				precision = num_of_digits + 1;
2468 			    }
2469 			}
2470 			// zero padding to specified precision?
2471 			if (num_of_digits < precision)
2472 			    number_of_zeros_to_pad = precision - num_of_digits;
2473 		    }
2474 		    // zero padding to specified minimal field width?
2475 		    if (!justify_left && zero_padding)
2476 		    {
2477 			int n = (int)(min_field_width - (str_arg_l
2478 						    + number_of_zeros_to_pad));
2479 			if (n > 0)
2480 			    number_of_zeros_to_pad += n;
2481 		    }
2482 		    break;
2483 		}
2484 
2485 # ifdef FEAT_FLOAT
2486 	    case 'f':
2487 	    case 'F':
2488 	    case 'e':
2489 	    case 'E':
2490 	    case 'g':
2491 	    case 'G':
2492 		{
2493 		    // Floating point.
2494 		    double	f;
2495 		    double	abs_f;
2496 		    char	format[40];
2497 		    int		l;
2498 		    int		remove_trailing_zeroes = FALSE;
2499 
2500 		    f =
2501 #  if defined(FEAT_EVAL)
2502 			tvs != NULL ? tv_float(tvs, &arg_idx) :
2503 #  endif
2504 			    va_arg(ap, double);
2505 		    abs_f = f < 0 ? -f : f;
2506 
2507 		    if (fmt_spec == 'g' || fmt_spec == 'G')
2508 		    {
2509 			// Would be nice to use %g directly, but it prints
2510 			// "1.0" as "1", we don't want that.
2511 			if ((abs_f >= 0.001 && abs_f < 10000000.0)
2512 							      || abs_f == 0.0)
2513 			    fmt_spec = ASCII_ISUPPER(fmt_spec) ? 'F' : 'f';
2514 			else
2515 			    fmt_spec = fmt_spec == 'g' ? 'e' : 'E';
2516 			remove_trailing_zeroes = TRUE;
2517 		    }
2518 
2519 		    if ((fmt_spec == 'f' || fmt_spec == 'F') &&
2520 #  ifdef VAX
2521 			    abs_f > 1.0e38
2522 #  else
2523 			    abs_f > 1.0e307
2524 #  endif
2525 			    )
2526 		    {
2527 			// Avoid a buffer overflow
2528 			STRCPY(tmp, infinity_str(f > 0.0, fmt_spec,
2529 					      force_sign, space_for_positive));
2530 			str_arg_l = STRLEN(tmp);
2531 			zero_padding = 0;
2532 		    }
2533 		    else
2534 		    {
2535 			if (isnan(f))
2536 			{
2537 			    // Not a number: nan or NAN
2538 			    STRCPY(tmp, ASCII_ISUPPER(fmt_spec) ? "NAN"
2539 								      : "nan");
2540 			    str_arg_l = 3;
2541 			    zero_padding = 0;
2542 			}
2543 			else if (isinf(f))
2544 			{
2545 			    STRCPY(tmp, infinity_str(f > 0.0, fmt_spec,
2546 					      force_sign, space_for_positive));
2547 			    str_arg_l = STRLEN(tmp);
2548 			    zero_padding = 0;
2549 			}
2550 			else
2551 			{
2552 			    // Regular float number
2553 			    format[0] = '%';
2554 			    l = 1;
2555 			    if (force_sign)
2556 				format[l++] = space_for_positive ? ' ' : '+';
2557 			    if (precision_specified)
2558 			    {
2559 				size_t max_prec = TMP_LEN - 10;
2560 
2561 				// Make sure we don't get more digits than we
2562 				// have room for.
2563 				if ((fmt_spec == 'f' || fmt_spec == 'F')
2564 								&& abs_f > 1.0)
2565 				    max_prec -= (size_t)log10(abs_f);
2566 				if (precision > max_prec)
2567 				    precision = max_prec;
2568 				l += sprintf(format + l, ".%d", (int)precision);
2569 			    }
2570 			    format[l] = fmt_spec == 'F' ? 'f' : fmt_spec;
2571 			    format[l + 1] = NUL;
2572 
2573 			    str_arg_l = sprintf(tmp, format, f);
2574 			}
2575 
2576 			if (remove_trailing_zeroes)
2577 			{
2578 			    int i;
2579 			    char *tp;
2580 
2581 			    // Using %g or %G: remove superfluous zeroes.
2582 			    if (fmt_spec == 'f' || fmt_spec == 'F')
2583 				tp = tmp + str_arg_l - 1;
2584 			    else
2585 			    {
2586 				tp = (char *)vim_strchr((char_u *)tmp,
2587 						 fmt_spec == 'e' ? 'e' : 'E');
2588 				if (tp != NULL)
2589 				{
2590 				    // Remove superfluous '+' and leading
2591 				    // zeroes from the exponent.
2592 				    if (tp[1] == '+')
2593 				    {
2594 					// Change "1.0e+07" to "1.0e07"
2595 					STRMOVE(tp + 1, tp + 2);
2596 					--str_arg_l;
2597 				    }
2598 				    i = (tp[1] == '-') ? 2 : 1;
2599 				    while (tp[i] == '0')
2600 				    {
2601 					// Change "1.0e07" to "1.0e7"
2602 					STRMOVE(tp + i, tp + i + 1);
2603 					--str_arg_l;
2604 				    }
2605 				    --tp;
2606 				}
2607 			    }
2608 
2609 			    if (tp != NULL && !precision_specified)
2610 				// Remove trailing zeroes, but keep the one
2611 				// just after a dot.
2612 				while (tp > tmp + 2 && *tp == '0'
2613 							     && tp[-1] != '.')
2614 				{
2615 				    STRMOVE(tp, tp + 1);
2616 				    --tp;
2617 				    --str_arg_l;
2618 				}
2619 			}
2620 			else
2621 			{
2622 			    char *tp;
2623 
2624 			    // Be consistent: some printf("%e") use 1.0e+12
2625 			    // and some 1.0e+012.  Remove one zero in the last
2626 			    // case.
2627 			    tp = (char *)vim_strchr((char_u *)tmp,
2628 						 fmt_spec == 'e' ? 'e' : 'E');
2629 			    if (tp != NULL && (tp[1] == '+' || tp[1] == '-')
2630 					  && tp[2] == '0'
2631 					  && vim_isdigit(tp[3])
2632 					  && vim_isdigit(tp[4]))
2633 			    {
2634 				STRMOVE(tp + 2, tp + 3);
2635 				--str_arg_l;
2636 			    }
2637 			}
2638 		    }
2639 		    if (zero_padding && min_field_width > str_arg_l
2640 					      && (tmp[0] == '-' || force_sign))
2641 		    {
2642 			// padding 0's should be inserted after the sign
2643 			number_of_zeros_to_pad = min_field_width - str_arg_l;
2644 			zero_padding_insertion_ind = 1;
2645 		    }
2646 		    str_arg = tmp;
2647 		    break;
2648 		}
2649 # endif
2650 
2651 	    default:
2652 		// unrecognized conversion specifier, keep format string
2653 		// as-is
2654 		zero_padding = 0;  // turn zero padding off for non-numeric
2655 				   // conversion
2656 		justify_left = 1;
2657 		min_field_width = 0;		    // reset flags
2658 
2659 		// discard the unrecognized conversion, just keep *
2660 		// the unrecognized conversion character
2661 		str_arg = p;
2662 		str_arg_l = 0;
2663 		if (*p != NUL)
2664 		    str_arg_l++;  // include invalid conversion specifier
2665 				  // unchanged if not at end-of-string
2666 		break;
2667 	    }
2668 
2669 	    if (*p != NUL)
2670 		p++;     // step over the just processed conversion specifier
2671 
2672 	    // insert padding to the left as requested by min_field_width;
2673 	    // this does not include the zero padding in case of numerical
2674 	    // conversions
2675 	    if (!justify_left)
2676 	    {
2677 		// left padding with blank or zero
2678 		int pn = (int)(min_field_width - (str_arg_l + number_of_zeros_to_pad));
2679 
2680 		if (pn > 0)
2681 		{
2682 		    if (str_l < str_m)
2683 		    {
2684 			size_t avail = str_m - str_l;
2685 
2686 			vim_memset(str + str_l, zero_padding ? '0' : ' ',
2687 					     (size_t)pn > avail ? avail
2688 								: (size_t)pn);
2689 		    }
2690 		    str_l += pn;
2691 		}
2692 	    }
2693 
2694 	    // zero padding as requested by the precision or by the minimal
2695 	    // field width for numeric conversions required?
2696 	    if (number_of_zeros_to_pad == 0)
2697 	    {
2698 		// will not copy first part of numeric right now, *
2699 		// force it to be copied later in its entirety
2700 		zero_padding_insertion_ind = 0;
2701 	    }
2702 	    else
2703 	    {
2704 		// insert first part of numerics (sign or '0x') before zero
2705 		// padding
2706 		int zn = (int)zero_padding_insertion_ind;
2707 
2708 		if (zn > 0)
2709 		{
2710 		    if (str_l < str_m)
2711 		    {
2712 			size_t avail = str_m - str_l;
2713 
2714 			mch_memmove(str + str_l, str_arg,
2715 					     (size_t)zn > avail ? avail
2716 								: (size_t)zn);
2717 		    }
2718 		    str_l += zn;
2719 		}
2720 
2721 		// insert zero padding as requested by the precision or min
2722 		// field width
2723 		zn = (int)number_of_zeros_to_pad;
2724 		if (zn > 0)
2725 		{
2726 		    if (str_l < str_m)
2727 		    {
2728 			size_t avail = str_m - str_l;
2729 
2730 			vim_memset(str + str_l, '0',
2731 					     (size_t)zn > avail ? avail
2732 								: (size_t)zn);
2733 		    }
2734 		    str_l += zn;
2735 		}
2736 	    }
2737 
2738 	    // insert formatted string
2739 	    // (or as-is conversion specifier for unknown conversions)
2740 	    {
2741 		int sn = (int)(str_arg_l - zero_padding_insertion_ind);
2742 
2743 		if (sn > 0)
2744 		{
2745 		    if (str_l < str_m)
2746 		    {
2747 			size_t avail = str_m - str_l;
2748 
2749 			mch_memmove(str + str_l,
2750 				str_arg + zero_padding_insertion_ind,
2751 				(size_t)sn > avail ? avail : (size_t)sn);
2752 		    }
2753 		    str_l += sn;
2754 		}
2755 	    }
2756 
2757 	    // insert right padding
2758 	    if (justify_left)
2759 	    {
2760 		// right blank padding to the field width
2761 		int pn = (int)(min_field_width
2762 				      - (str_arg_l + number_of_zeros_to_pad));
2763 
2764 		if (pn > 0)
2765 		{
2766 		    if (str_l < str_m)
2767 		    {
2768 			size_t avail = str_m - str_l;
2769 
2770 			vim_memset(str + str_l, ' ',
2771 					     (size_t)pn > avail ? avail
2772 								: (size_t)pn);
2773 		    }
2774 		    str_l += pn;
2775 		}
2776 	    }
2777 	    vim_free(tofree);
2778 	}
2779     }
2780 
2781     if (str_m > 0)
2782     {
2783 	// make sure the string is nul-terminated even at the expense of
2784 	// overwriting the last character (shouldn't happen, but just in case)
2785 	//
2786 	str[str_l <= str_m - 1 ? str_l : str_m - 1] = '\0';
2787     }
2788 
2789     if (tvs != NULL && tvs[arg_idx - 1].v_type != VAR_UNKNOWN)
2790 	emsg(_("E767: Too many arguments to printf()"));
2791 
2792     // Return the number of characters formatted (excluding trailing nul
2793     // character), that is, the number of characters that would have been
2794     // written to the buffer if it were large enough.
2795     return (int)str_l;
2796 }
2797 
2798 #endif // PROTO
2799