1/////////////////////////////////////////////////////////////////////////////// 2// Name: src/common/textfile.cpp 3// Purpose: implementation of wxTextFile class 4// Author: Vadim Zeitlin 5// Modified by: 6// Created: 03.04.98 7// RCS-ID: $Id: textfile.cpp 49298 2007-10-21 18:05:49Z SC $ 8// Copyright: (c) 1998 Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr> 9// Licence: wxWindows licence 10/////////////////////////////////////////////////////////////////////////////// 11 12// ============================================================================ 13// headers 14// ============================================================================ 15 16#include "wx/wxprec.h" 17 18#ifdef __BORLANDC__ 19 #pragma hdrstop 20#endif //__BORLANDC__ 21 22#if !wxUSE_FILE || !wxUSE_TEXTBUFFER 23 #undef wxUSE_TEXTFILE 24 #define wxUSE_TEXTFILE 0 25#endif // wxUSE_FILE 26 27#if wxUSE_TEXTFILE 28 29#ifndef WX_PRECOMP 30 #include "wx/string.h" 31 #include "wx/intl.h" 32 #include "wx/file.h" 33 #include "wx/log.h" 34#endif 35 36#include "wx/textfile.h" 37#include "wx/filename.h" 38#include "wx/buffer.h" 39 40// ============================================================================ 41// wxTextFile class implementation 42// ============================================================================ 43 44wxTextFile::wxTextFile(const wxString& strFileName) 45 : wxTextBuffer(strFileName) 46{ 47} 48 49 50// ---------------------------------------------------------------------------- 51// file operations 52// ---------------------------------------------------------------------------- 53 54bool wxTextFile::OnExists() const 55{ 56 return wxFile::Exists(m_strBufferName); 57} 58 59 60bool wxTextFile::OnOpen(const wxString &strBufferName, wxTextBufferOpenMode OpenMode) 61{ 62 wxFile::OpenMode FileOpenMode; 63 64 switch ( OpenMode ) 65 { 66 default: 67 wxFAIL_MSG( _T("unknown open mode in wxTextFile::Open") ); 68 // fall through 69 70 case ReadAccess : 71 FileOpenMode = wxFile::read; 72 break; 73 74 case WriteAccess : 75 FileOpenMode = wxFile::write; 76 break; 77 } 78 79 return m_file.Open(strBufferName.c_str(), FileOpenMode); 80} 81 82 83bool wxTextFile::OnClose() 84{ 85 return m_file.Close(); 86} 87 88 89bool wxTextFile::OnRead(const wxMBConv& conv) 90{ 91 // file should be opened 92 wxASSERT_MSG( m_file.IsOpened(), _T("can't read closed file") ); 93 94 // read the entire file in memory: this is not the most efficient thing to 95 // do but there is no good way to avoid it in Unicode build because if we 96 // read the file block by block we can't convert each block to Unicode 97 // separately (the last multibyte char in the block might be only partially 98 // read and so the conversion would fail) and, as the file contents is kept 99 // in memory by wxTextFile anyhow, it shouldn't be a big problem to read 100 // the file entirely 101 size_t bufSize = 0, 102 bufPos = 0; 103 char block[1024]; 104 wxCharBuffer buf; 105 106 // first determine if the file is seekable or not and so whether we can 107 // determine its length in advance 108 wxFileOffset fileLength; 109 { 110 wxLogNull logNull; 111 fileLength = m_file.Length(); 112 } 113 114 // some non-seekable files under /proc under Linux pretend that they're 115 // seekable but always return 0; others do return an error 116 const bool seekable = fileLength != wxInvalidOffset && fileLength != 0; 117 if ( seekable ) 118 { 119 // we know the required length, so set the buffer size in advance 120 bufSize = fileLength; 121 if ( !buf.extend(bufSize - 1 /* it adds 1 internally */) ) 122 return false; 123 124 // if the file is seekable, also check that we're at its beginning 125 wxASSERT_MSG( m_file.Tell() == 0, _T("should be at start of file") ); 126 } 127 128 for ( ;; ) 129 { 130 ssize_t nRead = m_file.Read(block, WXSIZEOF(block)); 131 132 if ( nRead == wxInvalidOffset ) 133 { 134 // read error (error message already given in wxFile::Read) 135 return false; 136 } 137 138 if ( nRead == 0 ) 139 { 140 // if no bytes have been read, presumably this is a valid-but-empty file 141 if ( bufPos == 0 ) 142 return true; 143 144 // otherwise we've finished reading the file 145 break; 146 } 147 148 if ( seekable ) 149 { 150 // this shouldn't happen but don't overwrite the buffer if it does 151 wxCHECK_MSG( bufPos + nRead <= bufSize, false, 152 _T("read more than file length?") ); 153 } 154 else // !seekable 155 { 156 // for non-seekable files we have to allocate more memory on the go 157 if ( !buf.extend(bufPos + nRead - 1 /* it adds 1 internally */) ) 158 return false; 159 } 160 161 // append to the buffer 162 memcpy(buf.data() + bufPos, block, nRead); 163 bufPos += nRead; 164 } 165 166 if ( !seekable ) 167 { 168 bufSize = bufPos; 169 } 170 171 const wxString str(buf, conv, bufPos); 172 173 // there's no risk of this happening in ANSI build 174#if wxUSE_UNICODE 175 if ( bufSize > 4 && str.empty() ) 176 { 177 wxLogError(_("Failed to convert file \"%s\" to Unicode."), GetName()); 178 return false; 179 } 180#endif // wxUSE_UNICODE 181 182 free(buf.release()); // we don't need this memory any more 183 184 185 // now break the buffer in lines 186 187 // last processed character, we need to know if it was a CR or not 188 wxChar chLast = '\0'; 189 190 // the beginning of the current line, changes inside the loop 191 wxString::const_iterator lineStart = str.begin(); 192 const wxString::const_iterator end = str.end(); 193 for ( wxString::const_iterator p = lineStart; p != end; p++ ) 194 { 195 const wxChar ch = *p; 196 switch ( ch ) 197 { 198 case '\n': 199 // could be a DOS or Unix EOL 200 if ( chLast == '\r' ) 201 { 202 if ( p - 1 >= lineStart ) 203 { 204 AddLine(wxString(lineStart, p - 1), wxTextFileType_Dos); 205 } 206 else 207 { 208 // there were two line endings, so add an empty line: 209 AddLine(wxEmptyString, wxTextFileType_Dos); 210 } 211 } 212 else // bare '\n', Unix style 213 { 214 AddLine(wxString(lineStart, p), wxTextFileType_Unix); 215 } 216 217 lineStart = p + 1; 218 break; 219 220 case '\r': 221 if ( chLast == '\r' ) 222 { 223 if ( p - 1 >= lineStart ) 224 { 225 AddLine(wxString(lineStart, p - 1), wxTextFileType_Mac); 226 } 227 // Mac empty line 228 AddLine(wxEmptyString, wxTextFileType_Mac); 229 lineStart = p + 1; 230 } 231 //else: we don't know what this is yet -- could be a Mac EOL or 232 // start of DOS EOL so wait for next char 233 break; 234 235 default: 236 if ( chLast == '\r' ) 237 { 238 // Mac line termination 239 if ( p - 1 >= lineStart ) 240 { 241 AddLine(wxString(lineStart, p - 1), wxTextFileType_Mac); 242 } 243 else 244 { 245 // there were two line endings, so add an empty line: 246 AddLine(wxEmptyString, wxTextFileType_Mac); 247 } 248 lineStart = p; 249 } 250 } 251 252 chLast = ch; 253 } 254 255 // anything in the last line? 256 if ( lineStart != end ) 257 { 258 // add unterminated last line 259 AddLine(wxString(lineStart, end), wxTextFileType_None); 260 } 261 262 return true; 263} 264 265 266bool wxTextFile::OnWrite(wxTextFileType typeNew, const wxMBConv& conv) 267{ 268 wxFileName fn = m_strBufferName; 269 270 // We do NOT want wxPATH_NORM_CASE here, or the case will not 271 // be preserved. 272 if ( !fn.IsAbsolute() ) 273 fn.Normalize(wxPATH_NORM_ENV_VARS | wxPATH_NORM_DOTS | wxPATH_NORM_TILDE | 274 wxPATH_NORM_ABSOLUTE | wxPATH_NORM_LONG); 275 276 wxTempFile fileTmp(fn.GetFullPath()); 277 278 if ( !fileTmp.IsOpened() ) { 279 wxLogError(_("can't write buffer '%s' to disk."), m_strBufferName.c_str()); 280 return false; 281 } 282 283 size_t nCount = GetLineCount(); 284 for ( size_t n = 0; n < nCount; n++ ) { 285 fileTmp.Write(GetLine(n) + 286 GetEOL(typeNew == wxTextFileType_None ? GetLineType(n) 287 : typeNew), 288 conv); 289 } 290 291 // replace the old file with this one 292 return fileTmp.Commit(); 293} 294 295#endif // wxUSE_TEXTFILE 296