/*
* Copyright (c) 2018 Jiri Svoboda
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* - 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.
* - The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
*/
/** @addtogroup libc
* @{
*/
/** @file
*/
#include <errno.h>
#include <stddef.h>
#include <stdlib.h>
#include <str_error.h>
#include <string.h>
/** Copy string.
*
* Copy the string pointed to by @a s2 to the array pointed to by @a s1
* including the terminating null character. The source and destination
* must not overlap.
*
* @param s1 Destination array
* @param s2 Source string
* @return @a s1
*/
char *strcpy(char *s1, const char *s2)
{
char *dp = s1;
/* Copy characters */
while (*s2 != '\0')
*dp++ = *s2++;
/* Copy the terminating null character */
*dp++ = *s2++;
return s1;
}
/** Copy not more than @a n characters.
*
* Copy not more than @a n characters from @a s2 to @a s1. Characters
* following a null character are not copied. If The string @a s2 is
* shorter than @a n characters, null characters are appended to the
* copy in @a s1, until @a n characters in all have been written.
*
* (I.e. @a s1 is padded with null characters up to size @a n).
* The source and destination must not overlap.
*
* @param s1 Destination array
* @param s2 Source string
* @param n Number of characters to copy
* @return @a s1
*/
char *strncpy(char *s1, const char *s2, size_t n)
{
char *dp = s1;
size_t i;
for (i = 0; i < n; i++) {
*dp++ = *s2;
if (*s2 != '\0')
++s2;
}
return s1;
}
/** Append string.
*
* Append a copy of string in @a s2 to the string pointed to by @a s1
* (including the terminating null character). @a s1 and @a s2 must not
* overlap.
*
* @param s1 Destination buffer holding a string to be appended to
* @param s2 String to be appended
* @return @a s1
*/
char *strcat(char *s1, const char *s2)
{
char *dp = s1;
/* Find end of first string */
while (*dp != '\0')
++dp;
/* Copy second string */
while (*s2 != '\0')
*dp++ = *s2++;
/* Copy null character */
*dp = '\0';
return s1;
}
/** Append not more than @a n characters.
*
* Append a copy of max. @a n characters from string in @a s2 to the string
* pointed to by @a s1. The resulting string is always null-terminated.
*
* @param s1 Destination buffer holding a string to be appended to
* @param s2 String to be appended
* @param n Maximum number of characters to copy
* @return @a s1
*/
char *strncat(char *s1, const char *s2, size_t n)
{
char *dp = s1;
/* Find end of first string */
while (*dp != '\0')
++dp;
/* Copy second string */
while (*s2 != '\0' && n > 0) {
*dp++ = *s2++;
--n;
}
/* Copy null character */
*dp = '\0';
return s1;
}
/** Compare two strings.
*
* @param s1 First string
* @param s2 Second string
* @return Greater than, equal to, less than zero if @a s1 > @a s2,
* @a s1 == @a s2, @a s1 < @a s2, resp.
*/
int strcmp(const char *s1, const char *s2)
{
while (*s1 == *s2 && *s1 != '\0') {
++s1;
++s2;
}
return *s1 - *s2;
}
/** Compare two strings based on LC_COLLATE of current locale.
*
* @param s1 First string
* @param s2 Second string
* @return Greater than, equal to, less than zero if @a s1 > @a s2,
* @a s1 == @a s2, @a s1 < @a s2, resp.
*/
int strcoll(const char *s1, const char *s2)
{
/* Note: we don't support locale other than "C" */
return strcmp(s1, s2);
}
/** Compare not more than @a n characters.
*
* @param s1 First string
* @param s2 Second string
* @param n Maximum number of characters to compare
* @return Greater than, equal to, less than zero if @a s1 > @a s2,
* @a s1 == @a s2, @a s1 < @a s2, resp. (within the first @a n chars.)
*/
int strncmp(const char *s1, const char *s2, size_t n)
{
while (*s1 == *s2 && *s1 != '\0' && n > 0) {
++s1;
++s2;
--n;
}
if (n > 0)
return *s1 - *s2;
return 0;
}
/** Transform string for collation.
*
* Transform string in @a s2 to the buffer @a s1, writing no more than
* @a n characters (including the terminating null character). The transformed
* string is such that using strcmp on two transformed strings should be
* equivalent to using strcoll on the original strings.
*
* @param s1 Destination buffer
* @param s2 Source string
* @param n Max. number of characters to write (including terminating null)
* @return Length of the transformed string not including the null terminator.
* If the value returned is @a n or more, the contents of the buffer
* pointed to by @a s1 are undefined.
*/
size_t strxfrm(char *s1, const char *s2, size_t n)
{
size_t i;
size_t len;
len = strlen(s2);
for (i = 0; i < n; i++) {
*s1++ = *s2;
if (*s2 == '\0')
break;
++s2;
}
return len;
}
/** Find the first occurrence of a character in a string.
*
* The character @a c is converted to char. The null character is
* considered part of the string.
*
* @param s String
* @param c Character
* @return Pointer to the located character or @c NULL if the character
* does not occur in the string.
*/
char *strchr(const char *s, int c)
{
do {
if (*s == (char) c)
return (char *) s;
} while (*s++ != '\0');
return NULL;
}
/** Compute the size of max. initial segment consisting of a complementary
* set of characters.
*
* Compute the size of the max. initial segment of @a s1 consisting only
* of characters *not* from @a s2.
*
* @param s1 String to search
* @param s2 String containing set of characters
* @return Size of initial segment of @a s1 consisting only of characters
* not from @a s2.
*/
size_t strcspn(const char *s1, const char *s2)
{
char *p;
size_t n;
n = 0;
while (*s1 != '\0') {
/* Look for current character in s2 */
p = strchr(s2, *s1);
/* If found, return current character count. */
if (p != NULL)
break;
++s1;
++n;
}
return n;
}
/** Search string for occurrence of any of a set of characters.
*
* @param s1 String to search
* @param s2 String containing a set of characters
*
* @return Pointer to first character found or @c NULL if not found
*/
char *strpbrk(const char *s1, const char *s2)
{
char *p;
while (*s1 != '\0') {
/* Look for current character in s2 */
p = strchr(s2, *s1);
/* If found, return pointer to current character. */
if (p != NULL)
return (char *) s1;
++s1;
}
return NULL;
}
/** Find the last occurrence of a character in a string.
*
* The character @a c is converted to char. The null character is
* considered part of the string.
*
* @param s String
* @param c Character
* @return Pointer to the located character or @c NULL if the character
* does not occur in the string.
*/
char *strrchr(const char *s, int c)
{
size_t i = strlen(s);
while (i > 0) {
if (s[i] == (char) c)
return (char *)s + i;
--i;
}
return NULL;
}
/** Compute the size of max. initial segment consisting of a set of characters.
*
* Compute tha size of the max. initial segment of @a s1 consisting only
* of characters from @a s2.
*
* @param s1 String to search
* @param s2 String containing set of characters
* @return Size of initial segment of @a s1 consisting only of characters
* from @a s2.
*/
size_t strspn(const char *s1, const char *s2)
{
char *p;
size_t n;
n = 0;
while (*s1 != '\0') {
/* Look for current character in s2 */
p = strchr(s2, *s1);
/* If not found, return current character count. */
if (p == NULL)
break;
++s1;
++n;
}
return n;
}
/** Find occurrence of substring in a string.
*
* Find the first occurrence in @a s1 of the characters in @a s2, excluding
* the terminating null character. If s2 is an empty string, returns @a s1.
*
* @param s1 String to search
* @param s2 Sequence of characters to find
*
* @return Pointer inside @a s1 or @c NULL if not found.
*/
char *strstr(const char *s1, const char *s2)
{
size_t len;
/*
* Naive search algorithm.
*
* Two-Way String-Matching might be a plausible alternative
* for larger haystack+needle combinations.
*/
len = strlen(s2);
while (*s1 != '\0') {
if (strncmp(s1, s2, len) == 0)
return (char *) s1;
++s1;
}
return NULL;
}
/** Tokenize a string (reentrant).
*
* The contents of @a s1 are modified (the separators get overwritten by null
* characters). The separators can be different each iteration, their identity\
* is, however, lost.
*
* @param s1 String buffer to get the first token, @c NULL to get the next
* token
* @param s2 String containing current separators
* @return Pointer to the next token
*/
char *__strtok_r(char *s1, const char *s2, char **saveptr)
{
char *s;
char *tbegin;
char *tend;
if (s1 != NULL) {
/* Starting tokenization of a new string */
s = s1;
} else {
/* Use saved position of next token */
s = *saveptr;
/* Check if we ran out of tokens */
if (s == NULL)
return NULL;
}
/* Find position of first character that is not a separator */
tbegin = s;
while (*tbegin != '\0' && strchr(s2, *tbegin) != NULL)
++tbegin;
/* If no such character is found, there are no tokens */
if (*tbegin == '\0')
return NULL;
/* Find first character that is a separator */
tend = strpbrk(tbegin, s2);
if (tend != NULL) {
/* Overwrite the separator, next token starts just after */
*tend = '\0';
*saveptr = tend + 1;
} else {
/* No more tokens will be returned next time */
*saveptr = NULL;
}
return tbegin;
}
/** Saved position of next token for function strtok */
static char *strtok_saveptr = NULL;
/** Tokenize a string.
*
* This function has internal state and thus is not reentrant.
* ISO C says the implementation should behave as if no (standard) library
* call calls strtok. The contents of @a s1 are modified (the separators
* get overwritten by null characters). The separators can be different
* each iteration, their identity is, however, lost.
*
* Never use this function since it's not reentrant.
*
* @param s1 String buffer to get the first token, @c NULL to get the next
* token
* @param s2 String containing current separators
* @return Pointer to the next token
*/
char *strtok(char *s1, const char *s2)
{
return __strtok_r(s1, s2, &strtok_saveptr);
}
/** Map error number to a string.
*
* The string returned by the function may be overwritten by a subsequent
* call to strerror (ISO C). In our implementation the function is, in fact,
* reentrant, as the string returned is static and never modified.
*
* @param errnum Error number
* @return Pointer to error message
*/
char *strerror(int errnum)
{
return (char *) str_error(errnum);
}
/** Return number of characters in string.
*
* @param s String
* @return Number of characters preceding the null character.
*/
size_t strlen(const char *s)
{
size_t n;
n = 0;
while (*s != '\0') {
++s;
++n;
}
return n;
}
/** Return number of characters in string with length limit.
*
* @param s String
* @param maxlen Maximum number of characters to read
* @return Number of characters preceding the null character, at most @a maxlen.
*/
size_t strnlen(const char *s, size_t maxlen)
{
size_t n;
n = 0;
while (n < maxlen && *s != '\0') {
++s;
++n;
}
return n;
}
/** Allocate a new duplicate of string.
*
* @param s String to duplicate
* @return New string or @c NULL on failure (in which case @c errno is set
* to ENOMEM).
*/
char *strdup(const char *s)
{
size_t sz;
char *dup;
sz = strlen(s);
dup = malloc(sz + 1);
if (dup == NULL) {
errno = ENOMEM;
return NULL;
}
strcpy(dup, s);
return dup;
}
/** Allocate a new duplicate of string with length limit.
*
* Creates a new duplicate of @a s. If @a s is longer than @a n characters,
* only @a n characters are copied and a null character is appended.
*
* @param s String to duplicate
* @param n Maximum number of characters to copy
* @return New string or @c NULL on failure (in which case @c errno is set
* to ENOMEM).
*/
char *strndup(const char *s, size_t n)
{
size_t sz;
char *dup;
sz = strnlen(s, n);
dup = malloc(sz + 1);
if (dup == NULL) {
errno = ENOMEM;
return NULL;
}
strncpy(dup, s, sz);
return dup;
}
/** @}
*/