about summary refs log tree commit diff
path: root/stdio-common
diff options
context:
space:
mode:
authorUlrich Drepper <drepper@redhat.com>2001-08-16 05:23:52 +0000
committerUlrich Drepper <drepper@redhat.com>2001-08-16 05:23:52 +0000
commit1fc0e33153186a90140c3d25f5d9b4537890d7cc (patch)
tree769ddc538b14a929b6b9ada06d5ed112998bcd9d /stdio-common
parentc0fd6e1d648ac5d4ac1a7e964ecf664a8aa5ce94 (diff)
downloadglibc-1fc0e33153186a90140c3d25f5d9b4537890d7cc.tar.gz
glibc-1fc0e33153186a90140c3d25f5d9b4537890d7cc.tar.xz
glibc-1fc0e33153186a90140c3d25f5d9b4537890d7cc.zip
Update.
	* 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.
Diffstat (limited to 'stdio-common')
-rw-r--r--stdio-common/Makefile2
-rw-r--r--stdio-common/perror.c55
-rw-r--r--stdio-common/tst-perror.c154
3 files changed, 202 insertions, 9 deletions
diff --git a/stdio-common/Makefile b/stdio-common/Makefile
index f2a26b367e..e91ebbb2bd 100644
--- a/stdio-common/Makefile
+++ b/stdio-common/Makefile
@@ -55,7 +55,7 @@ tests := tstscanf test_rdwr test-popen tstgetln test-fseek \
 	 tfformat tiformat tllformat tstdiomisc tst-printfsz tst-wc-printf \
 	 scanf1 scanf2 scanf3 scanf4 scanf5 scanf7 scanf8 scanf9 scanf10 \
 	 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
 
diff --git a/stdio-common/perror.c b/stdio-common/perror.c
index c22be6daf2..dfdd69a344 100644
--- a/stdio-common/perror.c
+++ b/stdio-common/perror.c
@@ -19,13 +19,14 @@
 #include <errno.h>
 #include <stdio.h>
 #include <string.h>
+#include <unistd.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,
-   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)
+static void
+perror_internal (FILE *fp, const char *s)
 {
   char buf[1024];
   int errnum = errno;
@@ -40,9 +41,47 @@ perror (const char *s)
   errstring = __strerror_r (errnum, buf, sizeof buf);
 
 #ifdef USE_IN_LIBIO
-  if (_IO_fwide (stderr, 0) > 0)
-    (void) fwprintf (stderr, L"%s%s%s\n", s, colon, errstring);
+  if (_IO_fwide (fp, 0) > 0)
+    (void) fwprintf (fp, L"%s%s%s\n", s, colon, errstring);
   else
 #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;
+    }
 }
diff --git a/stdio-common/tst-perror.c b/stdio-common/tst-perror.c
new file mode 100644
index 0000000000..b809c2fc40
--- /dev/null
+++ b/stdio-common/tst-perror.c
@@ -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;
+}