feat(string): complete string.h + strdup/memccpy
This commit is contained in:
@@ -0,0 +1,30 @@
|
||||
#ifndef VLIBC_INTERNAL_MALLOC_H
|
||||
#define VLIBC_INTERNAL_MALLOC_H
|
||||
|
||||
/*
|
||||
* vlibc — internal allocator seam (todo 8).
|
||||
*
|
||||
* The allocator todo (#7) owns the real implementation and provides these
|
||||
* entry points; library code that must allocate (strdup, strndup, and later
|
||||
* stdio/regex/...) calls them instead of the public malloc/free so that the
|
||||
* allocator remains the single allocation implementation. The public
|
||||
* malloc/free/calloc/realloc of todo 7 must be backed by the same allocator,
|
||||
* so a block returned by __libc_malloc can be released with public free.
|
||||
*
|
||||
* - __libc_malloc(n): allocate n bytes, 16-byte aligned; NULL + errno ENOMEM
|
||||
* on failure. May return a unique pointer even when n == 0.
|
||||
* - __libc_free(p): release a block returned by __libc_malloc; NULL is a
|
||||
* no-op.
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include "libc.h"
|
||||
|
||||
hidden void *
|
||||
__libc_malloc(size_t n); // NOLINT(bugprone-reserved-identifier)
|
||||
|
||||
hidden void
|
||||
__libc_free(void *p); // NOLINT(bugprone-reserved-identifier)
|
||||
|
||||
#endif /* VLIBC_INTERNAL_MALLOC_H */
|
||||
@@ -0,0 +1,40 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#if VLIBC_LEVEL_GE(2)
|
||||
|
||||
/*
|
||||
* XSI memccpy (todo 8): copy at most n bytes from src to dst, stopping
|
||||
* after the first byte equal to c (converted to unsigned char). Return a
|
||||
* pointer to the byte after c in dst when found, NULL otherwise. The
|
||||
* regions must not overlap (restrict).
|
||||
*
|
||||
* See strcpy.c: the copy loop is a recognizable pattern, so disable
|
||||
* -ftree-loop-distribute-patterns here too.
|
||||
*/
|
||||
__attribute__((optimize("no-tree-loop-distribute-patterns"))) void *
|
||||
memccpy(void *restrict dst, const void *restrict src, int c, size_t n) // NOLINT(bugprone-*)
|
||||
{
|
||||
unsigned char *d = dst;
|
||||
const unsigned char *s = src;
|
||||
const unsigned char uc = (unsigned char)c;
|
||||
|
||||
while (n != 0)
|
||||
{
|
||||
*d = *s;
|
||||
if (*d == uc)
|
||||
{
|
||||
return d + 1;
|
||||
}
|
||||
d++;
|
||||
s++;
|
||||
n--;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#endif /* VLIBC_LEVEL_GE(2) */
|
||||
@@ -0,0 +1,34 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <string.h>
|
||||
|
||||
/*
|
||||
* Return a pointer to the first occurrence of c (converted to unsigned
|
||||
* char) among the first n bytes of s, or NULL when absent. Bytes are
|
||||
* examined as unsigned char so negative c values search for the matching
|
||||
* 0x80..0xff byte.
|
||||
*
|
||||
* See strcpy.c: the scan loop is a pattern GCC's
|
||||
* -ftree-loop-distribute-patterns can recognize (memchr), so disable that
|
||||
* transformation to avoid self-recursion.
|
||||
*/
|
||||
__attribute__((optimize("no-tree-loop-distribute-patterns"))) void *
|
||||
memchr(const void *s, int c, size_t n) // NOLINT(bugprone-easily-swappable-parameters)
|
||||
{
|
||||
const unsigned char *p = s;
|
||||
const unsigned char uc = (unsigned char)c;
|
||||
|
||||
while (n != 0)
|
||||
{
|
||||
if (*p == uc)
|
||||
{
|
||||
return (void *)p;
|
||||
}
|
||||
p++;
|
||||
n--;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <string.h>
|
||||
|
||||
/*
|
||||
* Compare the first n bytes of lhs and rhs as unsigned char; return
|
||||
* negative, zero, or positive when lhs is less than, equal to, or greater
|
||||
* than rhs. Unlike strcmp the comparison never stops at a NUL byte.
|
||||
*
|
||||
* See strcpy.c: the compare loop is a pattern GCC's
|
||||
* -ftree-loop-distribute-patterns can recognize (memcmp), so disable that
|
||||
* transformation to avoid self-recursion.
|
||||
*/
|
||||
__attribute__((optimize("no-tree-loop-distribute-patterns"))) int
|
||||
memcmp(const void *lhs, const void *rhs, size_t n) // NOLINT(bugprone-easily-swappable-parameters)
|
||||
{
|
||||
const unsigned char *l = lhs;
|
||||
const unsigned char *r = rhs;
|
||||
|
||||
while (n != 0)
|
||||
{
|
||||
if (*l != *r)
|
||||
{
|
||||
return *l < *r ? -1 : 1;
|
||||
}
|
||||
l++;
|
||||
r++;
|
||||
n--;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#if VLIBC_LEVEL_GE(2)
|
||||
|
||||
/*
|
||||
* XSI stpcpy / stpncpy (todo 8): strcpy / strncpy variants that return a
|
||||
* pointer to the terminating NUL of dst instead of dst itself.
|
||||
*
|
||||
* See strcpy.c: the copy loops are recognizable patterns, so disable
|
||||
* -ftree-loop-distribute-patterns here too.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copy src to dst including the terminating NUL; return a pointer to the
|
||||
* NUL written into dst.
|
||||
*/
|
||||
__attribute__((optimize("no-tree-loop-distribute-patterns"))) char *
|
||||
stpcpy(char *restrict dst, const char *restrict src) // NOLINT(bugprone-*)
|
||||
{
|
||||
char *d = dst;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
*d = *src;
|
||||
if (*d == '\0')
|
||||
{
|
||||
return d;
|
||||
}
|
||||
d++;
|
||||
src++;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Copy at most n bytes from src to dst, NUL-padding when src is shorter;
|
||||
* return a pointer to the first NUL written into dst, or to dst + n when no
|
||||
* NUL was written.
|
||||
*/
|
||||
__attribute__((optimize("no-tree-loop-distribute-patterns"))) char *
|
||||
stpncpy(char *restrict dst, const char *restrict src, size_t n) // NOLINT(bugprone-*)
|
||||
{
|
||||
char *d = dst;
|
||||
char *end;
|
||||
|
||||
/* Copy until src ends or n bytes have been copied. */
|
||||
while (n != 0 && *src != '\0')
|
||||
{
|
||||
*d++ = *src++;
|
||||
n--;
|
||||
}
|
||||
|
||||
/* The first NUL (or one past the last byte) sits right here. */
|
||||
end = d;
|
||||
|
||||
/* NUL-pad the remainder. */
|
||||
while (n != 0)
|
||||
{
|
||||
*d++ = '\0';
|
||||
n--;
|
||||
}
|
||||
|
||||
return end;
|
||||
}
|
||||
|
||||
#endif /* VLIBC_LEVEL_GE(2) */
|
||||
@@ -0,0 +1,65 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <string.h>
|
||||
|
||||
/*
|
||||
* Append src (including its terminating NUL) to the end of dst; return dst.
|
||||
* The strings must not overlap (restrict).
|
||||
*
|
||||
* See strcpy.c: the copy loop is a recognizable pattern, so disable
|
||||
* -ftree-loop-distribute-patterns here too.
|
||||
*/
|
||||
__attribute__((optimize("no-tree-loop-distribute-patterns"))) char *
|
||||
strcat(char *restrict dst, const char *restrict src) // NOLINT(bugprone-*)
|
||||
{
|
||||
char *d = dst;
|
||||
|
||||
/* Find the terminating NUL of dst. */
|
||||
while (*d != '\0')
|
||||
{
|
||||
d++;
|
||||
}
|
||||
|
||||
/* Append src including its NUL. */
|
||||
for (;;)
|
||||
{
|
||||
*d++ = *src;
|
||||
if (*src == '\0')
|
||||
{
|
||||
return dst;
|
||||
}
|
||||
src++;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Append at most n bytes of src to dst and always NUL-terminate; return dst.
|
||||
*
|
||||
* See strcpy.c: the copy loop is a recognizable pattern, so disable
|
||||
* -ftree-loop-distribute-patterns here too.
|
||||
*/
|
||||
__attribute__((optimize("no-tree-loop-distribute-patterns"))) char *
|
||||
strncat(char *restrict dst, const char *restrict src, size_t n) // NOLINT(bugprone-*)
|
||||
{
|
||||
char *d = dst;
|
||||
|
||||
/* Find the terminating NUL of dst. */
|
||||
while (*d != '\0')
|
||||
{
|
||||
d++;
|
||||
}
|
||||
|
||||
/* Append at most n bytes of src, stopping early at its NUL. */
|
||||
while (n != 0 && *src != '\0')
|
||||
{
|
||||
*d++ = *src++;
|
||||
n--;
|
||||
}
|
||||
|
||||
/* strncat always NUL-terminates. */
|
||||
*d = '\0';
|
||||
|
||||
return dst;
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <string.h>
|
||||
|
||||
/*
|
||||
* Return a pointer to the first occurrence of c (converted to char) in s,
|
||||
* or NULL when absent. The terminating NUL is part of the string, so
|
||||
* strchr(s, '\0') returns a pointer to it.
|
||||
*/
|
||||
char *
|
||||
strchr(const char *s, int c) // NOLINT(bugprone-easily-swappable-parameters)
|
||||
{
|
||||
const char cc = (char)c;
|
||||
|
||||
for (;; s++)
|
||||
{
|
||||
if (*s == cc)
|
||||
{
|
||||
return (char *)s;
|
||||
}
|
||||
if (*s == '\0')
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Return a pointer to the last occurrence of c (converted to char) in s, or
|
||||
* NULL when absent. The terminating NUL is part of the string, so
|
||||
* strrchr(s, '\0') returns a pointer to it.
|
||||
*/
|
||||
char *
|
||||
strrchr(const char *s, int c) // NOLINT(bugprone-easily-swappable-parameters)
|
||||
{
|
||||
const char cc = (char)c;
|
||||
const char *last = NULL;
|
||||
|
||||
for (;; s++)
|
||||
{
|
||||
if (*s == cc)
|
||||
{
|
||||
last = s;
|
||||
}
|
||||
if (*s == '\0')
|
||||
{
|
||||
return (char *)last;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <string.h>
|
||||
|
||||
/*
|
||||
* C-locale collation (todo 49 owns real locales). In the "C" locale the
|
||||
* collating sequence is the machine collating sequence — byte order — so
|
||||
* strcoll degenerates to strcmp, and the strxfrm transformation is the
|
||||
* identity: the transformed form of a string is the string itself.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Compare s1 and s2 under the active locale's collating sequence and return
|
||||
* negative, zero, or positive. "C" locale: identical to strcmp.
|
||||
*/
|
||||
int
|
||||
strcoll(const char *s1, const char *s2) // NOLINT(bugprone-easily-swappable-parameters)
|
||||
{
|
||||
return strcmp(s1, s2);
|
||||
}
|
||||
|
||||
/*
|
||||
* Transform src so that strcmp on transformed strings orders them as
|
||||
* strcoll would, storing at most n bytes of the result in dst (always
|
||||
* NUL-terminated when n > 0; nothing is written when n == 0). Return the
|
||||
* length of the full transformed string excluding the NUL — in the "C"
|
||||
* locale, strlen(src).
|
||||
*/
|
||||
size_t
|
||||
strxfrm(char *restrict dst, const char *restrict src,
|
||||
size_t n) // NOLINT(bugprone-easily-swappable-parameters)
|
||||
{
|
||||
size_t len = strlen(src);
|
||||
size_t copy;
|
||||
size_t i;
|
||||
|
||||
if (n == 0)
|
||||
{
|
||||
return len;
|
||||
}
|
||||
|
||||
copy = len < n ? len : n - 1;
|
||||
for (i = 0; i < copy; i++)
|
||||
{
|
||||
dst[i] = src[i];
|
||||
}
|
||||
dst[copy] = '\0';
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
#if VLIBC_LEVEL_GE(2)
|
||||
/*
|
||||
* XSI locale-parameterized variants. No locale machinery exists yet (todo
|
||||
* 49), so the locale argument is accepted and ignored and the behavior is
|
||||
* the "C" locale behavior of the base functions. The locale parameter is
|
||||
* typed void * for now: locale_t will be an ABI-identical pointer typedef
|
||||
* defined by <locale.h>, and todo 49 updates these signatures to the real
|
||||
* type.
|
||||
*/
|
||||
|
||||
int
|
||||
strcoll_l(const char *s1, const char *s2,
|
||||
void *locale) // NOLINT(bugprone-easily-swappable-parameters)
|
||||
{
|
||||
(void)locale;
|
||||
return strcoll(s1, s2);
|
||||
}
|
||||
|
||||
size_t
|
||||
strxfrm_l(char *restrict dst, const char *restrict src, size_t n,
|
||||
void *locale) // NOLINT(bugprone-easily-swappable-parameters)
|
||||
{
|
||||
(void)locale;
|
||||
return strxfrm(dst, src, n);
|
||||
}
|
||||
#endif /* VLIBC_LEVEL_GE(2) */
|
||||
@@ -0,0 +1,31 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <string.h>
|
||||
|
||||
/*
|
||||
* Copy src to dst including the terminating NUL; return dst. The strings
|
||||
* must not overlap (restrict).
|
||||
*
|
||||
* The copy loop below is the canonical strcpy idiom; GCC's
|
||||
* -ftree-loop-distribute-patterns (on by default at -O2 and -O3) can rewrite
|
||||
* such loops into calls to the recognized string builtins — potentially this
|
||||
* very function — causing infinite self-recursion. Disable that one
|
||||
* transformation for this function only.
|
||||
*/
|
||||
__attribute__((optimize("no-tree-loop-distribute-patterns"))) char *
|
||||
strcpy(char *restrict dst, const char *restrict src) // NOLINT(bugprone-*)
|
||||
{
|
||||
char *d = dst;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
*d++ = *src;
|
||||
if (*src == '\0')
|
||||
{
|
||||
return dst;
|
||||
}
|
||||
src++;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
#include "../internal/malloc.h"
|
||||
|
||||
/*
|
||||
* strdup / strndup (todo 8).
|
||||
*
|
||||
* Both allocate through the internal allocator seam __libc_malloc (see
|
||||
* src/internal/malloc.h; todo 7 provides the real allocator), so the copies
|
||||
* are released with the public free. On allocation failure errno is set to
|
||||
* ENOMEM and NULL is returned.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Return a heap copy of s, or NULL with errno ENOMEM on allocation failure.
|
||||
*/
|
||||
char *
|
||||
strdup(const char *s)
|
||||
{
|
||||
size_t len = strlen(s) + 1;
|
||||
char *p = __libc_malloc(len);
|
||||
size_t i;
|
||||
|
||||
if (p == NULL)
|
||||
{
|
||||
errno = ENOMEM;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (i = 0; i < len; i++)
|
||||
{
|
||||
p[i] = s[i];
|
||||
}
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return a heap copy of the first n bytes of s, NUL-terminated. When s is
|
||||
* shorter than n the copy is the whole string; strndup(s, 0) returns the
|
||||
* empty string. NULL with errno ENOMEM on allocation failure.
|
||||
*/
|
||||
char *
|
||||
strndup(const char *s, size_t n)
|
||||
{
|
||||
size_t len = strnlen(s, n);
|
||||
char *p = __libc_malloc(len + 1);
|
||||
size_t i;
|
||||
|
||||
if (p == NULL)
|
||||
{
|
||||
errno = ENOMEM;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (i = 0; i < len; i++)
|
||||
{
|
||||
p[i] = s[i];
|
||||
}
|
||||
p[len] = '\0';
|
||||
|
||||
return p;
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <string.h>
|
||||
|
||||
/*
|
||||
* Compare at most n bytes of lhs and rhs as unsigned char, stopping early
|
||||
* at the first difference or the first NUL of either string. Return
|
||||
* negative, zero, or positive when lhs is less than, equal to, or greater
|
||||
* than rhs.
|
||||
*
|
||||
* See strcpy.c: the scan/compare loop is a pattern GCC's
|
||||
* -ftree-loop-distribute-patterns can recognize (strcmp/strncmp), so disable
|
||||
* that transformation to avoid self-recursion.
|
||||
*/
|
||||
__attribute__((optimize("no-tree-loop-distribute-patterns"))) int
|
||||
strncmp(const char *lhs, const char *rhs, size_t n) // NOLINT(bugprone-easily-swappable-parameters)
|
||||
{
|
||||
const unsigned char *l = (const unsigned char *)lhs;
|
||||
const unsigned char *r = (const unsigned char *)rhs;
|
||||
|
||||
while (n != 0 && *l != '\0' && *l == *r)
|
||||
{
|
||||
l++;
|
||||
r++;
|
||||
n--;
|
||||
}
|
||||
|
||||
if (n == 0 || *l == *r)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return *l < *r ? -1 : 1;
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <string.h>
|
||||
|
||||
/*
|
||||
* Copy at most n bytes from src to dst. When src is shorter than n, the
|
||||
* remainder of dst is filled with NULs; when src is n bytes or longer, no
|
||||
* NUL is written at all. Return dst.
|
||||
*
|
||||
* See strcpy.c: the copy loop is a recognizable pattern, so disable
|
||||
* -ftree-loop-distribute-patterns here too.
|
||||
*/
|
||||
__attribute__((optimize("no-tree-loop-distribute-patterns"))) char *
|
||||
strncpy(char *restrict dst, const char *restrict src, size_t n) // NOLINT(bugprone-*)
|
||||
{
|
||||
char *d = dst;
|
||||
|
||||
/* Copy until src ends or n bytes have been copied. */
|
||||
while (n != 0 && *src != '\0')
|
||||
{
|
||||
*d++ = *src++;
|
||||
n--;
|
||||
}
|
||||
|
||||
/* NUL-pad the remainder (this also terminates a short src). */
|
||||
while (n != 0)
|
||||
{
|
||||
*d++ = '\0';
|
||||
n--;
|
||||
}
|
||||
|
||||
return dst;
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <string.h>
|
||||
|
||||
/*
|
||||
* Return the length of s, excluding the terminating NUL, examining at most
|
||||
* maxlen bytes: the result is min(strlen(s), maxlen) when s is a proper
|
||||
* string. Unlike strlen, the scan provably stops at maxlen bytes, which is
|
||||
* why strndup and friends rely on it for untrusted buffers.
|
||||
*
|
||||
* See strcpy.c: the NUL scan is exactly the pattern GCC's
|
||||
* -ftree-loop-distribute-patterns recognizes as strnlen/strlen, so disable
|
||||
* that transformation to avoid self-recursion.
|
||||
*/
|
||||
__attribute__((optimize("no-tree-loop-distribute-patterns"))) size_t
|
||||
strnlen(const char *s, size_t maxlen)
|
||||
{
|
||||
size_t n = 0;
|
||||
|
||||
while (n < maxlen && s[n] != '\0')
|
||||
{
|
||||
n++;
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <string.h>
|
||||
|
||||
/*
|
||||
* vlibc — strsignal (todo 8).
|
||||
*
|
||||
* Maps a signal number to its description string. The table uses the
|
||||
* x86_64 Linux asm-generic signal numbers (SIGHUP=1 .. SIGSYS=31), which
|
||||
* are kernel-ABI facts; <signal.h> (todo 28) will become the canonical home
|
||||
* for the signal-number names, and this table should then reference those
|
||||
* names instead of the literal numbers.
|
||||
*
|
||||
* Known signals return immutable static string literals. Unrecognized
|
||||
* numbers — including 0, whose text POSIX deliberately leaves unspecified —
|
||||
* format as "Unknown signal <N>" into one shared static buffer. That is the
|
||||
* same single-buffer tradeoff as strerror (see src/errno/strerror.c): the
|
||||
* unknown path is rare diagnostics, there is no per-thread storage beyond
|
||||
* the errno TCB slot yet, and strsignal must not depend on malloc. Migrate
|
||||
* to a per-thread buffer once the TCB supports it.
|
||||
*/
|
||||
|
||||
/*
|
||||
* One entry per x86_64 asm-generic signal 1..31. The texts are standard
|
||||
* English descriptions of what each signal means; the wording is vlibc's
|
||||
* own (POSIX fixes the meanings, not the strings).
|
||||
*/
|
||||
static const char *const sigmsg[] = {
|
||||
[1] = "Hangup",
|
||||
[2] = "Interrupt",
|
||||
[3] = "Quit",
|
||||
[4] = "Illegal instruction",
|
||||
[5] = "Trace/breakpoint trap",
|
||||
[6] = "Aborted",
|
||||
[7] = "Bus error",
|
||||
[8] = "Floating point exception",
|
||||
[9] = "Killed",
|
||||
[10] = "User defined signal 1",
|
||||
[11] = "Segmentation fault",
|
||||
[12] = "User defined signal 2",
|
||||
[13] = "Broken pipe",
|
||||
[14] = "Alarm clock",
|
||||
[15] = "Terminated",
|
||||
[16] = "Stack fault",
|
||||
[17] = "Child exited",
|
||||
[18] = "Continued",
|
||||
[19] = "Stopped (signal)",
|
||||
[20] = "Stopped",
|
||||
[21] = "Stopped (tty input)",
|
||||
[22] = "Stopped (tty output)",
|
||||
[23] = "Urgent I/O condition",
|
||||
[24] = "CPU time limit exceeded",
|
||||
[25] = "File size limit exceeded",
|
||||
[26] = "Virtual timer expired",
|
||||
[27] = "Profiling timer expired",
|
||||
[28] = "Window changed",
|
||||
[29] = "I/O possible",
|
||||
[30] = "Power failure",
|
||||
[31] = "Bad system call",
|
||||
};
|
||||
|
||||
/* The table spans exactly 1..31 (index 0 stays NULL: signal 0 is unknown). */
|
||||
_Static_assert(sizeof sigmsg / sizeof sigmsg[0] == 32,
|
||||
"strsignal table must cover signal numbers 1..31");
|
||||
|
||||
/* Shared storage for the unknown-signal path (see the file-top comment). */
|
||||
static char sigbuf[32];
|
||||
|
||||
/*
|
||||
* Write "Unknown signal <sig>" into dst, truncating to cap bytes and always
|
||||
* NUL-terminating when cap > 0. Hand-rolled so this file needs no stdio.
|
||||
*/
|
||||
static void
|
||||
format_unknown(int sig, char *dst, size_t cap) // NOLINT(bugprone-easily-swappable-parameters)
|
||||
{
|
||||
static const char prefix[] = "Unknown signal ";
|
||||
char digits[12]; /* enough for "-2147483648" */
|
||||
size_t ndigits;
|
||||
size_t i;
|
||||
unsigned long mag;
|
||||
|
||||
if (cap == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* Absolute value as unsigned long; INT_MIN negates safely in long. */
|
||||
mag = (unsigned long)(sig < 0 ? -(long)sig : sig);
|
||||
|
||||
/* Digits, least significant first. */
|
||||
ndigits = 0;
|
||||
do
|
||||
{
|
||||
digits[ndigits] = (char)('0' + (int)(mag % 10));
|
||||
ndigits++;
|
||||
mag /= 10;
|
||||
} while (mag != 0);
|
||||
|
||||
i = 0;
|
||||
while (i + 1 < cap && prefix[i] != '\0')
|
||||
{
|
||||
dst[i] = prefix[i];
|
||||
i++;
|
||||
}
|
||||
if (i + 1 < cap && sig < 0)
|
||||
{
|
||||
dst[i] = '-';
|
||||
i++;
|
||||
}
|
||||
while (i + 1 < cap && ndigits > 0)
|
||||
{
|
||||
ndigits--;
|
||||
dst[i] = digits[ndigits];
|
||||
i++;
|
||||
}
|
||||
dst[i] = '\0';
|
||||
}
|
||||
|
||||
/*
|
||||
* Return a pointer to a static string describing the signal sig. Never
|
||||
* returns NULL. Distinct known signals yield distinct strings; signal 0 and
|
||||
* out-of-range numbers yield "Unknown signal <sig>".
|
||||
*/
|
||||
char *
|
||||
strsignal(int sig)
|
||||
{
|
||||
if (sig > 0 && sig < (int)(sizeof sigmsg / sizeof sigmsg[0]))
|
||||
{
|
||||
return (char *)sigmsg[sig];
|
||||
}
|
||||
|
||||
format_unknown(sig, sigbuf, sizeof sigbuf);
|
||||
return sigbuf;
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <string.h>
|
||||
|
||||
/*
|
||||
* True when c appears anywhere in set (including as the NUL of set — but set
|
||||
* is a string, so scanning stops at that NUL, as intended).
|
||||
*/
|
||||
static int
|
||||
str_in_set(const char *set, char c) // NOLINT(bugprone-easily-swappable-parameters)
|
||||
{
|
||||
for (; *set != '\0'; set++)
|
||||
{
|
||||
if (*set == c)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the length of the initial span of s consisting entirely of bytes
|
||||
* that occur in accept.
|
||||
*/
|
||||
size_t
|
||||
strspn(const char *s, const char *accept) // NOLINT(bugprone-easily-swappable-parameters)
|
||||
{
|
||||
size_t n = 0;
|
||||
|
||||
while (s[n] != '\0' && str_in_set(accept, s[n]))
|
||||
{
|
||||
n++;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the length of the initial span of s consisting entirely of bytes
|
||||
* that do NOT occur in reject.
|
||||
*/
|
||||
size_t
|
||||
strcspn(const char *s, const char *reject) // NOLINT(bugprone-easily-swappable-parameters)
|
||||
{
|
||||
size_t n = 0;
|
||||
|
||||
while (s[n] != '\0' && !str_in_set(reject, s[n]))
|
||||
{
|
||||
n++;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return a pointer to the first byte in s that also occurs in accept, or
|
||||
* NULL when none occurs.
|
||||
*/
|
||||
char *
|
||||
strpbrk(const char *s, const char *accept) // NOLINT(bugprone-easily-swappable-parameters)
|
||||
{
|
||||
for (; *s != '\0'; s++)
|
||||
{
|
||||
if (str_in_set(accept, *s))
|
||||
{
|
||||
return (char *)s;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <string.h>
|
||||
|
||||
/*
|
||||
* Return a pointer to the first occurrence of needle in haystack, or NULL
|
||||
* when absent. An empty needle matches haystack itself.
|
||||
*
|
||||
* Naive two-pointer scan: for each position in haystack, compare characters
|
||||
* until the needle is exhausted (match) or a mismatch occurs (advance). The
|
||||
* inner loop stops at the NUL terminator of either string, so the comparison
|
||||
* never reads past the end of haystack when needle is longer than the
|
||||
* remaining tail.
|
||||
*/
|
||||
char *
|
||||
strstr(const char *haystack, const char *needle) // NOLINT(bugprone-easily-swappable-parameters)
|
||||
{
|
||||
if (*needle == '\0')
|
||||
{
|
||||
return (char *)haystack;
|
||||
}
|
||||
|
||||
for (; *haystack != '\0'; haystack++)
|
||||
{
|
||||
const char *h = haystack;
|
||||
const char *n = needle;
|
||||
|
||||
while (*n != '\0' && *h == *n)
|
||||
{
|
||||
h++;
|
||||
n++;
|
||||
}
|
||||
|
||||
if (*n == '\0')
|
||||
{
|
||||
return (char *)haystack;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <string.h>
|
||||
|
||||
/*
|
||||
* Shared continuation pointer for the non-reentrant strtok. Plain static
|
||||
* storage: POSIX requires strtok itself to be neither thread-safe nor
|
||||
* reentrant; callers needing either use strtok_r.
|
||||
*/
|
||||
static char *strtok_next;
|
||||
|
||||
/*
|
||||
* Split s into tokens delimited by any byte from sep. On the first call s
|
||||
* names the string; on later calls with s == NULL the search continues in
|
||||
* the same string. Leading and consecutive delimiters produce no empty
|
||||
* tokens, and the delimiter bytes in s are overwritten with NUL. Returns
|
||||
* NULL when no token remains.
|
||||
*/
|
||||
char *
|
||||
strtok(char *restrict s, const char *restrict sep) // NOLINT(bugprone-easily-swappable-parameters)
|
||||
{
|
||||
return strtok_r(s, sep, &strtok_next);
|
||||
}
|
||||
|
||||
/*
|
||||
* Reentrant strtok: the continuation pointer lives in *state instead of
|
||||
* private static storage. When a call finds no token (including the first
|
||||
* call on an empty or all-delimiter string), *state is set to NULL, so a
|
||||
* later strtok_r(NULL, sep, state) call returns NULL without further reads.
|
||||
*/
|
||||
char *
|
||||
strtok_r(char *restrict s, const char *restrict sep, char **restrict state)
|
||||
{
|
||||
char *tok;
|
||||
|
||||
if (s == NULL)
|
||||
{
|
||||
s = *state;
|
||||
if (s == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* Skip leading delimiters (this also skips the empty tokens that
|
||||
consecutive delimiters would otherwise produce). */
|
||||
s += strspn(s, sep);
|
||||
if (*s == '\0')
|
||||
{
|
||||
*state = NULL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
tok = s;
|
||||
|
||||
/* Find the end of the token. */
|
||||
s += strcspn(s, sep);
|
||||
if (*s != '\0')
|
||||
{
|
||||
/* Replace the delimiter with NUL and continue after it. */
|
||||
*s++ = '\0';
|
||||
}
|
||||
else
|
||||
{
|
||||
/* The token ran to the end of the string. */
|
||||
s = NULL;
|
||||
}
|
||||
|
||||
*state = s;
|
||||
return tok;
|
||||
}
|
||||
Reference in New Issue
Block a user