2019-01-21 15:00:04 +01:00
|
|
|
//----------------------------------------------------------------------------//
|
|
|
|
// GNU GPL OS/K //
|
|
|
|
// //
|
2019-02-16 23:36:33 +01:00
|
|
|
// Desc: String manipulation utilities //
|
2019-01-21 15:00:04 +01:00
|
|
|
// //
|
2019-02-16 23:36:33 +01:00
|
|
|
// //
|
|
|
|
// Copyright © 2018-2019 The OS/K Team //
|
|
|
|
// //
|
|
|
|
// This file is part of OS/K. //
|
|
|
|
// //
|
|
|
|
// OS/K is free software: you can redistribute it and/or modify //
|
|
|
|
// it under the terms of the GNU General Public License as published by //
|
|
|
|
// the Free Software Foundation, either version 3 of the License, or //
|
|
|
|
// any later version. //
|
|
|
|
// //
|
|
|
|
// OS/K is distributed in the hope that it will be useful, //
|
|
|
|
// but WITHOUT ANY WARRANTY//without even the implied warranty of //
|
|
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
|
|
|
|
// GNU General Public License for more details. //
|
|
|
|
// //
|
|
|
|
// You should have received a copy of the GNU General Public License //
|
|
|
|
// along with OS/K. If not, see <https://www.gnu.org/licenses/>. //
|
2019-01-21 15:00:04 +01:00
|
|
|
//----------------------------------------------------------------------------//
|
|
|
|
|
|
|
|
#include <kalbase.h>
|
|
|
|
|
|
|
|
//
|
|
|
|
// Compare two strings
|
|
|
|
//
|
|
|
|
int strcmp(const char *str1, const char *str2)
|
|
|
|
{
|
|
|
|
while (*str1 == *str2 && *str2) str1++, str2++;
|
|
|
|
|
|
|
|
return *(uchar *)str1 - *(uchar *)str2;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Compare at most n bytes of two strings
|
|
|
|
//
|
|
|
|
int strncmp(const char *str1, const char *str2, size_t n)
|
|
|
|
{
|
|
|
|
size_t it = 0;
|
|
|
|
|
|
|
|
while (*str1 == *str2 && *str2 && it < n) str1++, str2++, it++;
|
|
|
|
|
|
|
|
return *(uchar *)str1 - *(uchar *)str2;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Return str's length
|
|
|
|
//
|
|
|
|
size_t strlen(const char *str)
|
|
|
|
{
|
|
|
|
const char *base = str;
|
|
|
|
|
|
|
|
while (*str) str++;
|
|
|
|
|
|
|
|
return str - base;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Return a pointer to the first occurence of ch in str,
|
|
|
|
// or str's null-terminator if none is found
|
|
|
|
//
|
|
|
|
char *strchrnul(const char *str, int ch)
|
|
|
|
{
|
|
|
|
while ((*str && *str != (char)ch)) str++;
|
|
|
|
|
|
|
|
return (char *)str;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Return a pointer to the first occurence of ch in str,
|
|
|
|
// NULL if none is found
|
|
|
|
//
|
|
|
|
char *strchr(const char *str, int ch)
|
|
|
|
{
|
|
|
|
while ((*str && *str != (char)ch)) str++;
|
|
|
|
|
|
|
|
return *str ? (char *)str : NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Return a point to the last occurence of ch in str,
|
|
|
|
// NULL if none is found
|
|
|
|
//
|
|
|
|
char *strrchr(const char *str, int ch)
|
|
|
|
{
|
|
|
|
char *ptr = NULL;
|
|
|
|
|
|
|
|
while (*str) {
|
|
|
|
if (*str == ch) {
|
|
|
|
ptr = (char *)str;
|
|
|
|
}
|
|
|
|
str++;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Return the length of the longest inital segment of str
|
|
|
|
// that only contains characters in acc
|
|
|
|
//
|
|
|
|
size_t strspn(const char *str, const char *acc)
|
|
|
|
{
|
|
|
|
const char *ptr = str;
|
|
|
|
|
|
|
|
while (*ptr && strchr(acc, *ptr) != NULL) ptr++;
|
|
|
|
|
|
|
|
return ptr - str;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Return the length of the longest initial segment of str
|
|
|
|
// that does not contain any character in rej
|
|
|
|
//
|
|
|
|
size_t strcspn(const char *str, const char *rej)
|
|
|
|
{
|
|
|
|
const char *ptr = str;
|
|
|
|
|
|
|
|
while (*ptr && strchr(rej, *ptr) == NULL) ptr++;
|
|
|
|
|
|
|
|
return ptr - str;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Return the first occurence in str of any byte in acc
|
|
|
|
//
|
|
|
|
char *strpbrk(const char *str, const char *acc)
|
|
|
|
{
|
|
|
|
str += strcspn(str, acc);
|
|
|
|
|
|
|
|
return *str ? (char *)str : NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Return the first occurence of the substring needle
|
|
|
|
// in the string haystack, NULL if none is found
|
|
|
|
// Null-terminators aren't compared
|
|
|
|
//
|
|
|
|
char *strstr(const char *haystack, const char *needle)
|
|
|
|
{
|
|
|
|
const size_t needle_size = strlen(needle);
|
|
|
|
|
|
|
|
// Moves haystack to first occurence of the needle's first byte
|
|
|
|
while ((haystack = strchr(haystack, *needle)) != NULL) {
|
|
|
|
if (strncmp(haystack, needle, needle_size) == 0) {
|
|
|
|
return (char *)haystack;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Tokenize a string, using saveptr as a savestate
|
|
|
|
// We let a segmentation fault happen if *saveptr == NULL
|
|
|
|
//
|
|
|
|
char *strtok_r(char *restrict str, const char *restrict delim, char **restrict saveptr)
|
|
|
|
{
|
|
|
|
assert(*saveptr != NULL);
|
|
|
|
|
|
|
|
if (str == NULL) str = *saveptr;
|
|
|
|
|
|
|
|
// Skip initial segments composed only of delimiters
|
|
|
|
str += strspn(str, delim);
|
|
|
|
|
|
|
|
// If str is empty, store it in saveptr so that next call
|
|
|
|
// still finds an empty strings and returns NULL
|
|
|
|
if (*str == 0) {
|
|
|
|
*saveptr = str;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
char *ptr = str, *tok_end = strpbrk(str, delim);
|
|
|
|
|
|
|
|
//
|
|
|
|
// If we found the last token, set *saveptr to a str's null-terminator
|
|
|
|
// Otherwise, null-terminate token and save next byte
|
|
|
|
//
|
|
|
|
|
|
|
|
if (tok_end == NULL) {
|
|
|
|
while (*ptr) ptr++;
|
|
|
|
*saveptr = ptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
else {
|
|
|
|
*tok_end = 0;
|
|
|
|
*saveptr = tok_end + 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return str;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Tokenize a string in a very thread-unsafe way
|
|
|
|
//
|
|
|
|
char *strtok(char *restrict str, const char *restrict delim)
|
|
|
|
{
|
|
|
|
static char *saveptr = NULL;
|
|
|
|
|
2019-02-06 14:07:38 +01:00
|
|
|
// Avoid this function if possible
|
2019-01-21 15:00:04 +01:00
|
|
|
KalAssert(FALSE);
|
|
|
|
|
|
|
|
if (str) saveptr = str;
|
|
|
|
|
|
|
|
return strtok_r(str, delim, &saveptr);
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Copy the string src into dest
|
|
|
|
//
|
|
|
|
char *strcpy(char *restrict dest, const char *restrict src)
|
|
|
|
{
|
|
|
|
char *base = dest;
|
|
|
|
|
|
|
|
while ((*dest++ = *src++));
|
|
|
|
|
|
|
|
return base;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// strcpy() but always writes n bytes
|
|
|
|
// Will not null-terminate for strings longer than n bytes
|
|
|
|
//
|
|
|
|
char *strncpy(char *restrict dest, const char *restrict src, size_t n)
|
|
|
|
{
|
|
|
|
size_t it;
|
|
|
|
|
|
|
|
for (it = 0; it < n && src[it]; it++) {
|
|
|
|
dest[it] = src[it];
|
|
|
|
}
|
|
|
|
|
|
|
|
while (it < n) dest[it++] = 0;
|
|
|
|
|
|
|
|
return dest;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Copies at most n-1 bytes from src to dest
|
|
|
|
// Always null-terminates dest, but doesn't fill
|
|
|
|
// dest's contents past the null-terminator
|
|
|
|
//
|
2019-02-06 14:07:38 +01:00
|
|
|
// Returns the number of bytes NOT written, not counting null-terminators
|
2019-01-21 15:00:04 +01:00
|
|
|
//
|
|
|
|
size_t strnzcpy(char *restrict dest, const char *restrict src, size_t n)
|
|
|
|
{
|
2019-02-06 14:07:38 +01:00
|
|
|
size_t it, loss;
|
2019-01-21 15:00:04 +01:00
|
|
|
|
|
|
|
for (it = 0; it < n - 1 && src[it]; it++) {
|
|
|
|
dest[it] = src[it];
|
|
|
|
}
|
|
|
|
|
2019-02-06 14:07:38 +01:00
|
|
|
dest[it] = 0;
|
2019-01-21 15:00:04 +01:00
|
|
|
|
2019-02-06 14:07:38 +01:00
|
|
|
// Compute how many bytes were not copied
|
|
|
|
for (loss = it; src[loss]; loss++);
|
|
|
|
|
|
|
|
return loss - it;
|
2019-01-21 15:00:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Appends a copy of src at the end of dest
|
|
|
|
//
|
|
|
|
char *strcat(char *restrict dest, const char *restrict src)
|
|
|
|
{
|
|
|
|
char *base = dest;
|
|
|
|
while (*dest) dest++;
|
|
|
|
while ((*dest++ = *src++));
|
|
|
|
return base;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Appends a copy of at most n bytes of src at the end of dest
|
|
|
|
//
|
|
|
|
char *strncat(char *restrict dest, const char *restrict src, size_t n)
|
|
|
|
{
|
|
|
|
size_t it, off = 0;
|
|
|
|
|
|
|
|
while (dest[off]) off++;
|
|
|
|
|
|
|
|
for (it = 0; it < n && src[it]; it++) {
|
|
|
|
dest[it+off] = src[it];
|
|
|
|
}
|
|
|
|
|
|
|
|
while (it++ < n) dest[it+off] = 0;
|
|
|
|
|
|
|
|
return dest;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
2019-02-06 14:07:38 +01:00
|
|
|
// Appends at most n-1 bytes from src to dest
|
|
|
|
// Always null-terminates dest, but doesn't fill
|
|
|
|
// dest's contents past the null-terminator
|
|
|
|
//
|
|
|
|
// Returns the number of bytes NOT written, not counting null-terminators
|
2019-01-21 15:00:04 +01:00
|
|
|
//
|
|
|
|
size_t strnzcat(char *restrict dest, const char *restrict src, size_t n)
|
|
|
|
{
|
2019-02-06 14:07:38 +01:00
|
|
|
size_t it, loss, off = 0;
|
2019-01-21 15:00:04 +01:00
|
|
|
|
|
|
|
while (dest[off]) off++;
|
|
|
|
|
|
|
|
for (it = 0; it < n - 1 && src[it]; it++) {
|
|
|
|
dest[it+off] = src[it];
|
|
|
|
}
|
|
|
|
|
|
|
|
dest[it+off] = 0;
|
|
|
|
|
2019-02-06 14:07:38 +01:00
|
|
|
// Compute how many bytes were not copied
|
|
|
|
for (loss = it; src[loss+off]; loss++);
|
|
|
|
|
|
|
|
return loss - it;
|
2019-01-21 15:00:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Reverses the string src, putting the result into dest
|
|
|
|
//
|
|
|
|
char *strrev(char *restrict dest, const char *restrict src)
|
|
|
|
{
|
|
|
|
char *orig = dest;
|
|
|
|
size_t n = strlen(src);
|
|
|
|
|
|
|
|
dest[n--] = '\0';
|
|
|
|
|
|
|
|
while ((*dest++ = src[n--]));
|
|
|
|
|
|
|
|
return orig;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Reverses a string, modifying it
|
|
|
|
//
|
|
|
|
char *strrev2(char *str)
|
|
|
|
{
|
|
|
|
char ch, *orig = str;
|
|
|
|
size_t n = strlen(str);
|
|
|
|
char *temp = str + n - 1;
|
|
|
|
|
|
|
|
while (temp > str) {
|
|
|
|
ch = *temp;
|
|
|
|
*temp-- = *str;
|
|
|
|
*str++ = ch;
|
|
|
|
}
|
|
|
|
|
|
|
|
return orig;
|
|
|
|
}
|