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

Add POSIX's C conversion specifier to printf funcs #1276

Merged
merged 1 commit into from
Sep 3, 2024

Conversation

GabrielRavier
Copy link
Collaborator

@GabrielRavier GabrielRavier commented Sep 1, 2024

POSIX specifies the C conversion specifier as being "equivalent to %lc",
i.e. printf("%C", arg) is equivalent in behaviour to printf("%lc", arg).

This patch implements this conversion specifier, and adds a test for it,
alongside another test, which ensures that va_arg uses the correct size,
even though we set signbit to 63 in the code (which one might think will
result in the wrong size of argument being va_arg-ed, but having signbit
set to 63 is in fact what __fmt_stoa expects and is a requirement for it
properly formatting the wchar_t argument - this does not result in wrong
usage of va_arg because the implementation of the c conversion specifier
(which the implementation of the C conversion specifier fallsthrough to)
always calls va_arg with an argument type of int, to avoid the very same
bug occuring with %lc, as the l length modifier also sets signbit to 63)

@github-actions github-actions bot added the libc label Sep 1, 2024
@@ -1073,6 +1073,9 @@ int __fmt(void *fn, void *arg, const char *format, va_list va, int *wrote) {
}
break;
}
case 'C':
signbit = 63;
Copy link
Owner

Choose a reason for hiding this comment

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

wchar_t / wint_t are 32-bit types. Why is signbit set to 63?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

__fmt_stoa only considers the argument to be of type wchar_t if signbit == 63. Otherwise, it's char16_t if signbit == 15 and otherwise it's char. I suppose the way wchar_t conversions in general could be changed so it uses a different value of signbit, but without such major changes to other parts of fmt.c, there isn't really any way to implement the C conversion specifier without having signbit end up with a value of 63

Copy link
Owner

Choose a reason for hiding this comment

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

Will it say va_arg(va, long)? Because if that's the case, you should write a test that does printf("%d%d%d%d%d%d%d%d%C%d\n", 0,0,0,0,0,0,0,0, L'x', 1) to prove it won't break.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

If that breaks %C, then I would also expect it to break %lc (I'll try it, though)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Neither break, I'll amend my commit with the corresponding test.

Copy link
Collaborator Author

@GabrielRavier GabrielRavier Sep 2, 2024

Choose a reason for hiding this comment

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

(this is because the code in case 'c': 2 lines later unconditionally does va_arg(va, int) - for this purpose, it does not care about signbit)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I've pushed the amended commit.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

(uh, now it should actually work, sorry for that lol)

@GabrielRavier GabrielRavier force-pushed the fix/printf-C-conversion-specifier branch 2 times, most recently from 2aa8402 to 651c081 Compare September 2, 2024 22:37
POSIX specifies the C conversion specifier as being "equivalent to %lc",
i.e. printf("%C", arg) is equivalent in behaviour to printf("%lc", arg).

This patch implements this conversion specifier, and adds a test for it,
alongside another test, which ensures that va_arg uses the correct size,
even though we set signbit to 63 in the code (which one might think will
result in the wrong size of argument being va_arg-ed, but having signbit
set to 63 is in fact what __fmt_stoa expects and is a requirement for it
properly formatting the wchar_t argument - this does not result in wrong
usage of va_arg because the implementation of the c conversion specifier
(which the implementation of the C conversion specifier fallsthrough to)
always calls va_arg with an argument type of int, to avoid the very same
bug occuring with %lc, as the l length modifier also sets signbit to 63)
@GabrielRavier GabrielRavier force-pushed the fix/printf-C-conversion-specifier branch from 651c081 to 23d3c12 Compare September 2, 2024 22:43
@jart jart merged commit 8f81451 into jart:master Sep 3, 2024
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants