Subversion Repositories Programming Utils

Rev

Rev 114 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
113 rm5248 1
/*
2
 * Licensed to the Apache Software Foundation (ASF) under one or more
3
 * contributor license agreements.  See the NOTICE file distributed with
4
 * this work for additional information regarding copyright ownership.
5
 * The ASF licenses this file to You under the Apache License, Version 2.0
6
 * (the "License"); you may not use this file except in compliance with
7
 * the License.  You may obtain a copy of the License at
8
 *
9
 *      http://www.apache.org/licenses/LICENSE-2.0
10
 *
11
 * Unless required by applicable law or agreed to in writing, software
12
 * distributed under the License is distributed on an "AS IS" BASIS,
13
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
 * See the License for the specific language governing permissions and
15
 * limitations under the License.
16
 */
114 rm5248 17
#include <cstring>
113 rm5248 18
#include <log4cxx/logstring.h>
19
#include <log4cxx/helpers/charsetencoder.h>
20
#include <log4cxx/helpers/bytebuffer.h>
21
#include <log4cxx/helpers/exception.h>
22
#include <log4cxx/helpers/stringhelper.h>
23
#include <log4cxx/helpers/transcoder.h>
24
#if !defined(LOG4CXX)
25
#define LOG4CXX 1
26
#endif
27
#include <log4cxx/private/log4cxx_private.h>
28
#include <log4cxx/helpers/mutex.h>
29
#include <log4cxx/helpers/synchronized.h>
114 rm5248 30
#include <stdlib.h>
113 rm5248 31
 
114 rm5248 32
#ifndef LOG4CXX_QT
33
#include <apr_xlate.h>
34
#include <apr_portable.h>
35
#endif
36
 
113 rm5248 37
using namespace log4cxx;
38
using namespace log4cxx::helpers;
39
 
40
IMPLEMENT_LOG4CXX_OBJECT(CharsetEncoder)
41
 
42
namespace log4cxx
43
{
44
 
45
        namespace helpers {
46
 
47
#if APR_HAS_XLATE
48
          /**
49
          * A character encoder implemented using apr_xlate.
50
          */
51
          class APRCharsetEncoder : public CharsetEncoder
52
          {
53
          public:
54
              APRCharsetEncoder(const LogString& topage) : pool(), mutex(pool) {
55
#if LOG4CXX_LOGCHAR_IS_WCHAR
56
                  const char* frompage = "WCHAR_T";
57
#endif
58
#if LOG4CXX_LOGCHAR_IS_UTF8
59
                  const char* frompage = "UTF-8";
60
#endif
61
#if LOG4CXX_LOGCHAR_IS_UNICHAR
62
                  const char* frompage = "UTF-16";
63
#endif
64
                  std::string tpage(Transcoder::encodeCharsetName(topage));
65
                  apr_status_t stat = apr_xlate_open(&convset,
66
                     tpage.c_str(),
67
                     frompage,
68
                     pool.getAPRPool());
69
                  if (stat != APR_SUCCESS) {
70
                     throw IllegalArgumentException(topage);
71
                  }
72
              }
73
 
74
              virtual ~APRCharsetEncoder() {
75
              }
76
 
77
              virtual log4cxx_status_t encode(const LogString& in,
78
                    LogString::const_iterator& iter,
79
                    ByteBuffer& out) {
80
                      apr_status_t stat;
81
                      size_t outbytes_left = out.remaining();
82
                      size_t initial_outbytes_left = outbytes_left;
83
                      size_t position = out.position();
84
                      if (iter == in.end()) {
85
                        synchronized sync(mutex);
86
                        stat = apr_xlate_conv_buffer(convset, NULL, NULL,
87
                           out.data() + position, &outbytes_left);
88
                      } else {
89
                        LogString::size_type inOffset = (iter - in.begin());
90
                        apr_size_t inbytes_left =
91
                            (in.size() - inOffset) * sizeof(LogString::value_type);
92
                        apr_size_t initial_inbytes_left = inbytes_left;
93
                        {
94
                             synchronized sync(mutex);
95
                             stat = apr_xlate_conv_buffer(convset,
96
                                (const char*) (in.data() + inOffset),
97
                                &inbytes_left,
98
                                out.data() + position,
99
                                &outbytes_left);
100
                        }
101
                        iter += ((initial_inbytes_left - inbytes_left) / sizeof(LogString::value_type));
102
                      }
103
                      out.position(out.position() + (initial_outbytes_left - outbytes_left));
104
                      return stat;
105
              }
106
 
107
          private:
108
                  APRCharsetEncoder(const APRCharsetEncoder&);
109
                  APRCharsetEncoder& operator=(const APRCharsetEncoder&);
110
                  Pool pool;
111
                  Mutex mutex;
112
                  apr_xlate_t *convset;
113
          };
114
#endif
115
 
116
#if LOG4CXX_LOGCHAR_IS_WCHAR && LOG4CXX_HAS_WCSTOMBS
117
          /**
118
           *  A character encoder implemented using wcstombs.
119
          */
120
          class WcstombsCharsetEncoder : public CharsetEncoder
121
          {
122
          public:
123
              WcstombsCharsetEncoder() {
124
              }
125
 
126
           /**
127
            *   Converts a wchar_t to the default external multibyte encoding.
128
            */
129
              log4cxx_status_t encode(const LogString& in,
130
                    LogString::const_iterator& iter,
131
                    ByteBuffer& out) {
114 rm5248 132
                      log4cxx_status_t stat = LOG4CXX_SUCCESS;
113 rm5248 133
 
134
                      if (iter != in.end()) {
135
                         size_t outbytes_left = out.remaining();
136
                         size_t position = out.position();
137
                         std::wstring::size_type inOffset = (iter - in.begin());
138
                         enum { BUFSIZE = 256 };
139
                         wchar_t buf[BUFSIZE];
140
                         size_t chunkSize = BUFSIZE - 1;
141
                         if (chunkSize * MB_LEN_MAX > outbytes_left) {
142
                             chunkSize = outbytes_left / MB_LEN_MAX;
143
                         }
144
                         if (chunkSize > in.length() - inOffset) {
145
                             chunkSize = in.length() - inOffset;
146
                         }
147
                         memset(buf, 0, BUFSIZE * sizeof(wchar_t));
148
                         memcpy(buf,
149
                             in.data() + inOffset,
150
                             chunkSize * sizeof(wchar_t));
151
                         size_t converted = wcstombs(out.data() + position, buf, outbytes_left);
152
 
153
                         if (converted == (size_t) -1) {
114 rm5248 154
                             stat = LOG4CXX_BADARG;
113 rm5248 155
                             //
156
                             //   if unconvertable character was encountered
157
                             //       repeatedly halve source to get fragment that
158
                             //       can be converted
159
                             for(chunkSize /= 2;
160
                                 chunkSize > 0;
161
                                 chunkSize /= 2) {
162
                                 buf[chunkSize] = 0;
163
                                 converted = wcstombs(out.data() + position, buf, outbytes_left);
164
                                 if (converted != (size_t) -1) {
165
                                    iter += chunkSize;
166
                                    out.position(out.position() + converted);
167
                           break;
168
                                 }
169
                             }
170
                         } else {
171
                            iter += chunkSize;
172
                            out.position(out.position() + converted);
173
                         }
174
                      }
175
                      return stat;
176
              }
177
 
178
 
179
 
180
          private:
181
                  WcstombsCharsetEncoder(const WcstombsCharsetEncoder&);
182
                  WcstombsCharsetEncoder& operator=(const WcstombsCharsetEncoder&);
183
          };
184
#endif
185
 
186
 
187
          /**
188
          *   Encodes a LogString to US-ASCII.
189
          */
190
          class USASCIICharsetEncoder : public CharsetEncoder
191
          {
192
          public:
193
              USASCIICharsetEncoder() {
194
              }
195
 
196
              virtual log4cxx_status_t encode(const LogString& in,
197
                    LogString::const_iterator& iter,
198
                    ByteBuffer& out) {
114 rm5248 199
                  log4cxx_status_t stat = LOG4CXX_SUCCESS;
113 rm5248 200
                  if (iter != in.end()) {
201
                      while(out.remaining() > 0 && iter != in.end()) {
202
                          LogString::const_iterator prev(iter);
203
                          unsigned int sv = Transcoder::decode(in, iter);
204
                          if (sv <= 0x7F) {
205
                              out.put((char) sv);
206
                          } else {
207
                              iter = prev;
114 rm5248 208
                              stat = LOG4CXX_BADARG;
113 rm5248 209
                              break;
210
                          }
211
                      }
212
                  }
213
                  return stat;
214
              }
215
 
216
          private:
217
                  USASCIICharsetEncoder(const USASCIICharsetEncoder&);
218
                  USASCIICharsetEncoder& operator=(const USASCIICharsetEncoder&);
219
          };
220
 
221
          /**
222
          *   Converts a LogString to ISO-8859-1.
223
          */
224
          class ISOLatinCharsetEncoder : public CharsetEncoder
225
          {
226
          public:
227
              ISOLatinCharsetEncoder() {
228
              }
229
 
230
              virtual log4cxx_status_t encode(const LogString& in,
231
                    LogString::const_iterator& iter,
232
                    ByteBuffer& out) {
114 rm5248 233
                  log4cxx_status_t stat = LOG4CXX_SUCCESS;
113 rm5248 234
                  if (iter != in.end()) {
235
                      while(out.remaining() > 0 && iter != in.end()) {
236
                          LogString::const_iterator prev(iter);
237
                          unsigned int sv = Transcoder::decode(in, iter);
238
                          if (sv <= 0xFF) {
239
                              out.put((char) sv);
240
                          } else {
241
                              iter = prev;
114 rm5248 242
                              stat = LOG4CXX_BADARG;
113 rm5248 243
                              break;
244
                          }
245
                      }
246
                  }
247
                  return stat;
248
              }
249
 
250
          private:
251
                  ISOLatinCharsetEncoder(const ISOLatinCharsetEncoder&);
252
                  ISOLatinCharsetEncoder& operator=(const ISOLatinCharsetEncoder&);
253
          };
254
 
255
          /**
256
          *   Encodes a LogString to a byte array when the encodings are identical.
257
          */
258
          class TrivialCharsetEncoder : public CharsetEncoder
259
          {
260
          public:
261
              TrivialCharsetEncoder() {
262
              }
263
 
264
 
265
              virtual log4cxx_status_t encode(const LogString& in,
266
                    LogString::const_iterator& iter,
267
                    ByteBuffer& out) {
268
                  if(iter != in.end()) {
269
                 size_t requested = in.length() - (iter - in.begin());
270
                 if (requested > out.remaining()/sizeof(logchar)) {
271
                    requested = out.remaining()/sizeof(logchar);
272
                 }
273
                 memcpy(out.current(),
274
                       (const char*) in.data() + (iter - in.begin()),
275
                      requested * sizeof(logchar));
276
                 iter += requested;
277
                 out.position(out.position() + requested * sizeof(logchar));
278
              }
114 rm5248 279
                  return LOG4CXX_SUCCESS;
113 rm5248 280
              }
281
 
282
          private:
283
                  TrivialCharsetEncoder(const TrivialCharsetEncoder&);
284
                  TrivialCharsetEncoder& operator=(const TrivialCharsetEncoder&);
285
          };
286
 
287
#if LOG4CXX_LOGCHAR_IS_UTF8
288
typedef TrivialCharsetEncoder UTF8CharsetEncoder;
289
#else
290
/**
291
 *  Converts a LogString to UTF-8.
292
 */
293
class UTF8CharsetEncoder : public CharsetEncoder {
294
public:
295
    UTF8CharsetEncoder() {
296
    }
297
 
298
    virtual log4cxx_status_t encode(const LogString& in,
299
         LogString::const_iterator& iter,
300
         ByteBuffer& out) {
301
         while(iter != in.end() && out.remaining() >= 8) {
302
              unsigned int sv = Transcoder::decode(in, iter);
303
              if (sv == 0xFFFF) {
114 rm5248 304
                   return LOG4CXX_BADARG;
113 rm5248 305
              }
306
              Transcoder::encodeUTF8(sv, out);
307
         }
114 rm5248 308
         return LOG4CXX_SUCCESS;
113 rm5248 309
     }
310
 
311
private:
312
     UTF8CharsetEncoder(const UTF8CharsetEncoder&);
313
     UTF8CharsetEncoder& operator=(const UTF8CharsetEncoder&);
314
};
315
#endif
316
 
317
/**
318
 *   Encodes a LogString to UTF16-BE.
319
 */
320
class UTF16BECharsetEncoder : public CharsetEncoder {
321
public:
322
     UTF16BECharsetEncoder() {
323
     }
324
 
325
     virtual log4cxx_status_t encode(const LogString& in,
326
             LogString::const_iterator& iter,
327
             ByteBuffer& out) {
328
             while(iter != in.end() && out.remaining() >= 4) {
329
                  unsigned int sv = Transcoder::decode(in, iter);
330
                  if (sv == 0xFFFF) {
114 rm5248 331
                      return LOG4CXX_BADARG;
113 rm5248 332
                  }
333
                  Transcoder::encodeUTF16BE(sv, out);
334
             }
114 rm5248 335
             return LOG4CXX_SUCCESS;
113 rm5248 336
     }
337
 
338
private:
339
     UTF16BECharsetEncoder(const UTF16BECharsetEncoder&);
340
     UTF16BECharsetEncoder& operator=(const UTF16BECharsetEncoder&);
341
};
342
 
343
/**
344
 *   Encodes a LogString to UTF16-LE.
345
 */
346
class UTF16LECharsetEncoder : public CharsetEncoder {
347
public:
348
     UTF16LECharsetEncoder() {
349
     }
350
 
351
 
352
     virtual log4cxx_status_t encode(const LogString& in,
353
             LogString::const_iterator& iter,
354
             ByteBuffer& out) {
355
             while(iter != in.end() && out.remaining() >= 4) {
356
                  unsigned int sv = Transcoder::decode(in, iter);
357
                  if (sv == 0xFFFF) {
114 rm5248 358
                      return LOG4CXX_BADARG;
113 rm5248 359
                  }
360
                  Transcoder::encodeUTF16LE(sv, out);
361
             }
114 rm5248 362
             return LOG4CXX_SUCCESS;
113 rm5248 363
     }
364
private:
365
     UTF16LECharsetEncoder(const UTF16LECharsetEncoder&);
366
     UTF16LECharsetEncoder& operator=(const UTF16LECharsetEncoder&);
367
};
368
 
369
/**
370
 *    Charset encoder that uses an embedded CharsetEncoder consistent
371
 *     with current locale settings.
372
 */
373
class LocaleCharsetEncoder : public CharsetEncoder {
374
public:
375
      LocaleCharsetEncoder() : pool(), mutex(pool), encoder(), encoding() {
376
      }
377
      virtual ~LocaleCharsetEncoder() {
378
      }
379
      virtual log4cxx_status_t encode(const LogString& in,
380
            LogString::const_iterator& iter,
381
            ByteBuffer& out) {
382
#if !LOG4CXX_CHARSET_EBCDIC
383
            char* current = out.current();
384
            size_t remain = out.remaining();
385
            for(;
386
                iter != in.end() && ((unsigned int) *iter) < 0x80 && remain > 0;
387
                iter++, remain--, current++) {
388
                *current = *iter;
389
            }
390
            out.position(current - out.data());
391
#endif
392
            if (iter != in.end() && out.remaining() > 0) {  
393
                  Pool subpool;
114 rm5248 394
                  //const char* enc = apr_os_locale_encoding(subpool.getAPRPool());
395
                  //RM5248 no idea what this should be
396
                  const char* enc = "UTF-8";
113 rm5248 397
                  {
398
                       synchronized sync(mutex);
399
                       if (enc == 0) {
400
                            if (encoder == 0) {
401
                                encoding = "C";
402
                                encoder = new USASCIICharsetEncoder();
403
                            }
404
                        } else if (encoding != enc) {
405
                            encoding = enc;
406
                            LogString ename;
407
                            Transcoder::decode(encoding, ename);
408
                            try {
409
                                encoder = CharsetEncoder::getEncoder(ename);
410
                            } catch(IllegalArgumentException ex) {
411
                                encoder = new USASCIICharsetEncoder();
412
                            }
413
                        }
414
                  }
415
                  return encoder->encode(in, iter, out);
416
            }
114 rm5248 417
            return LOG4CXX_SUCCESS;
113 rm5248 418
      }
419
 
420
private:
421
      LocaleCharsetEncoder(const LocaleCharsetEncoder&);
422
      LocaleCharsetEncoder& operator=(const LocaleCharsetEncoder&);
423
      Pool pool;
424
      Mutex mutex;
425
      CharsetEncoderPtr encoder;
426
      std::string encoding;
427
};
428
 
429
 
430
        } // namespace helpers
431
 
432
}  //namespace log4cxx
433
 
434
 
435
 
436
CharsetEncoder::CharsetEncoder() {
437
}
438
 
439
CharsetEncoder::~CharsetEncoder() {
440
}
441
 
442
CharsetEncoderPtr CharsetEncoder::getDefaultEncoder() {
443
  static CharsetEncoderPtr encoder(createDefaultEncoder());
444
  //
445
  //  if invoked after static variable destruction
446
  //     (if logging is called in the destructor of a static object)
447
  //     then create a new decoder.
448
  // 
449
  if (encoder == 0) {
450
       return createDefaultEncoder();
451
  }
452
  return encoder;
453
}
454
 
455
CharsetEncoder* CharsetEncoder::createDefaultEncoder() {
456
#if LOG4CXX_CHARSET_UTF8
457
   return new UTF8CharsetEncoder();
458
#elif LOG4CXX_CHARSET_ISO88591
459
   return new ISOLatinCharsetEncoder();
460
#elif LOG4CXX_CHARSET_USASCII
461
   return new USASCIICharsetEncoder();
462
#elif LOG4CXX_LOGCHAR_IS_WCHAR && LOG4CXX_HAS_WCSTOMBS
463
  return new WcstombsCharsetEncoder();
464
#else
465
  return new LocaleCharsetEncoder();
466
#endif
467
}
468
 
469
 
470
CharsetEncoderPtr CharsetEncoder::getUTF8Encoder() {
471
    return new UTF8CharsetEncoder();
472
}
473
 
474
 
475
 
476
CharsetEncoderPtr CharsetEncoder::getEncoder(const LogString& charset) {
477
    if (StringHelper::equalsIgnoreCase(charset, LOG4CXX_STR("UTF-8"), LOG4CXX_STR("utf-8"))) {
478
        return new UTF8CharsetEncoder();
479
    } else if (StringHelper::equalsIgnoreCase(charset, LOG4CXX_STR("C"), LOG4CXX_STR("c")) ||
480
        charset == LOG4CXX_STR("646") ||
481
        StringHelper::equalsIgnoreCase(charset, LOG4CXX_STR("US-ASCII"), LOG4CXX_STR("us-ascii")) ||
482
        StringHelper::equalsIgnoreCase(charset, LOG4CXX_STR("ISO646-US"), LOG4CXX_STR("iso646-US")) ||
483
        StringHelper::equalsIgnoreCase(charset, LOG4CXX_STR("ANSI_X3.4-1968"), LOG4CXX_STR("ansi_x3.4-1968"))) {
484
        return new USASCIICharsetEncoder();
485
    } else if (StringHelper::equalsIgnoreCase(charset, LOG4CXX_STR("ISO-8859-1"), LOG4CXX_STR("iso-8859-1")) ||
486
        StringHelper::equalsIgnoreCase(charset, LOG4CXX_STR("ISO-LATIN-1"), LOG4CXX_STR("iso-latin-1"))) {
487
        return new ISOLatinCharsetEncoder();
488
    } else if (StringHelper::equalsIgnoreCase(charset, LOG4CXX_STR("UTF-16BE"), LOG4CXX_STR("utf-16be"))
489
        || StringHelper::equalsIgnoreCase(charset, LOG4CXX_STR("UTF-16"), LOG4CXX_STR("utf-16"))) {
490
        return new UTF16BECharsetEncoder();
491
    } else if (StringHelper::equalsIgnoreCase(charset, LOG4CXX_STR("UTF-16LE"), LOG4CXX_STR("utf-16le"))) {
492
        return new UTF16LECharsetEncoder();
493
    }
115 rm5248 494
 
495
#ifdef LOG4CXX_QT
496
    //RM5248 not sure what this should be
497
    return new UTF8CharsetEncoder();
498
#elif APR_HAS_XLATE || !defined(_WIN32)
113 rm5248 499
    return new APRCharsetEncoder(charset);
500
#else    
501
    throw IllegalArgumentException(charset);
502
#endif
503
}
504
 
505
 
506
void CharsetEncoder::reset() {
507
}
508
 
509
void CharsetEncoder::flush(ByteBuffer& /* out */ ) {
510
}
511
 
512
 
513
void CharsetEncoder::encode(CharsetEncoderPtr& enc,
514
    const LogString& src,
515
    LogString::const_iterator& iter,
516
    ByteBuffer& dst) {
517
    log4cxx_status_t stat = enc->encode(src, iter, dst);
114 rm5248 518
    if (stat != LOG4CXX_SUCCESS && iter != src.end()) {
113 rm5248 519
#if LOG4CXX_LOGCHAR_IS_WCHAR || LOG4CXX_LOGCHAR_IS_UNICHAR
520
      iter++;
521
#elif LOG4CXX_LOGCHAR_IS_UTF8
522
      //  advance past this character and all continuation characters
523
     while((*(++iter) & 0xC0) == 0x80);
524
#else
525
#error logchar is unrecognized
526
#endif
527
      dst.put(Transcoder::LOSSCHAR);
528
    }
529
}