aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbobo1on1 <bobo1on1@svn>2010-10-15 21:37:04 +0000
committerbobo1on1 <bobo1on1@svn>2010-10-15 21:37:04 +0000
commitc0247e3d05ed81936542de35eb45fc08b803f6fa (patch)
tree9e4a8ea78b5969d777f156a516a9747c42e73ae4
parent57bfbe635239c4c6007e096c6a456e8115a33e8d (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.cpp132
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>