| /* |
| * libc string functions |
| * |
| * This code is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU Library General Public License version 2. |
| */ |
| |
| #include "libcflat.h" |
| #include "ctype.h" |
| #include "stdlib.h" |
| #include "linux/compiler.h" |
| |
| size_t strlen(const char *buf) |
| { |
| size_t len = 0; |
| |
| while (*buf++) |
| ++len; |
| return len; |
| } |
| |
| size_t strnlen(const char *buf, size_t maxlen) |
| { |
| const char *sc; |
| |
| for (sc = buf; maxlen-- && *sc != '\0'; ++sc) |
| /* nothing */ ; |
| return sc - buf; |
| } |
| |
| char *strcat(char *dest, const char *src) |
| { |
| char *p = dest; |
| |
| while (*p) |
| ++p; |
| while ((*p++ = *src++) != 0) |
| ; |
| return dest; |
| } |
| |
| char *strcpy(char *dest, const char *src) |
| { |
| *dest = 0; |
| return strcat(dest, src); |
| } |
| |
| int strncmp(const char *a, const char *b, size_t n) |
| { |
| for (; n--; ++a, ++b) |
| if (*a != *b || *a == '\0') |
| return *a - *b; |
| |
| return 0; |
| } |
| |
| int strncasecmp(const char *a, const char *b, size_t n) |
| { |
| for (; n--; ++a, ++b) |
| if (tolower(*a) != tolower(*b) || *a == '\0') |
| return tolower(*a) - tolower(*b); |
| |
| return 0; |
| } |
| |
| int strcmp(const char *a, const char *b) |
| { |
| return strncmp(a, b, SIZE_MAX); |
| } |
| |
| int strcasecmp(const char *a, const char *b) |
| { |
| return strncasecmp(a, b, SIZE_MAX); |
| } |
| |
| char *strchr(const char *s, int c) |
| { |
| while (*s != (char)c) |
| if (*s++ == '\0') |
| return NULL; |
| return (char *)s; |
| } |
| |
| char *strrchr(const char *s, int c) |
| { |
| const char *last = NULL; |
| do { |
| if (*s == (char)c) |
| last = s; |
| } while (*s++); |
| return (char *)last; |
| } |
| |
| char *strchrnul(const char *s, int c) |
| { |
| while (*s && *s != (char)c) |
| s++; |
| return (char *)s; |
| } |
| |
| char *strstr(const char *s1, const char *s2) |
| { |
| size_t l1, l2; |
| |
| l2 = strlen(s2); |
| if (!l2) |
| return (char *)s1; |
| l1 = strlen(s1); |
| while (l1 >= l2) { |
| l1--; |
| if (!memcmp(s1, s2, l2)) |
| return (char *)s1; |
| s1++; |
| } |
| return NULL; |
| } |
| |
| void *memset(void *s, int c, size_t n) |
| { |
| size_t i; |
| char *a = s; |
| |
| for (i = 0; i < n; ++i) |
| a[i] = c; |
| |
| return s; |
| } |
| |
| void *memcpy(void *dest, const void *src, size_t n) |
| { |
| size_t i; |
| char *a = dest; |
| const char *b = src; |
| |
| for (i = 0; i < n; ++i) |
| a[i] = b[i]; |
| |
| return dest; |
| } |
| |
| int memcmp(const void *s1, const void *s2, size_t n) |
| { |
| const unsigned char *a = s1, *b = s2; |
| int ret = 0; |
| |
| while (n--) { |
| ret = *a - *b; |
| if (ret) |
| break; |
| ++a, ++b; |
| } |
| return ret; |
| } |
| |
| void *memmove(void *dest, const void *src, size_t n) |
| { |
| const unsigned char *s = src; |
| unsigned char *d = dest; |
| |
| if (d <= s) { |
| while (n--) |
| *d++ = *s++; |
| } else { |
| d += n, s += n; |
| while (n--) |
| *--d = *--s; |
| } |
| return dest; |
| } |
| |
| void *memchr(const void *s, int c, size_t n) |
| { |
| const unsigned char *str = s, chr = (unsigned char)c; |
| |
| while (n--) |
| if (*str++ == chr) |
| return (void *)(str - 1); |
| return NULL; |
| } |
| |
| static unsigned long long __strtoll(const char *nptr, char **endptr, |
| int base, bool is_signed, bool is_longlong) |
| { |
| unsigned long long ull = 0; |
| const char *s = nptr; |
| int neg, c; |
| |
| assert(base == 0 || (base >= 2 && base <= 36)); |
| |
| while (isspace(*s)) |
| s++; |
| |
| if (*s == '-') { |
| neg = 1; |
| s++; |
| } else { |
| neg = 0; |
| if (*s == '+') |
| s++; |
| } |
| |
| if (base == 0 || base == 16) { |
| if (*s == '0') { |
| s++; |
| if (*s == 'x' || *s == 'X') { |
| s++; |
| base = 16; |
| } else if (base == 0) |
| base = 8; |
| } else if (base == 0) |
| base = 10; |
| } |
| |
| while (*s) { |
| if (*s >= '0' && *s < '0' + base && *s <= '9') |
| c = *s - '0'; |
| else if (*s >= 'a' && *s < 'a' + base - 10) |
| c = *s - 'a' + 10; |
| else if (*s >= 'A' && *s < 'A' + base - 10) |
| c = *s - 'A' + 10; |
| else |
| break; |
| |
| if (!is_longlong) { |
| if (is_signed) { |
| long sl = (long)ull; |
| assert(!check_mul_overflow(sl, base)); |
| assert(!check_add_overflow(sl * base, c)); |
| } else { |
| unsigned long ul = (unsigned long)ull; |
| assert(!check_mul_overflow(ul, base)); |
| assert(!check_add_overflow(ul * base, c)); |
| } |
| } else { |
| if (is_signed) { |
| long long sll = (long long)ull; |
| assert(!check_mul_overflow(sll, base)); |
| assert(!check_add_overflow(sll * base, c)); |
| } else { |
| assert(!check_mul_overflow(ull, base)); |
| assert(!check_add_overflow(ull * base, c)); |
| } |
| } |
| |
| ull = ull * base + c; |
| s++; |
| } |
| |
| if (neg) |
| ull = -ull; |
| |
| if (endptr) |
| *endptr = (char *)s; |
| |
| return ull; |
| } |
| |
| long int strtol(const char *nptr, char **endptr, int base) |
| { |
| return __strtoll(nptr, endptr, base, true, false); |
| } |
| |
| unsigned long int strtoul(const char *nptr, char **endptr, int base) |
| { |
| return __strtoll(nptr, endptr, base, false, false); |
| } |
| |
| long long int strtoll(const char *nptr, char **endptr, int base) |
| { |
| return __strtoll(nptr, endptr, base, true, true); |
| } |
| |
| unsigned long long int strtoull(const char *nptr, char **endptr, int base) |
| { |
| return __strtoll(nptr, endptr, base, false, true); |
| } |
| |
| long atol(const char *ptr) |
| { |
| return strtol(ptr, NULL, 10); |
| } |
| |
| extern char **environ; |
| |
| char *getenv(const char *name) |
| { |
| char **envp = environ, *delim; |
| int len; |
| |
| while (*envp) { |
| delim = strchr(*envp, '='); |
| assert(delim); |
| len = delim - *envp; |
| if (memcmp(name, *envp, len) == 0 && !name[len]) |
| return delim + 1; |
| ++envp; |
| } |
| return NULL; |
| } |
| |
| /* Very simple glob matching. Allows '*' at beginning and end of pattern. */ |
| bool simple_glob(const char *text, const char *pattern) |
| { |
| bool star_start = false; |
| bool star_end = false; |
| size_t n = strlen(pattern); |
| char copy[n + 1]; |
| |
| if (pattern[0] == '*') { |
| pattern += 1; |
| n -= 1; |
| star_start = true; |
| } |
| |
| strcpy(copy, pattern); |
| |
| if (n > 0 && pattern[n - 1] == '*') { |
| n -= 1; |
| copy[n] = '\0'; |
| star_end = true; |
| } |
| |
| if (star_start && star_end) |
| return strstr(text, copy); |
| |
| if (star_end) |
| return strstr(text, copy) == text; |
| |
| if (star_start) { |
| size_t text_len = strlen(text); |
| const char *suffix; |
| |
| if (n > text_len) |
| return false; |
| suffix = text + text_len - n; |
| return !strcmp(suffix, copy); |
| } |
| |
| return !strcmp(text, copy); |
| } |