strfmon: EINVAL if the '+' flag and both signs are empty

According to the Open Group Base Specifications Issue 8[1], strfmon(3)
should return EINVAL when the '+' flag was included in a conversion
specification and the locale's positive_sign and negative_sign values
would both be returned by localeconv(3) as empty strings.

Austin Group Defect 1199[2] is applied, adding the [EINVAL] error.

[1]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/strfmon.html
[2]: https://www.austingroupbugs.net/view.php?id=1199

Reviewed by:	kib
MFC after:	1 week
Differential Revision:	https://reviews.freebsd.org/D53912
This commit is contained in:
Jose Luis Duran
2025-11-26 20:34:55 +00:00
parent 19e153004f
commit 1fd018972a
3 changed files with 18 additions and 6 deletions
+11 -1
View File
@@ -22,7 +22,7 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
.Dd December 6, 2023
.Dd November 24, 2025
.Dt STRFMON 3
.Os
.Sh NAME
@@ -182,6 +182,16 @@ function will fail if:
Conversion stopped due to lack of space in the buffer.
.It Bq Er EINVAL
The format string is invalid.
.It Bq Er EINVAL
The
.Cm +
flag was included in a conversion specification and the locale's
.Va positive_sign
and
.Va negative_sign
values would both be returned by
.Xr localeconv 3
as empty strings.
.It Bq Er ENOMEM
Not enough memory for temporary buffers.
.El
+3 -1
View File
@@ -171,7 +171,9 @@ vstrfmon_l(char *__restrict s, size_t maxsize, locale_t loc,
flags &= ~(NEED_GROUPING);
continue;
case '+': /* use locale defined signs */
if (flags & SIGN_POSN_USED)
if ((flags & SIGN_POSN_USED) ||
((lc->positive_sign[0] == '\0') &&
(lc->negative_sign[0] == '\0')))
goto format_error;
flags |= (SIGN_POSN_USED | LOCALE_POSN);
continue;
+4 -4
View File
@@ -234,11 +234,11 @@ ATF_TC_BODY(strfmon_plus_or_parenthesis, tc)
if (setlocale(LC_MONETARY, "C") == NULL)
atf_tc_skip("unable to setlocale(): %s", tests[i].locale);
/* ATF_CHECK_ERRNO(EINVAL, strfmon(actual, sizeof(actual) - 1,
"[%+n] [%+n]", 123.45, -123.45)); XXX */
ATF_CHECK_ERRNO(EINVAL, strfmon(actual, sizeof(actual) - 1,
"[%+n] [%+n]", 123.45, -123.45));
/* ATF_CHECK_ERRNO(EINVAL, strfmon(actual, sizeof(actual) - 1,
"[%+i] [%+i]", 123.45, -123.45)); XXX */
ATF_CHECK_ERRNO(EINVAL, strfmon(actual, sizeof(actual) - 1,
"[%+i] [%+i]", 123.45, -123.45));
}
ATF_TC(strfmon_l);