blob: e16b74262b4e1b40e82cb98707367ccb90845268 [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;
154 int l;
155 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 }
272 if (do_special && find_cmdline_var(p, &l) >= 0)
273 {
274 *d++ = '\\'; // insert backslash
275 while (--l >= 0) // copy the var
276 *d++ = *p++;
277 continue;
278 }
Jason Cox6e823512021-08-29 12:36:49 +0200279 if (*p == '\\' && fish_like)
280 {
281 *d++ = '\\';
282 *d++ = *p++;
Bram Moolenaar66315972021-09-01 14:31:51 +0200283 continue;
Jason Cox6e823512021-08-29 12:36:49 +0200284 }
Yegappan Lakshmanana2438132021-07-10 21:29:18 +0200285
286 MB_COPY_CHAR(p, d);
287 }
288
289 // add terminating quote and finish with a NUL
290# ifdef MSWIN
291 if (double_quotes)
292 *d++ = '"';
293 else
294# endif
295 *d++ = '\'';
296 *d = NUL;
297 }
298
299 return escaped_string;
300}
301
302/*
303 * Like vim_strsave(), but make all characters uppercase.
304 * This uses ASCII lower-to-upper case translation, language independent.
305 */
306 char_u *
307vim_strsave_up(char_u *string)
308{
309 char_u *p1;
310
311 p1 = vim_strsave(string);
312 vim_strup(p1);
313 return p1;
314}
315
316/*
317 * Like vim_strnsave(), but make all characters uppercase.
318 * This uses ASCII lower-to-upper case translation, language independent.
319 */
320 char_u *
321vim_strnsave_up(char_u *string, size_t len)
322{
323 char_u *p1;
324
325 p1 = vim_strnsave(string, len);
326 vim_strup(p1);
327 return p1;
328}
329
330/*
331 * ASCII lower-to-upper case translation, language independent.
332 */
333 void
334vim_strup(
335 char_u *p)
336{
337 char_u *p2;
338 int c;
339
Yegappan Lakshmanan6ec66662023-01-23 20:46:21 +0000340 if (p == NULL)
341 return;
342
343 p2 = p;
344 while ((c = *p2) != NUL)
345 *p2++ = (c < 'a' || c > 'z') ? c : (c - 0x20);
Yegappan Lakshmanana2438132021-07-10 21:29:18 +0200346}
347
348#if defined(FEAT_EVAL) || defined(FEAT_SPELL) || defined(PROTO)
349/*
350 * Make string "s" all upper-case and return it in allocated memory.
351 * Handles multi-byte characters as well as possible.
352 * Returns NULL when out of memory.
353 */
354 static char_u *
355strup_save(char_u *orig)
356{
357 char_u *p;
358 char_u *res;
359
360 res = p = vim_strsave(orig);
361
362 if (res != NULL)
363 while (*p != NUL)
364 {
365 int l;
366
367 if (enc_utf8)
368 {
369 int c, uc;
370 int newl;
371 char_u *s;
372
373 c = utf_ptr2char(p);
374 l = utf_ptr2len(p);
375 if (c == 0)
376 {
377 // overlong sequence, use only the first byte
378 c = *p;
379 l = 1;
380 }
381 uc = utf_toupper(c);
382
383 // Reallocate string when byte count changes. This is rare,
384 // thus it's OK to do another malloc()/free().
385 newl = utf_char2len(uc);
386 if (newl != l)
387 {
388 s = alloc(STRLEN(res) + 1 + newl - l);
389 if (s == NULL)
390 {
391 vim_free(res);
392 return NULL;
393 }
394 mch_memmove(s, res, p - res);
395 STRCPY(s + (p - res) + newl, p + l);
396 p = s + (p - res);
397 vim_free(res);
398 res = s;
399 }
400
401 utf_char2bytes(uc, p);
402 p += newl;
403 }
404 else if (has_mbyte && (l = (*mb_ptr2len)(p)) > 1)
405 p += l; // skip multi-byte character
406 else
407 {
408 *p = TOUPPER_LOC(*p); // note that toupper() can be a macro
409 p++;
410 }
411 }
412
413 return res;
414}
415
416/*
417 * Make string "s" all lower-case and return it in allocated memory.
418 * Handles multi-byte characters as well as possible.
419 * Returns NULL when out of memory.
420 */
421 char_u *
422strlow_save(char_u *orig)
423{
424 char_u *p;
425 char_u *res;
426
427 res = p = vim_strsave(orig);
428
429 if (res != NULL)
430 while (*p != NUL)
431 {
432 int l;
433
434 if (enc_utf8)
435 {
436 int c, lc;
437 int newl;
438 char_u *s;
439
440 c = utf_ptr2char(p);
441 l = utf_ptr2len(p);
442 if (c == 0)
443 {
444 // overlong sequence, use only the first byte
445 c = *p;
446 l = 1;
447 }
448 lc = utf_tolower(c);
449
450 // Reallocate string when byte count changes. This is rare,
451 // thus it's OK to do another malloc()/free().
452 newl = utf_char2len(lc);
453 if (newl != l)
454 {
455 s = alloc(STRLEN(res) + 1 + newl - l);
456 if (s == NULL)
457 {
458 vim_free(res);
459 return NULL;
460 }
461 mch_memmove(s, res, p - res);
462 STRCPY(s + (p - res) + newl, p + l);
463 p = s + (p - res);
464 vim_free(res);
465 res = s;
466 }
467
468 utf_char2bytes(lc, p);
469 p += newl;
470 }
471 else if (has_mbyte && (l = (*mb_ptr2len)(p)) > 1)
472 p += l; // skip multi-byte character
473 else
474 {
475 *p = TOLOWER_LOC(*p); // note that tolower() can be a macro
476 p++;
477 }
478 }
479
480 return res;
481}
482#endif
483
484/*
485 * delete spaces at the end of a string
486 */
487 void
488del_trailing_spaces(char_u *ptr)
489{
490 char_u *q;
491
492 q = ptr + STRLEN(ptr);
493 while (--q > ptr && VIM_ISWHITE(q[0]) && q[-1] != '\\' && q[-1] != Ctrl_V)
494 *q = NUL;
495}
496
497/*
498 * Like strncpy(), but always terminate the result with one NUL.
499 * "to" must be "len + 1" long!
500 */
501 void
502vim_strncpy(char_u *to, char_u *from, size_t len)
503{
504 STRNCPY(to, from, len);
505 to[len] = NUL;
506}
507
508/*
509 * Like strcat(), but make sure the result fits in "tosize" bytes and is
510 * always NUL terminated. "from" and "to" may overlap.
511 */
512 void
513vim_strcat(char_u *to, char_u *from, size_t tosize)
514{
515 size_t tolen = STRLEN(to);
516 size_t fromlen = STRLEN(from);
517
518 if (tolen + fromlen + 1 > tosize)
519 {
520 mch_memmove(to + tolen, from, tosize - tolen - 1);
521 to[tosize - 1] = NUL;
522 }
523 else
524 mch_memmove(to + tolen, from, fromlen + 1);
525}
526
Bram Moolenaarc32949b2023-01-04 15:56:51 +0000527/*
528 * A version of strlen() that has a maximum length.
529 */
530 size_t
531vim_strlen_maxlen(char *s, size_t maxlen)
532{
533 size_t i;
534 for (i = 0; i < maxlen; ++i)
535 if (s[i] == NUL)
536 break;
537 return i;
538}
539
Yegappan Lakshmanana2438132021-07-10 21:29:18 +0200540#if (!defined(HAVE_STRCASECMP) && !defined(HAVE_STRICMP)) || defined(PROTO)
541/*
542 * Compare two strings, ignoring case, using current locale.
543 * Doesn't work for multi-byte characters.
544 * return 0 for match, < 0 for smaller, > 0 for bigger
545 */
546 int
547vim_stricmp(char *s1, char *s2)
548{
549 int i;
550
551 for (;;)
552 {
553 i = (int)TOLOWER_LOC(*s1) - (int)TOLOWER_LOC(*s2);
554 if (i != 0)
555 return i; // this character different
556 if (*s1 == NUL)
557 break; // strings match until NUL
558 ++s1;
559 ++s2;
560 }
561 return 0; // strings match
562}
563#endif
564
565#if (!defined(HAVE_STRNCASECMP) && !defined(HAVE_STRNICMP)) || defined(PROTO)
566/*
567 * Compare two strings, for length "len", ignoring case, using current locale.
568 * Doesn't work for multi-byte characters.
569 * return 0 for match, < 0 for smaller, > 0 for bigger
570 */
571 int
572vim_strnicmp(char *s1, char *s2, size_t len)
573{
574 int i;
575
576 while (len > 0)
577 {
578 i = (int)TOLOWER_LOC(*s1) - (int)TOLOWER_LOC(*s2);
579 if (i != 0)
580 return i; // this character different
581 if (*s1 == NUL)
582 break; // strings match until NUL
583 ++s1;
584 ++s2;
585 --len;
586 }
587 return 0; // strings match
588}
589#endif
590
591/*
592 * Search for first occurrence of "c" in "string".
593 * Version of strchr() that handles unsigned char strings with characters from
594 * 128 to 255 correctly. It also doesn't return a pointer to the NUL at the
595 * end of the string.
596 */
Bram Moolenaarc32949b2023-01-04 15:56:51 +0000597 char_u *
Yegappan Lakshmanana2438132021-07-10 21:29:18 +0200598vim_strchr(char_u *string, int c)
599{
600 char_u *p;
601 int b;
602
603 p = string;
604 if (enc_utf8 && c >= 0x80)
605 {
606 while (*p != NUL)
607 {
608 int l = utfc_ptr2len(p);
609
610 // Avoid matching an illegal byte here.
611 if (utf_ptr2char(p) == c && l > 1)
612 return p;
613 p += l;
614 }
615 return NULL;
616 }
617 if (enc_dbcs != 0 && c > 255)
618 {
619 int n2 = c & 0xff;
620
621 c = ((unsigned)c >> 8) & 0xff;
622 while ((b = *p) != NUL)
623 {
624 if (b == c && p[1] == n2)
625 return p;
626 p += (*mb_ptr2len)(p);
627 }
628 return NULL;
629 }
630 if (has_mbyte)
631 {
632 while ((b = *p) != NUL)
633 {
634 if (b == c)
635 return p;
636 p += (*mb_ptr2len)(p);
637 }
638 return NULL;
639 }
640 while ((b = *p) != NUL)
641 {
642 if (b == c)
643 return p;
644 ++p;
645 }
646 return NULL;
647}
648
649/*
650 * Version of strchr() that only works for bytes and handles unsigned char
651 * strings with characters above 128 correctly. It also doesn't return a
652 * pointer to the NUL at the end of the string.
653 */
654 char_u *
655vim_strbyte(char_u *string, int c)
656{
657 char_u *p = string;
658
659 while (*p != NUL)
660 {
661 if (*p == c)
662 return p;
663 ++p;
664 }
665 return NULL;
666}
667
668/*
669 * Search for last occurrence of "c" in "string".
670 * Version of strrchr() that handles unsigned char strings with characters from
671 * 128 to 255 correctly. It also doesn't return a pointer to the NUL at the
672 * end of the string.
673 * Return NULL if not found.
674 * Does not handle multi-byte char for "c"!
675 */
676 char_u *
677vim_strrchr(char_u *string, int c)
678{
679 char_u *retval = NULL;
680 char_u *p = string;
681
682 while (*p)
683 {
684 if (*p == c)
685 retval = p;
686 MB_PTR_ADV(p);
687 }
688 return retval;
689}
690
691/*
692 * Vim's version of strpbrk(), in case it's missing.
693 * Don't generate a prototype for this, causes problems when it's not used.
694 */
695#ifndef PROTO
696# ifndef HAVE_STRPBRK
697# ifdef vim_strpbrk
698# undef vim_strpbrk
699# endif
700 char_u *
701vim_strpbrk(char_u *s, char_u *charset)
702{
703 while (*s)
704 {
705 if (vim_strchr(charset, *s) != NULL)
706 return s;
707 MB_PTR_ADV(s);
708 }
709 return NULL;
710}
711# endif
712#endif
713
714/*
715 * Sort an array of strings.
716 */
717static int sort_compare(const void *s1, const void *s2);
718
719 static int
720sort_compare(const void *s1, const void *s2)
721{
722 return STRCMP(*(char **)s1, *(char **)s2);
723}
724
725 void
726sort_strings(
727 char_u **files,
728 int count)
729{
730 qsort((void *)files, (size_t)count, sizeof(char_u *), sort_compare);
731}
732
733#if defined(FEAT_QUICKFIX) || defined(FEAT_SPELL) || defined(PROTO)
734/*
735 * Return TRUE if string "s" contains a non-ASCII character (128 or higher).
736 * When "s" is NULL FALSE is returned.
737 */
738 int
739has_non_ascii(char_u *s)
740{
741 char_u *p;
742
743 if (s != NULL)
744 for (p = s; *p != NUL; ++p)
745 if (*p >= 128)
746 return TRUE;
747 return FALSE;
748}
749#endif
750
751/*
752 * Concatenate two strings and return the result in allocated memory.
753 * Returns NULL when out of memory.
754 */
755 char_u *
756concat_str(char_u *str1, char_u *str2)
757{
758 char_u *dest;
759 size_t l = str1 == NULL ? 0 : STRLEN(str1);
760
761 dest = alloc(l + (str2 == NULL ? 0 : STRLEN(str2)) + 1L);
Yegappan Lakshmanan6ec66662023-01-23 20:46:21 +0000762 if (dest == NULL)
763 return NULL;
764 if (str1 == NULL)
765 *dest = NUL;
766 else
767 STRCPY(dest, str1);
768 if (str2 != NULL)
769 STRCPY(dest + l, str2);
Yegappan Lakshmanana2438132021-07-10 21:29:18 +0200770 return dest;
771}
772
773#if defined(FEAT_EVAL) || defined(PROTO)
Yegappan Lakshmanana2438132021-07-10 21:29:18 +0200774/*
775 * Return string "str" in ' quotes, doubling ' characters.
776 * If "str" is NULL an empty string is assumed.
777 * If "function" is TRUE make it function('string').
778 */
779 char_u *
780string_quote(char_u *str, int function)
781{
782 unsigned len;
783 char_u *p, *r, *s;
784
785 len = (function ? 13 : 3);
786 if (str != NULL)
787 {
788 len += (unsigned)STRLEN(str);
789 for (p = str; *p != NUL; MB_PTR_ADV(p))
790 if (*p == '\'')
791 ++len;
792 }
793 s = r = alloc(len);
Yegappan Lakshmanan6ec66662023-01-23 20:46:21 +0000794 if (r == NULL)
795 return NULL;
796
797 if (function)
Yegappan Lakshmanana2438132021-07-10 21:29:18 +0200798 {
Yegappan Lakshmanan6ec66662023-01-23 20:46:21 +0000799 STRCPY(r, "function('");
800 r += 10;
Yegappan Lakshmanana2438132021-07-10 21:29:18 +0200801 }
Yegappan Lakshmanan6ec66662023-01-23 20:46:21 +0000802 else
803 *r++ = '\'';
804 if (str != NULL)
805 for (p = str; *p != NUL; )
806 {
807 if (*p == '\'')
808 *r++ = '\'';
809 MB_COPY_CHAR(p, r);
810 }
811 *r++ = '\'';
812 if (function)
813 *r++ = ')';
814 *r++ = NUL;
Yegappan Lakshmanana2438132021-07-10 21:29:18 +0200815 return s;
816}
817
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +0000818/*
819 * Count the number of times "needle" occurs in string "haystack". Case is
820 * ignored if "ic" is TRUE.
821 */
822 long
823string_count(char_u *haystack, char_u *needle, int ic)
824{
825 long n = 0;
826 char_u *p = haystack;
827 char_u *next;
828
829 if (p == NULL || needle == NULL || *needle == NUL)
830 return 0;
831
832 if (ic)
833 {
834 size_t len = STRLEN(needle);
835
836 while (*p != NUL)
837 {
838 if (MB_STRNICMP(p, needle, len) == 0)
839 {
840 ++n;
841 p += len;
842 }
843 else
844 MB_PTR_ADV(p);
845 }
846 }
847 else
848 while ((next = (char_u *)strstr((char *)p, (char *)needle)) != NULL)
849 {
850 ++n;
851 p = next + STRLEN(needle);
852 }
853
854 return n;
855}
856
857/*
Yegappan Lakshmanan03ff1c22023-05-06 14:08:21 +0100858 * Reverse the string in 'str' and set the result in 'rettv'.
859 */
860 void
861string_reverse(char_u *str, typval_T *rettv)
862{
863 rettv->v_type = VAR_STRING;
864 rettv->vval.v_string = NULL;
865 if (str == NULL)
866 return;
867
868 char_u *rstr = vim_strsave(str);
869 rettv->vval.v_string = rstr;
870 if (rstr == NULL || *str == NUL)
871 return;
872
873 size_t len = STRLEN(rstr);
874 if (has_mbyte)
875 {
876 char_u *src = str;
877 char_u *dest = rstr + len;
878
879 while (src < str + len)
880 {
881 int clen = mb_ptr2len(src);
882 dest -= clen;
883 mch_memmove(dest, src, (size_t)clen);
884 src += clen;
885 }
886 }
887 else
888 {
889 for (size_t i = 0; i < len / 2; i++)
890 {
891 char tmp = rstr[len - i - 1];
892 rstr[len - i - 1] = rstr[i];
893 rstr[i] = tmp;
894 }
895 }
896}
897
898/*
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +0000899 * Make a typval_T of the first character of "input" and store it in "output".
900 * Return OK or FAIL.
901 */
902 static int
903copy_first_char_to_tv(char_u *input, typval_T *output)
904{
905 char_u buf[MB_MAXBYTES + 1];
906 int len;
907
908 if (input == NULL || output == NULL)
909 return FAIL;
910
911 len = has_mbyte ? mb_ptr2len(input) : 1;
912 STRNCPY(buf, input, len);
913 buf[len] = NUL;
914 output->v_type = VAR_STRING;
915 output->vval.v_string = vim_strsave(buf);
916
917 return output->vval.v_string == NULL ? FAIL : OK;
918}
919
920/*
921 * Implementation of map() and filter() for a String. Apply "expr" to every
922 * character in string "str" and return the result in "rettv".
923 */
924 void
925string_filter_map(
926 char_u *str,
927 filtermap_T filtermap,
928 typval_T *expr,
929 typval_T *rettv)
930{
931 char_u *p;
932 typval_T tv;
933 garray_T ga;
934 int len = 0;
935 int idx = 0;
936 int rem;
Bram Moolenaar82418262022-09-28 16:16:15 +0100937 typval_T newtv;
938 funccall_T *fc;
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +0000939
940 rettv->v_type = VAR_STRING;
941 rettv->vval.v_string = NULL;
942
943 // set_vim_var_nr() doesn't set the type
944 set_vim_var_type(VV_KEY, VAR_NUMBER);
945
zeertzjqe7d49462023-04-16 20:53:55 +0100946 // Create one funccall_T for all eval_expr_typval() calls.
Bram Moolenaar82418262022-09-28 16:16:15 +0100947 fc = eval_expr_get_funccal(expr, &newtv);
948
Bram Moolenaar04935fb2022-01-08 16:19:22 +0000949 ga_init2(&ga, sizeof(char), 80);
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +0000950 for (p = str; *p != NUL; p += len)
951 {
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +0000952 if (copy_first_char_to_tv(p, &tv) == FAIL)
953 break;
954 len = (int)STRLEN(tv.vval.v_string);
955
Bram Moolenaardd7eff02022-05-06 11:02:05 +0100956 newtv.v_type = VAR_UNKNOWN;
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +0000957 set_vim_var_nr(VV_KEY, idx);
Bram Moolenaar82418262022-09-28 16:16:15 +0100958 if (filter_map_one(&tv, expr, filtermap, fc, &newtv, &rem) == FAIL
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +0000959 || did_emsg)
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +0000960 {
961 clear_tv(&newtv);
962 clear_tv(&tv);
963 break;
964 }
965 else if (filtermap != FILTERMAP_FILTER)
966 {
967 if (newtv.v_type != VAR_STRING)
968 {
969 clear_tv(&newtv);
970 clear_tv(&tv);
Bram Moolenaare70cec92022-01-01 14:25:55 +0000971 emsg(_(e_string_required));
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +0000972 break;
973 }
974 else
975 ga_concat(&ga, newtv.vval.v_string);
976 }
977 else if (!rem)
978 ga_concat(&ga, tv.vval.v_string);
979
980 clear_tv(&newtv);
981 clear_tv(&tv);
982
983 ++idx;
984 }
985 ga_append(&ga, NUL);
986 rettv->vval.v_string = ga.ga_data;
Bram Moolenaar82418262022-09-28 16:16:15 +0100987 if (fc != NULL)
988 remove_funccal();
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +0000989}
990
991/*
Bram Moolenaarf1c60d42022-09-22 17:07:00 +0100992 * Implementation of reduce() for String "argvars[0]" using the function "expr"
993 * starting with the optional initial value "argvars[2]" and return the result
994 * in "rettv".
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +0000995 */
996 void
997string_reduce(
998 typval_T *argvars,
Bram Moolenaarf1c60d42022-09-22 17:07:00 +0100999 typval_T *expr,
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +00001000 typval_T *rettv)
1001{
1002 char_u *p = tv_get_string(&argvars[0]);
1003 int len;
1004 typval_T argv[3];
1005 int r;
1006 int called_emsg_start = called_emsg;
Bram Moolenaar82418262022-09-28 16:16:15 +01001007 funccall_T *fc;
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +00001008
1009 if (argvars[2].v_type == VAR_UNKNOWN)
1010 {
1011 if (*p == NUL)
1012 {
Bram Moolenaare70cec92022-01-01 14:25:55 +00001013 semsg(_(e_reduce_of_an_empty_str_with_no_initial_value), "String");
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +00001014 return;
1015 }
1016 if (copy_first_char_to_tv(p, rettv) == FAIL)
1017 return;
1018 p += STRLEN(rettv->vval.v_string);
1019 }
Yegappan Lakshmanan8deb2b32022-09-02 15:15:27 +01001020 else if (check_for_string_arg(argvars, 2) == FAIL)
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +00001021 return;
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +00001022 else
1023 copy_tv(&argvars[2], rettv);
1024
zeertzjqe7d49462023-04-16 20:53:55 +01001025 // Create one funccall_T for all eval_expr_typval() calls.
Bram Moolenaar82418262022-09-28 16:16:15 +01001026 fc = eval_expr_get_funccal(expr, rettv);
1027
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +00001028 for ( ; *p != NUL; p += len)
1029 {
1030 argv[0] = *rettv;
1031 if (copy_first_char_to_tv(p, &argv[1]) == FAIL)
1032 break;
1033 len = (int)STRLEN(argv[1].vval.v_string);
Bram Moolenaarf1c60d42022-09-22 17:07:00 +01001034
Bram Moolenaar82418262022-09-28 16:16:15 +01001035 r = eval_expr_typval(expr, argv, 2, fc, rettv);
Bram Moolenaarf1c60d42022-09-22 17:07:00 +01001036
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +00001037 clear_tv(&argv[0]);
1038 clear_tv(&argv[1]);
1039 if (r == FAIL || called_emsg != called_emsg_start)
1040 return;
1041 }
Bram Moolenaar82418262022-09-28 16:16:15 +01001042
1043 if (fc != NULL)
1044 remove_funccal();
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +00001045}
1046
Bram Moolenaare4098452023-05-07 18:53:49 +01001047/*
1048 * Implementation of "byteidx()" and "byteidxcomp()" functions
1049 */
Yegappan Lakshmanana2438132021-07-10 21:29:18 +02001050 static void
Bram Moolenaare4098452023-05-07 18:53:49 +01001051byteidx_common(typval_T *argvars, typval_T *rettv, int comp UNUSED)
Yegappan Lakshmanana2438132021-07-10 21:29:18 +02001052{
Yegappan Lakshmanan1a71d312021-07-15 12:49:58 +02001053 rettv->vval.v_number = -1;
1054
1055 if (in_vim9script()
1056 && (check_for_string_arg(argvars, 0) == FAIL
1057 || check_for_number_arg(argvars, 1) == FAIL))
1058 return;
1059
Christian Brabandt67672ef2023-04-24 21:09:54 +01001060 char_u *str = tv_get_string_chk(&argvars[0]);
1061 varnumber_T idx = tv_get_number_chk(&argvars[1], NULL);
Yegappan Lakshmanana2438132021-07-10 21:29:18 +02001062 if (str == NULL || idx < 0)
1063 return;
1064
Christian Brabandt67672ef2023-04-24 21:09:54 +01001065 varnumber_T utf16idx = FALSE;
1066 if (argvars[2].v_type != VAR_UNKNOWN)
1067 {
1068 utf16idx = tv_get_bool(&argvars[2]);
1069 if (utf16idx < 0 || utf16idx > 1)
1070 {
Bram Moolenaare4098452023-05-07 18:53:49 +01001071 if (utf16idx != -1)
1072 semsg(_(e_using_number_as_bool_nr), utf16idx);
Christian Brabandt67672ef2023-04-24 21:09:54 +01001073 return;
1074 }
1075 }
1076
1077 int (*ptr2len)(char_u *);
1078 if (enc_utf8 && comp)
1079 ptr2len = utf_ptr2len;
1080 else
1081 ptr2len = mb_ptr2len;
1082
1083 char_u *t = str;
Yegappan Lakshmanana2438132021-07-10 21:29:18 +02001084 for ( ; idx > 0; idx--)
1085 {
1086 if (*t == NUL) // EOL reached
1087 return;
Christian Brabandt67672ef2023-04-24 21:09:54 +01001088 if (utf16idx)
1089 {
1090 int clen = ptr2len(t);
1091 int c = (clen > 1) ? utf_ptr2char(t) : *t;
1092 if (c > 0xFFFF)
1093 idx--;
1094 }
1095 if (idx > 0)
1096 t += ptr2len(t);
Yegappan Lakshmanana2438132021-07-10 21:29:18 +02001097 }
1098 rettv->vval.v_number = (varnumber_T)(t - str);
1099}
1100
1101/*
1102 * "byteidx()" function
1103 */
1104 void
1105f_byteidx(typval_T *argvars, typval_T *rettv)
1106{
Bram Moolenaare4098452023-05-07 18:53:49 +01001107 byteidx_common(argvars, rettv, FALSE);
Yegappan Lakshmanana2438132021-07-10 21:29:18 +02001108}
1109
1110/*
1111 * "byteidxcomp()" function
1112 */
1113 void
1114f_byteidxcomp(typval_T *argvars, typval_T *rettv)
1115{
Bram Moolenaare4098452023-05-07 18:53:49 +01001116 byteidx_common(argvars, rettv, TRUE);
Yegappan Lakshmanana2438132021-07-10 21:29:18 +02001117}
1118
1119/*
1120 * "charidx()" function
1121 */
1122 void
1123f_charidx(typval_T *argvars, typval_T *rettv)
1124{
Yegappan Lakshmanana2438132021-07-10 21:29:18 +02001125 rettv->vval.v_number = -1;
1126
Christian Brabandt67672ef2023-04-24 21:09:54 +01001127 if (check_for_string_arg(argvars, 0) == FAIL
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02001128 || check_for_number_arg(argvars, 1) == FAIL
Christian Brabandt67672ef2023-04-24 21:09:54 +01001129 || check_for_opt_bool_arg(argvars, 2) == FAIL
1130 || (argvars[2].v_type != VAR_UNKNOWN
1131 && check_for_opt_bool_arg(argvars, 3) == FAIL))
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02001132 return;
1133
Christian Brabandt67672ef2023-04-24 21:09:54 +01001134 char_u *str = tv_get_string_chk(&argvars[0]);
1135 varnumber_T idx = tv_get_number_chk(&argvars[1], NULL);
Yegappan Lakshmanana2438132021-07-10 21:29:18 +02001136 if (str == NULL || idx < 0)
1137 return;
1138
Christian Brabandt67672ef2023-04-24 21:09:54 +01001139 varnumber_T countcc = FALSE;
1140 varnumber_T utf16idx = FALSE;
Yegappan Lakshmanana2438132021-07-10 21:29:18 +02001141 if (argvars[2].v_type != VAR_UNKNOWN)
Yegappan Lakshmanana2438132021-07-10 21:29:18 +02001142 {
Christian Brabandt67672ef2023-04-24 21:09:54 +01001143 countcc = tv_get_bool(&argvars[2]);
1144 if (argvars[3].v_type != VAR_UNKNOWN)
1145 utf16idx = tv_get_bool(&argvars[3]);
Yegappan Lakshmanana2438132021-07-10 21:29:18 +02001146 }
1147
Christian Brabandt67672ef2023-04-24 21:09:54 +01001148 int (*ptr2len)(char_u *);
Yegappan Lakshmanana2438132021-07-10 21:29:18 +02001149 if (enc_utf8 && countcc)
1150 ptr2len = utf_ptr2len;
1151 else
1152 ptr2len = mb_ptr2len;
1153
Christian Brabandt67672ef2023-04-24 21:09:54 +01001154 char_u *p;
1155 int len;
1156 for (p = str, len = 0; utf16idx ? idx >= 0 : p <= str + idx; len++)
Yegappan Lakshmanana2438132021-07-10 21:29:18 +02001157 {
1158 if (*p == NUL)
1159 return;
Christian Brabandt67672ef2023-04-24 21:09:54 +01001160 if (utf16idx)
1161 {
1162 idx--;
1163 int clen = ptr2len(p);
1164 int c = (clen > 1) ? utf_ptr2char(p) : *p;
1165 if (c > 0xFFFF)
1166 idx--;
1167 }
Yegappan Lakshmanana2438132021-07-10 21:29:18 +02001168 p += ptr2len(p);
1169 }
1170
1171 rettv->vval.v_number = len > 0 ? len - 1 : 0;
1172}
1173
1174/*
1175 * "str2list()" function
1176 */
1177 void
1178f_str2list(typval_T *argvars, typval_T *rettv)
1179{
1180 char_u *p;
1181 int utf8 = FALSE;
1182
1183 if (rettv_list_alloc(rettv) == FAIL)
1184 return;
1185
Yegappan Lakshmanana9a7c0c2021-07-17 19:11:07 +02001186 if (in_vim9script()
1187 && (check_for_string_arg(argvars, 0) == FAIL
Yegappan Lakshmanan83494b42021-07-20 17:51:51 +02001188 || check_for_opt_bool_arg(argvars, 1) == FAIL))
Yegappan Lakshmanana9a7c0c2021-07-17 19:11:07 +02001189 return;
1190
Yegappan Lakshmanana2438132021-07-10 21:29:18 +02001191 if (argvars[1].v_type != VAR_UNKNOWN)
1192 utf8 = (int)tv_get_bool_chk(&argvars[1], NULL);
1193
1194 p = tv_get_string(&argvars[0]);
1195
1196 if (has_mbyte || utf8)
1197 {
1198 int (*ptr2len)(char_u *);
1199 int (*ptr2char)(char_u *);
1200
1201 if (utf8 || enc_utf8)
1202 {
1203 ptr2len = utf_ptr2len;
1204 ptr2char = utf_ptr2char;
1205 }
1206 else
1207 {
1208 ptr2len = mb_ptr2len;
1209 ptr2char = mb_ptr2char;
1210 }
1211
1212 for ( ; *p != NUL; p += (*ptr2len)(p))
1213 list_append_number(rettv->vval.v_list, (*ptr2char)(p));
1214 }
1215 else
1216 for ( ; *p != NUL; ++p)
1217 list_append_number(rettv->vval.v_list, *p);
1218}
1219
1220/*
1221 * "str2nr()" function
1222 */
1223 void
1224f_str2nr(typval_T *argvars, typval_T *rettv)
1225{
1226 int base = 10;
1227 char_u *p;
1228 varnumber_T n;
1229 int what = 0;
1230 int isneg;
1231
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02001232 if (in_vim9script()
1233 && (check_for_string_arg(argvars, 0) == FAIL
1234 || check_for_opt_number_arg(argvars, 1) == FAIL
1235 || (argvars[1].v_type != VAR_UNKNOWN
1236 && check_for_opt_bool_arg(argvars, 2) == FAIL)))
1237 return;
1238
Yegappan Lakshmanana2438132021-07-10 21:29:18 +02001239 if (argvars[1].v_type != VAR_UNKNOWN)
1240 {
1241 base = (int)tv_get_number(&argvars[1]);
1242 if (base != 2 && base != 8 && base != 10 && base != 16)
1243 {
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00001244 emsg(_(e_invalid_argument));
Yegappan Lakshmanana2438132021-07-10 21:29:18 +02001245 return;
1246 }
1247 if (argvars[2].v_type != VAR_UNKNOWN && tv_get_bool(&argvars[2]))
1248 what |= STR2NR_QUOTE;
1249 }
1250
1251 p = skipwhite(tv_get_string_strict(&argvars[0]));
1252 isneg = (*p == '-');
1253 if (*p == '+' || *p == '-')
1254 p = skipwhite(p + 1);
1255 switch (base)
1256 {
1257 case 2: what |= STR2NR_BIN + STR2NR_FORCE; break;
1258 case 8: what |= STR2NR_OCT + STR2NR_OOCT + STR2NR_FORCE; break;
1259 case 16: what |= STR2NR_HEX + STR2NR_FORCE; break;
1260 }
Bram Moolenaar5fb78c32023-03-04 20:47:39 +00001261 vim_str2nr(p, NULL, NULL, what, &n, NULL, 0, FALSE, NULL);
Yegappan Lakshmanana2438132021-07-10 21:29:18 +02001262 // Text after the number is silently ignored.
1263 if (isneg)
1264 rettv->vval.v_number = -n;
1265 else
1266 rettv->vval.v_number = n;
1267
1268}
1269
1270/*
1271 * "strgetchar()" function
1272 */
1273 void
1274f_strgetchar(typval_T *argvars, typval_T *rettv)
1275{
1276 char_u *str;
1277 int len;
1278 int error = FALSE;
1279 int charidx;
1280 int byteidx = 0;
1281
1282 rettv->vval.v_number = -1;
Yegappan Lakshmanan1a71d312021-07-15 12:49:58 +02001283
1284 if (in_vim9script()
1285 && (check_for_string_arg(argvars, 0) == FAIL
1286 || check_for_number_arg(argvars, 1) == FAIL))
1287 return;
1288
Yegappan Lakshmanana2438132021-07-10 21:29:18 +02001289 str = tv_get_string_chk(&argvars[0]);
1290 if (str == NULL)
1291 return;
1292 len = (int)STRLEN(str);
1293 charidx = (int)tv_get_number_chk(&argvars[1], &error);
1294 if (error)
1295 return;
1296
1297 while (charidx >= 0 && byteidx < len)
1298 {
1299 if (charidx == 0)
1300 {
1301 rettv->vval.v_number = mb_ptr2char(str + byteidx);
1302 break;
1303 }
1304 --charidx;
1305 byteidx += MB_CPTR2LEN(str + byteidx);
1306 }
1307}
1308
1309/*
1310 * "stridx()" function
1311 */
1312 void
1313f_stridx(typval_T *argvars, typval_T *rettv)
1314{
1315 char_u buf[NUMBUFLEN];
1316 char_u *needle;
1317 char_u *haystack;
1318 char_u *save_haystack;
1319 char_u *pos;
1320 int start_idx;
1321
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02001322 if (in_vim9script()
1323 && (check_for_string_arg(argvars, 0) == FAIL
1324 || check_for_string_arg(argvars, 1) == FAIL
1325 || check_for_opt_number_arg(argvars, 2) == FAIL))
1326 return;
1327
Yegappan Lakshmanana2438132021-07-10 21:29:18 +02001328 needle = tv_get_string_chk(&argvars[1]);
1329 save_haystack = haystack = tv_get_string_buf_chk(&argvars[0], buf);
1330 rettv->vval.v_number = -1;
1331 if (needle == NULL || haystack == NULL)
1332 return; // type error; errmsg already given
1333
1334 if (argvars[2].v_type != VAR_UNKNOWN)
1335 {
1336 int error = FALSE;
1337
1338 start_idx = (int)tv_get_number_chk(&argvars[2], &error);
1339 if (error || start_idx >= (int)STRLEN(haystack))
1340 return;
1341 if (start_idx >= 0)
1342 haystack += start_idx;
1343 }
1344
1345 pos = (char_u *)strstr((char *)haystack, (char *)needle);
1346 if (pos != NULL)
1347 rettv->vval.v_number = (varnumber_T)(pos - save_haystack);
1348}
1349
1350/*
1351 * "string()" function
1352 */
1353 void
1354f_string(typval_T *argvars, typval_T *rettv)
1355{
1356 char_u *tofree;
1357 char_u numbuf[NUMBUFLEN];
1358
1359 rettv->v_type = VAR_STRING;
1360 rettv->vval.v_string = tv2string(&argvars[0], &tofree, numbuf,
1361 get_copyID());
1362 // Make a copy if we have a value but it's not in allocated memory.
1363 if (rettv->vval.v_string != NULL && tofree == NULL)
1364 rettv->vval.v_string = vim_strsave(rettv->vval.v_string);
1365}
1366
1367/*
1368 * "strlen()" function
1369 */
1370 void
1371f_strlen(typval_T *argvars, typval_T *rettv)
1372{
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02001373 if (in_vim9script()
1374 && check_for_string_or_number_arg(argvars, 0) == FAIL)
1375 return;
1376
Yegappan Lakshmanana2438132021-07-10 21:29:18 +02001377 rettv->vval.v_number = (varnumber_T)(STRLEN(
1378 tv_get_string(&argvars[0])));
1379}
1380
1381 static void
1382strchar_common(typval_T *argvars, typval_T *rettv, int skipcc)
1383{
1384 char_u *s = tv_get_string(&argvars[0]);
1385 varnumber_T len = 0;
1386 int (*func_mb_ptr2char_adv)(char_u **pp);
1387
1388 func_mb_ptr2char_adv = skipcc ? mb_ptr2char_adv : mb_cptr2char_adv;
1389 while (*s != NUL)
1390 {
1391 func_mb_ptr2char_adv(&s);
1392 ++len;
1393 }
1394 rettv->vval.v_number = len;
1395}
1396
1397/*
1398 * "strcharlen()" function
1399 */
1400 void
1401f_strcharlen(typval_T *argvars, typval_T *rettv)
1402{
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02001403 if (in_vim9script()
1404 && check_for_string_or_number_arg(argvars, 0) == FAIL)
1405 return;
1406
Yegappan Lakshmanana2438132021-07-10 21:29:18 +02001407 strchar_common(argvars, rettv, TRUE);
1408}
1409
1410/*
1411 * "strchars()" function
1412 */
1413 void
1414f_strchars(typval_T *argvars, typval_T *rettv)
1415{
1416 varnumber_T skipcc = FALSE;
1417
Yegappan Lakshmanana9a7c0c2021-07-17 19:11:07 +02001418 if (in_vim9script()
1419 && (check_for_string_arg(argvars, 0) == FAIL
Yegappan Lakshmanan83494b42021-07-20 17:51:51 +02001420 || check_for_opt_bool_arg(argvars, 1) == FAIL))
Yegappan Lakshmanana9a7c0c2021-07-17 19:11:07 +02001421 return;
1422
Yegappan Lakshmanana2438132021-07-10 21:29:18 +02001423 if (argvars[1].v_type != VAR_UNKNOWN)
1424 skipcc = tv_get_bool(&argvars[1]);
1425 if (skipcc < 0 || skipcc > 1)
Bram Moolenaare4098452023-05-07 18:53:49 +01001426 {
1427 if (skipcc != -1)
1428 semsg(_(e_using_number_as_bool_nr), skipcc);
1429 }
Yegappan Lakshmanana2438132021-07-10 21:29:18 +02001430 else
1431 strchar_common(argvars, rettv, skipcc);
1432}
1433
1434/*
Christian Brabandt67672ef2023-04-24 21:09:54 +01001435 * "strutf16len()" function
1436 */
1437 void
1438f_strutf16len(typval_T *argvars, typval_T *rettv)
1439{
1440 rettv->vval.v_number = -1;
1441
1442 if (check_for_string_arg(argvars, 0) == FAIL
1443 || check_for_opt_bool_arg(argvars, 1) == FAIL)
1444 return;
1445
1446 varnumber_T countcc = FALSE;
1447 if (argvars[1].v_type != VAR_UNKNOWN)
1448 countcc = tv_get_bool(&argvars[1]);
1449
1450 char_u *s = tv_get_string(&argvars[0]);
1451 varnumber_T len = 0;
1452 int (*func_mb_ptr2char_adv)(char_u **pp);
1453 int ch;
1454
1455 func_mb_ptr2char_adv = countcc ? mb_cptr2char_adv : mb_ptr2char_adv;
1456 while (*s != NUL)
1457 {
1458 ch = func_mb_ptr2char_adv(&s);
1459 if (ch > 0xFFFF)
1460 ++len;
1461 ++len;
1462 }
1463 rettv->vval.v_number = len;
1464}
1465
1466/*
Yegappan Lakshmanana2438132021-07-10 21:29:18 +02001467 * "strdisplaywidth()" function
1468 */
1469 void
1470f_strdisplaywidth(typval_T *argvars, typval_T *rettv)
1471{
Yegappan Lakshmanan1a71d312021-07-15 12:49:58 +02001472 char_u *s;
Yegappan Lakshmanana2438132021-07-10 21:29:18 +02001473 int col = 0;
1474
Yegappan Lakshmanan1a71d312021-07-15 12:49:58 +02001475 rettv->vval.v_number = -1;
1476
1477 if (in_vim9script()
1478 && (check_for_string_arg(argvars, 0) == FAIL
Yegappan Lakshmanan83494b42021-07-20 17:51:51 +02001479 || check_for_opt_number_arg(argvars, 1) == FAIL))
Yegappan Lakshmanan1a71d312021-07-15 12:49:58 +02001480 return;
1481
1482 s = tv_get_string(&argvars[0]);
Yegappan Lakshmanana2438132021-07-10 21:29:18 +02001483 if (argvars[1].v_type != VAR_UNKNOWN)
1484 col = (int)tv_get_number(&argvars[1]);
1485
1486 rettv->vval.v_number = (varnumber_T)(linetabsize_col(col, s) - col);
1487}
1488
1489/*
1490 * "strwidth()" function
1491 */
1492 void
1493f_strwidth(typval_T *argvars, typval_T *rettv)
1494{
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02001495 char_u *s;
Yegappan Lakshmanana2438132021-07-10 21:29:18 +02001496
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02001497 if (in_vim9script() && check_for_string_arg(argvars, 0) == FAIL)
1498 return;
1499
1500 s = tv_get_string_strict(&argvars[0]);
Yegappan Lakshmanana2438132021-07-10 21:29:18 +02001501 rettv->vval.v_number = (varnumber_T)(mb_string2cells(s, -1));
1502}
1503
1504/*
1505 * "strcharpart()" function
1506 */
1507 void
1508f_strcharpart(typval_T *argvars, typval_T *rettv)
1509{
1510 char_u *p;
1511 int nchar;
1512 int nbyte = 0;
1513 int charlen;
1514 int skipcc = FALSE;
1515 int len = 0;
1516 int slen;
1517 int error = FALSE;
1518
Yegappan Lakshmanan83494b42021-07-20 17:51:51 +02001519 if (in_vim9script()
1520 && (check_for_string_arg(argvars, 0) == FAIL
1521 || check_for_number_arg(argvars, 1) == FAIL
1522 || check_for_opt_number_arg(argvars, 2) == FAIL
1523 || (argvars[2].v_type != VAR_UNKNOWN
1524 && check_for_opt_bool_arg(argvars, 3) == FAIL)))
1525 return;
1526
Yegappan Lakshmanana2438132021-07-10 21:29:18 +02001527 p = tv_get_string(&argvars[0]);
1528 slen = (int)STRLEN(p);
1529
1530 nchar = (int)tv_get_number_chk(&argvars[1], &error);
1531 if (!error)
1532 {
1533 if (argvars[2].v_type != VAR_UNKNOWN
1534 && argvars[3].v_type != VAR_UNKNOWN)
1535 {
1536 skipcc = tv_get_bool(&argvars[3]);
1537 if (skipcc < 0 || skipcc > 1)
1538 {
Bram Moolenaare4098452023-05-07 18:53:49 +01001539 if (skipcc != -1)
1540 semsg(_(e_using_number_as_bool_nr), skipcc);
Yegappan Lakshmanana2438132021-07-10 21:29:18 +02001541 return;
1542 }
1543 }
1544
1545 if (nchar > 0)
1546 while (nchar > 0 && nbyte < slen)
1547 {
1548 if (skipcc)
1549 nbyte += mb_ptr2len(p + nbyte);
1550 else
1551 nbyte += MB_CPTR2LEN(p + nbyte);
1552 --nchar;
1553 }
1554 else
1555 nbyte = nchar;
1556 if (argvars[2].v_type != VAR_UNKNOWN)
1557 {
1558 charlen = (int)tv_get_number(&argvars[2]);
1559 while (charlen > 0 && nbyte + len < slen)
1560 {
1561 int off = nbyte + len;
1562
1563 if (off < 0)
1564 len += 1;
1565 else
1566 {
1567 if (skipcc)
1568 len += mb_ptr2len(p + off);
1569 else
1570 len += MB_CPTR2LEN(p + off);
1571 }
1572 --charlen;
1573 }
1574 }
1575 else
1576 len = slen - nbyte; // default: all bytes that are available.
1577 }
1578
Yegappan Lakshmanan8ee52af2021-08-09 19:59:06 +02001579 // Only return the overlap between the specified part and the actual
1580 // string.
Yegappan Lakshmanana2438132021-07-10 21:29:18 +02001581 if (nbyte < 0)
1582 {
1583 len += nbyte;
1584 nbyte = 0;
1585 }
1586 else if (nbyte > slen)
1587 nbyte = slen;
1588 if (len < 0)
1589 len = 0;
1590 else if (nbyte + len > slen)
1591 len = slen - nbyte;
1592
1593 rettv->v_type = VAR_STRING;
1594 rettv->vval.v_string = vim_strnsave(p + nbyte, len);
1595}
1596
1597/*
1598 * "strpart()" function
1599 */
1600 void
1601f_strpart(typval_T *argvars, typval_T *rettv)
1602{
1603 char_u *p;
1604 int n;
1605 int len;
1606 int slen;
1607 int error = FALSE;
1608
Yegappan Lakshmanan83494b42021-07-20 17:51:51 +02001609 if (in_vim9script()
1610 && (check_for_string_arg(argvars, 0) == FAIL
1611 || check_for_number_arg(argvars, 1) == FAIL
1612 || check_for_opt_number_arg(argvars, 2) == FAIL
1613 || (argvars[2].v_type != VAR_UNKNOWN
1614 && check_for_opt_bool_arg(argvars, 3) == FAIL)))
1615 return;
1616
Yegappan Lakshmanana2438132021-07-10 21:29:18 +02001617 p = tv_get_string(&argvars[0]);
1618 slen = (int)STRLEN(p);
1619
1620 n = (int)tv_get_number_chk(&argvars[1], &error);
1621 if (error)
1622 len = 0;
1623 else if (argvars[2].v_type != VAR_UNKNOWN)
1624 len = (int)tv_get_number(&argvars[2]);
1625 else
1626 len = slen - n; // default len: all bytes that are available.
1627
1628 // Only return the overlap between the specified part and the actual
1629 // string.
1630 if (n < 0)
1631 {
1632 len += n;
1633 n = 0;
1634 }
1635 else if (n > slen)
1636 n = slen;
1637 if (len < 0)
1638 len = 0;
1639 else if (n + len > slen)
1640 len = slen - n;
1641
1642 if (argvars[2].v_type != VAR_UNKNOWN && argvars[3].v_type != VAR_UNKNOWN)
1643 {
1644 int off;
1645
1646 // length in characters
1647 for (off = n; off < slen && len > 0; --len)
1648 off += mb_ptr2len(p + off);
1649 len = off - n;
1650 }
1651
1652 rettv->v_type = VAR_STRING;
1653 rettv->vval.v_string = vim_strnsave(p + n, len);
1654}
1655
1656/*
1657 * "strridx()" function
1658 */
1659 void
1660f_strridx(typval_T *argvars, typval_T *rettv)
1661{
1662 char_u buf[NUMBUFLEN];
1663 char_u *needle;
1664 char_u *haystack;
1665 char_u *rest;
1666 char_u *lastmatch = NULL;
1667 int haystack_len, end_idx;
1668
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02001669 if (in_vim9script()
1670 && (check_for_string_arg(argvars, 0) == FAIL
1671 || check_for_string_arg(argvars, 1) == FAIL
1672 || check_for_opt_number_arg(argvars, 2) == FAIL))
1673 return;
1674
Yegappan Lakshmanana2438132021-07-10 21:29:18 +02001675 needle = tv_get_string_chk(&argvars[1]);
1676 haystack = tv_get_string_buf_chk(&argvars[0], buf);
1677
1678 rettv->vval.v_number = -1;
1679 if (needle == NULL || haystack == NULL)
1680 return; // type error; errmsg already given
1681
1682 haystack_len = (int)STRLEN(haystack);
1683 if (argvars[2].v_type != VAR_UNKNOWN)
1684 {
1685 // Third argument: upper limit for index
1686 end_idx = (int)tv_get_number_chk(&argvars[2], NULL);
1687 if (end_idx < 0)
1688 return; // can never find a match
1689 }
1690 else
1691 end_idx = haystack_len;
1692
1693 if (*needle == NUL)
1694 {
1695 // Empty string matches past the end.
1696 lastmatch = haystack + end_idx;
1697 }
1698 else
1699 {
1700 for (rest = haystack; *rest != '\0'; ++rest)
1701 {
1702 rest = (char_u *)strstr((char *)rest, (char *)needle);
1703 if (rest == NULL || rest > haystack + end_idx)
1704 break;
1705 lastmatch = rest;
1706 }
1707 }
1708
1709 if (lastmatch == NULL)
1710 rettv->vval.v_number = -1;
1711 else
1712 rettv->vval.v_number = (varnumber_T)(lastmatch - haystack);
1713}
1714
1715/*
1716 * "strtrans()" function
1717 */
1718 void
1719f_strtrans(typval_T *argvars, typval_T *rettv)
1720{
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02001721 if (in_vim9script() && check_for_string_arg(argvars, 0) == FAIL)
1722 return;
1723
Yegappan Lakshmanana2438132021-07-10 21:29:18 +02001724 rettv->v_type = VAR_STRING;
1725 rettv->vval.v_string = transstr(tv_get_string(&argvars[0]));
1726}
1727
Christian Brabandt67672ef2023-04-24 21:09:54 +01001728
1729/*
1730 *
1731 * "utf16idx()" function
1732 */
1733 void
1734f_utf16idx(typval_T *argvars, typval_T *rettv)
1735{
1736 rettv->vval.v_number = -1;
1737
1738 if (check_for_string_arg(argvars, 0) == FAIL
1739 || check_for_opt_number_arg(argvars, 1) == FAIL
1740 || check_for_opt_bool_arg(argvars, 2) == FAIL
1741 || (argvars[2].v_type != VAR_UNKNOWN
1742 && check_for_opt_bool_arg(argvars, 3) == FAIL))
1743 return;
1744
1745 char_u *str = tv_get_string_chk(&argvars[0]);
1746 varnumber_T idx = tv_get_number_chk(&argvars[1], NULL);
1747 if (str == NULL || idx < 0)
1748 return;
1749
1750 varnumber_T countcc = FALSE;
1751 varnumber_T charidx = FALSE;
1752 if (argvars[2].v_type != VAR_UNKNOWN)
1753 {
1754 countcc = tv_get_bool(&argvars[2]);
1755 if (argvars[3].v_type != VAR_UNKNOWN)
1756 charidx = tv_get_bool(&argvars[3]);
1757 }
1758
1759 int (*ptr2len)(char_u *);
1760 if (enc_utf8 && countcc)
1761 ptr2len = utf_ptr2len;
1762 else
1763 ptr2len = mb_ptr2len;
1764
1765 char_u *p;
1766 int len;
1767 for (p = str, len = 0; charidx ? idx >= 0 : p <= str + idx; len++)
1768 {
1769 if (*p == NUL)
1770 return;
1771 int clen = ptr2len(p);
1772 int c = (clen > 1) ? utf_ptr2char(p) : *p;
1773 if (c > 0xFFFF)
1774 len++;
1775 p += ptr2len(p);
1776 if (charidx)
1777 idx--;
1778 }
1779
1780 rettv->vval.v_number = len > 0 ? len - 1 : 0;
1781}
1782
Yegappan Lakshmanana2438132021-07-10 21:29:18 +02001783/*
1784 * "tolower(string)" function
1785 */
1786 void
1787f_tolower(typval_T *argvars, typval_T *rettv)
1788{
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02001789 if (in_vim9script() && check_for_string_arg(argvars, 0) == FAIL)
1790 return;
1791
Yegappan Lakshmanana2438132021-07-10 21:29:18 +02001792 rettv->v_type = VAR_STRING;
1793 rettv->vval.v_string = strlow_save(tv_get_string(&argvars[0]));
1794}
1795
1796/*
1797 * "toupper(string)" function
1798 */
1799 void
1800f_toupper(typval_T *argvars, typval_T *rettv)
1801{
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02001802 if (in_vim9script() && check_for_string_arg(argvars, 0) == FAIL)
1803 return;
1804
Yegappan Lakshmanana2438132021-07-10 21:29:18 +02001805 rettv->v_type = VAR_STRING;
1806 rettv->vval.v_string = strup_save(tv_get_string(&argvars[0]));
1807}
1808
1809/*
1810 * "tr(string, fromstr, tostr)" function
1811 */
1812 void
1813f_tr(typval_T *argvars, typval_T *rettv)
1814{
1815 char_u *in_str;
1816 char_u *fromstr;
1817 char_u *tostr;
1818 char_u *p;
1819 int inlen;
1820 int fromlen;
1821 int tolen;
1822 int idx;
1823 char_u *cpstr;
1824 int cplen;
1825 int first = TRUE;
1826 char_u buf[NUMBUFLEN];
1827 char_u buf2[NUMBUFLEN];
1828 garray_T ga;
1829
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02001830 if (in_vim9script()
1831 && (check_for_string_arg(argvars, 0) == FAIL
1832 || check_for_string_arg(argvars, 1) == FAIL
1833 || check_for_string_arg(argvars, 2) == FAIL))
1834 return;
1835
Yegappan Lakshmanana2438132021-07-10 21:29:18 +02001836 in_str = tv_get_string(&argvars[0]);
1837 fromstr = tv_get_string_buf_chk(&argvars[1], buf);
1838 tostr = tv_get_string_buf_chk(&argvars[2], buf2);
1839
1840 // Default return value: empty string.
1841 rettv->v_type = VAR_STRING;
1842 rettv->vval.v_string = NULL;
1843 if (fromstr == NULL || tostr == NULL)
1844 return; // type error; errmsg already given
Bram Moolenaar04935fb2022-01-08 16:19:22 +00001845 ga_init2(&ga, sizeof(char), 80);
Yegappan Lakshmanana2438132021-07-10 21:29:18 +02001846
1847 if (!has_mbyte)
1848 // not multi-byte: fromstr and tostr must be the same length
1849 if (STRLEN(fromstr) != STRLEN(tostr))
1850 {
1851error:
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00001852 semsg(_(e_invalid_argument_str), fromstr);
Yegappan Lakshmanana2438132021-07-10 21:29:18 +02001853 ga_clear(&ga);
1854 return;
1855 }
1856
1857 // fromstr and tostr have to contain the same number of chars
1858 while (*in_str != NUL)
1859 {
1860 if (has_mbyte)
1861 {
1862 inlen = (*mb_ptr2len)(in_str);
1863 cpstr = in_str;
1864 cplen = inlen;
1865 idx = 0;
1866 for (p = fromstr; *p != NUL; p += fromlen)
1867 {
1868 fromlen = (*mb_ptr2len)(p);
1869 if (fromlen == inlen && STRNCMP(in_str, p, inlen) == 0)
1870 {
1871 for (p = tostr; *p != NUL; p += tolen)
1872 {
1873 tolen = (*mb_ptr2len)(p);
1874 if (idx-- == 0)
1875 {
1876 cplen = tolen;
1877 cpstr = p;
1878 break;
1879 }
1880 }
1881 if (*p == NUL) // tostr is shorter than fromstr
1882 goto error;
1883 break;
1884 }
1885 ++idx;
1886 }
1887
1888 if (first && cpstr == in_str)
1889 {
1890 // Check that fromstr and tostr have the same number of
1891 // (multi-byte) characters. Done only once when a character
1892 // of in_str doesn't appear in fromstr.
1893 first = FALSE;
1894 for (p = tostr; *p != NUL; p += tolen)
1895 {
1896 tolen = (*mb_ptr2len)(p);
1897 --idx;
1898 }
1899 if (idx != 0)
1900 goto error;
1901 }
1902
1903 (void)ga_grow(&ga, cplen);
1904 mch_memmove((char *)ga.ga_data + ga.ga_len, cpstr, (size_t)cplen);
1905 ga.ga_len += cplen;
1906
1907 in_str += inlen;
1908 }
1909 else
1910 {
1911 // When not using multi-byte chars we can do it faster.
1912 p = vim_strchr(fromstr, *in_str);
1913 if (p != NULL)
1914 ga_append(&ga, tostr[p - fromstr]);
1915 else
1916 ga_append(&ga, *in_str);
1917 ++in_str;
1918 }
1919 }
1920
1921 // add a terminating NUL
1922 (void)ga_grow(&ga, 1);
1923 ga_append(&ga, NUL);
1924
1925 rettv->vval.v_string = ga.ga_data;
1926}
1927
1928/*
1929 * "trim({expr})" function
1930 */
1931 void
1932f_trim(typval_T *argvars, typval_T *rettv)
1933{
1934 char_u buf1[NUMBUFLEN];
1935 char_u buf2[NUMBUFLEN];
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02001936 char_u *head;
Yegappan Lakshmanana2438132021-07-10 21:29:18 +02001937 char_u *mask = NULL;
1938 char_u *tail;
1939 char_u *prev;
1940 char_u *p;
1941 int c1;
1942 int dir = 0;
1943
1944 rettv->v_type = VAR_STRING;
1945 rettv->vval.v_string = NULL;
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02001946
1947 if (in_vim9script()
1948 && (check_for_string_arg(argvars, 0) == FAIL
1949 || check_for_opt_string_arg(argvars, 1) == FAIL
1950 || (argvars[1].v_type != VAR_UNKNOWN
1951 && check_for_opt_number_arg(argvars, 2) == FAIL)))
1952 return;
1953
1954 head = tv_get_string_buf_chk(&argvars[0], buf1);
Yegappan Lakshmanana2438132021-07-10 21:29:18 +02001955 if (head == NULL)
1956 return;
1957
Yegappan Lakshmanan8deb2b32022-09-02 15:15:27 +01001958 if (check_for_opt_string_arg(argvars, 1) == FAIL)
Yegappan Lakshmanana2438132021-07-10 21:29:18 +02001959 return;
Yegappan Lakshmanana2438132021-07-10 21:29:18 +02001960
1961 if (argvars[1].v_type == VAR_STRING)
1962 {
1963 mask = tv_get_string_buf_chk(&argvars[1], buf2);
1964
1965 if (argvars[2].v_type != VAR_UNKNOWN)
1966 {
1967 int error = 0;
1968
1969 // leading or trailing characters to trim
1970 dir = (int)tv_get_number_chk(&argvars[2], &error);
1971 if (error)
1972 return;
1973 if (dir < 0 || dir > 2)
1974 {
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00001975 semsg(_(e_invalid_argument_str), tv_get_string(&argvars[2]));
Yegappan Lakshmanana2438132021-07-10 21:29:18 +02001976 return;
1977 }
1978 }
1979 }
1980
1981 if (dir == 0 || dir == 1)
1982 {
1983 // Trim leading characters
1984 while (*head != NUL)
1985 {
1986 c1 = PTR2CHAR(head);
1987 if (mask == NULL)
1988 {
1989 if (c1 > ' ' && c1 != 0xa0)
1990 break;
1991 }
1992 else
1993 {
1994 for (p = mask; *p != NUL; MB_PTR_ADV(p))
1995 if (c1 == PTR2CHAR(p))
1996 break;
1997 if (*p == NUL)
1998 break;
1999 }
2000 MB_PTR_ADV(head);
2001 }
2002 }
2003
2004 tail = head + STRLEN(head);
2005 if (dir == 0 || dir == 2)
2006 {
2007 // Trim trailing characters
2008 for (; tail > head; tail = prev)
2009 {
2010 prev = tail;
2011 MB_PTR_BACK(head, prev);
2012 c1 = PTR2CHAR(prev);
2013 if (mask == NULL)
2014 {
2015 if (c1 > ' ' && c1 != 0xa0)
2016 break;
2017 }
2018 else
2019 {
2020 for (p = mask; *p != NUL; MB_PTR_ADV(p))
2021 if (c1 == PTR2CHAR(p))
2022 break;
2023 if (*p == NUL)
2024 break;
2025 }
2026 }
2027 }
2028 rettv->vval.v_string = vim_strnsave(head, tail - head);
2029}
2030
Bram Moolenaar677658a2022-01-05 16:09:06 +00002031static char *e_printf = N_(e_insufficient_arguments_for_printf);
Yegappan Lakshmanan8ee52af2021-08-09 19:59:06 +02002032
2033/*
2034 * Get number argument from "idxp" entry in "tvs". First entry is 1.
2035 */
2036 static varnumber_T
2037tv_nr(typval_T *tvs, int *idxp)
2038{
2039 int idx = *idxp - 1;
2040 varnumber_T n = 0;
2041 int err = FALSE;
2042
2043 if (tvs[idx].v_type == VAR_UNKNOWN)
2044 emsg(_(e_printf));
2045 else
2046 {
2047 ++*idxp;
2048 n = tv_get_number_chk(&tvs[idx], &err);
2049 if (err)
2050 n = 0;
2051 }
2052 return n;
2053}
2054
2055/*
2056 * Get string argument from "idxp" entry in "tvs". First entry is 1.
2057 * If "tofree" is NULL tv_get_string_chk() is used. Some types (e.g. List)
2058 * are not converted to a string.
2059 * If "tofree" is not NULL echo_string() is used. All types are converted to
2060 * a string with the same format as ":echo". The caller must free "*tofree".
2061 * Returns NULL for an error.
2062 */
2063 static char *
2064tv_str(typval_T *tvs, int *idxp, char_u **tofree)
2065{
2066 int idx = *idxp - 1;
2067 char *s = NULL;
2068 static char_u numbuf[NUMBUFLEN];
2069
2070 if (tvs[idx].v_type == VAR_UNKNOWN)
2071 emsg(_(e_printf));
2072 else
2073 {
2074 ++*idxp;
2075 if (tofree != NULL)
2076 s = (char *)echo_string(&tvs[idx], tofree, numbuf, get_copyID());
2077 else
2078 s = (char *)tv_get_string_chk(&tvs[idx]);
2079 }
2080 return s;
2081}
2082
Yegappan Lakshmanan8ee52af2021-08-09 19:59:06 +02002083/*
2084 * Get float argument from "idxp" entry in "tvs". First entry is 1.
2085 */
2086 static double
2087tv_float(typval_T *tvs, int *idxp)
2088{
2089 int idx = *idxp - 1;
2090 double f = 0;
2091
2092 if (tvs[idx].v_type == VAR_UNKNOWN)
2093 emsg(_(e_printf));
2094 else
2095 {
2096 ++*idxp;
2097 if (tvs[idx].v_type == VAR_FLOAT)
2098 f = tvs[idx].vval.v_float;
2099 else if (tvs[idx].v_type == VAR_NUMBER)
2100 f = (double)tvs[idx].vval.v_number;
2101 else
Bram Moolenaar9d00e4a2022-01-05 17:49:15 +00002102 emsg(_(e_expected_float_argument_for_printf));
Yegappan Lakshmanan8ee52af2021-08-09 19:59:06 +02002103 }
2104 return f;
2105}
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +00002106
Yegappan Lakshmanan8ee52af2021-08-09 19:59:06 +02002107#endif
2108
Yegappan Lakshmanan8ee52af2021-08-09 19:59:06 +02002109/*
2110 * Return the representation of infinity for printf() function:
2111 * "-inf", "inf", "+inf", " inf", "-INF", "INF", "+INF" or " INF".
2112 */
2113 static const char *
2114infinity_str(int positive,
2115 char fmt_spec,
2116 int force_sign,
2117 int space_for_positive)
2118{
2119 static const char *table[] =
2120 {
2121 "-inf", "inf", "+inf", " inf",
2122 "-INF", "INF", "+INF", " INF"
2123 };
2124 int idx = positive * (1 + force_sign + force_sign * space_for_positive);
2125
2126 if (ASCII_ISUPPER(fmt_spec))
2127 idx += 4;
2128 return table[idx];
2129}
Yegappan Lakshmanan8ee52af2021-08-09 19:59:06 +02002130
2131/*
2132 * This code was included to provide a portable vsnprintf() and snprintf().
2133 * Some systems may provide their own, but we always use this one for
2134 * consistency.
2135 *
2136 * This code is based on snprintf.c - a portable implementation of snprintf
2137 * by Mark Martinec <mark.martinec@ijs.si>, Version 2.2, 2000-10-06.
2138 * Included with permission. It was heavily modified to fit in Vim.
2139 * The original code, including useful comments, can be found here:
2140 * http://www.ijs.si/software/snprintf/
2141 *
2142 * This snprintf() only supports the following conversion specifiers:
2143 * s, c, d, u, o, x, X, p (and synonyms: i, D, U, O - see below)
2144 * with flags: '-', '+', ' ', '0' and '#'.
2145 * An asterisk is supported for field width as well as precision.
2146 *
2147 * Limited support for floating point was added: 'f', 'F', 'e', 'E', 'g', 'G'.
2148 *
2149 * Length modifiers 'h' (short int) and 'l' (long int) and 'll' (long long int)
2150 * are supported. NOTE: for 'll' the argument is varnumber_T or uvarnumber_T.
2151 *
2152 * The locale is not used, the string is used as a byte string. This is only
2153 * relevant for double-byte encodings where the second byte may be '%'.
2154 *
2155 * It is permitted for "str_m" to be zero, and it is permitted to specify NULL
2156 * pointer for resulting string argument if "str_m" is zero (as per ISO C99).
2157 *
2158 * The return value is the number of characters which would be generated
2159 * for the given input, excluding the trailing NUL. If this value
2160 * is greater or equal to "str_m", not all characters from the result
2161 * have been stored in str, output bytes beyond the ("str_m"-1) -th character
2162 * are discarded. If "str_m" is greater than zero it is guaranteed
2163 * the resulting string will be NUL-terminated.
2164 */
2165
2166/*
2167 * When va_list is not supported we only define vim_snprintf().
2168 *
2169 * vim_vsnprintf_typval() can be invoked with either "va_list" or a list of
2170 * "typval_T". When the latter is not used it must be NULL.
2171 */
2172
2173// When generating prototypes all of this is skipped, cproto doesn't
2174// understand this.
2175#ifndef PROTO
2176
2177// Like vim_vsnprintf() but append to the string.
2178 int
2179vim_snprintf_add(char *str, size_t str_m, const char *fmt, ...)
2180{
2181 va_list ap;
2182 int str_l;
2183 size_t len = STRLEN(str);
2184 size_t space;
2185
2186 if (str_m <= len)
2187 space = 0;
2188 else
2189 space = str_m - len;
2190 va_start(ap, fmt);
2191 str_l = vim_vsnprintf(str + len, space, fmt, ap);
2192 va_end(ap);
2193 return str_l;
2194}
2195
2196 int
2197vim_snprintf(char *str, size_t str_m, const char *fmt, ...)
2198{
2199 va_list ap;
2200 int str_l;
2201
2202 va_start(ap, fmt);
2203 str_l = vim_vsnprintf(str, str_m, fmt, ap);
2204 va_end(ap);
2205 return str_l;
2206}
2207
2208 int
2209vim_vsnprintf(
2210 char *str,
2211 size_t str_m,
2212 const char *fmt,
2213 va_list ap)
2214{
2215 return vim_vsnprintf_typval(str, str_m, fmt, ap, NULL);
2216}
2217
2218 int
2219vim_vsnprintf_typval(
2220 char *str,
2221 size_t str_m,
2222 const char *fmt,
2223 va_list ap,
2224 typval_T *tvs)
2225{
2226 size_t str_l = 0;
2227 const char *p = fmt;
2228 int arg_idx = 1;
2229
2230 if (p == NULL)
2231 p = "";
2232 while (*p != NUL)
2233 {
2234 if (*p != '%')
2235 {
2236 char *q = strchr(p + 1, '%');
2237 size_t n = (q == NULL) ? STRLEN(p) : (size_t)(q - p);
2238
2239 // Copy up to the next '%' or NUL without any changes.
2240 if (str_l < str_m)
2241 {
2242 size_t avail = str_m - str_l;
2243
2244 mch_memmove(str + str_l, p, n > avail ? avail : n);
2245 }
2246 p += n;
2247 str_l += n;
2248 }
2249 else
2250 {
2251 size_t min_field_width = 0, precision = 0;
2252 int zero_padding = 0, precision_specified = 0, justify_left = 0;
2253 int alternate_form = 0, force_sign = 0;
2254
2255 // If both the ' ' and '+' flags appear, the ' ' flag should be
2256 // ignored.
2257 int space_for_positive = 1;
2258
2259 // allowed values: \0, h, l, L
2260 char length_modifier = '\0';
2261
2262 // temporary buffer for simple numeric->string conversion
Bram Moolenaar73e28dc2022-09-17 21:08:33 +01002263# define TMP_LEN 350 // On my system 1e308 is the biggest number possible.
Yegappan Lakshmanan8ee52af2021-08-09 19:59:06 +02002264 // That sounds reasonable to use as the maximum
2265 // printable.
Yegappan Lakshmanan8ee52af2021-08-09 19:59:06 +02002266 char tmp[TMP_LEN];
2267
2268 // string address in case of string argument
2269 const char *str_arg = NULL;
2270
2271 // natural field width of arg without padding and sign
2272 size_t str_arg_l;
2273
2274 // unsigned char argument value - only defined for c conversion.
2275 // N.B. standard explicitly states the char argument for the c
2276 // conversion is unsigned
2277 unsigned char uchar_arg;
2278
2279 // number of zeros to be inserted for numeric conversions as
2280 // required by the precision or minimal field width
2281 size_t number_of_zeros_to_pad = 0;
2282
2283 // index into tmp where zero padding is to be inserted
2284 size_t zero_padding_insertion_ind = 0;
2285
2286 // current conversion specifier character
2287 char fmt_spec = '\0';
2288
2289 // buffer for 's' and 'S' specs
2290 char_u *tofree = NULL;
2291
2292
2293 p++; // skip '%'
2294
2295 // parse flags
2296 while (*p == '0' || *p == '-' || *p == '+' || *p == ' '
2297 || *p == '#' || *p == '\'')
2298 {
2299 switch (*p)
2300 {
2301 case '0': zero_padding = 1; break;
2302 case '-': justify_left = 1; break;
2303 case '+': force_sign = 1; space_for_positive = 0; break;
2304 case ' ': force_sign = 1;
2305 // If both the ' ' and '+' flags appear, the ' '
2306 // flag should be ignored
2307 break;
2308 case '#': alternate_form = 1; break;
2309 case '\'': break;
2310 }
2311 p++;
2312 }
2313 // If the '0' and '-' flags both appear, the '0' flag should be
2314 // ignored.
2315
2316 // parse field width
2317 if (*p == '*')
2318 {
2319 int j;
2320
2321 p++;
2322 j =
2323# if defined(FEAT_EVAL)
2324 tvs != NULL ? tv_nr(tvs, &arg_idx) :
2325# endif
2326 va_arg(ap, int);
2327 if (j >= 0)
2328 min_field_width = j;
2329 else
2330 {
2331 min_field_width = -j;
2332 justify_left = 1;
2333 }
2334 }
2335 else if (VIM_ISDIGIT((int)(*p)))
2336 {
2337 // size_t could be wider than unsigned int; make sure we treat
2338 // argument like common implementations do
2339 unsigned int uj = *p++ - '0';
2340
2341 while (VIM_ISDIGIT((int)(*p)))
2342 uj = 10 * uj + (unsigned int)(*p++ - '0');
2343 min_field_width = uj;
2344 }
2345
2346 // parse precision
2347 if (*p == '.')
2348 {
2349 p++;
2350 precision_specified = 1;
2351 if (*p == '*')
2352 {
2353 int j;
2354
2355 j =
2356# if defined(FEAT_EVAL)
2357 tvs != NULL ? tv_nr(tvs, &arg_idx) :
2358# endif
2359 va_arg(ap, int);
2360 p++;
2361 if (j >= 0)
2362 precision = j;
2363 else
2364 {
2365 precision_specified = 0;
2366 precision = 0;
2367 }
2368 }
2369 else if (VIM_ISDIGIT((int)(*p)))
2370 {
2371 // size_t could be wider than unsigned int; make sure we
2372 // treat argument like common implementations do
2373 unsigned int uj = *p++ - '0';
2374
2375 while (VIM_ISDIGIT((int)(*p)))
2376 uj = 10 * uj + (unsigned int)(*p++ - '0');
2377 precision = uj;
2378 }
2379 }
2380
2381 // parse 'h', 'l' and 'll' length modifiers
2382 if (*p == 'h' || *p == 'l')
2383 {
2384 length_modifier = *p;
2385 p++;
2386 if (length_modifier == 'l' && *p == 'l')
2387 {
2388 // double l = __int64 / varnumber_T
2389 length_modifier = 'L';
2390 p++;
2391 }
2392 }
2393 fmt_spec = *p;
2394
2395 // common synonyms:
2396 switch (fmt_spec)
2397 {
2398 case 'i': fmt_spec = 'd'; break;
2399 case 'D': fmt_spec = 'd'; length_modifier = 'l'; break;
2400 case 'U': fmt_spec = 'u'; length_modifier = 'l'; break;
2401 case 'O': fmt_spec = 'o'; length_modifier = 'l'; break;
2402 default: break;
2403 }
2404
2405# if defined(FEAT_EVAL)
2406 switch (fmt_spec)
2407 {
2408 case 'd': case 'u': case 'o': case 'x': case 'X':
2409 if (tvs != NULL && length_modifier == '\0')
2410 length_modifier = 'L';
2411 }
2412# endif
2413
2414 // get parameter value, do initial processing
2415 switch (fmt_spec)
2416 {
2417 // '%' and 'c' behave similar to 's' regarding flags and field
2418 // widths
2419 case '%':
2420 case 'c':
2421 case 's':
2422 case 'S':
2423 str_arg_l = 1;
2424 switch (fmt_spec)
2425 {
2426 case '%':
2427 str_arg = p;
2428 break;
2429
2430 case 'c':
2431 {
2432 int j;
2433
2434 j =
2435# if defined(FEAT_EVAL)
2436 tvs != NULL ? tv_nr(tvs, &arg_idx) :
2437# endif
2438 va_arg(ap, int);
2439 // standard demands unsigned char
2440 uchar_arg = (unsigned char)j;
2441 str_arg = (char *)&uchar_arg;
2442 break;
2443 }
2444
2445 case 's':
2446 case 'S':
2447 str_arg =
2448# if defined(FEAT_EVAL)
2449 tvs != NULL ? tv_str(tvs, &arg_idx, &tofree) :
2450# endif
2451 va_arg(ap, char *);
2452 if (str_arg == NULL)
2453 {
2454 str_arg = "[NULL]";
2455 str_arg_l = 6;
2456 }
2457 // make sure not to address string beyond the specified
2458 // precision !!!
2459 else if (!precision_specified)
2460 str_arg_l = strlen(str_arg);
2461 // truncate string if necessary as requested by precision
2462 else if (precision == 0)
2463 str_arg_l = 0;
2464 else
2465 {
2466 // Don't put the #if inside memchr(), it can be a
2467 // macro.
2468 // memchr on HP does not like n > 2^31 !!!
2469 char *q = memchr(str_arg, '\0',
2470 precision <= (size_t)0x7fffffffL ? precision
2471 : (size_t)0x7fffffffL);
presukud85fccd2021-11-20 19:38:31 +00002472
Yegappan Lakshmanan8ee52af2021-08-09 19:59:06 +02002473 str_arg_l = (q == NULL) ? precision
2474 : (size_t)(q - str_arg);
2475 }
2476 if (fmt_spec == 'S')
2477 {
presuku1f2453f2021-11-24 15:32:57 +00002478 char_u *p1;
2479 size_t i;
2480 int cell;
presukud85fccd2021-11-20 19:38:31 +00002481
presuku1f2453f2021-11-24 15:32:57 +00002482 for (i = 0, p1 = (char_u *)str_arg; *p1;
Yegappan Lakshmanan8ee52af2021-08-09 19:59:06 +02002483 p1 += mb_ptr2len(p1))
presuku1f2453f2021-11-24 15:32:57 +00002484 {
2485 cell = mb_ptr2cells(p1);
2486 if (precision_specified && i + cell > precision)
2487 break;
2488 i += cell;
Yegappan Lakshmanan8ee52af2021-08-09 19:59:06 +02002489 }
presuku1f2453f2021-11-24 15:32:57 +00002490
2491 str_arg_l = p1 - (char_u *)str_arg;
presukud85fccd2021-11-20 19:38:31 +00002492 if (min_field_width != 0)
presuku1f2453f2021-11-24 15:32:57 +00002493 min_field_width += str_arg_l - i;
Yegappan Lakshmanan8ee52af2021-08-09 19:59:06 +02002494 }
2495 break;
2496
2497 default:
2498 break;
2499 }
2500 break;
2501
2502 case 'd': case 'u':
2503 case 'b': case 'B':
2504 case 'o':
2505 case 'x': case 'X':
2506 case 'p':
2507 {
2508 // NOTE: the u, b, o, x, X and p conversion specifiers
2509 // imply the value is unsigned; d implies a signed
2510 // value
2511
2512 // 0 if numeric argument is zero (or if pointer is
2513 // NULL for 'p'), +1 if greater than zero (or nonzero
2514 // for unsigned arguments), -1 if negative (unsigned
2515 // argument is never negative)
2516 int arg_sign = 0;
2517
2518 // only set for length modifier h, or for no length
2519 // modifiers
2520 int int_arg = 0;
2521 unsigned int uint_arg = 0;
2522
2523 // only set for length modifier l
2524 long int long_arg = 0;
2525 unsigned long int ulong_arg = 0;
2526
2527 // only set for length modifier ll
2528 varnumber_T llong_arg = 0;
2529 uvarnumber_T ullong_arg = 0;
2530
2531 // only set for b conversion
2532 uvarnumber_T bin_arg = 0;
2533
2534 // pointer argument value -only defined for p
2535 // conversion
2536 void *ptr_arg = NULL;
2537
2538 if (fmt_spec == 'p')
2539 {
2540 length_modifier = '\0';
2541 ptr_arg =
2542# if defined(FEAT_EVAL)
2543 tvs != NULL ? (void *)tv_str(tvs, &arg_idx,
2544 NULL) :
2545# endif
2546 va_arg(ap, void *);
2547 if (ptr_arg != NULL)
2548 arg_sign = 1;
2549 }
2550 else if (fmt_spec == 'b' || fmt_spec == 'B')
2551 {
2552 bin_arg =
2553# if defined(FEAT_EVAL)
2554 tvs != NULL ?
2555 (uvarnumber_T)tv_nr(tvs, &arg_idx) :
2556# endif
2557 va_arg(ap, uvarnumber_T);
2558 if (bin_arg != 0)
2559 arg_sign = 1;
2560 }
2561 else if (fmt_spec == 'd')
2562 {
2563 // signed
2564 switch (length_modifier)
2565 {
2566 case '\0':
2567 case 'h':
2568 // char and short arguments are passed as int.
2569 int_arg =
2570# if defined(FEAT_EVAL)
2571 tvs != NULL ? tv_nr(tvs, &arg_idx) :
2572# endif
2573 va_arg(ap, int);
2574 if (int_arg > 0)
2575 arg_sign = 1;
2576 else if (int_arg < 0)
2577 arg_sign = -1;
2578 break;
2579 case 'l':
2580 long_arg =
2581# if defined(FEAT_EVAL)
2582 tvs != NULL ? tv_nr(tvs, &arg_idx) :
2583# endif
2584 va_arg(ap, long int);
2585 if (long_arg > 0)
2586 arg_sign = 1;
2587 else if (long_arg < 0)
2588 arg_sign = -1;
2589 break;
2590 case 'L':
2591 llong_arg =
2592# if defined(FEAT_EVAL)
2593 tvs != NULL ? tv_nr(tvs, &arg_idx) :
2594# endif
2595 va_arg(ap, varnumber_T);
2596 if (llong_arg > 0)
2597 arg_sign = 1;
2598 else if (llong_arg < 0)
2599 arg_sign = -1;
2600 break;
2601 }
2602 }
2603 else
2604 {
2605 // unsigned
2606 switch (length_modifier)
2607 {
2608 case '\0':
2609 case 'h':
2610 uint_arg =
2611# if defined(FEAT_EVAL)
2612 tvs != NULL ? (unsigned)
2613 tv_nr(tvs, &arg_idx) :
2614# endif
2615 va_arg(ap, unsigned int);
2616 if (uint_arg != 0)
2617 arg_sign = 1;
2618 break;
2619 case 'l':
2620 ulong_arg =
2621# if defined(FEAT_EVAL)
2622 tvs != NULL ? (unsigned long)
2623 tv_nr(tvs, &arg_idx) :
2624# endif
2625 va_arg(ap, unsigned long int);
2626 if (ulong_arg != 0)
2627 arg_sign = 1;
2628 break;
2629 case 'L':
2630 ullong_arg =
2631# if defined(FEAT_EVAL)
2632 tvs != NULL ? (uvarnumber_T)
2633 tv_nr(tvs, &arg_idx) :
2634# endif
2635 va_arg(ap, uvarnumber_T);
2636 if (ullong_arg != 0)
2637 arg_sign = 1;
2638 break;
2639 }
2640 }
2641
2642 str_arg = tmp;
2643 str_arg_l = 0;
2644
2645 // NOTE:
2646 // For d, i, u, o, x, and X conversions, if precision is
2647 // specified, the '0' flag should be ignored. This is so
2648 // with Solaris 2.6, Digital UNIX 4.0, HPUX 10, Linux,
2649 // FreeBSD, NetBSD; but not with Perl.
2650 if (precision_specified)
2651 zero_padding = 0;
2652 if (fmt_spec == 'd')
2653 {
2654 if (force_sign && arg_sign >= 0)
2655 tmp[str_arg_l++] = space_for_positive ? ' ' : '+';
2656 // leave negative numbers for sprintf to handle, to
2657 // avoid handling tricky cases like (short int)-32768
2658 }
2659 else if (alternate_form)
2660 {
2661 if (arg_sign != 0
2662 && (fmt_spec == 'b' || fmt_spec == 'B'
2663 || fmt_spec == 'x' || fmt_spec == 'X') )
2664 {
2665 tmp[str_arg_l++] = '0';
2666 tmp[str_arg_l++] = fmt_spec;
2667 }
2668 // alternate form should have no effect for p
2669 // conversion, but ...
2670 }
2671
2672 zero_padding_insertion_ind = str_arg_l;
2673 if (!precision_specified)
2674 precision = 1; // default precision is 1
2675 if (precision == 0 && arg_sign == 0)
2676 {
2677 // When zero value is formatted with an explicit
2678 // precision 0, the resulting formatted string is
2679 // empty (d, i, u, b, B, o, x, X, p).
2680 }
2681 else
2682 {
2683 char f[6];
2684 int f_l = 0;
2685
2686 // construct a simple format string for sprintf
2687 f[f_l++] = '%';
2688 if (!length_modifier)
2689 ;
2690 else if (length_modifier == 'L')
2691 {
2692# ifdef MSWIN
2693 f[f_l++] = 'I';
2694 f[f_l++] = '6';
2695 f[f_l++] = '4';
2696# else
2697 f[f_l++] = 'l';
2698 f[f_l++] = 'l';
2699# endif
2700 }
2701 else
2702 f[f_l++] = length_modifier;
2703 f[f_l++] = fmt_spec;
2704 f[f_l++] = '\0';
2705
2706 if (fmt_spec == 'p')
2707 str_arg_l += sprintf(tmp + str_arg_l, f, ptr_arg);
2708 else if (fmt_spec == 'b' || fmt_spec == 'B')
2709 {
2710 char b[8 * sizeof(uvarnumber_T)];
2711 size_t b_l = 0;
2712 uvarnumber_T bn = bin_arg;
2713
2714 do
2715 {
2716 b[sizeof(b) - ++b_l] = '0' + (bn & 0x1);
2717 bn >>= 1;
2718 }
2719 while (bn != 0);
2720
2721 memcpy(tmp + str_arg_l, b + sizeof(b) - b_l, b_l);
2722 str_arg_l += b_l;
2723 }
2724 else if (fmt_spec == 'd')
2725 {
2726 // signed
2727 switch (length_modifier)
2728 {
2729 case '\0': str_arg_l += sprintf(
2730 tmp + str_arg_l, f,
2731 int_arg);
2732 break;
2733 case 'h': str_arg_l += sprintf(
2734 tmp + str_arg_l, f,
2735 (short)int_arg);
2736 break;
2737 case 'l': str_arg_l += sprintf(
2738 tmp + str_arg_l, f, long_arg);
2739 break;
2740 case 'L': str_arg_l += sprintf(
2741 tmp + str_arg_l, f, llong_arg);
2742 break;
2743 }
2744 }
2745 else
2746 {
2747 // unsigned
2748 switch (length_modifier)
2749 {
2750 case '\0': str_arg_l += sprintf(
2751 tmp + str_arg_l, f,
2752 uint_arg);
2753 break;
2754 case 'h': str_arg_l += sprintf(
2755 tmp + str_arg_l, f,
2756 (unsigned short)uint_arg);
2757 break;
2758 case 'l': str_arg_l += sprintf(
2759 tmp + str_arg_l, f, ulong_arg);
2760 break;
2761 case 'L': str_arg_l += sprintf(
2762 tmp + str_arg_l, f, ullong_arg);
2763 break;
2764 }
2765 }
2766
2767 // include the optional minus sign and possible
2768 // "0x" in the region before the zero padding
2769 // insertion point
2770 if (zero_padding_insertion_ind < str_arg_l
2771 && tmp[zero_padding_insertion_ind] == '-')
2772 zero_padding_insertion_ind++;
2773 if (zero_padding_insertion_ind + 1 < str_arg_l
2774 && tmp[zero_padding_insertion_ind] == '0'
2775 && (tmp[zero_padding_insertion_ind + 1] == 'x'
2776 || tmp[zero_padding_insertion_ind + 1] == 'X'))
2777 zero_padding_insertion_ind += 2;
2778 }
2779
2780 {
2781 size_t num_of_digits = str_arg_l
2782 - zero_padding_insertion_ind;
2783
2784 if (alternate_form && fmt_spec == 'o'
2785 // unless zero is already the first
2786 // character
2787 && !(zero_padding_insertion_ind < str_arg_l
2788 && tmp[zero_padding_insertion_ind] == '0'))
2789 {
2790 // assure leading zero for alternate-form
2791 // octal numbers
2792 if (!precision_specified
2793 || precision < num_of_digits + 1)
2794 {
2795 // precision is increased to force the
2796 // first character to be zero, except if a
2797 // zero value is formatted with an
2798 // explicit precision of zero
2799 precision = num_of_digits + 1;
2800 }
2801 }
2802 // zero padding to specified precision?
2803 if (num_of_digits < precision)
2804 number_of_zeros_to_pad = precision - num_of_digits;
2805 }
2806 // zero padding to specified minimal field width?
2807 if (!justify_left && zero_padding)
2808 {
2809 int n = (int)(min_field_width - (str_arg_l
2810 + number_of_zeros_to_pad));
2811 if (n > 0)
2812 number_of_zeros_to_pad += n;
2813 }
2814 break;
2815 }
2816
Yegappan Lakshmanan8ee52af2021-08-09 19:59:06 +02002817 case 'f':
2818 case 'F':
2819 case 'e':
2820 case 'E':
2821 case 'g':
2822 case 'G':
2823 {
2824 // Floating point.
2825 double f;
2826 double abs_f;
2827 char format[40];
2828 int l;
2829 int remove_trailing_zeroes = FALSE;
2830
2831 f =
Bram Moolenaar73e28dc2022-09-17 21:08:33 +01002832# if defined(FEAT_EVAL)
Yegappan Lakshmanan8ee52af2021-08-09 19:59:06 +02002833 tvs != NULL ? tv_float(tvs, &arg_idx) :
Bram Moolenaar73e28dc2022-09-17 21:08:33 +01002834# endif
Yegappan Lakshmanan8ee52af2021-08-09 19:59:06 +02002835 va_arg(ap, double);
2836 abs_f = f < 0 ? -f : f;
2837
2838 if (fmt_spec == 'g' || fmt_spec == 'G')
2839 {
2840 // Would be nice to use %g directly, but it prints
2841 // "1.0" as "1", we don't want that.
2842 if ((abs_f >= 0.001 && abs_f < 10000000.0)
2843 || abs_f == 0.0)
2844 fmt_spec = ASCII_ISUPPER(fmt_spec) ? 'F' : 'f';
2845 else
2846 fmt_spec = fmt_spec == 'g' ? 'e' : 'E';
2847 remove_trailing_zeroes = TRUE;
2848 }
2849
2850 if ((fmt_spec == 'f' || fmt_spec == 'F') &&
Bram Moolenaar73e28dc2022-09-17 21:08:33 +01002851# ifdef VAX
Yegappan Lakshmanan8ee52af2021-08-09 19:59:06 +02002852 abs_f > 1.0e38
Bram Moolenaar73e28dc2022-09-17 21:08:33 +01002853# else
Yegappan Lakshmanan8ee52af2021-08-09 19:59:06 +02002854 abs_f > 1.0e307
Bram Moolenaar73e28dc2022-09-17 21:08:33 +01002855# endif
Yegappan Lakshmanan8ee52af2021-08-09 19:59:06 +02002856 )
2857 {
2858 // Avoid a buffer overflow
2859 STRCPY(tmp, infinity_str(f > 0.0, fmt_spec,
2860 force_sign, space_for_positive));
2861 str_arg_l = STRLEN(tmp);
2862 zero_padding = 0;
2863 }
2864 else
2865 {
2866 if (isnan(f))
2867 {
2868 // Not a number: nan or NAN
2869 STRCPY(tmp, ASCII_ISUPPER(fmt_spec) ? "NAN"
2870 : "nan");
2871 str_arg_l = 3;
2872 zero_padding = 0;
2873 }
2874 else if (isinf(f))
2875 {
2876 STRCPY(tmp, infinity_str(f > 0.0, fmt_spec,
2877 force_sign, space_for_positive));
2878 str_arg_l = STRLEN(tmp);
2879 zero_padding = 0;
2880 }
2881 else
2882 {
2883 // Regular float number
2884 format[0] = '%';
2885 l = 1;
2886 if (force_sign)
2887 format[l++] = space_for_positive ? ' ' : '+';
2888 if (precision_specified)
2889 {
2890 size_t max_prec = TMP_LEN - 10;
2891
2892 // Make sure we don't get more digits than we
2893 // have room for.
2894 if ((fmt_spec == 'f' || fmt_spec == 'F')
2895 && abs_f > 1.0)
2896 max_prec -= (size_t)log10(abs_f);
2897 if (precision > max_prec)
2898 precision = max_prec;
2899 l += sprintf(format + l, ".%d", (int)precision);
2900 }
2901 format[l] = fmt_spec == 'F' ? 'f' : fmt_spec;
2902 format[l + 1] = NUL;
2903
2904 str_arg_l = sprintf(tmp, format, f);
2905 }
2906
2907 if (remove_trailing_zeroes)
2908 {
2909 int i;
2910 char *tp;
2911
2912 // Using %g or %G: remove superfluous zeroes.
2913 if (fmt_spec == 'f' || fmt_spec == 'F')
2914 tp = tmp + str_arg_l - 1;
2915 else
2916 {
2917 tp = (char *)vim_strchr((char_u *)tmp,
2918 fmt_spec == 'e' ? 'e' : 'E');
2919 if (tp != NULL)
2920 {
2921 // Remove superfluous '+' and leading
2922 // zeroes from the exponent.
2923 if (tp[1] == '+')
2924 {
2925 // Change "1.0e+07" to "1.0e07"
2926 STRMOVE(tp + 1, tp + 2);
2927 --str_arg_l;
2928 }
2929 i = (tp[1] == '-') ? 2 : 1;
2930 while (tp[i] == '0')
2931 {
2932 // Change "1.0e07" to "1.0e7"
2933 STRMOVE(tp + i, tp + i + 1);
2934 --str_arg_l;
2935 }
2936 --tp;
2937 }
2938 }
2939
2940 if (tp != NULL && !precision_specified)
2941 // Remove trailing zeroes, but keep the one
2942 // just after a dot.
2943 while (tp > tmp + 2 && *tp == '0'
2944 && tp[-1] != '.')
2945 {
2946 STRMOVE(tp, tp + 1);
2947 --tp;
2948 --str_arg_l;
2949 }
2950 }
2951 else
2952 {
2953 char *tp;
2954
2955 // Be consistent: some printf("%e") use 1.0e+12
2956 // and some 1.0e+012. Remove one zero in the last
2957 // case.
2958 tp = (char *)vim_strchr((char_u *)tmp,
2959 fmt_spec == 'e' ? 'e' : 'E');
2960 if (tp != NULL && (tp[1] == '+' || tp[1] == '-')
2961 && tp[2] == '0'
2962 && vim_isdigit(tp[3])
2963 && vim_isdigit(tp[4]))
2964 {
2965 STRMOVE(tp + 2, tp + 3);
2966 --str_arg_l;
2967 }
2968 }
2969 }
2970 if (zero_padding && min_field_width > str_arg_l
2971 && (tmp[0] == '-' || force_sign))
2972 {
2973 // padding 0's should be inserted after the sign
2974 number_of_zeros_to_pad = min_field_width - str_arg_l;
2975 zero_padding_insertion_ind = 1;
2976 }
2977 str_arg = tmp;
2978 break;
2979 }
Yegappan Lakshmanan8ee52af2021-08-09 19:59:06 +02002980
2981 default:
2982 // unrecognized conversion specifier, keep format string
2983 // as-is
2984 zero_padding = 0; // turn zero padding off for non-numeric
2985 // conversion
2986 justify_left = 1;
2987 min_field_width = 0; // reset flags
2988
2989 // discard the unrecognized conversion, just keep *
2990 // the unrecognized conversion character
2991 str_arg = p;
2992 str_arg_l = 0;
2993 if (*p != NUL)
2994 str_arg_l++; // include invalid conversion specifier
2995 // unchanged if not at end-of-string
2996 break;
2997 }
2998
2999 if (*p != NUL)
3000 p++; // step over the just processed conversion specifier
3001
3002 // insert padding to the left as requested by min_field_width;
3003 // this does not include the zero padding in case of numerical
3004 // conversions
3005 if (!justify_left)
3006 {
3007 // left padding with blank or zero
3008 int pn = (int)(min_field_width - (str_arg_l + number_of_zeros_to_pad));
3009
3010 if (pn > 0)
3011 {
3012 if (str_l < str_m)
3013 {
3014 size_t avail = str_m - str_l;
3015
3016 vim_memset(str + str_l, zero_padding ? '0' : ' ',
3017 (size_t)pn > avail ? avail
3018 : (size_t)pn);
3019 }
3020 str_l += pn;
3021 }
3022 }
3023
3024 // zero padding as requested by the precision or by the minimal
3025 // field width for numeric conversions required?
3026 if (number_of_zeros_to_pad == 0)
3027 {
3028 // will not copy first part of numeric right now, *
3029 // force it to be copied later in its entirety
3030 zero_padding_insertion_ind = 0;
3031 }
3032 else
3033 {
3034 // insert first part of numerics (sign or '0x') before zero
3035 // padding
3036 int zn = (int)zero_padding_insertion_ind;
3037
3038 if (zn > 0)
3039 {
3040 if (str_l < str_m)
3041 {
3042 size_t avail = str_m - str_l;
3043
3044 mch_memmove(str + str_l, str_arg,
3045 (size_t)zn > avail ? avail
3046 : (size_t)zn);
3047 }
3048 str_l += zn;
3049 }
3050
3051 // insert zero padding as requested by the precision or min
3052 // field width
3053 zn = (int)number_of_zeros_to_pad;
3054 if (zn > 0)
3055 {
3056 if (str_l < str_m)
3057 {
3058 size_t avail = str_m - str_l;
3059
3060 vim_memset(str + str_l, '0',
3061 (size_t)zn > avail ? avail
3062 : (size_t)zn);
3063 }
3064 str_l += zn;
3065 }
3066 }
3067
3068 // insert formatted string
3069 // (or as-is conversion specifier for unknown conversions)
3070 {
3071 int sn = (int)(str_arg_l - zero_padding_insertion_ind);
3072
3073 if (sn > 0)
3074 {
3075 if (str_l < str_m)
3076 {
3077 size_t avail = str_m - str_l;
3078
3079 mch_memmove(str + str_l,
3080 str_arg + zero_padding_insertion_ind,
3081 (size_t)sn > avail ? avail : (size_t)sn);
3082 }
3083 str_l += sn;
3084 }
3085 }
3086
3087 // insert right padding
3088 if (justify_left)
3089 {
3090 // right blank padding to the field width
3091 int pn = (int)(min_field_width
3092 - (str_arg_l + number_of_zeros_to_pad));
3093
3094 if (pn > 0)
3095 {
3096 if (str_l < str_m)
3097 {
3098 size_t avail = str_m - str_l;
3099
3100 vim_memset(str + str_l, ' ',
3101 (size_t)pn > avail ? avail
3102 : (size_t)pn);
3103 }
3104 str_l += pn;
3105 }
3106 }
3107 vim_free(tofree);
3108 }
3109 }
3110
3111 if (str_m > 0)
3112 {
3113 // make sure the string is nul-terminated even at the expense of
3114 // overwriting the last character (shouldn't happen, but just in case)
3115 //
3116 str[str_l <= str_m - 1 ? str_l : str_m - 1] = '\0';
3117 }
3118
3119 if (tvs != NULL && tvs[arg_idx - 1].v_type != VAR_UNKNOWN)
Bram Moolenaar677658a2022-01-05 16:09:06 +00003120 emsg(_(e_too_many_arguments_to_printf));
Yegappan Lakshmanan8ee52af2021-08-09 19:59:06 +02003121
3122 // Return the number of characters formatted (excluding trailing nul
3123 // character), that is, the number of characters that would have been
3124 // written to the buffer if it were large enough.
3125 return (int)str_l;
3126}
3127
3128#endif // PROTO