1 /*******************************************************************************
2 
3         copyright:      Copyright (c) 2006 Kris Bell. All rights reserved
4 
5         license:        BSD style: $(LICENSE)
6 
7         version:        Dec 2006: Initial release
8 
9         author:         Kris
10 
11         Placeholder for a selection of ASCII utilities. These generally will
12         not work with utf8, and cannot be easily extended to utf16 or utf32
13         
14 *******************************************************************************/
15 
16 module tango.text.Ascii;
17 
18 version (Win32)
19         {
20         private extern (C) int memicmp (in char *, in char *, uint);
21         private extern (C) int memcmp (in char *, in char *, uint);
22         }
23 
24 version (Posix)
25         {
26         private extern (C) int memcmp (in char *, in char *, uint);
27         private extern (C) int strncasecmp (in char *, in char*, uint);
28         private alias strncasecmp memicmp;
29         }
30 
31 /******************************************************************************
32 
33         Convert to lowercase in-place.
34 
35 ******************************************************************************/
36 
37 char[] toLower (char[] src)
38 {
39         foreach (ref c; src)
40                  if (c>= 'A' && c <= 'Z')
41                      c = cast(char)(c + 32);
42         return src;
43 }
44 
45 /******************************************************************************
46 
47         Convert to lowercase. Returns the converted content in dst.
48 
49 ******************************************************************************/
50 
51 char[] toLower (const(char[]) src, char[] dst)
52 {
53         assert (dst.length >= src.length);
54         dst[0 .. src.length] = src [0 .. $];
55 
56         return toLower(dst [0  .. src.length]);
57 }
58 
59 /******************************************************************************
60 
61         Convert to uppercase in-place.
62 
63 ******************************************************************************/
64 
65 char[] toUpper (char[] src)
66 {
67         foreach (ref c; src)
68                  if (c>= 'a' && c <= 'z')
69                      c = cast(char)(c - 32);
70         return src;
71 }
72 
73 /******************************************************************************
74 
75         Convert to uppercase. Returns the converted content in dst.
76 
77 ******************************************************************************/
78 
79 char[] toUpper (const(char[]) src, char[] dst)
80 {
81         assert (dst.length >= src.length);
82         dst[0 .. src.length] = src [0 .. $];
83 
84         return toUpper(dst [0  .. src.length]);
85 }
86 
87 /******************************************************************************
88 
89         Compare two char[] ignoring case. Returns 0 if equal
90         
91 ******************************************************************************/
92 
93 int icompare (const(char[]) s1, const(char[]) s2)
94 {
95         auto len = s1.length;
96         if (s2.length < len)
97             len = s2.length;
98 
99         auto result = memicmp (s1.ptr, s2.ptr, cast(int)len);
100 
101         if (result is 0)
102             result = cast(int)s1.length - cast(int)s2.length;
103         return result;
104 }
105 
106 
107 /******************************************************************************
108 
109         Compare two char[] with case. Returns 0 if equal
110         
111 ******************************************************************************/
112 
113 int compare (const(char[]) s1, const(char[]) s2)
114 {
115         auto len = s1.length;
116         if (s2.length < len)
117             len = s2.length;
118 
119         auto result = memcmp (s1.ptr, s2.ptr, cast(int)len);
120 
121         if (result is 0)
122             result = cast(int)s1.length - cast(int)s2.length;
123         return result;
124 }
125 
126 
127 
128 /******************************************************************************
129 
130         Return the index position of a text pattern within src, or
131         src.length upon failure.
132 
133         This is a case-insensitive search (with thanks to Nietsnie)
134         
135 ******************************************************************************/
136 
137 size_t isearch (in char[] src, in char[] pattern)
138 {
139         __gshared immutable char[] _caseMap = 
140                 [ 
141                 '\000','\001','\002','\003','\004','\005','\006','\007',
142                 '\010','\011','\012','\013','\014','\015','\016','\017',
143                 '\020','\021','\022','\023','\024','\025','\026','\027',
144                 '\030','\031','\032','\033','\034','\035','\036','\037',
145                 '\040','\041','\042','\043','\044','\045','\046','\047',
146                 '\050','\051','\052','\053','\054','\055','\056','\057',
147                 '\060','\061','\062','\063','\064','\065','\066','\067',
148                 '\070','\071','\072','\073','\074','\075','\076','\077',
149                 '\100','\141','\142','\143','\144','\145','\146','\147',
150                 '\150','\151','\152','\153','\154','\155','\156','\157',
151                 '\160','\161','\162','\163','\164','\165','\166','\167',
152                 '\170','\171','\172','\133','\134','\135','\136','\137',
153                 '\140','\141','\142','\143','\144','\145','\146','\147',
154                 '\150','\151','\152','\153','\154','\155','\156','\157',
155                 '\160','\161','\162','\163','\164','\165','\166','\167',
156                 '\170','\171','\172','\173','\174','\175','\176','\177',
157                 '\200','\201','\202','\203','\204','\205','\206','\207',
158                 '\210','\211','\212','\213','\214','\215','\216','\217',
159                 '\220','\221','\222','\223','\224','\225','\226','\227',
160                 '\230','\231','\232','\233','\234','\235','\236','\237',
161                 '\240','\241','\242','\243','\244','\245','\246','\247',
162                 '\250','\251','\252','\253','\254','\255','\256','\257',
163                 '\260','\261','\262','\263','\264','\265','\266','\267',
164                 '\270','\271','\272','\273','\274','\275','\276','\277',
165                 '\300','\341','\342','\343','\344','\345','\346','\347',
166                 '\350','\351','\352','\353','\354','\355','\356','\357',
167                 '\360','\361','\362','\363','\364','\365','\366','\367',
168                 '\370','\371','\372','\333','\334','\335','\336','\337',
169                 '\340','\341','\342','\343','\344','\345','\346','\347',
170                 '\350','\351','\352','\353','\354','\355','\356','\357',
171                 '\360','\361','\362','\363','\364','\365','\366','\367',
172                 '\370','\371','\372','\373','\374','\375','\376','\377',
173                 ];  
174 
175 
176         assert(src.ptr);
177         assert(pattern.ptr);
178 
179         for (int i1=0, i2; i1 <= cast(int)(src.length - pattern.length); ++i1)
180             {   
181             for (i2=0; i2 < pattern.length; ++i2)
182                  if (_caseMap[src[i1 + i2]] != _caseMap[pattern[i2]])
183                      break;
184 
185             if (i2 is pattern.length)
186                 return i1;
187             }   
188         return src.length;
189 }
190 
191 
192 
193 /******************************************************************************
194 
195 ******************************************************************************/
196 
197 debug (UnitTest)
198 {       
199         unittest
200         {
201         char[20] tmp;
202         
203         assert (toLower("1bac", tmp) == "1bac");
204         assert (toLower("1BAC", tmp) == "1bac");
205         assert (toUpper("1bac", tmp) == "1BAC");
206         assert (toUpper("1BAC", tmp) == "1BAC");
207         assert (icompare ("ABC", "abc") is 0);
208         assert (icompare ("abc", "abc") is 0);
209         assert (icompare ("abcd", "abc") > 0);
210         assert (icompare ("abc", "abcd") < 0);
211         assert (icompare ("ACC", "abc") > 0);
212 
213         assert (isearch ("ACC", "abc") is 3);
214         assert (isearch ("ACC", "acc") is 0);
215         assert (isearch ("aACC", "acc") is 1);
216         }
217 }
218 
219 debug (Ascii)
220 {
221         void main() {}
222 }