OSDN Git Service

PATCH: [ 841878 ] Fix wchar to char conversions at save
authorPerry Rapp <elsapo@users.sourceforge.net>
Sat, 15 Nov 2003 18:31:22 +0000 (18:31 +0000)
committerPerry Rapp <elsapo@users.sourceforge.net>
Sat, 15 Nov 2003 18:31:22 +0000 (18:31 +0000)
Src/Common/UniFile.cpp
Src/Common/UniFile.h
Src/Common/unicoder.cpp
Src/Common/unicoder.h
Src/MergeDoc.cpp
Src/MergeDoc.h
Src/readme.txt

index 6abfbc0..ac56bc6 100644 (file)
@@ -2,7 +2,7 @@
  *  @file   UniFile.cpp
  *  @author Perry Rapp, Creator, 2003
  *  @date   Created: 2003-10
- *  @date   Edited:  2003-10-21 (Perry)
+ *  @date   Edited:  2003-11-14 (Perry)
  *
  *  @brief Implementation of Memory-Mapped Unicode enabled file class
  */
@@ -162,7 +162,9 @@ bool UniMemFile::DoOpen(DWORD dwOpenAccess, DWORD dwOpenShareMode, DWORD dwOpenC
 
        if (sizelo == 0)
        {
-               // Empty file (but should be accepted anyway)
+               // Allow opening empty file, but memory mapping doesn't work on such
+               // m_base and m_current are 0 from the Close call above
+               // so ReadString will correctly return empty EOF immediately
                m_lineno = 0;
                return true;
        }
@@ -369,7 +371,8 @@ BOOL UniMemFile::ReadString(CString & line, CString & eol)
                                break;
                        }
                }
-               line = ucr::maketstring(m_current, eolptr-m_current, m_codepage);
+               bool lossy=false;
+               line = ucr::maketstring((LPCSTR)m_current, eolptr-m_current, m_codepage, &lossy);
                if (!eof)
                {
                        eol += (TCHAR)*eolptr;
@@ -378,6 +381,7 @@ BOOL UniMemFile::ReadString(CString & line, CString & eol)
                        ++m_lineno;
                }
                m_current = eolptr + eol.GetLength();
+               // TODO: What do we do if save was lossy ?
                return !eof;
        }
 
index 95eaea0..14868e8 100644 (file)
@@ -1,8 +1,8 @@
 /** 
  *  @file   UniFile.h
  *  @author Perry Rapp, Creator, 2003
- *  @date   Created: 2003-10
- *  @date   Edited:  2003-10-21 (Perry)
+ *  @date   Created: 2003-10 (Perry)
+ *  @date   Edited:  2003-11-15 (Perry)
  *
  *  @brief  Declaration of Memory-Mapped Unicode enabled file class
  */
@@ -32,10 +32,13 @@ public:
 
        virtual CString GetFullyQualifiedPath() const = 0;
 
-       virtual UniError GetLastUniError() const = 0;
+       virtual const UniError & GetLastUniError() const = 0;
 
        virtual bool ReadBom() = 0;
-       virtual int GetUnicoding() = 0;
+       virtual int GetUnicoding() const = 0;
+       virtual void SetUnicoding(int unicoding) = 0;
+
+       virtual int GetCodepage() const = 0;
        virtual void SetCodepage(int codepage) = 0;
 
        virtual BOOL ReadString(CString & line) = 0;
@@ -56,9 +59,8 @@ public:
        virtual const txtstats & GetTxtStats() const = 0;
 };
 
-
 /**
- * @brief Memory-Mapped disk file
+ * @brief Memory-Mapped disk file (read-only access)
  */
 class UniMemFile : public UniFile
 {
@@ -77,10 +79,13 @@ public:
        virtual CString GetFullyQualifiedPath() const { return m_filepath; }
        const CFileStatus & GetFileStatus() const { return m_filestatus; }
 
-       virtual UniError GetLastUniError() const { return m_lastError; }
+       virtual const UniError & GetLastUniError() const { return m_lastError; }
 
        virtual bool ReadBom();
-       virtual int GetUnicoding() { return m_unicoding; }
+       virtual int GetUnicoding() const { return m_unicoding; }
+       virtual void SetUnicoding(int unicoding) { m_unicoding = unicoding; }
+
+       virtual int GetCodepage() const { return m_codepage; }
        virtual void SetCodepage(int codepage) { m_codepage = codepage; }
 
        virtual BOOL ReadString(CString & line);
index 1b8eef2..98daec6 100644 (file)
@@ -2,7 +2,7 @@
  *  @file   unicoder.cpp
  *  @author Perry Rapp, Creator, 2003
  *  @date   Created: 2003-10
- *  @date   Edited:  2003-10-21 (Perry)
+ *  @date   Edited:  2003-11-09 (Perry)
  *
  *  @brief  Implementation of utility unicode conversion routines
  */
@@ -46,6 +46,18 @@ fetch_verinfo()
        f_osvi_fetched = true;
 }
 
+static LPCTSTR f_unicodesetNames[] = { _T("<NONE>"), _T("UCS-2LE"), _T("UCS-2BE"), _T("UTF-8") };
+/**
+ * @brief return string for enum value
+ */
+CString GetUnicodesetName(UNICODESET codeset)
+{
+       if (codeset>=0 && codeset<sizeof(f_unicodesetNames)/sizeof(f_unicodesetNames[0]))
+               return f_unicodesetNames[codeset];
+       else
+               return _T("?");
+}
+
 /**
  * @brief Convert unicode codepoint to UTF-8 byte string
  *
@@ -383,36 +395,77 @@ byteToUnicode (unsigned char ch, UINT codepage)
 }
 
 /**
- * @brief Write src string (TCHAR) to destination buffer in codeset specified.
- *
- * This is designed for use writing to memory-mapped file.
- * NB: for loop is deliberately repeated inside the if statements (for speed)
+ * @brief Get appropriate default codepage for this operating system
  */
-void
-convertToBuffer(const CString & src, LPVOID dest, UNICODESET codeset)
+int
+getDefaultCodepage()
+{
+       // default codepage is CP_THREAD_ACP if available, else CP_ACP
+       static bool vercheck=false;
+       static int defcodepage = CP_ACP;
+       if (!vercheck)
+       {
+               if (!f_osvi_fetched) fetch_verinfo();
+               // Need 2000 or better for CP_THREAD_ACP
+               if (f_osvi.dwMajorVersion>=5)
+                       defcodepage = CP_THREAD_ACP;
+               vercheck = true;
+       }
+       return defcodepage;
+}
+
+/**
+ * @brief Write appropriate BOM (Unicode byte order marker)
+ * returns #bytes written
+ */
+int
+writeBom(LPVOID dest, UNICODESET codeset)
 {
        unsigned char * lpd = reinterpret_cast<unsigned char *>(dest);
+       // write Unicode byte order marker (BOM)
        if (codeset == UCS2LE)
        {
                *lpd++ = 0xFF;
                *lpd++ = 0xFE;
+               return 2;
        }
        else if (codeset == UCS2BE)
        {
                *lpd++ = 0xFE;
                *lpd++ = 0xFF;
+               return 2;
        }
        else if (codeset == UTF8)
        {
                *lpd++ = 0xEF;
                *lpd++ = 0xBB;
                *lpd++ = 0xBF;
+               return 3;
        }
+       return 0;
+}
+
+/**
+ * @brief Write src string (TCHAR) to destination buffer in codeset specified.
+ *
+ * In ANSI build, source string is assumed to be in defcodepage.
+ * This is designed for use writing to memory-mapped file.
+ * NB: Destination is not zero-terminated!
+ */
+int
+convertToBuffer(const CString & src, LPVOID dest, UNICODESET codeset, int codepage)
+{
+       static int defcodepage = getDefaultCodepage();
+
+       unsigned char * lpd = reinterpret_cast<unsigned char *>(dest);
+       unsigned char * start = lpd;
+
 #ifdef _UNICODE
        if (codeset == UCS2LE)
        {
-               CopyMemory(lpd, src, src.GetLength() * 2);
-               return;
+               int nbytes = src.GetLength() * 2;
+               CopyMemory(lpd, src, nbytes);
+               return nbytes;
        }
        else if (codeset == UCS2BE)
        {
@@ -422,6 +475,7 @@ convertToBuffer(const CString & src, LPVOID dest, UNICODESET codeset)
                        *lpd++ = (unsigned char)(u >> 8);
                        *lpd++ = (unsigned char)(u & 0xFF);
                }
+               return lpd - start;
        }
        else if (codeset == UTF8)
        {
@@ -430,24 +484,23 @@ convertToBuffer(const CString & src, LPVOID dest, UNICODESET codeset)
                        UINT u = src[i];
                        ucr::to_utf8_advance(u, lpd);
                }
-       }
-       else if (codeset == NONE)
-       {
-               for (int i=0; i<src.GetLength(); ++i)
-               {
-                       UINT u = src[i];
-                       if (u <= 0xFF)
-                               *lpd++ = (unsigned char)u;
-                       else
-                               *lpd++ = '?';
-               }
+               return lpd - start;
        }
        else
        {
-               ASSERT(0);
+               ASSERT(codeset == NONE); // there aren't any other values in UNICODESET
+               // Write wchars to chars
+               DWORD flags = 0;
+               // Take a swag at the maximum length :(
+               int maxlen = src.GetLength() * 3;
+               BOOL replaced=FALSE;
+               int nbytes = WideCharToMultiByte(codepage, flags, src, src.GetLength(), (char *)lpd, maxlen, NULL, &replaced);
+               // TODO: Ought to output replaced flag to caller
+               return nbytes;
        }
+
 #else
-       // We really need the codeset of the 8-bit source
+       // ANSI build, TCHAR=char
 
        if (codeset == UCS2LE)
        {
@@ -457,6 +510,7 @@ convertToBuffer(const CString & src, LPVOID dest, UNICODESET codeset)
                        *lpd++ = (unsigned char)u;
                        *lpd++ = 0;
                }
+               return lpd - start;
        }
        else if (codeset == UCS2BE)
        {
@@ -466,6 +520,7 @@ convertToBuffer(const CString & src, LPVOID dest, UNICODESET codeset)
                        *lpd++ = 0;
                        *lpd++ = (unsigned char)u;
                }
+               return lpd - start;
        }
        else if (codeset == UTF8)
        {
@@ -474,10 +529,23 @@ convertToBuffer(const CString & src, LPVOID dest, UNICODESET codeset)
                        UINT u = src[i];
                        to_utf8_advance(u, lpd);
                }
+               return lpd - start;
        }
        else
        {
-               ASSERT(0);
+               ASSERT(codeset == NONE); // there aren't any other values in UNICODESET
+
+               if (codepage == defcodepage)
+               {
+                       // trivial case, string is already in the correct codepage
+                       CopyMemory(lpd, (LPCTSTR)src, src.GetLength());
+                       return src.GetLength();
+               }
+               bool lossy=false;
+               // Take a swag at the maximum length :(
+               int maxlen = src.GetLength() * 3;
+               int nbytes = CrossConvert(src, src.GetLength(), (LPSTR)lpd, maxlen, defcodepage, codepage, &lossy);
+               return nbytes;
        }
 #endif
 }
@@ -498,7 +566,9 @@ get_unicode_char(unsigned char * ptr, UNICODESET codeset, int codepage)
                ch = (ptr[0] << 8) + ptr[1];
                break;
        default:
-               ch = (codepage ? byteToUnicode(*ptr, codepage) : byteToUnicode(*ptr));
+               // TODO: How do we recognize valid codepage ?
+               // if not, use byteToUnicode(*ptr)
+               ch = byteToUnicode(*ptr, codepage);
        }
        return ch;
 }
@@ -510,73 +580,97 @@ get_unicode_char(unsigned char * ptr, UNICODESET codeset, int codepage)
  *  In fact, this doesn't even know. Probably going to have to make
  *  two passes, the first with MB_ERR_INVALID_CHARS. Ugh. :(
  */
-CString maketstring(unsigned char * lpd, UINT len, int codepage)
+CString maketstring(LPCSTR lpd, UINT len, int codepage, bool * lossy)
 {
-       static bool vercheck=false;
-       static int defcodepage = CP_ACP;
-       if (!vercheck)
-       {
-               if (!f_osvi_fetched) fetch_verinfo();
-               // Need 2000 or better for CP_THREAD_ACP
-               if (f_osvi.dwMajorVersion>=5)
-                       defcodepage = CP_THREAD_ACP;
-               vercheck = true;
-       }
+       static int defcodepage = getDefaultCodepage();
+
        if (!len) return _T("");
        if (!codepage)
                codepage = defcodepage;
 
+#ifdef UNICODE
+       // Convert input to Unicode, using specified codepage
+       // TCHAR is wchar_t, so convert into CString (str)
+       CString str = lpd; // TODO: fix 2003-11-13
+       DWORD flags = 0;
+       int wlen = len*2+6;
+       LPWSTR wbuff = str.GetBuffer(wlen);
+       int n = MultiByteToWideChar(codepage, flags, (LPCSTR)lpd, len, wbuff, wlen-1);
+       if (n)
+       {
+               str.ReleaseBuffer(n);
+       }
+       else
+       {
+               str = _T("?");
+       }
+       return str;
+
+#else
        if (codepage == defcodepage)
        {
                // trivial case, they want the bytes in the file interpreted in our current codepage
-#ifndef UNICODE
                return lpd;
-#else
-               CString s;
-               for (UINT i=0; i<len; ++i)
-                       s += (wchar_t)(*lpd++);
-               return s;
-#endif
        }
 
+       CString str = CrossConvertToStringA(lpd, len, codepage, defcodepage, lossy);
+       return str;
+#endif
+}
+
+/**
+ * @brief (ANSI build only) Convert from one 8 bit codepage to another
+ */
+#ifndef UNICODE
+CString
+CrossConvertToStringA(LPCSTR src, UINT srclen, int cpin, int cpout, bool * lossy)
+{
+
+       CString str;
+       int wlen = srclen*2+6;
+       int clen = wlen * 2 + 6;
+       LPSTR cbuff = str.GetBuffer(clen);
+       int nbytes = CrossConvert(src, srclen, cbuff, clen, cpin, cpout, lossy);
+       str.ReleaseBuffer(nbytes);
+       return str;
+}
+#endif
+
+/**
+ * @brief Convert from one 8-bit codepage to another
+ *
+ * destsize must be at least 2
+ */
+int
+CrossConvert(LPCSTR src, UINT srclen, LPSTR dest, UINT destsize, int cpin, int cpout, bool * lossy)
+{
+       ASSERT(destsize > 1);
+
        // Convert input to Unicode, using specified codepage
        DWORD flags = 0;
-       int wlen = len*2+6;
+       int wlen = srclen*2+6;
        wchar_t * wbuff = new wchar_t[wlen];
-       int n = MultiByteToWideChar(codepage, flags, (LPCSTR)lpd, len, wbuff, wlen-1);
+       int n = MultiByteToWideChar(cpin, flags, (LPCSTR)src, srclen, wbuff, wlen-1);
        if (!n)
        {
                delete [] wbuff;
-               return _T("?");
+               dest[0] = '?';
+               return 1;
        }
        wbuff[n] = 0; // zero-terminate string
 
-#ifdef UNICODE
-       // wchar_t is TCHAR, so we're done
-       CString str = wbuff;
-       delete [] wbuff;
-       return str;
-#else
        // Now convert to TCHAR (which means defcodepage)
        flags = WC_NO_BEST_FIT_CHARS; // TODO: Think about this
-       CString str;
        wlen = n;
        int clen = wlen * 2 + 6;
-       LPSTR cbuff = str.GetBuffer(clen);
        BOOL defaulted=FALSE;
-       n = WideCharToMultiByte(defcodepage, flags, wbuff, n, cbuff, clen-1, NULL, &defaulted);
-       if (n)
-       {
-               cbuff[n] = 0; // zero-terminate string
-               str.ReleaseBuffer();
-       }
-       else
-       {
-               str = _T("?");
-       }
+       LPSTR cdest = reinterpret_cast<LPSTR>(dest);
+       n = WideCharToMultiByte(cpout, flags, wbuff, n, cdest, destsize-1, NULL, &defaulted);
+       dest[n] = 0;
        delete [] wbuff;
-       return str;
-#endif
+       if (lossy)
+               *lossy = !!defaulted;
+       return n;
 }
 
 } // namespace ucr
index 8c3dc9e..fadcb50 100644 (file)
@@ -2,7 +2,7 @@
  *  @file   unicoder.h
  *  @author Perry Rapp, Creator, 2003
  *  @date   Created: 2003-10
- *  @date   Edited:  2003-10-21 (Perry)
+ *  @date   Edited:  2003-11-13 (Perry)
  *
  *  @brief  Declaration of utility unicode conversion routines
  */ 
@@ -13,6 +13,7 @@
 namespace ucr {
 
 typedef enum { NONE=0, UCS2LE, UCS2BE, UTF8 } UNICODESET;
+CString GetUnicodesetName(UNICODESET codeset);
 
 int Ucs4_to_Utf8(UINT unich, unsigned char * utf8);
 int Utf8len_fromLeadByte(unsigned char ch);
@@ -21,14 +22,20 @@ UINT Utf8len_of_string(const CString & text);
 UINT GetUtf8Char(unsigned char * str);
 int to_utf8_advance(UINT u, unsigned char * &lpd);
 CString maketchar(UINT ch, bool & lossy);
-void convertToBuffer(const CString & src, LPVOID dest, UNICODESET codeset);
+int writeBom(LPVOID dest, UNICODESET codeset);
+int convertToBuffer(const CString & src, LPVOID dest, UNICODESET codeset, int codepage);
 UINT get_unicode_char(unsigned char * ptr, UNICODESET codeset, int codepage=0);
-CString maketstring(unsigned char * lpd, UINT len, int codepage=0);
+CString maketstring(LPCSTR lpd, UINT len, int codepage, bool * lossy);
 CString maketchar(UINT unich, bool & lossy);
 CString maketchar(UINT unich, bool & lossy, UINT codepage);
 UINT byteToUnicode(unsigned char ch);
 UINT byteToUnicode(unsigned char ch, UINT codepage);
+int getDefaultCodepage();
 
+int CrossConvert(LPCSTR src, UINT srclen, LPSTR dest, UINT destsize, int cpin, int cpout, bool * lossy);
+#ifndef UNICODE
+CString CrossConvertToStringA(LPCSTR src, UINT srclen, int cpin, int cpout, bool * lossy);
+#endif
 
 } // namespace ucr
 
index a4f8c32..2569c7d 100644 (file)
@@ -254,11 +254,10 @@ static void SaveBuffForDiff(CMergeDoc::CDiffTextBuffer & buf, const CString & fi
 {
        // we subvert the buffer's memory of the original file encoding
        int temp=buf.m_nSourceEncoding;
+       int unicoding = buf.getUnicoding();
 
-       if (sizeof(TCHAR)>1 
-               || buf.m_nSourceEncoding==-20 // source file was UCS-2LE
-               || buf.m_nSourceEncoding==-21 // source file was UCS-2BE
-               || buf.m_nSourceEncoding==-22) // source file was UTF-8
+       // If Unicode build, or file was in Unicode
+       if (sizeof(TCHAR)>1 || unicoding!=ucr::NONE)
        {
                buf.m_nSourceEncoding = -20; // write as UCS-2LE (for preprocessing)
        }
@@ -1263,18 +1262,9 @@ int CMergeDoc::CDiffTextBuffer::LoadFromFile(LPCTSTR pszFileNameInit, PackingInf
                        nRetVal = FRESULT_OK_IMPURE;
 
                // stash original encoding away
-               switch (pufile->GetUnicoding())
-               {
-               case ucr::UCS2LE:
-                       m_nSourceEncoding = -20;
-                       break;
-               case ucr::UCS2BE:
-                       m_nSourceEncoding = -21;
-                       break;
-               case ucr::UTF8:
-                       m_nSourceEncoding = -22;
-                       break;
-               }
+               m_unicoding = pufile->GetUnicoding();
+               m_codepage = pufile->GetCodepage();
+
                if (pufile->GetTxtStats().nlosses)
                        readOnly = TRUE;
        }
@@ -1326,17 +1316,17 @@ BOOL CMergeDoc::CDiffTextBuffer::SaveToFile (LPCTSTR pszFileName,
        UINT nchars = text.GetLength();
 
        UINT nbytes = -1;
-       if (m_nSourceEncoding == -20)
+       if (m_unicoding == ucr::UCS2LE)
        {
                // UCS-2LE (+ 2 byte BOM)
                nbytes = 2 * nchars + 2;
        }
-       else if (m_nSourceEncoding == -21)
+       else if (m_unicoding == ucr::UCS2BE)
        {
                // UCS-2BE (+ 2 byte BOM)
                nbytes = 2 * nchars + 2;
        }
-       else if (m_nSourceEncoding == -22)
+       else if (m_unicoding == ucr::UTF8)
        {
                // UTF-8 (+ 3 byte BOM)
                nbytes = ucr::Utf8len_of_string(text) + 3;
@@ -1374,24 +1364,18 @@ BOOL CMergeDoc::CDiffTextBuffer::SaveToFile (LPCTSTR pszFileName,
                // Should these Unicode codeset conversions be moved
                // into the iconvert (editlib/cs2cs.*) module ?
 
-               if (m_nSourceEncoding == -20)
-               {
-                       ucr::convertToBuffer(text, fileData.pMapBase, ucr::UCS2LE);
-               }
-               else if (m_nSourceEncoding == -21)
-               {
-                       ucr::convertToBuffer(text, fileData.pMapBase, ucr::UCS2BE);
-               }
-               else if (m_nSourceEncoding == -22)
+               if (m_unicoding != ucr::NONE)
                {
-                       ucr::convertToBuffer(text, fileData.pMapBase, ucr::UTF8);
+                       ucr::writeBom(fileData.pMapBase, (ucr::UNICODESET)m_unicoding);
+                       ucr::convertToBuffer(text, fileData.pMapBase, (ucr::UNICODESET)m_unicoding, m_codepage);
                }
                else 
                {
 #ifdef _UNICODE
-                       // (We're ignoring m_nSourceEncoding here, which ought to be relevant
-                       // altho I don't think it ever gets set right now -- Perry, 2003-09-24)
-                       ucr::convertToBuffer(text, fileData.pMapBase, ucr::NONE);
+                       // WinMerge doesn't use crystal's m_nSourceEncoding
+                       // which anyway never gets set
+                       // WinMerge uses its own buffer's m_unicoding and m_codepage
+                       ucr::convertToBuffer(text, fileData.pMapBase, (ucr::UNICODESET)m_unicoding, m_codepage);
 #else
                        if (m_nSourceEncoding >= 0)
                        {
index 5d62726..20227ee 100644 (file)
@@ -108,6 +108,8 @@ private :
                BOOL FlagIsSet(UINT line, DWORD flag);
                CString m_strTempPath;
                int unpackerSubcode;
+               int m_unicoding; /**< Unicode encoding from ucr::UNICODESET */
+               int m_codepage; /**< @brief 8-bit codepage, if relevant m_unicoding==ucr::NONE */
 
                int NoteCRLFStyleFromBuffer(TCHAR *lpLineBegin, DWORD dwLineLen = 0);
                void ReadLineFromBuffer(TCHAR *lpLineBegin, DWORD dwLineNum, DWORD dwLineLen = 0);
@@ -122,11 +124,15 @@ public :
                BOOL LoadFromFile(LPCTSTR pszFileName, PackingInfo * infoUnpacker, CString filteredFilenames, BOOL & readOnly, int nCrlfStyle, int codepage);
                BOOL SaveToFile (LPCTSTR pszFileName, BOOL bTempFile, PackingInfo * infoUnpacker = NULL,
                                int nCrlfStyle = CRLF_STYLE_AUTOMATIC, BOOL bClearModifiedFlag = TRUE );
+               int getUnicoding() const { return m_unicoding; }
 
                CDiffTextBuffer (CMergeDoc * pDoc, BOOL bLeft)
                {
                        m_pOwnerDoc = pDoc;
                        m_bIsLeft=bLeft;
+                       unpackerSubcode = 0;
+                       m_unicoding = 0;
+                       m_codepage = 0;
                }
                // If line has text (excluding eol), set strLine to text (excluding eol)
                BOOL GetLine( int nLineIndex, CString &strLine ) 
index afeda59..ff256b1 100644 (file)
@@ -1,6 +1,9 @@
 2003-11-15 Perry
  PATCH: [ 838290 ] Move inf creation down in RunFileDiff (cosmetic)
   WinMerge: DiffWrapper.cpp
+ PATCH: [ 841878 ] Fix wchar to char conversions at save
+  WinMerge: MergeDoc.cpp MergeDoc.h
+  common: unicoder.cpp unicoder.h UniFile.cpp UniFile.h
 
 2003-11-15 WinMerge experimental release 2.1.3.9 (cvs)