Skip to content
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

lib: deprecate G_snprintf() and replace with C99 snprintf() #4189

Merged
merged 4 commits into from
Sep 3, 2024

Conversation

nilason
Copy link
Contributor

@nilason nilason commented Aug 16, 2024

Beginning with UCRT in Visual Studio 2015 and Windows 10, the behaviour of snprintf() (and vsnprintf()) is now C99 standard conformant (source).

Thus, the output is now always null-terminated. The justification for an internal fix for that is no longer needed.

I should add that the code base is already using snprintf() in a number of places.

Beginning with UCRT in Visual Studio 2015 and Windows 10, the behaviour
of snprintf (and vsnprintf) is now C99 standard conformant.

Thus, the output is now always null-terminated. The justification for a
internal fix for that is no longer needed.
@nilason nilason added this to the 8.5.0 milestone Aug 16, 2024
@nilason nilason self-assigned this Aug 16, 2024
@github-actions github-actions bot added vector Related to vector data processing raster Related to raster data processing temporal Related to temporal data processing C Related code is in C C++ Related code is in C++ libraries module imagery raster3d labels Aug 16, 2024
@echoix
Copy link
Member

echoix commented Aug 16, 2024

When you say deprecate, is it only in some docs, or by using super strict compiler flags we can make our builds in CI fail for it, to not introduce back some usages of it? Users compiling with normal flags would still be able to successfully build.

@wenzeslaus
Copy link
Member

Do I understand correctly that the following in Microsoft documentation is a typo?

Also, snprintf() includes one more character in the output because it doesn't null-terminate the buffer.

@nilason
Copy link
Contributor Author

nilason commented Aug 16, 2024

Do I understand correctly that the following in Microsoft documentation is a typo?

Also, snprintf() includes one more character in the output because it doesn't null-terminate the buffer.

That's most likely. Further down you can read "snprintf always stores a terminating NULL character, truncating the output if necessary.".

@nilason
Copy link
Contributor Author

nilason commented Aug 16, 2024

When you say deprecate, is it only in some docs, or by using super strict compiler flags we can make our builds in CI fail for it, to not introduce back some usages of it? Users compiling with normal flags would still be able to successfully build.

I was foremost thinking in documentation terms (it will end up listed here).
It will be easier the day we comply to >= C23, with the new [[ deprecated ]] attribute. We may consider implementing a macro for clang and/or gcc for checking this on CI, that is out of scope for this PR though.

@nilason
Copy link
Contributor Author

nilason commented Aug 16, 2024

Among Addons only r.sim.water.mp make use of G_snprintf(), whereas i.wavelet is the only one using snprintf().

@wenzeslaus
Copy link
Member

Among Addons only r.sim.water.mp make use of G_snprintf()

No need to change that. It should be removed given the parallel version is now in core.

@echoix
Copy link
Member

echoix commented Aug 16, 2024

When you say deprecate, is it only in some docs, or by using super strict compiler flags we can make our builds in CI fail for it, to not introduce back some usages of it? Users compiling with normal flags would still be able to successfully build.

I was foremost thinking in documentation terms (it will end up listed here).

It will be easier the day we comply to >= C23, with the new [[ deprecated ]] attribute. We may consider implementing a macro for clang and/or gcc for checking this on CI, that is out of scope for this PR though.

That's a bit what I was thinking about. I didn't know if it existed in C/C++. I've tried to read a bit about it, and it doesn't seem like it would be as easy as in C# where we could've placed a conditional compilation around the attribute to only appear when the compiler supports it, at a single place, and all usages of that function would appear as warnings during compilation. So it's fine with me to leave it for later.

Copy link
Member

@echoix echoix left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll resume what I understand from all this, to do like rubberducking, but for my comprehension of what I'm approving:

From all the changed files, there are two patterns used plus two exceptions.

  • For C files, stdio is imported if not imported yet, then G_as printer is replaced with snprintf.
  • For C++ files, cstdio is imported if not imported yet, then G_snprintf is replaced with std::snprintf.

For the two exceptions:

  • the file that used to host G_snprintf is changed to use snprintf
  • raster3d/r3.out.bin/main.c: this is the only file to double check. It has a unique change (with respect to the other changes in this PR). It declares the character array for the output filename with a size of GNAME_MAX instead of being a char pointer. Further below, it checks if there is an answer to the output parameter. If so, it is used as is with an empty string extension appended. If the output parameter doesn't have an answer, the name is used instead, and appended with a .bin extension. The complete snprintf call, since it returns the size (characters) that the buffer would have taken if it was successful, is used to check if it is bigger than the size of the outfile buffer (that was defined in this PR to GNAME_MAX). An error message is shown if the name is too long, and addition of this PR.

Ah, the rubberducking worked, there's a situation that isn't handled here.
If we look at the cppreference page, https://en.cppreference.com/w/cpp/io/c/fprintf

  1. Number of characters that would have been written for a sufficiently large buffer if successful (not including the terminating null character), or a negative value if an error occurred. Thus, the (null-terminated) output has been completely written if and only if the returned value is nonnegative and less than buf_size.

The new logic seems to miss what happens when snprintf returns a negative value.

If it wasn't for that last point, I was approving this PR.

@nilason
Copy link
Contributor Author

nilason commented Aug 17, 2024

  • the file that used to host G_snprintf is changed to use snprintf

This is not correct, it still uses vsnprintf(), but I have removed the unnecessary fix (adding null-terminator), and added notes (and cleaned up #iimports).

  • raster3d/r3.out.bin/main.c: this is the only file to double check. It has a unique change

The change of G_snprintf to C99 snprintf, made the compiler aware of a bug. The size of a pointer was checked instead of the size of the array. I addressed this and added a check whether the output string was truncated. This is the common practice (when checked at all!). This is not the PR to check for all potential errors.

@echoix
Copy link
Member

echoix commented Aug 17, 2024

  • the file that used to host G_snprintf is changed to use snprintf

This is not correct, it still uses vsnprintf(), but I have removed the unnecessary fix (adding null-terminator), and added notes (and cleaned up #iimports).

You're right, I missed the v when writing back from memory and at my first reading.

  • raster3d/r3.out.bin/main.c: this is the only file to double check. It has a unique change

The change of G_snprintf to C99 snprintf, made the compiler aware of a bug. The size of a pointer was checked instead of the size of the array. I addressed this and added a check whether the output string was truncated. This is the common practice (when checked at all!). This is not the PR to check for all potential errors.

So you are saying that it doesn't need to be checked for negative return value at all then? Or you do not want it here?

@echoix
Copy link
Member

echoix commented Aug 27, 2024

Was it almost ready?

@nilason
Copy link
Contributor Author

nilason commented Aug 29, 2024

Was it almost ready?

It is ready.

@echoix echoix merged commit 9222822 into OSGeo:main Sep 3, 2024
26 checks passed
a0x8o pushed a commit to a0x8o/grass that referenced this pull request Sep 5, 2024
* lib: deprecate G_snprintf() and replace with C99 snprintf()

Beginning with UCRT in Visual Studio 2015 and Windows 10, the behaviour of snprintf (and vsnprintf) is now C99 standard conformant.

Thus, the output is now always null-terminated. The justification for a internal fix for that is no longer needed.

* r3.out.bin: fix -Werror=sizeof-pointer-memaccess GCC warning

* cast explicitly

* add missing #include
a0x8o pushed a commit to a0x8o/grass that referenced this pull request Sep 5, 2024
* lib: deprecate G_snprintf() and replace with C99 snprintf()

Beginning with UCRT in Visual Studio 2015 and Windows 10, the behaviour of snprintf (and vsnprintf) is now C99 standard conformant.

Thus, the output is now always null-terminated. The justification for a internal fix for that is no longer needed.

* r3.out.bin: fix -Werror=sizeof-pointer-memaccess GCC warning

* cast explicitly

* add missing #include
a0x8o pushed a commit to a0x8o/grass that referenced this pull request Sep 5, 2024
* lib: deprecate G_snprintf() and replace with C99 snprintf()

Beginning with UCRT in Visual Studio 2015 and Windows 10, the behaviour of snprintf (and vsnprintf) is now C99 standard conformant.

Thus, the output is now always null-terminated. The justification for a internal fix for that is no longer needed.

* r3.out.bin: fix -Werror=sizeof-pointer-memaccess GCC warning

* cast explicitly

* add missing #include
@nilason nilason deleted the fix_deprecate_G_snprintf branch September 11, 2024 07:46
Mahesh1998 pushed a commit to Mahesh1998/grass that referenced this pull request Sep 19, 2024
* lib: deprecate G_snprintf() and replace with C99 snprintf()

Beginning with UCRT in Visual Studio 2015 and Windows 10, the behaviour of snprintf (and vsnprintf) is now C99 standard conformant.

Thus, the output is now always null-terminated. The justification for a internal fix for that is no longer needed.

* r3.out.bin: fix -Werror=sizeof-pointer-memaccess GCC warning

* cast explicitly

* add missing #include
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
C Related code is in C C++ Related code is in C++ imagery libraries module raster Related to raster data processing raster3d temporal Related to temporal data processing vector Related to vector data processing
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants