summaryrefslogtreecommitdiff
path: root/lib/libc/string
diff options
context:
space:
mode:
authorKacper <kacper@mail.openlinux.dev>2025-12-07 20:10:31 +0100
committerKacper <kacper@mail.openlinux.dev>2025-12-07 20:10:31 +0100
commitfc00c656c96528112d05cf0edf8631bd5eaea446 (patch)
treea6e0e6c588191a8bd1c64afc3b7a258e3e66c236 /lib/libc/string
Add build system scaffolding and libc headers
Diffstat (limited to 'lib/libc/string')
-rw-r--r--lib/libc/string/memccpy.c13
-rw-r--r--lib/libc/string/memchr.c12
-rw-r--r--lib/libc/string/memcmp.c14
-rw-r--r--lib/libc/string/memcpy.c16
-rw-r--r--lib/libc/string/memmem.c15
-rw-r--r--lib/libc/string/memmove.c21
-rw-r--r--lib/libc/string/memset.c10
-rw-r--r--lib/libc/string/stpcpy.c8
-rw-r--r--lib/libc/string/stpncpy.c14
-rw-r--r--lib/libc/string/strcat.c14
-rw-r--r--lib/libc/string/strchr.c13
-rw-r--r--lib/libc/string/strcmp.c23
-rw-r--r--lib/libc/string/strcoll.c13
-rw-r--r--lib/libc/string/strcpy.c7
-rw-r--r--lib/libc/string/strcspn.c11
-rw-r--r--lib/libc/string/strdup.c12
-rw-r--r--lib/libc/string/strerror.c125
-rw-r--r--lib/libc/string/strlcat.c19
-rw-r--r--lib/libc/string/strlcpy.c16
-rw-r--r--lib/libc/string/strlen.c14
-rw-r--r--lib/libc/string/strncat.c19
-rw-r--r--lib/libc/string/strncmp.c16
-rw-r--r--lib/libc/string/strncpy.c16
-rw-r--r--lib/libc/string/strndup.c15
-rw-r--r--lib/libc/string/strnlen.c11
-rw-r--r--lib/libc/string/strpbrk.c16
-rw-r--r--lib/libc/string/strrchr.c12
-rw-r--r--lib/libc/string/strspn.c12
-rw-r--r--lib/libc/string/strstr.c27
-rw-r--r--lib/libc/string/strtok.c40
-rw-r--r--lib/libc/string/strtok_r.c37
-rw-r--r--lib/libc/string/strxfrm.c18
32 files changed, 629 insertions, 0 deletions
diff --git a/lib/libc/string/memccpy.c b/lib/libc/string/memccpy.c
new file mode 100644
index 00000000..29828182
--- /dev/null
+++ b/lib/libc/string/memccpy.c
@@ -0,0 +1,13 @@
+#include <stddef.h>
+
+void *memccpy(void *restrict s1, const void *restrict s2, int c, size_t n)
+{
+ const unsigned char *p2 = s2;
+ unsigned char *p1 = s1;
+ while (n--) {
+ if (*p2 == (unsigned char)c)
+ return p1 + 1;
+ *p1++ = *p2++;
+ }
+ return NULL;
+}
diff --git a/lib/libc/string/memchr.c b/lib/libc/string/memchr.c
new file mode 100644
index 00000000..38850aed
--- /dev/null
+++ b/lib/libc/string/memchr.c
@@ -0,0 +1,12 @@
+#include <stddef.h>
+
+void *memchr(const void *s, int c, size_t n)
+{
+ const unsigned char *p = s;
+ while (n--) {
+ if (*p == (unsigned char)c)
+ return (void *)p;
+ p++;
+ }
+ return NULL;
+}
diff --git a/lib/libc/string/memcmp.c b/lib/libc/string/memcmp.c
new file mode 100644
index 00000000..5efb282f
--- /dev/null
+++ b/lib/libc/string/memcmp.c
@@ -0,0 +1,14 @@
+#include <stddef.h>
+
+int memcmp(const void *s1, const void *s2, size_t n)
+{
+ const unsigned char *p1 = s1;
+ const unsigned char *p2 = s2;
+ while (n--) {
+ if (*p1 != *p2)
+ return *p1 - *p2;
+ p1++;
+ p2++;
+ }
+ return 0;
+}
diff --git a/lib/libc/string/memcpy.c b/lib/libc/string/memcpy.c
new file mode 100644
index 00000000..505cc622
--- /dev/null
+++ b/lib/libc/string/memcpy.c
@@ -0,0 +1,16 @@
+#include <string.h>
+#include <features.h>
+
+weak void *memcpy(void *restrict s1, const void *restrict s2, size_t n);
+
+void *memcpy(void *restrict s1, const void *restrict s2, size_t n)
+{
+ unsigned char *dest = (unsigned char *)s1;
+ const unsigned char *src = (const unsigned char *)s2;
+
+ while (n--) {
+ *dest++ = *src++;
+ }
+
+ return s1;
+}
diff --git a/lib/libc/string/memmem.c b/lib/libc/string/memmem.c
new file mode 100644
index 00000000..7a450cf7
--- /dev/null
+++ b/lib/libc/string/memmem.c
@@ -0,0 +1,15 @@
+#include <string.h>
+
+void *memmem(const void *haystack, size_t haystacklen, const void *needle,
+ size_t needlelen)
+{
+ const unsigned char *p1 = haystack;
+ const unsigned char *p2 = needle;
+ while (haystacklen >= needlelen) {
+ if (!memcmp(p1, p2, needlelen))
+ return (void *)p1;
+ p1++;
+ haystacklen--;
+ }
+ return NULL;
+}
diff --git a/lib/libc/string/memmove.c b/lib/libc/string/memmove.c
new file mode 100644
index 00000000..88968185
--- /dev/null
+++ b/lib/libc/string/memmove.c
@@ -0,0 +1,21 @@
+#include <stddef.h>
+#include <stdint.h>
+
+void *memmove(void *dst, const void *src, size_t n)
+{
+ uint8_t *d = dst;
+ const uint8_t *s = src;
+
+ if (d == s || n == 0)
+ return dst;
+
+ if ((uintptr_t)d < (uintptr_t)s) {
+ for (size_t i = 0; i < n; i++)
+ d[i] = s[i];
+ } else {
+ for (size_t i = n; i != 0; i--)
+ d[i - 1] = s[i - 1];
+ }
+
+ return dst;
+}
diff --git a/lib/libc/string/memset.c b/lib/libc/string/memset.c
new file mode 100644
index 00000000..da969d16
--- /dev/null
+++ b/lib/libc/string/memset.c
@@ -0,0 +1,10 @@
+#include <string.h>
+
+void *memset(void *s, int c, size_t n)
+{
+ unsigned char *p = s;
+ while (n--) {
+ *p++ = (unsigned char)c;
+ }
+ return s;
+}
diff --git a/lib/libc/string/stpcpy.c b/lib/libc/string/stpcpy.c
new file mode 100644
index 00000000..04c3a17f
--- /dev/null
+++ b/lib/libc/string/stpcpy.c
@@ -0,0 +1,8 @@
+#include <string.h>
+
+char *stpcpy(char *restrict s1, const char *restrict s2)
+{
+ while ((*s1++ = *s2++))
+ ;
+ return s1 - 1;
+}
diff --git a/lib/libc/string/stpncpy.c b/lib/libc/string/stpncpy.c
new file mode 100644
index 00000000..8f7ebd01
--- /dev/null
+++ b/lib/libc/string/stpncpy.c
@@ -0,0 +1,14 @@
+#include <stddef.h>
+
+char *strncpy(char *restrict s1, const char *restrict s2, size_t n)
+{
+ char *d = s1;
+
+ while (n-- && *s2)
+ *d++ = *s2++;
+
+ while (n--)
+ *d++ = '\0';
+
+ return s1;
+}
diff --git a/lib/libc/string/strcat.c b/lib/libc/string/strcat.c
new file mode 100644
index 00000000..6caf8c78
--- /dev/null
+++ b/lib/libc/string/strcat.c
@@ -0,0 +1,14 @@
+char *strcat(char *restrict s1, const char *restrict s2)
+{
+ char *d = s1;
+
+ while (*d)
+ d++;
+
+ while (*s2)
+ *d++ = *s2++;
+
+ *d = '\0';
+
+ return s1;
+}
diff --git a/lib/libc/string/strchr.c b/lib/libc/string/strchr.c
new file mode 100644
index 00000000..db632a2e
--- /dev/null
+++ b/lib/libc/string/strchr.c
@@ -0,0 +1,13 @@
+#include <string.h>
+
+char *strchr(const char *s, int c)
+{
+ while (*s) {
+ if (*s == (char)c)
+ return (char *)s;
+ s++;
+ }
+ if (c == 0)
+ return (char *)s;
+ return NULL;
+}
diff --git a/lib/libc/string/strcmp.c b/lib/libc/string/strcmp.c
new file mode 100644
index 00000000..836522f1
--- /dev/null
+++ b/lib/libc/string/strcmp.c
@@ -0,0 +1,23 @@
+#include <stddef.h>
+
+int strcmp(const char *s1, const char *s2)
+{
+ const unsigned char *p1 = (const unsigned char *)s1;
+ const unsigned char *p2 = (const unsigned char *)s2;
+
+ if (p1 == NULL && p2 == NULL)
+ return 0;
+
+ if (p1 == NULL)
+ return -1;
+
+ if (p2 == NULL)
+ return 1;
+
+ while (*p1 == *p2 && *p1 != '\0') {
+ p1++;
+ p2++;
+ }
+
+ return *p1 - *p2;
+}
diff --git a/lib/libc/string/strcoll.c b/lib/libc/string/strcoll.c
new file mode 100644
index 00000000..6b2e532a
--- /dev/null
+++ b/lib/libc/string/strcoll.c
@@ -0,0 +1,13 @@
+#include <libc.h>
+#include <string.h>
+#include <locale.h>
+
+int strcoll(const char *s1, const char *s2)
+{
+ return strcmp(s1, s2);
+}
+
+weak int strcoll_l(const char *s1, const char *s2, locale_t unused locale)
+{
+ return strcoll(s1, s2);
+}
diff --git a/lib/libc/string/strcpy.c b/lib/libc/string/strcpy.c
new file mode 100644
index 00000000..8e1dab69
--- /dev/null
+++ b/lib/libc/string/strcpy.c
@@ -0,0 +1,7 @@
+char *strcpy(char *restrict s1, const char *restrict s2)
+{
+ char *p = s1;
+ while ((*p++ = *s2++) != '\0')
+ ;
+ return s1;
+}
diff --git a/lib/libc/string/strcspn.c b/lib/libc/string/strcspn.c
new file mode 100644
index 00000000..9f1f2bcd
--- /dev/null
+++ b/lib/libc/string/strcspn.c
@@ -0,0 +1,11 @@
+#include <string.h>
+
+size_t strcspn(const char *s1, const char *s2)
+{
+ size_t len = 0;
+ while (*s1 != '\0' && strchr(s2, *s1) == NULL) {
+ s1++;
+ len++;
+ }
+ return len;
+}
diff --git a/lib/libc/string/strdup.c b/lib/libc/string/strdup.c
new file mode 100644
index 00000000..1210bfdc
--- /dev/null
+++ b/lib/libc/string/strdup.c
@@ -0,0 +1,12 @@
+#include <stdlib.h>
+#include <string.h>
+
+char *strdup(const char *s)
+{
+ size_t len = strlen(s) + 1;
+ char *dup = malloc(len);
+ if (dup == NULL)
+ return NULL;
+ memcpy(dup, s, len);
+ return dup;
+}
diff --git a/lib/libc/string/strerror.c b/lib/libc/string/strerror.c
new file mode 100644
index 00000000..1d5903cb
--- /dev/null
+++ b/lib/libc/string/strerror.c
@@ -0,0 +1,125 @@
+#include <libc.h>
+#include <errno.h>
+#include <string.h>
+#include <locale.h>
+
+char *strerror(int errnum)
+{
+ char *table[] = {
+ [0] = "No error information",
+ [EILSEQ] = "Illegal byte sequence",
+ [EDOM] = "Domain error",
+ [ERANGE] = "Result not representable",
+ [ENOTTY] = "Not a tty",
+ [EACCES] = "Permission denied",
+ [EPERM] = "Operation not permitted",
+ [ENOENT] = "No such file or directory",
+ [ESRCH] = "No such process",
+ [EEXIST] = "File exists",
+ [EOVERFLOW] = "Value too large for data type",
+ [ENOSPC] = "No space left on device",
+ [ENOMEM] = "Out of memory",
+ [EBUSY] = "Resource busy",
+ [EINTR] = "Interrupted system call",
+ [EAGAIN] = "Resource temporarily unavailable",
+ [ESPIPE] = "Invalid seek",
+ [EXDEV] = "Cross-device link",
+ [EROFS] = "Read-only file system",
+ [ENOTEMPTY] = "Directory not empty",
+ [ECONNRESET] = "Connection reset by peer",
+ [ETIMEDOUT] = "Operation timed out",
+ [ECONNREFUSED] = "Connection refused",
+ [EHOSTDOWN] = "Host is down",
+ [EHOSTUNREACH] = "Host is unreachable",
+ [EADDRINUSE] = "Address in use",
+ [EPIPE] = "Broken pipe",
+ [EIO] = "I/O error",
+ [ENXIO] = "No such device or address",
+ [ENOTBLK] = "Block device required",
+ [ENODEV] = "No such device",
+ [ENOTDIR] = "Not a directory",
+ [EISDIR] = "Is a directory",
+ [ETXTBSY] = "Text file busy",
+ [ENOEXEC] = "Exec format error",
+ [EINVAL] = "Invalid argument",
+ [E2BIG] = "Argument list too long",
+ [ELOOP] = "Symbolic link loop",
+ [ENAMETOOLONG] = "Filename too long",
+ [ENFILE] = "Too many open files in system",
+ [EMFILE] = "No file descriptors available",
+ [EBADF] = "Bad file descriptor",
+ [ECHILD] = "No child process",
+ [EFAULT] = "Bad address",
+ [EFBIG] = "File too large",
+ [EMLINK] = "Too many links",
+ [ENOLCK] = "No locks available",
+ [EDEADLK] = "Resource deadlock would occur",
+ [ENOTRECOVERABLE] = "State not recoverable",
+ [EOWNERDEAD] = "Previous owner died",
+ [ECANCELED] = "Operation canceled",
+ [ENOSYS] = "Function not implemented",
+ [ENOMSG] = "No message of desired type",
+ [EIDRM] = "Identifier removed",
+ [ENOSTR] = "Device not a stream",
+ [ENODATA] = "No data available",
+ [ETIME] = "Device timeout",
+ [ENOSR] = "Out of streams resources",
+ [ENOLINK] = "Link has been severed",
+ [EPROTO] = "Protocol error",
+ [EBADMSG] = "Bad message",
+ [EBADFD] = "File descriptor in bad state",
+ [ENOTSOCK] = "Not a socket",
+ [EDESTADDRREQ] = "Destination address required",
+ [EMSGSIZE] = "Message too large",
+ [EPROTOTYPE] = "Protocol wrong type for socket",
+ [ENOPROTOOPT] = "Protocol not available",
+ [EPROTONOSUPPORT] = "Not supported",
+ [ESOCKTNOSUPPORT] = "Type not supported",
+ [ENOTSUP] = "Not supported",
+ [EPFNOSUPPORT] = "Protocol family not supported",
+ [EAFNOSUPPORT] = "Address family not supported by protocol",
+ [EADDRNOTAVAIL] = "Address not available",
+ [ENETDOWN] = "Network is down",
+ [ENETUNREACH] = "Network unreachable",
+ [ENETRESET] = "Connection reset by network",
+ [ECONNABORTED] = "Connection aborted",
+ [ENOBUFS] = "No buffer space available",
+ [EISCONN] = "Socket is connected",
+ [ENOTCONN] = "Socket not connected",
+ [ESHUTDOWN] = "Cannot send after socket shutdown",
+ [EALREADY] = "Operation already in progress",
+ [EINPROGRESS] = "Operation in progress",
+ [ESTALE] = "Stale file handle",
+ [EUCLEAN] = "Data consistency error",
+ [ENAVAIL] = "Resource not available",
+ [EREMOTEIO] = "Remote I/O error",
+ [EDQUOT] = "Quota exceeded",
+ [ENOMEDIUM] = "No medium found",
+ [EMEDIUMTYPE] = "Wrong medium type",
+ [EMULTIHOP] = "Multihop attempted",
+ [ENOKEY] = "Required key not available",
+ [EKEYEXPIRED] = "Key has expired",
+ [EKEYREVOKED] = "Key has been revoked",
+ [EKEYREJECTED] = "Key was rejected by service",
+ };
+
+ return table[errnum];
+}
+
+int strerror_r(int errnum, char *buf, size_t buflen)
+{
+ const char *msg = strerror(errnum);
+ size_t msglen = strlen(msg) + 1;
+
+ if (buflen < msglen) {
+ return ERANGE;
+ }
+
+ memcpy(buf, msg, msglen);
+ return 0;
+}
+
+weak char *strerror_l(int errnum, locale_t unused locale)
+{
+ return strerror(errnum);
+}
diff --git a/lib/libc/string/strlcat.c b/lib/libc/string/strlcat.c
new file mode 100644
index 00000000..a2c333e0
--- /dev/null
+++ b/lib/libc/string/strlcat.c
@@ -0,0 +1,19 @@
+#include <string.h>
+
+size_t strlcat(char *restrict dst, const char *restrict src, size_t dstsize)
+{
+ size_t dlen = strlen(dst);
+ size_t slen = strlen(src);
+ size_t n = dstsize - dlen - 1;
+
+ if (n == 0)
+ return dlen + slen;
+
+ if (n > slen)
+ n = slen;
+
+ memcpy(dst + dlen, src, n);
+ dst[dlen + n] = '\0';
+
+ return dlen + n;
+}
diff --git a/lib/libc/string/strlcpy.c b/lib/libc/string/strlcpy.c
new file mode 100644
index 00000000..2f2f6a3b
--- /dev/null
+++ b/lib/libc/string/strlcpy.c
@@ -0,0 +1,16 @@
+#include <string.h>
+
+size_t strlcpy(char *restrict dst, const char *restrict src, size_t dstsize)
+{
+ size_t srclen = strlen(src);
+
+ if (dstsize == 0) {
+ return srclen;
+ }
+
+ size_t copylen = srclen < dstsize - 1 ? srclen : dstsize - 1;
+ memcpy(dst, src, copylen);
+ dst[copylen] = '\0';
+
+ return srclen;
+}
diff --git a/lib/libc/string/strlen.c b/lib/libc/string/strlen.c
new file mode 100644
index 00000000..0eb222fa
--- /dev/null
+++ b/lib/libc/string/strlen.c
@@ -0,0 +1,14 @@
+#include <stddef.h>
+
+size_t strlen(const char *str)
+{
+ size_t len = 0;
+
+ if (str == NULL)
+ return 0;
+
+ while (*str++)
+ len++;
+
+ return len;
+}
diff --git a/lib/libc/string/strncat.c b/lib/libc/string/strncat.c
new file mode 100644
index 00000000..ce0d8e24
--- /dev/null
+++ b/lib/libc/string/strncat.c
@@ -0,0 +1,19 @@
+#include <string.h>
+
+char *strncat(char *restrict s1, const char *restrict s2, size_t n)
+{
+ char *dest = s1;
+ const char *src = s2;
+
+ while (*dest != '\0')
+ dest++;
+
+ while (*src != '\0' && n > 0) {
+ *dest++ = *src++;
+ n--;
+ }
+
+ *dest = '\0';
+
+ return s1;
+}
diff --git a/lib/libc/string/strncmp.c b/lib/libc/string/strncmp.c
new file mode 100644
index 00000000..2fbc2395
--- /dev/null
+++ b/lib/libc/string/strncmp.c
@@ -0,0 +1,16 @@
+#include <stddef.h>
+
+int strncmp(const char *s1, const char *s2, size_t n)
+{
+ const unsigned char *p1 = (const unsigned char *)s1;
+ const unsigned char *p2 = (const unsigned char *)s2;
+
+ while (n-- > 0) {
+ if (*p1 != *p2 || *p1 == '\0')
+ return *p1 - *p2;
+ p1++;
+ p2++;
+ }
+
+ return 0;
+}
diff --git a/lib/libc/string/strncpy.c b/lib/libc/string/strncpy.c
new file mode 100644
index 00000000..c43b88c8
--- /dev/null
+++ b/lib/libc/string/strncpy.c
@@ -0,0 +1,16 @@
+#include <stddef.h>
+
+char *strncpy(char *restrict s1, const char *restrict s2, size_t n)
+{
+ char *result = s1;
+
+ while (n > 0 && *s2 != '\0') {
+ *s1++ = *s2++;
+ n--;
+ }
+
+ if (n > 0)
+ *s1 = '\0';
+
+ return result;
+}
diff --git a/lib/libc/string/strndup.c b/lib/libc/string/strndup.c
new file mode 100644
index 00000000..70e10501
--- /dev/null
+++ b/lib/libc/string/strndup.c
@@ -0,0 +1,15 @@
+#include <stdlib.h>
+#include <string.h>
+
+char *strndup(const char *s, size_t size)
+{
+ char *result = malloc(size + 1);
+
+ if (result == NULL)
+ return NULL;
+
+ memcpy(result, s, size);
+ result[size] = '\0';
+
+ return result;
+}
diff --git a/lib/libc/string/strnlen.c b/lib/libc/string/strnlen.c
new file mode 100644
index 00000000..11da22ec
--- /dev/null
+++ b/lib/libc/string/strnlen.c
@@ -0,0 +1,11 @@
+#include <stddef.h>
+
+size_t strnlen(const char *s, size_t maxlen)
+{
+ size_t len = 0;
+
+ while (*s++ && len < maxlen)
+ len++;
+
+ return len;
+}
diff --git a/lib/libc/string/strpbrk.c b/lib/libc/string/strpbrk.c
new file mode 100644
index 00000000..21d6507f
--- /dev/null
+++ b/lib/libc/string/strpbrk.c
@@ -0,0 +1,16 @@
+#include <stddef.h>
+
+char *strpbrk(const char *s1, const char *s2)
+{
+ while (*s1 != '\0') {
+ const char *p = s2;
+ while (*p != '\0') {
+ if (*s1 == *p)
+ return (char *)s1;
+ p++;
+ }
+ s1++;
+ }
+
+ return NULL;
+}
diff --git a/lib/libc/string/strrchr.c b/lib/libc/string/strrchr.c
new file mode 100644
index 00000000..d2807569
--- /dev/null
+++ b/lib/libc/string/strrchr.c
@@ -0,0 +1,12 @@
+#include <stddef.h>
+
+char *strrchr(const char *s, int c)
+{
+ const char *last = NULL;
+ while (*s != '\0') {
+ if (*s == c)
+ last = s;
+ s++;
+ }
+ return (char *)last;
+}
diff --git a/lib/libc/string/strspn.c b/lib/libc/string/strspn.c
new file mode 100644
index 00000000..78277ae7
--- /dev/null
+++ b/lib/libc/string/strspn.c
@@ -0,0 +1,12 @@
+#include <stddef.h>
+#include <string.h>
+
+size_t strspn(const char *s1, const char *s2)
+{
+ size_t count = 0;
+ while (*s1 != '\0' && strchr(s2, *s1) != NULL) {
+ count++;
+ s1++;
+ }
+ return count;
+}
diff --git a/lib/libc/string/strstr.c b/lib/libc/string/strstr.c
new file mode 100644
index 00000000..c3e2d5dd
--- /dev/null
+++ b/lib/libc/string/strstr.c
@@ -0,0 +1,27 @@
+#include <stddef.h>
+
+char *strstr(const char *s1, const char *s2)
+{
+ const char *p = s1;
+ const char *q = s2;
+
+ if (*q == '\0')
+ return (char *)p;
+
+ while (*p != '\0') {
+ const char *pp = p;
+ const char *qq = q;
+
+ while (*pp == *qq && *pp != '\0') {
+ pp++;
+ qq++;
+ }
+
+ if (*qq == '\0')
+ return (char *)p;
+
+ p++;
+ }
+
+ return NULL;
+}
diff --git a/lib/libc/string/strtok.c b/lib/libc/string/strtok.c
new file mode 100644
index 00000000..33d0d685
--- /dev/null
+++ b/lib/libc/string/strtok.c
@@ -0,0 +1,40 @@
+#include <stddef.h>
+#include <string.h>
+
+char *strtok(char *restrict s, const char *restrict sep)
+{
+ static _Thread_local char *state = NULL;
+
+ if (s != NULL) {
+ state = s;
+ }
+
+ if (state == NULL) {
+ return NULL;
+ }
+
+ char *token = state;
+ while (*token && strchr(sep, *token)) {
+ token++;
+ }
+
+ if (*token == '\0') {
+ state = NULL;
+ return NULL;
+ }
+
+ char *start = token;
+
+ while (*token && strchr(sep, *token) == NULL) {
+ token++;
+ }
+
+ if (*token) {
+ *token = '\0';
+ state = token + 1;
+ } else {
+ state = NULL;
+ }
+
+ return start;
+}
diff --git a/lib/libc/string/strtok_r.c b/lib/libc/string/strtok_r.c
new file mode 100644
index 00000000..57555b14
--- /dev/null
+++ b/lib/libc/string/strtok_r.c
@@ -0,0 +1,37 @@
+#include <string.h>
+
+char *strtok_r(char *restrict s, const char *restrict sep,
+ char **restrict state)
+{
+ if (s == NULL) {
+ s = *state;
+ }
+
+ if (s == NULL) {
+ return NULL;
+ }
+
+ while (*s && strchr(sep, *s)) {
+ s++;
+ }
+
+ if (*s == '\0') {
+ *state = NULL;
+ return NULL;
+ }
+
+ char *start = s;
+
+ while (*s && strchr(sep, *s) == NULL) {
+ s++;
+ }
+
+ if (*s) {
+ *s = '\0';
+ *state = s + 1;
+ } else {
+ *state = NULL;
+ }
+
+ return start;
+}
diff --git a/lib/libc/string/strxfrm.c b/lib/libc/string/strxfrm.c
new file mode 100644
index 00000000..d62d0a96
--- /dev/null
+++ b/lib/libc/string/strxfrm.c
@@ -0,0 +1,18 @@
+#include <libc.h>
+#include <string.h>
+
+size_t strxfrm(char *restrict s1, const char *restrict s2, size_t n)
+{
+ size_t len = strlen(s2);
+
+ if (n > len)
+ strcpy(s1, s2);
+
+ return len;
+}
+
+weak size_t strxfrm_l(char *restrict s1, const char *restrict s2, size_t n,
+ locale_t unused locale)
+{
+ return strxfrm(s1, s2, n);
+}