blob: 54ac17873f2258d0ee14128c98e51aac3193f14b [file] [log] [blame]
Yegappan Lakshmanana2438132021-07-10 21:29:18 +02001/* 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
Yegappan Lakshmanan8ee52af2021-08-09 19:59:06 +020014#define USING_FLOAT_STUFF
Yegappan Lakshmanana2438132021-07-10 21:29:18 +020015#include "vim.h"
16
17/*
18 * Copy "string" into newly allocated memory.
19 */
20 char_u *
21vim_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 *
40vim_strnsave(char_u *string, size_t len)
41{
42 char_u *p;
43
44 p = alloc(len + 1);
Yegappan Lakshmanan6ec66662023-01-23 20:46:21 +000045 if (p == NULL)
46 return NULL;
47
48 STRNCPY(p, string, len);
49 p[len] = NUL;
Yegappan Lakshmanana2438132021-07-10 21:29:18 +020050 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 *
58vim_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 *
69vim_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
Yegappan Lakshmanan8ee52af2021-08-09 19:59:06 +020081 // First count the number of backslashes required.
82 // Then allocate the memory and insert them.
Yegappan Lakshmanana2438132021-07-10 21:29:18 +020083 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);
Yegappan Lakshmanan6ec66662023-01-23 20:46:21 +000097 if (escaped_string == NULL)
98 return NULL;
99 p2 = escaped_string;
100 for (p = string; *p; p++)
Yegappan Lakshmanana2438132021-07-10 21:29:18 +0200101 {
Yegappan Lakshmanan6ec66662023-01-23 20:46:21 +0000102 if (has_mbyte && (l = (*mb_ptr2len)(p)) > 1)
Yegappan Lakshmanana2438132021-07-10 21:29:18 +0200103 {
Yegappan Lakshmanan6ec66662023-01-23 20:46:21 +0000104 mch_memmove(p2, p, (size_t)l);
105 p2 += l;
106 p += l - 1; // skip multibyte char
107 continue;
Yegappan Lakshmanana2438132021-07-10 21:29:18 +0200108 }
Yegappan Lakshmanan6ec66662023-01-23 20:46:21 +0000109 if (vim_strchr(esc_chars, *p) != NULL || (bsl && rem_backslash(p)))
110 *p2++ = cc;
111 *p2++ = *p;
Yegappan Lakshmanana2438132021-07-10 21:29:18 +0200112 }
Yegappan Lakshmanan6ec66662023-01-23 20:46:21 +0000113 *p2 = NUL;
Yegappan Lakshmanana2438132021-07-10 21:29:18 +0200114 return escaped_string;
115}
116
117/*
118 * Return TRUE when 'shell' has "csh" in the tail.
119 */
120 int
121csh_like_shell(void)
122{
123 return (strstr((char *)gettail(p_sh), "csh") != NULL);
124}
125
126/*
Jason Cox6e823512021-08-29 12:36:49 +0200127 * Return TRUE when 'shell' has "fish" in the tail.
128 */
Dominique Pellede05ae72021-08-30 19:57:34 +0200129 static int
Jason Cox6e823512021-08-29 12:36:49 +0200130fish_like_shell(void)
131{
132 return (strstr((char *)gettail(p_sh), "fish") != NULL);
133}
134
135/*
Yegappan Lakshmanana2438132021-07-10 21:29:18 +0200136 * Escape "string" for use as a shell argument with system().
137 * This uses single quotes, except when we know we need to use double quotes
138 * (MS-DOS and MS-Windows not using PowerShell and without 'shellslash' set).
139 * PowerShell also uses a novel escaping for enclosed single quotes - double
140 * them up.
141 * Escape a newline, depending on the 'shell' option.
142 * When "do_special" is TRUE also replace "!", "%", "#" and things starting
143 * with "<" like "<cfile>".
144 * When "do_newline" is FALSE do not escape newline unless it is csh shell.
145 * Returns the result in allocated memory, NULL if we have run out.
146 */
147 char_u *
148vim_strsave_shellescape(char_u *string, int do_special, int do_newline)
149{
150 unsigned length;
151 char_u *p;
152 char_u *d;
153 char_u *escaped_string;
Mike Williams51024bb2024-05-30 07:46:30 +0200154 size_t l;
Yegappan Lakshmanana2438132021-07-10 21:29:18 +0200155 int csh_like;
Jason Cox6e823512021-08-29 12:36:49 +0200156 int fish_like;
Yegappan Lakshmanana2438132021-07-10 21:29:18 +0200157 char_u *shname;
158 int powershell;
159# ifdef MSWIN
160 int double_quotes;
161# endif
162
163 // Only csh and similar shells expand '!' within single quotes. For sh and
164 // the like we must not put a backslash before it, it will be taken
165 // literally. If do_special is set the '!' will be escaped twice.
166 // Csh also needs to have "\n" escaped twice when do_special is set.
167 csh_like = csh_like_shell();
168
Jason Cox6e823512021-08-29 12:36:49 +0200169 // Fish shell uses '\' as an escape character within single quotes, so '\'
170 // itself must be escaped to get a literal '\'.
171 fish_like = fish_like_shell();
172
Dominique Pelleaf4a61a2021-12-27 17:21:41 +0000173 // PowerShell uses its own version for quoting single quotes
Yegappan Lakshmanana2438132021-07-10 21:29:18 +0200174 shname = gettail(p_sh);
175 powershell = strstr((char *)shname, "pwsh") != NULL;
176# ifdef MSWIN
177 powershell = powershell || strstr((char *)shname, "powershell") != NULL;
178 // PowerShell only accepts single quotes so override shellslash.
179 double_quotes = !powershell && !p_ssl;
180# endif
181
182 // First count the number of extra bytes required.
183 length = (unsigned)STRLEN(string) + 3; // two quotes and a trailing NUL
184 for (p = string; *p != NUL; MB_PTR_ADV(p))
185 {
186# ifdef MSWIN
187 if (double_quotes)
188 {
189 if (*p == '"')
190 ++length; // " -> ""
191 }
192 else
193# endif
194 if (*p == '\'')
195 {
196 if (powershell)
197 length +=2; // ' => ''
198 else
199 length += 3; // ' => '\''
200 }
201 if ((*p == '\n' && (csh_like || do_newline))
202 || (*p == '!' && (csh_like || do_special)))
203 {
204 ++length; // insert backslash
205 if (csh_like && do_special)
206 ++length; // insert backslash
207 }
208 if (do_special && find_cmdline_var(p, &l) >= 0)
209 {
210 ++length; // insert backslash
211 p += l - 1;
212 }
Jason Cox6e823512021-08-29 12:36:49 +0200213 if (*p == '\\' && fish_like)
214 ++length; // insert backslash
Yegappan Lakshmanana2438132021-07-10 21:29:18 +0200215 }
216
217 // Allocate memory for the result and fill it.
218 escaped_string = alloc(length);
219 if (escaped_string != NULL)
220 {
221 d = escaped_string;
222
223 // add opening quote
224# ifdef MSWIN
225 if (double_quotes)
226 *d++ = '"';
227 else
228# endif
229 *d++ = '\'';
230
231 for (p = string; *p != NUL; )
232 {
233# ifdef MSWIN
234 if (double_quotes)
235 {
236 if (*p == '"')
237 {
238 *d++ = '"';
239 *d++ = '"';
240 ++p;
241 continue;
242 }
243 }
244 else
245# endif
246 if (*p == '\'')
247 {
248 if (powershell)
249 {
250 *d++ = '\'';
251 *d++ = '\'';
252 }
253 else
254 {
255 *d++ = '\'';
256 *d++ = '\\';
257 *d++ = '\'';
258 *d++ = '\'';
259 }
260 ++p;
261 continue;
262 }
263 if ((*p == '\n' && (csh_like || do_newline))
264 || (*p == '!' && (csh_like || do_special)))
265 {
266 *d++ = '\\';
267 if (csh_like && do_special)
268 *d++ = '\\';
269 *d++ = *p++;
270 continue;
271 }
zeertzjq88c8c542024-05-30 19:27:25 +0200272 if (do_special && find_cmdline_var(p, &l) >= 0)
Yegappan Lakshmanana2438132021-07-10 21:29:18 +0200273 {
274 *d++ = '\\'; // insert backslash
zeertzjq88c8c542024-05-30 19:27:25 +0200275 memcpy(d, p, l); // copy the var
276 d += l;
277 p += l;
Yegappan Lakshmanana2438132021-07-10 21:29:18 +0200278 continue;
279 }
Jason Cox6e823512021-08-29 12:36:49 +0200280 if (*p == '\\' && fish_like)
281 {
282 *d++ = '\\';
283 *d++ = *p++;
Bram Moolenaar66315972021-09-01 14:31:51 +0200284 continue;
Jason Cox6e823512021-08-29 12:36:49 +0200285 }
Yegappan Lakshmanana2438132021-07-10 21:29:18 +0200286
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 *
308vim_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 *
322vim_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
335vim_strup(
336 char_u *p)
337{
338 char_u *p2;
339 int c;
340
Yegappan Lakshmanan6ec66662023-01-23 20:46:21 +0000341 if (p == NULL)
342 return;
343
344 p2 = p;
345 while ((c = *p2) != NUL)
346 *p2++ = (c < 'a' || c > 'z') ? c : (c - 0x20);
Yegappan Lakshmanana2438132021-07-10 21:29:18 +0200347}
348
349#if defined(FEAT_EVAL) || defined(FEAT_SPELL) || defined(PROTO)
350/*
351 * Make string "s" all upper-case and return it in allocated memory.
352 * Handles multi-byte characters as well as possible.
353 * Returns NULL when out of memory.
354 */
355 static char_u *
356strup_save(char_u *orig)
357{
358 char_u *p;
359 char_u *res;
360
361 res = p = vim_strsave(orig);
362
363 if (res != NULL)
364 while (*p != NUL)
365 {
366 int l;
367
368 if (enc_utf8)
369 {
370 int c, uc;
371 int newl;
372 char_u *s;
373
374 c = utf_ptr2char(p);
375 l = utf_ptr2len(p);
376 if (c == 0)
377 {
378 // overlong sequence, use only the first byte
379 c = *p;
380 l = 1;
381 }
382 uc = utf_toupper(c);
383
384 // Reallocate string when byte count changes. This is rare,
385 // thus it's OK to do another malloc()/free().
386 newl = utf_char2len(uc);
387 if (newl != l)
388 {
389 s = alloc(STRLEN(res) + 1 + newl - l);
390 if (s == NULL)
391 {
392 vim_free(res);
393 return NULL;
394 }
395 mch_memmove(s, res, p - res);
396 STRCPY(s + (p - res) + newl, p + l);
397 p = s + (p - res);
398 vim_free(res);
399 res = s;
400 }
401
402 utf_char2bytes(uc, p);
403 p += newl;
404 }
405 else if (has_mbyte && (l = (*mb_ptr2len)(p)) > 1)
406 p += l; // skip multi-byte character
407 else
408 {
409 *p = TOUPPER_LOC(*p); // note that toupper() can be a macro
410 p++;
411 }
412 }
413
414 return res;
415}
416
417/*
418 * Make string "s" all lower-case and return it in allocated memory.
419 * Handles multi-byte characters as well as possible.
420 * Returns NULL when out of memory.
421 */
422 char_u *
423strlow_save(char_u *orig)
424{
425 char_u *p;
426 char_u *res;
427
428 res = p = vim_strsave(orig);
429
430 if (res != NULL)
431 while (*p != NUL)
432 {
433 int l;
434
435 if (enc_utf8)
436 {
437 int c, lc;
438 int newl;
439 char_u *s;
440
441 c = utf_ptr2char(p);
442 l = utf_ptr2len(p);
443 if (c == 0)
444 {
445 // overlong sequence, use only the first byte
446 c = *p;
447 l = 1;
448 }
449 lc = utf_tolower(c);
450
451 // Reallocate string when byte count changes. This is rare,
452 // thus it's OK to do another malloc()/free().
453 newl = utf_char2len(lc);
454 if (newl != l)
455 {
456 s = alloc(STRLEN(res) + 1 + newl - l);
457 if (s == NULL)
458 {
459 vim_free(res);
460 return NULL;
461 }
462 mch_memmove(s, res, p - res);
463 STRCPY(s + (p - res) + newl, p + l);
464 p = s + (p - res);
465 vim_free(res);
466 res = s;
467 }
468
469 utf_char2bytes(lc, p);
470 p += newl;
471 }
472 else if (has_mbyte && (l = (*mb_ptr2len)(p)) > 1)
473 p += l; // skip multi-byte character
474 else
475 {
476 *p = TOLOWER_LOC(*p); // note that tolower() can be a macro
477 p++;
478 }
479 }
480
481 return res;
482}
483#endif
484
485/*
486 * delete spaces at the end of a string
487 */
488 void
489del_trailing_spaces(char_u *ptr)
490{
491 char_u *q;
492
493 q = ptr + STRLEN(ptr);
494 while (--q > ptr && VIM_ISWHITE(q[0]) && q[-1] != '\\' && q[-1] != Ctrl_V)
495 *q = NUL;
496}
497
498/*
499 * Like strncpy(), but always terminate the result with one NUL.
500 * "to" must be "len + 1" long!
501 */
502 void
503vim_strncpy(char_u *to, char_u *from, size_t len)
504{
505 STRNCPY(to, from, len);
506 to[len] = NUL;
507}
508
509/*
510 * Like strcat(), but make sure the result fits in "tosize" bytes and is
511 * always NUL terminated. "from" and "to" may overlap.
512 */
513 void
514vim_strcat(char_u *to, char_u *from, size_t tosize)
515{
516 size_t tolen = STRLEN(to);
517 size_t fromlen = STRLEN(from);
518
519 if (tolen + fromlen + 1 > tosize)
520 {
521 mch_memmove(to + tolen, from, tosize - tolen - 1);
522 to[tosize - 1] = NUL;
523 }
524 else
525 mch_memmove(to + tolen, from, fromlen + 1);
526}
527
Bram Moolenaarc32949b2023-01-04 15:56:51 +0000528/*
529 * A version of strlen() that has a maximum length.
530 */
531 size_t
532vim_strlen_maxlen(char *s, size_t maxlen)
533{
534 size_t i;
535 for (i = 0; i < maxlen; ++i)
536 if (s[i] == NUL)
537 break;
538 return i;
539}
540
Yegappan Lakshmanana2438132021-07-10 21:29:18 +0200541#if (!defined(HAVE_STRCASECMP) && !defined(HAVE_STRICMP)) || defined(PROTO)
542/*
543 * Compare two strings, ignoring case, using current locale.
544 * Doesn't work for multi-byte characters.
545 * return 0 for match, < 0 for smaller, > 0 for bigger
546 */
547 int
548vim_stricmp(char *s1, char *s2)
549{
550 int i;
551
552 for (;;)
553 {
554 i = (int)TOLOWER_LOC(*s1) - (int)TOLOWER_LOC(*s2);
555 if (i != 0)
556 return i; // this character different
557 if (*s1 == NUL)
558 break; // strings match until NUL
559 ++s1;
560 ++s2;
561 }
562 return 0; // strings match
563}
564#endif
565
566#if (!defined(HAVE_STRNCASECMP) && !defined(HAVE_STRNICMP)) || defined(PROTO)
567/*
568 * Compare two strings, for length "len", ignoring case, using current locale.
569 * Doesn't work for multi-byte characters.
570 * return 0 for match, < 0 for smaller, > 0 for bigger
571 */
572 int
573vim_strnicmp(char *s1, char *s2, size_t len)
574{
575 int i;
576
577 while (len > 0)
578 {
579 i = (int)TOLOWER_LOC(*s1) - (int)TOLOWER_LOC(*s2);
580 if (i != 0)
581 return i; // this character different
582 if (*s1 == NUL)
583 break; // strings match until NUL
584 ++s1;
585 ++s2;
586 --len;
587 }
588 return 0; // strings match
589}
590#endif
591
592/*
Christian Brabandt84e31752024-09-02 09:59:18 +0200593 * Compare two ASCII strings, for length "len", ignoring case, ignoring locale
594 * (mostly matters for turkish locale where i I might be different).
595 * return 0 for match, < 0 for smaller, > 0 for bigger
596 */
597 int
598vim_strnicmp_asc(char *s1, char *s2, size_t len)
599{
John Marriottc847c122024-11-24 14:09:40 +0100600 int i = 0;
Christian Brabandt84e31752024-09-02 09:59:18 +0200601 int save_cmp_flags = cmp_flags;
602
603 cmp_flags |= CMP_KEEPASCII; // compare by ASCII value, ignoring locale
604 while (len > 0)
605 {
606 i = vim_tolower(*s1) - vim_tolower(*s2);
607 if (i != 0)
608 break; // this character is different
609 if (*s1 == NUL)
610 break; // strings match until NUL
611 ++s1;
612 ++s2;
613 --len;
614 }
615 cmp_flags = save_cmp_flags;
616 return i;
617}
618
619/*
Yegappan Lakshmanana2438132021-07-10 21:29:18 +0200620 * Search for first occurrence of "c" in "string".
621 * Version of strchr() that handles unsigned char strings with characters from
622 * 128 to 255 correctly. It also doesn't return a pointer to the NUL at the
623 * end of the string.
624 */
Bram Moolenaarc32949b2023-01-04 15:56:51 +0000625 char_u *
Yegappan Lakshmanana2438132021-07-10 21:29:18 +0200626vim_strchr(char_u *string, int c)
627{
628 char_u *p;
629 int b;
630
631 p = string;
632 if (enc_utf8 && c >= 0x80)
633 {
634 while (*p != NUL)
635 {
636 int l = utfc_ptr2len(p);
637
638 // Avoid matching an illegal byte here.
639 if (utf_ptr2char(p) == c && l > 1)
640 return p;
641 p += l;
642 }
643 return NULL;
644 }
645 if (enc_dbcs != 0 && c > 255)
646 {
647 int n2 = c & 0xff;
648
649 c = ((unsigned)c >> 8) & 0xff;
650 while ((b = *p) != NUL)
651 {
652 if (b == c && p[1] == n2)
653 return p;
654 p += (*mb_ptr2len)(p);
655 }
656 return NULL;
657 }
658 if (has_mbyte)
659 {
660 while ((b = *p) != NUL)
661 {
662 if (b == c)
663 return p;
664 p += (*mb_ptr2len)(p);
665 }
666 return NULL;
667 }
668 while ((b = *p) != NUL)
669 {
670 if (b == c)
671 return p;
672 ++p;
673 }
674 return NULL;
675}
676
Jonathon7c7a4e62025-01-12 09:58:00 +0100677// Sized version of strchr that can handle embedded NULs.
678// Adjusts n to the new size.
679 char *
680vim_strnchr(const char *p, size_t *n, int c)
681{
682 while (*n > 0)
683 {
684 if (*p == c)
685 return (char *)p;
686 p++;
687 (*n)--;
688 }
689
690 return NULL;
691}
692
Yegappan Lakshmanana2438132021-07-10 21:29:18 +0200693/*
694 * Version of strchr() that only works for bytes and handles unsigned char
695 * strings with characters above 128 correctly. It also doesn't return a
696 * pointer to the NUL at the end of the string.
697 */
698 char_u *
699vim_strbyte(char_u *string, int c)
700{
701 char_u *p = string;
702
703 while (*p != NUL)
704 {
705 if (*p == c)
706 return p;
707 ++p;
708 }
709 return NULL;
710}
711
712/*
713 * Search for last occurrence of "c" in "string".
714 * Version of strrchr() that handles unsigned char strings with characters from
715 * 128 to 255 correctly. It also doesn't return a pointer to the NUL at the
716 * end of the string.
717 * Return NULL if not found.
718 * Does not handle multi-byte char for "c"!
719 */
720 char_u *
721vim_strrchr(char_u *string, int c)
722{
723 char_u *retval = NULL;
724 char_u *p = string;
725
726 while (*p)
727 {
728 if (*p == c)
729 retval = p;
730 MB_PTR_ADV(p);
731 }
732 return retval;
733}
734
735/*
736 * Vim's version of strpbrk(), in case it's missing.
737 * Don't generate a prototype for this, causes problems when it's not used.
738 */
739#ifndef PROTO
740# ifndef HAVE_STRPBRK
741# ifdef vim_strpbrk
742# undef vim_strpbrk
743# endif
744 char_u *
745vim_strpbrk(char_u *s, char_u *charset)
746{
747 while (*s)
748 {
749 if (vim_strchr(charset, *s) != NULL)
750 return s;
751 MB_PTR_ADV(s);
752 }
753 return NULL;
754}
755# endif
756#endif
757
758/*
759 * Sort an array of strings.
760 */
761static int sort_compare(const void *s1, const void *s2);
762
763 static int
764sort_compare(const void *s1, const void *s2)
765{
766 return STRCMP(*(char **)s1, *(char **)s2);
767}
768
769 void
770sort_strings(
771 char_u **files,
772 int count)
773{
774 qsort((void *)files, (size_t)count, sizeof(char_u *), sort_compare);
775}
776
777#if defined(FEAT_QUICKFIX) || defined(FEAT_SPELL) || defined(PROTO)
778/*
779 * Return TRUE if string "s" contains a non-ASCII character (128 or higher).
780 * When "s" is NULL FALSE is returned.
781 */
782 int
783has_non_ascii(char_u *s)
784{
785 char_u *p;
786
787 if (s != NULL)
788 for (p = s; *p != NUL; ++p)
789 if (*p >= 128)
790 return TRUE;
791 return FALSE;
792}
793#endif
794
795/*
796 * Concatenate two strings and return the result in allocated memory.
797 * Returns NULL when out of memory.
798 */
799 char_u *
800concat_str(char_u *str1, char_u *str2)
801{
802 char_u *dest;
803 size_t l = str1 == NULL ? 0 : STRLEN(str1);
804
805 dest = alloc(l + (str2 == NULL ? 0 : STRLEN(str2)) + 1L);
Yegappan Lakshmanan6ec66662023-01-23 20:46:21 +0000806 if (dest == NULL)
807 return NULL;
808 if (str1 == NULL)
809 *dest = NUL;
810 else
811 STRCPY(dest, str1);
812 if (str2 != NULL)
813 STRCPY(dest + l, str2);
Yegappan Lakshmanana2438132021-07-10 21:29:18 +0200814 return dest;
815}
816
zeertzjq4dd266c2023-08-19 11:35:03 +0200817#if defined(FEAT_EVAL) || defined(FEAT_RIGHTLEFT) || defined(PROTO)
818/*
819 * Reverse text into allocated memory.
820 * Returns the allocated string, NULL when out of memory.
821 */
822 char_u *
823reverse_text(char_u *s)
824{
825 size_t len = STRLEN(s);
826 char_u *rev = alloc(len + 1);
827 if (rev == NULL)
828 return NULL;
829
830 for (size_t s_i = 0, rev_i = len; s_i < len; ++s_i)
831 {
832 if (has_mbyte)
833 {
834 int mb_len = (*mb_ptr2len)(s + s_i);
835 rev_i -= mb_len;
836 mch_memmove(rev + rev_i, s + s_i, mb_len);
837 s_i += mb_len - 1;
838 }
839 else
840 rev[--rev_i] = s[s_i];
841 }
842 rev[len] = NUL;
843 return rev;
844}
845#endif
846
Yegappan Lakshmanana2438132021-07-10 21:29:18 +0200847#if defined(FEAT_EVAL) || defined(PROTO)
Yegappan Lakshmanana2438132021-07-10 21:29:18 +0200848/*
849 * Return string "str" in ' quotes, doubling ' characters.
850 * If "str" is NULL an empty string is assumed.
851 * If "function" is TRUE make it function('string').
852 */
853 char_u *
854string_quote(char_u *str, int function)
855{
856 unsigned len;
857 char_u *p, *r, *s;
858
859 len = (function ? 13 : 3);
860 if (str != NULL)
861 {
862 len += (unsigned)STRLEN(str);
863 for (p = str; *p != NUL; MB_PTR_ADV(p))
864 if (*p == '\'')
865 ++len;
866 }
867 s = r = alloc(len);
Yegappan Lakshmanan6ec66662023-01-23 20:46:21 +0000868 if (r == NULL)
869 return NULL;
870
871 if (function)
Yegappan Lakshmanana2438132021-07-10 21:29:18 +0200872 {
Yegappan Lakshmanan6ec66662023-01-23 20:46:21 +0000873 STRCPY(r, "function('");
874 r += 10;
Yegappan Lakshmanana2438132021-07-10 21:29:18 +0200875 }
Yegappan Lakshmanan6ec66662023-01-23 20:46:21 +0000876 else
877 *r++ = '\'';
878 if (str != NULL)
879 for (p = str; *p != NUL; )
880 {
881 if (*p == '\'')
882 *r++ = '\'';
883 MB_COPY_CHAR(p, r);
884 }
885 *r++ = '\'';
886 if (function)
887 *r++ = ')';
888 *r++ = NUL;
Yegappan Lakshmanana2438132021-07-10 21:29:18 +0200889 return s;
890}
891
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +0000892/*
893 * Count the number of times "needle" occurs in string "haystack". Case is
894 * ignored if "ic" is TRUE.
895 */
896 long
897string_count(char_u *haystack, char_u *needle, int ic)
898{
899 long n = 0;
900 char_u *p = haystack;
901 char_u *next;
902
903 if (p == NULL || needle == NULL || *needle == NUL)
904 return 0;
905
906 if (ic)
907 {
908 size_t len = STRLEN(needle);
909
910 while (*p != NUL)
911 {
912 if (MB_STRNICMP(p, needle, len) == 0)
913 {
914 ++n;
915 p += len;
916 }
917 else
918 MB_PTR_ADV(p);
919 }
920 }
921 else
922 while ((next = (char_u *)strstr((char *)p, (char *)needle)) != NULL)
923 {
924 ++n;
925 p = next + STRLEN(needle);
926 }
927
928 return n;
929}
930
931/*
932 * Make a typval_T of the first character of "input" and store it in "output".
933 * Return OK or FAIL.
934 */
935 static int
936copy_first_char_to_tv(char_u *input, typval_T *output)
937{
938 char_u buf[MB_MAXBYTES + 1];
939 int len;
940
941 if (input == NULL || output == NULL)
942 return FAIL;
943
944 len = has_mbyte ? mb_ptr2len(input) : 1;
945 STRNCPY(buf, input, len);
946 buf[len] = NUL;
947 output->v_type = VAR_STRING;
948 output->vval.v_string = vim_strsave(buf);
949
950 return output->vval.v_string == NULL ? FAIL : OK;
951}
952
953/*
954 * Implementation of map() and filter() for a String. Apply "expr" to every
955 * character in string "str" and return the result in "rettv".
956 */
957 void
958string_filter_map(
959 char_u *str,
960 filtermap_T filtermap,
961 typval_T *expr,
962 typval_T *rettv)
963{
964 char_u *p;
965 typval_T tv;
966 garray_T ga;
967 int len = 0;
968 int idx = 0;
969 int rem;
Bram Moolenaar82418262022-09-28 16:16:15 +0100970 typval_T newtv;
971 funccall_T *fc;
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +0000972
973 rettv->v_type = VAR_STRING;
974 rettv->vval.v_string = NULL;
975
976 // set_vim_var_nr() doesn't set the type
977 set_vim_var_type(VV_KEY, VAR_NUMBER);
978
zeertzjqe7d49462023-04-16 20:53:55 +0100979 // Create one funccall_T for all eval_expr_typval() calls.
Bram Moolenaar82418262022-09-28 16:16:15 +0100980 fc = eval_expr_get_funccal(expr, &newtv);
981
Bram Moolenaar04935fb2022-01-08 16:19:22 +0000982 ga_init2(&ga, sizeof(char), 80);
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +0000983 for (p = str; *p != NUL; p += len)
984 {
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +0000985 if (copy_first_char_to_tv(p, &tv) == FAIL)
986 break;
987 len = (int)STRLEN(tv.vval.v_string);
988
989 set_vim_var_nr(VV_KEY, idx);
Bram Moolenaar82418262022-09-28 16:16:15 +0100990 if (filter_map_one(&tv, expr, filtermap, fc, &newtv, &rem) == FAIL
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +0000991 || did_emsg)
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +0000992 {
993 clear_tv(&newtv);
994 clear_tv(&tv);
995 break;
996 }
Ernie Raele79e2072024-01-13 11:47:33 +0100997 if (filtermap == FILTERMAP_MAP || filtermap == FILTERMAP_MAPNEW)
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +0000998 {
999 if (newtv.v_type != VAR_STRING)
1000 {
1001 clear_tv(&newtv);
1002 clear_tv(&tv);
Bram Moolenaare70cec92022-01-01 14:25:55 +00001003 emsg(_(e_string_required));
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +00001004 break;
1005 }
1006 else
1007 ga_concat(&ga, newtv.vval.v_string);
1008 }
Ernie Raele79e2072024-01-13 11:47:33 +01001009 else if (filtermap == FILTERMAP_FOREACH || !rem)
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +00001010 ga_concat(&ga, tv.vval.v_string);
1011
1012 clear_tv(&newtv);
1013 clear_tv(&tv);
1014
1015 ++idx;
1016 }
1017 ga_append(&ga, NUL);
1018 rettv->vval.v_string = ga.ga_data;
Bram Moolenaar82418262022-09-28 16:16:15 +01001019 if (fc != NULL)
1020 remove_funccal();
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +00001021}
1022
1023/*
Bram Moolenaarf1c60d42022-09-22 17:07:00 +01001024 * Implementation of reduce() for String "argvars[0]" using the function "expr"
1025 * starting with the optional initial value "argvars[2]" and return the result
1026 * in "rettv".
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +00001027 */
1028 void
1029string_reduce(
1030 typval_T *argvars,
Bram Moolenaarf1c60d42022-09-22 17:07:00 +01001031 typval_T *expr,
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +00001032 typval_T *rettv)
1033{
1034 char_u *p = tv_get_string(&argvars[0]);
1035 int len;
1036 typval_T argv[3];
1037 int r;
1038 int called_emsg_start = called_emsg;
Bram Moolenaar82418262022-09-28 16:16:15 +01001039 funccall_T *fc;
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +00001040
1041 if (argvars[2].v_type == VAR_UNKNOWN)
1042 {
1043 if (*p == NUL)
1044 {
Bram Moolenaare70cec92022-01-01 14:25:55 +00001045 semsg(_(e_reduce_of_an_empty_str_with_no_initial_value), "String");
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +00001046 return;
1047 }
1048 if (copy_first_char_to_tv(p, rettv) == FAIL)
1049 return;
1050 p += STRLEN(rettv->vval.v_string);
1051 }
Yegappan Lakshmanan8deb2b32022-09-02 15:15:27 +01001052 else if (check_for_string_arg(argvars, 2) == FAIL)
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +00001053 return;
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +00001054 else
1055 copy_tv(&argvars[2], rettv);
1056
zeertzjqe7d49462023-04-16 20:53:55 +01001057 // Create one funccall_T for all eval_expr_typval() calls.
Bram Moolenaar82418262022-09-28 16:16:15 +01001058 fc = eval_expr_get_funccal(expr, rettv);
1059
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +00001060 for ( ; *p != NUL; p += len)
1061 {
1062 argv[0] = *rettv;
1063 if (copy_first_char_to_tv(p, &argv[1]) == FAIL)
1064 break;
1065 len = (int)STRLEN(argv[1].vval.v_string);
Bram Moolenaarf1c60d42022-09-22 17:07:00 +01001066
zeertzjqad0c4422023-08-17 22:15:47 +02001067 r = eval_expr_typval(expr, TRUE, argv, 2, fc, rettv);
Bram Moolenaarf1c60d42022-09-22 17:07:00 +01001068
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +00001069 clear_tv(&argv[0]);
1070 clear_tv(&argv[1]);
1071 if (r == FAIL || called_emsg != called_emsg_start)
1072 return;
1073 }
Bram Moolenaar82418262022-09-28 16:16:15 +01001074
1075 if (fc != NULL)
1076 remove_funccal();
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +00001077}
1078
Bram Moolenaare4098452023-05-07 18:53:49 +01001079/*
1080 * Implementation of "byteidx()" and "byteidxcomp()" functions
1081 */
Yegappan Lakshmanana2438132021-07-10 21:29:18 +02001082 static void
Dominique Pellé0268ff32024-07-28 21:12:20 +02001083byteidx_common(typval_T *argvars, typval_T *rettv, int comp)
Yegappan Lakshmanana2438132021-07-10 21:29:18 +02001084{
Yegappan Lakshmanan1a71d312021-07-15 12:49:58 +02001085 rettv->vval.v_number = -1;
1086
1087 if (in_vim9script()
1088 && (check_for_string_arg(argvars, 0) == FAIL
Yegappan Lakshmanan577922b2023-06-08 17:09:45 +01001089 || check_for_number_arg(argvars, 1) == FAIL
1090 || check_for_opt_bool_arg(argvars, 2) == FAIL))
Yegappan Lakshmanan1a71d312021-07-15 12:49:58 +02001091 return;
1092
Christian Brabandt67672ef2023-04-24 21:09:54 +01001093 char_u *str = tv_get_string_chk(&argvars[0]);
1094 varnumber_T idx = tv_get_number_chk(&argvars[1], NULL);
Yegappan Lakshmanana2438132021-07-10 21:29:18 +02001095 if (str == NULL || idx < 0)
1096 return;
1097
Christian Brabandt67672ef2023-04-24 21:09:54 +01001098 varnumber_T utf16idx = FALSE;
1099 if (argvars[2].v_type != VAR_UNKNOWN)
1100 {
zeertzjq8cf51372023-05-08 15:31:38 +01001101 int error = FALSE;
1102 utf16idx = tv_get_bool_chk(&argvars[2], &error);
1103 if (error)
1104 return;
Christian Brabandt67672ef2023-04-24 21:09:54 +01001105 if (utf16idx < 0 || utf16idx > 1)
1106 {
zeertzjq8cf51372023-05-08 15:31:38 +01001107 semsg(_(e_using_number_as_bool_nr), utf16idx);
Christian Brabandt67672ef2023-04-24 21:09:54 +01001108 return;
1109 }
1110 }
1111
1112 int (*ptr2len)(char_u *);
1113 if (enc_utf8 && comp)
1114 ptr2len = utf_ptr2len;
1115 else
1116 ptr2len = mb_ptr2len;
1117
1118 char_u *t = str;
Yegappan Lakshmanana2438132021-07-10 21:29:18 +02001119 for ( ; idx > 0; idx--)
1120 {
1121 if (*t == NUL) // EOL reached
1122 return;
Christian Brabandt67672ef2023-04-24 21:09:54 +01001123 if (utf16idx)
1124 {
1125 int clen = ptr2len(t);
1126 int c = (clen > 1) ? utf_ptr2char(t) : *t;
1127 if (c > 0xFFFF)
1128 idx--;
1129 }
1130 if (idx > 0)
1131 t += ptr2len(t);
Yegappan Lakshmanana2438132021-07-10 21:29:18 +02001132 }
1133 rettv->vval.v_number = (varnumber_T)(t - str);
1134}
1135
1136/*
1137 * "byteidx()" function
1138 */
1139 void
1140f_byteidx(typval_T *argvars, typval_T *rettv)
1141{
Bram Moolenaare4098452023-05-07 18:53:49 +01001142 byteidx_common(argvars, rettv, FALSE);
Yegappan Lakshmanana2438132021-07-10 21:29:18 +02001143}
1144
1145/*
1146 * "byteidxcomp()" function
1147 */
1148 void
1149f_byteidxcomp(typval_T *argvars, typval_T *rettv)
1150{
Bram Moolenaare4098452023-05-07 18:53:49 +01001151 byteidx_common(argvars, rettv, TRUE);
Yegappan Lakshmanana2438132021-07-10 21:29:18 +02001152}
1153
1154/*
1155 * "charidx()" function
1156 */
1157 void
1158f_charidx(typval_T *argvars, typval_T *rettv)
1159{
Yegappan Lakshmanana2438132021-07-10 21:29:18 +02001160 rettv->vval.v_number = -1;
1161
Christian Brabandt67672ef2023-04-24 21:09:54 +01001162 if (check_for_string_arg(argvars, 0) == FAIL
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02001163 || check_for_number_arg(argvars, 1) == FAIL
Christian Brabandt67672ef2023-04-24 21:09:54 +01001164 || check_for_opt_bool_arg(argvars, 2) == FAIL
1165 || (argvars[2].v_type != VAR_UNKNOWN
1166 && check_for_opt_bool_arg(argvars, 3) == FAIL))
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02001167 return;
1168
Christian Brabandt67672ef2023-04-24 21:09:54 +01001169 char_u *str = tv_get_string_chk(&argvars[0]);
1170 varnumber_T idx = tv_get_number_chk(&argvars[1], NULL);
Yegappan Lakshmanana2438132021-07-10 21:29:18 +02001171 if (str == NULL || idx < 0)
1172 return;
1173
Christian Brabandt67672ef2023-04-24 21:09:54 +01001174 varnumber_T countcc = FALSE;
1175 varnumber_T utf16idx = FALSE;
Yegappan Lakshmanana2438132021-07-10 21:29:18 +02001176 if (argvars[2].v_type != VAR_UNKNOWN)
Yegappan Lakshmanana2438132021-07-10 21:29:18 +02001177 {
Christian Brabandt67672ef2023-04-24 21:09:54 +01001178 countcc = tv_get_bool(&argvars[2]);
1179 if (argvars[3].v_type != VAR_UNKNOWN)
1180 utf16idx = tv_get_bool(&argvars[3]);
Yegappan Lakshmanana2438132021-07-10 21:29:18 +02001181 }
1182
Christian Brabandt67672ef2023-04-24 21:09:54 +01001183 int (*ptr2len)(char_u *);
Yegappan Lakshmanana2438132021-07-10 21:29:18 +02001184 if (enc_utf8 && countcc)
1185 ptr2len = utf_ptr2len;
1186 else
1187 ptr2len = mb_ptr2len;
1188
Christian Brabandt67672ef2023-04-24 21:09:54 +01001189 char_u *p;
1190 int len;
1191 for (p = str, len = 0; utf16idx ? idx >= 0 : p <= str + idx; len++)
Yegappan Lakshmanana2438132021-07-10 21:29:18 +02001192 {
1193 if (*p == NUL)
Yegappan Lakshmanan577922b2023-06-08 17:09:45 +01001194 {
1195 // If the index is exactly the number of bytes or utf-16 code units
1196 // in the string then return the length of the string in
1197 // characters.
1198 if (utf16idx ? (idx == 0) : (p == (str + idx)))
1199 rettv->vval.v_number = len;
Yegappan Lakshmanana2438132021-07-10 21:29:18 +02001200 return;
Yegappan Lakshmanan577922b2023-06-08 17:09:45 +01001201 }
Christian Brabandt67672ef2023-04-24 21:09:54 +01001202 if (utf16idx)
1203 {
1204 idx--;
1205 int clen = ptr2len(p);
1206 int c = (clen > 1) ? utf_ptr2char(p) : *p;
1207 if (c > 0xFFFF)
1208 idx--;
1209 }
Yegappan Lakshmanana2438132021-07-10 21:29:18 +02001210 p += ptr2len(p);
1211 }
1212
1213 rettv->vval.v_number = len > 0 ? len - 1 : 0;
1214}
1215
1216/*
1217 * "str2list()" function
1218 */
1219 void
1220f_str2list(typval_T *argvars, typval_T *rettv)
1221{
1222 char_u *p;
1223 int utf8 = FALSE;
1224
1225 if (rettv_list_alloc(rettv) == FAIL)
1226 return;
1227
Yegappan Lakshmanana9a7c0c2021-07-17 19:11:07 +02001228 if (in_vim9script()
1229 && (check_for_string_arg(argvars, 0) == FAIL
Yegappan Lakshmanan83494b42021-07-20 17:51:51 +02001230 || check_for_opt_bool_arg(argvars, 1) == FAIL))
Yegappan Lakshmanana9a7c0c2021-07-17 19:11:07 +02001231 return;
1232
Yegappan Lakshmanana2438132021-07-10 21:29:18 +02001233 if (argvars[1].v_type != VAR_UNKNOWN)
1234 utf8 = (int)tv_get_bool_chk(&argvars[1], NULL);
1235
1236 p = tv_get_string(&argvars[0]);
1237
1238 if (has_mbyte || utf8)
1239 {
1240 int (*ptr2len)(char_u *);
1241 int (*ptr2char)(char_u *);
1242
1243 if (utf8 || enc_utf8)
1244 {
1245 ptr2len = utf_ptr2len;
1246 ptr2char = utf_ptr2char;
1247 }
1248 else
1249 {
1250 ptr2len = mb_ptr2len;
1251 ptr2char = mb_ptr2char;
1252 }
1253
1254 for ( ; *p != NUL; p += (*ptr2len)(p))
1255 list_append_number(rettv->vval.v_list, (*ptr2char)(p));
1256 }
1257 else
1258 for ( ; *p != NUL; ++p)
1259 list_append_number(rettv->vval.v_list, *p);
1260}
1261
1262/*
1263 * "str2nr()" function
1264 */
1265 void
1266f_str2nr(typval_T *argvars, typval_T *rettv)
1267{
1268 int base = 10;
1269 char_u *p;
1270 varnumber_T n;
1271 int what = 0;
1272 int isneg;
1273
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02001274 if (in_vim9script()
1275 && (check_for_string_arg(argvars, 0) == FAIL
1276 || check_for_opt_number_arg(argvars, 1) == FAIL
1277 || (argvars[1].v_type != VAR_UNKNOWN
1278 && check_for_opt_bool_arg(argvars, 2) == FAIL)))
1279 return;
1280
Yegappan Lakshmanana2438132021-07-10 21:29:18 +02001281 if (argvars[1].v_type != VAR_UNKNOWN)
1282 {
1283 base = (int)tv_get_number(&argvars[1]);
1284 if (base != 2 && base != 8 && base != 10 && base != 16)
1285 {
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00001286 emsg(_(e_invalid_argument));
Yegappan Lakshmanana2438132021-07-10 21:29:18 +02001287 return;
1288 }
1289 if (argvars[2].v_type != VAR_UNKNOWN && tv_get_bool(&argvars[2]))
1290 what |= STR2NR_QUOTE;
1291 }
1292
1293 p = skipwhite(tv_get_string_strict(&argvars[0]));
1294 isneg = (*p == '-');
1295 if (*p == '+' || *p == '-')
1296 p = skipwhite(p + 1);
1297 switch (base)
1298 {
1299 case 2: what |= STR2NR_BIN + STR2NR_FORCE; break;
1300 case 8: what |= STR2NR_OCT + STR2NR_OOCT + STR2NR_FORCE; break;
1301 case 16: what |= STR2NR_HEX + STR2NR_FORCE; break;
1302 }
Bram Moolenaar5fb78c32023-03-04 20:47:39 +00001303 vim_str2nr(p, NULL, NULL, what, &n, NULL, 0, FALSE, NULL);
Yegappan Lakshmanana2438132021-07-10 21:29:18 +02001304 // Text after the number is silently ignored.
1305 if (isneg)
1306 rettv->vval.v_number = -n;
1307 else
1308 rettv->vval.v_number = n;
1309
1310}
1311
1312/*
1313 * "strgetchar()" function
1314 */
1315 void
1316f_strgetchar(typval_T *argvars, typval_T *rettv)
1317{
1318 char_u *str;
1319 int len;
1320 int error = FALSE;
1321 int charidx;
1322 int byteidx = 0;
1323
1324 rettv->vval.v_number = -1;
Yegappan Lakshmanan1a71d312021-07-15 12:49:58 +02001325
1326 if (in_vim9script()
1327 && (check_for_string_arg(argvars, 0) == FAIL
1328 || check_for_number_arg(argvars, 1) == FAIL))
1329 return;
1330
Yegappan Lakshmanana2438132021-07-10 21:29:18 +02001331 str = tv_get_string_chk(&argvars[0]);
1332 if (str == NULL)
1333 return;
1334 len = (int)STRLEN(str);
1335 charidx = (int)tv_get_number_chk(&argvars[1], &error);
1336 if (error)
1337 return;
1338
1339 while (charidx >= 0 && byteidx < len)
1340 {
1341 if (charidx == 0)
1342 {
1343 rettv->vval.v_number = mb_ptr2char(str + byteidx);
1344 break;
1345 }
1346 --charidx;
1347 byteidx += MB_CPTR2LEN(str + byteidx);
1348 }
1349}
1350
1351/*
1352 * "stridx()" function
1353 */
1354 void
1355f_stridx(typval_T *argvars, typval_T *rettv)
1356{
1357 char_u buf[NUMBUFLEN];
1358 char_u *needle;
1359 char_u *haystack;
1360 char_u *save_haystack;
1361 char_u *pos;
1362 int start_idx;
1363
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02001364 if (in_vim9script()
1365 && (check_for_string_arg(argvars, 0) == FAIL
1366 || check_for_string_arg(argvars, 1) == FAIL
1367 || check_for_opt_number_arg(argvars, 2) == FAIL))
1368 return;
1369
Yegappan Lakshmanana2438132021-07-10 21:29:18 +02001370 needle = tv_get_string_chk(&argvars[1]);
1371 save_haystack = haystack = tv_get_string_buf_chk(&argvars[0], buf);
1372 rettv->vval.v_number = -1;
1373 if (needle == NULL || haystack == NULL)
1374 return; // type error; errmsg already given
1375
1376 if (argvars[2].v_type != VAR_UNKNOWN)
1377 {
1378 int error = FALSE;
1379
1380 start_idx = (int)tv_get_number_chk(&argvars[2], &error);
1381 if (error || start_idx >= (int)STRLEN(haystack))
1382 return;
1383 if (start_idx >= 0)
1384 haystack += start_idx;
1385 }
1386
1387 pos = (char_u *)strstr((char *)haystack, (char *)needle);
1388 if (pos != NULL)
1389 rettv->vval.v_number = (varnumber_T)(pos - save_haystack);
1390}
1391
1392/*
1393 * "string()" function
1394 */
1395 void
1396f_string(typval_T *argvars, typval_T *rettv)
1397{
1398 char_u *tofree;
1399 char_u numbuf[NUMBUFLEN];
1400
1401 rettv->v_type = VAR_STRING;
1402 rettv->vval.v_string = tv2string(&argvars[0], &tofree, numbuf,
1403 get_copyID());
1404 // Make a copy if we have a value but it's not in allocated memory.
1405 if (rettv->vval.v_string != NULL && tofree == NULL)
1406 rettv->vval.v_string = vim_strsave(rettv->vval.v_string);
1407}
1408
1409/*
1410 * "strlen()" function
1411 */
1412 void
1413f_strlen(typval_T *argvars, typval_T *rettv)
1414{
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02001415 if (in_vim9script()
1416 && check_for_string_or_number_arg(argvars, 0) == FAIL)
1417 return;
1418
Yegappan Lakshmanana2438132021-07-10 21:29:18 +02001419 rettv->vval.v_number = (varnumber_T)(STRLEN(
1420 tv_get_string(&argvars[0])));
1421}
1422
1423 static void
1424strchar_common(typval_T *argvars, typval_T *rettv, int skipcc)
1425{
1426 char_u *s = tv_get_string(&argvars[0]);
1427 varnumber_T len = 0;
1428 int (*func_mb_ptr2char_adv)(char_u **pp);
1429
1430 func_mb_ptr2char_adv = skipcc ? mb_ptr2char_adv : mb_cptr2char_adv;
1431 while (*s != NUL)
1432 {
1433 func_mb_ptr2char_adv(&s);
1434 ++len;
1435 }
1436 rettv->vval.v_number = len;
1437}
1438
1439/*
1440 * "strcharlen()" function
1441 */
1442 void
1443f_strcharlen(typval_T *argvars, typval_T *rettv)
1444{
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02001445 if (in_vim9script()
1446 && check_for_string_or_number_arg(argvars, 0) == FAIL)
1447 return;
1448
Yegappan Lakshmanana2438132021-07-10 21:29:18 +02001449 strchar_common(argvars, rettv, TRUE);
1450}
1451
1452/*
1453 * "strchars()" function
1454 */
1455 void
1456f_strchars(typval_T *argvars, typval_T *rettv)
1457{
1458 varnumber_T skipcc = FALSE;
1459
Yegappan Lakshmanana9a7c0c2021-07-17 19:11:07 +02001460 if (in_vim9script()
1461 && (check_for_string_arg(argvars, 0) == FAIL
Yegappan Lakshmanan83494b42021-07-20 17:51:51 +02001462 || check_for_opt_bool_arg(argvars, 1) == FAIL))
Yegappan Lakshmanana9a7c0c2021-07-17 19:11:07 +02001463 return;
1464
Yegappan Lakshmanana2438132021-07-10 21:29:18 +02001465 if (argvars[1].v_type != VAR_UNKNOWN)
Bram Moolenaare4098452023-05-07 18:53:49 +01001466 {
zeertzjq8cf51372023-05-08 15:31:38 +01001467 int error = FALSE;
1468 skipcc = tv_get_bool_chk(&argvars[1], &error);
1469 if (error)
1470 return;
1471 if (skipcc < 0 || skipcc > 1)
1472 {
Bram Moolenaare4098452023-05-07 18:53:49 +01001473 semsg(_(e_using_number_as_bool_nr), skipcc);
zeertzjq8cf51372023-05-08 15:31:38 +01001474 return;
1475 }
Bram Moolenaare4098452023-05-07 18:53:49 +01001476 }
zeertzjq8cf51372023-05-08 15:31:38 +01001477
1478 strchar_common(argvars, rettv, skipcc);
Yegappan Lakshmanana2438132021-07-10 21:29:18 +02001479}
1480
1481/*
Christian Brabandt67672ef2023-04-24 21:09:54 +01001482 * "strutf16len()" function
1483 */
1484 void
1485f_strutf16len(typval_T *argvars, typval_T *rettv)
1486{
1487 rettv->vval.v_number = -1;
1488
1489 if (check_for_string_arg(argvars, 0) == FAIL
1490 || check_for_opt_bool_arg(argvars, 1) == FAIL)
1491 return;
1492
1493 varnumber_T countcc = FALSE;
1494 if (argvars[1].v_type != VAR_UNKNOWN)
1495 countcc = tv_get_bool(&argvars[1]);
1496
1497 char_u *s = tv_get_string(&argvars[0]);
1498 varnumber_T len = 0;
1499 int (*func_mb_ptr2char_adv)(char_u **pp);
1500 int ch;
1501
1502 func_mb_ptr2char_adv = countcc ? mb_cptr2char_adv : mb_ptr2char_adv;
1503 while (*s != NUL)
1504 {
1505 ch = func_mb_ptr2char_adv(&s);
1506 if (ch > 0xFFFF)
1507 ++len;
1508 ++len;
1509 }
1510 rettv->vval.v_number = len;
1511}
1512
1513/*
Yegappan Lakshmanana2438132021-07-10 21:29:18 +02001514 * "strdisplaywidth()" function
1515 */
1516 void
1517f_strdisplaywidth(typval_T *argvars, typval_T *rettv)
1518{
Yegappan Lakshmanan1a71d312021-07-15 12:49:58 +02001519 char_u *s;
Yegappan Lakshmanana2438132021-07-10 21:29:18 +02001520 int col = 0;
1521
Yegappan Lakshmanan1a71d312021-07-15 12:49:58 +02001522 rettv->vval.v_number = -1;
1523
1524 if (in_vim9script()
1525 && (check_for_string_arg(argvars, 0) == FAIL
Yegappan Lakshmanan83494b42021-07-20 17:51:51 +02001526 || check_for_opt_number_arg(argvars, 1) == FAIL))
Yegappan Lakshmanan1a71d312021-07-15 12:49:58 +02001527 return;
1528
1529 s = tv_get_string(&argvars[0]);
Yegappan Lakshmanana2438132021-07-10 21:29:18 +02001530 if (argvars[1].v_type != VAR_UNKNOWN)
1531 col = (int)tv_get_number(&argvars[1]);
1532
1533 rettv->vval.v_number = (varnumber_T)(linetabsize_col(col, s) - col);
1534}
1535
1536/*
1537 * "strwidth()" function
1538 */
1539 void
1540f_strwidth(typval_T *argvars, typval_T *rettv)
1541{
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02001542 char_u *s;
Yegappan Lakshmanana2438132021-07-10 21:29:18 +02001543
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02001544 if (in_vim9script() && check_for_string_arg(argvars, 0) == FAIL)
1545 return;
1546
1547 s = tv_get_string_strict(&argvars[0]);
Yegappan Lakshmanana2438132021-07-10 21:29:18 +02001548 rettv->vval.v_number = (varnumber_T)(mb_string2cells(s, -1));
1549}
1550
1551/*
1552 * "strcharpart()" function
1553 */
1554 void
1555f_strcharpart(typval_T *argvars, typval_T *rettv)
1556{
1557 char_u *p;
1558 int nchar;
1559 int nbyte = 0;
1560 int charlen;
1561 int skipcc = FALSE;
1562 int len = 0;
1563 int slen;
1564 int error = FALSE;
1565
Yegappan Lakshmanan83494b42021-07-20 17:51:51 +02001566 if (in_vim9script()
1567 && (check_for_string_arg(argvars, 0) == FAIL
1568 || check_for_number_arg(argvars, 1) == FAIL
1569 || check_for_opt_number_arg(argvars, 2) == FAIL
1570 || (argvars[2].v_type != VAR_UNKNOWN
1571 && check_for_opt_bool_arg(argvars, 3) == FAIL)))
1572 return;
1573
Yegappan Lakshmanana2438132021-07-10 21:29:18 +02001574 p = tv_get_string(&argvars[0]);
1575 slen = (int)STRLEN(p);
1576
1577 nchar = (int)tv_get_number_chk(&argvars[1], &error);
1578 if (!error)
1579 {
1580 if (argvars[2].v_type != VAR_UNKNOWN
1581 && argvars[3].v_type != VAR_UNKNOWN)
1582 {
zeertzjq8cf51372023-05-08 15:31:38 +01001583 skipcc = tv_get_bool_chk(&argvars[3], &error);
1584 if (error)
1585 return;
Yegappan Lakshmanana2438132021-07-10 21:29:18 +02001586 if (skipcc < 0 || skipcc > 1)
1587 {
zeertzjq8cf51372023-05-08 15:31:38 +01001588 semsg(_(e_using_number_as_bool_nr), skipcc);
Yegappan Lakshmanana2438132021-07-10 21:29:18 +02001589 return;
1590 }
1591 }
1592
1593 if (nchar > 0)
1594 while (nchar > 0 && nbyte < slen)
1595 {
1596 if (skipcc)
1597 nbyte += mb_ptr2len(p + nbyte);
1598 else
1599 nbyte += MB_CPTR2LEN(p + nbyte);
1600 --nchar;
1601 }
1602 else
1603 nbyte = nchar;
1604 if (argvars[2].v_type != VAR_UNKNOWN)
1605 {
1606 charlen = (int)tv_get_number(&argvars[2]);
1607 while (charlen > 0 && nbyte + len < slen)
1608 {
1609 int off = nbyte + len;
1610
1611 if (off < 0)
1612 len += 1;
1613 else
1614 {
1615 if (skipcc)
1616 len += mb_ptr2len(p + off);
1617 else
1618 len += MB_CPTR2LEN(p + off);
1619 }
1620 --charlen;
1621 }
1622 }
1623 else
1624 len = slen - nbyte; // default: all bytes that are available.
1625 }
1626
Yegappan Lakshmanan8ee52af2021-08-09 19:59:06 +02001627 // Only return the overlap between the specified part and the actual
1628 // string.
Yegappan Lakshmanana2438132021-07-10 21:29:18 +02001629 if (nbyte < 0)
1630 {
1631 len += nbyte;
1632 nbyte = 0;
1633 }
1634 else if (nbyte > slen)
1635 nbyte = slen;
1636 if (len < 0)
1637 len = 0;
1638 else if (nbyte + len > slen)
1639 len = slen - nbyte;
1640
1641 rettv->v_type = VAR_STRING;
1642 rettv->vval.v_string = vim_strnsave(p + nbyte, len);
1643}
1644
1645/*
1646 * "strpart()" function
1647 */
1648 void
1649f_strpart(typval_T *argvars, typval_T *rettv)
1650{
1651 char_u *p;
1652 int n;
1653 int len;
1654 int slen;
1655 int error = FALSE;
1656
Yegappan Lakshmanan83494b42021-07-20 17:51:51 +02001657 if (in_vim9script()
1658 && (check_for_string_arg(argvars, 0) == FAIL
1659 || check_for_number_arg(argvars, 1) == FAIL
1660 || check_for_opt_number_arg(argvars, 2) == FAIL
1661 || (argvars[2].v_type != VAR_UNKNOWN
1662 && check_for_opt_bool_arg(argvars, 3) == FAIL)))
1663 return;
1664
Yegappan Lakshmanana2438132021-07-10 21:29:18 +02001665 p = tv_get_string(&argvars[0]);
1666 slen = (int)STRLEN(p);
1667
1668 n = (int)tv_get_number_chk(&argvars[1], &error);
1669 if (error)
1670 len = 0;
1671 else if (argvars[2].v_type != VAR_UNKNOWN)
1672 len = (int)tv_get_number(&argvars[2]);
1673 else
1674 len = slen - n; // default len: all bytes that are available.
1675
1676 // Only return the overlap between the specified part and the actual
1677 // string.
1678 if (n < 0)
1679 {
1680 len += n;
1681 n = 0;
1682 }
1683 else if (n > slen)
1684 n = slen;
1685 if (len < 0)
1686 len = 0;
1687 else if (n + len > slen)
1688 len = slen - n;
1689
1690 if (argvars[2].v_type != VAR_UNKNOWN && argvars[3].v_type != VAR_UNKNOWN)
1691 {
1692 int off;
1693
1694 // length in characters
1695 for (off = n; off < slen && len > 0; --len)
1696 off += mb_ptr2len(p + off);
1697 len = off - n;
1698 }
1699
1700 rettv->v_type = VAR_STRING;
1701 rettv->vval.v_string = vim_strnsave(p + n, len);
1702}
1703
1704/*
1705 * "strridx()" function
1706 */
1707 void
1708f_strridx(typval_T *argvars, typval_T *rettv)
1709{
1710 char_u buf[NUMBUFLEN];
1711 char_u *needle;
1712 char_u *haystack;
1713 char_u *rest;
1714 char_u *lastmatch = NULL;
1715 int haystack_len, end_idx;
1716
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02001717 if (in_vim9script()
1718 && (check_for_string_arg(argvars, 0) == FAIL
1719 || check_for_string_arg(argvars, 1) == FAIL
1720 || check_for_opt_number_arg(argvars, 2) == FAIL))
1721 return;
1722
Yegappan Lakshmanana2438132021-07-10 21:29:18 +02001723 needle = tv_get_string_chk(&argvars[1]);
1724 haystack = tv_get_string_buf_chk(&argvars[0], buf);
1725
1726 rettv->vval.v_number = -1;
1727 if (needle == NULL || haystack == NULL)
1728 return; // type error; errmsg already given
1729
1730 haystack_len = (int)STRLEN(haystack);
1731 if (argvars[2].v_type != VAR_UNKNOWN)
1732 {
1733 // Third argument: upper limit for index
1734 end_idx = (int)tv_get_number_chk(&argvars[2], NULL);
1735 if (end_idx < 0)
1736 return; // can never find a match
1737 }
1738 else
1739 end_idx = haystack_len;
1740
1741 if (*needle == NUL)
1742 {
1743 // Empty string matches past the end.
1744 lastmatch = haystack + end_idx;
1745 }
1746 else
1747 {
1748 for (rest = haystack; *rest != '\0'; ++rest)
1749 {
1750 rest = (char_u *)strstr((char *)rest, (char *)needle);
1751 if (rest == NULL || rest > haystack + end_idx)
1752 break;
1753 lastmatch = rest;
1754 }
1755 }
1756
1757 if (lastmatch == NULL)
1758 rettv->vval.v_number = -1;
1759 else
1760 rettv->vval.v_number = (varnumber_T)(lastmatch - haystack);
1761}
1762
1763/*
1764 * "strtrans()" function
1765 */
1766 void
1767f_strtrans(typval_T *argvars, typval_T *rettv)
1768{
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02001769 if (in_vim9script() && check_for_string_arg(argvars, 0) == FAIL)
1770 return;
1771
Yegappan Lakshmanana2438132021-07-10 21:29:18 +02001772 rettv->v_type = VAR_STRING;
1773 rettv->vval.v_string = transstr(tv_get_string(&argvars[0]));
1774}
1775
Christian Brabandt67672ef2023-04-24 21:09:54 +01001776
1777/*
Christian Brabandt67672ef2023-04-24 21:09:54 +01001778 * "utf16idx()" function
Yegappan Lakshmanan95707032023-06-14 13:10:15 +01001779 *
1780 * Converts a byte or character offset in a string to the corresponding UTF-16
1781 * code unit offset.
Christian Brabandt67672ef2023-04-24 21:09:54 +01001782 */
1783 void
1784f_utf16idx(typval_T *argvars, typval_T *rettv)
1785{
1786 rettv->vval.v_number = -1;
1787
1788 if (check_for_string_arg(argvars, 0) == FAIL
1789 || check_for_opt_number_arg(argvars, 1) == FAIL
1790 || check_for_opt_bool_arg(argvars, 2) == FAIL
1791 || (argvars[2].v_type != VAR_UNKNOWN
1792 && check_for_opt_bool_arg(argvars, 3) == FAIL))
1793 return;
1794
1795 char_u *str = tv_get_string_chk(&argvars[0]);
1796 varnumber_T idx = tv_get_number_chk(&argvars[1], NULL);
1797 if (str == NULL || idx < 0)
1798 return;
1799
1800 varnumber_T countcc = FALSE;
1801 varnumber_T charidx = FALSE;
1802 if (argvars[2].v_type != VAR_UNKNOWN)
1803 {
1804 countcc = tv_get_bool(&argvars[2]);
1805 if (argvars[3].v_type != VAR_UNKNOWN)
1806 charidx = tv_get_bool(&argvars[3]);
1807 }
1808
1809 int (*ptr2len)(char_u *);
1810 if (enc_utf8 && countcc)
1811 ptr2len = utf_ptr2len;
1812 else
1813 ptr2len = mb_ptr2len;
1814
1815 char_u *p;
1816 int len;
Yegappan Lakshmanan95707032023-06-14 13:10:15 +01001817 int utf16idx = 0;
Christian Brabandt67672ef2023-04-24 21:09:54 +01001818 for (p = str, len = 0; charidx ? idx >= 0 : p <= str + idx; len++)
1819 {
1820 if (*p == NUL)
Yegappan Lakshmanan577922b2023-06-08 17:09:45 +01001821 {
1822 // If the index is exactly the number of bytes or characters in the
1823 // string then return the length of the string in utf-16 code
1824 // units.
1825 if (charidx ? (idx == 0) : (p == (str + idx)))
1826 rettv->vval.v_number = len;
Christian Brabandt67672ef2023-04-24 21:09:54 +01001827 return;
Yegappan Lakshmanan577922b2023-06-08 17:09:45 +01001828 }
Yegappan Lakshmanan95707032023-06-14 13:10:15 +01001829 utf16idx = len;
Christian Brabandt67672ef2023-04-24 21:09:54 +01001830 int clen = ptr2len(p);
1831 int c = (clen > 1) ? utf_ptr2char(p) : *p;
1832 if (c > 0xFFFF)
1833 len++;
1834 p += ptr2len(p);
1835 if (charidx)
1836 idx--;
1837 }
1838
Yegappan Lakshmanan95707032023-06-14 13:10:15 +01001839 rettv->vval.v_number = utf16idx;
Christian Brabandt67672ef2023-04-24 21:09:54 +01001840}
1841
Yegappan Lakshmanana2438132021-07-10 21:29:18 +02001842/*
1843 * "tolower(string)" function
1844 */
1845 void
1846f_tolower(typval_T *argvars, typval_T *rettv)
1847{
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02001848 if (in_vim9script() && check_for_string_arg(argvars, 0) == FAIL)
1849 return;
1850
Yegappan Lakshmanana2438132021-07-10 21:29:18 +02001851 rettv->v_type = VAR_STRING;
1852 rettv->vval.v_string = strlow_save(tv_get_string(&argvars[0]));
1853}
1854
1855/*
1856 * "toupper(string)" function
1857 */
1858 void
1859f_toupper(typval_T *argvars, typval_T *rettv)
1860{
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02001861 if (in_vim9script() && check_for_string_arg(argvars, 0) == FAIL)
1862 return;
1863
Yegappan Lakshmanana2438132021-07-10 21:29:18 +02001864 rettv->v_type = VAR_STRING;
1865 rettv->vval.v_string = strup_save(tv_get_string(&argvars[0]));
1866}
1867
1868/*
1869 * "tr(string, fromstr, tostr)" function
1870 */
1871 void
1872f_tr(typval_T *argvars, typval_T *rettv)
1873{
1874 char_u *in_str;
1875 char_u *fromstr;
1876 char_u *tostr;
1877 char_u *p;
1878 int inlen;
1879 int fromlen;
1880 int tolen;
1881 int idx;
1882 char_u *cpstr;
1883 int cplen;
1884 int first = TRUE;
1885 char_u buf[NUMBUFLEN];
1886 char_u buf2[NUMBUFLEN];
1887 garray_T ga;
1888
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02001889 if (in_vim9script()
1890 && (check_for_string_arg(argvars, 0) == FAIL
1891 || check_for_string_arg(argvars, 1) == FAIL
1892 || check_for_string_arg(argvars, 2) == FAIL))
1893 return;
1894
Yegappan Lakshmanana2438132021-07-10 21:29:18 +02001895 in_str = tv_get_string(&argvars[0]);
1896 fromstr = tv_get_string_buf_chk(&argvars[1], buf);
1897 tostr = tv_get_string_buf_chk(&argvars[2], buf2);
1898
1899 // Default return value: empty string.
1900 rettv->v_type = VAR_STRING;
1901 rettv->vval.v_string = NULL;
1902 if (fromstr == NULL || tostr == NULL)
1903 return; // type error; errmsg already given
Bram Moolenaar04935fb2022-01-08 16:19:22 +00001904 ga_init2(&ga, sizeof(char), 80);
Yegappan Lakshmanana2438132021-07-10 21:29:18 +02001905
1906 if (!has_mbyte)
1907 // not multi-byte: fromstr and tostr must be the same length
1908 if (STRLEN(fromstr) != STRLEN(tostr))
1909 {
1910error:
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00001911 semsg(_(e_invalid_argument_str), fromstr);
Yegappan Lakshmanana2438132021-07-10 21:29:18 +02001912 ga_clear(&ga);
1913 return;
1914 }
1915
1916 // fromstr and tostr have to contain the same number of chars
1917 while (*in_str != NUL)
1918 {
1919 if (has_mbyte)
1920 {
1921 inlen = (*mb_ptr2len)(in_str);
1922 cpstr = in_str;
1923 cplen = inlen;
1924 idx = 0;
1925 for (p = fromstr; *p != NUL; p += fromlen)
1926 {
1927 fromlen = (*mb_ptr2len)(p);
1928 if (fromlen == inlen && STRNCMP(in_str, p, inlen) == 0)
1929 {
1930 for (p = tostr; *p != NUL; p += tolen)
1931 {
1932 tolen = (*mb_ptr2len)(p);
1933 if (idx-- == 0)
1934 {
1935 cplen = tolen;
1936 cpstr = p;
1937 break;
1938 }
1939 }
1940 if (*p == NUL) // tostr is shorter than fromstr
1941 goto error;
1942 break;
1943 }
1944 ++idx;
1945 }
1946
1947 if (first && cpstr == in_str)
1948 {
1949 // Check that fromstr and tostr have the same number of
1950 // (multi-byte) characters. Done only once when a character
1951 // of in_str doesn't appear in fromstr.
1952 first = FALSE;
1953 for (p = tostr; *p != NUL; p += tolen)
1954 {
1955 tolen = (*mb_ptr2len)(p);
1956 --idx;
1957 }
1958 if (idx != 0)
1959 goto error;
1960 }
1961
1962 (void)ga_grow(&ga, cplen);
1963 mch_memmove((char *)ga.ga_data + ga.ga_len, cpstr, (size_t)cplen);
1964 ga.ga_len += cplen;
1965
1966 in_str += inlen;
1967 }
1968 else
1969 {
1970 // When not using multi-byte chars we can do it faster.
1971 p = vim_strchr(fromstr, *in_str);
1972 if (p != NULL)
1973 ga_append(&ga, tostr[p - fromstr]);
1974 else
1975 ga_append(&ga, *in_str);
1976 ++in_str;
1977 }
1978 }
1979
1980 // add a terminating NUL
1981 (void)ga_grow(&ga, 1);
1982 ga_append(&ga, NUL);
1983
1984 rettv->vval.v_string = ga.ga_data;
1985}
1986
1987/*
1988 * "trim({expr})" function
1989 */
1990 void
1991f_trim(typval_T *argvars, typval_T *rettv)
1992{
1993 char_u buf1[NUMBUFLEN];
1994 char_u buf2[NUMBUFLEN];
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02001995 char_u *head;
Yegappan Lakshmanana2438132021-07-10 21:29:18 +02001996 char_u *mask = NULL;
1997 char_u *tail;
1998 char_u *prev;
1999 char_u *p;
2000 int c1;
2001 int dir = 0;
2002
2003 rettv->v_type = VAR_STRING;
2004 rettv->vval.v_string = NULL;
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02002005
2006 if (in_vim9script()
2007 && (check_for_string_arg(argvars, 0) == FAIL
Illia Bobyr80799172023-10-17 18:00:50 +02002008 || check_for_opt_string_arg(argvars, 1) == FAIL
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02002009 || (argvars[1].v_type != VAR_UNKNOWN
2010 && check_for_opt_number_arg(argvars, 2) == FAIL)))
2011 return;
2012
2013 head = tv_get_string_buf_chk(&argvars[0], buf1);
Yegappan Lakshmanana2438132021-07-10 21:29:18 +02002014 if (head == NULL)
2015 return;
2016
Illia Bobyr80799172023-10-17 18:00:50 +02002017 if (check_for_opt_string_arg(argvars, 1) == FAIL)
Yegappan Lakshmanana2438132021-07-10 21:29:18 +02002018 return;
Yegappan Lakshmanana2438132021-07-10 21:29:18 +02002019
2020 if (argvars[1].v_type == VAR_STRING)
Illia Bobyr6e638672023-10-17 11:09:45 +02002021 {
Illia Bobyr80799172023-10-17 18:00:50 +02002022 mask = tv_get_string_buf_chk(&argvars[1], buf2);
2023 if (*mask == NUL)
2024 mask = NULL;
Yegappan Lakshmanana2438132021-07-10 21:29:18 +02002025
Illia Bobyr80799172023-10-17 18:00:50 +02002026 if (argvars[2].v_type != VAR_UNKNOWN)
Illia Bobyr6e638672023-10-17 11:09:45 +02002027 {
Illia Bobyr80799172023-10-17 18:00:50 +02002028 int error = 0;
2029
2030 // leading or trailing characters to trim
2031 dir = (int)tv_get_number_chk(&argvars[2], &error);
2032 if (error)
2033 return;
2034 if (dir < 0 || dir > 2)
2035 {
2036 semsg(_(e_invalid_argument_str), tv_get_string(&argvars[2]));
2037 return;
2038 }
Yegappan Lakshmanana2438132021-07-10 21:29:18 +02002039 }
2040 }
2041
2042 if (dir == 0 || dir == 1)
2043 {
2044 // Trim leading characters
2045 while (*head != NUL)
2046 {
2047 c1 = PTR2CHAR(head);
2048 if (mask == NULL)
2049 {
2050 if (c1 > ' ' && c1 != 0xa0)
2051 break;
2052 }
2053 else
2054 {
2055 for (p = mask; *p != NUL; MB_PTR_ADV(p))
2056 if (c1 == PTR2CHAR(p))
2057 break;
2058 if (*p == NUL)
2059 break;
2060 }
2061 MB_PTR_ADV(head);
2062 }
2063 }
2064
2065 tail = head + STRLEN(head);
2066 if (dir == 0 || dir == 2)
2067 {
2068 // Trim trailing characters
2069 for (; tail > head; tail = prev)
2070 {
2071 prev = tail;
2072 MB_PTR_BACK(head, prev);
2073 c1 = PTR2CHAR(prev);
2074 if (mask == NULL)
2075 {
2076 if (c1 > ' ' && c1 != 0xa0)
2077 break;
2078 }
2079 else
2080 {
2081 for (p = mask; *p != NUL; MB_PTR_ADV(p))
2082 if (c1 == PTR2CHAR(p))
2083 break;
2084 if (*p == NUL)
2085 break;
2086 }
2087 }
2088 }
2089 rettv->vval.v_string = vim_strnsave(head, tail - head);
2090}
2091
Bram Moolenaar677658a2022-01-05 16:09:06 +00002092static char *e_printf = N_(e_insufficient_arguments_for_printf);
Yegappan Lakshmanan8ee52af2021-08-09 19:59:06 +02002093
2094/*
2095 * Get number argument from "idxp" entry in "tvs". First entry is 1.
2096 */
2097 static varnumber_T
2098tv_nr(typval_T *tvs, int *idxp)
2099{
2100 int idx = *idxp - 1;
2101 varnumber_T n = 0;
2102 int err = FALSE;
2103
2104 if (tvs[idx].v_type == VAR_UNKNOWN)
2105 emsg(_(e_printf));
2106 else
2107 {
2108 ++*idxp;
2109 n = tv_get_number_chk(&tvs[idx], &err);
2110 if (err)
2111 n = 0;
2112 }
2113 return n;
2114}
2115
2116/*
2117 * Get string argument from "idxp" entry in "tvs". First entry is 1.
2118 * If "tofree" is NULL tv_get_string_chk() is used. Some types (e.g. List)
2119 * are not converted to a string.
2120 * If "tofree" is not NULL echo_string() is used. All types are converted to
2121 * a string with the same format as ":echo". The caller must free "*tofree".
2122 * Returns NULL for an error.
2123 */
2124 static char *
2125tv_str(typval_T *tvs, int *idxp, char_u **tofree)
2126{
2127 int idx = *idxp - 1;
2128 char *s = NULL;
2129 static char_u numbuf[NUMBUFLEN];
2130
2131 if (tvs[idx].v_type == VAR_UNKNOWN)
2132 emsg(_(e_printf));
2133 else
2134 {
2135 ++*idxp;
2136 if (tofree != NULL)
2137 s = (char *)echo_string(&tvs[idx], tofree, numbuf, get_copyID());
2138 else
2139 s = (char *)tv_get_string_chk(&tvs[idx]);
2140 }
2141 return s;
2142}
2143
Yegappan Lakshmanan8ee52af2021-08-09 19:59:06 +02002144/*
2145 * Get float argument from "idxp" entry in "tvs". First entry is 1.
2146 */
2147 static double
2148tv_float(typval_T *tvs, int *idxp)
2149{
2150 int idx = *idxp - 1;
2151 double f = 0;
2152
2153 if (tvs[idx].v_type == VAR_UNKNOWN)
2154 emsg(_(e_printf));
2155 else
2156 {
2157 ++*idxp;
2158 if (tvs[idx].v_type == VAR_FLOAT)
2159 f = tvs[idx].vval.v_float;
2160 else if (tvs[idx].v_type == VAR_NUMBER)
2161 f = (double)tvs[idx].vval.v_number;
2162 else
Bram Moolenaar9d00e4a2022-01-05 17:49:15 +00002163 emsg(_(e_expected_float_argument_for_printf));
Yegappan Lakshmanan8ee52af2021-08-09 19:59:06 +02002164 }
2165 return f;
2166}
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +00002167
Yegappan Lakshmanan8ee52af2021-08-09 19:59:06 +02002168#endif
2169
Yegappan Lakshmanan8ee52af2021-08-09 19:59:06 +02002170/*
2171 * Return the representation of infinity for printf() function:
2172 * "-inf", "inf", "+inf", " inf", "-INF", "INF", "+INF" or " INF".
2173 */
2174 static const char *
2175infinity_str(int positive,
2176 char fmt_spec,
2177 int force_sign,
2178 int space_for_positive)
2179{
2180 static const char *table[] =
2181 {
2182 "-inf", "inf", "+inf", " inf",
2183 "-INF", "INF", "+INF", " INF"
2184 };
2185 int idx = positive * (1 + force_sign + force_sign * space_for_positive);
2186
2187 if (ASCII_ISUPPER(fmt_spec))
2188 idx += 4;
2189 return table[idx];
2190}
Yegappan Lakshmanan8ee52af2021-08-09 19:59:06 +02002191
2192/*
2193 * This code was included to provide a portable vsnprintf() and snprintf().
2194 * Some systems may provide their own, but we always use this one for
2195 * consistency.
2196 *
2197 * This code is based on snprintf.c - a portable implementation of snprintf
2198 * by Mark Martinec <mark.martinec@ijs.si>, Version 2.2, 2000-10-06.
2199 * Included with permission. It was heavily modified to fit in Vim.
2200 * The original code, including useful comments, can be found here:
2201 * http://www.ijs.si/software/snprintf/
2202 *
2203 * This snprintf() only supports the following conversion specifiers:
2204 * s, c, d, u, o, x, X, p (and synonyms: i, D, U, O - see below)
2205 * with flags: '-', '+', ' ', '0' and '#'.
2206 * An asterisk is supported for field width as well as precision.
2207 *
2208 * Limited support for floating point was added: 'f', 'F', 'e', 'E', 'g', 'G'.
2209 *
2210 * Length modifiers 'h' (short int) and 'l' (long int) and 'll' (long long int)
2211 * are supported. NOTE: for 'll' the argument is varnumber_T or uvarnumber_T.
2212 *
2213 * The locale is not used, the string is used as a byte string. This is only
2214 * relevant for double-byte encodings where the second byte may be '%'.
2215 *
2216 * It is permitted for "str_m" to be zero, and it is permitted to specify NULL
2217 * pointer for resulting string argument if "str_m" is zero (as per ISO C99).
2218 *
2219 * The return value is the number of characters which would be generated
2220 * for the given input, excluding the trailing NUL. If this value
2221 * is greater or equal to "str_m", not all characters from the result
2222 * have been stored in str, output bytes beyond the ("str_m"-1) -th character
2223 * are discarded. If "str_m" is greater than zero it is guaranteed
2224 * the resulting string will be NUL-terminated.
2225 */
2226
2227/*
2228 * When va_list is not supported we only define vim_snprintf().
2229 *
2230 * vim_vsnprintf_typval() can be invoked with either "va_list" or a list of
2231 * "typval_T". When the latter is not used it must be NULL.
2232 */
2233
2234// When generating prototypes all of this is skipped, cproto doesn't
2235// understand this.
2236#ifndef PROTO
2237
2238// Like vim_vsnprintf() but append to the string.
2239 int
2240vim_snprintf_add(char *str, size_t str_m, const char *fmt, ...)
2241{
2242 va_list ap;
2243 int str_l;
2244 size_t len = STRLEN(str);
2245 size_t space;
2246
2247 if (str_m <= len)
2248 space = 0;
2249 else
2250 space = str_m - len;
2251 va_start(ap, fmt);
2252 str_l = vim_vsnprintf(str + len, space, fmt, ap);
2253 va_end(ap);
2254 return str_l;
2255}
2256
2257 int
2258vim_snprintf(char *str, size_t str_m, const char *fmt, ...)
2259{
2260 va_list ap;
2261 int str_l;
2262
2263 va_start(ap, fmt);
2264 str_l = vim_vsnprintf(str, str_m, fmt, ap);
2265 va_end(ap);
2266 return str_l;
2267}
2268
2269 int
2270vim_vsnprintf(
2271 char *str,
2272 size_t str_m,
2273 const char *fmt,
2274 va_list ap)
2275{
2276 return vim_vsnprintf_typval(str, str_m, fmt, ap, NULL);
2277}
2278
Christ van Willegen0c6181f2023-08-13 18:03:14 +02002279enum
2280{
2281 TYPE_UNKNOWN = -1,
2282 TYPE_INT,
2283 TYPE_LONGINT,
2284 TYPE_LONGLONGINT,
2285 TYPE_UNSIGNEDINT,
2286 TYPE_UNSIGNEDLONGINT,
2287 TYPE_UNSIGNEDLONGLONGINT,
2288 TYPE_POINTER,
2289 TYPE_PERCENT,
2290 TYPE_CHAR,
2291 TYPE_STRING,
2292 TYPE_FLOAT
2293};
2294
2295/* Types that can be used in a format string
2296 */
zeertzjq7772c932023-08-15 22:48:40 +02002297 static int
Christ van Willegen0c6181f2023-08-13 18:03:14 +02002298format_typeof(
Christ van Willegenaa90d4f2023-09-03 17:22:37 +02002299 const char *type)
Christ van Willegen0c6181f2023-08-13 18:03:14 +02002300{
2301 // allowed values: \0, h, l, L
2302 char length_modifier = '\0';
2303
2304 // current conversion specifier character
2305 char fmt_spec = '\0';
2306
2307 // parse 'h', 'l' and 'll' length modifiers
2308 if (*type == 'h' || *type == 'l')
2309 {
2310 length_modifier = *type;
2311 type++;
2312 if (length_modifier == 'l' && *type == 'l')
2313 {
2314 // double l = __int64 / varnumber_T
2315 length_modifier = 'L';
2316 type++;
2317 }
2318 }
2319 fmt_spec = *type;
2320
2321 // common synonyms:
2322 switch (fmt_spec)
2323 {
2324 case 'i': fmt_spec = 'd'; break;
2325 case '*': fmt_spec = 'd'; length_modifier = 'h'; break;
2326 case 'D': fmt_spec = 'd'; length_modifier = 'l'; break;
2327 case 'U': fmt_spec = 'u'; length_modifier = 'l'; break;
2328 case 'O': fmt_spec = 'o'; length_modifier = 'l'; break;
2329 default: break;
2330 }
2331
Christ van Willegen0c6181f2023-08-13 18:03:14 +02002332 // get parameter value, do initial processing
2333 switch (fmt_spec)
2334 {
2335 // '%' and 'c' behave similar to 's' regarding flags and field
2336 // widths
2337 case '%':
2338 return TYPE_PERCENT;
2339
2340 case 'c':
2341 return TYPE_CHAR;
2342
2343 case 's':
2344 case 'S':
2345 return TYPE_STRING;
2346
2347 case 'd': case 'u':
2348 case 'b': case 'B':
2349 case 'o':
2350 case 'x': case 'X':
2351 case 'p':
2352 {
2353 // NOTE: the u, b, o, x, X and p conversion specifiers
2354 // imply the value is unsigned; d implies a signed
2355 // value
2356
2357 // 0 if numeric argument is zero (or if pointer is
2358 // NULL for 'p'), +1 if greater than zero (or nonzero
2359 // for unsigned arguments), -1 if negative (unsigned
2360 // argument is never negative)
2361
2362 if (fmt_spec == 'p')
2363 return TYPE_POINTER;
2364 else if (fmt_spec == 'b' || fmt_spec == 'B')
Christ van Willegenaa90d4f2023-09-03 17:22:37 +02002365 return TYPE_UNSIGNEDLONGLONGINT;
Christ van Willegen0c6181f2023-08-13 18:03:14 +02002366 else if (fmt_spec == 'd')
2367 {
2368 // signed
2369 switch (length_modifier)
2370 {
2371 case '\0':
2372 case 'h':
2373 // char and short arguments are passed as int.
2374 return TYPE_INT;
2375 case 'l':
2376 return TYPE_LONGINT;
2377 case 'L':
2378 return TYPE_LONGLONGINT;
2379 }
2380 }
2381 else
2382 {
2383 // unsigned
2384 switch (length_modifier)
2385 {
2386 case '\0':
2387 case 'h':
2388 return TYPE_UNSIGNEDINT;
2389 case 'l':
2390 return TYPE_UNSIGNEDLONGINT;
2391 case 'L':
2392 return TYPE_UNSIGNEDLONGLONGINT;
2393 }
2394 }
2395 }
2396 break;
2397
2398 case 'f':
2399 case 'F':
2400 case 'e':
2401 case 'E':
2402 case 'g':
2403 case 'G':
2404 return TYPE_FLOAT;
2405 }
2406
2407 return TYPE_UNKNOWN;
2408}
2409
zeertzjq7772c932023-08-15 22:48:40 +02002410 static char *
Christ van Willegen0c6181f2023-08-13 18:03:14 +02002411format_typename(
2412 const char *type)
2413{
Christ van Willegenaa90d4f2023-09-03 17:22:37 +02002414 switch (format_typeof(type))
Christ van Willegen0c6181f2023-08-13 18:03:14 +02002415 {
2416 case TYPE_INT:
2417 return _(typename_int);
2418
2419 case TYPE_LONGINT:
2420 return _(typename_longint);
2421
2422 case TYPE_LONGLONGINT:
2423 return _(typename_longlongint);
2424
2425 case TYPE_UNSIGNEDINT:
2426 return _(typename_unsignedint);
2427
2428 case TYPE_UNSIGNEDLONGINT:
2429 return _(typename_unsignedlongint);
2430
2431 case TYPE_UNSIGNEDLONGLONGINT:
2432 return _(typename_unsignedlonglongint);
2433
2434 case TYPE_POINTER:
2435 return _(typename_pointer);
2436
2437 case TYPE_PERCENT:
2438 return _(typename_percent);
2439
2440 case TYPE_CHAR:
2441 return _(typename_char);
2442
2443 case TYPE_STRING:
2444 return _(typename_string);
2445
2446 case TYPE_FLOAT:
2447 return _(typename_float);
2448 }
2449
2450 return _(typename_unknown);
2451}
2452
zeertzjq7772c932023-08-15 22:48:40 +02002453 static int
Christ van Willegen0c6181f2023-08-13 18:03:14 +02002454adjust_types(
2455 const char ***ap_types,
2456 int arg,
2457 int *num_posarg,
2458 const char *type)
2459{
2460 if (*ap_types == NULL || *num_posarg < arg)
2461 {
2462 int idx;
2463 const char **new_types;
2464
2465 if (*ap_types == NULL)
2466 new_types = ALLOC_CLEAR_MULT(const char *, arg);
2467 else
K.Takata4c215ec2023-08-26 18:05:08 +02002468 new_types = vim_realloc((char **)*ap_types,
2469 arg * sizeof(const char *));
Christ van Willegen0c6181f2023-08-13 18:03:14 +02002470
2471 if (new_types == NULL)
2472 return FAIL;
2473
2474 for (idx = *num_posarg; idx < arg; ++idx)
2475 new_types[idx] = NULL;
2476
2477 *ap_types = new_types;
2478 *num_posarg = arg;
2479 }
2480
2481 if ((*ap_types)[arg - 1] != NULL)
2482 {
2483 if ((*ap_types)[arg - 1][0] == '*' || type[0] == '*')
2484 {
2485 const char *pt = type;
2486 if (pt[0] == '*')
2487 pt = (*ap_types)[arg - 1];
2488
2489 if (pt[0] != '*')
2490 {
2491 switch (pt[0])
2492 {
2493 case 'd': case 'i': break;
2494 default:
2495 semsg(_(e_positional_num_field_spec_reused_str_str), arg, format_typename((*ap_types)[arg - 1]), format_typename(type));
2496 return FAIL;
2497 }
2498 }
2499 }
2500 else
2501 {
Christ van Willegenaa90d4f2023-09-03 17:22:37 +02002502 if (format_typeof(type) != format_typeof((*ap_types)[arg - 1]))
Christ van Willegen0c6181f2023-08-13 18:03:14 +02002503 {
2504 semsg(_( e_positional_arg_num_type_inconsistent_str_str), arg, format_typename(type), format_typename((*ap_types)[arg - 1]));
2505 return FAIL;
2506 }
2507 }
2508 }
2509
2510 (*ap_types)[arg - 1] = type;
2511
2512 return OK;
2513}
2514
Christ van Willegenc35fc032024-03-14 18:30:41 +01002515 static void
2516format_overflow_error(const char *pstart)
2517{
2518 size_t arglen = 0;
2519 char *argcopy = NULL;
2520 const char *p = pstart;
2521
2522 while (VIM_ISDIGIT((int)(*p)))
2523 ++p;
2524
2525 arglen = p - pstart;
2526 argcopy = ALLOC_CLEAR_MULT(char, arglen + 1);
2527 if (argcopy != NULL)
2528 {
2529 strncpy(argcopy, pstart, arglen);
2530 semsg(_( e_val_too_large), argcopy);
2531 free(argcopy);
2532 }
2533 else
2534 semsg(_(e_out_of_memory_allocating_nr_bytes), arglen);
2535}
2536
2537#define MAX_ALLOWED_STRING_WIDTH 6400
2538
2539 static int
2540get_unsigned_int(
2541 const char *pstart,
2542 const char **p,
zeertzjq0dff3152024-07-29 20:28:14 +02002543 unsigned int *uj,
2544 int overflow_err)
Christ van Willegenc35fc032024-03-14 18:30:41 +01002545{
2546 *uj = **p - '0';
2547 ++*p;
2548
2549 while (VIM_ISDIGIT((int)(**p)) && *uj < MAX_ALLOWED_STRING_WIDTH)
2550 {
2551 *uj = 10 * *uj + (unsigned int)(**p - '0');
2552 ++*p;
2553 }
2554
2555 if (*uj > MAX_ALLOWED_STRING_WIDTH)
2556 {
zeertzjq0dff3152024-07-29 20:28:14 +02002557 if (overflow_err)
2558 {
2559 format_overflow_error(pstart);
2560 return FAIL;
2561 }
2562 else
2563 *uj = MAX_ALLOWED_STRING_WIDTH;
Christ van Willegenc35fc032024-03-14 18:30:41 +01002564 }
2565
2566 return OK;
2567}
2568
2569
zeertzjq7772c932023-08-15 22:48:40 +02002570 static int
Christ van Willegen0c6181f2023-08-13 18:03:14 +02002571parse_fmt_types(
2572 const char ***ap_types,
2573 int *num_posarg,
2574 const char *fmt,
2575 typval_T *tvs UNUSED
2576 )
2577{
2578 const char *p = fmt;
2579 const char *arg = NULL;
2580
2581 int any_pos = 0;
2582 int any_arg = 0;
2583 int arg_idx;
2584
2585#define CHECK_POS_ARG do { \
2586 if (any_pos && any_arg) \
2587 { \
2588 semsg(_( e_cannot_mix_positional_and_non_positional_str), fmt); \
2589 goto error; \
2590 } \
2591} while (0);
2592
2593 if (p == NULL)
2594 return OK;
2595
2596 while (*p != NUL)
2597 {
2598 if (*p != '%')
2599 {
2600 char *q = strchr(p + 1, '%');
2601 size_t n = (q == NULL) ? STRLEN(p) : (size_t)(q - p);
2602
2603 p += n;
2604 }
2605 else
2606 {
2607 // allowed values: \0, h, l, L
2608 char length_modifier = '\0';
2609
2610 // variable for positional arg
2611 int pos_arg = -1;
2612 const char *ptype = NULL;
Christ van Willegenc35fc032024-03-14 18:30:41 +01002613 const char *pstart = p+1;
Christ van Willegen0c6181f2023-08-13 18:03:14 +02002614
2615 p++; // skip '%'
2616
2617 // First check to see if we find a positional
2618 // argument specifier
2619 ptype = p;
2620
2621 while (VIM_ISDIGIT(*ptype))
2622 ++ptype;
2623
2624 if (*ptype == '$')
2625 {
2626 if (*p == '0')
2627 {
2628 // 0 flag at the wrong place
2629 semsg(_( e_invalid_format_specifier_str), fmt);
2630 goto error;
2631 }
2632
2633 // Positional argument
Christ van Willegenc35fc032024-03-14 18:30:41 +01002634 unsigned int uj;
Christ van Willegen0c6181f2023-08-13 18:03:14 +02002635
zeertzjq0dff3152024-07-29 20:28:14 +02002636 if (get_unsigned_int(pstart, &p, &uj, tvs != NULL) == FAIL)
Christ van Willegenc35fc032024-03-14 18:30:41 +01002637 goto error;
2638
Christ van Willegen0c6181f2023-08-13 18:03:14 +02002639 pos_arg = uj;
2640
2641 any_pos = 1;
2642 CHECK_POS_ARG;
2643
2644 ++p;
2645 }
2646
2647 // parse flags
2648 while (*p == '0' || *p == '-' || *p == '+' || *p == ' '
2649 || *p == '#' || *p == '\'')
2650 {
2651 switch (*p)
2652 {
2653 case '0': break;
2654 case '-': break;
2655 case '+': break;
2656 case ' ': // If both the ' ' and '+' flags appear, the ' '
2657 // flag should be ignored
2658 break;
2659 case '#': break;
2660 case '\'': break;
2661 }
2662 p++;
2663 }
2664 // If the '0' and '-' flags both appear, the '0' flag should be
2665 // ignored.
2666
2667 // parse field width
2668 if (*(arg = p) == '*')
2669 {
2670 p++;
2671
2672 if (VIM_ISDIGIT((int)(*p)))
2673 {
2674 // Positional argument field width
Christ van Willegenc35fc032024-03-14 18:30:41 +01002675 unsigned int uj;
Christ van Willegen0c6181f2023-08-13 18:03:14 +02002676
zeertzjq0dff3152024-07-29 20:28:14 +02002677 if (get_unsigned_int(arg + 1, &p, &uj, tvs != NULL) == FAIL)
Christ van Willegenc35fc032024-03-14 18:30:41 +01002678 goto error;
Christ van Willegen0c6181f2023-08-13 18:03:14 +02002679
2680 if (*p != '$')
2681 {
2682 semsg(_( e_invalid_format_specifier_str), fmt);
2683 goto error;
2684 }
2685 else
2686 {
2687 ++p;
2688 any_pos = 1;
2689 CHECK_POS_ARG;
2690
2691 if (adjust_types(ap_types, uj, num_posarg, arg) == FAIL)
2692 goto error;
2693 }
2694 }
2695 else
2696 {
2697 any_arg = 1;
2698 CHECK_POS_ARG;
2699 }
2700 }
dundargoc580c1fc2023-10-06 19:41:14 +02002701 else if (VIM_ISDIGIT((int)(*p)))
Christ van Willegen0c6181f2023-08-13 18:03:14 +02002702 {
2703 // size_t could be wider than unsigned int; make sure we treat
2704 // argument like common implementations do
Christ van Willegenc35fc032024-03-14 18:30:41 +01002705 const char *digstart = p;
2706 unsigned int uj;
Christ van Willegen0c6181f2023-08-13 18:03:14 +02002707
zeertzjq0dff3152024-07-29 20:28:14 +02002708 if (get_unsigned_int(digstart, &p, &uj, tvs != NULL) == FAIL)
Christ van Willegenc35fc032024-03-14 18:30:41 +01002709 goto error;
Christ van Willegen0c6181f2023-08-13 18:03:14 +02002710
2711 if (*p == '$')
2712 {
2713 semsg(_( e_invalid_format_specifier_str), fmt);
2714 goto error;
2715 }
2716 }
2717
2718 // parse precision
2719 if (*p == '.')
2720 {
2721 p++;
2722
2723 if (*(arg = p) == '*')
2724 {
2725 p++;
2726
2727 if (VIM_ISDIGIT((int)(*p)))
2728 {
2729 // Parse precision
Christ van Willegenc35fc032024-03-14 18:30:41 +01002730 unsigned int uj;
Christ van Willegen0c6181f2023-08-13 18:03:14 +02002731
zeertzjq0dff3152024-07-29 20:28:14 +02002732 if (get_unsigned_int(arg + 1, &p, &uj, tvs != NULL) == FAIL)
Christ van Willegenc35fc032024-03-14 18:30:41 +01002733 goto error;
Christ van Willegen0c6181f2023-08-13 18:03:14 +02002734
2735 if (*p == '$')
2736 {
2737 any_pos = 1;
2738 CHECK_POS_ARG;
2739
2740 ++p;
2741
2742 if (adjust_types(ap_types, uj, num_posarg, arg) == FAIL)
2743 goto error;
2744 }
2745 else
2746 {
2747 semsg(_( e_invalid_format_specifier_str), fmt);
2748 goto error;
2749 }
2750 }
2751 else
2752 {
2753 any_arg = 1;
2754 CHECK_POS_ARG;
2755 }
2756 }
dundargoc580c1fc2023-10-06 19:41:14 +02002757 else if (VIM_ISDIGIT((int)(*p)))
Christ van Willegen0c6181f2023-08-13 18:03:14 +02002758 {
2759 // size_t could be wider than unsigned int; make sure we
2760 // treat argument like common implementations do
Christ van Willegenc35fc032024-03-14 18:30:41 +01002761 const char *digstart = p;
2762 unsigned int uj;
Christ van Willegen0c6181f2023-08-13 18:03:14 +02002763
zeertzjq0dff3152024-07-29 20:28:14 +02002764 if (get_unsigned_int(digstart, &p, &uj, tvs != NULL) == FAIL)
Christ van Willegenc35fc032024-03-14 18:30:41 +01002765 goto error;
Christ van Willegen0c6181f2023-08-13 18:03:14 +02002766
2767 if (*p == '$')
2768 {
2769 semsg(_( e_invalid_format_specifier_str), fmt);
2770 goto error;
2771 }
2772 }
2773 }
2774
2775 if (pos_arg != -1)
2776 {
2777 any_pos = 1;
2778 CHECK_POS_ARG;
2779
2780 ptype = p;
2781 }
2782
2783 // parse 'h', 'l' and 'll' length modifiers
2784 if (*p == 'h' || *p == 'l')
2785 {
2786 length_modifier = *p;
2787 p++;
2788 if (length_modifier == 'l' && *p == 'l')
2789 {
2790 // double l = __int64 / varnumber_T
dundargoc580c1fc2023-10-06 19:41:14 +02002791 // length_modifier = 'L';
Christ van Willegen0c6181f2023-08-13 18:03:14 +02002792 p++;
2793 }
2794 }
2795
2796 switch (*p)
2797 {
2798 // Check for known format specifiers. % is special!
2799 case 'i':
2800 case '*':
2801 case 'd':
2802 case 'u':
2803 case 'o':
2804 case 'D':
2805 case 'U':
2806 case 'O':
2807 case 'x':
2808 case 'X':
2809 case 'b':
2810 case 'B':
2811 case 'c':
2812 case 's':
2813 case 'S':
2814 case 'p':
2815 case 'f':
2816 case 'F':
2817 case 'e':
2818 case 'E':
2819 case 'g':
2820 case 'G':
2821 if (pos_arg != -1)
2822 {
2823 if (adjust_types(ap_types, pos_arg, num_posarg, ptype) == FAIL)
2824 goto error;
2825 }
2826 else
2827 {
2828 any_arg = 1;
2829 CHECK_POS_ARG;
2830 }
2831 break;
2832
2833 default:
2834 if (pos_arg != -1)
2835 {
2836 semsg(_( e_cannot_mix_positional_and_non_positional_str), fmt);
2837 goto error;
2838 }
2839 }
2840
2841 if (*p != NUL)
2842 p++; // step over the just processed conversion specifier
2843 }
2844 }
2845
2846 for (arg_idx = 0; arg_idx < *num_posarg; ++arg_idx)
2847 {
2848 if ((*ap_types)[arg_idx] == NULL)
2849 {
2850 semsg(_(e_fmt_arg_nr_unused_str), arg_idx + 1, fmt);
2851 goto error;
2852 }
2853
2854# if defined(FEAT_EVAL)
2855 if (tvs != NULL && tvs[arg_idx].v_type == VAR_UNKNOWN)
2856 {
2857 semsg(_(e_positional_nr_out_of_bounds_str), arg_idx + 1, fmt);
2858 goto error;
2859 }
2860# endif
2861 }
2862
2863 return OK;
2864
2865error:
K.Takata4c215ec2023-08-26 18:05:08 +02002866 vim_free((char**)*ap_types);
Christ van Willegen0c6181f2023-08-13 18:03:14 +02002867 *ap_types = NULL;
2868 *num_posarg = 0;
2869 return FAIL;
2870}
2871
zeertzjq7772c932023-08-15 22:48:40 +02002872 static void
Christ van Willegen0c6181f2023-08-13 18:03:14 +02002873skip_to_arg(
2874 const char **ap_types,
2875 va_list ap_start,
2876 va_list *ap,
2877 int *arg_idx,
Christ van Willegenaa90d4f2023-09-03 17:22:37 +02002878 int *arg_cur,
2879 const char *fmt)
Christ van Willegen0c6181f2023-08-13 18:03:14 +02002880{
2881 int arg_min = 0;
2882
2883 if (*arg_cur + 1 == *arg_idx)
2884 {
2885 ++*arg_cur;
2886 ++*arg_idx;
2887 return;
2888 }
2889
2890 if (*arg_cur >= *arg_idx)
2891 {
2892 // Reset ap to ap_start and skip arg_idx - 1 types
2893 va_end(*ap);
2894 va_copy(*ap, ap_start);
2895 }
2896 else
2897 {
2898 // Skip over any we should skip
2899 arg_min = *arg_cur;
2900 }
2901
2902 for (*arg_cur = arg_min; *arg_cur < *arg_idx - 1; ++*arg_cur)
2903 {
Christ van Willegenaa90d4f2023-09-03 17:22:37 +02002904 const char *p;
Christ van Willegen0c6181f2023-08-13 18:03:14 +02002905
Christ van Willegenaa90d4f2023-09-03 17:22:37 +02002906 if (ap_types == NULL || ap_types[*arg_cur] == NULL)
2907 {
Christ van Willegen1bd2cb12023-09-08 19:17:09 +02002908 siemsg(e_aptypes_is_null_nr_str, *arg_cur, fmt);
Christ van Willegenaa90d4f2023-09-03 17:22:37 +02002909 return;
2910 }
2911
2912 p = ap_types[*arg_cur];
2913
2914 int fmt_type = format_typeof(p);
Christ van Willegen0c6181f2023-08-13 18:03:14 +02002915
2916 // get parameter value, do initial processing
2917 switch (fmt_type)
2918 {
2919 case TYPE_PERCENT:
2920 case TYPE_UNKNOWN:
2921 break;
2922
2923 case TYPE_CHAR:
2924 va_arg(*ap, int);
2925 break;
2926
2927 case TYPE_STRING:
2928 va_arg(*ap, char *);
2929 break;
2930
2931 case TYPE_POINTER:
2932 va_arg(*ap, void *);
2933 break;
2934
2935 case TYPE_INT:
2936 va_arg(*ap, int);
2937 break;
2938
2939 case TYPE_LONGINT:
2940 va_arg(*ap, long int);
2941 break;
2942
2943 case TYPE_LONGLONGINT:
2944 va_arg(*ap, varnumber_T);
2945 break;
2946
2947 case TYPE_UNSIGNEDINT:
2948 va_arg(*ap, unsigned int);
2949 break;
2950
2951 case TYPE_UNSIGNEDLONGINT:
2952 va_arg(*ap, unsigned long int);
2953 break;
2954
2955 case TYPE_UNSIGNEDLONGLONGINT:
2956 va_arg(*ap, uvarnumber_T);
2957 break;
2958
2959 case TYPE_FLOAT:
2960 va_arg(*ap, double);
2961 break;
2962 }
2963 }
2964
2965 // Because we know that after we return from this call,
2966 // a va_arg() call is made, we can pre-emptively
2967 // increment the current argument index.
2968 ++*arg_cur;
2969 ++*arg_idx;
2970
2971 return;
2972}
2973
Yegappan Lakshmanan8ee52af2021-08-09 19:59:06 +02002974 int
2975vim_vsnprintf_typval(
2976 char *str,
2977 size_t str_m,
2978 const char *fmt,
Christ van Willegen0c6181f2023-08-13 18:03:14 +02002979 va_list ap_start,
Yegappan Lakshmanan8ee52af2021-08-09 19:59:06 +02002980 typval_T *tvs)
2981{
2982 size_t str_l = 0;
2983 const char *p = fmt;
Christ van Willegen0c6181f2023-08-13 18:03:14 +02002984 int arg_cur = 0;
2985 int num_posarg = 0;
Yegappan Lakshmanan8ee52af2021-08-09 19:59:06 +02002986 int arg_idx = 1;
Christ van Willegen0c6181f2023-08-13 18:03:14 +02002987 va_list ap;
2988 const char **ap_types = NULL;
2989
2990 if (parse_fmt_types(&ap_types, &num_posarg, fmt, tvs) == FAIL)
2991 return 0;
2992
2993 va_copy(ap, ap_start);
Yegappan Lakshmanan8ee52af2021-08-09 19:59:06 +02002994
2995 if (p == NULL)
2996 p = "";
2997 while (*p != NUL)
2998 {
2999 if (*p != '%')
3000 {
3001 char *q = strchr(p + 1, '%');
3002 size_t n = (q == NULL) ? STRLEN(p) : (size_t)(q - p);
3003
3004 // Copy up to the next '%' or NUL without any changes.
3005 if (str_l < str_m)
3006 {
3007 size_t avail = str_m - str_l;
3008
3009 mch_memmove(str + str_l, p, n > avail ? avail : n);
3010 }
3011 p += n;
3012 str_l += n;
3013 }
3014 else
3015 {
3016 size_t min_field_width = 0, precision = 0;
3017 int zero_padding = 0, precision_specified = 0, justify_left = 0;
3018 int alternate_form = 0, force_sign = 0;
3019
3020 // If both the ' ' and '+' flags appear, the ' ' flag should be
3021 // ignored.
3022 int space_for_positive = 1;
3023
3024 // allowed values: \0, h, l, L
3025 char length_modifier = '\0';
3026
3027 // temporary buffer for simple numeric->string conversion
Bram Moolenaar73e28dc2022-09-17 21:08:33 +01003028# define TMP_LEN 350 // On my system 1e308 is the biggest number possible.
Yegappan Lakshmanan8ee52af2021-08-09 19:59:06 +02003029 // That sounds reasonable to use as the maximum
3030 // printable.
Yegappan Lakshmanan8ee52af2021-08-09 19:59:06 +02003031 char tmp[TMP_LEN];
3032
3033 // string address in case of string argument
3034 const char *str_arg = NULL;
3035
3036 // natural field width of arg without padding and sign
3037 size_t str_arg_l;
3038
3039 // unsigned char argument value - only defined for c conversion.
3040 // N.B. standard explicitly states the char argument for the c
3041 // conversion is unsigned
3042 unsigned char uchar_arg;
3043
3044 // number of zeros to be inserted for numeric conversions as
3045 // required by the precision or minimal field width
3046 size_t number_of_zeros_to_pad = 0;
3047
3048 // index into tmp where zero padding is to be inserted
3049 size_t zero_padding_insertion_ind = 0;
3050
3051 // current conversion specifier character
3052 char fmt_spec = '\0';
3053
3054 // buffer for 's' and 'S' specs
3055 char_u *tofree = NULL;
3056
Christ van Willegen0c6181f2023-08-13 18:03:14 +02003057 // variables for positional arg
3058 int pos_arg = -1;
3059 const char *ptype;
3060
Yegappan Lakshmanan8ee52af2021-08-09 19:59:06 +02003061
3062 p++; // skip '%'
3063
Christ van Willegen0c6181f2023-08-13 18:03:14 +02003064 // First check to see if we find a positional
3065 // argument specifier
3066 ptype = p;
3067
3068 while (VIM_ISDIGIT(*ptype))
3069 ++ptype;
3070
3071 if (*ptype == '$')
3072 {
3073 // Positional argument
Christ van Willegenc35fc032024-03-14 18:30:41 +01003074 const char *digstart = p;
3075 unsigned int uj;
Christ van Willegen0c6181f2023-08-13 18:03:14 +02003076
zeertzjq0dff3152024-07-29 20:28:14 +02003077 if (get_unsigned_int(digstart, &p, &uj, tvs != NULL) == FAIL)
Christ van Willegenc35fc032024-03-14 18:30:41 +01003078 goto error;
3079
Christ van Willegen0c6181f2023-08-13 18:03:14 +02003080 pos_arg = uj;
3081
3082 ++p;
3083 }
3084
Yegappan Lakshmanan8ee52af2021-08-09 19:59:06 +02003085 // parse flags
3086 while (*p == '0' || *p == '-' || *p == '+' || *p == ' '
3087 || *p == '#' || *p == '\'')
3088 {
3089 switch (*p)
3090 {
3091 case '0': zero_padding = 1; break;
3092 case '-': justify_left = 1; break;
3093 case '+': force_sign = 1; space_for_positive = 0; break;
3094 case ' ': force_sign = 1;
3095 // If both the ' ' and '+' flags appear, the ' '
3096 // flag should be ignored
3097 break;
3098 case '#': alternate_form = 1; break;
3099 case '\'': break;
3100 }
3101 p++;
3102 }
3103 // If the '0' and '-' flags both appear, the '0' flag should be
3104 // ignored.
3105
3106 // parse field width
3107 if (*p == '*')
3108 {
3109 int j;
Christ van Willegenc35fc032024-03-14 18:30:41 +01003110 const char *digstart = p + 1;
Yegappan Lakshmanan8ee52af2021-08-09 19:59:06 +02003111
3112 p++;
Christ van Willegen0c6181f2023-08-13 18:03:14 +02003113
3114 if (VIM_ISDIGIT((int)(*p)))
3115 {
3116 // Positional argument field width
Christ van Willegenc35fc032024-03-14 18:30:41 +01003117 unsigned int uj;
Christ van Willegen0c6181f2023-08-13 18:03:14 +02003118
zeertzjq0dff3152024-07-29 20:28:14 +02003119 if (get_unsigned_int(digstart, &p, &uj, tvs != NULL) == FAIL)
Christ van Willegenc35fc032024-03-14 18:30:41 +01003120 goto error;
3121
Christ van Willegen0c6181f2023-08-13 18:03:14 +02003122 arg_idx = uj;
3123
3124 ++p;
3125 }
3126
Yegappan Lakshmanan8ee52af2021-08-09 19:59:06 +02003127 j =
3128# if defined(FEAT_EVAL)
3129 tvs != NULL ? tv_nr(tvs, &arg_idx) :
3130# endif
Christ van Willegenaa90d4f2023-09-03 17:22:37 +02003131 (skip_to_arg(ap_types, ap_start, &ap, &arg_idx,
3132 &arg_cur, fmt),
Christ van Willegen0c6181f2023-08-13 18:03:14 +02003133 va_arg(ap, int));
3134
Christ van Willegenc35fc032024-03-14 18:30:41 +01003135 if (j > MAX_ALLOWED_STRING_WIDTH)
3136 {
zeertzjq0dff3152024-07-29 20:28:14 +02003137 if (tvs != NULL)
3138 {
3139 format_overflow_error(digstart);
3140 goto error;
3141 }
3142 else
3143 j = MAX_ALLOWED_STRING_WIDTH;
Christ van Willegenc35fc032024-03-14 18:30:41 +01003144 }
3145
Yegappan Lakshmanan8ee52af2021-08-09 19:59:06 +02003146 if (j >= 0)
3147 min_field_width = j;
3148 else
3149 {
3150 min_field_width = -j;
3151 justify_left = 1;
3152 }
3153 }
3154 else if (VIM_ISDIGIT((int)(*p)))
3155 {
3156 // size_t could be wider than unsigned int; make sure we treat
3157 // argument like common implementations do
Christ van Willegenc35fc032024-03-14 18:30:41 +01003158 const char *digstart = p;
3159 unsigned int uj;
Yegappan Lakshmanan8ee52af2021-08-09 19:59:06 +02003160
zeertzjq0dff3152024-07-29 20:28:14 +02003161 if (get_unsigned_int(digstart, &p, &uj, tvs != NULL) == FAIL)
Christ van Willegenc35fc032024-03-14 18:30:41 +01003162 goto error;
3163
Yegappan Lakshmanan8ee52af2021-08-09 19:59:06 +02003164 min_field_width = uj;
3165 }
3166
3167 // parse precision
3168 if (*p == '.')
3169 {
3170 p++;
3171 precision_specified = 1;
Yegappan Lakshmanan8ee52af2021-08-09 19:59:06 +02003172
Christ van Willegen0c6181f2023-08-13 18:03:14 +02003173 if (VIM_ISDIGIT((int)(*p)))
Yegappan Lakshmanan8ee52af2021-08-09 19:59:06 +02003174 {
3175 // size_t could be wider than unsigned int; make sure we
3176 // treat argument like common implementations do
Christ van Willegenc35fc032024-03-14 18:30:41 +01003177 const char *digstart = p;
3178 unsigned int uj;
Yegappan Lakshmanan8ee52af2021-08-09 19:59:06 +02003179
zeertzjq0dff3152024-07-29 20:28:14 +02003180 if (get_unsigned_int(digstart, &p, &uj, tvs != NULL) == FAIL)
Christ van Willegenc35fc032024-03-14 18:30:41 +01003181 goto error;
3182
Yegappan Lakshmanan8ee52af2021-08-09 19:59:06 +02003183 precision = uj;
3184 }
Christ van Willegen0c6181f2023-08-13 18:03:14 +02003185 else if (*p == '*')
3186 {
3187 int j;
Christ van Willegenc35fc032024-03-14 18:30:41 +01003188 const char *digstart = p;
Christ van Willegen0c6181f2023-08-13 18:03:14 +02003189
3190 p++;
3191
3192 if (VIM_ISDIGIT((int)(*p)))
3193 {
3194 // positional argument
Christ van Willegenc35fc032024-03-14 18:30:41 +01003195 unsigned int uj;
Christ van Willegen0c6181f2023-08-13 18:03:14 +02003196
zeertzjq0dff3152024-07-29 20:28:14 +02003197 if (get_unsigned_int(digstart, &p, &uj, tvs != NULL) == FAIL)
Christ van Willegenc35fc032024-03-14 18:30:41 +01003198 goto error;
3199
Christ van Willegen0c6181f2023-08-13 18:03:14 +02003200 arg_idx = uj;
3201
3202 ++p;
3203 }
3204
3205 j =
3206# if defined(FEAT_EVAL)
3207 tvs != NULL ? tv_nr(tvs, &arg_idx) :
3208# endif
Christ van Willegenaa90d4f2023-09-03 17:22:37 +02003209 (skip_to_arg(ap_types, ap_start, &ap, &arg_idx,
3210 &arg_cur, fmt),
Christ van Willegen0c6181f2023-08-13 18:03:14 +02003211 va_arg(ap, int));
3212
Christ van Willegenc35fc032024-03-14 18:30:41 +01003213 if (j > MAX_ALLOWED_STRING_WIDTH)
3214 {
zeertzjq0dff3152024-07-29 20:28:14 +02003215 if (tvs != NULL)
3216 {
3217 format_overflow_error(digstart);
3218 goto error;
3219 }
3220 else
3221 j = MAX_ALLOWED_STRING_WIDTH;
Christ van Willegenc35fc032024-03-14 18:30:41 +01003222 }
3223
Christ van Willegen0c6181f2023-08-13 18:03:14 +02003224 if (j >= 0)
3225 precision = j;
3226 else
3227 {
3228 precision_specified = 0;
3229 precision = 0;
3230 }
3231 }
Yegappan Lakshmanan8ee52af2021-08-09 19:59:06 +02003232 }
3233
3234 // parse 'h', 'l' and 'll' length modifiers
3235 if (*p == 'h' || *p == 'l')
3236 {
3237 length_modifier = *p;
3238 p++;
3239 if (length_modifier == 'l' && *p == 'l')
3240 {
3241 // double l = __int64 / varnumber_T
3242 length_modifier = 'L';
3243 p++;
3244 }
3245 }
3246 fmt_spec = *p;
3247
3248 // common synonyms:
3249 switch (fmt_spec)
3250 {
3251 case 'i': fmt_spec = 'd'; break;
3252 case 'D': fmt_spec = 'd'; length_modifier = 'l'; break;
3253 case 'U': fmt_spec = 'u'; length_modifier = 'l'; break;
3254 case 'O': fmt_spec = 'o'; length_modifier = 'l'; break;
3255 default: break;
3256 }
3257
3258# if defined(FEAT_EVAL)
3259 switch (fmt_spec)
3260 {
3261 case 'd': case 'u': case 'o': case 'x': case 'X':
3262 if (tvs != NULL && length_modifier == '\0')
3263 length_modifier = 'L';
3264 }
3265# endif
3266
Christ van Willegen0c6181f2023-08-13 18:03:14 +02003267 if (pos_arg != -1)
3268 arg_idx = pos_arg;
3269
Yegappan Lakshmanan8ee52af2021-08-09 19:59:06 +02003270 // get parameter value, do initial processing
3271 switch (fmt_spec)
3272 {
3273 // '%' and 'c' behave similar to 's' regarding flags and field
3274 // widths
3275 case '%':
3276 case 'c':
3277 case 's':
3278 case 'S':
3279 str_arg_l = 1;
3280 switch (fmt_spec)
3281 {
3282 case '%':
3283 str_arg = p;
3284 break;
3285
3286 case 'c':
3287 {
3288 int j;
3289
3290 j =
3291# if defined(FEAT_EVAL)
3292 tvs != NULL ? tv_nr(tvs, &arg_idx) :
3293# endif
Christ van Willegenaa90d4f2023-09-03 17:22:37 +02003294 (skip_to_arg(ap_types, ap_start, &ap, &arg_idx,
3295 &arg_cur, fmt),
Christ van Willegen0c6181f2023-08-13 18:03:14 +02003296 va_arg(ap, int));
3297
Yegappan Lakshmanan8ee52af2021-08-09 19:59:06 +02003298 // standard demands unsigned char
3299 uchar_arg = (unsigned char)j;
3300 str_arg = (char *)&uchar_arg;
3301 break;
3302 }
3303
3304 case 's':
3305 case 'S':
3306 str_arg =
3307# if defined(FEAT_EVAL)
3308 tvs != NULL ? tv_str(tvs, &arg_idx, &tofree) :
3309# endif
Christ van Willegenaa90d4f2023-09-03 17:22:37 +02003310 (skip_to_arg(ap_types, ap_start, &ap, &arg_idx,
3311 &arg_cur, fmt),
Christ van Willegen0c6181f2023-08-13 18:03:14 +02003312 va_arg(ap, char *));
3313
Yegappan Lakshmanan8ee52af2021-08-09 19:59:06 +02003314 if (str_arg == NULL)
3315 {
3316 str_arg = "[NULL]";
3317 str_arg_l = 6;
3318 }
3319 // make sure not to address string beyond the specified
3320 // precision !!!
3321 else if (!precision_specified)
3322 str_arg_l = strlen(str_arg);
3323 // truncate string if necessary as requested by precision
3324 else if (precision == 0)
3325 str_arg_l = 0;
3326 else
3327 {
3328 // Don't put the #if inside memchr(), it can be a
3329 // macro.
3330 // memchr on HP does not like n > 2^31 !!!
3331 char *q = memchr(str_arg, '\0',
3332 precision <= (size_t)0x7fffffffL ? precision
3333 : (size_t)0x7fffffffL);
presukud85fccd2021-11-20 19:38:31 +00003334
Yegappan Lakshmanan8ee52af2021-08-09 19:59:06 +02003335 str_arg_l = (q == NULL) ? precision
3336 : (size_t)(q - str_arg);
3337 }
3338 if (fmt_spec == 'S')
3339 {
presuku1f2453f2021-11-24 15:32:57 +00003340 char_u *p1;
3341 size_t i;
3342 int cell;
presukud85fccd2021-11-20 19:38:31 +00003343
presuku1f2453f2021-11-24 15:32:57 +00003344 for (i = 0, p1 = (char_u *)str_arg; *p1;
Yegappan Lakshmanan8ee52af2021-08-09 19:59:06 +02003345 p1 += mb_ptr2len(p1))
presuku1f2453f2021-11-24 15:32:57 +00003346 {
3347 cell = mb_ptr2cells(p1);
3348 if (precision_specified && i + cell > precision)
3349 break;
3350 i += cell;
Yegappan Lakshmanan8ee52af2021-08-09 19:59:06 +02003351 }
presuku1f2453f2021-11-24 15:32:57 +00003352
3353 str_arg_l = p1 - (char_u *)str_arg;
presukud85fccd2021-11-20 19:38:31 +00003354 if (min_field_width != 0)
presuku1f2453f2021-11-24 15:32:57 +00003355 min_field_width += str_arg_l - i;
Yegappan Lakshmanan8ee52af2021-08-09 19:59:06 +02003356 }
3357 break;
3358
3359 default:
3360 break;
3361 }
3362 break;
3363
3364 case 'd': case 'u':
3365 case 'b': case 'B':
3366 case 'o':
3367 case 'x': case 'X':
3368 case 'p':
3369 {
3370 // NOTE: the u, b, o, x, X and p conversion specifiers
3371 // imply the value is unsigned; d implies a signed
3372 // value
3373
3374 // 0 if numeric argument is zero (or if pointer is
3375 // NULL for 'p'), +1 if greater than zero (or nonzero
3376 // for unsigned arguments), -1 if negative (unsigned
3377 // argument is never negative)
3378 int arg_sign = 0;
3379
3380 // only set for length modifier h, or for no length
3381 // modifiers
3382 int int_arg = 0;
3383 unsigned int uint_arg = 0;
3384
3385 // only set for length modifier l
3386 long int long_arg = 0;
3387 unsigned long int ulong_arg = 0;
3388
3389 // only set for length modifier ll
3390 varnumber_T llong_arg = 0;
3391 uvarnumber_T ullong_arg = 0;
3392
3393 // only set for b conversion
3394 uvarnumber_T bin_arg = 0;
3395
3396 // pointer argument value -only defined for p
3397 // conversion
3398 void *ptr_arg = NULL;
3399
3400 if (fmt_spec == 'p')
3401 {
3402 length_modifier = '\0';
3403 ptr_arg =
3404# if defined(FEAT_EVAL)
3405 tvs != NULL ? (void *)tv_str(tvs, &arg_idx,
3406 NULL) :
3407# endif
Christ van Willegenaa90d4f2023-09-03 17:22:37 +02003408 (skip_to_arg(ap_types, ap_start, &ap, &arg_idx,
3409 &arg_cur, fmt),
Christ van Willegen0c6181f2023-08-13 18:03:14 +02003410 va_arg(ap, void *));
3411
Yegappan Lakshmanan8ee52af2021-08-09 19:59:06 +02003412 if (ptr_arg != NULL)
3413 arg_sign = 1;
3414 }
3415 else if (fmt_spec == 'b' || fmt_spec == 'B')
3416 {
3417 bin_arg =
3418# if defined(FEAT_EVAL)
3419 tvs != NULL ?
3420 (uvarnumber_T)tv_nr(tvs, &arg_idx) :
3421# endif
Christ van Willegenaa90d4f2023-09-03 17:22:37 +02003422 (skip_to_arg(ap_types, ap_start, &ap, &arg_idx,
3423 &arg_cur, fmt),
Christ van Willegen0c6181f2023-08-13 18:03:14 +02003424 va_arg(ap, uvarnumber_T));
3425
Yegappan Lakshmanan8ee52af2021-08-09 19:59:06 +02003426 if (bin_arg != 0)
3427 arg_sign = 1;
3428 }
3429 else if (fmt_spec == 'd')
3430 {
3431 // signed
3432 switch (length_modifier)
3433 {
3434 case '\0':
3435 case 'h':
3436 // char and short arguments are passed as int.
3437 int_arg =
3438# if defined(FEAT_EVAL)
3439 tvs != NULL ? tv_nr(tvs, &arg_idx) :
3440# endif
Christ van Willegenaa90d4f2023-09-03 17:22:37 +02003441 (skip_to_arg(ap_types, ap_start, &ap, &arg_idx,
3442 &arg_cur, fmt),
Christ van Willegen0c6181f2023-08-13 18:03:14 +02003443 va_arg(ap, int));
3444
Yegappan Lakshmanan8ee52af2021-08-09 19:59:06 +02003445 if (int_arg > 0)
3446 arg_sign = 1;
3447 else if (int_arg < 0)
3448 arg_sign = -1;
3449 break;
3450 case 'l':
3451 long_arg =
3452# if defined(FEAT_EVAL)
3453 tvs != NULL ? tv_nr(tvs, &arg_idx) :
3454# endif
Christ van Willegenaa90d4f2023-09-03 17:22:37 +02003455 (skip_to_arg(ap_types, ap_start, &ap, &arg_idx,
3456 &arg_cur, fmt),
Christ van Willegen0c6181f2023-08-13 18:03:14 +02003457 va_arg(ap, long int));
3458
Yegappan Lakshmanan8ee52af2021-08-09 19:59:06 +02003459 if (long_arg > 0)
3460 arg_sign = 1;
3461 else if (long_arg < 0)
3462 arg_sign = -1;
3463 break;
3464 case 'L':
3465 llong_arg =
3466# if defined(FEAT_EVAL)
3467 tvs != NULL ? tv_nr(tvs, &arg_idx) :
3468# endif
Christ van Willegenaa90d4f2023-09-03 17:22:37 +02003469 (skip_to_arg(ap_types, ap_start, &ap, &arg_idx,
3470 &arg_cur, fmt),
Christ van Willegen0c6181f2023-08-13 18:03:14 +02003471 va_arg(ap, varnumber_T));
3472
Yegappan Lakshmanan8ee52af2021-08-09 19:59:06 +02003473 if (llong_arg > 0)
3474 arg_sign = 1;
3475 else if (llong_arg < 0)
3476 arg_sign = -1;
3477 break;
3478 }
3479 }
3480 else
3481 {
3482 // unsigned
3483 switch (length_modifier)
3484 {
3485 case '\0':
3486 case 'h':
3487 uint_arg =
3488# if defined(FEAT_EVAL)
3489 tvs != NULL ? (unsigned)
3490 tv_nr(tvs, &arg_idx) :
3491# endif
Christ van Willegenaa90d4f2023-09-03 17:22:37 +02003492 (skip_to_arg(ap_types, ap_start, &ap, &arg_idx,
3493 &arg_cur, fmt),
Christ van Willegen0c6181f2023-08-13 18:03:14 +02003494 va_arg(ap, unsigned int));
3495
Yegappan Lakshmanan8ee52af2021-08-09 19:59:06 +02003496 if (uint_arg != 0)
3497 arg_sign = 1;
3498 break;
3499 case 'l':
3500 ulong_arg =
3501# if defined(FEAT_EVAL)
3502 tvs != NULL ? (unsigned long)
3503 tv_nr(tvs, &arg_idx) :
3504# endif
Christ van Willegenaa90d4f2023-09-03 17:22:37 +02003505 (skip_to_arg(ap_types, ap_start, &ap, &arg_idx,
3506 &arg_cur, fmt),
Christ van Willegen0c6181f2023-08-13 18:03:14 +02003507 va_arg(ap, unsigned long int));
3508
Yegappan Lakshmanan8ee52af2021-08-09 19:59:06 +02003509 if (ulong_arg != 0)
3510 arg_sign = 1;
3511 break;
3512 case 'L':
3513 ullong_arg =
3514# if defined(FEAT_EVAL)
3515 tvs != NULL ? (uvarnumber_T)
3516 tv_nr(tvs, &arg_idx) :
3517# endif
Christ van Willegenaa90d4f2023-09-03 17:22:37 +02003518 (skip_to_arg(ap_types, ap_start, &ap, &arg_idx,
3519 &arg_cur, fmt),
Christ van Willegen0c6181f2023-08-13 18:03:14 +02003520 va_arg(ap, uvarnumber_T));
3521
Yegappan Lakshmanan8ee52af2021-08-09 19:59:06 +02003522 if (ullong_arg != 0)
3523 arg_sign = 1;
3524 break;
3525 }
3526 }
3527
3528 str_arg = tmp;
3529 str_arg_l = 0;
3530
3531 // NOTE:
3532 // For d, i, u, o, x, and X conversions, if precision is
3533 // specified, the '0' flag should be ignored. This is so
3534 // with Solaris 2.6, Digital UNIX 4.0, HPUX 10, Linux,
3535 // FreeBSD, NetBSD; but not with Perl.
3536 if (precision_specified)
3537 zero_padding = 0;
3538 if (fmt_spec == 'd')
3539 {
3540 if (force_sign && arg_sign >= 0)
3541 tmp[str_arg_l++] = space_for_positive ? ' ' : '+';
3542 // leave negative numbers for sprintf to handle, to
3543 // avoid handling tricky cases like (short int)-32768
3544 }
3545 else if (alternate_form)
3546 {
3547 if (arg_sign != 0
3548 && (fmt_spec == 'b' || fmt_spec == 'B'
3549 || fmt_spec == 'x' || fmt_spec == 'X') )
3550 {
3551 tmp[str_arg_l++] = '0';
3552 tmp[str_arg_l++] = fmt_spec;
3553 }
3554 // alternate form should have no effect for p
3555 // conversion, but ...
3556 }
3557
3558 zero_padding_insertion_ind = str_arg_l;
3559 if (!precision_specified)
3560 precision = 1; // default precision is 1
3561 if (precision == 0 && arg_sign == 0)
3562 {
3563 // When zero value is formatted with an explicit
3564 // precision 0, the resulting formatted string is
3565 // empty (d, i, u, b, B, o, x, X, p).
3566 }
3567 else
3568 {
3569 char f[6];
3570 int f_l = 0;
3571
3572 // construct a simple format string for sprintf
3573 f[f_l++] = '%';
3574 if (!length_modifier)
3575 ;
3576 else if (length_modifier == 'L')
3577 {
3578# ifdef MSWIN
3579 f[f_l++] = 'I';
3580 f[f_l++] = '6';
3581 f[f_l++] = '4';
3582# else
3583 f[f_l++] = 'l';
3584 f[f_l++] = 'l';
3585# endif
3586 }
3587 else
3588 f[f_l++] = length_modifier;
3589 f[f_l++] = fmt_spec;
3590 f[f_l++] = '\0';
3591
3592 if (fmt_spec == 'p')
3593 str_arg_l += sprintf(tmp + str_arg_l, f, ptr_arg);
3594 else if (fmt_spec == 'b' || fmt_spec == 'B')
3595 {
3596 char b[8 * sizeof(uvarnumber_T)];
3597 size_t b_l = 0;
3598 uvarnumber_T bn = bin_arg;
3599
3600 do
3601 {
3602 b[sizeof(b) - ++b_l] = '0' + (bn & 0x1);
3603 bn >>= 1;
3604 }
3605 while (bn != 0);
3606
3607 memcpy(tmp + str_arg_l, b + sizeof(b) - b_l, b_l);
3608 str_arg_l += b_l;
3609 }
3610 else if (fmt_spec == 'd')
3611 {
3612 // signed
3613 switch (length_modifier)
3614 {
3615 case '\0': str_arg_l += sprintf(
3616 tmp + str_arg_l, f,
3617 int_arg);
3618 break;
3619 case 'h': str_arg_l += sprintf(
3620 tmp + str_arg_l, f,
3621 (short)int_arg);
3622 break;
3623 case 'l': str_arg_l += sprintf(
3624 tmp + str_arg_l, f, long_arg);
3625 break;
3626 case 'L': str_arg_l += sprintf(
3627 tmp + str_arg_l, f, llong_arg);
3628 break;
3629 }
3630 }
3631 else
3632 {
3633 // unsigned
3634 switch (length_modifier)
3635 {
3636 case '\0': str_arg_l += sprintf(
3637 tmp + str_arg_l, f,
3638 uint_arg);
3639 break;
3640 case 'h': str_arg_l += sprintf(
3641 tmp + str_arg_l, f,
3642 (unsigned short)uint_arg);
3643 break;
3644 case 'l': str_arg_l += sprintf(
3645 tmp + str_arg_l, f, ulong_arg);
3646 break;
3647 case 'L': str_arg_l += sprintf(
3648 tmp + str_arg_l, f, ullong_arg);
3649 break;
3650 }
3651 }
3652
3653 // include the optional minus sign and possible
3654 // "0x" in the region before the zero padding
3655 // insertion point
3656 if (zero_padding_insertion_ind < str_arg_l
3657 && tmp[zero_padding_insertion_ind] == '-')
3658 zero_padding_insertion_ind++;
3659 if (zero_padding_insertion_ind + 1 < str_arg_l
3660 && tmp[zero_padding_insertion_ind] == '0'
3661 && (tmp[zero_padding_insertion_ind + 1] == 'x'
3662 || tmp[zero_padding_insertion_ind + 1] == 'X'))
3663 zero_padding_insertion_ind += 2;
3664 }
3665
3666 {
3667 size_t num_of_digits = str_arg_l
3668 - zero_padding_insertion_ind;
3669
3670 if (alternate_form && fmt_spec == 'o'
3671 // unless zero is already the first
3672 // character
3673 && !(zero_padding_insertion_ind < str_arg_l
3674 && tmp[zero_padding_insertion_ind] == '0'))
3675 {
3676 // assure leading zero for alternate-form
3677 // octal numbers
3678 if (!precision_specified
3679 || precision < num_of_digits + 1)
3680 {
3681 // precision is increased to force the
3682 // first character to be zero, except if a
3683 // zero value is formatted with an
3684 // explicit precision of zero
3685 precision = num_of_digits + 1;
3686 }
3687 }
3688 // zero padding to specified precision?
3689 if (num_of_digits < precision)
3690 number_of_zeros_to_pad = precision - num_of_digits;
3691 }
3692 // zero padding to specified minimal field width?
3693 if (!justify_left && zero_padding)
3694 {
3695 int n = (int)(min_field_width - (str_arg_l
3696 + number_of_zeros_to_pad));
3697 if (n > 0)
3698 number_of_zeros_to_pad += n;
3699 }
3700 break;
3701 }
3702
Yegappan Lakshmanan8ee52af2021-08-09 19:59:06 +02003703 case 'f':
3704 case 'F':
3705 case 'e':
3706 case 'E':
3707 case 'g':
3708 case 'G':
3709 {
3710 // Floating point.
3711 double f;
3712 double abs_f;
3713 char format[40];
3714 int l;
3715 int remove_trailing_zeroes = FALSE;
3716
3717 f =
Bram Moolenaar73e28dc2022-09-17 21:08:33 +01003718# if defined(FEAT_EVAL)
Yegappan Lakshmanan8ee52af2021-08-09 19:59:06 +02003719 tvs != NULL ? tv_float(tvs, &arg_idx) :
Bram Moolenaar73e28dc2022-09-17 21:08:33 +01003720# endif
Christ van Willegenaa90d4f2023-09-03 17:22:37 +02003721 (skip_to_arg(ap_types, ap_start, &ap, &arg_idx,
3722 &arg_cur, fmt),
Christ van Willegen0c6181f2023-08-13 18:03:14 +02003723 va_arg(ap, double));
3724
Yegappan Lakshmanan8ee52af2021-08-09 19:59:06 +02003725 abs_f = f < 0 ? -f : f;
3726
3727 if (fmt_spec == 'g' || fmt_spec == 'G')
3728 {
3729 // Would be nice to use %g directly, but it prints
3730 // "1.0" as "1", we don't want that.
3731 if ((abs_f >= 0.001 && abs_f < 10000000.0)
3732 || abs_f == 0.0)
3733 fmt_spec = ASCII_ISUPPER(fmt_spec) ? 'F' : 'f';
3734 else
3735 fmt_spec = fmt_spec == 'g' ? 'e' : 'E';
3736 remove_trailing_zeroes = TRUE;
3737 }
3738
3739 if ((fmt_spec == 'f' || fmt_spec == 'F') &&
Bram Moolenaar73e28dc2022-09-17 21:08:33 +01003740# ifdef VAX
Yegappan Lakshmanan8ee52af2021-08-09 19:59:06 +02003741 abs_f > 1.0e38
Bram Moolenaar73e28dc2022-09-17 21:08:33 +01003742# else
Yegappan Lakshmanan8ee52af2021-08-09 19:59:06 +02003743 abs_f > 1.0e307
Bram Moolenaar73e28dc2022-09-17 21:08:33 +01003744# endif
Yegappan Lakshmanan8ee52af2021-08-09 19:59:06 +02003745 )
3746 {
3747 // Avoid a buffer overflow
3748 STRCPY(tmp, infinity_str(f > 0.0, fmt_spec,
3749 force_sign, space_for_positive));
3750 str_arg_l = STRLEN(tmp);
3751 zero_padding = 0;
3752 }
3753 else
3754 {
3755 if (isnan(f))
3756 {
3757 // Not a number: nan or NAN
3758 STRCPY(tmp, ASCII_ISUPPER(fmt_spec) ? "NAN"
3759 : "nan");
3760 str_arg_l = 3;
3761 zero_padding = 0;
3762 }
3763 else if (isinf(f))
3764 {
3765 STRCPY(tmp, infinity_str(f > 0.0, fmt_spec,
3766 force_sign, space_for_positive));
3767 str_arg_l = STRLEN(tmp);
3768 zero_padding = 0;
3769 }
3770 else
3771 {
3772 // Regular float number
3773 format[0] = '%';
3774 l = 1;
3775 if (force_sign)
3776 format[l++] = space_for_positive ? ' ' : '+';
3777 if (precision_specified)
3778 {
3779 size_t max_prec = TMP_LEN - 10;
3780
3781 // Make sure we don't get more digits than we
3782 // have room for.
3783 if ((fmt_spec == 'f' || fmt_spec == 'F')
3784 && abs_f > 1.0)
3785 max_prec -= (size_t)log10(abs_f);
3786 if (precision > max_prec)
3787 precision = max_prec;
3788 l += sprintf(format + l, ".%d", (int)precision);
3789 }
3790 format[l] = fmt_spec == 'F' ? 'f' : fmt_spec;
3791 format[l + 1] = NUL;
3792
3793 str_arg_l = sprintf(tmp, format, f);
3794 }
3795
3796 if (remove_trailing_zeroes)
3797 {
3798 int i;
3799 char *tp;
3800
3801 // Using %g or %G: remove superfluous zeroes.
3802 if (fmt_spec == 'f' || fmt_spec == 'F')
3803 tp = tmp + str_arg_l - 1;
3804 else
3805 {
3806 tp = (char *)vim_strchr((char_u *)tmp,
3807 fmt_spec == 'e' ? 'e' : 'E');
3808 if (tp != NULL)
3809 {
3810 // Remove superfluous '+' and leading
3811 // zeroes from the exponent.
3812 if (tp[1] == '+')
3813 {
3814 // Change "1.0e+07" to "1.0e07"
3815 STRMOVE(tp + 1, tp + 2);
3816 --str_arg_l;
3817 }
3818 i = (tp[1] == '-') ? 2 : 1;
3819 while (tp[i] == '0')
3820 {
3821 // Change "1.0e07" to "1.0e7"
3822 STRMOVE(tp + i, tp + i + 1);
3823 --str_arg_l;
3824 }
3825 --tp;
3826 }
3827 }
3828
3829 if (tp != NULL && !precision_specified)
3830 // Remove trailing zeroes, but keep the one
3831 // just after a dot.
3832 while (tp > tmp + 2 && *tp == '0'
3833 && tp[-1] != '.')
3834 {
3835 STRMOVE(tp, tp + 1);
3836 --tp;
3837 --str_arg_l;
3838 }
3839 }
3840 else
3841 {
3842 char *tp;
3843
3844 // Be consistent: some printf("%e") use 1.0e+12
3845 // and some 1.0e+012. Remove one zero in the last
3846 // case.
3847 tp = (char *)vim_strchr((char_u *)tmp,
3848 fmt_spec == 'e' ? 'e' : 'E');
3849 if (tp != NULL && (tp[1] == '+' || tp[1] == '-')
3850 && tp[2] == '0'
3851 && vim_isdigit(tp[3])
3852 && vim_isdigit(tp[4]))
3853 {
3854 STRMOVE(tp + 2, tp + 3);
3855 --str_arg_l;
3856 }
3857 }
3858 }
3859 if (zero_padding && min_field_width > str_arg_l
3860 && (tmp[0] == '-' || force_sign))
3861 {
3862 // padding 0's should be inserted after the sign
3863 number_of_zeros_to_pad = min_field_width - str_arg_l;
3864 zero_padding_insertion_ind = 1;
3865 }
3866 str_arg = tmp;
3867 break;
3868 }
Yegappan Lakshmanan8ee52af2021-08-09 19:59:06 +02003869
3870 default:
3871 // unrecognized conversion specifier, keep format string
3872 // as-is
3873 zero_padding = 0; // turn zero padding off for non-numeric
3874 // conversion
3875 justify_left = 1;
3876 min_field_width = 0; // reset flags
3877
3878 // discard the unrecognized conversion, just keep *
3879 // the unrecognized conversion character
3880 str_arg = p;
3881 str_arg_l = 0;
3882 if (*p != NUL)
3883 str_arg_l++; // include invalid conversion specifier
3884 // unchanged if not at end-of-string
3885 break;
3886 }
3887
3888 if (*p != NUL)
3889 p++; // step over the just processed conversion specifier
3890
3891 // insert padding to the left as requested by min_field_width;
3892 // this does not include the zero padding in case of numerical
3893 // conversions
3894 if (!justify_left)
3895 {
3896 // left padding with blank or zero
3897 int pn = (int)(min_field_width - (str_arg_l + number_of_zeros_to_pad));
3898
3899 if (pn > 0)
3900 {
3901 if (str_l < str_m)
3902 {
3903 size_t avail = str_m - str_l;
3904
3905 vim_memset(str + str_l, zero_padding ? '0' : ' ',
3906 (size_t)pn > avail ? avail
3907 : (size_t)pn);
3908 }
3909 str_l += pn;
3910 }
3911 }
3912
3913 // zero padding as requested by the precision or by the minimal
3914 // field width for numeric conversions required?
3915 if (number_of_zeros_to_pad == 0)
3916 {
3917 // will not copy first part of numeric right now, *
3918 // force it to be copied later in its entirety
3919 zero_padding_insertion_ind = 0;
3920 }
3921 else
3922 {
3923 // insert first part of numerics (sign or '0x') before zero
3924 // padding
3925 int zn = (int)zero_padding_insertion_ind;
3926
3927 if (zn > 0)
3928 {
3929 if (str_l < str_m)
3930 {
3931 size_t avail = str_m - str_l;
3932
3933 mch_memmove(str + str_l, str_arg,
3934 (size_t)zn > avail ? avail
3935 : (size_t)zn);
3936 }
3937 str_l += zn;
3938 }
3939
3940 // insert zero padding as requested by the precision or min
3941 // field width
3942 zn = (int)number_of_zeros_to_pad;
3943 if (zn > 0)
3944 {
3945 if (str_l < str_m)
3946 {
3947 size_t avail = str_m - str_l;
3948
3949 vim_memset(str + str_l, '0',
3950 (size_t)zn > avail ? avail
3951 : (size_t)zn);
3952 }
3953 str_l += zn;
3954 }
3955 }
3956
3957 // insert formatted string
3958 // (or as-is conversion specifier for unknown conversions)
3959 {
3960 int sn = (int)(str_arg_l - zero_padding_insertion_ind);
3961
3962 if (sn > 0)
3963 {
3964 if (str_l < str_m)
3965 {
3966 size_t avail = str_m - str_l;
3967
3968 mch_memmove(str + str_l,
3969 str_arg + zero_padding_insertion_ind,
3970 (size_t)sn > avail ? avail : (size_t)sn);
3971 }
3972 str_l += sn;
3973 }
3974 }
3975
3976 // insert right padding
3977 if (justify_left)
3978 {
3979 // right blank padding to the field width
3980 int pn = (int)(min_field_width
3981 - (str_arg_l + number_of_zeros_to_pad));
3982
3983 if (pn > 0)
3984 {
3985 if (str_l < str_m)
3986 {
3987 size_t avail = str_m - str_l;
3988
3989 vim_memset(str + str_l, ' ',
3990 (size_t)pn > avail ? avail
3991 : (size_t)pn);
3992 }
3993 str_l += pn;
3994 }
3995 }
3996 vim_free(tofree);
3997 }
3998 }
3999
4000 if (str_m > 0)
4001 {
4002 // make sure the string is nul-terminated even at the expense of
4003 // overwriting the last character (shouldn't happen, but just in case)
4004 //
4005 str[str_l <= str_m - 1 ? str_l : str_m - 1] = '\0';
4006 }
4007
Christ van Willegen0c6181f2023-08-13 18:03:14 +02004008 if (tvs != NULL && tvs[num_posarg != 0 ? num_posarg : arg_idx - 1].v_type != VAR_UNKNOWN)
Bram Moolenaar677658a2022-01-05 16:09:06 +00004009 emsg(_(e_too_many_arguments_to_printf));
Yegappan Lakshmanan8ee52af2021-08-09 19:59:06 +02004010
Christ van Willegenc35fc032024-03-14 18:30:41 +01004011error:
K.Takata4c215ec2023-08-26 18:05:08 +02004012 vim_free((char*)ap_types);
Christ van Willegen0c6181f2023-08-13 18:03:14 +02004013 va_end(ap);
4014
Yegappan Lakshmanan8ee52af2021-08-09 19:59:06 +02004015 // Return the number of characters formatted (excluding trailing nul
4016 // character), that is, the number of characters that would have been
4017 // written to the buffer if it were large enough.
4018 return (int)str_l;
4019}
4020
4021#endif // PROTO