]> err.no Git - sope/blob - sope-core/NGExtensions/FdExt.subproj/NSString+Encoding.m
Drop apache 1 build-dependency
[sope] / sope-core / NGExtensions / FdExt.subproj / NSString+Encoding.m
1 /*
2   Copyright (C) 2000-2007 SKYRIX Software AG
3   Copyright (C) 2007      Helge Hess
4
5   This file is part of SOPE.
6
7   SOPE is free software; you can redistribute it and/or modify it under
8   the terms of the GNU Lesser General Public License as published by the
9   Free Software Foundation; either version 2, or (at your option) any
10   later version.
11
12   SOPE is distributed in the hope that it will be useful, but WITHOUT ANY
13   WARRANTY; without even the implied warranty of MERCHANTABILITY or
14   FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
15   License for more details.
16
17   You should have received a copy of the GNU Lesser General Public
18   License along with SOPE; see the file COPYING.  If not, write to the
19   Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
20   02111-1307, USA.
21 */
22
23 #include <NGExtensions/NSString+Encoding.h>
24 #include <NGExtensions/NSObject+Logs.h>
25 #include "common.h"
26
27 #if NeXT_Foundation_LIBRARY || COCOA_Foundation_LIBRARY
28 #  import <CoreFoundation/CoreFoundation.h>
29 #else
30 #  include <iconv.h>
31 #  import <Foundation/NSByteOrder.h>
32 #endif
33
34 #if GNUSTEP_BASE_LIBRARY
35 #import <GNUstepBase/GSMime.h>
36 #endif
37
38 // TODO: should move different implementations to different files ...
39
40
41 #if NeXT_Foundation_LIBRARY || COCOA_Foundation_LIBRARY
42
43 @interface NSString(Encoding_PrivateAPI)
44 + (NSStringEncoding)stringEncodingForEncodingNamed:(NSString *)encoding;
45 @end
46
47 @implementation NSString(Encoding)
48
49 + (NSStringEncoding)stringEncodingForEncodingNamed:(NSString *)_encoding
50 {
51   CFStringEncoding cfEncoding;
52
53   if(_encoding == nil)
54     return 0;
55
56   _encoding = [_encoding lowercaseString];
57   cfEncoding = CFStringConvertIANACharSetNameToEncoding((CFStringRef)_encoding);
58   if(cfEncoding == kCFStringEncodingInvalidId)
59     return 0;
60   return CFStringConvertEncodingToNSStringEncoding(cfEncoding);
61 }
62
63 + (NSString *)stringWithData:(NSData *)_data
64   usingEncodingNamed:(NSString *)_encoding
65 {
66   NSStringEncoding encoding;
67
68   encoding = [NSString stringEncodingForEncodingNamed:_encoding];
69   return [[[NSString alloc] initWithData:_data encoding:encoding] autorelease];
70 }
71
72 - (NSData *)dataUsingEncodingNamed:(NSString *)_encoding {
73   NSStringEncoding encoding;
74
75   encoding = [NSString stringEncodingForEncodingNamed:_encoding];
76   return [self dataUsingEncoding:encoding];
77 }
78
79 @end /* NSString(Encoding) */
80
81
82 #else /* ! NeXT_Foundation_LIBRARY */
83
84
85 @implementation NSString(Encoding)
86
87 #if GNUSTEP_BASE_LIBRARY
88
89 + (NSStringEncoding)stringEncodingForEncodingNamed:(NSString *)_encoding {
90   return [GSMimeDocument encodingFromCharset:_encoding];
91 }
92
93 #endif
94
95 #if LIB_FOUNDATION_LIBRARY
96
97 + (NSStringEncoding)stringEncodingForEncodingNamed:(NSString *)_encoding {
98   NSString *s  = [_encoding lowercaseString];
99   unsigned len = [s length];
100   
101   if (s == nil)
102     return 0;
103
104   switch(len) {
105   case 4:
106     if ([s isEqualToString:@"utf8"])
107       return NSUTF8StringEncoding;
108     break;
109     
110   case 5:
111     if ([s isEqualToString:@"utf-8"])
112       return NSUTF8StringEncoding;
113     if ([s isEqualToString:@"ascii"])
114       return NSASCIIStringEncoding;
115     break;
116     
117   case 6:
118     if ([s isEqualToString:@"latin1"])
119       return NSISOLatin1StringEncoding;
120     if ([s isEqualToString:@"latin9"])
121       return NSISOLatin9StringEncoding;
122     break;
123
124   case 10:
125     if ([s isEqualToString:@"iso-8859-1"]) 
126       return NSISOLatin1StringEncoding;
127     break;
128     
129   case 11:
130     if ([s isEqualToString:@"iso-8859-15"]) 
131       return NSISOLatin9StringEncoding;
132     break;
133   }
134   
135   NSLog(@"%s: could not derive NSStringEncoding from name: '%@'", _encoding);
136   return 0;
137 }
138
139 #endif
140
141
142 #ifdef __linux__
143 static NSString *unicharEncoding = @"UCS-2LE";
144 #else
145 static NSString *unicharEncoding = @"UCS-2-INTERNAL";
146 #endif
147 static int IconvLogEnabled = -1;
148
149 static void checkDefaults(void) {
150   NSUserDefaults *ud;
151   
152   if (IconvLogEnabled != -1) 
153     return;
154   ud = [NSUserDefaults standardUserDefaults];
155   IconvLogEnabled = [ud boolForKey:@"IconvLogEnabled"]?1:0;
156
157 #ifdef __linux__
158   if (NSHostByteOrder() == NS_BigEndian) {
159     NSLog(@"Note: using UCS-2 big endian on Linux.");
160     unicharEncoding = @"UCS-2BE";
161   }
162   else {
163     NSLog(@"Note: using UCS-2 little endian on Linux.");
164     unicharEncoding = @"UCS-2LE";
165   }
166 #endif
167 }
168
169 static char *iconv_wrapper(id self, char *_src, unsigned _srcLen,
170                            NSString *_fromEncode, NSString *_toEncode,
171                            unsigned *outLen_)
172 {
173   iconv_t    type;
174   size_t     inbytesleft, outbytesleft, write, outlen;
175   const char *fromEncode, *toEncode;
176   char       *inbuf, *outbuf, *tm;
177   NSString   *result;
178
179   checkDefaults();
180
181   if (IconvLogEnabled) {
182     [self logWithFormat:@"FromEncode: %@; ToEncode: %@", _fromEncode,
183           _toEncode];
184   }
185
186   _fromEncode = [_fromEncode uppercaseString];
187   _toEncode   = [_toEncode   uppercaseString];
188
189   if (0 && [_fromEncode isEqualToString:_toEncode]) {
190     outlen = _srcLen;
191     outbuf = calloc(sizeof(char), outlen+1);
192
193     memcpy(outbuf, _src, _srcLen);
194     *outLen_ = outlen;
195     
196     return outbuf;
197   }
198   result     = nil;
199   fromEncode = [_fromEncode cString];
200   toEncode   = [_toEncode   cString];
201   
202   type       = iconv_open(toEncode, fromEncode);
203   inbuf      = NULL;
204   outbuf     = NULL;
205   
206   if ((type == (iconv_t)-1)) {
207     [self logWithFormat:@"%s: Could not handle iconv encoding. FromEncoding:%@"
208           @" to encoding:%@", __PRETTY_FUNCTION__, _fromEncode, _toEncode];
209     goto CLEAR_AND_RETURN;
210   }
211   inbytesleft  = _srcLen;
212   inbuf        = _src;
213   outlen       = inbytesleft * 3;
214   outbuf       = calloc(outlen + 1, sizeof(char));;
215   tm           = outbuf;
216   outbytesleft = outlen;
217
218   write = iconv(type, &inbuf, &inbytesleft, &tm, &outbytesleft);
219
220   if (write == (size_t)-1) {
221     if (errno == EILSEQ) {
222       [self logWithFormat:@"Got invalid multibyte sequence. ToEncode: %@"
223             @" FromEncode: %@.", _toEncode, _fromEncode];
224       if (IconvLogEnabled) {
225         [self logWithFormat:@"ByteSequence:\n%s\n", _src];
226       }
227       goto CLEAR_AND_RETURN;
228     }
229     else if (errno == EINVAL) {
230       [self logWithFormat:@"Got incomplete multibyte sequence. ToEncode: %@"
231        @" FromEncode: %@", _toEncode, _fromEncode];
232       if (IconvLogEnabled)
233         [self logWithFormat:@"ByteSequence:\n%s\n", _src];
234       
235     }
236     else if (errno == E2BIG) {
237       [self logWithFormat:
238               @"Got to small outputbuffer (inbytesleft=%d, outbytesleft=%d, "
239               @"outlen=%d). ToEncode: %@ FromEncode: %@", 
240               inbytesleft, outbytesleft, outlen,
241               _toEncode, _fromEncode];
242       if (IconvLogEnabled)
243         [self logWithFormat:@"ByteSequence:\n%s\n", _src];
244       
245       goto CLEAR_AND_RETURN;
246     }
247     else {
248       [self logWithFormat:@"Got unexpected error. ToEncode: %@"
249        @" FromEncode: %@", _toEncode, _fromEncode];
250       goto CLEAR_AND_RETURN;
251     }
252   }
253 #if DEBUG_ICONV
254   NSLogL(@"outlen %d outbytesleft %d", outlen, outbytesleft);
255 #endif
256   if (type)
257     iconv_close(type);
258   
259   *outLen_ = outlen - outbytesleft;
260   
261   return outbuf;
262   
263  CLEAR_AND_RETURN:
264   if (type)
265     iconv_close(type);
266   
267   if (outbuf) {
268     free(outbuf); outbuf = NULL;
269   }
270   return NULL;
271 }
272
273 + (NSString *)stringWithData:(NSData *)_data
274   usingEncodingNamed:(NSString *)_encoding
275 {
276   void      *inbuf, *res;
277   unsigned  len, inbufLen;
278   NSString  *result;
279
280   if (![_encoding length])
281     return nil;
282   
283   inbufLen = [_data length];
284   inbuf    = calloc(sizeof(char), inbufLen + 4);
285   [_data getBytes:inbuf];
286   
287   result = nil;
288   res    = iconv_wrapper(self, inbuf, inbufLen, _encoding, unicharEncoding, &len);
289   if (res) {
290     result = [[NSString alloc] initWithCharacters:res length:(len / 2)];
291     free(res); res = NULL;
292   }
293   if (inbuf) free(inbuf); inbuf = NULL;
294   return [result autorelease];
295 }
296
297 - (NSData *)dataUsingEncodingNamed:(NSString *)_encoding {
298   unichar  *chars;
299   char     *res;
300   unsigned inputLen, resLen;
301   NSData   *data;
302   
303   if (![_encoding length])
304     return nil;
305
306   data     = nil;
307   inputLen = [self length];
308   chars    = calloc(sizeof(unichar), inputLen + 4);
309   [self getCharacters:chars];
310   
311   res = iconv_wrapper(self, (char *)chars, inputLen*2, 
312                       unicharEncoding, _encoding,
313                       &resLen);
314   if (res) data = [NSData dataWithBytes:res length:resLen];
315   
316   if (chars) free(chars); chars = NULL;
317   return data;
318 }
319
320 @end /* NSString(Encoding) */
321
322 #endif /* ! NeXT_Foundation_LIBRARY */