feat(errno): public errno.h with strerror/strerror_r

This commit is contained in:
2026-09-03 17:20:24 -04:00
parent c5a77c61fa
commit ce517ad0d6
4 changed files with 680 additions and 158 deletions
+145
View File
@@ -0,0 +1,145 @@
/*
* vlibc — strerror/strerror_r test (todo 2).
*
* Exercises the public <errno.h> end to end:
*
* 1. strerror(0) is "Success"; known values return the exact message and
* are non-NULL and distinct; aliases (EWOULDBLOCK/EDEADLOCK) resolve to
* their canonical entries; the last table value (EHWPOISON) is covered.
* 2. Unknown errno values never return NULL and never crash: 9999 and the
* ABI-gap value 41 format as "Unknown error <N>", negatives too.
* 3. strerror_r (XSI): returns 0 and fills buf with the right text; an
* unknown value still returns 0 with "Unknown error <N>"; truncation
* into a tiny buf stays NUL-terminated; buflen 0 writes nothing; a NULL
* buf with buflen > 0 returns EINVAL.
*
* The test deliberately never touches errno itself: under the host libc the
* TCB slot our errno macro addresses is glibc's private TLS state, and
* writing it would corrupt the host (see tests/syscall_test.c). All coverage
* here is through the errnum parameters.
*
* Not part of the library proper; compiled manually for this todo (the
* tests/ + make check wiring is owned by a later todo).
*/
#include <errno.h>
#include <stdio.h>
#include <string.h>
static int failures;
static void
check(int cond, const char *what)
{
if (!cond)
{
printf("FAIL: %s\n", what);
failures++;
}
}
/*
* Failure scenario (-f): an unknown errno must yield "Unknown error 9999",
* non-NULL, without crashing. Prints the observed string; exits 0 only when
* the failure behaved exactly as specified.
*/
static int
failure_scenario(void)
{
const char *s = strerror(9999);
if (s == NULL)
{
printf("strerror(9999)=NULL\n");
return 1;
}
printf("strerror(9999)=%s\n", s);
return strcmp(s, "Unknown error 9999") == 0 ? 0 : 1;
}
int
main(int argc, char **argv)
{
const char *a;
const char *b;
char buf[64];
char tiny[8];
char untouched[4];
int r;
if (argc == 2 && argv[1][0] == '-' && argv[1][1] == 'f')
{
return failure_scenario();
}
/* 1. Known values: exact text, non-NULL, distinct. */
check(strcmp(strerror(0), "Success") == 0, "strerror(0) == \"Success\"");
check(strcmp(strerror(2), "No such file or directory") == 0,
"strerror(2) == \"No such file or directory\"");
check(strcmp(strerror(EINVAL), "Invalid argument") == 0,
"strerror(EINVAL) == \"Invalid argument\"");
check(strcmp(strerror(EPERM), "Operation not permitted") == 0,
"strerror(EPERM) == \"Operation not permitted\"");
a = strerror(EINVAL);
b = strerror(EPERM);
check(a != NULL && b != NULL, "known values non-NULL");
check(a != b, "known values distinct");
/* Aliases resolve to their canonical entries. */
check(strerror(EWOULDBLOCK) == strerror(EAGAIN), "EWOULDBLOCK == EAGAIN");
check(strerror(EDEADLOCK) == strerror(EDEADLK), "EDEADLOCK == EDEADLK");
/* Table ends: last defined value and ABI gap values. */
check(strerror(EHWPOISON) != NULL, "EHWPOISON (last value) non-NULL");
check(strcmp(strerror(41), "Unknown error 41") == 0, "ABI gap 41 unknown");
check(strcmp(strerror(58), "Unknown error 58") == 0, "ABI gap 58 unknown");
/* 2. Unknown values: never NULL, never crash, formatted. */
a = strerror(9999);
check(a != NULL, "strerror(9999) non-NULL");
check(strcmp(a, "Unknown error 9999") == 0, "strerror(9999) text");
a = strerror(-7);
check(a != NULL, "strerror(-7) non-NULL");
check(strcmp(a, "Unknown error -7") == 0, "strerror(-7) text");
/* 3. strerror_r: XSI int return, exact fill. */
r = strerror_r(EDOM, buf, sizeof buf);
check(r == 0, "strerror_r(EDOM) returns 0");
check(strcmp(buf, "Numerical argument out of domain") == 0, "strerror_r(EDOM) fills buf");
/* Unknown errno still succeeds and formats into buf (XSI). */
r = strerror_r(9999, buf, sizeof buf);
check(r == 0, "strerror_r(9999) returns 0 (XSI)");
check(strcmp(buf, "Unknown error 9999") == 0, "strerror_r(9999) text");
/* Truncation into a tiny buf stays NUL-terminated. */
r = strerror_r(ENAMETOOLONG, tiny, sizeof tiny);
check(r == 0, "strerror_r(ENAMETOOLONG, tiny) returns 0");
check(tiny[sizeof tiny - 1] == '\0', "tiny buf NUL-terminated");
check(strcmp(tiny, "File na") == 0, "tiny buf truncated prefix");
/* buflen 0: nothing written, still success. */
untouched[0] = 'X';
untouched[1] = 'X';
untouched[2] = 'X';
untouched[3] = '\0';
r = strerror_r(EDOM, untouched, 0);
check(r == 0, "strerror_r(EDOM, buf, 0) returns 0");
check(untouched[0] == 'X', "buflen 0 writes nothing");
/* NULL buf with buflen > 0: EINVAL. */
r = strerror_r(EDOM, NULL, 16);
check(r == EINVAL, "strerror_r(NULL buf) returns EINVAL");
if (failures > 0)
{
printf("FAILED\n");
}
else
{
printf("strerror ok\n");
}
return failures == 0 ? 0 : 1;
}