<termios.h>: add two new POSIX functions.
musl already added tcgetwinsize() and tcsetwinsize(), but I didn't
notice.
Trivial single-line inlines added to a header that's already written
that way.
Test: treehugger
Change-Id: Iac95ea6a89f3872025c512f7e61987b81d0aafa7
diff --git a/docs/status.md b/docs/status.md
index de2fa10..ad7e1c5 100644
--- a/docs/status.md
+++ b/docs/status.md
@@ -56,6 +56,7 @@
Current libc symbols: https://android.googlesource.com/platform/bionic/+/master/libc/libc.map.txt
New libc functions in V (API level 35):
+ * `tcgetwinsize`, `tcsetwinsize` (POSIX Issue 8 additions).
* `timespec_getres` (C23 addition).
* `localtime_rz`, `mktime_z`, `tzalloc`, and `tzfree` (NetBSD
extensions implemented in tzcode, and the "least non-standard"
diff --git a/libc/bionic/termios.cpp b/libc/bionic/termios.cpp
index 5fe8eb0..57b34b7 100644
--- a/libc/bionic/termios.cpp
+++ b/libc/bionic/termios.cpp
@@ -34,6 +34,10 @@
#define __BIONIC_TERMIOS_INLINE /* Out of line. */
#include <bits/termios_inlines.h>
+// POSIX added a couple more functions much later, so do the same for them.
+#define __BIONIC_TERMIOS_WINSIZE_INLINE /* Out of line. */
+#include <bits/termios_winsize_inlines.h>
+
// Actually declared in <unistd.h>, present on all API levels.
pid_t tcgetpgrp(int fd) {
pid_t pid;
diff --git a/libc/include/android/legacy_termios_inlines.h b/libc/include/android/legacy_termios_inlines.h
index 6222786..a816b40 100644
--- a/libc/include/android/legacy_termios_inlines.h
+++ b/libc/include/android/legacy_termios_inlines.h
@@ -43,3 +43,10 @@
#include <bits/termios_inlines.h>
#endif
+
+#if __ANDROID_API__ < 35
+
+#define __BIONIC_TERMIOS_WINSIZE_INLINE static __inline
+#include <bits/termios_winsize_inlines.h>
+
+#endif
diff --git a/libc/include/bits/termios_winsize_inlines.h b/libc/include/bits/termios_winsize_inlines.h
new file mode 100644
index 0000000..ae246e4
--- /dev/null
+++ b/libc/include/bits/termios_winsize_inlines.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <errno.h>
+#include <sys/cdefs.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+
+#include <linux/termios.h>
+
+#if !defined(__BIONIC_TERMIOS_WINSIZE_INLINE)
+#define __BIONIC_TERMIOS_WINSIZE_INLINE static __inline
+#endif
+
+__BEGIN_DECLS
+
+__BIONIC_TERMIOS_WINSIZE_INLINE int tcgetwinsize(int __fd, struct winsize* _Nonnull __size) {
+ return ioctl(__fd, TIOCGWINSZ, __size);
+}
+
+__BIONIC_TERMIOS_WINSIZE_INLINE int tcsetwinsize(int __fd, const struct winsize* _Nonnull __size) {
+ return ioctl(__fd, TIOCSWINSZ, __size);
+}
+
+__END_DECLS
diff --git a/libc/include/termios.h b/libc/include/termios.h
index 853b4eb..7abff5d 100644
--- a/libc/include/termios.h
+++ b/libc/include/termios.h
@@ -149,6 +149,25 @@
#endif
+#if __ANDROID_API__ >= 35
+// These two functions were POSIX Issue 8 additions that we can also trivially
+// implement as inlines for older OS version.
+
+/**
+ * tcgetwinsize(3) gets the window size of the given terminal.
+ *
+ * Returns 0 on success and returns -1 and sets `errno` on failure.
+ */
+int tcgetwinsize(int __fd, struct winsize* _Nonnull __size);
+
+/**
+ * tcsetwinsize(3) sets the window size of the given terminal.
+ *
+ * Returns 0 on success and returns -1 and sets `errno` on failure.
+ */
+int tcsetwinsize(int __fd, const struct winsize* _Nonnull __size);
+#endif
+
__END_DECLS
#include <android/legacy_termios_inlines.h>
diff --git a/libc/libc.map.txt b/libc/libc.map.txt
index e90618d..b3ef185 100644
--- a/libc/libc.map.txt
+++ b/libc/libc.map.txt
@@ -1591,6 +1591,8 @@
mktime_z;
__riscv_flush_icache; # riscv64
__riscv_hwprobe; # riscv64
+ tcgetwinsize;
+ tcsetwinsize;
timespec_getres;
tzalloc;
tzfree;
diff --git a/tests/headers/posix/termios_h.c b/tests/headers/posix/termios_h.c
index 1255c16..0a67eaa 100644
--- a/tests/headers/posix/termios_h.c
+++ b/tests/headers/posix/termios_h.c
@@ -42,6 +42,12 @@
STRUCT_MEMBER(struct termios, tcflag_t, c_lflag);
STRUCT_MEMBER_ARRAY(struct termios, cc_t/*[]*/, c_cc);
+#if !defined(__GLIBC__) // Our glibc is too old.
+ TYPE(struct winsize);
+ STRUCT_MEMBER(struct winsize, unsigned short, ws_row);
+ STRUCT_MEMBER(struct winsize, unsigned short, ws_col);
+#endif
+
MACRO(NCCS);
MACRO(VEOF);
@@ -162,6 +168,12 @@
FUNCTION(tcflush, int (*f)(int, int));
FUNCTION(tcgetattr, int (*f)(int, struct termios*));
FUNCTION(tcgetsid, pid_t (*f)(int));
+#if !defined(__GLIBC__) // Our glibc is too old.
+ FUNCTION(tcgetwinsize, int (*f)(int, struct winsize*));
+#endif
FUNCTION(tcsendbreak, int (*f)(int, int));
FUNCTION(tcsetattr, int (*f)(int, int, const struct termios*));
+#if !defined(__GLIBC__) // Our glibc is too old.
+ FUNCTION(tcsetwinsize, int (*f)(int, const struct winsize*));
+#endif
}
diff --git a/tests/termios_test.cpp b/tests/termios_test.cpp
index 6290771..a8d5890 100644
--- a/tests/termios_test.cpp
+++ b/tests/termios_test.cpp
@@ -29,6 +29,8 @@
#include <termios.h>
#include <errno.h>
+#include <fcntl.h>
+#include <pty.h>
#include <gtest/gtest.h>
@@ -96,3 +98,49 @@
EXPECT_EQ(1, t.c_cc[VMIN]);
EXPECT_EQ(0, t.c_cc[VTIME]);
}
+
+TEST(termios, tcgetwinsize_tcsetwinsize_invalid) {
+#if !defined(__GLIBC__)
+ winsize ws = {};
+
+ errno = 0;
+ ASSERT_EQ(-1, tcgetwinsize(-1, &ws));
+ ASSERT_EQ(EBADF, errno);
+
+ errno = 0;
+ ASSERT_EQ(-1, tcsetwinsize(-1, &ws));
+ ASSERT_EQ(EBADF, errno);
+#else
+ GTEST_SKIP() << "glibc too old";
+#endif
+}
+
+TEST(termios, tcgetwinsize_tcsetwinsize) {
+#if !defined(__GLIBC__)
+ int pty, tty;
+ winsize ws = {123, 456, 9999, 9999};
+ ASSERT_EQ(0, openpty(&pty, &tty, nullptr, nullptr, &ws));
+
+ winsize actual = {};
+ ASSERT_EQ(0, tcgetwinsize(tty, &actual));
+ EXPECT_EQ(ws.ws_xpixel, actual.ws_xpixel);
+ EXPECT_EQ(ws.ws_ypixel, actual.ws_ypixel);
+ EXPECT_EQ(ws.ws_row, actual.ws_row);
+ EXPECT_EQ(ws.ws_col, actual.ws_col);
+
+ ws = {1, 2, 3, 4};
+ ASSERT_EQ(0, tcsetwinsize(tty, &ws));
+
+ actual = {};
+ ASSERT_EQ(0, tcgetwinsize(tty, &actual));
+ EXPECT_EQ(ws.ws_xpixel, actual.ws_xpixel);
+ EXPECT_EQ(ws.ws_ypixel, actual.ws_ypixel);
+ EXPECT_EQ(ws.ws_row, actual.ws_row);
+ EXPECT_EQ(ws.ws_col, actual.ws_col);
+
+ close(pty);
+ close(tty);
+#else
+ GTEST_SKIP() << "glibc too old";
+#endif
+}