migration from private to rsa
[external/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       0x0000
57 #define CASE_LOWER      0x0001
58 #define CASE_UPPER      0x0002
59 #define CASE_CAPITALIZE 0x0004
60 #define CASE_UNCAP      0x0008
61 #define CASE_TOGGLE     0x0010
62 #define CASE_TOGGLEALL  0x0020
63 #define CASE_UPFIRST    0x0040
64 #define CASE_LOWFIRST   0x0080
65
66 #define CASE_USEWORDS   0x1000          /* modify behavior to act on words in passed string */
67
68 extern char *substring __P((char *, int, int));
69
70 #if defined (HANDLE_MULTIBYTE)
71 static wchar_t
72 cval (s, i)
73      char *s;
74      int i;
75 {
76   size_t tmp;
77   wchar_t wc;
78   int l;
79   mbstate_t mps;  
80
81   if (MB_CUR_MAX == 1)
82     return ((wchar_t)s[i]);
83   l = strlen (s);
84   if (i >= (l - 1))
85     return ((wchar_t)s[i]);
86   memset (&mps, 0, sizeof (mbstate_t));
87   tmp = mbrtowc (&wc, s + i, l - i, &mps);
88   if (MB_INVALIDCH (tmp) || MB_NULLWCH (tmp))
89     return ((wchar_t)s[i]);
90   return wc;  
91 }
92 #endif
93
94 /* Modify the case of characters in STRING matching PAT based on the value of
95    FLAGS.  If PAT is null, modify the case of each character */
96 char *
97 sh_modcase (string, pat, flags)
98      const char *string;
99      char *pat;
100      int flags;
101 {
102   int start, next, end;
103   int inword, c, nc, nop, match, usewords;
104   char *ret, *s;
105   wchar_t wc;
106 #if defined (HANDLE_MULTIBYTE)
107   wchar_t nwc;
108   char mb[MB_LEN_MAX+1];
109   int mlen;
110   size_t m;
111   mbstate_t state;
112 #endif
113
114 #if defined (HANDLE_MULTIBYTE)
115   memset (&state, 0, sizeof (mbstate_t));
116 #endif
117
118   start = 0;
119   end = strlen (string);
120
121   ret = (char *)xmalloc (end + 1);
122   strcpy (ret, string);
123
124   /* See if we are supposed to split on alphanumerics and operate on each word */
125   usewords = (flags & CASE_USEWORDS);
126   flags &= ~CASE_USEWORDS;
127
128   inword = 0;
129   while (start < end)
130     {
131       wc = cval (ret, start);
132
133       if (iswalnum (wc) == 0)
134         {
135           inword = 0;
136           ADVANCE_CHAR (ret, end, start);
137           continue;
138         }
139
140       if (pat)
141         {
142           next = start;
143           ADVANCE_CHAR (ret, end, next);
144           s = substring (ret, start, next);
145           match = strmatch (pat, s, FNM_EXTMATCH) != FNM_NOMATCH;
146           free (s);
147           if (match == 0)
148             {
149               start = next;
150               inword = 1;
151               continue;
152             }
153         }
154
155       /* XXX - for now, the toggling operators work on the individual
156          words in the string, breaking on alphanumerics.  Should I
157          leave the capitalization operators to do that also? */
158       if (flags == CASE_CAPITALIZE)
159         {
160           if (usewords)
161             nop = inword ? CASE_LOWER : CASE_UPPER;
162           else
163             nop = (start > 0) ? CASE_LOWER : CASE_UPPER;
164           inword = 1;
165         }
166       else if (flags == CASE_UNCAP)
167         {
168           if (usewords)
169             nop = inword ? CASE_UPPER : CASE_LOWER;
170           else
171             nop = (start > 0) ? CASE_UPPER : CASE_LOWER;
172           inword = 1;
173         }
174       else if (flags == CASE_UPFIRST)
175         {
176           if (usewords)
177             nop = inword ? CASE_NOOP : CASE_UPPER;
178           else
179             nop = (start > 0) ? CASE_NOOP : CASE_UPPER;
180           inword = 1;
181         }
182       else if (flags == CASE_LOWFIRST)
183         {
184           if (usewords)
185             nop = inword ? CASE_NOOP : CASE_LOWER;
186           else
187             nop = (start > 0) ? CASE_NOOP : CASE_LOWER;
188           inword = 1;
189         }
190       else if (flags == CASE_TOGGLE)
191         {
192           nop = inword ? CASE_NOOP : CASE_TOGGLE;
193           inword = 1;
194         }
195       else
196         nop = flags;
197
198       if (MB_CUR_MAX == 1 || isascii (wc))
199         {
200           switch (nop)
201           {
202           default:
203           case CASE_NOOP:  nc = wc; break;
204           case CASE_UPPER:  nc = TOUPPER (wc); break;
205           case CASE_LOWER:  nc = TOLOWER (wc); break;
206           case CASE_TOGGLEALL:
207           case CASE_TOGGLE: nc = TOGGLE (wc); break;
208           }
209           ret[start] = nc;
210         }
211 #if defined (HANDLE_MULTIBYTE)
212       else
213         {
214           m = mbrtowc (&wc, string + start, end - start, &state);
215           if (MB_INVALIDCH (m))
216             wc = (wchar_t)string[start];
217           else if (MB_NULLWCH (m))
218             wc = L'\0';
219           switch (nop)
220           {
221           default:
222           case CASE_NOOP:  nwc = wc; break;
223           case CASE_UPPER:  nwc = TOUPPER (wc); break;
224           case CASE_LOWER:  nwc = TOLOWER (wc); break;
225           case CASE_TOGGLEALL:
226           case CASE_TOGGLE: nwc = TOGGLE (wc); break;
227           }
228           if  (nwc != wc)       /*  just skip unchanged characters */
229             {
230               mlen = wcrtomb (mb, nwc, &state);
231               if (mlen > 0)
232                 mb[mlen] = '\0';
233               /* Assume the same width */
234               strncpy (ret + start, mb, mlen);
235             }
236         }
237 #endif
238
239       /*  This assumes that the upper and lower case versions are the same width. */
240       ADVANCE_CHAR (ret, end, start);
241     }
242
243   return ret;
244 }