]> git.ipfire.org Git - thirdparty/bash.git/blob - lib/sh/casemod.c
83b8ebec204af8261242c18df12a95ed939d054e
[thirdparty/bash.git] / lib / sh / casemod.c
1 /* casemod.c -- functions to change case of strings */
2
3 /* Copyright (C) 2008,2009 Free Software Foundation, Inc.
4
5 This file is part of GNU Bash, the Bourne Again SHell.
6
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.
11
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.
16
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/>.
19 */
20
21 #if defined (HAVE_CONFIG_H)
22 # include <config.h>
23 #endif
24
25 #if defined (HAVE_UNISTD_H)
26 # include <unistd.h>
27 #endif /* HAVE_UNISTD_H */
28
29 #include <stdc.h>
30
31 #include <bashansi.h>
32 #include <bashintl.h>
33 #include <bashtypes.h>
34
35 #include <stdio.h>
36 #include <ctype.h>
37 #include <xmalloc.h>
38
39 #include <shmbutil.h>
40 #include <chartypes.h>
41
42 #include <glob/strmatch.h>
43
44 #define _to_wupper(wc) (iswlower (wc) ? towupper (wc) : (wc))
45 #define _to_wlower(wc) (iswupper (wc) ? towlower (wc) : (wc))
46
47 #if !defined (HANDLE_MULTIBYTE)
48 # define cval(s, i) ((s)[(i)])
49 # define iswalnum(c) (isalnum(c))
50 # define TOGGLE(x) (ISUPPER (x) ? tolower (x) : (TOUPPER (x)))
51 #else
52 # define TOGGLE(x) (iswupper (x) ? towlower (x) : (_to_wupper(x)))
53 #endif
54
55 /* These must agree with the defines in externs.h */
56 #define CASE_NOOP 0x0
57 #define CASE_LOWER 0x01
58 #define CASE_UPPER 0x02
59 #define CASE_CAPITALIZE 0x04
60 #define CASE_UNCAP 0x08
61 #define CASE_TOGGLE 0x10
62 #define CASE_TOGGLEALL 0x20
63
64 extern char *substring __P((char *, int, int));
65
66 #if defined (HANDLE_MULTIBYTE)
67 static wchar_t
68 cval (s, i)
69 char *s;
70 int i;
71 {
72 size_t tmp;
73 wchar_t wc;
74 int l;
75 mbstate_t mps;
76
77 if (MB_CUR_MAX == 1)
78 return ((wchar_t)s[i]);
79 l = strlen (s);
80 if (i >= (l - 1))
81 return ((wchar_t)s[i]);
82 memset (&mps, 0, sizeof (mbstate_t));
83 tmp = mbrtowc (&wc, s + i, l - i, &mps);
84 if (MB_INVALIDCH (tmp) || MB_NULLWCH (tmp))
85 return ((wchar_t)s[i]);
86 return wc;
87 }
88 #endif
89
90 /* Modify the case of characters in STRING matching PAT based on the value of
91 FLAGS. If PAT is null, modify the case of each character */
92 char *
93 sh_modcase (string, pat, flags)
94 const char *string;
95 char *pat;
96 int flags;
97 {
98 int start, next, end;
99 int inword, c, nc, nop, match;
100 char *ret, *s;
101 wchar_t wc;
102 #if defined (HANDLE_MULTIBYTE)
103 wchar_t nwc;
104 char mb[MB_LEN_MAX+1];
105 int mlen;
106 mbstate_t state;
107 #endif
108
109 #if defined (HANDLE_MULTIBYTE)
110 memset (&state, 0, sizeof (mbstate_t));
111 #endif
112
113 start = 0;
114 end = strlen (string);
115
116 ret = (char *)xmalloc (end + 1);
117 strcpy (ret, string);
118
119 inword = 0;
120 while (start < end)
121 {
122 wc = cval (ret, start);
123
124 if (iswalnum (wc) == 0)
125 {
126 inword = 0;
127 ADVANCE_CHAR (ret, end, start);
128 continue;
129 }
130
131 if (pat)
132 {
133 next = start;
134 ADVANCE_CHAR (ret, end, next);
135 s = substring (ret, start, next);
136 match = strmatch (pat, s, FNM_EXTMATCH) != FNM_NOMATCH;
137 free (s);
138 if (match == 0)
139 {
140 start = next;
141 inword = 1;
142 continue;
143 }
144 }
145
146 if (flags == CASE_CAPITALIZE)
147 {
148 nop = inword ? CASE_LOWER : CASE_UPPER;
149 inword = 1;
150 }
151 else if (flags == CASE_UNCAP)
152 {
153 nop = inword ? CASE_UPPER : CASE_LOWER;
154 inword = 1;
155 }
156 else if (flags == CASE_TOGGLE)
157 {
158 nop = inword ? CASE_NOOP : CASE_TOGGLE;
159 inword = 1;
160 }
161 else
162 nop = flags;
163
164 if (MB_CUR_MAX == 1 || isascii (wc))
165 {
166 switch (nop)
167 {
168 default:
169 case CASE_NOOP: nc = wc; break;
170 case CASE_UPPER: nc = TOUPPER (wc); break;
171 case CASE_LOWER: nc = TOLOWER (wc); break;
172 case CASE_TOGGLEALL:
173 case CASE_TOGGLE: nc = TOGGLE (wc); break;
174 }
175 ret[start] = nc;
176 }
177 #if defined (HANDLE_MULTIBYTE)
178 else
179 {
180 mbrtowc (&wc, string + start, end - start, &state);
181 switch (nop)
182 {
183 default:
184 case CASE_NOOP: nwc = wc; break;
185 case CASE_UPPER: nwc = TOUPPER (wc); break;
186 case CASE_LOWER: nwc = TOLOWER (wc); break;
187 case CASE_TOGGLEALL:
188 case CASE_TOGGLE: nwc = TOGGLE (wc); break;
189 }
190 if (nwc != wc) /* just skip unchanged characters */
191 {
192 mlen = wcrtomb (mb, nwc, &state);
193 if (mlen > 0)
194 mb[mlen] = '\0';
195 /* Assume the same width */
196 strncpy (ret + start, mb, mlen);
197 }
198 }
199 #endif
200
201 /* This assumes that the upper and lower case versions are the same width. */
202 ADVANCE_CHAR (ret, end, start);
203 }
204
205 return ret;
206 }