/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/*
* Authors: Jeffrey Stedfast <fejj@ximian.com>
*
* Copyright 2001 Ximian, Inc. (www.ximian.com)
*
* This program 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 2 of the License, or
* (at your option) any later version.
*
* This program 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 this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
*
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include "strlib.h"
#include <stdio.h>
#include <ctype.h>
#ifndef HAVE_MEMCHR
/**
* memchr:
* @s: memory area
* @c: character
* @n: memory area length
*
* Scans the first n bytes of the memory area pointed to by @s for the
* character @c. The first byte to match @c (interpreted as an
* unsigned character) stops the operation.
*
* Returns a pointer to the matching byte or NULL if the character
* does not occur in the given memory area.
**/
void *
memchr (const void *s, int c, size_t n)
{
unsigned char ch = c & 0xff;
register unsigned char *p;
unsigned char *q;
for (p = (unsigned char *) s, q = p + n; p < q; p++)
if (*p == ch)
return (void *) p;
return NULL;
}
#endif
#ifndef HAVE_MEMRCHR
/**
* memrchr:
* @s: memory area
* @c: character
* @n: memory area length
*
* The memrchr() function scans in reverse the first n bytes of the
* memory area pointed to by @s for the character @c. The first byte
* to match @c (interpreted as an unsigned character) stops the
* operation.
*
* Returns a pointer to the matching byte or NULL if the character
* does not occur in the given memory area.
**/
void *
memrchr (const void *s, int c, size_t n)
{
unsigned char ch = c & 0xff;
register unsigned char *q;
unsigned char *p;
for (p = (unsigned char *) s, q = p + n - 1; q >= p; q--)
if (*q == ch)
return (void *) q;
return NULL;
}
#endif
#define lowercase(c) (isupper ((int) (c)) ? tolower ((int) (c)) : (int) (c))
#define bm_index(c, icase) ((icase) ? lowercase (c) : (int) (c))
#define bm_equal(c1, c2, icase) ((icase) ? lowercase (c1) == lowercase (c2) : (c1) == (c2))
/* FIXME: this is just a guess... should really do some performace tests to get an accurate measure */
#define bm_optimal(hlen, nlen) (((hlen) ? (hlen) > 20 : 1) && (nlen) > 10 ? 1 : 0)
static unsigned char *
__boyer_moore (const unsigned char *haystack, size_t haystacklen,
const unsigned char *needle, size_t needlelen, int icase)
{
register unsigned char *hc_ptr, *nc_ptr;
unsigned char *he_ptr, *ne_ptr, *h_ptr;
size_t skiptable[256], n;
register int i;
#ifdef BOYER_MOORE_CHECKS
/* we don't need to do these checks since memmem/strstr/etc do it already */
/* if the haystack is shorter than the needle then we can't possibly match */
if (haystacklen < needlelen)
return NULL;
/* instant match if the pattern buffer is 0-length */
if (needlelen == 0)
return (unsigned char *) haystack;
#endif /* BOYER_MOORE_CHECKS */
/* set a pointer at the end of each string */
ne_ptr = (unsigned char *) needle + needlelen - 1;
he_ptr = (unsigned char *) haystack + haystacklen - 1;
/* create our skip table */
for (i = 0; i < 256; i++)
skiptable[i] = needlelen;
for (nc_ptr = (unsigned char *) needle; nc_ptr < ne_ptr; nc_ptr++)
skiptable[bm_index (*nc_ptr, icase)] = (size_t) (ne_ptr - nc_ptr);
h_ptr = (unsigned char *) haystack;
while (haystacklen >= needlelen) {
hc_ptr = h_ptr + needlelen - 1; /* set the haystack compare pointer */
nc_ptr = ne_ptr; /* set the needle compare pointer */
/* work our way backwards till they don't match */
for (i = 0; nc_ptr > (unsigned char *) needle; nc_ptr--, hc_ptr--, i++)
if (!bm_equal (*nc_ptr, *hc_ptr, icase))
break;
if (!bm_equal (*nc_ptr, *hc_ptr, icase)) {
n = skiptable[bm_index (*hc_ptr, icase)];
if (n == needlelen && i)
if (bm_equal (*ne_ptr, ((unsigned char *) needle)[0], icase))
n--;
h_ptr += n;
haystacklen -= n;
} else
return (unsigned char *) h_ptr;
}
return NULL;
}
#ifndef HAVE_MEMMEM
/**
* memmem:
* @haysack: memory area to search
* @haystacklen: memory area length
* @needle: substring to search for
* @needlelen: substring length
*
* Finds the start of the first occurence of the substring @needle of
* length @needlelen within the memory area @haystack of length
* @haystacklen.
*
* Returns a pointer to the beginning of the substring within the
* memory area, or NULL if the substring is not found.
**/
void *
memmem (const void *haystack, size_t haystacklen, const void *needle, size_t needlelen)
{
register unsigned char *h, *n, *hc, *nc;
unsigned char *he, *ne;
if (haystacklen < needlelen) {
return NULL;
} else if (needlelen == 0) {
return (void *) haystack;
} else if (needlelen == 1) {
return memchr (haystack, (int) ((unsigned char *) needle)[0], haystacklen);
} else if (bm_optimal (haystacklen, needlelen)) {
return (void *) __boyer_moore ((const unsigned char *) haystack, haystacklen,
(const unsigned char *) needle, needlelen, 0);
}
h = (unsigned char *) haystack;
he = (unsigned char *) haystack + haystacklen - needlelen;
n = (unsigned char *) needle;
ne = (unsigned char *) needle + needlelen;
while (h < he) {
if (*h == *n) {
for (hc = h + 1, nc = n + 1; nc < ne; hc++, nc++)
if (*hc != *nc)
break;
if (nc == ne)
return (void *) h;
}
h++;
}
return NULL;
}
#endif
#ifndef HAVE_STRLEN
/**
* strlen:
* @s: string
*
* Calculates the length of the string @s, not including the
* terminating '\0' character.
*
* Returns the number of characters in @s.
**/
size_t
strlen (const char *s)
{
register const char *p;
for (p = s; *p; p++);
return p - s;
}
#endif
#ifndef HAVE_STRCPY
/**
* strcpy:
* @dest: destination string
* @src: source string
*
* Copies the string pointed to by @src (including the terminating
* '\0' character) to the character array pointed to by @dest. The
* strings may not overlap and the destination string @dest must be
* large enough to receive the copy.
*
* Returns a pointer to the resulting destination string @dest.
**/
char *
strcpy (char *dest, const char *src)
{
register const char *s = src;
register char *d = dest;
while (*s)
*d++ = *s++;
*d = '\0';
return dest;
}
#endif
#ifndef HAVE_STRNCPY
/**
* strncpy:
* @dest: destination string
* @src: source string
* @n: number of bytes to copy
*
* Copies no more than the first @n characters of the string pointed
* to by @src to the character array pointed to by @dest. Thus if
* there is no null byte among the first @n bytes of @src, @dest will
* not be null-terminated. The strings may not overlap and the
* destination string @dest must be large enough to receive the copy.
*
* Returns a pointer to the resulting destination string @dest.
**/
char *
strncpy (char *dest, const char *src, size_t n)
{
register const char *s = src;
register char *d = dest;
while (*s && n)
*d++ = *s++, n--;
if (n)
*d = '\0';
return dest;
}
#endif
#ifndef HAVE_STRLCPY
/**
* strlcpy:
* @dest: destination string
* @src: source string
* @n: number of bytes to copy
*
* Copies at most @n-1 characters (@n being the size of the string
* buffer @dest) of the string pointed to by @src to the string
* pointed to by @dest and null-terminates @dest. The strings may not
* overlap.
*
* Returns the size of the resultant string, @dest.
**/
size_t
strlcpy (char *dest, const char *src, size_t n)
{
register const char *s = src;
register char *d = dest;
while (*s && n > 1)
*d++ = *s++, n--;
*d = '\0';
return d - dest;
}
#endif
#ifndef HAVE_STPCPY
/**
* stpcpy:
* @dest: destination string
* @src: source string
*
* Copies the string pointed to by @src (including the terminating
* '\0' character) to the character array pointed to by @dest. The
* strings may not overlap and the destination string @dest must be
* large enough to receive the copy.
*
* Returns a pointer to the end of the string @dest.
**/
char *
stpcpy (char *dest, const char *src)
{
register const char *s = src;
register char *d = dest;
while (*s)
*d++ = *s++;
*d = '\0';
return d;
}
#endif
#ifndef HAVE_STRCAT
/**
* strcat:
* @dest: destination string
* @src: source string
*
* Appends the @src string to the @dest string overwriting the '\0'
* character at the end of @dest, and then adds a terminating '\0'
* character. The strings may not overlap, and the destination string
* dest must have enough space for the result.
*
* Returns a pointer to the resulting destination string @dest.
**/
char *
strcat (char *dest, const char *src)
{
register const char *s = src;
register char *d = dest;
while (*d)
d++;
while (*s)
*d++ = *s++;
*d = '\0';
return dest;
}
#endif
#ifndef HAVE_STRNCAT
/**
* strncat:
* @dest: destination string
* @src: source string
* @n: number of bytes to append
*
* Appends at most the first @n characters of the @src string to the
* @dest string overwriting the '\0' character at the end of
* @dest. Thus if there is no null byte among the first @n bytes of
* @src, @dest will not be null-terminated. The strings may not
* overlap, and the destination string dest must have enough space for
* the result.
*
* Returns a pointer to the resulting destination string @dest.
**/
char *
strncat (char *dest, const char *src, size_t n)
{
register const char *s = src;
register char *d = dest;
while (*d)
d++;
while (*s && n)
*d++ = *s++, n--;
if (n)
*d = '\0';
return dest;
}
#endif
#ifndef HAVE_STRLCAT
/**
* strlcat:
* @dest: destination string
* @src: source string
* @n: number of bytes to append
*
* Appends at most (@n - strlen (dest) - 1) characters (@n being the
* size of the string @dest) of the @src string to the @dest string
* overwriting the null character at the end of @dest and
* null-terminates the resulting @dest. The strings may not overlap.
*
* Returns the size of the resultant string, @dest.
**/
size_t
strlcat (char *dest, const char *src, size_t n)
{
register const char *s = src;
register char *d = dest;
while (*d)
d++;
while (*s && n > 1)
*d++ = *s++, n--;
*d = '\0';
return d - dest;
}
#endif
#ifndef HAVE_STRCHR
/**
* strchr:
* @s: string
* @c: character
*
* Scans the string pointed to by @s for the character @c. The first
* byte to match @c (interpreted as an unsigned character) stops the
* operation.
*
* Returns a pointer to the matching character or NULL if the
* character does not occur in the given string.
**/
char *
strchr (const char *s, int c)
{
unsigned char ch = c & 0xff;
register unsigned char *p;
for (p = (unsigned char *) s; *p; p++)
if (*p == ch)
return (char *) p;
return NULL;
}
#endif
#ifndef HAVE_STRRCHR
/**
* strrchr:
* @s: string
* @c: character
*
* Scans the string pointed to by @s in reverse for the character
* @c. The first byte to match @c (interpreted as an unsigned
* character) stops the operation.
*
* Returns a pointer to the matching character or NULL if the
* character does not occur in the given string.
**/
char *
strrchr (const char *s, int c)
{
unsigned char ch = c & 0xff;
register unsigned char *p;
unsigned char *r = NULL;
for (p = (unsigned char *) s; *p; p++)
if (*p == ch)
r = p;
return (char *) r;
}
#endif
#ifndef HAVE_STRNSTR
/**
* strnstr:
* @haystack: string to search
* @needle: substring to search for
* @haystacklen: length of the haystack to search
*
* Finds the first occurence of the substring @needle within the
* bounds of string @haystack.
*
* Returns a pointer to the beginning of the substring match within
* @haystack, or NULL if the substring is not found.
**/
char *
strnstr (const char *haystack, const char *needle, size_t haystacklen)
{
register unsigned char *h, *n, *hc, *nc;
size_t needlelen;
needlelen = strlen (needle);
if (haystacklen < needlelen) {
return NULL;
} else if (needlelen == 0) {
return (char *) haystack;
} else if (needlelen == 1) {
return memchr (haystack, (int) ((unsigned char *) needle)[0], haystacklen);
} else if (bm_optimal (haystacklen, needlelen)) {
return (char *) __boyer_moore ((const unsigned char *) haystack, haystacklen,
(const unsigned char *) needle, needlelen, 0);
}
h = (unsigned char *) haystack;
n = (unsigned char *) needle;
while (haystacklen >= needlelen) {
if (*h == *n) {
for (hc = h + 1, nc = n + 1; *nc; hc++, nc++)
if (*hc != *nc)
break;
if (!*nc)
return (char *) h;
}
haystacklen--;
h++;
}
return NULL;
}
#endif
#ifndef HAVE_STRSTR
/**
* strstr:
* @haystack: string to search
* @needle: substring to search for
*
* Finds the first occurence of the substring @needle within the
* string @haystack.
*
* Returns a pointer to the beginning of the substring match within
* @haystack, or NULL if the substring is not found.
**/
char *
strstr (const char *haystack, const char *needle)
{
register unsigned char *h, *n, *hc, *nc;
size_t needlelen;
needlelen = strlen (needle);
if (needlelen == 0) {
return (char *) haystack;
} else if (needlelen == 1) {
return strchr (haystack, (int) ((unsigned char *) needle)[0]);
} else if (bm_optimal (0, needlelen)) {
return (char *) __boyer_moore ((const unsigned char *) haystack, strlen (haystack),
(const unsigned char *) needle, needlelen, 0);
}
h = (unsigned char *) haystack;
n = (unsigned char *) needle;
while (*(h + needlelen - 1)) {
if (*h == *n) {
for (hc = h + 1, nc = n + 1; *hc && *nc; hc++, nc++)
if (*hc != *nc)
break;
if (!*nc)
return (char *) h;
}
h++;
}
return NULL;
}
#endif
#ifndef HAVE_STRNCASESTR
/**
* strncasestr:
* @haystack: string to search
* @needle: substring to search for
* @haystacklen: length of the haystack to search
*
* Finds the first occurence of the substring @needle within the
* bounds of string @haystack ignoring case.
*
* Returns a pointer to the beginning of the substring match within
* @haystack, or NULL if the substring is not found.
**/
char *
strncasestr (const char *haystack, const char *needle, size_t haystacklen)
{
register unsigned char *h, *n, *hc, *nc;
size_t needlelen;
needlelen = strlen (needle);
if (needlelen == 0) {
return (char *) haystack;
} else if (bm_optimal (haystacklen, needlelen)) {
return (char *) __boyer_moore ((const unsigned char *) haystack, haystacklen,
(const unsigned char *) needle, needlelen, 1);
}
h = (unsigned char *) haystack;
n = (unsigned char *) needle;
while (haystacklen >= needlelen) {
if (lowercase (*h) == lowercase (*n)) {
for (hc = h + 1, nc = n + 1; *nc; hc++, nc++)
if (lowercase (*hc) != lowercase (*nc))
break;
if (!*nc)
return h;
}
haystacklen--;
h++;
}
return NULL;
}
#endif
#ifndef HAVE_STRCASESTR
/**
* strcasestr:
* @haystack: string to search
* @needle: substring to search for
*
* Finds the first occurence of the substring @needle within the
* string @haystack ignoring case.
*
* Returns a pointer to the beginning of the substring match within
* @haystack, or NULL if the substring is not found.
**/
char *
strcasestr (const char *haystack, const char *needle)
{
register unsigned char *h, *n, *hc, *nc;
size_t needlelen;
needlelen = strlen (needle);
if (needlelen == 0) {
return (char *) haystack;
} else if (bm_optimal (0, needlelen)) {
return (char *) __boyer_moore ((const unsigned char *) haystack, strlen (haystack),
(const unsigned char *) needle, needlelen, 1);
}
h = (unsigned char *) haystack;
n = (unsigned char *) needle;
while (*(h + needlelen - 1)) {
if (lowercase (*h) == lowercase (*n)) {
for (hc = h + 1, nc = n + 1; *hc && *nc; hc++, nc++)
if (lowercase (*hc) != lowercase (*nc))
break;
if (!*nc)
return (char *) h;
}
h++;
}
return NULL;
}
#endif
#ifndef HAVE_STRNCASECMP
/**
* strncasecmp:
* @s1: string 1
* @s2: string 2
* @n:
*
* Compares the first @n characters of the 2 strings, @s1 and @s2,
* ignoring the case of the characters.
*
* Returns an integer less than, equal to, or greater than zero if @s1
* is found, respectively, to be less than, to match, or to be greater
* than @s2.
**/
int
strncasecmp (const char *s1, const char *s2, size_t n)
{
register const unsigned char *p1 = s1, *p2 = s2;
const unsigned char *q1 = s1 + n;
for ( ; *p1 && p1 < q1; p1++, p2++)
if (lowercase (*p1) != lowercase (*p2))
return lowercase (*p1) - lowercase (*p2);
return 0;
}
#endif
#ifndef HAVE_STRCASECMP
/**
* strncasecmp:
* @s1: string 1
* @s2: string 2
*
* Compares the 2 strings, @s1 and @s2, ignoring the case of the
* characters.
*
* Returns an integer less than, equal to, or greater than zero if @s1
* is found, respectively, to be less than, to match, or to be greater
* than @s2.
**/
int
strcasecmp (const char *s1, const char *s2)
{
register const unsigned char *p1 = s1, *p2 = s2;
for ( ; *p1; p1++, p2++)
if (lowercase (*p1) != lowercase (*p2))
break;
return lowercase (*p1) - lowercase (*p2);
}
#endif
#ifdef TEST_SUITE
#define check(expr) { \
if (!(expr)) \
fprintf (stderr, "file %s: line %d (%s): check failed: (%s)\n", \
__FILE__, __LINE__, __FUNCTION__, #expr); \
}
static void
test_strchr (void)
{
char one[256];
(void) strcpy (one, "abcd");
check (strchr (one, 'a') == one);
check (strchr (one, 'b') == one + 1);
check (strchr (one, 'c') == one + 2);
check (strchr (one, 'd') == one + 3);
check (strchr (one, 'z') == NULL);
}
static void
test_strrchr (void)
{
char one[256];
(void) strcpy (one, "abcd");
check (strrchr (one, 'a') == one);
check (strrchr (one, 'b') == one + 1);
check (strrchr (one, 'c') == one + 2);
check (strrchr (one, 'd') == one + 3);
check (strrchr (one, 'z') == NULL);
(void) strcpy (one, "abcdabcabcabcac");
check (strrchr (one, 'c') == one + strlen (one) - 1);
check (strrchr (one, 'a') == one + strlen (one) - 2);
check (strrchr (one, 'b') == one + strlen (one) - 4);
}
static void
test_strstr (void)
{
char one[256];
check (strstr ("abcd", "z") == NULL); /* Not found. */
check (strstr ("abcd", "abx") == NULL); /* Dead end. */
(void) strcpy (one, "abcd");
check (strstr (one, "c") == one + 2); /* Basic test. */
check (strstr (one, "bc") == one + 1); /* Multichar. */
check (strstr (one, "d") == one + 3); /* End of string. */
check (strstr (one, "cd") == one + 2); /* Tail of string. */
check (strstr (one, "abc") == one); /* Beginning. */
check (strstr (one, "abcd") == one); /* Exact match. */
check (strstr (one, "abcde") == NULL); /* Too long. */
check (strstr (one, "de") == NULL); /* Past end. */
check (strstr (one, "") == one); /* Finding empty. */
(void) strcpy (one, "ababa");
check (strstr (one, "ba") == one + 1); /* Finding first. */
(void) strcpy (one, "");
check (strstr (one, "b") == NULL); /* Empty string. */
check (strstr (one, "") == one); /* Empty in empty string. */
(void) strcpy (one, "bcbca");
check (strstr (one, "bca") == one + 2); /* False start. */
(void) strcpy (one, "bbbcabbca");
check (strstr (one, "bbca") == one + 1); /* With overlap. */
}
static void
test_strnstr (void)
{
char one[256];
check (strnstr ("abcdefg", "g", 5) == NULL); /* Not found. */
}
static void
test_strcasestr (void)
{
char one[256];
check (strcasestr ("aBcd", "z") == NULL); /* Not found. */
check (strcasestr ("AbCd", "abx") == NULL); /* Dead end. */
(void) strcpy (one, "aBcD");
check (strcasestr (one, "c") == one + 2); /* Basic test. */
check (strcasestr (one, "bc") == one + 1); /* Multichar. */
check (strcasestr (one, "d") == one + 3); /* End of string. */
check (strcasestr (one, "cd") == one + 2); /* Tail of string. */
check (strcasestr (one, "abc") == one); /* Beginning. */
check (strcasestr (one, "abcd") == one); /* Exact match. */
check (strcasestr (one, "abcde") == NULL); /* Too long. */
check (strcasestr (one, "de") == NULL); /* Past end. */
check (strcasestr (one, "") == one); /* Finding empty. */
(void) strcpy (one, "abABa");
check (strcasestr (one, "ba") == one + 1); /* Finding first. */
(void) strcpy (one, "");
check (strcasestr (one, "b") == NULL); /* Empty string. */
check (strcasestr (one, "") == one); /* Empty in empty string. */
(void) strcpy (one, "bcbca");
check (strcasestr (one, "bca") == one + 2); /* False start. */
(void) strcpy (one, "bBbCabBcA");
check (strcasestr (one, "bbca") == one + 1); /* With overlap. */
}
static void
test_strcasecmp (void)
{
check (strcasecmp ("", "") == 0); /* Trivial case. */
check (strcasecmp ("a", "a") == 0); /* Identity. */
check (strcasecmp ("aBc", "abc") == 0); /* Multicharacter. */
check (strcasecmp ("aBc", "abcd") < 0); /* Length mismatches. */
check (strcasecmp ("AbcD", "abc") > 0);
check (strcasecmp ("aBcD", "abce") < 0); /* Honest miscompares. */
check (strcasecmp ("Abce", "abcd") > 0);
check (strcasecmp ("A\203", "a") > 0); /* Tricky if char signed. */
check (strcasecmp ("A\203", "a\003") > 0);
if (0) {
char buf1[0x40], buf2[0x40];
int i, j;
for (i = 0; i < 0x10; i++) {
for (j = 0; j < 0x10; j++) {
int k;
for (k = 0; k < 0x3f; k++) {
buf1[j] = '0' ^ (k & 4);
buf2[j] = '4' ^ (k & 4);
}
buf1[i] = buf1[0x3f] = 0;
buf2[j] = buf2[0x3f] = 0;
for (k = 0; k < 0xf; k++) {
int cnum = 0x10+0x10*k+0x100*j+0x1000*i;
check (strcasecmp (buf1+i,buf2+j) == 0);
buf1[i+k] = 'A' + i + k;
buf1[i+k+1] = 0;
check (strcasecmp (buf1+i,buf2+j) > 0);
check (strcasecmp (buf2+j,buf1+i) < 0);
buf2[j+k] = 'B' + i + k;
buf2[j+k+1] = 0;
check (strcasecmp (buf1+i,buf2+j) < 0);
check (strcasecmp (buf2+j,buf1+i) > 0);
buf2[j+k] = 'A' + i + k;
buf1[i] = 'A' + i + 0x80;
check (strcasecmp (buf1+i,buf2+j) > 0);
check (strcasecmp (buf2+j,buf1+i) < 0);
buf1[i] = 'A' + i;
}
}
}
}
}
int main (int argc, char **argv)
{
test_strchr ();
test_strrchr ();
test_strstr ();
test_strnstr ();
test_strcasestr ();
test_strcasecmp ();
return 0;
}
#endif
syntax highlighted by Code2HTML, v. 0.9.1