Merge "Clarify the fcntl() "that's not how F_SETFD works" error." into main
diff --git a/libc/include/ctype.h b/libc/include/ctype.h
index ae885b1..c15ee56 100644
--- a/libc/include/ctype.h
+++ b/libc/include/ctype.h
@@ -73,15 +73,6 @@
 /** Internal implementation detail. Do not use. */
 extern const char* _ctype_;
 
-/** Returns true if `ch` is in `[A-Za-z0-9]`. */
-__BIONIC_CTYPE_INLINE int isalnum(int __ch) {
-  // `isalnum(c)` is `isalpha(c) || isdigit(c)`, but there's no obvious way
-  // to simplify that, and the table lookup is just slightly faster...
-  // Note that this is unsafe for inputs less than -1 (EOF) or greater than
-  // 0xff. This is true of other C libraries too.
-  return (_ctype_[__ch + 1] & (_CTYPE_U|_CTYPE_L|_CTYPE_N));
-}
-
 /** Returns true if `ch` is in `[A-Za-z]`. */
 __BIONIC_CTYPE_INLINE int isalpha(int __ch) {
   return (__ch >= 'A' && __ch <= 'Z') || (__ch >= 'a' && __ch <= 'z');
@@ -117,15 +108,6 @@
   return (__ch >= ' ' && __ch <= '~');
 }
 
-/** Returns true if `ch` is punctuation. */
-__BIONIC_CTYPE_INLINE int ispunct(int __ch) {
-  // `ispunct(c)` is `isgraph(c) && !isalnum(c)`, but there's no obvious way
-  // to simplify that, and the table lookup is just slightly faster...
-  // Note that this is unsafe for inputs less than -1 (EOF) or greater than
-  // 0xff. This is true of other C libraries too.
-  return (_ctype_[__ch + 1] & _CTYPE_P);
-}
-
 /** Returns true if `ch` is in `[ \f\n\r\t\v]`. */
 __BIONIC_CTYPE_INLINE int isspace(int __ch) {
   return __ch == ' ' || (__ch >= '\t' && __ch <= '\r');
@@ -141,6 +123,16 @@
   return (__ch >= '0' && __ch <= '9') || (__ch >= 'a' && __ch <= 'f') || (__ch >= 'A' && __ch <= 'F');
 }
 
+/** Returns true if `ch` is in `[A-Za-z0-9]`. */
+__BIONIC_CTYPE_INLINE int isalnum(int __ch) {
+  return isalpha(__ch) || isdigit(__ch);
+}
+
+/** Returns true if `ch` is punctuation. */
+__BIONIC_CTYPE_INLINE int ispunct(int __ch) {
+  return isgraph(__ch) && !isalnum(__ch);
+}
+
 /**
  * Returns the corresponding lower-case character if `ch` is upper-case, or undefined otherwise.
  *
diff --git a/libc/upstream-openbsd/lib/libc/stdio/fvwrite.c b/libc/upstream-openbsd/lib/libc/stdio/fvwrite.c
index ba58f9d..d83de88 100644
--- a/libc/upstream-openbsd/lib/libc/stdio/fvwrite.c
+++ b/libc/upstream-openbsd/lib/libc/stdio/fvwrite.c
@@ -1,4 +1,4 @@
-/*	$OpenBSD: fvwrite.c,v 1.20 2017/03/17 16:06:33 millert Exp $ */
+/*	$OpenBSD: fvwrite.c,v 1.21 2023/10/06 16:41:02 millert Exp $ */
 /*-
  * Copyright (c) 1990, 1993
  *	The Regents of the University of California.  All rights reserved.
@@ -34,7 +34,6 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-#include <errno.h>
 #include <unistd.h>
 #include "local.h"
 #include "fvwrite.h"
@@ -58,10 +57,8 @@
 	if ((len = uio->uio_resid) == 0)
 		return (0);
 	/* make sure we can write */
-	if (cantwrite(fp)) {
-		errno = EBADF;
+	if (cantwrite(fp))
 		return (EOF);
-	}
 
 #define	MIN(a, b) ((a) < (b) ? (a) : (b))
 #define	COPY(n)	  (void)memcpy(fp->_p, p, n)
diff --git a/libc/upstream-openbsd/lib/libc/stdio/wbuf.c b/libc/upstream-openbsd/lib/libc/stdio/wbuf.c
index 2d07750..edb6a50 100644
--- a/libc/upstream-openbsd/lib/libc/stdio/wbuf.c
+++ b/libc/upstream-openbsd/lib/libc/stdio/wbuf.c
@@ -1,4 +1,4 @@
-/*	$OpenBSD: wbuf.c,v 1.13 2015/08/31 02:53:57 guenther Exp $ */
+/*	$OpenBSD: wbuf.c,v 1.14 2023/10/06 16:41:02 millert Exp $ */
 /*-
  * Copyright (c) 1990, 1993
  *	The Regents of the University of California.  All rights reserved.
@@ -32,7 +32,6 @@
  */
 
 #include <stdio.h>
-#include <errno.h>
 #include "local.h"
 
 /*
@@ -54,10 +53,8 @@
 	 * calls might wrap _w from negative to positive.
 	 */
 	fp->_w = fp->_lbfsize;
-	if (cantwrite(fp)) {
-		errno = EBADF;
+	if (cantwrite(fp))
 		return (EOF);
-	}
 	c = (unsigned char)c;
 
 	/*
diff --git a/libc/upstream-openbsd/lib/libc/stdio/wsetup.c b/libc/upstream-openbsd/lib/libc/stdio/wsetup.c
index 0834223..842036b 100644
--- a/libc/upstream-openbsd/lib/libc/stdio/wsetup.c
+++ b/libc/upstream-openbsd/lib/libc/stdio/wsetup.c
@@ -1,4 +1,4 @@
-/*	$OpenBSD: wsetup.c,v 1.7 2005/08/08 08:05:36 espie Exp $ */
+/*	$OpenBSD: wsetup.c,v 1.8 2023/10/06 16:41:02 millert Exp $ */
 /*-
  * Copyright (c) 1990, 1993
  *	The Regents of the University of California.  All rights reserved.
@@ -31,6 +31,7 @@
  * SUCH DAMAGE.
  */
 
+#include <errno.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include "local.h"
@@ -38,7 +39,7 @@
 /*
  * Various output routines call wsetup to be sure it is safe to write,
  * because either _flags does not include __SWR, or _buf is NULL.
- * _wsetup returns 0 if OK to write, nonzero otherwise.
+ * __swsetup returns 0 if OK to write, nonzero otherwise, setting errno.
  */
 int
 __swsetup(FILE *fp)
@@ -51,8 +52,11 @@
 	 * If we are not writing, we had better be reading and writing.
 	 */
 	if ((fp->_flags & __SWR) == 0) {
-		if ((fp->_flags & __SRW) == 0)
+		if ((fp->_flags & __SRW) == 0) {
+			errno = EBADF;
+			fp->_flags |= __SERR;
 			return (EOF);
+		}
 		if (fp->_flags & __SRD) {
 			/* clobber any ungetc data */
 			if (HASUB(fp))
@@ -68,8 +72,11 @@
 	 * Make a buffer if necessary, then set _w.
 	 */
 	if (fp->_bf._base == NULL) {
-		if ((fp->_flags & (__SSTR | __SALC)) == __SSTR)
+		if ((fp->_flags & (__SSTR | __SALC)) == __SSTR) {
+			errno = EINVAL;
+			fp->_flags |= __SERR;
 			return (EOF);
+		}
 		__smakebuf(fp);
 	}
 	if (fp->_flags & __SLBF) {
diff --git a/tests/stdio_test.cpp b/tests/stdio_test.cpp
index bfe8406..5cb634c 100644
--- a/tests/stdio_test.cpp
+++ b/tests/stdio_test.cpp
@@ -2153,6 +2153,27 @@
   test_fread_from_write_only_stream(64*1024);
 }
 
+TEST(STDIO_TEST, fwrite_to_read_only_stream) {
+  FILE* fp = fopen("/proc/version", "re");
+  ASSERT_FALSE(ferror(fp));
+  ASSERT_EQ(0U, fwrite("hello", 1, 5, fp));
+  ASSERT_TRUE(ferror(fp));
+}
+
+TEST(STDIO_TEST, fputc_to_read_only_stream) {
+  FILE* fp = fopen("/proc/version", "re");
+  ASSERT_FALSE(ferror(fp));
+  ASSERT_EQ(EOF, fputc('x', fp));
+  ASSERT_TRUE(ferror(fp));
+}
+
+TEST(STDIO_TEST, fprintf_to_read_only_stream) {
+  FILE* fp = fopen("/proc/version", "re");
+  ASSERT_FALSE(ferror(fp));
+  ASSERT_EQ(-1, fprintf(fp, "%s%d", "hello", 123));
+  ASSERT_TRUE(ferror(fp));
+}
+
 static void test_fwrite_after_fread(size_t n) {
   TemporaryFile tf;