Skip to content

Updates to documentation for string printf functions #3411

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 5 commits into from

Conversation

adr26
Copy link
Contributor

@adr26 adr26 commented Sep 27, 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

andrew-rogers-sndk and others added 5 commits September 27, 2021 21:53
* Fix description of behaviour when encoding errors occur.
* Clarify truncation behaviour (matching vsnprintf_s family of functions).
* Add error condition table (matching vsnprintf_s family of functions).
* Fix description of behaviour when encoding errors occur.
* Clarify truncation behaviour of vsnprintf_s family of functions.
* Clarify error condition table for vsnprintf_s family of functions.
* Clarify the return value behaviour of snprintf family of functions.
* Clarify truncation behaviour of snprintf family of functions.
…th snprintf family of functions:

* Fix description of behaviour when encoding errors occur.
* Clarify the return value behaviour of vsnprintf family of functions.
* Clarify truncation behaviour of vsnprintf family of functions.
@PRMerger9
Copy link
Contributor

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

@ktoliver
Copy link
Contributor

#label:"aq-pr-triaged"

@PRMerger20 PRMerger20 added the aq-pr-triaged Tracking label for the PR review team label Sep 27, 2021
@TylerMSFT
Copy link
Collaborator

Thank you very much, @adr26. I am referring this to the dev who works in this area for review. They are preparing for a conference right now so there may be some delay before he can look at it.
Really appreciate you helping out with the documentation.

@adr26
Copy link
Contributor Author

adr26 commented Sep 29, 2021

@TylerMSFT - thanks for the update:

I put this together on the back of some work I'd done for a codebase my team owns, which ships internally with sources, and where the codebase still has to support some clients who compile it with VS2008(!) and VS2013. C99-conformant snprintf() isn't available on these older VS versions (it was introduced with the move to UCRT in VS2015, and Windows shipping the UCRT).

We implemented wrappers for the snprintf() functions on the older VS versions which don't use UCRT, and I had to carefully characterise the behaviour of the snprintf() function families in both the latest VS/UCRT & when compiling against the older versions of VS — allowing the rest of our codebase to be built as-if we were always using the C99-conformant snprintf() (the version for VS2015+ wrapper being a straight pass-through to the UCRT implementation).

Having done that, I wanted to make sure that what I'd learned about the differences between the behaviour I'd observed in my characterisation and what was in the docs was preserved for posterity: mostly for my sanity, in case I ever have to look at that code again, but also for the benefit of the wider community.

As such, I hope this contribution is helpful, but it isn't very high-priority. I hope your dev's preparations go well, and I'll be glad to discuss further once they've got time to look at it: thank you again!

Andrew R

@TylerMSFT TylerMSFT added the product-team Work item for the MSFT product team. Feel free to help. label Mar 4, 2022
@TylerMSFT
Copy link
Collaborator

@adr26, you probably thought this was lost and forgotten. But no-I finally had time to go through it in detail. I'm going to close this, but nothing is lost. I took all this material and converted it to a table in this PR I thought a table would be a faster way for people to process the info that otherwise takes paragraphs to explain. You are welcome to review that PR because this space provides plenty of opportunity for errors. I tested the claims in the new tables against VS 2022.
The PR I refer to above makes extensive use of this one. So, this work wasn't lost or ignored. It lives on--just in a different form.

@TylerMSFT TylerMSFT closed this Jun 29, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
aq-pr-triaged Tracking label for the PR review team Change sent to author cpp-ucrt/tech do-not-merge product-team Work item for the MSFT product team. Feel free to help. visual-cpp/prod
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants