1 /* casemod.c -- functions to change case of strings */
3 /* Copyright (C) 2008,2009 Free Software Foundation, Inc.
5 This file is part of GNU Bash, the Bourne Again SHell.
7 Bash is free software: you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation, either version 3 of the License, or
10 (at your option) any later version.
12 Bash is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with Bash. If not, see <http://www.gnu.org/licenses/>.
21 #if defined (HAVE_CONFIG_H)
25 #if defined (HAVE_UNISTD_H)
27 #endif /* HAVE_UNISTD_H */
33 #include <bashtypes.h>
41 #include <chartypes.h>
44 #include <glob/strmatch.h>
46 #define _to_wupper(wc) (iswlower (wc) ? towupper (wc) : (wc))
47 #define _to_wlower(wc) (iswupper (wc) ? towlower (wc) : (wc))
49 #if !defined (HANDLE_MULTIBYTE)
50 # define cval(s, i) ((s)[(i)])
51 # define iswalnum(c) (isalnum(c))
52 # define TOGGLE(x) (ISUPPER (x) ? tolower (x) : (TOUPPER (x)))
54 # define TOGGLE(x) (iswupper (x) ? towlower (x) : (_to_wupper(x)))
57 /* These must agree with the defines in externs.h */
58 #define CASE_NOOP 0x0000
59 #define CASE_LOWER 0x0001
60 #define CASE_UPPER 0x0002
61 #define CASE_CAPITALIZE 0x0004
62 #define CASE_UNCAP 0x0008
63 #define CASE_TOGGLE 0x0010
64 #define CASE_TOGGLEALL 0x0020
65 #define CASE_UPFIRST 0x0040
66 #define CASE_LOWFIRST 0x0080
68 #define CASE_USEWORDS 0x1000 /* modify behavior to act on words in passed string */
70 extern char *substring __P((char *, int, int));
73 # define UCHAR_MAX TYPE_MAXIMUM(unsigned char)
76 #if defined (HANDLE_MULTIBYTE)
87 if (MB_CUR_MAX == 1 || is_basic (s[i]))
88 return ((wchar_t)s[i]);
91 return ((wchar_t)s[i]);
92 memset (&mps, 0, sizeof (mbstate_t));
93 tmp = mbrtowc (&wc, s + i, l - i, &mps);
94 if (MB_INVALIDCH (tmp) || MB_NULLWCH (tmp))
95 return ((wchar_t)s[i]);
100 /* Modify the case of characters in STRING matching PAT based on the value of
101 FLAGS. If PAT is null, modify the case of each character */
103 sh_modcase (string, pat, flags)
108 int start, next, end;
109 int inword, c, nc, nop, match, usewords;
112 #if defined (HANDLE_MULTIBYTE)
114 char mb[MB_LEN_MAX+1];
120 if (string == 0 || *string == 0)
122 ret = (char *)xmalloc (1);
127 #if defined (HANDLE_MULTIBYTE)
128 memset (&state, 0, sizeof (mbstate_t));
132 end = strlen (string);
134 ret = (char *)xmalloc (end + 1);
135 strcpy (ret, string);
137 /* See if we are supposed to split on alphanumerics and operate on each word */
138 usewords = (flags & CASE_USEWORDS);
139 flags &= ~CASE_USEWORDS;
144 wc = cval (ret, start);
146 if (iswalnum (wc) == 0)
150 ADVANCE_CHAR (ret, end, start);
158 ADVANCE_CHAR (ret, end, next);
159 s = substring (ret, start, next);
160 match = strmatch (pat, s, FNM_EXTMATCH) != FNM_NOMATCH;
170 /* XXX - for now, the toggling operators work on the individual
171 words in the string, breaking on alphanumerics. Should I
172 leave the capitalization operators to do that also? */
173 if (flags == CASE_CAPITALIZE)
176 nop = inword ? CASE_LOWER : CASE_UPPER;
178 nop = (start > 0) ? CASE_LOWER : CASE_UPPER;
181 else if (flags == CASE_UNCAP)
184 nop = inword ? CASE_UPPER : CASE_LOWER;
186 nop = (start > 0) ? CASE_UPPER : CASE_LOWER;
189 else if (flags == CASE_UPFIRST)
192 nop = inword ? CASE_NOOP : CASE_UPPER;
194 nop = (start > 0) ? CASE_NOOP : CASE_UPPER;
197 else if (flags == CASE_LOWFIRST)
200 nop = inword ? CASE_NOOP : CASE_LOWER;
202 nop = (start > 0) ? CASE_NOOP : CASE_LOWER;
205 else if (flags == CASE_TOGGLE)
207 nop = inword ? CASE_NOOP : CASE_TOGGLE;
213 /* Need to check UCHAR_MAX since wc may have already been converted to a
214 wide character by cval() */
215 if (MB_CUR_MAX == 1 || (wc <= UCHAR_MAX && is_basic ((int)wc)))
221 case CASE_NOOP: nc = wc; break;
222 case CASE_UPPER: nc = TOUPPER (wc); break;
223 case CASE_LOWER: nc = TOLOWER (wc); break;
225 case CASE_TOGGLE: nc = TOGGLE (wc); break;
229 #if defined (HANDLE_MULTIBYTE)
232 m = mbrtowc (&wc, string + start, end - start, &state);
233 if (MB_INVALIDCH (m))
235 wc = (unsigned char)string[start];
238 else if (MB_NULLWCH (m))
243 case CASE_NOOP: nwc = wc; break;
244 case CASE_UPPER: nwc = _to_wupper (wc); break;
245 case CASE_LOWER: nwc = _to_wlower (wc); break;
247 case CASE_TOGGLE: nwc = TOGGLE (wc); break;
249 if (nwc != wc) /* just skip unchanged characters */
251 mlen = wcrtomb (mb, nwc, &state);
254 /* Assume the same width */
255 strncpy (ret + start, mb, mlen);
260 /* This assumes that the upper and lower case versions are the same width. */
261 ADVANCE_CHAR (ret, end, start);