diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index 74502ae6d3..83c84dfc36 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -129,6 +129,15 @@ typedef enum { kBffInitChangedtick = 2, } BufFreeFlags; +/// Calculate the percentage that `part` is of the `whole`. +int calc_percentage(int64_t part, int64_t whole) +{ + // With 32 bit longs and more than 21,474,836 lines multiplying by 100 + // causes an overflow, thus for large numbers divide instead. + return (part > 1000000) ? (int)(part / (whole / 100)) + : (int)((part * 100) / whole); +} + /// @return the highest possible buffer number int get_highest_fnum(void) { @@ -2871,8 +2880,9 @@ void buflist_list(exarg_T *eap) && (buf == curbuf || curwin->w_alt_fnum != buf->b_fnum))) { continue; } - if (buf_spname(buf) != NULL) { - xstrlcpy(NameBuff, buf_spname(buf), MAXPATHL); + char *name = buf_spname(buf); + if (name != NULL) { + xstrlcpy(NameBuff, name, MAXPATHL); } else { home_replace(buf, buf->b_fname, NameBuff, MAXPATHL, true); } @@ -2890,14 +2900,18 @@ void buflist_list(exarg_T *eap) } msg_putchar('\n'); - int len = vim_snprintf(IObuff, IOSIZE - 20, "%3d%c%c%c%c%c \"%s\"", - buf->b_fnum, - buf->b_p_bl ? ' ' : 'u', - buf == curbuf ? '%' : (curwin->w_alt_fnum == buf->b_fnum ? '#' : ' '), - buf->b_ml.ml_mfp == NULL ? ' ' : (buf->b_nwindows == 0 ? 'h' : 'a'), - ro_char, - changed_char, - NameBuff); + int len = (int)vim_snprintf_safelen(IObuff, IOSIZE - 20, "%3d%c%c%c%c%c \"%s\"", + buf->b_fnum, + buf->b_p_bl ? ' ' : 'u', + buf == curbuf ? '%' + : (curwin->w_alt_fnum + == buf->b_fnum ? '#' : ' '), + buf->b_ml.ml_mfp == NULL ? ' ' + : (buf->b_nwindows + == 0 ? 'h' : 'a'), + ro_char, + changed_char, + NameBuff); len = MIN(len, IOSIZE - 20); @@ -3213,87 +3227,85 @@ static bool buf_same_file_id(buf_T *buf, FileID *file_id) /// @param fullname when non-zero print full path void fileinfo(int fullname, int shorthelp, bool dont_truncate) { - char *p; - char *buffer = xmalloc(IOSIZE); + size_t bufferlen = 0; if (fullname > 1) { // 2 CTRL-G: include buffer number - vim_snprintf(buffer, IOSIZE, "buf %d: ", curbuf->b_fnum); - p = buffer + strlen(buffer); - } else { - p = buffer; + bufferlen = vim_snprintf_safelen(buffer, IOSIZE, "buf %d: ", curbuf->b_fnum); } - *p++ = '"'; - if (buf_spname(curbuf) != NULL) { - xstrlcpy(p, buf_spname(curbuf), (size_t)(IOSIZE - (p - buffer))); + buffer[bufferlen++] = '"'; + + char *name = buf_spname(curbuf); + if (name != NULL) { + bufferlen += vim_snprintf_safelen(buffer + bufferlen, + IOSIZE - bufferlen, "%s", name); } else { - char *name = (!fullname && curbuf->b_fname != NULL) - ? curbuf->b_fname - : curbuf->b_ffname; - home_replace(shorthelp ? curbuf : NULL, name, p, - (size_t)(IOSIZE - (p - buffer)), true); + name = (!fullname && curbuf->b_fname != NULL) ? curbuf->b_fname : curbuf->b_ffname; + home_replace(shorthelp ? curbuf : NULL, name, buffer + bufferlen, + IOSIZE - bufferlen, true); + bufferlen += strlen(buffer + bufferlen); } bool dontwrite = bt_dontwrite(curbuf); - vim_snprintf_add(buffer, IOSIZE, "\"%s%s%s%s%s%s", - curbufIsChanged() - ? (shortmess(SHM_MOD) ? " [+]" : _(" [Modified]")) : " ", - (curbuf->b_flags & BF_NOTEDITED) && !dontwrite - ? _("[Not edited]") : "", - (curbuf->b_flags & BF_NEW) && !dontwrite - ? _("[New]") : "", - (curbuf->b_flags & BF_READERR) - ? _("[Read errors]") : "", - curbuf->b_p_ro - ? (shortmess(SHM_RO) ? _("[RO]") : _("[readonly]")) : "", - (curbufIsChanged() - || (curbuf->b_flags & BF_WRITE_MASK) - || curbuf->b_p_ro) - ? " " : ""); - int n; - // With 32 bit longs and more than 21,474,836 lines multiplying by 100 - // causes an overflow, thus for large numbers divide instead. - if (curwin->w_cursor.lnum > 1000000) { - n = ((curwin->w_cursor.lnum) / - (curbuf->b_ml.ml_line_count / 100)); - } else { - n = ((curwin->w_cursor.lnum * 100) / - curbuf->b_ml.ml_line_count); - } + bufferlen += vim_snprintf_safelen(buffer + bufferlen, + IOSIZE - bufferlen, + "\"%s%s%s%s%s%s", + curbufIsChanged() + ? (shortmess(SHM_MOD) ? " [+]" : _(" [Modified]")) + : " ", + (curbuf->b_flags & BF_NOTEDITED) && !dontwrite + ? _("[Not edited]") : "", + (curbuf->b_flags & BF_NEW) && !dontwrite + ? _("[New]") : "", + (curbuf->b_flags & BF_READERR) + ? _("[Read errors]") : "", + curbuf->b_p_ro + ? (shortmess(SHM_RO) ? _("[RO]") : _("[readonly]")) + : "", + (curbufIsChanged() + || (curbuf->b_flags & BF_WRITE_MASK) + || curbuf->b_p_ro) + ? " " : ""); + if (curbuf->b_ml.ml_flags & ML_EMPTY) { - vim_snprintf_add(buffer, IOSIZE, "%s", _(no_lines_msg)); + bufferlen += vim_snprintf_safelen(buffer + bufferlen, + IOSIZE - bufferlen, "%s", _(no_lines_msg)); } else if (p_ru) { // Current line and column are already on the screen -- webb - vim_snprintf_add(buffer, IOSIZE, - NGETTEXT("%" PRId64 " line --%d%%--", - "%" PRId64 " lines --%d%%--", - curbuf->b_ml.ml_line_count), - (int64_t)curbuf->b_ml.ml_line_count, n); + bufferlen += vim_snprintf_safelen(buffer + bufferlen, + IOSIZE - bufferlen, + NGETTEXT("%" PRId64 " line --%d%%--", + "%" PRId64 " lines --%d%%--", + curbuf->b_ml.ml_line_count), + (int64_t)curbuf->b_ml.ml_line_count, + calc_percentage(curwin->w_cursor.lnum, + curbuf->b_ml.ml_line_count)); } else { - vim_snprintf_add(buffer, IOSIZE, - _("line %" PRId64 " of %" PRId64 " --%d%%-- col "), - (int64_t)curwin->w_cursor.lnum, - (int64_t)curbuf->b_ml.ml_line_count, - n); + bufferlen += vim_snprintf_safelen(buffer + bufferlen, + IOSIZE - bufferlen, + _("line %" PRId64 " of %" PRId64 " --%d%%-- col "), + (int64_t)curwin->w_cursor.lnum, + (int64_t)curbuf->b_ml.ml_line_count, + calc_percentage(curwin->w_cursor.lnum, + curbuf->b_ml.ml_line_count)); validate_virtcol(curwin); - size_t len = strlen(buffer); - (void)col_print(buffer + len, IOSIZE - len, - (int)curwin->w_cursor.col + 1, (int)curwin->w_virtcol + 1); + bufferlen += (size_t)col_print(buffer + bufferlen, IOSIZE - bufferlen, + curwin->w_cursor.col + 1, curwin->w_virtcol + 1); } - append_arg_number(curwin, buffer, IOSIZE); + append_arg_number(curwin, buffer + bufferlen, IOSIZE - bufferlen); if (dont_truncate) { // Temporarily set msg_scroll to avoid the message being truncated. // First call msg_start() to get the message in the right place. msg_start(); - n = msg_scroll; + int n = msg_scroll; msg_scroll = true; msg(buffer, 0); msg_scroll = n; } else { - p = msg_trunc(buffer, false, 0); + char *p = msg_trunc(buffer, false, 0); if (restart_edit != 0 || (msg_scrolled && !need_wait_return)) { // Need to repeat the message after redrawing when: // - When restart_edit is set (otherwise there will be a delay @@ -3310,10 +3322,10 @@ void fileinfo(int fullname, int shorthelp, bool dont_truncate) int col_print(char *buf, size_t buflen, int col, int vcol) { if (col == vcol) { - return vim_snprintf(buf, buflen, "%d", col); + return (int)vim_snprintf_safelen(buf, buflen, "%d", col); } - return vim_snprintf(buf, buflen, "%d-%d", col, vcol); + return (int)vim_snprintf_safelen(buf, buflen, "%d-%d", col, vcol); } static char *lasttitle = NULL; @@ -3324,7 +3336,6 @@ void maketitle(void) { char *title_str = NULL; char *icon_str = NULL; - int maxlen = 0; char buf[IOSIZE]; if (!redrawing()) { @@ -3339,6 +3350,8 @@ void maketitle(void) } if (p_title) { + int maxlen = 0; + if (p_titlelen > 0) { maxlen = MAX((int)(p_titlelen * Columns / 100), 10); } @@ -3371,19 +3384,19 @@ void maketitle(void) icon_str = p_iconstring; } } else { - char *buf_p = buf_spname(curbuf) != NULL - ? buf_spname(curbuf) - : path_tail(curbuf->b_ffname); // use file name only in icon - *icon_str = NUL; - // Truncate name at 100 bytes. - int len = (int)strlen(buf_p); - if (len > 100) { - len -= 100; - len += utf_cp_bounds(buf_p, buf_p + len).end_off; - buf_p += len; + char *name = buf_spname(curbuf); + if (name == NULL) { + name = path_tail(curbuf->b_ffname); } - STRCPY(icon_str, buf_p); - trans_characters(icon_str, IOSIZE); + // Truncate name at 100 bytes. + int namelen = (int)strlen(name); + if (namelen > 100) { + namelen -= 100; + namelen += utf_cp_bounds(name, name + namelen).end_off; + name += namelen; + } + STRCPY(buf, name); + trans_characters(buf, sizeof(buf)); } } @@ -3435,7 +3448,7 @@ void free_titles(void) #endif -/// Get relative cursor position in window into "buf[buflen]", in the localized +/// Get relative cursor position in window into "buf[]", in the localized /// percentage form like %99, 99%; using "Top", "Bot" or "All" when appropriate. int get_rel_pos(win_T *wp, char *buf, int buflen) { @@ -3446,7 +3459,6 @@ int get_rel_pos(win_T *wp, char *buf, int buflen) linenr_T above; // number of lines above window linenr_T below; // number of lines below window - int len; above = wp->w_topline - 1; above += win_get_fill(wp, wp->w_topline) - wp->w_topfill; @@ -3457,46 +3469,38 @@ int get_rel_pos(win_T *wp, char *buf, int buflen) } below = wp->w_buffer->b_ml.ml_line_count - wp->w_botline + 1; if (below <= 0) { - len = vim_snprintf(buf, (size_t)buflen, "%s", (above == 0) ? _("All") : _("Bot")); - } else if (above <= 0) { - len = vim_snprintf(buf, (size_t)buflen, "%s", _("Top")); - } else { - int perc = (above > 1000000 - ? (above / ((above + below) / 100)) - : (above * 100 / (above + below))); - // localized percentage value - len = vim_snprintf(buf, (size_t)buflen, _("%s%d%%"), (perc < 10) ? " " : "", perc); - } - if (len < 0) { - buf[0] = NUL; - len = 0; - } else if (len > buflen - 1) { - len = buflen - 1; + return (int)vim_snprintf_safelen(buf, (size_t)buflen, + "%s", (above == 0) ? _("All") : _("Bot")); } - return len; + if (above <= 0) { + return (int)vim_snprintf_safelen(buf, (size_t)buflen, + "%s", _("Top")); + } + + // localized percentage value + return (int)vim_snprintf_safelen(buf, (size_t)buflen, + _("%2d%%"), calc_percentage(above, above + below)); } -/// Append (2 of 8) to "buf[buflen]", if editing more than one file. +/// Append (2 of 8) to "buf[]", if editing more than one file. /// /// @param wp window whose buffers to check /// @param[in,out] buf string buffer to add the text to /// @param buflen length of the string buffer /// -/// @return true if it was appended. -bool append_arg_number(win_T *wp, char *buf, int buflen) +/// @return the number of characters appended. +int append_arg_number(win_T *wp, char *buf, size_t buflen) FUNC_ATTR_NONNULL_ALL { // Nothing to do if (ARGCOUNT <= 1) { - return false; + return 0; } const char *msg = wp->w_arg_idx_invalid ? _(" ((%d) of %d)") : _(" (%d of %d)"); - char *p = buf + strlen(buf); // go to the end of the buffer - vim_snprintf(p, (size_t)(buflen - (p - buf)), msg, wp->w_arg_idx + 1, ARGCOUNT); - return true; + return (int)vim_snprintf_safelen(buf, buflen, msg, wp->w_arg_idx + 1, ARGCOUNT); } /// Make "*ffname" a full file name, set "*sfname" to "*ffname" if not NULL. @@ -3761,13 +3765,13 @@ void do_modelines(int flags) /// @param flags Same as for do_modelines(). static int chk_modeline(linenr_T lnum, int flags) { - char *s; char *e; - intmax_t vers; int retval = OK; int prev = -1; - for (s = ml_get(lnum); *s != NUL; s++) { + char *s = ml_get(lnum); + char *line_end = s + ml_get_len(lnum); + for (; *s != NUL; s++) { if (prev == -1 || ascii_isspace(prev)) { if ((prev != -1 && strncmp(s, "ex:", 3) == 0) || strncmp(s, "vi:", 3) == 0) { @@ -3780,6 +3784,7 @@ static int chk_modeline(linenr_T lnum, int flags) } else { e = s + 3; } + intmax_t vers; if (!try_getdigits(&e, &vers)) { continue; } @@ -3807,8 +3812,12 @@ static int chk_modeline(linenr_T lnum, int flags) s++; } while (s[-1] != ':'); - char *linecopy; // local copy of any modeline found - s = linecopy = xstrdup(s); // copy the line, it will change + size_t len = (size_t)(line_end - s); // remember the line length so we can restore + // 'line_end' after the copy + char *linecopy; // local copy of any modeline found + s = linecopy = xstrnsave(s, len); // copy the line, it will change + + line_end = s + len; // restore 'line_end' // prepare for emsg() estack_push(ETYPE_MODELINE, "modelines", lnum); @@ -3824,7 +3833,8 @@ static int chk_modeline(linenr_T lnum, int flags) // Skip over "\:", replacing it with ":". for (e = s; *e != ':' && *e != NUL; e++) { if (e[0] == '\\' && e[1] == ':') { - STRMOVE(e, e + 1); + memmove(e, e + 1, (size_t)(line_end - (e + 1)) + 1); // +1 for NUL + line_end--; } } if (*e == NUL) { @@ -3842,7 +3852,7 @@ static int chk_modeline(linenr_T lnum, int flags) break; } end = true; - s = vim_strchr(s, ' ') + 1; + s += (*(s + 2) == ' ') ? 3 : 4; } *e = NUL; // truncate the set command @@ -3863,7 +3873,8 @@ static int chk_modeline(linenr_T lnum, int flags) break; } } - s = e + 1; // advance to next part + s = (e == line_end) ? e : e + 1; // advance to next part + // careful not to go off the end } estack_pop(); diff --git a/src/nvim/statusline.c b/src/nvim/statusline.c index a806a0c73f..3d8610d26f 100644 --- a/src/nvim/statusline.c +++ b/src/nvim/statusline.c @@ -872,7 +872,6 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, OptIndex op #define TMPLEN 70 char buf_tmp[TMPLEN]; - char win_tmp[TMPLEN]; char *usefmt = fmt; const bool save_redraw_not_allowed = redraw_not_allowed; const bool save_KeyTyped = KeyTyped; @@ -1066,11 +1065,11 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, OptIndex op // Otherwise there would be no reason to do this step. if (curitem > stl_groupitems[groupdepth] + 1 && stl_items[stl_groupitems[groupdepth]].minwid == 0) { - // remove group if all items are empty and highlight group - // doesn't change int group_start_userhl = 0; int group_end_userhl = 0; int n; + // remove group if all items are empty and highlight group + // doesn't change for (n = stl_groupitems[groupdepth] - 1; n >= 0; n--) { if (stl_items[n].type == Highlight) { group_start_userhl = group_end_userhl = stl_items[n].minwid; @@ -1320,12 +1319,13 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, OptIndex op switch (opt) { case STL_FILEPATH: case STL_FULLPATH: - case STL_FILENAME: + case STL_FILENAME: { // Set fillable to false so that ' ' in the filename will not // get replaced with the fillchar fillable = false; - if (buf_spname(wp->w_buffer) != NULL) { - xstrlcpy(NameBuff, buf_spname(wp->w_buffer), MAXPATHL); + char *name = buf_spname(wp->w_buffer); + if (name != NULL) { + xstrlcpy(NameBuff, name, MAXPATHL); } else { char *t = (opt == STL_FULLPATH) ? wp->w_buffer->b_ffname : wp->w_buffer->b_fname; @@ -1338,6 +1338,8 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, OptIndex op str = path_tail(NameBuff); } break; + } + case STL_VIM_EXPR: // '{' { char *block_start = fmt_p - 1; @@ -1360,9 +1362,9 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, OptIndex op } fmt_p++; if (reevaluate) { - out_p[-1] = 0; // remove the % at the end of %{% expr %} + out_p[-1] = NUL; // remove the % at the end of %{% expr %} } else { - *out_p = 0; + *out_p = NUL; } // Move our position in the output buffer @@ -1374,8 +1376,8 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, OptIndex op // Store the current buffer number as a string variable vim_snprintf(buf_tmp, sizeof(buf_tmp), "%d", curbuf->b_fnum); set_internal_string_var("g:actual_curbuf", buf_tmp); - vim_snprintf(win_tmp, sizeof(win_tmp), "%d", curwin->handle); - set_internal_string_var("g:actual_curwin", win_tmp); + vim_snprintf(buf_tmp, sizeof(buf_tmp), "%d", curwin->handle); + set_internal_string_var("g:actual_curwin", buf_tmp); buf_T *const save_curbuf = curbuf; win_T *const save_curwin = curwin; @@ -1402,7 +1404,7 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, OptIndex op // Check if the evaluated result is a number. // If so, convert the number to an int and free the string. - if (str != NULL && *str != 0) { + if (str != NULL && *str != NUL) { if (*skipdigits(str) == NUL) { num = atoi(str); XFREE_CLEAR(str); @@ -1412,22 +1414,15 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, OptIndex op // If the output of the expression needs to be evaluated // replace the %{} block with the result of evaluation - if (reevaluate && str != NULL && *str != 0 + if (reevaluate && str != NULL && *str != NUL && strchr(str, '%') != NULL && evaldepth < MAX_STL_EVAL_DEPTH) { size_t parsed_usefmt = (size_t)(block_start - usefmt); - size_t str_length = strlen(str); - size_t fmt_length = strlen(fmt_p); - size_t new_fmt_len = parsed_usefmt + str_length + fmt_length + 3; + size_t new_fmt_len = parsed_usefmt + strlen(str) + strlen(fmt_p) + 3; char *new_fmt = xmalloc(new_fmt_len * sizeof(char)); - char *new_fmt_p = new_fmt; - new_fmt_p = (char *)memcpy(new_fmt_p, usefmt, parsed_usefmt) + parsed_usefmt; - new_fmt_p = (char *)memcpy(new_fmt_p, str, str_length) + str_length; - new_fmt_p = (char *)memcpy(new_fmt_p, "%}", 2) + 2; - new_fmt_p = (char *)memcpy(new_fmt_p, fmt_p, fmt_length) + fmt_length; - *new_fmt_p = 0; - new_fmt_p = NULL; + vim_snprintf(new_fmt, new_fmt_len, "%.*s%s%s%s", + (int)parsed_usefmt, usefmt, str, "%}", fmt_p); if (usefmt != fmt) { xfree(usefmt); @@ -1482,7 +1477,7 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, OptIndex op } case STL_PERCENTAGE: - num = ((wp->w_cursor.lnum * 100) / wp->w_buffer->b_ml.ml_line_count); + num = calc_percentage(wp->w_cursor.lnum, wp->w_buffer->b_ml.ml_line_count); break; case STL_ALTPERCENT: @@ -1501,16 +1496,8 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, OptIndex op case STL_ARGLISTSTAT: fillable = false; - - // Note: This is important because `append_arg_number` starts appending - // at the end of the null-terminated string. - // Setting the first byte to null means it will place the argument - // number string at the beginning of the buffer. - buf_tmp[0] = 0; - - // Note: The call will only return true if it actually - // appended data to the `buf_tmp` buffer. - if (append_arg_number(wp, buf_tmp, (int)sizeof(buf_tmp))) { + buf_tmp[0] = NUL; + if (append_arg_number(wp, buf_tmp, sizeof(buf_tmp)) > 0) { str = buf_tmp; } break; @@ -1815,7 +1802,7 @@ stcsign: // Note: The `*` means we take the width as one of the arguments *t++ = '*'; *t++ = base == kNumBaseHexadecimal ? 'X' : 'd'; - *t = 0; + *t = NUL; // } // { Determine how many characters the number will take up when printed @@ -1858,18 +1845,14 @@ stcsign: *t++ = '%'; // Use the same base as the first number *t = t[-3]; - *++t = 0; + *++t = NUL; // } - vim_snprintf(out_p, remaining_buf_len, nstr, 0, num, n); + out_p += vim_snprintf_safelen(out_p, remaining_buf_len, nstr, 0, num, n); } else { - vim_snprintf(out_p, remaining_buf_len, nstr, minwid, num); + out_p += vim_snprintf_safelen(out_p, remaining_buf_len, nstr, minwid, num); } - // Advance the output buffer position to the end of the - // number we just printed - out_p += strlen(out_p); - // Otherwise, there was nothing to print so mark the item as empty } else { stl_items[curitem].type = Empty; @@ -1895,6 +1878,8 @@ stcsign: } *out_p = NUL; + // Length of out[] used (excluding the NUL) + size_t outputlen = (size_t)(out_p - out); // Subtract offset from `itemcnt` and restore `curitem` to previous recursion level. int itemcnt = curitem - evalstart; curitem = evalstart; @@ -1967,10 +1952,12 @@ stcsign: // Truncate the output *trunc_p++ = '>'; - *trunc_p = 0; + *trunc_p = NUL; // Truncate at the truncation point we found } else { + char *end = out + outputlen; + // { Determine how many bytes to remove int trunc_len = 0; while (width >= maxwidth) { @@ -1981,7 +1968,8 @@ stcsign: // { Truncate the string char *trunc_end_p = trunc_p + trunc_len; - STRMOVE(trunc_p + 1, trunc_end_p); + memmove(trunc_p + 1, trunc_end_p, (size_t)(end - trunc_end_p) + 1); // +1 for NUL + end -= (size_t)(trunc_end_p - (trunc_p + 1)); // Put a `<` to mark where we truncated at *trunc_p = '<'; @@ -2009,13 +1997,15 @@ stcsign: if (width + 1 < maxwidth) { // Advance the pointer to the end of the string - trunc_p = trunc_p + strlen(trunc_p); + trunc_p = end; } // Fill up for half a double-wide character. while (++width < maxwidth) { schar_get_adv(&trunc_p, fillchar); + end = trunc_p; } + (void)end; } width = maxwidth; @@ -2023,7 +2013,7 @@ stcsign: // add characters at the separate marker (if there is one) to // fill up the available space. } else if (width < maxwidth - && strlen(out) + (size_t)(maxwidth - width) + 1 < outlen) { + && outputlen + (size_t)(maxwidth - width) + 1 < outlen) { // Find how many separators there are, which we will use when // figuring out how many groups there are. int num_separators = 0; diff --git a/src/nvim/strings.c b/src/nvim/strings.c index c56cda5699..24642374ef 100644 --- a/src/nvim/strings.c +++ b/src/nvim/strings.c @@ -791,6 +791,29 @@ static const char *infinity_str(bool positive, char fmt_spec, int force_sign, return table[idx]; } +/// Like vim_snprintf() except the return value can be safely used to increment a +/// buffer length. +/// Normal `snprintf()` (and `vim_snprintf()`) returns the number of bytes that +/// would have been copied if the destination buffer was large enough. +/// This means that you cannot rely on it's return value for the destination +/// length because the destination may be shorter than the source. This function +/// guarantees the returned length will never be greater than the destination length. +size_t vim_snprintf_safelen(char *str, size_t str_m, const char *fmt, ...) +{ + va_list ap; + int str_l; + + va_start(ap, fmt); + str_l = vim_vsnprintf(str, str_m, fmt, ap); + va_end(ap); + + if (str_l < 0) { + *str = NUL; + return 0; + } + return ((size_t)str_l >= str_m) ? str_m - 1 : (size_t)str_l; +} + int vim_vsnprintf(char *str, size_t str_m, const char *fmt, va_list ap) { return vim_vsnprintf_typval(str, str_m, fmt, ap, NULL);