summaryrefslogtreecommitdiff
path: root/Userland/Libraries/LibC/string.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Userland/Libraries/LibC/string.cpp')
-rw-r--r--Userland/Libraries/LibC/string.cpp486
1 files changed, 486 insertions, 0 deletions
diff --git a/Userland/Libraries/LibC/string.cpp b/Userland/Libraries/LibC/string.cpp
new file mode 100644
index 0000000000..86a72b72a9
--- /dev/null
+++ b/Userland/Libraries/LibC/string.cpp
@@ -0,0 +1,486 @@
+/*
+ * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <AK/MemMem.h>
+#include <AK/Platform.h>
+#include <AK/StdLibExtras.h>
+#include <AK/Types.h>
+#include <assert.h>
+#include <ctype.h>
+#include <errno.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+extern "C" {
+
+size_t strspn(const char* s, const char* accept)
+{
+ const char* p = s;
+cont:
+ char ch = *p++;
+ char ac;
+ for (const char* ap = accept; (ac = *ap++) != '\0';) {
+ if (ac == ch)
+ goto cont;
+ }
+ return p - 1 - s;
+}
+
+size_t strcspn(const char* s, const char* reject)
+{
+ for (auto* p = s;;) {
+ char c = *p++;
+ auto* rp = reject;
+ char rc;
+ do {
+ if ((rc = *rp++) == c)
+ return p - 1 - s;
+ } while (rc);
+ }
+}
+
+size_t strlen(const char* str)
+{
+ size_t len = 0;
+ while (*(str++))
+ ++len;
+ return len;
+}
+
+size_t strnlen(const char* str, size_t maxlen)
+{
+ size_t len = 0;
+ for (; len < maxlen && *str; str++)
+ len++;
+ return len;
+}
+
+char* strdup(const char* str)
+{
+ size_t len = strlen(str);
+ char* new_str = (char*)malloc(len + 1);
+ memcpy(new_str, str, len);
+ new_str[len] = '\0';
+ return new_str;
+}
+
+char* strndup(const char* str, size_t maxlen)
+{
+ size_t len = strnlen(str, maxlen);
+ char* new_str = (char*)malloc(len + 1);
+ memcpy(new_str, str, len);
+ new_str[len] = 0;
+ return new_str;
+}
+
+int strcmp(const char* s1, const char* s2)
+{
+ while (*s1 == *s2++)
+ if (*s1++ == 0)
+ return 0;
+ return *(const unsigned char*)s1 - *(const unsigned char*)--s2;
+}
+
+int strncmp(const char* s1, const char* s2, size_t n)
+{
+ if (!n)
+ return 0;
+ do {
+ if (*s1 != *s2++)
+ return *(const unsigned char*)s1 - *(const unsigned char*)--s2;
+ if (*s1++ == 0)
+ break;
+ } while (--n);
+ return 0;
+}
+
+int memcmp(const void* v1, const void* v2, size_t n)
+{
+ auto* s1 = (const uint8_t*)v1;
+ auto* s2 = (const uint8_t*)v2;
+ while (n-- > 0) {
+ if (*s1++ != *s2++)
+ return s1[-1] < s2[-1] ? -1 : 1;
+ }
+ return 0;
+}
+
+#if ARCH(I386)
+void* memcpy(void* dest_ptr, const void* src_ptr, size_t n)
+{
+ void* original_dest = dest_ptr;
+ asm volatile(
+ "rep movsb"
+ : "+D"(dest_ptr), "+S"(src_ptr), "+c"(n)::"memory");
+ return original_dest;
+}
+
+void* memset(void* dest_ptr, int c, size_t n)
+{
+ void* original_dest = dest_ptr;
+ asm volatile(
+ "rep stosb\n"
+ : "=D"(dest_ptr), "=c"(n)
+ : "0"(dest_ptr), "1"(n), "a"(c)
+ : "memory");
+ return original_dest;
+}
+#else
+void* memcpy(void* dest_ptr, const void* src_ptr, size_t n)
+{
+ auto* dest = (u8*)dest_ptr;
+ auto* src = (const u8*)src_ptr;
+ for (size_t i = 0; i < n; ++i)
+ dest[i] = src[i];
+ return dest_ptr;
+}
+
+void* memset(void* dest_ptr, int c, size_t n)
+{
+ auto* dest = (u8*)dest_ptr;
+ for (size_t i = 0; i < n; ++i)
+ dest[i] = (u8)c;
+ return dest_ptr;
+}
+#endif
+
+void* memmove(void* dest, const void* src, size_t n)
+{
+ if (dest < src)
+ return memcpy(dest, src, n);
+
+ u8* pd = (u8*)dest;
+ const u8* ps = (const u8*)src;
+ for (pd += n, ps += n; n--;)
+ *--pd = *--ps;
+ return dest;
+}
+
+const void* memmem(const void* haystack, size_t haystack_length, const void* needle, size_t needle_length)
+{
+ return AK::memmem(haystack, haystack_length, needle, needle_length);
+}
+
+char* strcpy(char* dest, const char* src)
+{
+ char* originalDest = dest;
+ while ((*dest++ = *src++) != '\0')
+ ;
+ return originalDest;
+}
+
+char* strncpy(char* dest, const char* src, size_t n)
+{
+ size_t i;
+ for (i = 0; i < n && src[i] != '\0'; ++i)
+ dest[i] = src[i];
+ for (; i < n; ++i)
+ dest[i] = '\0';
+ return dest;
+}
+
+size_t strlcpy(char* dest, const char* src, size_t n)
+{
+ size_t i;
+ // Would like to test i < n - 1 here, but n might be 0.
+ for (i = 0; i + 1 < n && src[i] != '\0'; ++i)
+ dest[i] = src[i];
+ if (n)
+ dest[i] = '\0';
+ for (; src[i] != '\0'; ++i)
+ ; // Determine the length of src, don't copy.
+ return i;
+}
+
+char* strchr(const char* str, int c)
+{
+ char ch = c;
+ for (;; ++str) {
+ if (*str == ch)
+ return const_cast<char*>(str);
+ if (!*str)
+ return nullptr;
+ }
+}
+
+char* strchrnul(const char* str, int c)
+{
+ char ch = c;
+ for (;; ++str) {
+ if (*str == ch || !*str)
+ return const_cast<char*>(str);
+ }
+}
+
+void* memchr(const void* ptr, int c, size_t size)
+{
+ char ch = c;
+ auto* cptr = (const char*)ptr;
+ for (size_t i = 0; i < size; ++i) {
+ if (cptr[i] == ch)
+ return const_cast<char*>(cptr + i);
+ }
+ return nullptr;
+}
+
+char* strrchr(const char* str, int ch)
+{
+ char* last = nullptr;
+ char c;
+ for (; (c = *str); ++str) {
+ if (c == ch)
+ last = const_cast<char*>(str);
+ }
+ return last;
+}
+
+char* strcat(char* dest, const char* src)
+{
+ size_t dest_length = strlen(dest);
+ size_t i;
+ for (i = 0; src[i] != '\0'; i++)
+ dest[dest_length + i] = src[i];
+ dest[dest_length + i] = '\0';
+ return dest;
+}
+
+char* strncat(char* dest, const char* src, size_t n)
+{
+ size_t dest_length = strlen(dest);
+ size_t i;
+ for (i = 0; i < n && src[i] != '\0'; i++)
+ dest[dest_length + i] = src[i];
+ dest[dest_length + i] = '\0';
+ return dest;
+}
+
+const char* const sys_errlist[] = {
+ "Success (not an error)",
+ "Operation not permitted",
+ "No such file or directory",
+ "No such process",
+ "Interrupted syscall",
+ "I/O error",
+ "No such device or address",
+ "Argument list too long",
+ "Exec format error",
+ "Bad fd number",
+ "No child processes",
+ "Try again",
+ "Out of memory",
+ "Permission denied",
+ "Bad address",
+ "Block device required",
+ "Device or resource busy",
+ "File already exists",
+ "Cross-device link",
+ "No such device",
+ "Not a directory",
+ "Is a directory",
+ "Invalid argument",
+ "File table overflow",
+ "Too many open files",
+ "Not a TTY",
+ "Text file busy",
+ "File too large",
+ "No space left on device",
+ "Illegal seek",
+ "Read-only filesystem",
+ "Too many links",
+ "Broken pipe",
+ "Range error",
+ "Name too long",
+ "Too many symlinks",
+ "Overflow",
+ "Operation not supported",
+ "No such syscall",
+ "Not implemented",
+ "Address family not supported",
+ "Not a socket",
+ "Address in use",
+ "Failed without setting an error code (bug!)",
+ "Directory not empty",
+ "Math argument out of domain",
+ "Connection refused",
+ "Address not available",
+ "Already connected",
+ "Connection aborted",
+ "Connection already in progress",
+ "Connection reset",
+ "Destination address required",
+ "Host unreachable",
+ "Illegal byte sequence",
+ "Message size",
+ "Network down",
+ "Network unreachable",
+ "Network reset",
+ "No buffer space",
+ "No lock available",
+ "No message",
+ "No protocol option",
+ "Not connected",
+ "Operation would block",
+ "Protocol not supported",
+ "Resource deadlock would occur",
+ "Timed out",
+ "Wrong protocol type",
+ "Operation in progress",
+ "No such thread",
+ "Protocol error",
+ "Not supported",
+ "Protocol family not supported",
+ "Cannot make directory a subdirectory of itself",
+ "The highest errno +1 :^)",
+};
+
+int sys_nerr = EMAXERRNO;
+
+char* strerror(int errnum)
+{
+ if (errnum < 0 || errnum >= EMAXERRNO) {
+ printf("strerror() missing string for errnum=%d\n", errnum);
+ return const_cast<char*>("Unknown error");
+ }
+ return const_cast<char*>(sys_errlist[errnum]);
+}
+
+char* strsignal(int signum)
+{
+ if (signum >= NSIG) {
+ printf("strsignal() missing string for signum=%d\n", signum);
+ return const_cast<char*>("Unknown signal");
+ }
+ return const_cast<char*>(sys_siglist[signum]);
+}
+
+char* strstr(const char* haystack, const char* needle)
+{
+ char nch;
+ char hch;
+
+ if ((nch = *needle++) != 0) {
+ size_t len = strlen(needle);
+ do {
+ do {
+ if ((hch = *haystack++) == 0)
+ return nullptr;
+ } while (hch != nch);
+ } while (strncmp(haystack, needle, len) != 0);
+ --haystack;
+ }
+ return const_cast<char*>(haystack);
+}
+
+char* strpbrk(const char* s, const char* accept)
+{
+ while (*s)
+ if (strchr(accept, *s++))
+ return const_cast<char*>(--s);
+ return nullptr;
+}
+
+char* strtok_r(char* str, const char* delim, char** saved_str)
+{
+ if (!str) {
+ if (!saved_str)
+ return nullptr;
+ str = *saved_str;
+ }
+
+ size_t token_start = 0;
+ size_t token_end = 0;
+ size_t str_len = strlen(str);
+ size_t delim_len = strlen(delim);
+
+ for (size_t i = 0; i < str_len; ++i) {
+ bool is_proper_delim = false;
+
+ for (size_t j = 0; j < delim_len; ++j) {
+ if (str[i] == delim[j]) {
+ // Skip beginning delimiters
+ if (token_end - token_start == 0) {
+ ++token_start;
+ break;
+ }
+
+ is_proper_delim = true;
+ }
+ }
+
+ ++token_end;
+ if (is_proper_delim && token_end > 0) {
+ --token_end;
+ break;
+ }
+ }
+
+ if (str[token_start] == '\0')
+ return nullptr;
+
+ if (token_end == 0) {
+ *saved_str = nullptr;
+ return &str[token_start];
+ }
+
+ if (str[token_end] == '\0')
+ *saved_str = &str[token_end];
+ else
+ *saved_str = &str[token_end + 1];
+
+ str[token_end] = '\0';
+ return &str[token_start];
+}
+
+char* strtok(char* str, const char* delim)
+{
+ static char* saved_str;
+ return strtok_r(str, delim, &saved_str);
+}
+
+int strcoll(const char* s1, const char* s2)
+{
+ return strcmp(s1, s2);
+}
+
+size_t strxfrm(char* dest, const char* src, size_t n)
+{
+ size_t i;
+ for (i = 0; i < n && src[i] != '\0'; ++i)
+ dest[i] = src[i];
+ for (; i < n; ++i)
+ dest[i] = '\0';
+ return i;
+}
+
+void explicit_bzero(void* ptr, size_t size)
+{
+ memset(ptr, 0, size);
+ asm volatile("" ::
+ : "memory");
+}
+}