New module 'c-strcasestr'.
[gnulib.git] / lib / c-strcasestr.c
1 /* c-strcasestr.c -- case insensitive substring search in C locale
2    Copyright (C) 2005 Free Software Foundation, Inc.
3    Written by Bruno Haible <bruno@clisp.org>, 2005.
4
5    This program is free software; you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published by
7    the Free Software Foundation; either version 2, or (at your option)
8    any later version.
9
10    This program is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13    GNU General Public License for more details.
14
15    You should have received a copy of the GNU General Public License
16    along with this program; if not, write to the Free Software Foundation,
17    Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
18
19 #ifdef HAVE_CONFIG_H
20 # include <config.h>
21 #endif
22
23 /* Specification.  */
24 #include "c-strcasestr.h"
25
26 #include <stddef.h>
27
28 #include "c-ctype.h"
29
30 /* Find the first occurrence of NEEDLE in HAYSTACK, using case-insensitive
31    comparison.
32    Note: This function may, in multibyte locales, return success even if
33    strlen (haystack) < strlen (needle) !  */
34 char *
35 c_strcasestr (const char *haystack, const char *needle)
36 {
37   /* Be careful not to look at the entire extent of haystack or needle
38      until needed.  This is useful because of these two cases:
39        - haystack may be very long, and a match of needle found early,
40        - needle may be very long, and not even a short initial segment of
41          needle may be found in haystack.  */
42   if (*needle != '\0')
43     {
44       /* Speed up the following searches of needle by caching its first
45          character.  */
46       unsigned char b = c_tolower ((unsigned char) *needle);
47
48       needle++;
49       for (;; haystack++)
50         {
51           if (*haystack == '\0')
52             /* No match.  */
53             return NULL;
54           if (c_tolower ((unsigned char) *haystack) == b)
55             /* The first character matches.  */
56             {
57               const char *rhaystack = haystack + 1;
58               const char *rneedle = needle;
59
60               for (;; rhaystack++, rneedle++)
61                 {
62                   if (*rneedle == '\0')
63                     /* Found a match.  */
64                     return (char *) haystack;
65                   if (*rhaystack == '\0')
66                     /* No match.  */
67                     return NULL;
68                   if (c_tolower ((unsigned char) *rhaystack)
69                       != c_tolower ((unsigned char) *rneedle))
70                     /* Nothing in this round.  */
71                     break;
72                 }
73             }
74         }
75     }
76   else
77     return (char *) haystack;
78 }