HelenOS sources

root/uspace/lib/c/generic/string.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. strcpy
  2. strncpy
  3. strcat
  4. strncat
  5. strcmp
  6. strcoll
  7. strncmp
  8. strxfrm
  9. strchr
  10. strcspn
  11. strpbrk
  12. strrchr
  13. strspn
  14. strstr
  15. __strtok_r
  16. strtok
  17. strerror
  18. strlen
  19. strnlen
  20. strdup
  21. strndup

/*
 * 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;
}

/** @}
 */

/* [<][>][^][v][top][bottom][index][help] */
HelenOS homepage, sources at GitHub