Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 13 Dec 2002 12:58:16 -0500
From:      Mike Barcroft <mike@FreeBSD.org>
To:        standards@FreeBSD.org
Subject:   strerror.diff and se-regress.shar for review
Message-ID:  <20021213125816.C61753@espresso.q9media.com>
In-Reply-To: <20021213114038.A61753@espresso.q9media.com>; from mike@FreeBSD.org on Fri, Dec 13, 2002 at 11:40:38AM -0500
References:  <20021213114038.A61753@espresso.q9media.com>

next in thread | previous in thread | raw e-mail | index | archive | help

--cWoXeonUoKmBZSoM
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline


Attached is my patch to resolved PR 44356 (strerror.diff).  I've also
taken the liberty of moving strerror()/strerror_r()'s regression tests
into src/tools/regression/lib/libc (se-regress.shar).

Comments appreciated.

Best regards,
Mike Barcroft

--cWoXeonUoKmBZSoM
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename="strerror.diff"

Rearrange strerror() so that its itoa procedure can be used with
strerror_r().  Doing this allows us to ensure that strerror_r() always
fills the supplied buffer regardless of EINVAL or ERANGE errors.

strerror()'s semantics have changed slightly such that an argument of
0 is now considered invalid and errno is set to EINVAL.

Remove internal regression test for strerror() and strerror_r().  This
will be reincarnated in src/tools/regression/lib/libc/string.

In strerror(3), add a comment about strerror()'s bogus return type.

PR:	44356

Index: strerror.3
===================================================================
RCS file: /work/repo/src/lib/libc/string/strerror.3,v
retrieving revision 1.20
diff -u -r1.20 strerror.3
--- strerror.3	24 Oct 2002 01:24:26 -0000	1.20
+++ strerror.3	13 Dec 2002 16:45:51 -0000
@@ -115,7 +115,7 @@
 unchanged and returns
 .Er EINVAL .
 Error numbers recognized by this implementation fall in
-the range 0 \(<=
+the range 0 \(<
 .Fa errnum
 <
 .Fa sys_nerr .
@@ -168,6 +168,11 @@
 .Fn strerror
 function will return its result in a static buffer which
 may be overwritten by subsequent calls.
+.Pp
+The return type for
+.Fn strerror
+is missing a type-qualifier; it should actually be
+.Vt const char * .
 .Pp
 The
 .Fn perror
Index: strerror.c
===================================================================
RCS file: /work/repo/src/lib/libc/string/strerror.c,v
retrieving revision 1.8
diff -u -r1.8 strerror.c
--- strerror.c	3 Jul 2002 06:28:04 -0000	1.8
+++ strerror.c	13 Dec 2002 17:23:14 -0000
@@ -37,108 +37,62 @@
 #include <sys/cdefs.h>
 __FBSDID("$FreeBSD: src/lib/libc/string/strerror.c,v 1.8 2002/07/03 06:28:04 mini Exp $");
 
+#include <errno.h>
 #include <stdio.h>
 #include <string.h>
-#include <errno.h>
-
-int
-strerror_r(int errnum, char *strerrbuf, size_t buflen)
-{
-	int             len;
 
-	if ((errnum >= 0) && (errnum < sys_nerr)) {
-		len = strlcpy(strerrbuf, (char *)sys_errlist[errnum], buflen);
-		return ((len < buflen) ? 0 : ERANGE);
-	}
-	return (EINVAL);
-}
-
-char *
-strerror(num)
-	int             num;
+#define	UPREFIX		"Unknown error: "
+/*
+ * Define a buffer size big enough to describe a 64-bit signed integer
+ * converted to ASCII decimal (19 bytes), with an optional leading sign
+ * (1 byte), finally we get the prefix and a trailing NUL from UPREFIX.
+ */
+#define	EBUFSIZE	(20 + sizeof(UPREFIX))
+
+/* Don't link to stdio(3) to avoid bloat for statically linked binaries. */
+static void
+errstr(int num, char *buf, size_t len)
 {
-	char           *p, *t;
-	unsigned int	uerr;
-	static char const unknown_prefix[] = "Unknown error: ";
-
-	/*
-	 * Define a buffer size big enough to describe a 64-bit
-	 * number in ASCII decimal (19), with optional leading sign
-	 * (+1) and trailing NUL (+1).
-	 */
-#       define		NUMLEN 21
-#	define		EBUFLEN (sizeof unknown_prefix + NUMLEN)
-	char            tmp[NUMLEN];	/* temporary number */
-	static char     ebuf[EBUFLEN];	/* error message */
-
-	if ((num >= 0) && (num < sys_nerr))
-		return ((char *)sys_errlist[num]);
-
-	/*
-	 * Set errno to EINVAL per P1003.1-200x Draft June 14, 2001.
-	 */
-	errno = EINVAL;
+	unsigned int uerr;
+	char *p, *t;
+	char tmp[EBUFSIZE];
 
-	/*
-	 * Print unknown errno by hand so we don't link to stdio(3).
-	 * This collects the ASCII digits in reverse order.
-	 */
-	uerr = (num > 0) ? num : -num;
+	if (strlcpy(buf, UPREFIX, len) >= len)
+		return;
 	t = tmp;
+	uerr = (num >= 0) ? num : -num;
 	do {
 		*t++ = "0123456789"[uerr % 10];
 	} while (uerr /= 10);
 	if (num < 0)
 		*t++ = '-';
-
-	/*
-	 * Copy the "unknown" message and the number into the caller
-	 * supplied buffer, inverting the number string.
-	 */
-	strcpy(ebuf, unknown_prefix);
-	for (p = ebuf + sizeof unknown_prefix - 1; t > tmp; )
+	for (p = buf + strlen(UPREFIX); t > tmp && p < buf + len - 1;)
 		*p++ = *--t;
 	*p = '\0';
-	return (ebuf);
 }
 
-#ifdef STANDALONE_TEST
+int
+strerror_r(int errnum, char *strerrbuf, size_t buflen)
+{
+	int retval;
 
-#include <limits.h>
+	retval = 0;
+	if (errnum < 1 || errnum >= sys_nerr) {
+		errstr(errnum, strerrbuf, buflen);
+		retval = EINVAL;
+	} else if (strlcpy(strerrbuf, sys_errlist[errnum], buflen) >= buflen)
+		retval = ERANGE;
+	return (retval);
+}
 
-main()
+char *
+strerror(int num)
 {
-	char            mybuf[64];
-	int             ret;
-
-	errno = 0;
+	static char ebuf[EBUFSIZE];
 
-	printf("strerror(0) yeilds: %s\n", strerror(0));
-	printf("strerror(1) yeilds: %s\n", strerror(1));
-	printf("strerror(47) yeilds: %s\n", strerror(47));
-	printf("strerror(sys_nerr - 1) yeilds: %s\n", strerror(sys_nerr - 1));
-	printf("errno = %d\n", errno); errno = 0;
-
-	printf("strerror(sys_nerr) yeilds: %s\n", strerror(sys_nerr));
-	printf("errno = %d\n", errno);  errno = 0;
-
-	printf("strerror(437) yeilds: %s\n", strerror(437));
-	printf("errno = %d\n", errno);  errno = 0;
-
-	printf("strerror(LONG_MAX) yeilds: %s\n", strerror(LONG_MAX));
-	printf("strerror(LONG_MIN) yeilds: %s\n", strerror(LONG_MIN));
-	printf("strerror(ULONG_MAX) yeilds: %s\n", strerror(ULONG_MAX));
-
-	memset(mybuf, '*', 63); mybuf[63] = '\0';
-	strerror_r(11, mybuf, 64);
-	printf("strerror_r(11) yeilds: %s\n", mybuf);
-
-	memset(mybuf, '*', 63); mybuf[63] = '\0';
-	ret = strerror_r(1234, mybuf, 64);
-	printf("strerror_r(1234) returns %d (%s)\n", ret, mybuf);
-
-	memset(mybuf, '*', 63); mybuf[63] = '\0';
-	ret = strerror_r(1, mybuf, 10);
-	printf("strerror_r on short buffer returns %d (%s)\n", ret, mybuf);
+	if (num > 0 && num < sys_nerr)
+		return ((char *)sys_errlist[num]);
+	errno = EINVAL;
+	errstr(num, ebuf, sizeof(ebuf));
+	return (ebuf);
 }
-#endif

--cWoXeonUoKmBZSoM
Content-Type: application/x-shar
Content-Disposition: attachment; filename="se-regress.shar"

# Apply this in src/tools/regression/lib/libc
#
# This is a shell archive.  Save it in a file, remove anything before
# this line, and then unpack it by entering "sh file".  Note, it may
# create directories; files and directories will be owned by you and
# have default permissions.
#
# This archive contains:
#
#	string
#	string/Makefile
#	string/test-strerror.c
#
echo c - string
mkdir -p string > /dev/null 2>&1
echo x - string/Makefile
sed 's/^X//' >string/Makefile << 'END-of-string/Makefile'
X# $FreeBSD$
X
XTESTS=	test-strerror
X
X.PHONY: tests
Xtests: ${TESTS}
X	for p in ${TESTS}; do ${.OBJDIR}/$$p; done
X
X.PHONY: clean
Xclean:
X	-rm -f ${TESTS}
END-of-string/Makefile
echo x - string/test-strerror.c
sed 's/^X//' >string/test-strerror.c << 'END-of-string/test-strerror.c'
X/*-
X * Copyright (c) 2001 Wes Peters <wes@FreeBSD.org>
X * All rights reserved.
X *
X * Redistribution and use in source and binary forms, with or without
X * modification, are permitted provided that the following conditions
X * are met:
X * 1. Redistributions of source code must retain the above copyright
X *    notice, this list of conditions and the following disclaimer.
X * 2. Redistributions in binary form must reproduce the above copyright
X *    notice, this list of conditions and the following disclaimer in the
X *    documentation and/or other materials provided with the distribution.
X *
X * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
X * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
X * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
X * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
X * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
X * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
X * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
X * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
X * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
X * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
X * SUCH DAMAGE.
X *
X * $FreeBSD$
X */
X
X#include <assert.h>
X#include <errno.h>
X#include <limits.h>
X#include <stdio.h>
X#include <stdlib.h>
X#include <string.h>
X
Xint
Xmain(void)
X{
X	char buf[64];
X	char *sret;
X	int iret;
X
X	/*
X	 * strerror() failure tests.
X	 */
X	errno = 0;
X	sret = strerror(0);
X	assert(strcmp(sret, "Unknown error: 0") == 0);
X	assert(errno == EINVAL);
X
X	errno = 0;
X	sret = strerror(INT_MAX);
X	snprintf(buf, sizeof(buf), "Unknown error: %d", INT_MAX);
X	assert(strcmp(sret, buf) == 0);
X	assert(errno == EINVAL);
X
X	/*
X	 * strerror() success tests.
X	 */
X	errno = 0;
X	sret = strerror(EPERM);
X	assert(strcmp(sret, "Operation not permitted") == 0);
X	assert(errno == 0);
X
X	errno = 0;
X	sret = strerror(EPFNOSUPPORT);
X	assert(strcmp(sret, "Protocol family not supported") == 0);
X	assert(errno == 0);
X
X	errno = 0;
X	sret = strerror(ELAST);
X	assert(errno == 0);
X
X	printf("PASS strerror()\n");
X
X	/*
X	 * strerror_r() failure tests.
X	 */
X	memset(buf, '*', sizeof(buf));
X	iret = strerror_r(0, buf, sizeof(buf));
X	assert(strcmp(buf, "Unknown error: 0") == 0);
X	assert(iret == EINVAL);
X
X	memset(buf, '*', sizeof(buf));
X	/* One byte too short. */
X	iret = strerror_r(EPERM, buf, strlen("Operation not permitted"));
X	assert(strcmp(buf, "Operation not permitte") == 0);
X	assert(iret == ERANGE);
X
X	memset(buf, '*', sizeof(buf));
X	/* One byte too short. */
X	iret = strerror_r(-1, buf, strlen("Unknown error: -1"));
X	assert(strcmp(buf, "Unknown error: -") == 0);
X	assert(iret == EINVAL);
X
X	memset(buf, '*', sizeof(buf));
X	/* Two bytes too short. */
X	iret = strerror_r(-2, buf, strlen("Unknown error: -2") - 1);
X	assert(strcmp(buf, "Unknown error: ") == 0);
X	assert(iret == EINVAL);
X
X	memset(buf, '*', sizeof(buf));
X	/* Three bytes too short. */
X	iret = strerror_r(-2, buf, strlen("Unknown error: -2") - 2);
X	assert(strcmp(buf, "Unknown error:") == 0);
X	assert(iret == EINVAL);
X
X	memset(buf, '*', sizeof(buf));
X	/* One byte too short. */
X	iret = strerror_r(12345, buf, strlen("Unknown error: 12345"));
X	assert(strcmp(buf, "Unknown error: 1234") == 0);
X	assert(iret == EINVAL);
X
X	/*
X	 * strerror_r() success tests.
X	 */
X	memset(buf, '*', sizeof(buf));
X	iret = strerror_r(EDEADLK, buf, sizeof(buf));
X	assert(strcmp(buf, "Resource deadlock avoided") == 0);
X	assert(iret == 0);
X
X	memset(buf, '*', sizeof(buf));
X	iret = strerror_r(EPROCLIM, buf, sizeof(buf));
X	assert(strcmp(buf, "Too many processes") == 0);
X	assert(iret == 0);
X
X	printf("PASS strerror_r()\n");
X
X	exit(0);
X}
END-of-string/test-strerror.c
exit


--cWoXeonUoKmBZSoM--

To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-standards" in the body of the message




Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20021213125816.C61753>