1// This is an open source non-commercial project. Dear PVS-Studio, please check
2// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
3
4#include <inttypes.h>
5#include <stdarg.h>
6#include <stdbool.h>
7#include <string.h>
8#include <math.h>
9#include <assert.h>
10
11#include "nvim/assert.h"
12#include "nvim/vim.h"
13#include "nvim/ascii.h"
14#include "nvim/strings.h"
15#include "nvim/file_search.h"
16#include "nvim/buffer.h"
17#include "nvim/charset.h"
18#include "nvim/diff.h"
19#include "nvim/edit.h"
20#include "nvim/eval.h"
21#include "nvim/ex_cmds.h"
22#include "nvim/ex_docmd.h"
23#include "nvim/ex_getln.h"
24#include "nvim/fileio.h"
25#include "nvim/func_attr.h"
26#include "nvim/fold.h"
27#include "nvim/func_attr.h"
28#include "nvim/getchar.h"
29#include "nvim/mark.h"
30#include "nvim/math.h"
31#include "nvim/mbyte.h"
32#include "nvim/memfile.h"
33#include "nvim/memline.h"
34#include "nvim/memory.h"
35#include "nvim/message.h"
36#include "nvim/misc1.h"
37#include "nvim/move.h"
38#include "nvim/option.h"
39#include "nvim/ops.h"
40#include "nvim/os_unix.h"
41#include "nvim/path.h"
42#include "nvim/quickfix.h"
43#include "nvim/regexp.h"
44#include "nvim/screen.h"
45#include "nvim/search.h"
46#include "nvim/spell.h"
47#include "nvim/syntax.h"
48#include "nvim/tag.h"
49#include "nvim/window.h"
50#include "nvim/os/os.h"
51#include "nvim/os/shell.h"
52#include "nvim/eval/encode.h"
53
54/// Copy "string" into newly allocated memory.
55char_u *vim_strsave(const char_u *string)
56 FUNC_ATTR_NONNULL_RET FUNC_ATTR_MALLOC FUNC_ATTR_NONNULL_ALL
57{
58 return (char_u *)xstrdup((char *)string);
59}
60
61/// Copy up to `len` bytes of `string` into newly allocated memory and
62/// terminate with a NUL. The allocated memory always has size `len + 1`, even
63/// when `string` is shorter.
64char_u *vim_strnsave(const char_u *string, size_t len)
65 FUNC_ATTR_NONNULL_RET FUNC_ATTR_MALLOC FUNC_ATTR_NONNULL_ALL
66{
67 // strncpy is intentional: some parts of Vim use `string` shorter than `len`
68 // and expect the remainder to be zeroed out.
69 return (char_u *)strncpy(xmallocz(len), (char *)string, len);
70}
71
72/*
73 * Same as vim_strsave(), but any characters found in esc_chars are preceded
74 * by a backslash.
75 */
76char_u *vim_strsave_escaped(const char_u *string, const char_u *esc_chars)
77 FUNC_ATTR_NONNULL_RET FUNC_ATTR_MALLOC FUNC_ATTR_NONNULL_ALL
78{
79 return vim_strsave_escaped_ext(string, esc_chars, '\\', false);
80}
81
82/*
83 * Same as vim_strsave_escaped(), but when "bsl" is true also escape
84 * characters where rem_backslash() would remove the backslash.
85 * Escape the characters with "cc".
86 */
87char_u *vim_strsave_escaped_ext(const char_u *string, const char_u *esc_chars,
88 char_u cc, bool bsl)
89 FUNC_ATTR_NONNULL_RET FUNC_ATTR_MALLOC FUNC_ATTR_NONNULL_ALL
90{
91 /*
92 * First count the number of backslashes required.
93 * Then allocate the memory and insert them.
94 */
95 size_t length = 1; // count the trailing NUL
96 for (const char_u *p = string; *p; p++) {
97 size_t l;
98 if (has_mbyte && (l = (size_t)(*mb_ptr2len)(p)) > 1) {
99 length += l; // count a multibyte char
100 p += l - 1;
101 continue;
102 }
103 if (vim_strchr(esc_chars, *p) != NULL || (bsl && rem_backslash(p)))
104 ++length; /* count a backslash */
105 ++length; /* count an ordinary char */
106 }
107
108 char_u *escaped_string = xmalloc(length);
109 char_u *p2 = escaped_string;
110 for (const char_u *p = string; *p; p++) {
111 size_t l;
112 if (has_mbyte && (l = (size_t)(*mb_ptr2len)(p)) > 1) {
113 memcpy(p2, p, l);
114 p2 += l;
115 p += l - 1; /* skip multibyte char */
116 continue;
117 }
118 if (vim_strchr(esc_chars, *p) != NULL || (bsl && rem_backslash(p)))
119 *p2++ = cc;
120 *p2++ = *p;
121 }
122 *p2 = NUL;
123
124 return escaped_string;
125}
126
127/// Save a copy of an unquoted string
128///
129/// Turns string like `a\bc"def\"ghi\\\n"jkl` into `a\bcdef"ghi\\njkl`, for use
130/// in shell_build_argv: the only purpose of backslash is making next character
131/// be treated literally inside the double quotes, if this character is
132/// backslash or quote.
133///
134/// @param[in] string String to copy.
135/// @param[in] length Length of the string to copy.
136///
137/// @return [allocated] Copy of the string.
138char *vim_strnsave_unquoted(const char *const string, const size_t length)
139 FUNC_ATTR_MALLOC FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL
140 FUNC_ATTR_NONNULL_RET
141{
142#define ESCAPE_COND(p, inquote, string_end) \
143 (*p == '\\' && inquote && p + 1 < string_end && (p[1] == '\\' || p[1] == '"'))
144 size_t ret_length = 0;
145 bool inquote = false;
146 const char *const string_end = string + length;
147 for (const char *p = string; p < string_end; p++) {
148 if (*p == '"') {
149 inquote = !inquote;
150 } else if (ESCAPE_COND(p, inquote, string_end)) {
151 ret_length++;
152 p++;
153 } else {
154 ret_length++;
155 }
156 }
157
158 char *const ret = xmallocz(ret_length);
159 char *rp = ret;
160 inquote = false;
161 for (const char *p = string; p < string_end; p++) {
162 if (*p == '"') {
163 inquote = !inquote;
164 } else if (ESCAPE_COND(p, inquote, string_end)) {
165 *rp++ = *(++p);
166 } else {
167 *rp++ = *p;
168 }
169 }
170#undef ESCAPE_COND
171
172 return ret;
173}
174
175/*
176 * Escape "string" for use as a shell argument with system().
177 * This uses single quotes, except when we know we need to use double quotes
178 * (MS-Windows without 'shellslash' set).
179 * Escape a newline, depending on the 'shell' option.
180 * When "do_special" is true also replace "!", "%", "#" and things starting
181 * with "<" like "<cfile>".
182 * When "do_newline" is false do not escape newline unless it is csh shell.
183 * Returns the result in allocated memory.
184 */
185char_u *vim_strsave_shellescape(const char_u *string,
186 bool do_special, bool do_newline)
187 FUNC_ATTR_NONNULL_RET FUNC_ATTR_MALLOC FUNC_ATTR_NONNULL_ALL
188{
189 char_u *d;
190 char_u *escaped_string;
191 size_t l;
192 int csh_like;
193
194 /* Only csh and similar shells expand '!' within single quotes. For sh and
195 * the like we must not put a backslash before it, it will be taken
196 * literally. If do_special is set the '!' will be escaped twice.
197 * Csh also needs to have "\n" escaped twice when do_special is set. */
198 csh_like = csh_like_shell();
199
200 /* First count the number of extra bytes required. */
201 size_t length = STRLEN(string) + 3; // two quotes and a trailing NUL
202 for (const char_u *p = string; *p != NUL; MB_PTR_ADV(p)) {
203#ifdef WIN32
204 if (!p_ssl) {
205 if (*p == '"') {
206 length++; // " -> ""
207 }
208 } else
209#endif
210 if (*p == '\'') {
211 length += 3; // ' => '\''
212 }
213 if ((*p == '\n' && (csh_like || do_newline))
214 || (*p == '!' && (csh_like || do_special))) {
215 ++length; /* insert backslash */
216 if (csh_like && do_special)
217 ++length; /* insert backslash */
218 }
219 if (do_special && find_cmdline_var(p, &l) >= 0) {
220 ++length; /* insert backslash */
221 p += l - 1;
222 }
223 }
224
225 /* Allocate memory for the result and fill it. */
226 escaped_string = xmalloc(length);
227 d = escaped_string;
228
229 // add opening quote
230#ifdef WIN32
231 if (!p_ssl) {
232 *d++ = '"';
233 } else
234#endif
235 *d++ = '\'';
236
237 for (const char_u *p = string; *p != NUL; ) {
238#ifdef WIN32
239 if (!p_ssl) {
240 if (*p == '"') {
241 *d++ = '"';
242 *d++ = '"';
243 p++;
244 continue;
245 }
246 } else
247#endif
248 if (*p == '\'') {
249 *d++ = '\'';
250 *d++ = '\\';
251 *d++ = '\'';
252 *d++ = '\'';
253 ++p;
254 continue;
255 }
256 if ((*p == '\n' && (csh_like || do_newline))
257 || (*p == '!' && (csh_like || do_special))) {
258 *d++ = '\\';
259 if (csh_like && do_special)
260 *d++ = '\\';
261 *d++ = *p++;
262 continue;
263 }
264 if (do_special && find_cmdline_var(p, &l) >= 0) {
265 *d++ = '\\'; /* insert backslash */
266 while (--l != SIZE_MAX) /* copy the var */
267 *d++ = *p++;
268 continue;
269 }
270
271 MB_COPY_CHAR(p, d);
272 }
273
274 // add terminating quote and finish with a NUL
275# ifdef WIN32
276 if (!p_ssl) {
277 *d++ = '"';
278 } else
279# endif
280 *d++ = '\'';
281 *d = NUL;
282
283 return escaped_string;
284}
285
286/*
287 * Like vim_strsave(), but make all characters uppercase.
288 * This uses ASCII lower-to-upper case translation, language independent.
289 */
290char_u *vim_strsave_up(const char_u *string)
291 FUNC_ATTR_NONNULL_RET FUNC_ATTR_MALLOC FUNC_ATTR_NONNULL_ALL
292{
293 char_u *p1;
294
295 p1 = vim_strsave(string);
296 vim_strup(p1);
297 return p1;
298}
299
300/*
301 * Like vim_strnsave(), but make all characters uppercase.
302 * This uses ASCII lower-to-upper case translation, language independent.
303 */
304char_u *vim_strnsave_up(const char_u *string, size_t len)
305 FUNC_ATTR_NONNULL_RET FUNC_ATTR_MALLOC FUNC_ATTR_NONNULL_ALL
306{
307 char_u *p1 = vim_strnsave(string, len);
308 vim_strup(p1);
309 return p1;
310}
311
312/*
313 * ASCII lower-to-upper case translation, language independent.
314 */
315void vim_strup(char_u *p)
316 FUNC_ATTR_NONNULL_ALL
317{
318 char_u c;
319 while ((c = *p) != NUL) {
320 *p++ = (char_u)(c < 'a' || c > 'z' ? c : c - 0x20);
321 }
322}
323
324/// Make given string all upper-case or all lower-case
325///
326/// Handles multi-byte characters as good as possible.
327///
328/// @param[in] orig Input string.
329/// @param[in] upper If true make uppercase, otherwise lowercase
330///
331/// @return [allocated] upper-cased string.
332char *strcase_save(const char *const orig, bool upper)
333 FUNC_ATTR_NONNULL_RET FUNC_ATTR_MALLOC FUNC_ATTR_NONNULL_ALL
334{
335 char *res = xstrdup(orig);
336
337 char *p = res;
338 while (*p != NUL) {
339 int c = utf_ptr2char((const char_u *)p);
340 int l = utf_ptr2len((const char_u *)p);
341 if (c == 0) {
342 // overlong sequence, use only the first byte
343 c = *p;
344 l = 1;
345 }
346 int uc = upper ? mb_toupper(c) : mb_tolower(c);
347
348 // Reallocate string when byte count changes. This is rare,
349 // thus it's OK to do another malloc()/free().
350 int newl = utf_char2len(uc);
351 if (newl != l) {
352 // TODO(philix): use xrealloc() in strup_save()
353 char *s = xmalloc(STRLEN(res) + (size_t)(1 + newl - l));
354 memcpy(s, res, (size_t)(p - res));
355 STRCPY(s + (p - res) + newl, p + l);
356 p = s + (p - res);
357 xfree(res);
358 res = s;
359 }
360
361 utf_char2bytes(uc, (char_u *)p);
362 p += newl;
363 }
364
365 return res;
366}
367
368/*
369 * delete spaces at the end of a string
370 */
371void del_trailing_spaces(char_u *ptr)
372 FUNC_ATTR_NONNULL_ALL
373{
374 char_u *q;
375
376 q = ptr + STRLEN(ptr);
377 while (--q > ptr && ascii_iswhite(q[0]) && q[-1] != '\\' && q[-1] != Ctrl_V)
378 *q = NUL;
379}
380
381#if (!defined(HAVE_STRCASECMP) && !defined(HAVE_STRICMP))
382/*
383 * Compare two strings, ignoring case, using current locale.
384 * Doesn't work for multi-byte characters.
385 * return 0 for match, < 0 for smaller, > 0 for bigger
386 */
387int vim_stricmp(const char *s1, const char *s2)
388 FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE
389{
390 int i;
391
392 for (;; ) {
393 i = (int)TOLOWER_LOC(*s1) - (int)TOLOWER_LOC(*s2);
394 if (i != 0)
395 return i; /* this character different */
396 if (*s1 == NUL)
397 break; /* strings match until NUL */
398 ++s1;
399 ++s2;
400 }
401 return 0; /* strings match */
402}
403#endif
404
405#if (!defined(HAVE_STRNCASECMP) && !defined(HAVE_STRNICMP))
406/*
407 * Compare two strings, for length "len", ignoring case, using current locale.
408 * Doesn't work for multi-byte characters.
409 * return 0 for match, < 0 for smaller, > 0 for bigger
410 */
411int vim_strnicmp(const char *s1, const char *s2, size_t len)
412 FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE
413{
414 int i;
415
416 while (len > 0) {
417 i = (int)TOLOWER_LOC(*s1) - (int)TOLOWER_LOC(*s2);
418 if (i != 0)
419 return i; /* this character different */
420 if (*s1 == NUL)
421 break; /* strings match until NUL */
422 ++s1;
423 ++s2;
424 --len;
425 }
426 return 0; /* strings match */
427}
428#endif
429
430/// strchr() version which handles multibyte strings
431///
432/// @param[in] string String to search in.
433/// @param[in] c Character to search for.
434///
435/// @return Pointer to the first byte of the found character in string or NULL
436/// if it was not found or character is invalid. NUL character is never
437/// found, use `strlen()` instead.
438char_u *vim_strchr(const char_u *const string, const int c)
439 FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
440{
441 if (c <= 0) {
442 return NULL;
443 } else if (c < 0x80) {
444 return (char_u *)strchr((const char *)string, c);
445 } else {
446 char u8char[MB_MAXBYTES + 1];
447 const int len = utf_char2bytes(c, (char_u *)u8char);
448 u8char[len] = NUL;
449 return (char_u *)strstr((const char *)string, u8char);
450 }
451}
452
453/*
454 * Sort an array of strings.
455 */
456
457#ifdef INCLUDE_GENERATED_DECLARATIONS
458# include "strings.c.generated.h"
459#endif
460static int sort_compare(const void *s1, const void *s2)
461 FUNC_ATTR_NONNULL_ALL
462{
463 return STRCMP(*(char **)s1, *(char **)s2);
464}
465
466void sort_strings(char_u **files, int count)
467{
468 qsort((void *)files, (size_t)count, sizeof(char_u *), sort_compare);
469}
470
471/*
472 * Return true if string "s" contains a non-ASCII character (128 or higher).
473 * When "s" is NULL false is returned.
474 */
475bool has_non_ascii(const char_u *s)
476 FUNC_ATTR_PURE
477{
478 const char_u *p;
479
480 if (s != NULL)
481 for (p = s; *p != NUL; ++p)
482 if (*p >= 128)
483 return true;
484 return false;
485}
486
487/// Return true if string "s" contains a non-ASCII character (128 or higher).
488/// When "s" is NULL false is returned.
489bool has_non_ascii_len(const char *const s, const size_t len)
490 FUNC_ATTR_PURE
491{
492 if (s != NULL) {
493 for (size_t i = 0; i < len; i++) {
494 if ((uint8_t) s[i] >= 128) {
495 return true;
496 }
497 }
498 }
499 return false;
500}
501
502/*
503 * Concatenate two strings and return the result in allocated memory.
504 */
505char_u *concat_str(const char_u *restrict str1, const char_u *restrict str2)
506 FUNC_ATTR_NONNULL_RET FUNC_ATTR_MALLOC FUNC_ATTR_NONNULL_ALL
507{
508 size_t l = STRLEN(str1);
509 char_u *dest = xmalloc(l + STRLEN(str2) + 1);
510 STRCPY(dest, str1);
511 STRCPY(dest + l, str2);
512 return dest;
513}
514
515
516static const char *const e_printf =
517 N_("E766: Insufficient arguments for printf()");
518
519/// Get number argument from idxp entry in tvs
520///
521/// Will give an error message for VimL entry with invalid type or for
522/// insufficient entries.
523///
524/// @param[in] tvs List of VimL values. List is terminated by VAR_UNKNOWN
525/// value.
526/// @param[in,out] idxp Index in a list. Will be incremented. Indexing starts
527/// at 1.
528///
529/// @return Number value or 0 in case of error.
530static varnumber_T tv_nr(typval_T *tvs, int *idxp)
531 FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
532{
533 int idx = *idxp - 1;
534 varnumber_T n = 0;
535
536 if (tvs[idx].v_type == VAR_UNKNOWN) {
537 EMSG(_(e_printf));
538 } else {
539 (*idxp)++;
540 bool err = false;
541 n = tv_get_number_chk(&tvs[idx], &err);
542 if (err) {
543 n = 0;
544 }
545 }
546 return n;
547}
548
549/// Get string argument from idxp entry in tvs
550///
551/// Will give an error message for VimL entry with invalid type or for
552/// insufficient entries.
553///
554/// @param[in] tvs List of VimL values. List is terminated by VAR_UNKNOWN
555/// value.
556/// @param[in,out] idxp Index in a list. Will be incremented.
557/// @param[out] tofree If the idxp entry in tvs is not a String or a Number,
558/// it will be converted to String in the same format
559/// as ":echo" and stored in "*tofree". The caller must
560/// free "*tofree".
561///
562/// @return String value or NULL in case of error.
563static const char *tv_str(typval_T *tvs, int *idxp, char **const tofree)
564 FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
565{
566 int idx = *idxp - 1;
567 const char *s = NULL;
568
569 if (tvs[idx].v_type == VAR_UNKNOWN) {
570 EMSG(_(e_printf));
571 } else {
572 (*idxp)++;
573 if (tvs[idx].v_type == VAR_STRING || tvs[idx].v_type == VAR_NUMBER) {
574 s = tv_get_string_chk(&tvs[idx]);
575 *tofree = NULL;
576 } else {
577 s = *tofree = encode_tv2echo(&tvs[idx], NULL);
578 }
579 }
580 return s;
581}
582
583/// Get pointer argument from the next entry in tvs
584///
585/// Will give an error message for VimL entry with invalid type or for
586/// insufficient entries.
587///
588/// @param[in] tvs List of typval_T values.
589/// @param[in,out] idxp Pointer to the index of the current value.
590///
591/// @return Pointer stored in typval_T or NULL.
592static const void *tv_ptr(const typval_T *const tvs, int *const idxp)
593 FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
594{
595#define OFF(attr) offsetof(union typval_vval_union, attr)
596 STATIC_ASSERT(
597 OFF(v_string) == OFF(v_list)
598 && OFF(v_string) == OFF(v_dict)
599 && OFF(v_string) == OFF(v_partial)
600 && sizeof(tvs[0].vval.v_string) == sizeof(tvs[0].vval.v_list)
601 && sizeof(tvs[0].vval.v_string) == sizeof(tvs[0].vval.v_dict)
602 && sizeof(tvs[0].vval.v_string) == sizeof(tvs[0].vval.v_partial),
603 "Strings, dictionaries, lists and partials are expected to be pointers, "
604 "so that all three of them can be accessed via v_string");
605#undef OFF
606 const int idx = *idxp - 1;
607 if (tvs[idx].v_type == VAR_UNKNOWN) {
608 EMSG(_(e_printf));
609 return NULL;
610 } else {
611 (*idxp)++;
612 return tvs[idx].vval.v_string;
613 }
614}
615
616/// Get float argument from idxp entry in tvs
617///
618/// Will give an error message for VimL entry with invalid type or for
619/// insufficient entries.
620///
621/// @param[in] tvs List of VimL values. List is terminated by VAR_UNKNOWN
622/// value.
623/// @param[in,out] idxp Index in a list. Will be incremented.
624///
625/// @return Floating-point value or zero in case of error.
626static float_T tv_float(typval_T *const tvs, int *const idxp)
627 FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
628{
629 int idx = *idxp - 1;
630 float_T f = 0;
631
632 if (tvs[idx].v_type == VAR_UNKNOWN) {
633 EMSG(_(e_printf));
634 } else {
635 (*idxp)++;
636 if (tvs[idx].v_type == VAR_FLOAT) {
637 f = tvs[idx].vval.v_float;
638 } else if (tvs[idx].v_type == VAR_NUMBER) {
639 f = (float_T)tvs[idx].vval.v_number;
640 } else {
641 EMSG(_("E807: Expected Float argument for printf()"));
642 }
643 }
644 return f;
645}
646
647// This code was included to provide a portable vsnprintf() and snprintf().
648// Some systems may provide their own, but we always use this one for
649// consistency.
650//
651// This code is based on snprintf.c - a portable implementation of snprintf
652// by Mark Martinec <mark.martinec@ijs.si>, Version 2.2, 2000-10-06.
653// Included with permission. It was heavily modified to fit in Vim.
654// The original code, including useful comments, can be found here:
655//
656// http://www.ijs.si/software/snprintf/
657//
658// This snprintf() only supports the following conversion specifiers:
659// s, c, b, B, d, u, o, x, X, p (and synonyms: i, D, U, O - see below)
660// with flags: '-', '+', ' ', '0' and '#'.
661// An asterisk is supported for field width as well as precision.
662//
663// Limited support for floating point was added: 'f', 'e', 'E', 'g', 'G'.
664//
665// Length modifiers 'h' (short int), 'l' (long int) and "ll" (long long int) are
666// supported.
667//
668// The locale is not used, the string is used as a byte string. This is only
669// relevant for double-byte encodings where the second byte may be '%'.
670//
671// It is permitted for "str_m" to be zero, and it is permitted to specify NULL
672// pointer for resulting string argument if "str_m" is zero (as per ISO C99).
673//
674// The return value is the number of characters which would be generated
675// for the given input, excluding the trailing NUL. If this value
676// is greater or equal to "str_m", not all characters from the result
677// have been stored in str, output bytes beyond the ("str_m"-1) -th character
678// are discarded. If "str_m" is greater than zero it is guaranteed
679// the resulting string will be NUL-terminated.
680
681// vim_vsnprintf_typval() can be invoked with either "va_list" or a list of
682// "typval_T". When the latter is not used it must be NULL.
683
684/// Append a formatted value to the string
685///
686/// @see vim_vsnprintf_typval().
687int vim_snprintf_add(char *str, size_t str_m, char *fmt, ...)
688 FUNC_ATTR_PRINTF(3, 4)
689{
690 const size_t len = strlen(str);
691 size_t space;
692
693 if (str_m <= len) {
694 space = 0;
695 } else {
696 space = str_m - len;
697 }
698 va_list ap;
699 va_start(ap, fmt);
700 const int str_l = vim_vsnprintf(str + len, space, fmt, ap);
701 va_end(ap);
702 return str_l;
703}
704
705/// Write formatted value to the string
706///
707/// @param[out] str String to write to.
708/// @param[in] str_m String length.
709/// @param[in] fmt String format.
710///
711/// @return Number of bytes excluding NUL byte that would be written to the
712/// string if str_m was greater or equal to the return value.
713int vim_snprintf(char *str, size_t str_m, const char *fmt, ...)
714 FUNC_ATTR_PRINTF(3, 4)
715{
716 va_list ap;
717 va_start(ap, fmt);
718 const int str_l = vim_vsnprintf(str, str_m, fmt, ap);
719 va_end(ap);
720 return str_l;
721}
722
723// Return the representation of infinity for printf() function:
724// "-inf", "inf", "+inf", " inf", "-INF", "INF", "+INF" or " INF".
725static const char *infinity_str(bool positive, char fmt_spec,
726 int force_sign, int space_for_positive)
727{
728 static const char *table[] = {
729 "-inf", "inf", "+inf", " inf",
730 "-INF", "INF", "+INF", " INF"
731 };
732 int idx = positive * (1 + force_sign + force_sign * space_for_positive);
733 if (ASCII_ISUPPER(fmt_spec)) {
734 idx += 4;
735 }
736 return table[idx];
737}
738
739int vim_vsnprintf(char *str, size_t str_m, const char *fmt, va_list ap)
740{
741 return vim_vsnprintf_typval(str, str_m, fmt, ap, NULL);
742}
743
744/// Write formatted value to the string
745///
746/// @param[out] str String to write to.
747/// @param[in] str_m String length.
748/// @param[in] fmt String format.
749/// @param[in] ap Values that should be formatted. Ignored if tvs is not NULL.
750/// @param[in] tvs Values that should be formatted, for printf() VimL
751/// function. Must be NULL in other cases.
752///
753/// @return Number of bytes excluding NUL byte that would be written to the
754/// string if str_m was greater or equal to the return value.
755int vim_vsnprintf_typval(
756 char *str, size_t str_m, const char *fmt, va_list ap, typval_T *const tvs)
757{
758 size_t str_l = 0;
759 bool str_avail = str_l < str_m;
760 const char *p = fmt;
761 int arg_idx = 1;
762
763 if (!p) {
764 p = "";
765 }
766 while (*p) {
767 if (*p != '%') {
768 // copy up to the next '%' or NUL without any changes
769 size_t n = (size_t)(xstrchrnul(p + 1, '%') - p);
770 if (str_avail) {
771 size_t avail = str_m - str_l;
772 memmove(str + str_l, p, MIN(n, avail));
773 str_avail = n < avail;
774 }
775 p += n;
776 assert(n <= SIZE_MAX - str_l);
777 str_l += n;
778 } else {
779 size_t min_field_width = 0, precision = 0;
780 int zero_padding = 0, precision_specified = 0, justify_left = 0;
781 int alternate_form = 0, force_sign = 0;
782
783 // if both ' ' and '+' flags appear, ' ' flag should be ignored
784 int space_for_positive = 1;
785
786 // allowed values: \0, h, l, 2 (for ll), z, L
787 char length_modifier = '\0';
788
789 // temporary buffer for simple numeric->string conversion
790# define TMP_LEN 350 // 1e308 seems reasonable as the maximum printable
791 char tmp[TMP_LEN];
792
793 // string address in case of string argument
794 const char *str_arg = NULL;
795
796 // natural field width of arg without padding and sign
797 size_t str_arg_l;
798
799 // unsigned char argument value (only defined for c conversion);
800 // standard explicitly states the char argument for the c
801 // conversion is unsigned
802 unsigned char uchar_arg;
803
804 // number of zeros to be inserted for numeric conversions as
805 // required by the precision or minimal field width
806 size_t number_of_zeros_to_pad = 0;
807
808 // index into tmp where zero padding is to be inserted
809 size_t zero_padding_insertion_ind = 0;
810
811 // current conversion specifier character
812 char fmt_spec = '\0';
813
814 // buffer for 's' and 'S' specs
815 char *tofree = NULL;
816
817 p++; // skip '%'
818
819 // parse flags
820 while (true) {
821 switch (*p) {
822 case '0': zero_padding = 1; p++; continue;
823 case '-': justify_left = 1; p++; continue;
824 // if both '0' and '-' flags appear, '0' should be ignored
825 case '+': force_sign = 1; space_for_positive = 0; p++; continue;
826 case ' ': force_sign = 1; p++; continue;
827 // if both ' ' and '+' flags appear, ' ' should be ignored
828 case '#': alternate_form = 1; p++; continue;
829 case '\'': p++; continue;
830 default: break;
831 }
832 break;
833 }
834
835 // parse field width
836 if (*p == '*') {
837 p++;
838 const int j = tvs ? (int)tv_nr(tvs, &arg_idx) : va_arg(ap, int);
839 if (j >= 0) {
840 min_field_width = (size_t)j;
841 } else {
842 min_field_width = (size_t)-j;
843 justify_left = 1;
844 }
845 } else if (ascii_isdigit((int)(*p))) {
846 // size_t could be wider than unsigned int; make sure we treat
847 // argument like common implementations do
848 unsigned int uj = (unsigned)(*p++ - '0');
849
850 while (ascii_isdigit((int)(*p))) {
851 uj = 10 * uj + (unsigned int)(*p++ - '0');
852 }
853 min_field_width = uj;
854 }
855
856 // parse precision
857 if (*p == '.') {
858 p++;
859 precision_specified = 1;
860 if (*p == '*') {
861 const int j = tvs ? (int)tv_nr(tvs, &arg_idx) : va_arg(ap, int);
862 p++;
863 if (j >= 0) {
864 precision = (size_t)j;
865 } else {
866 precision_specified = 0;
867 precision = 0;
868 }
869 } else if (ascii_isdigit((int)(*p))) {
870 // size_t could be wider than unsigned int; make sure we
871 // treat argument like common implementations do
872 unsigned int uj = (unsigned)(*p++ - '0');
873
874 while (ascii_isdigit((int)(*p))) {
875 uj = 10 * uj + (unsigned int)(*p++ - '0');
876 }
877 precision = uj;
878 }
879 }
880
881 // parse 'h', 'l', 'll' and 'z' length modifiers
882 if (*p == 'h' || *p == 'l' || *p == 'z') {
883 length_modifier = *p;
884 p++;
885 if (length_modifier == 'l' && *p == 'l') { // ll, encoded as 2
886 length_modifier = '2';
887 p++;
888 }
889 }
890
891 fmt_spec = *p;
892
893 // common synonyms
894 switch (fmt_spec) {
895 case 'i': fmt_spec = 'd'; break;
896 case 'D': fmt_spec = 'd'; length_modifier = 'l'; break;
897 case 'U': fmt_spec = 'u'; length_modifier = 'l'; break;
898 case 'O': fmt_spec = 'o'; length_modifier = 'l'; break;
899 default: break;
900 }
901
902 switch (fmt_spec) {
903 case 'd': case 'u': case 'o': case 'x': case 'X':
904 if (tvs && length_modifier == '\0') {
905 length_modifier = '2';
906 }
907 }
908
909 // get parameter value, do initial processing
910 switch (fmt_spec) {
911 // '%' and 'c' behave similar to 's' regarding flags and field widths
912 case '%': case 'c': case 's': case 'S':
913 str_arg_l = 1;
914 switch (fmt_spec) {
915 case '%':
916 str_arg = p;
917 break;
918
919 case 'c': {
920 const int j = tvs ? (int)tv_nr(tvs, &arg_idx) : va_arg(ap, int);
921 // standard demands unsigned char
922 uchar_arg = (unsigned char)j;
923 str_arg = (char *)&uchar_arg;
924 break;
925 }
926
927 case 's':
928 case 'S':
929 str_arg = tvs ? tv_str(tvs, &arg_idx, &tofree)
930 : va_arg(ap, const char *);
931 if (!str_arg) {
932 str_arg = "[NULL]";
933 str_arg_l = 6;
934 } else if (!precision_specified) {
935 // make sure not to address string beyond the specified
936 // precision
937 str_arg_l = strlen(str_arg);
938 } else if (precision == 0) {
939 // truncate string if necessary as requested by precision
940 str_arg_l = 0;
941 } else {
942 // memchr on HP does not like n > 2^31
943 // TODO(elmart): check if this still holds / is relevant
944 str_arg_l = (size_t)((char *)xmemscan(str_arg,
945 NUL,
946 MIN(precision,
947 0x7fffffff))
948 - str_arg);
949 }
950 if (fmt_spec == 'S') {
951 if (min_field_width != 0) {
952 min_field_width += (strlen(str_arg)
953 - mb_string2cells((char_u *)str_arg));
954 }
955 if (precision) {
956 const char *p1 = str_arg;
957 for (size_t i = 0; i < precision && *p1; i++) {
958 p1 += mb_ptr2len((const char_u *)p1);
959 }
960 str_arg_l = precision = (size_t)(p1 - str_arg);
961 }
962 }
963 break;
964
965 default:
966 break;
967 }
968 break;
969
970 case 'd':
971 case 'u':
972 case 'b': case 'B':
973 case 'o':
974 case 'x': case 'X':
975 case 'p': {
976 // u, b, B, o, x, X and p conversion specifiers imply
977 // the value is unsigned; d implies a signed value
978
979 // 0 if numeric argument is zero (or if pointer is NULL for 'p'),
980 // +1 if greater than zero (or non NULL for 'p'),
981 // -1 if negative (unsigned argument is never negative)
982 int arg_sign = 0;
983
984 intmax_t arg = 0;
985 uintmax_t uarg = 0;
986
987 // only defined for p conversion
988 const void *ptr_arg = NULL;
989
990 if (fmt_spec == 'p') {
991 ptr_arg = tvs ? tv_ptr(tvs, &arg_idx) : va_arg(ap, void *);
992 if (ptr_arg) {
993 arg_sign = 1;
994 }
995 } else if (fmt_spec == 'd') {
996 // signed
997 switch (length_modifier) {
998 case '\0': {
999 arg = (int)(tvs ? tv_nr(tvs, &arg_idx) : va_arg(ap, int));
1000 break;
1001 }
1002 case 'h': {
1003 // char and short arguments are passed as int16_t
1004 arg = (int16_t)(tvs ? tv_nr(tvs, &arg_idx) : va_arg(ap, int));
1005 break;
1006 }
1007 case 'l': {
1008 arg = (tvs ? (long)tv_nr(tvs, &arg_idx) : va_arg(ap, long));
1009 break;
1010 }
1011 case '2': {
1012 arg = (
1013 tvs
1014 ? (long long)tv_nr(tvs, &arg_idx) // NOLINT (runtime/int)
1015 : va_arg(ap, long long)); // NOLINT (runtime/int)
1016 break;
1017 }
1018 case 'z': {
1019 arg = (tvs
1020 ? (ptrdiff_t)tv_nr(tvs, &arg_idx)
1021 : va_arg(ap, ptrdiff_t));
1022 break;
1023 }
1024 }
1025 if (arg > 0) {
1026 arg_sign = 1;
1027 } else if (arg < 0) {
1028 arg_sign = -1;
1029 }
1030 } else {
1031 // unsigned
1032 switch (length_modifier) {
1033 case '\0': {
1034 uarg = (unsigned int)(tvs
1035 ? tv_nr(tvs, &arg_idx)
1036 : va_arg(ap, unsigned int));
1037 break;
1038 }
1039 case 'h': {
1040 uarg = (uint16_t)(tvs
1041 ? tv_nr(tvs, &arg_idx)
1042 : va_arg(ap, unsigned int));
1043 break;
1044 }
1045 case 'l': {
1046 uarg = (tvs
1047 ? (unsigned long)tv_nr(tvs, &arg_idx)
1048 : va_arg(ap, unsigned long));
1049 break;
1050 }
1051 case '2': {
1052 uarg = (uintmax_t)(unsigned long long)( // NOLINT (runtime/int)
1053 tvs
1054 ? ((unsigned long long) // NOLINT (runtime/int)
1055 tv_nr(tvs, &arg_idx))
1056 : va_arg(ap, unsigned long long)); // NOLINT (runtime/int)
1057 break;
1058 }
1059 case 'z': {
1060 uarg = (tvs
1061 ? (size_t)tv_nr(tvs, &arg_idx)
1062 : va_arg(ap, size_t));
1063 break;
1064 }
1065 }
1066 arg_sign = (uarg != 0);
1067 }
1068
1069 str_arg = tmp;
1070 str_arg_l = 0;
1071
1072 // For d, i, u, o, x, and X conversions, if precision is specified,
1073 // '0' flag should be ignored. This is so with Solaris 2.6, Digital
1074 // UNIX 4.0, HPUX 10, Linux, FreeBSD, NetBSD; but not with Perl.
1075 if (precision_specified) {
1076 zero_padding = 0;
1077 }
1078
1079 if (fmt_spec == 'd') {
1080 if (force_sign && arg_sign >= 0) {
1081 tmp[str_arg_l++] = space_for_positive ? ' ' : '+';
1082 }
1083 // leave negative numbers for snprintf to handle, to
1084 // avoid handling tricky cases like (short int)-32768
1085 } else if (alternate_form) {
1086 if (arg_sign != 0 && (fmt_spec == 'x' || fmt_spec == 'X'
1087 || fmt_spec == 'b' || fmt_spec == 'B')) {
1088 tmp[str_arg_l++] = '0';
1089 tmp[str_arg_l++] = fmt_spec;
1090 }
1091 // alternate form should have no effect for p * conversion, but ...
1092 }
1093
1094 zero_padding_insertion_ind = str_arg_l;
1095 if (!precision_specified) {
1096 precision = 1; // default precision is 1
1097 }
1098 if (precision == 0 && arg_sign == 0) {
1099 // when zero value is formatted with an explicit precision 0,
1100 // resulting formatted string is empty (d, i, u, b, B, o, x, X, p)
1101 } else {
1102 switch (fmt_spec) {
1103 case 'p': { // pointer
1104 str_arg_l += (size_t)snprintf(tmp + str_arg_l,
1105 sizeof(tmp) - str_arg_l,
1106 "%p", ptr_arg);
1107 break;
1108 }
1109 case 'd': { // signed
1110 str_arg_l += (size_t)snprintf(tmp + str_arg_l,
1111 sizeof(tmp) - str_arg_l,
1112 "%" PRIdMAX, arg);
1113 break;
1114 }
1115 case 'b': case 'B': { // binary
1116 size_t bits = 0;
1117 for (bits = sizeof(uintmax_t) * 8; bits > 0; bits--) {
1118 if ((uarg >> (bits - 1)) & 0x1) {
1119 break;
1120 }
1121 }
1122
1123 while (bits > 0) {
1124 tmp[str_arg_l++] = ((uarg >> --bits) & 0x1) ? '1' : '0';
1125 }
1126 break;
1127 }
1128 default: { // unsigned
1129 // construct a simple format string for snprintf
1130 char f[] = "%" PRIuMAX;
1131 f[sizeof("%" PRIuMAX) - 1 - 1] = fmt_spec;
1132 assert(PRIuMAX[sizeof(PRIuMAX) - 1 - 1] == 'u');
1133 str_arg_l += (size_t)snprintf(tmp + str_arg_l,
1134 sizeof(tmp) - str_arg_l,
1135 f, uarg);
1136 break;
1137 }
1138 }
1139 assert(str_arg_l < sizeof(tmp));
1140
1141 // include the optional minus sign and possible "0x" in the region
1142 // before the zero padding insertion point
1143 if (zero_padding_insertion_ind < str_arg_l
1144 && tmp[zero_padding_insertion_ind] == '-') {
1145 zero_padding_insertion_ind++;
1146 }
1147 if (zero_padding_insertion_ind + 1 < str_arg_l
1148 && tmp[zero_padding_insertion_ind] == '0'
1149 && (tmp[zero_padding_insertion_ind + 1] == 'x'
1150 || tmp[zero_padding_insertion_ind + 1] == 'X'
1151 || tmp[zero_padding_insertion_ind + 1] == 'b'
1152 || tmp[zero_padding_insertion_ind + 1] == 'B')) {
1153 zero_padding_insertion_ind += 2;
1154 }
1155 }
1156
1157 {
1158 size_t num_of_digits = str_arg_l - zero_padding_insertion_ind;
1159
1160 if (alternate_form && fmt_spec == 'o'
1161 // unless zero is already the first character
1162 && !(zero_padding_insertion_ind < str_arg_l
1163 && tmp[zero_padding_insertion_ind] == '0')) {
1164 // assure leading zero for alternate-form octal numbers
1165 if (!precision_specified
1166 || precision < num_of_digits + 1) {
1167 // precision is increased to force the first character to be
1168 // zero, except if a zero value is formatted with an explicit
1169 // precision of zero
1170 precision = num_of_digits + 1;
1171 }
1172 }
1173 // zero padding to specified precision?
1174 if (num_of_digits < precision) {
1175 number_of_zeros_to_pad = precision - num_of_digits;
1176 }
1177 }
1178 // zero padding to specified minimal field width?
1179 if (!justify_left && zero_padding) {
1180 const int n = (int)(min_field_width - (str_arg_l
1181 + number_of_zeros_to_pad));
1182 if (n > 0) {
1183 number_of_zeros_to_pad += (size_t)n;
1184 }
1185 }
1186 break;
1187 }
1188
1189 case 'f':
1190 case 'F':
1191 case 'e':
1192 case 'E':
1193 case 'g':
1194 case 'G':
1195 {
1196 // floating point
1197 char format[40];
1198 int remove_trailing_zeroes = false;
1199
1200 double f = tvs ? tv_float(tvs, &arg_idx) : va_arg(ap, double);
1201 double abs_f = f < 0 ? -f : f;
1202
1203 if (fmt_spec == 'g' || fmt_spec == 'G') {
1204 // can't use %g directly, cause it prints "1.0" as "1"
1205 if ((abs_f >= 0.001 && abs_f < 10000000.0) || abs_f == 0.0) {
1206 fmt_spec = ASCII_ISUPPER(fmt_spec) ? 'F' : 'f';
1207 } else {
1208 fmt_spec = fmt_spec == 'g' ? 'e' : 'E';
1209 }
1210 remove_trailing_zeroes = true;
1211 }
1212
1213 if (xisinf(f)
1214 || (strchr("fF", fmt_spec) != NULL && abs_f > 1.0e307)) {
1215 xstrlcpy(tmp, infinity_str(f > 0.0, fmt_spec,
1216 force_sign, space_for_positive),
1217 sizeof(tmp));
1218 str_arg_l = strlen(tmp);
1219 zero_padding = 0;
1220 } else if (xisnan(f)) {
1221 // Not a number: nan or NAN
1222 memmove(tmp, ASCII_ISUPPER(fmt_spec) ? "NAN" : "nan", 4);
1223 str_arg_l = 3;
1224 zero_padding = 0;
1225 } else {
1226 // Regular float number
1227 format[0] = '%';
1228 size_t l = 1;
1229 if (force_sign) {
1230 format[l++] = space_for_positive ? ' ' : '+';
1231 }
1232 if (precision_specified) {
1233 size_t max_prec = TMP_LEN - 10;
1234
1235 // make sure we don't get more digits than we have room for
1236 if ((fmt_spec == 'f' || fmt_spec == 'F') && abs_f > 1.0) {
1237 max_prec -= (size_t)log10(abs_f);
1238 }
1239 if (precision > max_prec) {
1240 precision = max_prec;
1241 }
1242 l += (size_t)snprintf(format + l, sizeof(format) - l, ".%d",
1243 (int)precision);
1244 }
1245
1246 // Cast to char to avoid a conversion warning on Ubuntu 12.04.
1247 assert(l + 1 < sizeof(format));
1248 format[l] = (char)(fmt_spec == 'F' ? 'f' : fmt_spec);
1249 format[l + 1] = NUL;
1250
1251 str_arg_l = (size_t)snprintf(tmp, sizeof(tmp), format, f);
1252 assert(str_arg_l < sizeof(tmp));
1253
1254 if (remove_trailing_zeroes) {
1255 int i;
1256 char *tp;
1257
1258 // using %g or %G: remove superfluous zeroes
1259 if (fmt_spec == 'f' || fmt_spec == 'F') {
1260 tp = tmp + str_arg_l - 1;
1261 } else {
1262 tp = (char *)vim_strchr((char_u *)tmp,
1263 fmt_spec == 'e' ? 'e' : 'E');
1264 if (tp) {
1265 // remove superfluous '+' and leading zeroes from exponent
1266 if (tp[1] == '+') {
1267 // change "1.0e+07" to "1.0e07"
1268 STRMOVE(tp + 1, tp + 2);
1269 str_arg_l--;
1270 }
1271 i = (tp[1] == '-') ? 2 : 1;
1272 while (tp[i] == '0') {
1273 // change "1.0e07" to "1.0e7"
1274 STRMOVE(tp + i, tp + i + 1);
1275 str_arg_l--;
1276 }
1277 tp--;
1278 }
1279 }
1280
1281 if (tp != NULL && !precision_specified) {
1282 // remove trailing zeroes, but keep the one just after a dot
1283 while (tp > tmp + 2 && *tp == '0' && tp[-1] != '.') {
1284 STRMOVE(tp, tp + 1);
1285 tp--;
1286 str_arg_l--;
1287 }
1288 }
1289 } else {
1290 // Be consistent: some printf("%e") use 1.0e+12 and some
1291 // 1.0e+012; remove one zero in the last case.
1292 char *tp = (char *)vim_strchr((char_u *)tmp,
1293 fmt_spec == 'e' ? 'e' : 'E');
1294 if (tp && (tp[1] == '+' || tp[1] == '-') && tp[2] == '0'
1295 && ascii_isdigit(tp[3]) && ascii_isdigit(tp[4])) {
1296 STRMOVE(tp + 2, tp + 3);
1297 str_arg_l--;
1298 }
1299 }
1300 }
1301 if (zero_padding && min_field_width > str_arg_l
1302 && (tmp[0] == '-' || force_sign)) {
1303 // Padding 0's should be inserted after the sign.
1304 number_of_zeros_to_pad = min_field_width - str_arg_l;
1305 zero_padding_insertion_ind = 1;
1306 }
1307 str_arg = tmp;
1308 break;
1309 }
1310
1311 default:
1312 // unrecognized conversion specifier, keep format string as-is
1313 zero_padding = 0; // turn zero padding off for non-numeric conversion
1314 justify_left = 1;
1315 min_field_width = 0; // reset flags
1316
1317 // discard the unrecognized conversion, just keep
1318 // the unrecognized conversion character
1319 str_arg = p;
1320 str_arg_l = 0;
1321 if (*p) {
1322 str_arg_l++; // include invalid conversion specifier
1323 }
1324 // unchanged if not at end-of-string
1325 break;
1326 }
1327
1328 if (*p) {
1329 p++; // step over the just processed conversion specifier
1330 }
1331
1332 // insert padding to the left as requested by min_field_width;
1333 // this does not include the zero padding in case of numerical conversions
1334 if (!justify_left) {
1335 assert(str_arg_l <= SIZE_MAX - number_of_zeros_to_pad);
1336 if (min_field_width > str_arg_l + number_of_zeros_to_pad) {
1337 // left padding with blank or zero
1338 size_t pn = min_field_width - (str_arg_l + number_of_zeros_to_pad);
1339 if (str_avail) {
1340 size_t avail = str_m - str_l;
1341 memset(str + str_l, zero_padding ? '0' : ' ', MIN(pn, avail));
1342 str_avail = pn < avail;
1343 }
1344 assert(pn <= SIZE_MAX - str_l);
1345 str_l += pn;
1346 }
1347 }
1348
1349 // zero padding as requested by the precision or by the minimal
1350 // field width for numeric conversions required?
1351 if (number_of_zeros_to_pad == 0) {
1352 // will not copy first part of numeric right now,
1353 // force it to be copied later in its entirety
1354 zero_padding_insertion_ind = 0;
1355 } else {
1356 // insert first part of numerics (sign or '0x') before zero padding
1357 if (zero_padding_insertion_ind > 0) {
1358 size_t zn = zero_padding_insertion_ind;
1359 if (str_avail) {
1360 size_t avail = str_m - str_l;
1361 memmove(str + str_l, str_arg, MIN(zn, avail));
1362 str_avail = zn < avail;
1363 }
1364 assert(zn <= SIZE_MAX - str_l);
1365 str_l += zn;
1366 }
1367
1368 // insert zero padding as requested by precision or min field width
1369 size_t zn = number_of_zeros_to_pad;
1370 if (str_avail) {
1371 size_t avail = str_m - str_l;
1372 memset(str + str_l, '0', MIN(zn, avail));
1373 str_avail = zn < avail;
1374 }
1375 assert(zn <= SIZE_MAX - str_l);
1376 str_l += zn;
1377 }
1378
1379 // insert formatted string
1380 // (or as-is conversion specifier for unknown conversions)
1381 if (str_arg_l > zero_padding_insertion_ind) {
1382 size_t sn = str_arg_l - zero_padding_insertion_ind;
1383 if (str_avail) {
1384 size_t avail = str_m - str_l;
1385 memmove(str + str_l,
1386 str_arg + zero_padding_insertion_ind,
1387 MIN(sn, avail));
1388 str_avail = sn < avail;
1389 }
1390 assert(sn <= SIZE_MAX - str_l);
1391 str_l += sn;
1392 }
1393
1394 // insert right padding
1395 if (justify_left) {
1396 assert(str_arg_l <= SIZE_MAX - number_of_zeros_to_pad);
1397 if (min_field_width > str_arg_l + number_of_zeros_to_pad) {
1398 // right blank padding to the field width
1399 size_t pn = min_field_width - (str_arg_l + number_of_zeros_to_pad);
1400 if (str_avail) {
1401 size_t avail = str_m - str_l;
1402 memset(str + str_l, ' ', MIN(pn, avail));
1403 str_avail = pn < avail;
1404 }
1405 assert(pn <= SIZE_MAX - str_l);
1406 str_l += pn;
1407 }
1408 }
1409
1410 xfree(tofree);
1411 }
1412 }
1413
1414 if (str_m > 0) {
1415 // make sure the string is nul-terminated even at the expense of
1416 // overwriting the last character (shouldn't happen, but just in case)
1417 str[str_l <= str_m - 1 ? str_l : str_m - 1] = '\0';
1418 }
1419
1420 if (tvs && tvs[arg_idx - 1].v_type != VAR_UNKNOWN) {
1421 EMSG(_("E767: Too many arguments to printf()"));
1422 }
1423
1424 // return the number of characters formatted (excluding trailing nul
1425 // character); that is, the number of characters that would have been
1426 // written to the buffer if it were large enough.
1427 return (int)str_l;
1428}
1429