| /* Copyright 2021 The ChromiumOS Authors |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| |
| /* Standard library utility functions for Chrome EC */ |
| |
| #include "common.h" |
| #include "console.h" |
| #include "printf.h" |
| #include "util.h" |
| |
| #include <stdio.h> |
| |
| /* |
| * The following macros are defined in stdlib.h in the C standard library, which |
| * conflict with the definitions in this file. |
| */ |
| #undef isspace |
| #undef isdigit |
| #undef isalpha |
| #undef isupper |
| #undef isprint |
| #undef tolower |
| |
| /* Context for snprintf() */ |
| struct snprintf_context { |
| char *str; |
| int size; |
| }; |
| |
| /** |
| * Add a character to the string context. |
| * |
| * @param context Context receiving character |
| * @param c Character to add |
| * @return 0 if character added, 1 if character dropped because no space. |
| */ |
| static int snprintf_addchar(void *context, int c) |
| { |
| struct snprintf_context *ctx = (struct snprintf_context *)context; |
| |
| if (!ctx->size) |
| return 1; |
| |
| *(ctx->str++) = c; |
| ctx->size--; |
| return 0; |
| } |
| |
| int crec_vsnprintf(char *str, size_t size, const char *format, va_list args) |
| { |
| struct snprintf_context ctx; |
| int rv; |
| |
| if (!str || !format || size <= 0) |
| return -EC_ERROR_INVAL; |
| |
| ctx.str = str; |
| ctx.size = size - 1; /* Reserve space for terminating '\0' */ |
| |
| rv = vfnprintf(snprintf_addchar, &ctx, format, args); |
| |
| /* Terminate string */ |
| *ctx.str = '\0'; |
| |
| return (rv == EC_SUCCESS) ? (ctx.str - str) : -rv; |
| } |
| #ifndef CONFIG_ZEPHYR |
| int vsnprintf(char *str, size_t size, const char *format, va_list args) |
| __attribute__((weak, alias("crec_vsnprintf"))); |
| #endif /* CONFIG_ZEPHYR */ |
| |
| int crec_snprintf(char *str, size_t size, const char *format, ...) |
| { |
| va_list args; |
| int rv; |
| |
| va_start(args, format); |
| rv = crec_vsnprintf(str, size, format, args); |
| va_end(args); |
| |
| return rv; |
| } |
| #ifndef CONFIG_ZEPHYR |
| int snprintf(char *str, size_t size, const char *format, ...) |
| __attribute__((weak, alias("crec_snprintf"))); |
| #endif /* CONFIG_ZEPHYR */ |
| |
| /* |
| * TODO(b/237712836): Zephyr's libc should provide strcasecmp. For now we'll |
| * use the EC implementation. |
| */ |
| __stdlib_compat int strcasecmp(const char *s1, const char *s2) |
| { |
| int diff; |
| |
| do { |
| diff = tolower(*s1) - tolower(*s2); |
| if (diff) |
| return diff; |
| } while (*(s1++) && *(s2++)); |
| return 0; |
| } |
| |
| /* |
| * TODO(b/237712836): Remove this conditional once strcasecmp is added to |
| * Zephyr's libc. |
| */ |
| #ifndef CONFIG_ZEPHYR |
| __stdlib_compat size_t strlen(const char *s) |
| { |
| int len = 0; |
| |
| while (*s++) |
| len++; |
| |
| return len; |
| } |
| |
| __stdlib_compat size_t strnlen(const char *s, size_t maxlen) |
| { |
| size_t len = 0; |
| |
| while (len < maxlen && *s) { |
| s++; |
| len++; |
| } |
| return len; |
| } |
| |
| __stdlib_compat size_t strcspn(const char *s, const char *reject) |
| { |
| size_t i; |
| size_t reject_len = strlen(reject); |
| |
| for (i = 0; s[i] != 0; i++) |
| for (size_t j = 0; j < reject_len; j++) |
| if (s[i] == reject[j]) |
| return i; |
| return i; |
| } |
| |
| __stdlib_compat int isspace(int c) |
| { |
| return c == ' ' || c == '\t' || c == '\r' || c == '\n'; |
| } |
| |
| __stdlib_compat int isdigit(int c) |
| { |
| return c >= '0' && c <= '9'; |
| } |
| |
| __stdlib_compat int isalpha(int c) |
| { |
| return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'); |
| } |
| |
| __stdlib_compat int isupper(int c) |
| { |
| return c >= 'A' && c <= 'Z'; |
| } |
| |
| __stdlib_compat int isprint(int c) |
| { |
| return c >= ' ' && c <= '~'; |
| } |
| |
| __stdlib_compat int tolower(int c) |
| { |
| return c >= 'A' && c <= 'Z' ? c + 'a' - 'A' : c; |
| } |
| |
| __stdlib_compat int strncasecmp(const char *s1, const char *s2, size_t size) |
| { |
| int diff; |
| |
| if (!size) |
| return 0; |
| |
| do { |
| diff = tolower(*s1) - tolower(*s2); |
| if (diff) |
| return diff; |
| } while (*(s1++) && *(s2++) && --size); |
| return 0; |
| } |
| |
| __stdlib_compat char *strstr(const char *s1, const char *s2) |
| { |
| const char *p, *q, *r; |
| size_t len1 = strlen(s1); |
| size_t len2 = strlen(s2); |
| |
| if (len1 == 0 || len2 == 0 || len1 < len2) |
| return NULL; |
| |
| r = s1 + len1 - len2 + 1; |
| for (; s1 < r; s1++) { |
| if (*s1 == *s2) { |
| p = s1 + 1; |
| q = s2 + 1; |
| for (; q < s2 + len2;) { |
| if (*p++ != *q++) |
| break; |
| } |
| if (*q == '\0') |
| return (char *)s1; |
| } |
| } |
| return NULL; |
| } |
| |
| __stdlib_compat unsigned long long int strtoull(const char *nptr, char **endptr, |
| int base) |
| { |
| uint64_t result = 0; |
| int c = '\0'; |
| |
| while ((c = *nptr++) && isspace(c)) |
| ; |
| |
| if (c == '+') { |
| c = *nptr++; |
| } else if (c == '-') { |
| if (endptr) |
| *endptr = (char *)nptr - 1; |
| return result; |
| } |
| |
| base = find_base(base, &c, &nptr); |
| |
| while (c) { |
| if (c >= '0' && c < '0' + MIN(base, 10)) |
| result = result * base + (c - '0'); |
| else if (c >= 'A' && c < 'A' + base - 10) |
| result = result * base + (c - 'A' + 10); |
| else if (c >= 'a' && c < 'a' + base - 10) |
| result = result * base + (c - 'a' + 10); |
| else |
| break; |
| |
| c = *nptr++; |
| } |
| |
| if (endptr) |
| *endptr = (char *)nptr - 1; |
| return result; |
| } |
| BUILD_ASSERT(sizeof(unsigned long long int) == sizeof(uint64_t)); |
| |
| __stdlib_compat int atoi(const char *nptr) |
| { |
| int result = 0; |
| int neg = 0; |
| char c = '\0'; |
| |
| while ((c = *nptr++) && isspace(c)) |
| ; |
| |
| if (c == '-') { |
| neg = 1; |
| c = *nptr++; |
| } |
| |
| while (isdigit(c)) { |
| result = result * 10 + (c - '0'); |
| c = *nptr++; |
| } |
| |
| return neg ? -result : result; |
| } |
| |
| __keep __stdlib_compat int memcmp(const void *s1, const void *s2, size_t len) |
| { |
| const char *sa = s1; |
| const char *sb = s2; |
| int diff = 0; |
| |
| while (len-- > 0) { |
| diff = *(sa++) - *(sb++); |
| if (diff) |
| return diff; |
| } |
| |
| return 0; |
| } |
| |
| #if !(__has_feature(address_sanitizer) || __has_feature(memory_sanitizer)) |
| __keep __stdlib_compat void *memcpy(void *dest, const void *src, size_t len) |
| { |
| char *d = (char *)dest; |
| const char *s = (const char *)src; |
| uint32_t *dw; |
| const uint32_t *sw; |
| char *head; |
| char *const tail = (char *)dest + len; |
| /* Set 'body' to the last word boundary */ |
| uint32_t *const body = (uint32_t *)((uintptr_t)tail & ~3); |
| |
| if (((uintptr_t)dest & 3) != ((uintptr_t)src & 3)) { |
| /* Misaligned. no body, no tail. */ |
| head = tail; |
| } else { |
| /* Aligned */ |
| if ((uintptr_t)tail < (((uintptr_t)d + 3) & ~3)) |
| /* len is shorter than the first word boundary */ |
| head = tail; |
| else |
| /* Set 'head' to the first word boundary */ |
| head = (char *)(((uintptr_t)d + 3) & ~3); |
| } |
| |
| /* Copy head */ |
| while (d < head) |
| *(d++) = *(s++); |
| |
| /* Copy body */ |
| dw = (uint32_t *)d; |
| sw = (uint32_t *)s; |
| while (dw < body) |
| *(dw++) = *(sw++); |
| |
| /* Copy tail */ |
| d = (char *)dw; |
| s = (const char *)sw; |
| while (d < tail) |
| *(d++) = *(s++); |
| |
| return dest; |
| } |
| #endif /* address_sanitizer || memory_sanitizer */ |
| |
| #if !(__has_feature(address_sanitizer) || __has_feature(memory_sanitizer)) |
| __keep __stdlib_compat __visible void *memset(void *dest, int c, size_t len) |
| { |
| char *d = (char *)dest; |
| uint32_t cccc; |
| uint32_t *dw; |
| char *head; |
| char *const tail = (char *)dest + len; |
| /* Set 'body' to the last word boundary */ |
| uint32_t *const body = (uint32_t *)((uintptr_t)tail & ~3); |
| |
| c &= 0xff; /* Clear upper bits before ORing below */ |
| cccc = c | (c << 8) | (c << 16) | (c << 24); |
| |
| if ((uintptr_t)tail < (((uintptr_t)d + 3) & ~3)) |
| /* len is shorter than the first word boundary */ |
| head = tail; |
| else |
| /* Set 'head' to the first word boundary */ |
| head = (char *)(((uintptr_t)d + 3) & ~3); |
| |
| /* Copy head */ |
| while (d < head) |
| *(d++) = c; |
| |
| /* Copy body */ |
| dw = (uint32_t *)d; |
| while (dw < body) |
| *(dw++) = cccc; |
| |
| /* Copy tail */ |
| d = (char *)dw; |
| while (d < tail) |
| *(d++) = c; |
| |
| return dest; |
| } |
| #endif /* address_sanitizer || memory_sanitizer */ |
| |
| #if !(__has_feature(address_sanitizer) || __has_feature(memory_sanitizer)) |
| __keep __stdlib_compat void *memmove(void *dest, const void *src, size_t len) |
| { |
| if ((uintptr_t)dest <= (uintptr_t)src || |
| (uintptr_t)dest >= (uintptr_t)src + len) { |
| /* Start of destination doesn't overlap source, so just use |
| * memcpy(). |
| */ |
| return memcpy(dest, src, len); |
| } else { |
| /* Need to copy from tail because there is overlap. */ |
| char *d = (char *)dest + len; |
| const char *s = (const char *)src + len; |
| uint32_t *dw; |
| const uint32_t *sw; |
| char *head; |
| char *const tail = (char *)dest; |
| /* Set 'body' to the last word boundary */ |
| uint32_t *const body = (uint32_t *)(((uintptr_t)tail + 3) & ~3); |
| |
| if (((uintptr_t)dest & 3) != ((uintptr_t)src & 3)) { |
| /* Misaligned. no body, no tail. */ |
| head = tail; |
| } else { |
| /* Aligned */ |
| if ((uintptr_t)tail > ((uintptr_t)d & ~3)) |
| /* Shorter than the first word boundary */ |
| head = tail; |
| else |
| /* Set 'head' to the first word boundary */ |
| head = (char *)((uintptr_t)d & ~3); |
| } |
| |
| /* Copy head */ |
| while (d > head) |
| *(--d) = *(--s); |
| |
| /* Copy body */ |
| dw = (uint32_t *)d; |
| sw = (uint32_t *)s; |
| while (dw > body) |
| *(--dw) = *(--sw); |
| |
| /* Copy tail */ |
| d = (char *)dw; |
| s = (const char *)sw; |
| while (d > tail) |
| *(--d) = *(--s); |
| |
| return dest; |
| } |
| } |
| #endif /* address_sanitizer || memory_sanitizer */ |
| |
| __stdlib_compat void *memchr(const void *buffer, int c, size_t n) |
| { |
| char *current = (char *)buffer; |
| char *end = current + n; |
| |
| while (current != end) { |
| if (*current == c) |
| return current; |
| current++; |
| } |
| return NULL; |
| } |
| |
| __stdlib_compat char *strncpy(char *dest, const char *src, size_t n) |
| { |
| char *d = dest; |
| |
| while (n && *src) { |
| *d++ = *src++; |
| n--; |
| } |
| if (n) |
| *d = '\0'; |
| return dest; |
| } |
| |
| __stdlib_compat int strncmp(const char *s1, const char *s2, size_t n) |
| { |
| while (n--) { |
| if (*s1 != *s2) |
| return *s1 - *s2; |
| if (!*s1) |
| break; |
| s1++; |
| s2++; |
| } |
| return 0; |
| } |
| #endif /* !CONFIG_ZEPHYR */ |