Back to index

lightning-sunbird  0.9+nobinonly
nsNativeUConvService.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is mozilla.org code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 2003
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either of the GNU General Public License Version 2 or later (the "GPL"),
00026  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027  * in which case the provisions of the GPL or the LGPL are applicable instead
00028  * of those above. If you wish to allow use of your version of this file only
00029  * under the terms of either the GPL or the LGPL, and not to allow others to
00030  * use your version of this file under the terms of the MPL, indicate your
00031  * decision by deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL or the LGPL. If you do not delete
00033  * the provisions above, a recipient may use your version of this file under
00034  * the terms of any one of the MPL, the GPL or the LGPL.
00035  *
00036  * ***** END LICENSE BLOCK ***** */
00037 
00038 
00039 #ifdef MOZ_USE_NATIVE_UCONV
00040 #include "nsString.h"
00041 #include "nsIGenericFactory.h"
00042 
00043 #include "nsINativeUConvService.h"
00044 
00045 #include "nsIUnicodeDecoder.h"
00046 #include "nsIUnicodeEncoder.h"
00047 #include "nsICharRepresentable.h"
00048 
00049 #include "nsNativeUConvService.h"
00050 
00051 #include <nl_types.h> // CODESET
00052 #include <langinfo.h> // nl_langinfo
00053 #include <iconv.h>    // iconv_open, iconv, iconv_close
00054 #include <errno.h>
00055 
00056 
00057 class IConvAdaptor : public nsIUnicodeDecoder, 
00058                      public nsIUnicodeEncoder, 
00059                      public nsICharRepresentable
00060 {
00061 public:
00062     IConvAdaptor();
00063     virtual ~IConvAdaptor();
00064     
00065     nsresult Init(const char* from, const char* to);
00066     
00067     NS_DECL_ISUPPORTS
00068     
00069     // Decoder methods:
00070     
00071     NS_IMETHOD Convert(const char * aSrc, 
00072                        PRInt32 * aSrcLength, 
00073                        PRUnichar * aDest, 
00074                        PRInt32 * aDestLength);
00075     
00076     NS_IMETHOD GetMaxLength(const char * aSrc, 
00077                             PRInt32 aSrcLength, 
00078                             PRInt32 * aDestLength);
00079     NS_IMETHOD Reset();
00080     
00081     // Encoder methods:
00082     
00083     NS_IMETHOD Convert(const PRUnichar * aSrc, 
00084                        PRInt32 * aSrcLength, 
00085                        char * aDest, 
00086                        PRInt32 * aDestLength);
00087     
00088     
00089     NS_IMETHOD Finish(char * aDest, PRInt32 * aDestLength);
00090     
00091     NS_IMETHOD GetMaxLength(const PRUnichar * aSrc, 
00092                             PRInt32 aSrcLength, 
00093                             PRInt32 * aDestLength);
00094     
00095     // defined by the Decoder:  NS_IMETHOD Reset();
00096     
00097     NS_IMETHOD SetOutputErrorBehavior(PRInt32 aBehavior, 
00098                                       nsIUnicharEncoder * aEncoder, 
00099                                       PRUnichar aChar);
00100     
00101     NS_IMETHOD FillInfo(PRUint32* aInfo);
00102     
00103     
00104 private:
00105     nsresult ConvertInternal(void * aSrc, 
00106                              PRInt32 * aSrcLength, 
00107                              PRInt32 aSrcCharSize,
00108                              void * aDest, 
00109                              PRInt32 * aDestLength,
00110                              PRInt32 aDestCharSize);
00111     
00112     
00113     iconv_t mConverter;
00114     PRBool    mReplaceOnError;
00115     PRUnichar mReplaceChar;
00116 
00117 #ifdef DEBUG
00118     nsCString mFrom, mTo;
00119 #endif
00120 };
00121 
00122 NS_IMPL_ISUPPORTS3(IConvAdaptor, 
00123                    nsIUnicodeEncoder, 
00124                    nsIUnicodeDecoder,
00125                    nsICharRepresentable)
00126 
00127 IConvAdaptor::IConvAdaptor()
00128 {
00129     mConverter = 0;
00130     mReplaceOnError = PR_FALSE;
00131 }
00132 
00133 IConvAdaptor::~IConvAdaptor()
00134 {
00135     if (mConverter)
00136         iconv_close(mConverter);
00137 }
00138 
00139 nsresult 
00140 IConvAdaptor::Init(const char* from, const char* to)
00141 {
00142 #ifdef DEBUG
00143     mFrom = from;
00144     mTo = to;
00145 #endif
00146 
00147     mConverter = iconv_open(to, from);
00148     if (mConverter == (iconv_t) -1 )    
00149     {
00150 #ifdef DEBUG
00151         printf(" * IConvAdaptor - FAILED Initing: %s ==> %s\n", from, to);
00152 #endif
00153         mConverter = nsnull;
00154         return NS_ERROR_FAILURE;
00155     }
00156     return NS_OK;
00157 }
00158 
00159 // From some charset to ucs2
00160 nsresult 
00161 IConvAdaptor::Convert(const char * aSrc, 
00162                      PRInt32 * aSrcLength, 
00163                      PRUnichar * aDest, 
00164                      PRInt32 * aDestLength)
00165 {
00166     return ConvertInternal( (void*) aSrc, 
00167                             aSrcLength, 
00168                             1,
00169                             (void*) aDest, 
00170                             aDestLength,
00171                             2);
00172 }
00173 
00174 nsresult
00175 IConvAdaptor::GetMaxLength(const char * aSrc, 
00176                           PRInt32 aSrcLength, 
00177                           PRInt32 * aDestLength)
00178 {
00179     if (!mConverter)
00180         return NS_ERROR_UENC_NOMAPPING;
00181 
00182     *aDestLength = aSrcLength*4; // sick
00183 #ifdef DEBUG
00184     printf(" * IConvAdaptor - - GetMaxLength %d ( %s -> %s )\n", *aDestLength, mFrom.get(), mTo.get());
00185 #endif
00186     return NS_OK;
00187 }
00188 
00189 
00190 nsresult 
00191 IConvAdaptor::Reset()
00192 {
00193     const char *zero_char_in_ptr  = NULL;
00194     char       *zero_char_out_ptr = NULL;
00195     size_t      zero_size_in      = 0,
00196                 zero_size_out     = 0;
00197 
00198     iconv(mConverter, 
00199           (char **)&zero_char_in_ptr,
00200           &zero_size_in,
00201           &zero_char_out_ptr,
00202           &zero_size_out);
00203 
00204 #ifdef DEBUG
00205     printf(" * IConvAdaptor - - Reset\n");
00206 #endif
00207     return NS_OK;
00208 }
00209 
00210 
00211 // convert unicode data into some charset.
00212 nsresult 
00213 IConvAdaptor::Convert(const PRUnichar * aSrc, 
00214                      PRInt32 * aSrcLength, 
00215                      char * aDest, 
00216                      PRInt32 * aDestLength)
00217 {
00218     return ConvertInternal( (void*) aSrc, 
00219                             aSrcLength, 
00220                             2,
00221                             (void*) aDest, 
00222                             aDestLength,
00223                             1);
00224 }
00225 
00226 
00227 nsresult 
00228 IConvAdaptor::Finish(char * aDest, PRInt32 * aDestLength)
00229 {
00230     *aDestLength = 0;
00231     return NS_OK;
00232 }
00233 
00234 nsresult 
00235 IConvAdaptor::GetMaxLength(const PRUnichar * aSrc, 
00236                           PRInt32 aSrcLength, 
00237                           PRInt32 * aDestLength)
00238 {
00239     if (!mConverter)
00240         return NS_ERROR_UENC_NOMAPPING;
00241 
00242     *aDestLength = aSrcLength*4; // sick
00243 
00244     return NS_OK;
00245 }
00246 
00247 
00248 nsresult 
00249 IConvAdaptor::SetOutputErrorBehavior(PRInt32 aBehavior, 
00250                                     nsIUnicharEncoder * aEncoder, 
00251                                     PRUnichar aChar)
00252 {
00253 
00254     if (aBehavior != kOnError_Replace) {
00255         mReplaceOnError = PR_TRUE;
00256         mReplaceChar = aChar;
00257         return NS_OK;
00258     }
00259 
00260     NS_WARNING("Uconv Error Behavior not support");
00261     return NS_ERROR_FAILURE;
00262 }
00263 
00264 nsresult 
00265 IConvAdaptor::FillInfo(PRUint32* aInfo)
00266 {
00267 #ifdef DEBUG
00268     printf(" * IConvAdaptor - FillInfo called\n");
00269 #endif
00270     *aInfo = 0;
00271     return NS_OK;
00272 }
00273 
00274 
00275 nsresult 
00276 IConvAdaptor::ConvertInternal(void * aSrc, 
00277                              PRInt32 * aSrcLength, 
00278                              PRInt32 aSrcCharSize,
00279                              void * aDest, 
00280                              PRInt32 * aDestLength,
00281                              PRInt32 aDestCharSize)
00282 {
00283     if (!mConverter) {
00284         NS_WARNING("Converter Not Initialize");
00285         return NS_ERROR_NOT_INITIALIZED;
00286     }
00287     size_t res = 0;
00288     size_t inLeft = (size_t) *aSrcLength * aSrcCharSize;
00289     size_t outLeft = (size_t) *aDestLength * aDestCharSize;
00290     size_t outputAvail = outLeft;
00291 
00292     while (true){
00293 
00294         res = iconv(mConverter, 
00295                     (char**)&aSrc, 
00296                     &inLeft, 
00297                     (char**)&aDest, 
00298                     &outLeft);
00299         
00300         if (res == (size_t) -1) {
00301             // on some platforms (e.g., linux) iconv will fail with
00302             // E2BIG if it cannot convert _all_ of its input.  it'll
00303             // still adjust all of the in/out params correctly, so we
00304             // can ignore this error.  the assumption is that we will
00305             // be called again to complete the conversion.
00306             if ((errno == E2BIG) && (outLeft < outputAvail)) {
00307                 res = 0;
00308                 break;
00309             }
00310             
00311             if (errno == EILSEQ) {
00312 
00313                 if (mReplaceOnError) {
00314                     if (aDestCharSize == 1) {
00315                         (*(char*)aDest) = (char)mReplaceChar;
00316                         aDest = (char*)aDest + sizeof(char);
00317                     }
00318                     else
00319                     {
00320                         (*(PRUnichar*)aDest) = (PRUnichar)mReplaceChar;
00321                         aDest = (PRUnichar*)aDest + sizeof(PRUnichar);
00322                     
00323                     }
00324                     inLeft -= aSrcCharSize;
00325                     outLeft -= aDestCharSize;
00326 
00327 #ifdef DEBUG
00328                     printf(" * IConvAdaptor - replacing char in output  ( %s -> %s )\n", 
00329                            mFrom.get(), mTo.get());
00330 
00331 #endif
00332                     res = 0;
00333                 }
00334             }
00335 
00336             if (res == -1) {
00337 #ifdef DEBUG
00338                 printf(" * IConvAdaptor - Bad input ( %s -> %s )\n", mFrom.get(), mTo.get());
00339 #endif
00340                 return NS_ERROR_UENC_NOMAPPING;
00341             }
00342         }
00343 
00344         if (inLeft <= 0 || outLeft <= 0 || res == -1)
00345             break;
00346     }
00347 
00348 
00349     if (res != (size_t) -1) {
00350 
00351         // xp_iconv deals with how much is remaining in a given buffer
00352         // but what uconv wants how much we read/written already.  So
00353         // we fix it up here.
00354         *aSrcLength  -= (inLeft  / aSrcCharSize);
00355         *aDestLength -= (outLeft / aDestCharSize);
00356         return NS_OK;
00357     }
00358     
00359 #ifdef DEBUG
00360     printf(" * IConvAdaptor - - xp_iconv error( %s -> %s )\n", mFrom.get(), mTo.get());
00361 #endif
00362     Reset();    
00363     return NS_ERROR_UENC_NOMAPPING;
00364 }
00365 
00366 NS_IMPL_ISUPPORTS1(NativeUConvService, nsINativeUConvService)
00367 
00368 NS_IMETHODIMP 
00369 NativeUConvService::GetNativeConverter(const char* from,
00370                                        const char* to,
00371                                        nsISupports** aResult) 
00372 {
00373     *aResult = nsnull;
00374 
00375     IConvAdaptor* ucl = new IConvAdaptor();
00376     if (!ucl)
00377         return NS_ERROR_OUT_OF_MEMORY;
00378 
00379     nsresult rv = ucl->Init(from, to);
00380 
00381     if (NS_SUCCEEDED(rv)) {
00382         NS_ADDREF(*aResult = (nsISupports*)(nsIUnicharEncoder*)ucl);
00383     }
00384 
00385     return rv;
00386 }
00387 #endif