]> err.no Git - sope/blob - sope-core/NGExtensions/FdExt.subproj/NSString+Encoding.m
do not escape URL safe chars (like @ or .)
[sope] / sope-core / NGExtensions / FdExt.subproj / NSString+Encoding.m
1 /*
2   Copyright (C) 2000-2004 SKYRIX Software AG
3
4   This file is part of OpenGroupware.org.
5
6   OGo is free software; you can redistribute it and/or modify it under
7   the terms of the GNU Lesser General Public License as published by the
8   Free Software Foundation; either version 2, or (at your option) any
9   later version.
10
11   OGo is distributed in the hope that it will be useful, but WITHOUT ANY
12   WARRANTY; without even the implied warranty of MERCHANTABILITY or
13   FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
14   License for more details.
15
16   You should have received a copy of the GNU Lesser General Public
17   License along with OGo; see the file COPYING.  If not, write to the
18   Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
19   02111-1307, USA.
20 */
21 // $Id$
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 // TODO: should move different implementations to different files ...
35
36 #if NeXT_Foundation_LIBRARY || COCOA_Foundation_LIBRARY
37
38 @interface NSString(Encoding_PrivateAPI)
39 + (NSStringEncoding)stringEncodingForEncodingNamed:(NSString *)encoding;
40 @end
41
42 @implementation NSString(Encoding)
43
44 + (NSStringEncoding)stringEncodingForEncodingNamed:(NSString *)_encoding
45 {
46   CFStringEncoding cfEncoding;
47
48   if(_encoding == nil)
49     return 0;
50
51   _encoding = [_encoding lowercaseString];
52   cfEncoding = CFStringConvertIANACharSetNameToEncoding((CFStringRef)_encoding);
53   if(cfEncoding == kCFStringEncodingInvalidId)
54     return 0;
55   return CFStringConvertEncodingToNSStringEncoding(cfEncoding);
56 }
57
58 + (NSString *)stringWithData:(NSData *)_data
59   usingEncodingNamed:(NSString *)_encoding
60 {
61   NSStringEncoding encoding;
62
63   encoding = [NSString stringEncodingForEncodingNamed:_encoding];
64   return [[[NSString alloc] initWithData:_data encoding:encoding] autorelease];
65 }
66
67 - (NSData *)dataUsingEncodingNamed:(NSString *)_encoding {
68   NSStringEncoding encoding;
69
70   encoding = [NSString stringEncodingForEncodingNamed:_encoding];
71   return [self dataUsingEncoding:encoding];
72 }
73
74 @end /* NSString(Encoding) */
75
76 #else /* ! NeXT_Foundation_LIBRARY */
77
78 @implementation NSString(Encoding)
79
80 #ifdef __linux__
81 static NSString *unicharEncoding = @"UCS-2LE";
82 #else
83 static NSString *unicharEncoding = @"UCS-2-INTERNAL";
84 #endif
85 static int IconvLogEnabled = -1;
86
87 static void checkDefaults(void) {
88   NSUserDefaults *ud;
89   
90   if (IconvLogEnabled != -1) 
91     return;
92   ud = [NSUserDefaults standardUserDefaults];
93   IconvLogEnabled = [ud boolForKey:@"IconvLogEnabled"]?1:0;
94
95 #ifdef __linux__
96   if (NSHostByteOrder() == NS_BigEndian) {
97     NSLog(@"Note: using UCS-2 big endian on Linux.");
98     unicharEncoding = @"UCS-2BE";
99   }
100   else {
101     NSLog(@"Note: using UCS-2 little endian on Linux.");
102     unicharEncoding = @"UCS-2LE";
103   }
104 #endif
105 }
106
107 static char *iconv_wrapper(id self, char *_src, unsigned _srcLen,
108                            NSString *_fromEncode, NSString *_toEncode,
109                            unsigned *outLen_)
110 {
111   iconv_t    type;
112   size_t     inbytesleft, outbytesleft, write, outlen;
113   const char *fromEncode, *toEncode;
114   char       *inbuf, *outbuf, *tm;
115   NSString   *result;
116
117   checkDefaults();
118
119   if (IconvLogEnabled) {
120     [self logWithFormat:@"FromEncode: %@; ToEncode: %@", _fromEncode,
121           _toEncode];
122   }
123
124   _fromEncode = [_fromEncode uppercaseString];
125   _toEncode   = [_toEncode   uppercaseString];
126
127   if (0 && [_fromEncode isEqualToString:_toEncode]) {
128     outlen = _srcLen;
129     outbuf = calloc(sizeof(char), outlen+1);
130
131     memcpy(outbuf, _src, _srcLen);
132     *outLen_ = outlen;
133     
134     return outbuf;
135   }
136   result     = nil;
137   fromEncode = [_fromEncode cString];
138   toEncode   = [_toEncode   cString];
139   
140   type       = iconv_open(toEncode, fromEncode);
141   inbuf      = NULL;
142   outbuf     = NULL;
143   
144   if ((type == (iconv_t)-1)) {
145     [self logWithFormat:@"%s: Could not handle iconv encoding. FromEncoding:%@"
146           @" to encoding:%@", __PRETTY_FUNCTION__, _fromEncode, _toEncode];
147     goto CLEAR_AND_RETURN;
148   }
149   inbytesleft  = _srcLen;
150   inbuf        = _src;
151   outlen       = inbytesleft * 3;
152   outbuf       = calloc(outlen + 1, sizeof(char));;
153   tm           = outbuf;
154   outbytesleft = outlen;
155
156   write = iconv(type, &inbuf, &inbytesleft, &tm, &outbytesleft);
157
158   if (write == (size_t)-1) {
159     if (errno == EILSEQ) {
160       [self logWithFormat:@"Got invalid multibyte sequence. ToEncode: %@"
161             @" FromEncode: %@.", _toEncode, _fromEncode];
162       if (IconvLogEnabled) {
163         [self logWithFormat:@"ByteSequence:\n%s\n", _src];
164       }
165       goto CLEAR_AND_RETURN;
166     }
167     else if (errno == EINVAL) {
168       [self logWithFormat:@"Got incomplete multibyte sequence. ToEncode: %@"
169        @" FromEncode: %@", _toEncode, _fromEncode];
170       if (IconvLogEnabled)
171         [self logWithFormat:@"ByteSequence:\n%s\n", _src];
172       
173     }
174     else if (errno == E2BIG) {
175       [self logWithFormat:
176               @"Got to small outputbuffer (inbytesleft=%d, outbytesleft=%d, "
177               @"outlen=%d). ToEncode: %@ FromEncode: %@", 
178               inbytesleft, outbytesleft, outlen,
179               _toEncode, _fromEncode];
180       if (IconvLogEnabled)
181         [self logWithFormat:@"ByteSequence:\n%s\n", _src];
182       
183       goto CLEAR_AND_RETURN;
184     }
185     else {
186       [self logWithFormat:@"Got unexpected error. ToEncode: %@"
187        @" FromEncode: %@", _toEncode, _fromEncode];
188       goto CLEAR_AND_RETURN;
189     }
190   }
191 #if DEBUG_ICONV
192   NSLogL(@"outlen %d outbytesleft %d", outlen, outbytesleft);
193 #endif
194   if (type)
195     iconv_close(type);
196   
197   *outLen_ = outlen - outbytesleft;
198   
199   return outbuf;
200   
201  CLEAR_AND_RETURN:
202   if (type)
203     iconv_close(type);
204   
205   if (outbuf) {
206     free(outbuf); outbuf = NULL;
207   }
208   return NULL;
209 }
210
211 + (NSString *)stringWithData:(NSData *)_data
212   usingEncodingNamed:(NSString *)_encoding
213 {
214   void      *inbuf, *res;
215   unsigned  len, inbufLen;
216   NSString  *result;
217
218   if (![_encoding length])
219     return nil;
220   
221   inbufLen = [_data length];
222   inbuf    = calloc(sizeof(char), inbufLen + 4);
223   [_data getBytes:inbuf];
224   
225   result = nil;
226   res    = iconv_wrapper(self, inbuf, inbufLen, _encoding, unicharEncoding, &len);
227   if (res) {
228     result = [[NSString alloc] initWithCharacters:res length:(len / 2)];
229     free(res); res = NULL;
230   }
231   if (inbuf) free(inbuf); inbuf = NULL;
232   return [result autorelease];
233 }
234
235 - (NSData *)dataUsingEncodingNamed:(NSString *)_encoding {
236   unichar  *chars;
237   char     *res;
238   unsigned inputLen, resLen;
239   NSData   *data;
240   
241   if (![_encoding length])
242     return nil;
243
244   data     = nil;
245   inputLen = [self length];
246   chars    = calloc(sizeof(unichar), inputLen + 4);
247   [self getCharacters:chars];
248   
249   res = iconv_wrapper(self, (char *)chars, inputLen*2, 
250                       unicharEncoding, _encoding,
251                       &resLen);
252   if (res) data = [NSData dataWithBytes:res length:resLen];
253   
254   if (chars) free(chars); chars = NULL;
255   return data;
256 }
257
258 @end /* NSString(Encoding) */
259
260 #endif /* ! NeXT_Foundation_LIBRARY */