]> git.saurik.com Git - wxWidgets.git/blame - src/common/file.cpp
Fix for #15520: wxRichTextCtrl: Drawing the selection doesn't respect its container...
[wxWidgets.git] / src / common / file.cpp
CommitLineData
c801d85f 1/////////////////////////////////////////////////////////////////////////////
f172cb82 2// Name: src/common/file.cpp
c801d85f
KB
3// Purpose: wxFile - encapsulates low-level "file descriptor"
4// wxTempFile
5// Author: Vadim Zeitlin
6// Modified by:
7// Created: 29/01/98
c801d85f 8// Copyright: (c) 1998 Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr>
65571936 9// Licence: wxWindows licence
c801d85f
KB
10/////////////////////////////////////////////////////////////////////////////
11
12// ----------------------------------------------------------------------------
13// headers
14// ----------------------------------------------------------------------------
15
c801d85f
KB
16// For compilers that support precompilation, includes "wx.h".
17#include "wx/wxprec.h"
c801d85f
KB
18
19#ifdef __BORLANDC__
ce4169a4 20 #pragma hdrstop
c801d85f
KB
21#endif
22
ce4169a4
RR
23#if wxUSE_FILE
24
c801d85f 25// standard
d98a58c5 26#if defined(__WINDOWS__) && !defined(__GNUWIN32__) && !defined(__WXMICROWIN__) && !defined(__WXWINCE__)
30a5be97 27
f172cb82
VZ
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
a3ef5bf5 48
d98a58c5 49#elif defined(__WINDOWS__) && defined(__WXWINCE__)
d109c584 50 #include "wx/msw/missing.h"
5a3912f2
SN
51#elif (defined(__OS2__))
52 #include <io.h>
c801d85f 53#elif (defined(__UNIX__) || defined(__GNUWIN32__))
3f4a0c5b 54 #include <unistd.h>
fd938b11 55 #include <time.h>
732b8386 56 #include <sys/stat.h>
3f4a0c5b 57 #ifdef __GNUWIN32__
9ed0d735 58 #include "wx/msw/wrapwin.h"
3f4a0c5b 59 #endif
d3b4d710
VS
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
34138703 70#elif (defined(__WXSTUBS__))
3f4a0c5b
VZ
71 // Have to ifdef this for different environments
72 #include <io.h>
17dff81c 73#elif (defined(__WXMAC__))
5b781a67 74#if __MSL__ < 0x6000
3f4a0c5b 75 int access( const char *path, int mode ) { return 0 ; }
5b781a67
SC
76#else
77 int _access( const char *path, int mode ) { return 0 ; }
78#endif
3f4a0c5b 79 char* mktemp( char * path ) { return path ;}
5b781a67 80 #include <stat.h>
6294ac2e 81 #include <unistd.h>
c801d85f 82#else
3f4a0c5b 83 #error "Please specify the header with file functions declarations."
c801d85f
KB
84#endif //Win/UNIX
85
86#include <stdio.h> // SEEK_xxx constants
1c193821 87
65fe93d8
VZ
88#ifndef __WXWINCE__
89 #include <errno.h>
90#endif
91
4ea2c29f
VZ
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
34138703 102
77ffb593 103// wxWidgets
ade35f11
VZ
104#ifndef WX_PRECOMP
105 #include "wx/string.h"
106 #include "wx/intl.h"
ade35f11 107 #include "wx/log.h"
0bf751e7 108 #include "wx/crt.h"
ade35f11
VZ
109#endif // !WX_PRECOMP
110
111#include "wx/filename.h"
44d568b6 112#include "wx/file.h"
4ea2c29f 113#include "wx/filefn.h"
f6bcfd97 114
7cbe148e
SN
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
d98a58c5 121#ifdef __WINDOWS__
28f5082b
VS
122 #include "wx/msw/mslu.h"
123#endif
124
1c193821
JS
125#ifdef __WXWINCE__
126 #include "wx/msw/private.h"
127#endif
128
7cbe148e
SN
129#ifndef MAX_PATH
130 #define MAX_PATH 512
131#endif
30984dea 132
c801d85f
KB
133// ============================================================================
134// implementation of wxFile
135// ============================================================================
136
137// ----------------------------------------------------------------------------
138// static functions
139// ----------------------------------------------------------------------------
4ea2c29f 140
fcea31d5 141bool wxFile::Exists(const wxString& name)
246037e2 142{
4ea2c29f 143 return wxFileExists(name);
d1427b70
VZ
144}
145
fcea31d5 146bool wxFile::Access(const wxString& name, OpenMode mode)
d1427b70 147{
4ea2c29f
VZ
148 int how;
149
150 switch ( mode )
151 {
152 default:
153 wxFAIL_MSG(wxT("bad wxFile::Access mode parameter."));
154 // fall through
d1427b70 155
49d5d881
VZ
156 case read:
157 how = R_OK;
158 break;
d1427b70 159
49d5d881
VZ
160 case write:
161 how = W_OK;
162 break;
d1427b70 163
4ea2c29f
VZ
164 case read_write:
165 how = R_OK | W_OK;
166 break;
49d5d881 167 }
d1427b70 168
4ea2c29f 169 return wxAccess(name, how) == 0;
c801d85f
KB
170}
171
172// ----------------------------------------------------------------------------
173// opening/closing
174// ----------------------------------------------------------------------------
175
176// ctors
11aac4ba 177wxFile::wxFile(const wxString& fileName, OpenMode mode)
c801d85f 178{
49d5d881 179 m_fd = fd_invalid;
65fe93d8 180 m_lasterror = 0;
c801d85f 181
11aac4ba 182 Open(fileName, mode);
c801d85f
KB
183}
184
65fe93d8
VZ
185bool 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
c801d85f 201// create the file, fail if it already exists and bOverwrite
fcea31d5 202bool wxFile::Create(const wxString& fileName, bool bOverwrite, int accessMode)
c801d85f 203{
49d5d881
VZ
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
7d1214cd 206 int fildes = wxOpen( fileName,
92980e90 207 O_BINARY | O_WRONLY | O_CREAT |
f172cb82
VZ
208 (bOverwrite ? O_TRUNC : O_EXCL),
209 accessMode );
7d1214cd 210 if ( CheckForError(fildes) )
92980e90 211 {
fcea31d5 212 wxLogSysError(_("can't create file '%s'"), fileName);
a62848fd 213 return false;
49d5d881 214 }
769627d7 215
7d1214cd 216 Attach(fildes);
769627d7 217 return true;
c801d85f
KB
218}
219
220// open the file
fcea31d5 221bool wxFile::Open(const wxString& fileName, OpenMode mode, int accessMode)
c801d85f 222{
49d5d881 223 int flags = O_BINARY;
c801d85f 224
92980e90
RR
225 switch ( mode )
226 {
49d5d881
VZ
227 case read:
228 flags |= O_RDONLY;
229 break;
c801d85f 230
f6bcfd97 231 case write_append:
fcea31d5 232 if ( wxFile::Exists(fileName) )
f6bcfd97
BP
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
49d5d881
VZ
240 case write:
241 flags |= O_WRONLY | O_CREAT | O_TRUNC;
242 break;
61b02744 243
68164137
RL
244 case write_excl:
245 flags |= O_WRONLY | O_CREAT | O_EXCL;
246 break;
247
49d5d881
VZ
248 case read_write:
249 flags |= O_RDWR;
250 break;
251 }
c801d85f 252
cc8cc54f
VZ
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
7d1214cd 260 int fildes = wxOpen( fileName, flags, accessMode);
6294ac2e 261
7d1214cd 262 if ( CheckForError(fildes) )
92980e90 263 {
fcea31d5 264 wxLogSysError(_("can't open file '%s'"), fileName);
a62848fd 265 return false;
49d5d881 266 }
769627d7 267
7d1214cd 268 Attach(fildes);
769627d7 269 return true;
c801d85f
KB
270}
271
272// close
61b02744 273bool wxFile::Close()
c801d85f 274{
49d5d881 275 if ( IsOpened() ) {
65fe93d8 276 if ( CheckForError(wxClose(m_fd)) )
1c193821 277 {
49d5d881
VZ
278 wxLogSysError(_("can't close file descriptor %d"), m_fd);
279 m_fd = fd_invalid;
a62848fd 280 return false;
49d5d881
VZ
281 }
282 else
283 m_fd = fd_invalid;
61b02744 284 }
61b02744 285
a62848fd 286 return true;
c801d85f
KB
287}
288
289// ----------------------------------------------------------------------------
290// read/write
291// ----------------------------------------------------------------------------
292
614108e2
VZ
293bool wxFile::ReadAll(wxString *str, const wxMBConv& conv)
294{
295 wxCHECK_MSG( str, false, wxS("Output string must be non-NULL") );
296
06a086e8 297 ssize_t length = Length();
614108e2
VZ
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 {
5fd78a8e 304 static const ssize_t READSIZE = 4096;
614108e2 305
7d1214cd
PC
306 ssize_t nread = Read(p, length > READSIZE ? READSIZE : length);
307 if ( nread == wxInvalidOffset )
614108e2
VZ
308 return false;
309
7d1214cd 310 p += nread;
06a086e8
VZ
311 if ( length <= nread )
312 break;
313
314 length -= nread;
614108e2
VZ
315 }
316
317 *p = 0;
318
319 wxString strTmp(buf, conv);
320 str->swap(strTmp);
321
322 return true;
323}
324
c801d85f 325// read
f8a586e0 326ssize_t wxFile::Read(void *pBuf, size_t nCount)
c801d85f 327{
49d5d881 328 wxCHECK( (pBuf != NULL) && IsOpened(), 0 );
c801d85f 329
30984dea 330 ssize_t iRc = wxRead(m_fd, pBuf, nCount);
6294ac2e 331
65fe93d8 332 if ( CheckForError(iRc) )
02aef13c 333 {
49d5d881 334 wxLogSysError(_("can't read from file descriptor %d"), m_fd);
f8a586e0 335 return wxInvalidOffset;
49d5d881 336 }
02aef13c
VZ
337
338 return iRc;
c801d85f
KB
339}
340
341// write
30984dea 342size_t wxFile::Write(const void *pBuf, size_t nCount)
c801d85f 343{
49d5d881 344 wxCHECK( (pBuf != NULL) && IsOpened(), 0 );
c801d85f 345
30984dea 346 ssize_t iRc = wxWrite(m_fd, pBuf, nCount);
6294ac2e 347
65fe93d8 348 if ( CheckForError(iRc) )
02aef13c 349 {
49d5d881 350 wxLogSysError(_("can't write to file descriptor %d"), m_fd);
02aef13c 351 iRc = 0;
49d5d881 352 }
02aef13c 353
30984dea 354 return iRc;
c801d85f
KB
355}
356
b1c67394
VZ
357bool wxFile::Write(const wxString& s, const wxMBConv& conv)
358{
359 const wxWX2MBbuf buf = s.mb_str(conv);
360 if ( !buf )
361 return false;
362
ae0e22dd 363#if wxUSE_UNICODE
227989f3 364 const size_t size = buf.length();
ae0e22dd
VZ
365#else
366 const size_t size = s.length();
367#endif
368
b1c67394
VZ
369 return Write(buf, size) == size;
370}
371
c801d85f
KB
372// flush
373bool wxFile::Flush()
374{
c8ccc915 375#ifdef HAVE_FSYNC
608a34bf
VZ
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 {
65fe93d8 380 if ( CheckForError(wxFsync(m_fd)) )
09914df7
VZ
381 {
382 wxLogSysError(_("can't flush file descriptor %d"), m_fd);
a62848fd 383 return false;
09914df7 384 }
49d5d881 385 }
c8ccc915 386#endif // HAVE_FSYNC
c801d85f 387
a62848fd 388 return true;
c801d85f
KB
389}
390
391// ----------------------------------------------------------------------------
392// seek
393// ----------------------------------------------------------------------------
394
395// seek
30984dea 396wxFileOffset wxFile::Seek(wxFileOffset ofs, wxSeekMode mode)
c801d85f 397{
9a83f860 398 wxASSERT_MSG( IsOpened(), wxT("can't seek on closed file") );
686a3ee0
VZ
399 wxCHECK_MSG( ofs != wxInvalidOffset || mode != wxFromStart,
400 wxInvalidOffset,
9a83f860 401 wxT("invalid absolute file offset") );
49d5d881 402
a1b82138 403 int origin;
49d5d881 404 switch ( mode ) {
a1b82138 405 default:
9a83f860 406 wxFAIL_MSG(wxT("unknown seek origin"));
a1b82138 407
49d5d881 408 case wxFromStart:
a1b82138 409 origin = SEEK_SET;
49d5d881
VZ
410 break;
411
412 case wxFromCurrent:
a1b82138 413 origin = SEEK_CUR;
49d5d881
VZ
414 break;
415
416 case wxFromEnd:
a1b82138 417 origin = SEEK_END;
49d5d881 418 break;
49d5d881
VZ
419 }
420
30984dea 421 wxFileOffset iRc = wxSeek(m_fd, ofs, origin);
65fe93d8 422 if ( CheckForError(iRc) )
02aef13c 423 {
49d5d881 424 wxLogSysError(_("can't seek on file descriptor %d"), m_fd);
49d5d881 425 }
02aef13c
VZ
426
427 return iRc;
c801d85f
KB
428}
429
02aef13c 430// get current file offset
30984dea 431wxFileOffset wxFile::Tell() const
c801d85f 432{
49d5d881
VZ
433 wxASSERT( IsOpened() );
434
30984dea 435 wxFileOffset iRc = wxTell(m_fd);
65fe93d8 436 if ( CheckForError(iRc) )
02aef13c 437 {
49d5d881 438 wxLogSysError(_("can't get seek position on file descriptor %d"), m_fd);
49d5d881 439 }
02aef13c
VZ
440
441 return iRc;
c801d85f
KB
442}
443
444// get current file length
30984dea 445wxFileOffset wxFile::Length() const
c801d85f 446{
49d5d881 447 wxASSERT( IsOpened() );
c801d85f 448
41f6f17d
VZ
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
30984dea 464 wxFileOffset iRc = Tell();
02aef13c 465 if ( iRc != wxInvalidOffset ) {
f48a1159 466 wxFileOffset iLen = const_cast<wxFile *>(this)->SeekEnd();
02aef13c 467 if ( iLen != wxInvalidOffset ) {
49d5d881 468 // restore old position
02aef13c 469 if ( ((wxFile *)this)->Seek(iRc) == wxInvalidOffset ) {
49d5d881 470 // error
02aef13c 471 iLen = wxInvalidOffset;
49d5d881 472 }
c801d85f 473 }
c801d85f 474
49d5d881
VZ
475 iRc = iLen;
476 }
49d5d881 477
02aef13c
VZ
478 if ( iRc == wxInvalidOffset )
479 {
65fe93d8 480 // last error was already set by Tell()
49d5d881 481 wxLogSysError(_("can't find length of file on file descriptor %d"), m_fd);
c801d85f 482 }
02aef13c
VZ
483
484 return iRc;
c801d85f
KB
485}
486
487// is end of file reached?
488bool wxFile::Eof() const
489{
49d5d881 490 wxASSERT( IsOpened() );
c801d85f 491
30984dea 492 wxFileOffset iRc;
61b02744 493
2415cf67 494#if defined(__DOS__) || defined(__UNIX__) || defined(__GNUWIN32__)
61b02744 495 // @@ this doesn't work, of course, on unseekable file descriptors
30984dea 496 wxFileOffset ofsCur = Tell(),
49d5d881 497 ofsMax = Length();
1678ad78 498 if ( ofsCur == wxInvalidOffset || ofsMax == wxInvalidOffset )
02aef13c 499 iRc = wxInvalidOffset;
61b02744 500 else
49d5d881
VZ
501 iRc = ofsCur == ofsMax;
502#else // Windows and "native" compiler
6294ac2e 503 iRc = wxEof(m_fd);
49d5d881 504#endif // Windows/Unix
c801d85f 505
43b2d5e7 506 if ( iRc == 0 )
b9daf00a 507 return false;
43b2d5e7
VZ
508
509 if ( iRc == wxInvalidOffset )
510 {
b9daf00a 511 wxLogSysError(_("can't determine if the end of file is reached on descriptor %d"), m_fd);
43b2d5e7 512 }
c801d85f 513
a62848fd 514 return true;
c801d85f
KB
515}
516
517// ============================================================================
518// implementation of wxTempFile
519// ============================================================================
520
521// ----------------------------------------------------------------------------
522// construction
523// ----------------------------------------------------------------------------
44b62d54 524
c801d85f
KB
525wxTempFile::wxTempFile(const wxString& strName)
526{
49d5d881 527 Open(strName);
c801d85f
KB
528}
529
530bool wxTempFile::Open(const wxString& strName)
531{
44b62d54
VZ
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();
246037e2 546
44b62d54 547 m_strTemp = wxFileName::CreateTempFileName(m_strName, &m_file);
ade35f11
VZ
548
549 if ( m_strTemp.empty() )
550 {
551 // CreateTempFileName() failed
a62848fd 552 return false;
ade35f11 553 }
49d5d881 554
49d5d881 555#ifdef __UNIX__
ade35f11
VZ
556 // the temp file should have the same permissions as the original one
557 mode_t mode;
a62848fd 558
f6bcfd97 559 wxStructStat st;
ca11abde 560 if ( stat( (const char*) m_strName.fn_str(), &st) == 0 )
49d5d881 561 {
ade35f11 562 mode = st.st_mode;
49d5d881
VZ
563 }
564 else
565 {
ade35f11 566 // file probably didn't exist, just give it the default mode _using_
68164137 567 // user's umask (new files creation should respect umask)
ade35f11
VZ
568 mode_t mask = umask(0777);
569 mode = 0666 & ~mask;
570 umask(mask);
49d5d881 571 }
49d5d881 572
ca11abde 573 if ( chmod( (const char*) m_strTemp.fn_str(), mode) == -1 )
fe99e285 574 {
5a3912f2 575#ifndef __OS2__
ade35f11 576 wxLogSysError(_("Failed to set temporary file permissions"));
5a3912f2 577#endif
fe99e285 578 }
49d5d881 579#endif // Unix
246037e2 580
a62848fd 581 return true;
c801d85f
KB
582}
583
584// ----------------------------------------------------------------------------
585// destruction
586// ----------------------------------------------------------------------------
587
588wxTempFile::~wxTempFile()
589{
49d5d881
VZ
590 if ( IsOpened() )
591 Discard();
c801d85f
KB
592}
593
594bool wxTempFile::Commit()
595{
49d5d881 596 m_file.Close();
c801d85f 597
f6bcfd97 598 if ( wxFile::Exists(m_strName) && wxRemove(m_strName) != 0 ) {
49d5d881 599 wxLogSysError(_("can't remove file '%s'"), m_strName.c_str());
a62848fd 600 return false;
49d5d881 601 }
c801d85f 602
f35746ce 603 if ( !wxRenameFile(m_strTemp, m_strName) ) {
49d5d881 604 wxLogSysError(_("can't commit changes to file '%s'"), m_strName.c_str());
a62848fd 605 return false;
49d5d881 606 }
c801d85f 607
a62848fd 608 return true;
c801d85f
KB
609}
610
611void wxTempFile::Discard()
612{
49d5d881 613 m_file.Close();
f6bcfd97 614 if ( wxRemove(m_strTemp) != 0 )
af588446 615 {
49d5d881 616 wxLogSysError(_("can't remove temporary file '%s'"), m_strTemp.c_str());
af588446 617 }
c801d85f 618}
ce4169a4 619
ade35f11 620#endif // wxUSE_FILE
cc985fac 621