1 /*=========================================================================
2  *
3  *  Copyright Insight Software Consortium
4  *
5  *  Licensed under the Apache License, Version 2.0 (the "License");
6  *  you may not use this file except in compliance with the License.
7  *  You may obtain a copy of the License at
8  *
9  *         http://www.apache.org/licenses/LICENSE-2.0.txt
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  *
17  *=========================================================================*/
18 #ifndef itkInternationalizationIOHelpers_h
19 #define itkInternationalizationIOHelpers_h
20 #include "ITKIOImageBaseExport.h"
21 
22 // This header provides some helper functions to deal with unicode filenames
23 // It is mainly directed towards being able to use utf-8 encoded filenames
24 // on windows.
25 // This should help better dealing with internationalization (a.k.a i18n)
26 
27 #include "itkMacro.h"
28 #include "itkIOConfigure.h"
29 
30 #ifdef ITK_HAVE_UNISTD_H
31 #include <unistd.h> // for unlink
32 #else
33 #include <io.h>
34 #endif
35 
36 #include <cstdio>
37 #include <fcntl.h>
38 #include <iostream>
39 #include <string>
40 #include <sys/stat.h>
41 
42 // Find out how to handle unicode filenames on windows:
43 // * VS>=8.0 has _wopen and _wfopen and can open a (i/o)fstream using a wide
44 // string
45 // * VS7.x and MinGW have _wopen and _wfopen but cannot open a
46 //   (i/o)fstream using a wide string. They can however compile fdstream
47 
48 #if defined( ITK_SUPPORTS_WCHAR_T_FILENAME_CSTYLEIO ) \
49   && ( defined( ITK_SUPPORTS_WCHAR_T_FILENAME_IOSTREAMS_CONSTRUCTORS ) || defined( ITK_SUPPORTS_FDSTREAM_HPP ) )
50 #define LOCAL_USE_WIN32_WOPEN 1
51 #include <windows.h> // required by winnls.h
52 #include <winnls.h>  // for MultiByteToWideChar
53 #else
54 #define LOCAL_USE_WIN32_WOPEN 0
55 #endif
56 
57 #if ( LOCAL_USE_WIN32_WOPEN && defined( ITK_SUPPORTS_WCHAR_T_FILENAME_IOSTREAMS_CONSTRUCTORS ) ) \
58   || ( !LOCAL_USE_WIN32_WOPEN )
59 #define LOCAL_USE_FDSTREAM 0
60 #include <fstream>
61 #else
62 #define LOCAL_USE_FDSTREAM 1
63 #include "itkfdstream/fdstream.hpp"
64 #endif
65 
66 namespace itk
67 {
68 namespace i18n
69 {
70 // Check if the string is correctly encoded
71 #if LOCAL_USE_WIN32_WOPEN
IsStringEncodingValid(const std::string & str)72 inline bool IsStringEncodingValid(const std::string & str)
73 {
74   // Check if the string is really encoded in utf-8 using windows API
75   // MultiByteToWideChar returns 0 if there was a problem during conversion
76   // when given the MB_ERR_INVALID_CHARS flag
77   const int utf16_size = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, str.c_str(),
78                                              static_cast< int >( str.length() ), 0, 0);
79 
80   return ( utf16_size != 0 );
81 }
82 
83 #else
84 inline bool IsStringEncodingValid( const std::string & itkNotUsed(str) )
85 {
86   return true;
87 }
88 
89 #endif
90 
91 #if LOCAL_USE_WIN32_WOPEN
92 // Convert a utf8 encoded std::string to a utf16 encoded wstring on windows
Utf8StringToWString(const std::string & str)93 inline std::wstring Utf8StringToWString(const std::string & str)
94 {
95   // We do not set the MB_ERR_INVALID_CHARS to do an approximate conversion when
96   // non
97   // utf8 characters are found. An alternative would be to throw an exception
98 
99   // First get the size
100   const int utf16_size = MultiByteToWideChar(CP_UTF8, 0, str.c_str(),
101                                              static_cast< int >( str.length() ), 0, 0);
102 
103   // Now do the conversion
104   std::wstring wstr;
105 
106   wstr.resize(utf16_size);
107   MultiByteToWideChar(CP_UTF8, 0, str.c_str(),
108                       static_cast< int >( str.length() ), &wstr[0], utf16_size);
109 
110   return wstr;
111 }
112 
113 #endif
114 
115 // Get a file descriptor from a filename (using utf8 to wstring
116 // on windows if requested) without specifying any specific permissions
I18nOpen(const std::string & str,const int & flags)117 inline int I18nOpen(const std::string & str, const int & flags)
118 {
119 #if LOCAL_USE_WIN32_WOPEN
120   // mingw has _wopen
121   // Convert to utf16
122   const std::wstring str_utf16 = Utf8StringToWString(str);
123   return _wopen(str_utf16.c_str(), flags);
124 #else
125   return open(str.c_str(), flags);
126 #endif
127 }
128 
129 // Get a file descriptor from a filename (using utf8 to wstring
130 // on windows if requested)
I18nOpen(const std::string & str,const int & flags,const int & mode)131 inline int I18nOpen(const std::string & str, const int & flags, const int & mode)
132 {
133 #if LOCAL_USE_WIN32_WOPEN
134   // mingw has _wopen
135   // Convert to utf16
136   const std::wstring str_utf16 = Utf8StringToWString(str);
137   return _wopen(str_utf16.c_str(), flags, mode);
138 #else
139   return open(str.c_str(), flags, mode);
140 #endif
141 }
142 
143 // Reading wrapper around I18nOpen to avoid explicitely specifying the flags
I18nOpenForReading(const std::string & str)144 inline int I18nOpenForReading(const std::string & str)
145 {
146 #if LOCAL_USE_WIN32_WOPEN
147   return I18nOpen(str, _O_RDONLY | _O_BINARY);
148 #else
149   return I18nOpen(str, O_RDONLY);
150 #endif
151 }
152 
153 // Writing wrapper around I18nOpen to avoid explicitely specifying the flags
154 inline int I18nOpenForWriting(const std::string & str, const bool append = false)
155 {
156 #if LOCAL_USE_WIN32_WOPEN
157   if ( !append ) { return I18nOpen(str, _O_WRONLY | _O_CREAT | _O_BINARY, _S_IREAD | _S_IWRITE); }
158   else { return I18nOpen(str, _O_WRONLY | _O_CREAT | _O_APPEND | _O_BINARY, _S_IREAD | _S_IWRITE); }
159 #elif S_IRUSR
160   if ( !append ) { return I18nOpen(str, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR); }
161   else { return I18nOpen(str, O_WRONLY | O_CREAT | O_APPEND, S_IRUSR | S_IWUSR); }
162 #else
163   if ( !append ) { return I18nOpen(str, O_WRONLY | O_CREAT, S_IREAD | S_IWRITE); }
164   else { return I18nOpen(str, O_WRONLY | O_CREAT | O_APPEND, S_IREAD | S_IWRITE); }
165 #endif
166 }
167 
168 // Get a FILE * pointer from a filename (using utf8 to wstring
169 // on windows if requested)
I18nFopen(const std::string & str,const std::string & mode)170 inline FILE * I18nFopen(const std::string & str, const std::string & mode)
171 {
172 #if LOCAL_USE_WIN32_WOPEN
173   // Convert to utf16
174   const std::wstring str_utf16 = Utf8StringToWString(str);
175   const std::wstring mode_utf16 = Utf8StringToWString(mode);
176   return _wfopen( str_utf16.c_str(), mode_utf16.c_str() );
177 #else
178   return fopen( str.c_str(), mode.c_str() );
179 #endif
180 }
181 
182 #if LOCAL_USE_FDSTREAM
183 class I18nOfstream:public std::ostream
184 {
185 public:
186   I18nOfstream(const char *str,
187                std::ios_base::openmode mode = std::ios_base::out):
188     std::ostream(0),
189     m_fd( I18nOpenForWriting(str, ( mode & std::ios::app ) ? true:false) ),
190     m_buf(m_fd)
191   {
192     ///\todo better handle mode flag
193     this->rdbuf(&m_buf);
194   }
195 
~I18nOfstream()196   ~I18nOfstream() { this->close(); }
197 
is_open()198   bool is_open() { return ( m_fd != -1 ); }
199 
close()200   void close()
201   {
202     if ( m_fd != -1 ) { ::close(m_fd); }
203     m_fd = -1;
204   }
205 
206 private:
207   int           m_fd;
208   itk::fdoutbuf m_buf;
209 };
210 
211 class I18nIfstream:public std::istream
212 {
213 public:
214   I18nIfstream(const char *str,
215                std::ios_base::openmode mode = std::ios_base::in):
216     std::istream(0),
217     m_fd( I18nOpenForReading(str) ),
218     m_buf(m_fd)
219   {
220     ///\todo better handle mode flag
221     (void) mode;
222     this->rdbuf(&m_buf);
223   }
224 
~I18nIfstream()225   ~I18nIfstream() { this->close(); }
226 
is_open()227   bool is_open() { return ( m_fd != -1 ); }
228 
close()229   void close()
230   {
231     if ( m_fd != -1 ) { ::close(m_fd); }
232     m_fd = -1;
233   }
234 
235 private:
236   int          m_fd;
237   itk::fdinbuf m_buf;
238 };
239 #elif LOCAL_USE_WIN32_WOPEN
240 class I18nOfstream:public std::ofstream
241 {
242 public:
243   I18nOfstream(const char *str, std::ios_base::openmode mode = std::ios_base::out):
Utf8StringToWString(str)244     std::ofstream(Utf8StringToWString(str).c_str(), mode)
245   {}
246 };
247 
248 class I18nIfstream:public std::ifstream
249 {
250 public:
251   I18nIfstream(const char *str, std::ios_base::openmode mode = std::ios_base::in):
Utf8StringToWString(str)252     std::ifstream(Utf8StringToWString(str).c_str(), mode)
253   {}
254 };
255 #else
256 using I18nOfstream = std::ofstream;
257 using I18nIfstream = std::ifstream;
258 #endif
259 } // end namespace
260 } // end namespace
261 
262 #undef LOCAL_USE_WIN32_WOPEN
263 #undef LOCAL_USE_FDSTREAM
264 
265 #endif  /* itkInternationalizationIOHelpers_h */
266