Skip to content

Updates to documentation for string printf functions #3328

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 6 commits into from

Conversation

adr26
Copy link
Contributor

@adr26 adr26 commented Aug 26, 2021

The behaviour of vsnprintf() with count == 0 and buffer != NULL is to return the length of the formatted data, rather than returning -1 — as the documentation currently states. I have cross-referenced the behaviour of vsnprintf() with the C specification, and confirmed that the implementation behaviour (returning the length of the formatted data) conforms to the C specification and is correct. Therefore, the current vsnprintf() documentation is incorrect and needs to be updated.

After making this observation, I conducted a broader review of the documentation for the *sn[w]printf* family of functions, making these updates as a result.

In general, I've corrected a number of errors in the description of the behaviour of the *sn[w]printf* family of functions, and have aligned the information for:

  • _snprintf_s()/_snwprintf_s() and _vsnprintf_s()/_vsnwprintf_s()
  • snprintf()/_snprintf()/_snwprintf() and vsnprintf()/_vsnprintf()/_vsnwprintf()

A detailed description of the problems addressed on each page is given below:

_snprintf_s()/_snwprintf_s():

  • Add description of behaviour which occurs when an encoding error occurs during formatting — setting errno to EILSEQ in all cases and either returning a negative value for a string conversion specifier, or skipping the character for a character conversion specifier.

  • Clarify that in cases where truncation does not occur, null-termination is performed

  • Clarify truncation behaviour:

    • If count < sizeOfBuffer, but formatted data > count, then formatted data is truncated to count characters, null-terminated and -1 is returned with no error handler / update to errno
    • With count == _TRUNCATE, and formatted data >= sizeOfBuffer, then formatted data is truncated to (sizeOfBuffer - 1) characters, null-terminated and -1 is returned with no error handler / update to errno
    • If count >= sizeOfBuffer, formatted data >= sizeOfBuffer, and count != _TRUNCATE, the invalid parameter handler is invoked. If continued, the functions truncate the buffer to an empty string, return -1 and set errno to ERANGE
  • Clarify error handling behaviour:

    • If buffer is NULL, an EINVAL error occurs if (and only if) either count or sizeOfBuffer are != 0 (if both are 0, no error occurs and 0 is returned)
    • An EINVAL error does not occur in all cases where count == 0, only when one of the other conditions are met (count cannot be negative, as size_t is unsigned)
    • If sizeOfBuffer (not count!) == 0, and additionally if buffer is not NULL, then an EINVAL error occurs
  • Add copy of error conditions table from _vsnprintf_s()/_vsnwprintf_s().

snprintf()/_snprintf()/_snwprintf():

  • Add description of behaviour which occurs when an encoding error occurs during formatting — setting errno to EILSEQ in all cases and either returning a negative value for a string conversion specifier, or skipping the character for a character conversion specifier.

  • Clarify truncation behaviour:

    • For snprintf(), clarify that null-termination by writing to buffer[count-1] is only performed if count != 0.
    • For _snprintf()/_snwprintf(), clarify that non-terminated truncation is performed only when buffer != NULL (if buffer == NULL and count != 0, it is an error and if if buffer == NULL and count == 0, the length of the formatted data is returned).

_vsnprintf_s()/_vsnwprintf_s():

  • Add description of behaviour which occurs when an encoding error occurs during formatting — setting errno to EILSEQ in all cases and either returning a negative value for a string conversion specifier, or skipping the character for a character conversion specifier.

  • Clarify that in cases where truncation does not occur, null-termination is performed

  • Clarify truncation behaviour:

    • Note that if count >= sizeOfBuffer and formatted data >= sizeOfBuffer, then only if also count != _TRUNCATE the invalid parameter handler is invoked, etc.
  • Clarify error handling behaviour:

    • If buffer is NULL, an EINVAL error occurs if (and only if) either count or sizeOfBuffer are != 0 (if both are 0, no error occurs and 0 is returned)
    • An EINVAL error does not occur in all cases where count == 0, only when one of the other conditions are met (count cannot be negative, as size_t is unsigned)
    • If sizeOfBuffer (not count!) == 0, and additionally if buffer is not NULL, then an EINVAL error occurs
  • Update error conditions table to correct from count == 0 to sizeOfBuffer == 0 as the condition required for an EINVAL error (when buffer != NULL).

vsnprintf()/_vsnprintf()/_vsnwprintf():

  • Add description of behaviour which occurs when an encoding error occurs during formatting — setting errno to EILSEQ in all cases and either returning a negative value for a string conversion specifier, or skipping the character for a character conversion specifier.

  • Clarify that in cases where truncation does not occur, null-termination is performed

  • Clarify truncation behaviour:

    • For vsnprintf(), clarify that null-termination by writing to buffer[count-1] is only performed if count != 0.
    • For _vsnprintf()/_vsnwprintf(), clarify that non-terminated truncation is performed only when buffer != NULL (if buffer == NULL and count != 0, it is an error and if if buffer == NULL and count == 0, the length of the formatted data is returned).
    • For vsnprintf(), clarify that if count == 0 and buffer != NULL, the length of the formatted data is returned, rather than -1 (as is the case for _vsnprintf()/_vsnwprintf()).

To verify the behaviour of these functions, I constructed the attached test code (printf.zip). The updated descriptions of these functions' behaviour can be verified using the output from this:

C:\Temp>cl /Zi /Od /MTd printf.c && printf >printf.txt
Microsoft (R) C/C++ Optimizing Compiler Version 19.30.30528 for x86
Copyright (C) Microsoft Corporation.  All rights reserved.

printf.c
Microsoft (R) Incremental Linker Version 14.30.30528.0
Copyright (C) Microsoft Corporation.  All rights reserved.

/out:printf.exe
/debug
printf.obj

…nt is zero, len is returned as the count of characters required to format the output (not including the terminating null).

* Clarify that [v]snprintf() will not attempt to truncate a zero-character formatted data string when count = 0, by placing a null-terminator at buffer[-1].
* Clarify that for functions other than [v]snprintf(), if len > count and buffer is a null pointer, then either len will be returned - without error - if count is zero, or the invalid parameter handler will be invoked if count is nonzero.
@PRMerger16
Copy link
Contributor

@adr26 : Thanks for your contribution! The author(s) have been notified to review your proposed change.

@adr26
Copy link
Contributor Author

adr26 commented Aug 26, 2021

I have incorrectly conflated the documentation on encoding errors (for wide string function variants) with the errors that can occur during formatting. From the C specification:

Encoding errors occur if the character sequence presented to the underlying mbrtowc function do not form a valid (generalized) multibyte character, or if the code value passed to the underlying wcrtomb does not correspond to a valid (generalized) multibyte character.

I will add a new test case for this to my test code, update the documentation accordingly and re-open the PR. Sorry for the confusion.

Thanks, Andrew R

@adr26 adr26 changed the title Updates to documentation for snprintf-style functions Updates to documentation for string printf functions Sep 16, 2021
@adr26
Copy link
Contributor Author

adr26 commented Sep 27, 2021

I added test cases to my test code to examine the behaviour of the various families of snprintf functions when an encoding error occurs.

In all cases, errno is set to EILSEQ without invoking the invalid parameter handler, and either the function returns a negative value (for a string conversion specifier), or (for a character conversion specifier) the character is simply skipped.

I have updated the PR description, plus updated the documentation accordingly — having also rebased to the latest code from the main cpp-docs repo.

Please let me know if you've any questions on what I've done.

Thanks, Andrew R

@adr26
Copy link
Contributor Author

adr26 commented Sep 27, 2021

Ah, alas — GitHub won't let me re-open this PR! I will set up a new one...

@TylerMSFT
Copy link
Collaborator

I can't reopen it either. I'll watch for your new one.

@adr26
Copy link
Contributor Author

adr26 commented Sep 28, 2021

Thanks @tycastMSFT — I've opened a new PR: #3411, where I've rolled in my corrected descriptions of the handling of encoding errors.

I realise that it'll need dev review, and there may be a discussion of to what extent you want to codify the current encoding-error-handling behaviour, but hopefully my changes to the docs are an accurate description of the current CRT behaviour and will serve as a useful basis for discussing how the docs should be updated (and may perhaps even be able to be accepted wholesale!)

Andrew R

@tycastMSFT
Copy link
Member

tycastMSFT commented Sep 28, 2021 via email

@colin-home
Copy link
Contributor

@TylerMSFT Do you not see a "Reopen and comment" button at the bottom of a comment box like this one? There may be other ways to reopen a PR, but this one is how I usually do it. If you don't, then maybe I didn't set up your admin privileges correctly.

@TylerMSFT
Copy link
Collaborator

@corob-msft - I see the button but it won't respond, i.e. it's not clickable.

@adr26
Copy link
Contributor Author

adr26 commented Sep 28, 2021

Hi @corob-msft, @TylerMSFT,

Please ignore #3328 (I think github refuses to allow you to re-open a PR if you force-pushed, which I did to my repo once I'd rebased my changes with up-to-date changes from the MicrosoftDocs repo).

Instead, please look at my new PR: #3411, where I've rolled in my corrected descriptions of the handling of encoding errors.

I realise that the PR will need dev review, and there may be a discussion of to what extent you want to codify the current encoding-error-handling behaviour, but hopefully my changes to the docs in #3411 are an accurate description of the current CRT behaviour and will serve as a useful basis for discussing how the docs should be updated (and may perhaps even be able to be accepted wholesale!)

Andrew R

@colin-home
Copy link
Contributor

@adr26 Don't worry, we'll work with your latest PR. We're just curious why one of our buttons doesn't work as expected. There's hover text associated with the button: "There is already an open pull request from adr26:master to MicrosoftDocs:master". It may be that you can only have one open PR from a particular branch at a time? I bet that's it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants