Declare strcasestr() in the <string.h> replacement, rather than in
[gnulib.git] / lib / strcasestr.c
1 /* Case-insensitive searching in a string.
2    Copyright (C) 2005-2007 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 #include <config.h>
20
21 /* Specification.  */
22 #include <string.h>
23
24 #include <ctype.h>
25 #include <stddef.h>  /* for NULL, in case a nonstandard string.h lacks it */
26
27 #if HAVE_MBRTOWC
28 # include "mbuiter.h"
29 #endif
30
31 #define TOLOWER(Ch) (isupper (Ch) ? tolower (Ch) : (Ch))
32
33 /* Find the first occurrence of NEEDLE in HAYSTACK, using case-insensitive
34    comparison.
35    Note: This function may, in multibyte locales, return success even if
36    strlen (haystack) < strlen (needle) !  */
37 char *
38 strcasestr (const char *haystack, const char *needle)
39 {
40   /* Be careful not to look at the entire extent of haystack or needle
41      until needed.  This is useful because of these two cases:
42        - haystack may be very long, and a match of needle found early,
43        - needle may be very long, and not even a short initial segment of
44          needle may be found in haystack.  */
45 #if HAVE_MBRTOWC
46   if (MB_CUR_MAX > 1)
47     {
48       mbui_iterator_t iter_needle;
49
50       mbui_init (iter_needle, needle);
51       if (mbui_avail (iter_needle))
52         {
53           mbchar_t b;
54           mbui_iterator_t iter_haystack;
55
56           mb_copy (&b, &mbui_cur (iter_needle));
57           if (b.wc_valid)
58             b.wc = towlower (b.wc);
59
60           mbui_init (iter_haystack, haystack);
61           for (;; mbui_advance (iter_haystack))
62             {
63               mbchar_t c;
64
65               if (!mbui_avail (iter_haystack))
66                 /* No match.  */
67                 return NULL;
68
69               mb_copy (&c, &mbui_cur (iter_haystack));
70               if (c.wc_valid)
71                 c.wc = towlower (c.wc);
72               if (mb_equal (c, b))
73                 /* The first character matches.  */
74                 {
75                   mbui_iterator_t rhaystack;
76                   mbui_iterator_t rneedle;
77
78                   memcpy (&rhaystack, &iter_haystack, sizeof (mbui_iterator_t));
79                   mbui_advance (rhaystack);
80
81                   mbui_init (rneedle, needle);
82                   if (!mbui_avail (rneedle))
83                     abort ();
84                   mbui_advance (rneedle);
85
86                   for (;; mbui_advance (rhaystack), mbui_advance (rneedle))
87                     {
88                       if (!mbui_avail (rneedle))
89                         /* Found a match.  */
90                         return (char *) mbui_cur_ptr (iter_haystack);
91                       if (!mbui_avail (rhaystack))
92                         /* No match.  */
93                         return NULL;
94                       if (!mb_caseequal (mbui_cur (rhaystack),
95                                          mbui_cur (rneedle)))
96                         /* Nothing in this round.  */
97                         break;
98                     }
99                 }
100             }
101         }
102       else
103         return (char *) haystack;
104     }
105   else
106 #endif
107     {
108       if (*needle != '\0')
109         {
110           /* Speed up the following searches of needle by caching its first
111              character.  */
112           unsigned char b = TOLOWER ((unsigned char) *needle);
113
114           needle++;
115           for (;; haystack++)
116             {
117               if (*haystack == '\0')
118                 /* No match.  */
119                 return NULL;
120               if (TOLOWER ((unsigned char) *haystack) == b)
121                 /* The first character matches.  */
122                 {
123                   const char *rhaystack = haystack + 1;
124                   const char *rneedle = needle;
125
126                   for (;; rhaystack++, rneedle++)
127                     {
128                       if (*rneedle == '\0')
129                         /* Found a match.  */
130                         return (char *) haystack;
131                       if (*rhaystack == '\0')
132                         /* No match.  */
133                         return NULL;
134                       if (TOLOWER ((unsigned char) *rhaystack)
135                           != TOLOWER ((unsigned char) *rneedle))
136                         /* Nothing in this round.  */
137                         break;
138                     }
139                 }
140             }
141         }
142       else
143         return (char *) haystack;
144     }
145 }