diff options
author | bobo1on1 <bobo1on1@svn> | 2010-10-15 21:37:04 +0000 |
---|---|---|
committer | bobo1on1 <bobo1on1@svn> | 2010-10-15 21:37:04 +0000 |
commit | c0247e3d05ed81936542de35eb45fc08b803f6fa (patch) | |
tree | 9e4a8ea78b5969d777f156a516a9747c42e73ae4 | |
parent | 57bfbe635239c4c6007e096c6a456e8115a33e8d (diff) |
rewrite of convert_checked(), old code kept segfaulting
git-svn-id: https://xbmc.svn.sourceforge.net/svnroot/xbmc/trunk@34770 568bbfeb-2a22-0410-94d2-cc84cf5bfa90
-rw-r--r-- | xbmc/utils/CharsetConverter.cpp | 132 |
1 files changed, 76 insertions, 56 deletions
diff --git a/xbmc/utils/CharsetConverter.cpp b/xbmc/utils/CharsetConverter.cpp index 300a266712..572ce066fb 100644 --- a/xbmc/utils/CharsetConverter.cpp +++ b/xbmc/utils/CharsetConverter.cpp @@ -95,76 +95,96 @@ size_t iconv_const (void* cd, const char** inbuf, size_t *inbytesleft, template<class INPUT,class OUTPUT> static bool convert_checked(iconv_t& type, int multiplier, const CStdString& strFromCharset, const CStdString& strToCharset, const INPUT& strSource, OUTPUT& strDest) { - size_t retBytes = (size_t)-1; - - if (type == (iconv_t) - 1) + if (type == (iconv_t)-1) { type = iconv_open(strToCharset.c_str(), strFromCharset.c_str()); + if (type == (iconv_t)-1) //iconv_open failed + { + CLog::Log(LOGERROR, "%s iconv_open() failed from %s to %s, errno=%d(%s)", + __FUNCTION__, strFromCharset.c_str(), strToCharset.c_str(), errno, strerror(errno)); + return false; + } } - if (type != (iconv_t) - 1) + if (strSource.IsEmpty()) { - if (strSource.IsEmpty()) - { - strDest.Empty(); - } - else + strDest.Empty(); //empty strings are easy + return true; + } + + //input buffer for iconv() is the buffer from strSource, but without the '\0' at the end + size_t inBufSize = strSource.length() * sizeof(strSource[0]); + const char* inBuf = (const char*)strSource.c_str(); + + //allocate output buffer for iconv() + size_t outBufSize = strSource.length() * multiplier; + char* outBuf = (char*)malloc(outBufSize); + + size_t inBytesAvail = inBufSize; //how many bytes iconv() can read + size_t outBytesAvail = outBufSize; //how many bytes iconv() can write + const char* inBufStart = inBuf; //where in our input buffer iconv() should start reading + char* outBufStart = outBuf; //where in out output buffer iconv() should start writing + + while(1) + { + //iconv() will update inBufStart, inBytesAvail, outBufStart and outBytesAvail + size_t returnV = iconv_const(type, &inBufStart, &inBytesAvail, &outBufStart, &outBytesAvail); + + if (returnV == (size_t)-1) { - size_t inBytes = (strSource.length() + 1)*sizeof(strSource[0]); - size_t outBytes = (strSource.length() + 1)*multiplier; - size_t buf_size = outBytes; - const char *src = (const char*)strSource.c_str(); - char *outbuf = (char*)malloc(buf_size); - int dest_len = 0; - bool convert_done = false; - - while (!convert_done) + if (errno == E2BIG) //output buffer is not big enough { - char *dst = outbuf + dest_len; - retBytes = iconv_const(type, &src, &inBytes, &dst, &outBytes); - int errno_save = errno; - dest_len = dst - outbuf; - if (retBytes != (size_t)-1) - { - if ((retBytes = iconv_const(type, NULL, NULL, &dst, &outBytes)) == (size_t)-1) - { - CLog::Log(LOGERROR, "%s failed cleanup", __FUNCTION__); - } - convert_done = true; - } - else + //save where iconv() ended converting, realloc might make outBufStart invalid + size_t bytesConverted = outBufSize - outBytesAvail; + + //make buffer twice as big + outBufSize *= 2; + char* newBuf = (char*)realloc(outBuf, outBufSize); + if (!newBuf) { - if (errno_save != E2BIG) - { - CLog::Log(LOGERROR, "%s failed from %s to %s, errno=%d", __FUNCTION__, strFromCharset.c_str(), strToCharset.c_str(), errno_save); - convert_done = true; - } - else - { - buf_size += 512; - outBytes += 512; - char *newbuf = (char*)realloc(outbuf, buf_size); - if (!newbuf) - { - CLog::Log(LOGERROR, "%s realloc failed", __FUNCTION__); - convert_done = true; - retBytes = (size_t)-1; - } - else - outbuf = newbuf; - } + CLog::Log(LOGERROR, "%s realloc failed with buffer=%p size=%u errno=%d(%s)", + __FUNCTION__, outBuf, outBufSize, errno, strerror(errno)); + free(outBuf); + return false; } + outBuf = newBuf; + + //update the buffer pointer and counter + outBufStart = outBuf + bytesConverted; + outBytesAvail = outBufSize - bytesConverted; + + //continue in the loop and convert the rest } - if (retBytes != (size_t)-1) + else //iconv() had some other error { - char *p = (char*)strDest.GetBuffer(dest_len); - memcpy(p, outbuf, dest_len); - strDest.ReleaseBuffer(); + CLog::Log(LOGERROR, "%s iconv() failed from %s to %s, errno=%d(%s)", + __FUNCTION__, strFromCharset.c_str(), strToCharset.c_str(), errno, strerror(errno)); + free(outBuf); + return false; } - free(outbuf); + } + else + { + //if you know what this is for, please leave a useful comment + returnV = iconv_const(type, NULL, NULL, &outBufStart, &outBytesAvail); + if (returnV == (size_t)-1) + CLog::Log(LOGERROR, "%s failed cleanup errno=%d(%s)", __FUNCTION__, errno, strerror(errno)); + + //we're done + break; } } - return retBytes != (size_t)-1; + + size_t bytesWritten = outBufSize - outBytesAvail; + char* dest = (char*)strDest.GetBuffer(bytesWritten + 1); + + //copy the output from iconv() into the CStdString, and put a '\0' at the end to make it a c-string + memcpy(dest, outBuf, bytesWritten); + dest[bytesWritten] = '\0'; + + strDest.ReleaseBuffer(); + + return true; } template<class INPUT,class OUTPUT> |