Add sig2str()/str2sig() from POSIX Issue 8.

Change-Id: I3b2c1601bd667af6b8f736e03a6e94035b812ff7
diff --git a/docs/status.md b/docs/status.md
index 0436c40..0810499 100644
--- a/docs/status.md
+++ b/docs/status.md
@@ -56,7 +56,7 @@
 Current libc symbols: https://android.googlesource.com/platform/bionic/+/main/libc/libc.map.txt
 
 New libc functions in API level 36:
-  * `qsort_r` (new POSIX addition).
+  * `qsort_r`, `sig2str`/`str2sig` (POSIX Issue 8 additions).
 
 New libc functions in V (API level 35):
   * New `android_crash_detail_register`, `android_crash_detail_unregister`,
diff --git a/libc/bionic/strsignal.cpp b/libc/bionic/strsignal.cpp
index f18b6d0..29c22e2 100644
--- a/libc/bionic/strsignal.cpp
+++ b/libc/bionic/strsignal.cpp
@@ -27,29 +27,29 @@
  */
 
 #include <signal.h>
+#include <stdlib.h>
 #include <string.h>
 
 #include "bionic/pthread_internal.h"
 
+// Maps regular signals like SIGSEGV to strings like "Segmentation fault".
+// Signal 0 and all the real-time signals are just nullptr, but that's the ABI.
 const char* const sys_siglist[NSIG] = {
 #define __BIONIC_SIGDEF(signal_number, signal_description) [signal_number] = signal_description,
 #include "private/bionic_sigdefs.h"
 };
 
+// Maps regular signals like SIGSEGV to strings like "SEGV".
+// Signal 0 and all the real-time signals are just nullptr, but that's the ABI.
 const char* const sys_signame[NSIG] = {
 #define __BIONIC_SIGDEF(signal_number, unused) [signal_number] = &(#signal_number)[3],
 #include "private/bionic_sigdefs.h"
 };
 
 extern "C" __LIBC_HIDDEN__ const char* __strsignal(int signal_number, char* buf, size_t buf_len) {
-  const char* signal_name = nullptr;
-  if (signal_number >= 0 && signal_number < NSIG) {
-    signal_name = sys_siglist[signal_number];
+  if (signal_number >= SIGHUP && signal_number < SIGSYS) {
+    return sys_siglist[signal_number];
   }
-  if (signal_name != nullptr) {
-    return signal_name;
-  }
-
   const char* prefix = "Unknown";
   if (signal_number >= SIGRTMIN && signal_number <= SIGRTMAX) {
     prefix = "Real-time";
@@ -66,3 +66,72 @@
   bionic_tls& tls = __get_bionic_tls();
   return const_cast<char*>(__strsignal(signal_number, tls.strsignal_buf, sizeof(tls.strsignal_buf)));
 }
+
+int sig2str(int sig, char* str) {
+  if (sig >= SIGHUP && sig <= SIGSYS) {
+    strcpy(str, sys_signame[sig]);
+    return 0;
+  }
+  if (sig == SIGRTMIN) {
+    strcpy(str, "RTMIN");
+    return 0;
+  }
+  if (sig == SIGRTMAX) {
+    strcpy(str, "RTMAX");
+    return 0;
+  }
+  if (sig > SIGRTMIN && sig < SIGRTMAX) {
+    if (sig - SIGRTMIN <= SIGRTMAX - sig) {
+      sprintf(str, "RTMIN+%d", sig - SIGRTMIN);
+    } else {
+      sprintf(str, "RTMAX-%d", SIGRTMAX - sig);
+    }
+    return 0;
+  }
+  return -1;
+}
+
+int str2sig(const char* str, int* sig) {
+  // A name in our list, like "SEGV"?
+  for (size_t i = SIGHUP; i <= SIGSYS; ++i) {
+    if (!strcmp(str, sys_signame[i])) {
+      *sig = i;
+      return 0;
+    }
+  }
+
+  // The two named special cases?
+  if (!strcmp(str, "RTMIN")) {
+    *sig = SIGRTMIN;
+    return 0;
+  }
+  if (!strcmp(str, "RTMAX")) {
+    *sig = SIGRTMAX;
+    return 0;
+  }
+
+  // Must be either an integer corresponding to a regular signal such as "9",
+  // or a string of the form "RTMIN+%d" or "RTMAX-%d".
+  int base = 0;
+  if (!strncmp(str, "RTMIN+", 6)) {
+    base = SIGRTMIN;
+    str += 5;
+  } else if (!strncmp(str, "RTMAX-", 6)) {
+    base = SIGRTMAX;
+    str += 5;
+  }
+  char* end = nullptr;
+  errno = 0;
+  int offset = strtol(str, &end, 10);
+  if (errno || *end) return -1;
+
+  // Reject out of range integers (like "666"),
+  // and out of range real-time signals (like "RTMIN+666" or "RTMAX-666").
+  int result = base + offset;
+  bool regular = (base == 0 && result >= SIGHUP && result <= SIGSYS);
+  bool realtime = (result >= SIGRTMIN && result <= SIGRTMAX);
+  if (!regular && !realtime) return -1;
+
+  *sig = result;
+  return 0;
+}
diff --git a/libc/include/signal.h b/libc/include/signal.h
index 9d47bcc..a827c2f 100644
--- a/libc/include/signal.h
+++ b/libc/include/signal.h
@@ -122,6 +122,34 @@
 int sigwaitinfo(const sigset_t* _Nonnull __set, siginfo_t* _Nullable __info) __INTRODUCED_IN(23);
 int sigwaitinfo64(const sigset64_t* _Nonnull __set, siginfo_t* _Nullable __info) __INTRODUCED_IN(28);
 
+/**
+ * Buffer size suitable for any call to sig2str().
+ */
+#define SIG2STR_MAX 32
+
+/**
+ * [sig2str(3)](http://man7.org/linux/man-pages/man3/sig2str.3.html)
+ * converts the integer corresponding to SIGSEGV (say) into a string
+ * like "SEGV" (not including the "SIG" used in the constants).
+ * SIG2STR_MAX is a safe size to use for the buffer.
+ *
+ * Returns 0 on success, and returns -1 _without_ setting errno otherwise.
+ *
+ * Available since API level 36.
+ */
+int sig2str(int __signal, char* _Nonnull __buf) __INTRODUCED_IN(36);
+
+/**
+ * [str2sig(3)](http://man7.org/linux/man-pages/man3/str2sig.3.html)
+ * converts a string like "SEGV" (not including the "SIG" used in the constants)
+ * into the integer corresponding to SIGSEGV.
+ *
+ * Returns 0 on success, and returns -1 _without_ setting errno otherwise.
+ *
+ * Available since API level 36.
+ */
+int str2sig(const char* _Nonnull __name, int* _Nonnull __signal) __INTRODUCED_IN(36);
+
 __END_DECLS
 
 #endif
diff --git a/libc/libc.map.txt b/libc/libc.map.txt
index 35e67c8..ffd5d1e 100644
--- a/libc/libc.map.txt
+++ b/libc/libc.map.txt
@@ -953,6 +953,7 @@
     setvbuf;
     setxattr;
     shutdown;
+    sig2str; # introduced=36
     sigaction;
     sigaddset;
     sigaltstack;
@@ -995,6 +996,7 @@
     stdout; # var introduced=23
     stpcpy;
     stpncpy;
+    str2sig; # introduced=36
     strcasecmp;
     strcasecmp_l; # introduced=23
     strcasestr;
diff --git a/tests/headers/posix/signal_h.c b/tests/headers/posix/signal_h.c
index c2e544e..82751f4 100644
--- a/tests/headers/posix/signal_h.c
+++ b/tests/headers/posix/signal_h.c
@@ -63,6 +63,10 @@
   MACRO(SIGEV_SIGNAL);
   MACRO(SIGEV_THREAD);
 
+#if !defined(__GLIBC__)  // Our glibc is too old.
+  MACRO(SIG2STR_MAX);
+#endif
+
   TYPE(union sigval);
   STRUCT_MEMBER(union sigval, int, sival_int);
   STRUCT_MEMBER(union sigval, void*, sival_ptr);
@@ -205,6 +209,9 @@
   FUNCTION(pthread_kill, int (*f)(pthread_t, int));
   FUNCTION(pthread_sigmask, int (*f)(int, const sigset_t*, sigset_t*));
   FUNCTION(raise, int (*f)(int));
+#if !defined(__GLIBC__)  // Our glibc is too old.
+  FUNCTION(sig2str, int (*f)(int, char*));
+#endif
   FUNCTION(sigaction, int (*f)(int, const struct sigaction*, struct sigaction*));
   FUNCTION(sigaddset, int (*f)(sigset_t*, int));
   FUNCTION(sigaltstack, int (*f)(const stack_t*, stack_t*));
@@ -226,4 +233,7 @@
   FUNCTION(sigtimedwait, int (*f)(const sigset_t*, siginfo_t*, const struct timespec*));
   FUNCTION(sigwait, int (*f)(const sigset_t*, int*));
   FUNCTION(sigwaitinfo, int (*f)(const sigset_t*, siginfo_t*));
+#if !defined(__GLIBC__)  // Our glibc is too old.
+  FUNCTION(str2sig, int (*f)(const char*, int*));
+#endif
 }
diff --git a/tests/signal_test.cpp b/tests/signal_test.cpp
index de126da..c1719dc 100644
--- a/tests/signal_test.cpp
+++ b/tests/signal_test.cpp
@@ -982,3 +982,94 @@
   ASSERT_EQ(-1, killpg(-1, SIGKILL));
   ASSERT_ERRNO(EINVAL);
 }
+
+TEST(signal, sig2str) {
+#if defined(__BIONIC__)
+  char str[SIG2STR_MAX];
+
+  // A regular signal.
+  ASSERT_EQ(0, sig2str(SIGHUP, str));
+  ASSERT_STREQ("HUP", str);
+
+  // A real-time signal.
+  ASSERT_EQ(0, sig2str(SIGRTMIN + 4, str));
+  ASSERT_STREQ("RTMIN+4", str);
+  ASSERT_EQ(0, sig2str(SIGRTMAX - 4, str));
+  ASSERT_STREQ("RTMAX-4", str);
+  // Special cases.
+  ASSERT_EQ(0, sig2str(SIGRTMAX, str));
+  ASSERT_STREQ("RTMAX", str);
+  ASSERT_EQ(0, sig2str(SIGRTMIN, str));
+  ASSERT_STREQ("RTMIN", str);
+  // One of the signals the C library keeps to itself.
+  ASSERT_EQ(-1, sig2str(32, str));  // __SIGRTMIN
+
+  // Errors.
+  ASSERT_EQ(-1, sig2str(-1, str));    // Too small.
+  ASSERT_EQ(-1, sig2str(0, str));     // Still too small.
+  ASSERT_EQ(-1, sig2str(1234, str));  // Too large.
+#else
+  GTEST_SKIP() << "our old glibc doesn't have sig2str";
+#endif
+}
+
+TEST(signal, str2sig) {
+#if defined(__BIONIC__)
+  int sig;
+
+  // A regular signal, by number.
+  sig = -1;
+  ASSERT_EQ(0, str2sig("9", &sig));
+  ASSERT_EQ(SIGKILL, sig);
+
+  // A regular signal, by name.
+  sig = -1;
+  ASSERT_EQ(0, str2sig("HUP", &sig));
+  ASSERT_EQ(SIGHUP, sig);
+
+  // A real-time signal, by number.
+  sig = -1;
+  ASSERT_EQ(0, str2sig("64", &sig));
+  ASSERT_EQ(SIGRTMAX, sig);
+
+  // A real-time signal, by name and offset.
+  sig = -1;
+  ASSERT_EQ(0, str2sig("RTMAX-4", &sig));
+  ASSERT_EQ(SIGRTMAX - 4, sig);
+  sig = -1;
+  ASSERT_EQ(0, str2sig("RTMIN+4", &sig));
+  ASSERT_EQ(SIGRTMIN + 4, sig);
+  // Unspecified by POSIX, but we try to be reasonable.
+  sig = -1;
+  ASSERT_EQ(0, str2sig("RTMAX-0", &sig));
+  ASSERT_EQ(SIGRTMAX, sig);
+  sig = -1;
+  ASSERT_EQ(0, str2sig("RTMIN+0", &sig));
+  ASSERT_EQ(SIGRTMIN, sig);
+  // One of the signals the C library keeps to itself, numerically.
+  ASSERT_EQ(-1, str2sig("32", &sig));  // __SIGRTMIN
+
+  // Special cases.
+  sig = -1;
+  ASSERT_EQ(0, str2sig("RTMAX", &sig));
+  ASSERT_EQ(SIGRTMAX, sig);
+  sig = -1;
+  ASSERT_EQ(0, str2sig("RTMIN", &sig));
+  ASSERT_EQ(SIGRTMIN, sig);
+
+  // Errors.
+  ASSERT_EQ(-1, str2sig("SIGHUP", &sig));     // No "SIG" prefix allowed.
+  ASSERT_EQ(-1, str2sig("-1", &sig));         // Too small.
+  ASSERT_EQ(-1, str2sig("0", &sig));          // Still too small.
+  ASSERT_EQ(-1, str2sig("1234", &sig));       // Too large.
+  ASSERT_EQ(-1, str2sig("RTMAX-666", &sig));  // Offset too small.
+  ASSERT_EQ(-1, str2sig("RTMIN+666", &sig));  // Offset too large.
+  ASSERT_EQ(-1, str2sig("RTMAX-+1", &sig));   // Silly.
+  ASSERT_EQ(-1, str2sig("RTMIN+-1", &sig));   // Silly.
+  ASSERT_EQ(-1, str2sig("HUPs", &sig));       // Trailing junk.
+  ASSERT_EQ(-1, str2sig("2b", &sig));         // Trailing junk.
+  ASSERT_EQ(-1, str2sig("RTMIN+2b", &sig));   // Trailing junk.
+#else
+  GTEST_SKIP() << "our old glibc doesn't have str2sig";
+#endif
+}