]> git.saurik.com Git - wxWidgets.git/blob - src/common/file.cpp
Workaround for #15404: wxRichTextCtrl: caret does not disappear when focus is lost...
[wxWidgets.git] / src / common / file.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/common/file.cpp
3 // Purpose: wxFile - encapsulates low-level "file descriptor"
4 // wxTempFile
5 // Author: Vadim Zeitlin
6 // Modified by:
7 // Created: 29/01/98
8 // Copyright: (c) 1998 Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr>
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11
12 // ----------------------------------------------------------------------------
13 // headers
14 // ----------------------------------------------------------------------------
15
16 // For compilers that support precompilation, includes "wx.h".
17 #include "wx/wxprec.h"
18
19 #ifdef __BORLANDC__
20 #pragma hdrstop
21 #endif
22
23 #if wxUSE_FILE
24
25 // standard
26 #if defined(__WINDOWS__) && !defined(__GNUWIN32__) && !defined(__WXMICROWIN__) && !defined(__WXWINCE__)
27
28 #define WIN32_LEAN_AND_MEAN
29 #define NOSERVICE
30 #define NOIME
31 #define NOATOM
32 #define NOGDI
33 #define NOGDICAPMASKS
34 #define NOMETAFILE
35 #define NOMINMAX
36 #define NOMSG
37 #define NOOPENFILE
38 #define NORASTEROPS
39 #define NOSCROLL
40 #define NOSOUND
41 #define NOSYSMETRICS
42 #define NOTEXTMETRIC
43 #define NOWH
44 #define NOCOMM
45 #define NOKANJI
46 #define NOCRYPT
47 #define NOMCX
48
49 #elif defined(__WINDOWS__) && defined(__WXWINCE__)
50 #include "wx/msw/missing.h"
51 #elif (defined(__OS2__))
52 #include <io.h>
53 #elif (defined(__UNIX__) || defined(__GNUWIN32__))
54 #include <unistd.h>
55 #include <time.h>
56 #include <sys/stat.h>
57 #ifdef __GNUWIN32__
58 #include "wx/msw/wrapwin.h"
59 #endif
60 #elif defined(__DOS__)
61 #if defined(__WATCOMC__)
62 #include <io.h>
63 #elif defined(__DJGPP__)
64 #include <io.h>
65 #include <unistd.h>
66 #include <stdio.h>
67 #else
68 #error "Please specify the header with file functions declarations."
69 #endif
70 #elif (defined(__WXSTUBS__))
71 // Have to ifdef this for different environments
72 #include <io.h>
73 #elif (defined(__WXMAC__))
74 #if __MSL__ < 0x6000
75 int access( const char *path, int mode ) { return 0 ; }
76 #else
77 int _access( const char *path, int mode ) { return 0 ; }
78 #endif
79 char* mktemp( char * path ) { return path ;}
80 #include <stat.h>
81 #include <unistd.h>
82 #else
83 #error "Please specify the header with file functions declarations."
84 #endif //Win/UNIX
85
86 #include <stdio.h> // SEEK_xxx constants
87
88 #ifndef __WXWINCE__
89 #include <errno.h>
90 #endif
91
92 // Windows compilers don't have these constants
93 #ifndef W_OK
94 enum
95 {
96 F_OK = 0, // test for existence
97 X_OK = 1, // execute permission
98 W_OK = 2, // write
99 R_OK = 4 // read
100 };
101 #endif // W_OK
102
103 // wxWidgets
104 #ifndef WX_PRECOMP
105 #include "wx/string.h"
106 #include "wx/intl.h"
107 #include "wx/log.h"
108 #include "wx/crt.h"
109 #endif // !WX_PRECOMP
110
111 #include "wx/filename.h"
112 #include "wx/file.h"
113 #include "wx/filefn.h"
114
115 // there is no distinction between text and binary files under Unix, so define
116 // O_BINARY as 0 if the system headers don't do it already
117 #if defined(__UNIX__) && !defined(O_BINARY)
118 #define O_BINARY (0)
119 #endif //__UNIX__
120
121 #ifdef __WINDOWS__
122 #include "wx/msw/mslu.h"
123 #endif
124
125 #ifdef __WXWINCE__
126 #include "wx/msw/private.h"
127 #endif
128
129 #ifndef MAX_PATH
130 #define MAX_PATH 512
131 #endif
132
133 // ============================================================================
134 // implementation of wxFile
135 // ============================================================================
136
137 // ----------------------------------------------------------------------------
138 // static functions
139 // ----------------------------------------------------------------------------
140
141 bool wxFile::Exists(const wxString& name)
142 {
143 return wxFileExists(name);
144 }
145
146 bool wxFile::Access(const wxString& name, OpenMode mode)
147 {
148 int how;
149
150 switch ( mode )
151 {
152 default:
153 wxFAIL_MSG(wxT("bad wxFile::Access mode parameter."));
154 // fall through
155
156 case read:
157 how = R_OK;
158 break;
159
160 case write:
161 how = W_OK;
162 break;
163
164 case read_write:
165 how = R_OK | W_OK;
166 break;
167 }
168
169 return wxAccess(name, how) == 0;
170 }
171
172 // ----------------------------------------------------------------------------
173 // opening/closing
174 // ----------------------------------------------------------------------------
175
176 // ctors
177 wxFile::wxFile(const wxString& fileName, OpenMode mode)
178 {
179 m_fd = fd_invalid;
180 m_lasterror = 0;
181
182 Open(fileName, mode);
183 }
184
185 bool wxFile::CheckForError(wxFileOffset rc) const
186 {
187 if ( rc != -1 )
188 return false;
189
190 const_cast<wxFile *>(this)->m_lasterror =
191 #ifndef __WXWINCE__
192 errno
193 #else
194 ::GetLastError()
195 #endif
196 ;
197
198 return true;
199 }
200
201 // create the file, fail if it already exists and bOverwrite
202 bool wxFile::Create(const wxString& fileName, bool bOverwrite, int accessMode)
203 {
204 // if bOverwrite we create a new file or truncate the existing one,
205 // otherwise we only create the new file and fail if it already exists
206 int fildes = wxOpen( fileName,
207 O_BINARY | O_WRONLY | O_CREAT |
208 (bOverwrite ? O_TRUNC : O_EXCL),
209 accessMode );
210 if ( CheckForError(fildes) )
211 {
212 wxLogSysError(_("can't create file '%s'"), fileName);
213 return false;
214 }
215
216 Attach(fildes);
217 return true;
218 }
219
220 // open the file
221 bool wxFile::Open(const wxString& fileName, OpenMode mode, int accessMode)
222 {
223 int flags = O_BINARY;
224
225 switch ( mode )
226 {
227 case read:
228 flags |= O_RDONLY;
229 break;
230
231 case write_append:
232 if ( wxFile::Exists(fileName) )
233 {
234 flags |= O_WRONLY | O_APPEND;
235 break;
236 }
237 //else: fall through as write_append is the same as write if the
238 // file doesn't exist
239
240 case write:
241 flags |= O_WRONLY | O_CREAT | O_TRUNC;
242 break;
243
244 case write_excl:
245 flags |= O_WRONLY | O_CREAT | O_EXCL;
246 break;
247
248 case read_write:
249 flags |= O_RDWR;
250 break;
251 }
252
253 #ifdef __WINDOWS__
254 // only read/write bits for "all" are supported by this function under
255 // Windows, and VC++ 8 returns EINVAL if any other bits are used in
256 // accessMode, so clear them as they have at best no effect anyhow
257 accessMode &= wxS_IRUSR | wxS_IWUSR;
258 #endif // __WINDOWS__
259
260 int fildes = wxOpen( fileName, flags, accessMode);
261
262 if ( CheckForError(fildes) )
263 {
264 wxLogSysError(_("can't open file '%s'"), fileName);
265 return false;
266 }
267
268 Attach(fildes);
269 return true;
270 }
271
272 // close
273 bool wxFile::Close()
274 {
275 if ( IsOpened() ) {
276 if ( CheckForError(wxClose(m_fd)) )
277 {
278 wxLogSysError(_("can't close file descriptor %d"), m_fd);
279 m_fd = fd_invalid;
280 return false;
281 }
282 else
283 m_fd = fd_invalid;
284 }
285
286 return true;
287 }
288
289 // ----------------------------------------------------------------------------
290 // read/write
291 // ----------------------------------------------------------------------------
292
293 bool wxFile::ReadAll(wxString *str, const wxMBConv& conv)
294 {
295 wxCHECK_MSG( str, false, wxS("Output string must be non-NULL") );
296
297 ssize_t length = Length();
298 wxCHECK_MSG( (wxFileOffset)length == Length(), false, wxT("huge file not supported") );
299
300 wxCharBuffer buf(length);
301 char* p = buf.data();
302 for ( ;; )
303 {
304 static const ssize_t READSIZE = 4096;
305
306 ssize_t nread = Read(p, length > READSIZE ? READSIZE : length);
307 if ( nread == wxInvalidOffset )
308 return false;
309
310 p += nread;
311 if ( length <= nread )
312 break;
313
314 length -= nread;
315 }
316
317 *p = 0;
318
319 wxString strTmp(buf, conv);
320 str->swap(strTmp);
321
322 return true;
323 }
324
325 // read
326 ssize_t wxFile::Read(void *pBuf, size_t nCount)
327 {
328 wxCHECK( (pBuf != NULL) && IsOpened(), 0 );
329
330 ssize_t iRc = wxRead(m_fd, pBuf, nCount);
331
332 if ( CheckForError(iRc) )
333 {
334 wxLogSysError(_("can't read from file descriptor %d"), m_fd);
335 return wxInvalidOffset;
336 }
337
338 return iRc;
339 }
340
341 // write
342 size_t wxFile::Write(const void *pBuf, size_t nCount)
343 {
344 wxCHECK( (pBuf != NULL) && IsOpened(), 0 );
345
346 ssize_t iRc = wxWrite(m_fd, pBuf, nCount);
347
348 if ( CheckForError(iRc) )
349 {
350 wxLogSysError(_("can't write to file descriptor %d"), m_fd);
351 iRc = 0;
352 }
353
354 return iRc;
355 }
356
357 bool wxFile::Write(const wxString& s, const wxMBConv& conv)
358 {
359 const wxWX2MBbuf buf = s.mb_str(conv);
360 if ( !buf )
361 return false;
362
363 #if wxUSE_UNICODE
364 const size_t size = buf.length();
365 #else
366 const size_t size = s.length();
367 #endif
368
369 return Write(buf, size) == size;
370 }
371
372 // flush
373 bool wxFile::Flush()
374 {
375 #ifdef HAVE_FSYNC
376 // fsync() only works on disk files and returns errors for pipes, don't
377 // call it then
378 if ( IsOpened() && GetKind() == wxFILE_KIND_DISK )
379 {
380 if ( CheckForError(wxFsync(m_fd)) )
381 {
382 wxLogSysError(_("can't flush file descriptor %d"), m_fd);
383 return false;
384 }
385 }
386 #endif // HAVE_FSYNC
387
388 return true;
389 }
390
391 // ----------------------------------------------------------------------------
392 // seek
393 // ----------------------------------------------------------------------------
394
395 // seek
396 wxFileOffset wxFile::Seek(wxFileOffset ofs, wxSeekMode mode)
397 {
398 wxASSERT_MSG( IsOpened(), wxT("can't seek on closed file") );
399 wxCHECK_MSG( ofs != wxInvalidOffset || mode != wxFromStart,
400 wxInvalidOffset,
401 wxT("invalid absolute file offset") );
402
403 int origin;
404 switch ( mode ) {
405 default:
406 wxFAIL_MSG(wxT("unknown seek origin"));
407
408 case wxFromStart:
409 origin = SEEK_SET;
410 break;
411
412 case wxFromCurrent:
413 origin = SEEK_CUR;
414 break;
415
416 case wxFromEnd:
417 origin = SEEK_END;
418 break;
419 }
420
421 wxFileOffset iRc = wxSeek(m_fd, ofs, origin);
422 if ( CheckForError(iRc) )
423 {
424 wxLogSysError(_("can't seek on file descriptor %d"), m_fd);
425 }
426
427 return iRc;
428 }
429
430 // get current file offset
431 wxFileOffset wxFile::Tell() const
432 {
433 wxASSERT( IsOpened() );
434
435 wxFileOffset iRc = wxTell(m_fd);
436 if ( CheckForError(iRc) )
437 {
438 wxLogSysError(_("can't get seek position on file descriptor %d"), m_fd);
439 }
440
441 return iRc;
442 }
443
444 // get current file length
445 wxFileOffset wxFile::Length() const
446 {
447 wxASSERT( IsOpened() );
448
449 // we use a special method for Linux systems where files in sysfs (i.e.
450 // those under /sys typically) return length of 4096 bytes even when
451 // they're much smaller -- this is a problem as it results in errors later
452 // when we try reading 4KB from them
453 #ifdef __LINUX__
454 struct stat st;
455 if ( fstat(m_fd, &st) == 0 )
456 {
457 // returning 0 for the special files indicates to the caller that they
458 // are not seekable
459 return st.st_blocks ? st.st_size : 0;
460 }
461 //else: failed to stat, try the normal method
462 #endif // __LINUX__
463
464 wxFileOffset iRc = Tell();
465 if ( iRc != wxInvalidOffset ) {
466 wxFileOffset iLen = const_cast<wxFile *>(this)->SeekEnd();
467 if ( iLen != wxInvalidOffset ) {
468 // restore old position
469 if ( ((wxFile *)this)->Seek(iRc) == wxInvalidOffset ) {
470 // error
471 iLen = wxInvalidOffset;
472 }
473 }
474
475 iRc = iLen;
476 }
477
478 if ( iRc == wxInvalidOffset )
479 {
480 // last error was already set by Tell()
481 wxLogSysError(_("can't find length of file on file descriptor %d"), m_fd);
482 }
483
484 return iRc;
485 }
486
487 // is end of file reached?
488 bool wxFile::Eof() const
489 {
490 wxASSERT( IsOpened() );
491
492 wxFileOffset iRc;
493
494 #if defined(__DOS__) || defined(__UNIX__) || defined(__GNUWIN32__)
495 // @@ this doesn't work, of course, on unseekable file descriptors
496 wxFileOffset ofsCur = Tell(),
497 ofsMax = Length();
498 if ( ofsCur == wxInvalidOffset || ofsMax == wxInvalidOffset )
499 iRc = wxInvalidOffset;
500 else
501 iRc = ofsCur == ofsMax;
502 #else // Windows and "native" compiler
503 iRc = wxEof(m_fd);
504 #endif // Windows/Unix
505
506 if ( iRc == 0 )
507 return false;
508
509 if ( iRc == wxInvalidOffset )
510 {
511 wxLogSysError(_("can't determine if the end of file is reached on descriptor %d"), m_fd);
512 }
513
514 return true;
515 }
516
517 // ============================================================================
518 // implementation of wxTempFile
519 // ============================================================================
520
521 // ----------------------------------------------------------------------------
522 // construction
523 // ----------------------------------------------------------------------------
524
525 wxTempFile::wxTempFile(const wxString& strName)
526 {
527 Open(strName);
528 }
529
530 bool wxTempFile::Open(const wxString& strName)
531 {
532 // we must have an absolute filename because otherwise CreateTempFileName()
533 // would create the temp file in $TMP (i.e. the system standard location
534 // for the temp files) which might be on another volume/drive/mount and
535 // wxRename()ing it later to m_strName from Commit() would then fail
536 //
537 // with the absolute filename, the temp file is created in the same
538 // directory as this one which ensures that wxRename() may work later
539 wxFileName fn(strName);
540 if ( !fn.IsAbsolute() )
541 {
542 fn.Normalize(wxPATH_NORM_ABSOLUTE);
543 }
544
545 m_strName = fn.GetFullPath();
546
547 m_strTemp = wxFileName::CreateTempFileName(m_strName, &m_file);
548
549 if ( m_strTemp.empty() )
550 {
551 // CreateTempFileName() failed
552 return false;
553 }
554
555 #ifdef __UNIX__
556 // the temp file should have the same permissions as the original one
557 mode_t mode;
558
559 wxStructStat st;
560 if ( stat( (const char*) m_strName.fn_str(), &st) == 0 )
561 {
562 mode = st.st_mode;
563 }
564 else
565 {
566 // file probably didn't exist, just give it the default mode _using_
567 // user's umask (new files creation should respect umask)
568 mode_t mask = umask(0777);
569 mode = 0666 & ~mask;
570 umask(mask);
571 }
572
573 if ( chmod( (const char*) m_strTemp.fn_str(), mode) == -1 )
574 {
575 #ifndef __OS2__
576 wxLogSysError(_("Failed to set temporary file permissions"));
577 #endif
578 }
579 #endif // Unix
580
581 return true;
582 }
583
584 // ----------------------------------------------------------------------------
585 // destruction
586 // ----------------------------------------------------------------------------
587
588 wxTempFile::~wxTempFile()
589 {
590 if ( IsOpened() )
591 Discard();
592 }
593
594 bool wxTempFile::Commit()
595 {
596 m_file.Close();
597
598 if ( wxFile::Exists(m_strName) && wxRemove(m_strName) != 0 ) {
599 wxLogSysError(_("can't remove file '%s'"), m_strName.c_str());
600 return false;
601 }
602
603 if ( !wxRenameFile(m_strTemp, m_strName) ) {
604 wxLogSysError(_("can't commit changes to file '%s'"), m_strName.c_str());
605 return false;
606 }
607
608 return true;
609 }
610
611 void wxTempFile::Discard()
612 {
613 m_file.Close();
614 if ( wxRemove(m_strTemp) != 0 )
615 {
616 wxLogSysError(_("can't remove temporary file '%s'"), m_strTemp.c_str());
617 }
618 }
619
620 #endif // wxUSE_FILE
621