diff options
| author | Kacper <kacper@mail.openlinux.dev> | 2025-12-07 20:10:31 +0100 |
|---|---|---|
| committer | Kacper <kacper@mail.openlinux.dev> | 2025-12-07 20:10:31 +0100 |
| commit | fc00c656c96528112d05cf0edf8631bd5eaea446 (patch) | |
| tree | a6e0e6c588191a8bd1c64afc3b7a258e3e66c236 /lib/libc/string | |
Add build system scaffolding and libc headers
Diffstat (limited to 'lib/libc/string')
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); +} |
