* misc/error.c (error): Handle wide oriented stderr stream correctly.
	* stdio-common/perror.c (perror): Implement according to standard.
	The stream orientation must not be changed if the stream was not
	oriented before the call.
	* stdio-common/Makefile (tests): Add tst-perror.
	* stdio-common/tst-perror.c: New file.
See ChangeLog.12 for earlier changes.
This commit is contained in:
Ulrich Drepper 2001-08-16 05:23:52 +00:00
parent c0fd6e1d64
commit 1fc0e33153
5 changed files with 338 additions and 10135 deletions

10099
ChangeLog

File diff suppressed because it is too large Load Diff

View File

@ -26,6 +26,9 @@
#include <stdio.h> #include <stdio.h>
#include <libintl.h> #include <libintl.h>
#ifdef _LIBC
# include <wchar.h>
#endif
#if HAVE_VPRINTF || HAVE_DOPRNT || _LIBC #if HAVE_VPRINTF || HAVE_DOPRNT || _LIBC
# if __STDC__ # if __STDC__
@ -117,12 +120,94 @@ private_strerror (errnum)
# endif /* HAVE_STRERROR_R */ # endif /* HAVE_STRERROR_R */
#endif /* not _LIBC */ #endif /* not _LIBC */
#ifdef VA_START
static void
error_tail (int status, int errnum, const char *message, va_list args)
{
# if HAVE_VPRINTF || _LIBC
# ifdef _LIBC
if (_IO_fwide (stderr, 0) > 0)
{
# define ALLOCA_LIMIT 2000
size_t len = strlen (message) + 1;
wchar_t *wmessage = NULL;
mbstate_t st;
size_t res;
const char *tmp;
do
{
if (len < ALLOCA_LIMIT)
wmessage = (wchar_t *) alloca (len * sizeof (wchar_t));
else
{
if (wmessage != NULL && len / 2 < ALLOCA_LIMIT)
wmessage = NULL;
wmessage = (wchar_t *) realloc (wmessage,
len * sizeof (wchar_t));
if (wmessage == NULL)
{
fputws (L"out of memory\n", stderr);
return;
}
}
memset (&st, '\0', sizeof (st));
tmp =message;
}
while ((res = mbsrtowcs (wmessage, &tmp, len, &st)) == len);
if (res == (size_t) -1)
/* The string cannot be converted. */
wmessage = (wchar_t *) L"???";
vfwprintf (stderr, wmessage, args);
}
else
# endif
vfprintf (stderr, message, args);
# else
_doprnt (message, args, stderr);
# endif
va_end (args);
++error_message_count;
if (errnum)
{
#if defined HAVE_STRERROR_R || _LIBC
char errbuf[1024];
char *s = __strerror_r (errnum, errbuf, sizeof errbuf);
# ifdef _LIBC
if (_IO_fwide (stderr, 0) > 0)
fwprintf (stderr, L": %s", s);
else
# endif
fprintf (stderr, ": %s", s);
#else
fprintf (stderr, ": %s", strerror (errnum));
#endif
}
#ifdef _LIBC
if (_IO_fwide (stderr, 0) > 0)
putwc (L'\n', stderr);
else
#endif
putc ('\n', stderr);
fflush (stderr);
if (status)
exit (status);
}
#endif
/* Print the program name and error message MESSAGE, which is a printf-style /* Print the program name and error message MESSAGE, which is a printf-style
format string with optional args. format string with optional args.
If ERRNUM is nonzero, print its corresponding system error message. If ERRNUM is nonzero, print its corresponding system error message.
Exit with status STATUS if it is nonzero. */ Exit with status STATUS if it is nonzero. */
/* VARARGS */ /* VARARGS */
void void
#if defined VA_START && __STDC__ #if defined VA_START && __STDC__
error (int status, int errnum, const char *message, ...) error (int status, int errnum, const char *message, ...)
@ -139,37 +224,38 @@ error (status, errnum, message, va_alist)
#endif #endif
fflush (stdout); fflush (stdout);
#ifdef _LIBC
flockfile (stderr);
#endif
if (error_print_progname) if (error_print_progname)
(*error_print_progname) (); (*error_print_progname) ();
else else
fprintf (stderr, "%s: ", program_name); {
#ifdef _LIBC
if (_IO_fwide (stderr, 0) > 0)
fwprintf (stderr, L"%s: ", program_name);
else
#endif
fprintf (stderr, "%s: ", program_name);
}
#ifdef VA_START #ifdef VA_START
VA_START (args, message); VA_START (args, message);
# if HAVE_VPRINTF || _LIBC error_tail (status, errnum, message, args);
vfprintf (stderr, message, args); # ifdef _LIBC
# else funlockfile (stderr);
_doprnt (message, args, stderr);
# endif # endif
va_end (args);
#else #else
fprintf (stderr, message, a1, a2, a3, a4, a5, a6, a7, a8); fprintf (stderr, message, a1, a2, a3, a4, a5, a6, a7, a8);
#endif
++error_message_count; ++error_message_count;
if (errnum) if (errnum)
{ fprintf (stderr, ": %s", strerror (errnum));
#if defined HAVE_STRERROR_R || _LIBC
char errbuf[1024];
fprintf (stderr, ": %s", __strerror_r (errnum, errbuf, sizeof errbuf));
#else
fprintf (stderr, ": %s", strerror (errnum));
#endif
}
putc ('\n', stderr); putc ('\n', stderr);
fflush (stderr); fflush (stderr);
if (status) if (status)
exit (status); exit (status);
#endif
} }
/* Sometimes we want to have at most one error per line. This /* Sometimes we want to have at most one error per line. This
@ -199,8 +285,9 @@ error_at_line (status, errnum, file_name, line_number, message, va_alist)
static const char *old_file_name; static const char *old_file_name;
static unsigned int old_line_number; static unsigned int old_line_number;
if (old_line_number == line_number && if (old_line_number == line_number
(file_name == old_file_name || !strcmp (old_file_name, file_name))) && (file_name == old_file_name
|| strcmp (old_file_name, file_name) == 0))
/* Simply return and print nothing. */ /* Simply return and print nothing. */
return; return;
@ -209,40 +296,48 @@ error_at_line (status, errnum, file_name, line_number, message, va_alist)
} }
fflush (stdout); fflush (stdout);
#ifdef _LIBC
flockfile (stderr);
#endif
if (error_print_progname) if (error_print_progname)
(*error_print_progname) (); (*error_print_progname) ();
else else
fprintf (stderr, "%s:", program_name); {
#ifdef _LIBC
if (_IO_fwide (stderr, 0) > 0)
fwprintf (stderr, L"%s: ", program_name);
else
#endif
fprintf (stderr, "%s:", program_name);
}
if (file_name != NULL) if (file_name != NULL)
fprintf (stderr, "%s:%d: ", file_name, line_number); {
#ifdef _LIBC
if (_IO_fwide (stderr, 0) > 0)
fwprintf (stderr, L"%s:%d: ", file_name, line_number);
else
#endif
fprintf (stderr, "%s:%d: ", file_name, line_number);
}
#ifdef VA_START #ifdef VA_START
VA_START (args, message); VA_START (args, message);
# if HAVE_VPRINTF || _LIBC error_tail (status, errnum, message, args);
vfprintf (stderr, message, args); # ifdef _LIBC
# else funlockfile (stderr);
_doprnt (message, args, stderr);
# endif # endif
va_end (args);
#else #else
fprintf (stderr, message, a1, a2, a3, a4, a5, a6, a7, a8); fprintf (stderr, message, a1, a2, a3, a4, a5, a6, a7, a8);
#endif
++error_message_count; ++error_message_count;
if (errnum) if (errnum)
{ fprintf (stderr, ": %s", strerror (errnum));
#if defined HAVE_STRERROR_R || _LIBC
char errbuf[1024];
fprintf (stderr, ": %s", __strerror_r (errnum, errbuf, sizeof errbuf));
#else
fprintf (stderr, ": %s", strerror (errnum));
#endif
}
putc ('\n', stderr); putc ('\n', stderr);
fflush (stderr); fflush (stderr);
if (status) if (status)
exit (status); exit (status);
#endif
} }
#ifdef _LIBC #ifdef _LIBC

View File

@ -55,7 +55,7 @@ tests := tstscanf test_rdwr test-popen tstgetln test-fseek \
tfformat tiformat tllformat tstdiomisc tst-printfsz tst-wc-printf \ tfformat tiformat tllformat tstdiomisc tst-printfsz tst-wc-printf \
scanf1 scanf2 scanf3 scanf4 scanf5 scanf7 scanf8 scanf9 scanf10 \ scanf1 scanf2 scanf3 scanf4 scanf5 scanf7 scanf8 scanf9 scanf10 \
scanf12 tst-tmpnam tst-cookie tst-obprintf tst-sscanf tst-swprintf \ scanf12 tst-tmpnam tst-cookie tst-obprintf tst-sscanf tst-swprintf \
tst-fseek tst-fmemopen test-vfprintf tst-gets tst-fseek tst-fmemopen test-vfprintf tst-gets tst-perror
test-srcs = tst-unbputc tst-printf test-srcs = tst-unbputc tst-printf

View File

@ -19,13 +19,14 @@
#include <errno.h> #include <errno.h>
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include <unistd.h>
#include <wchar.h> #include <wchar.h>
#ifdef USE_IN_LIBIO
# include "libioP.h"
#endif
/* Print a line on stderr consisting of the text in S, a colon, a space, static void
a message describing the meaning of the contents of `errno' and a newline. perror_internal (FILE *fp, const char *s)
If S is NULL or "", the colon and space are omitted. */
void
perror (const char *s)
{ {
char buf[1024]; char buf[1024];
int errnum = errno; int errnum = errno;
@ -40,9 +41,47 @@ perror (const char *s)
errstring = __strerror_r (errnum, buf, sizeof buf); errstring = __strerror_r (errnum, buf, sizeof buf);
#ifdef USE_IN_LIBIO #ifdef USE_IN_LIBIO
if (_IO_fwide (stderr, 0) > 0) if (_IO_fwide (fp, 0) > 0)
(void) fwprintf (stderr, L"%s%s%s\n", s, colon, errstring); (void) fwprintf (fp, L"%s%s%s\n", s, colon, errstring);
else else
#endif #endif
(void) fprintf (stderr, "%s%s%s\n", s, colon, errstring); (void) fprintf (fp, "%s%s%s\n", s, colon, errstring);
}
/* Print a line on stderr consisting of the text in S, a colon, a space,
a message describing the meaning of the contents of `errno' and a newline.
If S is NULL or "", the colon and space are omitted. */
void
perror (const char *s)
{
FILE *fp;
int fd = -1;
/* The standard says that 'perror' must not change the orientation
of the stream. What is supposed to happen when the stream isn't
oriented yet? In this case we'll create a new stream which is
using the same underlying file descriptor. */
if (__builtin_expect (_IO_fwide (stderr, 0) != 0, 1)
|| fileno_unlocked (stderr) == -1
|| (fd = dup (fileno_unlocked (stderr))) == -1
|| (fp = fdopen (fd, "w+")) == NULL)
{
if (__builtin_expect (fd != -1, 0))
close (fd);
/* Use standard error as is. */
perror_internal (stderr, s);
}
else
{
/* We don't have to do any special hacks regarding the file
position. Since the stderr stream wasn't used so far we just
write to the descriptor. */
perror_internal (fp, s);
/* Close the stream. */
fclose (fp);
((_IO_FILE *) stderr)->_offset = _IO_pos_BAD;
}
} }

154
stdio-common/tst-perror.c Normal file
View File

@ -0,0 +1,154 @@
/* Test of perror.
Contributed by Ulrich Drepper <drepper@redhat.com>, 2001.
To be used only for testing glibc. */
#include <errno.h>
#include <error.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <wchar.h>
#define MB_EXP \
"null mode test 1: Invalid or incomplete multibyte or wide character\n" \
"multibyte string\n" \
"<0 mode test: Invalid argument\n"
#define MB_EXP_LEN (sizeof (MB_EXP) - 1)
#define WC_EXP \
"null mode test 2: Invalid or incomplete multibyte or wide character\n" \
"wide string\n" \
">0 mode test: Invalid argument\n"
#define WC_EXP_LEN (sizeof (WC_EXP) - 1)
int
main (void)
{
int fd;
char fname[] = "/tmp/tst-perror.XXXXXX";
int result = 0;
char buf[200];
ssize_t n;
fd = mkstemp (fname);
if (fd == -1)
error (EXIT_FAILURE, errno, "cannot create temporary file");
/* Make sure the file gets removed. */
unlink (fname);
fclose (stderr);
if (dup2 (fd, 2) == -1)
{
printf ("cannot create file descriptor 2: %m\n");
exit (EXIT_FAILURE);
}
stderr = fdopen (2, "w");
if (stderr == NULL)
{
printf ("fdopen failed: %m\n");
exit (EXIT_FAILURE);
}
if (fwide (stderr, 0) != 0)
{
printf ("stderr not initially in mode 0\n");
exit (EXIT_FAILURE);
}
errno = EILSEQ;
perror ("null mode test 1");
if (fwide (stderr, 0) != 0)
{
puts ("perror changed the mode from 0");
result = 1;
}
fputs ("multibyte string\n", stderr);
if (fwide (stderr, 0) >= 0)
{
puts ("fputs didn't set orientation to narrow");
result = 1;
}
errno = EINVAL;
perror ("<0 mode test");
fclose (stderr);
lseek (fd, 0, SEEK_SET);
n = read (fd, buf, sizeof (buf));
if (n != MB_EXP_LEN || memcmp (buf, MB_EXP, MB_EXP_LEN) != 0)
{
printf ("multibyte test failed. Expected:\n%s\nGot:\n%.*s\n",
MB_EXP, (int) n, buf);
result = 1;
}
else
puts ("multibyte test succeeded");
lseek (fd, 0, SEEK_SET);
ftruncate (fd, 0);
if (dup2 (fd, 2) == -1)
{
printf ("cannot create file descriptor 2: %m\n");
exit (EXIT_FAILURE);
}
stderr = fdopen (2, "w");
if (stderr == NULL)
{
printf ("fdopen failed: %m\n");
exit (EXIT_FAILURE);
}
if (fwide (stderr, 0) != 0)
{
printf ("stderr not initially in mode 0\n");
exit (EXIT_FAILURE);
}
errno = EILSEQ;
perror ("null mode test 2");
if (fwide (stderr, 0) != 0)
{
puts ("perror changed the mode from 0");
result = 1;
}
fputws (L"wide string\n", stderr);
if (fwide (stderr, 0) <= 0)
{
puts ("fputws didn't set orientation to wide");
result = 1;
}
errno = EINVAL;
perror (">0 mode test");
fclose (stderr);
lseek (fd, 0, SEEK_SET);
n = read (fd, buf, sizeof (buf));
if (n != WC_EXP_LEN || memcmp (buf, WC_EXP, WC_EXP_LEN) != 0)
{
printf ("wide test failed. Expected:\n%s\nGot:\n%.*s\n",
WC_EXP, (int) n, buf);
result = 1;
}
else
puts ("wide test succeeded");
close (fd);
return result;
}